From fb50b020c5331c8c4bee0eb875865f5f8be6c03a Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 16 Nov 2012 13:53:09 -0800 Subject: [PATCH 0001/4607] x86: Move some contents of page_64_types.h into pgtable_64.h and page_64.h This patch is meant to clean-up the fact that we have several functions in page_64_types.h which really don't belong there. I found this issue when I had tried to replace __phys_addr with an inline function. It resulted in the realmode bits generating compile warnings about types. In order to resolve that I am relocating the address translation to page_64.h since this is in keeping with where these functions are located in 32 bit. In addtion I have relocated several functions defined in init_64.c to pgtable_64.h as this seems to be where most of the functions related to memory initialization were already located. [ hpa: added missing #include to apic_numachip.c, as reported by Yinghai Lu. ] Signed-off-by: Alexander Duyck Link: http://lkml.kernel.org/r/20121116215244.8521.31505.stgit@ahduyck-cp1.jf.intel.com Signed-off-by: H. Peter Anvin Cc: Yinghai Lu Cc: Daniel J Blueman --- arch/x86/include/asm/page_64.h | 19 +++++++++++++++++++ arch/x86/include/asm/page_64_types.h | 22 ---------------------- arch/x86/include/asm/pgtable_64.h | 5 +++++ arch/x86/kernel/apic/apic_numachip.c | 1 + 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/arch/x86/include/asm/page_64.h b/arch/x86/include/asm/page_64.h index 072694ed81a5..4150999fc20e 100644 --- a/arch/x86/include/asm/page_64.h +++ b/arch/x86/include/asm/page_64.h @@ -3,4 +3,23 @@ #include +#ifndef __ASSEMBLY__ + +/* duplicated to the one in bootmem.h */ +extern unsigned long max_pfn; +extern unsigned long phys_base; + +extern unsigned long __phys_addr(unsigned long); + +#define __phys_reloc_hide(x) (x) + +#ifdef CONFIG_FLATMEM +#define pfn_valid(pfn) ((pfn) < max_pfn) +#endif + +void clear_page(void *page); +void copy_page(void *to, void *from); + +#endif /* !__ASSEMBLY__ */ + #endif /* _ASM_X86_PAGE_64_H */ diff --git a/arch/x86/include/asm/page_64_types.h b/arch/x86/include/asm/page_64_types.h index 320f7bb95f76..8b491e66eaa8 100644 --- a/arch/x86/include/asm/page_64_types.h +++ b/arch/x86/include/asm/page_64_types.h @@ -50,26 +50,4 @@ #define KERNEL_IMAGE_SIZE (512 * 1024 * 1024) #define KERNEL_IMAGE_START _AC(0xffffffff80000000, UL) -#ifndef __ASSEMBLY__ -void clear_page(void *page); -void copy_page(void *to, void *from); - -/* duplicated to the one in bootmem.h */ -extern unsigned long max_pfn; -extern unsigned long phys_base; - -extern unsigned long __phys_addr(unsigned long); -#define __phys_reloc_hide(x) (x) - -#define vmemmap ((struct page *)VMEMMAP_START) - -extern void init_extra_mapping_uc(unsigned long phys, unsigned long size); -extern void init_extra_mapping_wb(unsigned long phys, unsigned long size); - -#endif /* !__ASSEMBLY__ */ - -#ifdef CONFIG_FLATMEM -#define pfn_valid(pfn) ((pfn) < max_pfn) -#endif - #endif /* _ASM_X86_PAGE_64_DEFS_H */ diff --git a/arch/x86/include/asm/pgtable_64.h b/arch/x86/include/asm/pgtable_64.h index 47356f9df82e..b5d30ad39022 100644 --- a/arch/x86/include/asm/pgtable_64.h +++ b/arch/x86/include/asm/pgtable_64.h @@ -183,6 +183,11 @@ extern void cleanup_highmap(void); #define __HAVE_ARCH_PTE_SAME +#define vmemmap ((struct page *)VMEMMAP_START) + +extern void init_extra_mapping_uc(unsigned long phys, unsigned long size); +extern void init_extra_mapping_wb(unsigned long phys, unsigned long size); + #endif /* !__ASSEMBLY__ */ #endif /* _ASM_X86_PGTABLE_64_H */ diff --git a/arch/x86/kernel/apic/apic_numachip.c b/arch/x86/kernel/apic/apic_numachip.c index a65829ac2b9a..ae9196f31261 100644 --- a/arch/x86/kernel/apic/apic_numachip.c +++ b/arch/x86/kernel/apic/apic_numachip.c @@ -27,6 +27,7 @@ #include #include #include +#include static int numachip_system __read_mostly; From 0bdf525f04afd3a32c14e5a8778771f9c9e0f074 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 16 Nov 2012 13:53:51 -0800 Subject: [PATCH 0002/4607] x86: Improve __phys_addr performance by making use of carry flags and inlining This patch is meant to improve overall system performance when making use of the __phys_addr call. To do this I have implemented several changes. First if CONFIG_DEBUG_VIRTUAL is not defined __phys_addr is made an inline, similar to how this is currently handled in 32 bit. However in order to do this it is required to export phys_base so that it is available if __phys_addr is used in kernel modules. The second change was to streamline the code by making use of the carry flag on an add operation instead of performing a compare on a 64 bit value. The advantage to this is that it allows us to significantly reduce the overall size of the call. On my Xeon E5 system the entire __phys_addr inline call consumes a little less than 32 bytes and 5 instructions. I also applied similar logic to the debug version of the function. My testing shows that the debug version of the function with this patch applied is slightly faster than the non-debug version without the patch. Finally I also applied the same logic changes to __virt_addr_valid since it used the same general code flow as __phys_addr and could achieve similar gains though these changes. Signed-off-by: Alexander Duyck Link: http://lkml.kernel.org/r/20121116215315.8521.46270.stgit@ahduyck-cp1.jf.intel.com Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/page_64.h | 14 +++++++++++ arch/x86/kernel/x8664_ksyms_64.c | 3 +++ arch/x86/mm/physaddr.c | 40 ++++++++++++++++++++------------ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/arch/x86/include/asm/page_64.h b/arch/x86/include/asm/page_64.h index 4150999fc20e..5138174c2101 100644 --- a/arch/x86/include/asm/page_64.h +++ b/arch/x86/include/asm/page_64.h @@ -9,7 +9,21 @@ extern unsigned long max_pfn; extern unsigned long phys_base; +static inline unsigned long __phys_addr_nodebug(unsigned long x) +{ + unsigned long y = x - __START_KERNEL_map; + + /* use the carry flag to determine if x was < __START_KERNEL_map */ + x = y + ((x > y) ? phys_base : (__START_KERNEL_map - PAGE_OFFSET)); + + return x; +} + +#ifdef CONFIG_DEBUG_VIRTUAL extern unsigned long __phys_addr(unsigned long); +#else +#define __phys_addr(x) __phys_addr_nodebug(x) +#endif #define __phys_reloc_hide(x) (x) diff --git a/arch/x86/kernel/x8664_ksyms_64.c b/arch/x86/kernel/x8664_ksyms_64.c index 1330dd102950..b014d9414d08 100644 --- a/arch/x86/kernel/x8664_ksyms_64.c +++ b/arch/x86/kernel/x8664_ksyms_64.c @@ -59,6 +59,9 @@ EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(__memcpy); EXPORT_SYMBOL(memmove); +#ifndef CONFIG_DEBUG_VIRTUAL +EXPORT_SYMBOL(phys_base); +#endif EXPORT_SYMBOL(empty_zero_page); #ifndef CONFIG_PARAVIRT EXPORT_SYMBOL(native_load_gs_index); diff --git a/arch/x86/mm/physaddr.c b/arch/x86/mm/physaddr.c index d2e2735327b4..fd40d75109ef 100644 --- a/arch/x86/mm/physaddr.c +++ b/arch/x86/mm/physaddr.c @@ -8,33 +8,43 @@ #ifdef CONFIG_X86_64 +#ifdef CONFIG_DEBUG_VIRTUAL unsigned long __phys_addr(unsigned long x) { - if (x >= __START_KERNEL_map) { - x -= __START_KERNEL_map; - VIRTUAL_BUG_ON(x >= KERNEL_IMAGE_SIZE); - x += phys_base; + unsigned long y = x - __START_KERNEL_map; + + /* use the carry flag to determine if x was < __START_KERNEL_map */ + if (unlikely(x > y)) { + x = y + phys_base; + + VIRTUAL_BUG_ON(y >= KERNEL_IMAGE_SIZE); } else { - VIRTUAL_BUG_ON(x < PAGE_OFFSET); - x -= PAGE_OFFSET; - VIRTUAL_BUG_ON(!phys_addr_valid(x)); + x = y + (__START_KERNEL_map - PAGE_OFFSET); + + /* carry flag will be set if starting x was >= PAGE_OFFSET */ + VIRTUAL_BUG_ON((x > y) || !phys_addr_valid(x)); } + return x; } EXPORT_SYMBOL(__phys_addr); +#endif bool __virt_addr_valid(unsigned long x) { - if (x >= __START_KERNEL_map) { - x -= __START_KERNEL_map; - if (x >= KERNEL_IMAGE_SIZE) + unsigned long y = x - __START_KERNEL_map; + + /* use the carry flag to determine if x was < __START_KERNEL_map */ + if (unlikely(x > y)) { + x = y + phys_base; + + if (y >= KERNEL_IMAGE_SIZE) return false; - x += phys_base; } else { - if (x < PAGE_OFFSET) - return false; - x -= PAGE_OFFSET; - if (!phys_addr_valid(x)) + x = y + (__START_KERNEL_map - PAGE_OFFSET); + + /* carry flag will be set if starting x was >= PAGE_OFFSET */ + if ((x > y) || !phys_addr_valid(x)) return false; } From 7d74275d39def4d3ccc8cf4725388bf79ef13861 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 16 Nov 2012 13:55:46 -0800 Subject: [PATCH 0003/4607] x86: Make it so that __pa_symbol can only process kernel symbols on x86_64 I submitted an earlier patch that make __phys_addr an inline. This obviously results in an increase in the code size. One step I can take to reduce that is to make it so that the __pa_symbol call does a direct translation for kernel addresses instead of covering all of virtual memory. On my system this reduced the size for __pa_symbol from 5 instructions totalling 30 bytes to 3 instructions totalling 16 bytes. Signed-off-by: Alexander Duyck Link: http://lkml.kernel.org/r/20121116215356.8521.92472.stgit@ahduyck-cp1.jf.intel.com Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/page.h | 3 ++- arch/x86/include/asm/page_32.h | 1 + arch/x86/include/asm/page_64.h | 3 +++ arch/x86/mm/physaddr.c | 11 +++++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/page.h b/arch/x86/include/asm/page.h index 8ca82839288a..3698a6a0a940 100644 --- a/arch/x86/include/asm/page.h +++ b/arch/x86/include/asm/page.h @@ -44,7 +44,8 @@ static inline void copy_user_page(void *to, void *from, unsigned long vaddr, * case properly. Once all supported versions of gcc understand it, we can * remove this Voodoo magic stuff. (i.e. once gcc3.x is deprecated) */ -#define __pa_symbol(x) __pa(__phys_reloc_hide((unsigned long)(x))) +#define __pa_symbol(x) \ + __phys_addr_symbol(__phys_reloc_hide((unsigned long)(x))) #define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) diff --git a/arch/x86/include/asm/page_32.h b/arch/x86/include/asm/page_32.h index da4e762406f7..4d550d04b609 100644 --- a/arch/x86/include/asm/page_32.h +++ b/arch/x86/include/asm/page_32.h @@ -15,6 +15,7 @@ extern unsigned long __phys_addr(unsigned long); #else #define __phys_addr(x) __phys_addr_nodebug(x) #endif +#define __phys_addr_symbol(x) __phys_addr(x) #define __phys_reloc_hide(x) RELOC_HIDE((x), 0) #ifdef CONFIG_FLATMEM diff --git a/arch/x86/include/asm/page_64.h b/arch/x86/include/asm/page_64.h index 5138174c2101..0f1ddee6a0ce 100644 --- a/arch/x86/include/asm/page_64.h +++ b/arch/x86/include/asm/page_64.h @@ -21,8 +21,11 @@ static inline unsigned long __phys_addr_nodebug(unsigned long x) #ifdef CONFIG_DEBUG_VIRTUAL extern unsigned long __phys_addr(unsigned long); +extern unsigned long __phys_addr_symbol(unsigned long); #else #define __phys_addr(x) __phys_addr_nodebug(x) +#define __phys_addr_symbol(x) \ + ((unsigned long)(x) - __START_KERNEL_map + phys_base) #endif #define __phys_reloc_hide(x) (x) diff --git a/arch/x86/mm/physaddr.c b/arch/x86/mm/physaddr.c index fd40d75109ef..c73feddd518d 100644 --- a/arch/x86/mm/physaddr.c +++ b/arch/x86/mm/physaddr.c @@ -28,6 +28,17 @@ unsigned long __phys_addr(unsigned long x) return x; } EXPORT_SYMBOL(__phys_addr); + +unsigned long __phys_addr_symbol(unsigned long x) +{ + unsigned long y = x - __START_KERNEL_map; + + /* only check upper bounds since lower bounds will trigger carry */ + VIRTUAL_BUG_ON(y >= KERNEL_IMAGE_SIZE); + + return y + phys_base; +} +EXPORT_SYMBOL(__phys_addr_symbol); #endif bool __virt_addr_valid(unsigned long x) From 05a476b6e3795f205806662bf09ab95774266292 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 16 Nov 2012 13:56:35 -0800 Subject: [PATCH 0004/4607] x86: Drop 4 unnecessary calls to __pa_symbol While debugging the __pa_symbol inline patch I found that there were a couple spots where __pa_symbol was used as follows: __pa_symbol(x) - __pa_symbol(y) The compiler had reduced them to: x - y Since we also support a debug case where __pa_symbol is a function call it would probably be useful to just change the two cases I found so that they are always just treated as "x - y". As such I am casting the values to phys_addr_t and then doing simple subtraction so that the correct type and value is returned. Signed-off-by: Alexander Duyck Link: http://lkml.kernel.org/r/20121116215552.8521.68085.stgit@ahduyck-cp1.jf.intel.com Signed-off-by: H. Peter Anvin --- arch/x86/kernel/head32.c | 4 ++-- arch/x86/kernel/head64.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c index c18f59d10101..f15db0c40713 100644 --- a/arch/x86/kernel/head32.c +++ b/arch/x86/kernel/head32.c @@ -30,8 +30,8 @@ static void __init i386_default_early_setup(void) void __init i386_start_kernel(void) { - memblock_reserve(__pa_symbol(&_text), - __pa_symbol(&__bss_stop) - __pa_symbol(&_text)); + memblock_reserve(__pa_symbol(_text), + (phys_addr_t)__bss_stop - (phys_addr_t)_text); #ifdef CONFIG_BLK_DEV_INITRD /* Reserve INITRD */ diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 037df57a99ac..42f5df134341 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -97,8 +97,8 @@ void __init x86_64_start_reservations(char *real_mode_data) { copy_bootdata(__va(real_mode_data)); - memblock_reserve(__pa_symbol(&_text), - __pa_symbol(&__bss_stop) - __pa_symbol(&_text)); + memblock_reserve(__pa_symbol(_text), + (phys_addr_t)__bss_stop - (phys_addr_t)_text); #ifdef CONFIG_BLK_DEV_INITRD /* Reserve INITRD */ From fc8d782677f163dee76427fdd8a92bebd2b50b23 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 16 Nov 2012 13:57:13 -0800 Subject: [PATCH 0005/4607] x86: Use __pa_symbol instead of __pa on C visible symbols When I made an attempt at separating __pa_symbol and __pa I found that there were a number of cases where __pa was used on an obvious symbol. I also caught one non-obvious case as _brk_start and _brk_end are based on the address of __brk_base which is a C visible symbol. In mark_rodata_ro I was able to reduce the overhead of kernel symbol to virtual memory translation by using a combination of __va(__pa_symbol()) instead of page_address(virt_to_page()). Signed-off-by: Alexander Duyck Link: http://lkml.kernel.org/r/20121116215640.8521.80483.stgit@ahduyck-cp1.jf.intel.com Signed-off-by: H. Peter Anvin --- arch/x86/kernel/cpu/intel.c | 2 +- arch/x86/kernel/setup.c | 16 ++++++++-------- arch/x86/mm/init_64.c | 18 ++++++++---------- arch/x86/mm/pageattr.c | 8 ++++---- arch/x86/platform/efi/efi.c | 4 ++-- arch/x86/realmode/init.c | 8 ++++---- 6 files changed, 27 insertions(+), 29 deletions(-) diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index 198e019a531a..2249e7e44521 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -168,7 +168,7 @@ int __cpuinit ppro_with_ram_bug(void) #ifdef CONFIG_X86_F00F_BUG static void __cpuinit trap_init_f00f_bug(void) { - __set_fixmap(FIX_F00F_IDT, __pa(&idt_table), PAGE_KERNEL_RO); + __set_fixmap(FIX_F00F_IDT, __pa_symbol(idt_table), PAGE_KERNEL_RO); /* * Update the IDT descriptor and reload the IDT so that diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index ca45696f30fb..2702c5d4acd2 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -300,8 +300,8 @@ static void __init cleanup_highmap(void) static void __init reserve_brk(void) { if (_brk_end > _brk_start) - memblock_reserve(__pa(_brk_start), - __pa(_brk_end) - __pa(_brk_start)); + memblock_reserve(__pa_symbol(_brk_start), + _brk_end - _brk_start); /* Mark brk area as locked down and no longer taking any new allocations */ @@ -761,12 +761,12 @@ void __init setup_arch(char **cmdline_p) init_mm.end_data = (unsigned long) _edata; init_mm.brk = _brk_end; - code_resource.start = virt_to_phys(_text); - code_resource.end = virt_to_phys(_etext)-1; - data_resource.start = virt_to_phys(_etext); - data_resource.end = virt_to_phys(_edata)-1; - bss_resource.start = virt_to_phys(&__bss_start); - bss_resource.end = virt_to_phys(&__bss_stop)-1; + code_resource.start = __pa_symbol(_text); + code_resource.end = __pa_symbol(_etext)-1; + data_resource.start = __pa_symbol(_etext); + data_resource.end = __pa_symbol(_edata)-1; + bss_resource.start = __pa_symbol(__bss_start); + bss_resource.end = __pa_symbol(__bss_stop)-1; #ifdef CONFIG_CMDLINE_BOOL #ifdef CONFIG_CMDLINE_OVERRIDE diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 3baff255adac..0374a10f4fb7 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -770,12 +770,10 @@ void set_kernel_text_ro(void) void mark_rodata_ro(void) { unsigned long start = PFN_ALIGN(_text); - unsigned long rodata_start = - ((unsigned long)__start_rodata + PAGE_SIZE - 1) & PAGE_MASK; + unsigned long rodata_start = PFN_ALIGN(__start_rodata); unsigned long end = (unsigned long) &__end_rodata_hpage_align; - unsigned long text_end = PAGE_ALIGN((unsigned long) &__stop___ex_table); - unsigned long rodata_end = PAGE_ALIGN((unsigned long) &__end_rodata); - unsigned long data_start = (unsigned long) &_sdata; + unsigned long text_end = PFN_ALIGN(&__stop___ex_table); + unsigned long rodata_end = PFN_ALIGN(&__end_rodata); printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n", (end - start) >> 10); @@ -800,12 +798,12 @@ void mark_rodata_ro(void) #endif free_init_pages("unused kernel memory", - (unsigned long) page_address(virt_to_page(text_end)), - (unsigned long) - page_address(virt_to_page(rodata_start))); + (unsigned long) __va(__pa_symbol(text_end)), + (unsigned long) __va(__pa_symbol(rodata_start))); + free_init_pages("unused kernel memory", - (unsigned long) page_address(virt_to_page(rodata_end)), - (unsigned long) page_address(virt_to_page(data_start))); + (unsigned long) __va(__pa_symbol(rodata_end)), + (unsigned long) __va(__pa_symbol(_sdata))); } #endif diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index a718e0d23503..40f92f3aea54 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -94,12 +94,12 @@ static inline void split_page_count(int level) { } static inline unsigned long highmap_start_pfn(void) { - return __pa(_text) >> PAGE_SHIFT; + return __pa_symbol(_text) >> PAGE_SHIFT; } static inline unsigned long highmap_end_pfn(void) { - return __pa(roundup(_brk_end, PMD_SIZE)) >> PAGE_SHIFT; + return __pa_symbol(roundup(_brk_end, PMD_SIZE)) >> PAGE_SHIFT; } #endif @@ -276,8 +276,8 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address, * The .rodata section needs to be read-only. Using the pfn * catches all aliases. */ - if (within(pfn, __pa((unsigned long)__start_rodata) >> PAGE_SHIFT, - __pa((unsigned long)__end_rodata) >> PAGE_SHIFT)) + if (within(pfn, __pa_symbol(__start_rodata) >> PAGE_SHIFT, + __pa_symbol(__end_rodata) >> PAGE_SHIFT)) pgprot_val(forbidden) |= _PAGE_RW; #if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA) diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index ad4439145f85..1b600266265e 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -410,8 +410,8 @@ void __init efi_reserve_boot_services(void) * - Not within any part of the kernel * - Not the bios reserved area */ - if ((start+size >= virt_to_phys(_text) - && start <= virt_to_phys(_end)) || + if ((start+size >= __pa_symbol(_text) + && start <= __pa_symbol(_end)) || !e820_all_mapped(start, start+size, E820_RAM) || memblock_is_region_reserved(start, size)) { /* Could not reserve, skip it */ diff --git a/arch/x86/realmode/init.c b/arch/x86/realmode/init.c index cbca565af5bd..80450261215c 100644 --- a/arch/x86/realmode/init.c +++ b/arch/x86/realmode/init.c @@ -62,9 +62,9 @@ void __init setup_real_mode(void) __va(real_mode_header->trampoline_header); #ifdef CONFIG_X86_32 - trampoline_header->start = __pa(startup_32_smp); + trampoline_header->start = __pa_symbol(startup_32_smp); trampoline_header->gdt_limit = __BOOT_DS + 7; - trampoline_header->gdt_base = __pa(boot_gdt); + trampoline_header->gdt_base = __pa_symbol(boot_gdt); #else /* * Some AMD processors will #GP(0) if EFER.LMA is set in WRMSR @@ -78,8 +78,8 @@ void __init setup_real_mode(void) *trampoline_cr4_features = read_cr4(); trampoline_pgd = (u64 *) __va(real_mode_header->trampoline_pgd); - trampoline_pgd[0] = __pa(level3_ident_pgt) + _KERNPG_TABLE; - trampoline_pgd[511] = __pa(level3_kernel_pgt) + _KERNPG_TABLE; + trampoline_pgd[0] = __pa_symbol(level3_ident_pgt) + _KERNPG_TABLE; + trampoline_pgd[511] = __pa_symbol(level3_kernel_pgt) + _KERNPG_TABLE; #endif } From 217f155e9fc68bf2a6c58a7b47e0d1ce68d78818 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 16 Nov 2012 13:57:32 -0800 Subject: [PATCH 0006/4607] x86/ftrace: Use __pa_symbol instead of __pa on C visible symbols Instead of using __pa which is meant to be a general function for converting virtual addresses to physical addresses we can use __pa_symbol which is the preferred way of decoding kernel text virtual addresses to physical addresses. In this case we are not directly converting C visible symbols however if we know that the instruction pointer is somewhere between _text and _etext we know that we are going to be translating an address form the kernel text space. Cc: Steven Rostedt Cc: Frederic Weisbecker Signed-off-by: Alexander Duyck Link: http://lkml.kernel.org/r/20121116215718.8521.24026.stgit@ahduyck-cp1.jf.intel.com Signed-off-by: H. Peter Anvin --- arch/x86/kernel/ftrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 1d414029f1d8..42a392a9fd02 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -89,7 +89,7 @@ do_ftrace_mod_code(unsigned long ip, const void *new_code) * kernel identity mapping to modify code. */ if (within(ip, (unsigned long)_text, (unsigned long)_etext)) - ip = (unsigned long)__va(__pa(ip)); + ip = (unsigned long)__va(__pa_symbol(ip)); return probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE); } @@ -279,7 +279,7 @@ static int ftrace_write(unsigned long ip, const char *val, int size) * kernel identity mapping to modify code. */ if (within(ip, (unsigned long)_text, (unsigned long)_etext)) - ip = (unsigned long)__va(__pa(ip)); + ip = (unsigned long)__va(__pa_symbol(ip)); return probe_kernel_write((void *)ip, val, size); } From afd51a0e32cd79261f0e823400886ed322a355ac Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 16 Nov 2012 13:57:43 -0800 Subject: [PATCH 0007/4607] x86/acpi: Use __pa_symbol instead of __pa on C visible symbols This change just updates one spot where __pa was being used when __pa_symbol should have been used. By using __pa_symbol we are able to drop a few extra lines of code as we don't have to test to see if the virtual pointer is a part of the kernel text or just standard virtual memory. Cc: Len Brown Cc: Pavel Machek Acked-by: "Rafael J. Wysocki" Signed-off-by: Alexander Duyck Link: http://lkml.kernel.org/r/20121116215737.8521.51167.stgit@ahduyck-cp1.jf.intel.com Signed-off-by: H. Peter Anvin --- arch/x86/kernel/acpi/sleep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index 11676cf65aee..f146a3c10814 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c @@ -69,7 +69,7 @@ int acpi_suspend_lowlevel(void) #ifndef CONFIG_64BIT header->pmode_entry = (u32)&wakeup_pmode_return; - header->pmode_cr3 = (u32)__pa(&initial_page_table); + header->pmode_cr3 = (u32)__pa_symbol(initial_page_table); saved_magic = 0x12345678; #else /* CONFIG_64BIT */ #ifdef CONFIG_SMP From 6a3956bd242926f8956992f6ed7805b0811be003 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Fri, 16 Nov 2012 13:58:12 -0800 Subject: [PATCH 0008/4607] x86/lguest: Use __pa_symbol instead of __pa on C visible symbols The function lguest_write_cr3 is using __pa to convert swapper_pg_dir and initial_page_table from virtual addresses to physical. The correct function to use for these values is __pa_symbol since they are C visible symbols. Cc: Rusty Russell Signed-off-by: Alexander Duyck Link: http://lkml.kernel.org/r/20121116215748.8521.83556.stgit@ahduyck-cp1.jf.intel.com Signed-off-by: H. Peter Anvin --- arch/x86/lguest/boot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 642d8805bc1b..139dd353c2f2 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -552,7 +552,8 @@ static void lguest_write_cr3(unsigned long cr3) current_cr3 = cr3; /* These two page tables are simple, linear, and used during boot */ - if (cr3 != __pa(swapper_pg_dir) && cr3 != __pa(initial_page_table)) + if (cr3 != __pa_symbol(swapper_pg_dir) && + cr3 != __pa_symbol(initial_page_table)) cr3_changed = true; } From fa62aafea9e415cd1efd8c4054106112fe809f19 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:38 -0800 Subject: [PATCH 0009/4607] x86, mm: Add global page_size_mask and probe one time only Now we pass around use_gbpages and use_pse for calculating page table size, Later we will need to call init_memory_mapping for every ram range one by one, that mean those calculation will be done several times. Those information are the same for all ram range and could be stored in page_size_mask and could be probed it one time only. Move that probing code out of init_memory_mapping into separated function probe_page_size_mask(), and call it before all init_memory_mapping. Suggested-by: Ingo Molnar Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-2-git-send-email-yinghai@kernel.org Reviewed-by: Pekka Enberg Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/pgtable.h | 1 + arch/x86/kernel/setup.c | 1 + arch/x86/mm/init.c | 55 ++++++++++++++++------------------ 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index a1f780d45f76..98ac76dc4eae 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -602,6 +602,7 @@ static inline int pgd_none(pgd_t pgd) #ifndef __ASSEMBLY__ extern int direct_gbpages; +void probe_page_size_mask(void); /* local pte updates need not use xchg for locking */ static inline pte_t native_local_ptep_get_and_clear(pte_t *ptep) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index ca45696f30fb..01fb5f9baf90 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -913,6 +913,7 @@ void __init setup_arch(char **cmdline_p) setup_real_mode(); init_gbpages(); + probe_page_size_mask(); /* max_pfn_mapped is updated here */ max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn< Date: Fri, 16 Nov 2012 19:38:39 -0800 Subject: [PATCH 0010/4607] x86, mm: Split out split_mem_range from init_memory_mapping So make init_memory_mapping smaller and readable. -v2: use 0 instead of nr_range as input parameter found by Yasuaki Ishimatsu. Suggested-by: Ingo Molnar Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-3-git-send-email-yinghai@kernel.org Reviewed-by: Pekka Enberg Cc: Yasuaki Ishimatsu Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index aa5b0da03f6d..6368b86b84e2 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -146,25 +146,13 @@ static int __meminit save_mr(struct map_range *mr, int nr_range, return nr_range; } -/* - * Setup the direct mapping of the physical memory at PAGE_OFFSET. - * This runs before bootmem is initialized and gets pages directly from - * the physical memory. To access them they are temporarily mapped. - */ -unsigned long __init_refok init_memory_mapping(unsigned long start, - unsigned long end) +static int __meminit split_mem_range(struct map_range *mr, int nr_range, + unsigned long start, + unsigned long end) { unsigned long start_pfn, end_pfn; - unsigned long ret = 0; unsigned long pos; - struct map_range mr[NR_RANGE_MR]; - int nr_range, i; - - printk(KERN_INFO "init_memory_mapping: [mem %#010lx-%#010lx]\n", - start, end - 1); - - memset(mr, 0, sizeof(mr)); - nr_range = 0; + int i; /* head if not big page alignment ? */ start_pfn = start >> PAGE_SHIFT; @@ -258,6 +246,27 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, (mr[i].page_size_mask & (1< Date: Fri, 16 Nov 2012 19:38:40 -0800 Subject: [PATCH 0011/4607] x86, mm: Move down find_early_table_space() It will need to call split_mem_range(). Move it down after that to avoid extra declaration. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-4-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 117 +++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 6368b86b84e2..701abbc24735 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -36,64 +36,6 @@ struct map_range { }; static int page_size_mask; -/* - * First calculate space needed for kernel direct mapping page tables to cover - * mr[0].start to mr[nr_range - 1].end, while accounting for possible 2M and 1GB - * pages. Then find enough contiguous space for those page tables. - */ -static void __init find_early_table_space(struct map_range *mr, int nr_range) -{ - int i; - unsigned long puds = 0, pmds = 0, ptes = 0, tables; - unsigned long start = 0, good_end; - phys_addr_t base; - - for (i = 0; i < nr_range; i++) { - unsigned long range, extra; - - range = mr[i].end - mr[i].start; - puds += (range + PUD_SIZE - 1) >> PUD_SHIFT; - - if (mr[i].page_size_mask & (1 << PG_LEVEL_1G)) { - extra = range - ((range >> PUD_SHIFT) << PUD_SHIFT); - pmds += (extra + PMD_SIZE - 1) >> PMD_SHIFT; - } else { - pmds += (range + PMD_SIZE - 1) >> PMD_SHIFT; - } - - if (mr[i].page_size_mask & (1 << PG_LEVEL_2M)) { - extra = range - ((range >> PMD_SHIFT) << PMD_SHIFT); -#ifdef CONFIG_X86_32 - extra += PMD_SIZE; -#endif - ptes += (extra + PAGE_SIZE - 1) >> PAGE_SHIFT; - } else { - ptes += (range + PAGE_SIZE - 1) >> PAGE_SHIFT; - } - } - - tables = roundup(puds * sizeof(pud_t), PAGE_SIZE); - tables += roundup(pmds * sizeof(pmd_t), PAGE_SIZE); - tables += roundup(ptes * sizeof(pte_t), PAGE_SIZE); - -#ifdef CONFIG_X86_32 - /* for fixmap */ - tables += roundup(__end_of_fixed_addresses * sizeof(pte_t), PAGE_SIZE); -#endif - good_end = max_pfn_mapped << PAGE_SHIFT; - - base = memblock_find_in_range(start, good_end, tables, PAGE_SIZE); - if (!base) - panic("Cannot find space for the kernel page tables"); - - pgt_buf_start = base >> PAGE_SHIFT; - pgt_buf_end = pgt_buf_start; - pgt_buf_top = pgt_buf_start + (tables >> PAGE_SHIFT); - - printk(KERN_DEBUG "kernel direct mapping tables up to %#lx @ [mem %#010lx-%#010lx]\n", - mr[nr_range - 1].end - 1, pgt_buf_start << PAGE_SHIFT, - (pgt_buf_top << PAGE_SHIFT) - 1); -} void probe_page_size_mask(void) { @@ -249,6 +191,65 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, return nr_range; } +/* + * First calculate space needed for kernel direct mapping page tables to cover + * mr[0].start to mr[nr_range - 1].end, while accounting for possible 2M and 1GB + * pages. Then find enough contiguous space for those page tables. + */ +static void __init find_early_table_space(struct map_range *mr, int nr_range) +{ + int i; + unsigned long puds = 0, pmds = 0, ptes = 0, tables; + unsigned long start = 0, good_end; + phys_addr_t base; + + for (i = 0; i < nr_range; i++) { + unsigned long range, extra; + + range = mr[i].end - mr[i].start; + puds += (range + PUD_SIZE - 1) >> PUD_SHIFT; + + if (mr[i].page_size_mask & (1 << PG_LEVEL_1G)) { + extra = range - ((range >> PUD_SHIFT) << PUD_SHIFT); + pmds += (extra + PMD_SIZE - 1) >> PMD_SHIFT; + } else { + pmds += (range + PMD_SIZE - 1) >> PMD_SHIFT; + } + + if (mr[i].page_size_mask & (1 << PG_LEVEL_2M)) { + extra = range - ((range >> PMD_SHIFT) << PMD_SHIFT); +#ifdef CONFIG_X86_32 + extra += PMD_SIZE; +#endif + ptes += (extra + PAGE_SIZE - 1) >> PAGE_SHIFT; + } else { + ptes += (range + PAGE_SIZE - 1) >> PAGE_SHIFT; + } + } + + tables = roundup(puds * sizeof(pud_t), PAGE_SIZE); + tables += roundup(pmds * sizeof(pmd_t), PAGE_SIZE); + tables += roundup(ptes * sizeof(pte_t), PAGE_SIZE); + +#ifdef CONFIG_X86_32 + /* for fixmap */ + tables += roundup(__end_of_fixed_addresses * sizeof(pte_t), PAGE_SIZE); +#endif + good_end = max_pfn_mapped << PAGE_SHIFT; + + base = memblock_find_in_range(start, good_end, tables, PAGE_SIZE); + if (!base) + panic("Cannot find space for the kernel page tables"); + + pgt_buf_start = base >> PAGE_SHIFT; + pgt_buf_end = pgt_buf_start; + pgt_buf_top = pgt_buf_start + (tables >> PAGE_SHIFT); + + printk(KERN_DEBUG "kernel direct mapping tables up to %#lx @ [mem %#010lx-%#010lx]\n", + mr[nr_range - 1].end - 1, pgt_buf_start << PAGE_SHIFT, + (pgt_buf_top << PAGE_SHIFT) - 1); +} + /* * Setup the direct mapping of the physical memory at PAGE_OFFSET. * This runs before bootmem is initialized and gets pages directly from From 22ddfcaa0dbae992332381d41b8a1fbc72269a13 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:41 -0800 Subject: [PATCH 0012/4607] x86, mm: Move init_memory_mapping calling out of setup.c Now init_memory_mapping is called two times, later will be called for every ram ranges. Could put all related init_mem calling together and out of setup.c. Actually, it reverts commit 1bbbbe7 x86: Exclude E820_RESERVED regions and memory holes above 4 GB from direct mapping. will address that later with complete solution include handling hole under 4g. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-5-git-send-email-yinghai@kernel.org Reviewed-by: Pekka Enberg Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/init.h | 1 - arch/x86/include/asm/pgtable.h | 2 +- arch/x86/kernel/setup.c | 27 +-------------------------- arch/x86/mm/init.c | 19 ++++++++++++++++++- 4 files changed, 20 insertions(+), 29 deletions(-) diff --git a/arch/x86/include/asm/init.h b/arch/x86/include/asm/init.h index adcc0ae73d09..4f13998be59a 100644 --- a/arch/x86/include/asm/init.h +++ b/arch/x86/include/asm/init.h @@ -12,7 +12,6 @@ kernel_physical_mapping_init(unsigned long start, unsigned long end, unsigned long page_size_mask); - extern unsigned long __initdata pgt_buf_start; extern unsigned long __meminitdata pgt_buf_end; extern unsigned long __meminitdata pgt_buf_top; diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index 98ac76dc4eae..dd1a88832d25 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -602,7 +602,7 @@ static inline int pgd_none(pgd_t pgd) #ifndef __ASSEMBLY__ extern int direct_gbpages; -void probe_page_size_mask(void); +void init_mem_mapping(void); /* local pte updates need not use xchg for locking */ static inline pte_t native_local_ptep_get_and_clear(pte_t *ptep) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 01fb5f9baf90..23b079fb93fc 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -913,34 +913,9 @@ void __init setup_arch(char **cmdline_p) setup_real_mode(); init_gbpages(); - probe_page_size_mask(); - /* max_pfn_mapped is updated here */ - max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn< max_low_pfn) { - int i; - unsigned long start, end; - unsigned long start_pfn, end_pfn; - - for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, - NULL) { - - end = PFN_PHYS(end_pfn); - if (end <= (1UL<<32)) - continue; - - start = PFN_PHYS(start_pfn); - max_pfn_mapped = init_memory_mapping( - max((1UL<<32), start), end); - } - - /* can we preseve max_low_pfn ?*/ - max_low_pfn = max_pfn; - } -#endif memblock.current_limit = get_max_mapped(); dma_contiguous_reserve(0); diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 701abbc24735..9e17f9e18a21 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -37,7 +37,7 @@ struct map_range { static int page_size_mask; -void probe_page_size_mask(void) +static void __init probe_page_size_mask(void) { #if !defined(CONFIG_DEBUG_PAGEALLOC) && !defined(CONFIG_KMEMCHECK) /* @@ -315,6 +315,23 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, return ret >> PAGE_SHIFT; } +void __init init_mem_mapping(void) +{ + probe_page_size_mask(); + + /* max_pfn_mapped is updated here */ + max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn< max_low_pfn) { + max_pfn_mapped = init_memory_mapping(1UL<<32, + max_pfn< Date: Fri, 16 Nov 2012 19:38:42 -0800 Subject: [PATCH 0013/4607] x86, mm: Revert back good_end setting for 64bit After | commit 8548c84da2f47e71bbbe300f55edb768492575f7 | Author: Takashi Iwai | Date: Sun Oct 23 23:19:12 2011 +0200 | | x86: Fix S4 regression | | Commit 4b239f458 ("x86-64, mm: Put early page table high") causes a S4 | regression since 2.6.39, namely the machine reboots occasionally at S4 | resume. It doesn't happen always, overall rate is about 1/20. But, | like other bugs, once when this happens, it continues to happen. | | This patch fixes the problem by essentially reverting the memory | assignment in the older way. Have some page table around 512M again, that will prevent kdump to find 512M under 768M. We need revert that reverting, so we could put page table high again for 64bit. Takashi agreed that S4 regression could be something else. https://lkml.org/lkml/2012/6/15/182 Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-6-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 9e17f9e18a21..dbef4ffe8d31 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -234,8 +234,8 @@ static void __init find_early_table_space(struct map_range *mr, int nr_range) #ifdef CONFIG_X86_32 /* for fixmap */ tables += roundup(__end_of_fixed_addresses * sizeof(pte_t), PAGE_SIZE); -#endif good_end = max_pfn_mapped << PAGE_SHIFT; +#endif base = memblock_find_in_range(start, good_end, tables, PAGE_SIZE); if (!base) From 84f1ae30bb68d8da98bca7ff2c2b825b2ac8c9a5 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:43 -0800 Subject: [PATCH 0014/4607] x86, mm: Change find_early_table_space() paramters call split_mem_range inside the function. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-7-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index dbef4ffe8d31..51f919febf64 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -196,12 +196,18 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, * mr[0].start to mr[nr_range - 1].end, while accounting for possible 2M and 1GB * pages. Then find enough contiguous space for those page tables. */ -static void __init find_early_table_space(struct map_range *mr, int nr_range) +static void __init find_early_table_space(unsigned long start, unsigned long end) { int i; unsigned long puds = 0, pmds = 0, ptes = 0, tables; - unsigned long start = 0, good_end; + unsigned long good_end; phys_addr_t base; + struct map_range mr[NR_RANGE_MR]; + int nr_range; + + memset(mr, 0, sizeof(mr)); + nr_range = 0; + nr_range = split_mem_range(mr, nr_range, start, end); for (i = 0; i < nr_range; i++) { unsigned long range, extra; @@ -276,7 +282,7 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, * nodes are discovered. */ if (!after_bootmem) - find_early_table_space(mr, nr_range); + find_early_table_space(start, end); for (i = 0; i < nr_range; i++) ret = kernel_physical_mapping_init(mr[i].start, mr[i].end, From c14fa0b63b5b4234667c03fdc3314c0881caa514 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:44 -0800 Subject: [PATCH 0015/4607] x86, mm: Find early page table buffer together We should not do that in every calling of init_memory_mapping. At the same time need to move down early_memtest, and could remove after_bootmem checking. -v2: fix one early_memtest with 32bit by passing max_pfn_mapped instead. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-8-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 66 ++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 51f919febf64..1ce0d033fafc 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -274,16 +274,6 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, memset(mr, 0, sizeof(mr)); nr_range = split_mem_range(mr, 0, start, end); - /* - * Find space for the kernel direct mapping tables. - * - * Later we should allocate these tables in the local node of the - * memory mapped. Unfortunately this is done currently before the - * nodes are discovered. - */ - if (!after_bootmem) - find_early_table_space(start, end); - for (i = 0; i < nr_range; i++) ret = kernel_physical_mapping_init(mr[i].start, mr[i].end, mr[i].page_size_mask); @@ -296,6 +286,36 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, __flush_tlb_all(); + return ret >> PAGE_SHIFT; +} + +void __init init_mem_mapping(void) +{ + probe_page_size_mask(); + + /* + * Find space for the kernel direct mapping tables. + * + * Later we should allocate these tables in the local node of the + * memory mapped. Unfortunately this is done currently before the + * nodes are discovered. + */ +#ifdef CONFIG_X86_64 + find_early_table_space(0, max_pfn< max_low_pfn) { + max_pfn_mapped = init_memory_mapping(1UL<<32, + max_pfn< pgt_buf_start) + if (pgt_buf_end > pgt_buf_start) x86_init.mapping.pagetable_reserve(PFN_PHYS(pgt_buf_start), PFN_PHYS(pgt_buf_end)); - if (!after_bootmem) - early_memtest(start, end); + /* stop the wrong using */ + pgt_buf_top = 0; - return ret >> PAGE_SHIFT; -} - -void __init init_mem_mapping(void) -{ - probe_page_size_mask(); - - /* max_pfn_mapped is updated here */ - max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn< max_low_pfn) { - max_pfn_mapped = init_memory_mapping(1UL<<32, - max_pfn< Date: Fri, 16 Nov 2012 19:38:45 -0800 Subject: [PATCH 0016/4607] x86, mm: Separate out calculate_table_space_size() It should take physical address range that will need to be mapped. find_early_table_space should take range that pgt buff should be in. Separating page table size calculating and finding early page table to reduce confusing. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-9-git-send-email-yinghai@kernel.org Reviewed-by: Pekka Enberg Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 1ce0d033fafc..7b961d0b1389 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -196,12 +196,10 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, * mr[0].start to mr[nr_range - 1].end, while accounting for possible 2M and 1GB * pages. Then find enough contiguous space for those page tables. */ -static void __init find_early_table_space(unsigned long start, unsigned long end) +static unsigned long __init calculate_table_space_size(unsigned long start, unsigned long end) { int i; unsigned long puds = 0, pmds = 0, ptes = 0, tables; - unsigned long good_end; - phys_addr_t base; struct map_range mr[NR_RANGE_MR]; int nr_range; @@ -240,9 +238,17 @@ static void __init find_early_table_space(unsigned long start, unsigned long end #ifdef CONFIG_X86_32 /* for fixmap */ tables += roundup(__end_of_fixed_addresses * sizeof(pte_t), PAGE_SIZE); - good_end = max_pfn_mapped << PAGE_SHIFT; #endif + return tables; +} + +static void __init find_early_table_space(unsigned long start, + unsigned long good_end, + unsigned long tables) +{ + phys_addr_t base; + base = memblock_find_in_range(start, good_end, tables, PAGE_SIZE); if (!base) panic("Cannot find space for the kernel page tables"); @@ -250,10 +256,6 @@ static void __init find_early_table_space(unsigned long start, unsigned long end pgt_buf_start = base >> PAGE_SHIFT; pgt_buf_end = pgt_buf_start; pgt_buf_top = pgt_buf_start + (tables >> PAGE_SHIFT); - - printk(KERN_DEBUG "kernel direct mapping tables up to %#lx @ [mem %#010lx-%#010lx]\n", - mr[nr_range - 1].end - 1, pgt_buf_start << PAGE_SHIFT, - (pgt_buf_top << PAGE_SHIFT) - 1); } /* @@ -291,6 +293,8 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, void __init init_mem_mapping(void) { + unsigned long tables, good_end, end; + probe_page_size_mask(); /* @@ -301,10 +305,18 @@ void __init init_mem_mapping(void) * nodes are discovered. */ #ifdef CONFIG_X86_64 - find_early_table_space(0, max_pfn< pgt_buf_start) + if (pgt_buf_end > pgt_buf_start) { + printk(KERN_DEBUG "kernel direct mapping tables up to %#lx @ [mem %#010lx-%#010lx] final\n", + end - 1, pgt_buf_start << PAGE_SHIFT, + (pgt_buf_end << PAGE_SHIFT) - 1); x86_init.mapping.pagetable_reserve(PFN_PHYS(pgt_buf_start), PFN_PHYS(pgt_buf_end)); + } /* stop the wrong using */ pgt_buf_top = 0; From dd7dfad7fb297b1746bcdbebbdc970d723a635bd Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:46 -0800 Subject: [PATCH 0017/4607] x86, mm: Set memblock initial limit to 1M memblock_x86_fill() could double memory array. If we set memblock.current_limit to 512M, so memory array could be around 512M. So kdump will not get big range (like 512M) under 1024M. Try to put it down under 1M, it would use about 4k or so, and that is limited. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-10-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/kernel/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 23b079fb93fc..4bd89218cbc3 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -890,7 +890,7 @@ void __init setup_arch(char **cmdline_p) cleanup_highmap(); - memblock.current_limit = get_max_mapped(); + memblock.current_limit = ISA_END_ADDRESS; memblock_x86_fill(); /* From 4eea6aa581abfeb2695ebe9f9d4672597e1bdd4b Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Fri, 16 Nov 2012 19:38:47 -0800 Subject: [PATCH 0018/4607] x86, mm: if kernel .text .data .bss are not marked as E820_RAM, complain and fix There could be cases where user supplied memmap=exactmap memory mappings do not mark the region where the kernel .text .data and .bss reside as E820_RAM, as reported here: https://lkml.org/lkml/2012/8/14/86 Handle it by complaining, and adding the range back into the e820. Signed-off-by: Jacob Shin Link: http://lkml.kernel.org/r/1353123563-3103-11-git-send-email-yinghai@kernel.org Signed-off-by: Yinghai Lu Reviewed-by: Pekka Enberg Signed-off-by: H. Peter Anvin --- arch/x86/kernel/setup.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 4bd89218cbc3..d85cbd96525d 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -832,6 +832,20 @@ void __init setup_arch(char **cmdline_p) insert_resource(&iomem_resource, &data_resource); insert_resource(&iomem_resource, &bss_resource); + /* + * Complain if .text .data and .bss are not marked as E820_RAM and + * attempt to fix it by adding the range. We may have a confused BIOS, + * or the user may have incorrectly supplied it via memmap=exactmap. If + * we really are running on top non-RAM, we will crash later anyways. + */ + if (!e820_all_mapped(code_resource.start, __pa(__brk_limit), E820_RAM)) { + pr_warn(".text .data .bss are not marked as E820_RAM!\n"); + + e820_add_region(code_resource.start, + __pa(__brk_limit) - code_resource.start + 1, + E820_RAM); + } + trim_bios_range(); #ifdef CONFIG_X86_32 if (ppro_with_ram_bug()) { From dda56e134059b840631fdfd034784056b627c2a6 Mon Sep 17 00:00:00 2001 From: Jacob Shin Date: Fri, 16 Nov 2012 19:38:48 -0800 Subject: [PATCH 0019/4607] x86, mm: Fixup code testing if a pfn is direct mapped Update code that previously assumed pfns [ 0 - max_low_pfn_mapped ) and [ 4GB - max_pfn_mapped ) were always direct mapped, to now look up pfn_mapped ranges instead. -v2: change applying sequence to keep git bisecting working. so add dummy pfn_range_is_mapped(). - Yinghai Lu Signed-off-by: Jacob Shin Link: http://lkml.kernel.org/r/1353123563-3103-12-git-send-email-yinghai@kernel.org Signed-off-by: Yinghai Lu Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/page_types.h | 8 ++++++++ arch/x86/kernel/cpu/amd.c | 8 +++----- arch/x86/platform/efi/efi.c | 7 +++---- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/arch/x86/include/asm/page_types.h b/arch/x86/include/asm/page_types.h index e21fdd10479f..45aae6e5f649 100644 --- a/arch/x86/include/asm/page_types.h +++ b/arch/x86/include/asm/page_types.h @@ -51,6 +51,14 @@ static inline phys_addr_t get_max_mapped(void) return (phys_addr_t)max_pfn_mapped << PAGE_SHIFT; } +static inline bool pfn_range_is_mapped(unsigned long start_pfn, + unsigned long end_pfn) +{ + return end_pfn <= max_low_pfn_mapped || + (end_pfn > (1UL << (32 - PAGE_SHIFT)) && + end_pfn <= max_pfn_mapped); +} + extern unsigned long init_memory_mapping(unsigned long start, unsigned long end); diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index f7e98a2c0d12..9619ba6528ca 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -676,12 +676,10 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) * benefit in doing so. */ if (!rdmsrl_safe(MSR_K8_TSEG_ADDR, &tseg)) { + unsigned long pfn = tseg >> PAGE_SHIFT; + printk(KERN_DEBUG "tseg: %010llx\n", tseg); - if ((tseg>>PMD_SHIFT) < - (max_low_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) || - ((tseg>>PMD_SHIFT) < - (max_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) && - (tseg>>PMD_SHIFT) >= (1ULL<<(32 - PMD_SHIFT)))) + if (pfn_range_is_mapped(pfn, pfn + 1)) set_memory_4k((unsigned long)__va(tseg), 1); } } diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index ad4439145f85..36e53f0a9ce3 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -835,7 +835,7 @@ void __init efi_enter_virtual_mode(void) efi_memory_desc_t *md, *prev_md = NULL; efi_status_t status; unsigned long size; - u64 end, systab, end_pfn; + u64 end, systab, start_pfn, end_pfn; void *p, *va, *new_memmap = NULL; int count = 0; @@ -888,10 +888,9 @@ void __init efi_enter_virtual_mode(void) size = md->num_pages << EFI_PAGE_SHIFT; end = md->phys_addr + size; + start_pfn = PFN_DOWN(md->phys_addr); end_pfn = PFN_UP(end); - if (end_pfn <= max_low_pfn_mapped - || (end_pfn > (1UL << (32 - PAGE_SHIFT)) - && end_pfn <= max_pfn_mapped)) { + if (pfn_range_is_mapped(start_pfn, end_pfn)) { va = __va(md->phys_addr); if (!(md->attribute & EFI_MEMORY_WB)) From 8eb5779f6b9c7e390c92f451edaafc039e06e743 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:49 -0800 Subject: [PATCH 0020/4607] x86, mm: use pfn_range_is_mapped() with CPA We are going to map ram only, so under max_low_pfn_mapped, between 4g and max_pfn_mapped does not mean mapped at all. Use pfn_range_is_mapped() directly. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-13-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/pageattr.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index a718e0d23503..44acfcd6c16f 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -551,16 +551,10 @@ static int split_large_page(pte_t *kpte, unsigned long address) for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc) set_pte(&pbase[i], pfn_pte(pfn, ref_prot)); - if (address >= (unsigned long)__va(0) && - address < (unsigned long)__va(max_low_pfn_mapped << PAGE_SHIFT)) + if (pfn_range_is_mapped(PFN_DOWN(__pa(address)), + PFN_DOWN(__pa(address)) + 1)) split_page_count(level); -#ifdef CONFIG_X86_64 - if (address >= (unsigned long)__va(1UL<<32) && - address < (unsigned long)__va(max_pfn_mapped << PAGE_SHIFT)) - split_page_count(level); -#endif - /* * Install the new, split up pagetable. * @@ -729,13 +723,9 @@ static int cpa_process_alias(struct cpa_data *cpa) unsigned long vaddr; int ret; - if (cpa->pfn >= max_pfn_mapped) + if (!pfn_range_is_mapped(cpa->pfn, cpa->pfn + 1)) return 0; -#ifdef CONFIG_X86_64 - if (cpa->pfn >= max_low_pfn_mapped && cpa->pfn < (1UL<<(32-PAGE_SHIFT))) - return 0; -#endif /* * No need to redo, when the primary call touched the direct * mapping already: From 5101730cb0613b91d40b9bb7be6bb023d2f6aa24 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:50 -0800 Subject: [PATCH 0021/4607] x86, mm: use pfn_range_is_mapped() with gart We are going to map ram only, so under max_low_pfn_mapped, between 4g and max_pfn_mapped does not mean mapped at all. Use pfn_range_is_mapped() directly. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-14-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/kernel/amd_gart_64.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/amd_gart_64.c b/arch/x86/kernel/amd_gart_64.c index e66311200cbd..b574b295a2f9 100644 --- a/arch/x86/kernel/amd_gart_64.c +++ b/arch/x86/kernel/amd_gart_64.c @@ -768,10 +768,9 @@ int __init gart_iommu_init(void) aper_base = info.aper_base; end_pfn = (aper_base>>PAGE_SHIFT) + (aper_size>>PAGE_SHIFT); - if (end_pfn > max_low_pfn_mapped) { - start_pfn = (aper_base>>PAGE_SHIFT); + start_pfn = PFN_DOWN(aper_base); + if (!pfn_range_is_mapped(start_pfn, end_pfn)) init_memory_mapping(start_pfn< Date: Fri, 16 Nov 2012 19:38:51 -0800 Subject: [PATCH 0022/4607] x86, mm: use pfn_range_is_mapped() with reserve_initrd We are going to map ram only, so under max_low_pfn_mapped, between 4g and max_pfn_mapped does not mean mapped at all. Use pfn_range_is_mapped() to find out if range is mapped for initrd. That could happen bootloader put initrd in range but user could use memmap to carve some of range out. Also during copying need to use early_memmap to map original initrd for accessing. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-15-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/kernel/setup.c | 52 ++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index d85cbd96525d..bd52f9da17cc 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -317,20 +317,19 @@ static void __init relocate_initrd(void) u64 ramdisk_image = boot_params.hdr.ramdisk_image; u64 ramdisk_size = boot_params.hdr.ramdisk_size; u64 area_size = PAGE_ALIGN(ramdisk_size); - u64 end_of_lowmem = max_low_pfn_mapped << PAGE_SHIFT; u64 ramdisk_here; unsigned long slop, clen, mapaddr; char *p, *q; - /* We need to move the initrd down into lowmem */ - ramdisk_here = memblock_find_in_range(0, end_of_lowmem, area_size, - PAGE_SIZE); + /* We need to move the initrd down into directly mapped mem */ + ramdisk_here = memblock_find_in_range(0, PFN_PHYS(max_low_pfn_mapped), + area_size, PAGE_SIZE); if (!ramdisk_here) panic("Cannot find place for new RAMDISK of size %lld\n", ramdisk_size); - /* Note: this includes all the lowmem currently occupied by + /* Note: this includes all the mem currently occupied by the initrd, we rely on that fact to keep the data intact. */ memblock_reserve(ramdisk_here, area_size); initrd_start = ramdisk_here + PAGE_OFFSET; @@ -340,17 +339,7 @@ static void __init relocate_initrd(void) q = (char *)initrd_start; - /* Copy any lowmem portion of the initrd */ - if (ramdisk_image < end_of_lowmem) { - clen = end_of_lowmem - ramdisk_image; - p = (char *)__va(ramdisk_image); - memcpy(q, p, clen); - q += clen; - ramdisk_image += clen; - ramdisk_size -= clen; - } - - /* Copy the highmem portion of the initrd */ + /* Copy the initrd */ while (ramdisk_size) { slop = ramdisk_image & ~PAGE_MASK; clen = ramdisk_size; @@ -364,7 +353,7 @@ static void __init relocate_initrd(void) ramdisk_image += clen; ramdisk_size -= clen; } - /* high pages is not converted by early_res_to_bootmem */ + ramdisk_image = boot_params.hdr.ramdisk_image; ramdisk_size = boot_params.hdr.ramdisk_size; printk(KERN_INFO "Move RAMDISK from [mem %#010llx-%#010llx] to" @@ -373,13 +362,27 @@ static void __init relocate_initrd(void) ramdisk_here, ramdisk_here + ramdisk_size - 1); } +static u64 __init get_mem_size(unsigned long limit_pfn) +{ + int i; + u64 mapped_pages = 0; + unsigned long start_pfn, end_pfn; + + for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, NULL) { + start_pfn = min_t(unsigned long, start_pfn, limit_pfn); + end_pfn = min_t(unsigned long, end_pfn, limit_pfn); + mapped_pages += end_pfn - start_pfn; + } + + return mapped_pages << PAGE_SHIFT; +} static void __init reserve_initrd(void) { /* Assume only end is not page aligned */ u64 ramdisk_image = boot_params.hdr.ramdisk_image; u64 ramdisk_size = boot_params.hdr.ramdisk_size; u64 ramdisk_end = PAGE_ALIGN(ramdisk_image + ramdisk_size); - u64 end_of_lowmem = max_low_pfn_mapped << PAGE_SHIFT; + u64 mapped_size; if (!boot_params.hdr.type_of_loader || !ramdisk_image || !ramdisk_size) @@ -387,18 +390,19 @@ static void __init reserve_initrd(void) initrd_start = 0; - if (ramdisk_size >= (end_of_lowmem>>1)) { + mapped_size = get_mem_size(max_low_pfn_mapped); + if (ramdisk_size >= (mapped_size>>1)) panic("initrd too large to handle, " "disabling initrd (%lld needed, %lld available)\n", - ramdisk_size, end_of_lowmem>>1); - } + ramdisk_size, mapped_size>>1); printk(KERN_INFO "RAMDISK: [mem %#010llx-%#010llx]\n", ramdisk_image, ramdisk_end - 1); - - if (ramdisk_end <= end_of_lowmem) { - /* All in lowmem, easy case */ + if (ramdisk_end <= (max_low_pfn_mapped< Date: Fri, 16 Nov 2012 19:38:52 -0800 Subject: [PATCH 0023/4607] x86, mm: Only direct map addresses that are marked as E820_RAM Currently direct mappings are created for [ 0 to max_low_pfn< Link: http://lkml.kernel.org/r/1353123563-3103-16-git-send-email-yinghai@kernel.org Signed-off-by: Yinghai Lu Reviewed-by: Pekka Enberg Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/page_types.h | 8 +- arch/x86/kernel/setup.c | 8 +- arch/x86/mm/init.c | 120 +++++++++++++++++++++++++++--- arch/x86/mm/init_64.c | 6 +- 4 files changed, 117 insertions(+), 25 deletions(-) diff --git a/arch/x86/include/asm/page_types.h b/arch/x86/include/asm/page_types.h index 45aae6e5f649..54c97879195e 100644 --- a/arch/x86/include/asm/page_types.h +++ b/arch/x86/include/asm/page_types.h @@ -51,13 +51,7 @@ static inline phys_addr_t get_max_mapped(void) return (phys_addr_t)max_pfn_mapped << PAGE_SHIFT; } -static inline bool pfn_range_is_mapped(unsigned long start_pfn, - unsigned long end_pfn) -{ - return end_pfn <= max_low_pfn_mapped || - (end_pfn > (1UL << (32 - PAGE_SHIFT)) && - end_pfn <= max_pfn_mapped); -} +bool pfn_range_is_mapped(unsigned long start_pfn, unsigned long end_pfn); extern unsigned long init_memory_mapping(unsigned long start, unsigned long end); diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index bd52f9da17cc..68dffeceb193 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -116,9 +116,11 @@ #include /* - * end_pfn only includes RAM, while max_pfn_mapped includes all e820 entries. - * The direct mapping extends to max_pfn_mapped, so that we can directly access - * apertures, ACPI and other tables without having to play with fixmaps. + * max_low_pfn_mapped: highest direct mapped pfn under 4GB + * max_pfn_mapped: highest direct mapped pfn over 4GB + * + * The direct mapping only covers E820_RAM regions, so the ranges and gaps are + * represented by pfn_mapped */ unsigned long max_low_pfn_mapped; unsigned long max_pfn_mapped; diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 7b961d0b1389..bb44e9f2cc49 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -243,6 +243,38 @@ static unsigned long __init calculate_table_space_size(unsigned long start, unsi return tables; } +static unsigned long __init calculate_all_table_space_size(void) +{ + unsigned long start_pfn, end_pfn; + unsigned long tables; + int i; + + /* the ISA range is always mapped regardless of memory holes */ + tables = calculate_table_space_size(0, ISA_END_ADDRESS); + + for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, NULL) { + u64 start = start_pfn << PAGE_SHIFT; + u64 end = end_pfn << PAGE_SHIFT; + + if (end <= ISA_END_ADDRESS) + continue; + + if (start < ISA_END_ADDRESS) + start = ISA_END_ADDRESS; +#ifdef CONFIG_X86_32 + /* on 32 bit, we only map up to max_low_pfn */ + if ((start >> PAGE_SHIFT) >= max_low_pfn) + continue; + + if ((end >> PAGE_SHIFT) > max_low_pfn) + end = max_low_pfn << PAGE_SHIFT; +#endif + tables += calculate_table_space_size(start, end); + } + + return tables; +} + static void __init find_early_table_space(unsigned long start, unsigned long good_end, unsigned long tables) @@ -258,6 +290,34 @@ static void __init find_early_table_space(unsigned long start, pgt_buf_top = pgt_buf_start + (tables >> PAGE_SHIFT); } +static struct range pfn_mapped[E820_X_MAX]; +static int nr_pfn_mapped; + +static void add_pfn_range_mapped(unsigned long start_pfn, unsigned long end_pfn) +{ + nr_pfn_mapped = add_range_with_merge(pfn_mapped, E820_X_MAX, + nr_pfn_mapped, start_pfn, end_pfn); + nr_pfn_mapped = clean_sort_range(pfn_mapped, E820_X_MAX); + + max_pfn_mapped = max(max_pfn_mapped, end_pfn); + + if (start_pfn < (1UL<<(32-PAGE_SHIFT))) + max_low_pfn_mapped = max(max_low_pfn_mapped, + min(end_pfn, 1UL<<(32-PAGE_SHIFT))); +} + +bool pfn_range_is_mapped(unsigned long start_pfn, unsigned long end_pfn) +{ + int i; + + for (i = 0; i < nr_pfn_mapped; i++) + if ((start_pfn >= pfn_mapped[i].start) && + (end_pfn <= pfn_mapped[i].end)) + return true; + + return false; +} + /* * Setup the direct mapping of the physical memory at PAGE_OFFSET. * This runs before bootmem is initialized and gets pages directly from @@ -288,9 +348,55 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, __flush_tlb_all(); + add_pfn_range_mapped(start >> PAGE_SHIFT, ret >> PAGE_SHIFT); + return ret >> PAGE_SHIFT; } +/* + * Iterate through E820 memory map and create direct mappings for only E820_RAM + * regions. We cannot simply create direct mappings for all pfns from + * [0 to max_low_pfn) and [4GB to max_pfn) because of possible memory holes in + * high addresses that cannot be marked as UC by fixed/variable range MTRRs. + * Depending on the alignment of E820 ranges, this may possibly result in using + * smaller size (i.e. 4K instead of 2M or 1G) page tables. + */ +static void __init init_all_memory_mapping(void) +{ + unsigned long start_pfn, end_pfn; + int i; + + /* the ISA range is always mapped regardless of memory holes */ + init_memory_mapping(0, ISA_END_ADDRESS); + + for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, NULL) { + u64 start = (u64)start_pfn << PAGE_SHIFT; + u64 end = (u64)end_pfn << PAGE_SHIFT; + + if (end <= ISA_END_ADDRESS) + continue; + + if (start < ISA_END_ADDRESS) + start = ISA_END_ADDRESS; +#ifdef CONFIG_X86_32 + /* on 32 bit, we only map up to max_low_pfn */ + if ((start >> PAGE_SHIFT) >= max_low_pfn) + continue; + + if ((end >> PAGE_SHIFT) > max_low_pfn) + end = max_low_pfn << PAGE_SHIFT; +#endif + init_memory_mapping(start, end); + } + +#ifdef CONFIG_X86_64 + if (max_pfn > max_low_pfn) { + /* can we preseve max_low_pfn ?*/ + max_low_pfn = max_pfn; + } +#endif +} + void __init init_mem_mapping(void) { unsigned long tables, good_end, end; @@ -311,23 +417,15 @@ void __init init_mem_mapping(void) end = max_low_pfn << PAGE_SHIFT; good_end = max_pfn_mapped << PAGE_SHIFT; #endif - tables = calculate_table_space_size(0, end); + tables = calculate_all_table_space_size(); find_early_table_space(0, good_end, tables); printk(KERN_DEBUG "kernel direct mapping tables up to %#lx @ [mem %#010lx-%#010lx] prealloc\n", end - 1, pgt_buf_start << PAGE_SHIFT, (pgt_buf_top << PAGE_SHIFT) - 1); - max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn< max_low_pfn) { - max_pfn_mapped = init_memory_mapping(1UL<<32, - max_pfn<node_zones + ZONE_NORMAL; - unsigned long last_mapped_pfn, start_pfn = start >> PAGE_SHIFT; + unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; int ret; - last_mapped_pfn = init_memory_mapping(start, start + size); - if (last_mapped_pfn > max_pfn_mapped) - max_pfn_mapped = last_mapped_pfn; + init_memory_mapping(start, start + size); ret = __add_pages(nid, zone, start_pfn, nr_pages); WARN_ON_ONCE(ret); From 74f27655dda84604d8bab47872020dcce5c88731 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:53 -0800 Subject: [PATCH 0024/4607] x86, mm: relocate initrd under all mem for 64bit instead of under 4g. For 64bit, we can use any mapped mem instead of low mem. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-17-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/kernel/setup.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 68dffeceb193..94f922a73c54 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -324,7 +324,7 @@ static void __init relocate_initrd(void) char *p, *q; /* We need to move the initrd down into directly mapped mem */ - ramdisk_here = memblock_find_in_range(0, PFN_PHYS(max_low_pfn_mapped), + ramdisk_here = memblock_find_in_range(0, PFN_PHYS(max_pfn_mapped), area_size, PAGE_SIZE); if (!ramdisk_here) @@ -392,7 +392,7 @@ static void __init reserve_initrd(void) initrd_start = 0; - mapped_size = get_mem_size(max_low_pfn_mapped); + mapped_size = get_mem_size(max_pfn_mapped); if (ramdisk_size >= (mapped_size>>1)) panic("initrd too large to handle, " "disabling initrd (%lld needed, %lld available)\n", @@ -401,8 +401,7 @@ static void __init reserve_initrd(void) printk(KERN_INFO "RAMDISK: [mem %#010llx-%#010llx]\n", ramdisk_image, ramdisk_end - 1); - if (ramdisk_end <= (max_low_pfn_mapped< Date: Fri, 16 Nov 2012 19:38:54 -0800 Subject: [PATCH 0025/4607] x86, mm: Align start address to correct big page size We are going to use buffer in BRK to map small range just under memory top, and use those new mapped ram to map ram range under it. The ram range that will be mapped at first could be only page aligned, but ranges around it are ram too, we could use bigger page to map it to avoid small page size. We will adjust page_size_mask in following patch: x86, mm: Use big page size for small memory range to use big page size for small ram range. Before that patch, this patch will make sure start address to be aligned down according to bigger page size, otherwise entry in page page will not have correct value. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-18-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init_32.c | 1 + arch/x86/mm/init_64.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 11a58001b4ce..27f7fc69cf8a 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -310,6 +310,7 @@ repeat: __pgprot(PTE_IDENT_ATTR | _PAGE_PSE); + pfn &= PMD_MASK >> PAGE_SHIFT; addr2 = (pfn + PTRS_PER_PTE-1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1; diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 32c7e3847cf6..869372a5d3cf 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -464,7 +464,7 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end, pages++; spin_lock(&init_mm.page_table_lock); set_pte((pte_t *)pmd, - pfn_pte(address >> PAGE_SHIFT, + pfn_pte((address & PMD_MASK) >> PAGE_SHIFT, __pgprot(pgprot_val(prot) | _PAGE_PSE))); spin_unlock(&init_mm.page_table_lock); last_map_addr = next; @@ -541,7 +541,8 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end, pages++; spin_lock(&init_mm.page_table_lock); set_pte((pte_t *)pud, - pfn_pte(addr >> PAGE_SHIFT, PAGE_KERNEL_LARGE)); + pfn_pte((addr & PUD_MASK) >> PAGE_SHIFT, + PAGE_KERNEL_LARGE)); spin_unlock(&init_mm.page_table_lock); last_map_addr = next; continue; From aeebe84cc96cde4181807bc67c300c550d0ef123 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:55 -0800 Subject: [PATCH 0026/4607] x86, mm: Use big page size for small memory range We could map small range in the middle of big range at first, so should use big page size at first to avoid using small page size to break down page table. Only can set big page bit when that range has ram area around it. -v2: fix 32bit boundary checking. We can not count ram above max_low_pfn for 32 bit. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-19-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index bb44e9f2cc49..da591ebc8d12 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -88,6 +88,40 @@ static int __meminit save_mr(struct map_range *mr, int nr_range, return nr_range; } +/* + * adjust the page_size_mask for small range to go with + * big page size instead small one if nearby are ram too. + */ +static void __init_refok adjust_range_page_size_mask(struct map_range *mr, + int nr_range) +{ + int i; + + for (i = 0; i < nr_range; i++) { + if ((page_size_mask & (1<> PAGE_SHIFT) > max_low_pfn) + continue; +#endif + + if (memblock_is_region_memory(start, end - start)) + mr[i].page_size_mask |= 1< Date: Fri, 16 Nov 2012 19:38:56 -0800 Subject: [PATCH 0027/4607] x86, mm: Don't clear page table if range is ram After we add code use buffer in BRK to pre-map buf for page table in following patch: x86, mm: setup page table in top-down it should be safe to remove early_memmap for page table accessing. Instead we get panic with that. It turns out that we clear the initial page table wrongly for next range that is separated by holes. And it only happens when we are trying to map ram range one by one. We need to check if the range is ram before clearing page table. We change the loop structure to remove the extra little loop and use one loop only, and in that loop will caculate next at first, and check if [addr,next) is covered by E820_RAM. -v2: E820_RESERVED_KERN is treated as E820_RAM. EFI one change some E820_RAM to that, so next kernel by kexec will know that range is used already. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-20-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init_64.c | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 869372a5d3cf..fa28e3e29741 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -363,20 +363,20 @@ static unsigned long __meminit phys_pte_init(pte_t *pte_page, unsigned long addr, unsigned long end, pgprot_t prot) { - unsigned pages = 0; + unsigned long pages = 0, next; unsigned long last_map_addr = end; int i; pte_t *pte = pte_page + pte_index(addr); - for(i = pte_index(addr); i < PTRS_PER_PTE; i++, addr += PAGE_SIZE, pte++) { - + for (i = pte_index(addr); i < PTRS_PER_PTE; i++, addr = next, pte++) { + next = (addr & PAGE_MASK) + PAGE_SIZE; if (addr >= end) { - if (!after_bootmem) { - for(; i < PTRS_PER_PTE; i++, pte++) - set_pte(pte, __pte(0)); - } - break; + if (!after_bootmem && + !e820_any_mapped(addr & PAGE_MASK, next, E820_RAM) && + !e820_any_mapped(addr & PAGE_MASK, next, E820_RESERVED_KERN)) + set_pte(pte, __pte(0)); + continue; } /* @@ -419,15 +419,14 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end, pte_t *pte; pgprot_t new_prot = prot; - if (address >= end) { - if (!after_bootmem) { - for (; i < PTRS_PER_PMD; i++, pmd++) - set_pmd(pmd, __pmd(0)); - } - break; - } - next = (address & PMD_MASK) + PMD_SIZE; + if (address >= end) { + if (!after_bootmem && + !e820_any_mapped(address & PMD_MASK, next, E820_RAM) && + !e820_any_mapped(address & PMD_MASK, next, E820_RESERVED_KERN)) + set_pmd(pmd, __pmd(0)); + continue; + } if (pmd_val(*pmd)) { if (!pmd_large(*pmd)) { @@ -497,13 +496,12 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end, pmd_t *pmd; pgprot_t prot = PAGE_KERNEL; - if (addr >= end) - break; - next = (addr & PUD_MASK) + PUD_SIZE; - - if (!after_bootmem && !e820_any_mapped(addr, next, 0)) { - set_pud(pud, __pud(0)); + if (addr >= end) { + if (!after_bootmem && + !e820_any_mapped(addr & PUD_MASK, next, E820_RAM) && + !e820_any_mapped(addr & PUD_MASK, next, E820_RESERVED_KERN)) + set_pud(pud, __pud(0)); continue; } From f763ad1d3870abb811ec7520b4c1adc56471a3a4 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:57 -0800 Subject: [PATCH 0028/4607] x86, mm: Break down init_all_memory_mapping Will replace that with top-down page table initialization. New API need to take range: init_range_memory_mapping() Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-21-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index da591ebc8d12..c688ea3887f2 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -398,40 +398,30 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, * Depending on the alignment of E820 ranges, this may possibly result in using * smaller size (i.e. 4K instead of 2M or 1G) page tables. */ -static void __init init_all_memory_mapping(void) +static void __init init_range_memory_mapping(unsigned long range_start, + unsigned long range_end) { unsigned long start_pfn, end_pfn; int i; - /* the ISA range is always mapped regardless of memory holes */ - init_memory_mapping(0, ISA_END_ADDRESS); - for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, NULL) { u64 start = (u64)start_pfn << PAGE_SHIFT; u64 end = (u64)end_pfn << PAGE_SHIFT; - if (end <= ISA_END_ADDRESS) + if (end <= range_start) continue; - if (start < ISA_END_ADDRESS) - start = ISA_END_ADDRESS; -#ifdef CONFIG_X86_32 - /* on 32 bit, we only map up to max_low_pfn */ - if ((start >> PAGE_SHIFT) >= max_low_pfn) + if (start < range_start) + start = range_start; + + if (start >= range_end) continue; - if ((end >> PAGE_SHIFT) > max_low_pfn) - end = max_low_pfn << PAGE_SHIFT; -#endif + if (end > range_end) + end = range_end; + init_memory_mapping(start, end); } - -#ifdef CONFIG_X86_64 - if (max_pfn > max_low_pfn) { - /* can we preseve max_low_pfn ?*/ - max_low_pfn = max_pfn; - } -#endif } void __init init_mem_mapping(void) @@ -461,8 +451,15 @@ void __init init_mem_mapping(void) (pgt_buf_top << PAGE_SHIFT) - 1); max_pfn_mapped = 0; /* will get exact value next */ - init_all_memory_mapping(); - + /* the ISA range is always mapped regardless of memory holes */ + init_memory_mapping(0, ISA_END_ADDRESS); + init_range_memory_mapping(ISA_END_ADDRESS, end); +#ifdef CONFIG_X86_64 + if (max_pfn > max_low_pfn) { + /* can we preseve max_low_pfn ?*/ + max_low_pfn = max_pfn; + } +#endif /* * Reserve the kernel pagetable pages we used (pgt_buf_start - * pgt_buf_end) and free the other ones (pgt_buf_end - pgt_buf_top) From 8d57470d8f859635deffe3919d7d4867b488b85a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:58 -0800 Subject: [PATCH 0029/4607] x86, mm: setup page table in top-down Get pgt_buf early from BRK, and use it to map PMD_SIZE from top at first. Then use mapped pages to map more ranges below, and keep looping until all pages get mapped. alloc_low_page will use page from BRK at first, after that buffer is used up, will use memblock to find and reserve pages for page table usage. Introduce min_pfn_mapped to make sure find new pages from mapped ranges, that will be updated when lower pages get mapped. Also add step_size to make sure that don't try to map too big range with limited mapped pages initially, and increase the step_size when we have more mapped pages on hand. We don't need to call pagetable_reserve anymore, reserve work is done in alloc_low_page() directly. At last we can get rid of calculation and find early pgt related code. -v2: update to after fix_xen change, also use MACRO for initial pgt_buf size and add comments with it. -v3: skip big reserved range in memblock.reserved near end. -v4: don't need fix_xen change now. -v5: add changelog about moving about reserving pagetable to alloc_low_page. Suggested-by: "H. Peter Anvin" Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-22-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/page_types.h | 1 + arch/x86/include/asm/pgtable.h | 1 + arch/x86/kernel/setup.c | 3 + arch/x86/mm/init.c | 210 +++++++++--------------------- arch/x86/mm/init_32.c | 17 ++- arch/x86/mm/init_64.c | 17 ++- 6 files changed, 94 insertions(+), 155 deletions(-) diff --git a/arch/x86/include/asm/page_types.h b/arch/x86/include/asm/page_types.h index 54c97879195e..9f6f3e66e84d 100644 --- a/arch/x86/include/asm/page_types.h +++ b/arch/x86/include/asm/page_types.h @@ -45,6 +45,7 @@ extern int devmem_is_allowed(unsigned long pagenr); extern unsigned long max_low_pfn_mapped; extern unsigned long max_pfn_mapped; +extern unsigned long min_pfn_mapped; static inline phys_addr_t get_max_mapped(void) { diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index dd1a88832d25..6991a3e1bf81 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -603,6 +603,7 @@ static inline int pgd_none(pgd_t pgd) extern int direct_gbpages; void init_mem_mapping(void); +void early_alloc_pgt_buf(void); /* local pte updates need not use xchg for locking */ static inline pte_t native_local_ptep_get_and_clear(pte_t *ptep) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 94f922a73c54..f7634092931b 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -124,6 +124,7 @@ */ unsigned long max_low_pfn_mapped; unsigned long max_pfn_mapped; +unsigned long min_pfn_mapped; #ifdef CONFIG_DMI RESERVE_BRK(dmi_alloc, 65536); @@ -900,6 +901,8 @@ void __init setup_arch(char **cmdline_p) reserve_ibft_region(); + early_alloc_pgt_buf(); + /* * Need to conclude brk, before memblock_x86_fill() * it could use memblock_find_in_range, could overlap with diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index c688ea3887f2..2393d0099e7f 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -21,6 +21,21 @@ unsigned long __initdata pgt_buf_start; unsigned long __meminitdata pgt_buf_end; unsigned long __meminitdata pgt_buf_top; +/* need 4 4k for initial PMD_SIZE, 4k for 0-ISA_END_ADDRESS */ +#define INIT_PGT_BUF_SIZE (5 * PAGE_SIZE) +RESERVE_BRK(early_pgt_alloc, INIT_PGT_BUF_SIZE); +void __init early_alloc_pgt_buf(void) +{ + unsigned long tables = INIT_PGT_BUF_SIZE; + phys_addr_t base; + + base = __pa(extend_brk(tables, PAGE_SIZE)); + + pgt_buf_start = base >> PAGE_SHIFT; + pgt_buf_end = pgt_buf_start; + pgt_buf_top = pgt_buf_start + (tables >> PAGE_SHIFT); +} + int after_bootmem; int direct_gbpages @@ -228,105 +243,6 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, return nr_range; } -/* - * First calculate space needed for kernel direct mapping page tables to cover - * mr[0].start to mr[nr_range - 1].end, while accounting for possible 2M and 1GB - * pages. Then find enough contiguous space for those page tables. - */ -static unsigned long __init calculate_table_space_size(unsigned long start, unsigned long end) -{ - int i; - unsigned long puds = 0, pmds = 0, ptes = 0, tables; - struct map_range mr[NR_RANGE_MR]; - int nr_range; - - memset(mr, 0, sizeof(mr)); - nr_range = 0; - nr_range = split_mem_range(mr, nr_range, start, end); - - for (i = 0; i < nr_range; i++) { - unsigned long range, extra; - - range = mr[i].end - mr[i].start; - puds += (range + PUD_SIZE - 1) >> PUD_SHIFT; - - if (mr[i].page_size_mask & (1 << PG_LEVEL_1G)) { - extra = range - ((range >> PUD_SHIFT) << PUD_SHIFT); - pmds += (extra + PMD_SIZE - 1) >> PMD_SHIFT; - } else { - pmds += (range + PMD_SIZE - 1) >> PMD_SHIFT; - } - - if (mr[i].page_size_mask & (1 << PG_LEVEL_2M)) { - extra = range - ((range >> PMD_SHIFT) << PMD_SHIFT); -#ifdef CONFIG_X86_32 - extra += PMD_SIZE; -#endif - ptes += (extra + PAGE_SIZE - 1) >> PAGE_SHIFT; - } else { - ptes += (range + PAGE_SIZE - 1) >> PAGE_SHIFT; - } - } - - tables = roundup(puds * sizeof(pud_t), PAGE_SIZE); - tables += roundup(pmds * sizeof(pmd_t), PAGE_SIZE); - tables += roundup(ptes * sizeof(pte_t), PAGE_SIZE); - -#ifdef CONFIG_X86_32 - /* for fixmap */ - tables += roundup(__end_of_fixed_addresses * sizeof(pte_t), PAGE_SIZE); -#endif - - return tables; -} - -static unsigned long __init calculate_all_table_space_size(void) -{ - unsigned long start_pfn, end_pfn; - unsigned long tables; - int i; - - /* the ISA range is always mapped regardless of memory holes */ - tables = calculate_table_space_size(0, ISA_END_ADDRESS); - - for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, NULL) { - u64 start = start_pfn << PAGE_SHIFT; - u64 end = end_pfn << PAGE_SHIFT; - - if (end <= ISA_END_ADDRESS) - continue; - - if (start < ISA_END_ADDRESS) - start = ISA_END_ADDRESS; -#ifdef CONFIG_X86_32 - /* on 32 bit, we only map up to max_low_pfn */ - if ((start >> PAGE_SHIFT) >= max_low_pfn) - continue; - - if ((end >> PAGE_SHIFT) > max_low_pfn) - end = max_low_pfn << PAGE_SHIFT; -#endif - tables += calculate_table_space_size(start, end); - } - - return tables; -} - -static void __init find_early_table_space(unsigned long start, - unsigned long good_end, - unsigned long tables) -{ - phys_addr_t base; - - base = memblock_find_in_range(start, good_end, tables, PAGE_SIZE); - if (!base) - panic("Cannot find space for the kernel page tables"); - - pgt_buf_start = base >> PAGE_SHIFT; - pgt_buf_end = pgt_buf_start; - pgt_buf_top = pgt_buf_start + (tables >> PAGE_SHIFT); -} - static struct range pfn_mapped[E820_X_MAX]; static int nr_pfn_mapped; @@ -391,17 +307,14 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, } /* - * Iterate through E820 memory map and create direct mappings for only E820_RAM - * regions. We cannot simply create direct mappings for all pfns from - * [0 to max_low_pfn) and [4GB to max_pfn) because of possible memory holes in - * high addresses that cannot be marked as UC by fixed/variable range MTRRs. - * Depending on the alignment of E820 ranges, this may possibly result in using - * smaller size (i.e. 4K instead of 2M or 1G) page tables. + * would have hole in the middle or ends, and only ram parts will be mapped. */ -static void __init init_range_memory_mapping(unsigned long range_start, +static unsigned long __init init_range_memory_mapping( + unsigned long range_start, unsigned long range_end) { unsigned long start_pfn, end_pfn; + unsigned long mapped_ram_size = 0; int i; for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, NULL) { @@ -421,71 +334,70 @@ static void __init init_range_memory_mapping(unsigned long range_start, end = range_end; init_memory_mapping(start, end); + + mapped_ram_size += end - start; } + + return mapped_ram_size; } +/* (PUD_SHIFT-PMD_SHIFT)/2 */ +#define STEP_SIZE_SHIFT 5 void __init init_mem_mapping(void) { - unsigned long tables, good_end, end; + unsigned long end, real_end, start, last_start; + unsigned long step_size; + unsigned long addr; + unsigned long mapped_ram_size = 0; + unsigned long new_mapped_ram_size; probe_page_size_mask(); - /* - * Find space for the kernel direct mapping tables. - * - * Later we should allocate these tables in the local node of the - * memory mapped. Unfortunately this is done currently before the - * nodes are discovered. - */ #ifdef CONFIG_X86_64 end = max_pfn << PAGE_SHIFT; - good_end = end; #else end = max_low_pfn << PAGE_SHIFT; - good_end = max_pfn_mapped << PAGE_SHIFT; #endif - tables = calculate_all_table_space_size(); - find_early_table_space(0, good_end, tables); - printk(KERN_DEBUG "kernel direct mapping tables up to %#lx @ [mem %#010lx-%#010lx] prealloc\n", - end - 1, pgt_buf_start << PAGE_SHIFT, - (pgt_buf_top << PAGE_SHIFT) - 1); - max_pfn_mapped = 0; /* will get exact value next */ /* the ISA range is always mapped regardless of memory holes */ init_memory_mapping(0, ISA_END_ADDRESS); - init_range_memory_mapping(ISA_END_ADDRESS, end); + + /* xen has big range in reserved near end of ram, skip it at first */ + addr = memblock_find_in_range(ISA_END_ADDRESS, end, PMD_SIZE, + PAGE_SIZE); + real_end = addr + PMD_SIZE; + + /* step_size need to be small so pgt_buf from BRK could cover it */ + step_size = PMD_SIZE; + max_pfn_mapped = 0; /* will get exact value next */ + min_pfn_mapped = real_end >> PAGE_SHIFT; + last_start = start = real_end; + while (last_start > ISA_END_ADDRESS) { + if (last_start > step_size) { + start = round_down(last_start - 1, step_size); + if (start < ISA_END_ADDRESS) + start = ISA_END_ADDRESS; + } else + start = ISA_END_ADDRESS; + new_mapped_ram_size = init_range_memory_mapping(start, + last_start); + last_start = start; + min_pfn_mapped = last_start >> PAGE_SHIFT; + /* only increase step_size after big range get mapped */ + if (new_mapped_ram_size > mapped_ram_size) + step_size <<= STEP_SIZE_SHIFT; + mapped_ram_size += new_mapped_ram_size; + } + + if (real_end < end) + init_range_memory_mapping(real_end, end); + #ifdef CONFIG_X86_64 if (max_pfn > max_low_pfn) { /* can we preseve max_low_pfn ?*/ max_low_pfn = max_pfn; } #endif - /* - * Reserve the kernel pagetable pages we used (pgt_buf_start - - * pgt_buf_end) and free the other ones (pgt_buf_end - pgt_buf_top) - * so that they can be reused for other purposes. - * - * On native it just means calling memblock_reserve, on Xen it also - * means marking RW the pagetable pages that we allocated before - * but that haven't been used. - * - * In fact on xen we mark RO the whole range pgt_buf_start - - * pgt_buf_top, because we have to make sure that when - * init_memory_mapping reaches the pagetable pages area, it maps - * RO all the pagetable pages, including the ones that are beyond - * pgt_buf_end at that time. - */ - if (pgt_buf_end > pgt_buf_start) { - printk(KERN_DEBUG "kernel direct mapping tables up to %#lx @ [mem %#010lx-%#010lx] final\n", - end - 1, pgt_buf_start << PAGE_SHIFT, - (pgt_buf_end << PAGE_SHIFT) - 1); - x86_init.mapping.pagetable_reserve(PFN_PHYS(pgt_buf_start), - PFN_PHYS(pgt_buf_end)); - } - - /* stop the wrong using */ - pgt_buf_top = 0; - early_memtest(0, max_pfn_mapped << PAGE_SHIFT); } diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 27f7fc69cf8a..7bb11064a9e1 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -61,11 +61,22 @@ bool __read_mostly __vmalloc_start_set = false; static __init void *alloc_low_page(void) { - unsigned long pfn = pgt_buf_end++; + unsigned long pfn; void *adr; - if (pfn >= pgt_buf_top) - panic("alloc_low_page: ran out of memory"); + if ((pgt_buf_end + 1) >= pgt_buf_top) { + unsigned long ret; + if (min_pfn_mapped >= max_pfn_mapped) + panic("alloc_low_page: ran out of memory"); + ret = memblock_find_in_range(min_pfn_mapped << PAGE_SHIFT, + max_pfn_mapped << PAGE_SHIFT, + PAGE_SIZE, PAGE_SIZE); + if (!ret) + panic("alloc_low_page: can not alloc memory"); + memblock_reserve(ret, PAGE_SIZE); + pfn = ret >> PAGE_SHIFT; + } else + pfn = pgt_buf_end++; adr = __va(pfn * PAGE_SIZE); clear_page(adr); diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index fa28e3e29741..eefaea637b3d 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -316,7 +316,7 @@ void __init cleanup_highmap(void) static __ref void *alloc_low_page(unsigned long *phys) { - unsigned long pfn = pgt_buf_end++; + unsigned long pfn; void *adr; if (after_bootmem) { @@ -326,8 +326,19 @@ static __ref void *alloc_low_page(unsigned long *phys) return adr; } - if (pfn >= pgt_buf_top) - panic("alloc_low_page: ran out of memory"); + if ((pgt_buf_end + 1) >= pgt_buf_top) { + unsigned long ret; + if (min_pfn_mapped >= max_pfn_mapped) + panic("alloc_low_page: ran out of memory"); + ret = memblock_find_in_range(min_pfn_mapped << PAGE_SHIFT, + max_pfn_mapped << PAGE_SHIFT, + PAGE_SIZE, PAGE_SIZE); + if (!ret) + panic("alloc_low_page: can not alloc memory"); + memblock_reserve(ret, PAGE_SIZE); + pfn = ret >> PAGE_SHIFT; + } else + pfn = pgt_buf_end++; adr = early_memremap(pfn * PAGE_SIZE, PAGE_SIZE); clear_page(adr); From 973dc4f3fad5890bc7b694148ad4c825b9af6dc1 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:38:59 -0800 Subject: [PATCH 0030/4607] x86, mm: Remove early_memremap workaround for page table accessing on 64bit We try to put page table high to make room for kdump, and at that time those ranges are not mapped yet, and have to use ioremap to access it. Now after patch that pre-map page table top down. x86, mm: setup page table in top-down We do not need that workaround anymore. Just use __va to return directly mapping address. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-23-git-send-email-yinghai@kernel.org Acked-by: Stefano Stabellini Signed-off-by: H. Peter Anvin --- arch/x86/mm/init_64.c | 38 ++++---------------------------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index eefaea637b3d..5ee924237689 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -340,36 +340,12 @@ static __ref void *alloc_low_page(unsigned long *phys) } else pfn = pgt_buf_end++; - adr = early_memremap(pfn * PAGE_SIZE, PAGE_SIZE); + adr = __va(pfn * PAGE_SIZE); clear_page(adr); *phys = pfn * PAGE_SIZE; return adr; } -static __ref void *map_low_page(void *virt) -{ - void *adr; - unsigned long phys, left; - - if (after_bootmem) - return virt; - - phys = __pa(virt); - left = phys & (PAGE_SIZE - 1); - adr = early_memremap(phys & PAGE_MASK, PAGE_SIZE); - adr = (void *)(((unsigned long)adr) | left); - - return adr; -} - -static __ref void unmap_low_page(void *adr) -{ - if (after_bootmem) - return; - - early_iounmap((void *)((unsigned long)adr & PAGE_MASK), PAGE_SIZE); -} - static unsigned long __meminit phys_pte_init(pte_t *pte_page, unsigned long addr, unsigned long end, pgprot_t prot) @@ -442,10 +418,9 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end, if (pmd_val(*pmd)) { if (!pmd_large(*pmd)) { spin_lock(&init_mm.page_table_lock); - pte = map_low_page((pte_t *)pmd_page_vaddr(*pmd)); + pte = (pte_t *)pmd_page_vaddr(*pmd); last_map_addr = phys_pte_init(pte, address, end, prot); - unmap_low_page(pte); spin_unlock(&init_mm.page_table_lock); continue; } @@ -483,7 +458,6 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end, pte = alloc_low_page(&pte_phys); last_map_addr = phys_pte_init(pte, address, end, new_prot); - unmap_low_page(pte); spin_lock(&init_mm.page_table_lock); pmd_populate_kernel(&init_mm, pmd, __va(pte_phys)); @@ -518,10 +492,9 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end, if (pud_val(*pud)) { if (!pud_large(*pud)) { - pmd = map_low_page(pmd_offset(pud, 0)); + pmd = pmd_offset(pud, 0); last_map_addr = phys_pmd_init(pmd, addr, end, page_size_mask, prot); - unmap_low_page(pmd); __flush_tlb_all(); continue; } @@ -560,7 +533,6 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end, pmd = alloc_low_page(&pmd_phys); last_map_addr = phys_pmd_init(pmd, addr, end, page_size_mask, prot); - unmap_low_page(pmd); spin_lock(&init_mm.page_table_lock); pud_populate(&init_mm, pud, __va(pmd_phys)); @@ -596,17 +568,15 @@ kernel_physical_mapping_init(unsigned long start, next = end; if (pgd_val(*pgd)) { - pud = map_low_page((pud_t *)pgd_page_vaddr(*pgd)); + pud = (pud_t *)pgd_page_vaddr(*pgd); last_map_addr = phys_pud_init(pud, __pa(start), __pa(end), page_size_mask); - unmap_low_page(pud); continue; } pud = alloc_low_page(&pud_phys); last_map_addr = phys_pud_init(pud, __pa(start), __pa(next), page_size_mask); - unmap_low_page(pud); spin_lock(&init_mm.page_table_lock); pgd_populate(&init_mm, pgd, __va(pud_phys)); From 868bf4d6b94c980d3ad87f892a5e528b8ee2c320 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:00 -0800 Subject: [PATCH 0031/4607] x86, mm: Remove parameter in alloc_low_page for 64bit Now all page table buf are pre-mapped, and could use virtual address directly. So don't need to remember physical address anymore. Remove that phys pointer in alloc_low_page(), and that will allow us to merge alloc_low_page between 64bit and 32bit. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-24-git-send-email-yinghai@kernel.org Acked-by: Stefano Stabellini Signed-off-by: H. Peter Anvin --- arch/x86/mm/init_64.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 5ee924237689..19608203fa5d 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -314,14 +314,13 @@ void __init cleanup_highmap(void) } } -static __ref void *alloc_low_page(unsigned long *phys) +static __ref void *alloc_low_page(void) { unsigned long pfn; void *adr; if (after_bootmem) { adr = (void *)get_zeroed_page(GFP_ATOMIC | __GFP_NOTRACK); - *phys = __pa(adr); return adr; } @@ -342,7 +341,6 @@ static __ref void *alloc_low_page(unsigned long *phys) adr = __va(pfn * PAGE_SIZE); clear_page(adr); - *phys = pfn * PAGE_SIZE; return adr; } @@ -401,7 +399,6 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end, int i = pmd_index(address); for (; i < PTRS_PER_PMD; i++, address = next) { - unsigned long pte_phys; pmd_t *pmd = pmd_page + pmd_index(address); pte_t *pte; pgprot_t new_prot = prot; @@ -456,11 +453,11 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end, continue; } - pte = alloc_low_page(&pte_phys); + pte = alloc_low_page(); last_map_addr = phys_pte_init(pte, address, end, new_prot); spin_lock(&init_mm.page_table_lock); - pmd_populate_kernel(&init_mm, pmd, __va(pte_phys)); + pmd_populate_kernel(&init_mm, pmd, pte); spin_unlock(&init_mm.page_table_lock); } update_page_count(PG_LEVEL_2M, pages); @@ -476,7 +473,6 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end, int i = pud_index(addr); for (; i < PTRS_PER_PUD; i++, addr = next) { - unsigned long pmd_phys; pud_t *pud = pud_page + pud_index(addr); pmd_t *pmd; pgprot_t prot = PAGE_KERNEL; @@ -530,12 +526,12 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end, continue; } - pmd = alloc_low_page(&pmd_phys); + pmd = alloc_low_page(); last_map_addr = phys_pmd_init(pmd, addr, end, page_size_mask, prot); spin_lock(&init_mm.page_table_lock); - pud_populate(&init_mm, pud, __va(pmd_phys)); + pud_populate(&init_mm, pud, pmd); spin_unlock(&init_mm.page_table_lock); } __flush_tlb_all(); @@ -560,7 +556,6 @@ kernel_physical_mapping_init(unsigned long start, for (; start < end; start = next) { pgd_t *pgd = pgd_offset_k(start); - unsigned long pud_phys; pud_t *pud; next = (start + PGDIR_SIZE) & PGDIR_MASK; @@ -574,12 +569,12 @@ kernel_physical_mapping_init(unsigned long start, continue; } - pud = alloc_low_page(&pud_phys); + pud = alloc_low_page(); last_map_addr = phys_pud_init(pud, __pa(start), __pa(next), page_size_mask); spin_lock(&init_mm.page_table_lock); - pgd_populate(&init_mm, pgd, __va(pud_phys)); + pgd_populate(&init_mm, pgd, pud); spin_unlock(&init_mm.page_table_lock); pgd_changed = true; } From 5c51bdbe4c74dce7996d0bbfa39974775cc3f13c Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:01 -0800 Subject: [PATCH 0032/4607] x86, mm: Merge alloc_low_page between 64bit and 32bit They are almost same except 64 bit need to handle after_bootmem case. Add mm_internal.h to make that alloc_low_page() only to be accessible from arch/x86/mm/init*.c Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-25-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 34 ++++++++++++++++++++++++++++++++++ arch/x86/mm/init_32.c | 26 ++------------------------ arch/x86/mm/init_64.c | 32 ++------------------------------ arch/x86/mm/mm_internal.h | 6 ++++++ 4 files changed, 44 insertions(+), 54 deletions(-) create mode 100644 arch/x86/mm/mm_internal.h diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 2393d0099e7f..848189229b2d 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -17,10 +17,44 @@ #include #include /* for MAX_DMA_PFN */ +#include "mm_internal.h" + unsigned long __initdata pgt_buf_start; unsigned long __meminitdata pgt_buf_end; unsigned long __meminitdata pgt_buf_top; +__ref void *alloc_low_page(void) +{ + unsigned long pfn; + void *adr; + +#ifdef CONFIG_X86_64 + if (after_bootmem) { + adr = (void *)get_zeroed_page(GFP_ATOMIC | __GFP_NOTRACK); + + return adr; + } +#endif + + if ((pgt_buf_end + 1) >= pgt_buf_top) { + unsigned long ret; + if (min_pfn_mapped >= max_pfn_mapped) + panic("alloc_low_page: ran out of memory"); + ret = memblock_find_in_range(min_pfn_mapped << PAGE_SHIFT, + max_pfn_mapped << PAGE_SHIFT, + PAGE_SIZE, PAGE_SIZE); + if (!ret) + panic("alloc_low_page: can not alloc memory"); + memblock_reserve(ret, PAGE_SIZE); + pfn = ret >> PAGE_SHIFT; + } else + pfn = pgt_buf_end++; + + adr = __va(pfn * PAGE_SIZE); + clear_page(adr); + return adr; +} + /* need 4 4k for initial PMD_SIZE, 4k for 0-ISA_END_ADDRESS */ #define INIT_PGT_BUF_SIZE (5 * PAGE_SIZE) RESERVE_BRK(early_pgt_alloc, INIT_PGT_BUF_SIZE); diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 7bb11064a9e1..a7f2df1cdcfd 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -53,36 +53,14 @@ #include #include +#include "mm_internal.h" + unsigned long highstart_pfn, highend_pfn; static noinline int do_test_wp_bit(void); bool __read_mostly __vmalloc_start_set = false; -static __init void *alloc_low_page(void) -{ - unsigned long pfn; - void *adr; - - if ((pgt_buf_end + 1) >= pgt_buf_top) { - unsigned long ret; - if (min_pfn_mapped >= max_pfn_mapped) - panic("alloc_low_page: ran out of memory"); - ret = memblock_find_in_range(min_pfn_mapped << PAGE_SHIFT, - max_pfn_mapped << PAGE_SHIFT, - PAGE_SIZE, PAGE_SIZE); - if (!ret) - panic("alloc_low_page: can not alloc memory"); - memblock_reserve(ret, PAGE_SIZE); - pfn = ret >> PAGE_SHIFT; - } else - pfn = pgt_buf_end++; - - adr = __va(pfn * PAGE_SIZE); - clear_page(adr); - return adr; -} - /* * Creates a middle page table and puts a pointer to it in the * given global directory entry. This only returns the gd entry diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 19608203fa5d..1d53defc99c9 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -54,6 +54,8 @@ #include #include +#include "mm_internal.h" + static int __init parse_direct_gbpages_off(char *arg) { direct_gbpages = 0; @@ -314,36 +316,6 @@ void __init cleanup_highmap(void) } } -static __ref void *alloc_low_page(void) -{ - unsigned long pfn; - void *adr; - - if (after_bootmem) { - adr = (void *)get_zeroed_page(GFP_ATOMIC | __GFP_NOTRACK); - - return adr; - } - - if ((pgt_buf_end + 1) >= pgt_buf_top) { - unsigned long ret; - if (min_pfn_mapped >= max_pfn_mapped) - panic("alloc_low_page: ran out of memory"); - ret = memblock_find_in_range(min_pfn_mapped << PAGE_SHIFT, - max_pfn_mapped << PAGE_SHIFT, - PAGE_SIZE, PAGE_SIZE); - if (!ret) - panic("alloc_low_page: can not alloc memory"); - memblock_reserve(ret, PAGE_SIZE); - pfn = ret >> PAGE_SHIFT; - } else - pfn = pgt_buf_end++; - - adr = __va(pfn * PAGE_SIZE); - clear_page(adr); - return adr; -} - static unsigned long __meminit phys_pte_init(pte_t *pte_page, unsigned long addr, unsigned long end, pgprot_t prot) diff --git a/arch/x86/mm/mm_internal.h b/arch/x86/mm/mm_internal.h new file mode 100644 index 000000000000..b3f993a2555e --- /dev/null +++ b/arch/x86/mm/mm_internal.h @@ -0,0 +1,6 @@ +#ifndef __X86_MM_INTERNAL_H +#define __X86_MM_INTERNAL_H + +void *alloc_low_page(void); + +#endif /* __X86_MM_INTERNAL_H */ From 9985b4c6fa7d660f685918a58282275e9e35d8e0 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:02 -0800 Subject: [PATCH 0033/4607] x86, mm: Move min_pfn_mapped back to mm/init.c Also change it to static. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-26-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/page_types.h | 1 - arch/x86/kernel/setup.c | 1 - arch/x86/mm/init.c | 2 ++ 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/page_types.h b/arch/x86/include/asm/page_types.h index 9f6f3e66e84d..54c97879195e 100644 --- a/arch/x86/include/asm/page_types.h +++ b/arch/x86/include/asm/page_types.h @@ -45,7 +45,6 @@ extern int devmem_is_allowed(unsigned long pagenr); extern unsigned long max_low_pfn_mapped; extern unsigned long max_pfn_mapped; -extern unsigned long min_pfn_mapped; static inline phys_addr_t get_max_mapped(void) { diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index f7634092931b..20151941cce8 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -124,7 +124,6 @@ */ unsigned long max_low_pfn_mapped; unsigned long max_pfn_mapped; -unsigned long min_pfn_mapped; #ifdef CONFIG_DMI RESERVE_BRK(dmi_alloc, 65536); diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 848189229b2d..6392bf9a3947 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -23,6 +23,8 @@ unsigned long __initdata pgt_buf_start; unsigned long __meminitdata pgt_buf_end; unsigned long __meminitdata pgt_buf_top; +static unsigned long min_pfn_mapped; + __ref void *alloc_low_page(void) { unsigned long pfn; From 6f80b68e9e515547edbacb0c37491730bf766db5 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:03 -0800 Subject: [PATCH 0034/4607] x86, mm, Xen: Remove mapping_pagetable_reserve() Page table area are pre-mapped now after x86, mm: setup page table in top-down x86, mm: Remove early_memremap workaround for page table accessing on 64bit mapping_pagetable_reserve is not used anymore, so remove it. Also remove operation in mask_rw_pte(), as modified allow_low_page always return pages that are already mapped, moreover xen_alloc_pte_init, xen_alloc_pmd_init, etc, will mark the page RO before hooking it into the pagetable automatically. -v2: add changelog about mask_rw_pte() from Stefano. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-27-git-send-email-yinghai@kernel.org Cc: Stefano Stabellini Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/pgtable_types.h | 1 - arch/x86/include/asm/x86_init.h | 12 ------------ arch/x86/kernel/x86_init.c | 4 ---- arch/x86/mm/init.c | 4 ---- arch/x86/xen/mmu.c | 28 ---------------------------- 5 files changed, 49 deletions(-) diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h index ec8a1fc9505d..79738f20aaf5 100644 --- a/arch/x86/include/asm/pgtable_types.h +++ b/arch/x86/include/asm/pgtable_types.h @@ -301,7 +301,6 @@ int phys_mem_access_prot_allowed(struct file *file, unsigned long pfn, /* Install a pte for a particular vaddr in kernel space. */ void set_pte_vaddr(unsigned long vaddr, pte_t pte); -extern void native_pagetable_reserve(u64 start, u64 end); #ifdef CONFIG_X86_32 extern void native_pagetable_init(void); #else diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index 57693498519c..3b2ce8fc995a 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h @@ -68,17 +68,6 @@ struct x86_init_oem { void (*banner)(void); }; -/** - * struct x86_init_mapping - platform specific initial kernel pagetable setup - * @pagetable_reserve: reserve a range of addresses for kernel pagetable usage - * - * For more details on the purpose of this hook, look in - * init_memory_mapping and the commit that added it. - */ -struct x86_init_mapping { - void (*pagetable_reserve)(u64 start, u64 end); -}; - /** * struct x86_init_paging - platform specific paging functions * @pagetable_init: platform specific paging initialization call to setup @@ -136,7 +125,6 @@ struct x86_init_ops { struct x86_init_mpparse mpparse; struct x86_init_irqs irqs; struct x86_init_oem oem; - struct x86_init_mapping mapping; struct x86_init_paging paging; struct x86_init_timers timers; struct x86_init_iommu iommu; diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c index 7a3d075a814a..50cf83ecd32e 100644 --- a/arch/x86/kernel/x86_init.c +++ b/arch/x86/kernel/x86_init.c @@ -62,10 +62,6 @@ struct x86_init_ops x86_init __initdata = { .banner = default_banner, }, - .mapping = { - .pagetable_reserve = native_pagetable_reserve, - }, - .paging = { .pagetable_init = native_pagetable_init, }, diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 6392bf9a3947..21173fcdb4a1 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -112,10 +112,6 @@ static void __init probe_page_size_mask(void) __supported_pte_mask |= _PAGE_GLOBAL; } } -void __init native_pagetable_reserve(u64 start, u64 end) -{ - memblock_reserve(start, end - start); -} #ifdef CONFIG_X86_32 #define NR_RANGE_MR 3 diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index dcf5f2dd91ec..bbb883f58bc4 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -1178,20 +1178,6 @@ static void xen_exit_mmap(struct mm_struct *mm) static void xen_post_allocator_init(void); -static __init void xen_mapping_pagetable_reserve(u64 start, u64 end) -{ - /* reserve the range used */ - native_pagetable_reserve(start, end); - - /* set as RW the rest */ - printk(KERN_DEBUG "xen: setting RW the range %llx - %llx\n", end, - PFN_PHYS(pgt_buf_top)); - while (end < PFN_PHYS(pgt_buf_top)) { - make_lowmem_page_readwrite(__va(end)); - end += PAGE_SIZE; - } -} - #ifdef CONFIG_X86_64 static void __init xen_cleanhighmap(unsigned long vaddr, unsigned long vaddr_end) @@ -1503,19 +1489,6 @@ static pte_t __init mask_rw_pte(pte_t *ptep, pte_t pte) #else /* CONFIG_X86_64 */ static pte_t __init mask_rw_pte(pte_t *ptep, pte_t pte) { - unsigned long pfn = pte_pfn(pte); - - /* - * If the new pfn is within the range of the newly allocated - * kernel pagetable, and it isn't being mapped into an - * early_ioremap fixmap slot as a freshly allocated page, make sure - * it is RO. - */ - if (((!is_early_ioremap_ptep(ptep) && - pfn >= pgt_buf_start && pfn < pgt_buf_top)) || - (is_early_ioremap_ptep(ptep) && pfn != (pgt_buf_end - 1))) - pte = pte_wrprotect(pte); - return pte; } #endif /* CONFIG_X86_64 */ @@ -2197,7 +2170,6 @@ static const struct pv_mmu_ops xen_mmu_ops __initconst = { void __init xen_init_mmu_ops(void) { - x86_init.mapping.pagetable_reserve = xen_mapping_pagetable_reserve; x86_init.paging.pagetable_init = xen_pagetable_init; pv_mmu_ops = xen_mmu_ops; From 22c8ca2ac256bb681be791858b35502b5d37e73b Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:04 -0800 Subject: [PATCH 0035/4607] x86, mm: Add alloc_low_pages(num) 32bit kmap mapping needs pages to be used for low to high. At this point those pages are still from pgt_buf_* from BRK, so it is ok now. But we want to move early_ioremap_page_table_range_init() out of init_memory_mapping() and only call it one time later, that will make page_table_range_init/page_table_kmap_check/alloc_low_page to use memblock to get page. memblock allocation for pages are from high to low. So will get panic from page_table_kmap_check() that has BUG_ON to do ordering checking. This patch add alloc_low_pages to make it possible to allocate serveral pages at first, and hand out pages one by one from low to high. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-28-git-send-email-yinghai@kernel.org Cc: Andrew Morton Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 33 +++++++++++++++++++++------------ arch/x86/mm/mm_internal.h | 6 +++++- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 21173fcdb4a1..02cea14c6d0c 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -25,36 +25,45 @@ unsigned long __meminitdata pgt_buf_top; static unsigned long min_pfn_mapped; -__ref void *alloc_low_page(void) +__ref void *alloc_low_pages(unsigned int num) { unsigned long pfn; - void *adr; + int i; #ifdef CONFIG_X86_64 if (after_bootmem) { - adr = (void *)get_zeroed_page(GFP_ATOMIC | __GFP_NOTRACK); + unsigned int order; - return adr; + order = get_order((unsigned long)num << PAGE_SHIFT); + return (void *)__get_free_pages(GFP_ATOMIC | __GFP_NOTRACK | + __GFP_ZERO, order); } #endif - if ((pgt_buf_end + 1) >= pgt_buf_top) { + if ((pgt_buf_end + num) >= pgt_buf_top) { unsigned long ret; if (min_pfn_mapped >= max_pfn_mapped) panic("alloc_low_page: ran out of memory"); ret = memblock_find_in_range(min_pfn_mapped << PAGE_SHIFT, max_pfn_mapped << PAGE_SHIFT, - PAGE_SIZE, PAGE_SIZE); + PAGE_SIZE * num , PAGE_SIZE); if (!ret) panic("alloc_low_page: can not alloc memory"); - memblock_reserve(ret, PAGE_SIZE); + memblock_reserve(ret, PAGE_SIZE * num); pfn = ret >> PAGE_SHIFT; - } else - pfn = pgt_buf_end++; + } else { + pfn = pgt_buf_end; + pgt_buf_end += num; + } - adr = __va(pfn * PAGE_SIZE); - clear_page(adr); - return adr; + for (i = 0; i < num; i++) { + void *adr; + + adr = __va((pfn + i) << PAGE_SHIFT); + clear_page(adr); + } + + return __va(pfn << PAGE_SHIFT); } /* need 4 4k for initial PMD_SIZE, 4k for 0-ISA_END_ADDRESS */ diff --git a/arch/x86/mm/mm_internal.h b/arch/x86/mm/mm_internal.h index b3f993a2555e..7e3b88ee078a 100644 --- a/arch/x86/mm/mm_internal.h +++ b/arch/x86/mm/mm_internal.h @@ -1,6 +1,10 @@ #ifndef __X86_MM_INTERNAL_H #define __X86_MM_INTERNAL_H -void *alloc_low_page(void); +void *alloc_low_pages(unsigned int num); +static inline void *alloc_low_page(void) +{ + return alloc_low_pages(1); +} #endif /* __X86_MM_INTERNAL_H */ From ddd3509df8f8d4f1cf4784f559d702ce00dc8846 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Fri, 16 Nov 2012 19:39:05 -0800 Subject: [PATCH 0036/4607] x86, mm: Add pointer about Xen mmu requirement for alloc_low_pages Add link for more information 279b706 x86,xen: introduce x86_init.mapping.pagetable_reserve -v2: updated to commets from hpa to include commit name. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-29-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 02cea14c6d0c..cb4f8ba70ecc 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -25,6 +25,15 @@ unsigned long __meminitdata pgt_buf_top; static unsigned long min_pfn_mapped; +/* + * Pages returned are already directly mapped. + * + * Changing that is likely to break Xen, see commit: + * + * 279b706 x86,xen: introduce x86_init.mapping.pagetable_reserve + * + * for detailed information. + */ __ref void *alloc_low_pages(unsigned int num) { unsigned long pfn; From 719272c45b821d38608fc333700bde1a89c56c59 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:06 -0800 Subject: [PATCH 0037/4607] x86, mm: only call early_ioremap_page_table_range_init() once On 32bit, before patcheset that only set page table for ram, we only call that one time. Now, we are calling that during every init_memory_mapping if we have holes under max_low_pfn. We should only call it one time after all ranges under max_low_page get mapped just like we did before. Also that could avoid the risk to run out of pgt_buf in BRK. Need to update page_table_range_init() to count the pages for kmap page table at first, and use new added alloc_low_pages() to get pages in sequence. That will conform to the requirement that pages need to be in low to high order. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-30-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 13 +++++------- arch/x86/mm/init_32.c | 47 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index cb4f8ba70ecc..bed4888c6f4f 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -343,14 +343,6 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, ret = kernel_physical_mapping_init(mr[i].start, mr[i].end, mr[i].page_size_mask); -#ifdef CONFIG_X86_32 - early_ioremap_page_table_range_init(); - - load_cr3(swapper_pg_dir); -#endif - - __flush_tlb_all(); - add_pfn_range_mapped(start >> PAGE_SHIFT, ret >> PAGE_SHIFT); return ret >> PAGE_SHIFT; @@ -447,7 +439,12 @@ void __init init_mem_mapping(void) /* can we preseve max_low_pfn ?*/ max_low_pfn = max_pfn; } +#else + early_ioremap_page_table_range_init(); + load_cr3(swapper_pg_dir); + __flush_tlb_all(); #endif + early_memtest(0, max_pfn_mapped << PAGE_SHIFT); } diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index a7f2df1cdcfd..0ae1ba8bc1b9 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -135,8 +135,39 @@ pte_t * __init populate_extra_pte(unsigned long vaddr) return one_page_table_init(pmd) + pte_idx; } +static unsigned long __init +page_table_range_init_count(unsigned long start, unsigned long end) +{ + unsigned long count = 0; +#ifdef CONFIG_HIGHMEM + int pmd_idx_kmap_begin = fix_to_virt(FIX_KMAP_END) >> PMD_SHIFT; + int pmd_idx_kmap_end = fix_to_virt(FIX_KMAP_BEGIN) >> PMD_SHIFT; + int pgd_idx, pmd_idx; + unsigned long vaddr; + + if (pmd_idx_kmap_begin == pmd_idx_kmap_end) + return 0; + + vaddr = start; + pgd_idx = pgd_index(vaddr); + + for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd_idx++) { + for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end); + pmd_idx++) { + if ((vaddr >> PMD_SHIFT) >= pmd_idx_kmap_begin && + (vaddr >> PMD_SHIFT) <= pmd_idx_kmap_end) + count++; + vaddr += PMD_SIZE; + } + pmd_idx = 0; + } +#endif + return count; +} + static pte_t *__init page_table_kmap_check(pte_t *pte, pmd_t *pmd, - unsigned long vaddr, pte_t *lastpte) + unsigned long vaddr, pte_t *lastpte, + void **adr) { #ifdef CONFIG_HIGHMEM /* @@ -150,16 +181,15 @@ static pte_t *__init page_table_kmap_check(pte_t *pte, pmd_t *pmd, if (pmd_idx_kmap_begin != pmd_idx_kmap_end && (vaddr >> PMD_SHIFT) >= pmd_idx_kmap_begin - && (vaddr >> PMD_SHIFT) <= pmd_idx_kmap_end - && ((__pa(pte) >> PAGE_SHIFT) < pgt_buf_start - || (__pa(pte) >> PAGE_SHIFT) >= pgt_buf_end)) { + && (vaddr >> PMD_SHIFT) <= pmd_idx_kmap_end) { pte_t *newpte; int i; BUG_ON(after_bootmem); - newpte = alloc_low_page(); + newpte = *adr; for (i = 0; i < PTRS_PER_PTE; i++) set_pte(newpte + i, pte[i]); + *adr = (void *)(((unsigned long)(*adr)) + PAGE_SIZE); paravirt_alloc_pte(&init_mm, __pa(newpte) >> PAGE_SHIFT); set_pmd(pmd, __pmd(__pa(newpte)|_PAGE_TABLE)); @@ -193,6 +223,11 @@ page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base) pgd_t *pgd; pmd_t *pmd; pte_t *pte = NULL; + unsigned long count = page_table_range_init_count(start, end); + void *adr = NULL; + + if (count) + adr = alloc_low_pages(count); vaddr = start; pgd_idx = pgd_index(vaddr); @@ -205,7 +240,7 @@ page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base) for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end); pmd++, pmd_idx++) { pte = page_table_kmap_check(one_page_table_init(pmd), - pmd, vaddr, pte); + pmd, vaddr, pte, &adr); vaddr += PMD_SIZE; } From cf47065961b48727b4e47bc3e2e67f4996878437 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:07 -0800 Subject: [PATCH 0038/4607] x86, mm: Move back pgt_buf_* to mm/init.c Also change them to static. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-31-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/init.h | 4 ---- arch/x86/mm/init.c | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/arch/x86/include/asm/init.h b/arch/x86/include/asm/init.h index 4f13998be59a..626ea8d31b6d 100644 --- a/arch/x86/include/asm/init.h +++ b/arch/x86/include/asm/init.h @@ -12,8 +12,4 @@ kernel_physical_mapping_init(unsigned long start, unsigned long end, unsigned long page_size_mask); -extern unsigned long __initdata pgt_buf_start; -extern unsigned long __meminitdata pgt_buf_end; -extern unsigned long __meminitdata pgt_buf_top; - #endif /* _ASM_X86_INIT_32_H */ diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index bed4888c6f4f..3cadf1013cea 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -19,9 +19,9 @@ #include "mm_internal.h" -unsigned long __initdata pgt_buf_start; -unsigned long __meminitdata pgt_buf_end; -unsigned long __meminitdata pgt_buf_top; +static unsigned long __initdata pgt_buf_start; +static unsigned long __initdata pgt_buf_end; +static unsigned long __initdata pgt_buf_top; static unsigned long min_pfn_mapped; From 148b20989e0b83cb301e1fcd9e987c7abde05333 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:08 -0800 Subject: [PATCH 0039/4607] x86, mm: Move init_gbpages() out of setup.c Put it in mm/init.c, and call it from probe_page_mask(). init_mem_mapping is calling probe_page_mask at first. So calling sequence is not changed. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-32-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/kernel/setup.c | 15 +-------------- arch/x86/mm/init.c | 12 ++++++++++++ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 20151941cce8..85b62f1c8071 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -282,18 +282,7 @@ void * __init extend_brk(size_t size, size_t align) return ret; } -#ifdef CONFIG_X86_64 -static void __init init_gbpages(void) -{ - if (direct_gbpages && cpu_has_gbpages) - printk(KERN_INFO "Using GB pages for direct mapping\n"); - else - direct_gbpages = 0; -} -#else -static inline void init_gbpages(void) -{ -} +#ifdef CONFIG_X86_32 static void __init cleanup_highmap(void) { } @@ -933,8 +922,6 @@ void __init setup_arch(char **cmdline_p) setup_real_mode(); - init_gbpages(); - init_mem_mapping(); memblock.current_limit = get_max_mapped(); diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 3cadf1013cea..8168bf8fcda7 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -98,6 +98,16 @@ int direct_gbpages #endif ; +static void __init init_gbpages(void) +{ +#ifdef CONFIG_X86_64 + if (direct_gbpages && cpu_has_gbpages) + printk(KERN_INFO "Using GB pages for direct mapping\n"); + else + direct_gbpages = 0; +#endif +} + struct map_range { unsigned long start; unsigned long end; @@ -108,6 +118,8 @@ static int page_size_mask; static void __init probe_page_size_mask(void) { + init_gbpages(); + #if !defined(CONFIG_DEBUG_PAGEALLOC) && !defined(CONFIG_KMEMCHECK) /* * For CONFIG_DEBUG_PAGEALLOC, identity mapping will use small pages. From f836e35a98ab3b2f0d4c8730610e4a4a7f533505 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:09 -0800 Subject: [PATCH 0040/4607] x86, mm: change low/hignmem_pfn_init to static on 32bit Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-33-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init_32.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 0ae1ba8bc1b9..322ee56ea1fe 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -575,7 +575,7 @@ early_param("highmem", parse_highmem); * artificially via the highmem=x boot parameter then create * it: */ -void __init lowmem_pfn_init(void) +static void __init lowmem_pfn_init(void) { /* max_low_pfn is 0, we already have early_res support */ max_low_pfn = max_pfn; @@ -611,7 +611,7 @@ void __init lowmem_pfn_init(void) * We have more RAM than fits into lowmem - we try to put it into * highmem, also taking the highmem=x boot parameter into account: */ -void __init highmem_pfn_init(void) +static void __init highmem_pfn_init(void) { max_low_pfn = MAXMEM_PFN; From c8dcdb9ce463ad4a660099a74a850f4f6fc81c41 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:10 -0800 Subject: [PATCH 0041/4607] x86, mm: Move function declaration into mm_internal.h They are only for mm/init*.c. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-34-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/init.h | 16 +++------------- arch/x86/mm/mm_internal.h | 7 +++++++ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/arch/x86/include/asm/init.h b/arch/x86/include/asm/init.h index 626ea8d31b6d..bac770b7cbc4 100644 --- a/arch/x86/include/asm/init.h +++ b/arch/x86/include/asm/init.h @@ -1,15 +1,5 @@ -#ifndef _ASM_X86_INIT_32_H -#define _ASM_X86_INIT_32_H +#ifndef _ASM_X86_INIT_H +#define _ASM_X86_INIT_H -#ifdef CONFIG_X86_32 -extern void __init early_ioremap_page_table_range_init(void); -#endif -extern void __init zone_sizes_init(void); - -extern unsigned long __init -kernel_physical_mapping_init(unsigned long start, - unsigned long end, - unsigned long page_size_mask); - -#endif /* _ASM_X86_INIT_32_H */ +#endif /* _ASM_X86_INIT_H */ diff --git a/arch/x86/mm/mm_internal.h b/arch/x86/mm/mm_internal.h index 7e3b88ee078a..dc79ac121774 100644 --- a/arch/x86/mm/mm_internal.h +++ b/arch/x86/mm/mm_internal.h @@ -7,4 +7,11 @@ static inline void *alloc_low_page(void) return alloc_low_pages(1); } +void early_ioremap_page_table_range_init(void); + +unsigned long kernel_physical_mapping_init(unsigned long start, + unsigned long end, + unsigned long page_size_mask); +void zone_sizes_init(void); + #endif /* __X86_MM_INTERNAL_H */ From 11ed9e927d573d78beda6e6a166612666ae97064 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:11 -0800 Subject: [PATCH 0042/4607] x86, mm: Add check before clear pte above max_low_pfn on 32bit During test patch that adjust page_size_mask to map small range ram with big page size, found page table is setup wrongly for 32bit. And native_pagetable_init wrong clear pte for pmd with large page support. 1. add more comments about why we are expecting pte. 2. add BUG checking, so next time we could find problem earlier when we mess up page table setup again. 3. max_low_pfn is not included boundary for low memory mapping. We should check from max_low_pfn instead of +1. 4. add print out when some pte really get cleared, or we should use WARN() to find out why above max_low_pfn get mapped? so we could fix it. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-35-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init_32.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 322ee56ea1fe..19ef9f018012 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -480,9 +480,14 @@ void __init native_pagetable_init(void) /* * Remove any mappings which extend past the end of physical - * memory from the boot time page table: + * memory from the boot time page table. + * In virtual address space, we should have at least two pages + * from VMALLOC_END to pkmap or fixmap according to VMALLOC_END + * definition. And max_low_pfn is set to VMALLOC_END physical + * address. If initial memory mapping is doing right job, we + * should have pte used near max_low_pfn or one pmd is not present. */ - for (pfn = max_low_pfn + 1; pfn < 1<<(32-PAGE_SHIFT); pfn++) { + for (pfn = max_low_pfn; pfn < 1<<(32-PAGE_SHIFT); pfn++) { va = PAGE_OFFSET + (pfn<> PAGE_SHIFT); From 5a0d3aeeeffbd1534a510fc10c4ab7c99c45afce Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:12 -0800 Subject: [PATCH 0043/4607] x86, mm: use round_up/down in split_mem_range() to replace own inline version for those roundup and rounddown. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-36-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 8168bf8fcda7..0e625e606e5d 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -218,13 +218,11 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, * slowdowns. */ if (pos == 0) - end_pfn = 1<<(PMD_SHIFT - PAGE_SHIFT); + end_pfn = PMD_SIZE >> PAGE_SHIFT; else - end_pfn = ((pos + (PMD_SIZE - 1))>>PMD_SHIFT) - << (PMD_SHIFT - PAGE_SHIFT); + end_pfn = round_up(pos, PMD_SIZE) >> PAGE_SHIFT; #else /* CONFIG_X86_64 */ - end_pfn = ((pos + (PMD_SIZE - 1)) >> PMD_SHIFT) - << (PMD_SHIFT - PAGE_SHIFT); + end_pfn = round_up(pos, PMD_SIZE) >> PAGE_SHIFT; #endif if (end_pfn > (end >> PAGE_SHIFT)) end_pfn = end >> PAGE_SHIFT; @@ -234,15 +232,13 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, } /* big page (2M) range */ - start_pfn = ((pos + (PMD_SIZE - 1))>>PMD_SHIFT) - << (PMD_SHIFT - PAGE_SHIFT); + start_pfn = round_up(pos, PMD_SIZE) >> PAGE_SHIFT; #ifdef CONFIG_X86_32 - end_pfn = (end>>PMD_SHIFT) << (PMD_SHIFT - PAGE_SHIFT); + end_pfn = round_down(end, PMD_SIZE) >> PAGE_SHIFT; #else /* CONFIG_X86_64 */ - end_pfn = ((pos + (PUD_SIZE - 1))>>PUD_SHIFT) - << (PUD_SHIFT - PAGE_SHIFT); - if (end_pfn > ((end>>PMD_SHIFT)<<(PMD_SHIFT - PAGE_SHIFT))) - end_pfn = ((end>>PMD_SHIFT)<<(PMD_SHIFT - PAGE_SHIFT)); + end_pfn = round_up(pos, PUD_SIZE) >> PAGE_SHIFT; + if (end_pfn > (round_down(end, PMD_SIZE) >> PAGE_SHIFT)) + end_pfn = round_down(end, PMD_SIZE) >> PAGE_SHIFT; #endif if (start_pfn < end_pfn) { @@ -253,9 +249,8 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, #ifdef CONFIG_X86_64 /* big page (1G) range */ - start_pfn = ((pos + (PUD_SIZE - 1))>>PUD_SHIFT) - << (PUD_SHIFT - PAGE_SHIFT); - end_pfn = (end >> PUD_SHIFT) << (PUD_SHIFT - PAGE_SHIFT); + start_pfn = round_up(pos, PUD_SIZE) >> PAGE_SHIFT; + end_pfn = round_down(end, PUD_SIZE) >> PAGE_SHIFT; if (start_pfn < end_pfn) { nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, page_size_mask & @@ -264,9 +259,8 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, } /* tail is not big page (1G) alignment */ - start_pfn = ((pos + (PMD_SIZE - 1))>>PMD_SHIFT) - << (PMD_SHIFT - PAGE_SHIFT); - end_pfn = (end >> PMD_SHIFT) << (PMD_SHIFT - PAGE_SHIFT); + start_pfn = round_up(pos, PMD_SIZE) >> PAGE_SHIFT; + end_pfn = round_down(end, PMD_SIZE) >> PAGE_SHIFT; if (start_pfn < end_pfn) { nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, page_size_mask & (1< Date: Fri, 16 Nov 2012 19:39:13 -0800 Subject: [PATCH 0044/4607] x86, mm: use PFN_DOWN in split_mem_range() to replace own inline version for shifting. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-37-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 0e625e606e5d..1cca052b2cbd 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -208,8 +208,8 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, int i; /* head if not big page alignment ? */ - start_pfn = start >> PAGE_SHIFT; - pos = start_pfn << PAGE_SHIFT; + start_pfn = PFN_DOWN(start); + pos = PFN_PHYS(start_pfn); #ifdef CONFIG_X86_32 /* * Don't use a large page for the first 2/4MB of memory @@ -218,59 +218,59 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, * slowdowns. */ if (pos == 0) - end_pfn = PMD_SIZE >> PAGE_SHIFT; + end_pfn = PFN_DOWN(PMD_SIZE); else - end_pfn = round_up(pos, PMD_SIZE) >> PAGE_SHIFT; + end_pfn = PFN_DOWN(round_up(pos, PMD_SIZE)); #else /* CONFIG_X86_64 */ - end_pfn = round_up(pos, PMD_SIZE) >> PAGE_SHIFT; + end_pfn = PFN_DOWN(round_up(pos, PMD_SIZE)); #endif - if (end_pfn > (end >> PAGE_SHIFT)) - end_pfn = end >> PAGE_SHIFT; + if (end_pfn > PFN_DOWN(end)) + end_pfn = PFN_DOWN(end); if (start_pfn < end_pfn) { nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, 0); - pos = end_pfn << PAGE_SHIFT; + pos = PFN_PHYS(end_pfn); } /* big page (2M) range */ - start_pfn = round_up(pos, PMD_SIZE) >> PAGE_SHIFT; + start_pfn = PFN_DOWN(round_up(pos, PMD_SIZE)); #ifdef CONFIG_X86_32 - end_pfn = round_down(end, PMD_SIZE) >> PAGE_SHIFT; + end_pfn = PFN_DOWN(round_down(end, PMD_SIZE)); #else /* CONFIG_X86_64 */ - end_pfn = round_up(pos, PUD_SIZE) >> PAGE_SHIFT; - if (end_pfn > (round_down(end, PMD_SIZE) >> PAGE_SHIFT)) - end_pfn = round_down(end, PMD_SIZE) >> PAGE_SHIFT; + end_pfn = PFN_DOWN(round_up(pos, PUD_SIZE)); + if (end_pfn > PFN_DOWN(round_down(end, PMD_SIZE))) + end_pfn = PFN_DOWN(round_down(end, PMD_SIZE)); #endif if (start_pfn < end_pfn) { nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, page_size_mask & (1<> PAGE_SHIFT; - end_pfn = round_down(end, PUD_SIZE) >> PAGE_SHIFT; + start_pfn = PFN_DOWN(round_up(pos, PUD_SIZE)); + end_pfn = PFN_DOWN(round_down(end, PUD_SIZE)); if (start_pfn < end_pfn) { nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, page_size_mask & ((1<> PAGE_SHIFT; - end_pfn = round_down(end, PMD_SIZE) >> PAGE_SHIFT; + start_pfn = PFN_DOWN(round_up(pos, PMD_SIZE)); + end_pfn = PFN_DOWN(round_down(end, PMD_SIZE)); if (start_pfn < end_pfn) { nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, page_size_mask & (1<>PAGE_SHIFT; - end_pfn = end>>PAGE_SHIFT; + start_pfn = PFN_DOWN(pos); + end_pfn = PFN_DOWN(end); nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, 0); /* try to merge same page size and continuous */ From 1829ae9ad7380bf17333ab9ad1610631d9cb8664 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:14 -0800 Subject: [PATCH 0045/4607] x86, mm: use pfn instead of pos in split_mem_range could save some bit shifting operations. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-38-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 1cca052b2cbd..4bf1c5374928 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -204,12 +204,11 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, unsigned long end) { unsigned long start_pfn, end_pfn; - unsigned long pos; + unsigned long pfn; int i; /* head if not big page alignment ? */ - start_pfn = PFN_DOWN(start); - pos = PFN_PHYS(start_pfn); + pfn = start_pfn = PFN_DOWN(start); #ifdef CONFIG_X86_32 /* * Don't use a large page for the first 2/4MB of memory @@ -217,26 +216,26 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, * and overlapping MTRRs into large pages can cause * slowdowns. */ - if (pos == 0) + if (pfn == 0) end_pfn = PFN_DOWN(PMD_SIZE); else - end_pfn = PFN_DOWN(round_up(pos, PMD_SIZE)); + end_pfn = round_up(pfn, PFN_DOWN(PMD_SIZE)); #else /* CONFIG_X86_64 */ - end_pfn = PFN_DOWN(round_up(pos, PMD_SIZE)); + end_pfn = round_up(pfn, PFN_DOWN(PMD_SIZE)); #endif if (end_pfn > PFN_DOWN(end)) end_pfn = PFN_DOWN(end); if (start_pfn < end_pfn) { nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, 0); - pos = PFN_PHYS(end_pfn); + pfn = end_pfn; } /* big page (2M) range */ - start_pfn = PFN_DOWN(round_up(pos, PMD_SIZE)); + start_pfn = round_up(pfn, PFN_DOWN(PMD_SIZE)); #ifdef CONFIG_X86_32 end_pfn = PFN_DOWN(round_down(end, PMD_SIZE)); #else /* CONFIG_X86_64 */ - end_pfn = PFN_DOWN(round_up(pos, PUD_SIZE)); + end_pfn = round_up(pfn, PFN_DOWN(PUD_SIZE)); if (end_pfn > PFN_DOWN(round_down(end, PMD_SIZE))) end_pfn = PFN_DOWN(round_down(end, PMD_SIZE)); #endif @@ -244,32 +243,32 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, if (start_pfn < end_pfn) { nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, page_size_mask & (1< Date: Fri, 16 Nov 2012 19:39:15 -0800 Subject: [PATCH 0046/4607] x86, mm: use limit_pfn for end pfn instead of shifting end to get that. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-39-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 4bf1c5374928..f410dc6f843e 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -203,10 +203,12 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, unsigned long start, unsigned long end) { - unsigned long start_pfn, end_pfn; + unsigned long start_pfn, end_pfn, limit_pfn; unsigned long pfn; int i; + limit_pfn = PFN_DOWN(end); + /* head if not big page alignment ? */ pfn = start_pfn = PFN_DOWN(start); #ifdef CONFIG_X86_32 @@ -223,8 +225,8 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, #else /* CONFIG_X86_64 */ end_pfn = round_up(pfn, PFN_DOWN(PMD_SIZE)); #endif - if (end_pfn > PFN_DOWN(end)) - end_pfn = PFN_DOWN(end); + if (end_pfn > limit_pfn) + end_pfn = limit_pfn; if (start_pfn < end_pfn) { nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, 0); pfn = end_pfn; @@ -233,11 +235,11 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, /* big page (2M) range */ start_pfn = round_up(pfn, PFN_DOWN(PMD_SIZE)); #ifdef CONFIG_X86_32 - end_pfn = PFN_DOWN(round_down(end, PMD_SIZE)); + end_pfn = round_down(limit_pfn, PFN_DOWN(PMD_SIZE)); #else /* CONFIG_X86_64 */ end_pfn = round_up(pfn, PFN_DOWN(PUD_SIZE)); - if (end_pfn > PFN_DOWN(round_down(end, PMD_SIZE))) - end_pfn = PFN_DOWN(round_down(end, PMD_SIZE)); + if (end_pfn > round_down(limit_pfn, PFN_DOWN(PMD_SIZE))) + end_pfn = round_down(limit_pfn, PFN_DOWN(PMD_SIZE)); #endif if (start_pfn < end_pfn) { @@ -249,7 +251,7 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, #ifdef CONFIG_X86_64 /* big page (1G) range */ start_pfn = round_up(pfn, PFN_DOWN(PUD_SIZE)); - end_pfn = PFN_DOWN(round_down(end, PUD_SIZE)); + end_pfn = round_down(limit_pfn, PFN_DOWN(PUD_SIZE)); if (start_pfn < end_pfn) { nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, page_size_mask & @@ -259,7 +261,7 @@ static int __meminit split_mem_range(struct map_range *mr, int nr_range, /* tail is not big page (1G) alignment */ start_pfn = round_up(pfn, PFN_DOWN(PMD_SIZE)); - end_pfn = PFN_DOWN(round_down(end, PMD_SIZE)); + end_pfn = round_down(limit_pfn, PFN_DOWN(PMD_SIZE)); if (start_pfn < end_pfn) { nr_range = save_mr(mr, nr_range, start_pfn, end_pfn, page_size_mask & (1< Date: Fri, 16 Nov 2012 19:39:16 -0800 Subject: [PATCH 0047/4607] x86, mm: Unifying after_bootmem for 32bit and 64bit after_bootmem has different meaning in 32bit and 64bit. 32bit: after bootmem is ready 64bit: after bootmem is distroyed Let's merget them make 32bit the same as 64bit. for 32bit, it is mixing alloc_bootmem_pages, and alloc_low_page under after_bootmem is set or not set. alloc_bootmem is just wrapper for memblock for x86. Now we have alloc_low_page() with memblock too. We can drop bootmem path now, and only alloc_low_page only. At the same time, we make alloc_low_page could handle real after_bootmem for 32bit, because alloc_bootmem_pages could fallback to use slab too. At last move after_bootmem set position for 32bit the same as 64bit. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-40-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 2 -- arch/x86/mm/init_32.c | 21 ++++----------------- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index f410dc6f843e..2a27e5ac1e3c 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -39,7 +39,6 @@ __ref void *alloc_low_pages(unsigned int num) unsigned long pfn; int i; -#ifdef CONFIG_X86_64 if (after_bootmem) { unsigned int order; @@ -47,7 +46,6 @@ __ref void *alloc_low_pages(unsigned int num) return (void *)__get_free_pages(GFP_ATOMIC | __GFP_NOTRACK | __GFP_ZERO, order); } -#endif if ((pgt_buf_end + num) >= pgt_buf_top) { unsigned long ret; diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 19ef9f018012..f4fc4a28393a 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -73,10 +73,7 @@ static pmd_t * __init one_md_table_init(pgd_t *pgd) #ifdef CONFIG_X86_PAE if (!(pgd_val(*pgd) & _PAGE_PRESENT)) { - if (after_bootmem) - pmd_table = (pmd_t *)alloc_bootmem_pages(PAGE_SIZE); - else - pmd_table = (pmd_t *)alloc_low_page(); + pmd_table = (pmd_t *)alloc_low_page(); paravirt_alloc_pmd(&init_mm, __pa(pmd_table) >> PAGE_SHIFT); set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT)); pud = pud_offset(pgd, 0); @@ -98,17 +95,7 @@ static pmd_t * __init one_md_table_init(pgd_t *pgd) static pte_t * __init one_page_table_init(pmd_t *pmd) { if (!(pmd_val(*pmd) & _PAGE_PRESENT)) { - pte_t *page_table = NULL; - - if (after_bootmem) { -#if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KMEMCHECK) - page_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE); -#endif - if (!page_table) - page_table = - (pte_t *)alloc_bootmem_pages(PAGE_SIZE); - } else - page_table = (pte_t *)alloc_low_page(); + pte_t *page_table = (pte_t *)alloc_low_page(); paravirt_alloc_pte(&init_mm, __pa(page_table) >> PAGE_SHIFT); set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); @@ -708,8 +695,6 @@ void __init setup_bootmem_allocator(void) printk(KERN_INFO " mapped low ram: 0 - %08lx\n", max_pfn_mapped< Date: Fri, 16 Nov 2012 19:39:17 -0800 Subject: [PATCH 0048/4607] x86, mm: Move after_bootmem to mm_internel.h it is only used in arch/x86/mm/init*.c Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-41-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/mm_internal.h | 2 ++ include/linux/mm.h | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/mm/mm_internal.h b/arch/x86/mm/mm_internal.h index dc79ac121774..6b563a118891 100644 --- a/arch/x86/mm/mm_internal.h +++ b/arch/x86/mm/mm_internal.h @@ -14,4 +14,6 @@ unsigned long kernel_physical_mapping_init(unsigned long start, unsigned long page_size_mask); void zone_sizes_init(void); +extern int after_bootmem; + #endif /* __X86_MM_INTERNAL_H */ diff --git a/include/linux/mm.h b/include/linux/mm.h index bcaab4e6fe91..64d5271a3d36 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1355,7 +1355,6 @@ extern void __init mmap_init(void); extern void show_mem(unsigned int flags); extern void si_meminfo(struct sysinfo * val); extern void si_meminfo_node(struct sysinfo *val, int nid); -extern int after_bootmem; extern __printf(3, 4) void warn_alloc_failed(gfp_t gfp_mask, int order, const char *fmt, ...); From b8fd39c036ab982aa087b7ee671f86e2574d31f2 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:18 -0800 Subject: [PATCH 0049/4607] x86, mm: Use clamp_t() in init_range_memory_mapping save some lines, and make code more readable. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-42-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/mm/init.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 2a27e5ac1e3c..6f85de8a1f28 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -357,31 +357,20 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, * would have hole in the middle or ends, and only ram parts will be mapped. */ static unsigned long __init init_range_memory_mapping( - unsigned long range_start, - unsigned long range_end) + unsigned long r_start, + unsigned long r_end) { unsigned long start_pfn, end_pfn; unsigned long mapped_ram_size = 0; int i; for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, NULL) { - u64 start = (u64)start_pfn << PAGE_SHIFT; - u64 end = (u64)end_pfn << PAGE_SHIFT; - - if (end <= range_start) + u64 start = clamp_val(PFN_PHYS(start_pfn), r_start, r_end); + u64 end = clamp_val(PFN_PHYS(end_pfn), r_start, r_end); + if (start >= end) continue; - if (start < range_start) - start = range_start; - - if (start >= range_end) - continue; - - if (end > range_end) - end = range_end; - init_memory_mapping(start, end); - mapped_ram_size += end - start; } From 94b43c3d86dddf95064fc83e9087448b35f985ff Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:19 -0800 Subject: [PATCH 0050/4607] x86, mm: kill numa_free_all_bootmem() Now NO_BOOTMEM version free_all_bootmem_node() does not really do free_bootmem at all, and it only call register_page_bootmem_info_node instead. That is confusing, try to kill that free_all_bootmem_node(). Before that, this patch will remove numa_free_all_bootmem(). That function could be replaced with register_page_bootmem_info() and free_all_bootmem(); Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-43-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/numa_64.h | 2 -- arch/x86/mm/init_64.c | 15 +++++++++++---- arch/x86/mm/numa_64.c | 13 ------------- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/arch/x86/include/asm/numa_64.h b/arch/x86/include/asm/numa_64.h index 0c05f7ae46e8..fe4d2d410ad2 100644 --- a/arch/x86/include/asm/numa_64.h +++ b/arch/x86/include/asm/numa_64.h @@ -1,6 +1,4 @@ #ifndef _ASM_X86_NUMA_64_H #define _ASM_X86_NUMA_64_H -extern unsigned long numa_free_all_bootmem(void); - #endif /* _ASM_X86_NUMA_64_H */ diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 1d53defc99c9..41785305f645 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -629,6 +629,16 @@ EXPORT_SYMBOL_GPL(arch_add_memory); static struct kcore_list kcore_vsyscall; +static void __init register_page_bootmem_info(void) +{ +#ifdef CONFIG_NUMA + int i; + + for_each_online_node(i) + register_page_bootmem_info_node(NODE_DATA(i)); +#endif +} + void __init mem_init(void) { long codesize, reservedpages, datasize, initsize; @@ -641,11 +651,8 @@ void __init mem_init(void) reservedpages = 0; /* this will put all low memory onto the freelists */ -#ifdef CONFIG_NUMA - totalram_pages = numa_free_all_bootmem(); -#else + register_page_bootmem_info(); totalram_pages = free_all_bootmem(); -#endif absent_pages = absent_pages_in_range(0, max_pfn); reservedpages = max_pfn - totalram_pages - absent_pages; diff --git a/arch/x86/mm/numa_64.c b/arch/x86/mm/numa_64.c index 92e27119ee1a..9405ffc91502 100644 --- a/arch/x86/mm/numa_64.c +++ b/arch/x86/mm/numa_64.c @@ -10,16 +10,3 @@ void __init initmem_init(void) { x86_numa_init(); } - -unsigned long __init numa_free_all_bootmem(void) -{ - unsigned long pages = 0; - int i; - - for_each_online_node(i) - pages += free_all_bootmem_node(NODE_DATA(i)); - - pages += free_low_memory_core_early(MAX_NUMNODES); - - return pages; -} From c074eaac2ab264c94520efff7e896b771de885ae Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:20 -0800 Subject: [PATCH 0051/4607] x86, mm: kill numa_64.h Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-44-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/numa.h | 2 -- arch/x86/include/asm/numa_64.h | 4 ---- arch/x86/kernel/acpi/boot.c | 1 - arch/x86/kernel/cpu/amd.c | 1 - arch/x86/kernel/cpu/intel.c | 1 - arch/x86/kernel/setup.c | 3 --- 6 files changed, 12 deletions(-) delete mode 100644 arch/x86/include/asm/numa_64.h diff --git a/arch/x86/include/asm/numa.h b/arch/x86/include/asm/numa.h index 49119fcea2dc..52560a2038e1 100644 --- a/arch/x86/include/asm/numa.h +++ b/arch/x86/include/asm/numa.h @@ -54,8 +54,6 @@ static inline int numa_cpu_node(int cpu) #ifdef CONFIG_X86_32 # include -#else -# include #endif #ifdef CONFIG_NUMA diff --git a/arch/x86/include/asm/numa_64.h b/arch/x86/include/asm/numa_64.h deleted file mode 100644 index fe4d2d410ad2..000000000000 --- a/arch/x86/include/asm/numa_64.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef _ASM_X86_NUMA_64_H -#define _ASM_X86_NUMA_64_H - -#endif /* _ASM_X86_NUMA_64_H */ diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index e651f7a589ac..4b23aa18518d 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -51,7 +51,6 @@ EXPORT_SYMBOL(acpi_disabled); #ifdef CONFIG_X86_64 # include -# include #endif /* X86 */ #define BAD_MADT_ENTRY(entry, end) ( \ diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 9619ba6528ca..913f94f9e8d9 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -12,7 +12,6 @@ #include #ifdef CONFIG_X86_64 -# include # include # include #endif diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index 198e019a531a..3b547cc4bd03 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -17,7 +17,6 @@ #ifdef CONFIG_X86_64 #include -#include #endif #include "cpu.h" diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 85b62f1c8071..6d29d1fcf068 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -108,9 +108,6 @@ #include #include #include -#ifdef CONFIG_X86_64 -#include -#endif #include #include #include From 961f8fa04b1c3dfe35c2d3ee7ccba28f9b0e0e09 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:21 -0800 Subject: [PATCH 0052/4607] sparc, mm: Remove calling of free_all_bootmem_node() Now NO_BOOTMEM version free_all_bootmem_node() does not really do free_bootmem at all, and it only call register_page_bootmem_info_node instead. That is confusing, try to kill that free_all_bootmem_node(). Before that, this patch will remove calling of free_all_bootmem_node() We add register_page_bootmem_info() to call register_page_bootmem_info_node directly. Also could use free_all_bootmem() for numa case, and it is just the same as free_low_memory_core_early(). Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-45-git-send-email-yinghai@kernel.org Cc: "David S. Miller" Cc: Andrew Morton Cc: sparclinux@vger.kernel.org Acked-by: "David S. Miller" Signed-off-by: H. Peter Anvin --- arch/sparc/mm/init_64.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index 9e28a118e6a4..b24bac238e34 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c @@ -2021,6 +2021,16 @@ static void __init patch_tlb_miss_handler_bitmap(void) flushi(&valid_addr_bitmap_insn[0]); } +static void __init register_page_bootmem_info(void) +{ +#ifdef CONFIG_NEED_MULTIPLE_NODES + int i; + + for_each_online_node(i) + if (NODE_DATA(i)->node_spanned_pages) + register_page_bootmem_info_node(NODE_DATA(i)); +#endif +} void __init mem_init(void) { unsigned long codepages, datapages, initpages; @@ -2038,20 +2048,8 @@ void __init mem_init(void) high_memory = __va(last_valid_pfn << PAGE_SHIFT); -#ifdef CONFIG_NEED_MULTIPLE_NODES - { - int i; - for_each_online_node(i) { - if (NODE_DATA(i)->node_spanned_pages != 0) { - totalram_pages += - free_all_bootmem_node(NODE_DATA(i)); - } - } - totalram_pages += free_low_memory_core_early(MAX_NUMNODES); - } -#else + register_page_bootmem_info(); totalram_pages = free_all_bootmem(); -#endif /* We subtract one to account for the mem_map_zero page * allocated below. From 600cc5b7f6371706679490d7ee108015ae57ac2f Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:22 -0800 Subject: [PATCH 0053/4607] mm: Kill NO_BOOTMEM version free_all_bootmem_node() Now NO_BOOTMEM version free_all_bootmem_node() does not really do free_bootmem at all, and it only call register_page_bootmem_info_node for online nodes instead. That is confusing. We can kill that free_all_bootmem_node(), after we kill two callings in x86 and sparc. Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-46-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- mm/nobootmem.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/mm/nobootmem.c b/mm/nobootmem.c index bd82f6b31411..ecc2f13d557d 100644 --- a/mm/nobootmem.c +++ b/mm/nobootmem.c @@ -137,20 +137,6 @@ unsigned long __init free_low_memory_core_early(int nodeid) return count; } -/** - * free_all_bootmem_node - release a node's free pages to the buddy allocator - * @pgdat: node to be released - * - * Returns the number of pages actually released. - */ -unsigned long __init free_all_bootmem_node(pg_data_t *pgdat) -{ - register_page_bootmem_info_node(pgdat); - - /* free_low_memory_core_early(MAX_NUMNODES) will be called later */ - return 0; -} - /** * free_all_bootmem - release free pages to the buddy allocator * From 9710f581bb4c35589ac046b0cfc0deb7f369fc85 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 16 Nov 2012 19:39:23 -0800 Subject: [PATCH 0054/4607] x86, mm: Let "memmap=" take more entries one time Current "memmap=" only can take one entry every time. when we have more entries, we have to use memmap= for each of them. For pxe booting, we have command line length limitation, those extra "memmap=" would waste too much space. This patch make memmap= could take several entries one time, and those entries will be split with ',' Signed-off-by: Yinghai Lu Link: http://lkml.kernel.org/r/1353123563-3103-47-git-send-email-yinghai@kernel.org Signed-off-by: H. Peter Anvin --- arch/x86/kernel/e820.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index df06ade26bef..d32abeabbda5 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -835,7 +835,7 @@ static int __init parse_memopt(char *p) } early_param("mem", parse_memopt); -static int __init parse_memmap_opt(char *p) +static int __init parse_memmap_one(char *p) { char *oldp; u64 start_at, mem_size; @@ -877,6 +877,20 @@ static int __init parse_memmap_opt(char *p) return *p == '\0' ? 0 : -EINVAL; } +static int __init parse_memmap_opt(char *str) +{ + while (str) { + char *k = strchr(str, ','); + + if (k) + *k++ = 0; + + parse_memmap_one(str); + str = k; + } + + return 0; +} early_param("memmap", parse_memmap_opt); void __init finish_e820_parsing(void) From 17b14ca25e9cd6c5cd7605941f6120e405a84f8b Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Mon, 19 Nov 2012 15:37:46 +0100 Subject: [PATCH 0055/4607] netfilter: ipset: Fix range bug in hash:ip,port,net Due to the missing ininitalization at adding/deleting entries, when a plain_ip,port,net element was the object, multiple elements were added/deleted instead. The bug came from the missing dangling default initialization. The error-prone default initialization is corrected in all hash:* types. --- net/netfilter/ipset/ip_set_hash_ip.c | 4 ++-- net/netfilter/ipset/ip_set_hash_ipport.c | 7 +++---- net/netfilter/ipset/ip_set_hash_ipportip.c | 7 +++---- net/netfilter/ipset/ip_set_hash_ipportnet.c | 7 +++++-- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index ec3dba5dcd62..5c0b78528e55 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -173,6 +173,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], return adtfn(set, &nip, timeout, flags); } + ip_to = ip; if (tb[IPSET_ATTR_IP_TO]) { ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); if (ret) @@ -185,8 +186,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], if (!cidr || cidr > 32) return -IPSET_ERR_INVALID_CIDR; ip_set_mask_from_to(ip, ip_to, cidr); - } else - ip_to = ip; + } hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1); diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index 0171f7502fa5..6283351f4eeb 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -162,7 +162,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport4_elem data = { }; - u32 ip, ip_to = 0, p = 0, port, port_to; + u32 ip, ip_to, p = 0, port, port_to; u32 timeout = h->timeout; bool with_ports = false; int ret; @@ -210,7 +210,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], return ip_set_eexist(ret, flags) ? 0 : ret; } - ip = ntohl(data.ip); + ip_to = ip = ntohl(data.ip); if (tb[IPSET_ATTR_IP_TO]) { ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); if (ret) @@ -223,8 +223,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], if (!cidr || cidr > 32) return -IPSET_ERR_INVALID_CIDR; ip_set_mask_from_to(ip, ip_to, cidr); - } else - ip_to = ip; + } port_to = port = ntohs(data.port); if (with_ports && tb[IPSET_ATTR_PORT_TO]) { diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index 6344ef551ec8..6a21271c8d5a 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -166,7 +166,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip4_elem data = { }; - u32 ip, ip_to = 0, p = 0, port, port_to; + u32 ip, ip_to, p = 0, port, port_to; u32 timeout = h->timeout; bool with_ports = false; int ret; @@ -218,7 +218,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], return ip_set_eexist(ret, flags) ? 0 : ret; } - ip = ntohl(data.ip); + ip_to = ip = ntohl(data.ip); if (tb[IPSET_ATTR_IP_TO]) { ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); if (ret) @@ -231,8 +231,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], if (!cidr || cidr > 32) return -IPSET_ERR_INVALID_CIDR; ip_set_mask_from_to(ip, ip_to, cidr); - } else - ip_to = ip; + } port_to = port = ntohs(data.port); if (with_ports && tb[IPSET_ATTR_PORT_TO]) { diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index cb71f9a774e7..2d5cd4ee30eb 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -215,8 +215,8 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], const struct ip_set_hash *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem data = { .cidr = HOST_MASK - 1 }; - u32 ip, ip_to = 0, p = 0, port, port_to; - u32 ip2_from = 0, ip2_to, ip2_last, ip2; + u32 ip, ip_to, p = 0, port, port_to; + u32 ip2_from, ip2_to, ip2_last, ip2; u32 timeout = h->timeout; bool with_ports = false; u8 cidr; @@ -286,6 +286,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], return ip_set_eexist(ret, flags) ? 0 : ret; } + ip_to = ip; if (tb[IPSET_ATTR_IP_TO]) { ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); if (ret) @@ -306,6 +307,8 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], if (port > port_to) swap(port, port_to); } + + ip2_to = ip2_from; if (tb[IPSET_ATTR_IP2_TO]) { ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); if (ret) From bbee3aec3472fc2ca10b6b1020aec84567ea25ce Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Mon, 19 Nov 2012 10:31:37 -0800 Subject: [PATCH 0056/4607] x86: Fix warning about cast from pointer to integer of different size This patch fixes a warning reported by the kbuild test robot where we were casting a pointer to a physical address which represents an integer of a different size. Per the suggestion of Peter Anvin I am replacing it and one other spot where I made a similar cast with an unsigned long. Signed-off-by: Alexander Duyck Link: http://lkml.kernel.org/r/20121119182927.3655.7641.stgit@ahduyck-cp1.jf.intel.com Signed-off-by: H. Peter Anvin --- arch/x86/kernel/head32.c | 2 +- arch/x86/kernel/head64.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c index f15db0c40713..e17554832991 100644 --- a/arch/x86/kernel/head32.c +++ b/arch/x86/kernel/head32.c @@ -31,7 +31,7 @@ static void __init i386_default_early_setup(void) void __init i386_start_kernel(void) { memblock_reserve(__pa_symbol(_text), - (phys_addr_t)__bss_stop - (phys_addr_t)_text); + (unsigned long)__bss_stop - (unsigned long)_text); #ifdef CONFIG_BLK_DEV_INITRD /* Reserve INITRD */ diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 42f5df134341..7b215a50ec1e 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -98,7 +98,7 @@ void __init x86_64_start_reservations(char *real_mode_data) copy_bootdata(__va(real_mode_data)); memblock_reserve(__pa_symbol(_text), - (phys_addr_t)__bss_stop - (phys_addr_t)_text); + (unsigned long)__bss_stop - (unsigned long)_text); #ifdef CONFIG_BLK_DEV_INITRD /* Reserve INITRD */ From 7d5bb966290d71d9dfe69a3ed0c31b26bf9afc63 Mon Sep 17 00:00:00 2001 From: Krzysztof Mazur Date: Mon, 8 Oct 2012 18:18:22 +0200 Subject: [PATCH 0057/4607] menuconfig: fix extended colors ncurses support The ncurses library allows for extended colors. The support for extended colors support depends on wide-character support. ncurses headers enable extended colors (NCURSES_EXT_COLORS) only when wide-character support is enabled (NCURSES_WIDECHAR). The "make menuconfig" uses wide-character ncursesw library, which can be compiled with wide-character support, but does not define NCURSES_WIDECHAR and it's using headers without wide-character (and extended colors) support. This fixes problems with colors on systems with enabled extended colors (like PLD Linux). Without this patch "make menuconfig" is hard to use. Signed-off-by: Krzysztof Mazur Signed-off-by: Michal Marek --- scripts/kconfig/lxdialog/check-lxdialog.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/kconfig/lxdialog/check-lxdialog.sh b/scripts/kconfig/lxdialog/check-lxdialog.sh index c8e8a7154753..80788137c670 100644 --- a/scripts/kconfig/lxdialog/check-lxdialog.sh +++ b/scripts/kconfig/lxdialog/check-lxdialog.sh @@ -21,6 +21,7 @@ ccflags() { if [ -f /usr/include/ncursesw/curses.h ]; then echo '-I/usr/include/ncursesw -DCURSES_LOC=""' + echo ' -DNCURSES_WIDECHAR=1' elif [ -f /usr/include/ncurses/ncurses.h ]; then echo '-I/usr/include/ncurses -DCURSES_LOC=""' elif [ -f /usr/include/ncurses/curses.h ]; then From 337a275d03e0b900dc8ac3ab5583d18099fedae6 Mon Sep 17 00:00:00 2001 From: "Yann E. MORIN" Date: Sat, 20 Oct 2012 01:06:23 +0200 Subject: [PATCH 0058/4607] kconfig: remove CONFIG_ from string constants Having the CONFIG_ prefix in string constants gets in the way of using a run-time-defined CONFIG_ prefix. Fix that by using temp growable strings (gstr) in which we printf the text. Signed-off-by: "Yann E. MORIN" Signed-off-by: Michal Marek --- scripts/kconfig/mconf.c | 10 ++++++++-- scripts/kconfig/nconf.c | 11 +++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index 48f67448af7b..5f29618d1845 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -348,15 +348,19 @@ static void search_conf(void) { struct symbol **sym_arr; struct gstr res; + struct gstr title; char *dialog_input; int dres, vscroll = 0, hscroll = 0; bool again; + title = str_new(); + str_printf( &title, _("Enter %s (sub)string to search for " + "(with or without \"%s\")"), CONFIG_, CONFIG_); + again: dialog_clear(); dres = dialog_inputbox(_("Search Configuration Parameter"), - _("Enter " CONFIG_ " (sub)string to search for " - "(with or without \"" CONFIG_ "\")"), + str_get(&title), 10, 75, ""); switch (dres) { case 0: @@ -365,6 +369,7 @@ again: show_helptext(_("Search Configuration"), search_help); goto again; default: + str_free(&title); return; } @@ -398,6 +403,7 @@ again: str_free(&res); } while (again); free(sym_arr); + str_free(&title); } static void build_conf(struct menu *menu) diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index 87d4b15da951..261f926d8f4b 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -696,13 +696,18 @@ static void search_conf(void) { struct symbol **sym_arr; struct gstr res; + struct gstr title; char *dialog_input; int dres; + + title = str_new(); + str_printf( &title, _("Enter %s (sub)string to search for " + "(with or without \"%s\")"), CONFIG_, CONFIG_); + again: dres = dialog_inputbox(main_window, _("Search Configuration Parameter"), - _("Enter " CONFIG_ " (sub)string to search for " - "(with or without \"" CONFIG_ "\")"), + str_get(&title), "", &dialog_input_result, &dialog_input_result_len); switch (dres) { case 0: @@ -712,6 +717,7 @@ again: _("Search Configuration"), search_help); goto again; default: + str_free(&title); return; } @@ -726,6 +732,7 @@ again: show_scroll_win(main_window, _("Search Results"), str_get(&res)); str_free(&res); + str_free(&title); } From b341f7882f04cc70b7437583f012a8d93fbfb00e Mon Sep 17 00:00:00 2001 From: "Yann E. MORIN" Date: Sat, 20 Oct 2012 01:06:24 +0200 Subject: [PATCH 0059/4607] kconfig: add a function to get the CONFIG_ prefix Currently, we get the CONFIG_ prefix via the CONFIG_ macro, which means the CONFIG_ prefix is hard-coded at compile time. This goes against having a run-time defined CONFIG_ prefix. Add a function that returns the CONFIG_ prefix to use (but keep the current hard-coded behavior, to be changed in a later patch). To avoid touching all the code that uses the CONFIG_ macro, we just undef it, and define it to be a call to the function. Signed-off-by: "Yann E. MORIN" Signed-off-by: Michal Marek --- scripts/kconfig/lkc.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index c18f2bd9c095..7aa9db0b2a77 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -39,6 +39,12 @@ extern "C" { #ifndef CONFIG_ #define CONFIG_ "CONFIG_" #endif +static inline const char *CONFIG_prefix(void) +{ + return CONFIG_; +} +#undef CONFIG_ +#define CONFIG_ CONFIG_prefix() #define TF_COMMAND 0x0001 #define TF_PARAM 0x0002 From 9a926d4354d84e424e738a6d401328340400331b Mon Sep 17 00:00:00 2001 From: "Yann E. MORIN" Date: Sat, 20 Oct 2012 01:06:25 +0200 Subject: [PATCH 0060/4607] kconfig: get CONFIG_ prefix from the environment Currently, the CONFIG_ prefix is hard-coded in the kconfig frontends executables. This means that two projects that use kconfig with different prefixes can not share the same kconfig frontends. Instead of hard-coding the prefix in the frontends, get it from the environment, and revert back to hard-coded value if not found. Signed-off-by: "Yann E. MORIN" Signed-off-by: Michal Marek --- scripts/kconfig/gconf.c | 2 +- scripts/kconfig/lkc.h | 2 +- scripts/kconfig/nconf.c | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index adc230638c5b..f2bee70e26f4 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -10,6 +10,7 @@ # include #endif +#include #include "lkc.h" #include "images.c" @@ -22,7 +23,6 @@ #include #include #include -#include //#define DEBUG diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 7aa9db0b2a77..7577a7fbb405 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -41,7 +41,7 @@ extern "C" { #endif static inline const char *CONFIG_prefix(void) { - return CONFIG_; + return getenv( "CONFIG_" ) ?: CONFIG_; } #undef CONFIG_ #define CONFIG_ CONFIG_prefix() diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index 261f926d8f4b..ce93e879a29c 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -7,6 +7,7 @@ */ #define _GNU_SOURCE #include +#include #include "lkc.h" #include "nconf.h" From 177acf78468bf5c359bcb8823ee3bd48b04b8380 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 6 Nov 2012 14:32:08 +0000 Subject: [PATCH 0061/4607] kconfig: Fix malloc handling in conf tools (and get them out of the noise in the audit work) Signed-off-by: Alan Cox Signed-off-by: Michal Marek --- scripts/kconfig/expr.c | 10 +++++----- scripts/kconfig/lkc.h | 2 ++ scripts/kconfig/menu.c | 4 ++-- scripts/kconfig/symbol.c | 12 ++++++------ scripts/kconfig/util.c | 23 +++++++++++++++++++++-- scripts/kconfig/zconf.l | 8 ++++---- 6 files changed, 40 insertions(+), 19 deletions(-) diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index 290ce41f8ba4..d6626521f9b9 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -13,7 +13,7 @@ struct expr *expr_alloc_symbol(struct symbol *sym) { - struct expr *e = calloc(1, sizeof(*e)); + struct expr *e = xcalloc(1, sizeof(*e)); e->type = E_SYMBOL; e->left.sym = sym; return e; @@ -21,7 +21,7 @@ struct expr *expr_alloc_symbol(struct symbol *sym) struct expr *expr_alloc_one(enum expr_type type, struct expr *ce) { - struct expr *e = calloc(1, sizeof(*e)); + struct expr *e = xcalloc(1, sizeof(*e)); e->type = type; e->left.expr = ce; return e; @@ -29,7 +29,7 @@ struct expr *expr_alloc_one(enum expr_type type, struct expr *ce) struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2) { - struct expr *e = calloc(1, sizeof(*e)); + struct expr *e = xcalloc(1, sizeof(*e)); e->type = type; e->left.expr = e1; e->right.expr = e2; @@ -38,7 +38,7 @@ struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2) { - struct expr *e = calloc(1, sizeof(*e)); + struct expr *e = xcalloc(1, sizeof(*e)); e->type = type; e->left.sym = s1; e->right.sym = s2; @@ -66,7 +66,7 @@ struct expr *expr_copy(const struct expr *org) if (!org) return NULL; - e = malloc(sizeof(*org)); + e = xmalloc(sizeof(*org)); memcpy(e, org, sizeof(*org)); switch (org->type) { case E_SYMBOL: diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 7577a7fbb405..f8aee5fc6d5e 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -122,6 +122,8 @@ void menu_set_type(int type); /* util.c */ struct file *file_lookup(const char *name); int file_write_dep(const char *name); +void *xmalloc(size_t size); +void *xcalloc(size_t nmemb, size_t size); struct gstr { size_t len; diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index a3cade659f89..84a2ba2077aa 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -48,7 +48,7 @@ void menu_add_entry(struct symbol *sym) { struct menu *menu; - menu = malloc(sizeof(*menu)); + menu = xmalloc(sizeof(*menu)); memset(menu, 0, sizeof(*menu)); menu->sym = sym; menu->parent = current_menu; @@ -531,7 +531,7 @@ static void get_prompt_str(struct gstr *r, struct property *prop, location = menu; } if (head && location) { - jump = malloc(sizeof(struct jump_key)); + jump = xmalloc(sizeof(struct jump_key)); if (menu_is_visible(prop->menu)) { /* diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 22a3c400fc41..ecc5aa5f865d 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -656,11 +656,11 @@ bool sym_set_string_value(struct symbol *sym, const char *newval) size = strlen(newval) + 1; if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { size += 2; - sym->def[S_DEF_USER].val = val = malloc(size); + sym->def[S_DEF_USER].val = val = xmalloc(size); *val++ = '0'; *val++ = 'x'; } else if (!oldval || strcmp(oldval, newval)) - sym->def[S_DEF_USER].val = val = malloc(size); + sym->def[S_DEF_USER].val = val = xmalloc(size); else return true; @@ -812,7 +812,7 @@ struct symbol *sym_lookup(const char *name, int flags) hash = 0; } - symbol = malloc(sizeof(*symbol)); + symbol = xmalloc(sizeof(*symbol)); memset(symbol, 0, sizeof(*symbol)); symbol->name = new_name; symbol->type = S_UNKNOWN; @@ -863,7 +863,7 @@ const char *sym_expand_string_value(const char *in) size_t reslen; reslen = strlen(in) + 1; - res = malloc(reslen); + res = xmalloc(reslen); res[0] = '\0'; while ((src = strchr(in, '$'))) { @@ -921,7 +921,7 @@ const char *sym_escape_string_value(const char *in) p++; } - res = malloc(reslen); + res = xmalloc(reslen); res[0] = '\0'; strcat(res, "\""); @@ -1228,7 +1228,7 @@ struct property *prop_alloc(enum prop_type type, struct symbol *sym) struct property *prop; struct property **propp; - prop = malloc(sizeof(*prop)); + prop = xmalloc(sizeof(*prop)); memset(prop, 0, sizeof(*prop)); prop->type = type; prop->sym = sym; diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c index d0b8b2318e48..6e7fbf196809 100644 --- a/scripts/kconfig/util.c +++ b/scripts/kconfig/util.c @@ -23,7 +23,7 @@ struct file *file_lookup(const char *name) } } - file = malloc(sizeof(*file)); + file = xmalloc(sizeof(*file)); memset(file, 0, sizeof(*file)); file->name = file_name; file->next = file_list; @@ -81,7 +81,7 @@ int file_write_dep(const char *name) struct gstr str_new(void) { struct gstr gs; - gs.s = malloc(sizeof(char) * 64); + gs.s = xmalloc(sizeof(char) * 64); gs.len = 64; gs.max_width = 0; strcpy(gs.s, "\0"); @@ -138,3 +138,22 @@ const char *str_get(struct gstr *gs) return gs->s; } +void *xmalloc(size_t size) +{ + void *p = malloc(size); + if (p) + return p; + fprintf(stderr, "Out of memory.\n"); + exit(1); +} + +void *xcalloc(size_t nmemb, size_t size) +{ + void *p = calloc(nmemb, size); + if (p) + return p; + fprintf(stderr, "Out of memory.\n"); + exit(1); +} + + diff --git a/scripts/kconfig/zconf.l b/scripts/kconfig/zconf.l index 00f9d3a9cf8b..6555a475453b 100644 --- a/scripts/kconfig/zconf.l +++ b/scripts/kconfig/zconf.l @@ -40,7 +40,7 @@ static void zconf_endfile(void); static void new_string(void) { - text = malloc(START_STRSIZE); + text = xmalloc(START_STRSIZE); text_asize = START_STRSIZE; text_size = 0; *text = 0; @@ -62,7 +62,7 @@ static void append_string(const char *str, int size) static void alloc_string(const char *str, int size) { - text = malloc(size + 1); + text = xmalloc(size + 1); memcpy(text, str, size); text[size] = 0; } @@ -288,7 +288,7 @@ void zconf_initscan(const char *name) exit(1); } - current_buf = malloc(sizeof(*current_buf)); + current_buf = xmalloc(sizeof(*current_buf)); memset(current_buf, 0, sizeof(*current_buf)); current_file = file_lookup(name); @@ -299,7 +299,7 @@ void zconf_nextfile(const char *name) { struct file *iter; struct file *file = file_lookup(name); - struct buffer *buf = malloc(sizeof(*buf)); + struct buffer *buf = xmalloc(sizeof(*buf)); memset(buf, 0, sizeof(*buf)); current_buf->state = YY_CURRENT_BUFFER; From 527ffe5811e39f9997a08902628c35068a46a5b7 Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Tue, 20 Nov 2012 12:12:57 +0100 Subject: [PATCH 0062/4607] kconfig: Regenerate lexer Apply changes from commit 177acf78 (kconfig: Fix malloc handling in conf tools) to the _shipped file. Signed-off-by: Michal Marek --- scripts/kconfig/zconf.lex.c_shipped | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/kconfig/zconf.lex.c_shipped b/scripts/kconfig/zconf.lex.c_shipped index c32b1a49f5a3..a0521aa5974b 100644 --- a/scripts/kconfig/zconf.lex.c_shipped +++ b/scripts/kconfig/zconf.lex.c_shipped @@ -802,7 +802,7 @@ static void zconf_endfile(void); static void new_string(void) { - text = malloc(START_STRSIZE); + text = xmalloc(START_STRSIZE); text_asize = START_STRSIZE; text_size = 0; *text = 0; @@ -824,7 +824,7 @@ static void append_string(const char *str, int size) static void alloc_string(const char *str, int size) { - text = malloc(size + 1); + text = xmalloc(size + 1); memcpy(text, str, size); text[size] = 0; } @@ -2343,7 +2343,7 @@ void zconf_initscan(const char *name) exit(1); } - current_buf = malloc(sizeof(*current_buf)); + current_buf = xmalloc(sizeof(*current_buf)); memset(current_buf, 0, sizeof(*current_buf)); current_file = file_lookup(name); @@ -2354,7 +2354,7 @@ void zconf_nextfile(const char *name) { struct file *iter; struct file *file = file_lookup(name); - struct buffer *buf = malloc(sizeof(*buf)); + struct buffer *buf = xmalloc(sizeof(*buf)); memset(buf, 0, sizeof(*buf)); current_buf->state = YY_CURRENT_BUFFER; From 5e4bf1a55da976a5ed60901bb8801f1024ef9774 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 20 Nov 2012 13:02:51 +0100 Subject: [PATCH 0063/4607] x86/mm: Don't flush the TLB on #WP pmd fixups If we have a write protection #PF and fix up the pmd then the hugetlb code [the only user of pmdp_set_access_flags], in its do_huge_pmd_wp_page() page fault resolution function calls pmdp_set_access_flags() to mark the pmd permissive again, and flushes the TLB. This TLB flush is unnecessary: a flush on #PF is guaranteed on most (all?) x86 CPUs, and even in the worst-case we'll generate a spurious fault. So remove it. Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Cc: Paul Turner Cc: Lee Schermerhorn Cc: Andrea Arcangeli Cc: Rik van Riel Cc: Johannes Weiner Cc: Christoph Lameter Cc: Mel Gorman Cc: Hugh Dickins Link: http://lkml.kernel.org/r/20121120120251.GA15742@gmail.com Signed-off-by: Ingo Molnar --- arch/x86/mm/pgtable.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c index 8573b83a63d0..8a828d773e58 100644 --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c @@ -328,7 +328,12 @@ int pmdp_set_access_flags(struct vm_area_struct *vma, if (changed && dirty) { *pmdp = entry; pmd_update_defer(vma->vm_mm, address, pmdp); - flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE); + /* + * We had a write-protection fault here and changed the pmd + * to to more permissive. No need to flush the TLB for that, + * #PF is architecturally guaranteed to do that and in the + * worst-case we'll generate a spurious fault. + */ } return changed; From f930ddd0583c1a9e68d80a27d4e5077e795007b1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 21 Nov 2012 15:55:21 +0100 Subject: [PATCH 0064/4607] drm/i915: remove duplicate register #defines Somehow a chunk of unused register defines ended up in the middle of the PLL defines. They go back to the original kms merging. The only used #define is SR01, move it to the register name together with the other legacy vga stuff. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 97fbd9d1823b..462b50a3d88e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -142,6 +142,7 @@ #define VGA_MSR_CGA_MODE (1<<0) #define VGA_SR_INDEX 0x3c4 +#define SR01 1 #define VGA_SR_DATA 0x3c5 #define VGA_AR_INDEX 0x3c0 @@ -938,23 +939,6 @@ #define DPLL_LOCK_VLV (1<<15) #define DPLL_INTEGRATED_CLOCK_VLV (1<<13) -#define SRX_INDEX 0x3c4 -#define SRX_DATA 0x3c5 -#define SR01 1 -#define SR01_SCREEN_OFF (1<<5) - -#define PPCR 0x61204 -#define PPCR_ON (1<<0) - -#define DVOB 0x61140 -#define DVOB_ON (1<<31) -#define DVOC 0x61160 -#define DVOC_ON (1<<31) -#define LVDS 0x61180 -#define LVDS_ON (1<<31) - -/* Scratch pad debug 0 reg: - */ #define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000 /* * The i830 generation, in LVDS mode, defines P1 as the bit number set within From ca9c46c5c77987acf1bf7137bf85e9221bc459ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 27 Nov 2012 20:34:58 +0200 Subject: [PATCH 0065/4607] drm/i915: Kill i915_gem_execbuffer_wait_for_flips() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As per Chris Wilson's suggestion make i915_gem_execbuffer_wait_for_flips() go away. This was used to stall the GPU ring while there are pending page flips involving the relevant BO. Ie. while the BO is still being scanned out by the display controller. The recommended alternative is to use the page flip events to wait for the page flips to fully complete before reusing the BO of the old front buffer. Or use more buffers. Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Acked-by: Kristian Høgsberg Acked-by: Jesse Barnes [danvet: don't remove obj->pending_flips, still required due to reorder patches.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 42 ---------------------- drivers/gpu/drm/i915/intel_display.c | 7 ---- 2 files changed, 49 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index ee8f97f0539e..802d925b2b57 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -601,45 +601,12 @@ err: return ret; } -static int -i915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips) -{ - u32 plane, flip_mask; - int ret; - - /* Check for any pending flips. As we only maintain a flip queue depth - * of 1, we can simply insert a WAIT for the next display flip prior - * to executing the batch and avoid stalling the CPU. - */ - - for (plane = 0; flips >> plane; plane++) { - if (((flips >> plane) & 1) == 0) - continue; - - if (plane) - flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; - else - flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; - - ret = intel_ring_begin(ring, 2); - if (ret) - return ret; - - intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); - intel_ring_emit(ring, MI_NOOP); - intel_ring_advance(ring); - } - - return 0; -} - static int i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, struct list_head *objects) { struct drm_i915_gem_object *obj; uint32_t flush_domains = 0; - uint32_t flips = 0; int ret; list_for_each_entry(obj, objects, exec_list) { @@ -650,18 +617,9 @@ i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, if (obj->base.write_domain & I915_GEM_DOMAIN_CPU) i915_gem_clflush_object(obj); - if (obj->base.pending_write_domain) - flips |= atomic_read(&obj->pending_flip); - flush_domains |= obj->base.write_domain; } - if (flips) { - ret = i915_gem_execbuffer_wait_for_flips(ring, flips); - if (ret) - return ret; - } - if (flush_domains & I915_GEM_DOMAIN_CPU) i915_gem_chipset_flush(ring->dev); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 78d12c471472..8cdf3a1226dc 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6945,8 +6945,6 @@ static void do_intel_finish_page_flip(struct drm_device *dev, obj = work->old_fb_obj; - atomic_clear_mask(1 << intel_crtc->plane, - &obj->pending_flip.counter); wake_up(&dev_priv->pending_flip_queue); queue_work(dev_priv->wq, &work->work); @@ -7292,10 +7290,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, work->enable_stall_check = true; - /* Block clients from rendering to the new back buffer until - * the flip occurs and the object is no longer visible. - */ - atomic_add(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip); atomic_inc(&intel_crtc->unpin_work_count); ret = dev_priv->display.queue_flip(dev, crtc, fb, obj); @@ -7312,7 +7306,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, cleanup_pending: atomic_dec(&intel_crtc->unpin_work_count); - atomic_sub(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip); drm_gem_object_unreference(&work->old_fb_obj->base); drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); From 04b97b3422c3403696f3bcb52140b7ebeaee9d3c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 27 Nov 2012 17:06:53 +0000 Subject: [PATCH 0066/4607] drm/i915/debugfs: Prune a couple of superfluous leading zeros from bo domains As we do not have any domains occupying the high bits, there is no point in always printing the leading 00. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 8afc0dd7de64..62619e39533f 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -102,7 +102,7 @@ static const char *cache_level_str(int type) static void describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) { - seq_printf(m, "%p: %s%s %8zdKiB %04x %04x %d %d %d%s%s%s", + seq_printf(m, "%p: %s%s %8zdKiB %02x %02x %d %d %d%s%s%s", &obj->base, get_pin_flag(obj), get_tiling_flag(obj), @@ -608,7 +608,7 @@ static void print_error_buffers(struct seq_file *m, seq_printf(m, "%s [%d]:\n", name, count); while (count--) { - seq_printf(m, " %08x %8u %04x %04x %x %x%s%s%s%s%s%s%s", + seq_printf(m, " %08x %8u %02x %02x %x %x%s%s%s%s%s%s%s", err->gtt_offset, err->size, err->read_domains, From dafd226c4f54eded10ba43c37789a6aa20b59c32 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:07 +0100 Subject: [PATCH 0067/4607] drm/i915: add encoder->pre_pll_enable callback Currently we have two encoder specific bits in the common mode_set functions: - lvds pin pair enabling - dp m/n setting and computation Now the lvds stuff needs to happen before the pll is enabled. Since that is done in the crtc_mode_set functions, we need to add a new callback to be able to move them to the encoder code (where they belong). The dp m/n stuff is a giant mess anyway (since it also confuses itself with the fdi link m/n handling), so that needs to be handled separately. I think that we can move the pll enabling down quite a bit, which might allow us to eventually merge encoder->pre_enable with this new pre_pll_enable callback. But for now this will allow us to clean things up a bit. Note that vlv doesn't support lvds, hence we don't need to change anything in there. v2: Fixup commit message, both suggested from Paulo Zanoni. - dp m/n doesn't need to happen before pll enabling - lvds doesn't exist on vlv, hence no changes required in the vlv pll function. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 14 ++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 1 + 2 files changed, 15 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8cdf3a1226dc..55d3e3dbc137 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4464,6 +4464,7 @@ static void i9xx_update_pll(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *encoder; int pipe = intel_crtc->pipe; u32 dpll; bool is_sdvo; @@ -4532,6 +4533,10 @@ static void i9xx_update_pll(struct drm_crtc *crtc, POSTING_READ(DPLL(pipe)); udelay(150); + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); + /* The LVDS pin pair needs to be on before the DPLLs are enabled. * This is an exception to the general rule that mode_set doesn't turn * things on. @@ -4576,6 +4581,7 @@ static void i8xx_update_pll(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *encoder; int pipe = intel_crtc->pipe; u32 dpll; @@ -4609,6 +4615,10 @@ static void i8xx_update_pll(struct drm_crtc *crtc, POSTING_READ(DPLL(pipe)); udelay(150); + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); + /* The LVDS pin pair needs to be on before the DPLLs are enabled. * This is an exception to the general rule that mode_set doesn't turn * things on. @@ -5537,6 +5547,10 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(TRANSDPLINK_N1(pipe), 0); } + for_each_encoder_on_crtc(dev, crtc, encoder) + if (encoder->pre_pll_enable) + encoder->pre_pll_enable(encoder); + if (intel_crtc->pch_pll) { I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 522061ca0685..a74c572f1d5e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -153,6 +153,7 @@ struct intel_encoder { bool cloneable; bool connectors_active; void (*hot_plug)(struct intel_encoder *); + void (*pre_pll_enable)(struct intel_encoder *); void (*pre_enable)(struct intel_encoder *); void (*enable)(struct intel_encoder *); void (*disable)(struct intel_encoder *); From a210b028f07690c127733addbbe137e8f4cad30c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:08 +0100 Subject: [PATCH 0068/4607] drm/i915: replace ad-hoc dual-link lvds checks ... with is_dual_link_lvds introduced in commit b03543857fd75876b96e10d4320b775e95041bb7 Author: Takashi Iwai Date: Tue Mar 20 13:07:05 2012 +0100 drm/i915: Check VBIOS value for determining LVDS dual channel mode, too All these checks predate this commit and have simply been overlooked. Since we don't support switching between single-link and dual-link modes anyway, this different checks could at best only get in the way of refactorings, and in the worst case cause inconsistencies. v2: Update the comment, we now have a solid way to figure out whether we need dual-link lvds or not (falling back to vbt values as a last resort). We still don't know how to switch between dual-link and single link so leave that part intact. I'm not sure though whether switching between these two modes makes any sense - we always drive the panel at its fixed mode (with a fixed bpc) anyway ... Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 55d3e3dbc137..be19b6d1ca52 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -690,13 +690,11 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, intel_clock_t clock; int err = target; - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && - (I915_READ(LVDS)) != 0) { + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { /* - * For LVDS, if the panel is on, just rely on its current - * settings for dual-channel. We haven't figured out how to - * reliably set up different single/dual channel state, if we - * even can. + * For LVDS just rely on its current settings for dual-channel. + * We haven't figured out how to reliably set up different + * single/dual channel state, if we even can. */ if (is_dual_link_lvds(dev_priv, LVDS)) clock.p2 = limit->p2.p2_fast; @@ -766,8 +764,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, lvds_reg = PCH_LVDS; else lvds_reg = LVDS; - if ((I915_READ(lvds_reg) & LVDS_CLKB_POWER_MASK) == - LVDS_CLKB_POWER_UP) + if (is_dual_link_lvds(dev_priv, lvds_reg)) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; @@ -5359,7 +5356,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, if (is_lvds) { if ((intel_panel_use_ssc(dev_priv) && dev_priv->lvds_ssc_freq == 100) || - (I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) + is_dual_link_lvds(dev_priv, PCH_LVDS)) factor = 25; } else if (is_sdvo && is_tv) factor = 20; From 1974cad0ee4ce84e5cb792e49c4f0d9421e0312c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:09 +0100 Subject: [PATCH 0069/4607] drm/i915: move is_dual_link_lvds to intel_lvds.c Just a prep patch to make this a property of intel_lvds. Makes more sense, removes clutter from intel_display.c and eventually I want to move all the encoder special cases wrt clock handling to encoders anyway. v2: Add an intel_ prefixe to is_dual_link_lvds since it's non-static now. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 60 +++------------------------- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_lvds.c | 53 ++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index be19b6d1ca52..2705da329ddf 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -472,61 +472,14 @@ static void vlv_init_dpio(struct drm_device *dev) POSTING_READ(DPIO_CTL); } -static int intel_dual_link_lvds_callback(const struct dmi_system_id *id) -{ - DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident); - return 1; -} - -static const struct dmi_system_id intel_dual_link_lvds[] = { - { - .callback = intel_dual_link_lvds_callback, - .ident = "Apple MacBook Pro (Core i5/i7 Series)", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"), - }, - }, - { } /* terminating entry */ -}; - -static bool is_dual_link_lvds(struct drm_i915_private *dev_priv, - unsigned int reg) -{ - unsigned int val; - - /* use the module option value if specified */ - if (i915_lvds_channel_mode > 0) - return i915_lvds_channel_mode == 2; - - if (dmi_check_system(intel_dual_link_lvds)) - return true; - - if (dev_priv->lvds_val) - val = dev_priv->lvds_val; - else { - /* BIOS should set the proper LVDS register value at boot, but - * in reality, it doesn't set the value when the lid is closed; - * we need to check "the value to be set" in VBT when LVDS - * register is uninitialized. - */ - val = I915_READ(reg); - if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) - val = dev_priv->bios_lvds_val; - dev_priv->lvds_val = val; - } - return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; -} - static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, int refclk) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; const intel_limit_t *limit; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if (is_dual_link_lvds(dev_priv, PCH_LVDS)) { + if (intel_is_dual_link_lvds(dev)) { /* LVDS dual channel */ if (refclk == 100000) limit = &intel_limits_ironlake_dual_lvds_100m; @@ -550,11 +503,10 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc, static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; const intel_limit_t *limit; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { - if (is_dual_link_lvds(dev_priv, LVDS)) + if (intel_is_dual_link_lvds(dev)) /* LVDS with dual channel */ limit = &intel_limits_g4x_dual_channel_lvds; else @@ -686,7 +638,6 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; intel_clock_t clock; int err = target; @@ -696,7 +647,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, * We haven't figured out how to reliably set up different * single/dual channel state, if we even can. */ - if (is_dual_link_lvds(dev_priv, LVDS)) + if (intel_is_dual_link_lvds(dev)) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; @@ -749,7 +700,6 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, intel_clock_t *best_clock) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; intel_clock_t clock; int max_n; bool found; @@ -764,7 +714,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc, lvds_reg = PCH_LVDS; else lvds_reg = LVDS; - if (is_dual_link_lvds(dev_priv, lvds_reg)) + if (intel_is_dual_link_lvds(dev)) clock.p2 = limit->p2.p2_fast; else clock.p2 = limit->p2.p2_slow; @@ -5356,7 +5306,7 @@ static uint32_t ironlake_compute_dpll(struct intel_crtc *intel_crtc, if (is_lvds) { if ((intel_panel_use_ssc(dev_priv) && dev_priv->lvds_ssc_freq == 100) || - is_dual_link_lvds(dev_priv, PCH_LVDS)) + intel_is_dual_link_lvds(dev)) factor = 25; } else if (is_sdvo && is_tv) factor = 20; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a74c572f1d5e..7ca7772eaccd 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -441,6 +441,7 @@ extern void intel_mark_idle(struct drm_device *dev); extern void intel_mark_fb_busy(struct drm_i915_gem_object *obj); extern void intel_mark_fb_idle(struct drm_i915_gem_object *obj); extern bool intel_lvds_init(struct drm_device *dev); +extern bool intel_is_dual_link_lvds(struct drm_device *dev); extern void intel_dp_init(struct drm_device *dev, int output_reg, enum port port); extern void intel_dp_init_connector(struct intel_digital_port *intel_dig_port, diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index b9a660a53677..4158a8839433 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -903,6 +903,59 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev, return false; } +static int intel_dual_link_lvds_callback(const struct dmi_system_id *id) +{ + DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident); + return 1; +} + +static const struct dmi_system_id intel_dual_link_lvds[] = { + { + .callback = intel_dual_link_lvds_callback, + .ident = "Apple MacBook Pro (Core i5/i7 Series)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"), + }, + }, + { } /* terminating entry */ +}; + +bool intel_is_dual_link_lvds(struct drm_device *dev) +{ + unsigned int val; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 lvds_reg; + + if (HAS_PCH_SPLIT(dev)) { + lvds_reg = PCH_LVDS; + } else { + lvds_reg = LVDS; + } + + /* use the module option value if specified */ + if (i915_lvds_channel_mode > 0) + return i915_lvds_channel_mode == 2; + + if (dmi_check_system(intel_dual_link_lvds)) + return true; + + if (dev_priv->lvds_val) + val = dev_priv->lvds_val; + else { + /* BIOS should set the proper LVDS register value at boot, but + * in reality, it doesn't set the value when the lid is closed; + * we need to check "the value to be set" in VBT when LVDS + * register is uninitialized. + */ + val = I915_READ(lvds_reg); + if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) + val = dev_priv->bios_lvds_val; + dev_priv->lvds_val = val; + } + return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; +} + static bool intel_lvds_supported(struct drm_device *dev) { /* With the introduction of the PCH we gained a dedicated From 13c7d8703127334890c894ddab13b3a92a26580a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:10 +0100 Subject: [PATCH 0070/4607] drm/i915: track is_dual_link in intel_lvds Yeah, all users (both the clock selection special cases and the lvds pin pair stuff) are still in common code, but this will change. v2: Rebase on top of Jani Nikula's panel rework. v3: Incorporate review from Paulo Zanoni: - s/__is_dual_link_lvds/compute_is_dual_link_lvds - kill dev_priv->lvds_val - drop spurious whitespace change v4: Add a debug printk to display the dual-link status, as suggested by Paulo Zanoni in review. Reviewed-by: Paulo Zanoni (v3) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/intel_lvds.c | 44 ++++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 79589bbff3db..9be7efd43c6a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -705,7 +705,6 @@ typedef struct drm_i915_private { unsigned int display_clock_mode:1; int lvds_ssc_freq; unsigned int bios_lvds_val; /* initial [PCH_]LVDS reg val in VBIOS */ - unsigned int lvds_val; /* used for checking LVDS channel mode */ struct { int rate; int lanes; diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 4158a8839433..31fdbfd48e44 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -52,6 +52,7 @@ struct intel_lvds_encoder { u32 pfit_control; u32 pfit_pgm_ratios; bool pfit_dirty; + bool is_dual_link; struct intel_lvds_connector *attached_connector; }; @@ -922,6 +923,23 @@ static const struct dmi_system_id intel_dual_link_lvds[] = { }; bool intel_is_dual_link_lvds(struct drm_device *dev) +{ + struct intel_encoder *encoder; + struct intel_lvds_encoder *lvds_encoder; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + if (encoder->type == INTEL_OUTPUT_LVDS) { + lvds_encoder = to_lvds_encoder(&encoder->base); + + return lvds_encoder->is_dual_link; + } + } + + return false; +} + +static bool compute_is_dual_link_lvds(struct drm_device *dev) { unsigned int val; struct drm_i915_private *dev_priv = dev->dev_private; @@ -940,19 +958,15 @@ bool intel_is_dual_link_lvds(struct drm_device *dev) if (dmi_check_system(intel_dual_link_lvds)) return true; - if (dev_priv->lvds_val) - val = dev_priv->lvds_val; - else { - /* BIOS should set the proper LVDS register value at boot, but - * in reality, it doesn't set the value when the lid is closed; - * we need to check "the value to be set" in VBT when LVDS - * register is uninitialized. - */ - val = I915_READ(lvds_reg); - if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) - val = dev_priv->bios_lvds_val; - dev_priv->lvds_val = val; - } + /* BIOS should set the proper LVDS register value at boot, but + * in reality, it doesn't set the value when the lid is closed; + * we need to check "the value to be set" in VBT when LVDS + * register is uninitialized. + */ + val = I915_READ(lvds_reg); + if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) + val = dev_priv->bios_lvds_val; + return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; } @@ -1162,6 +1176,10 @@ bool intel_lvds_init(struct drm_device *dev) goto failed; out: + lvds_encoder->is_dual_link = compute_is_dual_link_lvds(dev); + DRM_DEBUG_KMS("detected %s-link lvds configuration\n", + lvds_encoder->is_dual_link ? "dual" : "single"); + /* * Unlock registers and just * leave them unlocked From 7dec060675125f17a547950d715018a63131a437 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Sep 2012 14:12:25 +0200 Subject: [PATCH 0071/4607] drm/i915: add intel_lvds->reg To ditch at least some of the PCH_SPLIT ? PCH_LVDS : LVDS code ... v2: Rebase on top of Jani Nikula's panel rework. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_lvds.c | 48 +++++++++++++------------------ 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 31fdbfd48e44..6b6ed642b29f 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -53,6 +53,7 @@ struct intel_lvds_encoder { u32 pfit_pgm_ratios; bool pfit_dirty; bool is_dual_link; + u32 reg; struct intel_lvds_connector *attached_connector; }; @@ -72,15 +73,10 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder, { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 lvds_reg, tmp; + struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); + u32 tmp; - if (HAS_PCH_SPLIT(dev)) { - lvds_reg = PCH_LVDS; - } else { - lvds_reg = LVDS; - } - - tmp = I915_READ(lvds_reg); + tmp = I915_READ(lvds_encoder->reg); if (!(tmp & LVDS_PORT_EN)) return false; @@ -102,19 +98,17 @@ static void intel_enable_lvds(struct intel_encoder *encoder) struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); struct drm_i915_private *dev_priv = dev->dev_private; - u32 ctl_reg, lvds_reg, stat_reg; + u32 ctl_reg, stat_reg; if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; - lvds_reg = PCH_LVDS; stat_reg = PCH_PP_STATUS; } else { ctl_reg = PP_CONTROL; - lvds_reg = LVDS; stat_reg = PP_STATUS; } - I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN); + I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN); if (lvds_encoder->pfit_dirty) { /* @@ -133,7 +127,7 @@ static void intel_enable_lvds(struct intel_encoder *encoder) } I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); - POSTING_READ(lvds_reg); + POSTING_READ(lvds_encoder->reg); if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000)) DRM_ERROR("timed out waiting for panel to power on\n"); @@ -145,15 +139,13 @@ static void intel_disable_lvds(struct intel_encoder *encoder) struct drm_device *dev = encoder->base.dev; struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); struct drm_i915_private *dev_priv = dev->dev_private; - u32 ctl_reg, lvds_reg, stat_reg; + u32 ctl_reg, stat_reg; if (HAS_PCH_SPLIT(dev)) { ctl_reg = PCH_PP_CONTROL; - lvds_reg = PCH_LVDS; stat_reg = PCH_PP_STATUS; } else { ctl_reg = PP_CONTROL; - lvds_reg = LVDS; stat_reg = PP_STATUS; } @@ -168,8 +160,8 @@ static void intel_disable_lvds(struct intel_encoder *encoder) lvds_encoder->pfit_dirty = true; } - I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); - POSTING_READ(lvds_reg); + I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN); + POSTING_READ(lvds_encoder->reg); } static int intel_lvds_mode_valid(struct drm_connector *connector, @@ -939,17 +931,11 @@ bool intel_is_dual_link_lvds(struct drm_device *dev) return false; } -static bool compute_is_dual_link_lvds(struct drm_device *dev) +static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder) { + struct drm_device *dev = lvds_encoder->base.base.dev; unsigned int val; struct drm_i915_private *dev_priv = dev->dev_private; - u32 lvds_reg; - - if (HAS_PCH_SPLIT(dev)) { - lvds_reg = PCH_LVDS; - } else { - lvds_reg = LVDS; - } /* use the module option value if specified */ if (i915_lvds_channel_mode > 0) @@ -963,7 +949,7 @@ static bool compute_is_dual_link_lvds(struct drm_device *dev) * we need to check "the value to be set" in VBT when LVDS * register is uninitialized. */ - val = I915_READ(lvds_reg); + val = I915_READ(lvds_encoder->reg); if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) val = dev_priv->bios_lvds_val; @@ -1076,6 +1062,12 @@ bool intel_lvds_init(struct drm_device *dev) connector->interlace_allowed = false; connector->doublescan_allowed = false; + if (HAS_PCH_SPLIT(dev)) { + lvds_encoder->reg = PCH_LVDS; + } else { + lvds_encoder->reg = LVDS; + } + /* create the scaling mode property */ drm_mode_create_scaling_mode_property(dev); drm_object_attach_property(&connector->base, @@ -1176,7 +1168,7 @@ bool intel_lvds_init(struct drm_device *dev) goto failed; out: - lvds_encoder->is_dual_link = compute_is_dual_link_lvds(dev); + lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder); DRM_DEBUG_KMS("detected %s-link lvds configuration\n", lvds_encoder->is_dual_link ? "dual" : "single"); From fc683091eb57692679f629784f42dce453877430 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:12 +0100 Subject: [PATCH 0072/4607] drm/i915: move intel_update_lvds to intel_lvds->pre_pll_enable A few things needed to change: - HAS_PCH_SPLIT since ilk+ is not yet converted to this. - s/LVDS/intel_lvds->reg/ to prep for ilk conversion - replace the clock.p2 == 7 check with a is_dual_link check - s/adjusted_mode/intel_lvds->fixed_mode v2: Rebase on top of Jani Nikula's panel rework. I'm wondering whether we shouldn't add an attached_panel pointer to intel_encoder, to replace the encoder private ->attached_connector pointers, since that's essentially what we need. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 59 ---------------------------- drivers/gpu/drm/i915/intel_lvds.c | 57 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 59 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2705da329ddf..bb080fbde808 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4257,51 +4257,6 @@ static void i9xx_update_pll_dividers(struct drm_crtc *crtc, } } -static void intel_update_lvds(struct drm_crtc *crtc, intel_clock_t *clock, - struct drm_display_mode *adjusted_mode) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; - u32 temp; - - temp = I915_READ(LVDS); - temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (pipe == 1) { - temp |= LVDS_PIPEB_SELECT; - } else { - temp &= ~LVDS_PIPEB_SELECT; - } - /* set the corresponsding LVDS_BORDER bit */ - temp |= dev_priv->lvds_border_bits; - /* Set the B0-B3 data pairs corresponding to whether we're going to - * set the DPLLs for dual-channel mode or not. - */ - if (clock->p2 == 7) - temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - else - temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); - - /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) - * appropriately here, but we need to look more thoroughly into how - * panels behave in the two modes. - */ - /* set the dithering flag on LVDS as needed */ - if (INTEL_INFO(dev)->gen >= 4) { - if (dev_priv->lvds_dither) - temp |= LVDS_ENABLE_DITHER; - else - temp &= ~LVDS_ENABLE_DITHER; - } - temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - temp |= LVDS_HSYNC_POLARITY; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - temp |= LVDS_VSYNC_POLARITY; - I915_WRITE(LVDS, temp); -} - static void vlv_update_pll(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -4484,13 +4439,6 @@ static void i9xx_update_pll(struct drm_crtc *crtc, if (encoder->pre_pll_enable) encoder->pre_pll_enable(encoder); - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) - intel_update_lvds(crtc, clock, adjusted_mode); - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) intel_dp_set_m_n(crtc, mode, adjusted_mode); @@ -4566,13 +4514,6 @@ static void i8xx_update_pll(struct drm_crtc *crtc, if (encoder->pre_pll_enable) encoder->pre_pll_enable(encoder); - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) - intel_update_lvds(crtc, clock, adjusted_mode); - I915_WRITE(DPLL(pipe), dpll); /* Wait for the clocks to stabilize. */ diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 6b6ed642b29f..c93ec03974d6 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -89,6 +89,62 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder, return true; } +/* The LVDS pin pair needs to be on before the DPLLs are enabled. + * This is an exception to the general rule that mode_set doesn't turn + * things on. + */ +static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) +{ + struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); + struct drm_display_mode *fixed_mode = + lvds_encoder->attached_connector->base.panel.fixed_mode; + int pipe = intel_crtc->pipe; + u32 temp; + + /* pch split platforms are not yet converted. */ + if (HAS_PCH_SPLIT(dev)) + return; + + temp = I915_READ(lvds_encoder->reg); + temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; + if (pipe == 1) { + temp |= LVDS_PIPEB_SELECT; + } else { + temp &= ~LVDS_PIPEB_SELECT; + } + /* set the corresponsding LVDS_BORDER bit */ + temp |= dev_priv->lvds_border_bits; + /* Set the B0-B3 data pairs corresponding to whether we're going to + * set the DPLLs for dual-channel mode or not. + */ + if (lvds_encoder->is_dual_link) + temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; + else + temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); + + /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) + * appropriately here, but we need to look more thoroughly into how + * panels behave in the two modes. + */ + /* set the dithering flag on LVDS as needed */ + if (INTEL_INFO(dev)->gen >= 4) { + if (dev_priv->lvds_dither) + temp |= LVDS_ENABLE_DITHER; + else + temp &= ~LVDS_ENABLE_DITHER; + } + temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); + if (fixed_mode->flags & DRM_MODE_FLAG_NHSYNC) + temp |= LVDS_HSYNC_POLARITY; + if (fixed_mode->flags & DRM_MODE_FLAG_NVSYNC) + temp |= LVDS_VSYNC_POLARITY; + + I915_WRITE(lvds_encoder->reg, temp); +} + /** * Sets the power state for the panel. */ @@ -1041,6 +1097,7 @@ bool intel_lvds_init(struct drm_device *dev) DRM_MODE_ENCODER_LVDS); intel_encoder->enable = intel_enable_lvds; + intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds; intel_encoder->disable = intel_disable_lvds; intel_encoder->get_hw_state = intel_lvds_get_hw_state; intel_connector->get_hw_state = intel_connector_get_hw_state; From 62810e5a9df3f47b7261e5d78fc1c33e550f2171 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 26 Nov 2012 17:22:13 +0100 Subject: [PATCH 0073/4607] drm/i915: enable intel_lvds->pre_pll_enable for ilk+, too Only two things needed adjustment: - pipe select for PCH_CPT - There's no dithering bit on ilk+ in the lvds ctl reg Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 40 ---------------------------- drivers/gpu/drm/i915/intel_lvds.c | 24 ++++++++++------- 2 files changed, 15 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bb080fbde808..5958b3bd8650 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5322,7 +5322,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, bool ok, has_reduced_clock = false; bool is_lvds = false, is_dp = false, is_cpu_edp = false; struct intel_encoder *encoder; - u32 temp; int ret; bool dither, fdi_config_ok; @@ -5386,45 +5385,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } else intel_put_pch_pll(intel_crtc); - /* The LVDS pin pair needs to be on before the DPLLs are enabled. - * This is an exception to the general rule that mode_set doesn't turn - * things on. - */ - if (is_lvds) { - temp = I915_READ(PCH_LVDS); - temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (HAS_PCH_CPT(dev)) { - temp &= ~PORT_TRANS_SEL_MASK; - temp |= PORT_TRANS_SEL_CPT(pipe); - } else { - if (pipe == 1) - temp |= LVDS_PIPEB_SELECT; - else - temp &= ~LVDS_PIPEB_SELECT; - } - - /* set the corresponsding LVDS_BORDER bit */ - temp |= dev_priv->lvds_border_bits; - /* Set the B0-B3 data pairs corresponding to whether we're going to - * set the DPLLs for dual-channel mode or not. - */ - if (clock.p2 == 7) - temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - else - temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); - - /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) - * appropriately here, but we need to look more thoroughly into how - * panels behave in the two modes. - */ - temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - temp |= LVDS_HSYNC_POLARITY; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - temp |= LVDS_VSYNC_POLARITY; - I915_WRITE(PCH_LVDS, temp); - } - if (is_dp && !is_cpu_edp) { intel_dp_set_m_n(crtc, mode, adjusted_mode); } else { diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index c93ec03974d6..778106961e80 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -104,17 +104,20 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) int pipe = intel_crtc->pipe; u32 temp; - /* pch split platforms are not yet converted. */ - if (HAS_PCH_SPLIT(dev)) - return; - temp = I915_READ(lvds_encoder->reg); temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (pipe == 1) { - temp |= LVDS_PIPEB_SELECT; + + if (HAS_PCH_CPT(dev)) { + temp &= ~PORT_TRANS_SEL_MASK; + temp |= PORT_TRANS_SEL_CPT(pipe); } else { - temp &= ~LVDS_PIPEB_SELECT; + if (pipe == 1) { + temp |= LVDS_PIPEB_SELECT; + } else { + temp &= ~LVDS_PIPEB_SELECT; + } } + /* set the corresponsding LVDS_BORDER bit */ temp |= dev_priv->lvds_border_bits; /* Set the B0-B3 data pairs corresponding to whether we're going to @@ -129,8 +132,11 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder) * appropriately here, but we need to look more thoroughly into how * panels behave in the two modes. */ - /* set the dithering flag on LVDS as needed */ - if (INTEL_INFO(dev)->gen >= 4) { + + /* Set the dithering flag on LVDS as needed, note that there is no + * special lvds dither control bit on pch-split platforms, dithering is + * only controlled through the PIPECONF reg. */ + if (INTEL_INFO(dev)->gen == 4) { if (dev_priv->lvds_dither) temp |= LVDS_ENABLE_DITHER; else From a39a68054f63da0ea3b4806e1bfad79670a93d9f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Nov 2012 15:40:05 +0100 Subject: [PATCH 0074/4607] drm/i915: simplify shmem pwrite/pread slowpath handling The shmem paths for pwrite/pread used a clever trick to hold onto a single page when dropping the big dev->struct_mutex for the slowpath. But this ran the risk of reinstating (or not completely purging) the backing storage when dropping purgeable objects. Hence the code needed to keep track of whether it ever dropped the lock, and if it did, manually check whether it needs to re-purge the backing storage. But thanks to the pages pin count introduced in commit a5570178c059cec59e9835be20bc8546377fa7b5 Author: Chris Wilson Date: Tue Sep 4 21:02:54 2012 +0100 drm/i915: Pin backing pages whilst exporting through a dmabuf vmap which allowed us to pin the backing storage and remove that page reference trick from shmem_pwrite/read in commit f60d7f0c1d55a935475ab394955cafddefaa6533 Author: Chris Wilson Date: Tue Sep 4 21:02:56 2012 +0100 drm/i915: Pin backing pages for pread and commit 755d22184f1e5015b040acee794542d9cf8a16c5 Author: Chris Wilson Date: Tue Sep 4 21:02:55 2012 +0100 drm/i915: Pin backing pages for pwrite we can now abolish this check. The slowpath cleanup completely disappears from pread, and for pwrite we're only left with the domain fixup in case someone moved the object out of the cpu domain from under us. A follow-on patch will optimize that a notch more. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 4ef8b5714337..36f629a79d88 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -407,7 +407,6 @@ i915_gem_shmem_pread(struct drm_device *dev, loff_t offset; int shmem_page_offset, page_length, ret = 0; int obj_do_bit17_swizzling, page_do_bit17_swizzling; - int hit_slowpath = 0; int prefaulted = 0; int needs_clflush = 0; struct scatterlist *sg; @@ -469,7 +468,6 @@ i915_gem_shmem_pread(struct drm_device *dev, if (ret == 0) goto next_page; - hit_slowpath = 1; mutex_unlock(&dev->struct_mutex); if (!prefaulted) { @@ -502,12 +500,6 @@ next_page: out: i915_gem_object_unpin_pages(obj); - if (hit_slowpath) { - /* Fixup: Kill any reinstated backing storage pages */ - if (obj->madv == __I915_MADV_PURGED) - i915_gem_object_truncate(obj); - } - return ret; } @@ -838,11 +830,8 @@ out: i915_gem_object_unpin_pages(obj); if (hit_slowpath) { - /* Fixup: Kill any reinstated backing storage pages */ - if (obj->madv == __I915_MADV_PURGED) - i915_gem_object_truncate(obj); - /* and flush dirty cachelines in case the object isn't in the cpu write - * domain anymore. */ + /* Fixup: Flush dirty cachelines in case the object isn't in the + * cpu write domain anymore. */ if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) { i915_gem_clflush_object(obj); i915_gem_chipset_flush(dev); From 8dcf015eb967c718962c0690330d9a94d56f2c5d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Nov 2012 16:53:58 +0100 Subject: [PATCH 0075/4607] drm/i915: optimize the shmem_pwrite slowpath handling Since we drop dev->struct_mutex when going through the slowpath, the object might have been moved out of the cpu domain. Hence we need to clflush the entire object to ensure that after the ioctl returns, everything is coherent again (interwoven writes are ill-defined anyway). But we only need to do this if we start in the cpu domain and the object requires flushing for coherency. So don't do the flushing if the object is coherent anyway or if we've done in-line clfushing already. v2: i915_gem_clflush_object already checks whether the object is coherent and if so, drops the flushing. Hence we don't need to check that ourselves, simplifying the condition. v3: Reorder the checks for better clarity (and adjust the comment accordingly), suggested by Chris Wilson. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 36f629a79d88..5c8df572cd17 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -830,9 +830,13 @@ out: i915_gem_object_unpin_pages(obj); if (hit_slowpath) { - /* Fixup: Flush dirty cachelines in case the object isn't in the - * cpu write domain anymore. */ - if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) { + /* + * Fixup: Flush cpu caches in case we didn't flush the dirty + * cachelines in-line while writing and the object moved + * out of the cpu write domain while we've dropped the lock. + */ + if (!needs_clflush_after && + obj->base.write_domain != I915_GEM_DOMAIN_CPU) { i915_gem_clflush_object(obj); i915_gem_chipset_flush(dev); } From c1f63f9d688e68088ef743a48a3aa8e0dc01a160 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 23 Nov 2012 15:30:37 -0200 Subject: [PATCH 0076/4607] drm/i915: intel_prepare_ddi_buffers should be static It's not even declared on header files. Signed-off-by: Paulo Zanoni Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 852012b6fc5b..16d3e7854ced 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -84,7 +84,8 @@ static enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder) * in either FDI or DP modes only, as HDMI connections will work with both * of those */ -void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port, bool use_fdi_mode) +static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port, + bool use_fdi_mode) { struct drm_i915_private *dev_priv = dev->dev_private; u32 reg; From 20749730e39bba1c6100bec0e0d1a45c99db559e Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 23 Nov 2012 15:30:38 -0200 Subject: [PATCH 0077/4607] drm/i915: remove Haswell code from ironlake_fdi_pll_enable This function is not called on Haswell anymore. Signed-off-by: Paulo Zanoni Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5958b3bd8650..511b397b5aaa 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2787,18 +2787,14 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) POSTING_READ(reg); udelay(200); - /* On Haswell, the PLL configuration for ports and pipes is handled - * separately, as part of DDI setup */ - if (!IS_HASWELL(dev)) { - /* Enable CPU FDI TX PLL, always on for Ironlake */ - reg = FDI_TX_CTL(pipe); - temp = I915_READ(reg); - if ((temp & FDI_TX_PLL_ENABLE) == 0) { - I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE); + /* Enable CPU FDI TX PLL, always on for Ironlake */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + if ((temp & FDI_TX_PLL_ENABLE) == 0) { + I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE); - POSTING_READ(reg); - udelay(100); - } + POSTING_READ(reg); + udelay(100); } } From affa935440733a79c5a9eb0e5357e2564ca4b355 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 23 Nov 2012 15:30:39 -0200 Subject: [PATCH 0078/4607] drm/i915: add HAS_DDI check And use it whenever we call code that uses the DDIs. We already have intel_ddi.c and prefix every function with intel_ddi_something instead of haswell_something, so I think replacing the checks with HAS_DDI makes more sense. Just a cosmetical change, yes I know, but I have this OCD... Signed-off-by: Paulo Zanoni Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/intel_crt.c | 2 +- drivers/gpu/drm/i915/intel_ddi.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 19 +++++++++---------- drivers/gpu/drm/i915/intel_dp.c | 8 ++++---- drivers/gpu/drm/i915/intel_hdmi.c | 4 ++-- 6 files changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9be7efd43c6a..e908c498ed87 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1208,6 +1208,8 @@ struct drm_i915_file_private { #define HAS_PIPE_CONTROL(dev) (INTEL_INFO(dev)->gen >= 5) +#define HAS_DDI(dev) (IS_HASWELL(dev)) + #define INTEL_PCH_DEVICE_ID_MASK 0xff00 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00 diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 5c7774396e10..bc07b3f0d5e7 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -771,7 +771,7 @@ void intel_crt_init(struct drm_device *dev) crt->base.disable = intel_disable_crt; crt->base.enable = intel_enable_crt; - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) crt->base.get_hw_state = intel_ddi_get_hw_state; else crt->base.get_hw_state = intel_crt_get_hw_state; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 16d3e7854ced..197dede1870a 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -115,7 +115,7 @@ void intel_prepare_ddi(struct drm_device *dev) { int port; - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { for (port = PORT_A; port < PORT_E; port++) intel_prepare_ddi_buffers(dev, port, false); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 511b397b5aaa..52b6b0e811bc 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1072,8 +1072,8 @@ static void assert_fdi_tx(struct drm_i915_private *dev_priv, enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); - if (IS_HASWELL(dev_priv->dev)) { - /* On Haswell, DDI is used instead of FDI_TX_CTL */ + if (HAS_DDI(dev_priv->dev)) { + /* DDI does not have a specific FDI_TX register */ reg = TRANS_DDI_FUNC_CTL(cpu_transcoder); val = I915_READ(reg); cur_state = !!(val & TRANS_DDI_FUNC_ENABLE); @@ -1117,7 +1117,7 @@ static void assert_fdi_tx_pll_enabled(struct drm_i915_private *dev_priv, return; /* On Haswell, DDI ports are responsible for the FDI PLL setup */ - if (IS_HASWELL(dev_priv->dev)) + if (HAS_DDI(dev_priv->dev)) return; reg = FDI_TX_CTL(pipe); @@ -7976,7 +7976,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { static void intel_cpu_pll_init(struct drm_device *dev) { - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_ddi_pll_init(dev); } @@ -8112,11 +8112,10 @@ static void intel_setup_outputs(struct drm_device *dev) I915_WRITE(PFIT_CONTROL, 0); } - if (!(IS_HASWELL(dev) && - (I915_READ(DDI_BUF_CTL(PORT_A)) & DDI_A_4_LANES))) + if (!(HAS_DDI(dev) && (I915_READ(DDI_BUF_CTL(PORT_A)) & DDI_A_4_LANES))) intel_crt_init(dev); - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { int found; /* Haswell uses DDI functions to detect digital outputs */ @@ -8360,7 +8359,7 @@ static void intel_init_display(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; /* We always want a DPMS function */ - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { dev_priv->display.crtc_mode_set = haswell_crtc_mode_set; dev_priv->display.crtc_enable = haswell_crtc_enable; dev_priv->display.crtc_disable = haswell_crtc_disable; @@ -8849,7 +8848,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, struct intel_encoder *encoder; struct intel_connector *connector; - if (IS_HASWELL(dev)) { + if (HAS_DDI(dev)) { tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP)); if (tmp & TRANS_DDI_FUNC_ENABLE) { @@ -8890,7 +8889,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, crtc->active ? "enabled" : "disabled"); } - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_ddi_setup_hw_pll_state(dev); list_for_each_entry(encoder, &dev->mode_config.encoder_list, diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index d76258dcb8f8..f2904380a6f6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -379,7 +379,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, * clock divider. */ if (is_cpu_edp(intel_dp)) { - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) aux_clock_divider = intel_ddi_get_cdclk_freq(dev_priv) >> 1; else if (IS_VALLEYVIEW(dev)) aux_clock_divider = 100; @@ -1791,7 +1791,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) int voltage_tries, loop_tries; uint32_t DP = intel_dp->DP; - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_ddi_prepare_link_retrain(encoder); /* Write the link configuration data */ @@ -1981,7 +1981,7 @@ intel_dp_link_down(struct intel_dp *intel_dp) * intel_ddi_prepare_link_retrain will take care of redoing the link * train. */ - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) return; if (WARN_ON((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0)) @@ -2742,7 +2742,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, intel_connector_attach_encoder(intel_connector, intel_encoder); drm_sysfs_connector_add(connector); - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_connector->get_hw_state = intel_ddi_connector_get_hw_state; else intel_connector->get_hw_state = intel_connector_get_hw_state; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 2ee9821b9d93..53df0a88a6a8 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -48,7 +48,7 @@ assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi) struct drm_i915_private *dev_priv = dev->dev_private; uint32_t enabled_bits; - enabled_bits = IS_HASWELL(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE; + enabled_bits = HAS_DDI(dev) ? DDI_BUF_CTL_ENABLE : SDVO_ENABLE; WARN(I915_READ(intel_hdmi->sdvox_reg) & enabled_bits, "HDMI port enabled, expecting disabled\n"); @@ -1013,7 +1013,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, intel_hdmi->set_infoframes = cpt_set_infoframes; } - if (IS_HASWELL(dev)) + if (HAS_DDI(dev)) intel_connector->get_hw_state = intel_ddi_connector_get_hw_state; else intel_connector->get_hw_state = intel_connector_get_hw_state; From 0d536cb4e911c2496073f268e9730a9badaafa55 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 23 Nov 2012 16:46:41 -0200 Subject: [PATCH 0079/4607] drm/i915: invert the log inside intel_prepare_ddi Do an early return in case we don't have DDI instead of having the whole function inside an "if" statement. Signed-off-by: Paulo Zanoni Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 197dede1870a..ad936c68e4cb 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -115,16 +115,17 @@ void intel_prepare_ddi(struct drm_device *dev) { int port; - if (HAS_DDI(dev)) { - for (port = PORT_A; port < PORT_E; port++) - intel_prepare_ddi_buffers(dev, port, false); + if (!HAS_DDI(dev)) + return; - /* DDI E is the suggested one to work in FDI mode, so program is as such by - * default. It will have to be re-programmed in case a digital DP output - * will be detected on it - */ - intel_prepare_ddi_buffers(dev, PORT_E, true); - } + for (port = PORT_A; port < PORT_E; port++) + intel_prepare_ddi_buffers(dev, port, false); + + /* DDI E is the suggested one to work in FDI mode, so program is as such + * by default. It will have to be re-programmed in case a digital DP + * output will be detected on it + */ + intel_prepare_ddi_buffers(dev, PORT_E, true); } static const long hsw_ddi_buf_ctl_values[] = { From 9fa5f6522e6eecb5ab20192a264a29ba4f2f4e85 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 29 Nov 2012 11:31:29 -0200 Subject: [PATCH 0080/4607] drm/i915: kill intel_dp_link_clock() Use drm_dp_bw_code_to_link_rate insead. It's the same thing, but supports DP_LINK_BW_5_4 and is also used by the other drivers. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f2904380a6f6..b51043e651b5 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -148,15 +148,6 @@ intel_dp_max_link_bw(struct intel_dp *intel_dp) return max_link_bw; } -static int -intel_dp_link_clock(uint8_t link_bw) -{ - if (link_bw == DP_LINK_BW_2_7) - return 270000; - else - return 162000; -} - /* * The units on the numbers in the next two are... bizarre. Examples will * make it clearer; this one parallels an example in the eDP spec. @@ -191,7 +182,8 @@ intel_dp_adjust_dithering(struct intel_dp *intel_dp, struct drm_display_mode *mode, bool adjust_mode) { - int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); + int max_link_clock = + drm_dp_bw_code_to_link_rate(intel_dp_max_link_bw(intel_dp)); int max_lanes = drm_dp_max_lane_count(intel_dp->dpcd); int max_rate, mode_rate; @@ -722,12 +714,15 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, for (clock = 0; clock <= max_clock; clock++) { for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { - int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); + int link_bw_clock = + drm_dp_bw_code_to_link_rate(bws[clock]); + int link_avail = intel_dp_max_data_rate(link_bw_clock, + lane_count); if (mode_rate <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; - adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw); + adjusted_mode->clock = link_bw_clock; DRM_DEBUG_KMS("DP link bw %02x lane " "count %d clock %d bpp %d\n", intel_dp->link_bw, intel_dp->lane_count, From 99057c81037ee45e94e5c7e6b403b73caccbaf9e Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 29 Nov 2012 12:45:06 -0800 Subject: [PATCH 0081/4607] i915: convert struct spinlock to spinlock_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spinlock_t should always be used. LD drivers/gpu/drm/i915/built-in.o CHECK drivers/gpu/drm/i915/i915_drv.c CC [M] drivers/gpu/drm/i915/i915_drv.o CHECK drivers/gpu/drm/i915/i915_dma.c CC [M] drivers/gpu/drm/i915/i915_dma.o CHECK drivers/gpu/drm/i915/i915_irq.c CC [M] drivers/gpu/drm/i915/i915_irq.o CHECK drivers/gpu/drm/i915/i915_debugfs.c drivers/gpu/drm/i915/i915_debugfs.c:558:31: warning: dereference of noderef expression drivers/gpu/drm/i915/i915_debugfs.c:558:39: warning: dereference of noderef expression drivers/gpu/drm/i915/i915_debugfs.c:558:51: warning: dereference of noderef expression drivers/gpu/drm/i915/i915_debugfs.c:558:63: warning: dereference of noderef expression CC [M] drivers/gpu/drm/i915/i915_debugfs.o CHECK drivers/gpu/drm/i915/i915_suspend.c CC [M] drivers/gpu/drm/i915/i915_suspend.o CHECK drivers/gpu/drm/i915/i915_gem.c drivers/gpu/drm/i915/i915_gem.c:3703:14: warning: incorrect type in assignment (different base types) drivers/gpu/drm/i915/i915_gem.c:3703:14: expected unsigned int [unsigned] [usertype] mask drivers/gpu/drm/i915/i915_gem.c:3703:14: got restricted gfp_t drivers/gpu/drm/i915/i915_gem.c:3706:22: warning: invalid assignment: &= drivers/gpu/drm/i915/i915_gem.c:3706:22: left side has type unsigned int drivers/gpu/drm/i915/i915_gem.c:3706:22: right side has type restricted gfp_t drivers/gpu/drm/i915/i915_gem.c:3707:22: warning: invalid assignment: |= drivers/gpu/drm/i915/i915_gem.c:3707:22: left side has type unsigned int drivers/gpu/drm/i915/i915_gem.c:3707:22: right side has type restricted gfp_t drivers/gpu/drm/i915/i915_gem.c:3711:39: warning: incorrect type in argument 2 (different base types) drivers/gpu/drm/i915/i915_gem.c:3711:39: expected restricted gfp_t [usertype] mask drivers/gpu/drm/i915/i915_gem.c:3711:39: got unsigned int [unsigned] [usertype] mask CC [M] drivers/gpu/drm/i915/i915_gem.o CHECK drivers/gpu/drm/i915/i915_gem_context.c CC [M] drivers/gpu/drm/i915/i915_gem_context.o CHECK drivers/gpu/drm/i915/i915_gem_debug.c CC [M] drivers/gpu/drm/i915/i915_gem_debug.o CHECK drivers/gpu/drm/i915/i915_gem_evict.c CC [M] drivers/gpu/drm/i915/i915_gem_evict.o CHECK drivers/gpu/drm/i915/i915_gem_execbuffer.c CC [M] drivers/gpu/drm/i915/i915_gem_execbuffer.o CHECK drivers/gpu/drm/i915/i915_gem_gtt.c CC [M] drivers/gpu/drm/i915/i915_gem_gtt.o CHECK drivers/gpu/drm/i915/i915_gem_stolen.c CC [M] drivers/gpu/drm/i915/i915_gem_stolen.o CHECK drivers/gpu/drm/i915/i915_gem_tiling.c CC [M] drivers/gpu/drm/i915/i915_gem_tiling.o CHECK drivers/gpu/drm/i915/i915_sysfs.c CC [M] drivers/gpu/drm/i915/i915_sysfs.o CHECK drivers/gpu/drm/i915/i915_trace_points.c CC [M] drivers/gpu/drm/i915/i915_trace_points.o CHECK drivers/gpu/drm/i915/intel_display.c drivers/gpu/drm/i915/intel_display.c:1736:9: warning: mixing different enum types drivers/gpu/drm/i915/intel_display.c:1736:9: int enum transcoder versus drivers/gpu/drm/i915/intel_display.c:1736:9: int enum pipe drivers/gpu/drm/i915/intel_display.c:3659:48: warning: mixing different enum types drivers/gpu/drm/i915/intel_display.c:3659:48: int enum pipe versus drivers/gpu/drm/i915/intel_display.c:3659:48: int enum transcoder CC [M] drivers/gpu/drm/i915/intel_display.o CHECK drivers/gpu/drm/i915/intel_crt.c CC [M] drivers/gpu/drm/i915/intel_crt.o CHECK drivers/gpu/drm/i915/intel_lvds.c CC [M] drivers/gpu/drm/i915/intel_lvds.o CHECK drivers/gpu/drm/i915/intel_bios.c drivers/gpu/drm/i915/intel_bios.c:706:60: warning: incorrect type in initializer (different address spaces) drivers/gpu/drm/i915/intel_bios.c:706:60: expected struct vbt_header *vbt drivers/gpu/drm/i915/intel_bios.c:706:60: got void [noderef] *vbt drivers/gpu/drm/i915/intel_bios.c:726:42: warning: incorrect type in argument 1 (different address spaces) drivers/gpu/drm/i915/intel_bios.c:726:42: expected void const * drivers/gpu/drm/i915/intel_bios.c:726:42: got unsigned char [noderef] [usertype] * drivers/gpu/drm/i915/intel_bios.c:727:40: warning: cast removes address space of expression drivers/gpu/drm/i915/intel_bios.c:738:24: warning: cast removes address space of expression CC [M] drivers/gpu/drm/i915/intel_bios.o CHECK drivers/gpu/drm/i915/intel_ddi.c drivers/gpu/drm/i915/intel_ddi.c:87:6: warning: symbol 'intel_prepare_ddi_buffers' was not declared. Should it be static? drivers/gpu/drm/i915/intel_ddi.c:1036:34: warning: mixing different enum types drivers/gpu/drm/i915/intel_ddi.c:1036:34: int enum pipe versus drivers/gpu/drm/i915/intel_ddi.c:1036:34: int enum transcoder CC [M] drivers/gpu/drm/i915/intel_ddi.o drivers/gpu/drm/i915/intel_ddi.c: In function ‘intel_ddi_setup_hw_pll_state’: drivers/gpu/drm/i915/intel_ddi.c:1129:2: warning: ‘port’ may be used uninitialized in this function [-Wmaybe-uninitialized] drivers/gpu/drm/i915/intel_ddi.c:1111:12: note: ‘port’ was declared here CHECK drivers/gpu/drm/i915/intel_dp.c CC [M] drivers/gpu/drm/i915/intel_dp.o CHECK drivers/gpu/drm/i915/intel_hdmi.c CC [M] drivers/gpu/drm/i915/intel_hdmi.o CHECK drivers/gpu/drm/i915/intel_sdvo.c CC [M] drivers/gpu/drm/i915/intel_sdvo.o CHECK drivers/gpu/drm/i915/intel_modes.c CC [M] drivers/gpu/drm/i915/intel_modes.o CHECK drivers/gpu/drm/i915/intel_panel.c CC [M] drivers/gpu/drm/i915/intel_panel.o CHECK drivers/gpu/drm/i915/intel_pm.c drivers/gpu/drm/i915/intel_pm.c:2173:1: warning: symbol 'mchdev_lock' was not declared. Should it be static? CC [M] drivers/gpu/drm/i915/intel_pm.o CHECK drivers/gpu/drm/i915/intel_i2c.c CC [M] drivers/gpu/drm/i915/intel_i2c.o CHECK drivers/gpu/drm/i915/intel_fb.c CC [M] drivers/gpu/drm/i915/intel_fb.o CHECK drivers/gpu/drm/i915/intel_tv.c CC [M] drivers/gpu/drm/i915/intel_tv.o CHECK drivers/gpu/drm/i915/intel_dvo.c CC [M] drivers/gpu/drm/i915/intel_dvo.o CHECK drivers/gpu/drm/i915/intel_ringbuffer.c CC [M] drivers/gpu/drm/i915/intel_ringbuffer.o CHECK drivers/gpu/drm/i915/intel_overlay.c CC [M] drivers/gpu/drm/i915/intel_overlay.o CHECK drivers/gpu/drm/i915/intel_sprite.c CC [M] drivers/gpu/drm/i915/intel_sprite.o CHECK drivers/gpu/drm/i915/intel_opregion.c CC [M] drivers/gpu/drm/i915/intel_opregion.o CHECK drivers/gpu/drm/i915/dvo_ch7xxx.c CC [M] drivers/gpu/drm/i915/dvo_ch7xxx.o CHECK drivers/gpu/drm/i915/dvo_ch7017.c CC [M] drivers/gpu/drm/i915/dvo_ch7017.o CHECK drivers/gpu/drm/i915/dvo_ivch.c CC [M] drivers/gpu/drm/i915/dvo_ivch.o CHECK drivers/gpu/drm/i915/dvo_tfp410.c CC [M] drivers/gpu/drm/i915/dvo_tfp410.o CHECK drivers/gpu/drm/i915/dvo_sil164.c CC [M] drivers/gpu/drm/i915/dvo_sil164.o CHECK drivers/gpu/drm/i915/dvo_ns2501.c CC [M] drivers/gpu/drm/i915/dvo_ns2501.o CHECK drivers/gpu/drm/i915/i915_gem_dmabuf.c CC [M] drivers/gpu/drm/i915/i915_gem_dmabuf.o CHECK drivers/gpu/drm/i915/i915_ioc32.c CC [M] drivers/gpu/drm/i915/i915_ioc32.o CHECK drivers/gpu/drm/i915/intel_acpi.c CC [M] drivers/gpu/drm/i915/intel_acpi.o LD [M] drivers/gpu/drm/i915/i915.o Building modules, stage 2. MODPOST 1 modules CC drivers/gpu/drm/i915/i915.mod.o LD [M] drivers/gpu/drm/i915/i915.ko Cc: Daniel Vetter Cc: intel-gfx@lists.freedesktop.org Cc: dri-devel@lists.freedesktop.org Reported-by: Hauke Mehrtens Signed-off-by: Luis R. Rodriguez Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e908c498ed87..31ab43b79736 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -628,7 +628,7 @@ typedef struct drm_i915_private { /** forcewake_count is protected by gt_lock */ unsigned forcewake_count; /** gt_lock is also taken in irq contexts. */ - struct spinlock gt_lock; + spinlock_t gt_lock; struct intel_gmbus gmbus[GMBUS_NUM_PORTS]; @@ -1128,7 +1128,7 @@ struct drm_i915_gem_request { struct drm_i915_file_private { struct { - struct spinlock lock; + spinlock_t lock; struct list_head request_list; } mm; struct idr context_idr; From acd15b6cc20f85bcef9e08b6ed4f142c34791c32 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 30 Nov 2012 11:24:50 +0100 Subject: [PATCH 0082/4607] drm/i915: optimize ilk/snb irq handler We only need to read/write the south interrupt register if the corresponding bit is set in the north master interrupt register. Noticed while reading our interrupt handling code. Same optimization has already been applied on ivb in commit 0e43406bcc1868a316eea6012a0a09d992c53521 Author: Chris Wilson Date: Wed May 9 21:45:44 2012 +0100 drm/i915: Simplify interrupt processing for IvyBridge We can take advantage that the PCH_IIR is a subordinate register to reduce one of the required IIR reads, and that we only need to clear interrupts handled to reduce the writes. And by simply tidying the code we can reduce the line count and hopefully make it more readable. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6cd3dc94b472..26753ee6571d 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -758,7 +758,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = IRQ_NONE; - u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir; + u32 de_iir, gt_iir, de_ier, pm_iir; atomic_inc(&dev_priv->irq_received); @@ -769,11 +769,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) de_iir = I915_READ(DEIIR); gt_iir = I915_READ(GTIIR); - pch_iir = I915_READ(SDEIIR); pm_iir = I915_READ(GEN6_PMIIR); - if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 && - (!IS_GEN6(dev) || pm_iir == 0)) + if (de_iir == 0 && gt_iir == 0 && (!IS_GEN6(dev) || pm_iir == 0)) goto done; ret = IRQ_HANDLED; @@ -804,10 +802,15 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) /* check event from PCH */ if (de_iir & DE_PCH_EVENT) { + u32 pch_iir = I915_READ(SDEIIR); + if (HAS_PCH_CPT(dev)) cpt_irq_handler(dev, pch_iir); else ibx_irq_handler(dev, pch_iir); + + /* should clear PCH hotplug event before clear CPU irq */ + I915_WRITE(SDEIIR, pch_iir); } if (IS_GEN5(dev) && de_iir & DE_PCU_EVENT) @@ -816,8 +819,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); - /* should clear PCH hotplug event before clear CPU irq */ - I915_WRITE(SDEIIR, pch_iir); I915_WRITE(GTIIR, gt_iir); I915_WRITE(DEIIR, de_iir); I915_WRITE(GEN6_PMIIR, pm_iir); From 5973c7ee519e2a240c68b290a1836bdb25ed3701 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:16 +0000 Subject: [PATCH 0083/4607] drm: Introduce drm_mm_create_block() To be used later by i915 to preallocate exact blocks of space from the range manager. Signed-off-by: Chris Wilson Cc: Dave Airlie Acked-by: Dave Airlie Cc: dri-devel@lists.freedesktop.org Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_mm.c | 50 ++++++++++++++++++++++++++++++++++++++++ include/drm/drm_mm.h | 4 ++++ 2 files changed, 54 insertions(+) diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 0761a03cdbb2..bd203b61a72b 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -161,6 +161,56 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, } } +struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, + unsigned long start, + unsigned long size, + bool atomic) +{ + struct drm_mm_node *hole, *node; + unsigned long end = start + size; + + list_for_each_entry(hole, &mm->hole_stack, hole_stack) { + unsigned long hole_start; + unsigned long hole_end; + + BUG_ON(!hole->hole_follows); + hole_start = drm_mm_hole_node_start(hole); + hole_end = drm_mm_hole_node_end(hole); + + if (hole_start > start || hole_end < end) + continue; + + node = drm_mm_kmalloc(mm, atomic); + if (unlikely(node == NULL)) + return NULL; + + node->start = start; + node->size = size; + node->mm = mm; + node->allocated = 1; + + INIT_LIST_HEAD(&node->hole_stack); + list_add(&node->node_list, &hole->node_list); + + if (start == hole_start) { + hole->hole_follows = 0; + list_del_init(&hole->hole_stack); + } + + node->hole_follows = 0; + if (end != hole_end) { + list_add(&node->hole_stack, &mm->hole_stack); + node->hole_follows = 1; + } + + return node; + } + + WARN(1, "no hole found for block 0x%lx + 0x%lx\n", start, size); + return NULL; +} +EXPORT_SYMBOL(drm_mm_create_block); + struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node, unsigned long size, unsigned alignment, diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 06d7f798a08c..4020f9661c3c 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -102,6 +102,10 @@ static inline bool drm_mm_initialized(struct drm_mm *mm) /* * Basic range manager support (drm_mm.c) */ +extern struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, + unsigned long start, + unsigned long size, + bool atomic); extern struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node, unsigned long size, unsigned alignment, From 9e8944ab564f2e3dde90a518cd32048c58918608 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:17 +0000 Subject: [PATCH 0084/4607] drm: Introduce an iterator over holes in the drm_mm range manager This will be used i915 in forthcoming patches in order to measure the largest contiguous chunk of memory available for enabling chipset features. v2: Try to make the macro marginally safer and more readable by not depending upon the drm_mm_hole_node_end() being non-zero. Note that we need to open code list_for_each() in order to update the hole_start, hole_end variable on each iteration and keep the macro sane. v3: Tidy up few BUG_ONs that fell foul of adding additional tests to drm_mm_hole_node_start(). Signed-off-by: Chris Wilson Cc: Dave Airlie Acked-by: Dave Airlie Cc: dri-devel@lists.freedesktop.org Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_mm.c | 62 ++++++++++++++-------------------------- include/drm/drm_mm.h | 36 +++++++++++++++++++++++ 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index bd203b61a72b..b751b8e1e205 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -102,20 +102,6 @@ int drm_mm_pre_get(struct drm_mm *mm) } EXPORT_SYMBOL(drm_mm_pre_get); -static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node) -{ - return hole_node->start + hole_node->size; -} - -static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node) -{ - struct drm_mm_node *next_node = - list_entry(hole_node->node_list.next, struct drm_mm_node, - node_list); - - return next_node->start; -} - static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, unsigned long size, unsigned alignment, @@ -127,7 +113,7 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, unsigned long adj_start = hole_start; unsigned long adj_end = hole_end; - BUG_ON(!hole_node->hole_follows || node->allocated); + BUG_ON(node->allocated); if (mm->color_adjust) mm->color_adjust(hole_node, color, &adj_start, &adj_end); @@ -155,7 +141,7 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, BUG_ON(node->start + node->size > adj_end); node->hole_follows = 0; - if (node->start + node->size < hole_end) { + if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); node->hole_follows = 1; } @@ -168,15 +154,10 @@ struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, { struct drm_mm_node *hole, *node; unsigned long end = start + size; + unsigned long hole_start; + unsigned long hole_end; - list_for_each_entry(hole, &mm->hole_stack, hole_stack) { - unsigned long hole_start; - unsigned long hole_end; - - BUG_ON(!hole->hole_follows); - hole_start = drm_mm_hole_node_start(hole); - hole_end = drm_mm_hole_node_end(hole); - + drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { if (hole_start > start || hole_end < end) continue; @@ -293,7 +274,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, BUG_ON(node->start + node->size > end); node->hole_follows = 0; - if (node->start + node->size < hole_end) { + if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); node->hole_follows = 1; } @@ -358,12 +339,13 @@ void drm_mm_remove_node(struct drm_mm_node *node) list_entry(node->node_list.prev, struct drm_mm_node, node_list); if (node->hole_follows) { - BUG_ON(drm_mm_hole_node_start(node) - == drm_mm_hole_node_end(node)); + BUG_ON(__drm_mm_hole_node_start(node) == + __drm_mm_hole_node_end(node)); list_del(&node->hole_stack); } else - BUG_ON(drm_mm_hole_node_start(node) - != drm_mm_hole_node_end(node)); + BUG_ON(__drm_mm_hole_node_start(node) != + __drm_mm_hole_node_end(node)); + if (!prev_node->hole_follows) { prev_node->hole_follows = 1; @@ -421,6 +403,8 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, { struct drm_mm_node *entry; struct drm_mm_node *best; + unsigned long adj_start; + unsigned long adj_end; unsigned long best_size; BUG_ON(mm->scanned_blocks); @@ -428,17 +412,13 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - list_for_each_entry(entry, &mm->hole_stack, hole_stack) { - unsigned long adj_start = drm_mm_hole_node_start(entry); - unsigned long adj_end = drm_mm_hole_node_end(entry); - + drm_mm_for_each_hole(entry, mm, adj_start, adj_end) { if (mm->color_adjust) { mm->color_adjust(entry, color, &adj_start, &adj_end); if (adj_end <= adj_start) continue; } - BUG_ON(!entry->hole_follows); if (!check_free_hole(adj_start, adj_end, size, alignment)) continue; @@ -465,6 +445,8 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, { struct drm_mm_node *entry; struct drm_mm_node *best; + unsigned long adj_start; + unsigned long adj_end; unsigned long best_size; BUG_ON(mm->scanned_blocks); @@ -472,13 +454,11 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - list_for_each_entry(entry, &mm->hole_stack, hole_stack) { - unsigned long adj_start = drm_mm_hole_node_start(entry) < start ? - start : drm_mm_hole_node_start(entry); - unsigned long adj_end = drm_mm_hole_node_end(entry) > end ? - end : drm_mm_hole_node_end(entry); - - BUG_ON(!entry->hole_follows); + drm_mm_for_each_hole(entry, mm, adj_start, adj_end) { + if (adj_start < start) + adj_start = start; + if (adj_end > end) + adj_end = end; if (mm->color_adjust) { mm->color_adjust(entry, color, &adj_start, &adj_end); diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 4020f9661c3c..cd453653f634 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -89,6 +89,29 @@ static inline bool drm_mm_initialized(struct drm_mm *mm) { return mm->hole_stack.next; } + +static inline unsigned long __drm_mm_hole_node_start(struct drm_mm_node *hole_node) +{ + return hole_node->start + hole_node->size; +} + +static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node) +{ + BUG_ON(!hole_node->hole_follows); + return __drm_mm_hole_node_start(hole_node); +} + +static inline unsigned long __drm_mm_hole_node_end(struct drm_mm_node *hole_node) +{ + return list_entry(hole_node->node_list.next, + struct drm_mm_node, node_list)->start; +} + +static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node) +{ + return __drm_mm_hole_node_end(hole_node); +} + #define drm_mm_for_each_node(entry, mm) list_for_each_entry(entry, \ &(mm)->head_node.node_list, \ node_list) @@ -99,6 +122,19 @@ static inline bool drm_mm_initialized(struct drm_mm *mm) entry != NULL; entry = next, \ next = entry ? list_entry(entry->node_list.next, \ struct drm_mm_node, node_list) : NULL) \ + +/* Note that we need to unroll list_for_each_entry in order to inline + * setting hole_start and hole_end on each iteration and keep the + * macro sane. + */ +#define drm_mm_for_each_hole(entry, mm, hole_start, hole_end) \ + for (entry = list_entry((mm)->hole_stack.next, struct drm_mm_node, hole_stack); \ + &entry->hole_stack != &(mm)->hole_stack ? \ + hole_start = drm_mm_hole_node_start(entry), \ + hole_end = drm_mm_hole_node_end(entry), \ + 1 : 0; \ + entry = list_entry(entry->hole_stack.next, struct drm_mm_node, hole_stack)) + /* * Basic range manager support (drm_mm.c) */ From e12a2d53ae45a69aea499b64f75e7222cca0f12f Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:18 +0000 Subject: [PATCH 0085/4607] drm/i915: Fix detection of base of stolen memory The routine to query the base of stolen memory was using the wrong registers and the wrong encodings on virtually every platform. It was not until the G33 refresh, that a PCI config register was introduced that explicitly said where the stolen memory was. Prior to 865G there was not even a register that said where the end of usable low memory was and where the stolen memory began (or ended depending upon chipset). Before then, one has to look at the BIOS memory maps to find the Top of Memory. Alas that is not exported by arch/x86 and so we have to resort to disabling stolen memory on gen2 for the time being. Then SandyBridge enlarged the PCI register to a full 32-bits and change the encoding of the address, so even though we happened to be querying the right register, we read the wrong bits and ended up using address 0 for our stolen data, i.e. notably FBC. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem_stolen.c | 81 +++++++++++++------------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 31ab43b79736..21360a7e2c8f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -765,6 +765,7 @@ typedef struct drm_i915_private { unsigned long gtt_start; unsigned long gtt_mappable_end; unsigned long gtt_end; + unsigned long stolen_base; /* limited to low memory (32-bit) */ struct io_mapping *gtt_mapping; phys_addr_t gtt_base_addr; diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 8e91083b126f..be24312cd32f 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -42,56 +42,50 @@ * for is a boon. */ -#define PTE_ADDRESS_MASK 0xfffff000 -#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */ -#define PTE_MAPPING_TYPE_UNCACHED (0 << 1) -#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */ -#define PTE_MAPPING_TYPE_CACHED (3 << 1) -#define PTE_MAPPING_TYPE_MASK (3 << 1) -#define PTE_VALID (1 << 0) - -/** - * i915_stolen_to_phys - take an offset into stolen memory and turn it into - * a physical one - * @dev: drm device - * @offset: address to translate - * - * Some chip functions require allocations from stolen space and need the - * physical address of the memory in question. - */ -static unsigned long i915_stolen_to_phys(struct drm_device *dev, u32 offset) +static unsigned long i915_stolen_to_physical(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct pci_dev *pdev = dev_priv->bridge_dev; u32 base; -#if 0 /* On the machines I have tested the Graphics Base of Stolen Memory - * is unreliable, so compute the base by subtracting the stolen memory - * from the Top of Low Usable DRAM which is where the BIOS places - * the graphics stolen memory. + * is unreliable, so on those compute the base by subtracting the + * stolen memory from the Top of Low Usable DRAM which is where the + * BIOS places the graphics stolen memory. + * + * On gen2, the layout is slightly different with the Graphics Segment + * immediately following Top of Memory (or Top of Usable DRAM). Note + * it appears that TOUD is only reported by 865g, so we just use the + * top of memory as determined by the e820 probe. + * + * XXX gen2 requires an unavailable symbol and 945gm fails with + * its value of TOLUD. */ - if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { - /* top 32bits are reserved = 0 */ + base = 0; + if (INTEL_INFO(dev)->gen >= 6) { + /* Read Base Data of Stolen Memory Register (BDSM) directly. + * Note that there is also a MCHBAR miror at 0x1080c0 or + * we could use device 2:0x5c instead. + */ + pci_read_config_dword(pdev, 0xB0, &base); + base &= ~4095; /* lower bits used for locking register */ + } else if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { + /* Read Graphics Base of Stolen Memory directly */ pci_read_config_dword(pdev, 0xA4, &base); - } else { - /* XXX presume 8xx is the same as i915 */ - pci_bus_read_config_dword(pdev->bus, 2, 0x5C, &base); - } -#else - if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { - u16 val; - pci_read_config_word(pdev, 0xb0, &val); - base = val >> 4 << 20; - } else { +#if 0 + } else if (IS_GEN3(dev)) { u8 val; + /* Stolen is immediately below Top of Low Usable DRAM */ pci_read_config_byte(pdev, 0x9c, &val); base = val >> 3 << 27; - } - base -= dev_priv->mm.gtt->stolen_size; + base -= dev_priv->mm.gtt->stolen_size; + } else { + /* Stolen is immediately above Top of Memory */ + base = max_low_pfn_mapped << PAGE_SHIFT; #endif + } - return base + offset; + return base; } static void i915_warn_stolen(struct drm_device *dev) @@ -116,7 +110,7 @@ static void i915_setup_compression(struct drm_device *dev, int size) if (!compressed_fb) goto err; - cfb_base = i915_stolen_to_phys(dev, compressed_fb->start); + cfb_base = dev_priv->mm.stolen_base + compressed_fb->start; if (!cfb_base) goto err_fb; @@ -129,7 +123,7 @@ static void i915_setup_compression(struct drm_device *dev, int size) if (!compressed_llb) goto err_fb; - ll_base = i915_stolen_to_phys(dev, compressed_llb->start); + ll_base = dev_priv->mm.stolen_base + compressed_llb->start; if (!ll_base) goto err_llb; } @@ -148,7 +142,7 @@ static void i915_setup_compression(struct drm_device *dev, int size) } DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", - cfb_base, ll_base, size >> 20); + (long)cfb_base, (long)ll_base, size >> 20); return; err_llb: @@ -180,6 +174,13 @@ int i915_gem_init_stolen(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; unsigned long prealloc_size = dev_priv->mm.gtt->stolen_size; + dev_priv->mm.stolen_base = i915_stolen_to_physical(dev); + if (dev_priv->mm.stolen_base == 0) + return 0; + + DRM_DEBUG_KMS("found %d bytes of stolen memory at %08lx\n", + dev_priv->mm.gtt->stolen_size, dev_priv->mm.stolen_base); + /* Basic memrange allocator for stolen space */ drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size); From ed2f3452677e46a4270c25c1b7fa3e060fdd501e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:19 +0000 Subject: [PATCH 0086/4607] drm/i915: Avoid clearing preallocated regions from the GTT As yet we do not do any preallocation (chicken-and-egg problem), but we may like to preserve anything already allocated by the BIOS or grub and reuse for own purposes after initialising the driver. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_gem_gtt.c | 32 ++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 21360a7e2c8f..f16101fa8b4a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -935,6 +935,8 @@ enum i915_cache_level { I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */ }; +#define I915_GTT_RESERVED ((struct drm_mm_node *)0x1) + struct drm_i915_gem_object_ops { /* Interface between the GEM object and its backing storage. * get_pages() is called once prior to the use of the associated set diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index f7ac61ee1504..cc1be53a65f9 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -531,20 +531,46 @@ void i915_gem_init_global_gtt(struct drm_device *dev, unsigned long end) { drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_mm_node *entry; + struct drm_i915_gem_object *obj; + unsigned long hole_start, hole_end; - /* Substract the guard page ... */ + /* Subtract the guard page ... */ drm_mm_init(&dev_priv->mm.gtt_space, start, end - start - PAGE_SIZE); if (!HAS_LLC(dev)) dev_priv->mm.gtt_space.color_adjust = i915_gtt_color_adjust; + /* Mark any preallocated objects as occupied */ + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { + DRM_DEBUG_KMS("reserving preallocated space: %x + %zx\n", + obj->gtt_offset, obj->base.size); + + BUG_ON(obj->gtt_space != I915_GTT_RESERVED); + obj->gtt_space = drm_mm_create_block(&dev_priv->mm.gtt_space, + obj->gtt_offset, + obj->base.size, + false); + obj->has_global_gtt_mapping = 1; + } + dev_priv->mm.gtt_start = start; dev_priv->mm.gtt_mappable_end = mappable_end; dev_priv->mm.gtt_end = end; dev_priv->mm.gtt_total = end - start; dev_priv->mm.mappable_gtt_total = min(end, mappable_end) - start; - /* ... but ensure that we clear the entire range. */ - i915_ggtt_clear_range(dev, start / PAGE_SIZE, (end-start) / PAGE_SIZE); + /* Clear any non-preallocated blocks */ + drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space, + hole_start, hole_end) { + DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n", + hole_start, hole_end); + i915_ggtt_clear_range(dev, + hole_start / PAGE_SIZE, + (hole_end-hole_start) / PAGE_SIZE); + } + + /* And finally clear the reserved guard page */ + i915_ggtt_clear_range(dev, end / PAGE_SIZE - 1, 1); } static int setup_scratch_page(struct drm_device *dev) From 11be49eb4db24c5e971edef160afb87788cc270c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:20 +0000 Subject: [PATCH 0087/4607] drm/i915: Delay allocation of stolen space for FBC As FBC is commonly disabled due to limitations of the chipset upon output configurations, on many systems FBC is never enabled. For those systems, it is advantageous to make use of the stolen memory for other objects and so we defer allocation of the FBC chunk until we actually require it. This increases the likelihood of that allocation failing, but that in turns means that we are already taking advantage of the stolen memory! As well as delaying the allocation from driver initialisation until the first use of FBC, we also return the stolen block after we finish using it - allowing greater flexibility in our usage of stolen space. A side effect of this is that we can then attempt to allocate only the required amount of space (with a little slack to reduce reallocation rate and avoid fragmentation). Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/i915_gem_stolen.c | 110 ++++++++++++------------- drivers/gpu/drm/i915/intel_display.c | 3 + drivers/gpu/drm/i915/intel_pm.c | 15 ++-- 4 files changed, 65 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f16101fa8b4a..6969c722edf6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1571,6 +1571,8 @@ int i915_gem_evict_everything(struct drm_device *dev); /* i915_gem_stolen.c */ int i915_gem_init_stolen(struct drm_device *dev); +int i915_gem_stolen_setup_compression(struct drm_device *dev, int size); +void i915_gem_stolen_cleanup_compression(struct drm_device *dev); void i915_gem_cleanup_stolen(struct drm_device *dev); /* i915_gem_tiling.c */ diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index be24312cd32f..10ca473d85f2 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -88,33 +88,27 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev) return base; } -static void i915_warn_stolen(struct drm_device *dev) -{ - DRM_INFO("not enough stolen space for compressed buffer, disabling\n"); - DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); -} - -static void i915_setup_compression(struct drm_device *dev, int size) +static int i915_setup_compression(struct drm_device *dev, int size) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb); - unsigned long cfb_base; - unsigned long ll_base = 0; - /* Just in case the BIOS is doing something questionable. */ - intel_disable_fbc(dev); - - compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); + /* Try to over-allocate to reduce reallocations and fragmentation */ + compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, + size <<= 1, 4096, 0); + if (!compressed_fb) + compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, + size >>= 1, 4096, 0); if (compressed_fb) compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); if (!compressed_fb) goto err; - cfb_base = dev_priv->mm.stolen_base + compressed_fb->start; - if (!cfb_base) - goto err_fb; - - if (!(IS_GM45(dev) || HAS_PCH_SPLIT(dev))) { + if (HAS_PCH_SPLIT(dev)) + I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); + else if (IS_GM45(dev)) { + I915_WRITE(DPFC_CB_BASE, compressed_fb->start); + } else { compressed_llb = drm_mm_search_free(&dev_priv->mm.stolen, 4096, 4096, 0); if (compressed_llb) @@ -123,56 +117,68 @@ static void i915_setup_compression(struct drm_device *dev, int size) if (!compressed_llb) goto err_fb; - ll_base = dev_priv->mm.stolen_base + compressed_llb->start; - if (!ll_base) - goto err_llb; - } + dev_priv->compressed_llb = compressed_llb; - dev_priv->cfb_size = size; + I915_WRITE(FBC_CFB_BASE, + dev_priv->mm.stolen_base + compressed_fb->start); + I915_WRITE(FBC_LL_BASE, + dev_priv->mm.stolen_base + compressed_llb->start); + } dev_priv->compressed_fb = compressed_fb; - if (HAS_PCH_SPLIT(dev)) - I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); - else if (IS_GM45(dev)) { - I915_WRITE(DPFC_CB_BASE, compressed_fb->start); - } else { - I915_WRITE(FBC_CFB_BASE, cfb_base); - I915_WRITE(FBC_LL_BASE, ll_base); - dev_priv->compressed_llb = compressed_llb; - } + dev_priv->cfb_size = size; - DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", - (long)cfb_base, (long)ll_base, size >> 20); - return; + DRM_DEBUG_KMS("reserved %d bytes of contiguous stolen space for FBC\n", + size); + + return 0; -err_llb: - drm_mm_put_block(compressed_llb); err_fb: drm_mm_put_block(compressed_fb); err: - dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; - i915_warn_stolen(dev); + return -ENOSPC; } -static void i915_cleanup_compression(struct drm_device *dev) +int i915_gem_stolen_setup_compression(struct drm_device *dev, int size) { struct drm_i915_private *dev_priv = dev->dev_private; - drm_mm_put_block(dev_priv->compressed_fb); + if (dev_priv->mm.stolen_base == 0) + return -ENODEV; + + if (size < dev_priv->cfb_size) + return 0; + + /* Release any current block */ + i915_gem_stolen_cleanup_compression(dev); + + return i915_setup_compression(dev, size); +} + +void i915_gem_stolen_cleanup_compression(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->cfb_size == 0) + return; + + if (dev_priv->compressed_fb) + drm_mm_put_block(dev_priv->compressed_fb); + if (dev_priv->compressed_llb) drm_mm_put_block(dev_priv->compressed_llb); + + dev_priv->cfb_size = 0; } void i915_gem_cleanup_stolen(struct drm_device *dev) { - if (I915_HAS_FBC(dev) && i915_powersave) - i915_cleanup_compression(dev); + i915_gem_stolen_cleanup_compression(dev); } int i915_gem_init_stolen(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long prealloc_size = dev_priv->mm.gtt->stolen_size; dev_priv->mm.stolen_base = i915_stolen_to_physical(dev); if (dev_priv->mm.stolen_base == 0) @@ -182,21 +188,7 @@ int i915_gem_init_stolen(struct drm_device *dev) dev_priv->mm.gtt->stolen_size, dev_priv->mm.stolen_base); /* Basic memrange allocator for stolen space */ - drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size); - - /* Try to set up FBC with a reasonable compressed buffer size */ - if (I915_HAS_FBC(dev) && i915_powersave) { - int cfb_size; - - /* Leave 1M for line length buffer & misc. */ - - /* Try to get a 32M buffer... */ - if (prealloc_size > (36*1024*1024)) - cfb_size = 32*1024*1024; - else /* fall back to 7/8 of the stolen space */ - cfb_size = prealloc_size * 7 / 8; - i915_setup_compression(dev, cfb_size); - } + drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->mm.gtt->stolen_size); return 0; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 52b6b0e811bc..d303f2a90e70 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8655,6 +8655,9 @@ void intel_modeset_init(struct drm_device *dev) /* Just disable it once at startup */ i915_disable_vga(dev); intel_setup_outputs(dev); + + /* Just in case the BIOS is doing something questionable. */ + intel_disable_fbc(dev); } static void diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index f595b8d56cc3..abfff29ed13a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -440,12 +440,6 @@ void intel_update_fbc(struct drm_device *dev) dev_priv->no_fbc_reason = FBC_MODULE_PARAM; goto out_disable; } - if (intel_fb->obj->base.size > dev_priv->cfb_size) { - DRM_DEBUG_KMS("framebuffer too large, disabling " - "compression\n"); - dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; - goto out_disable; - } if ((crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) || (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)) { DRM_DEBUG_KMS("mode incompatible with compression, " @@ -479,6 +473,14 @@ void intel_update_fbc(struct drm_device *dev) if (in_dbg_master()) goto out_disable; + if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size)) { + DRM_INFO("not enough stolen space for compressed buffer (need %zd bytes), disabling\n", intel_fb->obj->base.size); + DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); + DRM_DEBUG_KMS("framebuffer too large, disabling compression\n"); + dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; + goto out_disable; + } + /* If the scanout has not changed, don't modify the FBC settings. * Note that we make the fundamental assumption that the fb->obj * cannot be unpinned (and have its GTT offset and fence revoked) @@ -526,6 +528,7 @@ out_disable: DRM_DEBUG_KMS("unsupported config, disabling FBC\n"); intel_disable_fbc(dev); } + i915_gem_stolen_cleanup_compression(dev); } static void i915_pineview_get_mem_freq(struct drm_device *dev) From c1ad11fce86405c955873974f58ab305d894be78 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:21 +0000 Subject: [PATCH 0088/4607] drm/i915: Allow objects to be created with no backing pages, but stolen space In order to accommodate objects that are not backed by struct pages, but instead point into a contiguous region of stolen space, we need to make various changes to avoid dereferencing obj->pages or obj->base.filp. First introduce a marker for the stolen object, that specifies its offset into the stolen region and implies that it has no backing pages. Signed-off-by: Chris Wilson Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 ++ drivers/gpu/drm/i915/i915_drv.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 62619e39533f..58e667687741 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -124,6 +124,8 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) if (obj->gtt_space != NULL) seq_printf(m, " (gtt offset: %08x, size: %08x)", obj->gtt_offset, (unsigned int)obj->gtt_space->size); + if (obj->stolen) + seq_printf(m, " (stolen: %08lx)", obj->stolen->start); if (obj->pin_mappable || obj->fault_mappable) { char s[3], *t = s; if (obj->pin_mappable) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6969c722edf6..1646d036db09 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -962,6 +962,8 @@ struct drm_i915_gem_object { /** Current space allocated to this object in the GTT, if any. */ struct drm_mm_node *gtt_space; + /** Stolen memory for this object, instead of being backed by shmem. */ + struct drm_mm_node *stolen; struct list_head gtt_list; /** This object's place on the active/inactive lists */ From 960e3564bfa464f92549383d41659f2aaeee1420 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:23 +0000 Subject: [PATCH 0089/4607] drm/i915: Support readback of stolen objects upon error Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 26753ee6571d..7a0ddee9751a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -929,6 +929,14 @@ i915_error_object_create(struct drm_i915_private *dev_priv, reloc_offset); memcpy_fromio(d, s, PAGE_SIZE); io_mapping_unmap_atomic(s); + } else if (src->stolen) { + unsigned long offset; + + offset = dev_priv->mm.stolen_base; + offset += src->stolen->start; + offset += i << PAGE_SHIFT; + + memcpy_fromio(d, (void *)offset, PAGE_SIZE); } else { struct page *page; void *s; From 0104fdbb84d7adb0e377ed05bf75eba97b007544 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:26 +0000 Subject: [PATCH 0090/4607] drm/i915: Introduce i915_gem_object_create_stolen() Allow for the creation of GEM objects backed by stolen memory. As these are not backed by ordinary pages, we create a fake dma mapping and store the address in the scatterlist rather than obj->pages. v2: Mark _i915_gem_object_create_stolen() as static, as noticed by Jesse Barnes. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 3 + drivers/gpu/drm/i915/i915_gem.c | 1 + drivers/gpu/drm/i915/i915_gem_stolen.c | 125 +++++++++++++++++++++++++ 3 files changed, 129 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1646d036db09..d1d68f0b55e8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1576,6 +1576,9 @@ int i915_gem_init_stolen(struct drm_device *dev); int i915_gem_stolen_setup_compression(struct drm_device *dev, int size); void i915_gem_stolen_cleanup_compression(struct drm_device *dev); void i915_gem_cleanup_stolen(struct drm_device *dev); +struct drm_i915_gem_object * +i915_gem_object_create_stolen(struct drm_device *dev, u32 size); +void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj); /* i915_gem_tiling.c */ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5c8df572cd17..3de62b0127a5 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3772,6 +3772,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) obj->pages_pin_count = 0; i915_gem_object_put_pages(obj); i915_gem_object_free_mmap_offset(obj); + i915_gem_object_release_stolen(obj); BUG_ON(obj->pages); diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 10ca473d85f2..7299d632663c 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -192,3 +192,128 @@ int i915_gem_init_stolen(struct drm_device *dev) return 0; } + +static struct sg_table * +i915_pages_create_for_stolen(struct drm_device *dev, + u32 offset, u32 size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct sg_table *st; + struct scatterlist *sg; + + DRM_DEBUG_DRIVER("offset=0x%x, size=%d\n", offset, size); + BUG_ON(offset > dev_priv->mm.gtt->stolen_size - size); + + /* We hide that we have no struct page backing our stolen object + * by wrapping the contiguous physical allocation with a fake + * dma mapping in a single scatterlist. + */ + + st = kmalloc(sizeof(*st), GFP_KERNEL); + if (st == NULL) + return NULL; + + if (sg_alloc_table(st, 1, GFP_KERNEL)) { + kfree(st); + return NULL; + } + + sg = st->sgl; + sg->offset = offset; + sg->length = size; + + sg_dma_address(sg) = (dma_addr_t)dev_priv->mm.stolen_base + offset; + sg_dma_len(sg) = size; + + return st; +} + +static int i915_gem_object_get_pages_stolen(struct drm_i915_gem_object *obj) +{ + BUG(); + return -EINVAL; +} + +static void i915_gem_object_put_pages_stolen(struct drm_i915_gem_object *obj) +{ + /* Should only be called during free */ + sg_free_table(obj->pages); + kfree(obj->pages); +} + +static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = { + .get_pages = i915_gem_object_get_pages_stolen, + .put_pages = i915_gem_object_put_pages_stolen, +}; + +static struct drm_i915_gem_object * +_i915_gem_object_create_stolen(struct drm_device *dev, + struct drm_mm_node *stolen) +{ + struct drm_i915_gem_object *obj; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (obj == NULL) + return NULL; + + if (drm_gem_private_object_init(dev, &obj->base, stolen->size)) + goto cleanup; + + i915_gem_object_init(obj, &i915_gem_object_stolen_ops); + + obj->pages = i915_pages_create_for_stolen(dev, + stolen->start, stolen->size); + if (obj->pages == NULL) + goto cleanup; + + obj->has_dma_mapping = true; + obj->pages_pin_count = 1; + obj->stolen = stolen; + + obj->base.write_domain = I915_GEM_DOMAIN_GTT; + obj->base.read_domains = I915_GEM_DOMAIN_GTT; + obj->cache_level = I915_CACHE_NONE; + + return obj; + +cleanup: + kfree(obj); + return NULL; +} + +struct drm_i915_gem_object * +i915_gem_object_create_stolen(struct drm_device *dev, u32 size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj; + struct drm_mm_node *stolen; + + if (dev_priv->mm.stolen_base == 0) + return NULL; + + DRM_DEBUG_KMS("creating stolen object: size=%x\n", size); + if (size == 0) + return NULL; + + stolen = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); + if (stolen) + stolen = drm_mm_get_block(stolen, size, 4096); + if (stolen == NULL) + return NULL; + + obj = _i915_gem_object_create_stolen(dev, stolen); + if (obj) + return obj; + + drm_mm_put_block(stolen); + return NULL; +} + +void +i915_gem_object_release_stolen(struct drm_i915_gem_object *obj) +{ + if (obj->stolen) { + drm_mm_put_block(obj->stolen); + obj->stolen = NULL; + } +} From 0ffb0ff283cca16f72caf29c44496d83b0c291fb Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:27 +0000 Subject: [PATCH 0091/4607] drm/i915: Allocate fbcon from stolen memory Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Acked-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_fb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 7b30b5c2c4ee..b7773e548911 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -83,7 +83,9 @@ static int intelfb_create(struct intel_fbdev *ifbdev, size = mode_cmd.pitches[0] * mode_cmd.height; size = ALIGN(size, PAGE_SIZE); - obj = i915_gem_alloc_object(dev, size); + obj = i915_gem_object_create_stolen(dev, size); + if (obj == NULL) + obj = i915_gem_alloc_object(dev, size); if (!obj) { DRM_ERROR("failed to allocate framebuffer\n"); ret = -ENOMEM; From ebc052e0c65f84f68626e388ffa1704b3a190ef7 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:28 +0000 Subject: [PATCH 0092/4607] drm/i915: Allocate ringbuffers from stolen memory Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Acked-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ringbuffer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index bc7cf7c63108..36e1e13ae946 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1113,7 +1113,11 @@ static int intel_init_ring_buffer(struct drm_device *dev, return ret; } - obj = i915_gem_alloc_object(dev, ring->size); + obj = NULL; + if (!HAS_LLC(dev)) + obj = i915_gem_object_create_stolen(dev, ring->size); + if (obj == NULL) + obj = i915_gem_alloc_object(dev, ring->size); if (obj == NULL) { DRM_ERROR("Failed to allocate ringbuffer\n"); ret = -ENOMEM; From 8040513870399f1cb032cb8bc805df5042fedcdf Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:29 +0000 Subject: [PATCH 0093/4607] drm/i915: Allocate overlay registers from stolen memory Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Acked-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_overlay.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index d7bc817f51a0..fabe0acf808d 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -1333,8 +1333,10 @@ void intel_setup_overlay(struct drm_device *dev) overlay->dev = dev; - reg_bo = i915_gem_alloc_object(dev, PAGE_SIZE); - if (!reg_bo) + reg_bo = i915_gem_object_create_stolen(dev, PAGE_SIZE); + if (reg_bo == NULL) + reg_bo = i915_gem_alloc_object(dev, PAGE_SIZE); + if (reg_bo == NULL) goto out_free; overlay->reg_bo = reg_bo; From 42dcedd4f2e715dc0313e359c8288e6397843fff Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:30 +0000 Subject: [PATCH 0094/4607] drm/i915: Use a slab for object allocation The primary purpose of this was to debug some use-after-free memory corruption that was causing an OOPS inside drm/i915. As it turned out the corruption was being caused elsewhere and i915.ko as a major user of many objects was being hit hardest. Indeed as we do frequent the generic kmalloc caches, dedicating one to ourselves (or at least naming one for us depending upon the core) aids debugging our own slab usage. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 +++ drivers/gpu/drm/i915/i915_drv.h | 4 ++++ drivers/gpu/drm/i915/i915_gem.c | 28 +++++++++++++++++++++----- drivers/gpu/drm/i915/i915_gem_dmabuf.c | 5 ++--- drivers/gpu/drm/i915/i915_gem_stolen.c | 4 ++-- 5 files changed, 34 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 80ed75117b6d..2635ee6a34d4 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1736,6 +1736,9 @@ int i915_driver_unload(struct drm_device *dev) destroy_workqueue(dev_priv->wq); + if (dev_priv->slab) + kmem_cache_destroy(dev_priv->slab); + pci_dev_put(dev_priv->bridge_dev); kfree(dev->dev_private); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d1d68f0b55e8..e2944e9bc750 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -614,6 +614,7 @@ struct intel_l3_parity { typedef struct drm_i915_private { struct drm_device *dev; + struct kmem_cache *slab; const struct intel_device_info *info; @@ -1379,12 +1380,15 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); +void *i915_gem_object_alloc(struct drm_device *dev); +void i915_gem_object_free(struct drm_i915_gem_object *obj); int i915_gem_init_object(struct drm_gem_object *obj); void i915_gem_object_init(struct drm_i915_gem_object *obj, const struct drm_i915_gem_object_ops *ops); struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, size_t size); void i915_gem_free_object(struct drm_gem_object *obj); + int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj, uint32_t alignment, bool map_and_fenceable, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 3de62b0127a5..dfe7174a7c03 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -192,6 +192,18 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, return 0; } +void *i915_gem_object_alloc(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + return kmem_cache_alloc(dev_priv->slab, GFP_KERNEL | __GFP_ZERO); +} + +void i915_gem_object_free(struct drm_i915_gem_object *obj) +{ + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + kmem_cache_free(dev_priv->slab, obj); +} + static int i915_gem_create(struct drm_file *file, struct drm_device *dev, @@ -215,7 +227,7 @@ i915_gem_create(struct drm_file *file, if (ret) { drm_gem_object_release(&obj->base); i915_gem_info_remove_obj(dev->dev_private, obj->base.size); - kfree(obj); + i915_gem_object_free(obj); return ret; } @@ -3695,12 +3707,12 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, struct address_space *mapping; u32 mask; - obj = kzalloc(sizeof(*obj), GFP_KERNEL); + obj = i915_gem_object_alloc(dev); if (obj == NULL) return NULL; if (drm_gem_object_init(dev, &obj->base, size) != 0) { - kfree(obj); + i915_gem_object_free(obj); return NULL; } @@ -3783,7 +3795,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) i915_gem_info_remove_obj(dev_priv, obj->base.size); kfree(obj->bit_17); - kfree(obj); + i915_gem_object_free(obj); } int @@ -4101,8 +4113,14 @@ init_ring_lists(struct intel_ring_buffer *ring) void i915_gem_load(struct drm_device *dev) { - int i; drm_i915_private_t *dev_priv = dev->dev_private; + int i; + + dev_priv->slab = + kmem_cache_create("i915_gem_object", + sizeof(struct drm_i915_gem_object), 0, + SLAB_HWCACHE_ALIGN, + NULL); INIT_LIST_HEAD(&dev_priv->mm.active_list); INIT_LIST_HEAD(&dev_priv->mm.inactive_list); diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index 773ef77b6c22..defb888ef7f5 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -276,8 +276,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, if (IS_ERR(attach)) return ERR_CAST(attach); - - obj = kzalloc(sizeof(*obj), GFP_KERNEL); + obj = i915_gem_object_alloc(dev); if (obj == NULL) { ret = -ENOMEM; goto fail_detach; @@ -285,7 +284,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, ret = drm_gem_private_object_init(dev, &obj->base, dma_buf->size); if (ret) { - kfree(obj); + i915_gem_object_free(obj); goto fail_detach; } diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 7299d632663c..f817b0cac116 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -252,7 +252,7 @@ _i915_gem_object_create_stolen(struct drm_device *dev, { struct drm_i915_gem_object *obj; - obj = kzalloc(sizeof(*obj), GFP_KERNEL); + obj = i915_gem_object_alloc(dev); if (obj == NULL) return NULL; @@ -277,7 +277,7 @@ _i915_gem_object_create_stolen(struct drm_device *dev, return obj; cleanup: - kfree(obj); + i915_gem_object_free(obj); return NULL; } From 4239ca779dbe47a310a3106d9f4cd5458014bdb6 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Mon, 3 Dec 2012 16:26:16 +0000 Subject: [PATCH 0095/4607] drm/i915: Fix dieing -> dying typo Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index dfe7174a7c03..b68bc02745db 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2818,7 +2818,7 @@ static bool i915_gem_valid_gtt_space(struct drm_device *dev, /* On non-LLC machines we have to be careful when putting differing * types of snoopable memory together to avoid the prefetcher - * crossing memory domains and dieing. + * crossing memory domains and dying. */ if (HAS_LLC(dev)) return true; From 2a2d548240ff5f5bd826640bdf8baa016ab6cec4 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 3 Dec 2012 11:49:06 +0000 Subject: [PATCH 0096/4607] drm/i915: Tighten the checks for invalid relocation domains Be specific for the GPU domains so that we can detect if userspace ever passed in an invalid combination, as well as accurately reflect the known GPU domains when printing state. Fixes i-g-t/gem_exec_bad_domains References: https://bugs.freedesktop.org/show_bug.cgi?id=57826 Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e2944e9bc750..3473298a137f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -83,7 +83,12 @@ enum port { }; #define port_name(p) ((p) + 'A') -#define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) +#define I915_GEM_GPU_DOMAINS \ + (I915_GEM_DOMAIN_RENDER | \ + I915_GEM_DOMAIN_SAMPLER | \ + I915_GEM_DOMAIN_COMMAND | \ + I915_GEM_DOMAIN_INSTRUCTION | \ + I915_GEM_DOMAIN_VERTEX) #define for_each_pipe(p) for ((p) = 0; (p) < dev_priv->num_pipe; (p)++) From c1f093e09c4ceb583b04d11e767bb3201812e4d2 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 3 Dec 2012 11:49:07 +0000 Subject: [PATCH 0097/4607] drm/i915: Remove check for conflicting relocation write-domains Simply use the last write-domain set for the object in the batch, trusting userspace to have correctly flushed the caches between usage as a write target. This check dates back from the golden age of having only a single operation per batch with the kernel repeating it for each cliprect, and conflicts both with userspace trying to efficiently batch multiple operations and with reducing the kernel overhead of relocation processing. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 802d925b2b57..6cd3e1c1629e 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -150,17 +150,6 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, reloc->write_domain); return ret; } - if (unlikely(reloc->write_domain && target_obj->pending_write_domain && - reloc->write_domain != target_obj->pending_write_domain)) { - DRM_DEBUG("Write domain conflict: " - "obj %p target %d offset %d " - "new %08x old %08x\n", - obj, reloc->target_handle, - (int) reloc->offset, - reloc->write_domain, - target_obj->pending_write_domain); - return ret; - } target_obj->pending_read_domains |= reloc->read_domains; target_obj->pending_write_domain |= reloc->write_domain; From 1a240d4de2ccf40de5796a4d1dbb3a0236051fc9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 22:18:51 +0100 Subject: [PATCH 0098/4607] drm/i915: fixup sparse warnings - __iomem where there is none (I love how we mix these things up). - Use gfp_t instead of an other plain type. - Unconfuse one place about enum pipe vs enum transcoder - for the pch transcoder we actually use the pipe enum. Fixup the other cases where we assign the pipe to the cpu transcoder with explicit casts. - Declare the mch_lock properly in a header. There is still a decent mess in intel_bios.c about __iomem, but heck, this is x86 and we're allowed to do that. Makes-sparse-happy: Chris Wilson [danvet: Use a space after the cast consistently and fix up the newly-added cast in i915_irq.c to properly use __iomem.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 4 ++-- drivers/gpu/drm/i915/i915_drv.h | 3 +++ drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/i915_irq.c | 5 +---- drivers/gpu/drm/i915/intel_ddi.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 9 +++++---- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 58e667687741..ce60506b574b 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -546,11 +546,11 @@ static int i915_hws_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct intel_ring_buffer *ring; - const volatile u32 __iomem *hws; + const u32 *hws; int i; ring = &dev_priv->ring[(uintptr_t)node->info_ent->data]; - hws = (volatile u32 __iomem *)ring->status_page.page_addr; + hws = ring->status_page.page_addr; if (hws == NULL) return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3473298a137f..bb0eb0f8f6b3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -577,6 +577,9 @@ struct intel_gen6_power_mgmt { struct mutex hw_lock; }; +/* defined intel_pm.c */ +extern spinlock_t mchdev_lock; + struct intel_ilk_power_mgmt { u8 cur_delay; u8 min_delay; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b68bc02745db..cb941448c2bc 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3705,7 +3705,7 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, { struct drm_i915_gem_object *obj; struct address_space *mapping; - u32 mask; + gfp_t mask; obj = i915_gem_object_alloc(dev); if (obj == NULL) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 7a0ddee9751a..68e7cafa13de 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -300,9 +300,6 @@ static void i915_hotplug_work_func(struct work_struct *work) drm_helper_hpd_irq_event(dev); } -/* defined intel_pm.c */ -extern spinlock_t mchdev_lock; - static void ironlake_handle_rps_change(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -936,7 +933,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, offset += src->stolen->start; offset += i << PAGE_SHIFT; - memcpy_fromio(d, (void *)offset, PAGE_SIZE); + memcpy_fromio(d, (void __iomem *) offset, PAGE_SIZE); } else { struct page *page; void *s; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index ad936c68e4cb..f02b3feff504 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1044,7 +1044,7 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) if (port == PORT_A) cpu_transcoder = TRANSCODER_EDP; else - cpu_transcoder = pipe; + cpu_transcoder = (enum transcoder) pipe; tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d303f2a90e70..093a163d4b1c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1671,7 +1671,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, BUG_ON(dev_priv->info->gen < 5); /* FDI must be feeding us bits for PCH ports */ - assert_fdi_tx_enabled(dev_priv, cpu_transcoder); + assert_fdi_tx_enabled(dev_priv, (enum pipe) cpu_transcoder); assert_fdi_rx_enabled(dev_priv, TRANSCODER_A); /* Workaround: set timing override bit. */ @@ -1759,7 +1759,7 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, { enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); - enum transcoder pch_transcoder; + enum pipe pch_transcoder; int reg; u32 val; @@ -1779,7 +1779,8 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, if (pch_port) { /* if driving the PCH, we need FDI enabled */ assert_fdi_rx_pll_enabled(dev_priv, pch_transcoder); - assert_fdi_tx_pll_enabled(dev_priv, cpu_transcoder); + assert_fdi_tx_pll_enabled(dev_priv, + (enum pipe) cpu_transcoder); } /* FIXME: assert CPU port conditions for SNB+ */ } @@ -3598,7 +3599,7 @@ static void haswell_crtc_off(struct drm_crtc *crtc) /* Stop saying we're using TRANSCODER_EDP because some other CRTC might * start using it. */ - intel_crtc->cpu_transcoder = intel_crtc->pipe; + intel_crtc->cpu_transcoder = (enum transcoder) intel_crtc->pipe; intel_ddi_put_crtc_pll(crtc); } From 6823627b933decf5842fcfc37cc71c549f56c6a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Cardona?= Date: Fri, 28 Sep 2012 08:59:32 -0300 Subject: [PATCH 0099/4607] [media] dw2102: Declare MODULE_FIRMWARE usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémi Cardona Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dw2102.c | 36 +++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 9382895b1b88..937c744217c8 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -80,6 +80,15 @@ #define DW2102_RC_QUERY (0x1a00) #define DW2102_LED_CTRL (0x1b00) +#define DW2101_FIRMWARE "dvb-usb-dw2101.fw" +#define DW2102_FIRMWARE "dvb-usb-dw2102.fw" +#define DW2104_FIRMWARE "dvb-usb-dw2104.fw" +#define DW3101_FIRMWARE "dvb-usb-dw3101.fw" +#define S630_FIRMWARE "dvb-usb-s630.fw" +#define S660_FIRMWARE "dvb-usb-s660.fw" +#define P1100_FIRMWARE "dvb-usb-p1100.fw" +#define P7500_FIRMWARE "dvb-usb-p7500.fw" + #define err_str "did not find the firmware file. (%s) " \ "Please see linux/Documentation/dvb/ for more details " \ "on firmware-problems." @@ -1478,13 +1487,12 @@ static int dw2102_load_firmware(struct usb_device *dev, u8 reset; u8 reset16[] = {0, 0, 0, 0, 0, 0, 0}; const struct firmware *fw; - const char *fw_2101 = "dvb-usb-dw2101.fw"; switch (dev->descriptor.idProduct) { case 0x2101: - ret = request_firmware(&fw, fw_2101, &dev->dev); + ret = request_firmware(&fw, DW2101_FIRMWARE, &dev->dev); if (ret != 0) { - err(err_str, fw_2101); + err(err_str, DW2101_FIRMWARE); return ret; } break; @@ -1586,7 +1594,7 @@ static int dw2102_load_firmware(struct usb_device *dev, static struct dvb_usb_device_properties dw2102_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw2102.fw", + .firmware = DW2102_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw2102_serit_i2c_algo, @@ -1641,7 +1649,7 @@ static struct dvb_usb_device_properties dw2102_properties = { static struct dvb_usb_device_properties dw2104_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw2104.fw", + .firmware = DW2104_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw2104_i2c_algo, @@ -1691,7 +1699,7 @@ static struct dvb_usb_device_properties dw2104_properties = { static struct dvb_usb_device_properties dw3101_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, - .firmware = "dvb-usb-dw3101.fw", + .firmware = DW3101_FIRMWARE, .no_reconnect = 1, .i2c_algo = &dw3101_i2c_algo, @@ -1739,7 +1747,7 @@ static struct dvb_usb_device_properties s6x0_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, .size_of_priv = sizeof(struct s6x0_state), - .firmware = "dvb-usb-s630.fw", + .firmware = S630_FIRMWARE, .no_reconnect = 1, .i2c_algo = &s6x0_i2c_algo, @@ -1879,7 +1887,7 @@ static int dw2102_probe(struct usb_interface *intf, return -ENOMEM; /* copy default structure */ /* fill only different fields */ - p1100->firmware = "dvb-usb-p1100.fw"; + p1100->firmware = P1100_FIRMWARE; p1100->devices[0] = d1100; p1100->rc.legacy.rc_map_table = rc_map_tbs_table; p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); @@ -1891,7 +1899,7 @@ static int dw2102_probe(struct usb_interface *intf, kfree(p1100); return -ENOMEM; } - s660->firmware = "dvb-usb-s660.fw"; + s660->firmware = S660_FIRMWARE; s660->num_device_descs = 3; s660->devices[0] = d660; s660->devices[1] = d480_1; @@ -1905,7 +1913,7 @@ static int dw2102_probe(struct usb_interface *intf, kfree(s660); return -ENOMEM; } - p7500->firmware = "dvb-usb-p7500.fw"; + p7500->firmware = P7500_FIRMWARE; p7500->devices[0] = d7500; p7500->rc.legacy.rc_map_table = rc_map_tbs_table; p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); @@ -1949,3 +1957,11 @@ MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," " Geniatech SU3000 devices"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(DW2101_FIRMWARE); +MODULE_FIRMWARE(DW2102_FIRMWARE); +MODULE_FIRMWARE(DW2104_FIRMWARE); +MODULE_FIRMWARE(DW3101_FIRMWARE); +MODULE_FIRMWARE(S630_FIRMWARE); +MODULE_FIRMWARE(S660_FIRMWARE); +MODULE_FIRMWARE(P1100_FIRMWARE); +MODULE_FIRMWARE(P7500_FIRMWARE); From 16427faf28674451a7a0485ab0a929402f355ffd Mon Sep 17 00:00:00 2001 From: Julian Scheel Date: Thu, 4 Oct 2012 10:04:28 -0300 Subject: [PATCH 0100/4607] [media] tm6000: Add parameter to keep urb bufs allocated On systems where it cannot be assured that enough continous memory is available all the time it can be very useful to only allocate the memory once when it is needed the first time. Afterwards the initially allocated memory will be reused, so it is ensured that the memory will stay available until the driver is unloaded. [mchehab@redhat.com: Codingstyle fixups] Signed-off-by: Julian Scheel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 112 ++++++++++++++++++++---- drivers/media/usb/tm6000/tm6000.h | 5 ++ 2 files changed, 98 insertions(+), 19 deletions(-) diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index f656fd7a39a2..82968017a253 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -49,12 +49,15 @@ #define TM6000_MIN_BUF 4 #define TM6000_DEF_BUF 8 +#define TM6000_NUM_URB_BUF 8 + #define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */ /* Declare static vars that will be used as parameters */ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ +static int keep_urb; /* keep urb buffers allocated */ /* Debug level */ int tm6000_debug; @@ -537,6 +540,71 @@ static void tm6000_irq_callback(struct urb *urb) urb->status); } +/* + * Allocate URB buffers + */ +static int tm6000_alloc_urb_buffers(struct tm6000_core *dev) +{ + int num_bufs = TM6000_NUM_URB_BUF; + int i; + + if (dev->urb_buffer != NULL) + return 0; + + dev->urb_buffer = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->urb_buffer) { + tm6000_err("cannot allocate memory for urb buffers\n"); + return -ENOMEM; + } + + dev->urb_dma = kmalloc(sizeof(dma_addr_t *)*num_bufs, GFP_KERNEL); + if (!dev->urb_dma) { + tm6000_err("cannot allocate memory for urb dma pointers\n"); + return -ENOMEM; + } + + for (i = 0; i < num_bufs; i++) { + dev->urb_buffer[i] = usb_alloc_coherent( + dev->udev, dev->urb_size, + GFP_KERNEL, &dev->urb_dma[i]); + if (!dev->urb_buffer[i]) { + tm6000_err("unable to allocate %i bytes for transfer buffer %i\n", + dev->urb_size, i); + return -ENOMEM; + } + memset(dev->urb_buffer[i], 0, dev->urb_size); + } + + return 0; +} + +/* + * Free URB buffers + */ +static int tm6000_free_urb_buffers(struct tm6000_core *dev) +{ + int i; + + if (dev->urb_buffer == NULL) + return 0; + + for (i = 0; i < TM6000_NUM_URB_BUF; i++) { + if (dev->urb_buffer[i]) { + usb_free_coherent(dev->udev, + dev->urb_size, + dev->urb_buffer[i], + dev->urb_dma[i]); + dev->urb_buffer[i] = NULL; + } + } + kfree(dev->urb_buffer); + kfree(dev->urb_dma); + dev->urb_buffer = NULL; + dev->urb_dma = NULL; + + return 0; +} + /* * Stop and Deallocate URBs */ @@ -551,18 +619,15 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) if (urb) { usb_kill_urb(urb); usb_unlink_urb(urb); - if (dev->isoc_ctl.transfer_buffer[i]) { - usb_free_coherent(dev->udev, - urb->transfer_buffer_length, - dev->isoc_ctl.transfer_buffer[i], - urb->transfer_dma); - } usb_free_urb(urb); dev->isoc_ctl.urb[i] = NULL; } dev->isoc_ctl.transfer_buffer[i] = NULL; } + if (!keep_urb) + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); kfree(dev->isoc_ctl.transfer_buffer); @@ -572,12 +637,13 @@ static void tm6000_uninit_isoc(struct tm6000_core *dev) } /* - * Allocate URBs and start IRQ + * Assign URBs and start IRQ */ static int tm6000_prepare_isoc(struct tm6000_core *dev) { struct tm6000_dmaqueue *dma_q = &dev->vidq; - int i, j, sb_size, pipe, size, max_packets, num_bufs = 8; + int i, j, sb_size, pipe, size, max_packets; + int num_bufs = TM6000_NUM_URB_BUF; struct urb *urb; /* De-allocates all pending stuff */ @@ -605,6 +671,7 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) max_packets = TM6000_MAX_ISO_PACKETS; sb_size = max_packets * size; + dev->urb_size = sb_size; dev->isoc_ctl.num_bufs = num_bufs; @@ -627,6 +694,17 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) max_packets, num_bufs, sb_size, dev->isoc_in.maxsize, size); + + if (!dev->urb_buffer && tm6000_alloc_urb_buffers(dev) < 0) { + tm6000_err("cannot allocate memory for urb buffers\n"); + + /* call free, as some buffers might have been allocated */ + tm6000_free_urb_buffers(dev); + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + return -ENOMEM; + } + /* allocate urbs and transfer buffers */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { urb = usb_alloc_urb(max_packets, GFP_KERNEL); @@ -638,17 +716,8 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev) } dev->isoc_ctl.urb[i] = urb; - dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev, - sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!dev->isoc_ctl.transfer_buffer[i]) { - tm6000_err("unable to allocate %i bytes for transfer" - " buffer %i%s\n", - sb_size, i, - in_interrupt() ? " while in int" : ""); - tm6000_uninit_isoc(dev); - return -ENOMEM; - } - memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + urb->transfer_dma = dev->urb_dma[i]; + dev->isoc_ctl.transfer_buffer[i] = dev->urb_buffer[i]; usb_fill_bulk_urb(urb, dev->udev, pipe, dev->isoc_ctl.transfer_buffer[i], sb_size, @@ -1826,6 +1895,9 @@ int tm6000_v4l2_unregister(struct tm6000_core *dev) { video_unregister_device(dev->vfd); + /* if URB buffers are still allocated free them now */ + tm6000_free_urb_buffers(dev); + if (dev->radio_dev) { if (video_is_registered(dev->radio_dev)) video_unregister_device(dev->radio_dev); @@ -1851,3 +1923,5 @@ MODULE_PARM_DESC(debug, "activates debug info"); module_param(vid_limit, int, 0644); MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); +module_param(keep_urb, bool, 0); +MODULE_PARM_DESC(keep_urb, "Keep urb buffers allocated even when the device is closed by the user"); diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h index 6df418658c9c..173dcd7a7284 100644 --- a/drivers/media/usb/tm6000/tm6000.h +++ b/drivers/media/usb/tm6000/tm6000.h @@ -264,6 +264,11 @@ struct tm6000_core { spinlock_t slock; + /* urb dma buffers */ + char **urb_buffer; + dma_addr_t *urb_dma; + unsigned int urb_size; + unsigned long quirks; }; From 605a410325535e0d9dd3c539ac144fc52e0bda21 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Wed, 28 Nov 2012 17:15:51 -0300 Subject: [PATCH 0101/4607] [media] soc_camera: fix VIDIOC_S_CROP ioctl Sometimes VIDIOC_S_CROP ioctl doesn't work, soc-camera driver reports: soc-camera-pdrv soc-camera-pdrv.0: S_CROP denied: getting current crop failed The VIDIOC_G_CROP documentation states that the type field needs to be set to the respective buffer type when querying, so the check in .g_crop() of the subdevices returns -EINVAL if the type is not set properly. Here the uninitialized local variable 'current_crop' is passed to the .g_crop() and this leads to the observed error. Initialize the type field of the local 'current_crop' before get_crop call. Signed-off-by: Anatolij Gustschin Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 4e3735679f17..54da3a5f900c 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -908,6 +908,8 @@ static int soc_camera_s_crop(struct file *file, void *fh, dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, rect->left, rect->top); + current_crop.type = a->type; + /* If get_crop fails, we'll let host and / or client drivers decide */ ret = ici->ops->get_crop(icd, ¤t_crop); From bc1ebd704751bb6b86612e63fdfac56918364d07 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 26 Nov 2012 14:32:48 -0300 Subject: [PATCH 0102/4607] [media] media: sh-vou: fix compiler warnings sh-vou causes several "may be used uninitialized" warnings. Even though they all are purely theoretical, it is better to fix them. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index a1c87f0ceaab..7494858a2158 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -207,6 +207,7 @@ static void sh_vou_stream_start(struct sh_vou_device *vou_dev, #endif switch (vou_dev->pix.pixelformat) { + default: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: row_coeff = 1; @@ -595,9 +596,9 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std) */ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) { - unsigned int best_err = UINT_MAX, best, width_max, height_max, - img_height_max; - int i, idx; + unsigned int best_err = UINT_MAX, best = geo->in_width, + width_max, height_max, img_height_max; + int i, idx = 0; if (std & V4L2_STD_525_60) { width_max = 858; From 4a8a8042be289f6d53fc6cbfbed1722db14d7af1 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Mon, 19 Nov 2012 18:36:09 -0300 Subject: [PATCH 0103/4607] [media] mx2_camera: use GFP_ATOMIC under spin lock Found using the following semantic patch: @@ @@ spin_lock_irqsave(...); ... when != spin_unlock_irqrestore(...); * GFP_KERNEL Signed-off-by: Cyril Roelandt Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx2_camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 9a55f4c4c7f4..77529f860386 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -879,7 +879,7 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) pcdev->discard_size = icd->user_height * bytesperline; pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, pcdev->discard_size, &pcdev->discard_buffer_dma, - GFP_KERNEL); + GFP_ATOMIC); if (!pcdev->discard_buffer) { spin_unlock_irqrestore(&pcdev->lock, flags); return -ENOMEM; From 5b97fabdc815b4b60bac2328b58c5c7274debf58 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Tue, 9 Oct 2012 01:43:24 +0000 Subject: [PATCH 0104/4607] libfc: fix REC handling Currently fc_fcp_timeout doesn't check FC_RP_FLAGS_REC_SUPPORTED flag first, this prevents REC request ever going out at all to the target having REC support. So this patches fixes the fc_fcp_timeout by checking FC_RP_FLAGS_REC_SUPPORTED flag first. The changed order won't cause any issue during clearing FC_RP_FLAGS_REC_SUPPORTED on failed IO with target not supporting FC_RP_FLAGS_REC_SUPPORTED, since retry on failed IO would succeed. Signed-off-by: Vasu Dev Tested-by: Ross Brattain Signed-off-by: Robert Love --- drivers/scsi/libfc/fc_fcp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index fcb9d0b20ee4..09c81b2f2169 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -1381,10 +1381,10 @@ static void fc_fcp_timeout(unsigned long data) fsp->state |= FC_SRB_FCP_PROCESSING_TMO; - if (fsp->state & FC_SRB_RCV_STATUS) - fc_fcp_complete_locked(fsp); - else if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED) + if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED) fc_fcp_rec(fsp); + else if (fsp->state & FC_SRB_RCV_STATUS) + fc_fcp_complete_locked(fsp); else fc_fcp_recovery(fsp, FC_TIMED_OUT); fsp->state &= ~FC_SRB_FCP_PROCESSING_TMO; From 354d1123c16942b8a3ca9131b8fc2f8c06e2ed7f Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 30 Oct 2012 01:55:46 +0000 Subject: [PATCH 0105/4607] Documentation: Add missing devices/ to devices path Add missing 'devices/ subdirectory to /sys/bus/fcoe/devices/ctlr_X and /sys/bus/fcoe/devices/fcf_X references. Signed-off-by: Robert Love Tested-by: Ross Brattain --- Documentation/ABI/testing/sysfs-bus-fcoe | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-fcoe b/Documentation/ABI/testing/sysfs-bus-fcoe index 50e2a80ea28f..971dc22e6c2a 100644 --- a/Documentation/ABI/testing/sysfs-bus-fcoe +++ b/Documentation/ABI/testing/sysfs-bus-fcoe @@ -1,4 +1,4 @@ -What: /sys/bus/fcoe/ctlr_X +What: /sys/bus/fcoe/devices/ctlr_X Date: March 2012 KernelVersion: TBD Contact: Robert Love , devel@open-fcoe.org @@ -26,7 +26,7 @@ Attributes: Notes: ctlr_X (global increment starting at 0) -What: /sys/bus/fcoe/fcf_X +What: /sys/bus/fcoe/devices/fcf_X Date: March 2012 KernelVersion: TBD Contact: Robert Love , devel@open-fcoe.org From ef60f674344cdb6d1da199f6b8d7d7016813cc6f Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 27 Nov 2012 06:53:19 +0000 Subject: [PATCH 0106/4607] libfcoe: Save some memory and optimize name lookups Instead of creating a structure with an enum and a pointer to a string, simply allocate an array of strings and use the enum values for the indicies. This means that we do not need to iterate through the list of entries when looking up a string name by its enum key. This will also help with a latter patch that will add more fcoe_sysfs attributes that will also use the fcoe_enum_name_search macro. One attribute will also do a reverse lookup which requires less code when the enum-to-string mappings are organized as this patch makes them to be. Signed-off-by: Robert Love Acked-by: Neil Horman --- drivers/scsi/fcoe/fcoe_sysfs.c | 44 +++++++++++++--------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c index 5e751689a089..e6fce2862a33 100644 --- a/drivers/scsi/fcoe/fcoe_sysfs.c +++ b/drivers/scsi/fcoe/fcoe_sysfs.c @@ -210,25 +210,23 @@ static ssize_t show_fcoe_fcf_device_##field(struct device *dev, \ #define fcoe_enum_name_search(title, table_type, table) \ static const char *get_fcoe_##title##_name(enum table_type table_key) \ { \ - int i; \ - char *name = NULL; \ - \ - for (i = 0; i < ARRAY_SIZE(table); i++) { \ - if (table[i].value == table_key) { \ - name = table[i].name; \ - break; \ - } \ - } \ - return name; \ + if (table_key < 0 || table_key >= ARRAY_SIZE(table)) \ + return NULL; \ + return table[table_key]; \ } -static struct { - enum fcf_state value; - char *name; -} fcf_state_names[] = { - { FCOE_FCF_STATE_UNKNOWN, "Unknown" }, - { FCOE_FCF_STATE_DISCONNECTED, "Disconnected" }, - { FCOE_FCF_STATE_CONNECTED, "Connected" }, +static char *fip_conn_type_names[] = { + [ FIP_CONN_TYPE_UNKNOWN ] = "Unknown", + [ FIP_CONN_TYPE_FABRIC ] = "Fabric", + [ FIP_CONN_TYPE_VN2VN ] = "VN2VN", +}; +fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names) +#define FCOE_CTLR_MODE_MAX_NAMELEN 50 + +static char *fcf_state_names[] = { + [ FCOE_FCF_STATE_UNKNOWN ] = "Unknown", + [ FCOE_FCF_STATE_DISCONNECTED ] = "Disconnected", + [ FCOE_FCF_STATE_CONNECTED ] = "Connected", }; fcoe_enum_name_search(fcf_state, fcf_state, fcf_state_names) #define FCOE_FCF_STATE_MAX_NAMELEN 50 @@ -246,17 +244,7 @@ static ssize_t show_fcf_state(struct device *dev, } static FCOE_DEVICE_ATTR(fcf, state, S_IRUGO, show_fcf_state, NULL); -static struct { - enum fip_conn_type value; - char *name; -} fip_conn_type_names[] = { - { FIP_CONN_TYPE_UNKNOWN, "Unknown" }, - { FIP_CONN_TYPE_FABRIC, "Fabric" }, - { FIP_CONN_TYPE_VN2VN, "VN2VN" }, -}; -fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names) -#define FCOE_CTLR_MODE_MAX_NAMELEN 50 - +#define FCOE_MAX_MODENAME_LEN 20 static ssize_t show_ctlr_mode(struct device *dev, struct device_attribute *attr, char *buf) From 40633219a0ff4953601c7b2326f8faabc0171fd9 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 4 Dec 2012 15:12:00 +0200 Subject: [PATCH 0107/4607] drm/i915: Add debugfs entry to read/write next_seqno This is needed for testing seqno wrapping. Be careful to not bump next_seqno more than 0x7FFFFFFF at a time (between some handled requests) as i915_seqno_passed() can't handle bigger difference in between. v2: Address review comments from Chris Wilson. Signed-off-by: Mika Kuoppala Acked-by: Chris Wilson [danvet: Squash in fixup to properly remove the debugfs file on driver unload again.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 88 +++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index ce60506b574b..602058fb5e25 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -843,6 +843,86 @@ static const struct file_operations i915_error_state_fops = { .release = i915_error_state_release, }; +static ssize_t +i915_next_seqno_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + drm_i915_private_t *dev_priv = dev->dev_private; + char buf[80]; + int len; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + len = snprintf(buf, sizeof(buf), + "next_seqno : 0x%x\n", + dev_priv->next_seqno); + + mutex_unlock(&dev->struct_mutex); + + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_next_seqno_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + drm_i915_private_t *dev_priv = dev->dev_private; + char buf[20]; + u32 val = 1; + int ret; + + if (cnt > 0) { + if (cnt > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + } + + if (val == 0) + return -EINVAL; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + if (i915_seqno_passed(val, dev_priv->next_seqno)) { + dev_priv->next_seqno = val; + DRM_DEBUG_DRIVER("Advancing seqno to %u\n", val); + } else { + ret = -EINVAL; + } + + mutex_unlock(&dev->struct_mutex); + + return ret ?: cnt; +} + +static const struct file_operations i915_next_seqno_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = i915_next_seqno_read, + .write = i915_next_seqno_write, + .llseek = default_llseek, +}; + static int i915_rstdby_delays(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *) m->private; @@ -2107,6 +2187,12 @@ int i915_debugfs_init(struct drm_minor *minor) if (ret) return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, + "i915_next_seqno", + &i915_next_seqno_fops); + if (ret) + return ret; + return drm_debugfs_create_files(i915_debugfs_list, I915_DEBUGFS_ENTRIES, minor->debugfs_root, minor); @@ -2130,6 +2216,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor) 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_error_state_fops, 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_next_seqno_fops, + 1, minor); } #endif /* CONFIG_DEBUG_FS */ From 43a7b924a920729ff775e6a847f671d0ea456801 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 4 Dec 2012 15:12:01 +0200 Subject: [PATCH 0108/4607] drm/i915: Fix debugfs seqno info print to use uint seqno's are u32 so print accordingly Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 602058fb5e25..7e516eebdc80 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -389,7 +389,7 @@ static void i915_ring_seqno_info(struct seq_file *m, struct intel_ring_buffer *ring) { if (ring->get_seqno) { - seq_printf(m, "Current sequence (%s): %d\n", + seq_printf(m, "Current sequence (%s): %u\n", ring->name, ring->get_seqno(ring, false)); } } From 531fca1649dab3dff47ed2becd484b9cc020ea4e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 06:53:24 -0300 Subject: [PATCH 0109/4607] [media] MAINTAINERS: add adv7604/ad9389b entries Cisco maintains these drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9fba9ed4f615..34f4eb4fb490 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -558,6 +558,18 @@ L: linux-rdma@vger.kernel.org S: Maintained F: drivers/infiniband/hw/amso1100/ +ANALOG DEVICES INC AD9389B DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/ad9389b* + +ANALOG DEVICES INC ADV7604 DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/adv7604* + ANALOG DEVICES INC ASOC CODEC DRIVERS M: Lars-Peter Clausen L: device-drivers-devel@blackfin.uclinux.org From 3f101d916b5b2b95d36369e1b2505720db2dcecb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:00:02 -0300 Subject: [PATCH 0110/4607] [media] MAINTAINERS: add cx2341x entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 34f4eb4fb490..c001ed68df19 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2195,6 +2195,15 @@ F: Documentation/video4linux/cx18.txt F: drivers/media/pci/cx18/ F: include/uapi/linux/ivtv* +CX2341X MPEG ENCODER HELPER MODULE +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/i2c/cx2341x* +F: include/media/cx2341x* + CX88 VIDEO4LINUX DRIVER M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org From 35e3540b9cc744b2334e8bf7f38cb1fa1552b008 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:02:29 -0300 Subject: [PATCH 0111/4607] [media] MAINTAINERS: add entry for the quickcam parallel port webcams Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index c001ed68df19..0b8e2bb44049 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6156,6 +6156,14 @@ L: linux-hexagon@vger.kernel.org S: Supported F: arch/hexagon/ +QUICKCAM PARALLEL PORT WEBCAMS +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/parport/*-qcam* + RADOS BLOCK DEVICE (RBD) M: Yehuda Sadeh M: Sage Weil From f41bf02f3dbc3bc5d416c0f147e79983433d68f6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:04:36 -0300 Subject: [PATCH 0112/4607] [media] MAINTAINERS: add radio-keene entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0b8e2bb44049..d09affd58c65 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4342,6 +4342,14 @@ W: http://lse.sourceforge.net/kdump/ S: Maintained F: Documentation/kdump/ +KEENE FM RADIO TRANSMITTER DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-keene* + KERNEL AUTOMOUNTER v4 (AUTOFS4) M: Ian Kent L: autofs@vger.kernel.org From 169dfd880ad3f301f60587b2e6145afd3edbbd59 Mon Sep 17 00:00:00 2001 From: "Yann E. MORIN" Date: Mon, 12 Nov 2012 22:09:18 +0100 Subject: [PATCH 0113/4607] Revert "kconfig-language: add to hints" This reverts commit 64b81ed7fb1e45c914317b20316f32827bc6444b. As Martin says about this paragraph: However, I can not reproduce this. The attached file contains both cases. [...] AFAICS, both versions behave equivalently. I suggest changing the documentation accordingly. [in http://marc.info/?l=linux-kbuild&m=134987006202410&w=2] Reported-by: Martin Walch Cc: Martin Walch Signed-off-by: "Yann E. MORIN" Signed-off-by: Michal Marek --- Documentation/kbuild/kconfig-language.txt | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/Documentation/kbuild/kconfig-language.txt b/Documentation/kbuild/kconfig-language.txt index a686f9cd69c1..c858f8419eba 100644 --- a/Documentation/kbuild/kconfig-language.txt +++ b/Documentation/kbuild/kconfig-language.txt @@ -388,26 +388,3 @@ config FOO depends on BAR && m limits FOO to module (=m) or disabled (=n). - -Kconfig symbol existence -~~~~~~~~~~~~~~~~~~~~~~~~ -The following two methods produce the same kconfig symbol dependencies -but differ greatly in kconfig symbol existence (production) in the -generated config file. - -case 1: - -config FOO - tristate "about foo" - depends on BAR - -vs. case 2: - -if BAR -config FOO - tristate "about foo" -endif - -In case 1, the symbol FOO will always exist in the config file (given -no other dependencies). In case 2, the symbol FOO will only exist in -the config file if BAR is enabled. From cbcc80dff3896015385c67d6be0beb3399999e5c Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 4 Dec 2012 15:12:03 +0200 Subject: [PATCH 0114/4607] drm/i915: Split intel_ring_begin In preparation for handling ring seqno wrapping, split intel_ring_begin into helper part which doesn't allocate seqno. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ringbuffer.c | 37 +++++++++++++++---------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 36e1e13ae946..01a660a7c167 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1363,11 +1363,31 @@ intel_ring_alloc_seqno(struct intel_ring_buffer *ring) return i915_gem_get_seqno(ring->dev, &ring->outstanding_lazy_request); } +static int __intel_ring_begin(struct intel_ring_buffer *ring, + int bytes) +{ + int ret; + + if (unlikely(ring->tail + bytes > ring->effective_size)) { + ret = intel_wrap_ring_buffer(ring); + if (unlikely(ret)) + return ret; + } + + if (unlikely(ring->space < bytes)) { + ret = ring_wait_for_space(ring, bytes); + if (unlikely(ret)) + return ret; + } + + ring->space -= bytes; + return 0; +} + int intel_ring_begin(struct intel_ring_buffer *ring, int num_dwords) { drm_i915_private_t *dev_priv = ring->dev->dev_private; - int n = 4*num_dwords; int ret; ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); @@ -1379,20 +1399,7 @@ int intel_ring_begin(struct intel_ring_buffer *ring, if (ret) return ret; - if (unlikely(ring->tail + n > ring->effective_size)) { - ret = intel_wrap_ring_buffer(ring); - if (unlikely(ret)) - return ret; - } - - if (unlikely(ring->space < n)) { - ret = ring_wait_for_space(ring, n); - if (unlikely(ret)) - return ret; - } - - ring->space -= n; - return 0; + return __intel_ring_begin(ring, num_dwords * sizeof(uint32_t)); } void intel_ring_advance(struct intel_ring_buffer *ring) From 498d2ac15ce0fc08edb005a7faf9ed6b5aa028d8 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 4 Dec 2012 15:12:04 +0200 Subject: [PATCH 0115/4607] drm/i915: Add intel_ring_handle_seqno wrap If there are pre-wrap values in semaphore-mbox registers after wrap, syncing against some after-wrap request will complete immediately. Fix this by emitting ring commands to set mbox registers to zero when the wrap happens. v2: Use __intel_ring_begin to emit ring commands, from Chris Wilson. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson [danvet: Add a small comment to handle_seqno_wrap.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 4 ++++ drivers/gpu/drm/i915/intel_ringbuffer.c | 22 ++++++++++++++++++++++ drivers/gpu/drm/i915/intel_ringbuffer.h | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index cb941448c2bc..02d315164aa9 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1952,6 +1952,10 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) i915_gem_retire_requests(dev); for_each_ring(ring, dev_priv, i) { + ret = intel_ring_handle_seqno_wrap(ring); + if (ret) + return ret; + for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++) ring->sync_seqno[j] = 0; } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 01a660a7c167..0d03dc649c5e 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1402,6 +1402,28 @@ int intel_ring_begin(struct intel_ring_buffer *ring, return __intel_ring_begin(ring, num_dwords * sizeof(uint32_t)); } +int intel_ring_handle_seqno_wrap(struct intel_ring_buffer *ring) +{ + int ret; + + BUG_ON(ring->outstanding_lazy_request); + + if (INTEL_INFO(ring->dev)->gen < 6) + return 0; + + ret = __intel_ring_begin(ring, 6 * sizeof(uint32_t)); + if (ret) + return ret; + + /* Leaving a stale, pre-wrap seqno behind in the mboxes will result in + * post-wrap semaphore waits completing immediately. Clear them. */ + update_mboxes(ring, ring->signal_mbox[0]); + update_mboxes(ring, ring->signal_mbox[1]); + intel_ring_advance(ring); + + return 0; +} + void intel_ring_advance(struct intel_ring_buffer *ring) { struct drm_i915_private *dev_priv = ring->dev->dev_private; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index d4b7416fa1b0..b4a533e53fd1 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -196,7 +196,7 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring, } void intel_ring_advance(struct intel_ring_buffer *ring); int __must_check intel_ring_idle(struct intel_ring_buffer *ring); - +int __must_check intel_ring_handle_seqno_wrap(struct intel_ring_buffer *ring); int intel_ring_flush_all_caches(struct intel_ring_buffer *ring); int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring); From 4a06e201dae98ed893bae677b7b05bff29fcbab0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:40 +0100 Subject: [PATCH 0116/4607] drm/i915: haswell has the same irq handlers as ivb No need to have the exaxt same code twice. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 68e7cafa13de..2aa23e7fc2ab 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2711,7 +2711,7 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = valleyview_irq_uninstall; dev->driver->enable_vblank = valleyview_enable_vblank; dev->driver->disable_vblank = valleyview_disable_vblank; - } else if (IS_IVYBRIDGE(dev)) { + } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { /* Share pre & uninstall handlers with ILK/SNB */ dev->driver->irq_handler = ivybridge_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; @@ -2719,14 +2719,6 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->enable_vblank = ivybridge_enable_vblank; dev->driver->disable_vblank = ivybridge_disable_vblank; - } else if (IS_HASWELL(dev)) { - /* Share interrupts handling with IVB */ - dev->driver->irq_handler = ivybridge_irq_handler; - dev->driver->irq_preinstall = ironlake_irq_preinstall; - dev->driver->irq_postinstall = ivybridge_irq_postinstall; - dev->driver->irq_uninstall = ironlake_irq_uninstall; - dev->driver->enable_vblank = ivybridge_enable_vblank; - dev->driver->disable_vblank = ivybridge_disable_vblank; } else if (HAS_PCH_SPLIT(dev)) { dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; From d83779a9cb9374977c1c05364b4d7dfe9ec68ef3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:41 +0100 Subject: [PATCH 0117/4607] drm/i915: don't handle PIPE_LEGACY_BLC_EVENT_STATUS on vlv This is for legacy legacy stuff, and checking with the leftover pipe from the previous loop is propably not what we want. Since pipe == 2 after the loop ... Then we only assing a variable and do nothing with it. Cc: Jesse Barnes Reviewed-by: Jesse Barnes Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2aa23e7fc2ab..935b6b7bae73 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -530,7 +530,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) unsigned long irqflags; int pipe; u32 pipe_stats[I915_MAX_PIPES]; - bool blc_event; atomic_inc(&dev_priv->irq_received); @@ -587,9 +586,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) I915_READ(PORT_HOTPLUG_STAT); } - if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) - blc_event = true; - if (pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); From 61bac78e03d4385e225cb2837e33974feda489c2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 21:03:21 +0100 Subject: [PATCH 0118/4607] drm/i915: setup the hangcheck timer early ... together with all the other irq related resources in intel_irq_init. I've managed to oops in the notify_ring function on my ilk, presumably because of the powerctx setup call to i915_gpu_idle. Note that this is only a problem with the reorder irq setup sequence for irq-driver gmbus/dp aux. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 --- drivers/gpu/drm/i915/i915_irq.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 2635ee6a34d4..901f8fc3ed88 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1611,9 +1611,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) intel_opregion_init(dev); acpi_video_register(); - setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, - (unsigned long) dev); - if (IS_GEN5(dev)) intel_gpu_ips_init(dev_priv); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 935b6b7bae73..568820bf4587 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2687,6 +2687,9 @@ void intel_irq_init(struct drm_device *dev) INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work); INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work); + setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, + (unsigned long) dev); + dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { From 52d7ecedac3f96fb562cb482c139015372728638 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 21:03:22 +0100 Subject: [PATCH 0119/4607] drm/i915: reorder setup sequence to have irqs for output setup Otherwise the new&shiny irq-driven gmbus and dp aux code won't work that well. Noticed since the dp aux code doesn't have an automatic fallback with a timeout (since the hw provides for that already). v2: Simple move drm_irq_install before intel_modeset_gem_init, as suggested by Ben Widawsky. v3: Now that interrupts are enabled before all connectors are fully set up, we might fall over serving a HPD interrupt while things are still being set up. Instead of jumping through massive hoops and complicating the code with a separate hpd irq enable step, simply block out the hotplug work item from doing anything until things are in place. v4: Actually, we can enable hotplug processing only after the fbdev is fully set up, since we call down into the fbdev from the hotplug work functions. So stick the hpd enabling right next to the poll helper initialization. v5: We need to enable irqs before intel_modeset_init, since that function sets up the outputs. v6: Fixup cleanup sequence, too. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 23 ++++++++++++++--------- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_irq.c | 4 ++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 901f8fc3ed88..14d679ab52b7 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1294,19 +1294,21 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_vga_switcheroo; + ret = drm_irq_install(dev); + if (ret) + goto cleanup_gem_stolen; + + /* Important: The output setup functions called by modeset_init need + * working irqs for e.g. gmbus and dp aux transfers. */ intel_modeset_init(dev); ret = i915_gem_init(dev); if (ret) - goto cleanup_gem_stolen; - - intel_modeset_gem_init(dev); + goto cleanup_irq; INIT_WORK(&dev_priv->console_resume_work, intel_console_resume); - ret = drm_irq_install(dev); - if (ret) - goto cleanup_gem; + intel_modeset_gem_init(dev); /* Always safe in the mode setting case. */ /* FIXME: do pre/post-mode set stuff in core KMS code */ @@ -1314,7 +1316,10 @@ static int i915_load_modeset_init(struct drm_device *dev) ret = intel_fbdev_init(dev); if (ret) - goto cleanup_irq; + goto cleanup_gem; + + /* Only enable hotplug handling once the fbdev is fully set up. */ + dev_priv->enable_hotplug_processing = true; drm_kms_helper_poll_init(dev); @@ -1323,13 +1328,13 @@ static int i915_load_modeset_init(struct drm_device *dev) return 0; -cleanup_irq: - drm_irq_uninstall(dev); cleanup_gem: mutex_lock(&dev->struct_mutex); i915_gem_cleanup_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_aliasing_ppgtt(dev); +cleanup_irq: + drm_irq_uninstall(dev); cleanup_gem_stolen: i915_gem_cleanup_stolen(dev); cleanup_vga_switcheroo: diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index bb0eb0f8f6b3..e7411f363b3d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -673,6 +673,7 @@ typedef struct drm_i915_private { u32 hotplug_supported_mask; struct work_struct hotplug_work; + bool enable_hotplug_processing; int num_pipe; int num_pch_pll; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 568820bf4587..02a13ab148df 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -287,6 +287,10 @@ static void i915_hotplug_work_func(struct work_struct *work) struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *encoder; + /* HPD irq before everything is fully set up. */ + if (!dev_priv->enable_hotplug_processing) + return; + mutex_lock(&mode_config->mutex); DRM_DEBUG_KMS("running encoder hotplug functions\n"); From 61168c53f5be309332006dbd967aaac501cde8b2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:43 +0100 Subject: [PATCH 0120/4607] drm/i915: extract gmbus_wait_hw_status The gmbus interrupt generation is rather fiddly: We can only ever enable one interrupt source (but we always want to check for NAK in addition to the real bit). And the bits in the gmbus status register don't map at all to the bis in the irq register. To prepare for this mess, start by extracting the hw status wait loop into it's own function, consolidate the NAK error handling a bit. To keep things flexible, pass in the status bit we care about (in addition to any NAK signalling). v2: I've failed to notice that the sense of GMBUS_ACTIVE is inverted, Chris Wilson gladly pointed that out for me. To keep things simple, ignore that case for now (we only need to idle the gmbus controller at the end of an entire i2c transaction, not after every message). Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_i2c.c | 46 +++++++++++++++++--------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 3ef5af15b812..a16eecdf1ed1 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -202,6 +202,24 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin) algo->data = bus; } +static int +gmbus_wait_hw_status(struct drm_i915_private *dev_priv, + u32 gmbus2_status) +{ + int ret; + int reg_offset = dev_priv->gpio_mmio_base; + u32 gmbus2; + + ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & + (GMBUS_SATOER | gmbus2_status), + 50); + + if (gmbus2 & GMBUS_SATOER) + return -ENXIO; + + return ret; +} + static int gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, u32 gmbus1_index) @@ -219,15 +237,10 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, while (len) { int ret; u32 val, loop = 0; - u32 gmbus2; - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | GMBUS_HW_RDY), - 50); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); if (ret) - return -ETIMEDOUT; - if (gmbus2 & GMBUS_SATOER) - return -ENXIO; + return ret; val = I915_READ(GMBUS3 + reg_offset); do { @@ -261,7 +274,6 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg) GMBUS_SLAVE_WRITE | GMBUS_SW_RDY); while (len) { int ret; - u32 gmbus2; val = loop = 0; do { @@ -270,13 +282,9 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg) I915_WRITE(GMBUS3 + reg_offset, val); - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | GMBUS_HW_RDY), - 50); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); if (ret) - return -ETIMEDOUT; - if (gmbus2 & GMBUS_SATOER) - return -ENXIO; + return ret; } return 0; } @@ -345,8 +353,6 @@ gmbus_xfer(struct i2c_adapter *adapter, I915_WRITE(GMBUS0 + reg_offset, bus->reg0); for (i = 0; i < num; i++) { - u32 gmbus2; - if (gmbus_is_index_read(msgs, i, num)) { ret = gmbus_xfer_index_read(dev_priv, &msgs[i]); i += 1; /* set i to the index of the read xfer */ @@ -361,13 +367,11 @@ gmbus_xfer(struct i2c_adapter *adapter, if (ret == -ENXIO) goto clear_err; - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), - 50); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE); + if (ret == -ENXIO) + goto clear_err; if (ret) goto timeout; - if (gmbus2 & GMBUS_SATOER) - goto clear_err; } /* Generate a STOP condition on the bus. Note that gmbus can't generata From 515ac2bb95f609bc4a0d2ad5f7011b3264b2bb21 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:44 +0100 Subject: [PATCH 0121/4607] drm/i915: wire up gmbus irq handler Only enables the interrupt and puts a irq handler into place, doesn't do anything yet. Unfortunately there's no gmbus interrupt support for gen2/3 (safe for pnv, but there the irq is marked as "Test mode"). v2: Wire up the irq handler for vlv and gen4 properly. v3: i915_enable_pipestat expects the mask bit, not the status bits ... and for added hilarity those are rather inconsistently named. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 02a13ab148df..956b95f1f113 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -525,6 +525,11 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, queue_work(dev_priv->wq, &dev_priv->rps.work); } +static void gmbus_irq_handler(struct drm_device *dev) +{ + DRM_DEBUG_DRIVER("GMBUS interrupt\n"); +} + static irqreturn_t valleyview_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; @@ -590,6 +595,9 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) I915_READ(PORT_HOTPLUG_STAT); } + if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) + gmbus_irq_handler(dev); + if (pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); @@ -616,7 +624,7 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) SDE_AUDIO_POWER_SHIFT); if (pch_iir & SDE_GMBUS) - DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n"); + gmbus_irq_handler(dev); if (pch_iir & SDE_AUDIO_HDCP_MASK) DRM_DEBUG_DRIVER("PCH HDCP audio interrupt\n"); @@ -662,7 +670,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) DRM_DEBUG_DRIVER("AUX channel interrupt\n"); if (pch_iir & SDE_GMBUS_CPT) - DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n"); + gmbus_irq_handler(dev); if (pch_iir & SDE_AUDIO_CP_REQ_CPT) DRM_DEBUG_DRIVER("Audio CP request interrupt\n"); @@ -1880,12 +1888,14 @@ static int ironlake_irq_postinstall(struct drm_device *dev) hotplug_mask = (SDE_CRT_HOTPLUG_CPT | SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT); + SDE_PORTD_HOTPLUG_CPT | + SDE_GMBUS_CPT); } else { hotplug_mask = (SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG | SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG | + SDE_GMBUS | SDE_AUX_MASK); } @@ -1945,7 +1955,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) hotplug_mask = (SDE_CRT_HOTPLUG_CPT | SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT); + SDE_PORTD_HOTPLUG_CPT | + SDE_GMBUS_CPT); dev_priv->pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); @@ -1999,6 +2010,7 @@ static int valleyview_irq_postinstall(struct drm_device *dev) POSTING_READ(VLV_IER); i915_enable_pipestat(dev_priv, 0, pipestat_enable); + i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE); i915_enable_pipestat(dev_priv, 1, pipestat_enable); I915_WRITE(VLV_IIR, 0xffffffff); @@ -2483,6 +2495,7 @@ static int i965_irq_postinstall(struct drm_device *dev) dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; + i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE); /* * Enable some error detection, note the instruction error mask @@ -2636,6 +2649,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) if (blc_event || (iir & I915_ASLE_INTERRUPT)) intel_opregion_asle_intr(dev); + if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) + gmbus_irq_handler(dev); + /* With MSI, interrupts are only generated when iir * transitions from zero to nonzero. If another bit got * set while we were handling the existing iir bits, then From 28c70f162a315bdcfbe0bf940a740ef8bfb918d6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:45 +0100 Subject: [PATCH 0122/4607] drm/i915: use the gmbus irq for waits We need two special things to properly wire this up: - Add another argument to gmbus_wait_hw_status to pass in the correct interrupt bit in gmbus4. - Since we can only get an irq for one of the two events we want, hand-roll the wait_event_timeout code so that we wake up every jiffie and can check for NAKs. This way we also subsume gmbus support for platforms without interrupts (or where those are not yet enabled). The important bit really is to only enable one gmbus interrupt source at the same time - with that piece of lore figured out, this seems to work flawlessly. Ben Widawsky rightfully complained the lack of measurements for the claimed benefits (especially since the first version was actually broken and fell back to bit-banging). Previously reading the 256 byte hdmi EDID takes about 72 ms here. With this patch it's down to 33 ms. Given that transfering the 256 bytes over i2c at wire speed takes 20.5ms alone, the reduction in additional overhead is rather nice. v2: Chris Wilson wondered whether GMBUS4 might contain some set bits when booting up an hence result in some spurious interrupts. Since we clear GMBUS4 after every wait and we do gmbus transfer really early in the setup sequence to detect displays the window is small, but still be paranoid and clear it properly. v3: Clarify the comment that gmbus irq generation can only support one kind of event, why it bothers us and how we work around that limit. Cc: Daniel Kurtz Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 3 +++ drivers/gpu/drm/i915/i915_irq.c | 4 +++ drivers/gpu/drm/i915/intel_i2c.c | 45 ++++++++++++++++++++++++-------- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e7411f363b3d..3843383e13cc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -641,6 +641,7 @@ typedef struct drm_i915_private { struct intel_gmbus gmbus[GMBUS_NUM_PORTS]; + /** gmbus_mutex protects against concurrent usage of the single hw gmbus * controller on different i2c buses. */ struct mutex gmbus_mutex; @@ -650,6 +651,8 @@ typedef struct drm_i915_private { */ uint32_t gpio_mmio_base; + wait_queue_head_t gmbus_wait_queue; + struct pci_dev *bridge_dev; struct intel_ring_buffer ring[I915_NUM_RINGS]; uint32_t next_seqno; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 956b95f1f113..917a48de08fa 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -527,7 +527,11 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, static void gmbus_irq_handler(struct drm_device *dev) { + struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; + DRM_DEBUG_DRIVER("GMBUS interrupt\n"); + + wake_up_all(&dev_priv->gmbus_wait_queue); } static irqreturn_t valleyview_irq_handler(int irq, void *arg) diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index a16eecdf1ed1..8b7189253cb4 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -63,6 +63,7 @@ intel_i2c_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0); + I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0); } static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable) @@ -204,20 +205,38 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin) static int gmbus_wait_hw_status(struct drm_i915_private *dev_priv, - u32 gmbus2_status) + u32 gmbus2_status, + u32 gmbus4_irq_en) { - int ret; + int i; int reg_offset = dev_priv->gpio_mmio_base; - u32 gmbus2; + u32 gmbus2 = 0; + DEFINE_WAIT(wait); - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | gmbus2_status), - 50); + /* Important: The hw handles only the first bit, so set only one! Since + * we also need to check for NAKs besides the hw ready/idle signal, we + * need to wake up periodically and check that ourselves. */ + I915_WRITE(GMBUS4 + reg_offset, gmbus4_irq_en); + + for (i = 0; i < msecs_to_jiffies(50) + 1; i++) { + prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait, + TASK_UNINTERRUPTIBLE); + + gmbus2 = I915_READ(GMBUS2 + reg_offset); + if (gmbus2 & (GMBUS_SATOER | gmbus2_status)) + break; + + schedule_timeout(1); + } + finish_wait(&dev_priv->gmbus_wait_queue, &wait); + + I915_WRITE(GMBUS4 + reg_offset, 0); if (gmbus2 & GMBUS_SATOER) return -ENXIO; - - return ret; + if (gmbus2 & gmbus2_status) + return 0; + return -ETIMEDOUT; } static int @@ -238,7 +257,8 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, int ret; u32 val, loop = 0; - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY, + GMBUS_HW_RDY_EN); if (ret) return ret; @@ -282,7 +302,8 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg) I915_WRITE(GMBUS3 + reg_offset, val); - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY, + GMBUS_HW_RDY_EN); if (ret) return ret; } @@ -367,7 +388,8 @@ gmbus_xfer(struct i2c_adapter *adapter, if (ret == -ENXIO) goto clear_err; - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE, + GMBUS_HW_WAIT_EN); if (ret == -ENXIO) goto clear_err; if (ret) @@ -473,6 +495,7 @@ int intel_setup_gmbus(struct drm_device *dev) dev_priv->gpio_mmio_base = 0; mutex_init(&dev_priv->gmbus_mutex); + init_waitqueue_head(&dev_priv->gmbus_wait_queue); for (i = 0; i < GMBUS_NUM_PORTS; i++) { struct intel_gmbus *bus = &dev_priv->gmbus[i]; From 2c438c0273b76d6cb158f8bdd0aa3ebf66e48a28 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:46 +0100 Subject: [PATCH 0123/4607] drm/i915: use gmbus irq to wait for gmbus idle GMBUS_ACTIVE has inverted sense and so doesn't fit into the wait_hw_status helper, hence create a new gmbus_wait_idle functions. Also, we only care about the idle irq event and nothing else, which allows us to use the wait_event_timeout helper directly without jumping through hoops to catch NAKs. Since gen2/3 don't have gmbus interrupts, handle them separately with the old wait_for macro. This shaves another few ms off reading EDID from a hdmi screen on my testbox here. EDID reading with interrupt driven gmbus is now as fast as with busy-looping gmbus at 28 ms here (with negligible cpu overhead). Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_i2c.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 8b7189253cb4..1fc31191b910 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -203,6 +203,7 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin) algo->data = bus; } +#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 4) static int gmbus_wait_hw_status(struct drm_i915_private *dev_priv, u32 gmbus2_status, @@ -239,6 +240,31 @@ gmbus_wait_hw_status(struct drm_i915_private *dev_priv, return -ETIMEDOUT; } +static int +gmbus_wait_idle(struct drm_i915_private *dev_priv) +{ + int ret; + int reg_offset = dev_priv->gpio_mmio_base; + +#define C ((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0) + + if (!HAS_GMBUS_IRQ(dev_priv->dev)) + return wait_for(C, 10); + + /* Important: The hw handles only the first bit, so set only one! */ + I915_WRITE(GMBUS4 + reg_offset, GMBUS_IDLE_EN); + + ret = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10); + + I915_WRITE(GMBUS4 + reg_offset, 0); + + if (ret) + return 0; + else + return -ETIMEDOUT; +#undef C +} + static int gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, u32 gmbus1_index) @@ -406,8 +432,7 @@ gmbus_xfer(struct i2c_adapter *adapter, * We will re-enable it at the start of the next xfer, * till then let it sleep. */ - if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0, - 10)) { + if (gmbus_wait_idle(dev_priv)) { DRM_DEBUG_KMS("GMBUS [%s] timed out waiting for idle\n", adapter->name); ret = -ETIMEDOUT; @@ -431,8 +456,7 @@ clear_err: * it's slow responding and only answers on the 2nd retry. */ ret = -ENXIO; - if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0, - 10)) { + if (gmbus_wait_idle(dev_priv)) { DRM_DEBUG_KMS("GMBUS [%s] timed out after NAK\n", adapter->name); ret = -ETIMEDOUT; From ce99c2569dfcec9662993961db7f433e160c50f3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:47 +0100 Subject: [PATCH 0124/4607] drm/i915: wire up do aux channel done interrupt Doesn't do anything yet than call dp_aux_irq_handler. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 917a48de08fa..1e245e501e5a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -534,6 +534,11 @@ static void gmbus_irq_handler(struct drm_device *dev) wake_up_all(&dev_priv->gmbus_wait_queue); } +static void dp_aux_irq_handler(struct drm_device *dev) +{ + DRM_DEBUG_DRIVER("AUX channel interrupt\n"); +} + static irqreturn_t valleyview_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; @@ -627,6 +632,9 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) (pch_iir & SDE_AUDIO_POWER_MASK) >> SDE_AUDIO_POWER_SHIFT); + if (pch_iir & SDE_AUX_MASK) + dp_aux_irq_handler(dev); + if (pch_iir & SDE_GMBUS) gmbus_irq_handler(dev); @@ -671,7 +679,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) SDE_AUDIO_POWER_SHIFT_CPT); if (pch_iir & SDE_AUX_MASK_CPT) - DRM_DEBUG_DRIVER("AUX channel interrupt\n"); + dp_aux_irq_handler(dev); if (pch_iir & SDE_GMBUS_CPT) gmbus_irq_handler(dev); @@ -712,6 +720,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) de_iir = I915_READ(DEIIR); if (de_iir) { + if (de_iir & DE_AUX_CHANNEL_A_IVB) + dp_aux_irq_handler(dev); + if (de_iir & DE_GSE_IVB) intel_opregion_gse_intr(dev); @@ -790,6 +801,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) else snb_gt_irq_handler(dev, dev_priv, gt_iir); + if (de_iir & DE_AUX_CHANNEL_A) + dp_aux_irq_handler(dev); + if (de_iir & DE_GSE) intel_opregion_gse_intr(dev); @@ -1858,7 +1872,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; /* enable kind of interrupts always enabled */ u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | - DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE; + DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | + DE_AUX_CHANNEL_A; u32 render_irqs; u32 hotplug_mask; @@ -1893,7 +1908,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT); + SDE_GMBUS_CPT | + SDE_AUX_MASK_CPT); } else { hotplug_mask = (SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG | @@ -1930,7 +1946,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | DE_PCH_EVENT_IVB | DE_PLANEC_FLIP_DONE_IVB | DE_PLANEB_FLIP_DONE_IVB | - DE_PLANEA_FLIP_DONE_IVB; + DE_PLANEA_FLIP_DONE_IVB | + DE_AUX_CHANNEL_A_IVB; u32 render_irqs; u32 hotplug_mask; @@ -1960,7 +1977,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT); + SDE_GMBUS_CPT | + SDE_AUX_MASK_CPT); dev_priv->pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); From 9ee32fea5fe810ec06af3a15e4c65478de56d4f5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:48 +0100 Subject: [PATCH 0125/4607] drm/i915: irq-drive the dp aux communication At least on the platforms that have a dp aux irq and also have it enabled - vlvhsw should have one, too. But I don't have a machine to test this on. Judging from docs there's no dp aux interrupt for gm45. Also, I only have an ivb cpu edp machine, so the dp aux A code for snb/ilk is untested. For dpcd probing when nothing is connected it slashes about 5ms of cpu time (cpu time is now negligible), which agrees with 3 * 5 400 usec timeouts. A previous version of this patch increases the time required to go through the dp_detect cycle (which includes reading the edid) from around 33 ms to around 40 ms. Experiments indicated that this is purely due to the irq latency - the hw doesn't allow us to queue up dp aux transactions and hence irq latency directly affects throughput. gmbus is much better, there we have a 8 byte buffer, and we get the irq once another 4 bytes can be queued up. But by using the pm_qos interface to request the lowest possible cpu wake-up latency this slowdown completely disappeared. Since all our output detection logic is single-threaded with the mode_config mutex right now anyway, I've decide not ot play fancy and to just reuse the gmbus wait queue. But this would definitely prep the way to run dp detection on different ports in parallel v2: Add a timeout for dp aux transfers when using interrupts - the hw _does_ prevent this with the hw-based 400 usec timeout, but if the irq somehow doesn't arrive we're screwed. Lesson learned while developing this ;-) v3: While at it also convert the busy-loop to wait_for_atomic, so that we don't run the risk of an infinite loop any more. v4: Ensure we have the smallest possible irq latency by using the pm_qos interface. v5: Add a comment to the code to explain why we frob pm_qos. Suggested by Chris Wilson. v6: Disable dp irq for vlv, that's easier than trying to get at docs and hw. v7: Squash in a fix for Haswell that Paulo Zanoni tracked down - the dp aux registers aren't at a fixed offset any more, but can be on the PCH while the DP port is on the cpu die. Reviewed-by: Imre Deak (v6) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 4 ++ drivers/gpu/drm/i915/i915_irq.c | 6 +++ drivers/gpu/drm/i915/intel_dp.c | 79 +++++++++++++++++++++++++++------ 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 14d679ab52b7..93630669c6dc 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1737,6 +1737,7 @@ int i915_driver_unload(struct drm_device *dev) intel_teardown_mchbar(dev); destroy_workqueue(dev_priv->wq); + pm_qos_remove_request(&dev_priv->pm_qos); if (dev_priv->slab) kmem_cache_destroy(dev_priv->slab); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3843383e13cc..a00ee3da632f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -40,6 +40,7 @@ #include #include #include +#include /* General customization: */ @@ -665,6 +666,9 @@ typedef struct drm_i915_private { /* protects the irq masks */ spinlock_t irq_lock; + /* To control wakeup latency, e.g. for irq-driven dp aux transfers. */ + struct pm_qos_request pm_qos; + /* DPIO indirect register protection */ spinlock_t dpio_lock; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 1e245e501e5a..58bb11b58796 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -536,7 +536,11 @@ static void gmbus_irq_handler(struct drm_device *dev) static void dp_aux_irq_handler(struct drm_device *dev) { + struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; + DRM_DEBUG_DRIVER("AUX channel interrupt\n"); + + wake_up_all(&dev_priv->gmbus_wait_queue); } static irqreturn_t valleyview_irq_handler(int irq, void *arg) @@ -2732,6 +2736,8 @@ void intel_irq_init(struct drm_device *dev) setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, (unsigned long) dev); + pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, 0); + dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b51043e651b5..ada1b316195c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -322,6 +322,48 @@ intel_dp_check_edp(struct intel_dp *intel_dp) } } +static uint32_t +intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t ch_ctl = intel_dp->output_reg + 0x10; + uint32_t status; + bool done; + + if (IS_HASWELL(dev)) { + switch (intel_dig_port->port) { + case PORT_A: + ch_ctl = DPA_AUX_CH_CTL; + break; + case PORT_B: + ch_ctl = PCH_DPB_AUX_CH_CTL; + break; + case PORT_C: + ch_ctl = PCH_DPC_AUX_CH_CTL; + break; + case PORT_D: + ch_ctl = PCH_DPD_AUX_CH_CTL; + break; + default: + BUG(); + } + } + +#define C (((status = I915_READ(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0) + if (has_aux_irq) + done = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10); + else + done = wait_for_atomic(C, 10) == 0; + if (!done) + DRM_ERROR("dp aux hw did not signal timeout (has irq: %i)!\n", + has_aux_irq); +#undef C + + return status; +} + static int intel_dp_aux_ch(struct intel_dp *intel_dp, uint8_t *send, int send_bytes, @@ -333,11 +375,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, struct drm_i915_private *dev_priv = dev->dev_private; uint32_t ch_ctl = output_reg + 0x10; uint32_t ch_data = ch_ctl + 4; - int i; - int recv_bytes; + int i, ret, recv_bytes; uint32_t status; uint32_t aux_clock_divider; int try, precharge; + bool has_aux_irq = INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev); + + /* dp aux is extremely sensitive to irq latency, hence request the + * lowest possible wakeup latency and so prevent the cpu from going into + * deep sleep states. + */ + pm_qos_update_request(&dev_priv->pm_qos, 0); if (IS_HASWELL(dev)) { switch (intel_dig_port->port) { @@ -400,7 +448,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, if (try == 3) { WARN(1, "dp_aux_ch not started status 0x%08x\n", I915_READ(ch_ctl)); - return -EBUSY; + ret = -EBUSY; + goto out; } /* Must try at least 3 times according to DP spec */ @@ -413,6 +462,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, /* Send the command and wait for it to complete */ I915_WRITE(ch_ctl, DP_AUX_CH_CTL_SEND_BUSY | + (has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) | DP_AUX_CH_CTL_TIME_OUT_400us | (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | @@ -420,12 +470,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, DP_AUX_CH_CTL_DONE | DP_AUX_CH_CTL_TIME_OUT_ERROR | DP_AUX_CH_CTL_RECEIVE_ERROR); - for (;;) { - status = I915_READ(ch_ctl); - if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) - break; - udelay(100); - } + + status = intel_dp_aux_wait_done(intel_dp, has_aux_irq); /* Clear done status and any errors */ I915_WRITE(ch_ctl, @@ -443,7 +489,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, if ((status & DP_AUX_CH_CTL_DONE) == 0) { DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); - return -EBUSY; + ret = -EBUSY; + goto out; } /* Check for timeout or receive error. @@ -451,14 +498,16 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, */ if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); - return -EIO; + ret = -EIO; + goto out; } /* Timeouts occur when the device isn't connected, so they're * "normal" -- don't fill the kernel log with these */ if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) { DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } /* Unload any bytes sent back from the other side */ @@ -471,7 +520,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, unpack_aux(I915_READ(ch_data + i), recv + i, recv_bytes - i); - return recv_bytes; + ret = recv_bytes; +out: + pm_qos_update_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE); + + return ret; } /* Write data to the aux channel in native mode */ From ef04f00d12312e43b13ba8f79435763e25aaff5c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 21:03:59 +0100 Subject: [PATCH 0126/4607] drm/i915: use _NOTRACE for gmbus/dp aux wait loops Less clutter in the traces. And in both cases we yell rather loud into the logs if we time out. Patch suggested by Chris Wilson. v2: Annotate another I915_READ in dp_aux to be consistent - we filter out all register io in wait_for and similar loops. Chris also suggested to mark all dp_aux register access as _NOTRACE, but I think we should keep all functionally relevant access around, and filter unneeded bits in userspace after the trace is captured. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 4 ++-- drivers/gpu/drm/i915/intel_i2c.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ada1b316195c..3f633cadc8ba 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -351,7 +351,7 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq) } } -#define C (((status = I915_READ(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0) +#define C (((status = I915_READ_NOTRACE(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0) if (has_aux_irq) done = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10); else @@ -439,7 +439,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, /* Try to wait for any previous AUX channel activity */ for (try = 0; try < 3; try++) { - status = I915_READ(ch_ctl); + status = I915_READ_NOTRACE(ch_ctl); if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) break; msleep(1); diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 1fc31191b910..7f0904170963 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -223,7 +223,7 @@ gmbus_wait_hw_status(struct drm_i915_private *dev_priv, prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait, TASK_UNINTERRUPTIBLE); - gmbus2 = I915_READ(GMBUS2 + reg_offset); + gmbus2 = I915_READ_NOTRACE(GMBUS2 + reg_offset); if (gmbus2 & (GMBUS_SATOER | gmbus2_status)) break; @@ -246,7 +246,7 @@ gmbus_wait_idle(struct drm_i915_private *dev_priv) int ret; int reg_offset = dev_priv->gpio_mmio_base; -#define C ((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0) +#define C ((I915_READ_NOTRACE(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0) if (!HAS_GMBUS_IRQ(dev_priv->dev)) return wait_for(C, 10); From 6ef6a450b9a4c8851d63e58aa62ee6a34c01c156 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 5 Dec 2012 22:37:17 +0800 Subject: [PATCH 0127/4607] drm/i915: Remove duplicate and unused register #defines in i915_reg.h TRANS_DP_VIDEO_AUDIO is not used at all. The other 3 has duplicated #defines. Signed-off-by: Dexuan Cui Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 462b50a3d88e..f2a5ea694684 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1875,8 +1875,6 @@ #define PFIT_SCALING_PILLAR (2 << 26) #define PFIT_SCALING_LETTER (3 << 26) #define PFIT_PGM_RATIOS 0x61234 -#define PFIT_VERT_SCALE_MASK 0xfff00000 -#define PFIT_HORIZ_SCALE_MASK 0x0000fff0 /* Pre-965 */ #define PFIT_VERT_SCALE_SHIFT 20 #define PFIT_VERT_SCALE_MASK 0xfff00000 @@ -3799,8 +3797,6 @@ #define TRANS_FSYNC_DELAY_HB2 (1<<27) #define TRANS_FSYNC_DELAY_HB3 (2<<27) #define TRANS_FSYNC_DELAY_HB4 (3<<27) -#define TRANS_DP_AUDIO_ONLY (1<<26) -#define TRANS_DP_VIDEO_AUDIO (0<<26) #define TRANS_INTERLACE_MASK (7<<21) #define TRANS_PROGRESSIVE (0<<21) #define TRANS_INTERLACED (3<<21) From 36dacf5b8b3c0bab7010943c34d2dbcc09a0d6f3 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 6 Dec 2012 09:44:52 -0200 Subject: [PATCH 0128/4607] drm/i915: be less verbose when handling gmbus/aux irqs Having 9500 lines repeated on dmesg does not help me at all. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 58bb11b58796..2e1d80de6ccc 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -529,8 +529,6 @@ static void gmbus_irq_handler(struct drm_device *dev) { struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; - DRM_DEBUG_DRIVER("GMBUS interrupt\n"); - wake_up_all(&dev_priv->gmbus_wait_queue); } @@ -538,8 +536,6 @@ static void dp_aux_irq_handler(struct drm_device *dev) { struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; - DRM_DEBUG_DRIVER("AUX channel interrupt\n"); - wake_up_all(&dev_priv->gmbus_wait_queue); } From ed7ef43989b3a8235b1cbf1e2b025aa4af3368a6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 6 Dec 2012 14:24:21 +0100 Subject: [PATCH 0129/4607] drm/i915: rip out pre-DDI stuff from haswell_crtc_mode_set Especially getting rid of all things lvds is ... great! v2: Drop the two additional pre-hsw hunks noticed by Paulo Zanoni. v3: - handle DP ports correctly (spoted by Paulo) - don't leave {} behind for a single-line block (again spotted by Paulo) - kill another if (IBX || CPT) block Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 128 +-------------------------- 1 file changed, 2 insertions(+), 126 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 093a163d4b1c..5865cd9ddc3a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5461,20 +5461,13 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; int num_connectors = 0; - intel_clock_t clock, reduced_clock; - u32 dpll = 0, fp = 0, fp2 = 0; - bool ok, has_reduced_clock = false; - bool is_lvds = false, is_dp = false, is_cpu_edp = false; + bool is_dp = false, is_cpu_edp = false; struct intel_encoder *encoder; - u32 temp; int ret; bool dither; for_each_encoder_on_crtc(dev, crtc, encoder) { switch (encoder->type) { - case INTEL_OUTPUT_LVDS: - is_lvds = true; - break; case INTEL_OUTPUT_DISPLAYPORT: is_dp = true; break; @@ -5508,143 +5501,26 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, if (!intel_ddi_pll_mode_set(crtc, adjusted_mode->clock)) return -EINVAL; - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - ok = ironlake_compute_clocks(crtc, adjusted_mode, &clock, - &has_reduced_clock, - &reduced_clock); - if (!ok) { - DRM_ERROR("Couldn't find PLL settings for mode!\n"); - return -EINVAL; - } - } - /* Ensure that the cursor is valid for the new mode before changing... */ intel_crtc_update_cursor(crtc, true); /* determine panel color depth */ dither = intel_choose_pipe_bpp_dither(crtc, fb, &intel_crtc->bpp, adjusted_mode); - if (is_lvds && dev_priv->lvds_dither) - dither = true; DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); drm_mode_debug_printmodeline(mode); - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - fp = clock.n << 16 | clock.m1 << 8 | clock.m2; - if (has_reduced_clock) - fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | - reduced_clock.m2; - - dpll = ironlake_compute_dpll(intel_crtc, adjusted_mode, &clock, - fp); - - /* CPU eDP is the only output that doesn't need a PCH PLL of its - * own on pre-Haswell/LPT generation */ - if (!is_cpu_edp) { - struct intel_pch_pll *pll; - - pll = intel_get_pch_pll(intel_crtc, dpll, fp); - if (pll == NULL) { - DRM_DEBUG_DRIVER("failed to find PLL for pipe %d\n", - pipe); - return -EINVAL; - } - } else - intel_put_pch_pll(intel_crtc); - - /* The LVDS pin pair needs to be on before the DPLLs are - * enabled. This is an exception to the general rule that - * mode_set doesn't turn things on. - */ - if (is_lvds) { - temp = I915_READ(PCH_LVDS); - temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (HAS_PCH_CPT(dev)) { - temp &= ~PORT_TRANS_SEL_MASK; - temp |= PORT_TRANS_SEL_CPT(pipe); - } else { - if (pipe == 1) - temp |= LVDS_PIPEB_SELECT; - else - temp &= ~LVDS_PIPEB_SELECT; - } - - /* set the corresponsding LVDS_BORDER bit */ - temp |= dev_priv->lvds_border_bits; - /* Set the B0-B3 data pairs corresponding to whether - * we're going to set the DPLLs for dual-channel mode or - * not. - */ - if (clock.p2 == 7) - temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; - else - temp &= ~(LVDS_B0B3_POWER_UP | - LVDS_CLKB_POWER_UP); - - /* It would be nice to set 24 vs 18-bit mode - * (LVDS_A3_POWER_UP) appropriately here, but we need to - * look more thoroughly into how panels behave in the - * two modes. - */ - temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - temp |= LVDS_HSYNC_POLARITY; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - temp |= LVDS_VSYNC_POLARITY; - I915_WRITE(PCH_LVDS, temp); - } - } - - if (is_dp && !is_cpu_edp) { + if (is_dp && !is_cpu_edp) intel_dp_set_m_n(crtc, mode, adjusted_mode); - } else { - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - /* For non-DP output, clear any trans DP clock recovery - * setting.*/ - I915_WRITE(TRANSDATA_M1(pipe), 0); - I915_WRITE(TRANSDATA_N1(pipe), 0); - I915_WRITE(TRANSDPLINK_M1(pipe), 0); - I915_WRITE(TRANSDPLINK_N1(pipe), 0); - } - } intel_crtc->lowfreq_avail = false; - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { - if (intel_crtc->pch_pll) { - I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); - - /* Wait for the clocks to stabilize. */ - POSTING_READ(intel_crtc->pch_pll->pll_reg); - udelay(150); - - /* The pixel multiplier can only be updated once the - * DPLL is enabled and the clocks are stable. - * - * So write it again. - */ - I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); - } - - if (intel_crtc->pch_pll) { - if (is_lvds && has_reduced_clock && i915_powersave) { - I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp2); - intel_crtc->lowfreq_avail = true; - } else { - I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp); - } - } - } intel_set_pipe_timings(intel_crtc, mode, adjusted_mode); if (!is_dp || is_cpu_edp) ironlake_set_m_n(crtc, mode, adjusted_mode); - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) - if (is_cpu_edp) - ironlake_set_pll_edp(crtc, adjusted_mode->clock); - haswell_set_pipeconf(crtc, adjusted_mode, dither); /* Set up the display plane register */ From ea9b6006b51b79cfbb87c1ca81923761b7799c0f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:31 +0100 Subject: [PATCH 0130/4607] drm/i915: move set_pll_edp to intel_dp.c Now that we enable the cpu edp pll in intel_dp->pre_enable and no longer in crtc_mode_set, we can also move the modeset part to the intel_dp->mode_set callback. Previously this was not possible because the encoder ->mode_set callbacks are called after the crtc mode set callback. v2: Rebase on top of copy&pasted hsw crtc_mode_set. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 40 ---------------------------- drivers/gpu/drm/i915/intel_dp.c | 40 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5865cd9ddc3a..f3e58a43886b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2294,43 +2294,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; } -static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock) -{ - struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - u32 dpa_ctl; - - DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock); - dpa_ctl = I915_READ(DP_A); - dpa_ctl &= ~DP_PLL_FREQ_MASK; - - if (clock < 200000) { - u32 temp; - dpa_ctl |= DP_PLL_FREQ_160MHZ; - /* workaround for 160Mhz: - 1) program 0x4600c bits 15:0 = 0x8124 - 2) program 0x46010 bit 0 = 1 - 3) program 0x46034 bit 24 = 1 - 4) program 0x64000 bit 14 = 1 - */ - temp = I915_READ(0x4600c); - temp &= 0xffff0000; - I915_WRITE(0x4600c, temp | 0x8124); - - temp = I915_READ(0x46010); - I915_WRITE(0x46010, temp | 1); - - temp = I915_READ(0x46034); - I915_WRITE(0x46034, temp | (1 << 24)); - } else { - dpa_ctl |= DP_PLL_FREQ_270MHZ; - } - I915_WRITE(DP_A, dpa_ctl); - - POSTING_READ(DP_A); - udelay(500); -} - static void intel_fdi_normal_train(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -5429,9 +5392,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, fdi_config_ok = ironlake_check_fdi_lanes(intel_crtc); - if (is_cpu_edp) - ironlake_set_pll_edp(crtc, adjusted_mode->clock); - ironlake_set_pipeconf(crtc, adjusted_mode, dither); intel_wait_for_vblank(dev, pipe); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3f633cadc8ba..e525f0302d8f 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -899,6 +899,43 @@ void intel_dp_init_link_config(struct intel_dp *intel_dp) } } +static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 dpa_ctl; + + DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock); + dpa_ctl = I915_READ(DP_A); + dpa_ctl &= ~DP_PLL_FREQ_MASK; + + if (clock < 200000) { + u32 temp; + dpa_ctl |= DP_PLL_FREQ_160MHZ; + /* workaround for 160Mhz: + 1) program 0x4600c bits 15:0 = 0x8124 + 2) program 0x46010 bit 0 = 1 + 3) program 0x46034 bit 24 = 1 + 4) program 0x64000 bit 14 = 1 + */ + temp = I915_READ(0x4600c); + temp &= 0xffff0000; + I915_WRITE(0x4600c, temp | 0x8124); + + temp = I915_READ(0x46010); + I915_WRITE(0x46010, temp | 1); + + temp = I915_READ(0x46034); + I915_WRITE(0x46034, temp | (1 << 24)); + } else { + dpa_ctl |= DP_PLL_FREQ_270MHZ; + } + I915_WRITE(DP_A, dpa_ctl); + + POSTING_READ(DP_A); + udelay(500); +} + static void intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -998,6 +1035,9 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, } else { intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; } + + if (is_cpu_edp(intel_dp)) + ironlake_set_pll_edp(crtc, adjusted_mode->clock); } #define IDLE_ON_MASK (PP_ON | 0 | PP_SEQUENCE_MASK | 0 | PP_SEQUENCE_STATE_MASK) From 1ce17038093f02e708e5816f645bbec63aff8bd2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:32 +0100 Subject: [PATCH 0131/4607] drm/i915: rip out pre-production ilk cpu edp w/a MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While reading docs I've noticed that this special workaround to select the 1.6 GHz DP clock only applies to pre-production ilk machines. Since the registers we're touching here are rather undocumented and might be harmful on later chips, rip it out. For the Bspec reference of this w/a look in "vol4g CPU Display Registers [DevILK]", Section 4.1.7.1 "DP_A—DisplayPort A Control Register", "DP_PLL_Frequency_Select". v2: Keep a debug message as a hint in case something regresses. Requested by Chris Wilson. Reviewed-by: Paulo Zanoni Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index e525f0302d8f..c01163d22d17 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -910,26 +910,15 @@ static void ironlake_set_pll_edp(struct drm_crtc *crtc, int clock) dpa_ctl &= ~DP_PLL_FREQ_MASK; if (clock < 200000) { - u32 temp; + /* For a long time we've carried around a ILK-DevA w/a for the + * 160MHz clock. If we're really unlucky, it's still required. + */ + DRM_DEBUG_KMS("160MHz cpu eDP clock, might need ilk devA w/a\n"); dpa_ctl |= DP_PLL_FREQ_160MHZ; - /* workaround for 160Mhz: - 1) program 0x4600c bits 15:0 = 0x8124 - 2) program 0x46010 bit 0 = 1 - 3) program 0x46034 bit 24 = 1 - 4) program 0x64000 bit 14 = 1 - */ - temp = I915_READ(0x4600c); - temp &= 0xffff0000; - I915_WRITE(0x4600c, temp | 0x8124); - - temp = I915_READ(0x46010); - I915_WRITE(0x46010, temp | 1); - - temp = I915_READ(0x46034); - I915_WRITE(0x46034, temp | (1 << 24)); } else { dpa_ctl |= DP_PLL_FREQ_270MHZ; } + I915_WRITE(DP_A, dpa_ctl); POSTING_READ(DP_A); From ab527efc2feadcab1cad0740be307cb4153b493f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:33 +0100 Subject: [PATCH 0132/4607] drm/i915: use wait_for_vblank instead of msleep(17) 17 ms is eerily close to 60 Hz ^-1 Unfortunately this goes back to the original DP enabling for ilk, and unfortunately does not come with a reason for it's existance attached. Some closer inspection of the code and DP specs shows that we set the idle link pattern before we disable the port. And it seems like that the DP spec (or at least our hw) only switch to the idle pattern on the next vblank. Hence a vblank wait at this spot makes _much_ more sense than a really long wait. v2: Rebase fixup. v3: Add comment requested by Paulo Zanoni saying that we don't really know what this wait is for. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index c01163d22d17..89164c553b2d 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2041,6 +2041,8 @@ intel_dp_link_down(struct intel_dp *intel_dp) struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = + to_intel_crtc(intel_dig_port->base.base.crtc); uint32_t DP = intel_dp->DP; /* @@ -2075,7 +2077,8 @@ intel_dp_link_down(struct intel_dp *intel_dp) } POSTING_READ(intel_dp->output_reg); - msleep(17); + /* We don't really know why we're doing this */ + intel_wait_for_vblank(dev, intel_crtc->pipe); if (HAS_PCH_IBX(dev) && I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) { @@ -2107,7 +2110,7 @@ intel_dp_link_down(struct intel_dp *intel_dp) POSTING_READ(intel_dp->output_reg); msleep(50); } else - intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe); + intel_wait_for_vblank(dev, intel_crtc->pipe); } DP &= ~DP_AUDIO_OUTPUT_ENABLE; From ff50afe9aceb6264a4fbe40459da75170fb9a2a2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:34 +0100 Subject: [PATCH 0133/4607] drm/i915: WARN on !crtc in intel_dp_link_down This could have happened with the old crtc helper based modeset code, but can't happen any longer with the new code. Hence put in a WARN and adjust the comment. If no one hits this, we can eventually remove it (like a few other such cases across our code). Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 89164c553b2d..84652ca3b161 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2098,15 +2098,10 @@ intel_dp_link_down(struct intel_dp *intel_dp) /* Changes to enable or select take place the vblank * after being written. */ - if (crtc == NULL) { - /* We can arrive here never having been attached - * to a CRTC, for instance, due to inheriting - * random state from the BIOS. - * - * If the pipe is not running, play safe and - * wait for the clocks to stabilise before - * continuing. - */ + if (WARN_ON(crtc == NULL)) { + /* We should never try to disable a port without a crtc + * attached. For paranoia keep the code around for a + * bit. */ POSTING_READ(intel_dp->output_reg); msleep(50); } else From 2f0c2ad18b88691496e23d1ddbc2d0af8f6df5fa Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:35 +0100 Subject: [PATCH 0134/4607] drm/i915: drop unnecessary clearing of pch dp transcoder timings This has originally been added in commit 8db9d77b1b14fd730561f64beea8c00e4478d7c5 Author: Zhenyu Wang Date: Wed Apr 7 16:15:54 2010 +0800 drm/i915: Support for Cougarpoint PCH display pipeline probably to combat issues with hw state left behind by the BIOS. And indeed, I've checked out that specific revision, and there is no DP support yet. So the pch dp transcoder won't be correctly disabled, and that's important since it requires a rether special disable dance: Just writing 0 to TRANS_DP_CTL won't cut it, since we need to select the NONE port when disabling, too. And indeed, things seem to still work, so let's just remove this. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f3e58a43886b..953796c84fbd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5345,15 +5345,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } else intel_put_pch_pll(intel_crtc); - if (is_dp && !is_cpu_edp) { + if (is_dp && !is_cpu_edp) intel_dp_set_m_n(crtc, mode, adjusted_mode); - } else { - /* For non-DP output, clear any trans DP clock recovery setting.*/ - I915_WRITE(TRANSDATA_M1(pipe), 0); - I915_WRITE(TRANSDATA_N1(pipe), 0); - I915_WRITE(TRANSDPLINK_M1(pipe), 0); - I915_WRITE(TRANSDPLINK_N1(pipe), 0); - } for_each_encoder_on_crtc(dev, crtc, encoder) if (encoder->pre_pll_enable) From e69d0bc1c67520c302e070ac078975ea9c786de8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 15:59:36 +0100 Subject: [PATCH 0135/4607] drm/i915: extract common link_m_n helpers Both the dp and fdi code use the exact same computations (ignore minor differences in conversion between bits and bytes). This makes it even more apparent that we have a _massive_ mess between cpu transcoder/fdi link/pch transcoder and pch link settings. And also that we have hilarious amounts of confusion between edp and dp (despite that they're identical at a link level). Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 13 ++++++++++ drivers/gpu/drm/i915/intel_display.c | 31 +++++++--------------- drivers/gpu/drm/i915/intel_dp.c | 39 +++------------------------- 3 files changed, 26 insertions(+), 57 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a00ee3da632f..e9ac3603fbdb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -107,6 +107,19 @@ struct intel_pch_pll { }; #define I915_NUM_PLLS 2 +/* Used by dp and fdi links */ +struct intel_link_m_n { + uint32_t tu; + uint32_t gmch_m; + uint32_t gmch_n; + uint32_t link_m; + uint32_t link_n; +}; + +void intel_link_compute_m_n(int bpp, int nlanes, + int pixel_clock, int link_clock, + struct intel_link_m_n *m_n); + struct intel_ddi_plls { int spll_refcount; int wrpll1_refcount; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 953796c84fbd..34832bc04931 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3951,16 +3951,8 @@ static int i830_get_display_clock_speed(struct drm_device *dev) return 133000; } -struct fdi_m_n { - u32 tu; - u32 gmch_m; - u32 gmch_n; - u32 link_m; - u32 link_n; -}; - static void -fdi_reduce_ratio(u32 *num, u32 *den) +intel_reduce_ratio(uint32_t *num, uint32_t *den) { while (*num > 0xffffff || *den > 0xffffff) { *num >>= 1; @@ -3968,20 +3960,18 @@ fdi_reduce_ratio(u32 *num, u32 *den) } } -static void -ironlake_compute_m_n(int bits_per_pixel, int nlanes, int pixel_clock, - int link_clock, struct fdi_m_n *m_n) +void +intel_link_compute_m_n(int bits_per_pixel, int nlanes, + int pixel_clock, int link_clock, + struct intel_link_m_n *m_n) { - m_n->tu = 64; /* default size */ - - /* BUG_ON(pixel_clock > INT_MAX / 36); */ + m_n->tu = 64; m_n->gmch_m = bits_per_pixel * pixel_clock; m_n->gmch_n = link_clock * nlanes * 8; - fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); - + intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); m_n->link_m = pixel_clock; m_n->link_n = link_clock; - fdi_reduce_ratio(&m_n->link_m, &m_n->link_n); + intel_reduce_ratio(&m_n->link_m, &m_n->link_n); } static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) @@ -5095,7 +5085,7 @@ static void ironlake_set_m_n(struct drm_crtc *crtc, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; struct intel_encoder *intel_encoder, *edp_encoder = NULL; - struct fdi_m_n m_n = {0}; + struct intel_link_m_n m_n = {0}; int target_clock, pixel_multiplier, lane, link_bw; bool is_dp = false, is_cpu_edp = false; @@ -5153,8 +5143,7 @@ static void ironlake_set_m_n(struct drm_crtc *crtc, if (pixel_multiplier > 1) link_bw *= pixel_multiplier; - ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, - &m_n); + intel_link_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, &m_n); I915_WRITE(PIPE_DATA_M1(cpu_transcoder), TU_SIZE(m_n.tu) | m_n.gmch_m); I915_WRITE(PIPE_DATA_N1(cpu_transcoder), m_n.gmch_n); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 84652ca3b161..b2130bc6c297 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -790,39 +790,6 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, return false; } -struct intel_dp_m_n { - uint32_t tu; - uint32_t gmch_m; - uint32_t gmch_n; - uint32_t link_m; - uint32_t link_n; -}; - -static void -intel_reduce_ratio(uint32_t *num, uint32_t *den) -{ - while (*num > 0xffffff || *den > 0xffffff) { - *num >>= 1; - *den >>= 1; - } -} - -static void -intel_dp_compute_m_n(int bpp, - int nlanes, - int pixel_clock, - int link_clock, - struct intel_dp_m_n *m_n) -{ - m_n->tu = 64; - m_n->gmch_m = (pixel_clock * bpp) >> 3; - m_n->gmch_n = link_clock * nlanes; - intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); - m_n->link_m = pixel_clock; - m_n->link_n = link_clock; - intel_reduce_ratio(&m_n->link_m, &m_n->link_n); -} - void intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -833,7 +800,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int lane_count = 4; - struct intel_dp_m_n m_n; + struct intel_link_m_n m_n; int pipe = intel_crtc->pipe; enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; @@ -856,8 +823,8 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, * the number of bytes_per_pixel post-LUT, which we always * set up for 8-bits of R/G/B, or 3 bytes total. */ - intel_dp_compute_m_n(intel_crtc->bpp, lane_count, - mode->clock, adjusted_mode->clock, &m_n); + intel_link_compute_m_n(intel_crtc->bpp, lane_count, + mode->clock, adjusted_mode->clock, &m_n); if (IS_HASWELL(dev)) { I915_WRITE(PIPE_DATA_M1(cpu_transcoder), From e9b73c67390a5d4faec1d22cbdf24cd6fcca53f6 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 3 Dec 2012 21:03:14 +0000 Subject: [PATCH 0136/4607] drm/i915: Reduce memory pressure during shrinker by preallocating swizzle pages On a machine with bit17 swizzling, we need to store the bit17 of the physical page address in put-pages. This requires a memory allocation, on average less than a page, which may be difficult to satisfy is the request to put-pages is on behalf of the shrinker. We could allow that allocation to pull from the reserved memory pools, but it seems much safer to preallocate the array for tiled objects on affected machines. v2: Export i915_gem_object_needs_bit17_swizzle() for reuse. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 10 ++++++++++ drivers/gpu/drm/i915/i915_gem.c | 8 -------- drivers/gpu/drm/i915/i915_gem_tiling.c | 12 ++++++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e9ac3603fbdb..2ab476df1dc2 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -30,6 +30,8 @@ #ifndef _I915_DRV_H_ #define _I915_DRV_H_ +#include + #include "i915_reg.h" #include "intel_bios.h" #include "intel_ringbuffer.h" @@ -1614,6 +1616,14 @@ i915_gem_object_create_stolen(struct drm_device *dev, u32 size); void i915_gem_object_release_stolen(struct drm_i915_gem_object *obj); /* i915_gem_tiling.c */ +inline static bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj) +{ + drm_i915_private_t *dev_priv = obj->base.dev->dev_private; + + return dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_9_10_17 && + obj->tiling_mode != I915_TILING_NONE; +} + void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj); void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 02d315164aa9..e4b233df576f 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -271,14 +271,6 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data, args->size, &args->handle); } -static int i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj) -{ - drm_i915_private_t *dev_priv = obj->base.dev->dev_private; - - return dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_9_10_17 && - obj->tiling_mode != I915_TILING_NONE; -} - static inline int __copy_to_user_swizzled(char __user *cpu_vaddr, const char *gpu_vaddr, int gpu_offset, diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index cedbfd7b3dfa..65f1d4f3f775 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -396,6 +396,18 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, /* we have to maintain this existing ABI... */ args->stride = obj->stride; args->tiling_mode = obj->tiling_mode; + + /* Try to preallocate memory required to save swizzling on put-pages */ + if (i915_gem_object_needs_bit17_swizzle(obj)) { + if (obj->bit_17 == NULL) { + obj->bit_17 = kmalloc(BITS_TO_LONGS(obj->base.size >> PAGE_SHIFT) * + sizeof(long), GFP_KERNEL); + } + } else { + kfree(obj->bit_17); + obj->bit_17 = NULL; + } + drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); From 97a19a247c23e286814a5ac7ec0825d0ff82a16c Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Sat, 8 Dec 2012 13:48:13 +0100 Subject: [PATCH 0137/4607] drm/i915: don't prevent CPU idle states Commit 9ee32fea5f unconditionally prevents the CPU from entering idle states until intel_dp_aux_ch completes for the first time, which never happens on my DisplayPort-less intel gfx, causing the CPU to get rather hot. Signed-off-by: Tomas Janousek Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2e1d80de6ccc..914ecf4acfa6 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2732,7 +2732,7 @@ void intel_irq_init(struct drm_device *dev) setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, (unsigned long) dev); - pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, 0); + pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ From 409f117e2d6b38d714ab26def840801e33dfe115 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 3 Dec 2012 15:36:10 +0800 Subject: [PATCH 0138/4607] merge_config.sh: Add option to specify output dir Provide a -O option to specify dir to put generated .config Then merge_config.sh does not need to be copied to target dir, for easy re-usage in other script Signed-off-by: Zhangfei Gao Tested-by: Jon Medhurst (Tixy) Acked-by: John Stultz Signed-off-by: Michal Marek --- scripts/kconfig/merge_config.sh | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/kconfig/merge_config.sh b/scripts/kconfig/merge_config.sh index 974d5cb7e30a..05274fccb88e 100755 --- a/scripts/kconfig/merge_config.sh +++ b/scripts/kconfig/merge_config.sh @@ -32,11 +32,13 @@ usage() { echo " -m only merge the fragments, do not execute the make command" echo " -n use allnoconfig instead of alldefconfig" echo " -r list redundant entries when merging fragments" + echo " -O dir to put generated output files" } MAKE=true ALLTARGET=alldefconfig WARNREDUN=false +OUTPUT=. while true; do case $1 in @@ -59,6 +61,16 @@ while true; do shift continue ;; + "-O") + if [ -d $2 ];then + OUTPUT=$(echo $2 | sed 's/\/*$//') + else + echo "output directory $2 does not exist" 1>&2 + exit 1 + fi + shift 2 + continue + ;; *) break ;; @@ -100,9 +112,9 @@ for MERGE_FILE in $MERGE_LIST ; do done if [ "$MAKE" = "false" ]; then - cp $TMP_FILE .config + cp $TMP_FILE $OUTPUT/.config echo "#" - echo "# merged configuration written to .config (needs make)" + echo "# merged configuration written to $OUTPUT/.config (needs make)" echo "#" clean_up exit @@ -111,14 +123,14 @@ fi # Use the merged file as the starting point for: # alldefconfig: Fills in any missing symbols with Kconfig default # allnoconfig: Fills in any missing symbols with # CONFIG_* is not set -make KCONFIG_ALLCONFIG=$TMP_FILE $ALLTARGET +make KCONFIG_ALLCONFIG=$TMP_FILE O=$OUTPUT $ALLTARGET # Check all specified config values took (might have missed-dependency issues) for CFG in $(sed -n "$SED_CONFIG_EXP" $TMP_FILE); do REQUESTED_VAL=$(grep -w -e "$CFG" $TMP_FILE) - ACTUAL_VAL=$(grep -w -e "$CFG" .config) + ACTUAL_VAL=$(grep -w -e "$CFG" $OUTPUT/.config) if [ "x$REQUESTED_VAL" != "x$ACTUAL_VAL" ] ; then echo "Value requested for $CFG not in final .config" echo "Requested value: $REQUESTED_VAL" From 378a6a77ae59d3312627b996ded94e23166d9e63 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 7 Dec 2012 14:18:29 +0530 Subject: [PATCH 0139/4607] drm/i915: Remove duplicate inclusion of drm/drm_edid.h drm/drm_edid.h was included twice. Signed-off-by: Sachin Kamat Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_modes.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c index b00f1c83adce..49249bb97485 100644 --- a/drivers/gpu/drm/i915/intel_modes.c +++ b/drivers/gpu/drm/i915/intel_modes.c @@ -28,7 +28,6 @@ #include #include #include -#include #include "intel_drv.h" #include "i915_drv.h" From 3ac18232946aacf0a9807c3143f8449ed4aa68f4 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Fri, 7 Dec 2012 07:54:26 -0700 Subject: [PATCH 0140/4607] i915: intel_set_mode: Reduce stack allocation from 500 bytes to 2 pointers smatch warning: drivers/gpu/drm/i915/intel_display.c:7019 intel_set_mode() warn: function puts 500 bytes on stack Refactor so that saved_mode and saved_hwmode are dynamically allocated as opposed to being automatic variables. 500 bytes seems like it could run the potential for blowing the kernel stack. Cc: Daniel Vetter Cc: David Airlie Cc: dri-devel@lists.freedesktop.org Signed-off-by: Tim Gardner Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 34832bc04931..115bf626037c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7398,11 +7398,18 @@ bool intel_set_mode(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; + struct drm_display_mode *adjusted_mode, *saved_mode, *saved_hwmode; struct intel_crtc *intel_crtc; unsigned disable_pipes, prepare_pipes, modeset_pipes; bool ret = true; + saved_mode = kmalloc(2 * sizeof(*saved_mode), GFP_KERNEL); + if (!saved_mode) { + DRM_ERROR("i915: Could not allocate saved display mode.\n"); + return false; + } + saved_hwmode = saved_mode + 1; + intel_modeset_affected_pipes(crtc, &modeset_pipes, &prepare_pipes, &disable_pipes); @@ -7412,8 +7419,8 @@ bool intel_set_mode(struct drm_crtc *crtc, for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) intel_crtc_disable(&intel_crtc->base); - saved_hwmode = crtc->hwmode; - saved_mode = crtc->mode; + *saved_hwmode = crtc->hwmode; + *saved_mode = crtc->mode; /* Hack: Because we don't (yet) support global modeset on multiple * crtcs, we don't keep track of the new mode for more than one crtc. @@ -7424,7 +7431,8 @@ bool intel_set_mode(struct drm_crtc *crtc, if (modeset_pipes) { adjusted_mode = intel_modeset_adjusted_mode(crtc, mode); if (IS_ERR(adjusted_mode)) { - return false; + ret = false; + goto out; } } @@ -7476,12 +7484,14 @@ bool intel_set_mode(struct drm_crtc *crtc, done: drm_mode_destroy(dev, adjusted_mode); if (!ret && crtc->enabled) { - crtc->hwmode = saved_hwmode; - crtc->mode = saved_mode; + crtc->hwmode = *saved_hwmode; + crtc->mode = *saved_mode; } else { intel_modeset_check_state(dev); } +out: + kfree(saved_mode); return ret; } From f72b3435c1a75406d82d6e252bb78f009efd4bd9 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Mon, 10 Dec 2012 15:41:48 +0200 Subject: [PATCH 0141/4607] drm/i915: Don't emit semaphore wait if wrap happened If wrap just happened we need to prevent emitting waits for pre wrap values. Detect this and emit no-ops instead. v2: Use olr > seqno to detect wrap instead of *seqno == 0 as suggested by Chris Wilson. v3: Use last used seqno to detect the wraparound. From Chris Wilson v4: Fixed unnecessary last_seqno assigment References: https://bugs.freedesktop.org/show_bug.cgi?id=57967 Signed-off-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/intel_ringbuffer.c | 26 ++++++++++++++++++++----- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2ab476df1dc2..9da67824dde3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -671,7 +671,7 @@ typedef struct drm_i915_private { struct pci_dev *bridge_dev; struct intel_ring_buffer ring[I915_NUM_RINGS]; - uint32_t next_seqno; + uint32_t last_seqno, next_seqno; drm_dma_handle_t *status_page_dmah; struct resource mch_res; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e4b233df576f..a81b78a59bd9 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1969,7 +1969,7 @@ i915_gem_get_seqno(struct drm_device *dev, u32 *seqno) dev_priv->next_seqno = 1; } - *seqno = dev_priv->next_seqno++; + *seqno = dev_priv->last_seqno = dev_priv->next_seqno++; return 0; } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 0d03dc649c5e..69bbe7bb9f6d 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -596,6 +596,13 @@ gen6_add_request(struct intel_ring_buffer *ring) return 0; } +static inline bool i915_gem_has_seqno_wrapped(struct drm_device *dev, + u32 seqno) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + return dev_priv->last_seqno < seqno; +} + /** * intel_ring_sync - sync the waiter to the signaller on seqno * @@ -626,11 +633,20 @@ gen6_ring_sync(struct intel_ring_buffer *waiter, if (ret) return ret; - intel_ring_emit(waiter, - dw1 | signaller->semaphore_register[waiter->id]); - intel_ring_emit(waiter, seqno); - intel_ring_emit(waiter, 0); - intel_ring_emit(waiter, MI_NOOP); + /* If seqno wrap happened, omit the wait with no-ops */ + if (likely(!i915_gem_has_seqno_wrapped(waiter->dev, seqno))) { + intel_ring_emit(waiter, + dw1 | + signaller->semaphore_register[waiter->id]); + intel_ring_emit(waiter, seqno); + intel_ring_emit(waiter, 0); + intel_ring_emit(waiter, MI_NOOP); + } else { + intel_ring_emit(waiter, MI_NOOP); + intel_ring_emit(waiter, MI_NOOP); + intel_ring_emit(waiter, MI_NOOP); + intel_ring_emit(waiter, MI_NOOP); + } intel_ring_advance(waiter); return 0; From 107f27a5df8ad33a351d6e82fe95ff92b428f72e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 10 Dec 2012 13:56:17 +0200 Subject: [PATCH 0142/4607] drm/i915: Open-code i915_gpu_idle() for handling seqno wrapping The complication is that during seqno wrapping we must be extremely careful not to write to any ring as that will require a new seqno, and so would recurse back into the seqno wrap handler. So we cannot call i915_gpu_idle() as that does additional work beyond simply retiring the current set of requests, and instead must do the minimal work ourselves during seqno wrapping. Signed-off-by: Chris Wilson Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a81b78a59bd9..bc73f98c3631 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1938,11 +1938,15 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) if (ret == 0) return ret; - ret = i915_gpu_idle(dev); - if (ret) - return ret; - + /* Carefully retire all requests without writing to the rings */ + for_each_ring(ring, dev_priv, i) { + ret = intel_ring_idle(ring); + if (ret) + return ret; + } i915_gem_retire_requests(dev); + + /* Finally reset hw state */ for_each_ring(ring, dev_priv, i) { ret = intel_ring_handle_seqno_wrap(ring); if (ret) From 9e8e36879f268f1652e11b1c8560bbb67cf4f08e Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 4 Dec 2012 15:12:05 +0200 Subject: [PATCH 0143/4607] drm/i915: Set initial seqno value close to wrap boundary To gain confidence in the wrap handling, make it happen quite soon after the boot. Signed-off-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index bc73f98c3631..6380c6083cb2 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3943,7 +3943,7 @@ i915_gem_init_hw(struct drm_device *dev) goto cleanup_bsd_ring; } - dev_priv->next_seqno = 1; + dev_priv->next_seqno = (u32)-1 - 0x1000; /* * XXX: There was some w/a described somewhere suggesting loading From 20afbda209d708be66944907966486d0c1331cb8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 14:05:07 +0100 Subject: [PATCH 0144/4607] drm/i915: Fixup hpd irq register setup ordering For GMCH platforms we set up the hpd irq registers in the irq postinstall hook. But since we only enable the irq sources we actually need in PORT_HOTPLUG_EN/STATUS, taking dev_priv->hotplug_supported_mask into account, no hpd interrupt sources is enabled since commit 52d7ecedac3f96fb562cb482c139015372728638 Author: Daniel Vetter Date: Sat Dec 1 21:03:22 2012 +0100 drm/i915: reorder setup sequence to have irqs for output setup Wrongly set-up interrupts also lead to broken hw-based load-detection on at least GM45, resulting in ghost VGA/TV-out outputs. To fix this, delay the hotplug register setup until after all outputs are set up, by moving it into a new dev_priv->display.hpd_irq_callback. We might also move the PCH_SPLIT platforms to such a setup eventually. Another funny part is that we need to delay the fbdev initial config probing until after the hpd regs are setup, for otherwise it'll detect ghost outputs. But we can only enable the hpd interrupt handling itself (and the output polling) _after_ that initial scan, due to massive locking brain-damage in the fbdev setup code. Add a big comment to explain this cute little dragon lair. v2: Encapsulate all the fbdev handling by wrapping the move call into intel_fbdev_initial_config in intel_fb.c. Requested by Chris Wilson. v3: Applied bikeshed from Jesse Barnes. v4: Imre Deak noticed that we also need to call intel_hpd_init after the drm_irqinstall calls in the gpu reset and resume paths - otherwise hotplug will be broken. Also improve the comment a bit about why hpd_init needs to be called before we set up the initial fbdev config. Bugzilla: Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=54943 Reported-by: Chris Wilson Reviewed-by: Jesse Barnes (v3) Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 15 ++++++++ drivers/gpu/drm/i915/i915_drv.c | 2 + drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/i915_irq.c | 63 +++++++++++++++++++++++++------- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_fb.c | 10 ++++- 6 files changed, 79 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 93630669c6dc..0c2ab40ab2ed 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1318,6 +1318,21 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_gem; + /* Only enable hotplug handling once the fbdev is fully set up. */ + intel_hpd_init(dev); + + /* + * Some ports require correctly set-up hpd registers for detection to + * work properly (leading to ghost connected connector status), e.g. VGA + * on gm45. Hence we can only set up the initial fbdev config after hpd + * irqs are fully enabled. Now we should scan for the initial config + * only once hotplug handling is enabled, but due to screwed-up locking + * around kms/fbdev init we can't protect the fdbev initial config + * scanning against hotplug events. Hence do this first and ignore the + * tiny window where we will loose hotplug notifactions. + */ + intel_fbdev_initial_config(dev); + /* Only enable hotplug handling once the fbdev is fully set up. */ dev_priv->enable_hotplug_processing = true; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index a12921892446..fbd0b28b7200 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -566,6 +566,7 @@ static int __i915_drm_thaw(struct drm_device *dev) intel_modeset_init_hw(dev); intel_modeset_setup_hw_state(dev, false); drm_irq_install(dev); + intel_hpd_init(dev); } intel_opregion_init(dev); @@ -871,6 +872,7 @@ int i915_reset(struct drm_device *dev) drm_irq_uninstall(dev); drm_irq_install(dev); + intel_hpd_init(dev); } else { mutex_unlock(&dev->struct_mutex); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9da67824dde3..e55303e2f74e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -297,6 +297,7 @@ struct drm_i915_display_funcs { struct drm_i915_gem_object *obj); int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y); + void (*hpd_irq_setup)(struct drm_device *dev); /* clock updates for mode set */ /* cursor updates */ /* render clock increase/decrease */ @@ -1343,6 +1344,7 @@ void i915_hangcheck_elapsed(unsigned long data); void i915_handle_error(struct drm_device *dev, bool wedged); extern void intel_irq_init(struct drm_device *dev); +extern void intel_hpd_init(struct drm_device *dev); extern void intel_gt_init(struct drm_device *dev); extern void intel_gt_reset(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 914ecf4acfa6..551f370657b7 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1995,7 +1995,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 enable_mask; - u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV; u32 render_irqs; u16 msid; @@ -2024,6 +2023,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev) msid |= (1<<14); pci_write_config_word(dev_priv->dev->pdev, 0x98, msid); + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + I915_WRITE(VLV_IMR, dev_priv->irq_mask); I915_WRITE(VLV_IER, enable_mask); I915_WRITE(VLV_IIR, 0xffffffff); @@ -2053,6 +2055,15 @@ static int valleyview_irq_postinstall(struct drm_device *dev) #endif I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE); + + return 0; +} + +static void valleyview_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); + /* Note HDMI and DP share bits */ if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) hotplug_en |= HDMIB_HOTPLUG_INT_EN; @@ -2070,8 +2081,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) } I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); - - return 0; } static void valleyview_irq_uninstall(struct drm_device *dev) @@ -2301,6 +2310,9 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_USER_INTERRUPT; if (I915_HAS_HOTPLUG(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + /* Enable in IER... */ enable_mask |= I915_DISPLAY_PORT_INTERRUPT; /* and unmask in IMR */ @@ -2311,8 +2323,18 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_WRITE(IER, enable_mask); POSTING_READ(IER); + intel_opregion_enable_asle(dev); + + return 0; +} + +static void i915_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en; + if (I915_HAS_HOTPLUG(dev)) { - u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); + hotplug_en = I915_READ(PORT_HOTPLUG_EN); if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) hotplug_en |= HDMIB_HOTPLUG_INT_EN; @@ -2333,10 +2355,6 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); } - - intel_opregion_enable_asle(dev); - - return 0; } static irqreturn_t i915_irq_handler(int irq, void *arg) @@ -2496,7 +2514,6 @@ static void i965_irq_preinstall(struct drm_device * dev) static int i965_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 hotplug_en; u32 enable_mask; u32 error_mask; @@ -2538,6 +2555,19 @@ static int i965_irq_postinstall(struct drm_device *dev) I915_WRITE(IER, enable_mask); POSTING_READ(IER); + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + + intel_opregion_enable_asle(dev); + + return 0; +} + +static void i965_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en; + /* Note HDMI and DP share hotplug bits */ hotplug_en = 0; if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) @@ -2572,10 +2602,6 @@ static int i965_irq_postinstall(struct drm_device *dev) /* Ignore TV since it's buggy */ I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); - - intel_opregion_enable_asle(dev); - - return 0; } static irqreturn_t i965_irq_handler(int irq, void *arg) @@ -2754,6 +2780,7 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = valleyview_irq_uninstall; dev->driver->enable_vblank = valleyview_enable_vblank; dev->driver->disable_vblank = valleyview_disable_vblank; + dev_priv->display.hpd_irq_setup = valleyview_hpd_irq_setup; } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { /* Share pre & uninstall handlers with ILK/SNB */ dev->driver->irq_handler = ivybridge_irq_handler; @@ -2780,13 +2807,23 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_postinstall = i915_irq_postinstall; dev->driver->irq_uninstall = i915_irq_uninstall; dev->driver->irq_handler = i915_irq_handler; + dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup; } else { dev->driver->irq_preinstall = i965_irq_preinstall; dev->driver->irq_postinstall = i965_irq_postinstall; dev->driver->irq_uninstall = i965_irq_uninstall; dev->driver->irq_handler = i965_irq_handler; + dev_priv->display.hpd_irq_setup = i965_hpd_irq_setup; } dev->driver->enable_vblank = i915_enable_vblank; dev->driver->disable_vblank = i915_disable_vblank; } } + +void intel_hpd_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->display.hpd_irq_setup) + dev_priv->display.hpd_irq_setup(dev); +} diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 7ca7772eaccd..22728f2c1d83 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -587,6 +587,7 @@ extern int intel_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_i915_gem_object *obj); extern int intel_fbdev_init(struct drm_device *dev); +extern void intel_fbdev_initial_config(struct drm_device *dev); extern void intel_fbdev_fini(struct drm_device *dev); extern void intel_fbdev_set_suspend(struct drm_device *dev, int state); extern void intel_prepare_page_flip(struct drm_device *dev, int plane); diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index b7773e548911..822896782cd0 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -243,10 +243,18 @@ int intel_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&ifbdev->helper); - drm_fb_helper_initial_config(&ifbdev->helper, 32); + return 0; } +void intel_fbdev_initial_config(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + /* Due to peculiar init order wrt to hpd handling this is separate. */ + drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32); +} + void intel_fbdev_fini(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; From c815ca39a0aa9d438e7caf3c61b8beb22be10ed9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:05:54 -0300 Subject: [PATCH 0145/4607] [media] MAINTAINERS: add radio-cadet entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d09affd58c65..124c811bb8d1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1797,6 +1797,14 @@ S: Supported F: Documentation/filesystems/caching/cachefiles.txt F: fs/cachefiles/ +CADET FM/AM RADIO RECEIVER DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-cadet* + CAFE CMOS INTEGRATED CAMERA CONTROLLER DRIVER M: Jonathan Corbet L: linux-media@vger.kernel.org From d39b8420da85c8a8cc5a08c682ea3d7e95b49c26 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:07:02 -0300 Subject: [PATCH 0146/4607] [media] MAINTAINERS: add radio-isa entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 124c811bb8d1..0a4652d223b4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4193,6 +4193,14 @@ F: Documentation/isapnp.txt F: drivers/pnp/isapnp/ F: include/linux/isapnp.h +ISA RADIO MODULE +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-isa* + iSCSI BOOT FIRMWARE TABLE (iBFT) DRIVER M: Peter Jones M: Konrad Rzeszutek Wilk From 6777376e0d5b579e645c76effec4b8b4f0cbff82 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:09:23 -0300 Subject: [PATCH 0147/4607] [media] MAINTAINERS: add radio-aztech entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0a4652d223b4..5e47a5d3c96f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1515,6 +1515,14 @@ T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/usb/dvb-usb-v2/az6007.c +AZTECH FM RADIO RECEIVER DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-aztech* + B43 WIRELESS DRIVER M: Stefano Brivio L: linux-wireless@vger.kernel.org From 450500ad1d58d5b1dcfeb342fa737eaf35f9d31a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:11:33 -0300 Subject: [PATCH 0148/4607] [media] MAINTAINERS: add radio-aimslab entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5e47a5d3c96f..d691e0e196b1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -464,6 +464,14 @@ S: Maintained F: drivers/scsi/aic7xxx/ F: drivers/scsi/aic7xxx_old/ +AIMSLAB FM RADIO RECEIVER DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-aimslab* + AIO M: Benjamin LaHaise L: linux-aio@kvack.org From 3169a1c77f87a1f2b7c57bd1d105583681a6e407 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:11:58 -0300 Subject: [PATCH 0149/4607] [media] MAINTAINERS: add radio-gemtek entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d691e0e196b1..d23656b72ec4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3336,6 +3336,14 @@ W: http://www.icp-vortex.com/ S: Supported F: drivers/scsi/gdt* +GEMTEK FM RADIO RECEIVER DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-gemtek* + GENERIC GPIO I2C DRIVER M: Haavard Skinnemoen S: Supported From 9be3c9a5f0cc1c8fd4da5af402f278da770758ed Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:12:43 -0300 Subject: [PATCH 0150/4607] [media] MAINTAINERS: add radio-maxiradio entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d23656b72ec4..09f0be93105a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4950,6 +4950,14 @@ S: Maintained F: Documentation/hwmon/max6650 F: drivers/hwmon/max6650.c +MAXIRADIO FM RADIO RECEIVER DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/radio-maxiradio* + MEDIA INPUT INFRASTRUCTURE (V4L/DVB) M: Mauro Carvalho Chehab P: LinuxTV.org Project From 08b7620a66042b947f3812c53e3a85e77601264f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:15:42 -0300 Subject: [PATCH 0151/4607] [media] MAINTAINERS: add radio-miropcm20 entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 09f0be93105a..11d311ea623d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5041,6 +5041,14 @@ S: Supported F: Documentation/mips/ F: arch/mips/ +MIROSOUND PCM20 FM RADIO RECEIVER DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/radio/radio-miropcm20* + MODULE SUPPORT M: Rusty Russell S: Maintained From 6149a9367e34050dda46ceb295cb83385719c11c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:21:19 -0300 Subject: [PATCH 0152/4607] [media] MAINTAINERS: add pms entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 11d311ea623d..c63db476345d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4980,6 +4980,14 @@ F: include/uapi/linux/meye.h F: include/uapi/linux/ivtv* F: include/uapi/linux/uvcvideo.h +MEDIAVISION PRO MOVIE STUDIO DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/parport/pms* + MEGARAID SCSI DRIVERS M: Neela Syam Kolli L: linux-scsi@vger.kernel.org From 1f15a229bb962e024cb15ffe1618e3e028dfc937 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:45:29 -0300 Subject: [PATCH 0153/4607] [media] MAINTAINERS: add saa6588 entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index c63db476345d..085f1828e087 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6509,6 +6509,14 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: drivers/mmc/host/s3cmci.* +SAA6588 RDS RECEIVER DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/i2c/saa6588* + SAA7134 VIDEO4LINUX DRIVER M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org From b60b9c45a2c1f5874f31d55e7f50e7ab3329d1b0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 07:54:42 -0300 Subject: [PATCH 0154/4607] [media] MAINTAINERS: add usbvision entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 085f1828e087..b6dd66438420 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8125,6 +8125,14 @@ S: Maintained F: drivers/media/usb/uvc/ F: include/uapi/linux/uvcvideo.h +USB VISION DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/usb/usbvision/ + USB WEBCAM GADGET M: Laurent Pinchart L: linux-usb@vger.kernel.org From 0b7bc1faa0663c22c3e9abde1bc1cce0c0541eb4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 08:21:50 -0300 Subject: [PATCH 0155/4607] [media] MAINTAINERS: add vivi entry Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index b6dd66438420..9d07ad6d1cf2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8280,6 +8280,14 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/via/via-velocity.* +VIVI VIRTUAL VIDEO DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/platform/vivi* + VLAN (802.1Q) M: Patrick McHardy L: netdev@vger.kernel.org From 566b815789832655072f18b1166c429d0506e982 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 09:58:12 -0300 Subject: [PATCH 0156/4607] [media] MAINTAINERS: Taking over saa7146 maintainership from Michael Hunold Signed-off-by: Hans Verkuil Acked-by: Michael Hunold Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 9d07ad6d1cf2..38242e5460d9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6527,10 +6527,9 @@ F: Documentation/video4linux/saa7134/ F: drivers/media/pci/saa7134/ SAA7146 VIDEO4LINUX-2 DRIVER -M: Michael Hunold +M: Hans Verkuil L: linux-media@vger.kernel.org T: git git://linuxtv.org/media_tree.git -W: http://www.mihu.de/linux/saa7146 S: Maintained F: drivers/media/common/saa7146/ F: drivers/media/pci/saa7146/ From 4b9fba300714cade4ab92ec29a37afd93faa91f5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Nov 2012 10:02:05 -0300 Subject: [PATCH 0157/4607] [media] MAINTAINERS: add tda9840, tea6415c and tea6420 entries Signed-off-by: Hans Verkuil Acked-by: Michael Hunold Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 38242e5460d9..ca9f9091d43f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7499,6 +7499,14 @@ T: git git://linuxtv.org/mkrufky/tuners.git S: Maintained F: drivers/media/tuners/tda8290.* +TDA9840 MEDIA DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/i2c/tda9840* + TEA5761 TUNER DRIVER M: Mauro Carvalho Chehab L: linux-media@vger.kernel.org @@ -7515,6 +7523,22 @@ T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/tuners/tea5767.* +TEA6415C MEDIA DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/i2c/tea6415c* + +TEA6420 MEDIA DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/i2c/tea6420* + TEAM DRIVER M: Jiri Pirko L: netdev@vger.kernel.org From 49cc629df16f2a15917800a8579bd9c25c41b634 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 30 Nov 2012 07:13:29 -0300 Subject: [PATCH 0158/4607] [media] MAINTAINERS: add si470x-usb+common and si470x-i2c entries Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ca9f9091d43f..e6378c4cde8a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6827,6 +6827,24 @@ M: Robin Holt S: Maintained F: drivers/misc/sgi-xp/ +SI470X FM RADIO RECEIVER I2C DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Odd Fixes +F: drivers/media/radio/si470x/radio-si470x-i2c.c + +SI470X FM RADIO RECEIVER USB DRIVER +M: Hans Verkuil +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +W: http://linuxtv.org +S: Maintained +F: drivers/media/radio/si470x/radio-si470x-common.c +F: drivers/media/radio/si470x/radio-si470x.h +F: drivers/media/radio/si470x/radio-si470x-usb.c + SIMPLE FIRMWARE INTERFACE (SFI) M: Len Brown L: sfi-devel@simplefirmware.org From 09153000b8ca32a539a1207edebabd0d40b6c61b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 12 Dec 2012 14:06:44 +0100 Subject: [PATCH 0159/4607] drm/i915: rework locking for intel_dpio|sbi_read|write Spinning for up to 200 us with interrupts locked out is not good. So let's just spin (and even that seems to be excessive). And we don't call these functions from interrupt context, so this is not required. Besides that doing anything in interrupt contexts which might take a few hundred us is a no-go. So just convert the entire thing to a mutex. Also move the mutex-grabbing out of the read/write functions (add a WARN_ON(!is_locked)) instead) since all callers are nicely grouped together. Finally the real motivation for this change: Dont grab the modeset mutex in the dpio debugfs file, we don't need that consistency. And correctness of the dpio interface is ensured with the dpio_lock. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 4 +-- drivers/gpu/drm/i915/i915_dma.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/intel_display.c | 53 +++++++++++----------------- 4 files changed, 25 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 7e516eebdc80..7047c4a9fb9e 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1633,7 +1633,7 @@ static int i915_dpio_info(struct seq_file *m, void *data) return 0; } - ret = mutex_lock_interruptible(&dev->mode_config.mutex); + ret = mutex_lock_interruptible(&dev_priv->dpio_lock); if (ret) return ret; @@ -1662,7 +1662,7 @@ static int i915_dpio_info(struct seq_file *m, void *data) seq_printf(m, "DPIO_FASTCLK_DISABLE: 0x%08x\n", intel_dpio_read(dev_priv, DPIO_FASTCLK_DISABLE)); - mutex_unlock(&dev->mode_config.mutex); + mutex_unlock(&dev_priv->dpio_lock); return 0; } diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0c2ab40ab2ed..a99c46d1e49a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1599,7 +1599,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) spin_lock_init(&dev_priv->irq_lock); spin_lock_init(&dev_priv->error_lock); spin_lock_init(&dev_priv->rps.lock); - spin_lock_init(&dev_priv->dpio_lock); + mutex_init(&dev_priv->dpio_lock); mutex_init(&dev_priv->rps.hw_lock); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e55303e2f74e..514aee895ecb 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -686,7 +686,7 @@ typedef struct drm_i915_private { struct pm_qos_request pm_qos; /* DPIO indirect register protection */ - spinlock_t dpio_lock; + struct mutex dpio_lock; /** Cached value of IMR to avoid reads in updating the bitfield */ u32 pipestat[2]; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 115bf626037c..2a01b09221fb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -416,13 +416,11 @@ static const intel_limit_t intel_limits_vlv_dp = { u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) { - unsigned long flags; - u32 val = 0; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - spin_lock_irqsave(&dev_priv->dpio_lock, flags); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { DRM_ERROR("DPIO idle wait timed out\n"); - goto out_unlock; + return 0; } I915_WRITE(DPIO_REG, reg); @@ -430,24 +428,20 @@ u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) DPIO_BYTE); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { DRM_ERROR("DPIO read wait timed out\n"); - goto out_unlock; + return 0; } - val = I915_READ(DPIO_DATA); -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); - return val; + return I915_READ(DPIO_DATA); } static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, u32 val) { - unsigned long flags; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - spin_lock_irqsave(&dev_priv->dpio_lock, flags); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { DRM_ERROR("DPIO idle wait timed out\n"); - goto out_unlock; + return; } I915_WRITE(DPIO_DATA, val); @@ -456,9 +450,6 @@ static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, DPIO_BYTE); if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) DRM_ERROR("DPIO write wait timed out\n"); - -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); } static void vlv_init_dpio(struct drm_device *dev) @@ -1455,13 +1446,12 @@ static void intel_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) static void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value) { - unsigned long flags; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - spin_lock_irqsave(&dev_priv->dpio_lock, flags); if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, 100)) { DRM_ERROR("timeout waiting for SBI to become ready\n"); - goto out_unlock; + return; } I915_WRITE(SBI_ADDR, @@ -1475,24 +1465,19 @@ intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value) if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, 100)) { DRM_ERROR("timeout waiting for SBI to complete write transaction\n"); - goto out_unlock; + return; } - -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); } static u32 intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg) { - unsigned long flags; - u32 value = 0; + WARN_ON(!mutex_is_locked(&dev_priv->dpio_lock)); - spin_lock_irqsave(&dev_priv->dpio_lock, flags); if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_BUSY) == 0, 100)) { DRM_ERROR("timeout waiting for SBI to become ready\n"); - goto out_unlock; + return 0; } I915_WRITE(SBI_ADDR, @@ -1504,14 +1489,10 @@ intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg) if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_BUSY | SBI_RESPONSE_FAIL)) == 0, 100)) { DRM_ERROR("timeout waiting for SBI to complete read transaction\n"); - goto out_unlock; + return 0; } - value = I915_READ(SBI_DATA); - -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); - return value; + return I915_READ(SBI_DATA); } /** @@ -2924,6 +2905,8 @@ static void lpt_program_iclkip(struct drm_crtc *crtc) u32 divsel, phaseinc, auxdiv, phasedir = 0; u32 temp; + mutex_lock(&dev_priv->dpio_lock); + /* It is necessary to ungate the pixclk gate prior to programming * the divisors, and gate it back when it is done. */ @@ -3005,6 +2988,8 @@ static void lpt_program_iclkip(struct drm_crtc *crtc) udelay(24); I915_WRITE(PIXCLK_GATE, PIXCLK_GATE_UNGATE); + + mutex_unlock(&dev_priv->dpio_lock); } /* @@ -4222,6 +4207,8 @@ static void vlv_update_pll(struct drm_crtc *crtc, bool is_sdvo; u32 temp; + mutex_lock(&dev_priv->dpio_lock); + is_sdvo = intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) || intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI); @@ -4305,6 +4292,8 @@ static void vlv_update_pll(struct drm_crtc *crtc, temp |= (1 << 21); intel_dpio_write(dev_priv, DPIO_DATA_CHANNEL2, temp); } + + mutex_unlock(&dev_priv->dpio_lock); } static void i9xx_update_pll(struct drm_crtc *crtc, From 7f662273e476e2d7ff44f411fa9f17c946480100 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 10 Dec 2012 11:42:30 +0200 Subject: [PATCH 0160/4607] KVM: emulator: implement AAD instruction Windows2000 uses it during boot. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=50921 Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index a27e76371108..47d62e16f9e9 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -2852,6 +2852,27 @@ static int em_das(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } +static int em_aad(struct x86_emulate_ctxt *ctxt) +{ + u8 al = ctxt->dst.val & 0xff; + u8 ah = (ctxt->dst.val >> 8) & 0xff; + + al = (al + (ah * ctxt->src.val)) & 0xff; + + ctxt->dst.val = (ctxt->dst.val & 0xffff0000) | al; + + ctxt->eflags &= ~(X86_EFLAGS_PF | X86_EFLAGS_SF | X86_EFLAGS_ZF); + + if (!al) + ctxt->eflags |= X86_EFLAGS_ZF; + if (!(al & 1)) + ctxt->eflags |= X86_EFLAGS_PF; + if (al & 0x80) + ctxt->eflags |= X86_EFLAGS_SF; + + return X86EMUL_CONTINUE; +} + static int em_call(struct x86_emulate_ctxt *ctxt) { long rel = ctxt->src.val; @@ -3801,7 +3822,7 @@ static const struct opcode opcode_table[256] = { D(ImplicitOps | No64), II(ImplicitOps, em_iret, iret), /* 0xD0 - 0xD7 */ D2bv(DstMem | SrcOne | ModRM), D2bv(DstMem | ModRM), - N, N, N, N, + N, I(DstAcc | SrcImmByte | No64, em_aad), N, N, /* 0xD8 - 0xDF */ N, N, N, N, N, N, N, N, /* 0xE0 - 0xE7 */ From 5e2c688351f4aee9981918661b6c1679f4155f06 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 6 Dec 2012 21:55:10 -0200 Subject: [PATCH 0161/4607] KVM: x86: fix mov immediate emulation for 64-bit operands MOV immediate instruction (opcodes 0xB8-0xBF) may take 64-bit operand. The previous emulation implementation assumes the operand is no longer than 32. Adding OpImm64 for this matter. Fixes https://bugzilla.redhat.com/show_bug.cgi?id=881579 Signed-off-by: Nadav Amit Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 47d62e16f9e9..c7547b3fd527 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -43,7 +43,7 @@ #define OpCL 9ull /* CL register (for shifts) */ #define OpImmByte 10ull /* 8-bit sign extended immediate */ #define OpOne 11ull /* Implied 1 */ -#define OpImm 12ull /* Sign extended immediate */ +#define OpImm 12ull /* Sign extended up to 32-bit immediate */ #define OpMem16 13ull /* Memory operand (16-bit). */ #define OpMem32 14ull /* Memory operand (32-bit). */ #define OpImmU 15ull /* Immediate operand, zero extended */ @@ -58,6 +58,7 @@ #define OpFS 24ull /* FS */ #define OpGS 25ull /* GS */ #define OpMem8 26ull /* 8-bit zero extended memory operand */ +#define OpImm64 27ull /* Sign extended 16/32/64-bit immediate */ #define OpBits 5 /* Width of operand field */ #define OpMask ((1ull << OpBits) - 1) @@ -101,6 +102,7 @@ #define SrcMemFAddr (OpMemFAddr << SrcShift) #define SrcAcc (OpAcc << SrcShift) #define SrcImmU16 (OpImmU16 << SrcShift) +#define SrcImm64 (OpImm64 << SrcShift) #define SrcDX (OpDX << SrcShift) #define SrcMem8 (OpMem8 << SrcShift) #define SrcMask (OpMask << SrcShift) @@ -3807,7 +3809,7 @@ static const struct opcode opcode_table[256] = { /* 0xB0 - 0xB7 */ X8(I(ByteOp | DstReg | SrcImm | Mov, em_mov)), /* 0xB8 - 0xBF */ - X8(I(DstReg | SrcImm | Mov, em_mov)), + X8(I(DstReg | SrcImm64 | Mov, em_mov)), /* 0xC0 - 0xC7 */ D2bv(DstMem | SrcImmByte | ModRM), I(ImplicitOps | Stack | SrcImmU16, em_ret_near_imm), @@ -3971,6 +3973,9 @@ static int decode_imm(struct x86_emulate_ctxt *ctxt, struct operand *op, case 4: op->val = insn_fetch(s32, ctxt); break; + case 8: + op->val = insn_fetch(s64, ctxt); + break; } if (!sign_extension) { switch (op->bytes) { @@ -4049,6 +4054,9 @@ static int decode_operand(struct x86_emulate_ctxt *ctxt, struct operand *op, case OpImm: rc = decode_imm(ctxt, op, imm_size(ctxt), true); break; + case OpImm64: + rc = decode_imm(ctxt, op, ctxt->op_bytes, true); + break; case OpMem8: ctxt->memop.bytes = 1; goto mem_common; From f3200d00ea42e485772ff92d6d649aa8eeb640c0 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 10 Dec 2012 14:05:55 +0200 Subject: [PATCH 0162/4607] KVM: inject ExtINT interrupt before APIC interrupts According to Intel SDM Volume 3 Section 10.8.1 "Interrupt Handling with the Pentium 4 and Intel Xeon Processors" and Section 10.8.2 "Interrupt Handling with the P6 Family and Pentium Processors" ExtINT interrupts are sent directly to the processor core for handling. Currently KVM checks APIC before it considers ExtINT interrupts for injection which is backwards from the spec. Make code behave according to the SDM. Signed-off-by: Gleb Natapov Acked-by: "Zhang, Yang Z" Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/i8259.c | 2 ++ arch/x86/kvm/irq.c | 26 ++++++++------------------ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c index 848206df0967..cc31f7c06d3d 100644 --- a/arch/x86/kvm/i8259.c +++ b/arch/x86/kvm/i8259.c @@ -241,6 +241,8 @@ int kvm_pic_read_irq(struct kvm *kvm) int irq, irq2, intno; struct kvm_pic *s = pic_irqchip(kvm); + s->output = 0; + pic_lock(s); irq = pic_get_irq(&s->pics[0]); if (irq >= 0) { diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c index 7e06ba1618bd..ebd98d0c4f6e 100644 --- a/arch/x86/kvm/irq.c +++ b/arch/x86/kvm/irq.c @@ -48,14 +48,10 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *v) if (!irqchip_in_kernel(v->kvm)) return v->arch.interrupt.pending; - if (kvm_apic_has_interrupt(v) == -1) { /* LAPIC */ - if (kvm_apic_accept_pic_intr(v)) { - s = pic_irqchip(v->kvm); /* PIC */ - return s->output; - } else - return 0; - } - return 1; + if (kvm_apic_accept_pic_intr(v) && pic_irqchip(v->kvm)->output) + return pic_irqchip(v->kvm)->output; /* PIC */ + + return kvm_apic_has_interrupt(v) != -1; /* LAPIC */ } EXPORT_SYMBOL_GPL(kvm_cpu_has_interrupt); @@ -65,20 +61,14 @@ EXPORT_SYMBOL_GPL(kvm_cpu_has_interrupt); int kvm_cpu_get_interrupt(struct kvm_vcpu *v) { struct kvm_pic *s; - int vector; if (!irqchip_in_kernel(v->kvm)) return v->arch.interrupt.nr; - vector = kvm_get_apic_interrupt(v); /* APIC */ - if (vector == -1) { - if (kvm_apic_accept_pic_intr(v)) { - s = pic_irqchip(v->kvm); - s->output = 0; /* PIC */ - vector = kvm_pic_read_irq(v->kvm); - } - } - return vector; + if (kvm_apic_accept_pic_intr(v) && pic_irqchip(v->kvm)->output) + return kvm_pic_read_irq(v->kvm); /* PIC */ + + return kvm_get_apic_interrupt(v); /* APIC */ } EXPORT_SYMBOL_GPL(kvm_cpu_get_interrupt); From f0736cf0550b349a5d5a374d65ca0488cc2eee40 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 10 Dec 2012 10:32:45 -0700 Subject: [PATCH 0163/4607] KVM: Restrict non-existing slot state transitions The API documentation states: When changing an existing slot, it may be moved in the guest physical memory space, or its flags may be modified. An "existing slot" requires a non-zero npages (memory_size). The only transition we should therefore allow for a non-existing slot should be to create the slot, which includes setting a non-zero memory_size. We currently allow calls to modify non-existing slots, which is pointless, confusing, and possibly wrong. With this we know that the invalidation path of __kvm_set_memory_region is always for a delete or move and never for adding a zero size slot. Reviewed-by: Gleb Natapov Signed-off-by: Alex Williamson Signed-off-by: Marcelo Tosatti --- virt/kvm/kvm_main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 1cd693a76a51..3caf8162eb6b 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -758,10 +758,15 @@ int __kvm_set_memory_region(struct kvm *kvm, new.npages = npages; new.flags = mem->flags; - /* Disallow changing a memory slot's size. */ + /* + * Disallow changing a memory slot's size or changing anything about + * zero sized slots that doesn't involve making them non-zero. + */ r = -EINVAL; if (npages && old.npages && npages != old.npages) goto out_free; + if (!npages && !old.npages) + goto out_free; /* Check for overlaps */ r = -EEXIST; @@ -780,7 +785,7 @@ int __kvm_set_memory_region(struct kvm *kvm, r = -ENOMEM; /* Allocate if a slot is being created */ - if (npages && !old.npages) { + if (!old.npages) { new.user_alloc = user_alloc; new.userspace_addr = mem->userspace_addr; From 9c695d42dbd465bcaa865603b411a73c60e60978 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 10 Dec 2012 10:32:51 -0700 Subject: [PATCH 0164/4607] KVM: Check userspace_addr when modifying a memory slot The API documents that only flags and guest physical memory space can be modified on an existing slot, but we don't enforce that the userspace address cannot be modified. Instead we just ignore it. This means that a user may think they've successfully moved both the guest and user addresses, when in fact only the guest address changed. Check and error instead. Reviewed-by: Gleb Natapov Signed-off-by: Alex Williamson Signed-off-by: Marcelo Tosatti --- virt/kvm/kvm_main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 3caf8162eb6b..e4d358195e54 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -784,13 +784,19 @@ int __kvm_set_memory_region(struct kvm *kvm, r = -ENOMEM; - /* Allocate if a slot is being created */ + /* + * Allocate if a slot is being created. If modifying a slot, + * the userspace_addr cannot change. + */ if (!old.npages) { new.user_alloc = user_alloc; new.userspace_addr = mem->userspace_addr; if (kvm_arch_create_memslot(&new, npages)) goto out_free; + } else if (npages && mem->userspace_addr != old.userspace_addr) { + r = -EINVAL; + goto out_free; } /* Allocate page dirty bitmap if needed */ From e40f193f5bb022e927a57a4f5d5194e4f12ddb74 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 10 Dec 2012 10:32:57 -0700 Subject: [PATCH 0165/4607] KVM: Fix iommu map/unmap to handle memory slot moves The iommu integration into memory slots expects memory slots to be added or removed and doesn't handle the move case. We can unmap slots from the iommu after we mark them invalid and map them before installing the final memslot array. Also re-order the kmemdup vs map so we don't leave iommu mappings if we get ENOMEM. Reviewed-by: Gleb Natapov Signed-off-by: Alex Williamson Signed-off-by: Marcelo Tosatti --- virt/kvm/kvm_main.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index e4d358195e54..9a56ca2fa257 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -822,6 +822,8 @@ int __kvm_set_memory_region(struct kvm *kvm, old_memslots = kvm->memslots; rcu_assign_pointer(kvm->memslots, slots); synchronize_srcu_expedited(&kvm->srcu); + /* slot was deleted or moved, clear iommu mapping */ + kvm_iommu_unmap_pages(kvm, &old); /* From this point no new shadow pages pointing to a deleted, * or moved, memslot will be created. * @@ -837,20 +839,19 @@ int __kvm_set_memory_region(struct kvm *kvm, if (r) goto out_free; - /* map/unmap the pages in iommu page table */ - if (npages) { - r = kvm_iommu_map_pages(kvm, &new); - if (r) - goto out_free; - } else - kvm_iommu_unmap_pages(kvm, &old); - r = -ENOMEM; slots = kmemdup(kvm->memslots, sizeof(struct kvm_memslots), GFP_KERNEL); if (!slots) goto out_free; + /* map new memory slot into the iommu */ + if (npages) { + r = kvm_iommu_map_pages(kvm, &new); + if (r) + goto out_slots; + } + /* actual memory is freed via old in kvm_free_physmem_slot below */ if (!npages) { new.dirty_bitmap = NULL; @@ -869,6 +870,8 @@ int __kvm_set_memory_region(struct kvm *kvm, return 0; +out_slots: + kfree(slots); out_free: kvm_free_physmem_slot(&new, &old); out: From b7f69c555ca430129b6cde81e9f0927531420c5c Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 10 Dec 2012 10:33:03 -0700 Subject: [PATCH 0166/4607] KVM: Minor memory slot optimization If a slot is removed or moved in the guest physical address space, we first allocate and install a new slot array with the invalidated entry. The old array is then freed. We then proceed to allocate yet another slot array to install the permanent replacement. Re-use the original array when this occurs and avoid the extra kfree/kmalloc. Reviewed-by: Gleb Natapov Signed-off-by: Alex Williamson Signed-off-by: Marcelo Tosatti --- virt/kvm/kvm_main.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 9a56ca2fa257..4c8c0657e370 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -716,7 +716,7 @@ int __kvm_set_memory_region(struct kvm *kvm, unsigned long npages; struct kvm_memory_slot *memslot, *slot; struct kvm_memory_slot old, new; - struct kvm_memslots *slots, *old_memslots; + struct kvm_memslots *slots = NULL, *old_memslots; r = check_memory_region_flags(mem); if (r) @@ -832,18 +832,25 @@ int __kvm_set_memory_region(struct kvm *kvm, * - kvm_is_visible_gfn (mmu_check_roots) */ kvm_arch_flush_shadow_memslot(kvm, slot); - kfree(old_memslots); + slots = old_memslots; } r = kvm_arch_prepare_memory_region(kvm, &new, old, mem, user_alloc); if (r) - goto out_free; + goto out_slots; r = -ENOMEM; - slots = kmemdup(kvm->memslots, sizeof(struct kvm_memslots), - GFP_KERNEL); - if (!slots) - goto out_free; + /* + * We can re-use the old_memslots from above, the only difference + * from the currently installed memslots is the invalid flag. This + * will get overwritten by update_memslots anyway. + */ + if (!slots) { + slots = kmemdup(kvm->memslots, sizeof(struct kvm_memslots), + GFP_KERNEL); + if (!slots) + goto out_free; + } /* map new memory slot into the iommu */ if (npages) { From bbacc0c111c3c5d1f3192b8cc1642b9c3954f80d Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 10 Dec 2012 10:33:09 -0700 Subject: [PATCH 0167/4607] KVM: Rename KVM_MEMORY_SLOTS -> KVM_USER_MEM_SLOTS It's easy to confuse KVM_MEMORY_SLOTS and KVM_MEM_SLOTS_NUM. One is the user accessible slots and the other is user + private. Make this more obvious. Reviewed-by: Gleb Natapov Signed-off-by: Alex Williamson Signed-off-by: Marcelo Tosatti --- arch/ia64/include/asm/kvm_host.h | 2 +- arch/ia64/kvm/kvm-ia64.c | 2 +- arch/powerpc/include/asm/kvm_host.h | 4 ++-- arch/powerpc/kvm/book3s_hv.c | 2 +- arch/s390/include/asm/kvm_host.h | 2 +- arch/x86/include/asm/kvm_host.h | 4 ++-- arch/x86/include/asm/vmx.h | 6 +++--- arch/x86/kvm/x86.c | 6 +++--- include/linux/kvm_host.h | 2 +- virt/kvm/kvm_main.c | 8 ++++---- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/arch/ia64/include/asm/kvm_host.h b/arch/ia64/include/asm/kvm_host.h index 6d6a5ac48d85..48d7b0e1dba9 100644 --- a/arch/ia64/include/asm/kvm_host.h +++ b/arch/ia64/include/asm/kvm_host.h @@ -23,7 +23,7 @@ #ifndef __ASM_KVM_HOST_H #define __ASM_KVM_HOST_H -#define KVM_MEMORY_SLOTS 32 +#define KVM_USER_MEM_SLOTS 32 /* memory slots that does not exposed to userspace */ #define KVM_PRIVATE_MEM_SLOTS 4 diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index bd1c51555038..9bacfe207b43 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1834,7 +1834,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, mutex_lock(&kvm->slots_lock); r = -EINVAL; - if (log->slot >= KVM_MEMORY_SLOTS) + if (log->slot >= KVM_USER_MEM_SLOTS) goto out; memslot = id_to_memslot(kvm->memslots, log->slot); diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index ca9bf459db6a..ac19ad60ae8c 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -37,10 +37,10 @@ #define KVM_MAX_VCPUS NR_CPUS #define KVM_MAX_VCORES NR_CPUS -#define KVM_MEMORY_SLOTS 32 +#define KVM_USER_MEM_SLOTS 32 /* memory slots that does not exposed to userspace */ #define KVM_PRIVATE_MEM_SLOTS 4 -#define KVM_MEM_SLOTS_NUM (KVM_MEMORY_SLOTS + KVM_PRIVATE_MEM_SLOTS) +#define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS) #ifdef CONFIG_KVM_MMIO #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 71d0c90b62bf..80dcc53a1aba 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -1549,7 +1549,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) mutex_lock(&kvm->slots_lock); r = -EINVAL; - if (log->slot >= KVM_MEMORY_SLOTS) + if (log->slot >= KVM_USER_MEM_SLOTS) goto out; memslot = id_to_memslot(kvm->memslots, log->slot); diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index b7841546991f..ac3343264040 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -20,7 +20,7 @@ #include #define KVM_MAX_VCPUS 64 -#define KVM_MEMORY_SLOTS 32 +#define KVM_USER_MEM_SLOTS 32 /* memory slots that does not exposed to userspace */ #define KVM_PRIVATE_MEM_SLOTS 4 diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index dc87b65e9c3a..c7df6ffd2437 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -33,10 +33,10 @@ #define KVM_MAX_VCPUS 254 #define KVM_SOFT_MAX_VCPUS 160 -#define KVM_MEMORY_SLOTS 32 +#define KVM_USER_MEM_SLOTS 32 /* memory slots that does not exposed to userspace */ #define KVM_PRIVATE_MEM_SLOTS 4 -#define KVM_MEM_SLOTS_NUM (KVM_MEMORY_SLOTS + KVM_PRIVATE_MEM_SLOTS) +#define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS) #define KVM_MMIO_SIZE 16 diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index c2d56b34830d..e385df97bfdc 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -427,9 +427,9 @@ enum vmcs_field { #define AR_RESERVD_MASK 0xfffe0f00 -#define TSS_PRIVATE_MEMSLOT (KVM_MEMORY_SLOTS + 0) -#define APIC_ACCESS_PAGE_PRIVATE_MEMSLOT (KVM_MEMORY_SLOTS + 1) -#define IDENTITY_PAGETABLE_PRIVATE_MEMSLOT (KVM_MEMORY_SLOTS + 2) +#define TSS_PRIVATE_MEMSLOT (KVM_USER_MEM_SLOTS + 0) +#define APIC_ACCESS_PAGE_PRIVATE_MEMSLOT (KVM_USER_MEM_SLOTS + 1) +#define IDENTITY_PAGETABLE_PRIVATE_MEMSLOT (KVM_USER_MEM_SLOTS + 2) #define VMX_NR_VPIDS (1 << 16) #define VMX_VPID_EXTENT_SINGLE_CONTEXT 1 diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 76f54461f7cb..816074757c96 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2518,7 +2518,7 @@ int kvm_dev_ioctl_check_extension(long ext) r = KVM_MAX_VCPUS; break; case KVM_CAP_NR_MEMSLOTS: - r = KVM_MEMORY_SLOTS; + r = KVM_USER_MEM_SLOTS; break; case KVM_CAP_PV_MMU: /* obsolete */ r = 0; @@ -3435,7 +3435,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) mutex_lock(&kvm->slots_lock); r = -EINVAL; - if (log->slot >= KVM_MEMORY_SLOTS) + if (log->slot >= KVM_USER_MEM_SLOTS) goto out; memslot = id_to_memslot(kvm->memslots, log->slot); @@ -6845,7 +6845,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, int map_flags = MAP_PRIVATE | MAP_ANONYMOUS; /* Prevent internal slot pages from being moved by fork()/COW. */ - if (memslot->id >= KVM_MEMORY_SLOTS) + if (memslot->id >= KVM_USER_MEM_SLOTS) map_flags = MAP_SHARED | MAP_ANONYMOUS; /*To keep backward compatibility with older userspace, diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 2c497ab0d03d..abad7f30771e 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -314,7 +314,7 @@ struct kvm_irq_routing_table {}; #endif #ifndef KVM_MEM_SLOTS_NUM -#define KVM_MEM_SLOTS_NUM (KVM_MEMORY_SLOTS + KVM_PRIVATE_MEM_SLOTS) +#define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS) #endif /* diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 4c8c0657e370..5f0638cb6968 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -771,7 +771,7 @@ int __kvm_set_memory_region(struct kvm *kvm, /* Check for overlaps */ r = -EEXIST; kvm_for_each_memslot(slot, kvm->memslots) { - if (slot->id >= KVM_MEMORY_SLOTS || slot == memslot) + if (slot->id >= KVM_USER_MEM_SLOTS || slot == memslot) continue; if (!((base_gfn + npages <= slot->base_gfn) || (base_gfn >= slot->base_gfn + slot->npages))) @@ -905,7 +905,7 @@ int kvm_vm_ioctl_set_memory_region(struct kvm *kvm, kvm_userspace_memory_region *mem, int user_alloc) { - if (mem->slot >= KVM_MEMORY_SLOTS) + if (mem->slot >= KVM_USER_MEM_SLOTS) return -EINVAL; return kvm_set_memory_region(kvm, mem, user_alloc); } @@ -919,7 +919,7 @@ int kvm_get_dirty_log(struct kvm *kvm, unsigned long any = 0; r = -EINVAL; - if (log->slot >= KVM_MEMORY_SLOTS) + if (log->slot >= KVM_USER_MEM_SLOTS) goto out; memslot = id_to_memslot(kvm->memslots, log->slot); @@ -965,7 +965,7 @@ int kvm_is_visible_gfn(struct kvm *kvm, gfn_t gfn) { struct kvm_memory_slot *memslot = gfn_to_memslot(kvm, gfn); - if (!memslot || memslot->id >= KVM_MEMORY_SLOTS || + if (!memslot || memslot->id >= KVM_USER_MEM_SLOTS || memslot->flags & KVM_MEMSLOT_INVALID) return 0; From 0743247fbf0c4a27185b2aa1fdda91d0745dfed1 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 10 Dec 2012 10:33:15 -0700 Subject: [PATCH 0168/4607] KVM: Make KVM_PRIVATE_MEM_SLOTS optional Seems like everyone copied x86 and defined 4 private memory slots that never actually get used. Even x86 only uses 3 of the 4. These aren't exposed so there's no need to add padding. Reviewed-by: Gleb Natapov Signed-off-by: Alex Williamson Signed-off-by: Marcelo Tosatti --- arch/ia64/include/asm/kvm_host.h | 2 -- arch/powerpc/include/asm/kvm_host.h | 4 +--- arch/s390/include/asm/kvm_host.h | 2 -- arch/x86/include/asm/kvm_host.h | 4 ++-- include/linux/kvm_host.h | 4 ++++ 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/arch/ia64/include/asm/kvm_host.h b/arch/ia64/include/asm/kvm_host.h index 48d7b0e1dba9..cfa74983c675 100644 --- a/arch/ia64/include/asm/kvm_host.h +++ b/arch/ia64/include/asm/kvm_host.h @@ -24,8 +24,6 @@ #define __ASM_KVM_HOST_H #define KVM_USER_MEM_SLOTS 32 -/* memory slots that does not exposed to userspace */ -#define KVM_PRIVATE_MEM_SLOTS 4 #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index ac19ad60ae8c..ab49c6cf891c 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -38,9 +38,7 @@ #define KVM_MAX_VCPUS NR_CPUS #define KVM_MAX_VCORES NR_CPUS #define KVM_USER_MEM_SLOTS 32 -/* memory slots that does not exposed to userspace */ -#define KVM_PRIVATE_MEM_SLOTS 4 -#define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS) +#define KVM_MEM_SLOTS_NUM KVM_USER_MEM_SLOTS #ifdef CONFIG_KVM_MMIO #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index ac3343264040..711c5ab391cf 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -21,8 +21,6 @@ #define KVM_MAX_VCPUS 64 #define KVM_USER_MEM_SLOTS 32 -/* memory slots that does not exposed to userspace */ -#define KVM_PRIVATE_MEM_SLOTS 4 struct sca_entry { atomic_t scn; diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index c7df6ffd2437..51d52108f109 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -34,8 +34,8 @@ #define KVM_MAX_VCPUS 254 #define KVM_SOFT_MAX_VCPUS 160 #define KVM_USER_MEM_SLOTS 32 -/* memory slots that does not exposed to userspace */ -#define KVM_PRIVATE_MEM_SLOTS 4 +/* memory slots that are not exposed to userspace */ +#define KVM_PRIVATE_MEM_SLOTS 3 #define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS) #define KVM_MMIO_SIZE 16 diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index abad7f30771e..5a3581ceb036 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -313,6 +313,10 @@ struct kvm_irq_routing_table {}; #endif +#ifndef KVM_PRIVATE_MEM_SLOTS +#define KVM_PRIVATE_MEM_SLOTS 0 +#endif + #ifndef KVM_MEM_SLOTS_NUM #define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS) #endif From f82a8cfe9354f5cdea55ebeceba3fd19051d3ee8 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 10 Dec 2012 10:33:21 -0700 Subject: [PATCH 0169/4607] KVM: struct kvm_memory_slot.user_alloc -> bool There's no need for this to be an int, it holds a boolean. Move to the end of the struct for alignment. Reviewed-by: Gleb Natapov Signed-off-by: Alex Williamson Signed-off-by: Marcelo Tosatti --- arch/ia64/kvm/kvm-ia64.c | 6 +++--- arch/powerpc/kvm/powerpc.c | 4 ++-- arch/s390/kvm/kvm-s390.c | 4 ++-- arch/x86/kvm/vmx.c | 6 +++--- arch/x86/kvm/x86.c | 4 ++-- include/linux/kvm_host.h | 12 ++++++------ virt/kvm/kvm_main.c | 8 ++++---- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 9bacfe207b43..ad3126a58644 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -955,7 +955,7 @@ long kvm_arch_vm_ioctl(struct file *filp, kvm_mem.guest_phys_addr; kvm_userspace_mem.memory_size = kvm_mem.memory_size; r = kvm_vm_ioctl_set_memory_region(kvm, - &kvm_userspace_mem, 0); + &kvm_userspace_mem, false); if (r) goto out; break; @@ -1580,7 +1580,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_memory_slot old, struct kvm_userspace_memory_region *mem, - int user_alloc) + bool user_alloc) { unsigned long i; unsigned long pfn; @@ -1611,7 +1611,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, struct kvm_memory_slot old, - int user_alloc) + bool user_alloc) { return; } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 70739a089560..be83fca2e8fd 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -412,7 +412,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_memory_slot old, struct kvm_userspace_memory_region *mem, - int user_alloc) + bool user_alloc) { return kvmppc_core_prepare_memory_region(kvm, memslot, mem); } @@ -420,7 +420,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, struct kvm_memory_slot old, - int user_alloc) + bool user_alloc) { kvmppc_core_commit_memory_region(kvm, mem, old); } diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index c9011bfaabbe..f718bc65835c 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -928,7 +928,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_memory_slot old, struct kvm_userspace_memory_region *mem, - int user_alloc) + bool user_alloc) { /* A few sanity checks. We can have exactly one memory slot which has to start at guest virtual zero and which has to be located at a @@ -958,7 +958,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, struct kvm_memory_slot old, - int user_alloc) + bool user_alloc) { int rc; diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 9120ae1901e4..b3101e368079 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3667,7 +3667,7 @@ static int alloc_apic_access_page(struct kvm *kvm) kvm_userspace_mem.flags = 0; kvm_userspace_mem.guest_phys_addr = 0xfee00000ULL; kvm_userspace_mem.memory_size = PAGE_SIZE; - r = __kvm_set_memory_region(kvm, &kvm_userspace_mem, 0); + r = __kvm_set_memory_region(kvm, &kvm_userspace_mem, false); if (r) goto out; @@ -3697,7 +3697,7 @@ static int alloc_identity_pagetable(struct kvm *kvm) kvm_userspace_mem.guest_phys_addr = kvm->arch.ept_identity_map_addr; kvm_userspace_mem.memory_size = PAGE_SIZE; - r = __kvm_set_memory_region(kvm, &kvm_userspace_mem, 0); + r = __kvm_set_memory_region(kvm, &kvm_userspace_mem, false); if (r) goto out; @@ -4251,7 +4251,7 @@ static int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr) .flags = 0, }; - ret = kvm_set_memory_region(kvm, &tss_mem, 0); + ret = kvm_set_memory_region(kvm, &tss_mem, false); if (ret) return ret; kvm->arch.tss_addr = addr; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 816074757c96..1c9c834b72f0 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6839,7 +6839,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_memory_slot old, struct kvm_userspace_memory_region *mem, - int user_alloc) + bool user_alloc) { int npages = memslot->npages; int map_flags = MAP_PRIVATE | MAP_ANONYMOUS; @@ -6875,7 +6875,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, struct kvm_memory_slot old, - int user_alloc) + bool user_alloc) { int nr_mmu_pages = 0, npages = mem->memory_size >> PAGE_SHIFT; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 5a3581ceb036..d897f035749f 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -270,8 +270,8 @@ struct kvm_memory_slot { unsigned long *dirty_bitmap; struct kvm_arch_memory_slot arch; unsigned long userspace_addr; - int user_alloc; int id; + bool user_alloc; }; static inline unsigned long kvm_dirty_bitmap_bytes(struct kvm_memory_slot *memslot) @@ -451,10 +451,10 @@ id_to_memslot(struct kvm_memslots *slots, int id) int kvm_set_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - int user_alloc); + bool user_alloc); int __kvm_set_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - int user_alloc); + bool user_alloc); void kvm_arch_free_memslot(struct kvm_memory_slot *free, struct kvm_memory_slot *dont); int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages); @@ -462,11 +462,11 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_memory_slot old, struct kvm_userspace_memory_region *mem, - int user_alloc); + bool user_alloc); void kvm_arch_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, struct kvm_memory_slot old, - int user_alloc); + bool user_alloc); bool kvm_largepages_enabled(void); void kvm_disable_largepages(void); /* flush all memory translations */ @@ -553,7 +553,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, int kvm_vm_ioctl_set_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - int user_alloc); + bool user_alloc); int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level); long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 5f0638cb6968..42c1eb73a0e3 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -709,7 +709,7 @@ static int check_memory_region_flags(struct kvm_userspace_memory_region *mem) */ int __kvm_set_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - int user_alloc) + bool user_alloc) { int r; gfn_t base_gfn; @@ -889,7 +889,7 @@ EXPORT_SYMBOL_GPL(__kvm_set_memory_region); int kvm_set_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - int user_alloc) + bool user_alloc) { int r; @@ -903,7 +903,7 @@ EXPORT_SYMBOL_GPL(kvm_set_memory_region); int kvm_vm_ioctl_set_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, - int user_alloc) + bool user_alloc) { if (mem->slot >= KVM_USER_MEM_SLOTS) return -EINVAL; @@ -2148,7 +2148,7 @@ static long kvm_vm_ioctl(struct file *filp, sizeof kvm_userspace_mem)) goto out; - r = kvm_vm_ioctl_set_memory_region(kvm, &kvm_userspace_mem, 1); + r = kvm_vm_ioctl_set_memory_region(kvm, &kvm_userspace_mem, true); break; } case KVM_GET_DIRTY_LOG: { From 6104f472a5ea287fbdcf4644e74867dfd905a018 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 10 Dec 2012 10:33:26 -0700 Subject: [PATCH 0170/4607] KVM: struct kvm_memory_slot.flags -> u32 struct kvm_userspace_memory_region.flags is a u32 with a comment that bits 0 ~ 15 are visible to userspace and the other bits are reserved for kvm internal use. KVM_MEMSLOT_INVALID is the only internal use flag and it has a comment that bits 16 ~ 31 are internally used and the other bits are visible to userspace. Therefore, let's define this as a u32 so we don't waste bytes on LP64 systems. Move to the end of the struct for alignment. Reviewed-by: Gleb Natapov Signed-off-by: Alex Williamson Signed-off-by: Marcelo Tosatti --- include/linux/kvm_host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index d897f035749f..fec607537fa3 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -266,10 +266,10 @@ static inline int kvm_vcpu_exiting_guest_mode(struct kvm_vcpu *vcpu) struct kvm_memory_slot { gfn_t base_gfn; unsigned long npages; - unsigned long flags; unsigned long *dirty_bitmap; struct kvm_arch_memory_slot arch; unsigned long userspace_addr; + u32 flags; int id; bool user_alloc; }; From 1e702d9af5d633cf0eca76f6340b3c50fbb5a4e5 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 10 Dec 2012 10:33:32 -0700 Subject: [PATCH 0171/4607] KVM: struct kvm_memory_slot.id -> short We're currently offering a whopping 32 memory slots to user space, an int is a bit excessive for storing this. We would like to increase our memslots, but SHRT_MAX should be more than enough. Reviewed-by: Gleb Natapov Signed-off-by: Alex Williamson Signed-off-by: Marcelo Tosatti --- include/linux/kvm_host.h | 4 ++-- virt/kvm/kvm_main.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index fec607537fa3..32fdc45ca35e 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -270,7 +270,7 @@ struct kvm_memory_slot { struct kvm_arch_memory_slot arch; unsigned long userspace_addr; u32 flags; - int id; + short id; bool user_alloc; }; @@ -330,7 +330,7 @@ struct kvm_memslots { u64 generation; struct kvm_memory_slot memslots[KVM_MEM_SLOTS_NUM]; /* The mapping table from slot id to the index in memslots[]. */ - int id_to_index[KVM_MEM_SLOTS_NUM]; + short id_to_index[KVM_MEM_SLOTS_NUM]; }; struct kvm { diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 42c1eb73a0e3..bd31096e3698 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -474,6 +474,8 @@ static struct kvm *kvm_create_vm(unsigned long type) INIT_HLIST_HEAD(&kvm->irq_ack_notifier_list); #endif + BUILD_BUG_ON(KVM_MEM_SLOTS_NUM > SHRT_MAX); + r = -ENOMEM; kvm->memslots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL); if (!kvm->memslots) From 0f888f5acd0cd806d4fd9f4067276b3855a13309 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 10 Dec 2012 10:33:38 -0700 Subject: [PATCH 0172/4607] KVM: Increase user memory slots on x86 to 125 With the 3 private slots, this gives us a nice round 128 slots total. The primary motivation for this is to support more assigned devices. Each assigned device can theoretically use up to 8 slots (6 MMIO BARs, 1 ROM BAR, 1 spare for a split MSI-X table mapping) though it's far more typical for a device to use 3-4 slots. If we assume a typical VM uses a dozen slots for non-assigned devices purposes, we should always be able to support 14 worst case assigned devices or 28 to 37 typical devices. Reviewed-by: Gleb Natapov Signed-off-by: Alex Williamson Signed-off-by: Marcelo Tosatti --- arch/x86/include/asm/kvm_host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 51d52108f109..c431b33271f3 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -33,7 +33,7 @@ #define KVM_MAX_VCPUS 254 #define KVM_SOFT_MAX_VCPUS 160 -#define KVM_USER_MEM_SLOTS 32 +#define KVM_USER_MEM_SLOTS 125 /* memory slots that are not exposed to userspace */ #define KVM_PRIVATE_MEM_SLOTS 3 #define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS) From b696519e51910943f084ea0fc8bdceceae8383ef Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:08:59 +0000 Subject: [PATCH 0173/4607] drm/i915: Cleanup SHOTPLUG_CTL status bits definitions Those status bits don't follow the usual pattern: _MASK (those bits are write 1 to clear, useful to select the value we want to read) and the values shifted by the same amount. Cleaned that that up when poking at the register for testing purposes, might as well upstream that cleanup. Reviewed-by: Jani Nikula Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f2a5ea694684..f83480403ccc 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3558,27 +3558,30 @@ #define PORTD_PULSE_DURATION_6ms (2 << 18) #define PORTD_PULSE_DURATION_100ms (3 << 18) #define PORTD_PULSE_DURATION_MASK (3 << 18) -#define PORTD_HOTPLUG_NO_DETECT (0) -#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) -#define PORTD_HOTPLUG_LONG_DETECT (1 << 17) +#define PORTD_HOTPLUG_STATUS_MASK (0x3 << 16) +#define PORTD_HOTPLUG_NO_DETECT (0 << 16) +#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) +#define PORTD_HOTPLUG_LONG_DETECT (2 << 16) #define PORTC_HOTPLUG_ENABLE (1 << 12) #define PORTC_PULSE_DURATION_2ms (0) #define PORTC_PULSE_DURATION_4_5ms (1 << 10) #define PORTC_PULSE_DURATION_6ms (2 << 10) #define PORTC_PULSE_DURATION_100ms (3 << 10) #define PORTC_PULSE_DURATION_MASK (3 << 10) -#define PORTC_HOTPLUG_NO_DETECT (0) -#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) -#define PORTC_HOTPLUG_LONG_DETECT (1 << 9) +#define PORTC_HOTPLUG_STATUS_MASK (0x3 << 8) +#define PORTC_HOTPLUG_NO_DETECT (0 << 8) +#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) +#define PORTC_HOTPLUG_LONG_DETECT (2 << 8) #define PORTB_HOTPLUG_ENABLE (1 << 4) #define PORTB_PULSE_DURATION_2ms (0) #define PORTB_PULSE_DURATION_4_5ms (1 << 2) #define PORTB_PULSE_DURATION_6ms (2 << 2) #define PORTB_PULSE_DURATION_100ms (3 << 2) #define PORTB_PULSE_DURATION_MASK (3 << 2) -#define PORTB_HOTPLUG_NO_DETECT (0) -#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) -#define PORTB_HOTPLUG_LONG_DETECT (1 << 1) +#define PORTB_HOTPLUG_STATUS_MASK (0x3 << 0) +#define PORTB_HOTPLUG_NO_DETECT (0 << 0) +#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) +#define PORTB_HOTPLUG_LONG_DETECT (2 << 0) #define PCH_GPIOA 0xc5010 #define PCH_GPIOB 0xc5014 From b0ea7d37a8f63eeec5ae80b4a6403cfba01da02f Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:09:00 +0000 Subject: [PATCH 0174/4607] drm/i915/hdmi: Read the HPD status before trying to read the EDID If you unplug the hdmi connector slowly enough, the hotplug interrupt fires but then the kernel code tries to read the EDID and succeeds (because the connector is still half connected, the HPD pin is shorter than the others, and DDC works). Since EDID succeeds it thinks the monitor is still connected. To prevent that, read the live HPD status in the hotplug handler before trying to read the EDID. v2: Rename the function to ibx_ (Chris Wilson) Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=55372 Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 33 ++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 3 +++ drivers/gpu/drm/i915/intel_hdmi.c | 9 ++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2a01b09221fb..6adeabda2587 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -985,6 +985,39 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) } } +/* + * ibx_digital_port_connected - is the specified port connected? + * @dev_priv: i915 private structure + * @port: the port to test + * + * Returns true if @port is connected, false otherwise. + */ +bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port) +{ + u32 bit; + + /* XXX: IBX has different SDEISR bits */ + if (HAS_PCH_IBX(dev_priv->dev)) + return true; + + switch(port->port) { + case PORT_B: + bit = SDE_PORTB_HOTPLUG_CPT; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG_CPT; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG_CPT; + break; + default: + return true; + } + + return I915_READ(SDEISR) & bit; +} + static const char *state_string(bool enabled) { return enabled ? "on" : "off"; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 22728f2c1d83..53d4c8fec2a0 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -545,6 +545,9 @@ hdmi_to_dig_port(struct intel_hdmi *intel_hdmi) return container_of(intel_hdmi, struct intel_digital_port, hdmi); } +bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port); + extern void intel_connector_attach_encoder(struct intel_connector *connector, struct intel_encoder *encoder); extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 53df0a88a6a8..9f834d324cfd 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -793,16 +793,21 @@ static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi) static enum drm_connector_status intel_hdmi_detect(struct drm_connector *connector, bool force) { + struct drm_device *dev = connector->dev; struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); struct intel_digital_port *intel_dig_port = hdmi_to_dig_port(intel_hdmi); struct intel_encoder *intel_encoder = &intel_dig_port->base; - struct drm_i915_private *dev_priv = connector->dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; struct edid *edid; enum drm_connector_status status = connector_status_disconnected; - if (IS_G4X(connector->dev) && !g4x_hdmi_connected(intel_hdmi)) + + if (IS_G4X(dev) && !g4x_hdmi_connected(intel_hdmi)) return status; + else if (HAS_PCH_SPLIT(dev) && + !ibx_digital_port_connected(dev_priv, intel_dig_port)) + return status; intel_hdmi->has_hdmi_sink = false; intel_hdmi->has_audio = false; From 1b4696394aeb8a550f8537e92ae6cc65f444dca0 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:09:01 +0000 Subject: [PATCH 0175/4607] drm/i915/dp: Read the HPD status before trying to read the DPCD Just like: Author: Damien Lespiau Date: Wed Dec 12 19:37:22 2012 +0000 drm/i915/hdmi: Read the HPD status before trying to read the EDID But this time for DiplayPort. v2: Adapt to the ibx_ name change and don't add commit hash (Chris Wilson, Jani Nikula) Reviewed-by: Jani Nikula Signed-off-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b2130bc6c297..19a0d8984079 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2248,6 +2248,8 @@ static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); enum drm_connector_status status; /* Can't disconnect eDP, but you can close the lid... */ @@ -2258,6 +2260,9 @@ ironlake_dp_detect(struct intel_dp *intel_dp) return status; } + if (!ibx_digital_port_connected(dev_priv, intel_dig_port)) + return connector_status_disconnected; + return intel_dp_detect_dpcd(intel_dp); } From 577c7a505b5601a9a441039dd37543775f9af8f5 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:09:02 +0000 Subject: [PATCH 0176/4607] drm/i915/dp: Log the DPCD only if we have successfully retrieved one Moving the DPCD just after a successful read will allow to: - log all DPCD reads (eDP ones, changes signalled by HPD IRQ) - don't log it if we haven't been able to read it v2: Be sure to log the DPCD when a downstream port does not have HPD support and the branch device asserts HPD (Jani Nikula) Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 19a0d8984079..5ed8fb3435ee 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2084,10 +2084,16 @@ intel_dp_link_down(struct intel_dp *intel_dp) static bool intel_dp_get_dpcd(struct intel_dp *intel_dp) { + char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3]; + if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd, sizeof(intel_dp->dpcd)) == 0) return false; /* aux transfer failed */ + hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd), + 32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false); + DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump); + if (intel_dp->dpcd[DP_DPCD_REV] == 0) return false; /* DPCD not present */ @@ -2353,7 +2359,6 @@ intel_dp_detect(struct drm_connector *connector, bool force) struct drm_device *dev = connector->dev; enum drm_connector_status status; struct edid *edid = NULL; - char dpcd_hex_dump[sizeof(intel_dp->dpcd) * 3]; intel_dp->has_audio = false; @@ -2362,10 +2367,6 @@ intel_dp_detect(struct drm_connector *connector, bool force) else status = g4x_dp_detect(intel_dp); - hex_dump_to_buffer(intel_dp->dpcd, sizeof(intel_dp->dpcd), - 32, 1, dpcd_hex_dump, sizeof(dpcd_hex_dump), false); - DRM_DEBUG_KMS("DPCD: %s\n", dpcd_hex_dump); - if (status != connector_status_connected) return status; From c36346e302b3a36bfb10b58167b69ab7ac95f10a Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:09:03 +0000 Subject: [PATCH 0177/4607] drm/i915: Implement ibx_digital_port_connected() for IBX CPT+ PCHs have different bit definition to read the HPD live status. I don't have an ILK with digital ports handy, which is why this patch is separate from the CPT+ implementation. If the docs don't lie, it should all be fine though. Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 44 ++++++++++++++++++---------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6adeabda2587..7f1b07c9061d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -997,22 +997,34 @@ bool ibx_digital_port_connected(struct drm_i915_private *dev_priv, { u32 bit; - /* XXX: IBX has different SDEISR bits */ - if (HAS_PCH_IBX(dev_priv->dev)) - return true; - - switch(port->port) { - case PORT_B: - bit = SDE_PORTB_HOTPLUG_CPT; - break; - case PORT_C: - bit = SDE_PORTC_HOTPLUG_CPT; - break; - case PORT_D: - bit = SDE_PORTD_HOTPLUG_CPT; - break; - default: - return true; + if (HAS_PCH_IBX(dev_priv->dev)) { + switch(port->port) { + case PORT_B: + bit = SDE_PORTB_HOTPLUG; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG; + break; + default: + return true; + } + } else { + switch(port->port) { + case PORT_B: + bit = SDE_PORTB_HOTPLUG_CPT; + break; + case PORT_C: + bit = SDE_PORTC_HOTPLUG_CPT; + break; + case PORT_D: + bit = SDE_PORTD_HOTPLUG_CPT; + break; + default: + return true; + } } return I915_READ(SDEISR) & bit; From 3f8c65d60467c6ccf4d690f5266567d6c423ae7d Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Thu, 13 Dec 2012 16:09:04 +0000 Subject: [PATCH 0178/4607] drm/i915: Remove stale comment about intel_dp_detect() The function doesn't use any of the registers mentioned, nor does it return true or false. Hard to do worse. Remove it, the function is absolutely descriptive enough to not need any comment. Signed-off-by: Damien Lespiau Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 5ed8fb3435ee..185bf4e7614e 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2343,13 +2343,6 @@ intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *ada return intel_ddc_get_modes(connector, adapter); } - -/** - * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection. - * - * \return true if DP port is connected. - * \return false if DP port is disconnected. - */ static enum drm_connector_status intel_dp_detect(struct drm_connector *connector, bool force) { From 3993de6183885a099163b9562a2ea9c07b994a0e Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 27 Nov 2012 06:53:24 +0000 Subject: [PATCH 0179/4607] libfcoe: Add fcoe_sysfs debug logging level Add a macro to print fcoe_sysfs debug statements. Signed-off-by: Robert Love Acked-by: Neil Horman --- drivers/scsi/fcoe/fcoe_sysfs.c | 7 +++++++ drivers/scsi/fcoe/libfcoe.h | 11 ++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c index e6fce2862a33..9c74374b53cd 100644 --- a/drivers/scsi/fcoe/fcoe_sysfs.c +++ b/drivers/scsi/fcoe/fcoe_sysfs.c @@ -24,6 +24,13 @@ #include +/* + * OK to include local libfcoe.h for debug_logging, but cannot include + * otherwise non-netdev based fcoe solutions would have + * have to include more than fcoe_sysfs.h. + */ +#include "libfcoe.h" + static atomic_t ctlr_num; static atomic_t fcf_num; diff --git a/drivers/scsi/fcoe/libfcoe.h b/drivers/scsi/fcoe/libfcoe.h index 6af5fc3a17d8..63d8faedca9f 100644 --- a/drivers/scsi/fcoe/libfcoe.h +++ b/drivers/scsi/fcoe/libfcoe.h @@ -2,9 +2,10 @@ #define _FCOE_LIBFCOE_H_ extern unsigned int libfcoe_debug_logging; -#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */ -#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */ -#define LIBFCOE_TRANSPORT_LOGGING 0x04 /* FCoE transport logging */ +#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */ +#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */ +#define LIBFCOE_TRANSPORT_LOGGING 0x04 /* FCoE transport logging */ +#define LIBFCOE_SYSFS_LOGGING 0x08 /* fcoe_sysfs logging */ #define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \ do { \ @@ -28,4 +29,8 @@ do { \ printk(KERN_INFO "%s: " fmt, \ __func__, ##args);) +#define LIBFCOE_SYSFS_DBG(cdev, fmt, args...) \ + LIBFCOE_CHECK_LOGGING(LIBFCOE_SYSFS_LOGGING, \ + pr_info("ctlr_%d: " fmt, cdev->id, ##args);) + #endif /* _FCOE_LIBFCOE_H_ */ From 6a891b071b640e1de44c4a5117fa2c974dcfa84a Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 27 Nov 2012 06:53:30 +0000 Subject: [PATCH 0180/4607] libfcoe, fcoe, bnx2fc: Add new fcoe control interface This patch does a few things. 1) Makes /sys/bus/fcoe/ctlr_{create,destroy} interfaces. These interfaces take an and will either create an FCoE Controller or destroy an FCoE Controller depending on which file is written to. The new FCoE Controller will start in a DISABLED state and will not do discovery or login until it is ENABLED. This pause will allow us to configure the FCoE Controller before enabling it. 2) Makes the 'mode' attribute of a fcoe_ctlr_device writale. This allows the user to configure the mode in which the FCoE Controller will start in when it is ENABLED. Possible modes are 'Fabric', or 'VN2VN'. The default mode for a fcoe_ctlr{,_device} is 'Fabric'. Drivers must implement the set_fcoe_ctlr_mode routine to support this feature. libfcoe offers an exported routine to set a FCoE Controller's mode. The mode can only be changed when the FCoE Controller is DISABLED. This patch also removes the get_fcoe_ctlr_mode pointer in the fcoe_sysfs function template, the code in fcoe_ctlr.c to get the mode and the assignment of the fcoe_sysfs function pointer to the fcoe_ctlr.c implementation (in fcoe and bnx2fc). fcoe_sysfs can return that value for the mode without consulting the LLD. 3) Make a 'enabled' attribute of a fcoe_ctlr_device. On a read, fcoe_sysfs will return the attribute's value. On a write, fcoe_sysfs will call the LLD (if there is a callback) to notifiy that the enalbed state has changed. This patch maintains the old FCoE control interfaces as module parameters, but it adds comments pointing out that the old interfaces are deprecated. Signed-off-by: Robert Love Acked-by: Neil Horman --- Documentation/ABI/testing/sysfs-bus-fcoe | 41 ++++++- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 1 - drivers/scsi/fcoe/fcoe.c | 1 - drivers/scsi/fcoe/fcoe_sysfs.c | 137 +++++++++++++++++++++-- drivers/scsi/fcoe/fcoe_transport.c | 104 +++++++++++++++++ include/scsi/fcoe_sysfs.h | 11 +- include/scsi/libfcoe.h | 16 ++- 7 files changed, 298 insertions(+), 13 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-fcoe b/Documentation/ABI/testing/sysfs-bus-fcoe index 971dc22e6c2a..21640eaad371 100644 --- a/Documentation/ABI/testing/sysfs-bus-fcoe +++ b/Documentation/ABI/testing/sysfs-bus-fcoe @@ -1,14 +1,53 @@ +What: /sys/bus/fcoe/ +Date: August 2012 +KernelVersion: TBD +Contact: Robert Love , devel@open-fcoe.org +Description: The FCoE bus. Attributes in this directory are control interfaces. +Attributes: + + ctlr_create: 'FCoE Controller' instance creation interface. Writing an + to this file will allocate and populate sysfs with a + fcoe_ctlr_device (ctlr_X). The user can then configure any + per-port settings and finally write to the fcoe_ctlr_device's + 'start' attribute to begin the kernel's discovery and login + process. + + ctlr_destroy: 'FCoE Controller' instance removal interface. Writing a + fcoe_ctlr_device's sysfs name to this file will log the + fcoe_ctlr_device out of the fabric or otherwise connected + FCoE devices. It will also free all kernel memory allocated + for this fcoe_ctlr_device and any structures associated + with it, this includes the scsi_host. + What: /sys/bus/fcoe/devices/ctlr_X Date: March 2012 KernelVersion: TBD Contact: Robert Love , devel@open-fcoe.org -Description: 'FCoE Controller' instances on the fcoe bus +Description: 'FCoE Controller' instances on the fcoe bus. + The FCoE Controller now has a three stage creation process. + 1) Write interface name to ctlr_create 2) Configure the FCoE + Controller (ctlr_X) 3) Enable the FCoE Controller to begin + discovery and login. The FCoE Controller is destroyed by + writing it's name, i.e. ctlr_X to the ctlr_delete file. + Attributes: fcf_dev_loss_tmo: Device loss timeout peroid (see below). Changing this value will change the dev_loss_tmo for all FCFs discovered by this controller. + mode: Display or change the FCoE Controller's mode. Possible + modes are 'Fabric' and 'VN2VN'. If a FCoE Controller + is started in 'Fabric' mode then FIP FCF discovery is + initiated and ultimately a fabric login is attempted. + If a FCoE Controller is started in 'VN2VN' mode then + FIP VN2VN discovery and login is performed. A FCoE + Controller only supports one mode at a time. + + enabled: Whether an FCoE controller is enabled or disabled. + 0 if disabled, 1 if enabled. Writing either 0 or 1 + to this file will enable or disable the FCoE controller. + lesb/link_fail: Link Error Status Block (LESB) link failure count. lesb/vlink_fail: Link Error Status Block (LESB) virtual link diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index e0558656c646..9b38794d5665 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -2555,7 +2555,6 @@ module_init(bnx2fc_mod_init); module_exit(bnx2fc_mod_exit); static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ = { - .get_fcoe_ctlr_mode = fcoe_ctlr_get_fip_mode, .get_fcoe_ctlr_link_fail = bnx2fc_ctlr_get_lesb, .get_fcoe_ctlr_vlink_fail = bnx2fc_ctlr_get_lesb, .get_fcoe_ctlr_miss_fka = bnx2fc_ctlr_get_lesb, diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 666b7ac4475f..9bd982d2c07f 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -155,7 +155,6 @@ static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *); static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *); static struct fcoe_sysfs_function_template fcoe_sysfs_templ = { - .get_fcoe_ctlr_mode = fcoe_ctlr_get_fip_mode, .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb, .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb, .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb, diff --git a/drivers/scsi/fcoe/fcoe_sysfs.c b/drivers/scsi/fcoe/fcoe_sysfs.c index 9c74374b53cd..8c05ae017f5b 100644 --- a/drivers/scsi/fcoe/fcoe_sysfs.c +++ b/drivers/scsi/fcoe/fcoe_sysfs.c @@ -21,8 +21,10 @@ #include #include #include +#include #include +#include /* * OK to include local libfcoe.h for debug_logging, but cannot include @@ -78,6 +80,8 @@ MODULE_PARM_DESC(fcf_dev_loss_tmo, ((x)->lesb.lesb_err_block) #define fcoe_ctlr_fcs_error(x) \ ((x)->lesb.lesb_fcs_error) +#define fcoe_ctlr_enabled(x) \ + ((x)->enabled) #define fcoe_fcf_state(x) \ ((x)->state) #define fcoe_fcf_fabric_name(x) \ @@ -228,7 +232,18 @@ static char *fip_conn_type_names[] = { [ FIP_CONN_TYPE_VN2VN ] = "VN2VN", }; fcoe_enum_name_search(ctlr_mode, fip_conn_type, fip_conn_type_names) -#define FCOE_CTLR_MODE_MAX_NAMELEN 50 + +static enum fip_conn_type fcoe_parse_mode(const char *buf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fip_conn_type_names); i++) { + if (strcasecmp(buf, fip_conn_type_names[i]) == 0) + return i; + } + + return FIP_CONN_TYPE_UNKNOWN; +} static char *fcf_state_names[] = { [ FCOE_FCF_STATE_UNKNOWN ] = "Unknown", @@ -259,17 +274,116 @@ static ssize_t show_ctlr_mode(struct device *dev, struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); const char *name; - if (ctlr->f->get_fcoe_ctlr_mode) - ctlr->f->get_fcoe_ctlr_mode(ctlr); - name = get_fcoe_ctlr_mode_name(ctlr->mode); if (!name) return -EINVAL; - return snprintf(buf, FCOE_CTLR_MODE_MAX_NAMELEN, + return snprintf(buf, FCOE_MAX_MODENAME_LEN, "%s\n", name); } -static FCOE_DEVICE_ATTR(ctlr, mode, S_IRUGO, - show_ctlr_mode, NULL); + +static ssize_t store_ctlr_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); + char mode[FCOE_MAX_MODENAME_LEN + 1]; + + if (count > FCOE_MAX_MODENAME_LEN) + return -EINVAL; + + strncpy(mode, buf, count); + + if (mode[count - 1] == '\n') + mode[count - 1] = '\0'; + else + mode[count] = '\0'; + + switch (ctlr->enabled) { + case FCOE_CTLR_ENABLED: + LIBFCOE_SYSFS_DBG(ctlr, "Cannot change mode when enabled."); + return -EBUSY; + case FCOE_CTLR_DISABLED: + if (!ctlr->f->set_fcoe_ctlr_mode) { + LIBFCOE_SYSFS_DBG(ctlr, + "Mode change not supported by LLD."); + return -ENOTSUPP; + } + + ctlr->mode = fcoe_parse_mode(mode); + if (ctlr->mode == FIP_CONN_TYPE_UNKNOWN) { + LIBFCOE_SYSFS_DBG(ctlr, + "Unknown mode %s provided.", buf); + return -EINVAL; + } + + ctlr->f->set_fcoe_ctlr_mode(ctlr); + LIBFCOE_SYSFS_DBG(ctlr, "Mode changed to %s.", buf); + + return count; + case FCOE_CTLR_UNUSED: + default: + LIBFCOE_SYSFS_DBG(ctlr, "Mode change not supported."); + return -ENOTSUPP; + }; +} + +static FCOE_DEVICE_ATTR(ctlr, mode, S_IRUGO | S_IWUSR, + show_ctlr_mode, store_ctlr_mode); + +static ssize_t store_ctlr_enabled(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); + int rc; + + switch (ctlr->enabled) { + case FCOE_CTLR_ENABLED: + if (*buf == '1') + return count; + ctlr->enabled = FCOE_CTLR_DISABLED; + break; + case FCOE_CTLR_DISABLED: + if (*buf == '0') + return count; + ctlr->enabled = FCOE_CTLR_ENABLED; + break; + case FCOE_CTLR_UNUSED: + return -ENOTSUPP; + }; + + rc = ctlr->f->set_fcoe_ctlr_enabled(ctlr); + if (rc) + return rc; + + return count; +} + +static char *ctlr_enabled_state_names[] = { + [ FCOE_CTLR_ENABLED ] = "1", + [ FCOE_CTLR_DISABLED ] = "0", +}; +fcoe_enum_name_search(ctlr_enabled_state, ctlr_enabled_state, + ctlr_enabled_state_names) +#define FCOE_CTLR_ENABLED_MAX_NAMELEN 50 + +static ssize_t show_ctlr_enabled_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fcoe_ctlr_device *ctlr = dev_to_ctlr(dev); + const char *name; + + name = get_fcoe_ctlr_enabled_state_name(ctlr->enabled); + if (!name) + return -EINVAL; + return snprintf(buf, FCOE_CTLR_ENABLED_MAX_NAMELEN, + "%s\n", name); +} + +static FCOE_DEVICE_ATTR(ctlr, enabled, S_IRUGO | S_IWUSR, + show_ctlr_enabled_state, + store_ctlr_enabled); static ssize_t store_private_fcoe_ctlr_fcf_dev_loss_tmo(struct device *dev, @@ -354,6 +468,7 @@ static struct attribute_group fcoe_ctlr_lesb_attr_group = { static struct attribute *fcoe_ctlr_attrs[] = { &device_attr_fcoe_ctlr_fcf_dev_loss_tmo.attr, + &device_attr_fcoe_ctlr_enabled.attr, &device_attr_fcoe_ctlr_mode.attr, NULL, }; @@ -438,9 +553,16 @@ struct device_type fcoe_fcf_device_type = { .release = fcoe_fcf_device_release, }; +struct bus_attribute fcoe_bus_attr_group[] = { + __ATTR(ctlr_create, S_IWUSR, NULL, fcoe_ctlr_create_store), + __ATTR(ctlr_destroy, S_IWUSR, NULL, fcoe_ctlr_destroy_store), + __ATTR_NULL +}; + struct bus_type fcoe_bus_type = { .name = "fcoe", .match = &fcoe_bus_match, + .bus_attrs = fcoe_bus_attr_group, }; /** @@ -561,6 +683,7 @@ struct fcoe_ctlr_device *fcoe_ctlr_device_add(struct device *parent, ctlr->id = atomic_inc_return(&ctlr_num) - 1; ctlr->f = f; + ctlr->mode = FIP_CONN_TYPE_FABRIC; INIT_LIST_HEAD(&ctlr->fcfs); mutex_init(&ctlr->lock); ctlr->dev.parent = parent; diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index ac76d8a042d7..83e78f922054 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -627,6 +627,110 @@ static int libfcoe_device_notification(struct notifier_block *notifier, return NOTIFY_OK; } +ssize_t fcoe_ctlr_create_store(struct bus_type *bus, + const char *buf, size_t count) +{ + struct net_device *netdev = NULL; + struct fcoe_transport *ft = NULL; + struct fcoe_ctlr_device *ctlr_dev = NULL; + int rc = 0; + int err; + + mutex_lock(&ft_mutex); + + netdev = fcoe_if_to_netdev(buf); + if (!netdev) { + LIBFCOE_TRANSPORT_DBG("Invalid device %s.\n", buf); + rc = -ENODEV; + goto out_nodev; + } + + ft = fcoe_netdev_map_lookup(netdev); + if (ft) { + LIBFCOE_TRANSPORT_DBG("transport %s already has existing " + "FCoE instance on %s.\n", + ft->name, netdev->name); + rc = -EEXIST; + goto out_putdev; + } + + ft = fcoe_transport_lookup(netdev); + if (!ft) { + LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", + netdev->name); + rc = -ENODEV; + goto out_putdev; + } + + /* pass to transport create */ + err = ft->alloc ? ft->alloc(netdev) : -ENODEV; + if (err) { + fcoe_del_netdev_mapping(netdev); + rc = -ENOMEM; + goto out_putdev; + } + + err = fcoe_add_netdev_mapping(netdev, ft); + if (err) { + LIBFCOE_TRANSPORT_DBG("failed to add new netdev mapping " + "for FCoE transport %s for %s.\n", + ft->name, netdev->name); + rc = -ENODEV; + goto out_putdev; + } + + LIBFCOE_TRANSPORT_DBG("transport %s %s to create fcoe on %s.\n", + ft->name, (ctlr_dev) ? "succeeded" : "failed", + netdev->name); + +out_putdev: + dev_put(netdev); +out_nodev: + mutex_unlock(&ft_mutex); + if (rc) + return rc; + return count; +} + +ssize_t fcoe_ctlr_destroy_store(struct bus_type *bus, + const char *buf, size_t count) +{ + int rc = -ENODEV; + struct net_device *netdev = NULL; + struct fcoe_transport *ft = NULL; + + mutex_lock(&ft_mutex); + + netdev = fcoe_if_to_netdev(buf); + if (!netdev) { + LIBFCOE_TRANSPORT_DBG("invalid device %s.\n", buf); + goto out_nodev; + } + + ft = fcoe_netdev_map_lookup(netdev); + if (!ft) { + LIBFCOE_TRANSPORT_DBG("no FCoE transport found for %s.\n", + netdev->name); + goto out_putdev; + } + + /* pass to transport destroy */ + rc = ft->destroy(netdev); + if (rc) + goto out_putdev; + + fcoe_del_netdev_mapping(netdev); + LIBFCOE_TRANSPORT_DBG("transport %s %s to destroy fcoe on %s.\n", + ft->name, (rc) ? "failed" : "succeeded", + netdev->name); + rc = count; /* required for successful return */ +out_putdev: + dev_put(netdev); +out_nodev: + mutex_unlock(&ft_mutex); + return rc; +} +EXPORT_SYMBOL(fcoe_ctlr_destroy_store); /** * fcoe_transport_create() - Create a fcoe interface diff --git a/include/scsi/fcoe_sysfs.h b/include/scsi/fcoe_sysfs.h index 604cb9bb3e76..7e2314870341 100644 --- a/include/scsi/fcoe_sysfs.h +++ b/include/scsi/fcoe_sysfs.h @@ -34,7 +34,8 @@ struct fcoe_sysfs_function_template { void (*get_fcoe_ctlr_symb_err)(struct fcoe_ctlr_device *); void (*get_fcoe_ctlr_err_block)(struct fcoe_ctlr_device *); void (*get_fcoe_ctlr_fcs_error)(struct fcoe_ctlr_device *); - void (*get_fcoe_ctlr_mode)(struct fcoe_ctlr_device *); + void (*set_fcoe_ctlr_mode)(struct fcoe_ctlr_device *); + int (*set_fcoe_ctlr_enabled)(struct fcoe_ctlr_device *); void (*get_fcoe_fcf_selected)(struct fcoe_fcf_device *); void (*get_fcoe_fcf_vlan_id)(struct fcoe_fcf_device *); }; @@ -48,6 +49,12 @@ enum fip_conn_type { FIP_CONN_TYPE_VN2VN, }; +enum ctlr_enabled_state { + FCOE_CTLR_ENABLED, + FCOE_CTLR_DISABLED, + FCOE_CTLR_UNUSED, +}; + struct fcoe_ctlr_device { u32 id; @@ -64,6 +71,8 @@ struct fcoe_ctlr_device { int fcf_dev_loss_tmo; enum fip_conn_type mode; + enum ctlr_enabled_state enabled; + /* expected in host order for displaying */ struct fcoe_fc_els_lesb lesb; }; diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 8742d853a3b8..6add37ac8609 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -289,8 +289,11 @@ static inline bool is_fip_mode(struct fcoe_ctlr *fip) * @attached: whether this transport is already attached * @list: list linkage to all attached transports * @match: handler to allow the transport driver to match up a given netdev + * @alloc: handler to allocate per-instance FCoE structures + * (no discovery or login) * @create: handler to sysfs entry of create for FCoE instances - * @destroy: handler to sysfs entry of destroy for FCoE instances + * @destroy: handler to delete per-instance FCoE structures + * (frees all memory) * @enable: handler to sysfs entry of enable for FCoE instances * @disable: handler to sysfs entry of disable for FCoE instances */ @@ -299,6 +302,7 @@ struct fcoe_transport { bool attached; struct list_head list; bool (*match) (struct net_device *device); + int (*alloc) (struct net_device *device); int (*create) (struct net_device *device, enum fip_state fip_mode); int (*destroy) (struct net_device *device); int (*enable) (struct net_device *device); @@ -356,7 +360,7 @@ int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen, /* FCoE Sysfs helpers */ void fcoe_fcf_get_selected(struct fcoe_fcf_device *); -void fcoe_ctlr_get_fip_mode(struct fcoe_ctlr_device *); +void fcoe_ctlr_set_fip_mode(struct fcoe_ctlr_device *); /** * struct netdev_list @@ -372,4 +376,12 @@ struct fcoe_netdev_mapping { int fcoe_transport_attach(struct fcoe_transport *ft); int fcoe_transport_detach(struct fcoe_transport *ft); +/* sysfs store handler for ctrl_control interface */ +ssize_t fcoe_ctlr_create_store(struct bus_type *bus, + const char *buf, size_t count); +ssize_t fcoe_ctlr_destroy_store(struct bus_type *bus, + const char *buf, size_t count); + #endif /* _LIBFCOE_H */ + + From 435c86679a24ead623c8a47ca31038e250a75e05 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 27 Nov 2012 06:53:35 +0000 Subject: [PATCH 0181/4607] fcoe: Use the fcoe_sysfs control interface This patch adds support for the new fcoe_sysfs control interface to fcoe.ko. It keeps the deprecated interface in tact and therefore either the legacy or the new control interfaces can be used. A mixed mode is not supported. A user must either use the new interfaces or the old ones, but not both. The fcoe_ctlr's link state is now driven by both the netdev link state as well as the fcoe_ctlr_device's enabled attribute. The link must be up and the fcoe_ctlr_device must be enabled before the FCoE Controller starts discovery or login. Signed-off-by: Robert Love Acked-by: Neil Horman --- drivers/scsi/fcoe/fcoe.c | 147 ++++++++++++++++++++++++++++++---- drivers/scsi/fcoe/fcoe_ctlr.c | 17 ++-- 2 files changed, 140 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 9bd982d2c07f..21927f7952d8 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -117,6 +117,11 @@ static int fcoe_destroy(struct net_device *netdev); static int fcoe_enable(struct net_device *netdev); static int fcoe_disable(struct net_device *netdev); +/* fcoe_syfs control interface handlers */ +static int fcoe_ctlr_alloc(struct net_device *netdev); +static int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev); + + static struct fc_seq *fcoe_elsct_send(struct fc_lport *, u32 did, struct fc_frame *, unsigned int op, @@ -155,6 +160,8 @@ static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *); static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *); static struct fcoe_sysfs_function_template fcoe_sysfs_templ = { + .set_fcoe_ctlr_mode = fcoe_ctlr_set_fip_mode, + .set_fcoe_ctlr_enabled = fcoe_ctlr_enabled, .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb, .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb, .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb, @@ -1963,6 +1970,7 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier, static int fcoe_device_notification(struct notifier_block *notifier, ulong event, void *ptr) { + struct fcoe_ctlr_device *cdev; struct fc_lport *lport = NULL; struct net_device *netdev = ptr; struct fcoe_ctlr *ctlr; @@ -2019,13 +2027,29 @@ static int fcoe_device_notification(struct notifier_block *notifier, fcoe_link_speed_update(lport); - if (link_possible && !fcoe_link_ok(lport)) - fcoe_ctlr_link_up(ctlr); - else if (fcoe_ctlr_link_down(ctlr)) { - stats = per_cpu_ptr(lport->stats, get_cpu()); - stats->LinkFailureCount++; - put_cpu(); - fcoe_clean_pending_queue(lport); + cdev = fcoe_ctlr_to_ctlr_dev(ctlr); + + if (link_possible && !fcoe_link_ok(lport)) { + switch (cdev->enabled) { + case FCOE_CTLR_DISABLED: + pr_info("Link up while interface is disabled.\n"); + break; + case FCOE_CTLR_ENABLED: + case FCOE_CTLR_UNUSED: + fcoe_ctlr_link_up(ctlr); + }; + } else if (fcoe_ctlr_link_down(ctlr)) { + switch (cdev->enabled) { + case FCOE_CTLR_DISABLED: + pr_info("Link down while interface is disabled.\n"); + break; + case FCOE_CTLR_ENABLED: + case FCOE_CTLR_UNUSED: + stats = per_cpu_ptr(lport->stats, get_cpu()); + stats->LinkFailureCount++; + put_cpu(); + fcoe_clean_pending_queue(lport); + }; } out: return rc; @@ -2038,6 +2062,8 @@ out: * Called from fcoe transport. * * Returns: 0 for success + * + * Deprecated: use fcoe_ctlr_enabled() */ static int fcoe_disable(struct net_device *netdev) { @@ -2096,6 +2122,33 @@ out: return rc; } +/** + * fcoe_ctlr_enabled() - Enable or disable an FCoE Controller + * @cdev: The FCoE Controller that is being enabled or disabled + * + * fcoe_sysfs will ensure that the state of 'enabled' has + * changed, so no checking is necessary here. This routine simply + * calls fcoe_enable or fcoe_disable, both of which are deprecated. + * When those routines are removed the functionality can be merged + * here. + */ +static int fcoe_ctlr_enabled(struct fcoe_ctlr_device *cdev) +{ + struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(cdev); + struct fc_lport *lport = ctlr->lp; + struct net_device *netdev = fcoe_netdev(lport); + + switch (cdev->enabled) { + case FCOE_CTLR_ENABLED: + return fcoe_enable(netdev); + case FCOE_CTLR_DISABLED: + return fcoe_disable(netdev); + case FCOE_CTLR_UNUSED: + default: + return -ENOTSUPP; + }; +} + /** * fcoe_destroy() - Destroy a FCoE interface * @netdev : The net_device object the Ethernet interface to create on @@ -2203,16 +2256,26 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe) #endif } +enum fcoe_create_link_state { + FCOE_CREATE_LINK_DOWN, + FCOE_CREATE_LINK_UP, +}; + /** - * fcoe_create() - Create a fcoe interface - * @netdev : The net_device object the Ethernet interface to create on - * @fip_mode: The FIP mode for this creation + * _fcoe_create() - (internal) Create a fcoe interface + * @netdev : The net_device object the Ethernet interface to create on + * @fip_mode: The FIP mode for this creation + * @link_state: The ctlr link state on creation * - * Called from fcoe transport + * Called from either the libfcoe 'create' module parameter + * via fcoe_create or from fcoe_syfs's ctlr_create file. * - * Returns: 0 for success + * libfcoe's 'create' module parameter is deprecated so some + * consolidation of code can be done when that interface is + * removed. */ -static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) +static int _fcoe_create(struct net_device *netdev, enum fip_state fip_mode, + enum fcoe_create_link_state link_state) { int rc = 0; struct fcoe_ctlr_device *ctlr_dev; @@ -2259,7 +2322,26 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) /* start FIP Discovery and FLOGI */ lport->boot_time = jiffies; fc_fabric_login(lport); - if (!fcoe_link_ok(lport)) { + + /* + * If the fcoe_ctlr_device is to be set to DISABLED + * it must be done after the lport is added to the + * hostlist, but before the rtnl_lock is released. + * This is because the rtnl_lock protects the + * hostlist that fcoe_device_notification uses. If + * the FCoE Controller is intended to be created + * DISABLED then 'enabled' needs to be considered + * handling link events. 'enabled' must be set + * before the lport can be found in the hostlist + * when a link up event is received. + */ + if (link_state == FCOE_CREATE_LINK_UP) + ctlr_dev->enabled = FCOE_CTLR_ENABLED; + else + ctlr_dev->enabled = FCOE_CTLR_DISABLED; + + if (link_state == FCOE_CREATE_LINK_UP && + !fcoe_link_ok(lport)) { rtnl_unlock(); fcoe_ctlr_link_up(ctlr); mutex_unlock(&fcoe_config_mutex); @@ -2273,6 +2355,37 @@ out_nortnl: return rc; } +/** + * fcoe_create() - Create a fcoe interface + * @netdev : The net_device object the Ethernet interface to create on + * @fip_mode: The FIP mode for this creation + * + * Called from fcoe transport + * + * Returns: 0 for success + */ +static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) +{ + return _fcoe_create(netdev, fip_mode, FCOE_CREATE_LINK_UP); +} + +/** + * fcoe_ctlr_alloc() - Allocate a fcoe interface from fcoe_sysfs + * @netdev: The net_device to be used by the allocated FCoE Controller + * + * This routine is called from fcoe_sysfs. It will start the fcoe_ctlr + * in a link_down state. The allows the user an opportunity to configure + * the FCoE Controller from sysfs before enabling the FCoE Controller. + * + * Creating in with this routine starts the FCoE Controller in Fabric + * mode. The user can change to VN2VN or another mode before enabling. + */ +static int fcoe_ctlr_alloc(struct net_device *netdev) +{ + return _fcoe_create(netdev, FIP_MODE_FABRIC, + FCOE_CREATE_LINK_DOWN); +} + /** * fcoe_link_speed_update() - Update the supported and actual link speeds * @lport: The local port to update speeds for @@ -2374,10 +2487,13 @@ static int fcoe_reset(struct Scsi_Host *shost) struct fcoe_port *port = lport_priv(lport); struct fcoe_interface *fcoe = port->priv; struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); + struct fcoe_ctlr_device *cdev = fcoe_ctlr_to_ctlr_dev(ctlr); fcoe_ctlr_link_down(ctlr); fcoe_clean_pending_queue(ctlr->lp); - if (!fcoe_link_ok(ctlr->lp)) + + if (cdev->enabled != FCOE_CTLR_DISABLED && + !fcoe_link_ok(ctlr->lp)) fcoe_ctlr_link_up(ctlr); return 0; } @@ -2450,6 +2566,7 @@ static struct fcoe_transport fcoe_sw_transport = { .attached = false, .list = LIST_HEAD_INIT(fcoe_sw_transport.list), .match = fcoe_match, + .alloc = fcoe_ctlr_alloc, .create = fcoe_create, .destroy = fcoe_destroy, .enable = fcoe_enable, diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index 2ebe03a4b51d..75834255bf07 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -2864,22 +2864,21 @@ void fcoe_fcf_get_selected(struct fcoe_fcf_device *fcf_dev) } EXPORT_SYMBOL(fcoe_fcf_get_selected); -void fcoe_ctlr_get_fip_mode(struct fcoe_ctlr_device *ctlr_dev) +void fcoe_ctlr_set_fip_mode(struct fcoe_ctlr_device *ctlr_dev) { struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(ctlr_dev); mutex_lock(&ctlr->ctlr_mutex); - switch (ctlr->mode) { - case FIP_MODE_FABRIC: - ctlr_dev->mode = FIP_CONN_TYPE_FABRIC; - break; - case FIP_MODE_VN2VN: - ctlr_dev->mode = FIP_CONN_TYPE_VN2VN; + switch (ctlr_dev->mode) { + case FIP_CONN_TYPE_VN2VN: + ctlr->mode = FIP_MODE_VN2VN; break; + case FIP_CONN_TYPE_FABRIC: default: - ctlr_dev->mode = FIP_CONN_TYPE_UNKNOWN; + ctlr->mode = FIP_MODE_FABRIC; break; } + mutex_unlock(&ctlr->ctlr_mutex); } -EXPORT_SYMBOL(fcoe_ctlr_get_fip_mode); +EXPORT_SYMBOL(fcoe_ctlr_set_fip_mode); From 6e89ea3f1032d5c762f1ae9b0bd2039794596813 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 27 Nov 2012 06:53:40 +0000 Subject: [PATCH 0182/4607] bnx2fc: Use the fcoe_sysfs control interface This patch adds support for the new fcoe_sysfs control interface to bnx2fc.ko. It keeps the deprecated interface in tact and therefore either the legacy or the new control interfaces can be used. A mixed mode is not supported. A user must either use the new interfaces or the old ones, but not both. The fcoe_ctlr's link state is now driven by both the netdev link state as well as the fcoe_ctlr_device's enabled attribute. The link must be up and the fcoe_ctlr_device must be enabled before the FCoE Controller starts discovery or login. Signed-off-by: Robert Love Acked-by: Neil Horman --- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 168 ++++++++++++++++++++++++------ 1 file changed, 139 insertions(+), 29 deletions(-) diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 9b38794d5665..8d975ef4eb69 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -62,6 +62,10 @@ static int bnx2fc_destroy(struct net_device *net_device); static int bnx2fc_enable(struct net_device *netdev); static int bnx2fc_disable(struct net_device *netdev); +/* fcoe_syfs control interface handlers */ +static int bnx2fc_ctlr_alloc(struct net_device *netdev); +static int bnx2fc_ctlr_enabled(struct fcoe_ctlr_device *cdev); + static void bnx2fc_recv_frame(struct sk_buff *skb); static void bnx2fc_start_disc(struct bnx2fc_interface *interface); @@ -864,6 +868,7 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event, u16 vlan_id) { struct bnx2fc_hba *hba = (struct bnx2fc_hba *)context; + struct fcoe_ctlr_device *cdev; struct fc_lport *lport; struct fc_lport *vport; struct bnx2fc_interface *interface, *tmp; @@ -925,28 +930,45 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event, bnx2fc_link_speed_update(lport); + cdev = fcoe_ctlr_to_ctlr_dev(ctlr); + if (link_possible && !bnx2fc_link_ok(lport)) { - /* Reset max recv frame size to default */ - fc_set_mfs(lport, BNX2FC_MFS); - /* - * ctlr link up will only be handled during - * enable to avoid sending discovery solicitation - * on a stale vlan - */ - if (interface->enabled) - fcoe_ctlr_link_up(ctlr); + switch (cdev->enabled) { + case FCOE_CTLR_DISABLED: + pr_info("Link up while interface is disabled.\n"); + break; + case FCOE_CTLR_ENABLED: + case FCOE_CTLR_UNUSED: + /* Reset max recv frame size to default */ + fc_set_mfs(lport, BNX2FC_MFS); + /* + * ctlr link up will only be handled during + * enable to avoid sending discovery + * solicitation on a stale vlan + */ + if (interface->enabled) + fcoe_ctlr_link_up(ctlr); + }; } else if (fcoe_ctlr_link_down(ctlr)) { - mutex_lock(&lport->lp_mutex); - list_for_each_entry(vport, &lport->vports, list) - fc_host_port_type(vport->host) = - FC_PORTTYPE_UNKNOWN; - mutex_unlock(&lport->lp_mutex); - fc_host_port_type(lport->host) = FC_PORTTYPE_UNKNOWN; - per_cpu_ptr(lport->stats, - get_cpu())->LinkFailureCount++; - put_cpu(); - fcoe_clean_pending_queue(lport); - wait_for_upload = 1; + switch (cdev->enabled) { + case FCOE_CTLR_DISABLED: + pr_info("Link down while interface is disabled.\n"); + break; + case FCOE_CTLR_ENABLED: + case FCOE_CTLR_UNUSED: + mutex_lock(&lport->lp_mutex); + list_for_each_entry(vport, &lport->vports, list) + fc_host_port_type(vport->host) = + FC_PORTTYPE_UNKNOWN; + mutex_unlock(&lport->lp_mutex); + fc_host_port_type(lport->host) = + FC_PORTTYPE_UNKNOWN; + per_cpu_ptr(lport->stats, + get_cpu())->LinkFailureCount++; + put_cpu(); + fcoe_clean_pending_queue(lport); + wait_for_upload = 1; + }; } } mutex_unlock(&bnx2fc_dev_lock); @@ -1996,7 +2018,9 @@ static void bnx2fc_ulp_init(struct cnic_dev *dev) set_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic); } - +/** + * Deperecated: Use bnx2fc_enabled() + */ static int bnx2fc_disable(struct net_device *netdev) { struct bnx2fc_interface *interface; @@ -2022,7 +2046,9 @@ static int bnx2fc_disable(struct net_device *netdev) return rc; } - +/** + * Deprecated: Use bnx2fc_enabled() + */ static int bnx2fc_enable(struct net_device *netdev) { struct bnx2fc_interface *interface; @@ -2048,17 +2074,57 @@ static int bnx2fc_enable(struct net_device *netdev) } /** - * bnx2fc_create - Create bnx2fc FCoE interface + * bnx2fc_ctlr_enabled() - Enable or disable an FCoE Controller + * @cdev: The FCoE Controller that is being enabled or disabled * - * @buffer: The name of Ethernet interface to create on - * @kp: The associated kernel param + * fcoe_sysfs will ensure that the state of 'enabled' has + * changed, so no checking is necessary here. This routine simply + * calls fcoe_enable or fcoe_disable, both of which are deprecated. + * When those routines are removed the functionality can be merged + * here. + */ +static int bnx2fc_ctlr_enabled(struct fcoe_ctlr_device *cdev) +{ + struct fcoe_ctlr *ctlr = fcoe_ctlr_device_priv(cdev); + struct fc_lport *lport = ctlr->lp; + struct net_device *netdev = bnx2fc_netdev(lport); + + switch (cdev->enabled) { + case FCOE_CTLR_ENABLED: + return bnx2fc_enable(netdev); + case FCOE_CTLR_DISABLED: + return bnx2fc_disable(netdev); + case FCOE_CTLR_UNUSED: + default: + return -ENOTSUPP; + }; +} + +enum bnx2fc_create_link_state { + BNX2FC_CREATE_LINK_DOWN, + BNX2FC_CREATE_LINK_UP, +}; + +/** + * _bnx2fc_create() - Create bnx2fc FCoE interface + * @netdev : The net_device object the Ethernet interface to create on + * @fip_mode: The FIP mode for this creation + * @link_state: The ctlr link state on creation * - * Called from sysfs. + * Called from either the libfcoe 'create' module parameter + * via fcoe_create or from fcoe_syfs's ctlr_create file. + * + * libfcoe's 'create' module parameter is deprecated so some + * consolidation of code can be done when that interface is + * removed. * * Returns: 0 for success */ -static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) +static int _bnx2fc_create(struct net_device *netdev, + enum fip_state fip_mode, + enum bnx2fc_create_link_state link_state) { + struct fcoe_ctlr_device *cdev; struct fcoe_ctlr *ctlr; struct bnx2fc_interface *interface; struct bnx2fc_hba *hba; @@ -2153,7 +2219,15 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) /* Make this master N_port */ ctlr->lp = lport; - if (!bnx2fc_link_ok(lport)) { + cdev = fcoe_ctlr_to_ctlr_dev(ctlr); + + if (link_state == BNX2FC_CREATE_LINK_UP) + cdev->enabled = FCOE_CTLR_ENABLED; + else + cdev->enabled = FCOE_CTLR_DISABLED; + + if (link_state == BNX2FC_CREATE_LINK_UP && + !bnx2fc_link_ok(lport)) { fcoe_ctlr_link_up(ctlr); fc_host_port_type(lport->host) = FC_PORTTYPE_NPORT; set_bit(ADAPTER_STATE_READY, &interface->hba->adapter_state); @@ -2161,7 +2235,10 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) BNX2FC_HBA_DBG(lport, "create: START DISC\n"); bnx2fc_start_disc(interface); - interface->enabled = true; + + if (link_state == BNX2FC_CREATE_LINK_UP) + interface->enabled = true; + /* * Release from kref_init in bnx2fc_interface_setup, on success * lport should be holding a reference taken in bnx2fc_if_create @@ -2186,6 +2263,37 @@ mod_err: return rc; } +/** + * bnx2fc_create() - Create a bnx2fc interface + * @netdev : The net_device object the Ethernet interface to create on + * @fip_mode: The FIP mode for this creation + * + * Called from fcoe transport + * + * Returns: 0 for success + */ +static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) +{ + return _bnx2fc_create(netdev, fip_mode, BNX2FC_CREATE_LINK_UP); +} + +/** + * bnx2fc_ctlr_alloc() - Allocate a bnx2fc interface from fcoe_sysfs + * @netdev: The net_device to be used by the allocated FCoE Controller + * + * This routine is called from fcoe_sysfs. It will start the fcoe_ctlr + * in a link_down state. The allows the user an opportunity to configure + * the FCoE Controller from sysfs before enabling the FCoE Controller. + * + * Creating in with this routine starts the FCoE Controller in Fabric + * mode. The user can change to VN2VN or another mode before enabling. + */ +static int bnx2fc_ctlr_alloc(struct net_device *netdev) +{ + return _bnx2fc_create(netdev, FIP_MODE_FABRIC, + BNX2FC_CREATE_LINK_DOWN); +} + /** * bnx2fc_find_hba_for_cnic - maps cnic instance to bnx2fc hba instance * @@ -2311,6 +2419,7 @@ static struct fcoe_transport bnx2fc_transport = { .name = {"bnx2fc"}, .attached = false, .list = LIST_HEAD_INIT(bnx2fc_transport.list), + .alloc = bnx2fc_ctlr_alloc, .match = bnx2fc_match, .create = bnx2fc_create, .destroy = bnx2fc_destroy, @@ -2555,6 +2664,7 @@ module_init(bnx2fc_mod_init); module_exit(bnx2fc_mod_exit); static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ = { + .set_fcoe_ctlr_enabled = bnx2fc_ctlr_enabled, .get_fcoe_ctlr_link_fail = bnx2fc_ctlr_get_lesb, .get_fcoe_ctlr_vlink_fail = bnx2fc_ctlr_get_lesb, .get_fcoe_ctlr_miss_fka = bnx2fc_ctlr_get_lesb, From 8e6c5363dc52afbc60011c2c079bf4c4d26b1272 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Tue, 4 Dec 2012 02:14:53 +0000 Subject: [PATCH 0183/4607] libfc, libfcoe, fcoe: Convert debug_logging macros to pr_info Convert libfc, libfcoe and fcoe's debug_logging macros to use pr_info() instead of printk(KERN_INFO, ...). checkpatch.pl now complains about this, so convert libfcoe to preferred method. Signed-off-by: Robert Love Tested-by: Marcus Dennis --- drivers/scsi/fcoe/fcoe.h | 6 +++--- drivers/scsi/fcoe/libfcoe.h | 9 ++++----- drivers/scsi/libfc/fc_libfc.h | 38 +++++++++++++++++------------------ 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index b42dc32cb5eb..2b53672bf932 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -55,12 +55,12 @@ do { \ #define FCOE_DBG(fmt, args...) \ FCOE_CHECK_LOGGING(FCOE_LOGGING, \ - printk(KERN_INFO "fcoe: " fmt, ##args);) + pr_info("fcoe: " fmt, ##args);) #define FCOE_NETDEV_DBG(netdev, fmt, args...) \ FCOE_CHECK_LOGGING(FCOE_NETDEV_LOGGING, \ - printk(KERN_INFO "fcoe: %s: " fmt, \ - netdev->name, ##args);) + pr_info("fcoe: %s: " fmt, \ + netdev->name, ##args);) /** * struct fcoe_interface - A FCoE interface diff --git a/drivers/scsi/fcoe/libfcoe.h b/drivers/scsi/fcoe/libfcoe.h index 63d8faedca9f..d3bb16d11401 100644 --- a/drivers/scsi/fcoe/libfcoe.h +++ b/drivers/scsi/fcoe/libfcoe.h @@ -17,17 +17,16 @@ do { \ #define LIBFCOE_DBG(fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING, \ - printk(KERN_INFO "libfcoe: " fmt, ##args);) + pr_info("libfcoe: " fmt, ##args);) #define LIBFCOE_FIP_DBG(fip, fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING, \ - printk(KERN_INFO "host%d: fip: " fmt, \ - (fip)->lp->host->host_no, ##args);) + pr_info("host%d: fip: " fmt, \ + (fip)->lp->host->host_no, ##args);) #define LIBFCOE_TRANSPORT_DBG(fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_TRANSPORT_LOGGING, \ - printk(KERN_INFO "%s: " fmt, \ - __func__, ##args);) + pr_info("%s: " fmt, __func__, ##args);) #define LIBFCOE_SYSFS_DBG(cdev, fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_SYSFS_LOGGING, \ diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h index c2830cc66d6a..b74189d89322 100644 --- a/drivers/scsi/libfc/fc_libfc.h +++ b/drivers/scsi/libfc/fc_libfc.h @@ -41,25 +41,25 @@ extern unsigned int fc_debug_logging; #define FC_LIBFC_DBG(fmt, args...) \ FC_CHECK_LOGGING(FC_LIBFC_LOGGING, \ - printk(KERN_INFO "libfc: " fmt, ##args)) + pr_info("libfc: " fmt, ##args)) #define FC_LPORT_DBG(lport, fmt, args...) \ FC_CHECK_LOGGING(FC_LPORT_LOGGING, \ - printk(KERN_INFO "host%u: lport %6.6x: " fmt, \ - (lport)->host->host_no, \ - (lport)->port_id, ##args)) + pr_info("host%u: lport %6.6x: " fmt, \ + (lport)->host->host_no, \ + (lport)->port_id, ##args)) -#define FC_DISC_DBG(disc, fmt, args...) \ - FC_CHECK_LOGGING(FC_DISC_LOGGING, \ - printk(KERN_INFO "host%u: disc: " fmt, \ - fc_disc_lport(disc)->host->host_no, \ - ##args)) +#define FC_DISC_DBG(disc, fmt, args...) \ + FC_CHECK_LOGGING(FC_DISC_LOGGING, \ + pr_info("host%u: disc: " fmt, \ + fc_disc_lport(disc)->host->host_no, \ + ##args)) #define FC_RPORT_ID_DBG(lport, port_id, fmt, args...) \ FC_CHECK_LOGGING(FC_RPORT_LOGGING, \ - printk(KERN_INFO "host%u: rport %6.6x: " fmt, \ - (lport)->host->host_no, \ - (port_id), ##args)) + pr_info("host%u: rport %6.6x: " fmt, \ + (lport)->host->host_no, \ + (port_id), ##args)) #define FC_RPORT_DBG(rdata, fmt, args...) \ FC_RPORT_ID_DBG((rdata)->local_port, (rdata)->ids.port_id, fmt, ##args) @@ -70,13 +70,13 @@ extern unsigned int fc_debug_logging; if ((pkt)->seq_ptr) { \ struct fc_exch *_ep = NULL; \ _ep = fc_seq_exch((pkt)->seq_ptr); \ - printk(KERN_INFO "host%u: fcp: %6.6x: " \ + pr_info("host%u: fcp: %6.6x: " \ "xid %04x-%04x: " fmt, \ (pkt)->lp->host->host_no, \ (pkt)->rport->port_id, \ (_ep)->oxid, (_ep)->rxid, ##args); \ } else { \ - printk(KERN_INFO "host%u: fcp: %6.6x: " fmt, \ + pr_info("host%u: fcp: %6.6x: " fmt, \ (pkt)->lp->host->host_no, \ (pkt)->rport->port_id, ##args); \ } \ @@ -84,14 +84,14 @@ extern unsigned int fc_debug_logging; #define FC_EXCH_DBG(exch, fmt, args...) \ FC_CHECK_LOGGING(FC_EXCH_LOGGING, \ - printk(KERN_INFO "host%u: xid %4x: " fmt, \ - (exch)->lp->host->host_no, \ - exch->xid, ##args)) + pr_info("host%u: xid %4x: " fmt, \ + (exch)->lp->host->host_no, \ + exch->xid, ##args)) #define FC_SCSI_DBG(lport, fmt, args...) \ FC_CHECK_LOGGING(FC_SCSI_LOGGING, \ - printk(KERN_INFO "host%u: scsi: " fmt, \ - (lport)->host->host_no, ##args)) + pr_info("host%u: scsi: " fmt, \ + (lport)->host->host_no, ##args)) /* * FC-4 Providers. From 8106fb4790c33547a034db53f7658bccd3cfbf6b Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:23:27 +0000 Subject: [PATCH 0184/4607] fcoe: prep work to start consolidate the usage of fcoe_netdev Currently, in the default kernel fcoe driver, it is needed to get to the underlying private per fcoe transport's private structure, e.g., fcoe_interface in fcoe.ko, and returns the associated netdev. The similar logic exists in other fcoe drivers, e.g., bnx2fc, so we add a function pointer into the common fcoe_port struct to allow individual fcoe transport implementaion (fcoe and bnx2fc) to get the corresponding netdev associated with a give lport. Then a inline fcoe_get_netdev() is added as part of libfcoe for all underlying fcoe transport drivers to use regardless of its individual fcoe transport driver, and also allows move more common code such as fcoe_link_speed_update or fcoe_ctlr_get_lesb to be in libfcoe, rather than specific to fcoe. This patch is a prep work that adds aforementioned fucntion pointer, and followed by the actual code changes to make use of it. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- include/scsi/libfcoe.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 6add37ac8609..52bba7138069 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -351,6 +351,7 @@ struct fcoe_port { struct timer_list timer; struct work_struct destroy_work; u8 data_src_addr[ETH_ALEN]; + struct net_device * (*get_netdev)(const struct fc_lport *lport); }; void fcoe_clean_pending_queue(struct fc_lport *); void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb); From 66524ec9d0aeaa8bc59077c7c5f78d09ec9eeb9d Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:23:43 +0000 Subject: [PATCH 0185/4607] fcoe: add support to the get_netdev() for fcoe_interface Adds support to fcoe_port's newly added get_netdev fucntion pointer. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/fcoe/fcoe.c | 1 + include/scsi/libfcoe.h | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 21927f7952d8..4cec9ddc03ba 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1118,6 +1118,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, port = lport_priv(lport); port->lport = lport; port->priv = fcoe; + port->get_netdev = fcoe_netdev; port->max_queue_depth = FCOE_MAX_QUEUE_DEPTH; port->min_queue_depth = FCOE_MIN_QUEUE_DEPTH; INIT_WORK(&port->destroy_work, fcoe_destroy_work); diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 52bba7138069..746bc587ae34 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -353,6 +353,18 @@ struct fcoe_port { u8 data_src_addr[ETH_ALEN]; struct net_device * (*get_netdev)(const struct fc_lport *lport); }; + +/** + * fcoe_get_netdev() - Return the net device associated with a local port + * @lport: The local port to get the net device from + */ +static inline struct net_device *fcoe_get_netdev(const struct fc_lport *lport) +{ + struct fcoe_port *port = ((struct fcoe_port *)lport_priv(lport)); + + return (port->get_netdev) ? port->get_netdev(lport) : NULL; +} + void fcoe_clean_pending_queue(struct fc_lport *); void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb); void fcoe_queue_timer(ulong lport); From 03702689fcc985e9cb45b57099ebd5066f674739 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:23:58 +0000 Subject: [PATCH 0186/4607] libfcoe, fcoe: move fcoe_link_speed_update() to libfcoe and export it With the previous patch, fcoe_link_speed_update() can be moved into libfcoe and exported to used by fcoe, bnx2fc, and etc. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/fcoe/fcoe.c | 35 ------------------------------ drivers/scsi/fcoe/fcoe_transport.c | 35 ++++++++++++++++++++++++++++++ include/scsi/libfcoe.h | 1 + 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 4cec9ddc03ba..64bb53156af0 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -82,7 +82,6 @@ static int fcoe_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); static int fcoe_percpu_receive_thread(void *); static void fcoe_percpu_clean(struct fc_lport *); -static int fcoe_link_speed_update(struct fc_lport *); static int fcoe_link_ok(struct fc_lport *); static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); @@ -2387,40 +2386,6 @@ static int fcoe_ctlr_alloc(struct net_device *netdev) FCOE_CREATE_LINK_DOWN); } -/** - * fcoe_link_speed_update() - Update the supported and actual link speeds - * @lport: The local port to update speeds for - * - * Returns: 0 if the ethtool query was successful - * -1 if the ethtool query failed - */ -static int fcoe_link_speed_update(struct fc_lport *lport) -{ - struct net_device *netdev = fcoe_netdev(lport); - struct ethtool_cmd ecmd; - - if (!__ethtool_get_settings(netdev, &ecmd)) { - lport->link_supported_speeds &= - ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); - if (ecmd.supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) - lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; - if (ecmd.supported & SUPPORTED_10000baseT_Full) - lport->link_supported_speeds |= - FC_PORTSPEED_10GBIT; - switch (ethtool_cmd_speed(&ecmd)) { - case SPEED_1000: - lport->link_speed = FC_PORTSPEED_1GBIT; - break; - case SPEED_10000: - lport->link_speed = FC_PORTSPEED_10GBIT; - break; - } - return 0; - } - return -1; -} - /** * fcoe_link_ok() - Check if the link is OK for a local port * @lport: The local port to check link on diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index 83e78f922054..3c6846f1268b 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -83,6 +83,41 @@ static struct notifier_block libfcoe_notifier = { .notifier_call = libfcoe_device_notification, }; +/** + * fcoe_link_speed_update() - Update the supported and actual link speeds + * @lport: The local port to update speeds for + * + * Returns: 0 if the ethtool query was successful + * -1 if the ethtool query failed + */ +int fcoe_link_speed_update(struct fc_lport *lport) +{ + struct net_device *netdev = fcoe_get_netdev(lport); + struct ethtool_cmd ecmd; + + if (!__ethtool_get_settings(netdev, &ecmd)) { + lport->link_supported_speeds &= + ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); + if (ecmd.supported & (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full)) + lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; + if (ecmd.supported & SUPPORTED_10000baseT_Full) + lport->link_supported_speeds |= + FC_PORTSPEED_10GBIT; + switch (ethtool_cmd_speed(&ecmd)) { + case SPEED_1000: + lport->link_speed = FC_PORTSPEED_1GBIT; + break; + case SPEED_10000: + lport->link_speed = FC_PORTSPEED_10GBIT; + break; + } + return 0; + } + return -1; +} +EXPORT_SYMBOL_GPL(fcoe_link_speed_update); + void __fcoe_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb, struct net_device *netdev) diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 746bc587ae34..6c59ba7af28a 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -260,6 +260,7 @@ void __fcoe_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb, struct net_device *netdev); void fcoe_wwn_to_str(u64 wwn, char *buf, int len); int fcoe_validate_vport_create(struct fc_vport *vport); +int fcoe_link_speed_update(struct fc_lport *); /** * is_fip_mode() - returns true if FIP mode selected. From 57c2728fa806aff08703e5739620454d723bc865 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:24:13 +0000 Subject: [PATCH 0187/4607] libfcoe, fcoe: consolidate the fcoe_ctlr_get_lesb/fcoe_get_lesb Similarly they can be moved into libfcoe instead of being private to fcoe now. Also add comments particularly on the term LESB to the corresponding function. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/fcoe/fcoe.c | 40 ---------------------- drivers/scsi/fcoe/fcoe_transport.c | 54 ++++++++++++++++++++++++++++++ include/scsi/libfcoe.h | 2 ++ 3 files changed, 56 insertions(+), 40 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 64bb53156af0..0b333681a645 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -130,8 +130,6 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *, void *, u32 timeout); static void fcoe_recv_frame(struct sk_buff *skb); -static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *); - /* notification function for packets from net device */ static struct notifier_block fcoe_notifier = { .notifier_call = fcoe_device_notification, @@ -155,7 +153,6 @@ static int fcoe_vport_create(struct fc_vport *, bool disabled); static int fcoe_vport_disable(struct fc_vport *, bool disable); static void fcoe_set_vport_symbolic_name(struct fc_vport *); static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *); -static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *); static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *); static struct fcoe_sysfs_function_template fcoe_sysfs_templ = { @@ -2858,43 +2855,6 @@ static void fcoe_set_vport_symbolic_name(struct fc_vport *vport) NULL, NULL, 3 * lport->r_a_tov); } -/** - * fcoe_get_lesb() - Fill the FCoE Link Error Status Block - * @lport: the local port - * @fc_lesb: the link error status block - */ -static void fcoe_get_lesb(struct fc_lport *lport, - struct fc_els_lesb *fc_lesb) -{ - struct net_device *netdev = fcoe_netdev(lport); - - __fcoe_get_lesb(lport, fc_lesb, netdev); -} - -static void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) -{ - struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); - struct net_device *netdev = fcoe_netdev(fip->lp); - struct fcoe_fc_els_lesb *fcoe_lesb; - struct fc_els_lesb fc_lesb; - - __fcoe_get_lesb(fip->lp, &fc_lesb, netdev); - fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb); - - ctlr_dev->lesb.lesb_link_fail = - ntohl(fcoe_lesb->lesb_link_fail); - ctlr_dev->lesb.lesb_vlink_fail = - ntohl(fcoe_lesb->lesb_vlink_fail); - ctlr_dev->lesb.lesb_miss_fka = - ntohl(fcoe_lesb->lesb_miss_fka); - ctlr_dev->lesb.lesb_symb_err = - ntohl(fcoe_lesb->lesb_symb_err); - ctlr_dev->lesb.lesb_err_block = - ntohl(fcoe_lesb->lesb_err_block); - ctlr_dev->lesb.lesb_fcs_error = - ntohl(fcoe_lesb->lesb_fcs_error); -} - static void fcoe_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev) { struct fcoe_ctlr_device *ctlr_dev = diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index 3c6846f1268b..dd1aeb45f4cd 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -118,6 +118,15 @@ int fcoe_link_speed_update(struct fc_lport *lport) } EXPORT_SYMBOL_GPL(fcoe_link_speed_update); +/** + * __fcoe_get_lesb() - Get the Link Error Status Block (LESB) for a given lport + * @lport: The local port to update speeds for + * @fc_lesb: Pointer to the LESB to be filled up + * @netdev: Pointer to the netdev that is associated with the lport + * + * Note, the Link Error Status Block (LESB) for FCoE is defined in FC-BB-6 + * Clause 7.11 in v1.04. + */ void __fcoe_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb, struct net_device *netdev) @@ -147,6 +156,51 @@ void __fcoe_get_lesb(struct fc_lport *lport, } EXPORT_SYMBOL_GPL(__fcoe_get_lesb); +/** + * fcoe_get_lesb() - Fill the FCoE Link Error Status Block + * @lport: the local port + * @fc_lesb: the link error status block + */ +void fcoe_get_lesb(struct fc_lport *lport, + struct fc_els_lesb *fc_lesb) +{ + struct net_device *netdev = fcoe_get_netdev(lport); + + __fcoe_get_lesb(lport, fc_lesb, netdev); +} +EXPORT_SYMBOL_GPL(fcoe_get_lesb); + +/** + * fcoe_ctlr_get_lesb() - Get the Link Error Status Block (LESB) for a given + * fcoe controller device + * @ctlr_dev: The given fcoe controller device + * + */ +void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) +{ + struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); + struct net_device *netdev = fcoe_get_netdev(fip->lp); + struct fcoe_fc_els_lesb *fcoe_lesb; + struct fc_els_lesb fc_lesb; + + __fcoe_get_lesb(fip->lp, &fc_lesb, netdev); + fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb); + + ctlr_dev->lesb.lesb_link_fail = + ntohl(fcoe_lesb->lesb_link_fail); + ctlr_dev->lesb.lesb_vlink_fail = + ntohl(fcoe_lesb->lesb_vlink_fail); + ctlr_dev->lesb.lesb_miss_fka = + ntohl(fcoe_lesb->lesb_miss_fka); + ctlr_dev->lesb.lesb_symb_err = + ntohl(fcoe_lesb->lesb_symb_err); + ctlr_dev->lesb.lesb_err_block = + ntohl(fcoe_lesb->lesb_err_block); + ctlr_dev->lesb.lesb_fcs_error = + ntohl(fcoe_lesb->lesb_fcs_error); +} +EXPORT_SYMBOL_GPL(fcoe_ctlr_get_lesb); + void fcoe_wwn_to_str(u64 wwn, char *buf, int len) { u8 wwpn[8]; diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index 6c59ba7af28a..4427393115ea 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -261,6 +261,8 @@ void __fcoe_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb, void fcoe_wwn_to_str(u64 wwn, char *buf, int len); int fcoe_validate_vport_create(struct fc_vport *vport); int fcoe_link_speed_update(struct fc_lport *); +void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *); +void fcoe_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev); /** * is_fip_mode() - returns true if FIP mode selected. From c3d7909b865274ffa74fa2715c6663caa51a80e1 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:24:29 +0000 Subject: [PATCH 0188/4607] bnx2fc: add support to get_netdev for bnx2f_interface Adds support to fcoe_port's newly added get_netdev fucntion pointer for bnx2fc. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 8d975ef4eb69..9b9ccc94e7ae 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -119,7 +119,7 @@ static inline struct net_device *bnx2fc_netdev(const struct fc_lport *lport) static void bnx2fc_get_lesb(struct fc_lport *lport, struct fc_els_lesb *fc_lesb) { - struct net_device *netdev = bnx2fc_netdev(lport); + struct net_device *netdev = fcoe_get_netdev(lport); __fcoe_get_lesb(lport, fc_lesb, netdev); } @@ -127,7 +127,7 @@ static void bnx2fc_get_lesb(struct fc_lport *lport, static void bnx2fc_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) { struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); - struct net_device *netdev = bnx2fc_netdev(fip->lp); + struct net_device *netdev = fcoe_get_netdev(fip->lp); struct fcoe_fc_els_lesb *fcoe_lesb; struct fc_els_lesb fc_lesb; @@ -1499,6 +1499,7 @@ static struct fc_lport *bnx2fc_if_create(struct bnx2fc_interface *interface, port = lport_priv(lport); port->lport = lport; port->priv = interface; + port->get_netdev = bnx2fc_netdev; INIT_WORK(&port->destroy_work, bnx2fc_destroy_work); /* Configure fcoe_port */ From 0e0f9cd6a80dc883dab4c82c17edc1abe485cbd9 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:24:44 +0000 Subject: [PATCH 0189/4607] bnx2fc: use fcoe_link_speed_update() from the exported symbol in libfcoe We have fcoe_link_speed_update() in libfcoe ready for use now, take out the bnx2fc version which is almost the same. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 33 ++----------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 9b9ccc94e7ae..a1c687c699ee 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -738,35 +738,6 @@ static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev) return 0; } -static void bnx2fc_link_speed_update(struct fc_lport *lport) -{ - struct fcoe_port *port = lport_priv(lport); - struct bnx2fc_interface *interface = port->priv; - struct net_device *netdev = interface->netdev; - struct ethtool_cmd ecmd; - - if (!__ethtool_get_settings(netdev, &ecmd)) { - lport->link_supported_speeds &= - ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); - if (ecmd.supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) - lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; - if (ecmd.supported & SUPPORTED_10000baseT_Full) - lport->link_supported_speeds |= FC_PORTSPEED_10GBIT; - - switch (ethtool_cmd_speed(&ecmd)) { - case SPEED_1000: - lport->link_speed = FC_PORTSPEED_1GBIT; - break; - case SPEED_2500: - lport->link_speed = FC_PORTSPEED_2GBIT; - break; - case SPEED_10000: - lport->link_speed = FC_PORTSPEED_10GBIT; - break; - } - } -} static int bnx2fc_link_ok(struct fc_lport *lport) { struct fcoe_port *port = lport_priv(lport); @@ -824,7 +795,7 @@ static int bnx2fc_net_config(struct fc_lport *lport, struct net_device *netdev) port->fcoe_pending_queue_active = 0; setup_timer(&port->timer, fcoe_queue_timer, (unsigned long) lport); - bnx2fc_link_speed_update(lport); + fcoe_link_speed_update(lport); if (!lport->vport) { if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN)) @@ -928,7 +899,7 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event, BNX2FC_HBA_DBG(lport, "netevent handler - event=%s %ld\n", interface->netdev->name, event); - bnx2fc_link_speed_update(lport); + fcoe_link_speed_update(lport); cdev = fcoe_ctlr_to_ctlr_dev(ctlr); From b8b3e697d15165090583bed850879b1f3b250db0 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Thu, 6 Dec 2012 06:24:59 +0000 Subject: [PATCH 0190/4607] bnx2fc: use fcoe_get_lesb/fcoe_ctlr_get_lesb() directly from libfcoe Drop the bnx2fc_xxx versions as they are basically the same. Signed-off-by: Yi Zou Cc: Bhanu Prakash Gollapudi Tested-by: Marcus Dennis Signed-off-by: Robert Love --- drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 53 ++++--------------------------- 1 file changed, 7 insertions(+), 46 deletions(-) diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index a1c687c699ee..4d5c72159346 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -93,7 +93,6 @@ static void bnx2fc_port_shutdown(struct fc_lport *lport); static void bnx2fc_stop(struct bnx2fc_interface *interface); static int __init bnx2fc_mod_init(void); static void __exit bnx2fc_mod_exit(void); -static void bnx2fc_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev); unsigned int bnx2fc_debug_level; module_param_named(debug_logging, bnx2fc_debug_level, int, S_IRUGO|S_IWUSR); @@ -111,44 +110,6 @@ static inline struct net_device *bnx2fc_netdev(const struct fc_lport *lport) ((struct fcoe_port *)lport_priv(lport))->priv)->netdev; } -/** - * bnx2fc_get_lesb() - Fill the FCoE Link Error Status Block - * @lport: the local port - * @fc_lesb: the link error status block - */ -static void bnx2fc_get_lesb(struct fc_lport *lport, - struct fc_els_lesb *fc_lesb) -{ - struct net_device *netdev = fcoe_get_netdev(lport); - - __fcoe_get_lesb(lport, fc_lesb, netdev); -} - -static void bnx2fc_ctlr_get_lesb(struct fcoe_ctlr_device *ctlr_dev) -{ - struct fcoe_ctlr *fip = fcoe_ctlr_device_priv(ctlr_dev); - struct net_device *netdev = fcoe_get_netdev(fip->lp); - struct fcoe_fc_els_lesb *fcoe_lesb; - struct fc_els_lesb fc_lesb; - - __fcoe_get_lesb(fip->lp, &fc_lesb, netdev); - fcoe_lesb = (struct fcoe_fc_els_lesb *)(&fc_lesb); - - ctlr_dev->lesb.lesb_link_fail = - ntohl(fcoe_lesb->lesb_link_fail); - ctlr_dev->lesb.lesb_vlink_fail = - ntohl(fcoe_lesb->lesb_vlink_fail); - ctlr_dev->lesb.lesb_miss_fka = - ntohl(fcoe_lesb->lesb_miss_fka); - ctlr_dev->lesb.lesb_symb_err = - ntohl(fcoe_lesb->lesb_symb_err); - ctlr_dev->lesb.lesb_err_block = - ntohl(fcoe_lesb->lesb_err_block); - ctlr_dev->lesb.lesb_fcs_error = - ntohl(fcoe_lesb->lesb_fcs_error); -} -EXPORT_SYMBOL(bnx2fc_ctlr_get_lesb); - static void bnx2fc_fcf_get_vlan_id(struct fcoe_fcf_device *fcf_dev) { struct fcoe_ctlr_device *ctlr_dev = @@ -2637,12 +2598,12 @@ module_exit(bnx2fc_mod_exit); static struct fcoe_sysfs_function_template bnx2fc_fcoe_sysfs_templ = { .set_fcoe_ctlr_enabled = bnx2fc_ctlr_enabled, - .get_fcoe_ctlr_link_fail = bnx2fc_ctlr_get_lesb, - .get_fcoe_ctlr_vlink_fail = bnx2fc_ctlr_get_lesb, - .get_fcoe_ctlr_miss_fka = bnx2fc_ctlr_get_lesb, - .get_fcoe_ctlr_symb_err = bnx2fc_ctlr_get_lesb, - .get_fcoe_ctlr_err_block = bnx2fc_ctlr_get_lesb, - .get_fcoe_ctlr_fcs_error = bnx2fc_ctlr_get_lesb, + .get_fcoe_ctlr_link_fail = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_vlink_fail = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_miss_fka = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_symb_err = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_err_block = fcoe_ctlr_get_lesb, + .get_fcoe_ctlr_fcs_error = fcoe_ctlr_get_lesb, .get_fcoe_fcf_selected = fcoe_fcf_get_selected, .get_fcoe_fcf_vlan_id = bnx2fc_fcf_get_vlan_id, @@ -2749,7 +2710,7 @@ static struct libfc_function_template bnx2fc_libfc_fcn_templ = { .elsct_send = bnx2fc_elsct_send, .fcp_abort_io = bnx2fc_abort_io, .fcp_cleanup = bnx2fc_cleanup, - .get_lesb = bnx2fc_get_lesb, + .get_lesb = fcoe_get_lesb, .rport_event_callback = bnx2fc_rport_event_handler, }; From 4f670ff8eb4cb3e9e6ae0c0c6976faa0a4503751 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 7 Dec 2012 14:10:14 +0000 Subject: [PATCH 0191/4607] debris left by "[SCSI] libfcoe: Remove mutex_trylock/restart_syscall checks" AFAICS, the situation for fcoe_transport_disable() seems to be the same as for fcoe_transport_enable(). IOW, shouldn't it have restart_syscall() removed as well? I don't see any in-tree ->disable() instances that could return -ERESTARTSYS, anyway... Signed-off-by: Al Viro Signed-off-by: Robert Love --- drivers/scsi/fcoe/fcoe_transport.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index dd1aeb45f4cd..f3a5a53e8631 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -962,11 +962,7 @@ out_putdev: dev_put(netdev); out_nodev: mutex_unlock(&ft_mutex); - - if (rc == -ERESTARTSYS) - return restart_syscall(); - else - return rc; + return rc; } /** From e11ae1a102b46f76441e328a2743ae5d6e201423 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Fri, 14 Dec 2012 15:23:16 +0200 Subject: [PATCH 0192/4607] KVM: remove unused variable. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/irq.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c index ebd98d0c4f6e..b111aee815f8 100644 --- a/arch/x86/kvm/irq.c +++ b/arch/x86/kvm/irq.c @@ -43,8 +43,6 @@ EXPORT_SYMBOL(kvm_cpu_has_pending_timer); */ int kvm_cpu_has_interrupt(struct kvm_vcpu *v) { - struct kvm_pic *s; - if (!irqchip_in_kernel(v->kvm)) return v->arch.interrupt.pending; @@ -60,8 +58,6 @@ EXPORT_SYMBOL_GPL(kvm_cpu_has_interrupt); */ int kvm_cpu_get_interrupt(struct kvm_vcpu *v) { - struct kvm_pic *s; - if (!irqchip_in_kernel(v->kvm)) return v->arch.interrupt.nr; From eb119bd612906519cacef2536a9a524c2da5f7fb Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 16 Dec 2012 12:43:36 +0000 Subject: [PATCH 0193/4607] drm/i915: Access to snooped system memory through the GTT is incoherent We ignore all the user requests to handle flushing to the GTT domain if the user requests such on a snoopable bo, and as such access through the GTT to such pages remains incoherent. The specs even warn that such behaviour is undefined - a strong reason never to do so. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6380c6083cb2..d15c86279d02 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1341,6 +1341,12 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) trace_i915_gem_object_fault(obj, page_offset, true, write); + /* Access to snoopable pages through the GTT is incoherent. */ + if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(dev)) { + ret = -EINVAL; + goto unlock; + } + /* Now bind it into the GTT if needed */ ret = i915_gem_object_pin(obj, 0, true, false); if (ret) From 88afe715dd5469bc24ca7a19ac62dd3c241cab48 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 16 Dec 2012 12:15:41 +0000 Subject: [PATCH 0194/4607] drm/i915: Clear the stolen fb before enabling As the stolen memory region will contain the contents of whatever was last there, it invariably contains garbage. To be consistent with the shmemfs backed fb and the expectations of the fb layer, we need to clear the fb prior to installing it as an fbcon. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=58111 Signed-off-by: Chris Wilson [danvet: Fixup sparse __iomem confusion reported by Wu Fengguang.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_fb.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 822896782cd0..71d55801c0d9 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -155,6 +155,13 @@ static int intelfb_create(struct intel_fbdev *ifbdev, drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height); + /* If the object is shmemfs backed, it will have given us zeroed pages. + * If the object is stolen however, it will be full of whatever + * garbage was left in there. + */ + if (ifbdev->ifb.obj->stolen) + memset_io(info->screen_base, 0, info->screen_size); + /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x, bo %p\n", From 681e5811f88b7ecca77734421adafb62f19563be Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 6 Dec 2012 11:12:38 -0200 Subject: [PATCH 0195/4607] drm/i915: check for the PCH when setting pch_transcoder Don't check the CPU, it doesn't have any PCH transcoder. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7f1b07c9061d..fa73b4ad88bb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1789,7 +1789,7 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, int reg; u32 val; - if (IS_HASWELL(dev_priv->dev)) + if (HAS_PCH_LPT(dev_priv->dev)) pch_transcoder = TRANSCODER_A; else pch_transcoder = pipe; From a0e63c22ee6daae8fda98e1c6eaff5f5f1e2ba31 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 6 Dec 2012 11:12:39 -0200 Subject: [PATCH 0196/4607] drm/i915: remove leftover display.update_wm assignment This was moved to intel_init_pm. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index fa73b4ad88bb..f02ec7b7d6eb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8284,8 +8284,7 @@ static void intel_init_display(struct drm_device *dev) } else if (IS_HASWELL(dev)) { dev_priv->display.fdi_link_train = hsw_fdi_link_train; dev_priv->display.write_eld = haswell_write_eld; - } else - dev_priv->display.update_wm = NULL; + } } else if (IS_G4X(dev)) { dev_priv->display.write_eld = g4x_write_eld; } From f0a3424e964ec8e934497a0724cd1d58690fd9ab Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 6 Dec 2012 16:51:50 -0200 Subject: [PATCH 0197/4607] drm/i915: add intel_dp_set_signal_levels So we can de-duplicate code that's inside intel_dp_start_link_train and intel_dp_complete_link_train. V2: Rebase since patch 3/5 was discarded. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 67 ++++++++++++++++----------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 185bf4e7614e..2d3b26832397 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1587,7 +1587,7 @@ intel_get_adjust_train(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_ST } static uint32_t -intel_dp_signal_levels(uint8_t train_set) +intel_gen4_signal_levels(uint8_t train_set) { uint32_t signal_levels = 0; @@ -1685,7 +1685,7 @@ intel_gen7_edp_signal_levels(uint8_t train_set) /* Gen7.5's (HSW) DP voltage swing and pre-emphasis control */ static uint32_t -intel_dp_signal_levels_hsw(uint8_t train_set) +intel_hsw_signal_levels(uint8_t train_set) { int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK | DP_TRAIN_PRE_EMPHASIS_MASK); @@ -1717,6 +1717,34 @@ intel_dp_signal_levels_hsw(uint8_t train_set) } } +/* Properly updates "DP" with the correct signal levels. */ +static void +intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + uint32_t signal_levels, mask; + uint8_t train_set = intel_dp->train_set[0]; + + if (IS_HASWELL(dev)) { + signal_levels = intel_hsw_signal_levels(train_set); + mask = DDI_BUF_EMP_MASK; + } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { + signal_levels = intel_gen7_edp_signal_levels(train_set); + mask = EDP_LINK_TRAIN_VOL_EMP_MASK_IVB; + } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { + signal_levels = intel_gen6_edp_signal_levels(train_set); + mask = EDP_LINK_TRAIN_VOL_EMP_MASK_SNB; + } else { + signal_levels = intel_gen4_signal_levels(train_set); + mask = DP_VOLTAGE_MASK | DP_PRE_EMPHASIS_MASK; + } + + DRM_DEBUG_KMS("Using signal levels %08x\n", signal_levels); + + *DP = (*DP & ~mask) | signal_levels; +} + static bool intel_dp_set_link_train(struct intel_dp *intel_dp, uint32_t dp_reg_value, @@ -1853,24 +1881,8 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ uint8_t link_status[DP_LINK_STATUS_SIZE]; - uint32_t signal_levels; - if (IS_HASWELL(dev)) { - signal_levels = intel_dp_signal_levels_hsw( - intel_dp->train_set[0]); - DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels; - } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { - signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels; - } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { - signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; - } else { - signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; - } - DRM_DEBUG_KMS("training pattern 1 signal levels %08x\n", - signal_levels); + intel_dp_set_signal_levels(intel_dp, &DP); /* Set training pattern 1 */ if (!intel_dp_set_link_train(intel_dp, DP, @@ -1926,7 +1938,6 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) void intel_dp_complete_link_train(struct intel_dp *intel_dp) { - struct drm_device *dev = intel_dp_to_dev(intel_dp); bool channel_eq = false; int tries, cr_tries; uint32_t DP = intel_dp->DP; @@ -1936,8 +1947,6 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) cr_tries = 0; channel_eq = false; for (;;) { - /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ - uint32_t signal_levels; uint8_t link_status[DP_LINK_STATUS_SIZE]; if (cr_tries > 5) { @@ -1946,19 +1955,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) break; } - if (IS_HASWELL(dev)) { - signal_levels = intel_dp_signal_levels_hsw(intel_dp->train_set[0]); - DP = (DP & ~DDI_BUF_EMP_MASK) | signal_levels; - } else if (IS_GEN7(dev) && is_cpu_edp(intel_dp) && !IS_VALLEYVIEW(dev)) { - signal_levels = intel_gen7_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_IVB) | signal_levels; - } else if (IS_GEN6(dev) && is_cpu_edp(intel_dp)) { - signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels; - } else { - signal_levels = intel_dp_signal_levels(intel_dp->train_set[0]); - DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; - } + intel_dp_set_signal_levels(intel_dp, &DP); /* channel eq pattern */ if (!intel_dp_set_link_train(intel_dp, DP, From dfd07d72cf70cc8845006b698b55369f5d9c2733 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 17 Dec 2012 11:21:38 +0100 Subject: [PATCH 0198/4607] drm/i915: clean up PIPECONF bpc #defines Ilk+ somehow used #defines in near the PIPESTAT definitions, which decently confused me. Earlier platforms called it BPP instead of BPC. Clean this all up. Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 15 +++++---------- drivers/gpu/drm/i915/intel_display.c | 28 ++++++++++++++-------------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f83480403ccc..5b3020f836b9 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2648,11 +2648,11 @@ #define PIPECONF_INTERLACED_DBL_ILK (4 << 21) /* ilk/snb only */ #define PIPECONF_PFIT_PF_INTERLACED_DBL_ILK (5 << 21) /* ilk/snb only */ #define PIPECONF_CXSR_DOWNCLOCK (1<<16) -#define PIPECONF_BPP_MASK (0x000000e0) -#define PIPECONF_BPP_8 (0<<5) -#define PIPECONF_BPP_10 (1<<5) -#define PIPECONF_BPP_6 (2<<5) -#define PIPECONF_BPP_12 (3<<5) +#define PIPECONF_BPC_MASK (0x7 << 5) +#define PIPECONF_8BPC (0<<5) +#define PIPECONF_10BPC (1<<5) +#define PIPECONF_6BPC (2<<5) +#define PIPECONF_12BPC (3<<5) #define PIPECONF_DITHER_EN (1<<4) #define PIPECONF_DITHER_TYPE_MASK (0x0000000c) #define PIPECONF_DITHER_TYPE_SP (0<<2) @@ -2696,11 +2696,6 @@ #define PIPE_START_VBLANK_INTERRUPT_STATUS (1UL<<2) /* 965 or later */ #define PIPE_VBLANK_INTERRUPT_STATUS (1UL<<1) #define PIPE_OVERLAY_UPDATED_STATUS (1UL<<0) -#define PIPE_BPC_MASK (7 << 5) /* Ironlake */ -#define PIPE_8BPC (0 << 5) -#define PIPE_10BPC (1 << 5) -#define PIPE_6BPC (2 << 5) -#define PIPE_12BPC (3 << 5) #define PIPESRC(pipe) _PIPE(pipe, _PIPEASRC, _PIPEBSRC) #define PIPECONF(tran) _TRANSCODER(tran, _PIPEACONF, _PIPEBCONF) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f02ec7b7d6eb..1bb68169aaf0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1669,8 +1669,8 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv, * make the BPC in transcoder be consistent with * that in pipeconf reg. */ - val &= ~PIPE_BPC_MASK; - val |= pipeconf_val & PIPE_BPC_MASK; + val &= ~PIPECONF_BPC_MASK; + val |= pipeconf_val & PIPECONF_BPC_MASK; } val &= ~TRANS_INTERLACE_MASK; @@ -2764,7 +2764,7 @@ static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) temp = I915_READ(reg); temp &= ~((0x7 << 19) | (0x7 << 16)); temp |= (intel_crtc->fdi_lanes - 1) << 19; - temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp | FDI_RX_PLL_ENABLE); POSTING_READ(reg); @@ -2845,7 +2845,7 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc) reg = FDI_RX_CTL(pipe); temp = I915_READ(reg); temp &= ~(0x7 << 16); - temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp & ~FDI_RX_ENABLE); POSTING_READ(reg); @@ -2876,7 +2876,7 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc) } /* BPC in FDI rx is consistent with that in PIPECONF */ temp &= ~(0x07 << 16); - temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + temp |= (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) << 11; I915_WRITE(reg, temp); POSTING_READ(reg); @@ -3115,7 +3115,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) if (HAS_PCH_CPT(dev) && (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) || intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))) { - u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) >> 5; + u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPECONF_BPC_MASK) >> 5; reg = TRANS_DP_CTL(pipe); temp = I915_READ(reg); temp &= ~(TRANS_DP_PORT_SEL_MASK | @@ -4686,10 +4686,10 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, } /* default to 8bpc */ - pipeconf &= ~(PIPECONF_BPP_MASK | PIPECONF_DITHER_EN); + pipeconf &= ~(PIPECONF_BPC_MASK | PIPECONF_DITHER_EN); if (is_dp) { if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { - pipeconf |= PIPECONF_BPP_6 | + pipeconf |= PIPECONF_6BPC | PIPECONF_DITHER_EN | PIPECONF_DITHER_TYPE_SP; } @@ -4697,7 +4697,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, if (IS_VALLEYVIEW(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) { if (adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC) { - pipeconf |= PIPECONF_BPP_6 | + pipeconf |= PIPECONF_6BPC | PIPECONF_ENABLE | I965_PIPECONF_ACTIVE; } @@ -4907,19 +4907,19 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, val = I915_READ(PIPECONF(pipe)); - val &= ~PIPE_BPC_MASK; + val &= ~PIPECONF_BPC_MASK; switch (intel_crtc->bpp) { case 18: - val |= PIPE_6BPC; + val |= PIPECONF_6BPC; break; case 24: - val |= PIPE_8BPC; + val |= PIPECONF_8BPC; break; case 30: - val |= PIPE_10BPC; + val |= PIPECONF_10BPC; break; case 36: - val |= PIPE_12BPC; + val |= PIPECONF_12BPC; break; default: /* Case prevented by intel_choose_pipe_bpp_dither. */ From ffc809886b6cbeb0964130bdbcf62504ceeec18e Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 28 Nov 2012 01:17:50 -0300 Subject: [PATCH 0199/4607] [media] au0828: add missing model 72281, usb id 2040:7270 to the model matrix Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-cards.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 0cb7c28dcb17..d12bdd3589f5 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -170,6 +170,7 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ case 72261: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; @@ -333,6 +334,8 @@ struct usb_device_id au0828_usb_id_table[] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { USB_DEVICE(0x2040, 0x7213), .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7270), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, { }, }; From c70ffd5968476ffe9fe2a8bfdc88956ecef92d2a Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 28 Nov 2012 11:46:24 -0300 Subject: [PATCH 0200/4607] [media] au0828: update model matrix entries for 72261, 72271 & 72281 Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-cards.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index d12bdd3589f5..cf309d8102c0 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -169,8 +169,9 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ - case 72261: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ - case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ + case 72261: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ + case 72271: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ + case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; From 8a4e786660f512b029b56d94d1b8f0201e67aab3 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 4 Dec 2012 11:30:00 -0300 Subject: [PATCH 0201/4607] [media] au0828: remove forced dependency of VIDEO_AU0828 on VIDEO_V4L2 This patch removes the dependendency of VIDEO_AU0828 on VIDEO_V4L2 by creating a new Kconfig option, VIDEO_AU0828_V4L2, which enables analog video capture support and depends on VIDEO_V4L2 itself. With VIDEO_AU0828_V4L2 disabled, the driver will only support digital television and will not depend on the v4l2-core. With VIDEO_AU0828_V4L2 enabled, the driver will be built with the analog v4l2 support included. By default, the VIDEO_AU0828_V4L2 option will be set to Y, so as to preserve the original behavior. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/Kconfig | 2 +- drivers/media/usb/au0828/Kconfig | 17 ++++++++++++++--- drivers/media/usb/au0828/Makefile | 6 +++++- drivers/media/usb/au0828/au0828-cards.c | 4 ++++ drivers/media/usb/au0828/au0828-core.c | 13 ++++++++++++- drivers/media/usb/au0828/au0828-i2c.c | 4 ++++ drivers/media/usb/au0828/au0828.h | 2 ++ 7 files changed, 42 insertions(+), 6 deletions(-) diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 6746994d03fe..0a7d520636a9 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -21,7 +21,6 @@ endif if MEDIA_ANALOG_TV_SUPPORT comment "Analog TV USB devices" -source "drivers/media/usb/au0828/Kconfig" source "drivers/media/usb/pvrusb2/Kconfig" source "drivers/media/usb/hdpvr/Kconfig" source "drivers/media/usb/tlg2300/Kconfig" @@ -31,6 +30,7 @@ endif if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT) comment "Analog/digital TV USB devices" +source "drivers/media/usb/au0828/Kconfig" source "drivers/media/usb/cx231xx/Kconfig" source "drivers/media/usb/tm6000/Kconfig" endif diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 1766c0ce93be..953a37c613b1 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -1,17 +1,28 @@ config VIDEO_AU0828 tristate "Auvitek AU0828 support" - depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 + depends on I2C && INPUT && DVB_CORE && USB select I2C_ALGOBIT select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT - select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ---help--- - This is a video4linux driver for Auvitek's USB device. + This is a hybrid analog/digital tv capture driver for + Auvitek's AU0828 USB device. To compile this driver as a module, choose M here: the module will be called au0828 + +config VIDEO_AU0828_V4L2 + bool "Auvitek AU0828 v4l2 analog video support" + depends on VIDEO_AU0828 && VIDEO_V4L2 + select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT + default y + ---help--- + This is a video4linux driver for Auvitek's USB device. + + Choose Y here to include support for v4l2 analog video + capture within the au0828 driver. diff --git a/drivers/media/usb/au0828/Makefile b/drivers/media/usb/au0828/Makefile index 98cc20cc0ffb..be3bdf698022 100644 --- a/drivers/media/usb/au0828/Makefile +++ b/drivers/media/usb/au0828/Makefile @@ -1,4 +1,8 @@ -au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o au0828-vbi.o +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o + +ifeq ($(CONFIG_VIDEO_AU0828_V4L2),y) + au0828-objs += au0828-video.o au0828-vbi.o +endif obj-$(CONFIG_VIDEO_AU0828) += au0828.o diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index cf309d8102c0..7b5b7420066d 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -188,9 +188,11 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) void au0828_card_setup(struct au0828_dev *dev) { static u8 eeprom[256]; +#ifdef CONFIG_VIDEO_AU0828_V4L2 struct tuner_setup tun_setup; struct v4l2_subdev *sd; unsigned int mode_mask = T_ANALOG_TV; +#endif dprintk(1, "%s()\n", __func__); @@ -211,6 +213,7 @@ void au0828_card_setup(struct au0828_dev *dev) break; } +#ifdef CONFIG_VIDEO_AU0828_V4L2 if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { /* Load the analog demodulator driver (note this would need to be abstracted out if we ever need to support a different @@ -236,6 +239,7 @@ void au0828_card_setup(struct au0828_dev *dev) v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); } +#endif } /* diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c index 745a80a798c8..1e6f40ef1c6b 100644 --- a/drivers/media/usb/au0828/au0828-core.c +++ b/drivers/media/usb/au0828/au0828-core.c @@ -134,13 +134,17 @@ static void au0828_usb_disconnect(struct usb_interface *interface) /* Digital TV */ au0828_dvb_unregister(dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) au0828_analog_unregister(dev); +#endif /* I2C */ au0828_i2c_unregister(dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 v4l2_device_unregister(&dev->v4l2_dev); +#endif usb_set_intfdata(interface, NULL); @@ -155,7 +159,10 @@ static void au0828_usb_disconnect(struct usb_interface *interface) static int au0828_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { - int ifnum, retval; + int ifnum; +#ifdef CONFIG_VIDEO_AU0828_V4L2 + int retval; +#endif struct au0828_dev *dev; struct usb_device *usbdev = interface_to_usbdev(interface); @@ -194,6 +201,7 @@ static int au0828_usb_probe(struct usb_interface *interface, dev->usbdev = usbdev; dev->boardnr = id->driver_info; +#ifdef CONFIG_VIDEO_AU0828_V4L2 /* Create the v4l2_device */ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); if (retval) { @@ -203,6 +211,7 @@ static int au0828_usb_probe(struct usb_interface *interface, kfree(dev); return -EIO; } +#endif /* Power Up the bridge */ au0828_write(dev, REG_600, 1 << 4); @@ -216,9 +225,11 @@ static int au0828_usb_probe(struct usb_interface *interface, /* Setup */ au0828_card_setup(dev); +#ifdef CONFIG_VIDEO_AU0828_V4L2 /* Analog TV */ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) au0828_analog_register(dev, interface); +#endif /* Digital TV */ au0828_dvb_register(dev); diff --git a/drivers/media/usb/au0828/au0828-i2c.c b/drivers/media/usb/au0828/au0828-i2c.c index 4ded17fe1957..20d69b565255 100644 --- a/drivers/media/usb/au0828/au0828-i2c.c +++ b/drivers/media/usb/au0828/au0828-i2c.c @@ -378,7 +378,11 @@ int au0828_i2c_register(struct au0828_dev *dev) dev->i2c_adap.algo = &dev->i2c_algo; dev->i2c_adap.algo_data = dev; +#ifdef CONFIG_VIDEO_AU0828_V4L2 i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); +#else + i2c_set_adapdata(&dev->i2c_adap, dev); +#endif i2c_add_adapter(&dev->i2c_adap); dev->i2c_client.adapter = &dev->i2c_adap; diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 66a56ef7bbe4..e579ff69ca4a 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -199,8 +199,10 @@ struct au0828_dev { struct au0828_dvb dvb; struct work_struct restart_streaming; +#ifdef CONFIG_VIDEO_AU0828_V4L2 /* Analog */ struct v4l2_device v4l2_dev; +#endif int users; unsigned int resources; /* resources in use */ struct video_device *vdev; From 5b7d8de7d2328f7b25fe4645eafee7e48f9b7df3 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 4 Dec 2012 12:46:38 -0300 Subject: [PATCH 0202/4607] [media] au0828: break au0828_card_setup() down into smaller functions Pull the analog frontend setup code out of au0828_card_setup into its own seperate function, au0828_card_analog_fe_setup(). Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-cards.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 7b5b7420066d..88e35dfa33d4 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -185,14 +185,11 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) __func__, tv.model); } +void au0828_card_analog_fe_setup(struct au0828_dev *dev); + void au0828_card_setup(struct au0828_dev *dev) { static u8 eeprom[256]; -#ifdef CONFIG_VIDEO_AU0828_V4L2 - struct tuner_setup tun_setup; - struct v4l2_subdev *sd; - unsigned int mode_mask = T_ANALOG_TV; -#endif dprintk(1, "%s()\n", __func__); @@ -213,7 +210,16 @@ void au0828_card_setup(struct au0828_dev *dev) break; } + au0828_card_analog_fe_setup(dev); +} + +void au0828_card_analog_fe_setup(struct au0828_dev *dev) +{ #ifdef CONFIG_VIDEO_AU0828_V4L2 + struct tuner_setup tun_setup; + struct v4l2_subdev *sd; + unsigned int mode_mask = T_ANALOG_TV; + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { /* Load the analog demodulator driver (note this would need to be abstracted out if we ever need to support a different From d4b06c2d4cce466e2d62163c0a954e1b2ce96f8b Mon Sep 17 00:00:00 2001 From: Nickolai Zeldovich Date: Sat, 15 Dec 2012 06:34:37 -0500 Subject: [PATCH 0203/4607] kvm: fix i8254 counter 0 wraparound The kvm i8254 emulation for counter 0 (but not for counters 1 and 2) has at least two bugs in mode 0: 1. The OUT bit, computed by pit_get_out(), is never set high. 2. The counter value, computed by pit_get_count(), wraps back around to the initial counter value, rather than wrapping back to 0xFFFF (which is the behavior described in the comment in __kpit_elapsed, the behavior implemented by qemu, and the behavior observed on AMD hardware). The bug stems from __kpit_elapsed computing the elapsed time mod the initial counter value (stored as nanoseconds in ps->period). This is both unnecessary (none of the callers of kpit_elapsed expect the value to be at most the initial counter value) and incorrect (it causes pit_get_count to appear to wrap around to the initial counter value rather than 0xFFFF). Removing this mod from __kpit_elapsed fixes both of the above bugs. Signed-off-by: Nickolai Zeldovich Reviewed-by: Marcelo Tosatti Signed-off-by: Gleb Natapov --- arch/x86/kvm/i8254.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c index 11300d2fa714..c1d30b2fc9bb 100644 --- a/arch/x86/kvm/i8254.c +++ b/arch/x86/kvm/i8254.c @@ -122,7 +122,6 @@ static s64 __kpit_elapsed(struct kvm *kvm) */ remaining = hrtimer_get_remaining(&ps->timer); elapsed = ps->period - ktime_to_ns(remaining); - elapsed = mod_64(elapsed, ps->period); return elapsed; } From 55c171a6d90dc0574021f9c836127cfd1a7d2e30 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 14 Dec 2012 17:02:16 +0100 Subject: [PATCH 0204/4607] KVM: s390: Handle hosts not supporting s390-virtio. Running under a kvm host does not necessarily imply the presence of a page mapped above the main memory with the virtio information; however, the code includes a hard coded access to that page. Instead, check for the presence of the page and exit gracefully before we hit an addressing exception if it does not exist. Reviewed-by: Marcelo Tosatti Reviewed-by: Alexander Graf Signed-off-by: Cornelia Huck cc: stable@vger.kernel.org Signed-off-by: Gleb Natapov --- drivers/s390/kvm/kvm_virtio.c | 38 +++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 7dabef624da3..b846b6c4130a 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -421,6 +421,26 @@ static void kvm_extint_handler(struct ext_code ext_code, } } +/* + * For s390-virtio, we expect a page above main storage containing + * the virtio configuration. Try to actually load from this area + * in order to figure out if the host provides this page. + */ +static int __init test_devices_support(unsigned long addr) +{ + int ret = -EIO; + + asm volatile( + "0: lura 0,%1\n" + "1: xgr %0,%0\n" + "2:\n" + EX_TABLE(0b,2b) + EX_TABLE(1b,2b) + : "+d" (ret) + : "a" (addr) + : "0", "cc"); + return ret; +} /* * Init function for virtio * devices are in a single page above top of "normal" mem @@ -432,21 +452,23 @@ static int __init kvm_devices_init(void) if (!MACHINE_IS_KVM) return -ENODEV; + if (test_devices_support(real_memory_size) < 0) + return -ENODEV; + + rc = vmem_add_mapping(real_memory_size, PAGE_SIZE); + if (rc) + return rc; + + kvm_devices = (void *) real_memory_size; + kvm_root = root_device_register("kvm_s390"); if (IS_ERR(kvm_root)) { rc = PTR_ERR(kvm_root); printk(KERN_ERR "Could not register kvm_s390 root device"); + vmem_remove_mapping(real_memory_size, PAGE_SIZE); return rc; } - rc = vmem_add_mapping(real_memory_size, PAGE_SIZE); - if (rc) { - root_device_unregister(kvm_root); - return rc; - } - - kvm_devices = (void *) real_memory_size; - INIT_WORK(&hotplug_work, hotplug_devices); service_subclass_irq_register(); From 0abbe448eddb2263db3fb776757f480b34accd88 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 14 Dec 2012 17:02:17 +0100 Subject: [PATCH 0205/4607] s390/ccwdev: Include asm/schid.h. Get the definition of struct subchannel_id. Reviewed-by: Alexander Graf Signed-off-by: Cornelia Huck Signed-off-by: Gleb Natapov --- arch/s390/include/asm/ccwdev.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h index 6d1f3573f0df..e6061617a50b 100644 --- a/arch/s390/include/asm/ccwdev.h +++ b/arch/s390/include/asm/ccwdev.h @@ -12,15 +12,13 @@ #include #include #include +#include /* structs from asm/cio.h */ struct irb; struct ccw1; struct ccw_dev_id; -/* from asm/schid.h */ -struct subchannel_id; - /* simplified initializers for struct ccw_device: * CCW_DEVICE and CCW_DEVICE_DEVTYPE initialize one * entry in your MODULE_DEVICE_TABLE and set the match_flag correctly */ From 7e64e0597fd67c975bfa3e76401bfbcdd5ae0ff9 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 14 Dec 2012 17:02:18 +0100 Subject: [PATCH 0206/4607] KVM: s390: Add a channel I/O based virtio transport driver. Add a driver for kvm guests that matches virtual ccw devices provided by the host as virtio bridge devices. These virtio-ccw devices use a special set of channel commands in order to perform virtio functions. Reviewed-by: Marcelo Tosatti Reviewed-by: Alexander Graf Signed-off-by: Cornelia Huck Signed-off-by: Gleb Natapov --- arch/s390/include/asm/irq.h | 1 + arch/s390/kernel/irq.c | 1 + drivers/s390/kvm/Makefile | 2 +- drivers/s390/kvm/virtio_ccw.c | 853 ++++++++++++++++++++++++++++++++++ 4 files changed, 856 insertions(+), 1 deletion(-) create mode 100644 drivers/s390/kvm/virtio_ccw.c diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index e6972f85d2b0..aa6d0d74cec9 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -35,6 +35,7 @@ enum interruption_class { IOINT_CSC, IOINT_PCI, IOINT_MSI, + IOINT_VIR, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index bf24293970ce..a9806ea3ebd7 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -60,6 +60,7 @@ static const struct irq_class intrclass_names[] = { [IOINT_CSC] = {.name = "CSC", .desc = "[I/O] CHSC Subchannel"}, [IOINT_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" }, [IOINT_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" }, + [IOINT_VIR] = {.name = "VIR", .desc = "[I/O] Virtual I/O Devices"}, [NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"}, }; diff --git a/drivers/s390/kvm/Makefile b/drivers/s390/kvm/Makefile index 0815690ac1e0..241891a57caf 100644 --- a/drivers/s390/kvm/Makefile +++ b/drivers/s390/kvm/Makefile @@ -6,4 +6,4 @@ # it under the terms of the GNU General Public License (version 2 only) # as published by the Free Software Foundation. -obj-$(CONFIG_S390_GUEST) += kvm_virtio.o +obj-$(CONFIG_S390_GUEST) += kvm_virtio.o virtio_ccw.o diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c new file mode 100644 index 000000000000..1a5aff31d752 --- /dev/null +++ b/drivers/s390/kvm/virtio_ccw.c @@ -0,0 +1,853 @@ +/* + * ccw based virtio transport + * + * Copyright IBM Corp. 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + * Author(s): Cornelia Huck + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * virtio related functions + */ + +struct vq_config_block { + __u16 index; + __u16 num; +} __packed; + +#define VIRTIO_CCW_CONFIG_SIZE 0x100 +/* same as PCI config space size, should be enough for all drivers */ + +struct virtio_ccw_device { + struct virtio_device vdev; + __u8 status; + __u8 config[VIRTIO_CCW_CONFIG_SIZE]; + struct ccw_device *cdev; + struct ccw1 *ccw; + __u32 area; + __u32 curr_io; + int err; + wait_queue_head_t wait_q; + spinlock_t lock; + struct list_head virtqueues; + unsigned long indicators; + unsigned long indicators2; + struct vq_config_block *config_block; +}; + +struct vq_info_block { + __u64 queue; + __u32 align; + __u16 index; + __u16 num; +} __packed; + +struct virtio_feature_desc { + __u32 features; + __u8 index; +} __packed; + +struct virtio_ccw_vq_info { + struct virtqueue *vq; + int num; + void *queue; + struct vq_info_block *info_block; + struct list_head node; +}; + +#define KVM_VIRTIO_CCW_RING_ALIGN 4096 + +#define KVM_S390_VIRTIO_CCW_NOTIFY 3 + +#define CCW_CMD_SET_VQ 0x13 +#define CCW_CMD_VDEV_RESET 0x33 +#define CCW_CMD_SET_IND 0x43 +#define CCW_CMD_SET_CONF_IND 0x53 +#define CCW_CMD_READ_FEAT 0x12 +#define CCW_CMD_WRITE_FEAT 0x11 +#define CCW_CMD_READ_CONF 0x22 +#define CCW_CMD_WRITE_CONF 0x21 +#define CCW_CMD_WRITE_STATUS 0x31 +#define CCW_CMD_READ_VQ_CONF 0x32 + +#define VIRTIO_CCW_DOING_SET_VQ 0x00010000 +#define VIRTIO_CCW_DOING_RESET 0x00040000 +#define VIRTIO_CCW_DOING_READ_FEAT 0x00080000 +#define VIRTIO_CCW_DOING_WRITE_FEAT 0x00100000 +#define VIRTIO_CCW_DOING_READ_CONFIG 0x00200000 +#define VIRTIO_CCW_DOING_WRITE_CONFIG 0x00400000 +#define VIRTIO_CCW_DOING_WRITE_STATUS 0x00800000 +#define VIRTIO_CCW_DOING_SET_IND 0x01000000 +#define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000 +#define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000 +#define VIRTIO_CCW_INTPARM_MASK 0xffff0000 + +static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) +{ + return container_of(vdev, struct virtio_ccw_device, vdev); +} + +static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) +{ + unsigned long flags; + __u32 ret; + + spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); + if (vcdev->err) + ret = 0; + else + ret = vcdev->curr_io & flag; + spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); + return ret; +} + +static int ccw_io_helper(struct virtio_ccw_device *vcdev, __u32 intparm) +{ + int ret; + unsigned long flags; + int flag = intparm & VIRTIO_CCW_INTPARM_MASK; + + spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); + ret = ccw_device_start(vcdev->cdev, vcdev->ccw, intparm, 0, 0); + if (!ret) + vcdev->curr_io |= flag; + spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); + wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0); + return ret ? ret : vcdev->err; +} + +static inline long do_kvm_notify(struct subchannel_id schid, + unsigned long queue_index) +{ + register unsigned long __nr asm("1") = KVM_S390_VIRTIO_CCW_NOTIFY; + register struct subchannel_id __schid asm("2") = schid; + register unsigned long __index asm("3") = queue_index; + register long __rc asm("2"); + + asm volatile ("diag 2,4,0x500\n" + : "=d" (__rc) : "d" (__nr), "d" (__schid), "d" (__index) + : "memory", "cc"); + return __rc; +} + +static void virtio_ccw_kvm_notify(struct virtqueue *vq) +{ + struct virtio_ccw_vq_info *info = vq->priv; + struct virtio_ccw_device *vcdev; + struct subchannel_id schid; + + vcdev = to_vc_device(info->vq->vdev); + ccw_device_get_schid(vcdev->cdev, &schid); + do_kvm_notify(schid, virtqueue_get_queue_index(vq)); +} + +static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, int index) +{ + vcdev->config_block->index = index; + vcdev->ccw->cmd_code = CCW_CMD_READ_VQ_CONF; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(struct vq_config_block); + vcdev->ccw->cda = (__u32)(unsigned long)(vcdev->config_block); + ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_VQ_CONF); + return vcdev->config_block->num; +} + +static void virtio_ccw_del_vq(struct virtqueue *vq) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev); + struct virtio_ccw_vq_info *info = vq->priv; + unsigned long flags; + unsigned long size; + int ret; + unsigned int index = virtqueue_get_queue_index(vq); + + /* Remove from our list. */ + spin_lock_irqsave(&vcdev->lock, flags); + list_del(&info->node); + spin_unlock_irqrestore(&vcdev->lock, flags); + + /* Release from host. */ + info->info_block->queue = 0; + info->info_block->align = 0; + info->info_block->index = index; + info->info_block->num = 0; + vcdev->ccw->cmd_code = CCW_CMD_SET_VQ; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(*info->info_block); + vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block); + ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | index); + /* + * -ENODEV isn't considered an error: The device is gone anyway. + * This may happen on device detach. + */ + if (ret && (ret != -ENODEV)) + dev_warn(&vq->vdev->dev, "Error %d while deleting queue %d", + ret, index); + + vring_del_virtqueue(vq); + size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); + free_pages_exact(info->queue, size); + kfree(info->info_block); + kfree(info); +} + +static void virtio_ccw_del_vqs(struct virtio_device *vdev) +{ + struct virtqueue *vq, *n; + + list_for_each_entry_safe(vq, n, &vdev->vqs, list) + virtio_ccw_del_vq(vq); +} + +static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, + int i, vq_callback_t *callback, + const char *name) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + int err; + struct virtqueue *vq; + struct virtio_ccw_vq_info *info; + unsigned long size; + unsigned long flags; + + /* Allocate queue. */ + info = kzalloc(sizeof(struct virtio_ccw_vq_info), GFP_KERNEL); + if (!info) { + dev_warn(&vcdev->cdev->dev, "no info\n"); + err = -ENOMEM; + goto out_err; + } + info->info_block = kzalloc(sizeof(*info->info_block), + GFP_DMA | GFP_KERNEL); + if (!info->info_block) { + dev_warn(&vcdev->cdev->dev, "no info block\n"); + err = -ENOMEM; + goto out_err; + } + info->num = virtio_ccw_read_vq_conf(vcdev, i); + size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); + info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); + if (info->queue == NULL) { + dev_warn(&vcdev->cdev->dev, "no queue\n"); + err = -ENOMEM; + goto out_err; + } + + vq = vring_new_virtqueue(i, info->num, KVM_VIRTIO_CCW_RING_ALIGN, vdev, + true, info->queue, virtio_ccw_kvm_notify, + callback, name); + if (!vq) { + /* For now, we fail if we can't get the requested size. */ + dev_warn(&vcdev->cdev->dev, "no vq\n"); + err = -ENOMEM; + free_pages_exact(info->queue, size); + goto out_err; + } + info->vq = vq; + vq->priv = info; + + /* Register it with the host. */ + info->info_block->queue = (__u64)info->queue; + info->info_block->align = KVM_VIRTIO_CCW_RING_ALIGN; + info->info_block->index = i; + info->info_block->num = info->num; + vcdev->ccw->cmd_code = CCW_CMD_SET_VQ; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(*info->info_block); + vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block); + err = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | i); + if (err) { + dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n"); + free_pages_exact(info->queue, size); + info->vq = NULL; + vq->priv = NULL; + goto out_err; + } + + /* Save it to our list. */ + spin_lock_irqsave(&vcdev->lock, flags); + list_add(&info->node, &vcdev->virtqueues); + spin_unlock_irqrestore(&vcdev->lock, flags); + + return vq; + +out_err: + if (info) + kfree(info->info_block); + kfree(info); + return ERR_PTR(err); +} + +static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[]) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + unsigned long *indicatorp = NULL; + int ret, i; + + for (i = 0; i < nvqs; ++i) { + vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i]); + if (IS_ERR(vqs[i])) { + ret = PTR_ERR(vqs[i]); + vqs[i] = NULL; + goto out; + } + } + ret = -ENOMEM; + /* We need a data area under 2G to communicate. */ + indicatorp = kmalloc(sizeof(&vcdev->indicators), GFP_DMA | GFP_KERNEL); + if (!indicatorp) + goto out; + *indicatorp = (unsigned long) &vcdev->indicators; + /* Register queue indicators with host. */ + vcdev->indicators = 0; + vcdev->ccw->cmd_code = CCW_CMD_SET_IND; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(vcdev->indicators); + vcdev->ccw->cda = (__u32)(unsigned long) indicatorp; + ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_IND); + if (ret) + goto out; + /* Register indicators2 with host for config changes */ + *indicatorp = (unsigned long) &vcdev->indicators2; + vcdev->indicators2 = 0; + vcdev->ccw->cmd_code = CCW_CMD_SET_CONF_IND; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(vcdev->indicators2); + vcdev->ccw->cda = (__u32)(unsigned long) indicatorp; + ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_CONF_IND); + if (ret) + goto out; + + kfree(indicatorp); + return 0; +out: + kfree(indicatorp); + virtio_ccw_del_vqs(vdev); + return ret; +} + +static void virtio_ccw_reset(struct virtio_device *vdev) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + + /* Zero status bits. */ + vcdev->status = 0; + + /* Send a reset ccw on device. */ + vcdev->ccw->cmd_code = CCW_CMD_VDEV_RESET; + vcdev->ccw->flags = 0; + vcdev->ccw->count = 0; + vcdev->ccw->cda = 0; + ccw_io_helper(vcdev, VIRTIO_CCW_DOING_RESET); +} + +static u32 virtio_ccw_get_features(struct virtio_device *vdev) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + struct virtio_feature_desc features; + int ret; + + /* Read the feature bits from the host. */ + /* TODO: Features > 32 bits */ + features.index = 0; + vcdev->ccw->cmd_code = CCW_CMD_READ_FEAT; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(features); + vcdev->ccw->cda = vcdev->area; + ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_FEAT); + if (ret) + return 0; + + memcpy(&features, (void *)(unsigned long)vcdev->area, + sizeof(features)); + return le32_to_cpu(features.features); +} + +static void virtio_ccw_finalize_features(struct virtio_device *vdev) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + struct virtio_feature_desc features; + int i; + + /* Give virtio_ring a chance to accept features. */ + vring_transport_features(vdev); + + for (i = 0; i < sizeof(*vdev->features) / sizeof(features.features); + i++) { + int highbits = i % 2 ? 32 : 0; + features.index = i; + features.features = cpu_to_le32(vdev->features[i / 2] + >> highbits); + memcpy((void *)(unsigned long)vcdev->area, &features, + sizeof(features)); + /* Write the feature bits to the host. */ + vcdev->ccw->cmd_code = CCW_CMD_WRITE_FEAT; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(features); + vcdev->ccw->cda = vcdev->area; + ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_FEAT); + } +} + +static void virtio_ccw_get_config(struct virtio_device *vdev, + unsigned int offset, void *buf, unsigned len) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + int ret; + + /* Read the config area from the host. */ + vcdev->ccw->cmd_code = CCW_CMD_READ_CONF; + vcdev->ccw->flags = 0; + vcdev->ccw->count = offset + len; + vcdev->ccw->cda = vcdev->area; + ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_CONFIG); + if (ret) + return; + + memcpy(vcdev->config, (void *)(unsigned long)vcdev->area, + sizeof(vcdev->config)); + memcpy(buf, &vcdev->config[offset], len); +} + +static void virtio_ccw_set_config(struct virtio_device *vdev, + unsigned int offset, const void *buf, + unsigned len) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + + memcpy(&vcdev->config[offset], buf, len); + /* Write the config area to the host. */ + memcpy((void *)(unsigned long)vcdev->area, vcdev->config, + sizeof(vcdev->config)); + vcdev->ccw->cmd_code = CCW_CMD_WRITE_CONF; + vcdev->ccw->flags = 0; + vcdev->ccw->count = offset + len; + vcdev->ccw->cda = vcdev->area; + ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_CONFIG); +} + +static u8 virtio_ccw_get_status(struct virtio_device *vdev) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + + return vcdev->status; +} + +static void virtio_ccw_set_status(struct virtio_device *vdev, u8 status) +{ + struct virtio_ccw_device *vcdev = to_vc_device(vdev); + + /* Write the status to the host. */ + vcdev->status = status; + memcpy((void *)(unsigned long)vcdev->area, &status, sizeof(status)); + vcdev->ccw->cmd_code = CCW_CMD_WRITE_STATUS; + vcdev->ccw->flags = 0; + vcdev->ccw->count = sizeof(status); + vcdev->ccw->cda = vcdev->area; + ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_STATUS); +} + +static struct virtio_config_ops virtio_ccw_config_ops = { + .get_features = virtio_ccw_get_features, + .finalize_features = virtio_ccw_finalize_features, + .get = virtio_ccw_get_config, + .set = virtio_ccw_set_config, + .get_status = virtio_ccw_get_status, + .set_status = virtio_ccw_set_status, + .reset = virtio_ccw_reset, + .find_vqs = virtio_ccw_find_vqs, + .del_vqs = virtio_ccw_del_vqs, +}; + + +/* + * ccw bus driver related functions + */ + +static void virtio_ccw_release_dev(struct device *_d) +{ + struct virtio_device *dev = container_of(_d, struct virtio_device, + dev); + struct virtio_ccw_device *vcdev = to_vc_device(dev); + + kfree((void *)(unsigned long)vcdev->area); + kfree(vcdev->config_block); + kfree(vcdev->ccw); + kfree(vcdev); +} + +static int irb_is_error(struct irb *irb) +{ + if (scsw_cstat(&irb->scsw) != 0) + return 1; + if (scsw_dstat(&irb->scsw) & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) + return 1; + if (scsw_cc(&irb->scsw) != 0) + return 1; + return 0; +} + +static struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev, + int index) +{ + struct virtio_ccw_vq_info *info; + unsigned long flags; + struct virtqueue *vq; + + vq = NULL; + spin_lock_irqsave(&vcdev->lock, flags); + list_for_each_entry(info, &vcdev->virtqueues, node) { + if (virtqueue_get_queue_index(info->vq) == index) { + vq = info->vq; + break; + } + } + spin_unlock_irqrestore(&vcdev->lock, flags); + return vq; +} + +static void virtio_ccw_int_handler(struct ccw_device *cdev, + unsigned long intparm, + struct irb *irb) +{ + __u32 activity = intparm & VIRTIO_CCW_INTPARM_MASK; + struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); + int i; + struct virtqueue *vq; + struct virtio_driver *drv; + + /* Check if it's a notification from the host. */ + if ((intparm == 0) && + (scsw_stctl(&irb->scsw) == + (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) { + /* OK */ + } + if (irb_is_error(irb)) + vcdev->err = -EIO; /* XXX - use real error */ + if (vcdev->curr_io & activity) { + switch (activity) { + case VIRTIO_CCW_DOING_READ_FEAT: + case VIRTIO_CCW_DOING_WRITE_FEAT: + case VIRTIO_CCW_DOING_READ_CONFIG: + case VIRTIO_CCW_DOING_WRITE_CONFIG: + case VIRTIO_CCW_DOING_WRITE_STATUS: + case VIRTIO_CCW_DOING_SET_VQ: + case VIRTIO_CCW_DOING_SET_IND: + case VIRTIO_CCW_DOING_SET_CONF_IND: + case VIRTIO_CCW_DOING_RESET: + case VIRTIO_CCW_DOING_READ_VQ_CONF: + vcdev->curr_io &= ~activity; + wake_up(&vcdev->wait_q); + break; + default: + /* don't know what to do... */ + dev_warn(&cdev->dev, "Suspicious activity '%08x'\n", + activity); + WARN_ON(1); + break; + } + } + for_each_set_bit(i, &vcdev->indicators, + sizeof(vcdev->indicators) * BITS_PER_BYTE) { + /* The bit clear must happen before the vring kick. */ + clear_bit(i, &vcdev->indicators); + barrier(); + vq = virtio_ccw_vq_by_ind(vcdev, i); + vring_interrupt(0, vq); + } + if (test_bit(0, &vcdev->indicators2)) { + drv = container_of(vcdev->vdev.dev.driver, + struct virtio_driver, driver); + + if (drv && drv->config_changed) + drv->config_changed(&vcdev->vdev); + clear_bit(0, &vcdev->indicators2); + } +} + +/* + * We usually want to autoonline all devices, but give the admin + * a way to exempt devices from this. + */ +#define __DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \ + (8*sizeof(long))) +static unsigned long devs_no_auto[__MAX_SSID + 1][__DEV_WORDS]; + +static char *no_auto = ""; + +module_param(no_auto, charp, 0444); +MODULE_PARM_DESC(no_auto, "list of ccw bus id ranges not to be auto-onlined"); + +static int virtio_ccw_check_autoonline(struct ccw_device *cdev) +{ + struct ccw_dev_id id; + + ccw_device_get_id(cdev, &id); + if (test_bit(id.devno, devs_no_auto[id.ssid])) + return 0; + return 1; +} + +static void virtio_ccw_auto_online(void *data, async_cookie_t cookie) +{ + struct ccw_device *cdev = data; + int ret; + + ret = ccw_device_set_online(cdev); + if (ret) + dev_warn(&cdev->dev, "Failed to set online: %d\n", ret); +} + +static int virtio_ccw_probe(struct ccw_device *cdev) +{ + cdev->handler = virtio_ccw_int_handler; + + if (virtio_ccw_check_autoonline(cdev)) + async_schedule(virtio_ccw_auto_online, cdev); + return 0; +} + +static void virtio_ccw_remove(struct ccw_device *cdev) +{ + struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); + + if (cdev->online) { + unregister_virtio_device(&vcdev->vdev); + dev_set_drvdata(&cdev->dev, NULL); + } + cdev->handler = NULL; +} + +static int virtio_ccw_offline(struct ccw_device *cdev) +{ + struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); + + unregister_virtio_device(&vcdev->vdev); + dev_set_drvdata(&cdev->dev, NULL); + return 0; +} + + +/* Area needs to be big enough to fit status, features or configuration. */ +#define VIRTIO_AREA_SIZE VIRTIO_CCW_CONFIG_SIZE /* biggest possible use */ + +static int virtio_ccw_online(struct ccw_device *cdev) +{ + int ret; + struct virtio_ccw_device *vcdev; + + vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); + if (!vcdev) { + dev_warn(&cdev->dev, "Could not get memory for virtio\n"); + ret = -ENOMEM; + goto out_free; + } + vcdev->area = (__u32)(unsigned long)kzalloc(VIRTIO_AREA_SIZE, + GFP_DMA | GFP_KERNEL); + if (!vcdev->area) { + dev_warn(&cdev->dev, "Cound not get memory for virtio\n"); + ret = -ENOMEM; + goto out_free; + } + vcdev->config_block = kzalloc(sizeof(*vcdev->config_block), + GFP_DMA | GFP_KERNEL); + if (!vcdev->config_block) { + ret = -ENOMEM; + goto out_free; + } + vcdev->ccw = kzalloc(sizeof(*vcdev->ccw), GFP_DMA | GFP_KERNEL); + if (!vcdev->ccw) { + ret = -ENOMEM; + goto out_free; + } + + vcdev->vdev.dev.parent = &cdev->dev; + vcdev->vdev.dev.release = virtio_ccw_release_dev; + vcdev->vdev.config = &virtio_ccw_config_ops; + vcdev->cdev = cdev; + init_waitqueue_head(&vcdev->wait_q); + INIT_LIST_HEAD(&vcdev->virtqueues); + spin_lock_init(&vcdev->lock); + + dev_set_drvdata(&cdev->dev, vcdev); + vcdev->vdev.id.vendor = cdev->id.cu_type; + vcdev->vdev.id.device = cdev->id.cu_model; + ret = register_virtio_device(&vcdev->vdev); + if (ret) { + dev_warn(&cdev->dev, "Failed to register virtio device: %d\n", + ret); + goto out_put; + } + return 0; +out_put: + dev_set_drvdata(&cdev->dev, NULL); + put_device(&vcdev->vdev.dev); + return ret; +out_free: + if (vcdev) { + kfree((void *)(unsigned long)vcdev->area); + kfree(vcdev->config_block); + kfree(vcdev->ccw); + } + kfree(vcdev); + return ret; +} + +static int virtio_ccw_cio_notify(struct ccw_device *cdev, int event) +{ + /* TODO: Check whether we need special handling here. */ + return 0; +} + +static struct ccw_device_id virtio_ids[] = { + { CCW_DEVICE(0x3832, 0) }, + {}, +}; +MODULE_DEVICE_TABLE(ccw, virtio_ids); + +static struct ccw_driver virtio_ccw_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "virtio_ccw", + }, + .ids = virtio_ids, + .probe = virtio_ccw_probe, + .remove = virtio_ccw_remove, + .set_offline = virtio_ccw_offline, + .set_online = virtio_ccw_online, + .notify = virtio_ccw_cio_notify, + .int_class = IOINT_VIR, +}; + +static int __init pure_hex(char **cp, unsigned int *val, int min_digit, + int max_digit, int max_val) +{ + int diff; + + diff = 0; + *val = 0; + + while (diff <= max_digit) { + int value = hex_to_bin(**cp); + + if (value < 0) + break; + *val = *val * 16 + value; + (*cp)++; + diff++; + } + + if ((diff < min_digit) || (diff > max_digit) || (*val > max_val)) + return 1; + + return 0; +} + +static int __init parse_busid(char *str, unsigned int *cssid, + unsigned int *ssid, unsigned int *devno) +{ + char *str_work; + int rc, ret; + + rc = 1; + + if (*str == '\0') + goto out; + + str_work = str; + ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID); + if (ret || (str_work[0] != '.')) + goto out; + str_work++; + ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID); + if (ret || (str_work[0] != '.')) + goto out; + str_work++; + ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL); + if (ret || (str_work[0] != '\0')) + goto out; + + rc = 0; +out: + return rc; +} + +static void __init no_auto_parse(void) +{ + unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to; + char *parm, *str; + int rc; + + str = no_auto; + while ((parm = strsep(&str, ","))) { + rc = parse_busid(strsep(&parm, "-"), &from_cssid, + &from_ssid, &from); + if (rc) + continue; + if (parm != NULL) { + rc = parse_busid(parm, &to_cssid, + &to_ssid, &to); + if ((from_ssid > to_ssid) || + ((from_ssid == to_ssid) && (from > to))) + rc = -EINVAL; + } else { + to_cssid = from_cssid; + to_ssid = from_ssid; + to = from; + } + if (rc) + continue; + while ((from_ssid < to_ssid) || + ((from_ssid == to_ssid) && (from <= to))) { + set_bit(from, devs_no_auto[from_ssid]); + from++; + if (from > __MAX_SUBCHANNEL) { + from_ssid++; + from = 0; + } + } + } +} + +static int __init virtio_ccw_init(void) +{ + /* parse no_auto string before we do anything further */ + no_auto_parse(); + return ccw_driver_register(&virtio_ccw_driver); +} +module_init(virtio_ccw_init); + +static void __exit virtio_ccw_exit(void) +{ + ccw_driver_unregister(&virtio_ccw_driver); +} +module_exit(virtio_ccw_exit); From 4d7bb01162b768ca0f7e50aa3f31790d8d08402c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 18 Dec 2012 15:24:37 +0100 Subject: [PATCH 0207/4607] drm/i915: fixup overlay stolen memory leak We need to clean up the overlay first, before taking down the stolen memory allocator. This regression has been introducec in commit 8040513870399f1cb032cb8bc805df5042fedcdf Author: Chris Wilson Date: Thu Nov 15 11:32:29 2012 +0000 drm/i915: Allocate overlay registers from stolen memory v2: Rework the patch a bit as suggested by Chris Wilson: - move the overlay teardown up, into the modeset cleanup - move the stolen mm takedown into i915_gem_cleanup_stolen Cc: Chris Wilson Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 --- drivers/gpu/drm/i915/i915_gem_stolen.c | 3 +++ drivers/gpu/drm/i915/intel_display.c | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index a99c46d1e49a..272e500c920e 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1737,9 +1737,6 @@ int i915_driver_unload(struct drm_device *dev) mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_aliasing_ppgtt(dev); i915_gem_cleanup_stolen(dev); - drm_mm_takedown(&dev_priv->mm.stolen); - - intel_cleanup_overlay(dev); if (!I915_NEED_GFX_HWS(dev)) i915_free_hws(dev); diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index f817b0cac116..f21ae17e298f 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -173,7 +173,10 @@ void i915_gem_stolen_cleanup_compression(struct drm_device *dev) void i915_gem_cleanup_stolen(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; + i915_gem_stolen_cleanup_compression(dev); + drm_mm_takedown(&dev_priv->mm.stolen); } int i915_gem_init_stolen(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1bb68169aaf0..f7f4ef17cbe4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8869,6 +8869,8 @@ void intel_modeset_cleanup(struct drm_device *dev) flush_scheduled_work(); drm_mode_config_cleanup(dev); + + intel_cleanup_overlay(dev); } /* From 0176fd4d25d684ea38014b8e2c3221790d5c94d8 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 16 Dec 2012 13:24:59 -0300 Subject: [PATCH 0208/4607] [media] tda10071: add tuner_i2c_addr to struct tda10071_config The default i2c address for the tuner is 0x14, allow this to be overridden with a configuration parameter Signed-off-by: Michael Krufky Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 2 +- drivers/media/dvb-frontends/tda10071.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 16a4bc54dbe7..710362916f83 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -1064,7 +1064,7 @@ static int tda10071_init(struct dvb_frontend *fe) cmd.args[2] = 0x00; cmd.args[3] = 0x00; cmd.args[4] = 0x00; - cmd.args[5] = 0x14; + cmd.args[5] = (priv->cfg.tuner_i2c_addr) ? priv->cfg.tuner_i2c_addr : 0x14; cmd.args[6] = 0x00; cmd.args[7] = 0x03; cmd.args[8] = 0x02; diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h index 21163c4b555c..a20d5c41e048 100644 --- a/drivers/media/dvb-frontends/tda10071.h +++ b/drivers/media/dvb-frontends/tda10071.h @@ -30,6 +30,12 @@ struct tda10071_config { */ u8 i2c_address; + /* Tuner I2C address. + * Default: 0x14 + * Values: 0x14, 0x54, ... + */ + u8 tuner_i2c_addr; + /* Max bytes I2C provider can write at once. * Note: Buffer is taken from the stack currently! * Default: none, must set From 7c62f5a11c2c7f5a6a93a41d5fb1084ebd125d9a Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sat, 15 Dec 2012 23:34:09 -0300 Subject: [PATCH 0209/4607] [media] cx23885: add basic DVB-S2 support for Hauppauge HVR-4400 Add basic DVB-S2 support for the Hauppauge HVR-4400 PCIe board. Thanks to Antti Palosaari and Devin Heitmueller for their suggestions and testing. Signed-off-by: Michael Krufky Reviewed-by: Devin Heitmueller Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/Kconfig | 2 ++ drivers/media/pci/cx23885/cx23885-cards.c | 38 ++++++++++++++++++++++- drivers/media/pci/cx23885/cx23885-dvb.c | 27 ++++++++++++++++ drivers/media/pci/cx23885/cx23885.h | 1 + 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index eafa1144b17d..733d6c8ebe4c 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -26,6 +26,8 @@ config VIDEO_CX23885 select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT + select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 6277e145f0b8..7a79a17105c4 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -572,7 +572,11 @@ struct cx23885_board cx23885_boards[] = { [CX23885_BOARD_PROF_8000] = { .name = "Prof Revolution DVB-S2 8000", .portb = CX23885_MPEG_DVB, - } + }, + [CX23885_BOARD_HAUPPAUGE_HVR4400] = { + .name = "Hauppauge WinTV-HVR4400", + .portb = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -788,6 +792,22 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x8000, .subdevice = 0x3034, .card = CX23885_BOARD_PROF_8000, + }, { + .subvendor = 0x0070, + .subdevice = 0xc108, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc138, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc12a, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x0070, + .subdevice = 0xc1f8, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1301,6 +1321,16 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* enable irq */ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + /* GPIO-8 tda10071 demod reset */ + + /* Put the parts into reset and back */ + cx23885_gpio_enable(dev, GPIO_8, 1); + cx23885_gpio_clear(dev, GPIO_8); + mdelay(100); + cx23885_gpio_set(dev, GPIO_8); + mdelay(100); + break; } } @@ -1509,6 +1539,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: + case CX23885_BOARD_HAUPPAUGE_HVR4400: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -1581,6 +1612,11 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index 2f5b902e63ae..cf84c534f005 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -66,6 +66,8 @@ #include "stv090x.h" #include "stb6100.h" #include "stb6100_cfg.h" +#include "tda10071.h" +#include "a8293.h" static unsigned int debug; @@ -659,6 +661,20 @@ static struct mt2063_config terratec_mt2063_config[] = { }, }; +static const struct tda10071_config hauppauge_tda10071_config = { + .i2c_address = 0x05, + .tuner_i2c_addr = 0x54, + .i2c_wr_max = 64, + .ts_mode = TDA10071_TS_SERIAL, + .spec_inv = 0, + .xtal = 40444000, /* 40.444 MHz */ + .pll_multiplier = 20, +}; + +static const struct a8293_config hauppauge_a8293_config = { + .i2c_addr = 0x0b, +}; + static int netup_altera_fpga_rw(void *device, int flag, int data, int read) { struct cx23885_dev *dev = (struct cx23885_dev *)device; @@ -1242,6 +1258,17 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->ops.set_voltage = p8000_set_voltage; } break; + case CX23885_BOARD_HAUPPAUGE_HVR4400: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(tda10071_attach, + &hauppauge_tda10071_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(a8293_attach, fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &hauppauge_a8293_config); + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 67f40d31450b..61889b25a2af 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -91,6 +91,7 @@ #define CX23885_BOARD_TEVII_S471 35 #define CX23885_BOARD_HAUPPAUGE_HVR1255_22111 36 #define CX23885_BOARD_PROF_8000 37 +#define CX23885_BOARD_HAUPPAUGE_HVR4400 38 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 From 4e791048e0ae2ea5fe8dbbb0a4898c9e680bc643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20Sch=C3=A4fer?= Date: Tue, 4 Dec 2012 16:13:33 -0300 Subject: [PATCH 0210/4607] [media] tda18271: add missing entries for qam_7 to tda18271_update_std_map() and tda18271_dump_std_map() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tda18271-fe.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index 72c26fd77922..e7786862dab2 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -1122,6 +1122,7 @@ static int tda18271_dump_std_map(struct dvb_frontend *fe) tda18271_dump_std_item(dvbt_7, "dvbt 7"); tda18271_dump_std_item(dvbt_8, "dvbt 8"); tda18271_dump_std_item(qam_6, "qam 6 "); + tda18271_dump_std_item(qam_7, "qam 7 "); tda18271_dump_std_item(qam_8, "qam 8 "); return 0; @@ -1149,6 +1150,7 @@ static int tda18271_update_std_map(struct dvb_frontend *fe, tda18271_update_std(dvbt_7, "dvbt 7"); tda18271_update_std(dvbt_8, "dvbt 8"); tda18271_update_std(qam_6, "qam 6"); + tda18271_update_std(qam_7, "qam 7"); tda18271_update_std(qam_8, "qam 8"); return 0; From 07a092fa740442415a982a659316a3ab15d7e33e Mon Sep 17 00:00:00 2001 From: Alexey Klimov Date: Mon, 12 Nov 2012 02:57:32 -0300 Subject: [PATCH 0211/4607] [media] MAINTAINERS: add entry for radio-ma901 driver This patch adds MAINTAINERS entry for radio-ma901 usb radio driver. Signed-off-by: Alexey Klimov Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e6378c4cde8a..8695e30eb5b7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4859,6 +4859,13 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/ S: Maintained F: drivers/media/dvb-frontends/m88rs2000* +MA901 MASTERKIT USB FM RADIO DRIVER +M: Alexey Klimov +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/radio/radio-ma901.c + MAC80211 M: Johannes Berg L: linux-wireless@vger.kernel.org From 3ce4ccb630ceac293d2a4ff5e72d0edc22b319ab Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Wed, 28 Nov 2012 06:14:22 -0300 Subject: [PATCH 0212/4607] [media] MAINTAINERS: add g2d entry Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8695e30eb5b7..77ed965649dd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1128,6 +1128,14 @@ F: arch/arm/mach-s5pv210/mach-goni.c F: arch/arm/mach-exynos/mach-universal_c210.c F: arch/arm/mach-exynos/mach-nuri.c +ARM/SAMSUNG S5P SERIES 2D GRAPHICS ACCELERATION (G2D) SUPPORT +M: Kyungmin Park +M: Kamil Debski +L: linux-arm-kernel@lists.infradead.org +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/platform/s5p-g2d/ + ARM/SAMSUNG S5P SERIES FIMC SUPPORT M: Kyungmin Park M: Sylwester Nawrocki From 598df1ac29814450c37ff53aa32b38e8133c452b Mon Sep 17 00:00:00 2001 From: Alexey Klimov Date: Wed, 28 Nov 2012 17:16:32 -0300 Subject: [PATCH 0213/4607] [media] MAINTAINERS: add entry for dsbr100 usb radio driver This patch adds MAINTAINERS entry for dsbr100 usb radio driver. Signed-off-by: Alexey Klimov Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 77ed965649dd..df16caf13a8d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2598,6 +2598,13 @@ S: Supported F: drivers/gpu/drm/exynos F: include/drm/exynos* +DSBR100 USB FM RADIO DRIVER +M: Alexey Klimov +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/radio/dsbr100.c + DSCC4 DRIVER M: Francois Romieu L: netdev@vger.kernel.org From fb1cc0758074519bd0ac4d534d4a6b1ec7c811b5 Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Tue, 11 Dec 2012 17:49:48 -0300 Subject: [PATCH 0214/4607] [media] MAINTAINERS: fix drivers/media/platform/atmel-isi.c This file was moved to drivers/media/platform/soc_camera/atmel-isi.c by commit b47ff4a ([media] move soc_camera to its own directory). Cc: Josh Wu Signed-off-by: Cesar Eduardo Barros Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index df16caf13a8d..46cf22a8bbf5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1434,7 +1434,7 @@ ATMEL ISI DRIVER M: Josh Wu L: linux-media@vger.kernel.org S: Supported -F: drivers/media/platform/atmel-isi.c +F: drivers/media/platform/soc_camera/atmel-isi.c F: include/media/atmel-isi.h ATMEL LCDFB DRIVER From 3408d886173ff57c91bdeda9c3c35c62b5fac1be Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Fri, 23 Nov 2012 20:26:32 -0300 Subject: [PATCH 0215/4607] [media] MAINTAINERS: fix drivers/media/usb/dvb-usb/cxusb* This driver was never at dvb-usb-v2, as far as I could see. Signed-off-by: Cesar Eduardo Barros Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 46cf22a8bbf5..c4f8cf91c0db 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2669,7 +2669,7 @@ W: http://github.com/mkrufky Q: http://patchwork.linuxtv.org/project/linux-media/list/ T: git git://linuxtv.org/media_tree.git S: Maintained -F: drivers/media/usb/dvb-usb-v2/cxusb* +F: drivers/media/usb/dvb-usb/cxusb* DVB_USB_CYPRESS_FIRMWARE MEDIA DRIVER M: Antti Palosaari From f818b358dc94c845490dfc8a9771492e5311fe9e Mon Sep 17 00:00:00 2001 From: Nicolas THERY Date: Fri, 26 Oct 2012 09:01:55 -0300 Subject: [PATCH 0216/4607] [media] Documentation: fix outdated statement re. v4l2 Fix tense used for describing struct v4l2_fh as it has been added a while ago. Signed-off-by: Nicolas Thery Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 32bfe926e8d7..0a1ef671c9d4 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -68,8 +68,7 @@ Structure of the framework The framework closely resembles the driver structure: it has a v4l2_device struct for the device instance data, a v4l2_subdev struct to refer to sub-device instances, the video_device struct stores V4L2 device node data -and in the future a v4l2_fh struct will keep track of filehandle instances -(this is not yet implemented). +and the v4l2_fh struct keeps track of filehandle instances. The V4L2 framework also optionally integrates with the media framework. If a driver sets the struct v4l2_device mdev field, sub-devices and video nodes From 7dbf9d6e0fd8d42398955de119ccba6c35813c88 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:22 -0800 Subject: [PATCH 0217/4607] drm/i915: BUG() if fences are used on unsupported platform Signed-off-by: Ben Widawsky Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/i915_irq.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d15c86279d02..ad03dea210bd 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2649,7 +2649,7 @@ static void i915_gem_write_fence(struct drm_device *dev, int reg, case 4: i965_write_fence_reg(dev, reg, obj); break; case 3: i915_write_fence_reg(dev, reg, obj); break; case 2: i830_write_fence_reg(dev, reg, obj); break; - default: break; + default: BUG(); } } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 551f370657b7..6ba0573e7f16 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1106,6 +1106,8 @@ static void i915_gem_record_fences(struct drm_device *dev, error->fence[i] = I915_READ(FENCE_REG_830_0 + (i * 4)); break; + default: + BUG(); } } From 8782e26c0cf3444a0d11400277bb44de710a0142 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:23 -0800 Subject: [PATCH 0218/4607] drm/i915: Bug on unsupported swizzled platforms Signed-off-by: Ben Widawsky Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ad03dea210bd..6685861cd176 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3897,8 +3897,10 @@ void i915_gem_init_swizzling(struct drm_device *dev) I915_WRITE(TILECTL, I915_READ(TILECTL) | TILECTL_SWZCTL); if (IS_GEN6(dev)) I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_SNB)); - else + else if (IS_GEN7(dev)) I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_IVB)); + else + BUG(); } static bool From 079a43f67f41274b137d8be1fb6093c8f6b6a762 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:24 -0800 Subject: [PATCH 0219/4607] drm/i915: Missed conversion to gtt_pte_t commit f61c0609073133375ace61f74b0e4e7cf631406b Author: Ben Widawsky Date: Mon Oct 22 11:44:43 2012 -0700 drm/i915: introduce gtt_pte_t Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index cc1be53a65f9..ea646b4c312a 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -282,7 +282,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) uint32_t pd_offset; struct intel_ring_buffer *ring; struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt; - uint32_t __iomem *pd_addr; + gtt_pte_t __iomem *pd_addr; uint32_t pd_entry; int i; @@ -290,7 +290,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) return; - pd_addr = dev_priv->mm.gtt->gtt + ppgtt->pd_offset/sizeof(uint32_t); + pd_addr = dev_priv->mm.gtt->gtt + ppgtt->pd_offset/sizeof(gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; From b70ec5bf439b35a18e702f88078d393261c3e3f2 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 19 Dec 2012 11:13:05 +0200 Subject: [PATCH 0220/4607] drm/i915: Introduce ring set_seqno In preparation for setting per ring initial seqno values add ring::set_seqno(). Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ringbuffer.c | 20 ++++++++++++++++++++ drivers/gpu/drm/i915/intel_ringbuffer.h | 9 +++++++++ 2 files changed, 29 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 69bbe7bb9f6d..f536a9951ab3 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -727,6 +727,12 @@ ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) return intel_read_status_page(ring, I915_GEM_HWS_INDEX); } +static void +ring_set_seqno(struct intel_ring_buffer *ring, u32 seqno) +{ + intel_write_status_page(ring, I915_GEM_HWS_INDEX, seqno); +} + static u32 pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) { @@ -734,6 +740,13 @@ pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) return pc->cpu_page[0]; } +static void +pc_render_set_seqno(struct intel_ring_buffer *ring, u32 seqno) +{ + struct pipe_control *pc = ring->private; + pc->cpu_page[0] = seqno; +} + static bool gen5_ring_get_irq(struct intel_ring_buffer *ring) { @@ -1602,6 +1615,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->irq_put = gen6_ring_put_irq; ring->irq_enable_mask = GT_USER_INTERRUPT; ring->get_seqno = gen6_ring_get_seqno; + ring->set_seqno = ring_set_seqno; ring->sync_to = gen6_ring_sync; ring->semaphore_register[0] = MI_SEMAPHORE_SYNC_INVALID; ring->semaphore_register[1] = MI_SEMAPHORE_SYNC_RV; @@ -1612,6 +1626,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->add_request = pc_render_add_request; ring->flush = gen4_render_ring_flush; ring->get_seqno = pc_render_get_seqno; + ring->set_seqno = pc_render_set_seqno; ring->irq_get = gen5_ring_get_irq; ring->irq_put = gen5_ring_put_irq; ring->irq_enable_mask = GT_USER_INTERRUPT | GT_PIPE_NOTIFY; @@ -1622,6 +1637,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) else ring->flush = gen4_render_ring_flush; ring->get_seqno = ring_get_seqno; + ring->set_seqno = ring_set_seqno; if (IS_GEN2(dev)) { ring->irq_get = i8xx_ring_get_irq; ring->irq_put = i8xx_ring_put_irq; @@ -1672,6 +1688,7 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size) else ring->flush = gen4_render_ring_flush; ring->get_seqno = ring_get_seqno; + ring->set_seqno = ring_set_seqno; if (IS_GEN2(dev)) { ring->irq_get = i8xx_ring_get_irq; ring->irq_put = i8xx_ring_put_irq; @@ -1732,6 +1749,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev) ring->flush = gen6_ring_flush; ring->add_request = gen6_add_request; ring->get_seqno = gen6_ring_get_seqno; + ring->set_seqno = ring_set_seqno; ring->irq_enable_mask = GEN6_BSD_USER_INTERRUPT; ring->irq_get = gen6_ring_get_irq; ring->irq_put = gen6_ring_put_irq; @@ -1747,6 +1765,7 @@ int intel_init_bsd_ring_buffer(struct drm_device *dev) ring->flush = bsd_ring_flush; ring->add_request = i9xx_add_request; ring->get_seqno = ring_get_seqno; + ring->set_seqno = ring_set_seqno; if (IS_GEN5(dev)) { ring->irq_enable_mask = GT_BSD_USER_INTERRUPT; ring->irq_get = gen5_ring_get_irq; @@ -1776,6 +1795,7 @@ int intel_init_blt_ring_buffer(struct drm_device *dev) ring->flush = blt_ring_flush; ring->add_request = gen6_add_request; ring->get_seqno = gen6_ring_get_seqno; + ring->set_seqno = ring_set_seqno; ring->irq_enable_mask = GEN6_BLITTER_USER_INTERRUPT; ring->irq_get = gen6_ring_get_irq; ring->irq_put = gen6_ring_put_irq; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index b4a533e53fd1..4a7cd67742da 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -79,6 +79,8 @@ struct intel_ring_buffer { */ u32 (*get_seqno)(struct intel_ring_buffer *ring, bool lazy_coherency); + void (*set_seqno)(struct intel_ring_buffer *ring, + u32 seqno); int (*dispatch_execbuffer)(struct intel_ring_buffer *ring, u32 offset, u32 length, unsigned flags); @@ -166,6 +168,13 @@ intel_read_status_page(struct intel_ring_buffer *ring, return ring->status_page.page_addr[reg]; } +static inline void +intel_write_status_page(struct intel_ring_buffer *ring, + int reg, u32 value) +{ + ring->status_page.page_addr[reg] = value; +} + /** * Reads a dword out of the status page, which is written to from the command * queue by automatic updates, MI_REPORT_HEAD, MI_STORE_DATA_INDEX, or From f7e98ad4d4a8afa043126a6f24d0a154a684e081 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 19 Dec 2012 11:13:06 +0200 Subject: [PATCH 0221/4607] drm/i915: Initialize hardware semaphore state on ring init Hardware status page needs to have proper seqno set as our initial seqno can be arbitrary. If initial seqno is close to wrap boundary on init and i915_seqno_passed() (31bit space) refers to hw status page which contains zero, errorneous result will be returned. v2: clear mboxes and set hws page directly instead of going through rings. Suggested by Chris Wilson. v3: hws needs to be updated for all gens. Noticed by Chris Wilson. References: https://bugs.freedesktop.org/show_bug.cgi?id=58230 Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 8 +++----- drivers/gpu/drm/i915/intel_ringbuffer.c | 24 +++++++++--------------- drivers/gpu/drm/i915/intel_ringbuffer.h | 2 +- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6685861cd176..51282b2c4943 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1954,9 +1954,7 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) /* Finally reset hw state */ for_each_ring(ring, dev_priv, i) { - ret = intel_ring_handle_seqno_wrap(ring); - if (ret) - return ret; + intel_ring_init_seqno(ring, 0); for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++) ring->sync_seqno[j] = 0; @@ -3935,6 +3933,8 @@ i915_gem_init_hw(struct drm_device *dev) i915_gem_init_swizzling(dev); + dev_priv->next_seqno = dev_priv->last_seqno = (u32)~0 - 0x1000; + ret = intel_init_render_ring_buffer(dev); if (ret) return ret; @@ -3951,8 +3951,6 @@ i915_gem_init_hw(struct drm_device *dev) goto cleanup_bsd_ring; } - dev_priv->next_seqno = (u32)-1 - 0x1000; - /* * XXX: There was some w/a described somewhere suggesting loading * contexts before PPGTT. diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index f536a9951ab3..2bd074ad6f54 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1184,6 +1184,8 @@ static int intel_init_ring_buffer(struct drm_device *dev, if (IS_I830(ring->dev) || IS_845G(ring->dev)) ring->effective_size -= 128; + intel_ring_init_seqno(ring, dev_priv->last_seqno); + return 0; err_unmap: @@ -1431,26 +1433,18 @@ int intel_ring_begin(struct intel_ring_buffer *ring, return __intel_ring_begin(ring, num_dwords * sizeof(uint32_t)); } -int intel_ring_handle_seqno_wrap(struct intel_ring_buffer *ring) +void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno) { - int ret; + struct drm_i915_private *dev_priv = ring->dev->dev_private; BUG_ON(ring->outstanding_lazy_request); - if (INTEL_INFO(ring->dev)->gen < 6) - return 0; + if (INTEL_INFO(ring->dev)->gen >= 6) { + I915_WRITE(RING_SYNC_0(ring->mmio_base), 0); + I915_WRITE(RING_SYNC_1(ring->mmio_base), 0); + } - ret = __intel_ring_begin(ring, 6 * sizeof(uint32_t)); - if (ret) - return ret; - - /* Leaving a stale, pre-wrap seqno behind in the mboxes will result in - * post-wrap semaphore waits completing immediately. Clear them. */ - update_mboxes(ring, ring->signal_mbox[0]); - update_mboxes(ring, ring->signal_mbox[1]); - intel_ring_advance(ring); - - return 0; + ring->set_seqno(ring, seqno); } void intel_ring_advance(struct intel_ring_buffer *ring) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 4a7cd67742da..e7b9a6aac955 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -205,7 +205,7 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring, } void intel_ring_advance(struct intel_ring_buffer *ring); int __must_check intel_ring_idle(struct intel_ring_buffer *ring); -int __must_check intel_ring_handle_seqno_wrap(struct intel_ring_buffer *ring); +void intel_ring_init_seqno(struct intel_ring_buffer *ring, u32 seqno); int intel_ring_flush_all_caches(struct intel_ring_buffer *ring); int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring); From ba1a7067c00f4cbdd99b00a570ff072639ae59f2 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 19 Dec 2012 11:13:07 +0200 Subject: [PATCH 0222/4607] drm/i915: Always clear semaphore mboxes on seqno wrap In preparation for setting the seqno to arbitrary value on init or through debugfs. We need to always clear the semaphores and set the hws page seqno index by calling intel_ring_init_seqno(). v2: rewrote the commit message as suggested by Chris Wilson. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 51282b2c4943..bb38bca02559 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1932,18 +1932,6 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) struct intel_ring_buffer *ring; int ret, i, j; - /* The hardware uses various monotonic 32-bit counters, if we - * detect that they will wraparound we need to idle the GPU - * and reset those counters. - */ - ret = 0; - for_each_ring(ring, dev_priv, i) { - for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++) - ret |= ring->sync_seqno[j] != 0; - } - if (ret == 0) - return ret; - /* Carefully retire all requests without writing to the rings */ for_each_ring(ring, dev_priv, i) { ret = intel_ring_idle(ring); From fca26bb45375266d9d9b8b4b57fee905ac38fe3c Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 19 Dec 2012 11:13:08 +0200 Subject: [PATCH 0223/4607] drm/i915: Introduce i915_gem_set_seqno() This function can be used to set the driver's next_seqno to arbitrary value. i915_gem_set_seqno() will idle the gpu, retire outstanding requests, clear the semaphore mailboxes and set the hardware status page's seqno index. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 4 ++-- drivers/gpu/drm/i915/i915_gem.c | 32 +++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 514aee895ecb..b0faa9149a1a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1479,8 +1479,8 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2) return (int32_t)(seq1 - seq2) >= 0; } -extern int i915_gem_get_seqno(struct drm_device *dev, u32 *seqno); - +int __must_check i915_gem_get_seqno(struct drm_device *dev, u32 *seqno); +int __must_check i915_gem_set_seqno(struct drm_device *dev, u32 seqno); int __must_check i915_gem_object_get_fence(struct drm_i915_gem_object *obj); int __must_check i915_gem_object_put_fence(struct drm_i915_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index bb38bca02559..23b883a135db 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1926,7 +1926,7 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) } static int -i915_gem_handle_seqno_wrap(struct drm_device *dev) +i915_gem_init_seqno(struct drm_device *dev, u32 seqno) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ring_buffer *ring; @@ -1942,7 +1942,7 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) /* Finally reset hw state */ for_each_ring(ring, dev_priv, i) { - intel_ring_init_seqno(ring, 0); + intel_ring_init_seqno(ring, seqno); for (j = 0; j < ARRAY_SIZE(ring->sync_seqno); j++) ring->sync_seqno[j] = 0; @@ -1951,6 +1951,32 @@ i915_gem_handle_seqno_wrap(struct drm_device *dev) return 0; } +int i915_gem_set_seqno(struct drm_device *dev, u32 seqno) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + if (seqno == 0) + return -EINVAL; + + /* HWS page needs to be set less than what we + * will inject to ring + */ + ret = i915_gem_init_seqno(dev, seqno - 1); + if (ret) + return ret; + + /* Carefully set the last_seqno value so that wrap + * detection still works + */ + dev_priv->next_seqno = seqno; + dev_priv->last_seqno = seqno - 1; + if (dev_priv->last_seqno == 0) + dev_priv->last_seqno--; + + return 0; +} + int i915_gem_get_seqno(struct drm_device *dev, u32 *seqno) { @@ -1958,7 +1984,7 @@ i915_gem_get_seqno(struct drm_device *dev, u32 *seqno) /* reserve 0 for non-seqno */ if (dev_priv->next_seqno == 0) { - int ret = i915_gem_handle_seqno_wrap(dev); + int ret = i915_gem_init_seqno(dev, 0); if (ret) return ret; From e94fbaa8750a8f20c14718633764fba2e6755825 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Wed, 19 Dec 2012 11:13:09 +0200 Subject: [PATCH 0224/4607] drm/i915: Make next_seqno debugs entry to use i915_gem_set_seqno This debugs entry can be used to set arbitrary value to next_seqno. Use i915_gem_set_seqno instead of poking next_seqno. v2: nasty details of next_seqno and last_seqno handling moved inside i915_gem_set_seqno as suggested by Chris Wilson. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 7047c4a9fb9e..882a7352b9b9 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -878,7 +878,6 @@ i915_next_seqno_write(struct file *filp, loff_t *ppos) { struct drm_device *dev = filp->private_data; - drm_i915_private_t *dev_priv = dev->dev_private; char buf[20]; u32 val = 1; int ret; @@ -896,19 +895,11 @@ i915_next_seqno_write(struct file *filp, return ret; } - if (val == 0) - return -EINVAL; - ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; - if (i915_seqno_passed(val, dev_priv->next_seqno)) { - dev_priv->next_seqno = val; - DRM_DEBUG_DRIVER("Advancing seqno to %u\n", val); - } else { - ret = -EINVAL; - } + ret = i915_gem_set_seqno(dev, val); mutex_unlock(&dev->struct_mutex); From c196f6ee61e42df9acec797ab41a0c8d87e8aa2e Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Thu, 18 Oct 2012 07:54:59 -0300 Subject: [PATCH 0225/4607] [media] media: add new mediabus format enums for dm365 add new enum entries for supporting the media-bus formats on dm365. These include some bayer and some non-bayer formats. V4L2_MBUS_FMT_YDYUYDYV8_1X16 and V4L2_MBUS_FMT_UV8_1X8 are used internal to the hardware by the resizer. V4L2_MBUS_FMT_SBGGR10_ALAW8_1X8 represents the bayer ALAW format that is supported by dm365 hardware. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Sakari Ailus Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/subdev-formats.xml | 250 +++++++++++++++++- include/uapi/linux/v4l2-mediabus.h | 10 +- 2 files changed, 252 insertions(+), 8 deletions(-) diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index a0a936455fae..6f341d1eca1a 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -353,9 +353,9 @@ The number of bits per pixel component. All components are transferred on the same number of bits. Common values are 8, 10 and 12. - If the pixel components are DPCM-compressed, a mention of the - DPCM compression and the number of bits per compressed pixel component. - + The compression (optional). If the pixel components are + ALAW- or DPCM-compressed, a mention of the compression scheme and the + number of bits per compressed pixel component. The number of bus samples per pixel. Pixels that are wider than the bus width must be transferred in multiple samples. Common values are 1 and 2. @@ -504,6 +504,74 @@ r1 r0 + + V4L2_MBUS_FMT_SBGGR10_ALAW8_1X8 + 0x3015 + + - + - + - + - + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + + + V4L2_MBUS_FMT_SGBRG10_ALAW8_1X8 + 0x3016 + + - + - + - + - + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8 + 0x3017 + + - + - + - + - + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + + + V4L2_MBUS_FMT_SRGGB10_ALAW8_1X8 + 0x3018 + + - + - + - + - + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 0x300b @@ -853,10 +921,16 @@ Packed YUV Formats Those data formats transfer pixel data as (possibly downsampled) Y, U - and V components. The format code is made of the following information. + and V components. Some formats include dummy bits in some of their samples + and are collectively referred to as "YDYC" (Y-Dummy-Y-Chroma) formats. + One cannot rely on the values of these dummy bits as those are undefined. + + The format code is made of the following information. The Y, U and V components order code, as transferred on the - bus. Possible values are YUYV, UYVY, YVYU and VYUY. + bus. Possible values are YUYV, UYVY, YVYU and VYUY for formats with no + dummy bit, and YDYUYDYV, YDYVYDYU, YUYDYVYD and YVYDYUYD for YDYC formats. + The number of bits per pixel component. All components are transferred on the same number of bits. Common values are 8, 10 and 12. @@ -877,7 +951,21 @@ U, Y, V, Y order will be named V4L2_MBUS_FMT_UYVY8_2X8. - The following table lisst existing packet YUV formats. + list existing packet YUV + formats and describes the organization of each pixel data in each sample. + When a format pattern is split across multiple samples each of the samples + in the pattern is described. + + The role of each bit transferred over the bus is identified by one + of the following codes. + + + yx for luma component bit number x + ux for blue chroma component bit number x + vx for red chroma component bit number x + - for non-available bits (for positions higher than the bus width) + d for dummy bits + YUV Formats @@ -965,6 +1053,56 @@ y1y0 + + V4L2_MBUS_FMT_UV8_1X8 + 0x2015 + + - + - + - + - + - + - + - + - + - + - + - + - + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + V4L2_MBUS_FMT_UYVY8_1_5X8 0x2002 @@ -2415,6 +2553,106 @@ u1 u0 + + V4L2_MBUS_FMT_YDYUYDYV8_1X16 + 0x2014 + + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + d + d + d + d + d + d + d + d + + + + + + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + + + + + + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + d + d + d + d + d + d + d + d + + + + + + - + - + - + - + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + V4L2_MBUS_FMT_YUYV10_1X20 0x200d diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 7d64e0e1a18b..e860f55820ec 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -47,8 +47,9 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB565_2X8_BE = 0x1007, V4L2_MBUS_FMT_RGB565_2X8_LE = 0x1008, - /* YUV (including grey) - next is 0x2014 */ + /* YUV (including grey) - next is 0x2016 */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, + V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, V4L2_MBUS_FMT_VYUY8_1_5X8 = 0x2003, V4L2_MBUS_FMT_YUYV8_1_5X8 = 0x2004, @@ -65,14 +66,19 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_VYUY8_1X16 = 0x2010, V4L2_MBUS_FMT_YUYV8_1X16 = 0x2011, V4L2_MBUS_FMT_YVYU8_1X16 = 0x2012, + V4L2_MBUS_FMT_YDYUYDYV8_1X16 = 0x2014, V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, - /* Bayer - next is 0x3015 */ + /* Bayer - next is 0x3019 */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, V4L2_MBUS_FMT_SGBRG8_1X8 = 0x3013, V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002, V4L2_MBUS_FMT_SRGGB8_1X8 = 0x3014, + V4L2_MBUS_FMT_SBGGR10_ALAW8_1X8 = 0x3015, + V4L2_MBUS_FMT_SGBRG10_ALAW8_1X8 = 0x3016, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8 = 0x3017, + V4L2_MBUS_FMT_SRGGB10_ALAW8_1X8 = 0x3018, V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8 = 0x300b, V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8 = 0x300c, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 = 0x3009, From 028986fe0edab3ca7f14a9fe92f3e492304b3eec Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 19 Dec 2012 13:00:27 -0200 Subject: [PATCH 0226/4607] [media] DocBook: fix an index reference Error: no ID for constraint linkend: V4L2-PIX-FMT-NV12MT-16X16. Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/pixfmt-nv12m.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml b/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml index a990b34d911a..f3a3d459fcdf 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-nv12m.xml @@ -6,7 +6,7 @@ V4L2_PIX_FMT_NV12M V4L2_PIX_FMT_NV21M - V4L2_PIX_FMT_NV12MT_16X16 + V4L2_PIX_FMT_NV12MT_16X16 Variation of V4L2_PIX_FMT_NV12 and V4L2_PIX_FMT_NV21 with planes non contiguous in memory. From 05ad6fc1d54f106d5b8c598e2f9b59b12f3fb476 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Thu, 18 Oct 2012 07:58:02 -0300 Subject: [PATCH 0227/4607] [media] v4l2: add new pixel formats supported on dm365 add new macro V4L2_PIX_FMT_SGRBG10ALAW8 and associated formats to represent Bayer format frames compressed by A-LAW algorithm, add V4L2_PIX_FMT_UV8 to represent storage of CbCr data (UV interleaved) only. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/pixfmt-srggb10alaw8.xml | 34 ++++++++++ .../DocBook/media/v4l/pixfmt-uv8.xml | 62 +++++++++++++++++++ Documentation/DocBook/media/v4l/pixfmt.xml | 2 + include/uapi/linux/videodev2.h | 8 +++ 4 files changed, 106 insertions(+) create mode 100644 Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml create mode 100644 Documentation/DocBook/media/v4l/pixfmt-uv8.xml diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml new file mode 100644 index 000000000000..c934192e98ce --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml @@ -0,0 +1,34 @@ + + + + V4L2_PIX_FMT_SBGGR10ALAW8 ('aBA8'), + V4L2_PIX_FMT_SGBRG10ALAW8 ('aGA8'), + V4L2_PIX_FMT_SGRBG10ALAW8 ('agA8'), + V4L2_PIX_FMT_SRGGB10ALAW8 ('aRA8'), + + &manvol; + + + + V4L2_PIX_FMT_SBGGR10ALAW8 + + + V4L2_PIX_FMT_SGBRG10ALAW8 + + + V4L2_PIX_FMT_SGRBG10ALAW8 + + + V4L2_PIX_FMT_SRGGB10ALAW8 + + 10-bit Bayer formats compressed to 8 bits + + + Description + The following four pixel formats are raw sRGB / Bayer + formats with 10 bits per color compressed to 8 bits each, + using the A-LAW algorithm. Each color component consumes 8 + bits of memory. In other respects this format is similar to + . + + diff --git a/Documentation/DocBook/media/v4l/pixfmt-uv8.xml b/Documentation/DocBook/media/v4l/pixfmt-uv8.xml new file mode 100644 index 000000000000..c507c1f73cd0 --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-uv8.xml @@ -0,0 +1,62 @@ + + + V4L2_PIX_FMT_UV8 ('UV8') + &manvol; + + + V4L2_PIX_FMT_UV8 + UV plane interleaved + + + Description + In this format there is no Y plane, Only CbCr plane. ie + (UV interleaved) + + + <constant>V4L2_PIX_FMT_UV8</constant> + pixel image + + + + Byte Order. + Each cell is one byte. + + + + + + start + 0: + Cb00 + Cr00 + Cb01 + Cr01 + + + start + 4: + Cb10 + Cr10 + Cb11 + Cr11 + + + start + 8: + Cb20 + Cr20 + Cb21 + Cr21 + + + start + 12: + Cb30 + Cr30 + Cb31 + Cr31 + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index bf94f417592c..99b8d2ad6e4f 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -673,6 +673,7 @@ access the palette, this must be done with ioctls of the Linux framebuffer API.< &sub-srggb8; &sub-sbggr16; &sub-srggb10; + &sub-srggb10alaw8; &sub-srggb10dpcm8; &sub-srggb12; @@ -701,6 +702,7 @@ information. &sub-y12; &sub-y10b; &sub-y16; + &sub-uv8; &sub-yuyv; &sub-uyvy; &sub-yvyu; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 3cf3e946e331..39d2cecdf38c 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -334,6 +334,9 @@ struct v4l2_pix_format { /* Palette formats */ #define V4L2_PIX_FMT_PAL8 v4l2_fourcc('P', 'A', 'L', '8') /* 8 8-bit palette */ +/* Chrominance formats */ +#define V4L2_PIX_FMT_UV8 v4l2_fourcc('U', 'V', '8', ' ') /* 8 UV 4:4 */ + /* Luminance+Chrominance formats */ #define V4L2_PIX_FMT_YVU410 v4l2_fourcc('Y', 'V', 'U', '9') /* 9 YVU 4:1:0 */ #define V4L2_PIX_FMT_YVU420 v4l2_fourcc('Y', 'V', '1', '2') /* 12 YVU 4:2:0 */ @@ -386,6 +389,11 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGBRG12 v4l2_fourcc('G', 'B', '1', '2') /* 12 GBGB.. RGRG.. */ #define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12 GRGR.. BGBG.. */ #define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12 RGRG.. GBGB.. */ + /* 10bit raw bayer a-law compressed to 8 bits */ +#define V4L2_PIX_FMT_SBGGR10ALAW8 v4l2_fourcc('a', 'B', 'A', '8') +#define V4L2_PIX_FMT_SGBRG10ALAW8 v4l2_fourcc('a', 'G', 'A', '8') +#define V4L2_PIX_FMT_SGRBG10ALAW8 v4l2_fourcc('a', 'g', 'A', '8') +#define V4L2_PIX_FMT_SRGGB10ALAW8 v4l2_fourcc('a', 'R', 'A', '8') /* 10bit raw bayer DPCM compressed to 8 bits */ #define V4L2_PIX_FMT_SBGGR10DPCM8 v4l2_fourcc('b', 'B', 'A', '8') #define V4L2_PIX_FMT_SGBRG10DPCM8 v4l2_fourcc('b', 'G', 'A', '8') From 71d5e7af12894497882b0497f331b6a2b6c97d23 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 13 Nov 2012 10:35:38 -0300 Subject: [PATCH 0228/4607] [media] omap_vout: Drop overlay format enumeration Enumerating formats for output overlays doesn't make sense, as the pixel format is defined by the display API, not the V4L2 API. Drop the vidioc_enum_fmt_vid_overlay ioctl operation. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap/omap_vout.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 837cb6db747f..90f890875b8b 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1232,21 +1232,6 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh, return ret; } -static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh, - struct v4l2_fmtdesc *fmt) -{ - int index = fmt->index; - - if (index >= NUM_OUTPUT_FORMATS) - return -EINVAL; - - fmt->flags = omap_formats[index].flags; - strlcpy(fmt->description, omap_formats[index].description, - sizeof(fmt->description)); - fmt->pixelformat = omap_formats[index].pixelformat; - return 0; -} - static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) { @@ -1862,7 +1847,6 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = { .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, - .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, .vidioc_cropcap = vidioc_cropcap, .vidioc_g_crop = vidioc_g_crop, From 7b2607c99e36854987df2f78eb540cd712490450 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 13 Nov 2012 10:00:22 -0300 Subject: [PATCH 0229/4607] [media] omap_vout: Use the output overlay ioctl operations The omap_vout device implements the output overlay API, use the corresponding ioctl operations. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap/omap_vout.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index 90f890875b8b..f7ad54106bc5 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -1845,9 +1845,9 @@ static const struct v4l2_ioctl_ops vout_ioctl_ops = { .vidioc_s_fbuf = vidioc_s_fbuf, .vidioc_g_fbuf = vidioc_g_fbuf, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, - .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, - .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_out_overlay = vidioc_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_out_overlay = vidioc_s_fmt_vid_overlay, + .vidioc_g_fmt_vid_out_overlay = vidioc_g_fmt_vid_overlay, .vidioc_cropcap = vidioc_cropcap, .vidioc_g_crop = vidioc_g_crop, .vidioc_s_crop = vidioc_s_crop, From b1252eb83fe57b838c19e2c65cba685c93696693 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 11 Sep 2012 06:32:17 -0300 Subject: [PATCH 0230/4607] [media] media: mem2mem: make reference to struct m2m_ops in the core const The mem2mem core doesn't change struct m2m_ops, provided by the driver, make references to it const. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 4 ++-- include/media/v4l2-mem2mem.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 438ea45d1074..da99cf727162 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -62,7 +62,7 @@ struct v4l2_m2m_dev { struct list_head job_queue; spinlock_t job_spinlock; - struct v4l2_m2m_ops *m2m_ops; + const struct v4l2_m2m_ops *m2m_ops; }; static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx, @@ -519,7 +519,7 @@ EXPORT_SYMBOL(v4l2_m2m_mmap); * * Usually called from driver's probe() function. */ -struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops) +struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops) { struct v4l2_m2m_dev *m2m_dev; diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 7e82d2b193d5..d3eef01da648 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -125,7 +125,7 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct vm_area_struct *vma); -struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops); +struct v4l2_m2m_dev *v4l2_m2m_init(const struct v4l2_m2m_ops *m2m_ops); void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev); struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev, From ee7afd717c26299c85675d84c7ff89a9c989f4fa Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 19 Dec 2012 16:07:23 +0000 Subject: [PATCH 0231/4607] UAPI: (Scripted) Disintegrate include/scsi Signed-off-by: David Howells Acked-by: Arnd Bergmann Acked-by: Thomas Gleixner Acked-by: Michael Kerrisk Acked-by: Paul E. McKenney Acked-by: Dave Jones --- include/scsi/Kbuild | 3 --- include/uapi/scsi/Kbuild | 3 +++ include/{ => uapi}/scsi/scsi_bsg_fc.h | 0 include/{ => uapi}/scsi/scsi_netlink.h | 0 include/{ => uapi}/scsi/scsi_netlink_fc.h | 0 5 files changed, 3 insertions(+), 3 deletions(-) rename include/{ => uapi}/scsi/scsi_bsg_fc.h (100%) rename include/{ => uapi}/scsi/scsi_netlink.h (100%) rename include/{ => uapi}/scsi/scsi_netlink_fc.h (100%) diff --git a/include/scsi/Kbuild b/include/scsi/Kbuild index f2b94918994d..562ff9d591b8 100644 --- a/include/scsi/Kbuild +++ b/include/scsi/Kbuild @@ -1,4 +1 @@ -header-y += scsi_netlink.h -header-y += scsi_netlink_fc.h -header-y += scsi_bsg_fc.h header-y += fc/ diff --git a/include/uapi/scsi/Kbuild b/include/uapi/scsi/Kbuild index 29a87dd26cfb..75746d52f208 100644 --- a/include/uapi/scsi/Kbuild +++ b/include/uapi/scsi/Kbuild @@ -1,2 +1,5 @@ # UAPI Header export list header-y += fc/ +header-y += scsi_bsg_fc.h +header-y += scsi_netlink.h +header-y += scsi_netlink_fc.h diff --git a/include/scsi/scsi_bsg_fc.h b/include/uapi/scsi/scsi_bsg_fc.h similarity index 100% rename from include/scsi/scsi_bsg_fc.h rename to include/uapi/scsi/scsi_bsg_fc.h diff --git a/include/scsi/scsi_netlink.h b/include/uapi/scsi/scsi_netlink.h similarity index 100% rename from include/scsi/scsi_netlink.h rename to include/uapi/scsi/scsi_netlink.h diff --git a/include/scsi/scsi_netlink_fc.h b/include/uapi/scsi/scsi_netlink_fc.h similarity index 100% rename from include/scsi/scsi_netlink_fc.h rename to include/uapi/scsi/scsi_netlink_fc.h From d7e5008f7c2077d856e40a3af746f1a47028b5f2 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:25 -0800 Subject: [PATCH 0232/4607] drm/i915: Move even more gtt code to i915_gem_gtt This really should have been part of the kill agp series. Signed-off-by: Ben Widawsky Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 7 ++-- drivers/gpu/drm/i915/i915_gem.c | 51 ++----------------------- drivers/gpu/drm/i915/i915_gem_gtt.c | 59 +++++++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 56 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b0faa9149a1a..15799e783b7a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1587,10 +1587,9 @@ void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj); void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj); -void i915_gem_init_global_gtt(struct drm_device *dev, - unsigned long start, - unsigned long mappable_end, - unsigned long end); +void i915_gem_init_global_gtt(struct drm_device *dev); +void i915_gem_setup_global_gtt(struct drm_device *dev, unsigned long start, + unsigned long mappable_end, unsigned long end); int i915_gem_gtt_init(struct drm_device *dev); void i915_gem_gtt_fini(struct drm_device *dev); static inline void i915_gem_chipset_flush(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 23b883a135db..ad98db5d22ea 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -163,8 +163,8 @@ i915_gem_init_ioctl(struct drm_device *dev, void *data, return -ENODEV; mutex_lock(&dev->struct_mutex); - i915_gem_init_global_gtt(dev, args->gtt_start, - args->gtt_end, args->gtt_end); + i915_gem_setup_global_gtt(dev, args->gtt_start, args->gtt_end, + args->gtt_end); mutex_unlock(&dev->struct_mutex); return 0; @@ -3981,58 +3981,13 @@ cleanup_render_ring: return ret; } -static bool -intel_enable_ppgtt(struct drm_device *dev) -{ - if (i915_enable_ppgtt >= 0) - return i915_enable_ppgtt; - -#ifdef CONFIG_INTEL_IOMMU - /* Disable ppgtt on SNB if VT-d is on. */ - if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) - return false; -#endif - - return true; -} - int i915_gem_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long gtt_size, mappable_size; int ret; - gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; - mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - mutex_lock(&dev->struct_mutex); - if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { - /* PPGTT pdes are stolen from global gtt ptes, so shrink the - * aperture accordingly when using aliasing ppgtt. */ - gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE; - - i915_gem_init_global_gtt(dev, 0, mappable_size, gtt_size); - - ret = i915_gem_init_aliasing_ppgtt(dev); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ret; - } - } else { - /* Let GEM Manage all of the aperture. - * - * However, leave one page at the end still bound to the scratch - * page. There are a number of places where the hardware - * apparently prefetches past the end of the object, and we've - * seen multiple hangs with the GPU head pointer stuck in a - * batchbuffer bound at the last page of the aperture. One page - * should be enough to keep any prefetching inside of the - * aperture. - */ - i915_gem_init_global_gtt(dev, 0, mappable_size, - gtt_size); - } - + i915_gem_init_global_gtt(dev); ret = i915_gem_init_hw(dev); mutex_unlock(&dev->struct_mutex); if (ret) { diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index ea646b4c312a..61b3e728be34 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -525,10 +525,10 @@ static void i915_gtt_color_adjust(struct drm_mm_node *node, } } -void i915_gem_init_global_gtt(struct drm_device *dev, - unsigned long start, - unsigned long mappable_end, - unsigned long end) +void i915_gem_setup_global_gtt(struct drm_device *dev, + unsigned long start, + unsigned long mappable_end, + unsigned long end) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_mm_node *entry; @@ -573,6 +573,57 @@ void i915_gem_init_global_gtt(struct drm_device *dev, i915_ggtt_clear_range(dev, end / PAGE_SIZE - 1, 1); } +static bool +intel_enable_ppgtt(struct drm_device *dev) +{ + if (i915_enable_ppgtt >= 0) + return i915_enable_ppgtt; + +#ifdef CONFIG_INTEL_IOMMU + /* Disable ppgtt on SNB if VT-d is on. */ + if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) + return false; +#endif + + return true; +} + +void i915_gem_init_global_gtt(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long gtt_size, mappable_size; + int ret; + + gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; + mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + + if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { + /* PPGTT pdes are stolen from global gtt ptes, so shrink the + * aperture accordingly when using aliasing ppgtt. */ + gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE; + + i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size); + + ret = i915_gem_init_aliasing_ppgtt(dev); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return; + } + } else { + /* Let GEM Manage all of the aperture. + * + * However, leave one page at the end still bound to the scratch + * page. There are a number of places where the hardware + * apparently prefetches past the end of the object, and we've + * seen multiple hangs with the GPU head pointer stuck in a + * batchbuffer bound at the last page of the aperture. One page + * should be enough to keep any prefetching inside of the + * aperture. + */ + i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size); + } +} + static int setup_scratch_page(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; From 06e5598fce5ce89fe8bf081398296e5b08d993dd Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:26 -0800 Subject: [PATCH 0233/4607] drm/i915: Move GSM mapping into dev_priv This removes an unused field from the AGP structure and moves it into the dev_priv structure (with a slightly better name). This builds upon the kill-agp series already merged. GSM is a well defined term in the bspec: GSM: Graphics Stolen Memory GTT stolen space is defined for storage of the GFX GTT entries in physical memory. IA can not access GSM directly , it can only access via GTTMMADR. GT can access GSM directly or through GTTMMADR. This is not the entire stolen space. Signed-off-by: Ben Widawsky Reviewed-by: Mika Kuoppala Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 1 - drivers/gpu/drm/i915/i915_drv.h | 3 +++ drivers/gpu/drm/i915/i915_gem_gtt.c | 14 +++++++------- include/drm/intel-gtt.h | 2 -- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index dbd901e94ea6..c8d9dcb15db0 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -602,7 +602,6 @@ static int intel_gtt_init(void) iounmap(intel_private.registers); return -ENOMEM; } - intel_private.base.gtt = intel_private.gtt; global_cache_flush(); /* FIXME: ? */ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 15799e783b7a..ae88d87be951 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -800,6 +800,9 @@ typedef struct drm_i915_private { unsigned long gtt_end; unsigned long stolen_base; /* limited to low memory (32-bit) */ + /** "Graphics Stolen Memory" holds the global PTEs */ + uint32_t __iomem *gsm; + struct io_mapping *gtt_mapping; phys_addr_t gtt_base_addr; int gtt_mtrr; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 61b3e728be34..912389e38a7d 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -290,7 +290,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) return; - pd_addr = dev_priv->mm.gtt->gtt + ppgtt->pd_offset/sizeof(gtt_pte_t); + pd_addr = dev_priv->mm.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; @@ -367,7 +367,7 @@ static void i915_ggtt_clear_range(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; gtt_pte_t scratch_pte; - gtt_pte_t __iomem *gtt_base = dev_priv->mm.gtt->gtt + first_entry; + gtt_pte_t __iomem *gtt_base = (gtt_pte_t __iomem *) dev_priv->mm.gsm + first_entry; const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; int i; @@ -432,7 +432,7 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, struct scatterlist *sg = st->sgl; const int first_entry = obj->gtt_space->start >> PAGE_SHIFT; const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; - gtt_pte_t __iomem *gtt_entries = dev_priv->mm.gtt->gtt + first_entry; + gtt_pte_t __iomem *gtt_entries = dev_priv->mm.gsm + first_entry; int unused, i = 0; unsigned int len, m = 0; dma_addr_t addr; @@ -747,9 +747,9 @@ int i915_gem_gtt_init(struct drm_device *dev) goto err_out; } - dev_priv->mm.gtt->gtt = ioremap_wc(gtt_bus_addr, - dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); - if (!dev_priv->mm.gtt->gtt) { + dev_priv->mm.gsm = ioremap_wc(gtt_bus_addr, + dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); + if (!dev_priv->mm.gsm) { DRM_ERROR("Failed to map the gtt page table\n"); teardown_scratch_page(dev); ret = -ENOMEM; @@ -773,7 +773,7 @@ err_out: void i915_gem_gtt_fini(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - iounmap(dev_priv->mm.gtt->gtt); + iounmap(dev_priv->mm.gsm); teardown_scratch_page(dev); if (INTEL_INFO(dev)->gen < 6) intel_gmch_remove(); diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 6eb76a1f11ab..3e3a166a2690 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -18,8 +18,6 @@ struct intel_gtt { /* Share the scratch page dma with ppgtts. */ dma_addr_t scratch_page_dma; struct page *scratch_page; - /* for ppgtt PDE access */ - u32 __iomem *gtt; /* needed for ioremap in drm/i915 */ phys_addr_t gma_bus_addr; } *intel_gtt_get(void); From 1c45140d3da0c98fa7eda2cd10f9805232a5e9ac Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:27 -0800 Subject: [PATCH 0234/4607] drm/i915: Make GSM void The iomapping of the register region has historically been a uint32_t for the obvious reason that our PTE size was always 4b. In the future however, we cannot make this assumption. By making the type void, it makes the upcoming pointer math we will do much easier, and hopefully gives the compiler opportunities to warn us when we do stupid things. v2: Cast to __iomem, caught by Ville Signed-off-by: Ben Widawsky [danvet: Fixup __iomem issue for real.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/i915_gem_gtt.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ae88d87be951..d2b93a4a3d71 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -801,7 +801,7 @@ typedef struct drm_i915_private { unsigned long stolen_base; /* limited to low memory (32-bit) */ /** "Graphics Stolen Memory" holds the global PTEs */ - uint32_t __iomem *gsm; + void __iomem *gsm; struct io_mapping *gtt_mapping; phys_addr_t gtt_base_addr; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 912389e38a7d..eac2cec71652 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -290,7 +290,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) return; - pd_addr = dev_priv->mm.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); + pd_addr = (gtt_pte_t __iomem*)dev_priv->mm.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; @@ -432,7 +432,8 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, struct scatterlist *sg = st->sgl; const int first_entry = obj->gtt_space->start >> PAGE_SHIFT; const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; - gtt_pte_t __iomem *gtt_entries = dev_priv->mm.gsm + first_entry; + gtt_pte_t __iomem *gtt_entries = + (gtt_pte_t __iomem *)dev_priv->mm.gsm + first_entry; int unused, i = 0; unsigned int len, m = 0; dma_addr_t addr; From 05efa71bdc0e352edc9189fdf66af6e96eadd1c9 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 5 Oct 2012 07:43:41 -0300 Subject: [PATCH 0235/4607] [media] media: add a VEU MEM2MEM format conversion and scaling driver Video Engine Unit (VEU) is an IP block, found in multiple SuperH and ARM- based sh-mobile and r-mobile SoCs, capable of processing video data. It can perform colour-space conversion, scaling and several filtering transformations. This patch adds an initial implementation of a mem2mem V4L2 driver for VEU. So far only conversion from NV12 to RGB565 is supported. Further functionality shall be added in the future. This driver is based on a VEU vidix driver by Magnus Damm. Signed-off-by: Guennadi Liakhovetski Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 9 + drivers/media/platform/Makefile | 2 + drivers/media/platform/sh_veu.c | 1264 +++++++++++++++++++++++++++++++ 3 files changed, 1275 insertions(+) create mode 100644 drivers/media/platform/sh_veu.c diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 3dcfea612c42..c071b079de23 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -202,6 +202,15 @@ config VIDEO_SAMSUNG_EXYNOS_GSC help This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. +config VIDEO_SH_VEU + tristate "SuperH VEU mem2mem video processing driver" + depends on VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + Support for the Video Engine Unit (VEU) on SuperH and + SH-Mobile SoCs. + endif # V4L_MEM2MEM_DRIVERS menuconfig V4L_TEST_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 4817d2802171..42089ba3600f 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -25,6 +25,8 @@ obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o obj-$(CONFIG_VIDEO_CODA) += coda.o +obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o + obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c new file mode 100644 index 000000000000..7365fb5a2615 --- /dev/null +++ b/drivers/media/platform/sh_veu.c @@ -0,0 +1,1264 @@ +/* + * sh-mobile VEU mem2mem driver + * + * Copyright (C) 2012 Renesas Electronics Corporation + * Author: Guennadi Liakhovetski, + * Copyright (C) 2008 Magnus Damm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License as + * published by the Free Software Foundation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define VEU_STR 0x00 /* start register */ +#define VEU_SWR 0x10 /* src: line length */ +#define VEU_SSR 0x14 /* src: image size */ +#define VEU_SAYR 0x18 /* src: y/rgb plane address */ +#define VEU_SACR 0x1c /* src: c plane address */ +#define VEU_BSSR 0x20 /* bundle mode register */ +#define VEU_EDWR 0x30 /* dst: line length */ +#define VEU_DAYR 0x34 /* dst: y/rgb plane address */ +#define VEU_DACR 0x38 /* dst: c plane address */ +#define VEU_TRCR 0x50 /* transform control */ +#define VEU_RFCR 0x54 /* resize scale */ +#define VEU_RFSR 0x58 /* resize clip */ +#define VEU_ENHR 0x5c /* enhance */ +#define VEU_FMCR 0x70 /* filter mode */ +#define VEU_VTCR 0x74 /* lowpass vertical */ +#define VEU_HTCR 0x78 /* lowpass horizontal */ +#define VEU_APCR 0x80 /* color match */ +#define VEU_ECCR 0x84 /* color replace */ +#define VEU_AFXR 0x90 /* fixed mode */ +#define VEU_SWPR 0x94 /* swap */ +#define VEU_EIER 0xa0 /* interrupt mask */ +#define VEU_EVTR 0xa4 /* interrupt event */ +#define VEU_STAR 0xb0 /* status */ +#define VEU_BSRR 0xb4 /* reset */ + +#define VEU_MCR00 0x200 /* color conversion matrix coefficient 00 */ +#define VEU_MCR01 0x204 /* color conversion matrix coefficient 01 */ +#define VEU_MCR02 0x208 /* color conversion matrix coefficient 02 */ +#define VEU_MCR10 0x20c /* color conversion matrix coefficient 10 */ +#define VEU_MCR11 0x210 /* color conversion matrix coefficient 11 */ +#define VEU_MCR12 0x214 /* color conversion matrix coefficient 12 */ +#define VEU_MCR20 0x218 /* color conversion matrix coefficient 20 */ +#define VEU_MCR21 0x21c /* color conversion matrix coefficient 21 */ +#define VEU_MCR22 0x220 /* color conversion matrix coefficient 22 */ +#define VEU_COFFR 0x224 /* color conversion offset */ +#define VEU_CBR 0x228 /* color conversion clip */ + +/* + * 4092x4092 max size is the normal case. In some cases it can be reduced to + * 2048x2048, in other cases it can be 4092x8188 or even 8188x8188. + */ +#define MAX_W 4092 +#define MAX_H 4092 +#define MIN_W 8 +#define MIN_H 8 +#define ALIGN_W 4 + +/* 3 buffers of 2048 x 1536 - 3 megapixels @ 16bpp */ +#define VIDEO_MEM_LIMIT ALIGN(2048 * 1536 * 2 * 3, 1024 * 1024) + +#define MEM2MEM_DEF_TRANSLEN 1 + +struct sh_veu_dev; + +struct sh_veu_file { + struct sh_veu_dev *veu_dev; + bool cfg_needed; +}; + +struct sh_veu_format { + char *name; + u32 fourcc; + unsigned int depth; + unsigned int ydepth; +}; + +/* video data format */ +struct sh_veu_vfmt { + /* Replace with v4l2_rect */ + struct v4l2_rect frame; + unsigned int bytesperline; + unsigned int offset_y; + unsigned int offset_c; + const struct sh_veu_format *fmt; +}; + +struct sh_veu_dev { + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct v4l2_m2m_dev *m2m_dev; + struct device *dev; + struct v4l2_m2m_ctx *m2m_ctx; + struct sh_veu_vfmt vfmt_out; + struct sh_veu_vfmt vfmt_in; + /* Only single user per direction so far */ + struct sh_veu_file *capture; + struct sh_veu_file *output; + struct mutex fop_lock; + void __iomem *base; + struct vb2_alloc_ctx *alloc_ctx; + spinlock_t lock; + bool is_2h; + unsigned int xaction; + bool aborting; +}; + +enum sh_veu_fmt_idx { + SH_VEU_FMT_NV12, + SH_VEU_FMT_NV16, + SH_VEU_FMT_NV24, + SH_VEU_FMT_RGB332, + SH_VEU_FMT_RGB444, + SH_VEU_FMT_RGB565, + SH_VEU_FMT_RGB666, + SH_VEU_FMT_RGB24, +}; + +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 + +#define DEFAULT_IN_WIDTH VGA_WIDTH +#define DEFAULT_IN_HEIGHT VGA_HEIGHT +#define DEFAULT_IN_FMTIDX SH_VEU_FMT_NV12 +#define DEFAULT_OUT_WIDTH VGA_WIDTH +#define DEFAULT_OUT_HEIGHT VGA_HEIGHT +#define DEFAULT_OUT_FMTIDX SH_VEU_FMT_RGB565 + +/* + * Alignment: Y-plane should be 4-byte aligned for NV12 and NV16, and 8-byte + * aligned for NV24. + */ +static const struct sh_veu_format sh_veu_fmt[] = { + [SH_VEU_FMT_NV12] = { .ydepth = 8, .depth = 12, .name = "NV12", .fourcc = V4L2_PIX_FMT_NV12 }, + [SH_VEU_FMT_NV16] = { .ydepth = 8, .depth = 16, .name = "NV16", .fourcc = V4L2_PIX_FMT_NV16 }, + [SH_VEU_FMT_NV24] = { .ydepth = 8, .depth = 24, .name = "NV24", .fourcc = V4L2_PIX_FMT_NV24 }, + [SH_VEU_FMT_RGB332] = { .ydepth = 8, .depth = 8, .name = "RGB332", .fourcc = V4L2_PIX_FMT_RGB332 }, + [SH_VEU_FMT_RGB444] = { .ydepth = 16, .depth = 16, .name = "RGB444", .fourcc = V4L2_PIX_FMT_RGB444 }, + [SH_VEU_FMT_RGB565] = { .ydepth = 16, .depth = 16, .name = "RGB565", .fourcc = V4L2_PIX_FMT_RGB565 }, + [SH_VEU_FMT_RGB666] = { .ydepth = 32, .depth = 32, .name = "BGR666", .fourcc = V4L2_PIX_FMT_BGR666 }, + [SH_VEU_FMT_RGB24] = { .ydepth = 24, .depth = 24, .name = "RGB24", .fourcc = V4L2_PIX_FMT_RGB24 }, +}; + +#define DEFAULT_IN_VFMT (struct sh_veu_vfmt){ \ + .frame = { \ + .width = VGA_WIDTH, \ + .height = VGA_HEIGHT, \ + }, \ + .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_IN_FMTIDX].ydepth) >> 3, \ + .fmt = &sh_veu_fmt[DEFAULT_IN_FMTIDX], \ +} + +#define DEFAULT_OUT_VFMT (struct sh_veu_vfmt){ \ + .frame = { \ + .width = VGA_WIDTH, \ + .height = VGA_HEIGHT, \ + }, \ + .bytesperline = (VGA_WIDTH * sh_veu_fmt[DEFAULT_OUT_FMTIDX].ydepth) >> 3, \ + .fmt = &sh_veu_fmt[DEFAULT_OUT_FMTIDX], \ +} + +/* + * TODO: add support for further output formats: + * SH_VEU_FMT_NV12, + * SH_VEU_FMT_NV16, + * SH_VEU_FMT_NV24, + * SH_VEU_FMT_RGB332, + * SH_VEU_FMT_RGB444, + * SH_VEU_FMT_RGB666, + * SH_VEU_FMT_RGB24, + */ + +static const int sh_veu_fmt_out[] = { + SH_VEU_FMT_RGB565, +}; + +/* + * TODO: add support for further input formats: + * SH_VEU_FMT_NV16, + * SH_VEU_FMT_NV24, + * SH_VEU_FMT_RGB565, + * SH_VEU_FMT_RGB666, + * SH_VEU_FMT_RGB24, + */ +static const int sh_veu_fmt_in[] = { + SH_VEU_FMT_NV12, +}; + +static enum v4l2_colorspace sh_veu_4cc2cspace(u32 fourcc) +{ + switch (fourcc) { + default: + BUG(); + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV24: + return V4L2_COLORSPACE_JPEG; + case V4L2_PIX_FMT_RGB332: + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_BGR666: + case V4L2_PIX_FMT_RGB24: + return V4L2_COLORSPACE_SRGB; + } +} + +static u32 sh_veu_reg_read(struct sh_veu_dev *veu, unsigned int reg) +{ + return ioread32(veu->base + reg); +} + +static void sh_veu_reg_write(struct sh_veu_dev *veu, unsigned int reg, + u32 value) +{ + iowrite32(value, veu->base + reg); +} + + /* ========== mem2mem callbacks ========== */ + +static void sh_veu_job_abort(void *priv) +{ + struct sh_veu_dev *veu = priv; + + /* Will cancel the transaction in the next interrupt handler */ + veu->aborting = true; +} + +static void sh_veu_lock(void *priv) +{ + struct sh_veu_dev *veu = priv; + + mutex_lock(&veu->fop_lock); +} + +static void sh_veu_unlock(void *priv) +{ + struct sh_veu_dev *veu = priv; + + mutex_unlock(&veu->fop_lock); +} + +static void sh_veu_process(struct sh_veu_dev *veu, + struct vb2_buffer *src_buf, + struct vb2_buffer *dst_buf) +{ + dma_addr_t addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + + sh_veu_reg_write(veu, VEU_DAYR, addr + veu->vfmt_out.offset_y); + sh_veu_reg_write(veu, VEU_DACR, veu->vfmt_out.offset_c ? + addr + veu->vfmt_out.offset_c : 0); + dev_dbg(veu->dev, "%s(): dst base %x, y: %x, c: %x\n", __func__, + addr, veu->vfmt_out.offset_y, veu->vfmt_out.offset_c); + + addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); + sh_veu_reg_write(veu, VEU_SAYR, addr + veu->vfmt_in.offset_y); + sh_veu_reg_write(veu, VEU_SACR, veu->vfmt_in.offset_c ? + addr + veu->vfmt_in.offset_c : 0); + dev_dbg(veu->dev, "%s(): src base %x, y: %x, c: %x\n", __func__, + addr, veu->vfmt_in.offset_y, veu->vfmt_in.offset_c); + + sh_veu_reg_write(veu, VEU_STR, 1); + + sh_veu_reg_write(veu, VEU_EIER, 1); /* enable interrupt in VEU */ +} + +/** + * sh_veu_device_run() - prepares and starts the device + * + * This will be called by the framework when it decides to schedule a particular + * instance. + */ +static void sh_veu_device_run(void *priv) +{ + struct sh_veu_dev *veu = priv; + struct vb2_buffer *src_buf, *dst_buf; + + src_buf = v4l2_m2m_next_src_buf(veu->m2m_ctx); + dst_buf = v4l2_m2m_next_dst_buf(veu->m2m_ctx); + + if (src_buf && dst_buf) + sh_veu_process(veu, src_buf, dst_buf); +} + + /* ========== video ioctls ========== */ + +static bool sh_veu_is_streamer(struct sh_veu_dev *veu, struct sh_veu_file *veu_file, + enum v4l2_buf_type type) +{ + return (type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + veu_file == veu->capture) || + (type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + veu_file == veu->output); +} + +static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); + +/* + * It is not unusual to have video nodes open()ed multiple times. While some + * V4L2 operations are non-intrusive, like querying formats and various + * parameters, others, like setting formats, starting and stopping streaming, + * queuing and dequeuing buffers, directly affect hardware configuration and / + * or execution. This function verifies availability of the requested interface + * and, if available, reserves it for the requesting user. + */ +static int sh_veu_stream_init(struct sh_veu_dev *veu, struct sh_veu_file *veu_file, + enum v4l2_buf_type type) +{ + struct sh_veu_file **stream; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + stream = &veu->capture; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + stream = &veu->output; + break; + default: + return -EINVAL; + } + + if (*stream == veu_file) + return 0; + + if (*stream) + return -EBUSY; + + *stream = veu_file; + + return 0; +} + +static int sh_veu_context_init(struct sh_veu_dev *veu) +{ + if (veu->m2m_ctx) + return 0; + + veu->m2m_ctx = v4l2_m2m_ctx_init(veu->m2m_dev, veu, + sh_veu_queue_init); + + if (IS_ERR(veu->m2m_ctx)) + return PTR_ERR(veu->m2m_ctx); + + return 0; +} + +static int sh_veu_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->driver, "sh-veu", sizeof(cap->driver)); + strlcpy(cap->card, "sh-mobile VEU", sizeof(cap->card)); + strlcpy(cap->bus_info, "platform:sh-veu", sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int sh_veu_enum_fmt(struct v4l2_fmtdesc *f, const int *fmt, int fmt_num) +{ + if (f->index >= fmt_num) + return -EINVAL; + + strlcpy(f->description, sh_veu_fmt[fmt[f->index]].name, sizeof(f->description)); + f->pixelformat = sh_veu_fmt[fmt[f->index]].fourcc; + return 0; +} + +static int sh_veu_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return sh_veu_enum_fmt(f, sh_veu_fmt_out, ARRAY_SIZE(sh_veu_fmt_out)); +} + +static int sh_veu_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return sh_veu_enum_fmt(f, sh_veu_fmt_in, ARRAY_SIZE(sh_veu_fmt_in)); +} + +static struct sh_veu_vfmt *sh_veu_get_vfmt(struct sh_veu_dev *veu, + enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &veu->vfmt_out; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &veu->vfmt_in; + default: + return NULL; + } +} + +static int sh_veu_g_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + struct sh_veu_dev *veu = veu_file->veu_dev; + struct sh_veu_vfmt *vfmt; + + vfmt = sh_veu_get_vfmt(veu, f->type); + + pix->width = vfmt->frame.width; + pix->height = vfmt->frame.height; + pix->field = V4L2_FIELD_NONE; + pix->pixelformat = vfmt->fmt->fourcc; + pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); + pix->bytesperline = vfmt->bytesperline; + pix->sizeimage = vfmt->bytesperline * pix->height * + vfmt->fmt->depth / vfmt->fmt->ydepth; + pix->priv = 0; + dev_dbg(veu->dev, "%s(): type: %d, size %u @ %ux%u, fmt %x\n", __func__, + f->type, pix->sizeimage, pix->width, pix->height, pix->pixelformat); + + return 0; +} + +static int sh_veu_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + return sh_veu_g_fmt(priv, f); +} + +static int sh_veu_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + return sh_veu_g_fmt(priv, f); +} + +static int sh_veu_try_fmt(struct v4l2_format *f, const struct sh_veu_format *fmt) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + unsigned int y_bytes_used; + + /* + * V4L2 specification suggests, that the driver should correct the + * format struct if any of the dimensions is unsupported + */ + switch (pix->field) { + default: + case V4L2_FIELD_ANY: + pix->field = V4L2_FIELD_NONE; + /* fall through: continue handling V4L2_FIELD_NONE */ + case V4L2_FIELD_NONE: + break; + } + + v4l_bound_align_image(&pix->width, MIN_W, MAX_W, ALIGN_W, + &pix->height, MIN_H, MAX_H, 0, 0); + + y_bytes_used = (pix->width * fmt->ydepth) >> 3; + + if (pix->bytesperline < y_bytes_used) + pix->bytesperline = y_bytes_used; + pix->sizeimage = pix->height * pix->bytesperline * fmt->depth / fmt->ydepth; + + pix->pixelformat = fmt->fourcc; + pix->colorspace = sh_veu_4cc2cspace(pix->pixelformat); + pix->priv = 0; + + pr_debug("%s(): type: %d, size %u\n", __func__, f->type, pix->sizeimage); + + return 0; +} + +static const struct sh_veu_format *sh_veu_find_fmt(const struct v4l2_format *f) +{ + const int *fmt; + int i, n, dflt; + + pr_debug("%s(%d;%d)\n", __func__, f->type, f->fmt.pix.field); + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + fmt = sh_veu_fmt_out; + n = ARRAY_SIZE(sh_veu_fmt_out); + dflt = DEFAULT_OUT_FMTIDX; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + default: + fmt = sh_veu_fmt_in; + n = ARRAY_SIZE(sh_veu_fmt_in); + dflt = DEFAULT_IN_FMTIDX; + break; + } + + for (i = 0; i < n; i++) + if (sh_veu_fmt[fmt[i]].fourcc == f->fmt.pix.pixelformat) + return &sh_veu_fmt[fmt[i]]; + + return &sh_veu_fmt[dflt]; +} + +static int sh_veu_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct sh_veu_format *fmt; + + fmt = sh_veu_find_fmt(f); + if (!fmt) + /* wrong buffer type */ + return -EINVAL; + + return sh_veu_try_fmt(f, fmt); +} + +static int sh_veu_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct sh_veu_format *fmt; + + fmt = sh_veu_find_fmt(f); + if (!fmt) + /* wrong buffer type */ + return -EINVAL; + + return sh_veu_try_fmt(f, fmt); +} + +static void sh_veu_colour_offset(struct sh_veu_dev *veu, struct sh_veu_vfmt *vfmt) +{ + /* dst_left and dst_top validity will be verified in CROP / COMPOSE */ + unsigned int left = vfmt->frame.left & ~0x03; + unsigned int top = vfmt->frame.top; + dma_addr_t offset = ((left * veu->vfmt_out.fmt->depth) >> 3) + + top * veu->vfmt_out.bytesperline; + unsigned int y_line; + + vfmt->offset_y = offset; + + switch (vfmt->fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV24: + y_line = ALIGN(vfmt->frame.width, 16); + vfmt->offset_c = offset + y_line * vfmt->frame.height; + break; + case V4L2_PIX_FMT_RGB332: + case V4L2_PIX_FMT_RGB444: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_BGR666: + case V4L2_PIX_FMT_RGB24: + vfmt->offset_c = 0; + break; + default: + BUG(); + } +} + +static int sh_veu_s_fmt(struct sh_veu_file *veu_file, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + struct sh_veu_dev *veu = veu_file->veu_dev; + struct sh_veu_vfmt *vfmt; + struct vb2_queue *vq; + int ret = sh_veu_context_init(veu); + if (ret < 0) + return ret; + + vq = v4l2_m2m_get_vq(veu->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + if (vb2_is_busy(vq)) { + v4l2_err(&veu_file->veu_dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + vfmt = sh_veu_get_vfmt(veu, f->type); + /* called after try_fmt(), hence vfmt != NULL. Implicit BUG_ON() below */ + + vfmt->fmt = sh_veu_find_fmt(f); + /* vfmt->fmt != NULL following the same argument as above */ + vfmt->frame.width = pix->width; + vfmt->frame.height = pix->height; + vfmt->bytesperline = pix->bytesperline; + + sh_veu_colour_offset(veu, vfmt); + + /* + * We could also verify and require configuration only if any parameters + * actually have changed, but it is unlikely, that the user requests the + * same configuration several times without closing the device. + */ + veu_file->cfg_needed = true; + + dev_dbg(veu->dev, + "Setting format for type %d, wxh: %dx%d, fmt: %x\n", + f->type, pix->width, pix->height, vfmt->fmt->fourcc); + + return 0; +} + +static int sh_veu_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret = sh_veu_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + return sh_veu_s_fmt(priv, f); +} + +static int sh_veu_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + int ret = sh_veu_try_fmt_vid_out(file, priv, f); + if (ret) + return ret; + + return sh_veu_s_fmt(priv, f); +} + +static int sh_veu_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct sh_veu_file *veu_file = priv; + struct sh_veu_dev *veu = veu_file->veu_dev; + int ret = sh_veu_context_init(veu); + if (ret < 0) + return ret; + + ret = sh_veu_stream_init(veu, veu_file, reqbufs->type); + if (ret < 0) + return ret; + + return v4l2_m2m_reqbufs(file, veu->m2m_ctx, reqbufs); +} + +static int sh_veu_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_querybuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static int sh_veu_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type); + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_qbuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static int sh_veu_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct sh_veu_file *veu_file = priv; + + dev_dbg(veu_file->veu_dev->dev, "%s(%d)\n", __func__, buf->type); + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, buf->type)) + return -EBUSY; + + return v4l2_m2m_dqbuf(file, veu_file->veu_dev->m2m_ctx, buf); +} + +static void sh_veu_calc_scale(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out, + u32 *mant, u32 *frac, u32 *rep) +{ + u32 fixpoint; + + /* calculate FRAC and MANT */ + *rep = *mant = *frac = 0; + + if (size_in == size_out) { + if (crop_out != size_out) + *mant = 1; /* needed for cropping */ + return; + } + + /* VEU2H special upscale */ + if (veu->is_2h && size_out > size_in) { + u32 fixpoint = (4096 * size_in) / size_out; + *mant = fixpoint / 4096; + *frac = (fixpoint - (*mant * 4096)) & ~0x07; + + switch (*frac) { + case 0x800: + *rep = 1; + break; + case 0x400: + *rep = 3; + break; + case 0x200: + *rep = 7; + break; + } + if (*rep) + return; + } + + fixpoint = (4096 * (size_in - 1)) / (size_out + 1); + *mant = fixpoint / 4096; + *frac = fixpoint - (*mant * 4096); + + if (*frac & 0x07) { + /* + * FIXME: do we really have to round down twice in the + * up-scaling case? + */ + *frac &= ~0x07; + if (size_out > size_in) + *frac -= 8; /* round down if scaling up */ + else + *frac += 8; /* round up if scaling down */ + } +} + +static unsigned long sh_veu_scale_v(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out) +{ + u32 mant, frac, value, rep; + + sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep); + + /* set scale */ + value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff0000) | + (((mant << 12) | frac) << 16); + + sh_veu_reg_write(veu, VEU_RFCR, value); + + /* set clip */ + value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff0000) | + (((rep << 12) | crop_out) << 16); + + sh_veu_reg_write(veu, VEU_RFSR, value); + + return ALIGN((size_in * crop_out) / size_out, 4); +} + +static unsigned long sh_veu_scale_h(struct sh_veu_dev *veu, + int size_in, int size_out, int crop_out) +{ + u32 mant, frac, value, rep; + + sh_veu_calc_scale(veu, size_in, size_out, crop_out, &mant, &frac, &rep); + + /* set scale */ + value = (sh_veu_reg_read(veu, VEU_RFCR) & ~0xffff) | + (mant << 12) | frac; + + sh_veu_reg_write(veu, VEU_RFCR, value); + + /* set clip */ + value = (sh_veu_reg_read(veu, VEU_RFSR) & ~0xffff) | + (rep << 12) | crop_out; + + sh_veu_reg_write(veu, VEU_RFSR, value); + + return ALIGN((size_in * crop_out) / size_out, 4); +} + +static void sh_veu_configure(struct sh_veu_dev *veu) +{ + u32 src_width, src_stride, src_height; + u32 dst_width, dst_stride, dst_height; + u32 real_w, real_h; + + /* reset VEU */ + sh_veu_reg_write(veu, VEU_BSRR, 0x100); + + src_width = veu->vfmt_in.frame.width; + src_height = veu->vfmt_in.frame.height; + src_stride = ALIGN(veu->vfmt_in.frame.width, 16); + + dst_width = real_w = veu->vfmt_out.frame.width; + dst_height = real_h = veu->vfmt_out.frame.height; + /* Datasheet is unclear - whether it's always number of bytes or not */ + dst_stride = veu->vfmt_out.bytesperline; + + /* + * So far real_w == dst_width && real_h == dst_height, but it wasn't + * necessarily the case in the original vidix driver, so, it may change + * here in the future too. + */ + src_width = sh_veu_scale_h(veu, src_width, real_w, dst_width); + src_height = sh_veu_scale_v(veu, src_height, real_h, dst_height); + + sh_veu_reg_write(veu, VEU_SWR, src_stride); + sh_veu_reg_write(veu, VEU_SSR, src_width | (src_height << 16)); + sh_veu_reg_write(veu, VEU_BSSR, 0); /* not using bundle mode */ + + sh_veu_reg_write(veu, VEU_EDWR, dst_stride); + sh_veu_reg_write(veu, VEU_DACR, 0); /* unused for RGB */ + + sh_veu_reg_write(veu, VEU_SWPR, 0x67); + sh_veu_reg_write(veu, VEU_TRCR, (6 << 16) | (0 << 14) | 2 | 4); + + if (veu->is_2h) { + sh_veu_reg_write(veu, VEU_MCR00, 0x0cc5); + sh_veu_reg_write(veu, VEU_MCR01, 0x0950); + sh_veu_reg_write(veu, VEU_MCR02, 0x0000); + + sh_veu_reg_write(veu, VEU_MCR10, 0x397f); + sh_veu_reg_write(veu, VEU_MCR11, 0x0950); + sh_veu_reg_write(veu, VEU_MCR12, 0x3ccd); + + sh_veu_reg_write(veu, VEU_MCR20, 0x0000); + sh_veu_reg_write(veu, VEU_MCR21, 0x0950); + sh_veu_reg_write(veu, VEU_MCR22, 0x1023); + + sh_veu_reg_write(veu, VEU_COFFR, 0x00800010); + } +} + +static int sh_veu_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type)) + return -EBUSY; + + if (veu_file->cfg_needed) { + struct sh_veu_dev *veu = veu_file->veu_dev; + veu_file->cfg_needed = false; + sh_veu_configure(veu_file->veu_dev); + veu->xaction = 0; + veu->aborting = false; + } + + return v4l2_m2m_streamon(file, veu_file->veu_dev->m2m_ctx, type); +} + +static int sh_veu_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct sh_veu_file *veu_file = priv; + + if (!sh_veu_is_streamer(veu_file->veu_dev, veu_file, type)) + return -EBUSY; + + return v4l2_m2m_streamoff(file, veu_file->veu_dev->m2m_ctx, type); +} + +static const struct v4l2_ioctl_ops sh_veu_ioctl_ops = { + .vidioc_querycap = sh_veu_querycap, + + .vidioc_enum_fmt_vid_cap = sh_veu_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = sh_veu_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = sh_veu_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = sh_veu_s_fmt_vid_cap, + + .vidioc_enum_fmt_vid_out = sh_veu_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = sh_veu_g_fmt_vid_out, + .vidioc_try_fmt_vid_out = sh_veu_try_fmt_vid_out, + .vidioc_s_fmt_vid_out = sh_veu_s_fmt_vid_out, + + .vidioc_reqbufs = sh_veu_reqbufs, + .vidioc_querybuf = sh_veu_querybuf, + + .vidioc_qbuf = sh_veu_qbuf, + .vidioc_dqbuf = sh_veu_dqbuf, + + .vidioc_streamon = sh_veu_streamon, + .vidioc_streamoff = sh_veu_streamoff, +}; + + /* ========== Queue operations ========== */ + +static int sh_veu_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *f, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vq); + struct sh_veu_vfmt *vfmt; + unsigned int size, count = *nbuffers; + + if (f) { + const struct v4l2_pix_format *pix = &f->fmt.pix; + const struct sh_veu_format *fmt = sh_veu_find_fmt(f); + struct v4l2_format ftmp = *f; + + if (fmt->fourcc != pix->pixelformat) + return -EINVAL; + sh_veu_try_fmt(&ftmp, fmt); + if (ftmp.fmt.pix.width != pix->width || + ftmp.fmt.pix.height != pix->height) + return -EINVAL; + size = pix->bytesperline ? pix->bytesperline * pix->height : + pix->width * pix->height * fmt->depth >> 3; + } else { + vfmt = sh_veu_get_vfmt(veu, vq->type); + size = vfmt->bytesperline * vfmt->frame.height; + } + + if (count < 2) + *nbuffers = count = 2; + + if (size * count > VIDEO_MEM_LIMIT) { + count = VIDEO_MEM_LIMIT / size; + *nbuffers = count; + } + + *nplanes = 1; + sizes[0] = size; + alloc_ctxs[0] = veu->alloc_ctx; + + dev_dbg(veu->dev, "get %d buffer(s) of size %d each.\n", count, size); + + return 0; +} + +static int sh_veu_buf_prepare(struct vb2_buffer *vb) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue); + struct sh_veu_vfmt *vfmt; + unsigned int sizeimage; + + vfmt = sh_veu_get_vfmt(veu, vb->vb2_queue->type); + sizeimage = vfmt->bytesperline * vfmt->frame.height * + vfmt->fmt->depth / vfmt->fmt->ydepth; + + if (vb2_plane_size(vb, 0) < sizeimage) { + dev_dbg(veu->dev, "%s data will not fit into plane (%lu < %u)\n", + __func__, vb2_plane_size(vb, 0), sizeimage); + return -EINVAL; + } + + vb2_set_plane_payload(vb, 0, sizeimage); + + return 0; +} + +static void sh_veu_buf_queue(struct vb2_buffer *vb) +{ + struct sh_veu_dev *veu = vb2_get_drv_priv(vb->vb2_queue); + dev_dbg(veu->dev, "%s(%d)\n", __func__, vb->v4l2_buf.type); + v4l2_m2m_buf_queue(veu->m2m_ctx, vb); +} + +static void sh_veu_wait_prepare(struct vb2_queue *q) +{ + sh_veu_unlock(vb2_get_drv_priv(q)); +} + +static void sh_veu_wait_finish(struct vb2_queue *q) +{ + sh_veu_lock(vb2_get_drv_priv(q)); +} + +static const struct vb2_ops sh_veu_qops = { + .queue_setup = sh_veu_queue_setup, + .buf_prepare = sh_veu_buf_prepare, + .buf_queue = sh_veu_buf_queue, + .wait_prepare = sh_veu_wait_prepare, + .wait_finish = sh_veu_wait_finish, +}; + +static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = priv; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + src_vq->ops = &sh_veu_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + + ret = vb2_queue_init(src_vq); + if (ret < 0) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->drv_priv = priv; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &sh_veu_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + + return vb2_queue_init(dst_vq); +} + + /* ========== File operations ========== */ + +static int sh_veu_open(struct file *file) +{ + struct sh_veu_dev *veu = video_drvdata(file); + struct sh_veu_file *veu_file; + + veu_file = kzalloc(sizeof(*veu_file), GFP_KERNEL); + if (!veu_file) + return -ENOMEM; + + veu_file->veu_dev = veu; + veu_file->cfg_needed = true; + + file->private_data = veu_file; + + pm_runtime_get_sync(veu->dev); + + dev_dbg(veu->dev, "Created instance %p\n", veu_file); + + return 0; +} + +static int sh_veu_release(struct file *file) +{ + struct sh_veu_dev *veu = video_drvdata(file); + struct sh_veu_file *veu_file = file->private_data; + + dev_dbg(veu->dev, "Releasing instance %p\n", veu_file); + + pm_runtime_put(veu->dev); + + if (veu_file == veu->capture) { + veu->capture = NULL; + vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE)); + } + + if (veu_file == veu->output) { + veu->output = NULL; + vb2_queue_release(v4l2_m2m_get_vq(veu->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT)); + } + + if (!veu->output && !veu->capture && veu->m2m_ctx) { + v4l2_m2m_ctx_release(veu->m2m_ctx); + veu->m2m_ctx = NULL; + } + + kfree(veu_file); + + return 0; +} + +static unsigned int sh_veu_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct sh_veu_file *veu_file = file->private_data; + + return v4l2_m2m_poll(file, veu_file->veu_dev->m2m_ctx, wait); +} + +static int sh_veu_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sh_veu_file *veu_file = file->private_data; + + return v4l2_m2m_mmap(file, veu_file->veu_dev->m2m_ctx, vma); +} + +static const struct v4l2_file_operations sh_veu_fops = { + .owner = THIS_MODULE, + .open = sh_veu_open, + .release = sh_veu_release, + .poll = sh_veu_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = sh_veu_mmap, +}; + +static const struct video_device sh_veu_videodev = { + .name = "sh-veu", + .fops = &sh_veu_fops, + .ioctl_ops = &sh_veu_ioctl_ops, + .minor = -1, + .release = video_device_release_empty, + .vfl_dir = VFL_DIR_M2M, +}; + +static const struct v4l2_m2m_ops sh_veu_m2m_ops = { + .device_run = sh_veu_device_run, + .job_abort = sh_veu_job_abort, +}; + +static irqreturn_t sh_veu_bh(int irq, void *dev_id) +{ + struct sh_veu_dev *veu = dev_id; + + if (veu->xaction == MEM2MEM_DEF_TRANSLEN || veu->aborting) { + v4l2_m2m_job_finish(veu->m2m_dev, veu->m2m_ctx); + veu->xaction = 0; + } else { + sh_veu_device_run(veu); + } + + return IRQ_HANDLED; +} + +static irqreturn_t sh_veu_isr(int irq, void *dev_id) +{ + struct sh_veu_dev *veu = dev_id; + struct vb2_buffer *dst; + struct vb2_buffer *src; + u32 status = sh_veu_reg_read(veu, VEU_EVTR); + + /* bundle read mode not used */ + if (!(status & 1)) + return IRQ_NONE; + + /* disable interrupt in VEU */ + sh_veu_reg_write(veu, VEU_EIER, 0); + /* halt operation */ + sh_veu_reg_write(veu, VEU_STR, 0); + /* ack int, write 0 to clear bits */ + sh_veu_reg_write(veu, VEU_EVTR, status & ~1); + + /* conversion completed */ + dst = v4l2_m2m_dst_buf_remove(veu->m2m_ctx); + src = v4l2_m2m_src_buf_remove(veu->m2m_ctx); + if (!src || !dst) + return IRQ_NONE; + + spin_lock(&veu->lock); + v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); + v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); + spin_unlock(&veu->lock); + + veu->xaction++; + + if (!veu->aborting) + return IRQ_WAKE_THREAD; + + return IRQ_HANDLED; +} + +static int __devinit sh_veu_probe(struct platform_device *pdev) +{ + struct sh_veu_dev *veu; + struct resource *reg_res; + struct video_device *vdev; + int irq, ret; + + reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + + if (!reg_res || irq <= 0) { + dev_err(&pdev->dev, "Insufficient VEU platform information.\n"); + return -ENODEV; + } + + veu = devm_kzalloc(&pdev->dev, sizeof(*veu), GFP_KERNEL); + if (!veu) + return -ENOMEM; + + veu->is_2h = resource_size(reg_res) == 0x22c; + + veu->base = devm_request_and_ioremap(&pdev->dev, reg_res); + if (!veu->base) + return -ENOMEM; + + ret = devm_request_threaded_irq(&pdev->dev, irq, sh_veu_isr, sh_veu_bh, + 0, "veu", veu); + if (ret < 0) + return ret; + + ret = v4l2_device_register(&pdev->dev, &veu->v4l2_dev); + if (ret < 0) { + dev_err(&pdev->dev, "Error registering v4l2 device\n"); + return ret; + } + + vdev = &veu->vdev; + + veu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(veu->alloc_ctx)) { + ret = PTR_ERR(veu->alloc_ctx); + goto einitctx; + } + + *vdev = sh_veu_videodev; + spin_lock_init(&veu->lock); + mutex_init(&veu->fop_lock); + vdev->lock = &veu->fop_lock; + + video_set_drvdata(vdev, veu); + + veu->dev = &pdev->dev; + veu->vfmt_out = DEFAULT_OUT_VFMT; + veu->vfmt_in = DEFAULT_IN_VFMT; + + veu->m2m_dev = v4l2_m2m_init(&sh_veu_m2m_ops); + if (IS_ERR(veu->m2m_dev)) { + ret = PTR_ERR(veu->m2m_dev); + v4l2_err(&veu->v4l2_dev, "Failed to init mem2mem device: %d\n", ret); + goto em2minit; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + pm_runtime_suspend(&pdev->dev); + if (ret < 0) + goto evidreg; + + return ret; + +evidreg: + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(veu->m2m_dev); +em2minit: + vb2_dma_contig_cleanup_ctx(veu->alloc_ctx); +einitctx: + v4l2_device_unregister(&veu->v4l2_dev); + return ret; +} + +static int __devexit sh_veu_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct sh_veu_dev *veu = container_of(v4l2_dev, + struct sh_veu_dev, v4l2_dev); + + video_unregister_device(&veu->vdev); + pm_runtime_disable(&pdev->dev); + v4l2_m2m_release(veu->m2m_dev); + vb2_dma_contig_cleanup_ctx(veu->alloc_ctx); + v4l2_device_unregister(&veu->v4l2_dev); + + return 0; +} + +static struct platform_driver __refdata sh_veu_pdrv = { + .remove = __devexit_p(sh_veu_remove), + .driver = { + .name = "sh_veu", + .owner = THIS_MODULE, + }, +}; + +static int __init sh_veu_init(void) +{ + return platform_driver_probe(&sh_veu_pdrv, sh_veu_probe); +} + +static void __exit sh_veu_exit(void) +{ + platform_driver_unregister(&sh_veu_pdrv); +} + +module_init(sh_veu_init); +module_exit(sh_veu_exit); + +MODULE_DESCRIPTION("sh-mobile VEU mem2mem driver"); +MODULE_AUTHOR("Guennadi Liakhovetski, "); +MODULE_LICENSE("GPL v2"); From c9a8d89673276a8a9410c68521c0b9523ed10493 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 27 Sep 2012 06:40:30 -0300 Subject: [PATCH 0236/4607] [media] media: soc-camera: use managed devm_regulator_bulk_get() Using device-managed devm_regulator_bulk_get() eliminates the need to release regulators explicitly. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 54da3a5f900c..a8ca956e7a40 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1139,8 +1139,8 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) return ret; - ret = regulator_bulk_get(icd->pdev, icl->num_regulators, - icl->regulators); + ret = devm_regulator_bulk_get(icd->pdev, icl->num_regulators, + icl->regulators); if (ret < 0) goto ereg; @@ -1244,7 +1244,6 @@ eadddev: evdc: ici->ops->remove(icd); eadd: - regulator_bulk_free(icl->num_regulators, icl->regulators); ereg: v4l2_ctrl_handler_free(&icd->ctrl_handler); return ret; @@ -1278,8 +1277,6 @@ static int soc_camera_remove(struct soc_camera_device *icd) } soc_camera_free_user_formats(icd); - regulator_bulk_free(icl->num_regulators, icl->regulators); - return 0; } From 57f1b1c8fd705f48eb9fcd87d054e1dc46b1cedc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 14 Sep 2012 12:00:24 -0300 Subject: [PATCH 0237/4607] [media] media: sh-mobile-ceu-camera: runtime PM suspending doesn't have to be synchronous In both error and clean up cases there is no need to wait for runtime PM to finish suspending the device. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 2d8861c0e8f2..bf66cb4b63e0 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -572,7 +572,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) ret = v4l2_subdev_call(csi2_sd, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - pm_runtime_put_sync(ici->v4l2_dev.dev); + pm_runtime_put(ici->v4l2_dev.dev); return ret; } @@ -612,7 +612,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) } spin_unlock_irq(&pcdev->lock); - pm_runtime_put_sync(ici->v4l2_dev.dev); + pm_runtime_put(ici->v4l2_dev.dev); dev_info(icd->parent, "SuperH Mobile CEU driver detached from camera %d\n", From 454547fb8217ac82de82fdffe0c2a41d8cd47bf4 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 5 Oct 2012 12:33:45 -0300 Subject: [PATCH 0238/4607] [media] media: soc-camera: update documentation Update soc-camera documentation to reflect the current camera host API and the use of the common V4L2 subdev API. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/soc-camera.txt | 146 ++++++++++++----------- 1 file changed, 75 insertions(+), 71 deletions(-) diff --git a/Documentation/video4linux/soc-camera.txt b/Documentation/video4linux/soc-camera.txt index 3f87c7da4ca2..f62fcdbc8b9f 100644 --- a/Documentation/video4linux/soc-camera.txt +++ b/Documentation/video4linux/soc-camera.txt @@ -9,32 +9,36 @@ The following terms are used in this document: of connecting to a variety of systems and interfaces, typically uses i2c for control and configuration, and a parallel or a serial bus for data. - camera host - an interface, to which a camera is connected. Typically a - specialised interface, present on many SoCs, e.g., PXA27x and PXA3xx, SuperH, + specialised interface, present on many SoCs, e.g. PXA27x and PXA3xx, SuperH, AVR32, i.MX27, i.MX31. - camera host bus - a connection between a camera host and a camera. Can be - parallel or serial, consists of data and control lines, e.g., clock, vertical + parallel or serial, consists of data and control lines, e.g. clock, vertical and horizontal synchronization signals. Purpose of the soc-camera subsystem ----------------------------------- -The soc-camera subsystem provides a unified API between camera host drivers and -camera sensor drivers. It implements a V4L2 interface to the user, currently -only the mmap method is supported. +The soc-camera subsystem initially provided a unified API between camera host +drivers and camera sensor drivers. Later the soc-camera sensor API has been +replaced with the V4L2 standard subdev API. This also made camera driver re-use +with non-soc-camera hosts possible. The camera host API to the soc-camera core +has been preserved. -This subsystem has been written to connect drivers for System-on-Chip (SoC) -video capture interfaces with drivers for CMOS camera sensor chips to enable -the reuse of sensor drivers with various hosts. The subsystem has been designed -to support multiple camera host interfaces and multiple cameras per interface, -although most applications have only one camera sensor. +Soc-camera implements a V4L2 interface to the user, currently only the "mmap" +method is supported by host drivers. However, the soc-camera core also provides +support for the "read" method. + +The subsystem has been designed to support multiple camera host interfaces and +multiple cameras per interface, although most applications have only one camera +sensor. Existing drivers ---------------- -As of 2.6.27-rc4 there are two host drivers in the mainline: pxa_camera.c for -PXA27x SoCs and sh_mobile_ceu_camera.c for SuperH SoCs, and four sensor drivers: -mt9m001.c, mt9m111.c, mt9v022.c and a generic soc_camera_platform.c driver. This -list is not supposed to be updated, look for more examples in your tree. +As of 3.7 there are seven host drivers in the mainline: atmel-isi.c, +mx1_camera.c (broken, scheduled for removal), mx2_camera.c, mx3_camera.c, +omap1_camera.c, pxa_camera.c, sh_mobile_ceu_camera.c, and multiple sensor +drivers under drivers/media/i2c/soc_camera/. Camera host API --------------- @@ -45,38 +49,37 @@ soc_camera_host_register(struct soc_camera_host *); function. The host object can be initialized as follows: -static struct soc_camera_host pxa_soc_camera_host = { - .drv_name = PXA_CAM_DRV_NAME, - .ops = &pxa_soc_camera_host_ops, -}; + struct soc_camera_host *ici; + ici->drv_name = DRV_NAME; + ici->ops = &camera_host_ops; + ici->priv = pcdev; + ici->v4l2_dev.dev = &pdev->dev; + ici->nr = pdev->id; All camera host methods are passed in a struct soc_camera_host_ops: -static struct soc_camera_host_ops pxa_soc_camera_host_ops = { +static struct soc_camera_host_ops camera_host_ops = { .owner = THIS_MODULE, - .add = pxa_camera_add_device, - .remove = pxa_camera_remove_device, - .suspend = pxa_camera_suspend, - .resume = pxa_camera_resume, - .set_fmt_cap = pxa_camera_set_fmt_cap, - .try_fmt_cap = pxa_camera_try_fmt_cap, - .init_videobuf = pxa_camera_init_videobuf, - .reqbufs = pxa_camera_reqbufs, - .poll = pxa_camera_poll, - .querycap = pxa_camera_querycap, - .try_bus_param = pxa_camera_try_bus_param, - .set_bus_param = pxa_camera_set_bus_param, + .add = camera_add_device, + .remove = camera_remove_device, + .set_fmt = camera_set_fmt_cap, + .try_fmt = camera_try_fmt_cap, + .init_videobuf2 = camera_init_videobuf2, + .poll = camera_poll, + .querycap = camera_querycap, + .set_bus_param = camera_set_bus_param, + /* The rest of host operations are optional */ }; .add and .remove methods are called when a sensor is attached to or detached -from the host, apart from performing host-internal tasks they shall also call -sensor driver's .init and .release methods respectively. .suspend and .resume -methods implement host's power-management functionality and its their -responsibility to call respective sensor's methods. .try_bus_param and -.set_bus_param are used to negotiate physical connection parameters between the -host and the sensor. .init_videobuf is called by soc-camera core when a -video-device is opened, further video-buffer management is implemented completely -by the specific camera host driver. The rest of the methods are called from +from the host. .set_bus_param is used to configure physical connection +parameters between the host and the sensor. .init_videobuf2 is called by +soc-camera core when a video-device is opened, the host driver would typically +call vb2_queue_init() in this method. Further video-buffer management is +implemented completely by the specific camera host driver. If the host driver +supports non-standard pixel format conversion, it should implement a +.get_formats and, possibly, a .put_formats operations. See below for more +details about format conversion. The rest of the methods are called from respective V4L2 operations. Camera API @@ -84,37 +87,21 @@ Camera API Sensor drivers can use struct soc_camera_link, typically provided by the platform, and used to specify to which camera host bus the sensor is connected, -and arbitrarily provide platform .power and .reset methods for the camera. -soc_camera_device_register() and soc_camera_device_unregister() functions are -used to add a sensor driver to or remove one from the system. The registration -function takes a pointer to struct soc_camera_device as the only parameter. -This struct can be initialized as follows: - - /* link to driver operations */ - icd->ops = &mt9m001_ops; - /* link to the underlying physical (e.g., i2c) device */ - icd->control = &client->dev; - /* window geometry */ - icd->x_min = 20; - icd->y_min = 12; - icd->x_current = 20; - icd->y_current = 12; - icd->width_min = 48; - icd->width_max = 1280; - icd->height_min = 32; - icd->height_max = 1024; - icd->y_skip_top = 1; - /* camera bus ID, typically obtained from platform data */ - icd->iface = icl->bus_id; - -struct soc_camera_ops provides .probe and .remove methods, which are called by -the soc-camera core, when a camera is matched against or removed from a camera -host bus, .init, .release, .suspend, and .resume are called from the camera host -driver as discussed above. Other members of this struct provide respective V4L2 -functionality. - -struct soc_camera_device also links to an array of struct soc_camera_data_format, -listing pixel formats, supported by the camera. +and optionally provide platform .power and .reset methods for the camera. This +struct is provided to the camera driver via the I2C client device platform data +and can be obtained, using the soc_camera_i2c_to_link() macro. Care should be +taken, when using soc_camera_vdev_to_subdev() and when accessing struct +soc_camera_device, using v4l2_get_subdev_hostdata(): both only work, when +running on an soc-camera host. The actual camera driver operation is implemented +using the V4L2 subdev API. Additionally soc-camera camera drivers can use +auxiliary soc-camera helper functions like soc_camera_power_on() and +soc_camera_power_off(), which switch regulators, provided by the platform and call +board-specific power switching methods. soc_camera_apply_board_flags() takes +camera bus configuration capability flags and applies any board transformations, +e.g. signal polarity inversion. soc_mbus_get_fmtdesc() can be used to obtain a +pixel format descriptor, corresponding to a certain media-bus pixel format code. +soc_camera_limit_side() can be used to restrict beginning and length of a frame +side, based on camera capabilities. VIDIOC_S_CROP and VIDIOC_S_FMT behaviour ---------------------------------------- @@ -153,8 +140,25 @@ implemented. User window geometry is kept in .user_width and .user_height fields in struct soc_camera_device and used by the soc-camera core and host drivers. The core updates these fields upon successful completion of a .s_fmt() call, but if these -fields change elsewhere, e.g., during .s_crop() processing, the host driver is +fields change elsewhere, e.g. during .s_crop() processing, the host driver is responsible for updating them. +Format conversion +----------------- + +V4L2 distinguishes between pixel formats, as they are stored in memory, and as +they are transferred over a media bus. Soc-camera provides support to +conveniently manage these formats. A table of standard transformations is +maintained by soc-camera core, which describes, what FOURCC pixel format will +be obtained, if a media-bus pixel format is stored in memory according to +certain rules. E.g. if V4L2_MBUS_FMT_YUYV8_2X8 data is sampled with 8 bits per +sample and stored in memory in the little-endian order with no gaps between +bytes, data in memory will represent the V4L2_PIX_FMT_YUYV FOURCC format. These +standard transformations will be used by soc-camera or by camera host drivers to +configure camera drivers to produce the FOURCC format, requested by the user, +using the VIDIOC_S_FMT ioctl(). Apart from those standard format conversions, +host drivers can also provide their own conversion rules by implementing a +.get_formats and, if required, a .put_formats methods. + -- Author: Guennadi Liakhovetski From cea4c9e46c4f656a81c93f09b5e3bde38bebb160 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 8 Oct 2012 10:02:55 -0300 Subject: [PATCH 0239/4607] [media] media: soc-camera: remove superfluous JPEG checking Explicit checks for the JPEG pixel format in soc_mbus_bytes_per_line() and soc_mbus_image_size() are superfluous, because also without them these functions will perform correctly. The former will return 0 based on packing == SOC_MBUS_PACKING_VARIABLE and the latter will simply multiply the user-provided line length by the image height to obtain a frame buffer size estimate. The original version of the "media: soc_camera: don't clear pix->sizeimage in JPEG mode" patch was correct and my amendment, adding these two checks was superfluous. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_mediabus.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/media/platform/soc_camera/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c index a397812635d6..89dce097a827 100644 --- a/drivers/media/platform/soc_camera/soc_mediabus.c +++ b/drivers/media/platform/soc_camera/soc_mediabus.c @@ -378,9 +378,6 @@ EXPORT_SYMBOL(soc_mbus_samples_per_pixel); s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) { - if (mf->fourcc == V4L2_PIX_FMT_JPEG) - return 0; - if (mf->layout != SOC_MBUS_LAYOUT_PACKED) return width * mf->bits_per_sample / 8; @@ -403,9 +400,6 @@ EXPORT_SYMBOL(soc_mbus_bytes_per_line); s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, u32 bytes_per_line, u32 height) { - if (mf->fourcc == V4L2_PIX_FMT_JPEG) - return 0; - if (mf->layout == SOC_MBUS_LAYOUT_PACKED) return bytes_per_line * height; From f8cabc3628f047fc45c62f0c4a38a88febbf8383 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 10 Oct 2012 05:02:30 -0300 Subject: [PATCH 0240/4607] [media] media: sh_mobile_csi2: use managed memory and resource allocations Use managed allocations to simplify error handling and clean up paths. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- .../platform/soc_camera/sh_mobile_csi2.c | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c index 05286500b4d4..c573be702641 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c +++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c @@ -318,23 +318,16 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev) return -EINVAL; } - priv = kzalloc(sizeof(struct sh_csi2), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL); if (!priv) return -ENOMEM; priv->irq = irq; - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "CSI2 register region already claimed\n"); - ret = -EBUSY; - goto ereqreg; - } - - priv->base = ioremap(res->start, resource_size(res)); + priv->base = devm_request_and_ioremap(&pdev->dev, res); if (!priv->base) { - ret = -ENXIO; dev_err(&pdev->dev, "Unable to ioremap CSI2 registers.\n"); - goto eremap; + return -ENXIO; } priv->pdev = pdev; @@ -357,11 +350,7 @@ static __devinit int sh_csi2_probe(struct platform_device *pdev) return 0; esdreg: - iounmap(priv->base); -eremap: - release_mem_region(res->start, resource_size(res)); -ereqreg: - kfree(priv); + platform_set_drvdata(pdev, NULL); return ret; } @@ -369,14 +358,10 @@ ereqreg: static __devexit int sh_csi2_remove(struct platform_device *pdev) { struct sh_csi2 *priv = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); v4l2_device_unregister_subdev(&priv->subdev); pm_runtime_disable(&pdev->dev); - iounmap(priv->base); - release_mem_region(res->start, resource_size(res)); platform_set_drvdata(pdev, NULL); - kfree(priv); return 0; } From a500a185462d08f63ec3c8a0ba3185d1b84cb85d Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 10 Oct 2012 05:18:51 -0300 Subject: [PATCH 0241/4607] [media] sh_mobile_ceu_camera: use managed memory and resource allocations Use managed allocations to simplify error handling and clean up paths. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- .../soc_camera/sh_mobile_ceu_camera.c | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index bf66cb4b63e0..27eeca15bbf0 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -2088,15 +2088,13 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (!res || (int)irq <= 0) { dev_err(&pdev->dev, "Not enough CEU platform resources.\n"); - err = -ENODEV; - goto exit; + return -ENODEV; } - pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); if (!pcdev) { dev_err(&pdev->dev, "Could not allocate pcdev\n"); - err = -ENOMEM; - goto exit; + return -ENOMEM; } INIT_LIST_HEAD(&pcdev->capture); @@ -2105,19 +2103,17 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->pdata = pdev->dev.platform_data; if (!pcdev->pdata) { - err = -EINVAL; dev_err(&pdev->dev, "CEU platform data not set.\n"); - goto exit_kfree; + return -EINVAL; } pcdev->max_width = pcdev->pdata->max_width ? : 2560; pcdev->max_height = pcdev->pdata->max_height ? : 1920; - base = ioremap_nocache(res->start, resource_size(res)); + base = devm_request_and_ioremap(&pdev->dev, res); if (!base) { - err = -ENXIO; dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n"); - goto exit_kfree; + return -ENXIO; } pcdev->irq = irq; @@ -2133,16 +2129,15 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) DMA_MEMORY_EXCLUSIVE); if (!err) { dev_err(&pdev->dev, "Unable to declare CEU memory.\n"); - err = -ENXIO; - goto exit_iounmap; + return -ENXIO; } pcdev->video_limit = resource_size(res); } /* request irq */ - err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED, - dev_name(&pdev->dev), pcdev); + err = devm_request_irq(&pdev->dev, pcdev->irq, sh_mobile_ceu_irq, + IRQF_DISABLED, dev_name(&pdev->dev), pcdev); if (err) { dev_err(&pdev->dev, "Unable to register CEU interrupt.\n"); goto exit_release_mem; @@ -2246,15 +2241,9 @@ exit_free_ctx: vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); exit_free_clk: pm_runtime_disable(&pdev->dev); - free_irq(pcdev->irq, pcdev); exit_release_mem: if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); -exit_iounmap: - iounmap(base); -exit_kfree: - kfree(pcdev); -exit: return err; } @@ -2267,10 +2256,8 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) soc_camera_host_unregister(soc_host); pm_runtime_disable(&pdev->dev); - free_irq(pcdev->irq, pcdev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); - iounmap(pcdev->base); vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); if (csi2_pdev && csi2_pdev->dev.driver) { struct module *csi2_drv = csi2_pdev->dev.driver->owner; @@ -2279,7 +2266,6 @@ static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) platform_device_put(csi2_pdev); module_put(csi2_drv); } - kfree(pcdev); return 0; } From b618b69c108b4fa792ff3dfe1ea95eeca7bc6af8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 29 Nov 2012 06:57:30 -0300 Subject: [PATCH 0242/4607] [media] MAINTAINERS: add entries for sh_veu and sh_vou V4L2 drivers sh_vou might be better described by "Odd Fixes," but mark it "Maintained" for now. sh_veu is a new driver and might see some development in the future. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index c4f8cf91c0db..7cea7ea606c7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6867,6 +6867,20 @@ F: drivers/media/radio/si470x/radio-si470x-common.c F: drivers/media/radio/si470x/radio-si470x.h F: drivers/media/radio/si470x/radio-si470x-usb.c +SH_VEU V4L2 MEM2MEM DRIVER +M: Guennadi Liakhovetski +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/platform/sh_veu.c +F: include/media/sh_veu.h + +SH_VOU V4L2 OUTPUT DRIVER +M: Guennadi Liakhovetski +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/platform/sh_vou.c +F: include/media/sh_vou.h + SIMPLE FIRMWARE INTERFACE (SFI) M: Len Brown L: sfi-devel@simplefirmware.org From 6ec5575c381de50b17e68796435f20ce1b27de79 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 30 Oct 2012 11:29:00 -0300 Subject: [PATCH 0243/4607] [media] media: mx2_camera: Add image size HW limits The CSI on i.MX27 has some constraints regarding image width. This patch makes sure those requirements are met in try_fmt(). Signed-off-by: Javier Martin [g.liakhovetski@gmx.de: make constraint i.MX27-specific] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx2_camera.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 77529f860386..2c148028d8c0 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -1394,8 +1394,6 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, return -EINVAL; } - /* FIXME: implement MX27 limits */ - /* limit to MX25 hardware capabilities */ if (cpu_is_mx25()) { if (xlate->host_fmt->bits_per_sample <= 8) @@ -1427,6 +1425,12 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline, pix->height); } + } else { + /* + * Width must be a multiple of 8 as requested by the CSI. + * (Table 39-2 in the i.MX27 Reference Manual). + */ + pix->width &= ~0x7; } /* limit to sensor capabilities */ From 59dad49e5c176d8c9c0d06f6b744d0a54c578310 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Dec 2012 14:53:26 -0200 Subject: [PATCH 0244/4607] [media] sh_veu.c: fix two compilation warnings drivers/media/platform/sh_veu.c:269:2: warning: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'dma_addr_t' [-Wformat] drivers/media/platform/sh_veu.c:276:2: warning: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'dma_addr_t' [-Wformat] Cc: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_veu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 7365fb5a2615..a0186768479d 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -266,15 +266,17 @@ static void sh_veu_process(struct sh_veu_dev *veu, sh_veu_reg_write(veu, VEU_DAYR, addr + veu->vfmt_out.offset_y); sh_veu_reg_write(veu, VEU_DACR, veu->vfmt_out.offset_c ? addr + veu->vfmt_out.offset_c : 0); - dev_dbg(veu->dev, "%s(): dst base %x, y: %x, c: %x\n", __func__, - addr, veu->vfmt_out.offset_y, veu->vfmt_out.offset_c); + dev_dbg(veu->dev, "%s(): dst base %lx, y: %x, c: %x\n", __func__, + (unsigned long)addr, + veu->vfmt_out.offset_y, veu->vfmt_out.offset_c); addr = vb2_dma_contig_plane_dma_addr(src_buf, 0); sh_veu_reg_write(veu, VEU_SAYR, addr + veu->vfmt_in.offset_y); sh_veu_reg_write(veu, VEU_SACR, veu->vfmt_in.offset_c ? addr + veu->vfmt_in.offset_c : 0); - dev_dbg(veu->dev, "%s(): src base %x, y: %x, c: %x\n", __func__, - addr, veu->vfmt_in.offset_y, veu->vfmt_in.offset_c); + dev_dbg(veu->dev, "%s(): src base %lx, y: %x, c: %x\n", __func__, + (unsigned long)addr, + veu->vfmt_in.offset_y, veu->vfmt_in.offset_c); sh_veu_reg_write(veu, VEU_STR, 1); From b889fcf63cb62e7fdb7816565e28f44dbe4a76a5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 20 Dec 2012 17:14:26 +0000 Subject: [PATCH 0245/4607] UAPI: (Scripted) Disintegrate include/video Signed-off-by: David Howells Acked-by: Arnd Bergmann Acked-by: Thomas Gleixner Acked-by: Michael Kerrisk Acked-by: Paul E. McKenney Acked-by: Dave Jones --- include/uapi/video/Kbuild | 3 + include/uapi/video/edid.h | 9 ++ include/uapi/video/sisfb.h | 209 +++++++++++++++++++++++++++++++++++ include/uapi/video/uvesafb.h | 60 ++++++++++ include/video/Kbuild | 3 - include/video/edid.h | 7 +- include/video/sisfb.h | 189 +------------------------------ include/video/uvesafb.h | 58 +--------- 8 files changed, 284 insertions(+), 254 deletions(-) create mode 100644 include/uapi/video/edid.h create mode 100644 include/uapi/video/sisfb.h create mode 100644 include/uapi/video/uvesafb.h diff --git a/include/uapi/video/Kbuild b/include/uapi/video/Kbuild index aafaa5aa54d4..ac7203bb32cc 100644 --- a/include/uapi/video/Kbuild +++ b/include/uapi/video/Kbuild @@ -1 +1,4 @@ # UAPI Header export list +header-y += edid.h +header-y += sisfb.h +header-y += uvesafb.h diff --git a/include/uapi/video/edid.h b/include/uapi/video/edid.h new file mode 100644 index 000000000000..8c0f032014c9 --- /dev/null +++ b/include/uapi/video/edid.h @@ -0,0 +1,9 @@ +#ifndef _UAPI__linux_video_edid_h__ +#define _UAPI__linux_video_edid_h__ + +struct edid_info { + unsigned char dummy[128]; +}; + + +#endif /* _UAPI__linux_video_edid_h__ */ diff --git a/include/uapi/video/sisfb.h b/include/uapi/video/sisfb.h new file mode 100644 index 000000000000..9250b22b10f8 --- /dev/null +++ b/include/uapi/video/sisfb.h @@ -0,0 +1,209 @@ +/* + * sisfb.h - definitions for the SiS framebuffer driver + * + * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the named License, + * or any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA + */ + +#ifndef _UAPI_LINUX_SISFB_H_ +#define _UAPI_LINUX_SISFB_H_ + +#include +#include + +/**********************************************/ +/* PUBLIC */ +/**********************************************/ + +/* vbflags, public (others in sis.h) */ +#define CRT2_DEFAULT 0x00000001 +#define CRT2_LCD 0x00000002 +#define CRT2_TV 0x00000004 +#define CRT2_VGA 0x00000008 +#define TV_NTSC 0x00000010 +#define TV_PAL 0x00000020 +#define TV_HIVISION 0x00000040 +#define TV_YPBPR 0x00000080 +#define TV_AVIDEO 0x00000100 +#define TV_SVIDEO 0x00000200 +#define TV_SCART 0x00000400 +#define TV_PALM 0x00001000 +#define TV_PALN 0x00002000 +#define TV_NTSCJ 0x00001000 +#define TV_CHSCART 0x00008000 +#define TV_CHYPBPR525I 0x00010000 +#define CRT1_VGA 0x00000000 +#define CRT1_LCDA 0x00020000 +#define VGA2_CONNECTED 0x00040000 +#define VB_DISPTYPE_CRT1 0x00080000 /* CRT1 connected and used */ +#define VB_SINGLE_MODE 0x20000000 /* CRT1 or CRT2; determined by DISPTYPE_CRTx */ +#define VB_MIRROR_MODE 0x40000000 /* CRT1 + CRT2 identical (mirror mode) */ +#define VB_DUALVIEW_MODE 0x80000000 /* CRT1 + CRT2 independent (dual head mode) */ + +/* Aliases: */ +#define CRT2_ENABLE (CRT2_LCD | CRT2_TV | CRT2_VGA) +#define TV_STANDARD (TV_NTSC | TV_PAL | TV_PALM | TV_PALN | TV_NTSCJ) +#define TV_INTERFACE (TV_AVIDEO|TV_SVIDEO|TV_SCART|TV_HIVISION|TV_YPBPR|TV_CHSCART|TV_CHYPBPR525I) + +/* Only if TV_YPBPR is set: */ +#define TV_YPBPR525I TV_NTSC +#define TV_YPBPR525P TV_PAL +#define TV_YPBPR750P TV_PALM +#define TV_YPBPR1080I TV_PALN +#define TV_YPBPRALL (TV_YPBPR525I | TV_YPBPR525P | TV_YPBPR750P | TV_YPBPR1080I) + +#define VB_DISPTYPE_DISP2 CRT2_ENABLE +#define VB_DISPTYPE_CRT2 CRT2_ENABLE +#define VB_DISPTYPE_DISP1 VB_DISPTYPE_CRT1 +#define VB_DISPMODE_SINGLE VB_SINGLE_MODE +#define VB_DISPMODE_MIRROR VB_MIRROR_MODE +#define VB_DISPMODE_DUAL VB_DUALVIEW_MODE +#define VB_DISPLAY_MODE (SINGLE_MODE | MIRROR_MODE | DUALVIEW_MODE) + +/* Structure argument for SISFB_GET_INFO ioctl */ +struct sisfb_info { + __u32 sisfb_id; /* for identifying sisfb */ +#ifndef SISFB_ID +#define SISFB_ID 0x53495346 /* Identify myself with 'SISF' */ +#endif + __u32 chip_id; /* PCI-ID of detected chip */ + __u32 memory; /* total video memory in KB */ + __u32 heapstart; /* heap start offset in KB */ + __u8 fbvidmode; /* current sisfb mode */ + + __u8 sisfb_version; + __u8 sisfb_revision; + __u8 sisfb_patchlevel; + + __u8 sisfb_caps; /* sisfb capabilities */ + + __u32 sisfb_tqlen; /* turbo queue length (in KB) */ + + __u32 sisfb_pcibus; /* The card's PCI ID */ + __u32 sisfb_pcislot; + __u32 sisfb_pcifunc; + + __u8 sisfb_lcdpdc; /* PanelDelayCompensation */ + + __u8 sisfb_lcda; /* Detected status of LCDA for low res/text modes */ + + __u32 sisfb_vbflags; + __u32 sisfb_currentvbflags; + + __u32 sisfb_scalelcd; + __u32 sisfb_specialtiming; + + __u8 sisfb_haveemi; + __u8 sisfb_emi30,sisfb_emi31,sisfb_emi32,sisfb_emi33; + __u8 sisfb_haveemilcd; + + __u8 sisfb_lcdpdca; /* PanelDelayCompensation for LCD-via-CRT1 */ + + __u16 sisfb_tvxpos, sisfb_tvypos; /* Warning: Values + 32 ! */ + + __u32 sisfb_heapsize; /* heap size (in KB) */ + __u32 sisfb_videooffset; /* Offset of viewport in video memory (in bytes) */ + + __u32 sisfb_curfstn; /* currently running FSTN/DSTN mode */ + __u32 sisfb_curdstn; + + __u16 sisfb_pci_vendor; /* PCI vendor (SiS or XGI) */ + + __u32 sisfb_vbflags2; /* ivideo->vbflags2 */ + + __u8 sisfb_can_post; /* sisfb can POST this card */ + __u8 sisfb_card_posted; /* card is POSTED */ + __u8 sisfb_was_boot_device; /* This card was the boot video device (ie is primary) */ + + __u8 reserved[183]; /* for future use */ +}; + +#define SISFB_CMD_GETVBFLAGS 0x55AA0001 /* no arg; result[1] = vbflags */ +#define SISFB_CMD_SWITCHCRT1 0x55AA0010 /* arg[0]: 99 = query, 0 = off, 1 = on */ +/* more to come */ + +#define SISFB_CMD_ERR_OK 0x80000000 /* command succeeded */ +#define SISFB_CMD_ERR_LOCKED 0x80000001 /* sisfb is locked */ +#define SISFB_CMD_ERR_EARLY 0x80000002 /* request before sisfb took over gfx system */ +#define SISFB_CMD_ERR_NOVB 0x80000003 /* No video bridge */ +#define SISFB_CMD_ERR_NOCRT2 0x80000004 /* can't change CRT1 status, CRT2 disabled */ +/* more to come */ +#define SISFB_CMD_ERR_UNKNOWN 0x8000ffff /* Unknown command */ +#define SISFB_CMD_ERR_OTHER 0x80010000 /* Other error */ + +/* Argument for SISFB_CMD ioctl */ +struct sisfb_cmd { + __u32 sisfb_cmd; + __u32 sisfb_arg[16]; + __u32 sisfb_result[4]; +}; + +/* Additional IOCTLs for communication sisfb <> X driver */ +/* If changing this, vgatypes.h must also be changed (for X driver) */ + +/* ioctl for identifying and giving some info (esp. memory heap start) */ +#define SISFB_GET_INFO_SIZE _IOR(0xF3,0x00,__u32) +#define SISFB_GET_INFO _IOR(0xF3,0x01,struct sisfb_info) + +/* ioctrl to get current vertical retrace status */ +#define SISFB_GET_VBRSTATUS _IOR(0xF3,0x02,__u32) + +/* ioctl to enable/disable panning auto-maximize (like nomax parameter) */ +#define SISFB_GET_AUTOMAXIMIZE _IOR(0xF3,0x03,__u32) +#define SISFB_SET_AUTOMAXIMIZE _IOW(0xF3,0x03,__u32) + +/* ioctls to relocate TV output (x=D[31:16], y=D[15:0], + 32)*/ +#define SISFB_GET_TVPOSOFFSET _IOR(0xF3,0x04,__u32) +#define SISFB_SET_TVPOSOFFSET _IOW(0xF3,0x04,__u32) + +/* ioctl for internal sisfb commands (sisfbctrl) */ +#define SISFB_COMMAND _IOWR(0xF3,0x05,struct sisfb_cmd) + +/* ioctl for locking sisfb (no register access during lock) */ +/* As of now, only used to avoid register access during + * the ioctls listed above. + */ +#define SISFB_SET_LOCK _IOW(0xF3,0x06,__u32) + +/* ioctls 0xF3 up to 0x3F reserved for sisfb */ + +/****************************************************************/ +/* The following are deprecated and should not be used anymore: */ +/****************************************************************/ +/* ioctl for identifying and giving some info (esp. memory heap start) */ +#define SISFB_GET_INFO_OLD _IOR('n',0xF8,__u32) +/* ioctrl to get current vertical retrace status */ +#define SISFB_GET_VBRSTATUS_OLD _IOR('n',0xF9,__u32) +/* ioctl to enable/disable panning auto-maximize (like nomax parameter) */ +#define SISFB_GET_AUTOMAXIMIZE_OLD _IOR('n',0xFA,__u32) +#define SISFB_SET_AUTOMAXIMIZE_OLD _IOW('n',0xFA,__u32) +/****************************************************************/ +/* End of deprecated ioctl numbers */ +/****************************************************************/ + +/* For fb memory manager (FBIO_ALLOC, FBIO_FREE) */ +struct sis_memreq { + __u32 offset; + __u32 size; +}; + +/**********************************************/ +/* PRIVATE */ +/* (for IN-KERNEL usage only) */ +/**********************************************/ + + +#endif /* _UAPI_LINUX_SISFB_H_ */ diff --git a/include/uapi/video/uvesafb.h b/include/uapi/video/uvesafb.h new file mode 100644 index 000000000000..cee063d723ad --- /dev/null +++ b/include/uapi/video/uvesafb.h @@ -0,0 +1,60 @@ +#ifndef _UAPI_UVESAFB_H +#define _UAPI_UVESAFB_H + +#include + +struct v86_regs { + __u32 ebx; + __u32 ecx; + __u32 edx; + __u32 esi; + __u32 edi; + __u32 ebp; + __u32 eax; + __u32 eip; + __u32 eflags; + __u32 esp; + __u16 cs; + __u16 ss; + __u16 es; + __u16 ds; + __u16 fs; + __u16 gs; +}; + +/* Task flags */ +#define TF_VBEIB 0x01 +#define TF_BUF_ESDI 0x02 +#define TF_BUF_ESBX 0x04 +#define TF_BUF_RET 0x08 +#define TF_EXIT 0x10 + +struct uvesafb_task { + __u8 flags; + int buf_len; + struct v86_regs regs; +}; + +/* Constants for the capabilities field + * in vbe_ib */ +#define VBE_CAP_CAN_SWITCH_DAC 0x01 +#define VBE_CAP_VGACOMPAT 0x02 + +/* The VBE Info Block */ +struct vbe_ib { + char vbe_signature[4]; + __u16 vbe_version; + __u32 oem_string_ptr; + __u32 capabilities; + __u32 mode_list_ptr; + __u16 total_memory; + __u16 oem_software_rev; + __u32 oem_vendor_name_ptr; + __u32 oem_product_name_ptr; + __u32 oem_product_rev_ptr; + __u8 reserved[222]; + char oem_data[256]; + char misc_data[512]; +} __attribute__ ((packed)); + +#endif /* _UAPI_UVESAFB_H */ diff --git a/include/video/Kbuild b/include/video/Kbuild index ad3e622c5339..e69de29bb2d1 100644 --- a/include/video/Kbuild +++ b/include/video/Kbuild @@ -1,3 +0,0 @@ -header-y += edid.h -header-y += sisfb.h -header-y += uvesafb.h diff --git a/include/video/edid.h b/include/video/edid.h index c5f198704912..0cb8b2a92b75 100644 --- a/include/video/edid.h +++ b/include/video/edid.h @@ -1,14 +1,9 @@ #ifndef __linux_video_edid_h__ #define __linux_video_edid_h__ -struct edid_info { - unsigned char dummy[128]; -}; +#include -#ifdef __KERNEL__ #ifdef CONFIG_X86 extern struct edid_info edid_info; #endif -#endif - #endif /* __linux_video_edid_h__ */ diff --git a/include/video/sisfb.h b/include/video/sisfb.h index 6dc5df9e43f3..6ddff93108fb 100644 --- a/include/video/sisfb.h +++ b/include/video/sisfb.h @@ -17,197 +17,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA */ - #ifndef _LINUX_SISFB_H_ #define _LINUX_SISFB_H_ -#include -#include - -/**********************************************/ -/* PUBLIC */ -/**********************************************/ - -/* vbflags, public (others in sis.h) */ -#define CRT2_DEFAULT 0x00000001 -#define CRT2_LCD 0x00000002 -#define CRT2_TV 0x00000004 -#define CRT2_VGA 0x00000008 -#define TV_NTSC 0x00000010 -#define TV_PAL 0x00000020 -#define TV_HIVISION 0x00000040 -#define TV_YPBPR 0x00000080 -#define TV_AVIDEO 0x00000100 -#define TV_SVIDEO 0x00000200 -#define TV_SCART 0x00000400 -#define TV_PALM 0x00001000 -#define TV_PALN 0x00002000 -#define TV_NTSCJ 0x00001000 -#define TV_CHSCART 0x00008000 -#define TV_CHYPBPR525I 0x00010000 -#define CRT1_VGA 0x00000000 -#define CRT1_LCDA 0x00020000 -#define VGA2_CONNECTED 0x00040000 -#define VB_DISPTYPE_CRT1 0x00080000 /* CRT1 connected and used */ -#define VB_SINGLE_MODE 0x20000000 /* CRT1 or CRT2; determined by DISPTYPE_CRTx */ -#define VB_MIRROR_MODE 0x40000000 /* CRT1 + CRT2 identical (mirror mode) */ -#define VB_DUALVIEW_MODE 0x80000000 /* CRT1 + CRT2 independent (dual head mode) */ - -/* Aliases: */ -#define CRT2_ENABLE (CRT2_LCD | CRT2_TV | CRT2_VGA) -#define TV_STANDARD (TV_NTSC | TV_PAL | TV_PALM | TV_PALN | TV_NTSCJ) -#define TV_INTERFACE (TV_AVIDEO|TV_SVIDEO|TV_SCART|TV_HIVISION|TV_YPBPR|TV_CHSCART|TV_CHYPBPR525I) - -/* Only if TV_YPBPR is set: */ -#define TV_YPBPR525I TV_NTSC -#define TV_YPBPR525P TV_PAL -#define TV_YPBPR750P TV_PALM -#define TV_YPBPR1080I TV_PALN -#define TV_YPBPRALL (TV_YPBPR525I | TV_YPBPR525P | TV_YPBPR750P | TV_YPBPR1080I) - -#define VB_DISPTYPE_DISP2 CRT2_ENABLE -#define VB_DISPTYPE_CRT2 CRT2_ENABLE -#define VB_DISPTYPE_DISP1 VB_DISPTYPE_CRT1 -#define VB_DISPMODE_SINGLE VB_SINGLE_MODE -#define VB_DISPMODE_MIRROR VB_MIRROR_MODE -#define VB_DISPMODE_DUAL VB_DUALVIEW_MODE -#define VB_DISPLAY_MODE (SINGLE_MODE | MIRROR_MODE | DUALVIEW_MODE) - -/* Structure argument for SISFB_GET_INFO ioctl */ -struct sisfb_info { - __u32 sisfb_id; /* for identifying sisfb */ -#ifndef SISFB_ID -#define SISFB_ID 0x53495346 /* Identify myself with 'SISF' */ -#endif - __u32 chip_id; /* PCI-ID of detected chip */ - __u32 memory; /* total video memory in KB */ - __u32 heapstart; /* heap start offset in KB */ - __u8 fbvidmode; /* current sisfb mode */ - - __u8 sisfb_version; - __u8 sisfb_revision; - __u8 sisfb_patchlevel; - - __u8 sisfb_caps; /* sisfb capabilities */ - - __u32 sisfb_tqlen; /* turbo queue length (in KB) */ - - __u32 sisfb_pcibus; /* The card's PCI ID */ - __u32 sisfb_pcislot; - __u32 sisfb_pcifunc; - - __u8 sisfb_lcdpdc; /* PanelDelayCompensation */ - - __u8 sisfb_lcda; /* Detected status of LCDA for low res/text modes */ - - __u32 sisfb_vbflags; - __u32 sisfb_currentvbflags; - - __u32 sisfb_scalelcd; - __u32 sisfb_specialtiming; - - __u8 sisfb_haveemi; - __u8 sisfb_emi30,sisfb_emi31,sisfb_emi32,sisfb_emi33; - __u8 sisfb_haveemilcd; - - __u8 sisfb_lcdpdca; /* PanelDelayCompensation for LCD-via-CRT1 */ - - __u16 sisfb_tvxpos, sisfb_tvypos; /* Warning: Values + 32 ! */ - - __u32 sisfb_heapsize; /* heap size (in KB) */ - __u32 sisfb_videooffset; /* Offset of viewport in video memory (in bytes) */ - - __u32 sisfb_curfstn; /* currently running FSTN/DSTN mode */ - __u32 sisfb_curdstn; - - __u16 sisfb_pci_vendor; /* PCI vendor (SiS or XGI) */ - - __u32 sisfb_vbflags2; /* ivideo->vbflags2 */ - - __u8 sisfb_can_post; /* sisfb can POST this card */ - __u8 sisfb_card_posted; /* card is POSTED */ - __u8 sisfb_was_boot_device; /* This card was the boot video device (ie is primary) */ - - __u8 reserved[183]; /* for future use */ -}; - -#define SISFB_CMD_GETVBFLAGS 0x55AA0001 /* no arg; result[1] = vbflags */ -#define SISFB_CMD_SWITCHCRT1 0x55AA0010 /* arg[0]: 99 = query, 0 = off, 1 = on */ -/* more to come */ - -#define SISFB_CMD_ERR_OK 0x80000000 /* command succeeded */ -#define SISFB_CMD_ERR_LOCKED 0x80000001 /* sisfb is locked */ -#define SISFB_CMD_ERR_EARLY 0x80000002 /* request before sisfb took over gfx system */ -#define SISFB_CMD_ERR_NOVB 0x80000003 /* No video bridge */ -#define SISFB_CMD_ERR_NOCRT2 0x80000004 /* can't change CRT1 status, CRT2 disabled */ -/* more to come */ -#define SISFB_CMD_ERR_UNKNOWN 0x8000ffff /* Unknown command */ -#define SISFB_CMD_ERR_OTHER 0x80010000 /* Other error */ - -/* Argument for SISFB_CMD ioctl */ -struct sisfb_cmd { - __u32 sisfb_cmd; - __u32 sisfb_arg[16]; - __u32 sisfb_result[4]; -}; - -/* Additional IOCTLs for communication sisfb <> X driver */ -/* If changing this, vgatypes.h must also be changed (for X driver) */ - -/* ioctl for identifying and giving some info (esp. memory heap start) */ -#define SISFB_GET_INFO_SIZE _IOR(0xF3,0x00,__u32) -#define SISFB_GET_INFO _IOR(0xF3,0x01,struct sisfb_info) - -/* ioctrl to get current vertical retrace status */ -#define SISFB_GET_VBRSTATUS _IOR(0xF3,0x02,__u32) - -/* ioctl to enable/disable panning auto-maximize (like nomax parameter) */ -#define SISFB_GET_AUTOMAXIMIZE _IOR(0xF3,0x03,__u32) -#define SISFB_SET_AUTOMAXIMIZE _IOW(0xF3,0x03,__u32) - -/* ioctls to relocate TV output (x=D[31:16], y=D[15:0], + 32)*/ -#define SISFB_GET_TVPOSOFFSET _IOR(0xF3,0x04,__u32) -#define SISFB_SET_TVPOSOFFSET _IOW(0xF3,0x04,__u32) - -/* ioctl for internal sisfb commands (sisfbctrl) */ -#define SISFB_COMMAND _IOWR(0xF3,0x05,struct sisfb_cmd) - -/* ioctl for locking sisfb (no register access during lock) */ -/* As of now, only used to avoid register access during - * the ioctls listed above. - */ -#define SISFB_SET_LOCK _IOW(0xF3,0x06,__u32) - -/* ioctls 0xF3 up to 0x3F reserved for sisfb */ - -/****************************************************************/ -/* The following are deprecated and should not be used anymore: */ -/****************************************************************/ -/* ioctl for identifying and giving some info (esp. memory heap start) */ -#define SISFB_GET_INFO_OLD _IOR('n',0xF8,__u32) -/* ioctrl to get current vertical retrace status */ -#define SISFB_GET_VBRSTATUS_OLD _IOR('n',0xF9,__u32) -/* ioctl to enable/disable panning auto-maximize (like nomax parameter) */ -#define SISFB_GET_AUTOMAXIMIZE_OLD _IOR('n',0xFA,__u32) -#define SISFB_SET_AUTOMAXIMIZE_OLD _IOW('n',0xFA,__u32) -/****************************************************************/ -/* End of deprecated ioctl numbers */ -/****************************************************************/ - -/* For fb memory manager (FBIO_ALLOC, FBIO_FREE) */ -struct sis_memreq { - __u32 offset; - __u32 size; -}; - -/**********************************************/ -/* PRIVATE */ -/* (for IN-KERNEL usage only) */ -/**********************************************/ - -#ifdef __KERNEL__ #include +#include #define UNKNOWN_VGA 0 #define SIS_300_VGA 1 @@ -220,5 +35,3 @@ extern void sis_malloc_new(struct pci_dev *pdev, struct sis_memreq *req); extern void sis_free(u32 base); extern void sis_free_new(struct pci_dev *pdev, u32 base); #endif - -#endif diff --git a/include/video/uvesafb.h b/include/video/uvesafb.h index 0993a220a3e6..1a91850cb961 100644 --- a/include/video/uvesafb.h +++ b/include/video/uvesafb.h @@ -1,63 +1,8 @@ #ifndef _UVESAFB_H #define _UVESAFB_H -#include +#include -struct v86_regs { - __u32 ebx; - __u32 ecx; - __u32 edx; - __u32 esi; - __u32 edi; - __u32 ebp; - __u32 eax; - __u32 eip; - __u32 eflags; - __u32 esp; - __u16 cs; - __u16 ss; - __u16 es; - __u16 ds; - __u16 fs; - __u16 gs; -}; - -/* Task flags */ -#define TF_VBEIB 0x01 -#define TF_BUF_ESDI 0x02 -#define TF_BUF_ESBX 0x04 -#define TF_BUF_RET 0x08 -#define TF_EXIT 0x10 - -struct uvesafb_task { - __u8 flags; - int buf_len; - struct v86_regs regs; -}; - -/* Constants for the capabilities field - * in vbe_ib */ -#define VBE_CAP_CAN_SWITCH_DAC 0x01 -#define VBE_CAP_VGACOMPAT 0x02 - -/* The VBE Info Block */ -struct vbe_ib { - char vbe_signature[4]; - __u16 vbe_version; - __u32 oem_string_ptr; - __u32 capabilities; - __u32 mode_list_ptr; - __u16 total_memory; - __u16 oem_software_rev; - __u32 oem_vendor_name_ptr; - __u32 oem_product_name_ptr; - __u32 oem_product_rev_ptr; - __u8 reserved[222]; - char oem_data[256]; - char misc_data[512]; -} __attribute__ ((packed)); - -#ifdef __KERNEL__ /* VBE CRTC Info Block */ struct vbe_crtc_ib { @@ -191,5 +136,4 @@ struct uvesafb_par { struct vbe_crtc_ib crtc; }; -#endif /* __KERNEL__ */ #endif /* _UVESAFB_H */ From b391b0ef0f9399dcba8fa025ab5e5d4229847467 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 20 Dec 2012 15:21:23 -0200 Subject: [PATCH 0246/4607] [media] tm6000-video.c: warning fix drivers/media/usb/tm6000/tm6000-video.c: In function '__check_keep_urb': drivers/media/usb/tm6000/tm6000-video.c:1926:1: warning: return from incompatible pointer type [enabled by default] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 82968017a253..1edc2517603f 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -57,7 +57,7 @@ static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ static int radio_nr = -1; /* /dev/radioN, -1 for autodetect */ -static int keep_urb; /* keep urb buffers allocated */ +static bool keep_urb; /* keep urb buffers allocated */ /* Debug level */ int tm6000_debug; From 4bb891ebf60eb43ebd04e09bbcad24013067873f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 24 Oct 2012 08:14:16 -0300 Subject: [PATCH 0247/4607] [media] ivtv: ivtv-driver: Replace 'flush_work_sync()' Since commit 43829731d (workqueue: deprecate flush[_delayed]_work_sync()), flush_work() should be used instead of flush_work_sync(). Signed-off-by: Fabio Estevam Acked-by: Tejun Heo Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index 74e9a5032364..5d0a5df61078 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -304,7 +304,7 @@ static void request_modules(struct ivtv *dev) static void flush_request_modules(struct ivtv *dev) { - flush_work_sync(&dev->request_module_wk); + flush_work(&dev->request_module_wk); } #else #define request_modules(dev) From c0c36b941b6f0be6ac74f340040cbb29d6a0b06c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 19 Dec 2012 16:08:43 +0000 Subject: [PATCH 0248/4607] drm/i915: Return the real error code from intel_set_mode() Note: This patch also adds a little helper intel_crtc_restore_mode for the common case where we do a full modeset but with the same parameters, e.g. to undo bios damage or update a property. Signed-off-by: Chris Wilson Reviewed-by: Rodrigo Vivi [danvet: Added note.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 56 ++++++++++++++-------------- drivers/gpu/drm/i915/intel_dp.c | 7 +--- drivers/gpu/drm/i915/intel_drv.h | 5 ++- drivers/gpu/drm/i915/intel_hdmi.c | 7 +--- drivers/gpu/drm/i915/intel_lvds.c | 3 +- drivers/gpu/drm/i915/intel_sdvo.c | 7 +--- drivers/gpu/drm/i915/intel_tv.c | 3 +- 7 files changed, 39 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f7f4ef17cbe4..1464e472ce44 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6313,7 +6313,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, return false; } - if (!intel_set_mode(crtc, mode, 0, 0, fb)) { + if (intel_set_mode(crtc, mode, 0, 0, fb)) { DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); if (old->release_fb) old->release_fb->funcs->destroy(old->release_fb); @@ -7426,22 +7426,20 @@ intel_modeset_check_state(struct drm_device *dev) } } -bool intel_set_mode(struct drm_crtc *crtc, - struct drm_display_mode *mode, - int x, int y, struct drm_framebuffer *fb) +int intel_set_mode(struct drm_crtc *crtc, + struct drm_display_mode *mode, + int x, int y, struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct drm_display_mode *adjusted_mode, *saved_mode, *saved_hwmode; struct intel_crtc *intel_crtc; unsigned disable_pipes, prepare_pipes, modeset_pipes; - bool ret = true; + int ret = 0; saved_mode = kmalloc(2 * sizeof(*saved_mode), GFP_KERNEL); - if (!saved_mode) { - DRM_ERROR("i915: Could not allocate saved display mode.\n"); - return false; - } + if (!saved_mode) + return -ENOMEM; saved_hwmode = saved_mode + 1; intel_modeset_affected_pipes(crtc, &modeset_pipes, @@ -7465,7 +7463,7 @@ bool intel_set_mode(struct drm_crtc *crtc, if (modeset_pipes) { adjusted_mode = intel_modeset_adjusted_mode(crtc, mode); if (IS_ERR(adjusted_mode)) { - ret = false; + ret = PTR_ERR(adjusted_mode); goto out; } } @@ -7492,11 +7490,11 @@ bool intel_set_mode(struct drm_crtc *crtc, * on the DPLL. */ for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) { - ret = !intel_crtc_mode_set(&intel_crtc->base, - mode, adjusted_mode, - x, y, fb); - if (!ret) - goto done; + ret = intel_crtc_mode_set(&intel_crtc->base, + mode, adjusted_mode, + x, y, fb); + if (ret) + goto done; } /* Now enable the clocks, plane, pipe, and connectors that we set up. */ @@ -7517,7 +7515,7 @@ bool intel_set_mode(struct drm_crtc *crtc, /* FIXME: add subpixel order */ done: drm_mode_destroy(dev, adjusted_mode); - if (!ret && crtc->enabled) { + if (ret && crtc->enabled) { crtc->hwmode = *saved_hwmode; crtc->mode = *saved_mode; } else { @@ -7529,6 +7527,11 @@ out: return ret; } +void intel_crtc_restore_mode(struct drm_crtc *crtc) +{ + intel_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->fb); +} + #undef for_each_intel_crtc_masked static void intel_set_config_free(struct intel_set_config *config) @@ -7798,11 +7801,11 @@ static int intel_crtc_set_config(struct drm_mode_set *set) drm_mode_debug_printmodeline(set->mode); } - if (!intel_set_mode(set->crtc, set->mode, - set->x, set->y, set->fb)) { - DRM_ERROR("failed to set mode on [CRTC:%d]\n", - set->crtc->base.id); - ret = -EINVAL; + ret = intel_set_mode(set->crtc, set->mode, + set->x, set->y, set->fb); + if (ret) { + DRM_ERROR("failed to set mode on [CRTC:%d], err = %d\n", + set->crtc->base.id, ret); goto fail; } } else if (config->fb_changed) { @@ -7819,8 +7822,8 @@ fail: /* Try to restore the config */ if (config->mode_changed && - !intel_set_mode(save_set.crtc, save_set.mode, - save_set.x, save_set.y, save_set.fb)) + intel_set_mode(save_set.crtc, save_set.mode, + save_set.x, save_set.y, save_set.fb)) DRM_ERROR("failed to restore config after modeset failure\n"); out_config: @@ -8804,11 +8807,8 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, } if (force_restore) { - for_each_pipe(pipe) { - crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); - intel_set_mode(&crtc->base, &crtc->base.mode, - crtc->base.x, crtc->base.y, crtc->base.fb); - } + for_each_pipe(pipe) + intel_crtc_restore_mode(dev_priv->pipe_to_crtc_mapping[pipe]); } else { intel_modeset_update_staged_output_state(dev); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 2d3b26832397..1dd89d5fe511 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2483,11 +2483,8 @@ intel_dp_set_property(struct drm_connector *connector, return -EINVAL; done: - if (intel_encoder->base.crtc) { - struct drm_crtc *crtc = intel_encoder->base.crtc; - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); - } + if (intel_encoder->base.crtc) + intel_crtc_restore_mode(intel_encoder->base.crtc); return 0; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 53d4c8fec2a0..116580b623dd 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -501,9 +501,10 @@ struct intel_set_config { bool mode_changed; }; -extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, - int x, int y, struct drm_framebuffer *old_fb); +extern int intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, + int x, int y, struct drm_framebuffer *old_fb); extern void intel_modeset_disable(struct drm_device *dev); +extern void intel_crtc_restore_mode(struct drm_crtc *crtc); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_update_dpms(struct drm_crtc *crtc); extern void intel_encoder_noop(struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 9f834d324cfd..6387f9b0df99 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -917,11 +917,8 @@ intel_hdmi_set_property(struct drm_connector *connector, return -EINVAL; done: - if (intel_dig_port->base.base.crtc) { - struct drm_crtc *crtc = intel_dig_port->base.base.crtc; - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); - } + if (intel_dig_port->base.base.crtc) + intel_crtc_restore_mode(intel_dig_port->base.base.crtc); return 0; } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 778106961e80..8c61876dbe95 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -646,8 +646,7 @@ static int intel_lvds_set_property(struct drm_connector *connector, * If the CRTC is enabled, the display will be changed * according to the new panel fitting mode. */ - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); + intel_crtc_restore_mode(crtc); } } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 0e03985b0fe4..ea2e79f63d2b 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1997,11 +1997,8 @@ set_value: done: - if (intel_sdvo->base.base.crtc) { - struct drm_crtc *crtc = intel_sdvo->base.base.crtc; - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); - } + if (intel_sdvo->base.base.crtc) + intel_crtc_restore_mode(intel_sdvo->base.base.crtc); return 0; #undef CHECK_PROPERTY diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index ea93520c1278..984a113c5d13 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1479,8 +1479,7 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop } if (changed && crtc) - intel_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); + intel_crtc_restore_mode(crtc); out: return ret; } From c2838e6ee2d2eae893646f9a271039ae434ed760 Mon Sep 17 00:00:00 2001 From: "Yann E. MORIN" Date: Thu, 22 Nov 2012 01:06:04 +0100 Subject: [PATCH 0249/4607] scripts/kconfig: ensure we use proper CONFIG_ prefix Now that we get the CONFIG_ prefix from the environment, we must ensure we use the proper prefix in case the user has it set in the environment. Simply unexport CONFIG_ to fallback to our hard-coded default. Signed-off-by: "Yann E. MORIN" Signed-off-by: Michal Marek --- scripts/kconfig/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index 3091794e9354..231b4759c714 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -11,6 +11,9 @@ else Kconfig := Kconfig endif +# We need this, in case the user has it in its environment +unexport CONFIG_ + xconfig: $(obj)/qconf $< $(Kconfig) From 80f5ab097b87c86581cb9736a8e55c5a3047d4bb Mon Sep 17 00:00:00 2001 From: Shaun Ruffell Date: Sun, 19 Aug 2012 01:11:24 -0300 Subject: [PATCH 0250/4607] edac: edac_mc no longer deals with kobjects directly There are no more embedded kobjects in struct mem_ctl_info. Remove a header and a comment that does not reflect the code anymore. Signed-off-by: Shaun Ruffell Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_mc.c | 7 ------- include/linux/edac.h | 1 - 2 files changed, 8 deletions(-) diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 281f566a5513..a641f623fffd 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -441,13 +441,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num, mci->op_state = OP_ALLOC; - /* at this point, the root kobj is valid, and in order to - * 'free' the object, then the function: - * edac_mc_unregister_sysfs_main_kobj() must be called - * which will perform kobj unregistration and the actual free - * will occur during the kobject callback operation - */ - return mci; error: diff --git a/include/linux/edac.h b/include/linux/edac.h index 1b8c02b36f76..4784213c819d 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -14,7 +14,6 @@ #include #include -#include #include #include #include From da14d93d95ee3923b6e690220bdb7ce191e76d95 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 25 Oct 2012 09:07:21 -0200 Subject: [PATCH 0251/4607] sb_edac: add a missing /n on a debug message [ 17.024963] EDAC DEBUG: get_memory_layout: TOHM: 132.160 GB (0x0000002043ffffff)<7>[ 17.024971] EDAC DEBUG: get_memory_layout: SAD#0 DRAM up to 33.792 GB (0x0000000840000000) Interleave: 8:6 reg=0x000083c3 Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/sb_edac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c index 5715b7c2c517..9ad24242ea18 100644 --- a/drivers/edac/sb_edac.c +++ b/drivers/edac/sb_edac.c @@ -639,7 +639,7 @@ static void get_memory_layout(const struct mem_ctl_info *mci) tmp_mb = (1 + pvt->tohm) >> 20; mb = div_u64_rem(tmp_mb, 1000, &kb); - edac_dbg(0, "TOHM: %u.%03u GB (0x%016Lx)", mb, kb, (u64)pvt->tohm); + edac_dbg(0, "TOHM: %u.%03u GB (0x%016Lx)\n", mb, kb, (u64)pvt->tohm); /* * Step 2) Get SAD range and SAD Interleave list From c31d34fe92e9d0b146907d7294269ee03e1b403f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20S=C3=B6derlund?= Date: Sun, 29 Jan 2012 23:04:32 +0100 Subject: [PATCH 0252/4607] i7core_edac: fix erroneous size of static array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove size from lookup arrays and mark them as const. Reviewed-by: Jesper Juhl Signed-off-by: Niklas Söderlund Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/i7core_edac.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c index 10c8c00d6469..ad5f934c95d3 100644 --- a/drivers/edac/i7core_edac.c +++ b/drivers/edac/i7core_edac.c @@ -420,21 +420,21 @@ static inline int numdimms(u32 dimms) static inline int numrank(u32 rank) { - static int ranks[4] = { 1, 2, 4, -EINVAL }; + static const int ranks[] = { 1, 2, 4, -EINVAL }; return ranks[rank & 0x3]; } static inline int numbank(u32 bank) { - static int banks[4] = { 4, 8, 16, -EINVAL }; + static const int banks[] = { 4, 8, 16, -EINVAL }; return banks[bank & 0x3]; } static inline int numrow(u32 row) { - static int rows[8] = { + static const int rows[] = { 1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, -EINVAL, -EINVAL, -EINVAL, }; @@ -444,7 +444,7 @@ static inline int numrow(u32 row) static inline int numcol(u32 col) { - static int cols[8] = { + static const int cols[] = { 1 << 10, 1 << 11, 1 << 12, -EINVAL, }; return cols[col & 0x3]; From 1c069100c1f5577ecde06b3a366b73f520854c4e Mon Sep 17 00:00:00 2001 From: Lans Zhang Date: Thu, 20 Dec 2012 10:16:07 +0800 Subject: [PATCH 0253/4607] i7core_edac: fix kernel crash on unloading i7core_edac. It is easy to trigger this crash on 3.7.0: root@intel_westmere_ep-3:~# modprobe -r i7core_edac EDAC PCI: Removed device 0 for i7core_edac EDAC PCI controller: DEV 0000:fe:03.0 EDAC MC: Removed device 1 for i7core_edac.c i7 core #1: DEV 0000:fe:03.0 EDAC PCI: Removed device 1 for i7core_edac EDAC PCI controller: DEV 0000:ff:03.0 EDAC MC: Removed device 0 for i7core_edac.c i7 core #0: DEV 0000:ff:03.0 BUG: unable to handle kernel NULL pointer dereference at 0000000000000110 IP: [] __blocking_notifier_call_chain+0x29/0x80 PGD 1eaae7067 PUD 1e96e4067 PMD 0 Oops: 0000 [#1] PREEMPT SMP Modules linked in: minix acpi_cpufreq freq_table mperf ioatdma processor edac_core(-) iTCO_wdt coretemp evdev hwmon lpc_ich dca mfd_core crc32c_intel ioapic [last unloaded: i7core_edac] CPU 3 Pid: 1268, comm: modprobe Not tainted 3.7.0-WR5.0.1.0_standard+ #30 Intel Corporation S5520HC/S5520HC RIP: 0010:[] [] __blocking_notifier_call_chain+0x29/0x80 RSP: 0018:ffff8801eb12de28 EFLAGS: 00010246 RAX: 0000000000000000 RBX: 00000000000000f0 RCX: 00000000ffffffff RDX: ffff88012b452800 RSI: 0000000000000002 RDI: 00000000000000f0 RBP: ffff8801eb12de68 R08: 0000000000000000 R09: ffffea0004ad1118 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: ffff8801eb12dee8 R14: ffff88012b452800 R15: 000000000060e518 FS: 00007f9ea95a9700(0000) GS:ffff8801efc20000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000110 CR3: 00000001262f1000 CR4: 00000000000007e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process modprobe (pid: 1268, threadinfo ffff8801eb12c000, task ffff8801e8421690) Stack: ffff88012c802a00 ffff88012b445ec0 ffff88012c802300 ffff88012b452800 0000000000000000 ffff8801eb12dee8 000000000060e080 000000000060e518 ffff8801eb12de78 ffffffff82069f56 ffff8801eb12dea8 ffffffff824ead7c Call Trace: [] blocking_notifier_call_chain+0x16/0x20 [] device_del+0x3c/0x1d0 [] edac_mc_sysfs_exit+0x1c/0x2f [edac_core] [] edac_exit+0x4f/0x56 [edac_core] [] sys_delete_module+0x17a/0x240 [] ? vm_munmap+0x5c/0x80 [] system_call_fastpath+0x16/0x1b Code: 90 90 55 48 89 e5 48 83 ec 40 48 89 5d d8 4c 89 65 e0 4c 89 6d e8 4c 89 75 f0 4c 89 7d f8 66 66 66 66 90 31 c0 49 89 d6 48 89 fb <48> 8b 57 20 49 89 f5 41 89 cf 4c 8d 67 20 48 85 d2 74 2c 4c 89 RIP [] __blocking_notifier_call_chain+0x29/0x80 RSP CR2: 0000000000000110 ---[ end trace b69acf12ccad1c0d ]--- Usually, edac_subsys is grabbed one time by pci at initialization. But edac_subsys may be released several times if multiple pci MCs exist. The fix just makes the operations balanced. Signed-off-by: Lans Zhang Signed-off-by: Mauro Carvalho Chehab --- drivers/edac/edac_pci_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c index dc6e905ee1a5..7684426fafa2 100644 --- a/drivers/edac/edac_pci_sysfs.c +++ b/drivers/edac/edac_pci_sysfs.c @@ -429,8 +429,8 @@ static void edac_pci_main_kobj_teardown(void) if (atomic_dec_return(&edac_pci_sysfs_refcount) == 0) { edac_dbg(0, "called kobject_put on main kobj\n"); kobject_put(edac_pci_top_main_kobj); + edac_put_sysfs_subsys(); } - edac_put_sysfs_subsys(); } /* From aecede4c45ae32944e822ef98d4837733837887d Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Wed, 7 Nov 2012 03:37:07 -0300 Subject: [PATCH 0254/4607] [media] exynos-gsc: Adding tiled multi-planar format to G-Scaler Adding V4L2_PIX_FMT_NV12MT_16X16 to G-Scaler supported formats. If the output or input format is V4L2_PIX_FMT_NV12MT_16X16, configure G-Scaler to use GSC_IN_TILE_MODE. [s.nawrocki: shortened the pixel format description] Signed-off-by: Shaik Ameer Basha Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 9 +++++++++ drivers/media/platform/exynos-gsc/gsc-core.h | 5 +++++ drivers/media/platform/exynos-gsc/gsc-regs.c | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index cc7b218d047c..6d6f65d8c1e5 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -185,6 +185,15 @@ static const struct gsc_fmt gsc_formats[] = { .corder = GSC_CRCB, .num_planes = 3, .num_comp = 3, + }, { + .name = "YUV 4:2:0 n.c. 2p, Y/CbCr tiled", + .pixelformat = V4L2_PIX_FMT_NV12MT_16X16, + .depth = { 8, 4 }, + .color = GSC_YUV420, + .yorder = GSC_LSB_Y, + .corder = GSC_CBCR, + .num_planes = 2, + .num_comp = 2, } }; diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index 5f157efd24f0..cc19bba09bd1 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -427,6 +427,11 @@ static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx) spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); } +static inline int is_tiled(const struct gsc_fmt *fmt) +{ + return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16; +} + static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on) { u32 cfg = readl(dev->regs + GSC_ENABLE); diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c index 0146b354dc22..6f5b5a486cf3 100644 --- a/drivers/media/platform/exynos-gsc/gsc-regs.c +++ b/drivers/media/platform/exynos-gsc/gsc-regs.c @@ -214,6 +214,9 @@ void gsc_hw_set_in_image_format(struct gsc_ctx *ctx) break; } + if (is_tiled(frame->fmt)) + cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE; + writel(cfg, dev->regs + GSC_IN_CON); } @@ -334,6 +337,9 @@ void gsc_hw_set_out_image_format(struct gsc_ctx *ctx) break; } + if (is_tiled(frame->fmt)) + cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE; + end_set: writel(cfg, dev->regs + GSC_OUT_CON); } From f60e160e126bdd8f0d928cd8b3fce54659597394 Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Thu, 22 Nov 2012 02:25:06 -0300 Subject: [PATCH 0255/4607] [media] exynos-gsc: propagate timestamps from src to dst buffers Make gsc-m2m propagate the timestamp field from source to destination buffers. Signed-off-by: John Sheu Signed-off-by: Shaik Ameer Basha Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-m2m.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index c267c57c76fd..a8e5050340aa 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -99,22 +99,28 @@ static void gsc_m2m_job_abort(void *priv) gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); } -static int gsc_fill_addr(struct gsc_ctx *ctx) +static int gsc_get_bufs(struct gsc_ctx *ctx) { struct gsc_frame *s_frame, *d_frame; - struct vb2_buffer *vb = NULL; + struct vb2_buffer *src_vb, *dst_vb; int ret; s_frame = &ctx->s_frame; d_frame = &ctx->d_frame; - vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = gsc_prepare_addr(ctx, vb, s_frame, &s_frame->addr); + src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = gsc_prepare_addr(ctx, src_vb, s_frame, &s_frame->addr); if (ret) return ret; - vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - return gsc_prepare_addr(ctx, vb, d_frame, &d_frame->addr); + dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = gsc_prepare_addr(ctx, dst_vb, d_frame, &d_frame->addr); + if (ret) + return ret; + + dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; + + return 0; } static void gsc_m2m_device_run(void *priv) @@ -148,7 +154,7 @@ static void gsc_m2m_device_run(void *priv) goto put_device; } - ret = gsc_fill_addr(ctx); + ret = gsc_get_bufs(ctx); if (ret) { pr_err("Wrong address"); goto put_device; From 2c8cc13f36b0563c62aa18454c8f853c287fdfe9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 23 Nov 2012 08:04:42 -0300 Subject: [PATCH 0256/4607] [media] exynos-gsc: Fix checkpatch warning in gsc-m2m.c Fixes the following warning: WARNING: space prohibited between function name and open parenthesis '(' FILE: media/platform/exynos-gsc/gsc-m2m.c:606: ctx = kzalloc(sizeof (*ctx), GFP_KERNEL); Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-m2m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index a8e5050340aa..0d06d6c6f373 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -603,7 +603,7 @@ static int gsc_m2m_open(struct file *file) if (mutex_lock_interruptible(&gsc->lock)) return -ERESTARTSYS; - ctx = kzalloc(sizeof (*ctx), GFP_KERNEL); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) { ret = -ENOMEM; goto unlock; From 9318ab69c50b82f9f513a20955ebd2cb1f482adc Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 03:20:19 -0300 Subject: [PATCH 0257/4607] [media] exynos-gsc: Rearrange error messages for valid prints In case of clk_prepare failure, the function gsc_clk_get also prints "failed to get clock" which is not correct. Hence move the error messages to their respective blocks. While at it, also renamed the labels meaningfully. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 6d6f65d8c1e5..45bcfa7506db 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1017,25 +1017,26 @@ static int gsc_clk_get(struct gsc_dev *gsc) dev_dbg(&gsc->pdev->dev, "gsc_clk_get Called\n"); gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); - if (IS_ERR(gsc->clock)) - goto err_print; + if (IS_ERR(gsc->clock)) { + dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", + GSC_CLOCK_GATE_NAME); + goto err_clk_get; + } ret = clk_prepare(gsc->clock); if (ret < 0) { + dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", + GSC_CLOCK_GATE_NAME); clk_put(gsc->clock); gsc->clock = NULL; - goto err; + goto err_clk_prepare; } return 0; -err: - dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", - GSC_CLOCK_GATE_NAME); +err_clk_prepare: gsc_clk_put(gsc); -err_print: - dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", - GSC_CLOCK_GATE_NAME); +err_clk_get: return -ENXIO; } From 21ae96d3973b926e89edf52bae560475105455ef Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 26 Nov 2012 03:20:20 -0300 Subject: [PATCH 0258/4607] [media] exynos-gsc: Correct the clock handling Make sure there is no unbalanced clk_unprepare call and add missing clock release in the driver's remove() callback. Signed-off-by: Sylwester Nawrocki Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 45bcfa7506db..c8b82c006bd0 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1002,12 +1002,11 @@ static void *gsc_get_drv_data(struct platform_device *pdev) static void gsc_clk_put(struct gsc_dev *gsc) { - if (IS_ERR_OR_NULL(gsc->clock)) - return; - - clk_unprepare(gsc->clock); - clk_put(gsc->clock); - gsc->clock = NULL; + if (!IS_ERR(gsc->clock)) { + clk_unprepare(gsc->clock); + clk_put(gsc->clock); + gsc->clock = NULL; + } } static int gsc_clk_get(struct gsc_dev *gsc) @@ -1028,7 +1027,7 @@ static int gsc_clk_get(struct gsc_dev *gsc) dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", GSC_CLOCK_GATE_NAME); clk_put(gsc->clock); - gsc->clock = NULL; + gsc->clock = ERR_PTR(-EINVAL); goto err_clk_prepare; } @@ -1106,6 +1105,7 @@ static int gsc_probe(struct platform_device *pdev) init_waitqueue_head(&gsc->irq_queue); spin_lock_init(&gsc->slock); mutex_init(&gsc->lock); + gsc->clock = ERR_PTR(-EINVAL); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); gsc->regs = devm_request_and_ioremap(dev, res); @@ -1169,6 +1169,7 @@ static int __devexit gsc_remove(struct platform_device *pdev) vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); pm_runtime_disable(&pdev->dev); + gsc_clk_put(gsc); dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); return 0; From e2732ae5dd9c765732dda6b7120eb74896562c22 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 03:20:21 -0300 Subject: [PATCH 0259/4607] [media] exynos-gsc: Use devm_clk_get() devm_clk_get() is a device managed function and makes error handling a bit simpler. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index c8b82c006bd0..0c22ad570500 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1002,11 +1002,8 @@ static void *gsc_get_drv_data(struct platform_device *pdev) static void gsc_clk_put(struct gsc_dev *gsc) { - if (!IS_ERR(gsc->clock)) { + if (!IS_ERR(gsc->clock)) clk_unprepare(gsc->clock); - clk_put(gsc->clock); - gsc->clock = NULL; - } } static int gsc_clk_get(struct gsc_dev *gsc) @@ -1015,28 +1012,22 @@ static int gsc_clk_get(struct gsc_dev *gsc) dev_dbg(&gsc->pdev->dev, "gsc_clk_get Called\n"); - gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); + gsc->clock = devm_clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); if (IS_ERR(gsc->clock)) { dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", GSC_CLOCK_GATE_NAME); - goto err_clk_get; + return PTR_ERR(gsc->clock); } ret = clk_prepare(gsc->clock); if (ret < 0) { dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", GSC_CLOCK_GATE_NAME); - clk_put(gsc->clock); gsc->clock = ERR_PTR(-EINVAL); - goto err_clk_prepare; + return ret; } return 0; - -err_clk_prepare: - gsc_clk_put(gsc); -err_clk_get: - return -ENXIO; } static int gsc_m2m_suspend(struct gsc_dev *gsc) From 1b5901331ff3af4bdc1b998a056a248c9924e2d1 Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Tue, 27 Nov 2012 09:48:58 -0300 Subject: [PATCH 0260/4607] [media] exynos-gsc: modify number of output/capture buffers G-Scaler src buffer count as well as destination buffer count is increased to 32. This is required for G-Scaler to interface with MFC, as MFC demands 32 capture buffers for some H264 streams. Signed-off-by: Shaik Ameer Basha Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 0c22ad570500..ae885c7ebc41 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -944,8 +944,8 @@ static struct gsc_variant gsc_v_100_variant = { .pix_max = &gsc_v_100_max, .pix_min = &gsc_v_100_min, .pix_align = &gsc_v_100_align, - .in_buf_cnt = 8, - .out_buf_cnt = 16, + .in_buf_cnt = 32, + .out_buf_cnt = 32, .sc_up_max = 8, .sc_down_max = 16, .poly_sc_down_max = 4, From 1202ecdc24fc88d5b144824f55ec9c8899591caf Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 21 Oct 2012 16:02:47 -0300 Subject: [PATCH 0261/4607] [media] v4l: Define video buffer flags for timestamp types Define video buffer flags for different timestamp types. Everything up to now have used either realtime clock or monotonic clock, without a way to tell which clock the timestamp was taken from. Also document that the clock source of the timestamp in the timestamp field depends on buffer flags. [mchehab@redhat.com: fix a few wrong references to Kernel 3.8 - as this patch is meant for 3.9] Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/compat.xml | 12 +++++ Documentation/DocBook/media/v4l/io.xml | 53 +++++++++++++++++----- Documentation/DocBook/media/v4l/v4l2.xml | 12 ++++- include/uapi/linux/videodev2.h | 4 ++ 4 files changed, 69 insertions(+), 12 deletions(-) diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index 3dd9e78815d1..ebd2bfd1ee8e 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2477,6 +2477,18 @@ that used it. It was originally scheduled for removal in 2.6.35. +
+ V4L2 in Linux 3.9 + + + Added timestamp types to + flags field in + v4l2_buffer. See . + + +
+
Relation of V4L2 to other Linux multimedia APIs diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index 388a34032653..09e8dcf5e9c4 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -741,17 +741,19 @@ applications when an output stream. struct timeval timestamp - For input streams this is the -system time (as returned by the gettimeofday() -function) when the first data byte was captured. For output streams -the data will not be displayed before this time, secondary to the -nominal frame rate determined by the current video standard in -enqueued order. Applications can for example zero this field to -display frames as soon as possible. The driver stores the time at -which the first data byte was actually sent out in the -timestamp field. This permits -applications to monitor the drift between the video and system -clock. + For input streams this is time when the first data + byte was captured, as returned by the + clock_gettime() function for the relevant + clock id; see V4L2_BUF_FLAG_TIMESTAMP_* in + . For output streams the data + will not be displayed before this time, secondary to the nominal + frame rate determined by the current video standard in enqueued + order. Applications can for example zero this field to display + frames as soon as possible. The driver stores the time at which + the first data byte was actually sent out in the + timestamp field. This permits + applications to monitor the drift between the video and system + clock. &v4l2-timecode; @@ -1114,6 +1116,35 @@ Typically applications shall use this flag for output buffers if the data in this buffer has not been created by the CPU but by some DMA-capable unit, in which case caches have not been used. + + V4L2_BUF_FLAG_TIMESTAMP_MASK + 0xe000 + Mask for timestamp types below. To test the + timestamp type, mask out bits not belonging to timestamp + type by performing a logical and operation with buffer + flags and timestamp mask. + + + V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN + 0x0000 + Unknown timestamp type. This type is used by + drivers before Linux 3.9 and may be either monotonic (see + below) or realtime (wall clock). Monotonic clock has been + favoured in embedded systems whereas most of the drivers + use the realtime clock. Either kinds of timestamps are + available in user space via + clock_gettime(2) using clock IDs + CLOCK_MONOTONIC and + CLOCK_REALTIME, respectively. + + + V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC + 0x2000 + The buffer timestamp has been taken from the + CLOCK_MONOTONIC clock. To access the + same clock outside V4L2, use + clock_gettime(2) . +
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index 4d110b1ad3e9..8fe29427c8e4 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -139,6 +139,16 @@ structs, ioctls) must be noted in more detail in the history chapter (compat.xml), along with the possible impact on existing drivers and applications. --> + + 3.9 + 2012-12-03 + sa + Added timestamp types to + v4l2_buffer, see . + + + 3.6 2012-07-02 @@ -472,7 +482,7 @@ and discussions on the V4L mailing list. Video for Linux Two API Specification - Revision 3.6 + Revision 3.9 &sub-common; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 39d2cecdf38c..94cbe26e9f00 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -701,6 +701,10 @@ struct v4l2_buffer { /* Cache handling flags */ #define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800 #define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x1000 +/* Timestamp type */ +#define V4L2_BUF_FLAG_TIMESTAMP_MASK 0xe000 +#define V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN 0x0000 +#define V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC 0x2000 /** * struct v4l2_exportbuffer - export of video buffer as DMABUF file descriptor From abd23295648a9e3ae72a806e70a510d3dcd8b374 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 15 Sep 2012 07:51:47 -0300 Subject: [PATCH 0262/4607] [media] v4l: Helper function for obtaining timestamps v4l2_get_timestamp() produces a monotonic timestamp but unlike ktime_get_ts(), it uses struct timeval instead of struct timespec, saving the drivers the conversion job when getting timestamps for v4l2_buffer's timestamp field. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-common.c | 10 ++++++++++ include/media/v4l2-common.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 380ddd89fa4c..614316f9b7a4 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -978,3 +978,13 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( return best; } EXPORT_SYMBOL_GPL(v4l2_find_nearest_format); + +void v4l2_get_timestamp(struct timeval *tv) +{ + struct timespec ts; + + ktime_get_ts(&ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; +} +EXPORT_SYMBOL_GPL(v4l2_get_timestamp); diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 1a0b2db4c5d3..ec7c9c00b256 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -225,4 +225,6 @@ bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync, struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait); +void v4l2_get_timestamp(struct timeval *tv); + #endif /* V4L2_COMMON_H_ */ From 8e6057b510aad354e017c6dfca7f386a0eb91b63 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 15 Sep 2012 15:14:42 -0300 Subject: [PATCH 0263/4607] [media] v4l: Convert drivers to use monotonic timestamps Convert drivers using wall clock time (CLOCK_REALTIME) to timestamp from the monotonic timer (CLOCK_MONOTONIC). Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_fops.c | 2 +- drivers/media/pci/bt8xx/bttv-driver.c | 6 +++--- drivers/media/pci/cx23885/cx23885-core.c | 2 +- drivers/media/pci/cx23885/cx23885-video.c | 2 +- drivers/media/pci/cx25821/cx25821-video.c | 2 +- drivers/media/pci/cx88/cx88-core.c | 2 +- drivers/media/pci/meye/meye.c | 4 ++-- drivers/media/pci/saa7134/saa7134-core.c | 2 +- drivers/media/pci/sta2x11/sta2x11_vip.c | 2 +- drivers/media/pci/zoran/zoran_device.c | 4 ++-- drivers/media/platform/blackfin/bfin_capture.c | 4 +--- drivers/media/platform/davinci/vpfe_capture.c | 5 +---- drivers/media/platform/davinci/vpif_capture.c | 2 +- drivers/media/platform/davinci/vpif_display.c | 6 +++--- drivers/media/platform/fsl-viu.c | 2 +- drivers/media/platform/omap/omap_vout.c | 2 +- drivers/media/platform/omap24xxcam.c | 2 +- drivers/media/platform/sh_vou.c | 2 +- drivers/media/platform/soc_camera/atmel-isi.c | 2 +- drivers/media/platform/soc_camera/mx1_camera.c | 2 +- drivers/media/platform/soc_camera/mx2_camera.c | 4 ++-- drivers/media/platform/soc_camera/mx3_camera.c | 2 +- drivers/media/platform/soc_camera/omap1_camera.c | 2 +- drivers/media/platform/soc_camera/pxa_camera.c | 2 +- drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c | 2 +- drivers/media/platform/timblogiw.c | 2 +- drivers/media/platform/vino.c | 8 ++++---- drivers/media/platform/vivi.c | 6 ++---- drivers/media/usb/au0828/au0828-video.c | 4 ++-- drivers/media/usb/cpia2/cpia2_usb.c | 2 +- drivers/media/usb/cx231xx/cx231xx-417.c | 4 ++-- drivers/media/usb/cx231xx/cx231xx-vbi.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 2 +- drivers/media/usb/em28xx/em28xx-video.c | 4 ++-- drivers/media/usb/pwc/pwc-if.c | 3 ++- drivers/media/usb/s2255/s2255drv.c | 6 ++---- drivers/media/usb/sn9c102/sn9c102_core.c | 3 ++- drivers/media/usb/stk1160/stk1160-video.c | 2 +- drivers/media/usb/stkwebcam/stk-webcam.c | 2 +- drivers/media/usb/tlg2300/pd-video.c | 2 +- drivers/media/usb/tm6000/tm6000-video.c | 2 +- drivers/media/usb/usbvision/usbvision-core.c | 2 +- drivers/media/usb/zr364xx/zr364xx.c | 6 ++---- 43 files changed, 61 insertions(+), 70 deletions(-) diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index b3890bd49df6..2652f9155c34 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -105,7 +105,7 @@ void saa7146_buffer_finish(struct saa7146_dev *dev, } q->curr->vb.state = state; - do_gettimeofday(&q->curr->vb.ts); + v4l2_get_timestamp(&q->curr->vb.ts); wake_up(&q->curr->vb.done); q->curr = NULL; diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index de6f41f19187..346458bba2c5 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3835,7 +3835,7 @@ bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, { struct timeval ts; - do_gettimeofday(&ts); + v4l2_get_timestamp(&ts); if (wakeup->top == wakeup->bottom) { if (NULL != wakeup->top && curr->top != wakeup->top) { @@ -3878,7 +3878,7 @@ bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup, if (NULL == wakeup) return; - do_gettimeofday(&ts); + v4l2_get_timestamp(&ts); wakeup->vb.ts = ts; wakeup->vb.field_count = btv->field_count; wakeup->vb.state = state; @@ -3949,7 +3949,7 @@ bttv_irq_wakeup_top(struct bttv *btv) btv->curr.top = NULL; bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); - do_gettimeofday(&wakeup->vb.ts); + v4l2_get_timestamp(&wakeup->vb.ts); wakeup->vb.field_count = btv->field_count; wakeup->vb.state = VIDEOBUF_DONE; wake_up(&wakeup->vb.done); diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 065ecd54bda3..c7572594d434 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -439,7 +439,7 @@ void cx23885_wakeup(struct cx23885_tsport *port, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 1a21926ca412..83975313e0f2 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -300,7 +300,7 @@ void cx23885_video_wakeup(struct cx23885_dev *dev, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 53b16dd70320..d4de021dc844 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -130,7 +130,7 @@ void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index 19a58754c6e1..39f095c37ffd 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -549,7 +549,7 @@ void cx88_wakeup(struct cx88_core *core, * up to 32767 buffers in flight... */ if ((s16) (count - buf->count) < 0) break; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i, count, buf->count); buf->vb.state = VIDEOBUF_DONE; diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index ae7d32027bf7..288adea55e33 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -811,7 +811,7 @@ again: mchip_hsize() * mchip_vsize() * 2); meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - do_gettimeofday(&meye.grab_buffer[reqnr].timestamp); + v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); @@ -832,7 +832,7 @@ again: size); meye.grab_buffer[reqnr].size = size; meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; - do_gettimeofday(&meye.grab_buffer[reqnr].timestamp); + v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); meye.grab_buffer[reqnr].sequence = sequence++; kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, sizeof(int), &meye.doneq_lock); diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c index 8976d0e65813..e1aeb51e25ba 100644 --- a/drivers/media/pci/saa7134/saa7134-core.c +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -308,7 +308,7 @@ void saa7134_buffer_finish(struct saa7134_dev *dev, /* finish current buffer */ q->curr->vb.state = state; - do_gettimeofday(&q->curr->vb.ts); + v4l2_get_timestamp(&q->curr->vb.ts); wake_up(&q->curr->vb.done); q->curr = NULL; } diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 4c10205264d4..ed1337a89a26 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1088,7 +1088,7 @@ static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip) REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) & ~DVP_CTL_ENA); if (vip->active) { - do_gettimeofday(&vip->active->ts); + v4l2_get_timestamp(&vip->active->ts); vip->active->field_count++; vip->active->state = VIDEOBUF_DONE; wake_up(&vip->active->done); diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c index a4cd504b8eee..519164c572c8 100644 --- a/drivers/media/pci/zoran/zoran_device.c +++ b/drivers/media/pci/zoran/zoran_device.c @@ -1169,7 +1169,7 @@ zoran_reap_stat_com (struct zoran *zr) } frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; buffer = &zr->jpg_buffers.buffer[frame]; - do_gettimeofday(&buffer->bs.timestamp); + v4l2_get_timestamp(&buffer->bs.timestamp); if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { buffer->bs.length = (stat_com & 0x7fffff) >> 1; @@ -1407,7 +1407,7 @@ zoran_irq (int irq, zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE; zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq; - do_gettimeofday(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); + v4l2_get_timestamp(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); zr->v4l_grab_frame = NO_GRAB_ACTIVE; zr->v4l_pend_tail++; } diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index ec476ef5b709..d422d3c379e4 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -484,15 +484,13 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) { struct ppi_if *ppi = dev_id; struct bcap_device *bcap_dev = ppi->priv; - struct timeval timevalue; struct vb2_buffer *vb = &bcap_dev->cur_frm->vb; dma_addr_t addr; spin_lock(&bcap_dev->lock); if (bcap_dev->cur_frm != bcap_dev->next_frm) { - do_gettimeofday(&timevalue); - vb->v4l2_buf.timestamp = timevalue; + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb2_buffer_done(vb, VB2_BUF_STATE_DONE); bcap_dev->cur_frm = bcap_dev->next_frm; } diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index 8be492cd8ed4..65f4264bd5b4 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -560,10 +560,7 @@ static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) { - struct timeval timevalue; - - do_gettimeofday(&timevalue); - vpfe_dev->cur_frm->ts = timevalue; + v4l2_get_timestamp(&vpfe_dev->cur_frm->ts); vpfe_dev->cur_frm->state = VIDEOBUF_DONE; vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; wake_up_interruptible(&vpfe_dev->cur_frm->done); diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index a409ccefb380..5892d2bc8eee 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -411,7 +411,7 @@ static struct vb2_ops video_qops = { */ static void vpif_process_buffer_complete(struct common_obj *common) { - do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); /* Make curFrm pointing to nextFrm */ diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 9f2b603be9c9..dd249c96126d 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -402,7 +402,7 @@ static void process_interlaced_mode(int fid, struct common_obj *common) /* one frame is displayed If next frame is * available, release cur_frm and move on */ /* Copy frame display time */ - do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); /* Change status of the cur_frm */ vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); @@ -462,8 +462,8 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) if (!channel_first_int[i][channel_id]) { /* Mark status of the cur_frm to * done and unlock semaphore on it */ - do_gettimeofday(&common->cur_frm->vb. - v4l2_buf.timestamp); + v4l2_get_timestamp(&common->cur_frm->vb. + v4l2_buf.timestamp); vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_DONE); /* Make cur_frm pointing to next_frm */ diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index a8ddb0cacab8..d464509d0f0e 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1181,7 +1181,7 @@ static void viu_capture_intr(struct viu_dev *dev, u32 status) if (waitqueue_active(&buf->vb.done)) { list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; wake_up(&buf->vb.done); diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index f7ad54106bc5..c74b0d48dcd6 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -597,7 +597,7 @@ static void omap_vout_isr(void *arg, unsigned int irqstatus) return; spin_lock(&vout->vbq_lock); - do_gettimeofday(&timevalue); + v4l2_get_timestamp(&timevalue); switch (cur_display->type) { case OMAP_DISPLAY_TYPE_DSI: diff --git a/drivers/media/platform/omap24xxcam.c b/drivers/media/platform/omap24xxcam.c index 70f45c381318..eda3274abf8e 100644 --- a/drivers/media/platform/omap24xxcam.c +++ b/drivers/media/platform/omap24xxcam.c @@ -402,7 +402,7 @@ static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma, omap24xxcam_core_disable(cam); spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags); - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count = atomic_add_return(2, &fh->field_count); if (csr & csr_error) { vb->state = VIDEOBUF_ERROR; diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 7494858a2158..1039ae82401b 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1092,7 +1092,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id) list_del(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 6274a91c25c7..c8d748a31944 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -166,7 +166,7 @@ static irqreturn_t atmel_isi_handle_streaming(struct atmel_isi *isi) struct frame_buffer *buf = isi->active; list_del_init(&buf->list); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.sequence = isi->sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); } diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index 032b8c9097f9..674ded646b66 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -307,7 +307,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev, /* _init is used to debug races, see comment in mx1_camera_reqbufs() */ list_del_init(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 2c148028d8c0..3c5ba63cd311 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -516,7 +516,7 @@ static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); @@ -1561,7 +1561,7 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, vb2_get_plane_payload(vb, 0)); list_del_init(&buf->internal.queue); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.sequence = pcdev->frame_count; if (err) vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 261f6e9e1b17..e6bc06bca496 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -156,7 +156,7 @@ static void mx3_cam_dma_done(void *arg) struct mx3_camera_buffer *buf = to_mx3_vb(vb); list_del_init(&buf->queue); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); vb->v4l2_buf.field = mx3_cam->field; vb->v4l2_buf.sequence = mx3_cam->sequence++; vb2_buffer_done(vb, VB2_BUF_STATE_DONE); diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index 13636a585106..b573bd5899de 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -591,7 +591,7 @@ static void videobuf_done(struct omap1_cam_dev *pcdev, suspend_capture(pcdev); } vb->state = result; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); if (result != VIDEOBUF_ERROR) vb->field_count++; wake_up(&vb->done); diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 3434ffe79c6e..8ff961eec39d 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -681,7 +681,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ list_del_init(&vb->queue); vb->state = VIDEOBUF_DONE; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count++; wake_up(&vb->done); dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s dequeud buffer (vb=0x%p)\n", diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 27eeca15bbf0..9f021043cfe6 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -516,7 +516,7 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) pcdev->active = NULL; ret = sh_mobile_ceu_capture(pcdev); - do_gettimeofday(&vb->v4l2_buf.timestamp); + v4l2_get_timestamp(&vb->v4l2_buf.timestamp); if (!ret) { vb->v4l2_buf.field = pcdev->field; vb->v4l2_buf.sequence = pcdev->sequence++; diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c index 02194c056b00..9de014100a0f 100644 --- a/drivers/media/platform/timblogiw.c +++ b/drivers/media/platform/timblogiw.c @@ -130,7 +130,7 @@ static void timblogiw_dma_cb(void *data) if (vb->state != VIDEOBUF_ERROR) { list_del(&vb->queue); - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); vb->field_count = fh->frame_count * 2; vb->state = VIDEOBUF_DONE; diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index 70b0bf4b2900..28350e78b564 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -2474,8 +2474,8 @@ static irqreturn_t vino_interrupt(int irq, void *dev_id) if ((!handled_a) && (done_a || skip_a)) { if (!skip_a) { - do_gettimeofday(&vino_drvdata-> - a.int_data.timestamp); + v4l2_get_timestamp( + &vino_drvdata->a.int_data.timestamp); vino_drvdata->a.int_data.frame_counter = fc_a; } vino_drvdata->a.int_data.skip = skip_a; @@ -2489,8 +2489,8 @@ static irqreturn_t vino_interrupt(int irq, void *dev_id) if ((!handled_b) && (done_b || skip_b)) { if (!skip_b) { - do_gettimeofday(&vino_drvdata-> - b.int_data.timestamp); + v4l2_get_timestamp( + &vino_drvdata->b.int_data.timestamp); vino_drvdata->b.int_data.frame_counter = fc_b; } vino_drvdata->b.int_data.skip = skip_b; diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 0d59b9db83cb..c2f424f32450 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -554,7 +554,6 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { int wmax = dev->width; int hmax = dev->height; - struct timeval ts; void *vbuf = vb2_plane_vaddr(&buf->vb, 0); unsigned ms; char str[100]; @@ -622,8 +621,7 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; dev->field_count++; buf->vb.v4l2_buf.sequence = dev->field_count >> 1; - do_gettimeofday(&ts); - buf->vb.v4l2_buf.timestamp = ts; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); } static void vivi_thread_tick(struct vivi_dev *dev) @@ -645,7 +643,7 @@ static void vivi_thread_tick(struct vivi_dev *dev) list_del(&buf->list); spin_unlock_irqrestore(&dev->slock, flags); - do_gettimeofday(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); /* Fill buffer */ vivi_fillbuff(dev, buf); diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 45387aab10c7..8b9e8268e911 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -304,7 +304,7 @@ static inline void buffer_filled(struct au0828_dev *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.buf = NULL; @@ -321,7 +321,7 @@ static inline void vbi_buffer_filled(struct au0828_dev *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.vbi_buf = NULL; diff --git a/drivers/media/usb/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c index 95b5d6e7cdc4..be1719283609 100644 --- a/drivers/media/usb/cpia2/cpia2_usb.c +++ b/drivers/media/usb/cpia2/cpia2_usb.c @@ -328,7 +328,7 @@ static void cpia2_usb_complete(struct urb *urb) continue; } DBG("Start of frame pattern found\n"); - do_gettimeofday(&cam->workbuff->timestamp); + v4l2_get_timestamp(&cam->workbuff->timestamp); cam->workbuff->seq = cam->frame_count++; cam->workbuff->data[0] = 0xFF; cam->workbuff->data[1] = 0xD8; diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c index b024e5197a75..28688dbcb609 100644 --- a/drivers/media/usb/cx231xx/cx231xx-417.c +++ b/drivers/media/usb/cx231xx/cx231xx-417.c @@ -1291,7 +1291,7 @@ static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *ur buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); dma_q->mpeg_buffer_completed = 0; @@ -1327,7 +1327,7 @@ static void buffer_filled(char *data, int len, struct urb *urb, memcpy(vbuf, data, len); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/usb/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c index ac7db52f404f..46e3892557c2 100644 --- a/drivers/media/usb/cx231xx/cx231xx-vbi.c +++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c @@ -530,7 +530,7 @@ static inline void vbi_buffer_filled(struct cx231xx *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->vbi_mode.bulk_ctl.buf = NULL; diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index fedf7852a355..239cb913be5c 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -235,7 +235,7 @@ static inline void buffer_filled(struct cx231xx *dev, cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); if (dev->USE_ISO) dev->video_mode.isoc_ctl.buf = NULL; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 1e553d357380..766ad125fc08 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -163,7 +163,7 @@ static inline void buffer_filled(struct em28xx *dev, em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.vid_buf = NULL; @@ -180,7 +180,7 @@ static inline void vbi_buffer_filled(struct em28xx *dev, buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); dev->isoc_ctl.vbi_buf = NULL; diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 5210239cbaee..21c15233c6ae 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -316,7 +316,8 @@ static void pwc_isoc_handler(struct urb *urb) struct pwc_frame_buf *fbuf = pdev->fill_buf; if (pdev->vsync == 1) { - do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp( + &fbuf->vb.v4l2_buf.timestamp); pdev->vsync = 2; } diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 8ebec0d7bf59..498c57ea5d32 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -593,7 +593,7 @@ static int s2255_got_frame(struct s2255_channel *channel, int jpgsize) buf = list_entry(dma_q->active.next, struct s2255_buffer, vb.queue); list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); s2255_fillbuff(channel, buf, jpgsize); wake_up(&buf->vb.done); dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); @@ -629,7 +629,6 @@ static void s2255_fillbuff(struct s2255_channel *channel, struct s2255_buffer *buf, int jpgsize) { int pos = 0; - struct timeval ts; const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; @@ -674,8 +673,7 @@ static void s2255_fillbuff(struct s2255_channel *channel, /* tell v4l buffer was filled */ buf->vb.field_count = channel->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; } diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c index 73605864fffa..8dbf0c721c4a 100644 --- a/drivers/media/usb/sn9c102/sn9c102_core.c +++ b/drivers/media/usb/sn9c102/sn9c102_core.c @@ -773,7 +773,8 @@ end_of_frame: img); if ((*f)->buf.bytesused == 0) - do_gettimeofday(&(*f)->buf.timestamp); + v4l2_get_timestamp( + &(*f)->buf.timestamp); (*f)->buf.bytesused += img; diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index fa3671de02aa..0a4ee85f4399 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -101,7 +101,7 @@ void stk1160_buffer_done(struct stk1160 *dev) buf->vb.v4l2_buf.sequence = dev->field_count >> 1; buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; buf->vb.v4l2_buf.bytesused = buf->bytesused; - do_gettimeofday(&buf->vb.v4l2_buf.timestamp); + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); vb2_set_plane_payload(&buf->vb, 0, buf->bytesused); vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 5d3c032d733c..bf56904decb4 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -1113,7 +1113,7 @@ static int stk_vidioc_dqbuf(struct file *filp, sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; sbuf->v4lbuf.sequence = ++dev->sequence; - do_gettimeofday(&sbuf->v4lbuf.timestamp); + v4l2_get_timestamp(&sbuf->v4lbuf.timestamp); *buf = sbuf->v4lbuf; return 0; diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c index 3082bfa9b2c5..21723378bb8f 100644 --- a/drivers/media/usb/tlg2300/pd-video.c +++ b/drivers/media/usb/tlg2300/pd-video.c @@ -212,7 +212,7 @@ static void submit_frame(struct front_face *front) front->curr_frame = NULL; vb->state = VIDEOBUF_DONE; vb->field_count++; - do_gettimeofday(&vb->ts); + v4l2_get_timestamp(&vb->ts); wake_up(&vb->done); } diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 1edc2517603f..e3c567c27918 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -194,7 +194,7 @@ static inline void buffer_filled(struct tm6000_core *dev, dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); list_del(&buf->vb.queue); wake_up(&buf->vb.done); diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index c9b2042f8bdf..816b1cffab7d 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -1169,7 +1169,7 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) if (newstate == parse_state_next_frame) { frame->grabstate = frame_state_done; - do_gettimeofday(&(frame->timestamp)); + v4l2_get_timestamp(&(frame->timestamp)); frame->sequence = usbvision->frame_num; spin_lock_irqsave(&usbvision->queue_lock, lock_flags); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 39edd4442932..74d56df3347f 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -501,7 +501,6 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, int jpgsize) { int pos = 0; - struct timeval ts; const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; @@ -530,8 +529,7 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, /* tell v4l buffer was filled */ buf->vb.field_count = cam->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; + v4l2_get_timestamp(&buf->vb.ts); buf->vb.state = VIDEOBUF_DONE; } @@ -559,7 +557,7 @@ static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) goto unlock; } list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); + v4l2_get_timestamp(&buf->vb.ts); DBG("[%p/%d] wakeup\n", buf, buf->vb.i); zr364xx_fillbuff(cam, buf, jpgsize); wake_up(&buf->vb.done); From 1b18e7a0be859911b22138ce27258687efc528b8 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Mon, 22 Oct 2012 17:10:16 -0300 Subject: [PATCH 0264/4607] [media] v4l: Tell user space we're using monotonic timestamps Set buffer timestamp flags for videobuf, videobuf2 and drivers that use neither. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/meye/meye.c | 4 ++-- drivers/media/pci/zoran/zoran_driver.c | 2 +- drivers/media/platform/omap3isp/ispqueue.c | 1 + drivers/media/platform/vino.c | 3 +++ drivers/media/usb/cpia2/cpia2_v4l.c | 5 ++++- drivers/media/usb/sn9c102/sn9c102_core.c | 2 +- drivers/media/usb/stkwebcam/stk-webcam.c | 1 + drivers/media/usb/usbvision/usbvision-video.c | 5 +++-- drivers/media/v4l2-core/videobuf-core.c | 2 +- drivers/media/v4l2-core/videobuf2-core.c | 10 ++++++---- 10 files changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 288adea55e33..ac7ab6edb06d 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -1426,7 +1426,7 @@ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) return -EINVAL; buf->bytesused = meye.grab_buffer[index].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; if (meye.grab_buffer[index].state == MEYE_BUF_USING) buf->flags |= V4L2_BUF_FLAG_QUEUED; @@ -1499,7 +1499,7 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->index = reqnr; buf->bytesused = meye.grab_buffer[reqnr].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; buf->timestamp = meye.grab_buffer[reqnr].timestamp; buf->sequence = meye.grab_buffer[reqnr].sequence; diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c index 53f12c7466b0..33521a4f23a7 100644 --- a/drivers/media/pci/zoran/zoran_driver.c +++ b/drivers/media/pci/zoran/zoran_driver.c @@ -1334,7 +1334,7 @@ static int zoran_v4l2_buffer_status(struct zoran_fh *fh, struct zoran *zr = fh->zr; unsigned long flags; - buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c index 15bf3eab2224..6599963cdd9b 100644 --- a/drivers/media/platform/omap3isp/ispqueue.c +++ b/drivers/media/platform/omap3isp/ispqueue.c @@ -674,6 +674,7 @@ static int isp_video_queue_alloc(struct isp_video_queue *queue, buf->vbuf.index = i; buf->vbuf.length = size; buf->vbuf.type = queue->type; + buf->vbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->vbuf.field = V4L2_FIELD_NONE; buf->vbuf.memory = memory; diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c index 28350e78b564..eb5d6f955709 100644 --- a/drivers/media/platform/vino.c +++ b/drivers/media/platform/vino.c @@ -3410,6 +3410,9 @@ static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs, if (fb->map_count > 0) b->flags |= V4L2_BUF_FLAG_MAPPED; + b->flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MASK; + b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + b->index = fb->id; b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ? V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR; diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c index aeb9d2275725..d5d42b6e94be 100644 --- a/drivers/media/usb/cpia2/cpia2_v4l.c +++ b/drivers/media/usb/cpia2/cpia2_v4l.c @@ -825,6 +825,8 @@ static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) else buf->flags = 0; + buf->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + switch (cam->buffers[buf->index].status) { case FRAME_EMPTY: case FRAME_ERROR: @@ -943,7 +945,8 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) buf->index = frame; buf->bytesused = cam->buffers[buf->index].length; - buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE; + buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE + | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->field = V4L2_FIELD_NONE; buf->timestamp = cam->buffers[buf->index].timestamp; buf->sequence = cam->buffers[buf->index].seq; diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c index 8dbf0c721c4a..6bda81aebf87 100644 --- a/drivers/media/usb/sn9c102/sn9c102_core.c +++ b/drivers/media/usb/sn9c102/sn9c102_core.c @@ -173,7 +173,7 @@ sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, cam->frame[i].buf.sequence = 0; cam->frame[i].buf.field = V4L2_FIELD_NONE; cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = 0; + cam->frame[i].buf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; } return cam->nbuffers; diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index bf56904decb4..52296f7ec966 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -466,6 +466,7 @@ static int stk_setup_siobuf(struct stk_camera *dev, int index) buf->dev = dev; buf->v4lbuf.index = index; buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->v4lbuf.flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; buf->v4lbuf.field = V4L2_FIELD_NONE; buf->v4lbuf.memory = V4L2_MEMORY_MMAP; buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 5c36a57e6590..c6bc8ce67375 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -761,7 +761,7 @@ static int vidioc_querybuf(struct file *file, if (vb->index >= usbvision->num_frames) return -EINVAL; /* Updating the corresponding frame state */ - vb->flags = 0; + vb->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; frame = &usbvision->frame[vb->index]; if (frame->grabstate >= frame_state_ready) vb->flags |= V4L2_BUF_FLAG_QUEUED; @@ -843,7 +843,8 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb) vb->memory = V4L2_MEMORY_MMAP; vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | - V4L2_BUF_FLAG_DONE; + V4L2_BUF_FLAG_DONE | + V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; vb->index = f->index; vb->sequence = f->sequence; vb->timestamp = f->timestamp; diff --git a/drivers/media/v4l2-core/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c index 5449e8aa984a..fb5ee5dd8fe9 100644 --- a/drivers/media/v4l2-core/videobuf-core.c +++ b/drivers/media/v4l2-core/videobuf-core.c @@ -340,7 +340,7 @@ static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, break; } - b->flags = 0; + b->flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; if (vb->map) b->flags |= V4L2_BUF_FLAG_MAPPED; diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 9f81be23a81f..85e3c221dadc 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -40,9 +40,10 @@ module_param(debug, int, 0644); #define call_qop(q, op, args...) \ (((q)->ops->op) ? ((q)->ops->op(args)) : 0) -#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ +#define V4L2_BUFFER_MASK_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \ - V4L2_BUF_FLAG_PREPARED) + V4L2_BUF_FLAG_PREPARED | \ + V4L2_BUF_FLAG_TIMESTAMP_MASK) /** * __vb2_buf_mem_alloc() - allocate video memory for the given buffer @@ -401,7 +402,8 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) /* * Clear any buffer state related flags. */ - b->flags &= ~V4L2_BUFFER_STATE_FLAGS; + b->flags &= ~V4L2_BUFFER_MASK_FLAGS; + b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; switch (vb->state) { case VB2_BUF_STATE_QUEUED: @@ -939,7 +941,7 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b vb->v4l2_buf.field = b->field; vb->v4l2_buf.timestamp = b->timestamp; - vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS; + vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS; } /** From fe4abb550f68ace5bbc23030c968c138c6a69759 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 16 Nov 2012 17:42:12 -0300 Subject: [PATCH 0265/4607] [media] v4l: There's no __unsigned Correct a typo. v4l2_plane.m.userptr is unsigned long, not __unsigned long. Signed-off-by: Sakari Ailus Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/io.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index 09e8dcf5e9c4..2c4646d504c2 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -905,7 +905,7 @@ should set this to 0.
- __unsigned long + unsigned long userptr When the memory type in the containing &v4l2-buffer; is V4L2_MEMORY_USERPTR, this is a userspace From 1bc05e77db1b0c2f5ec3917a9b21d64c01342b5f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 26 Nov 2012 11:08:26 -0300 Subject: [PATCH 0266/4607] [media] s5p-fimc: Fix horizontal/vertical image flip Setting FIMC_REG_CITRGFMT_FLIP_X_MIRROR bit causes X-axis image flip (vertical flip) and thus it corresponds to V4L2_CID_VFLIP. Likewise, setting FIMC_REG_CITRGFMT_FLIP_Y_MIRROR bit causes Y-axis image flip (horizontal flip) and thus it corresponds to V4L2_CID_HFLIP. Currently the driver does X-axis flip when V4L2_CID_HFLIP is set and Y-axis flip for V4L2_CID_VFLIP. Fix this incorrect assignment by setting proper FIMC_REG_CITRGFMT register bits for ctx->hflip and ctx->vflip. Reported-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-reg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c index 2c9d0c06c9e8..9c3c461a5099 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-reg.c @@ -44,9 +44,9 @@ static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx) u32 flip = FIMC_REG_MSCTRL_FLIP_NORMAL; if (ctx->hflip) - flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR; - if (ctx->vflip) flip = FIMC_REG_MSCTRL_FLIP_Y_MIRROR; + if (ctx->vflip) + flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR; if (ctx->rotation <= 90) return flip; @@ -59,9 +59,9 @@ static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx) u32 flip = FIMC_REG_CITRGFMT_FLIP_NORMAL; if (ctx->hflip) - flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR; - if (ctx->vflip) flip |= FIMC_REG_CITRGFMT_FLIP_Y_MIRROR; + if (ctx->vflip) + flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR; if (ctx->rotation <= 90) return flip; From ef2c83262a989dc20268dc25d4ce3725ac9d7886 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 23 Nov 2012 15:17:40 -0300 Subject: [PATCH 0267/4607] [media] s5p-csis: Correct the event counters logging The counter field is unsigned so >= 0 condition always evaluates to true. Fix this to log events for which counter is > 0 or for all when in debug mode. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 4c961b1b68e6..9528ee65a843 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -401,12 +401,12 @@ static void s5pcsis_log_counters(struct csis_state *state, bool non_errors) spin_lock_irqsave(&state->slock, flags); - for (i--; i >= 0; i--) - if (state->events[i].counter >= 0) + for (i--; i >= 0; i--) { + if (state->events[i].counter > 0 || debug) v4l2_info(&state->sd, "%s events: %d\n", state->events[i].name, state->events[i].counter); - + } spin_unlock_irqrestore(&state->slock, flags); } From b01189b85b7653a51ebe851fc4d70702cebfed3c Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 28 Nov 2012 14:40:32 -0300 Subject: [PATCH 0268/4607] [media] V4L: DocBook: Add V4L2_MBUS_FMT_YUV10_1X30 media bus pixel code This patch adds definition of media bus code for YUV pixel format transferred in 30-bit samples where each component has 10 bits width. [mchehab@redhat.com: fix a merge conflict at v4l2-mediabus.h] Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/subdev-formats.xml | 710 +++++------------- Documentation/DocBook/media_api.tmpl | 1 + include/uapi/linux/v4l2-mediabus.h | 3 +- 3 files changed, 192 insertions(+), 522 deletions(-) diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 6f341d1eca1a..cc51372ed5e0 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -973,27 +973,37 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Identifier @@ -1005,6 +1015,16 @@ Bit + 29 + 28 + 27 + 26 + 25 + 24 + 23 + 22 + 21 + 10 19 18 17 @@ -1032,16 +1052,8 @@ V4L2_MBUS_FMT_Y8_1X8 0x2001 - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1107,16 +1119,8 @@ V4L2_MBUS_FMT_UYVY8_1_5X8 0x2002 - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - u7 @@ -1132,16 +1136,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1157,16 +1153,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1182,16 +1170,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - v7 @@ -1207,16 +1187,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1232,16 +1204,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1257,16 +1221,8 @@ V4L2_MBUS_FMT_VYUY8_1_5X8 0x2003 - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - v7 @@ -1282,16 +1238,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1307,16 +1255,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1332,16 +1272,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - u7 @@ -1357,16 +1289,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1382,16 +1306,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1407,16 +1323,8 @@ V4L2_MBUS_FMT_YUYV8_1_5X8 0x2004 - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1432,16 +1340,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1457,16 +1357,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - u7 @@ -1482,16 +1374,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1507,16 +1391,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1532,16 +1408,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - v7 @@ -1557,16 +1425,8 @@ V4L2_MBUS_FMT_YVYU8_1_5X8 0x2005 - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1582,16 +1442,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1607,16 +1459,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - v7 @@ -1632,16 +1476,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1657,16 +1493,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1682,16 +1510,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - u7 @@ -1707,16 +1527,8 @@ V4L2_MBUS_FMT_UYVY8_2X8 0x2006 - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - u7 @@ -1732,16 +1544,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1757,16 +1561,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - v7 @@ -1782,16 +1578,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1807,16 +1595,8 @@ V4L2_MBUS_FMT_VYUY8_2X8 0x2007 - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - v7 @@ -1832,16 +1612,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1857,16 +1629,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - u7 @@ -1882,16 +1646,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1907,16 +1663,8 @@ V4L2_MBUS_FMT_YUYV8_2X8 0x2008 - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1932,16 +1680,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - u7 @@ -1957,16 +1697,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -1982,16 +1714,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - v7 @@ -2007,16 +1731,8 @@ V4L2_MBUS_FMT_YVYU8_2X8 0x2009 - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -2032,16 +1748,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - v7 @@ -2057,16 +1765,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - y7 @@ -2082,16 +1782,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; - - u7 @@ -2107,16 +1799,8 @@ V4L2_MBUS_FMT_Y10_1X10 0x200a - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; y9 y8 y7 @@ -2132,16 +1816,8 @@ V4L2_MBUS_FMT_YUYV10_2X10 0x200b - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; y9 y8 y7 @@ -2157,16 +1833,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; u9 u8 u7 @@ -2182,16 +1850,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; y9 y8 y7 @@ -2207,16 +1867,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; v9 v8 v7 @@ -2232,16 +1884,8 @@ V4L2_MBUS_FMT_YVYU10_2X10 0x200c - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; y9 y8 y7 @@ -2257,16 +1901,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; v9 v8 v7 @@ -2282,16 +1918,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; y9 y8 y7 @@ -2307,16 +1935,8 @@ - - - - - - - - - - - - - - - - - - - - + &dash-ent-10; + &dash-ent-10; u9 u8 u7 @@ -2332,6 +1952,7 @@ V4L2_MBUS_FMT_Y12_1X12 0x2013 + &dash-ent-10; - - - @@ -2357,6 +1978,7 @@ V4L2_MBUS_FMT_UYVY8_1X16 0x200f + &dash-ent-10; - - - @@ -2382,6 +2004,7 @@ + &dash-ent-10; - - - @@ -2407,6 +2030,7 @@ V4L2_MBUS_FMT_VYUY8_1X16 0x2010 + &dash-ent-10; - - - @@ -2432,6 +2056,7 @@ + &dash-ent-10; - - - @@ -2457,6 +2082,7 @@ V4L2_MBUS_FMT_YUYV8_1X16 0x2011 + &dash-ent-10; - - - @@ -2482,6 +2108,7 @@ + &dash-ent-10; - - - @@ -2507,6 +2134,7 @@ V4L2_MBUS_FMT_YVYU8_1X16 0x2012 + &dash-ent-10; - - - @@ -2532,6 +2160,7 @@ + &dash-ent-10; - - - @@ -2657,6 +2286,7 @@ V4L2_MBUS_FMT_YUYV10_1X20 0x200d + &dash-ent-10; y9 y8 y7 @@ -2682,6 +2312,7 @@ + &dash-ent-10; y9 y8 y7 @@ -2707,6 +2338,7 @@ V4L2_MBUS_FMT_YVYU10_1X20 0x200e + &dash-ent-10; y9 y8 y7 @@ -2732,6 +2364,7 @@ + &dash-ent-10; y9 y8 y7 @@ -2753,6 +2386,41 @@ u1 u0 + + V4L2_MBUS_FMT_YUV10_1X30 + 0x2014 + + y9 + y8 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u9 + u8 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + v9 + v8 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl index f2413acfe241..1f6593deb995 100644 --- a/Documentation/DocBook/media_api.tmpl +++ b/Documentation/DocBook/media_api.tmpl @@ -22,6 +22,7 @@ http://linuxtv.org/repo/"> +----------"> ]> diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index e860f55820ec..b9b7bea04537 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -47,7 +47,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB565_2X8_BE = 0x1007, V4L2_MBUS_FMT_RGB565_2X8_LE = 0x1008, - /* YUV (including grey) - next is 0x2016 */ + /* YUV (including grey) - next is 0x2017 */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, @@ -69,6 +69,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_YDYUYDYV8_1X16 = 0x2014, V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, + V4L2_MBUS_FMT_YUV10_1X30 = 0x2016, /* Bayer - next is 0x3019 */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, From a62082ffa11eb78ea67788b4fb6dbb32ae27464b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 16 Nov 2012 16:52:57 -0300 Subject: [PATCH 0269/4607] [media] fimc-lite: Register dump function cleanup Use v4l2_info() to make it possible to identify which FIMC-LITE device instance the logs refer to. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-lite-reg.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c index a22d7eb05c82..ad63ebf082c5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c @@ -292,9 +292,11 @@ void flite_hw_dump_regs(struct fimc_lite *dev, const char *label) }; u32 i; - pr_info("--- %s ---\n", label); + v4l2_info(&dev->subdev, "--- %s ---\n", label); + for (i = 0; i < ARRAY_SIZE(registers); i++) { u32 cfg = readl(dev->regs + registers[i].offset); - pr_info("%s: %s:\t0x%08x\n", __func__, registers[i].name, cfg); + v4l2_info(&dev->subdev, "%9s: 0x%08x\n", + registers[i].name, cfg); } } From 35f29248548b86df50b0b9b280f3b759529fe853 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 22 Nov 2012 14:01:39 -0300 Subject: [PATCH 0270/4607] [media] s5p-fimc: Clean up capture enable/disable helpers The FIMC FIFO output is not supported in the driver due to some hardware issues thus we can remove some code as out_path is always FIMC_IO_DMA. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-reg.c | 38 +++++++++------------- drivers/media/platform/s5p-fimc/fimc-reg.h | 4 +-- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c index 9c3c461a5099..f0c34ca41894 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-reg.c @@ -344,30 +344,31 @@ void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) } } -void fimc_hw_en_capture(struct fimc_ctx *ctx) +void fimc_hw_enable_capture(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; + u32 cfg; - u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); - - if (ctx->out_path == FIMC_IO_DMA) { - /* one shot mode */ - cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | - FIMC_REG_CIIMGCPT_IMGCPTEN; - } else { - /* Continuous frame capture mode (freerun). */ - cfg &= ~(FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | - FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT); - cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; - } + cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); + cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE; if (ctx->scaler.enabled) cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; + else + cfg &= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); } +void fimc_hw_disable_capture(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); + cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | + FIMC_REG_CIIMGCPT_IMGCPTEN_SC); + writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); +} + void fimc_hw_set_effect(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; @@ -737,13 +738,6 @@ void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on) writel(cfg, dev->regs + FIMC_REG_MSCTRL); } -void fimc_hw_dis_capture(struct fimc_dev *dev) -{ - u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); - cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | FIMC_REG_CIIMGCPT_IMGCPTEN_SC); - writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); -} - /* Return an index to the buffer actually being written. */ s32 fimc_hw_get_frame_index(struct fimc_dev *dev) { @@ -776,13 +770,13 @@ s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev) void fimc_activate_capture(struct fimc_ctx *ctx) { fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled); - fimc_hw_en_capture(ctx); + fimc_hw_enable_capture(ctx); } void fimc_deactivate_capture(struct fimc_dev *fimc) { fimc_hw_en_lastirq(fimc, true); - fimc_hw_dis_capture(fimc); + fimc_hw_disable_capture(fimc); fimc_hw_enable_scaler(fimc, false); fimc_hw_en_lastirq(fimc, false); } diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.h b/drivers/media/platform/s5p-fimc/fimc-reg.h index b6abfc7b72ac..f3e0b78a3736 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.h +++ b/drivers/media/platform/s5p-fimc/fimc-reg.h @@ -287,7 +287,7 @@ void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable); void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); void fimc_hw_set_prescaler(struct fimc_ctx *ctx); void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); -void fimc_hw_en_capture(struct fimc_ctx *ctx); +void fimc_hw_enable_capture(struct fimc_ctx *ctx); void fimc_hw_set_effect(struct fimc_ctx *ctx); void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx); void fimc_hw_set_in_dma(struct fimc_ctx *ctx); @@ -306,7 +306,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, void fimc_hw_clear_irq(struct fimc_dev *dev); void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on); void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on); -void fimc_hw_dis_capture(struct fimc_dev *dev); +void fimc_hw_disable_capture(struct fimc_dev *dev); s32 fimc_hw_get_frame_index(struct fimc_dev *dev); s32 fimc_hw_get_prev_frame_index(struct fimc_dev *dev); void fimc_activate_capture(struct fimc_ctx *ctx); From 405f230c44d80976e0ecfc04015ca3bd976dd284 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 2 Aug 2012 10:27:46 -0300 Subject: [PATCH 0271/4607] [media] s5p-fimc: Add variant data structure for Exynos4x12 Add variant data structures for Exynos4212 and Exynos4412 SoC. Add 'const' qualifier for the variant description structures. Also remove has_cam_if flags from FIMC3 on Exynos4210 SoC is it has no interconnections the camera ports. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/s5p-fimc/fimc-capture.c | 8 +- drivers/media/platform/s5p-fimc/fimc-core.c | 90 ++++++++++++++----- drivers/media/platform/s5p-fimc/fimc-core.h | 8 +- drivers/media/platform/s5p-fimc/fimc-m2m.c | 2 +- drivers/media/platform/s5p-fimc/fimc-reg.c | 2 +- 5 files changed, 78 insertions(+), 32 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index fdb6740248a7..52d8d885d287 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -626,8 +626,8 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, { bool rotation = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *var = fimc->variant; - struct fimc_pix_limit *pl = var->pix_limit; + const struct fimc_variant *var = fimc->variant; + const struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *dst = &ctx->d_frame; u32 depth, min_w, max_w, min_h, align_h = 3; u32 mask = FMT_FLAGS_CAM; @@ -699,8 +699,8 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx, { bool rotate = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *var = fimc->variant; - struct fimc_pix_limit *pl = var->pix_limit; + const struct fimc_variant *var = fimc->variant; + const struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *sink = &ctx->s_frame; u32 max_w, max_h, min_w = 0, min_h = 0, min_sz; u32 align_sz = 0, align_h = 4; diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 8d0d2b94a135..2a1558a37a41 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -241,7 +241,7 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) int fimc_set_scaler_info(struct fimc_ctx *ctx) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; struct device *dev = &ctx->fimc_dev->pdev->dev; struct fimc_scaler *sc = &ctx->scaler; struct fimc_frame *s_frame = &ctx->s_frame; @@ -440,7 +440,7 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; u32 i, depth = 0; for (i = 0; i < f->fmt->colplanes; i++) @@ -524,7 +524,7 @@ static int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) { struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *variant = fimc->variant; + const struct fimc_variant *variant = fimc->variant; unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT; int ret = 0; @@ -591,7 +591,7 @@ static const struct v4l2_ctrl_ops fimc_ctrl_ops = { int fimc_ctrls_create(struct fimc_ctx *ctx) { - struct fimc_variant *variant = ctx->fimc_dev->variant; + const struct fimc_variant *variant = ctx->fimc_dev->variant; unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt); struct fimc_ctrls *ctrls = &ctx->ctrls; struct v4l2_ctrl_handler *handler = &ctrls->handler; @@ -881,7 +881,7 @@ static int fimc_m2m_resume(struct fimc_dev *fimc) static int fimc_probe(struct platform_device *pdev) { - struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); + const struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); struct s5p_platform_fimc *pdata; struct fimc_dev *fimc; struct resource *res; @@ -1053,7 +1053,7 @@ static int __devexit fimc_remove(struct platform_device *pdev) } /* Image pixel limits, similar across several FIMC HW revisions. */ -static struct fimc_pix_limit s5p_pix_limit[4] = { +static const struct fimc_pix_limit s5p_pix_limit[4] = { [0] = { .scaler_en_w = 3264, .scaler_dis_w = 8192, @@ -1088,7 +1088,7 @@ static struct fimc_pix_limit s5p_pix_limit[4] = { }, }; -static struct fimc_variant fimc0_variant_s5p = { +static const struct fimc_variant fimc0_variant_s5p = { .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1100,7 +1100,7 @@ static struct fimc_variant fimc0_variant_s5p = { .pix_limit = &s5p_pix_limit[0], }; -static struct fimc_variant fimc2_variant_s5p = { +static const struct fimc_variant fimc2_variant_s5p = { .has_cam_if = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, @@ -1110,7 +1110,7 @@ static struct fimc_variant fimc2_variant_s5p = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc0_variant_s5pv210 = { +static const struct fimc_variant fimc0_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1123,7 +1123,7 @@ static struct fimc_variant fimc0_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc1_variant_s5pv210 = { +static const struct fimc_variant fimc1_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1137,7 +1137,7 @@ static struct fimc_variant fimc1_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct fimc_variant fimc2_variant_s5pv210 = { +static const struct fimc_variant fimc2_variant_s5pv210 = { .has_cam_if = 1, .pix_hoff = 1, .min_inp_pixsize = 16, @@ -1148,7 +1148,7 @@ static struct fimc_variant fimc2_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct fimc_variant fimc0_variant_exynos4 = { +static const struct fimc_variant fimc0_variant_exynos4210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1164,9 +1164,8 @@ static struct fimc_variant fimc0_variant_exynos4 = { .pix_limit = &s5p_pix_limit[1], }; -static struct fimc_variant fimc3_variant_exynos4 = { +static const struct fimc_variant fimc3_variant_exynos4210 = { .pix_hoff = 1, - .has_cam_if = 1, .has_cistatus2 = 1, .has_mainscaler_ext = 1, .has_alpha = 1, @@ -1178,8 +1177,38 @@ static struct fimc_variant fimc3_variant_exynos4 = { .pix_limit = &s5p_pix_limit[3], }; +static const struct fimc_variant fimc0_variant_exynos4x12 = { + .pix_hoff = 1, + .has_inp_rot = 1, + .has_out_rot = 1, + .has_cam_if = 1, + .has_isp_wb = 1, + .has_cistatus2 = 1, + .has_mainscaler_ext = 1, + .has_alpha = 1, + .min_inp_pixsize = 16, + .min_out_pixsize = 16, + .hor_offs_align = 2, + .min_vsize_align = 1, + .out_buf_count = 32, + .pix_limit = &s5p_pix_limit[1], +}; + +static const struct fimc_variant fimc3_variant_exynos4x12 = { + .pix_hoff = 1, + .has_cistatus2 = 1, + .has_mainscaler_ext = 1, + .has_alpha = 1, + .min_inp_pixsize = 16, + .min_out_pixsize = 16, + .hor_offs_align = 2, + .min_vsize_align = 1, + .out_buf_count = 32, + .pix_limit = &s5p_pix_limit[3], +}; + /* S5PC100 */ -static struct fimc_drvdata fimc_drvdata_s5p = { +static const struct fimc_drvdata fimc_drvdata_s5p = { .variant = { [0] = &fimc0_variant_s5p, [1] = &fimc0_variant_s5p, @@ -1190,7 +1219,7 @@ static struct fimc_drvdata fimc_drvdata_s5p = { }; /* S5PV210, S5PC110 */ -static struct fimc_drvdata fimc_drvdata_s5pv210 = { +static const struct fimc_drvdata fimc_drvdata_s5pv210 = { .variant = { [0] = &fimc0_variant_s5pv210, [1] = &fimc1_variant_s5pv210, @@ -1201,18 +1230,30 @@ static struct fimc_drvdata fimc_drvdata_s5pv210 = { }; /* EXYNOS4210, S5PV310, S5PC210 */ -static struct fimc_drvdata fimc_drvdata_exynos4 = { +static const struct fimc_drvdata fimc_drvdata_exynos4210 = { .variant = { - [0] = &fimc0_variant_exynos4, - [1] = &fimc0_variant_exynos4, - [2] = &fimc0_variant_exynos4, - [3] = &fimc3_variant_exynos4, + [0] = &fimc0_variant_exynos4210, + [1] = &fimc0_variant_exynos4210, + [2] = &fimc0_variant_exynos4210, + [3] = &fimc3_variant_exynos4210, }, .num_entities = 4, .lclk_frequency = 166000000UL, }; -static struct platform_device_id fimc_driver_ids[] = { +/* EXYNOS4212, EXYNOS4412 */ +static const struct fimc_drvdata fimc_drvdata_exynos4x12 = { + .variant = { + [0] = &fimc0_variant_exynos4x12, + [1] = &fimc0_variant_exynos4x12, + [2] = &fimc0_variant_exynos4x12, + [3] = &fimc3_variant_exynos4x12, + }, + .num_entities = 4, + .lclk_frequency = 166000000UL, +}; + +static const struct platform_device_id fimc_driver_ids[] = { { .name = "s5p-fimc", .driver_data = (unsigned long)&fimc_drvdata_s5p, @@ -1221,7 +1262,10 @@ static struct platform_device_id fimc_driver_ids[] = { .driver_data = (unsigned long)&fimc_drvdata_s5pv210, }, { .name = "exynos4-fimc", - .driver_data = (unsigned long)&fimc_drvdata_exynos4, + .driver_data = (unsigned long)&fimc_drvdata_exynos4210, + }, { + .name = "exynos4x12-fimc", + .driver_data = (unsigned long)&fimc_drvdata_exynos4x12, }, {}, }; diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/s5p-fimc/fimc-core.h index c0040d792499..424ff960f0d9 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.h +++ b/drivers/media/platform/s5p-fimc/fimc-core.h @@ -372,6 +372,7 @@ struct fimc_pix_limit { * @has_mainscaler_ext: 1 if extended mainscaler ratios in CIEXTEN register * are present in this IP revision * @has_cam_if: set if this instance has a camera input interface + * @has_isp_wb: set if this instance has ISP writeback input * @pix_limit: pixel size constraints for the scaler * @min_inp_pixsize: minimum input pixel size * @min_out_pixsize: minimum output pixel size @@ -386,8 +387,9 @@ struct fimc_variant { unsigned int has_cistatus2:1; unsigned int has_mainscaler_ext:1; unsigned int has_cam_if:1; + unsigned int has_isp_wb:1; unsigned int has_alpha:1; - struct fimc_pix_limit *pix_limit; + const struct fimc_pix_limit *pix_limit; u16 min_inp_pixsize; u16 min_out_pixsize; u16 hor_offs_align; @@ -402,7 +404,7 @@ struct fimc_variant { * @lclk_frequency: local bus clock frequency */ struct fimc_drvdata { - struct fimc_variant *variant[FIMC_MAX_DEVS]; + const struct fimc_variant *variant[FIMC_MAX_DEVS]; int num_entities; unsigned long lclk_frequency; }; @@ -435,7 +437,7 @@ struct fimc_dev { struct mutex lock; struct platform_device *pdev; struct s5p_platform_fimc *pdata; - struct fimc_variant *variant; + const struct fimc_variant *variant; u16 id; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index 1d21da4bd24b..1d57f3b87aa3 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -300,7 +300,7 @@ static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) { struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_variant *variant = fimc->variant; + const struct fimc_variant *variant = fimc->variant; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; struct fimc_fmt *fmt; u32 max_w, mod_x, mod_y; diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c index f0c34ca41894..c05d0444192f 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-reg.c @@ -312,7 +312,7 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx) void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - struct fimc_variant *variant = dev->variant; + const struct fimc_variant *variant = dev->variant; struct fimc_scaler *sc = &ctx->scaler; u32 cfg; From e26991b49a132626130428d64222a355ffdd7139 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 29 Aug 2012 14:35:21 -0300 Subject: [PATCH 0272/4607] [media] s5p-csis: Add support for raw Bayer pixel formats The MIPI CSIS device supports MIPI CSI-2 RAW8, RAW10, RAW12 data types. Add related media bus pixel format definitions. This doesn't cover all possible supported media bus pixel formats. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 9528ee65a843..36916d3f199a 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -220,6 +220,18 @@ static const struct csis_pix_format s5pcsis_formats[] = { .code = V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8, .fmt_reg = S5PCSIS_CFG_FMT_USER(1), .data_alignment = 32, + }, { + .code = V4L2_MBUS_FMT_SGRBG8_1X8, + .fmt_reg = S5PCSIS_CFG_FMT_RAW8, + .data_alignment = 24, + }, { + .code = V4L2_MBUS_FMT_SGRBG10_1X10, + .fmt_reg = S5PCSIS_CFG_FMT_RAW10, + .data_alignment = 24, + }, { + .code = V4L2_MBUS_FMT_SGRBG12_1X12, + .fmt_reg = S5PCSIS_CFG_FMT_RAW12, + .data_alignment = 24, } }; From cd65a645a4f5e456607067734f9a11385c9dce7b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 2 Nov 2012 14:20:27 -0300 Subject: [PATCH 0273/4607] [media] s5p-csis: Enable only data lanes that are actively used Enable only MIPI CSI-2 data lanes at the DPHY that are actively used, rather than unmasking all unconditionally. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 36916d3f199a..8ec7c3b79659 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -273,7 +273,8 @@ static void s5pcsis_reset(struct csis_state *state) static void s5pcsis_system_enable(struct csis_state *state, int on) { - u32 val; + struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data; + u32 val, mask; val = s5pcsis_read(state, S5PCSIS_CTRL); if (on) @@ -283,10 +284,11 @@ static void s5pcsis_system_enable(struct csis_state *state, int on) s5pcsis_write(state, S5PCSIS_CTRL, val); val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); - if (on) - val |= S5PCSIS_DPHYCTRL_ENABLE; - else - val &= ~S5PCSIS_DPHYCTRL_ENABLE; + val &= ~S5PCSIS_DPHYCTRL_ENABLE; + if (on) { + mask = (1 << (pdata->lanes + 1)) - 1; + val |= (mask & S5PCSIS_DPHYCTRL_ENABLE); + } s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); } From a2fea0dfddf95b7f1e7adb3630c7d07a92cfb09b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 28 Sep 2012 11:05:53 -0300 Subject: [PATCH 0274/4607] [media] s5p-csis: Add registers logging for debugging Dump registers contents together with the event counters state in VIDIOC_LOG_STATUS ioctl. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 8ec7c3b79659..8a06f1402f37 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -383,6 +383,30 @@ err: return -ENXIO; } +static void dump_regs(struct csis_state *state, const char *label) +{ + struct { + u32 offset; + const char * const name; + } registers[] = { + { 0x00, "CTRL" }, + { 0x04, "DPHYCTRL" }, + { 0x08, "CONFIG" }, + { 0x0c, "DPHYSTS" }, + { 0x10, "INTMSK" }, + { 0x2c, "RESOL" }, + { 0x38, "SDW_CONFIG" }, + }; + u32 i; + + v4l2_info(&state->sd, "--- %s ---\n", label); + + for (i = 0; i < ARRAY_SIZE(registers); i++) { + u32 cfg = s5pcsis_read(state, registers[i].offset); + v4l2_info(&state->sd, "%10s: 0x%08x\n", registers[i].name, cfg); + } +} + static void s5pcsis_start_stream(struct csis_state *state) { s5pcsis_reset(state); @@ -583,7 +607,11 @@ static int s5pcsis_log_status(struct v4l2_subdev *sd) { struct csis_state *state = sd_to_csis_state(sd); + mutex_lock(&state->lock); s5pcsis_log_counters(state, true); + if (debug && (state->flags & ST_POWERED)) + dump_regs(state, __func__); + mutex_unlock(&state->lock); return 0; } From 588c87be0b44ccf44e321eeae52c674a67a6adc0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 27 Nov 2012 11:57:42 -0300 Subject: [PATCH 0275/4607] [media] s5p-fimc: Add sensor group ids for fimc-is Add subdev group id definition for FIMC-IS ISP and sensor subdev. While at it rename all group id definitions to start with GRP_ID. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/s5p-fimc/fimc-mdevice.c | 21 ++++++++++--------- .../media/platform/s5p-fimc/fimc-mdevice.h | 12 ++++++----- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 1bd5678cfeb9..280a4d6321ed 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -62,16 +62,17 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, sd = media_entity_to_v4l2_subdev(pad->entity); switch (sd->grp_id) { - case SENSOR_GROUP_ID: + case GRP_ID_FIMC_IS_SENSOR: + case GRP_ID_SENSOR: p->subdevs[IDX_SENSOR] = sd; break; - case CSIS_GROUP_ID: + case GRP_ID_CSIS: p->subdevs[IDX_CSIS] = sd; break; - case FLITE_GROUP_ID: + case GRP_ID_FLITE: p->subdevs[IDX_FLITE] = sd; break; - case FIMC_GROUP_ID: + case GRP_ID_FIMC: /* No need to control FIMC subdev through subdev ops */ break; default: @@ -269,7 +270,7 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, return ERR_PTR(-EPROBE_DEFER); } v4l2_set_subdev_hostdata(sd, s_info); - sd->grp_id = SENSOR_GROUP_ID; + sd->grp_id = GRP_ID_SENSOR; v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n", s_info->pdata.board_info->type); @@ -351,7 +352,7 @@ static int fimc_register_callback(struct device *dev, void *p) return 0; sd = &fimc->vid_cap.subdev; - sd->grp_id = FIMC_GROUP_ID; + sd->grp_id = GRP_ID_FIMC; v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); @@ -374,7 +375,7 @@ static int fimc_lite_register_callback(struct device *dev, void *p) if (fimc == NULL || fimc->index >= FIMC_LITE_MAX_DEVS) return 0; - fimc->subdev.grp_id = FLITE_GROUP_ID; + fimc->subdev.grp_id = GRP_ID_FLITE; v4l2_set_subdev_hostdata(&fimc->subdev, (void *)&fimc_pipeline_ops); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, &fimc->subdev); @@ -404,7 +405,7 @@ static int csis_register_callback(struct device *dev, void *p) v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name); id = pdev->id < 0 ? 0 : pdev->id; - sd->grp_id = CSIS_GROUP_ID; + sd->grp_id = GRP_ID_CSIS; ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (!ret) @@ -828,11 +829,11 @@ static int fimc_md_link_notify(struct media_pad *source, sd = media_entity_to_v4l2_subdev(sink->entity); switch (sd->grp_id) { - case FLITE_GROUP_ID: + case GRP_ID_FLITE: fimc_lite = v4l2_get_subdevdata(sd); pipeline = &fimc_lite->pipeline; break; - case FIMC_GROUP_ID: + case GRP_ID_FIMC: fimc = v4l2_get_subdevdata(sd); pipeline = &fimc->pipeline; break; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.h b/drivers/media/platform/s5p-fimc/fimc-mdevice.h index 2d8d41d82620..da7d9922cf58 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.h @@ -22,11 +22,13 @@ #include "mipi-csis.h" /* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */ -#define SENSOR_GROUP_ID (1 << 8) -#define CSIS_GROUP_ID (1 << 9) -#define WRITEBACK_GROUP_ID (1 << 10) -#define FIMC_GROUP_ID (1 << 11) -#define FLITE_GROUP_ID (1 << 12) +#define GRP_ID_SENSOR (1 << 8) +#define GRP_ID_FIMC_IS_SENSOR (1 << 9) +#define GRP_ID_WRITEBACK (1 << 10) +#define GRP_ID_CSIS (1 << 11) +#define GRP_ID_FIMC (1 << 12) +#define GRP_ID_FLITE (1 << 13) +#define GRP_ID_FIMC_IS (1 << 14) #define FIMC_MAX_SENSORS 8 #define FIMC_MAX_CAMCLKS 2 From 6319d6a002beb2644b4a9058a12a04b2bbf98502 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 28 Nov 2012 15:41:01 -0300 Subject: [PATCH 0276/4607] [media] fimc-lite: Add ISP FIFO output support Add second source media pad for the FIFO data output to FIMC-IS and implement subdev s_stream op for configurations where FIMC-LITE is used as a glue logic between FIMC-IS and MIPI-CSIS or an image sensor. The second source media pad will be linked to the FIMC-LITE video node. For proper configuration the attached image sensor/video encoder properties are needed, like video bus type, signal polarities, etc. For this purpose there is a small routine added that walks the pipeline and returns the sensor subdev. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-lite.c | 150 ++++++++++++++---- drivers/media/platform/s5p-fimc/fimc-lite.h | 7 +- .../media/platform/s5p-fimc/fimc-mdevice.c | 2 +- 3 files changed, 122 insertions(+), 37 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index 1b309a72f09f..765b8e4cbf4e 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -120,25 +120,29 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, return def_fmt; } -static int fimc_lite_hw_init(struct fimc_lite *fimc) +static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output) { struct fimc_pipeline *pipeline = &fimc->pipeline; - struct fimc_sensor_info *sensor; + struct v4l2_subdev *sensor; + struct fimc_sensor_info *si; unsigned long flags; - if (pipeline->subdevs[IDX_SENSOR] == NULL) + sensor = isp_output ? fimc->sensor : pipeline->subdevs[IDX_SENSOR]; + + if (sensor == NULL) return -ENXIO; if (fimc->fmt == NULL) return -EINVAL; - sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]); + /* Get sensor configuration data from the sensor subdev */ + si = v4l2_get_subdev_hostdata(sensor); spin_lock_irqsave(&fimc->slock, flags); - flite_hw_set_camera_bus(fimc, &sensor->pdata); + flite_hw_set_camera_bus(fimc, &si->pdata); flite_hw_set_source_format(fimc, &fimc->inp_frame); flite_hw_set_window_offset(fimc, &fimc->inp_frame); - flite_hw_set_output_dma(fimc, &fimc->out_frame, true); + flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output); flite_hw_set_interrupt_mask(fimc); flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); @@ -296,7 +300,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) fimc->frame_count = 0; - ret = fimc_lite_hw_init(fimc); + ret = fimc_lite_hw_init(fimc, false); if (ret) { fimc_lite_reinit(fimc, false); return ret; @@ -460,6 +464,11 @@ static int fimc_lite_open(struct file *file) if (mutex_lock_interruptible(&fimc->lock)) return -ERESTARTSYS; + if (fimc->out_path != FIMC_IO_DMA) { + ret = -EBUSY; + goto done; + } + set_bit(ST_FLITE_IN_USE, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); if (ret < 0) @@ -962,6 +971,29 @@ static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { .vidioc_streamoff = fimc_lite_streamoff, }; +/* Called with the media graph mutex held */ +static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me) +{ + struct media_pad *pad = &me->pads[0]; + struct v4l2_subdev *sd; + + while (pad->flags & MEDIA_PAD_FL_SINK) { + /* source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + + if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR) + return sd; + /* sink pad */ + pad = &sd->entity.pads[0]; + } + return NULL; +} + /* Capture subdev media entity operations */ static int fimc_lite_link_setup(struct media_entity *entity, const struct media_pad *local, @@ -970,46 +1002,59 @@ static int fimc_lite_link_setup(struct media_entity *entity, struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct fimc_lite *fimc = v4l2_get_subdevdata(sd); unsigned int remote_ent_type = media_entity_type(remote->entity); + int ret = 0; if (WARN_ON(fimc == NULL)) return 0; v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x", - __func__, local->entity->name, remote->entity->name, + __func__, remote->entity->name, local->entity->name, flags, fimc->source_subdev_grp_id); + mutex_lock(&fimc->lock); + switch (local->index) { - case FIMC_SD_PAD_SINK: - if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) - return -EINVAL; - - if (flags & MEDIA_LNK_FL_ENABLED) { - if (fimc->source_subdev_grp_id != 0) - return -EBUSY; - fimc->source_subdev_grp_id = sd->grp_id; - return 0; + case FLITE_SD_PAD_SINK: + if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) { + ret = -EINVAL; + break; + } + if (flags & MEDIA_LNK_FL_ENABLED) { + if (fimc->source_subdev_grp_id == 0) + fimc->source_subdev_grp_id = sd->grp_id; + else + ret = -EBUSY; + } else { + fimc->source_subdev_grp_id = 0; + fimc->sensor = NULL; } - - fimc->source_subdev_grp_id = 0; break; - case FIMC_SD_PAD_SOURCE: - if (!(flags & MEDIA_LNK_FL_ENABLED)) { + case FLITE_SD_PAD_SOURCE_DMA: + if (!(flags & MEDIA_LNK_FL_ENABLED)) fimc->out_path = FIMC_IO_NONE; - return 0; - } - if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) + else if (remote_ent_type == MEDIA_ENT_T_DEVNODE) + fimc->out_path = FIMC_IO_DMA; + else + ret = -EINVAL; + break; + + case FLITE_SD_PAD_SOURCE_ISP: + if (!(flags & MEDIA_LNK_FL_ENABLED)) + fimc->out_path = FIMC_IO_NONE; + else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) fimc->out_path = FIMC_IO_ISP; else - fimc->out_path = FIMC_IO_DMA; + ret = -EINVAL; break; default: v4l2_err(sd, "Invalid pad index\n"); - return -EINVAL; + ret = -EINVAL; } - return 0; + mutex_unlock(&fimc->lock); + return ret; } static const struct media_entity_operations fimc_lite_subdev_media_ops = { @@ -1188,13 +1233,49 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + unsigned long flags; + int ret; - if (fimc->out_path == FIMC_IO_DMA) + /* + * Find sensor subdev linked to FIMC-LITE directly or through + * MIPI-CSIS. This is required for configuration where FIMC-LITE + * is used as a subdev only and feeds data internally to FIMC-IS. + * The pipeline links are protected through entity.stream_count + * so there is no need to take the media graph mutex here. + */ + fimc->sensor = __find_remote_sensor(&sd->entity); + + mutex_lock(&fimc->lock); + if (fimc->out_path != FIMC_IO_ISP) { + mutex_unlock(&fimc->lock); return -ENOIOCTLCMD; + } - /* TODO: */ + if (on) { + flite_hw_reset(fimc); + ret = fimc_lite_hw_init(fimc, true); + if (!ret) { + spin_lock_irqsave(&fimc->slock, flags); + flite_hw_capture_start(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + } + } else { + set_bit(ST_FLITE_OFF, &fimc->state); - return 0; + spin_lock_irqsave(&fimc->slock, flags); + flite_hw_capture_stop(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + + ret = wait_event_timeout(fimc->irq_queue, + !test_bit(ST_FLITE_OFF, &fimc->state), + msecs_to_jiffies(200)); + if (ret == 0) + v4l2_err(sd, "s_stream(0) timeout\n"); + clear_bit(ST_FLITE_RUN, &fimc->state); + } + + mutex_unlock(&fimc->lock); + return ret; } static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on) @@ -1347,9 +1428,10 @@ static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index); - fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, + fimc->subdev_pads[FLITE_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + fimc->subdev_pads[FLITE_SD_PAD_SOURCE_DMA].flags = MEDIA_PAD_FL_SOURCE; + fimc->subdev_pads[FLITE_SD_PAD_SOURCE_ISP].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, FLITE_SD_PADS_NUM, fimc->subdev_pads, 0); if (ret) return ret; @@ -1518,7 +1600,7 @@ static int fimc_lite_resume(struct device *dev) INIT_LIST_HEAD(&fimc->active_buf_q); fimc_pipeline_call(fimc, open, &fimc->pipeline, &fimc->vfd.entity, false); - fimc_lite_hw_init(fimc); + fimc_lite_hw_init(fimc, fimc->out_path == FIMC_IO_ISP); clear_bit(ST_FLITE_SUSPENDED, &fimc->state); for (i = 0; i < fimc->reqbufs_count; i++) { diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.h b/drivers/media/platform/s5p-fimc/fimc-lite.h index 3081db35c5b0..4576922952f3 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.h +++ b/drivers/media/platform/s5p-fimc/fimc-lite.h @@ -45,8 +45,9 @@ enum { }; #define FLITE_SD_PAD_SINK 0 -#define FLITE_SD_PAD_SOURCE 1 -#define FLITE_SD_PADS_NUM 2 +#define FLITE_SD_PAD_SOURCE_DMA 1 +#define FLITE_SD_PAD_SOURCE_ISP 2 +#define FLITE_SD_PADS_NUM 3 struct flite_variant { unsigned short max_width; @@ -104,6 +105,7 @@ struct flite_buffer { * @subdev: FIMC-LITE subdev * @vd_pad: media (sink) pad for the capture video node * @subdev_pads: the subdev media pads + * @sensor: sensor subdev attached to FIMC-LITE directly or through MIPI-CSIS * @ctrl_handler: v4l2 control handler * @test_pattern: test pattern controls * @index: FIMC-LITE platform device index @@ -139,6 +141,7 @@ struct fimc_lite { struct v4l2_subdev subdev; struct media_pad vd_pad; struct media_pad subdev_pads[FLITE_SD_PADS_NUM]; + struct v4l2_subdev *sensor; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *test_pattern; u32 index; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 280a4d6321ed..d0028740640a 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -603,7 +603,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) source = &fimc->subdev.entity; sink = &fimc->vfd.entity; /* FIMC-LITE's subdev and video node */ - ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, + ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA, sink, 0, flags); if (ret) break; From 1c9f5bd7cb8a74965e7a19eead5d77c35bf30db7 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Thu, 22 Nov 2012 12:13:27 -0300 Subject: [PATCH 0277/4607] [media] s5p-fimc: Add support for sensors with multiple pads Some sensors can have more than one pad (case of S5C73M3). In such cases FIMC assumes the last pad of the sensor is the source pad. Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 6 ++++-- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 52d8d885d287..50c0da9c788b 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -884,14 +884,16 @@ static int fimc_get_sensor_frame_desc(struct v4l2_subdev *sensor, { struct v4l2_mbus_frame_desc fd; int i, ret; + int pad; for (i = 0; i < num_planes; i++) fd.entry[i].length = plane_fmt[i].sizeimage; + pad = sensor->entity.num_pads - 1; if (try) - ret = v4l2_subdev_call(sensor, pad, set_frame_desc, 0, &fd); + ret = v4l2_subdev_call(sensor, pad, set_frame_desc, pad, &fd); else - ret = v4l2_subdev_call(sensor, pad, get_frame_desc, 0, &fd); + ret = v4l2_subdev_call(sensor, pad, get_frame_desc, pad, &fd); if (ret < 0) return ret; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index d0028740640a..8b43f982c12d 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -659,7 +659,8 @@ static int fimc_md_create_links(struct fimc_md *fmd) "but s5p-csis module is not loaded!\n")) return -EINVAL; - ret = media_entity_create_link(&sensor->entity, 0, + pad = sensor->entity.num_pads - 1; + ret = media_entity_create_link(&sensor->entity, pad, &csis->entity, CSIS_PAD_SINK, MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); From 04881127340c43fc5b8dbc2c381c1928ee22559e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Moine?= Date: Thu, 22 Nov 2012 08:59:06 -0300 Subject: [PATCH 0278/4607] [media] gspca - stv06xx: Fix a regression with the bridge/sensor vv6410 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting the H and V flip controls at webcam connection time prevents the webcam to work correctly. This patch checks if the webcam is streaming before setting the flips. It does not set the flips (nor other controls) at webcam start time. Tested-by: Philippe ROUBACH Signed-off-by: Jean-François Moine Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c index 748e1421d6d8..cbb153180d59 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c @@ -52,9 +52,13 @@ static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_HFLIP: + if (!gspca_dev->streaming) + return 0; err = vv6410_set_hflip(gspca_dev, ctrl->val); break; case V4L2_CID_VFLIP: + if (!gspca_dev->streaming) + return 0; err = vv6410_set_vflip(gspca_dev, ctrl->val); break; case V4L2_CID_GAIN: From 143dd34597a2e051cb0938b28387b94742d4e4e4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 29 Nov 2012 06:39:49 -0300 Subject: [PATCH 0279/4607] [media] gspca-pac207: Add a led_invert module parameter Some cams have their led connected in such a way that on = off and visa versa unfortunately we cannot tell this from the driver in any way, so add a module parameter for this. Reported-by: Yuri Glushkov Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/pac207.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/media/usb/gspca/pac207.c b/drivers/media/usb/gspca/pac207.c index d236d1791f78..1f253dfebe47 100644 --- a/drivers/media/usb/gspca/pac207.c +++ b/drivers/media/usb/gspca/pac207.c @@ -55,6 +55,11 @@ MODULE_LICENSE("GPL"); #define PAC207_AUTOGAIN_DEADZONE 30 +/* global parameters */ +static int led_invert; +module_param(led_invert, int, 0644); +MODULE_PARM_DESC(led_invert, "Invert led"); + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ @@ -187,10 +192,14 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - pac207_write_reg(gspca_dev, 0x41, 0x00); - /* Bit_0=Image Format, - * Bit_1=LED, - * Bit_2=Compression test mode enable */ + u8 mode; + + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x02; + else + mode = 0x00; + pac207_write_reg(gspca_dev, 0x41, mode); pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ return gspca_dev->usb_err; @@ -303,7 +312,11 @@ static int sd_start(struct gspca_dev *gspca_dev) pac207_write_reg(gspca_dev, 0x02, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */ - mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x00; + else + mode = 0x02; if (gspca_dev->width == 176) { /* 176x144 */ mode |= 0x01; PDEBUG(D_STREAM, "pac207_start mode 176x144"); @@ -325,8 +338,15 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { + u8 mode; + + /* mode: Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (led_invert) + mode = 0x02; + else + mode = 0x00; pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */ - pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */ + pac207_write_reg(gspca_dev, 0x41, mode); /* Turn off LED */ pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ } From 7a29ee2e37b3f9675f46c87998c67b68c315c54a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 29 Nov 2012 06:56:45 -0300 Subject: [PATCH 0280/4607] [media] stk-webcam: Add an upside down dmi table, and add the Asus G1 to it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stk-webcam module is inserted upside-down in some webcams, so we need to de hflip and vflip by default on some models. Note that this patch inverts the value of the controls as reported by the control API in this case so that for the user they still make sense (iow not doing any flipping from the ctrl API pov results in an upright image). Reported-by: Jose Gómez Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stkwebcam/stk-webcam.c | 56 +++++++++++++++++++----- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 52296f7ec966..4cbab085e348 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -38,12 +39,12 @@ #include "stk-webcam.h" -static bool hflip; -module_param(hflip, bool, 0444); +static int hflip = -1; +module_param(hflip, int, 0444); MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); -static bool vflip; -module_param(vflip, bool, 0444); +static int vflip = -1; +module_param(vflip, int, 0444); MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); static int debug; @@ -62,6 +63,19 @@ static struct usb_device_id stkwebcam_table[] = { }; MODULE_DEVICE_TABLE(usb, stkwebcam_table); +/* The stk webcam laptop module is mounted upside down in some laptops :( */ +static const struct dmi_system_id stk_upside_down_dmi_table[] = { + { + .ident = "ASUS G1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "G1") + } + }, + {} +}; + + /* * Basic stuff */ @@ -817,10 +831,16 @@ static int stk_vidioc_g_ctrl(struct file *filp, c->value = dev->vsettings.brightness; break; case V4L2_CID_HFLIP: - c->value = dev->vsettings.hflip; + if (dmi_check_system(stk_upside_down_dmi_table)) + c->value = !dev->vsettings.hflip; + else + c->value = dev->vsettings.hflip; break; case V4L2_CID_VFLIP: - c->value = dev->vsettings.vflip; + if (dmi_check_system(stk_upside_down_dmi_table)) + c->value = !dev->vsettings.vflip; + else + c->value = dev->vsettings.vflip; break; default: return -EINVAL; @@ -837,10 +857,16 @@ static int stk_vidioc_s_ctrl(struct file *filp, dev->vsettings.brightness = c->value; return stk_sensor_set_brightness(dev, c->value >> 8); case V4L2_CID_HFLIP: - dev->vsettings.hflip = c->value; + if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.hflip = !c->value; + else + dev->vsettings.hflip = c->value; return 0; case V4L2_CID_VFLIP: - dev->vsettings.vflip = c->value; + if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.vflip = !c->value; + else + dev->vsettings.vflip = c->value; return 0; default: return -EINVAL; @@ -1276,8 +1302,18 @@ static int stk_camera_probe(struct usb_interface *interface, dev->interface = interface; usb_get_intf(interface); - dev->vsettings.vflip = vflip; - dev->vsettings.hflip = hflip; + if (hflip != -1) + dev->vsettings.hflip = hflip; + else if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.hflip = 1; + else + dev->vsettings.hflip = 0; + if (vflip != -1) + dev->vsettings.vflip = vflip; + else if (dmi_check_system(stk_upside_down_dmi_table)) + dev->vsettings.vflip = 1; + else + dev->vsettings.vflip = 0; dev->n_sbufs = 0; set_present(dev); From 23e8321624076317e9ffd6df949a5a3a5e59b448 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 29 Nov 2012 07:06:22 -0300 Subject: [PATCH 0281/4607] [media] Documentation/media: Remove docs for obsoleted and removed v4l1 drivers When the v4l1 drivers were removed, there docs were forgotten. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/et61x251.txt | 315 ----------------- Documentation/video4linux/ibmcam.txt | 323 ----------------- Documentation/video4linux/m5602.txt | 12 - Documentation/video4linux/ov511.txt | 288 ---------------- Documentation/video4linux/se401.txt | 54 --- Documentation/video4linux/stv680.txt | 53 --- Documentation/video4linux/w9968cf.txt | 458 ------------------------- Documentation/video4linux/zc0301.txt | 270 --------------- 8 files changed, 1773 deletions(-) delete mode 100644 Documentation/video4linux/et61x251.txt delete mode 100644 Documentation/video4linux/ibmcam.txt delete mode 100644 Documentation/video4linux/m5602.txt delete mode 100644 Documentation/video4linux/ov511.txt delete mode 100644 Documentation/video4linux/se401.txt delete mode 100644 Documentation/video4linux/stv680.txt delete mode 100644 Documentation/video4linux/w9968cf.txt delete mode 100644 Documentation/video4linux/zc0301.txt diff --git a/Documentation/video4linux/et61x251.txt b/Documentation/video4linux/et61x251.txt deleted file mode 100644 index e0cdae491858..000000000000 --- a/Documentation/video4linux/et61x251.txt +++ /dev/null @@ -1,315 +0,0 @@ - - ET61X[12]51 PC Camera Controllers - Driver for Linux - ================================= - - - Documentation - - - -Index -===== -1. Copyright -2. Disclaimer -3. License -4. Overview and features -5. Module dependencies -6. Module loading -7. Module parameters -8. Optional device control through "sysfs" -9. Supported devices -10. Notes for V4L2 application developers -11. Contact information - - -1. Copyright -============ -Copyright (C) 2006-2007 by Luca Risolia - - -2. Disclaimer -============= -Etoms is a trademark of Etoms Electronics Corp. -This software is not developed or sponsored by Etoms Electronics. - - -3. License -========== -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -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., 675 Mass Ave, Cambridge, MA 02139, USA. - - -4. Overview and features -======================== -This driver supports the video interface of the devices mounting the ET61X151 -or ET61X251 PC Camera Controllers. - -It's worth to note that Etoms Electronics has never collaborated with the -author during the development of this project; despite several requests, -Etoms Electronics also refused to release enough detailed specifications of -the video compression engine. - -The driver relies on the Video4Linux2 and USB core modules. It has been -designed to run properly on SMP systems as well. - -The latest version of the ET61X[12]51 driver can be found at the following URL: -http://www.linux-projects.org/ - -Some of the features of the driver are: - -- full compliance with the Video4Linux2 API (see also "Notes for V4L2 - application developers" paragraph); -- available mmap or read/poll methods for video streaming through isochronous - data transfers; -- automatic detection of image sensor; -- support for any window resolutions and optional panning within the maximum - pixel area of image sensor; -- image downscaling with arbitrary scaling factors from 1 and 2 in both - directions (see "Notes for V4L2 application developers" paragraph); -- two different video formats for uncompressed or compressed data in low or - high compression quality (see also "Notes for V4L2 application developers" - paragraph); -- full support for the capabilities of every possible image sensors that can - be connected to the ET61X[12]51 bridges, including, for instance, red, green, - blue and global gain adjustments and exposure control (see "Supported - devices" paragraph for details); -- use of default color settings for sunlight conditions; -- dynamic I/O interface for both ET61X[12]51 and image sensor control (see - "Optional device control through 'sysfs'" paragraph); -- dynamic driver control thanks to various module parameters (see "Module - parameters" paragraph); -- up to 64 cameras can be handled at the same time; they can be connected and - disconnected from the host many times without turning off the computer, if - the system supports hotplugging; -- no known bugs. - - -5. Module dependencies -====================== -For it to work properly, the driver needs kernel support for Video4Linux and -USB. - -The following options of the kernel configuration file must be enabled and -corresponding modules must be compiled: - - # Multimedia devices - # - CONFIG_VIDEO_DEV=m - -To enable advanced debugging functionality on the device through /sysfs: - - # Multimedia devices - # - CONFIG_VIDEO_ADV_DEBUG=y - - # USB support - # - CONFIG_USB=m - -In addition, depending on the hardware being used, the modules below are -necessary: - - # USB Host Controller Drivers - # - CONFIG_USB_EHCI_HCD=m - CONFIG_USB_UHCI_HCD=m - CONFIG_USB_OHCI_HCD=m - -And finally: - - # USB Multimedia devices - # - CONFIG_USB_ET61X251=m - - -6. Module loading -================= -To use the driver, it is necessary to load the "et61x251" module into memory -after every other module required: "videodev", "v4l2_common", "compat_ioctl32", -"usbcore" and, depending on the USB host controller you have, "ehci-hcd", -"uhci-hcd" or "ohci-hcd". - -Loading can be done as shown below: - - [root@localhost home]# modprobe et61x251 - -At this point the devices should be recognized. You can invoke "dmesg" to -analyze kernel messages and verify that the loading process has gone well: - - [user@localhost home]$ dmesg - - -7. Module parameters -==================== -Module parameters are listed below: -------------------------------------------------------------------------------- -Name: video_nr -Type: short array (min = 0, max = 64) -Syntax: <-1|n[,...]> -Description: Specify V4L2 minor mode number: - -1 = use next available - n = use minor number n - You can specify up to 64 cameras this way. - For example: - video_nr=-1,2,-1 would assign minor number 2 to the second - registered camera and use auto for the first one and for every - other camera. -Default: -1 -------------------------------------------------------------------------------- -Name: force_munmap -Type: bool array (min = 0, max = 64) -Syntax: <0|1[,...]> -Description: Force the application to unmap previously mapped buffer memory - before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not - all the applications support this feature. This parameter is - specific for each detected camera. - 0 = do not force memory unmapping - 1 = force memory unmapping (save memory) -Default: 0 -------------------------------------------------------------------------------- -Name: frame_timeout -Type: uint array (min = 0, max = 64) -Syntax: -Description: Timeout for a video frame in seconds. This parameter is - specific for each detected camera. This parameter can be - changed at runtime thanks to the /sys filesystem interface. -Default: 2 -------------------------------------------------------------------------------- -Name: debug -Type: ushort -Syntax: -Description: Debugging information level, from 0 to 3: - 0 = none (use carefully) - 1 = critical errors - 2 = significant information - 3 = more verbose messages - Level 3 is useful for testing only, when only one device - is used at the same time. It also shows some more information - about the hardware being detected. This module parameter can be - changed at runtime thanks to the /sys filesystem interface. -Default: 2 -------------------------------------------------------------------------------- - - -8. Optional device control through "sysfs" -========================================== -If the kernel has been compiled with the CONFIG_VIDEO_ADV_DEBUG option enabled, -it is possible to read and write both the ET61X[12]51 and the image sensor -registers by using the "sysfs" filesystem interface. - -There are four files in the /sys/class/video4linux/videoX directory for each -registered camera: "reg", "val", "i2c_reg" and "i2c_val". The first two files -control the ET61X[12]51 bridge, while the other two control the sensor chip. -"reg" and "i2c_reg" hold the values of the current register index where the -following reading/writing operations are addressed at through "val" and -"i2c_val". Their use is not intended for end-users, unless you know what you -are doing. Remember that you must be logged in as root before writing to them. - -As an example, suppose we were to want to read the value contained in the -register number 1 of the sensor register table - which is usually the product -identifier - of the camera registered as "/dev/video0": - - [root@localhost #] cd /sys/class/video4linux/video0 - [root@localhost #] echo 1 > i2c_reg - [root@localhost #] cat i2c_val - -Note that if the sensor registers cannot be read, "cat" will fail. -To avoid race conditions, all the I/O accesses to the files are serialized. - - -9. Supported devices -==================== -None of the names of the companies as well as their products will be mentioned -here. They have never collaborated with the author, so no advertising. - -From the point of view of a driver, what unambiguously identify a device are -its vendor and product USB identifiers. Below is a list of known identifiers of -devices mounting the ET61X[12]51 PC camera controllers: - -Vendor ID Product ID ---------- ---------- -0x102c 0x6151 -0x102c 0x6251 -0x102c 0x6253 -0x102c 0x6254 -0x102c 0x6255 -0x102c 0x6256 -0x102c 0x6257 -0x102c 0x6258 -0x102c 0x6259 -0x102c 0x625a -0x102c 0x625b -0x102c 0x625c -0x102c 0x625d -0x102c 0x625e -0x102c 0x625f -0x102c 0x6260 -0x102c 0x6261 -0x102c 0x6262 -0x102c 0x6263 -0x102c 0x6264 -0x102c 0x6265 -0x102c 0x6266 -0x102c 0x6267 -0x102c 0x6268 -0x102c 0x6269 - -The following image sensors are supported: - -Model Manufacturer ------ ------------ -TAS5130D1B Taiwan Advanced Sensor Corporation - -All the available control settings of each image sensor are supported through -the V4L2 interface. - - -10. Notes for V4L2 application developers -========================================= -This driver follows the V4L2 API specifications. In particular, it enforces two -rules: - -- exactly one I/O method, either "mmap" or "read", is associated with each -file descriptor. Once it is selected, the application must close and reopen the -device to switch to the other I/O method; - -- although it is not mandatory, previously mapped buffer memory should always -be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's. -The same number of buffers as before will be allocated again to match the size -of the new video frames, so you have to map the buffers again before any I/O -attempts on them. - -Consistently with the hardware limits, this driver also supports image -downscaling with arbitrary scaling factors from 1 and 2 in both directions. -However, the V4L2 API specifications don't correctly define how the scaling -factor can be chosen arbitrarily by the "negotiation" of the "source" and -"target" rectangles. To work around this flaw, we have added the convention -that, during the negotiation, whenever the "VIDIOC_S_CROP" ioctl is issued, the -scaling factor is restored to 1. - -This driver supports two different video formats: the first one is the "8-bit -Sequential Bayer" format and can be used to obtain uncompressed video data -from the device through the current I/O method, while the second one provides -"raw" compressed video data (without frame headers not related to the -compressed data). The current compression quality may vary from 0 to 1 and can -be selected or queried thanks to the VIDIOC_S_JPEGCOMP and VIDIOC_G_JPEGCOMP -V4L2 ioctl's. - - -11. Contact information -======================= -The author may be contacted by e-mail at . - -GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is -'FCE635A4'; the public 1024-bit key should be available at any keyserver; -the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. diff --git a/Documentation/video4linux/ibmcam.txt b/Documentation/video4linux/ibmcam.txt deleted file mode 100644 index a51055211e62..000000000000 --- a/Documentation/video4linux/ibmcam.txt +++ /dev/null @@ -1,323 +0,0 @@ -README for Linux device driver for the IBM "C-It" USB video camera - -INTRODUCTION: - -This driver does not use all features known to exist in -the IBM camera. However most of needed features work well. - -This driver was developed using logs of observed USB traffic -which was produced by standard Windows driver (c-it98.sys). -I did not have data sheets from Xirlink. - -Video formats: - 128x96 [model 1] - 176x144 - 320x240 [model 2] - 352x240 [model 2] - 352x288 -Frame rate: 3 - 30 frames per second (FPS) -External interface: USB -Internal interface: Video For Linux (V4L) -Supported controls: -- by V4L: Contrast, Brightness, Color, Hue -- by driver options: frame rate, lighting conditions, video format, - default picture settings, sharpness. - -SUPPORTED CAMERAS: - -Xirlink "C-It" camera, also known as "IBM PC Camera". -The device uses proprietary ASIC (and compression method); -it is manufactured by Xirlink. See http://xirlinkwebcam.sourceforge.net, -http://www.ibmpccamera.com, or http://www.c-itnow.com/ for details and pictures. - -This very chipset ("X Chip", as marked at the factory) -is used in several other cameras, and they are supported -as well: - -- IBM NetCamera -- Veo Stingray - -The Linux driver was developed with camera with following -model number (or FCC ID): KSX-XVP510. This camera has three -interfaces, each with one endpoint (control, iso, iso). This -type of cameras is referred to as "model 1". These cameras are -no longer manufactured. - -Xirlink now manufactures new cameras which are somewhat different. -In particular, following models [FCC ID] belong to that category: - -XVP300 [KSX-X9903] -XVP600 [KSX-X9902] -XVP610 [KSX-X9902] - -(see http://www.xirlink.com/ibmpccamera/ for updates, they refer -to these new cameras by Windows driver dated 12-27-99, v3005 BETA) -These cameras have two interfaces, one endpoint in each (iso, bulk). -Such type of cameras is referred to as "model 2". They are supported -(with exception of 352x288 native mode). - -Some IBM NetCameras (Model 4) are made to generate only compressed -video streams. This is great for performance, but unfortunately -nobody knows how to decompress the stream :-( Therefore, these -cameras are *unsupported* and if you try to use one of those, all -you get is random colored horizontal streaks, not the image! -If you have one of those cameras, you probably should return it -to the store and get something that is supported. - -Tell me more about all that "model" business --------------------------------------------- - -I just invented model numbers to uniquely identify flavors of the -hardware/firmware that were sold. It was very confusing to use -brand names or some other internal numbering schemes. So I found -by experimentation that all Xirlink chipsets fall into four big -classes, and I called them "models". Each model is programmed in -its own way, and each model sends back the video in its own way. - -Quirks of Model 2 cameras: -------------------------- - -Model 2 does not have hardware contrast control. Corresponding V4L -control is implemented in software, which is not very nice to your -CPU, but at least it works. - -This driver provides 352x288 mode by switching the camera into -quasi-352x288 RGB mode (800 Kbits per frame) essentially limiting -this mode to 10 frames per second or less, in ideal conditions on -the bus (USB is shared, after all). The frame rate -has to be programmed very conservatively. Additional concern is that -frame rate depends on brightness setting; therefore the picture can -be good at one brightness and broken at another! I did not want to fix -the frame rate at slowest setting, but I had to move it pretty much down -the scale (so that framerate option barely matters). I also noticed that -camera after first powering up produces frames slightly faster than during -consecutive uses. All this means that if you use 352x288 (which is -default), be warned - you may encounter broken picture on first connect; -try to adjust brightness - brighter image is slower, so USB will be able -to send all data. However if you regularly use Model 2 cameras you may -prefer 176x144 which makes perfectly good I420, with no scaling and -lesser demands on USB (300 Kbits per second, or 26 frames per second). - -Another strange effect of 352x288 mode is the fine vertical grid visible -on some colored surfaces. I am sure it is caused by me not understanding -what the camera is trying to say. Blame trade secrets for that. - -The camera that I had also has a hardware quirk: if disconnected, -it needs few minutes to "relax" before it can be plugged in again -(poorly designed USB processor reset circuit?) - -[Veo Stingray with Product ID 0x800C is also Model 2, but I haven't -observed this particular flaw in it.] - -Model 2 camera can be programmed for very high sensitivity (even starlight -may be enough), this makes it convenient for tinkering with. The driver -code has enough comments to help a programmer to tweak the camera -as s/he feels necessary. - -WHAT YOU NEED: - -- A supported IBM PC (C-it) camera (model 1 or 2) - -- A Linux box with USB support (2.3/2.4; 2.2 w/backport may work) - -- A Video4Linux compatible frame grabber program such as xawtv. - -HOW TO COMPILE THE DRIVER: - -You need to compile the driver only if you are a developer -or if you want to make changes to the code. Most distributions -precompile all modules, so you can go directly to the next -section "HOW TO USE THE DRIVER". - -The ibmcam driver uses usbvideo helper library (module), -so if you are studying the ibmcam code you will be led there. - -The driver itself consists of only one file in usb/ directory: -ibmcam.c. This file is included into the Linux kernel build -process if you configure the kernel for CONFIG_USB_IBMCAM. -Run "make xconfig" and in USB section you will find the IBM -camera driver. Select it, save the configuration and recompile. - -HOW TO USE THE DRIVER: - -I recommend to compile driver as a module. This gives you an -easier access to its configuration. The camera has many more -settings than V4L can operate, so some settings are done using -module options. - -To begin with, on most modern Linux distributions the driver -will be automatically loaded whenever you plug the supported -camera in. Therefore, you don't need to do anything. However -if you want to experiment with some module parameters then -you can load and unload the driver manually, with camera -plugged in or unplugged. - -Typically module is installed with command 'modprobe', like this: - -# modprobe ibmcam framerate=1 - -Alternatively you can use 'insmod' in similar fashion: - -# insmod /lib/modules/2.x.y/usb/ibmcam.o framerate=1 - -Module can be inserted with camera connected or disconnected. - -The driver can have options, though some defaults are provided. - -Driver options: (* indicates that option is model-dependent) - -Name Type Range [default] Example --------------- -------------- -------------- ------------------ -debug Integer 0-9 [0] debug=1 -flags Integer 0-0xFF [0] flags=0x0d -framerate Integer 0-6 [2] framerate=1 -hue_correction Integer 0-255 [128] hue_correction=115 -init_brightness Integer 0-255 [128] init_brightness=100 -init_contrast Integer 0-255 [192] init_contrast=200 -init_color Integer 0-255 [128] init_color=130 -init_hue Integer 0-255 [128] init_hue=115 -lighting Integer 0-2* [1] lighting=2 -sharpness Integer 0-6* [4] sharpness=3 -size Integer 0-2* [2] size=1 - -Options for Model 2 only: - -Name Type Range [default] Example --------------- -------------- -------------- ------------------ -init_model2_rg Integer 0..255 [0x70] init_model2_rg=128 -init_model2_rg2 Integer 0..255 [0x2f] init_model2_rg2=50 -init_model2_sat Integer 0..255 [0x34] init_model2_sat=65 -init_model2_yb Integer 0..255 [0xa0] init_model2_yb=200 - -debug You don't need this option unless you are a developer. - If you are a developer then you will see in the code - what values do what. 0=off. - -flags This is a bit mask, and you can combine any number of - bits to produce what you want. Usually you don't want - any of extra features this option provides: - - FLAGS_RETRY_VIDIOCSYNC 1 This bit allows to retry failed - VIDIOCSYNC ioctls without failing. - Will work with xawtv, will not - with xrealproducer. Default is - not set. - FLAGS_MONOCHROME 2 Activates monochrome (b/w) mode. - FLAGS_DISPLAY_HINTS 4 Shows colored pixels which have - magic meaning to developers. - FLAGS_OVERLAY_STATS 8 Shows tiny numbers on screen, - useful only for debugging. - FLAGS_FORCE_TESTPATTERN 16 Shows blue screen with numbers. - FLAGS_SEPARATE_FRAMES 32 Shows each frame separately, as - it was received from the camera. - Default (not set) is to mix the - preceding frame in to compensate - for occasional loss of Isoc data - on high frame rates. - FLAGS_CLEAN_FRAMES 64 Forces "cleanup" of each frame - prior to use; relevant only if - FLAGS_SEPARATE_FRAMES is set. - Default is not to clean frames, - this is a little faster but may - produce flicker if frame rate is - too high and Isoc data gets lost. - FLAGS_NO_DECODING 128 This flag turns the video stream - decoder off, and dumps the raw - Isoc data from the camera into - the reading process. Useful to - developers, but not to users. - -framerate This setting controls frame rate of the camera. This is - an approximate setting (in terms of "worst" ... "best") - because camera changes frame rate depending on amount - of light available. Setting 0 is slowest, 6 is fastest. - Beware - fast settings are very demanding and may not - work well with all video sizes. Be conservative. - -hue_correction This highly optional setting allows to adjust the - hue of the image in a way slightly different from - what usual "hue" control does. Both controls affect - YUV colorspace: regular "hue" control adjusts only - U component, and this "hue_correction" option similarly - adjusts only V component. However usually it is enough - to tweak only U or V to compensate for colored light or - color temperature; this option simply allows more - complicated correction when and if it is necessary. - -init_brightness These settings specify _initial_ values which will be -init_contrast used to set up the camera. If your V4L application has -init_color its own controls to adjust the picture then these -init_hue controls will be used too. These options allow you to - preconfigure the camera when it gets connected, before - any V4L application connects to it. Good for webcams. - -init_model2_rg These initial settings alter color balance of the -init_model2_rg2 camera on hardware level. All four settings may be used -init_model2_sat to tune the camera to specific lighting conditions. These -init_model2_yb settings only apply to Model 2 cameras. - -lighting This option selects one of three hardware-defined - photosensitivity settings of the camera. 0=bright light, - 1=Medium (default), 2=Low light. This setting affects - frame rate: the dimmer the lighting the lower the frame - rate (because longer exposition time is needed). The - Model 2 cameras allow values more than 2 for this option, - thus enabling extremely high sensitivity at cost of frame - rate, color saturation and imaging sensor noise. - -sharpness This option controls smoothing (noise reduction) - made by camera. Setting 0 is most smooth, setting 6 - is most sharp. Be aware that CMOS sensor used in the - camera is pretty noisy, so if you choose 6 you will - be greeted with "snowy" image. Default is 4. Model 2 - cameras do not support this feature. - -size This setting chooses one of several image sizes that are - supported by this driver. Cameras may support more, but - it's difficult to reverse-engineer all formats. - Following video sizes are supported: - - size=0 128x96 (Model 1 only) - size=1 160x120 - size=2 176x144 - size=3 320x240 (Model 2 only) - size=4 352x240 (Model 2 only) - size=5 352x288 - size=6 640x480 (Model 3 only) - - The 352x288 is the native size of the Model 1 sensor - array, so it's the best resolution the camera can - yield. The best resolution of Model 2 is 176x144, and - larger images are produced by stretching the bitmap. - Model 3 has sensor with 640x480 grid, and it works too, - but the frame rate will be exceptionally low (1-2 FPS); - it may be still OK for some applications, like security. - Choose the image size you need. The smaller image can - support faster frame rate. Default is 352x288. - -For more information and the Troubleshooting FAQ visit this URL: - - http://www.linux-usb.org/ibmcam/ - -WHAT NEEDS TO BE DONE: - -- The button on the camera is not used. I don't know how to get to it. - I know now how to read button on Model 2, but what to do with it? - -- Camera reports its status back to the driver; however I don't know - what returned data means. If camera fails at some initialization - stage then something should be done, and I don't do that because - I don't even know that some command failed. This is mostly Model 1 - concern because Model 2 uses different commands which do not return - status (and seem to complete successfully every time). - -- Some flavors of Model 4 NetCameras produce only compressed video - streams, and I don't know how to decode them. - -CREDITS: - -The code is based in no small part on the CPiA driver by Johannes Erdfelt, -Randy Dunlap, and others. Big thanks to them for their pioneering work on that -and the USB stack. - -I also thank John Lightsey for his donation of the Veo Stingray camera. diff --git a/Documentation/video4linux/m5602.txt b/Documentation/video4linux/m5602.txt deleted file mode 100644 index 4450ab13f37b..000000000000 --- a/Documentation/video4linux/m5602.txt +++ /dev/null @@ -1,12 +0,0 @@ -This document describes the ALi m5602 bridge connected -to the following supported sensors: -OmniVision OV9650, -Samsung s5k83a, -Samsung s5k4aa, -Micron mt9m111, -Pixel plus PO1030 - -This driver mimics the windows drivers, which have a braindead implementation sending bayer-encoded frames at VGA resolution. -In a perfect world we should be able to reprogram the m5602 and the connected sensor in hardware instead, supporting a range of resolutions and pixelformats - -Anyway, have fun and please report any bugs to m560x-driver-devel@lists.sourceforge.net diff --git a/Documentation/video4linux/ov511.txt b/Documentation/video4linux/ov511.txt deleted file mode 100644 index b3326b167ada..000000000000 --- a/Documentation/video4linux/ov511.txt +++ /dev/null @@ -1,288 +0,0 @@ -------------------------------------------------------------------------------- -Readme for Linux device driver for the OmniVision OV511 USB to camera bridge IC -------------------------------------------------------------------------------- - -Author: Mark McClelland -Homepage: http://alpha.dyndns.org/ov511 - -INTRODUCTION: - -This is a driver for the OV511, a USB-only chip used in many "webcam" devices. -Any camera using the OV511/OV511+ and the OV6620/OV7610/20/20AE should work. -Video capture devices that use the Philips SAA7111A decoder also work. It -supports streaming and capture of color or monochrome video via the Video4Linux -API. Most V4L apps are compatible with it. Most resolutions with a width and -height that are a multiple of 8 are supported. - -If you need more information, please visit the OV511 homepage at the above URL. - -WHAT YOU NEED: - -- If you want to help with the development, get the chip's specification docs at - http://www.ovt.com/omniusbp.html - -- A Video4Linux compatible frame grabber program (I recommend vidcat and xawtv) - vidcat is part of the w3cam package: http://mpx.freeshell.net/ - xawtv is available at: http://linux.bytesex.org/xawtv/ - -HOW TO USE IT: - -Note: These are simplified instructions. For complete instructions see: - http://alpha.dyndns.org/ov511/install.html - -You must have first compiled USB support, support for your specific USB host -controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend -making them modules.) Make sure "Enforce bandwidth allocation" is NOT enabled. - -Next, (as root): - - modprobe usbcore - modprobe usb-uhci modprobe usb-ohci - modprobe videodev - modprobe ov511 - -If it is not already there (it usually is), create the video device: - - mknod /dev/video0 c 81 0 - -Optionally, symlink /dev/video to /dev/video0 - -You will have to set permissions on this device to allow you to read/write -from it: - - chmod 666 /dev/video - chmod 666 /dev/video0 (if necessary) - -Now you are ready to run a video app! Both vidcat and xawtv work well for me -at 640x480. - -[Using vidcat:] - - vidcat -s 640x480 -p c > test.jpg - xview test.jpg - -[Using xawtv:] - -From the main xawtv directory: - - make clean - ./configure - make - make install - -Now you should be able to run xawtv. Right click for the options dialog. - -MODULE PARAMETERS: - - You can set these with: insmod ov511 NAME=VALUE - There is currently no way to set these on a per-camera basis. - - NAME: autobright - TYPE: integer (Boolean) - DEFAULT: 1 - DESC: Brightness is normally under automatic control and can't be set - manually by the video app. Set to 0 for manual control. - - NAME: autogain - TYPE: integer (Boolean) - DEFAULT: 1 - DESC: Auto Gain Control enable. This feature is not yet implemented. - - NAME: autoexp - TYPE: integer (Boolean) - DEFAULT: 1 - DESC: Auto Exposure Control enable. This feature is not yet implemented. - - NAME: debug - TYPE: integer (0-6) - DEFAULT: 3 - DESC: Sets the threshold for printing debug messages. The higher the value, - the more is printed. The levels are cumulative, and are as follows: - 0=no debug messages - 1=init/detection/unload and other significant messages - 2=some warning messages - 3=config/control function calls - 4=most function calls and data parsing messages - 5=highly repetitive mesgs - - NAME: snapshot - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: Set to 1 to enable snapshot mode. read()/VIDIOCSYNC will block until - the snapshot button is pressed. Note: enabling this mode disables - /proc/video/ov511//button - - NAME: cams - TYPE: integer (1-4 for OV511, 1-31 for OV511+) - DEFAULT: 1 - DESC: Number of cameras allowed to stream simultaneously on a single bus. - Values higher than 1 reduce the data rate of each camera, allowing two - or more to be used at once. If you have a complicated setup involving - both OV511 and OV511+ cameras, trial-and-error may be necessary for - finding the optimum setting. - - NAME: compress - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: Set this to 1 to turn on the camera's compression engine. This can - potentially increase the frame rate at the expense of quality, if you - have a fast CPU. You must load the proper compression module for your - camera before starting your application (ov511_decomp or ov518_decomp). - - NAME: testpat - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: This configures the camera's sensor to transmit a colored test-pattern - instead of an image. This does not work correctly yet. - - NAME: dumppix - TYPE: integer (0-2) - DEFAULT: 0 - DESC: Dumps raw pixel data and skips post-processing and format conversion. - It is for debugging purposes only. Options are: - 0: Disable (default) - 1: Dump raw data from camera, excluding headers and trailers - 2: Dumps data exactly as received from camera - - NAME: led - TYPE: integer (0-2) - DEFAULT: 1 (Always on) - DESC: Controls whether the LED (the little light) on the front of the camera - is always off (0), always on (1), or only on when driver is open (2). - This is not supported with the OV511, and might only work with certain - cameras (ones that actually have the LED wired to the control pin, and - not just hard-wired to be on all the time). - - NAME: dump_bridge - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: Dumps the bridge (OV511[+] or OV518[+]) register values to the system - log. Only useful for serious debugging/development purposes. - - NAME: dump_sensor - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: Dumps the sensor register values to the system log. Only useful for - serious debugging/development purposes. - - NAME: printph - TYPE: integer (Boolean) - DEFAULT: 0 - DESC: Setting this to 1 will dump the first 12 bytes of each isoc frame. This - is only useful if you are trying to debug problems with the isoc data - stream (i.e.: camera initializes, but vidcat hangs until Ctrl-C). Be - warned that this dumps a large number of messages to your kernel log. - - NAME: phy, phuv, pvy, pvuv, qhy, qhuv, qvy, qvuv - TYPE: integer (0-63 for phy and phuv, 0-255 for rest) - DEFAULT: OV511 default values - DESC: These are registers 70h - 77h of the OV511, which control the - prediction ranges and quantization thresholds of the compressor, for - the Y and UV channels in the horizontal and vertical directions. See - the OV511 or OV511+ data sheet for more detailed descriptions. These - normally do not need to be changed. - - NAME: lightfreq - TYPE: integer (0, 50, or 60) - DEFAULT: 0 (use sensor default) - DESC: Sets the sensor to match your lighting frequency. This can reduce the - appearance of "banding", i.e. horizontal lines or waves of light and - dark that are often caused by artificial lighting. Valid values are: - 0 - Use default (depends on sensor, most likely 60 Hz) - 50 - For European and Asian 50 Hz power - 60 - For American 60 Hz power - - NAME: bandingfilter - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Enables the sensor´s banding filter exposure algorithm. This reduces - or stabilizes the "banding" caused by some artificial light sources - (especially fluorescent). You might have to set lightfreq correctly for - this to work right. As an added bonus, this sometimes makes it - possible to capture your monitor´s output. - - NAME: fastset - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Allows picture settings (brightness, contrast, color, and hue) to take - effect immediately, even in the middle of a frame. This reduces the - time to change settings, but can ruin frames during the change. Only - affects OmniVision sensors. - - NAME: force_palette - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Forces the palette (color format) to a specific value. If an - application requests a different palette, it will be rejected, thereby - forcing it to try others until it succeeds. This is useful for forcing - greyscale mode with a color camera, for example. Supported modes are: - 0 (Allows all the following formats) - 1 VIDEO_PALETTE_GREY (Linear greyscale) - 10 VIDEO_PALETTE_YUV420 (YUV 4:2:0 Planar) - 15 VIDEO_PALETTE_YUV420P (YUV 4:2:0 Planar, same as 10) - - NAME: backlight - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Setting this flag changes the exposure algorithm for OmniVision sensors - such that objects in the camera's view (i.e. your head) can be clearly - seen when they are illuminated from behind. It reduces or eliminates - the sensor's auto-exposure function, so it should only be used when - needed. Additionally, it is only supported with the OV6620 and OV7620. - - NAME: unit_video - TYPE: Up to 16 comma-separated integers - DEFAULT: 0,0,0... (automatically assign the next available minor(s)) - DESC: You can specify up to 16 minor numbers to be assigned to ov511 devices. - For example, "unit_video=1,3" will make the driver use /dev/video1 and - /dev/video3 for the first two devices it detects. Additional devices - will be assigned automatically starting at the first available device - node (/dev/video0 in this case). Note that you cannot specify 0 as a - minor number. This feature requires kernel version 2.4.5 or higher. - - NAME: remove_zeros - TYPE: integer (Boolean) - DEFAULT: 0 (do not skip any incoming data) - DESC: Setting this to 1 will remove zero-padding from incoming data. This - will compensate for the blocks of corruption that can appear when the - camera cannot keep up with the speed of the USB bus (eg. at low frame - resolutions). This feature is always enabled when compression is on. - - NAME: mirror - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Setting this to 1 will reverse ("mirror") the image horizontally. This - might be necessary if your camera has a custom lens assembly. This has - no effect with video capture devices. - - NAME: ov518_color - TYPE: integer (Boolean) - DEFAULT: 0 (off) - DESC: Enable OV518 color support. This is off by default since it doesn't - work most of the time. If you want to try it, you must also load - ov518_decomp with the "nouv=0" parameter. If you get improper colors or - diagonal lines through the image, restart your video app and try again. - Repeat as necessary. - -WORKING FEATURES: - o Color streaming/capture at most widths and heights that are multiples of 8. - o Monochrome (use force_palette=1 to enable) - o Setting/getting of saturation, contrast, brightness, and hue (only some of - them work the OV7620 and OV7620AE) - o /proc status reporting - o SAA7111A video capture support at 320x240 and 640x480 - o Compression support - o SMP compatibility - -HOW TO CONTACT ME: - -You can email me at mark@alpha.dyndns.org . Please prefix the subject line -with "OV511: " so that I am certain to notice your message. - -CREDITS: - -The code is based in no small part on the CPiA driver by Johannes Erdfelt, -Randy Dunlap, and others. Big thanks to them for their pioneering work on that -and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and -image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio -Matsuoka for their work as well. diff --git a/Documentation/video4linux/se401.txt b/Documentation/video4linux/se401.txt deleted file mode 100644 index bd6526ec8dd7..000000000000 --- a/Documentation/video4linux/se401.txt +++ /dev/null @@ -1,54 +0,0 @@ -Linux driver for SE401 based USB cameras - -Copyright, 2001, Jeroen Vreeken - - -INTRODUCTION: - -The SE401 chip is the used in low-cost usb webcams. -It is produced by Endpoints Inc. (www.endpoints.com). -It interfaces directly to a cmos image sensor and USB. The only other major -part in a se401 based camera is a dram chip. - -The following cameras are known to work with this driver: - -Aox se401 (non-branded) cameras -Philips PVCV665 USB VGA webcam 'Vesta Fun' -Kensington VideoCAM PC Camera Model 67014 -Kensington VideoCAM PC Camera Model 67015 -Kensington VideoCAM PC Camera Model 67016 -Kensington VideoCAM PC Camera Model 67017 - - -WHAT YOU NEED: - -- USB support -- VIDEO4LINUX support - -More information about USB support for linux can be found at: -http://www.linux-usb.org - - -MODULE OPTIONS: - -When the driver is compiled as a module you can also use the 'flickerless' -option. With it exposure is limited to values that do not interfere with the -net frequency. Valid options for this option are 0, 50 and 60. (0=disable, -50=50hz, 60=60hz) - - -KNOWN PROBLEMS: - -The driver works fine with the usb-ohci and uhci host controller drivers, -the default settings also work with usb-uhci. But sending more than one bulk -transfer at a time with usb-uhci doesn't work yet. -Users of usb-ohci and uhci can safely enlarge SE401_NUMSBUF in se401.h in -order to increase the throughput (and thus framerate). - - -HELP: - -The latest info on this driver can be found at: -http://members.chello.nl/~j.vreeken/se401/ -And questions to me can be send to: -pe1rxq@amsat.org diff --git a/Documentation/video4linux/stv680.txt b/Documentation/video4linux/stv680.txt deleted file mode 100644 index e3de33645308..000000000000 --- a/Documentation/video4linux/stv680.txt +++ /dev/null @@ -1,53 +0,0 @@ -Linux driver for STV0680 based USB cameras - -Copyright, 2001, Kevin Sisson - - -INTRODUCTION: - -STMicroelectronics produces the STV0680B chip, which comes in two -types, -001 and -003. The -003 version allows the recording and downloading -of sound clips from the camera, and allows a flash attachment. Otherwise, -it uses the same commands as the -001 version. Both versions support a -variety of SDRAM sizes and sensors, allowing for a maximum of 26 VGA or 20 -CIF pictures. The STV0680 supports either a serial or a usb interface, and -video is possible through the usb interface. - -The following cameras are known to work with this driver, although any -camera with Vendor/Product codes of 0553/0202 should work: - -Aiptek Pencam (various models) -Nisis QuickPix 2 -Radio Shack 'Kid's digital camera' (#60-1207) -At least one Trust Spycam model -Several other European brand models - -WHAT YOU NEED: - -- USB support -- VIDEO4LINUX support - -More information about USB support for linux can be found at: -http://www.linux-usb.org - - -MODULE OPTIONS: - -When the driver is compiled as a module, you can set a "swapRGB=1" -option, if necessary, for those applications that require it -(such as xawtv). However, the driver should detect and set this -automatically, so this option should not normally be used. - - -KNOWN PROBLEMS: - -The driver seems to work better with the usb-ohci than the usb-uhci host -controller driver. - -HELP: - -The latest info on this driver can be found at: -http://personal.clt.bellsouth.net/~kjsisson or at -http://stv0680-usb.sourceforge.net - -Any questions to me can be send to: kjsisson@bellsouth.net diff --git a/Documentation/video4linux/w9968cf.txt b/Documentation/video4linux/w9968cf.txt deleted file mode 100644 index 9649450f3b90..000000000000 --- a/Documentation/video4linux/w9968cf.txt +++ /dev/null @@ -1,458 +0,0 @@ - - W996[87]CF JPEG USB Dual Mode Camera Chip - Driver for Linux 2.6 (basic version) - ========================================= - - - Documentation - - - -Index -===== -1. Copyright -2. Disclaimer -3. License -4. Overview -5. Supported devices -6. Module dependencies -7. Module loading -8. Module parameters -9. Contact information -10. Credits - - -1. Copyright -============ -Copyright (C) 2002-2004 by Luca Risolia - - -2. Disclaimer -============= -Winbond is a trademark of Winbond Electronics Corporation. -This software is not sponsored or developed by Winbond. - - -3. License -========== -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -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., 675 Mass Ave, Cambridge, MA 02139, USA. - - -4. Overview -=========== -This driver supports the video streaming capabilities of the devices mounting -Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips. OV681 -based cameras should be supported as well. - -The driver is divided into two modules: the basic one, "w9968cf", is needed for -the supported devices to work; the second one, "w9968cf-vpp", is an optional -module, which provides some useful video post-processing functions like video -decoding, up-scaling and colour conversions. - -Note that the official kernels do neither include nor support the second -module for performance purposes. Therefore, it is always recommended to -download and install the latest and complete release of the driver, -replacing the existing one, if present. - -The latest and full-featured version of the W996[87]CF driver can be found at: -http://www.linux-projects.org. Please refer to the documentation included in -that package, if you are going to use it. - -Up to 32 cameras can be handled at the same time. They can be connected and -disconnected from the host many times without turning off the computer, if -your system supports the hotplug facility. - -To change the default settings for each camera, many parameters can be passed -through command line when the module is loaded into memory. - -The driver relies on the Video4Linux, USB and I2C core modules. It has been -designed to run properly on SMP systems as well. An additional module, -"ovcamchip", is mandatory; it provides support for some OmniVision image -sensors connected to the W996[87]CF chips; if found in the system, the module -will be automatically loaded by default (provided that the kernel has been -compiled with the automatic module loading option). - - -5. Supported devices -==================== -At the moment, known W996[87]CF and OV681 based devices are: -- Aroma Digi Pen VGA Dual Mode ADG-5000 (unknown image sensor) -- AVerMedia AVerTV USB (SAA7111A, Philips FI1216Mk2 tuner, PT2313L audio chip) -- Creative Labs Video Blaster WebCam Go (OmniVision OV7610 sensor) -- Creative Labs Video Blaster WebCam Go Plus (OmniVision OV7620 sensor) -- Lebon LDC-035A (unknown image sensor) -- Ezonics EZ-802 EZMega Cam (OmniVision OV8610C sensor) -- OmniVision OV8610-EDE (OmniVision OV8610 sensor) -- OPCOM Digi Pen VGA Dual Mode Pen Camera (unknown image sensor) -- Pretec Digi Pen-II (OmniVision OV7620 sensor) -- Pretec DigiPen-480 (OmniVision OV8610 sensor) - -If you know any other W996[87]CF or OV681 based cameras, please contact me. - -The list above does not imply that all those devices work with this driver: up -until now only webcams that have an image sensor supported by the "ovcamchip" -module work. Kernel messages will always tell you whether this is case. - -Possible external microcontrollers of those webcams are not supported: this -means that still images cannot be downloaded from the device memory. - -Furthermore, it's worth to note that I was only able to run tests on my -"Creative Labs Video Blaster WebCam Go". Donations of other models, for -additional testing and full support, would be much appreciated. - - -6. Module dependencies -====================== -For it to work properly, the driver needs kernel support for Video4Linux, USB -and I2C, and the "ovcamchip" module for the image sensor. Make sure you are not -actually using any external "ovcamchip" module, given that the W996[87]CF -driver depends on the version of the module present in the official kernels. - -The following options of the kernel configuration file must be enabled and -corresponding modules must be compiled: - - # Multimedia devices - # - CONFIG_VIDEO_DEV=m - - # I2C support - # - CONFIG_I2C=m - -The I2C core module can be compiled statically in the kernel as well. - - # OmniVision Camera Chip support - # - CONFIG_VIDEO_OVCAMCHIP=m - - # USB support - # - CONFIG_USB=m - -In addition, depending on the hardware being used, only one of the modules -below is necessary: - - # USB Host Controller Drivers - # - CONFIG_USB_EHCI_HCD=m - CONFIG_USB_UHCI_HCD=m - CONFIG_USB_OHCI_HCD=m - -And finally: - - # USB Multimedia devices - # - CONFIG_USB_W9968CF=m - - -7. Module loading -================= -To use the driver, it is necessary to load the "w9968cf" module into memory -after every other module required. - -Loading can be done this way, from root: - - [root@localhost home]# modprobe usbcore - [root@localhost home]# modprobe i2c-core - [root@localhost home]# modprobe videodev - [root@localhost home]# modprobe w9968cf - -At this point the pertinent devices should be recognized: "dmesg" can be used -to analyze kernel messages: - - [user@localhost home]$ dmesg - -There are a lot of parameters the module can use to change the default -settings for each device. To list every possible parameter with a brief -explanation about them and which syntax to use, it is recommended to run the -"modinfo" command: - - [root@locahost home]# modinfo w9968cf - - -8. Module parameters -==================== -Module parameters are listed below: -------------------------------------------------------------------------------- -Name: ovmod_load -Type: bool -Syntax: <0|1> -Description: Automatic 'ovcamchip' module loading: 0 disabled, 1 enabled. - If enabled, 'insmod' searches for the required 'ovcamchip' - module in the system, according to its configuration, and - loads that module automatically. This action is performed as - once soon as the 'w9968cf' module is loaded into memory. -Default: 1 -------------------------------------------------------------------------------- -Name: simcams -Type: int -Syntax: -Description: Number of cameras allowed to stream simultaneously. - n may vary from 0 to 32. -Default: 32 -------------------------------------------------------------------------------- -Name: video_nr -Type: int array (min = 0, max = 32) -Syntax: <-1|n[,...]> -Description: Specify V4L minor mode number. - -1 = use next available - n = use minor number n - You can specify up to 32 cameras this way. - For example: - video_nr=-1,2,-1 would assign minor number 2 to the second - recognized camera and use auto for the first one and for every - other camera. -Default: -1 -------------------------------------------------------------------------------- -Name: packet_size -Type: int array (min = 0, max = 32) -Syntax: -Description: Specify the maximum data payload size in bytes for alternate - settings, for each device. n is scaled between 63 and 1023. -Default: 1023 -------------------------------------------------------------------------------- -Name: max_buffers -Type: int array (min = 0, max = 32) -Syntax: -Description: For advanced users. - Specify the maximum number of video frame buffers to allocate - for each device, from 2 to 32. -Default: 2 -------------------------------------------------------------------------------- -Name: double_buffer -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Hardware double buffering: 0 disabled, 1 enabled. - It should be enabled if you want smooth video output: if you - obtain out of sync. video, disable it, or try to - decrease the 'clockdiv' module parameter value. -Default: 1 for every device. -------------------------------------------------------------------------------- -Name: clamping -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Video data clamping: 0 disabled, 1 enabled. -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: filter_type -Type: int array (min = 0, max = 32) -Syntax: <0|1|2[,...]> -Description: Video filter type. - 0 none, 1 (1-2-1) 3-tap filter, 2 (2-3-6-3-2) 5-tap filter. - The filter is used to reduce noise and aliasing artifacts - produced by the CCD or CMOS image sensor. -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: largeview -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Large view: 0 disabled, 1 enabled. -Default: 1 for every device. -------------------------------------------------------------------------------- -Name: upscaling -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Software scaling (for non-compressed video only): - 0 disabled, 1 enabled. - Disable it if you have a slow CPU or you don't have enough - memory. -Default: 0 for every device. -Note: If 'w9968cf-vpp' is not present, this parameter is set to 0. -------------------------------------------------------------------------------- -Name: decompression -Type: int array (min = 0, max = 32) -Syntax: <0|1|2[,...]> -Description: Software video decompression: - 0 = disables decompression - (doesn't allow formats needing decompression). - 1 = forces decompression - (allows formats needing decompression only). - 2 = allows any permitted formats. - Formats supporting (de)compressed video are YUV422P and - YUV420P/YUV420 in any resolutions where width and height are - multiples of 16. -Default: 2 for every device. -Note: If 'w9968cf-vpp' is not present, forcing decompression is not - allowed; in this case this parameter is set to 2. -------------------------------------------------------------------------------- -Name: force_palette -Type: int array (min = 0, max = 32) -Syntax: <0|9|10|13|15|8|7|1|6|3|4|5[,...]> -Description: Force picture palette. - In order: - 0 = Off - allows any of the following formats: - 9 = UYVY 16 bpp - Original video, compression disabled - 10 = YUV420 12 bpp - Original video, compression enabled - 13 = YUV422P 16 bpp - Original video, compression enabled - 15 = YUV420P 12 bpp - Original video, compression enabled - 8 = YUVY 16 bpp - Software conversion from UYVY - 7 = YUV422 16 bpp - Software conversion from UYVY - 1 = GREY 8 bpp - Software conversion from UYVY - 6 = RGB555 16 bpp - Software conversion from UYVY - 3 = RGB565 16 bpp - Software conversion from UYVY - 4 = RGB24 24 bpp - Software conversion from UYVY - 5 = RGB32 32 bpp - Software conversion from UYVY - When not 0, this parameter will override 'decompression'. -Default: 0 for every device. Initial palette is 9 (UYVY). -Note: If 'w9968cf-vpp' is not present, this parameter is set to 9. -------------------------------------------------------------------------------- -Name: force_rgb -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Read RGB video data instead of BGR: - 1 = use RGB component ordering. - 0 = use BGR component ordering. - This parameter has effect when using RGBX palettes only. -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: autobright -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Image sensor automatically changes brightness: - 0 = no, 1 = yes -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: autoexp -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Image sensor automatically changes exposure: - 0 = no, 1 = yes -Default: 1 for every device. -------------------------------------------------------------------------------- -Name: lightfreq -Type: int array (min = 0, max = 32) -Syntax: <50|60[,...]> -Description: Light frequency in Hz: - 50 for European and Asian lighting, 60 for American lighting. -Default: 50 for every device. -------------------------------------------------------------------------------- -Name: bandingfilter -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Banding filter to reduce effects of fluorescent - lighting: - 0 disabled, 1 enabled. - This filter tries to reduce the pattern of horizontal - light/dark bands caused by some (usually fluorescent) lighting. -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: clockdiv -Type: int array (min = 0, max = 32) -Syntax: <-1|n[,...]> -Description: Force pixel clock divisor to a specific value (for experts): - n may vary from 0 to 127. - -1 for automatic value. - See also the 'double_buffer' module parameter. -Default: -1 for every device. -------------------------------------------------------------------------------- -Name: backlight -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Objects are lit from behind: - 0 = no, 1 = yes -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: mirror -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Reverse image horizontally: - 0 = no, 1 = yes -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: monochrome -Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: The image sensor is monochrome: - 0 = no, 1 = yes -Default: 0 for every device. -------------------------------------------------------------------------------- -Name: brightness -Type: long array (min = 0, max = 32) -Syntax: -Description: Set picture brightness (0-65535). - This parameter has no effect if 'autobright' is enabled. -Default: 31000 for every device. -------------------------------------------------------------------------------- -Name: hue -Type: long array (min = 0, max = 32) -Syntax: -Description: Set picture hue (0-65535). -Default: 32768 for every device. -------------------------------------------------------------------------------- -Name: colour -Type: long array (min = 0, max = 32) -Syntax: -Description: Set picture saturation (0-65535). -Default: 32768 for every device. -------------------------------------------------------------------------------- -Name: contrast -Type: long array (min = 0, max = 32) -Syntax: -Description: Set picture contrast (0-65535). -Default: 50000 for every device. -------------------------------------------------------------------------------- -Name: whiteness -Type: long array (min = 0, max = 32) -Syntax: -Description: Set picture whiteness (0-65535). -Default: 32768 for every device. -------------------------------------------------------------------------------- -Name: debug -Type: int -Syntax: -Description: Debugging information level, from 0 to 6: - 0 = none (use carefully) - 1 = critical errors - 2 = significant information - 3 = configuration or general messages - 4 = warnings - 5 = called functions - 6 = function internals - Level 5 and 6 are useful for testing only, when only one - device is used. -Default: 2 -------------------------------------------------------------------------------- -Name: specific_debug -Type: bool -Syntax: <0|1> -Description: Enable or disable specific debugging messages: - 0 = print messages concerning every level <= 'debug' level. - 1 = print messages concerning the level indicated by 'debug'. -Default: 0 -------------------------------------------------------------------------------- - - -9. Contact information -====================== -I may be contacted by e-mail at . - -I can accept GPG/PGP encrypted e-mail. My GPG key ID is 'FCE635A4'. -My public 1024-bit key should be available at your keyserver; the fingerprint -is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. - - -10. Credits -========== -The development would not have proceed much further without having looked at -the source code of other drivers and without the help of several persons; in -particular: - -- the I2C interface to kernel and high-level image sensor control routines have - been taken from the OV511 driver by Mark McClelland; - -- memory management code has been copied from the bttv driver by Ralph Metzler, - Marcus Metzler and Gerd Knorr; - -- the low-level I2C read function has been written by Frederic Jouault; - -- the low-level I2C fast write function has been written by Piotr Czerczak. diff --git a/Documentation/video4linux/zc0301.txt b/Documentation/video4linux/zc0301.txt deleted file mode 100644 index b41c83cf09f4..000000000000 --- a/Documentation/video4linux/zc0301.txt +++ /dev/null @@ -1,270 +0,0 @@ - - ZC0301 and ZC0301P Image Processor and Control Chip - Driver for Linux - =================================================== - - - Documentation - - - -Index -===== -1. Copyright -2. Disclaimer -3. License -4. Overview and features -5. Module dependencies -6. Module loading -7. Module parameters -8. Supported devices -9. Notes for V4L2 application developers -10. Contact information -11. Credits - - -1. Copyright -============ -Copyright (C) 2006-2007 by Luca Risolia - - -2. Disclaimer -============= -This software is not developed or sponsored by Z-Star Microelectronics Corp. -Trademarks are property of their respective owner. - - -3. License -========== -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -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., 675 Mass Ave, Cambridge, MA 02139, USA. - - -4. Overview and features -======================== -This driver supports the video interface of the devices mounting the ZC0301 or -ZC0301P Image Processors and Control Chips. - -The driver relies on the Video4Linux2 and USB core modules. It has been -designed to run properly on SMP systems as well. - -The latest version of the ZC0301[P] driver can be found at the following URL: -http://www.linux-projects.org/ - -Some of the features of the driver are: - -- full compliance with the Video4Linux2 API (see also "Notes for V4L2 - application developers" paragraph); -- available mmap or read/poll methods for video streaming through isochronous - data transfers; -- automatic detection of image sensor; -- video format is standard JPEG; -- dynamic driver control thanks to various module parameters (see "Module - parameters" paragraph); -- up to 64 cameras can be handled at the same time; they can be connected and - disconnected from the host many times without turning off the computer, if - the system supports hotplugging; - - -5. Module dependencies -====================== -For it to work properly, the driver needs kernel support for Video4Linux and -USB. - -The following options of the kernel configuration file must be enabled and -corresponding modules must be compiled: - - # Multimedia devices - # - CONFIG_VIDEO_DEV=m - - # USB support - # - CONFIG_USB=m - -In addition, depending on the hardware being used, the modules below are -necessary: - - # USB Host Controller Drivers - # - CONFIG_USB_EHCI_HCD=m - CONFIG_USB_UHCI_HCD=m - CONFIG_USB_OHCI_HCD=m - -The ZC0301 controller also provides a built-in microphone interface. It is -supported by the USB Audio driver thanks to the ALSA API: - - # Sound - # - CONFIG_SOUND=y - - # Advanced Linux Sound Architecture - # - CONFIG_SND=m - - # USB devices - # - CONFIG_SND_USB_AUDIO=m - -And finally: - - # V4L USB devices - # - CONFIG_USB_ZC0301=m - - -6. Module loading -================= -To use the driver, it is necessary to load the "zc0301" module into memory -after every other module required: "videodev", "v4l2_common", "compat_ioctl32", -"usbcore" and, depending on the USB host controller you have, "ehci-hcd", -"uhci-hcd" or "ohci-hcd". - -Loading can be done as shown below: - - [root@localhost home]# modprobe zc0301 - -At this point the devices should be recognized. You can invoke "dmesg" to -analyze kernel messages and verify that the loading process has gone well: - - [user@localhost home]$ dmesg - - -7. Module parameters -==================== -Module parameters are listed below: -------------------------------------------------------------------------------- -Name: video_nr -Type: short array (min = 0, max = 64) -Syntax: <-1|n[,...]> -Description: Specify V4L2 minor mode number: - -1 = use next available - n = use minor number n - You can specify up to 64 cameras this way. - For example: - video_nr=-1,2,-1 would assign minor number 2 to the second - registered camera and use auto for the first one and for every - other camera. -Default: -1 -------------------------------------------------------------------------------- -Name: force_munmap -Type: bool array (min = 0, max = 64) -Syntax: <0|1[,...]> -Description: Force the application to unmap previously mapped buffer memory - before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not - all the applications support this feature. This parameter is - specific for each detected camera. - 0 = do not force memory unmapping - 1 = force memory unmapping (save memory) -Default: 0 -------------------------------------------------------------------------------- -Name: frame_timeout -Type: uint array (min = 0, max = 64) -Syntax: -Description: Timeout for a video frame in seconds. This parameter is - specific for each detected camera. This parameter can be - changed at runtime thanks to the /sys filesystem interface. -Default: 2 -------------------------------------------------------------------------------- -Name: debug -Type: ushort -Syntax: -Description: Debugging information level, from 0 to 3: - 0 = none (use carefully) - 1 = critical errors - 2 = significant information - 3 = more verbose messages - Level 3 is useful for testing only, when only one device - is used at the same time. It also shows some information - about the hardware being detected. This module parameter can be - changed at runtime thanks to the /sys filesystem interface. -Default: 2 -------------------------------------------------------------------------------- - - -8. Supported devices -==================== -None of the names of the companies as well as their products will be mentioned -here. They have never collaborated with the author, so no advertising. - -From the point of view of a driver, what unambiguously identify a device are -its vendor and product USB identifiers. Below is a list of known identifiers of -devices mounting the ZC0301 Image Processor and Control Chips: - -Vendor ID Product ID ---------- ---------- -0x041e 0x4017 -0x041e 0x401c -0x041e 0x401e -0x041e 0x401f -0x041e 0x4022 -0x041e 0x4034 -0x041e 0x4035 -0x041e 0x4036 -0x041e 0x403a -0x0458 0x7007 -0x0458 0x700c -0x0458 0x700f -0x046d 0x08ae -0x055f 0xd003 -0x055f 0xd004 -0x0ac8 0x0301 -0x0ac8 0x301b -0x0ac8 0x303b -0x10fd 0x0128 -0x10fd 0x8050 -0x10fd 0x804e - -The list above does not imply that all those devices work with this driver: up -until now only the ones that mount the following image sensors are supported; -kernel messages will always tell you whether this is the case: - -Model Manufacturer ------ ------------ -PAS202BCB PixArt Imaging, Inc. -PB-0330 Photobit Corporation - - -9. Notes for V4L2 application developers -======================================== -This driver follows the V4L2 API specifications. In particular, it enforces two -rules: - -- exactly one I/O method, either "mmap" or "read", is associated with each -file descriptor. Once it is selected, the application must close and reopen the -device to switch to the other I/O method; - -- although it is not mandatory, previously mapped buffer memory should always -be unmapped before calling any "VIDIOC_S_CROP" or "VIDIOC_S_FMT" ioctl's. -The same number of buffers as before will be allocated again to match the size -of the new video frames, so you have to map the buffers again before any I/O -attempts on them. - - -10. Contact information -======================= -The author may be contacted by e-mail at . - -GPG/PGP encrypted e-mail's are accepted. The GPG key ID of the author is -'FCE635A4'; the public 1024-bit key should be available at any keyserver; -the fingerprint is: '88E8 F32F 7244 68BA 3958 5D40 99DA 5D2A FCE6 35A4'. - - -11. Credits -=========== -- Information about the chip internals needed to enable the I2C protocol have - been taken from the documentation of the ZC030x Video4Linux1 driver written - by Andrew Birkett ; -- The initialization values of the ZC0301 controller connected to the PAS202BCB - and PB-0330 image sensors have been taken from the SPCA5XX driver maintained - by Michel Xhaard ; -- Stanislav Lechev donated one camera. From c3075388881fbafda3b0991fbefc6a5eef4d483c Mon Sep 17 00:00:00 2001 From: Jacob Schloss Date: Sun, 9 Dec 2012 20:18:25 -0300 Subject: [PATCH 0282/4607] [media] gspca_kinect: add Kinect for Windows USB id Add the USB ID for the Kinect for Windows RGB camera so it can be used with the gspca_kinect driver. Signed-off-by: Jacob Schloss Signed-off-by: Antonio Ospite Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/kinect.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c index 40ad6687ee5d..3773a8a745df 100644 --- a/drivers/media/usb/gspca/kinect.c +++ b/drivers/media/usb/gspca/kinect.c @@ -381,6 +381,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x045e, 0x02ae)}, + {USB_DEVICE(0x045e, 0x02bf)}, {} }; From e52ec68015b81ccd5f0950d56328b78f43ae2985 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 12 Dec 2012 05:58:12 -0300 Subject: [PATCH 0283/4607] [media] gspca: Use module_usb_driver macro module_usb_driver eliminates a lot of boilerplate by replacing module_init() and module_exit() calls. Signed-off-by: Sachin Kamat Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/jl2005bcd.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index 62ba80d9b998..fdaeeb14453f 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -536,20 +536,4 @@ static struct usb_driver sd_driver = { #endif }; -/* -- module insert / remove -- */ -static int __init sd_mod_init(void) -{ - int ret; - - ret = usb_register(&sd_driver); - if (ret < 0) - return ret; - return 0; -} -static void __exit sd_mod_exit(void) -{ - usb_deregister(&sd_driver); -} - -module_init(sd_mod_init); -module_exit(sd_mod_exit); +module_usb_driver(sd_driver); From d83fcb793b8a17d2ea29e3450295094c12693f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Andr=C3=A9n?= Date: Mon, 10 Dec 2012 16:35:19 -0300 Subject: [PATCH 0284/4607] [media] gspca_stv06xx: Disable flip controls for vv6410 sensor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable the hardware VFLIP and HFLIP controls for now as we lack a mechanism to adjust the frame offset, thus rending a bayerimage not compliant with the announced format. Signed-off-by: Erik Andrén Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c index cbb153180d59..e95fa8997d22 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c @@ -98,11 +98,14 @@ static int vv6410_init_controls(struct sd *sd) { struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_handler_init(hdl, 2); + /* Disable the hardware VFLIP and HFLIP as we currently lack a + mechanism to adjust the image offset in such a way that + we don't need to renegotiate the announced format */ + /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ + /* V4L2_CID_HFLIP, 0, 1, 1, 0); */ + /* v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, */ + /* V4L2_CID_VFLIP, 0, 1, 1, 0); */ v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, V4L2_CID_EXPOSURE, 0, 32768, 1, 20000); v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, From 8547fd18e9009ea0f010c5ec7463d61efa72b555 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 21 Dec 2012 11:01:48 -0300 Subject: [PATCH 0285/4607] [media] gspca_t613: Fix compiling with GSPCA_DEBUG defined Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/t613.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/gspca/t613.c b/drivers/media/usb/gspca/t613.c index 8bc6c3ceec2c..b92d4ef2de6e 100644 --- a/drivers/media/usb/gspca/t613.c +++ b/drivers/media/usb/gspca/t613.c @@ -494,7 +494,7 @@ static void setcolors(struct gspca_dev *gspca_dev, s32 val) static void setgamma(struct gspca_dev *gspca_dev, s32 val) { - PDEBUG(D_CONF, "Gamma: %d", sd->gamma); + PDEBUG(D_CONF, "Gamma: %d", val); reg_w_ixbuf(gspca_dev, 0x90, gamma_table[val], sizeof gamma_table[0]); } From 18fa0d36ad5b451d835ca79301265649334214c0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 21 Dec 2012 08:08:47 -0300 Subject: [PATCH 0286/4607] [media] gspca_sonixb: Properly wait between i2c writes We must wait for the previous i2c write to complete before starting a new one. Sofar we were getting away with this, but it seems that some parts of the usb-subsystem has been sped up making us go to fast :) This fixes streaming on sn9c103 based cams not working with an "i2c_w error" error. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/sonixb.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c index 70511d5f9538..1220340e7602 100644 --- a/drivers/media/usb/gspca/sonixb.c +++ b/drivers/media/usb/gspca/sonixb.c @@ -496,7 +496,7 @@ static void reg_w(struct gspca_dev *gspca_dev, } } -static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) +static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buf) { int retry = 60; @@ -504,16 +504,19 @@ static void i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) return; /* is i2c ready */ - reg_w(gspca_dev, 0x08, buffer, 8); + reg_w(gspca_dev, 0x08, buf, 8); while (retry--) { if (gspca_dev->usb_err < 0) return; - msleep(10); + msleep(1); reg_r(gspca_dev, 0x08); if (gspca_dev->usb_buf[0] & 0x04) { if (gspca_dev->usb_buf[0] & 0x08) { dev_err(gspca_dev->v4l2_dev.dev, - "i2c write error\n"); + "i2c error writing %02x %02x %02x %02x" + " %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); gspca_dev->usb_err = -EIO; } return; @@ -530,7 +533,7 @@ static void i2c_w_vector(struct gspca_dev *gspca_dev, for (;;) { if (gspca_dev->usb_err < 0) return; - reg_w(gspca_dev, 0x08, *buffer, 8); + i2c_w(gspca_dev, *buffer); len -= 8; if (len <= 0) break; From 2bffebc1e6dcd49e5e92b348c9a1c6bcea8c2f5e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 21 Dec 2012 11:17:42 -0300 Subject: [PATCH 0287/4607] [media] gspca_sonixj: Add a small delay after i2c_w1 We already have the same delay in i2c_w8, but it was missing from i2c_w1, adding this delay fixes the Microsoft VX-3000 camera often (but not always) streaming video data with a very green-ish tint. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/gspca/sonixj.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c index 5a86047b846f..36307a9028a9 100644 --- a/drivers/media/usb/gspca/sonixj.c +++ b/drivers/media/usb/gspca/sonixj.c @@ -1550,6 +1550,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) 0, gspca_dev->usb_buf, 8, 500); + msleep(2); if (ret < 0) { pr_err("i2c_w1 err %d\n", ret); gspca_dev->usb_err = ret; From 47800bc43eda104d49ef51aefe549f90c00922d0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 3 Dec 2012 06:24:32 -0300 Subject: [PATCH 0288/4607] [media] s5p-fimc: Improved pipeline try format routine Make the pipeline try format routine more generic to support any number of subdevs in the pipeline, rather than hard coding it for only a sensor, MIPI-CSIS and FIMC subdevs and the FIMC video node. Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/s5p-fimc/fimc-capture.c | 93 ++++++++++++------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 50c0da9c788b..95e6a7820b5e 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -793,6 +793,21 @@ static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv, return 0; } +static struct media_entity *fimc_pipeline_get_head(struct media_entity *me) +{ + struct media_pad *pad = &me->pads[0]; + + while (!(pad->flags & MEDIA_PAD_FL_SOURCE)) { + pad = media_entity_remote_source(pad); + if (!pad) + break; + me = pad->entity; + pad = &me->pads[0]; + } + + return me; +} + /** * fimc_pipeline_try_format - negotiate and/or set formats at pipeline * elements @@ -808,19 +823,23 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, { struct fimc_dev *fimc = ctx->fimc_dev; struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; - struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS]; struct v4l2_subdev_format sfmt; struct v4l2_mbus_framefmt *mf = &sfmt.format; - struct fimc_fmt *ffmt = NULL; - int ret, i = 0; + struct media_entity *me; + struct fimc_fmt *ffmt; + struct media_pad *pad; + int ret, i = 1; + u32 fcc; if (WARN_ON(!sd || !tfmt)) return -EINVAL; memset(&sfmt, 0, sizeof(sfmt)); sfmt.format = *tfmt; - sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY; + + me = fimc_pipeline_get_head(&sd->entity); + while (1) { ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL, FMT_FLAGS_CAM, i++); @@ -833,40 +852,52 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, } mf->code = tfmt->code = ffmt->mbus_code; - ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); - if (ret) - return ret; - if (mf->code != tfmt->code) { - mf->code = 0; - continue; - } - if (mf->width != tfmt->width || mf->height != tfmt->height) { - u32 fcc = ffmt->fourcc; - tfmt->width = mf->width; - tfmt->height = mf->height; - ffmt = fimc_capture_try_format(ctx, - &tfmt->width, &tfmt->height, - NULL, &fcc, FIMC_SD_PAD_SOURCE); - if (ffmt && ffmt->mbus_code) - mf->code = ffmt->mbus_code; - if (mf->width != tfmt->width || - mf->height != tfmt->height) - continue; - tfmt->code = mf->code; - } - if (csis) - ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt); + /* set format on all pipeline subdevs */ + while (me != &fimc->vid_cap.subdev.entity) { + sd = media_entity_to_v4l2_subdev(me); - if (mf->code == tfmt->code && - mf->width == tfmt->width && mf->height == tfmt->height) - break; + sfmt.pad = 0; + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt); + if (ret) + return ret; + + if (me->pads[0].flags & MEDIA_PAD_FL_SINK) { + sfmt.pad = me->num_pads - 1; + mf->code = tfmt->code; + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, + &sfmt); + if (ret) + return ret; + } + + pad = media_entity_remote_source(&me->pads[sfmt.pad]); + if (!pad) + return -EINVAL; + me = pad->entity; + } + + if (mf->code != tfmt->code) + continue; + + fcc = ffmt->fourcc; + tfmt->width = mf->width; + tfmt->height = mf->height; + ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, + NULL, &fcc, FIMC_SD_PAD_SINK); + ffmt = fimc_capture_try_format(ctx, &tfmt->width, &tfmt->height, + NULL, &fcc, FIMC_SD_PAD_SOURCE); + if (ffmt && ffmt->mbus_code) + mf->code = ffmt->mbus_code; + if (mf->width != tfmt->width || mf->height != tfmt->height) + continue; + tfmt->code = mf->code; + break; } if (fmt_id && ffmt) *fmt_id = ffmt; *tfmt = *mf; - dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt); return 0; } From c1819fc5dbe4582575965e587ba61184ab9b45fa Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Tue, 21 Aug 2012 05:27:59 -0300 Subject: [PATCH 0289/4607] [media] davinci: vpss: dm365: enable ISP registers enable the clocks required for VPFE to work in PCCR register, and enbale ISIF out on BCR to get the correct operation from ISIF. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpss.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index 146e4b01ac17..1c96ce8269d4 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -51,7 +51,18 @@ MODULE_AUTHOR("Texas Instruments"); /* VENCINT - vpss_int8 */ #define DM355_VPSSBL_EVTSEL_DEFAULT 0x4 -#define DM365_ISP5_PCCR 0x04 +#define DM365_ISP5_PCCR 0x04 +#define DM365_ISP5_PCCR_BL_CLK_ENABLE BIT(0) +#define DM365_ISP5_PCCR_ISIF_CLK_ENABLE BIT(1) +#define DM365_ISP5_PCCR_H3A_CLK_ENABLE BIT(2) +#define DM365_ISP5_PCCR_RSZ_CLK_ENABLE BIT(3) +#define DM365_ISP5_PCCR_IPIPE_CLK_ENABLE BIT(4) +#define DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE BIT(5) +#define DM365_ISP5_PCCR_RSV BIT(6) + +#define DM365_ISP5_BCR 0x08 +#define DM365_ISP5_BCR_ISIF_OUT_ENABLE BIT(1) + #define DM365_ISP5_INTSEL1 0x10 #define DM365_ISP5_INTSEL2 0x14 #define DM365_ISP5_INTSEL3 0x18 @@ -426,6 +437,16 @@ static int __devinit vpss_probe(struct platform_device *pdev) oper_cfg.hw_ops.enable_clock = dm365_enable_clock; oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source; /* Setup vpss interrupts */ + isp5_write((isp5_read(DM365_ISP5_PCCR) | + DM365_ISP5_PCCR_BL_CLK_ENABLE | + DM365_ISP5_PCCR_ISIF_CLK_ENABLE | + DM365_ISP5_PCCR_H3A_CLK_ENABLE | + DM365_ISP5_PCCR_RSZ_CLK_ENABLE | + DM365_ISP5_PCCR_IPIPE_CLK_ENABLE | + DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE | + DM365_ISP5_PCCR_RSV), DM365_ISP5_PCCR); + isp5_write((isp5_read(DM365_ISP5_BCR) | + DM365_ISP5_BCR_ISIF_OUT_ENABLE), DM365_ISP5_BCR); isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1); isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2); isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3); From 3de939419cfaf613b87cf89adc5dda97ca1720e0 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Tue, 21 Aug 2012 05:50:27 -0300 Subject: [PATCH 0290/4607] [media] davinci: vpss: dm365: set vpss clk ctrl request_mem_region for VPSS_CLK_CTRL register and ioremap. and enable clocks appropriately. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpss.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index 1c96ce8269d4..e4ad63d15265 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -69,6 +69,11 @@ MODULE_AUTHOR("Texas Instruments"); #define DM365_ISP5_CCDCMUX 0x20 #define DM365_ISP5_PG_FRAME_SIZE 0x28 #define DM365_VPBE_CLK_CTRL 0x00 + +#define VPSS_CLK_CTRL 0x01c40044 +#define VPSS_CLK_CTRL_VENCCLKEN BIT(3) +#define VPSS_CLK_CTRL_DACCLKEN BIT(4) + /* * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1, * AF - vpss_int3 @@ -112,6 +117,7 @@ struct vpss_hw_ops { struct vpss_oper_config { __iomem void *vpss_regs_base0; __iomem void *vpss_regs_base1; + resource_size_t *vpss_regs_base2; enum vpss_platform_type platform; spinlock_t vpss_lock; struct vpss_hw_ops hw_ops; @@ -492,11 +498,20 @@ static struct platform_driver vpss_driver = { static void vpss_exit(void) { + iounmap(oper_cfg.vpss_regs_base2); + release_mem_region(VPSS_CLK_CTRL, 4); platform_driver_unregister(&vpss_driver); } static int __init vpss_init(void) { + if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control")) + return -EBUSY; + + oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); + writel(VPSS_CLK_CTRL_VENCCLKEN | + VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); + return platform_driver_register(&vpss_driver); } subsys_initcall(vpss_init); From d31c100250bb07b6d317e1cfc44614b45a16154a Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Tue, 21 Aug 2012 05:56:21 -0300 Subject: [PATCH 0291/4607] [media] davinci/vpss: add helper functions for setting hw params Add vpss helper functions to be used in the main driver for setting hardware parameters. Add interface functions to set sync polarity, interrupt completion and pageframe size in vpss to be used by the main driver. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpss.c | 32 +++++++++++++++++++++++++++ include/media/davinci/vpss.h | 16 ++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c index e4ad63d15265..d945f94053a8 100644 --- a/drivers/media/platform/davinci/vpss.c +++ b/drivers/media/platform/davinci/vpss.c @@ -111,6 +111,12 @@ struct vpss_hw_ops { void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); /* clear wbl overflow bit */ int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); + /* set sync polarity */ + void (*set_sync_pol)(struct vpss_sync_pol); + /* set the PG_FRAME_SIZE register*/ + void (*set_pg_frame_size)(struct vpss_pg_frame_size); + /* check and clear interrupt if occured */ + int (*dma_complete_interrupt)(void); }; /* vpss configuration */ @@ -175,6 +181,14 @@ static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); } +int vpss_dma_complete_interrupt(void) +{ + if (!oper_cfg.hw_ops.dma_complete_interrupt) + return 2; + return oper_cfg.hw_ops.dma_complete_interrupt(); +} +EXPORT_SYMBOL(vpss_dma_complete_interrupt); + int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) { if (!oper_cfg.hw_ops.select_ccdc_source) @@ -200,6 +214,15 @@ static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) return 0; } +void vpss_set_sync_pol(struct vpss_sync_pol sync) +{ + if (!oper_cfg.hw_ops.set_sync_pol) + return; + + oper_cfg.hw_ops.set_sync_pol(sync); +} +EXPORT_SYMBOL(vpss_set_sync_pol); + int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) { if (!oper_cfg.hw_ops.clear_wbl_overflow) @@ -365,6 +388,15 @@ void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync) } EXPORT_SYMBOL(dm365_vpss_set_sync_pol); +void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) +{ + if (!oper_cfg.hw_ops.set_pg_frame_size) + return; + + oper_cfg.hw_ops.set_pg_frame_size(frame_size); +} +EXPORT_SYMBOL(vpss_set_pg_frame_size); + void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) { int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16; diff --git a/include/media/davinci/vpss.h b/include/media/davinci/vpss.h index b586495bcd53..153473daaa32 100644 --- a/include/media/davinci/vpss.h +++ b/include/media/davinci/vpss.h @@ -105,4 +105,20 @@ enum vpss_wbl_sel { }; /* clear wbl overflow flag for DM6446 */ int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel); + +/* set sync polarity*/ +void vpss_set_sync_pol(struct vpss_sync_pol sync); +/* set the PG_FRAME_SIZE register */ +void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size); +/* + * vpss_check_and_clear_interrupt - check and clear interrupt + * @irq - common enumerator for IRQ + * + * Following return values used:- + * 0 - interrupt occurred and cleared + * 1 - interrupt not occurred + * 2 - interrupt status not available + */ +int vpss_dma_complete_interrupt(void); + #endif From 91825400ba9a8a081d0efe7ac21e55d9fd6545f9 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:02:02 -0300 Subject: [PATCH 0292/4607] [media] davinci: vpfe: add v4l2 capture driver with media interface Add the vpfe capture driver which implements media controller interface. The driver supports the DM365 sub ip units for capture namely - ISIF, IPIPE, IPIPEIF, Resizer. This file represents the main driver which does isr registration, v4l2 device registration, media registration and platform driver registrations. It calls the appropriate subdevs from here to create subdevices and media entities. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/davinci_vpfe/davinci_vpfe_user.h | 1290 +++++++++++++++++ drivers/staging/media/davinci_vpfe/vpfe.h | 86 ++ .../media/davinci_vpfe/vpfe_mc_capture.c | 740 ++++++++++ .../media/davinci_vpfe/vpfe_mc_capture.h | 97 ++ 4 files changed, 2213 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h create mode 100644 drivers/staging/media/davinci_vpfe/vpfe.h create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h diff --git a/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h new file mode 100644 index 000000000000..7b7e7b26c1e8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/davinci_vpfe_user.h @@ -0,0 +1,1290 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_USER_H +#define _DAVINCI_VPFE_USER_H + +#include +#include + +/* + * Private IOCTL + * + * VIDIOC_VPFE_ISIF_S_RAW_PARAMS: Set raw params in isif + * VIDIOC_VPFE_ISIF_G_RAW_PARAMS: Get raw params from isif + * VIDIOC_VPFE_PRV_S_CONFIG: Set ipipe engine configuration + * VIDIOC_VPFE_PRV_G_CONFIG: Get ipipe engine configuration + * VIDIOC_VPFE_RSZ_S_CONFIG: Set resizer engine configuration + * VIDIOC_VPFE_RSZ_G_CONFIG: Get resizer engine configuration + */ + +#define VIDIOC_VPFE_ISIF_S_RAW_PARAMS \ + _IOW('V', BASE_VIDIOC_PRIVATE + 1, struct vpfe_isif_raw_config) +#define VIDIOC_VPFE_ISIF_G_RAW_PARAMS \ + _IOR('V', BASE_VIDIOC_PRIVATE + 2, struct vpfe_isif_raw_config) +#define VIDIOC_VPFE_IPIPE_S_CONFIG \ + _IOWR('P', BASE_VIDIOC_PRIVATE + 3, struct vpfe_ipipe_config) +#define VIDIOC_VPFE_IPIPE_G_CONFIG \ + _IOWR('P', BASE_VIDIOC_PRIVATE + 4, struct vpfe_ipipe_config) +#define VIDIOC_VPFE_RSZ_S_CONFIG \ + _IOWR('R', BASE_VIDIOC_PRIVATE + 5, struct vpfe_rsz_config) +#define VIDIOC_VPFE_RSZ_G_CONFIG \ + _IOWR('R', BASE_VIDIOC_PRIVATE + 6, struct vpfe_rsz_config) + +/* + * Private Control's for ISIF + */ +#define VPFE_ISIF_CID_CRGAIN (V4L2_CID_USER_BASE | 0xa001) +#define VPFE_ISIF_CID_CGRGAIN (V4L2_CID_USER_BASE | 0xa002) +#define VPFE_ISIF_CID_CGBGAIN (V4L2_CID_USER_BASE | 0xa003) +#define VPFE_ISIF_CID_CBGAIN (V4L2_CID_USER_BASE | 0xa004) +#define VPFE_ISIF_CID_GAIN_OFFSET (V4L2_CID_USER_BASE | 0xa005) + +/* + * Private Control's for ISIF and IPIPEIF + */ +#define VPFE_CID_DPCM_PREDICTOR (V4L2_CID_USER_BASE | 0xa006) + +/************************************************************************ + * Vertical Defect Correction parameters + ***********************************************************************/ + +/** + * vertical defect correction methods + */ +enum vpfe_isif_vdfc_corr_mode { + /* Defect level subtraction. Just fed through if saturating */ + VPFE_ISIF_VDFC_NORMAL, + /** + * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2 + * if data saturating + */ + VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT, + /* Horizontal interpolation (((i-2)+(i+2))/2) */ + VPFE_ISIF_VDFC_HORZ_INTERPOL +}; + +/** + * Max Size of the Vertical Defect Correction table + */ +#define VPFE_ISIF_VDFC_TABLE_SIZE 8 + +/** + * Values used for shifting up the vdfc defect level + */ +enum vpfe_isif_vdfc_shift { + /* No Shift */ + VPFE_ISIF_VDFC_NO_SHIFT, + /* Shift by 1 bit */ + VPFE_ISIF_VDFC_SHIFT_1, + /* Shift by 2 bit */ + VPFE_ISIF_VDFC_SHIFT_2, + /* Shift by 3 bit */ + VPFE_ISIF_VDFC_SHIFT_3, + /* Shift by 4 bit */ + VPFE_ISIF_VDFC_SHIFT_4 +}; + +/** + * Defect Correction (DFC) table entry + */ +struct vpfe_isif_vdfc_entry { + /* vertical position of defect */ + unsigned short pos_vert; + /* horizontal position of defect */ + unsigned short pos_horz; + /** + * Defect level of Vertical line defect position. This is subtracted + * from the data at the defect position + */ + unsigned char level_at_pos; + /** + * Defect level of the pixels upper than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_up_pixels; + /** + * Defect level of the pixels lower than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_low_pixels; +}; + +/** + * Structure for Defect Correction (DFC) parameter + */ +struct vpfe_isif_dfc { + /* enable vertical defect correction */ + unsigned char en; + /* Correction methods */ + enum vpfe_isif_vdfc_corr_mode corr_mode; + /** + * 0 - whole line corrected, 1 - not + * pixels upper than the defect + */ + unsigned char corr_whole_line; + /** + * defect level shift value. level_at_pos, level_upper_pos, + * and level_lower_pos can be shifted up by this value + */ + enum vpfe_isif_vdfc_shift def_level_shift; + /* defect saturation level */ + unsigned short def_sat_level; + /* number of vertical defects. Max is VPFE_ISIF_VDFC_TABLE_SIZE */ + short num_vdefects; + /* VDFC table ptr */ + struct vpfe_isif_vdfc_entry table[VPFE_ISIF_VDFC_TABLE_SIZE]; +}; + +/************************************************************************ +* Digital/Black clamp or DC Subtract parameters +************************************************************************/ +/** + * Horizontal Black Clamp modes + */ +enum vpfe_isif_horz_bc_mode { + /** + * Horizontal clamp disabled. Only vertical clamp + * value is subtracted + */ + VPFE_ISIF_HORZ_BC_DISABLE, + /** + * Horizontal clamp value is calculated and subtracted + * from image data along with vertical clamp value + */ + VPFE_ISIF_HORZ_BC_CLAMP_CALC_ENABLED, + /** + * Horizontal clamp value calculated from previous image + * is subtracted from image data along with vertical clamp + * value. How the horizontal clamp value for the first image + * is calculated in this case ??? + */ + VPFE_ISIF_HORZ_BC_CLAMP_NOT_UPDATED +}; + +/** + * Base window selection for Horizontal Black Clamp calculations + */ +enum vpfe_isif_horz_bc_base_win_sel { + /* Select Most left window for bc calculation */ + VPFE_ISIF_SEL_MOST_LEFT_WIN, + + /* Select Most right window for bc calculation */ + VPFE_ISIF_SEL_MOST_RIGHT_WIN, +}; + +/* Size of window in horizontal direction for horizontal bc */ +enum vpfe_isif_horz_bc_sz_h { + VPFE_ISIF_HORZ_BC_SZ_H_2PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_4PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_8PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_16PIXELS +}; + +/* Size of window in vertcal direction for vertical bc */ +enum vpfe_isif_horz_bc_sz_v { + VPFE_ISIF_HORZ_BC_SZ_H_32PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_64PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_128PIXELS, + VPFE_ISIF_HORZ_BC_SZ_H_256PIXELS +}; + +/** + * Structure for Horizontal Black Clamp config params + */ +struct vpfe_isif_horz_bclamp { + /* horizontal clamp mode */ + enum vpfe_isif_horz_bc_mode mode; + /** + * pixel value limit enable. + * 0 - limit disabled + * 1 - pixel value limited to 1023 + */ + unsigned char clamp_pix_limit; + /** + * Select most left or right window for clamp val + * calculation + */ + enum vpfe_isif_horz_bc_base_win_sel base_win_sel_calc; + /* Window count per color for calculation. range 1-32 */ + unsigned char win_count_calc; + /* Window start position - horizontal for calculation. 0 - 8191 */ + unsigned short win_start_h_calc; + /* Window start position - vertical for calculation 0 - 8191 */ + unsigned short win_start_v_calc; + /* Width of the sample window in pixels for calculation */ + enum vpfe_isif_horz_bc_sz_h win_h_sz_calc; + /* Height of the sample window in pixels for calculation */ + enum vpfe_isif_horz_bc_sz_v win_v_sz_calc; +}; + +/** + * Black Clamp vertical reset values + */ +enum vpfe_isif_vert_bc_reset_val_sel { + /* Reset value used is the clamp value calculated */ + VPFE_ISIF_VERT_BC_USE_HORZ_CLAMP_VAL, + /* Reset value used is reset_clamp_val configured */ + VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL, + /* No update, previous image value is used */ + VPFE_ISIF_VERT_BC_NO_UPDATE +}; + +enum vpfe_isif_vert_bc_sz_h { + VPFE_ISIF_VERT_BC_SZ_H_2PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_4PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_8PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_16PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_32PIXELS, + VPFE_ISIF_VERT_BC_SZ_H_64PIXELS +}; + +/** + * Structure for Vertical Black Clamp configuration params + */ +struct vpfe_isif_vert_bclamp { + /* Reset value selection for vertical clamp calculation */ + enum vpfe_isif_vert_bc_reset_val_sel reset_val_sel; + /* U12 value if reset_sel = ISIF_BC_VERT_USE_CONFIG_CLAMP_VAL */ + unsigned short reset_clamp_val; + /** + * U8Q8. Line average coefficient used in vertical clamp + * calculation + */ + unsigned char line_ave_coef; + /* Width in pixels of the optical black region used for calculation. */ + enum vpfe_isif_vert_bc_sz_h ob_h_sz_calc; + /* Height of the optical black region for calculation */ + unsigned short ob_v_sz_calc; + /* Optical black region start position - horizontal. 0 - 8191 */ + unsigned short ob_start_h; + /* Optical black region start position - vertical 0 - 8191 */ + unsigned short ob_start_v; +}; + +/** + * Structure for Black Clamp configuration params + */ +struct vpfe_isif_black_clamp { + /** + * this offset value is added irrespective of the clamp + * enable status. S13 + */ + unsigned short dc_offset; + /** + * Enable black/digital clamp value to be subtracted + * from the image data + */ + unsigned char en; + /** + * black clamp mode. same/separate clamp for 4 colors + * 0 - disable - same clamp value for all colors + * 1 - clamp value calculated separately for all colors + */ + unsigned char bc_mode_color; + /* Vertical start position for bc subtraction */ + unsigned short vert_start_sub; + /* Black clamp for horizontal direction */ + struct vpfe_isif_horz_bclamp horz; + /* Black clamp for vertical direction */ + struct vpfe_isif_vert_bclamp vert; +}; + +/************************************************************************* +** Color Space Conversion (CSC) +*************************************************************************/ +/** + * Number of Coefficient values used for CSC + */ +#define VPFE_ISIF_CSC_NUM_COEFF 16 + +struct float_8_bit { + /* 8 bit integer part */ + __u8 integer; + /* 8 bit decimal part */ + __u8 decimal; +}; + +struct float_16_bit { + /* 16 bit integer part */ + __u16 integer; + /* 16 bit decimal part */ + __u16 decimal; +}; + +/************************************************************************* +** Color Space Conversion parameters +*************************************************************************/ +/** + * Structure used for CSC config params + */ +struct vpfe_isif_color_space_conv { + /* Enable color space conversion */ + unsigned char en; + /** + * csc coefficient table. S8Q5, M00 at index 0, M01 at index 1, and + * so forth + */ + struct float_8_bit coeff[VPFE_ISIF_CSC_NUM_COEFF]; +}; + +enum vpfe_isif_datasft { + /* No Shift */ + VPFE_ISIF_NO_SHIFT, + /* 1 bit Shift */ + VPFE_ISIF_1BIT_SHIFT, + /* 2 bit Shift */ + VPFE_ISIF_2BIT_SHIFT, + /* 3 bit Shift */ + VPFE_ISIF_3BIT_SHIFT, + /* 4 bit Shift */ + VPFE_ISIF_4BIT_SHIFT, + /* 5 bit Shift */ + VPFE_ISIF_5BIT_SHIFT, + /* 6 bit Shift */ + VPFE_ISIF_6BIT_SHIFT +}; + +#define VPFE_ISIF_LINEAR_TAB_SIZE 192 +/************************************************************************* +** Linearization parameters +*************************************************************************/ +/** + * Structure for Sensor data linearization + */ +struct vpfe_isif_linearize { + /* Enable or Disable linearization of data */ + unsigned char en; + /* Shift value applied */ + enum vpfe_isif_datasft corr_shft; + /* scale factor applied U11Q10 */ + struct float_16_bit scale_fact; + /* Size of the linear table */ + unsigned short table[VPFE_ISIF_LINEAR_TAB_SIZE]; +}; + +/************************************************************************* +** ISIF Raw configuration parameters +*************************************************************************/ +enum vpfe_isif_fmt_mode { + VPFE_ISIF_SPLIT, + VPFE_ISIF_COMBINE +}; + +enum vpfe_isif_lnum { + VPFE_ISIF_1LINE, + VPFE_ISIF_2LINES, + VPFE_ISIF_3LINES, + VPFE_ISIF_4LINES +}; + +enum vpfe_isif_line { + VPFE_ISIF_1STLINE, + VPFE_ISIF_2NDLINE, + VPFE_ISIF_3RDLINE, + VPFE_ISIF_4THLINE +}; + +struct vpfe_isif_fmtplen { + /** + * number of program entries for SET0, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen0; + /** + * number of program entries for SET1, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen1; + /** + * number of program entries for SET2, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen2; + /** + * number of program entries for SET3, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + unsigned short plen3; +}; + +struct vpfe_isif_fmt_cfg { + /* Split or combine or line alternate */ + enum vpfe_isif_fmt_mode fmtmode; + /* enable or disable line alternating mode */ + unsigned char ln_alter_en; + /* Split/combine line number */ + enum vpfe_isif_lnum lnum; + /* Address increment Range 1 - 16 */ + unsigned int addrinc; +}; + +struct vpfe_isif_fmt_addr_ptr { + /* Initial address */ + unsigned int init_addr; + /* output line number */ + enum vpfe_isif_line out_line; +}; + +struct vpfe_isif_fmtpgm_ap { + /* program address pointer */ + unsigned char pgm_aptr; + /* program address increment or decrement */ + unsigned char pgmupdt; +}; + +struct vpfe_isif_data_formatter { + /* Enable/Disable data formatter */ + unsigned char en; + /* data formatter configuration */ + struct vpfe_isif_fmt_cfg cfg; + /* Formatter program entries length */ + struct vpfe_isif_fmtplen plen; + /* first pixel in a line fed to formatter */ + unsigned short fmtrlen; + /* HD interval for output line. Only valid when split line */ + unsigned short fmthcnt; + /* formatter address pointers */ + struct vpfe_isif_fmt_addr_ptr fmtaddr_ptr[16]; + /* program enable/disable */ + unsigned char pgm_en[32]; + /* program address pointers */ + struct vpfe_isif_fmtpgm_ap fmtpgm_ap[32]; +}; + +struct vpfe_isif_df_csc { + /* Color Space Conversion configuration, 0 - csc, 1 - df */ + unsigned int df_or_csc; + /* csc configuration valid if df_or_csc is 0 */ + struct vpfe_isif_color_space_conv csc; + /* data formatter configuration valid if df_or_csc is 1 */ + struct vpfe_isif_data_formatter df; + /* start pixel in a line at the input */ + unsigned int start_pix; + /* number of pixels in input line */ + unsigned int num_pixels; + /* start line at the input */ + unsigned int start_line; + /* number of lines at the input */ + unsigned int num_lines; +}; + +struct vpfe_isif_gain_offsets_adj { + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char gain_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char gain_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char gain_h3a_en; + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char offset_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char offset_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char offset_h3a_en; +}; + +struct vpfe_isif_cul { + /* Horizontal Cull pattern for odd lines */ + unsigned char hcpat_odd; + /* Horizontal Cull pattern for even lines */ + unsigned char hcpat_even; + /* Vertical Cull pattern */ + unsigned char vcpat; + /* Enable or disable lpf. Apply when cull is enabled */ + unsigned char en_lpf; +}; + +/* all the stuff in this struct will be provided by userland */ +struct vpfe_isif_raw_config { + /* Linearization parameters for image sensor data input */ + struct vpfe_isif_linearize linearize; + /* Data formatter or CSC */ + struct vpfe_isif_df_csc df_csc; + /* Defect Pixel Correction (DFC) confguration */ + struct vpfe_isif_dfc dfc; + /* Black/Digital Clamp configuration */ + struct vpfe_isif_black_clamp bclamp; + /* Gain, offset adjustments */ + struct vpfe_isif_gain_offsets_adj gain_offset; + /* Culling */ + struct vpfe_isif_cul culling; + /* horizontal offset for Gain/LSC/DFC */ + unsigned short horz_offset; + /* vertical offset for Gain/LSC/DFC */ + unsigned short vert_offset; +}; + +/********************************************************************** + IPIPE API Structures +**********************************************************************/ + +/* IPIPE module configurations */ + +/* IPIPE input configuration */ +#define VPFE_IPIPE_INPUT_CONFIG (1 << 0) +/* LUT based Defect Pixel Correction */ +#define VPFE_IPIPE_LUTDPC (1 << 1) +/* On the fly (OTF) Defect Pixel Correction */ +#define VPFE_IPIPE_OTFDPC (1 << 2) +/* Noise Filter - 1 */ +#define VPFE_IPIPE_NF1 (1 << 3) +/* Noise Filter - 2 */ +#define VPFE_IPIPE_NF2 (1 << 4) +/* White Balance. Also a control ID */ +#define VPFE_IPIPE_WB (1 << 5) +/* 1st RGB to RBG Blend module */ +#define VPFE_IPIPE_RGB2RGB_1 (1 << 6) +/* 2nd RGB to RBG Blend module */ +#define VPFE_IPIPE_RGB2RGB_2 (1 << 7) +/* Gamma Correction */ +#define VPFE_IPIPE_GAMMA (1 << 8) +/* 3D LUT color conversion */ +#define VPFE_IPIPE_3D_LUT (1 << 9) +/* RGB to YCbCr module */ +#define VPFE_IPIPE_RGB2YUV (1 << 10) +/* YUV 422 conversion module */ +#define VPFE_IPIPE_YUV422_CONV (1 << 11) +/* Edge Enhancement */ +#define VPFE_IPIPE_YEE (1 << 12) +/* Green Imbalance Correction */ +#define VPFE_IPIPE_GIC (1 << 13) +/* CFA Interpolation */ +#define VPFE_IPIPE_CFA (1 << 14) +/* Chroma Artifact Reduction */ +#define VPFE_IPIPE_CAR (1 << 15) +/* Chroma Gain Suppression */ +#define VPFE_IPIPE_CGS (1 << 16) +/* Global brightness and contrast control */ +#define VPFE_IPIPE_GBCE (1 << 17) + +#define VPFE_IPIPE_MAX_MODULES 18 + +struct ipipe_float_u16 { + unsigned short integer; + unsigned short decimal; +}; + +struct ipipe_float_s16 { + short integer; + unsigned short decimal; +}; + +struct ipipe_float_u8 { + unsigned char integer; + unsigned char decimal; +}; + +/* Copy method selection for vertical correction + * Used when ipipe_dfc_corr_meth is IPIPE_DPC_CTORB_AFTER_HINT + */ +enum vpfe_ipipe_dpc_corr_meth { + /* replace by black or white dot specified by repl_white */ + VPFE_IPIPE_DPC_REPL_BY_DOT = 0, + /* Copy from left */ + VPFE_IPIPE_DPC_CL = 1, + /* Copy from right */ + VPFE_IPIPE_DPC_CR = 2, + /* Horizontal interpolation */ + VPFE_IPIPE_DPC_H_INTP = 3, + /* Vertical interpolation */ + VPFE_IPIPE_DPC_V_INTP = 4, + /* Copy from top */ + VPFE_IPIPE_DPC_CT = 5, + /* Copy from bottom */ + VPFE_IPIPE_DPC_CB = 6, + /* 2D interpolation */ + VPFE_IPIPE_DPC_2D_INTP = 7, +}; + +struct vpfe_ipipe_lutdpc_entry { + /* Horizontal position */ + unsigned short horz_pos; + /* vertical position */ + unsigned short vert_pos; + enum vpfe_ipipe_dpc_corr_meth method; +}; + +#define VPFE_IPIPE_MAX_SIZE_DPC 256 + +/* Structure for configuring DPC module */ +struct vpfe_ipipe_lutdpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* 0 - replace with black dot, 1 - white dot when correction + * method is IPIPE_DFC_REPL_BY_DOT=0, + */ + unsigned char repl_white; + /* number of entries in the correction table. Currently only + * support up-to 256 entries. infinite mode is not supported + */ + unsigned short dpc_size; + struct vpfe_ipipe_lutdpc_entry table[VPFE_IPIPE_MAX_SIZE_DPC]; +}; + +enum vpfe_ipipe_otfdpc_det_meth { + VPFE_IPIPE_DPC_OTF_MIN_MAX, + VPFE_IPIPE_DPC_OTF_MIN_MAX2 +}; + +struct vpfe_ipipe_otfdpc_thr { + unsigned short r; + unsigned short gr; + unsigned short gb; + unsigned short b; +}; + +enum vpfe_ipipe_otfdpc_alg { + VPFE_IPIPE_OTFDPC_2_0, + VPFE_IPIPE_OTFDPC_3_0 +}; + +struct vpfe_ipipe_otfdpc_2_0_cfg { + /* defect detection threshold for MIN_MAX2 method (DPC 2.0 alg) */ + struct vpfe_ipipe_otfdpc_thr det_thr; + /* defect correction threshold for MIN_MAX2 method (DPC 2.0 alg) or + * maximum value for MIN_MAX method + */ + struct vpfe_ipipe_otfdpc_thr corr_thr; +}; + +struct vpfe_ipipe_otfdpc_3_0_cfg { + /* DPC3.0 activity adj shf. activity = (max2-min2) >> (6 -shf) + */ + unsigned char act_adj_shf; + /* DPC3.0 detection threshold, THR */ + unsigned short det_thr; + /* DPC3.0 detection threshold slope, SLP */ + unsigned short det_slp; + /* DPC3.0 detection threshold min, MIN */ + unsigned short det_thr_min; + /* DPC3.0 detection threshold max, MAX */ + unsigned short det_thr_max; + /* DPC3.0 correction threshold, THR */ + unsigned short corr_thr; + /* DPC3.0 correction threshold slope, SLP */ + unsigned short corr_slp; + /* DPC3.0 correction threshold min, MIN */ + unsigned short corr_thr_min; + /* DPC3.0 correction threshold max, MAX */ + unsigned short corr_thr_max; +}; + +struct vpfe_ipipe_otfdpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + union { + /* if alg is IPIPE_OTFDPC_2_0 */ + struct vpfe_ipipe_otfdpc_2_0_cfg dpc_2_0; + /* if alg is IPIPE_OTFDPC_3_0 */ + struct vpfe_ipipe_otfdpc_3_0_cfg dpc_3_0; + } alg_cfg; +}; + +/* Threshold values table size */ +#define VPFE_IPIPE_NF_THR_TABLE_SIZE 8 +/* Intensity values table size */ +#define VPFE_IPIPE_NF_STR_TABLE_SIZE 8 + +/* NF, sampling method for green pixels */ +enum vpfe_ipipe_nf_sampl_meth { + /* Same as R or B */ + VPFE_IPIPE_NF_BOX, + /* Diamond mode */ + VPFE_IPIPE_NF_DIAMOND +}; + +/* Structure for configuring NF module */ +struct vpfe_ipipe_nf { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* Sampling method for green pixels */ + enum vpfe_ipipe_nf_sampl_meth gr_sample_meth; + /* Down shift value in LUT reference address + */ + unsigned char shft_val; + /* Spread value in NF algorithm + */ + unsigned char spread_val; + /* Apply LSC gain to threshold. Enable this only if + * LSC is enabled in ISIF + */ + unsigned char apply_lsc_gain; + /* Threshold values table */ + unsigned short thr[VPFE_IPIPE_NF_THR_TABLE_SIZE]; + /* intensity values table */ + unsigned char str[VPFE_IPIPE_NF_STR_TABLE_SIZE]; + /* Edge detection minimum threshold */ + unsigned short edge_det_min_thr; + /* Edge detection maximum threshold */ + unsigned short edge_det_max_thr; +}; + +enum vpfe_ipipe_gic_alg { + VPFE_IPIPE_GIC_ALG_CONST_GAIN, + VPFE_IPIPE_GIC_ALG_ADAPT_GAIN +}; + +enum vpfe_ipipe_gic_thr_sel { + VPFE_IPIPE_GIC_THR_REG, + VPFE_IPIPE_GIC_THR_NF +}; + +enum vpfe_ipipe_gic_wt_fn_type { + /* Use difference as index */ + VPFE_IPIPE_GIC_WT_FN_TYP_DIF, + /* Use weight function as index */ + VPFE_IPIPE_GIC_WT_FN_TYP_HP_VAL +}; + +/* structure for Green Imbalance Correction */ +struct vpfe_ipipe_gic { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* 0 - Constant gain , 1 - Adaptive gain algorithm */ + enum vpfe_ipipe_gic_alg gic_alg; + /* GIC gain or weight. Used for Constant gain and Adaptive algorithms + */ + unsigned short gain; + /* Threshold selection. GIC register values or NF2 thr table */ + enum vpfe_ipipe_gic_thr_sel thr_sel; + /* thr1. Used when thr_sel is IPIPE_GIC_THR_REG */ + unsigned short thr; + /* this value is used for thr2-thr1, thr3-thr2 or + * thr4-thr3 when wt_fn_type is index. Otherwise it + * is the + */ + unsigned short slope; + /* Apply LSC gain to threshold. Enable this only if + * LSC is enabled in ISIF & thr_sel is IPIPE_GIC_THR_REG + */ + unsigned char apply_lsc_gain; + /* Multiply Nf2 threshold by this gain. Use this when thr_sel + * is IPIPE_GIC_THR_NF + */ + struct ipipe_float_u8 nf2_thr_gain; + /* Weight function uses difference as index or high pass value. + * Used for adaptive gain algorithm + */ + enum vpfe_ipipe_gic_wt_fn_type wt_fn_type; +}; + +/* Structure for configuring WB module */ +struct vpfe_ipipe_wb { + /* Offset (S12) for R */ + short ofst_r; + /* Offset (S12) for Gr */ + short ofst_gr; + /* Offset (S12) for Gb */ + short ofst_gb; + /* Offset (S12) for B */ + short ofst_b; + /* Gain (U13Q9) for Red */ + struct ipipe_float_u16 gain_r; + /* Gain (U13Q9) for Gr */ + struct ipipe_float_u16 gain_gr; + /* Gain (U13Q9) for Gb */ + struct ipipe_float_u16 gain_gb; + /* Gain (U13Q9) for Blue */ + struct ipipe_float_u16 gain_b; +}; + +enum vpfe_ipipe_cfa_alg { + /* Algorithm is 2DirAC */ + VPFE_IPIPE_CFA_ALG_2DIRAC, + /* Algorithm is 2DirAC + Digital Antialiasing (DAA) */ + VPFE_IPIPE_CFA_ALG_2DIRAC_DAA, + /* Algorithm is DAA */ + VPFE_IPIPE_CFA_ALG_DAA +}; + +/* Structure for CFA Interpolation */ +struct vpfe_ipipe_cfa { + /* 2DirAC or 2DirAC + DAA */ + enum vpfe_ipipe_cfa_alg alg; + /* 2Dir CFA HP value Low Threshold */ + unsigned short hpf_thr_2dir; + /* 2Dir CFA HP value slope */ + unsigned short hpf_slp_2dir; + /* 2Dir CFA HP mix threshold */ + unsigned short hp_mix_thr_2dir; + /* 2Dir CFA HP mix slope */ + unsigned short hp_mix_slope_2dir; + /* 2Dir Direction threshold */ + unsigned short dir_thr_2dir; + /* 2Dir Direction slope */ + unsigned short dir_slope_2dir; + /* 2Dir Non Directional Weight */ + unsigned short nd_wt_2dir; + /* DAA Mono Hue Fraction */ + unsigned short hue_fract_daa; + /* DAA Mono Edge threshold */ + unsigned short edge_thr_daa; + /* DAA Mono threshold minimum */ + unsigned short thr_min_daa; + /* DAA Mono threshold slope */ + unsigned short thr_slope_daa; + /* DAA Mono slope minimum */ + unsigned short slope_min_daa; + /* DAA Mono slope slope */ + unsigned short slope_slope_daa; + /* DAA Mono LP wight */ + unsigned short lp_wt_daa; +}; + +/* Struct for configuring RGB2RGB blending module */ +struct vpfe_ipipe_rgb2rgb { + /* Matrix coefficient for RR S12Q8 for ID = 1 and S11Q8 for ID = 2 */ + struct ipipe_float_s16 coef_rr; + /* Matrix coefficient for GR S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gr; + /* Matrix coefficient for BR S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_br; + /* Matrix coefficient for RG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_rg; + /* Matrix coefficient for GG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gg; + /* Matrix coefficient for BG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_bg; + /* Matrix coefficient for RB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_rb; + /* Matrix coefficient for GB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gb; + /* Matrix coefficient for BB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_bb; + /* Output offset for R S13/S11 */ + int out_ofst_r; + /* Output offset for G S13/S11 */ + int out_ofst_g; + /* Output offset for B S13/S11 */ + int out_ofst_b; +}; + +#define VPFE_IPIPE_MAX_SIZE_GAMMA 512 + +enum vpfe_ipipe_gamma_tbl_size { + VPFE_IPIPE_GAMMA_TBL_SZ_64 = 64, + VPFE_IPIPE_GAMMA_TBL_SZ_128 = 128, + VPFE_IPIPE_GAMMA_TBL_SZ_256 = 256, + VPFE_IPIPE_GAMMA_TBL_SZ_512 = 512, +}; + +enum vpfe_ipipe_gamma_tbl_sel { + VPFE_IPIPE_GAMMA_TBL_RAM = 0, + VPFE_IPIPE_GAMMA_TBL_ROM = 1, +}; + +struct vpfe_ipipe_gamma_entry { + /* 10 bit slope */ + short slope; + /* 10 bit offset */ + unsigned short offset; +}; + +/* Structure for configuring Gamma correction module */ +struct vpfe_ipipe_gamma { + /* 0 - Enable Gamma correction for Red + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_r; + /* 0 - Enable Gamma correction for Blue + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_b; + /* 0 - Enable Gamma correction for Green + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_g; + /* IPIPE_GAMMA_TBL_RAM or IPIPE_GAMMA_TBL_ROM */ + enum vpfe_ipipe_gamma_tbl_sel tbl_sel; + /* Table size for RAM gamma table. + */ + enum vpfe_ipipe_gamma_tbl_size tbl_size; + /* R table */ + struct vpfe_ipipe_gamma_entry table_r[VPFE_IPIPE_MAX_SIZE_GAMMA]; + /* Blue table */ + struct vpfe_ipipe_gamma_entry table_b[VPFE_IPIPE_MAX_SIZE_GAMMA]; + /* Green table */ + struct vpfe_ipipe_gamma_entry table_g[VPFE_IPIPE_MAX_SIZE_GAMMA]; +}; + +#define VPFE_IPIPE_MAX_SIZE_3D_LUT 729 + +struct vpfe_ipipe_3d_lut_entry { + /* 10 bit entry for red */ + unsigned short r; + /* 10 bit entry for green */ + unsigned short g; + /* 10 bit entry for blue */ + unsigned short b; +}; + +/* structure for 3D-LUT */ +struct vpfe_ipipe_3d_lut { + /* enable/disable 3D lut */ + unsigned char en; + /* 3D - LUT table entry */ + struct vpfe_ipipe_3d_lut_entry table[VPFE_IPIPE_MAX_SIZE_3D_LUT]; +}; + +/* Struct for configuring rgb2ycbcr module */ +struct vpfe_ipipe_rgb2yuv { + /* Matrix coefficient for RY S12Q8 */ + struct ipipe_float_s16 coef_ry; + /* Matrix coefficient for GY S12Q8 */ + struct ipipe_float_s16 coef_gy; + /* Matrix coefficient for BY S12Q8 */ + struct ipipe_float_s16 coef_by; + /* Matrix coefficient for RCb S12Q8 */ + struct ipipe_float_s16 coef_rcb; + /* Matrix coefficient for GCb S12Q8 */ + struct ipipe_float_s16 coef_gcb; + /* Matrix coefficient for BCb S12Q8 */ + struct ipipe_float_s16 coef_bcb; + /* Matrix coefficient for RCr S12Q8 */ + struct ipipe_float_s16 coef_rcr; + /* Matrix coefficient for GCr S12Q8 */ + struct ipipe_float_s16 coef_gcr; + /* Matrix coefficient for BCr S12Q8 */ + struct ipipe_float_s16 coef_bcr; + /* Output offset for R S11 */ + int out_ofst_y; + /* Output offset for Cb S11 */ + int out_ofst_cb; + /* Output offset for Cr S11 */ + int out_ofst_cr; +}; + +enum vpfe_ipipe_gbce_type { + VPFE_IPIPE_GBCE_Y_VAL_TBL = 0, + VPFE_IPIPE_GBCE_GAIN_TBL = 1, +}; + +#define VPFE_IPIPE_MAX_SIZE_GBCE_LUT 1024 + +/* structure for Global brightness and Contrast */ +struct vpfe_ipipe_gbce { + /* enable/disable GBCE */ + unsigned char en; + /* Y - value table or Gain table */ + enum vpfe_ipipe_gbce_type type; + /* ptr to LUT for GBCE with 1024 entries */ + unsigned short table[VPFE_IPIPE_MAX_SIZE_GBCE_LUT]; +}; + +/* Chrominance position. Applicable only for YCbCr input + * Applied after edge enhancement + */ +enum vpfe_chr_pos { + /* Co-siting, same position with luminance */ + VPFE_IPIPE_YUV422_CHR_POS_COSITE = 0, + /* Centering, In the middle of luminance */ + VPFE_IPIPE_YUV422_CHR_POS_CENTRE = 1, +}; + +/* Structure for configuring yuv422 conversion module */ +struct vpfe_ipipe_yuv422_conv { + /* Max Chrominance value */ + unsigned char en_chrom_lpf; + /* 1 - enable LPF for chrminance, 0 - disable */ + enum vpfe_chr_pos chrom_pos; +}; + +#define VPFE_IPIPE_MAX_SIZE_YEE_LUT 1024 + +enum vpfe_ipipe_yee_merge_meth { + VPFE_IPIPE_YEE_ABS_MAX = 0, + VPFE_IPIPE_YEE_EE_ES = 1, +}; + +/* Structure for configuring YUV Edge Enhancement module */ +struct vpfe_ipipe_yee { + /* 1 - enable enhancement, 0 - disable */ + unsigned char en; + /* enable/disable halo reduction in edge sharpner */ + unsigned char en_halo_red; + /* Merge method between Edge Enhancer and Edge sharpner */ + enum vpfe_ipipe_yee_merge_meth merge_meth; + /* HPF Shift length */ + unsigned char hpf_shft; + /* HPF Coefficient 00, S10 */ + short hpf_coef_00; + /* HPF Coefficient 01, S10 */ + short hpf_coef_01; + /* HPF Coefficient 02, S10 */ + short hpf_coef_02; + /* HPF Coefficient 10, S10 */ + short hpf_coef_10; + /* HPF Coefficient 11, S10 */ + short hpf_coef_11; + /* HPF Coefficient 12, S10 */ + short hpf_coef_12; + /* HPF Coefficient 20, S10 */ + short hpf_coef_20; + /* HPF Coefficient 21, S10 */ + short hpf_coef_21; + /* HPF Coefficient 22, S10 */ + short hpf_coef_22; + /* Lower threshold before referring to LUT */ + unsigned short yee_thr; + /* Edge sharpener Gain */ + unsigned short es_gain; + /* Edge sharpener lower threshold */ + unsigned short es_thr1; + /* Edge sharpener upper threshold */ + unsigned short es_thr2; + /* Edge sharpener gain on gradient */ + unsigned short es_gain_grad; + /* Edge sharpener offset on gradient */ + unsigned short es_ofst_grad; + /* Ptr to EE table. Must have 1024 entries */ + short table[VPFE_IPIPE_MAX_SIZE_YEE_LUT]; +}; + +enum vpfe_ipipe_car_meth { + /* Chromatic Gain Control */ + VPFE_IPIPE_CAR_CHR_GAIN_CTRL = 0, + /* Dynamic switching between CHR_GAIN_CTRL + * and MED_FLTR + */ + VPFE_IPIPE_CAR_DYN_SWITCH = 1, + /* Median Filter */ + VPFE_IPIPE_CAR_MED_FLTR = 2, +}; + +enum vpfe_ipipe_car_hpf_type { + VPFE_IPIPE_CAR_HPF_Y = 0, + VPFE_IPIPE_CAR_HPF_H = 1, + VPFE_IPIPE_CAR_HPF_V = 2, + VPFE_IPIPE_CAR_HPF_2D = 3, + /* 2D HPF from YUV Edge Enhancement */ + VPFE_IPIPE_CAR_HPF_2D_YEE = 4, +}; + +struct vpfe_ipipe_car_gain { + /* csup_gain */ + unsigned char gain; + /* csup_shf. */ + unsigned char shft; + /* gain minimum */ + unsigned short gain_min; +}; + +/* Structure for Chromatic Artifact Reduction */ +struct vpfe_ipipe_car { + /* enable/disable */ + unsigned char en; + /* Gain control or Dynamic switching */ + enum vpfe_ipipe_car_meth meth; + /* Gain1 function configuration for Gain control */ + struct vpfe_ipipe_car_gain gain1; + /* Gain2 function configuration for Gain control */ + struct vpfe_ipipe_car_gain gain2; + /* HPF type used for CAR */ + enum vpfe_ipipe_car_hpf_type hpf; + /* csup_thr: HPF threshold for Gain control */ + unsigned char hpf_thr; + /* Down shift value for hpf. 2 bits */ + unsigned char hpf_shft; + /* switch limit for median filter */ + unsigned char sw0; + /* switch coefficient for Gain control */ + unsigned char sw1; +}; + +/* structure for Chromatic Gain Suppression */ +struct vpfe_ipipe_cgs { + /* enable/disable */ + unsigned char en; + /* gain1 bright side threshold */ + unsigned char h_thr; + /* gain1 bright side slope */ + unsigned char h_slope; + /* gain1 down shift value for bright side */ + unsigned char h_shft; + /* gain1 bright side minimum gain */ + unsigned char h_min; +}; + +/* Max pixels allowed in the input. If above this either decimation + * or frame division mode to be enabled + */ +#define VPFE_IPIPE_MAX_INPUT_WIDTH 2600 + +struct vpfe_ipipe_input_config { + unsigned int vst; + unsigned int hst; +}; + +/** + * struct vpfe_ipipe_config - IPIPE engine configuration (user) + * @input_config: Pointer to structure for ipipe configuration. + * @flag: Specifies which ISP IPIPE functions should be enabled. + * @lutdpc: Pointer to luma enhancement structure. + * @otfdpc: Pointer to structure for defect correction. + * @nf1: Pointer to structure for Noise Filter. + * @nf2: Pointer to structure for Noise Filter. + * @gic: Pointer to structure for Green Imbalance. + * @wbal: Pointer to structure for White Balance. + * @cfa: Pointer to structure containing the CFA interpolation. + * @rgb2rgb1: Pointer to structure for RGB to RGB Blending. + * @rgb2rgb2: Pointer to structure for RGB to RGB Blending. + * @gamma: Pointer to gamma structure. + * @lut: Pointer to structure for 3D LUT. + * @rgb2yuv: Pointer to structure for RGB-YCbCr conversion. + * @gbce: Pointer to structure for Global Brightness,Contrast Control. + * @yuv422_conv: Pointer to structure for YUV 422 conversion. + * @yee: Pointer to structure for Edge Enhancer. + * @car: Pointer to structure for Chromatic Artifact Reduction. + * @cgs: Pointer to structure for Chromatic Gain Suppression. + */ +struct vpfe_ipipe_config { + __u32 flag; + struct vpfe_ipipe_input_config __user *input_config; + struct vpfe_ipipe_lutdpc __user *lutdpc; + struct vpfe_ipipe_otfdpc __user *otfdpc; + struct vpfe_ipipe_nf __user *nf1; + struct vpfe_ipipe_nf __user *nf2; + struct vpfe_ipipe_gic __user *gic; + struct vpfe_ipipe_wb __user *wbal; + struct vpfe_ipipe_cfa __user *cfa; + struct vpfe_ipipe_rgb2rgb __user *rgb2rgb1; + struct vpfe_ipipe_rgb2rgb __user *rgb2rgb2; + struct vpfe_ipipe_gamma __user *gamma; + struct vpfe_ipipe_3d_lut __user *lut; + struct vpfe_ipipe_rgb2yuv __user *rgb2yuv; + struct vpfe_ipipe_gbce __user *gbce; + struct vpfe_ipipe_yuv422_conv __user *yuv422_conv; + struct vpfe_ipipe_yee __user *yee; + struct vpfe_ipipe_car __user *car; + struct vpfe_ipipe_cgs __user *cgs; +}; + +/******************************************************************* +** Resizer API structures +*******************************************************************/ +/* Interpolation types used for horizontal rescale */ +enum vpfe_rsz_intp_t { + VPFE_RSZ_INTP_CUBIC, + VPFE_RSZ_INTP_LINEAR +}; + +/* Horizontal LPF intensity selection */ +enum vpfe_rsz_h_lpf_lse_t { + VPFE_RSZ_H_LPF_LSE_INTERN, + VPFE_RSZ_H_LPF_LSE_USER_VAL +}; + +enum vpfe_rsz_down_scale_ave_sz { + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + VPFE_IPIPE_DWN_SCALE_1_OVER_4, + VPFE_IPIPE_DWN_SCALE_1_OVER_8, + VPFE_IPIPE_DWN_SCALE_1_OVER_16, + VPFE_IPIPE_DWN_SCALE_1_OVER_32, + VPFE_IPIPE_DWN_SCALE_1_OVER_64, + VPFE_IPIPE_DWN_SCALE_1_OVER_128, + VPFE_IPIPE_DWN_SCALE_1_OVER_256 +}; + +struct vpfe_rsz_output_spec { + /* enable horizontal flip */ + unsigned char h_flip; + /* enable vertical flip */ + unsigned char v_flip; + /* line start offset for y. */ + unsigned int vst_y; + /* line start offset for c. Only for 420 */ + unsigned int vst_c; + /* vertical rescale interpolation type, YCbCr or Luminance */ + enum vpfe_rsz_intp_t v_typ_y; + /* vertical rescale interpolation type for Chrominance */ + enum vpfe_rsz_intp_t v_typ_c; + /* vertical lpf intensity - Luminance */ + unsigned char v_lpf_int_y; + /* vertical lpf intensity - Chrominance */ + unsigned char v_lpf_int_c; + /* horizontal rescale interpolation types, YCbCr or Luminance */ + enum vpfe_rsz_intp_t h_typ_y; + /* horizontal rescale interpolation types, Chrominance */ + enum vpfe_rsz_intp_t h_typ_c; + /* horizontal lpf intensity - Luminance */ + unsigned char h_lpf_int_y; + /* horizontal lpf intensity - Chrominance */ + unsigned char h_lpf_int_c; + /* Use down scale mode for scale down */ + unsigned char en_down_scale; + /* if downscale, set the downscale more average size for horizontal + * direction. Used only if output width and height is less than + * input sizes + */ + enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz; + /* if downscale, set the downscale more average size for vertical + * direction. Used only if output width and height is less than + * input sizes + */ + enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz; + /* Y offset. If set, the offset would be added to the base address + */ + unsigned int user_y_ofst; + /* C offset. If set, the offset would be added to the base address + */ + unsigned int user_c_ofst; +}; + +struct vpfe_rsz_config_params { + unsigned int vst; + /* horizontal start position of the image + * data to IPIPE + */ + unsigned int hst; + /* output spec of the image data coming out of resizer - 0(UYVY). + */ + struct vpfe_rsz_output_spec output1; + /* output spec of the image data coming out of resizer - 1(UYVY). + */ + struct vpfe_rsz_output_spec output2; + /* 0 , chroma sample at odd pixel, 1 - even pixel */ + unsigned char chroma_sample_even; + unsigned char frame_div_mode_en; + unsigned char yuv_y_min; + unsigned char yuv_y_max; + unsigned char yuv_c_min; + unsigned char yuv_c_max; + enum vpfe_chr_pos out_chr_pos; + unsigned char bypass; +}; + +/* Structure for VIDIOC_VPFE_RSZ_[S/G]_CONFIG IOCTLs */ +struct vpfe_rsz_config { + struct vpfe_rsz_config_params *config; +}; + +#endif /* _DAVINCI_VPFE_USER_H */ diff --git a/drivers/staging/media/davinci_vpfe/vpfe.h b/drivers/staging/media/davinci_vpfe/vpfe.h new file mode 100644 index 000000000000..0587bc52a840 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _VPFE_H +#define _VPFE_H + +#ifdef __KERNEL__ +#include +#include +#include + +#include + +#define CAPTURE_DRV_NAME "vpfe-capture" + +struct vpfe_route { + __u32 input; + __u32 output; +}; + +enum vpfe_subdev_id { + VPFE_SUBDEV_TVP5146 = 1, + VPFE_SUBDEV_MT9T031 = 2, + VPFE_SUBDEV_TVP7002 = 3, + VPFE_SUBDEV_MT9P031 = 4, +}; + +struct vpfe_ext_subdev_info { + /* v4l2 subdev */ + struct v4l2_subdev *subdev; + /* Sub device module name */ + char module_name[32]; + /* Sub device group id */ + int grp_id; + /* Number of inputs supported */ + int num_inputs; + /* inputs available at the sub device */ + struct v4l2_input *inputs; + /* Sub dev routing information for each input */ + struct vpfe_route *routes; + /* ccdc bus/interface configuration */ + struct vpfe_hw_if_param ccdc_if_params; + /* i2c subdevice board info */ + struct i2c_board_info board_info; + /* Is this a camera sub device ? */ + unsigned is_camera:1; + /* check if sub dev supports routing */ + unsigned can_route:1; + /* registered ? */ + unsigned registered:1; +}; + +struct vpfe_config { + /* Number of sub devices connected to vpfe */ + int num_subdevs; + /* information about each subdev */ + struct vpfe_ext_subdev_info *sub_devs; + /* evm card info */ + char *card_name; + /* setup function for the input path */ + int (*setup_input)(enum vpfe_subdev_id id); + /* number of clocks */ + int num_clocks; + /* clocks used for vpfe capture */ + char *clocks[]; +}; +#endif +#endif diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c new file mode 100644 index 000000000000..7b351717bcbb --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.c @@ -0,0 +1,740 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + * + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355/DM365 only). + * + * Features supported + * - MMAP IO + * - USERPTR IO + * - Capture using TVP5146 over BT.656 + * - Support for interfacing decoders using sub device model + * - Work with DM365 or DM355 or DM6446 CCDC to do Raw Bayer + * RGB/YUV data capture to SDRAM. + * - Chaining of Image Processor + * - SINGLE-SHOT mode + */ + +#include +#include +#include + +#include "vpfe.h" +#include "vpfe_mc_capture.h" + +static bool debug; +static bool interface; + +module_param(interface, bool, S_IRUGO); +module_param(debug, bool, 0644); + +/** + * VPFE capture can be used for capturing video such as from TVP5146 or TVP7002 + * and for capture raw bayer data from camera sensors such as mt9p031. At this + * point there is problem in co-existence of mt9p031 and tvp5146 due to i2c + * address collision. So set the variable below from bootargs to do either video + * capture or camera capture. + * interface = 0 - video capture (from TVP514x or such), + * interface = 1 - Camera capture (from mt9p031 or such) + * Re-visit this when we fix the co-existence issue + */ +MODULE_PARM_DESC(interface, "interface 0-1 (default:0)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* map mbus_fmt to pixelformat */ +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix) +{ + switch (mbus->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_YUYV8_2X8: + pix->pixelformat = V4L2_PIX_FMT_YUYV; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_YUYV10_1X20: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + pix->pixelformat = V4L2_PIX_FMT_SBGGR16; + pix->bytesperline = pix->width * 2; + break; + + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_YDYUYDYV8_1X16: + pix->pixelformat = V4L2_PIX_FMT_NV12; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_Y8_1X8: + pix->pixelformat = V4L2_PIX_FMT_GREY; + pix->bytesperline = pix->width; + break; + + case V4L2_MBUS_FMT_UV8_1X8: + pix->pixelformat = V4L2_PIX_FMT_UV8; + pix->bytesperline = pix->width; + break; + + default: + pr_err("Invalid mbus code set\n"); + } + /* pitch should be 32 bytes aligned */ + pix->bytesperline = ALIGN(pix->bytesperline, 32); + if (pix->pixelformat == V4L2_PIX_FMT_NV12) + pix->sizeimage = pix->bytesperline * pix->height + + ((pix->bytesperline * pix->height) >> 1); + else + pix->sizeimage = pix->bytesperline * pix->height; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_isr\n"); + vpfe_isif_buffer_isr(&vpfe_dev->vpfe_isif); + vpfe_resizer_buffer_isr(&vpfe_dev->vpfe_resizer); + return IRQ_HANDLED; +} + +/* vpfe_vdint1_isr() - isr handler for VINT1 interrupt */ +static irqreturn_t vpfe_vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_vdint1_isr\n"); + vpfe_isif_vidint1_isr(&vpfe_dev->vpfe_isif); + return IRQ_HANDLED; +} + +/* vpfe_imp_dma_isr() - ISR for ipipe dma completion */ +static irqreturn_t vpfe_imp_dma_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_imp_dma_isr\n"); + vpfe_ipipeif_ss_buffer_isr(&vpfe_dev->vpfe_ipipeif); + vpfe_resizer_dma_isr(&vpfe_dev->vpfe_resizer); + return IRQ_HANDLED; +} + +/* + * vpfe_disable_clock() - Disable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Disables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static void vpfe_disable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int i; + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + clk_disable_unprepare(vpfe_dev->clks[i]); + clk_put(vpfe_dev->clks[i]); + } + kzfree(vpfe_dev->clks); + v4l2_info(vpfe_dev->pdev->driver, "vpfe capture clocks disabled\n"); +} + +/* + * vpfe_enable_clock() - Enable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Enables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int ret = -EFAULT; + int i; + + if (!vpfe_cfg->num_clocks) + return 0; + + vpfe_dev->clks = kzalloc(vpfe_cfg->num_clocks * + sizeof(struct clock *), GFP_KERNEL); + if (vpfe_dev->clks == NULL) { + v4l2_err(vpfe_dev->pdev->driver, "Memory allocation failed\n"); + return -ENOMEM; + } + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + if (vpfe_cfg->clocks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "clock %s is not defined in vpfe config\n", + vpfe_cfg->clocks[i]); + goto out; + } + + vpfe_dev->clks[i] = + clk_get(vpfe_dev->pdev, vpfe_cfg->clocks[i]); + if (vpfe_dev->clks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "Failed to get clock %s\n", + vpfe_cfg->clocks[i]); + goto out; + } + + if (clk_prepare_enable(vpfe_dev->clks[i])) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe clock %s not enabled\n", + vpfe_cfg->clocks[i]); + goto out; + } + + v4l2_info(vpfe_dev->pdev->driver, "vpss clock %s enabled", + vpfe_cfg->clocks[i]); + } + + return 0; +out: + for (i = 0; i < vpfe_cfg->num_clocks; i++) + if (vpfe_dev->clks[i]) { + clk_disable_unprepare(vpfe_dev->clks[i]); + clk_put(vpfe_dev->clks[i]); + } + + v4l2_err(vpfe_dev->pdev->driver, "Failed to enable clocks\n"); + kzfree(vpfe_dev->clks); + + return ret; +} + +/* + * vpfe_detach_irq() - Detach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Detach all IRQs defined in vpfe configuration. + */ +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->imp_dma_irq, vpfe_dev); +} + +/* + * vpfe_attach_irq() - Attach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Attach all IRQs defined in vpfe configuration. + */ +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + int ret = 0; + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, + "vpfe_capture0", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT0 interrupt\n"); + return ret; + } + + ret = request_irq(vpfe_dev->ccdc_irq1, vpfe_vdint1_isr, IRQF_DISABLED, + "vpfe_capture1", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT1 interrupt\n"); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + ret = request_irq(vpfe_dev->imp_dma_irq, vpfe_imp_dma_isr, + IRQF_DISABLED, "Imp_Sdram_Irq", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting IMP IRQ interrupt\n"); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + return 0; +} + +/* + * register_i2c_devices() - register all i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * + * register all i2c v4l2 subdevs + */ +static int register_i2c_devices(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct i2c_adapter *i2c_adap; + unsigned int num_subdevs; + int ret; + int i; + int k; + + vpfe_cfg = vpfe_dev->cfg; + i2c_adap = i2c_get_adapter(1); + num_subdevs = vpfe_cfg->num_subdevs; + vpfe_dev->sd = + kzalloc(sizeof(struct v4l2_subdev *)*num_subdevs, GFP_KERNEL); + if (vpfe_dev->sd == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for subdevice\n"); + return -ENOMEM; + } + + for (i = 0, k = 0; i < num_subdevs; i++) { + sdinfo = &vpfe_cfg->sub_devs[i]; + /* + * register subdevices based on interface setting. Currently + * tvp5146 and mt9p031 cannot co-exists due to i2c address + * conflicts. So only one of them is registered. Re-visit this + * once we have support for i2c switch handling in i2c driver + * framework + */ + if (interface == sdinfo->is_camera) { + /* setup input path */ + if (vpfe_cfg->setup_input && + vpfe_cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_info(&vpfe_dev->v4l2_dev, + "could not setup input for %s\n", + sdinfo->module_name); + goto probe_sd_out; + } + /* Load up the subdevice */ + vpfe_dev->sd[k] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, &sdinfo->board_info, + NULL); + if (vpfe_dev->sd[k]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->module_name); + + vpfe_dev->sd[k]->grp_id = sdinfo->grp_id; + k++; + + sdinfo->registered = 1; + } + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s is not registered\n", + sdinfo->module_name); + } + } + vpfe_dev->num_ext_subdevs = k; + + return 0; + +probe_sd_out: + kzfree(vpfe_dev->sd); + + return ret; +} + +/* + * vpfe_register_entities() - register all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * register all v4l2 subdevs, media entities, and creates links + * between entities + */ +static int vpfe_register_entities(struct vpfe_device *vpfe_dev) +{ + unsigned int flags = 0; + int ret; + int i; + + /* register i2c devices first */ + ret = register_i2c_devices(vpfe_dev); + if (ret) + return ret; + + /* register rest of the sub-devs */ + ret = vpfe_isif_register_entities(&vpfe_dev->vpfe_isif, + &vpfe_dev->v4l2_dev); + if (ret) + return ret; + + ret = vpfe_ipipeif_register_entities(&vpfe_dev->vpfe_ipipeif, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_isif_register; + + ret = vpfe_ipipe_register_entities(&vpfe_dev->vpfe_ipipe, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_ipipeif_register; + + ret = vpfe_resizer_register_entities(&vpfe_dev->vpfe_resizer, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_ipipe_register; + + /* create links now, starting with external(i2c) entities */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) + /* if entity has no pads (ex: amplifier), + cant establish link */ + if (vpfe_dev->sd[i]->entity.num_pads) { + ret = media_entity_create_link(&vpfe_dev->sd[i]->entity, + 0, &vpfe_dev->vpfe_isif.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + } + + ret = media_entity_create_link(&vpfe_dev->vpfe_isif.subdev.entity, 1, + &vpfe_dev->vpfe_ipipeif.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + &vpfe_dev->vpfe_ipipe.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipe.subdev.entity, + 1, &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ipipeif.subdev.entity, 1, + &vpfe_dev->vpfe_resizer.crop_resizer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = v4l2_device_register_subdev_nodes(&vpfe_dev->v4l2_dev); + if (ret < 0) + goto out_resizer_register; + + return 0; + +out_resizer_register: + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); +out_ipipe_register: + vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe); +out_ipipeif_register: + vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif); +out_isif_register: + vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif); + + return ret; +} + +/* + * vpfe_unregister_entities() - unregister all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * unregister all v4l2 subdevs and media entities + */ +static void vpfe_unregister_entities(struct vpfe_device *vpfe_dev) +{ + vpfe_isif_unregister_entities(&vpfe_dev->vpfe_isif); + vpfe_ipipeif_unregister_entities(&vpfe_dev->vpfe_ipipeif); + vpfe_ipipe_unregister_entities(&vpfe_dev->vpfe_ipipe); + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); +} + +/* + * vpfe_cleanup_modules() - cleanup all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * cleanup all v4l2 subdevs + */ +static void vpfe_cleanup_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev); + vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev); + vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev); + vpfe_resizer_cleanup(&vpfe_dev->vpfe_resizer, pdev); +} + +/* + * vpfe_initialize_modules() - initialize all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * intialize all v4l2 subdevs and media entities + */ +static int vpfe_initialize_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + int ret; + + ret = vpfe_isif_init(&vpfe_dev->vpfe_isif, pdev); + if (ret) + return ret; + + ret = vpfe_ipipeif_init(&vpfe_dev->vpfe_ipipeif, pdev); + if (ret) + goto out_isif_init; + + ret = vpfe_ipipe_init(&vpfe_dev->vpfe_ipipe, pdev); + if (ret) + goto out_ipipeif_init; + + ret = vpfe_resizer_init(&vpfe_dev->vpfe_resizer, pdev); + if (ret) + goto out_ipipe_init; + + return 0; + +out_ipipe_init: + vpfe_ipipe_cleanup(&vpfe_dev->vpfe_ipipe, pdev); +out_ipipeif_init: + vpfe_ipipeif_cleanup(&vpfe_dev->vpfe_ipipeif, pdev); +out_isif_init: + vpfe_isif_cleanup(&vpfe_dev->vpfe_isif, pdev); + + return ret; +} + +/* + * vpfe_probe() : vpfe probe function + * @pdev: platform device pointer + * + * This function creates device entries by register itself to the V4L2 driver + * and initializes fields of each device objects + */ +static int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev; + struct resource *res1; + int ret = -ENOMEM; + + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return ret; + } + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + vpfe_dev->cfg = pdev->dev.platform_data; + if (vpfe_dev->cfg->card_name == NULL || + vpfe_dev->cfg->sub_devs == NULL) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq1 = res1->start; + + /* Get DMA irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for DMA\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->imp_dma_irq = res1->start; + + vpfe_dev->pdev = &pdev->dev; + + /* enable vpss clocks */ + ret = vpfe_enable_clock(vpfe_dev); + if (ret) + goto probe_free_dev_mem; + + if (vpfe_initialize_modules(vpfe_dev, pdev)) + goto probe_disable_clock; + + vpfe_dev->media_dev.dev = vpfe_dev->pdev; + strcpy((char *)&vpfe_dev->media_dev.model, "davinci-media"); + + ret = media_device_register(&vpfe_dev->media_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register media device.\n"); + goto probe_out_entities_cleanup; + } + + vpfe_dev->v4l2_dev.mdev = &vpfe_dev->media_dev; + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, "Unable to register v4l2 device.\n"); + goto probe_out_media_unregister; + } + + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + /* register subdevs/entities */ + if (vpfe_register_entities(vpfe_dev)) + goto probe_out_v4l2_unregister; + + ret = vpfe_attach_irq(vpfe_dev); + if (ret) + goto probe_out_entities_unregister; + + return 0; + +probe_out_entities_unregister: + vpfe_unregister_entities(vpfe_dev); + kzfree(vpfe_dev->sd); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_media_unregister: + media_device_unregister(&vpfe_dev->media_dev); +probe_out_entities_cleanup: + vpfe_cleanup_modules(vpfe_dev, pdev); +probe_disable_clock: + vpfe_disable_clock(vpfe_dev); +probe_free_dev_mem: + kzfree(vpfe_dev); + + return ret; +} + +/* + * vpfe_remove : This function un-registers device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + kzfree(vpfe_dev->sd); + vpfe_detach_irq(vpfe_dev); + vpfe_unregister_entities(vpfe_dev); + vpfe_cleanup_modules(vpfe_dev, pdev); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + media_device_unregister(&vpfe_dev->media_dev); + vpfe_disable_clock(vpfe_dev); + kzfree(vpfe_dev); + + return 0; +} + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = vpfe_probe, + .remove = vpfe_remove, +}; + +/** + * vpfe_init : This function registers device driver + */ +static __init int vpfe_init(void) +{ + /* Register driver to the kernel */ + return platform_driver_register(&vpfe_driver); +} + +/** + * vpfe_cleanup : This function un-registers device driver + */ +static void vpfe_cleanup(void) +{ + platform_driver_unregister(&vpfe_driver); +} + +module_init(vpfe_init); +module_exit(vpfe_cleanup); diff --git a/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h new file mode 100644 index 000000000000..68f6fe43a5b5 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_mc_capture.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_MC_CAPTURE_H +#define _DAVINCI_VPFE_MC_CAPTURE_H + +#include "dm365_ipipe.h" +#include "dm365_ipipeif.h" +#include "dm365_isif.h" +#include "dm365_resizer.h" +#include "vpfe_video.h" + +#define VPFE_MAJOR_RELEASE 0 +#define VPFE_MINOR_RELEASE 0 +#define VPFE_BUILD 1 +#define VPFE_CAPTURE_VERSION_CODE ((VPFE_MAJOR_RELEASE << 16) | \ + (VPFE_MINOR_RELEASE << 8) | \ + VPFE_BUILD) + +/* IPIPE hardware limits */ +#define IPIPE_MAX_OUTPUT_WIDTH_A 2176 +#define IPIPE_MAX_OUTPUT_WIDTH_B 640 + +/* Based on max resolution supported. QXGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_A 1536 +/* Based on max resolution supported. VGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_B 480 + +#define to_vpfe_device(ptr_module) \ + container_of(ptr_module, struct vpfe_device, vpfe_##ptr_module) +#define to_device(ptr_module) \ + (to_vpfe_device(ptr_module)->dev) + +struct vpfe_device { + /* external registered sub devices */ + struct v4l2_subdev **sd; + /* number of registered external subdevs */ + unsigned int num_ext_subdevs; + /* vpfe cfg */ + struct vpfe_config *cfg; + /* clock ptrs for vpfe capture */ + struct clk **clks; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* parent device */ + struct device *pdev; + /* IRQ number for DMA transfer completion at the image processor */ + unsigned int imp_dma_irq; + /* CCDC IRQs used when CCDC/ISIF output to SDRAM */ + unsigned int ccdc_irq0; + unsigned int ccdc_irq1; + /* maximum video memory that is available*/ + unsigned int video_limit; + /* media device */ + struct media_device media_dev; + /* ccdc subdevice */ + struct vpfe_isif_device vpfe_isif; + /* ipipeif subdevice */ + struct vpfe_ipipeif_device vpfe_ipipeif; + /* ipipe subdevice */ + struct vpfe_ipipe_device vpfe_ipipe; + /* resizer subdevice */ + struct vpfe_resizer_device vpfe_resizer; +}; + +/* File handle structure */ +struct vpfe_fh { + struct v4l2_fh vfh; + struct vpfe_video_device *video; + /* Indicates whether this file handle is doing IO */ + u8 io_allowed; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; +}; + +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix); + +#endif /* _DAVINCI_VPFE_MC_CAPTURE_H */ From 622897da67b38290a2a0fbbebdd13f2448a1b5e0 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:03:38 -0300 Subject: [PATCH 0293/4607] [media] davinci: vpfe: add v4l2 video driver support Add a generic video driver functionality to be used by all the vpfe drivers for davinci SoCs. The functionality includes all the standard v4l2 interfaces including streaming. The video node interface can be used both as an input and output node for both continuous and single shot modes. Also supports dv_presets to include HD modes, wth support for both user pointer IO and mmap. The buffering mechanism is based on videobuf2 interface. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/davinci_vpfe/vpfe_video.c | 1620 +++++++++++++++++ .../staging/media/davinci_vpfe/vpfe_video.h | 155 ++ 2 files changed, 1775 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_video.c create mode 100644 drivers/staging/media/davinci_vpfe/vpfe_video.h diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c new file mode 100644 index 000000000000..99ccbebea598 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c @@ -0,0 +1,1620 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#include +#include + +#include + +#include "vpfe.h" +#include "vpfe_mc_capture.h" + +/* minimum number of buffers needed in cont-mode */ +#define MIN_NUM_BUFFERS 3 + +static int debug; + +/* get v4l2 subdev pointer to external subdev which is active */ +static struct media_entity *vpfe_get_input_entity + (struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct media_pad *remote; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]); + if (remote == NULL) { + pr_err("Invalid media connection to isif/ccdc\n"); + return NULL; + } + return remote->entity; +} + +/* updates external subdev(sensor/decoder) which is active */ +static int vpfe_update_current_ext_subdev(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_config *vpfe_cfg; + struct v4l2_subdev *subdev; + struct media_pad *remote; + int i; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_isif.pads[0]); + if (remote == NULL) { + pr_err("Invalid media connection to isif/ccdc\n"); + return -EINVAL; + } + + subdev = media_entity_to_v4l2_subdev(remote->entity); + vpfe_cfg = vpfe_dev->pdev->platform_data; + for (i = 0; i < vpfe_cfg->num_subdevs; i++) { + if (!strcmp(vpfe_cfg->sub_devs[i].module_name, subdev->name)) { + video->current_ext_subdev = &vpfe_cfg->sub_devs[i]; + break; + } + } + + /* if user not linked decoder/sensor to isif/ccdc */ + if (i == vpfe_cfg->num_subdevs) { + pr_err("Invalid media chain connection to isif/ccdc\n"); + return -EINVAL; + } + /* find the v4l2 subdev pointer */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) { + if (!strcmp(video->current_ext_subdev->module_name, + vpfe_dev->sd[i]->name)) + video->current_ext_subdev->subdev = vpfe_dev->sd[i]; + } + return 0; +} + +/* get the subdev which is connected to the output video node */ +static struct v4l2_subdev * +vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad) +{ + struct media_pad *remote = media_entity_remote_source(&video->pad); + + if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + if (pad) + *pad = remote->index; + return media_entity_to_v4l2_subdev(remote->entity); +} + +/* get the format set at output pad of the adjacent subdev */ +static int +__vpfe_video_get_format(struct vpfe_video_device *video, + struct v4l2_format *format) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + struct media_pad *remote; + u32 pad; + int ret; + + subdev = vpfe_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + remote = media_entity_remote_source(&video->pad); + fmt.pad = remote->index; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret == -ENOIOCTLCMD) + return -EINVAL; + + format->type = video->type; + /* convert mbus_format to v4l2_format */ + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); + mbus_to_pix(&fmt.format, &format->fmt.pix); + + return 0; +} + +/* make a note of pipeline details */ +static void vpfe_prepare_pipeline(struct vpfe_video_device *video) +{ + struct media_entity *entity = &video->video_dev.entity; + struct media_device *mdev = entity->parent; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_video_device *far_end = NULL; + struct media_entity_graph graph; + + pipe->input_num = 0; + pipe->output_num = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = video; + else + pipe->outputs[pipe->output_num++] = video; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + while ((entity = media_entity_graph_walk_next(&graph))) { + if (entity == &video->video_dev.entity) + continue; + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + continue; + far_end = to_vpfe_video(media_entity_to_video_device(entity)); + if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = far_end; + else + pipe->outputs[pipe->output_num++] = far_end; + } + mutex_unlock(&mdev->graph_mutex); +} + +/* update pipe state selected by user */ +static int vpfe_update_pipe_state(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret; + + vpfe_prepare_pipeline(video); + + /* Find out if there is any input video + if yes, it is single shot. + */ + if (pipe->input_num == 0) { + pipe->state = VPFE_PIPELINE_STREAM_CONTINUOUS; + ret = vpfe_update_current_ext_subdev(video); + if (ret) { + pr_err("Invalid external subdev\n"); + return ret; + } + } else { + pipe->state = VPFE_PIPELINE_STREAM_SINGLESHOT; + } + video->initialized = 1; + video->skip_frame_count = 1; + video->skip_frame_count_init = 1; + return 0; +} + +/* checks wether pipeline is ready for enabling */ +int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe) +{ + int i; + + for (i = 0; i < pipe->input_num; i++) + if (!pipe->inputs[i]->started || + pipe->inputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + for (i = 0; i < pipe->output_num; i++) + if (!pipe->outputs[i]->started || + pipe->outputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + return 1; +} + +/** + * Validate a pipeline by checking both ends of all links for format + * discrepancies. + * + * Return 0 if all formats match, or -EPIPE if at least one link is found with + * different formats on its two ends. + */ +static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe) +{ + struct v4l2_subdev_format fmt_source; + struct v4l2_subdev_format fmt_sink; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + /* + * Should not matter if it is output[0] or 1 as + * the general ideas is to traverse backwards and + * the fact that the out video node always has the + * format of the connected pad. + */ + subdev = vpfe_video_remote_subdev(pipe->outputs[0], NULL); + if (subdev == NULL) + return -EPIPE; + + while (1) { + /* Retrieve the sink format */ + pad = &subdev->entity.pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_sink.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, + &fmt_sink); + + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Retrieve the source format */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + subdev = media_entity_to_v4l2_subdev(pad->entity); + + fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_source.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Check if the two ends match */ + if (fmt_source.format.code != fmt_sink.format.code || + fmt_source.format.width != fmt_sink.format.width || + fmt_source.format.height != fmt_sink.format.height) + return -EPIPE; + } + return 0; +} + +/* + * vpfe_pipeline_enable() - Enable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: vpfe pipeline + * + * Walk the entities chain starting at the pipeline output video node and start + * all modules in the chain in the given mode. + * + * Return 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + subdev = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + mutex_unlock(&mdev->graph_mutex); + return ret; +} + +/* + * vpfe_pipeline_disable() - Disable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: VPFE pipeline + * + * Walk the entities chain starting at the pipeline output video node and stop + * all modules in the chain. + * + * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module + * can't be stopped. + */ +static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + subdev = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + mutex_unlock(&mdev->graph_mutex); + + return (ret == 0) ? ret : -ETIMEDOUT ; +} + +/* + * vpfe_pipeline_set_stream() - Enable/disable streaming on a pipeline + * @vpfe_dev: VPFE device + * @pipe: VPFE pipeline + * @state: Stream state (stopped or active) + * + * Set the pipeline to the given stream state. + * + * Return 0 if successfull, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_set_stream(struct vpfe_pipeline *pipe, + enum vpfe_pipeline_stream_state state) +{ + if (state == VPFE_PIPELINE_STREAM_STOPPED) + return vpfe_pipeline_disable(pipe); + + return vpfe_pipeline_enable(pipe); +} + +static int all_videos_stopped(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int i; + + for (i = 0; i < pipe->input_num; i++) + if (pipe->inputs[i]->started) + return 0; + for (i = 0; i < pipe->output_num; i++) + if (pipe->outputs[i]->started) + return 0; + return 1; +} + +/* + * vpfe_open() - open video device + * @file: file pointer + * + * initialize media pipeline state, allocate memory for file handle + * + * Return 0 if successful, or the return -ENODEV otherwise. + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_fh *handle; + + /* Allocate memory for the file handle object */ + handle = kzalloc(sizeof(struct vpfe_fh), GFP_KERNEL); + + if (handle == NULL) + return -ENOMEM; + + v4l2_fh_init(&handle->vfh, &video->video_dev); + v4l2_fh_add(&handle->vfh); + + mutex_lock(&video->lock); + /* If decoder is not initialized. initialize it */ + if (!video->initialized && vpfe_update_pipe_state(video)) { + mutex_unlock(&video->lock); + return -ENODEV; + } + /* Increment device users counter */ + video->usrs++; + /* Set io_allowed member to false */ + handle->io_allowed = 0; + v4l2_prio_open(&video->prio, &handle->prio); + handle->video = video; + file->private_data = &handle->vfh; + mutex_unlock(&video->lock); + + return 0; +} + +/* get the next buffer available from dma queue */ +static unsigned long +vpfe_video_get_next_buffer(struct vpfe_video_device *video) +{ + video->cur_frm = video->next_frm = + list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + + list_del(&video->next_frm->list); + video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; + return vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0); +} + +/* schedule the next buffer which is available on dma queue */ +void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + if (list_empty(&video->dma_queue)) + return; + + video->next_frm = list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + + if (VPFE_PIPELINE_STREAM_SINGLESHOT == video->pipe.state) + video->cur_frm = video->next_frm; + + list_del(&video->next_frm->list); + video->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; + addr = vb2_dma_contig_plane_dma_addr(&video->next_frm->vb, 0); + video->ops->queue(vpfe_dev, addr); + video->state = VPFE_VIDEO_BUFFER_QUEUED; +} + +/* schedule the buffer for capturing bottom field */ +void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0); + addr += video->field_off; + video->ops->queue(vpfe_dev, addr); +} + +/* make buffer available for dequeue */ +void vpfe_video_process_buffer_complete(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + + do_gettimeofday(&video->cur_frm->vb.v4l2_buf.timestamp); + vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_DONE); + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + video->cur_frm = video->next_frm; +} + +/* vpfe_stop_capture() - stop streaming */ +static void vpfe_stop_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + + video->started = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return; + if (all_videos_stopped(video)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_STOPPED); +} + +/* + * vpfe_release() - release video device + * @file: file pointer + * + * deletes buffer queue, frees the buffers and the vpfe file handle + * + * Return 0 + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct v4l2_fh *vfh = file->private_data; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = container_of(vfh, struct vpfe_fh, vfh); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&video->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (video->started) { + vpfe_stop_capture(video); + /* mark pipe state as stopped in vpfe_release(), + as app might call streamon() after streamoff() + in which case driver has to start streaming. + */ + video->pipe.state = VPFE_PIPELINE_STREAM_STOPPED; + vb2_streamoff(&video->buffer_queue, + video->buffer_queue.type); + } + video->io_usrs = 0; + /* Free buffers allocated */ + vb2_queue_release(&video->buffer_queue); + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); + } + /* Decrement device users counter */ + video->usrs--; + /* Close the priority */ + v4l2_prio_close(&video->prio, fh->prio); + /* If this is the last file handle */ + if (!video->usrs) + video->initialized = 0; + mutex_unlock(&video->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + v4l2_fh_del(vfh); + kzfree(fh); + return 0; +} + +/* + * vpfe_mmap() - It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + return vb2_mmap(&video->buffer_queue, vma); +} + +/* + * vpfe_poll() - It is used for select/poll system call + */ +static unsigned int vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + if (video->started) + return vb2_poll(&video->buffer_queue, file, wait); + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_querycap() - query capabilities of video device + * @file: file pointer + * @priv: void pointer + * @cap: pointer to v4l2_capability structure + * + * fills v4l2 capabilities structure + * + * Return 0 + */ +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + cap->device_caps = cap->capabilities; + cap->version = VPFE_CAPTURE_VERSION_CODE; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + + return 0; +} + +/* + * vpfe_g_fmt() - get the format which is active on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * fills v4l2 format structure with active format + * + * Return 0 + */ +static int vpfe_g_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt\n"); + /* Fill in the information about format */ + *fmt = video->fmt; + return 0; +} + +/* + * vpfe_enum_fmt() - enum formats supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_fmtdesc structure with output format set on adjacent subdev, + * only one format is enumearted as subdevs are already configured + * + * Return 0 if successfull, error code otherwise + */ +static int vpfe_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt mbus; + struct v4l2_subdev *subdev; + struct v4l2_format format; + struct media_pad *remote; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt\n"); + + /* since already subdev pad format is set, + only one pixel format is available */ + if (fmt->index > 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid index\n"); + return -EINVAL; + } + /* get the remote pad */ + remote = media_entity_remote_source(&video->pad); + if (remote == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote pad for video node\n"); + return -EINVAL; + } + /* get the remote subdev */ + subdev = vpfe_video_remote_subdev(video, NULL); + if (subdev == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote subdev for video node\n"); + return -EINVAL; + } + sd_fmt.pad = remote->index; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + /* get output format of remote subdev */ + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &sd_fmt); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "invalid remote subdev for video node\n"); + return ret; + } + /* convert to pix format */ + mbus.code = sd_fmt.format.code; + mbus_to_pix(&mbus, &format.fmt.pix); + /* copy the result */ + fmt->pixelformat = format.fmt.pix.pixelformat; + + return 0; +} + +/* + * vpfe_s_fmt() - set the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate and set the format on video device + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt\n"); + /* If streaming is started, return error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + *fmt = format; + video->fmt = *fmt; + return 0; +} + +/* + * vpfe_try_fmt() - try the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate the format, update with correct format + * based on output format set on adjacent subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt\n"); + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + + *fmt = format; + return 0; +} + +/* + * vpfe_enum_input() - enum inputs supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_input structure with input available on media chain, + * only one input is enumearted as media chain is setup by this time + * + * Return 0 if successfull, -EINVAL is media chain is invalid + */ +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_ext_subdev_info *sdinfo = video->current_ext_subdev; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + /* enumerate from the subdev user has choosen through mc */ + if (inp->index < sdinfo->num_inputs) { + memcpy(inp, &sdinfo->inputs[inp->index], + sizeof(struct v4l2_input)); + return 0; + } + return -EINVAL; +} + +/* + * vpfe_g_input() - get index of the input which is active + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set index with input index which is active + */ +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + *index = video->current_input; + return 0; +} + +/* + * vpfe_s_input() - set input which is pointed by input index + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set input on external subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_route *route; + struct v4l2_input *inps; + u32 output; + u32 input; + int ret; + int i; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + /* + * If streaming is started return device busy + * error + */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + + sdinfo = video->current_ext_subdev; + if (!sdinfo->registered) { + ret = -EINVAL; + goto unlock_out; + } + if (vpfe_dev->cfg->setup_input && + vpfe_dev->cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "couldn't setup input for %s\n", + sdinfo->module_name); + goto unlock_out; + } + route = &sdinfo->routes[index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, + s_routing, input, output, 0); + if (ret) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "s_input:error in setting input in decoder\n"); + ret = -EINVAL; + goto unlock_out; + } + } + /* set standards set by subdev in video device */ + for (i = 0; i < sdinfo->num_inputs; i++) { + inps = &sdinfo->inputs[i]; + video->video_dev.tvnorms |= inps->std; + } + video->current_input = index; +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querystd() - query std which is being input on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * call external subdev through v4l2_device_call_until_err to + * get the std that is being active. + * + * Return 0 on success, error code otherwise + */ +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&video->lock); + sdinfo = video->current_ext_subdev; + if (ret) + return ret; + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_s_std() - set std on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * set std pointed by std_id on external subdev by calling it using + * v4l2_device_call_until_err + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + sdinfo = video->current_ext_subdev; + /* If streaming is started, return device busy error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_std, *std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + video->stdid = V4L2_STD_UNKNOWN; + goto unlock_out; + } + video->stdid = *std_id; +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); + *tvnorm = video->stdid; + return 0; +} + +/* + * vpfe_enum_dv_timings() - enumerate dv_timings which are supported by + * to external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_enum_dv_timings structure + * + * enum dv_timings's which are supported by external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_enum_dv_timings(struct file *file, void *fh, + struct v4l2_enum_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_dv_timings\n"); + return v4l2_subdev_call(subdev, video, enum_dv_timings, timings); +} + +/* + * vpfe_query_dv_timings() - query the dv_timings which is being input + * to external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * get dv_timings which is being input on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_query_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_query_dv_timings\n"); + return v4l2_subdev_call(subdev, video, query_dv_timings, timings); +} + +/* + * vpfe_s_dv_timings() - set dv_preset on external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * set dv_timings pointed by preset on external subdev through + * v4l2_device_call_until_err, this configures amplifier also + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_s_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_dv_timings\n"); + + video->stdid = V4L2_STD_UNKNOWN; + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + video->current_ext_subdev->grp_id, + video, s_dv_timings, timings); +} + +/* + * vpfe_g_dv_timings() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @timings: pointer to v4l2_dv_timings structure + * + * get dv_preset which is set on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int +vpfe_g_dv_timings(struct file *file, void *fh, + struct v4l2_dv_timings *timings) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_dv_timings\n"); + return v4l2_subdev_call(subdev, video, g_dv_timings, timings); +} + +/* + * Videobuf operations + */ +/* + * vpfe_buffer_queue_setup : Callback function for buffer setup. + * @vq: vb2_queue ptr + * @fmt: v4l2 format + * @nbuffers: ptr to number of buffers requested by application + * @nplanes:: contains number of distinct video planes needed to hold a frame + * @sizes[]: contains the size (in bytes) of each plane. + * @alloc_ctxs: ptr to allocation context + * + * This callback function is called when reqbuf() is called to adjust + * the buffer nbuffers and buffer size + */ +static int +vpfe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + unsigned long size; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue_setup\n"); + size = video->fmt.fmt.pix.sizeimage; + + if (vpfe_dev->video_limit) { + while (size * *nbuffers > vpfe_dev->video_limit) + (*nbuffers)--; + } + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) { + if (*nbuffers < MIN_NUM_BUFFERS) + *nbuffers = MIN_NUM_BUFFERS; + } + *nplanes = 1; + sizes[0] = size; + alloc_ctxs[0] = video->alloc_ctx; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "nbuffers=%d, size=%lu\n", *nbuffers, size); + return 0; +} + +/* + * vpfe_buffer_prepare : callback function for buffer prepare + * @vb: ptr to vb2_buffer + * + * This is the callback function for buffer prepare when vb2_qbuf() + * function is called. The buffer is prepared and user space virtual address + * or user address is converted into physical address + */ +static int vpfe_buffer_prepare(struct vb2_buffer *vb) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); + + if (vb->state != VB2_BUF_STATE_ACTIVE && + vb->state != VB2_BUF_STATE_PREPARED) + return 0; + + /* Initialize buffer */ + vb2_set_plane_payload(vb, 0, video->fmt.fmt.pix.sizeimage); + if (vb2_plane_vaddr(vb, 0) && + vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) + return -EINVAL; + + addr = vb2_dma_contig_plane_dma_addr(vb, 0); + /* Make sure user addresses are aligned to 32 bytes */ + if (!ALIGN(addr, 32)) + return -EINVAL; + + return 0; +} + +static void vpfe_buffer_queue(struct vb2_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + unsigned long flags; + unsigned long empty; + unsigned long addr; + + spin_lock_irqsave(&video->dma_queue_lock, flags); + empty = list_empty(&video->dma_queue); + /* add the buffer to the DMA queue */ + list_add_tail(&buf->list, &video->dma_queue); + spin_unlock_irqrestore(&video->dma_queue_lock, flags); + /* this case happens in case of single shot */ + if (empty && video->started && pipe->state == + VPFE_PIPELINE_STREAM_SINGLESHOT && + video->state == VPFE_VIDEO_BUFFER_NOT_QUEUED) { + spin_lock(&video->dma_queue_lock); + addr = vpfe_video_get_next_buffer(video); + video->ops->queue(vpfe_dev, addr); + + video->state = VPFE_VIDEO_BUFFER_QUEUED; + spin_unlock(&video->dma_queue_lock); + + /* enable h/w each time in single shot */ + if (vpfe_video_is_pipe_ready(pipe)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_SINGLESHOT); + } +} + +/* vpfe_start_capture() - start streaming on all the subdevs */ +static int vpfe_start_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret = 0; + + video->started = 1; + if (vpfe_video_is_pipe_ready(pipe)) + ret = vpfe_pipeline_set_stream(pipe, pipe->state); + + return ret; +} + +static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + int ret; + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + goto streamoff; + + /* Get the next frame from the buffer queue */ + video->cur_frm = video->next_frm = + list_entry(video->dma_queue.next, struct vpfe_cap_buffer, list); + /* Remove buffer from the buffer queue */ + list_del(&video->cur_frm->list); + /* Mark state of the current frame to active */ + video->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; + /* Initialize field_id and started member */ + video->field_id = 0; + addr = vb2_dma_contig_plane_dma_addr(&video->cur_frm->vb, 0); + video->ops->queue(vpfe_dev, addr); + video->state = VPFE_VIDEO_BUFFER_QUEUED; + + ret = vpfe_start_capture(video); + if (ret) + goto unlock_out; + + mutex_unlock(&video->lock); + + return ret; +unlock_out: + mutex_unlock(&video->lock); +streamoff: + ret = vb2_streamoff(&video->buffer_queue, video->buffer_queue.type); + return 0; +} + +static int vpfe_buffer_init(struct vb2_buffer *vb) +{ + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + + INIT_LIST_HEAD(&buf->list); + return 0; +} + +/* abort streaming and wait for last buffer */ +static int vpfe_stop_streaming(struct vb2_queue *vq) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vq); + struct vpfe_video_device *video = fh->video; + + if (!vb2_is_streaming(vq)) + return 0; + /* release all active buffers */ + while (!list_empty(&video->dma_queue)) { + video->next_frm = list_entry(video->dma_queue.next, + struct vpfe_cap_buffer, list); + list_del(&video->next_frm->list); + vb2_buffer_done(&video->next_frm->vb, VB2_BUF_STATE_ERROR); + } + return 0; +} + +static void vpfe_buf_cleanup(struct vb2_buffer *vb) +{ + struct vpfe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_cap_buffer *buf = container_of(vb, + struct vpfe_cap_buffer, vb); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buf_cleanup\n"); + if (vb->state == VB2_BUF_STATE_ACTIVE) + list_del_init(&buf->list); +} + +static struct vb2_ops video_qops = { + .queue_setup = vpfe_buffer_queue_setup, + .buf_init = vpfe_buffer_init, + .buf_prepare = vpfe_buffer_prepare, + .start_streaming = vpfe_start_streaming, + .stop_streaming = vpfe_stop_streaming, + .buf_cleanup = vpfe_buf_cleanup, + .buf_queue = vpfe_buffer_queue, +}; + +/* + * vpfe_reqbufs() - supported REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + struct vb2_queue *q; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + if (video->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + video->memory = req_buf->memory; + + /* Initialize videobuf2 queue as per the buffer type */ + video->alloc_ctx = vb2_dma_contig_init_ctx(vpfe_dev->pdev); + if (IS_ERR(video->alloc_ctx)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to get the context\n"); + return PTR_ERR(video->alloc_ctx); + } + + q = &video->buffer_queue; + q->type = req_buf->type; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->drv_priv = fh; + q->ops = &video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct vpfe_cap_buffer); + + ret = vb2_queue_init(q); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, "vb2_queue_init() failed\n"); + vb2_dma_contig_cleanup_ctx(vpfe_dev->pdev); + return ret; + } + + fh->io_allowed = 1; + video->io_usrs = 1; + INIT_LIST_HEAD(&video->dma_queue); + ret = vb2_reqbufs(&video->buffer_queue, req_buf); + +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querybuf() - query buffers for exchange + */ +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (video->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + + /* Call vb2_querybuf to get information */ + return vb2_querybuf(&video->buffer_queue, buf); +} + +/* + * vpfe_qbuf() - queue buffers for capture or processing + */ +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + return vb2_qbuf(&video->buffer_queue, p); +} + +/* + * vpfe_dqbuf() - deque buffer which is done with processing + */ +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return vb2_dqbuf(&video->buffer_queue, + buf, (file->f_flags & O_NONBLOCK)); +} + +/* + * vpfe_streamon() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * queue buffer onto hardware for capture/processing and + * start all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_fh *fh = file->private_data; + struct vpfe_ext_subdev_info *sdinfo; + int ret = -EINVAL; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return ret; + } + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + sdinfo = video->current_ext_subdev; + /* If buffer queue is empty, return error */ + if (list_empty(&video->buffer_queue.queued_list)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + /* Validate the pipeline */ + if (V4L2_BUF_TYPE_VIDEO_CAPTURE == buf_type) { + ret = vpfe_video_validate_pipeline(pipe); + if (ret < 0) + return ret; + } + /* Call vb2_streamon to start streaming */ + return vb2_streamon(&video->buffer_queue, buf_type); +} + +/* + * vpfe_streamoff() - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * stop all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + buf_type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device is not started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + vpfe_stop_capture(video); + ret = vb2_streamoff(&video->buffer_queue, buf_type); + mutex_unlock(&video->lock); + + return ret; +} + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt, + .vidioc_g_fmt_vid_out = vpfe_g_fmt, + .vidioc_s_fmt_vid_out = vpfe_s_fmt, + .vidioc_try_fmt_vid_out = vpfe_try_fmt, + .vidioc_enum_fmt_vid_out = vpfe_enum_fmt, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + .vidioc_enum_dv_timings = vpfe_enum_dv_timings, + .vidioc_query_dv_timings = vpfe_query_dv_timings, + .vidioc_s_dv_timings = vpfe_s_dv_timings, + .vidioc_g_dv_timings = vpfe_g_dv_timings, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, +}; + +/* VPFE video init function */ +int vpfe_video_init(struct vpfe_video_device *video, const char *name) +{ + const char *direction; + int ret; + + switch (video->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + direction = "output"; + video->pad.flags = MEDIA_PAD_FL_SINK; + video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + direction = "input"; + video->pad.flags = MEDIA_PAD_FL_SOURCE; + video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + break; + + default: + return -EINVAL; + } + /* Initialize field of video device */ + video->video_dev.release = video_device_release; + video->video_dev.fops = &vpfe_fops; + video->video_dev.ioctl_ops = &vpfe_ioctl_ops; + video->video_dev.minor = -1; + video->video_dev.tvnorms = 0; + snprintf(video->video_dev.name, sizeof(video->video_dev.name), + "DAVINCI VIDEO %s %s", name, direction); + + /* Initialize prio member of device object */ + v4l2_prio_init(&video->prio); + spin_lock_init(&video->irqlock); + spin_lock_init(&video->dma_queue_lock); + mutex_init(&video->lock); + ret = media_entity_init(&video->video_dev.entity, + 1, &video->pad, 0); + if (ret < 0) + return ret; + + video_set_drvdata(&video->video_dev, video); + + return 0; +} + +/* vpfe video device register function */ +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev) +{ + int ret; + + video->video_dev.v4l2_dev = vdev; + + ret = video_register_device(&video->video_dev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + pr_err("%s: could not register video device (%d)\n", + __func__, ret); + return ret; +} + +/* vpfe video device unregister function */ +void vpfe_video_unregister(struct vpfe_video_device *video) +{ + if (video_is_registered(&video->video_dev)) { + media_entity_cleanup(&video->video_dev.entity); + video_unregister_device(&video->video_dev); + } +} diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.h b/drivers/staging/media/davinci_vpfe/vpfe_video.h new file mode 100644 index 000000000000..bf8af01d4a1b --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/vpfe_video.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_VIDEO_H +#define _DAVINCI_VPFE_VIDEO_H + +#include + +struct vpfe_device; + +/* + * struct vpfe_video_operations - VPFE video operations + * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF + * if there was no buffer previously queued. + */ +struct vpfe_video_operations { + int(*queue) (struct vpfe_device *vpfe_dev, unsigned long addr); +}; + +enum vpfe_pipeline_stream_state { + VPFE_PIPELINE_STREAM_STOPPED = 0, + VPFE_PIPELINE_STREAM_CONTINUOUS = 1, + VPFE_PIPELINE_STREAM_SINGLESHOT = 2, +}; + +enum vpfe_video_state { + /* indicates that buffer is not queued */ + VPFE_VIDEO_BUFFER_NOT_QUEUED = 0, + /* indicates that buffer is queued */ + VPFE_VIDEO_BUFFER_QUEUED = 1, +}; + +struct vpfe_pipeline { + /* media pipeline */ + struct media_pipeline *pipe; + /* state of the pipeline, continuous, + * single-shot or stopped + */ + enum vpfe_pipeline_stream_state state; + /* number of active input video entities */ + unsigned int input_num; + /* number of active output video entities */ + unsigned int output_num; + /* input video nodes in case of single-shot mode */ + struct vpfe_video_device *inputs[10]; + /* capturing video nodes */ + struct vpfe_video_device *outputs[10]; +}; + +#define to_vpfe_pipeline(__e) \ + container_of((__e)->pipe, struct vpfe_pipeline, pipe) + +#define to_vpfe_video(vdev) \ + container_of(vdev, struct vpfe_video_device, video_dev) + +struct vpfe_cap_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +struct vpfe_video_device { + /* vpfe device */ + struct vpfe_device *vpfe_dev; + /* video dev */ + struct video_device video_dev; + /* media pad of video entity */ + struct media_pad pad; + /* video operations supported by video device */ + const struct vpfe_video_operations *ops; + /* type of the video buffers used by user */ + enum v4l2_buf_type type; + /* Indicates id of the field which is being captured */ + u32 field_id; + /* pipeline for which video device is part of */ + struct vpfe_pipeline pipe; + /* Indicates whether streaming started */ + u8 started; + /* Indicates state of the stream */ + unsigned int state; + /* current input at the sub device */ + int current_input; + /* + * This field keeps track of type of buffer exchange mechanism + * user has selected + */ + enum v4l2_memory memory; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* number of open instances of the channel */ + u32 usrs; + /* flag to indicate whether decoder is initialized */ + u8 initialized; + /* skip frame count */ + u8 skip_frame_count; + /* skip frame count init value */ + u8 skip_frame_count_init; + /* time per frame for skipping */ + struct v4l2_fract timeperframe; + /* ptr to currently selected sub device */ + struct vpfe_ext_subdev_info *current_ext_subdev; + /* Pointer pointing to current vpfe_cap_buffer */ + struct vpfe_cap_buffer *cur_frm; + /* Pointer pointing to next vpfe_cap_buffer */ + struct vpfe_cap_buffer *next_frm; + /* Used to store pixel format */ + struct v4l2_format fmt; + struct vb2_queue buffer_queue; + /* allocator-specific contexts for each plane */ + struct vb2_alloc_ctx *alloc_ctx; + /* Queue of filled frames */ + struct list_head dma_queue; + spinlock_t irqlock; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* lock used to access this structure */ + struct mutex lock; + /* number of users performing IO */ + u32 io_usrs; + /* Currently selected or default standard */ + v4l2_std_id stdid; + /* + * offset where second field starts from the starting of the + * buffer for field seperated YCbCr formats + */ + u32 field_off; +}; + +int vpfe_video_is_pipe_ready(struct vpfe_pipeline *pipe); +void vpfe_video_unregister(struct vpfe_video_device *video); +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev); +int vpfe_video_init(struct vpfe_video_device *video, const char *name); +void vpfe_video_process_buffer_complete(struct vpfe_video_device *video); +void vpfe_video_schedule_bottom_field(struct vpfe_video_device *video); +void vpfe_video_schedule_next_buffer(struct vpfe_video_device *video); + +#endif /* _DAVINCI_VPFE_VIDEO_H */ From f7fa454dc1771155a2ceaf490e1dec9d4c2613bd Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:06:17 -0300 Subject: [PATCH 0294/4607] [media] davinci: vpfe: dm365: add IPIPEIF driver based on media framework add support for dm365 IPIPEIF driver based on media framework. The IPIPEIF is exposed as a subdev, and it supports features like fault pixel correction, dark frame subtraction and other necessary hardware setup. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/davinci_vpfe/dm365_ipipeif.c | 1071 +++++++++++++++++ .../media/davinci_vpfe/dm365_ipipeif.h | 233 ++++ .../media/davinci_vpfe/dm365_ipipeif_user.h | 93 ++ 3 files changed, 1397 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif.c create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif.h create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c new file mode 100644 index 000000000000..c8cae51d3d00 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c @@ -0,0 +1,1071 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#include "dm365_ipipeif.h" +#include "vpfe_mc_capture.h" + +static const unsigned int ipipeif_input_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SBGGR8_1X8, +}; + +static const unsigned int ipipeif_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SBGGR8_1X8, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, +}; + +static int +ipipeif_get_pack_mode(enum v4l2_mbus_pixelcode in_pix_fmt) +{ + switch (in_pix_fmt) { + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + return IPIPEIF_5_1_PACK_8_BIT; + + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + return IPIPEIF_5_1_PACK_8_BIT_A_LAW; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + return IPIPEIF_5_1_PACK_16_BIT; + + case V4L2_MBUS_FMT_SBGGR12_1X12: + return IPIPEIF_5_1_PACK_12_BIT; + + default: + return IPIPEIF_5_1_PACK_16_BIT; + } +} + +static inline u32 ipipeif_read(void *addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline void ipipeif_write(u32 val, void *addr, u32 offset) +{ + writel(val, addr + offset); +} + +static void ipipeif_config_dpc(void *addr, struct ipipeif_dpc *dpc) +{ + u32 val = 0; + + if (dpc->en) { + val = (dpc->en & 1) << IPIPEIF_DPC2_EN_SHIFT; + val |= dpc->thr & IPIPEIF_DPC2_THR_MASK; + } + ipipeif_write(val, addr, IPIPEIF_DPC2); +} + +#define IPIPEIF_MODE_CONTINUOUS 0 +#define IPIPEIF_MODE_ONE_SHOT 1 + +static int get_oneshot_mode(enum ipipeif_input_entity input) +{ + if (input == IPIPEIF_INPUT_MEMORY) + return IPIPEIF_MODE_ONE_SHOT; + else if (input == IPIPEIF_INPUT_ISIF) + return IPIPEIF_MODE_CONTINUOUS; + + return -EINVAL; +} + +static int +ipipeif_get_cfg_src1(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + if (ipipeif->input == IPIPEIF_INPUT_MEMORY && + (informat->code == V4L2_MBUS_FMT_Y8_1X8 || + informat->code == V4L2_MBUS_FMT_UV8_1X8)) + return IPIPEIF_CCDC; + + return IPIPEIF_SRC1_PARALLEL_PORT; +} + +static int +ipipeif_get_data_shift(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + + switch (informat->code) { + case V4L2_MBUS_FMT_SGRBG12_1X12: + return IPIPEIF_5_1_BITS11_0; + + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + return IPIPEIF_5_1_BITS11_0; + + default: + return IPIPEIF_5_1_BITS7_0; + } +} + +static enum ipipeif_input_source +ipipeif_get_source(struct vpfe_ipipeif_device *ipipeif) +{ + struct v4l2_mbus_framefmt *informat; + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + if (ipipeif->input == IPIPEIF_INPUT_ISIF) + return IPIPEIF_CCDC; + + if (informat->code == V4L2_MBUS_FMT_UYVY8_2X8) + return IPIPEIF_SDRAM_YUV; + + return IPIPEIF_SDRAM_RAW; +} + +void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif) +{ + struct vpfe_video_device *video_in = &ipipeif->video_in; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return; + + spin_lock(&video_in->dma_queue_lock); + vpfe_video_process_buffer_complete(video_in); + video_in->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_in); + spin_unlock(&video_in->dma_queue_lock); +} + +int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + + return ipipeif->config.decimation; +} + +int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + + return ipipeif->config.rsz; +} + +#define RD_DATA_15_2 0x7 + +/* + * ipipeif_hw_setup() - This function sets up IPIPEIF + * @sd: pointer to v4l2 subdev structure + * return -EINVAL or zero on success + */ +static int ipipeif_hw_setup(struct v4l2_subdev *sd) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *informat, *outformat; + struct ipipeif_params params = ipipeif->config; + enum ipipeif_input_source ipipeif_source; + enum v4l2_mbus_pixelcode isif_port_if; + void *ipipeif_base_addr; + unsigned int val; + int data_shift; + int pack_mode; + int source1; + + ipipeif_base_addr = ipipeif->ipipeif_base_addr; + + /* Enable clock to IPIPEIF and IPIPE */ + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + + informat = &ipipeif->formats[IPIPEIF_PAD_SINK]; + outformat = &ipipeif->formats[IPIPEIF_PAD_SOURCE]; + + /* Combine all the fields to make CFG1 register of IPIPEIF */ + val = get_oneshot_mode(ipipeif->input); + if (val < 0) { + pr_err("ipipeif: links setup required"); + return -EINVAL; + } + val = val << ONESHOT_SHIFT; + + ipipeif_source = ipipeif_get_source(ipipeif); + val |= ipipeif_source << INPSRC_SHIFT; + + val |= params.clock_select << CLKSEL_SHIFT; + val |= params.avg_filter << AVGFILT_SHIFT; + val |= params.decimation << DECIM_SHIFT; + + pack_mode = ipipeif_get_pack_mode(informat->code); + val |= pack_mode << PACK8IN_SHIFT; + + source1 = ipipeif_get_cfg_src1(ipipeif); + val |= source1 << INPSRC1_SHIFT; + + data_shift = ipipeif_get_data_shift(ipipeif); + if (ipipeif_source != IPIPEIF_SDRAM_YUV) + val |= data_shift << DATASFT_SHIFT; + else + val &= ~(RD_DATA_15_2 << DATASFT_SHIFT); + + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG1); + + switch (ipipeif_source) { + case IPIPEIF_CCDC: + ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN); + break; + + case IPIPEIF_SDRAM_RAW: + case IPIPEIF_CCDC_DARKFM: + ipipeif_write(ipipeif->gain, ipipeif_base_addr, IPIPEIF_GAIN); + /* fall through */ + case IPIPEIF_SDRAM_YUV: + val |= data_shift << DATASFT_SHIFT; + ipipeif_write(params.ppln, ipipeif_base_addr, IPIPEIF_PPLN); + ipipeif_write(params.lpfr, ipipeif_base_addr, IPIPEIF_LPFR); + ipipeif_write(informat->width, ipipeif_base_addr, IPIPEIF_HNUM); + ipipeif_write(informat->height, + ipipeif_base_addr, IPIPEIF_VNUM); + break; + + default: + return -EINVAL; + } + + /*check if decimation is enable or not */ + if (params.decimation) + ipipeif_write(params.rsz, ipipeif_base_addr, IPIPEIF_RSZ); + + /* Setup sync alignment and initial rsz position */ + val = params.if_5_1.align_sync & 1; + val <<= IPIPEIF_INIRSZ_ALNSYNC_SHIFT; + val |= params.if_5_1.rsz_start & IPIPEIF_INIRSZ_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_INIRSZ); + isif_port_if = informat->code; + + if (isif_port_if == V4L2_MBUS_FMT_Y8_1X8) + isif_port_if = V4L2_MBUS_FMT_YUYV8_1X16; + else if (isif_port_if == V4L2_MBUS_FMT_UV8_1X8) + isif_port_if = V4L2_MBUS_FMT_SGRBG12_1X12; + + /* Enable DPCM decompression */ + switch (ipipeif_source) { + case IPIPEIF_SDRAM_RAW: + val = 0; + if (outformat->code == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) { + val = 1; + val |= (IPIPEIF_DPCM_8BIT_10BIT & 1) << + IPIPEIF_DPCM_BITS_SHIFT; + val |= (ipipeif->dpcm_predictor & 1) << + IPIPEIF_DPCM_PRED_SHIFT; + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DPCM); + + /* set DPC */ + ipipeif_config_dpc(ipipeif_base_addr, ¶ms.if_5_1.dpc); + + ipipeif_write(params.if_5_1.clip, + ipipeif_base_addr, IPIPEIF_OCLIP); + + /* fall through for SDRAM YUV mode */ + /* configure CFG2 */ + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CFG2); + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_Y8_1X8: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + + default: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + RESETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + } + + case IPIPEIF_SDRAM_YUV: + /* Set clock divider */ + if (params.clock_select == IPIPEIF_SDRAM_CLK) { + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_CLKDIV); + val |= (params.if_5_1.clk_div.m - 1) << + IPIPEIF_CLKDIV_M_SHIFT; + val |= (params.if_5_1.clk_div.n - 1); + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CLKDIV); + } + break; + + case IPIPEIF_CCDC: + case IPIPEIF_CCDC_DARKFM: + /* set DPC */ + ipipeif_config_dpc(ipipeif_base_addr, ¶ms.if_5_1.dpc); + + /* Set DF gain & threshold control */ + val = 0; + if (params.if_5_1.df_gain_en) { + val = params.if_5_1.df_gain_thr & + IPIPEIF_DF_GAIN_THR_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGTH); + val = (params.if_5_1.df_gain_en & 1) << + IPIPEIF_DF_GAIN_EN_SHIFT; + val |= params.if_5_1.df_gain & + IPIPEIF_DF_GAIN_MASK; + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_DFSGVL); + /* configure CFG2 */ + val = VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_HDPOL_SHIFT; + val |= VPFE_PINPOL_POSITIVE << IPIPEIF_CFG2_VDPOL_SHIFT; + + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + break; + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + SETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + val |= IPIPEIF_CBCR_Y << IPIPEIF_CFG2_YUV8P_SHIFT; + break; + + default: + /* Bayer */ + ipipeif_write(params.if_5_1.clip, ipipeif_base_addr, + IPIPEIF_OCLIP); + } + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int +ipipeif_set_config(struct v4l2_subdev *sd, struct ipipeif_params *config) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct device *dev = ipipeif->subdev.v4l2_dev->dev; + + if (!config) { + dev_err(dev, "Invalid configuration pointer\n"); + return -EINVAL; + } + + ipipeif->config.clock_select = config->clock_select; + ipipeif->config.ppln = config->ppln; + ipipeif->config.lpfr = config->lpfr; + ipipeif->config.rsz = config->rsz; + ipipeif->config.decimation = config->decimation; + if (ipipeif->config.decimation && + (ipipeif->config.rsz < IPIPEIF_RSZ_MIN || + ipipeif->config.rsz > IPIPEIF_RSZ_MAX)) { + dev_err(dev, "rsz range is %d to %d\n", + IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); + return -EINVAL; + } + + ipipeif->config.avg_filter = config->avg_filter; + + ipipeif->config.if_5_1.df_gain_thr = config->if_5_1.df_gain_thr; + ipipeif->config.if_5_1.df_gain = config->if_5_1.df_gain; + ipipeif->config.if_5_1.df_gain_en = config->if_5_1.df_gain_en; + + ipipeif->config.if_5_1.rsz_start = config->if_5_1.rsz_start; + ipipeif->config.if_5_1.align_sync = config->if_5_1.align_sync; + ipipeif->config.if_5_1.clip = config->if_5_1.clip; + + ipipeif->config.if_5_1.dpc.en = config->if_5_1.dpc.en; + ipipeif->config.if_5_1.dpc.thr = config->if_5_1.dpc.thr; + + ipipeif->config.if_5_1.clk_div.m = config->if_5_1.clk_div.m; + ipipeif->config.if_5_1.clk_div.n = config->if_5_1.clk_div.n; + + return 0; +} + +static int +ipipeif_get_config(struct v4l2_subdev *sd, void __user *arg) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct ipipeif_params *config = (struct ipipeif_params *)arg; + struct device *dev = ipipeif->subdev.v4l2_dev->dev; + + if (!arg) { + dev_err(dev, "Invalid configuration pointer\n"); + return -EINVAL; + } + + config->clock_select = ipipeif->config.clock_select; + config->ppln = ipipeif->config.ppln; + config->lpfr = ipipeif->config.lpfr; + config->rsz = ipipeif->config.rsz; + config->decimation = ipipeif->config.decimation; + config->avg_filter = ipipeif->config.avg_filter; + + config->if_5_1.df_gain_thr = ipipeif->config.if_5_1.df_gain_thr; + config->if_5_1.df_gain = ipipeif->config.if_5_1.df_gain; + config->if_5_1.df_gain_en = ipipeif->config.if_5_1.df_gain_en; + + config->if_5_1.rsz_start = ipipeif->config.if_5_1.rsz_start; + config->if_5_1.align_sync = ipipeif->config.if_5_1.align_sync; + config->if_5_1.clip = ipipeif->config.if_5_1.clip; + + config->if_5_1.dpc.en = ipipeif->config.if_5_1.dpc.en; + config->if_5_1.dpc.thr = ipipeif->config.if_5_1.dpc.thr; + + config->if_5_1.clk_div.m = ipipeif->config.if_5_1.clk_div.m; + config->if_5_1.clk_div.n = ipipeif->config.if_5_1.clk_div.n; + + return 0; +} + +/* + * ipipeif_ioctl() - Handle ipipeif module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long ipipeif_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + struct ipipeif_params *config = (struct ipipeif_params *)arg; + int ret = -ENOIOCTLCMD; + + switch (cmd) { + case VIDIOC_VPFE_IPIPEIF_S_CONFIG: + ret = ipipeif_set_config(sd, config); + break; + + case VIDIOC_VPFE_IPIPEIF_G_CONFIG: + ret = ipipeif_get_config(sd, arg); + break; + } + return ret; +} + +/* + * ipipeif_s_ctrl() - Handle set control subdev method + * @ctrl: pointer to v4l2 control structure + */ +static int ipipeif_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_ipipeif_device *ipipeif = + container_of(ctrl->handler, struct vpfe_ipipeif_device, ctrls); + + switch (ctrl->id) { + case VPFE_CID_DPCM_PREDICTOR: + ipipeif->dpcm_predictor = ctrl->val; + break; + + case V4L2_CID_GAIN: + ipipeif->gain = ctrl->val; + break; + + default: + return -EINVAL; + } + + return 0; +} + +#define ENABLE_IPIPEIF 0x1 + +void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + void *ipipeif_base_addr = ipipeif->ipipeif_base_addr; + unsigned char val; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return; + + do { + val = ipipeif_read(ipipeif_base_addr, IPIPEIF_ENABLE); + } while (val & 0x1); + + ipipeif_write(ENABLE_IPIPEIF, ipipeif_base_addr, IPIPEIF_ENABLE); +} + +/* + * ipipeif_set_stream() - Enable/Disable streaming on ipipeif subdev + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int ipipeif_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif); + int ret = 0; + + if (!enable) + return ret; + + ret = ipipeif_hw_setup(sd); + if (!ret) + vpfe_ipipeif_enable(vpfe_dev); + + return ret; +} + +/* + * ipipeif_enum_mbus_code() - Handle pixel format enumeration + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * return -EINVAL or zero on success + */ +static int ipipeif_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case IPIPEIF_PAD_SINK: + if (code->index >= ARRAY_SIZE(ipipeif_input_fmts)) + return -EINVAL; + + code->code = ipipeif_input_fmts[code->index]; + break; + + case IPIPEIF_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(ipipeif_output_fmts)) + return -EINVAL; + + code->code = ipipeif_output_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipeif_get_format() - Handle get format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + */ +static int +ipipeif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + fmt->format = ipipeif->formats[fmt->pad]; + else + fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad)); + + return 0; +} + +#define MIN_OUT_WIDTH 32 +#define MIN_OUT_HEIGHT 32 + +/* + * ipipeif_try_format() - Handle try format by pad subdev method + * @ipipeif: VPFE ipipeif device. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which : wanted subdev format + */ +static void +ipipeif_try_format(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + if (pad == IPIPEIF_PAD_SINK) { + for (i = 0; i < ARRAY_SIZE(ipipeif_input_fmts); i++) + if (fmt->code == ipipeif_input_fmts[i]) + break; + + /* If not found, use SBGGR10 as default */ + if (i >= ARRAY_SIZE(ipipeif_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG12_1X12; + } else if (pad == IPIPEIF_PAD_SOURCE) { + for (i = 0; i < ARRAY_SIZE(ipipeif_output_fmts); i++) + if (fmt->code == ipipeif_output_fmts[i]) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(ipipeif_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + } + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width); + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height); +} + +static int +ipipeif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ipipeif_try_format(ipipeif, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ipipeif_try_format(ipipeif, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * __ipipeif_get_format() - helper function for getting ipipeif format + * @ipipeif: pointer to ipipeif private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + * + */ +static struct v4l2_mbus_framefmt * +__ipipeif_get_format(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_subdev_fh *fh, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + + return &ipipeif->formats[pad]; +} + +/* + * ipipeif_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int +ipipeif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipeif_get_format(ipipeif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ipipeif_try_format(ipipeif, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == IPIPEIF_PAD_SINK && + ipipeif->input != IPIPEIF_INPUT_NONE) + ipipeif->formats[fmt->pad] = fmt->format; + else if (fmt->pad == IPIPEIF_PAD_SOURCE && + ipipeif->output != IPIPEIF_OUTPUT_NONE) + ipipeif->formats[fmt->pad] = fmt->format; + else + return -EINVAL; + + return 0; +} + +static void ipipeif_set_default_config(struct vpfe_ipipeif_device *ipipeif) +{ +#define WIDTH_I 640 +#define HEIGHT_I 480 + + const struct ipipeif_params ipipeif_defaults = { + .clock_select = IPIPEIF_SDRAM_CLK, + .ppln = WIDTH_I + 8, + .lpfr = HEIGHT_I + 10, + .rsz = 16, /* resize ratio 16/rsz */ + .decimation = IPIPEIF_DECIMATION_OFF, + .avg_filter = IPIPEIF_AVG_OFF, + .if_5_1 = { + .clk_div = { + .m = 1, /* clock = sdram clock * (m/n) */ + .n = 6 + }, + .clip = 4095, + }, + }; + memset(&ipipeif->config, 0, sizeof(struct ipipeif_params)); + memcpy(&ipipeif->config, &ipipeif_defaults, + sizeof(struct ipipeif_params)); +} + +/* + * ipipeif_init_formats() - Initialize formats on all pads + * @sd: VPFE ipipeif V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +ipipeif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = IPIPEIF_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipeif_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = IPIPEIF_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipeif_set_format(sd, fh, &format); + + ipipeif_set_default_config(ipipeif); + + return 0; +} + +/* + * ipipeif_video_in_queue() - ipipeif video in queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static int +ipipeif_video_in_queue(struct vpfe_device *vpfe_dev, unsigned long addr) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + void *ipipeif_base_addr = ipipeif->ipipeif_base_addr; + unsigned int adofs; + u32 val; + + if (ipipeif->input != IPIPEIF_INPUT_MEMORY) + return -EINVAL; + + switch (ipipeif->formats[IPIPEIF_PAD_SINK].code) { + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + case V4L2_MBUS_FMT_YDYUYDYV8_1X16: + adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width; + break; + + default: + adofs = ipipeif->formats[IPIPEIF_PAD_SINK].width << 1; + break; + } + + /* adjust the line len to be a multiple of 32 */ + adofs += 31; + adofs &= ~0x1f; + val = (adofs >> 5) & IPIPEIF_ADOFS_LSB_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADOFS); + + /* lower sixteen bit */ + val = (addr >> IPIPEIF_ADDRL_SHIFT) & IPIPEIF_ADDRL_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRL); + + /* upper next seven bit */ + val = (addr >> IPIPEIF_ADDRU_SHIFT) & IPIPEIF_ADDRU_MASK; + ipipeif_write(val, ipipeif_base_addr, IPIPEIF_ADDRU); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops ipipeif_v4l2_core_ops = { + .ioctl = ipipeif_ioctl, +}; + +static const struct v4l2_ctrl_ops ipipeif_ctrl_ops = { + .s_ctrl = ipipeif_s_ctrl, +}; + +static const struct v4l2_ctrl_config vpfe_ipipeif_dpcm_pred = { + .ops = &ipipeif_ctrl_ops, + .id = VPFE_CID_DPCM_PREDICTOR, + .name = "DPCM Predictor", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 1, + .step = 1, + .def = 0, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops ipipeif_v4l2_internal_ops = { + .open = ipipeif_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops ipipeif_v4l2_video_ops = { + .s_stream = ipipeif_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops ipipeif_v4l2_pad_ops = { + .enum_mbus_code = ipipeif_enum_mbus_code, + .enum_frame_size = ipipeif_enum_frame_size, + .get_fmt = ipipeif_get_format, + .set_fmt = ipipeif_set_format, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops ipipeif_v4l2_ops = { + .core = &ipipeif_v4l2_core_ops, + .video = &ipipeif_v4l2_video_ops, + .pad = &ipipeif_v4l2_pad_ops, +}; + +static const struct vpfe_video_operations video_in_ops = { + .queue = ipipeif_video_in_queue, +}; + +static int +ipipeif_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_ipipeif_device *ipipeif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe = to_vpfe_device(ipipeif); + + switch (local->index | media_entity_type(remote->entity)) { + case IPIPEIF_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* Single shot mode */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->input = IPIPEIF_INPUT_NONE; + break; + } + ipipeif->input = IPIPEIF_INPUT_MEMORY; + break; + + case IPIPEIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from isif */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->input = IPIPEIF_INPUT_NONE; + break; + } + if (ipipeif->input != IPIPEIF_INPUT_NONE) + return -EBUSY; + + ipipeif->input = IPIPEIF_INPUT_ISIF; + break; + + case IPIPEIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipeif->output = IPIPEIF_OUTPUT_NONE; + break; + } + if (remote->entity == &vpfe->vpfe_ipipe.subdev.entity) + /* connencted to ipipe */ + ipipeif->output = IPIPEIF_OUTPUT_IPIPE; + else if (remote->entity == &vpfe->vpfe_resizer. + crop_resizer.subdev.entity) + /* connected to resizer */ + ipipeif->output = IPIPEIF_OUTPUT_RESIZER; + else + return -EINVAL; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations ipipeif_media_ops = { + .link_setup = ipipeif_link_setup, +}; + +/* + * vpfe_ipipeif_unregister_entities() - Unregister entity + * @ipipeif - pointer to ipipeif subdevice structure. + */ +void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif) +{ + /* unregister video device */ + vpfe_video_unregister(&ipipeif->video_in); + + /* cleanup entity */ + media_entity_cleanup(&ipipeif->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&ipipeif->subdev); +} + +int +vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipeif); + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &ipipeif->subdev); + if (ret < 0) + return ret; + + ret = vpfe_video_register(&ipipeif->video_in, vdev); + if (ret) { + pr_err("Failed to register ipipeif video-in device\n"); + goto fail; + } + ipipeif->video_in.vpfe_dev = vpfe_dev; + + flags = 0; + ret = media_entity_create_link(&ipipeif->video_in.video_dev.entity, 0, + &ipipeif->subdev.entity, 0, flags); + if (ret < 0) + goto fail; + + return 0; +fail: + v4l2_device_unregister_subdev(&ipipeif->subdev); + + return ret; +} + +#define IPIPEIF_GAIN_HIGH 0x3ff +#define IPIPEIF_DEFAULT_GAIN 0x200 + +int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &ipipeif->subdev; + struct media_pad *pads = &ipipeif->pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + ipipeif->ipipeif_base_addr = ioremap_nocache(res->start, res_len); + if (!ipipeif->ipipeif_base_addr) { + ret = -EBUSY; + goto fail; + } + + v4l2_subdev_init(sd, &ipipeif_v4l2_ops); + + sd->internal_ops = &ipipeif_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI IPIPEIF", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + + v4l2_set_subdevdata(sd, ipipeif); + + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + pads[IPIPEIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[IPIPEIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ipipeif->input = IPIPEIF_INPUT_NONE; + ipipeif->output = IPIPEIF_OUTPUT_NONE; + me->ops = &ipipeif_media_ops; + + ret = media_entity_init(me, IPIPEIF_NUM_PADS, pads, 0); + if (ret) + goto fail; + + v4l2_ctrl_handler_init(&ipipeif->ctrls, 2); + v4l2_ctrl_new_std(&ipipeif->ctrls, &ipipeif_ctrl_ops, + V4L2_CID_GAIN, 0, + IPIPEIF_GAIN_HIGH, 1, IPIPEIF_DEFAULT_GAIN); + v4l2_ctrl_new_custom(&ipipeif->ctrls, &vpfe_ipipeif_dpcm_pred, NULL); + v4l2_ctrl_handler_setup(&ipipeif->ctrls); + sd->ctrl_handler = &ipipeif->ctrls; + + ipipeif->video_in.ops = &video_in_ops; + ipipeif->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ret = vpfe_video_init(&ipipeif->video_in, "IPIPEIF"); + if (ret) { + pr_err("Failed to init IPIPEIF video-in device\n"); + goto fail; + } + ipipeif_set_default_config(ipipeif); + return 0; +fail: + release_mem_region(res->start, res_len); + return ret; +} + +void +vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev) +{ + struct resource *res; + + v4l2_ctrl_handler_free(&ipipeif->ctrls); + iounmap(ipipeif->ipipeif_base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); + +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h new file mode 100644 index 000000000000..608701fc5fed --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.h @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_H +#define _DAVINCI_VPFE_DM365_IPIPEIF_H + +#include + +#include +#include +#include + +#include "dm365_ipipeif_user.h" +#include "vpfe_video.h" + +/* IPIPE base specific types */ +enum ipipeif_data_shift { + IPIPEIF_BITS15_2 = 0, + IPIPEIF_BITS14_1 = 1, + IPIPEIF_BITS13_0 = 2, + IPIPEIF_BITS12_0 = 3, + IPIPEIF_BITS11_0 = 4, + IPIPEIF_BITS10_0 = 5, + IPIPEIF_BITS9_0 = 6, +}; + +enum ipipeif_clkdiv { + IPIPEIF_DIVIDE_HALF = 0, + IPIPEIF_DIVIDE_THIRD = 1, + IPIPEIF_DIVIDE_FOURTH = 2, + IPIPEIF_DIVIDE_FIFTH = 3, + IPIPEIF_DIVIDE_SIXTH = 4, + IPIPEIF_DIVIDE_EIGHTH = 5, + IPIPEIF_DIVIDE_SIXTEENTH = 6, + IPIPEIF_DIVIDE_THIRTY = 7, +}; + +enum ipipeif_pack_mode { + IPIPEIF_PACK_16_BIT = 0, + IPIPEIF_PACK_8_BIT = 1, +}; + +enum ipipeif_5_1_pack_mode { + IPIPEIF_5_1_PACK_16_BIT = 0, + IPIPEIF_5_1_PACK_8_BIT = 1, + IPIPEIF_5_1_PACK_8_BIT_A_LAW = 2, + IPIPEIF_5_1_PACK_12_BIT = 3 +}; + +enum ipipeif_input_source { + IPIPEIF_CCDC = 0, + IPIPEIF_SDRAM_RAW = 1, + IPIPEIF_CCDC_DARKFM = 2, + IPIPEIF_SDRAM_YUV = 3, +}; + +enum ipipeif_ialaw { + IPIPEIF_ALAW_OFF = 0, + IPIPEIF_ALAW_ON = 1, +}; + +enum ipipeif_input_src1 { + IPIPEIF_SRC1_PARALLEL_PORT = 0, + IPIPEIF_SRC1_SDRAM_RAW = 1, + IPIPEIF_SRC1_ISIF_DARKFM = 2, + IPIPEIF_SRC1_SDRAM_YUV = 3, +}; + +enum ipipeif_dfs_dir { + IPIPEIF_PORT_MINUS_SDRAM = 0, + IPIPEIF_SDRAM_MINUS_PORT = 1, +}; + +enum ipipeif_chroma_phase { + IPIPEIF_CBCR_Y = 0, + IPIPEIF_Y_CBCR = 1, +}; + +enum ipipeif_dpcm_type { + IPIPEIF_DPCM_8BIT_10BIT = 0, + IPIPEIF_DPCM_8BIT_12BIT = 1, +}; + +/* data shift for IPIPE 5.1 */ +enum ipipeif_5_1_data_shift { + IPIPEIF_5_1_BITS11_0 = 0, + IPIPEIF_5_1_BITS10_0 = 1, + IPIPEIF_5_1_BITS9_0 = 2, + IPIPEIF_5_1_BITS8_0 = 3, + IPIPEIF_5_1_BITS7_0 = 4, + IPIPEIF_5_1_BITS15_4 = 5, +}; + +#define IPIPEIF_PAD_SINK 0 +#define IPIPEIF_PAD_SOURCE 1 + +#define IPIPEIF_NUM_PADS 2 + +enum ipipeif_input_entity { + IPIPEIF_INPUT_NONE = 0, + IPIPEIF_INPUT_ISIF = 1, + IPIPEIF_INPUT_MEMORY = 2, +}; + +enum ipipeif_output_entity { + IPIPEIF_OUTPUT_NONE = 0, + IPIPEIF_OUTPUT_IPIPE = 1, + IPIPEIF_OUTPUT_RESIZER = 2, +}; + +struct vpfe_ipipeif_device { + struct v4l2_subdev subdev; + struct media_pad pads[IPIPEIF_NUM_PADS]; + struct v4l2_mbus_framefmt formats[IPIPEIF_NUM_PADS]; + enum ipipeif_input_entity input; + unsigned int output; + struct vpfe_video_device video_in; + struct v4l2_ctrl_handler ctrls; + void *__iomem ipipeif_base_addr; + struct ipipeif_params config; + int dpcm_predictor; + int gain; +}; + +/* IPIPEIF Register Offsets from the base address */ +#define IPIPEIF_ENABLE 0x00 +#define IPIPEIF_CFG1 0x04 +#define IPIPEIF_PPLN 0x08 +#define IPIPEIF_LPFR 0x0c +#define IPIPEIF_HNUM 0x10 +#define IPIPEIF_VNUM 0x14 +#define IPIPEIF_ADDRU 0x18 +#define IPIPEIF_ADDRL 0x1c +#define IPIPEIF_ADOFS 0x20 +#define IPIPEIF_RSZ 0x24 +#define IPIPEIF_GAIN 0x28 + +/* Below registers are available only on IPIPE 5.1 */ +#define IPIPEIF_DPCM 0x2c +#define IPIPEIF_CFG2 0x30 +#define IPIPEIF_INIRSZ 0x34 +#define IPIPEIF_OCLIP 0x38 +#define IPIPEIF_DTUDF 0x3c +#define IPIPEIF_CLKDIV 0x40 +#define IPIPEIF_DPC1 0x44 +#define IPIPEIF_DPC2 0x48 +#define IPIPEIF_DFSGVL 0x4c +#define IPIPEIF_DFSGTH 0x50 +#define IPIPEIF_RSZ3A 0x54 +#define IPIPEIF_INIRSZ3A 0x58 +#define IPIPEIF_RSZ_MIN 16 +#define IPIPEIF_RSZ_MAX 112 +#define IPIPEIF_RSZ_CONST 16 +#define SETBIT(reg, bit) (reg = ((reg) | ((0x00000001)<<(bit)))) +#define RESETBIT(reg, bit) (reg = ((reg) & (~(0x00000001<<(bit))))) + +#define IPIPEIF_ADOFS_LSB_MASK 0x1ff +#define IPIPEIF_ADOFS_LSB_SHIFT 5 +#define IPIPEIF_ADOFS_MSB_MASK 0x200 +#define IPIPEIF_ADDRU_MASK 0x7ff +#define IPIPEIF_ADDRL_SHIFT 5 +#define IPIPEIF_ADDRL_MASK 0xffff +#define IPIPEIF_ADDRU_SHIFT 21 +#define IPIPEIF_ADDRMSB_SHIFT 31 +#define IPIPEIF_ADDRMSB_LEFT_SHIFT 10 + +/* CFG1 Masks and shifts */ +#define ONESHOT_SHIFT 0 +#define DECIM_SHIFT 1 +#define INPSRC_SHIFT 2 +#define CLKDIV_SHIFT 4 +#define AVGFILT_SHIFT 7 +#define PACK8IN_SHIFT 8 +#define IALAW_SHIFT 9 +#define CLKSEL_SHIFT 10 +#define DATASFT_SHIFT 11 +#define INPSRC1_SHIFT 14 + +/* DPC2 */ +#define IPIPEIF_DPC2_EN_SHIFT 12 +#define IPIPEIF_DPC2_THR_MASK 0xfff +/* Applicable for IPIPE 5.1 */ +#define IPIPEIF_DF_GAIN_EN_SHIFT 10 +#define IPIPEIF_DF_GAIN_MASK 0x3ff +#define IPIPEIF_DF_GAIN_THR_MASK 0xfff +/* DPCM */ +#define IPIPEIF_DPCM_BITS_SHIFT 2 +#define IPIPEIF_DPCM_PRED_SHIFT 1 +/* CFG2 */ +#define IPIPEIF_CFG2_HDPOL_SHIFT 1 +#define IPIPEIF_CFG2_VDPOL_SHIFT 2 +#define IPIPEIF_CFG2_YUV8_SHIFT 6 +#define IPIPEIF_CFG2_YUV16_SHIFT 3 +#define IPIPEIF_CFG2_YUV8P_SHIFT 7 + +/* INIRSZ */ +#define IPIPEIF_INIRSZ_ALNSYNC_SHIFT 13 +#define IPIPEIF_INIRSZ_MASK 0x1fff + +/* CLKDIV */ +#define IPIPEIF_CLKDIV_M_SHIFT 8 + +void vpfe_ipipeif_enable(struct vpfe_device *vpfe_dev); +void vpfe_ipipeif_ss_buffer_isr(struct vpfe_ipipeif_device *ipipeif); +int vpfe_ipipeif_decimation_enabled(struct vpfe_device *vpfe_dev); +int vpfe_ipipeif_get_rsz(struct vpfe_device *vpfe_dev); +void vpfe_ipipeif_cleanup(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev); +int vpfe_ipipeif_init(struct vpfe_ipipeif_device *ipipeif, + struct platform_device *pdev); +int vpfe_ipipeif_register_entities(struct vpfe_ipipeif_device *ipipeif, + struct v4l2_device *vdev); +void vpfe_ipipeif_unregister_entities(struct vpfe_ipipeif_device *ipipeif); + +#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h new file mode 100644 index 000000000000..e2a69b59554a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif_user.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPEIF_USER_H +#define _DAVINCI_VPFE_DM365_IPIPEIF_USER_H + +/* clockdiv for IPIPE 5.1 */ +struct ipipeif_5_1_clkdiv { + unsigned char m; + unsigned char n; +}; + +enum ipipeif_decimation { + IPIPEIF_DECIMATION_OFF, + IPIPEIF_DECIMATION_ON +}; + +/* DPC at the if for IPIPE 5.1 */ +struct ipipeif_dpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* threshold */ + unsigned short thr; +}; + +enum ipipeif_clock { + IPIPEIF_PIXCEL_CLK, + IPIPEIF_SDRAM_CLK +}; + +enum ipipeif_avg_filter { + IPIPEIF_AVG_OFF, + IPIPEIF_AVG_ON +}; + +struct ipipeif_5_1 { + struct ipipeif_5_1_clkdiv clk_div; + /* Defect pixel correction */ + struct ipipeif_dpc dpc; + /* clipped to this value */ + unsigned short clip; + /* Align HSync and VSync to rsz_start */ + unsigned char align_sync; + /* resizer start position */ + unsigned int rsz_start; + /* DF gain enable */ + unsigned char df_gain_en; + /* DF gain value */ + unsigned short df_gain; + /* DF gain threshold value */ + unsigned short df_gain_thr; +}; + +struct ipipeif_params { + enum ipipeif_clock clock_select; + unsigned int ppln; + unsigned int lpfr; + unsigned char rsz; + enum ipipeif_decimation decimation; + enum ipipeif_avg_filter avg_filter; + /* IPIPE 5.1 */ + struct ipipeif_5_1 if_5_1; +}; + +/* + * Private IOCTL + * VIDIOC_VPFE_IPIPEIF_S_CONFIG: Set IPIEIF configuration + * VIDIOC_VPFE_IPIPEIF_G_CONFIG: Get IPIEIF configuration + */ +#define VIDIOC_VPFE_IPIPEIF_S_CONFIG \ + _IOWR('I', BASE_VIDIOC_PRIVATE + 1, struct ipipeif_params) +#define VIDIOC_VPFE_IPIPEIF_G_CONFIG \ + _IOWR('I', BASE_VIDIOC_PRIVATE + 2, struct ipipeif_params) + +#endif /* _DAVINCI_VPFE_DM365_IPIPEIF_USER_H */ From 6a630533c4461c1e6dabfab5f11d9e98cb743bc2 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:09:35 -0300 Subject: [PATCH 0295/4607] [media] davinci: vpfe: dm365: add ISIF driver based on media framework add support for ISIF as a subdevice for dm365 vpfe capture driver. ISIF is responsible for capturing video data both in raw bayer format on sync seperate signals and YUV through BT656/1120 interfaces. ISIF is exposed as a subdev for the vpfe driver and allows users to use the driver through standard media controller interface. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/davinci_vpfe/dm365_isif.c | 2104 +++++++++++++++++ .../staging/media/davinci_vpfe/dm365_isif.h | 203 ++ .../media/davinci_vpfe/dm365_isif_regs.h | 294 +++ 3 files changed, 2601 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif.c create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif.h create mode 100644 drivers/staging/media/davinci_vpfe/dm365_isif_regs.h diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c new file mode 100644 index 000000000000..ebeea72e176a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -0,0 +1,2104 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#include "dm365_isif.h" +#include "vpfe_mc_capture.h" + +#define MAX_WIDTH 4096 +#define MAX_HEIGHT 4096 + +static const unsigned int isif_fmts[] = { + V4L2_MBUS_FMT_YUYV8_2X8, + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_YUYV8_1X16, + V4L2_MBUS_FMT_YUYV10_1X20, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, +}; + +#define ISIF_COLPTN_R_Ye 0x0 +#define ISIF_COLPTN_Gr_Cy 0x1 +#define ISIF_COLPTN_Gb_G 0x2 +#define ISIF_COLPTN_B_Mg 0x3 + +#define ISIF_CCOLP_CP01_0 0 +#define ISIF_CCOLP_CP03_2 2 +#define ISIF_CCOLP_CP05_4 4 +#define ISIF_CCOLP_CP07_6 6 +#define ISIF_CCOLP_CP11_0 8 +#define ISIF_CCOLP_CP13_2 10 +#define ISIF_CCOLP_CP15_4 12 +#define ISIF_CCOLP_CP17_6 14 + +static const u32 isif_sgrbg_pattern = + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP01_0 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP03_2 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP05_4 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP07_6 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP11_0 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP13_2 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP15_4 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP17_6; + +static const u32 isif_srggb_pattern = + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP01_0 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP03_2 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP05_4 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP07_6 | + ISIF_COLPTN_R_Ye << ISIF_CCOLP_CP11_0 | + ISIF_COLPTN_Gr_Cy << ISIF_CCOLP_CP13_2 | + ISIF_COLPTN_Gb_G << ISIF_CCOLP_CP15_4 | + ISIF_COLPTN_B_Mg << ISIF_CCOLP_CP17_6; + +static inline u32 isif_read(void *__iomem base_addr, u32 offset) +{ + return readl(base_addr + offset); +} + +static inline void isif_write(void *__iomem base_addr, u32 val, u32 offset) +{ + writel(val, base_addr + offset); +} + +static inline u32 isif_merge(void *__iomem base_addr, u32 mask, u32 val, + u32 offset) +{ + u32 new_val = (isif_read(base_addr, offset) & ~mask) | (val & mask); + + isif_write(base_addr, new_val, offset); + + return new_val; +} + +static void isif_enable_output_to_sdram(struct vpfe_isif_device *isif, int en) +{ + isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_WEN_MASK, + en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN); +} + +static inline void +isif_regw_lin_tbl(struct vpfe_isif_device *isif, u32 val, u32 offset, int i) +{ + if (!i) + writel(val, isif->isif_cfg.linear_tbl0_addr + offset); + else + writel(val, isif->isif_cfg.linear_tbl1_addr + offset); +} + +static void isif_disable_all_modules(struct vpfe_isif_device *isif) +{ + /* disable BC */ + isif_write(isif->isif_cfg.base_addr, 0, CLAMPCFG); + /* disable vdfc */ + isif_write(isif->isif_cfg.base_addr, 0, DFCCTL); + /* disable CSC */ + isif_write(isif->isif_cfg.base_addr, 0, CSCCTL); + /* disable linearization */ + isif_write(isif->isif_cfg.base_addr, 0, LINCFG0); +} + +static void isif_enable(struct vpfe_isif_device *isif, int en) +{ + if (!en) + /* Before disable isif, disable all ISIF modules */ + isif_disable_all_modules(isif); + + /* + * wait for next VD. Assume lowest scan rate is 12 Hz. So + * 100 msec delay is good enough + */ + msleep(100); + isif_merge(isif->isif_cfg.base_addr, ISIF_SYNCEN_VDHDEN_MASK, + en, SYNCEN); +} + +/* + * ISIF helper functions + */ + +#define DM365_ISIF_MDFS_OFFSET 15 +#define DM365_ISIF_MDFS_MASK 0x1 + +/* get field id in isif hardware */ +enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev) +{ + struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif; + u32 field_status; + + field_status = isif_read(isif->isif_cfg.base_addr, MODESET); + field_status = (field_status >> DM365_ISIF_MDFS_OFFSET) & + DM365_ISIF_MDFS_MASK; + return field_status; +} + +static int +isif_set_pixel_format(struct vpfe_isif_device *isif, unsigned int pixfmt) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) { + if (pixfmt == V4L2_PIX_FMT_SBGGR16) + isif->isif_cfg.data_pack = ISIF_PACK_16BIT; + else if ((pixfmt == V4L2_PIX_FMT_SGRBG10DPCM8) || + (pixfmt == V4L2_PIX_FMT_SGRBG10ALAW8)) + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; + else + return -EINVAL; + + isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW; + isif->isif_cfg.bayer.v4l2_pix_fmt = pixfmt; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY; + else + return -EINVAL; + + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; + isif->isif_cfg.ycbcr.v4l2_pix_fmt = pixfmt; + } + + return 0; +} + +static int +isif_set_frame_format(struct vpfe_isif_device *isif, + enum isif_frmfmt frm_fmt) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) + isif->isif_cfg.bayer.frm_fmt = frm_fmt; + else + isif->isif_cfg.ycbcr.frm_fmt = frm_fmt; + + return 0; +} + +static int isif_set_image_window(struct vpfe_isif_device *isif) +{ + struct v4l2_rect *win = &isif->crop; + + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) { + isif->isif_cfg.bayer.win.top = win->top; + isif->isif_cfg.bayer.win.left = win->left; + isif->isif_cfg.bayer.win.width = win->width; + isif->isif_cfg.bayer.win.height = win->height; + return 0; + } + isif->isif_cfg.ycbcr.win.top = win->top; + isif->isif_cfg.ycbcr.win.left = win->left; + isif->isif_cfg.ycbcr.win.width = win->width; + isif->isif_cfg.ycbcr.win.height = win->height; + + return 0; +} + +static int +isif_set_buftype(struct vpfe_isif_device *isif, enum isif_buftype buf_type) +{ + if (isif->formats[ISIF_PAD_SINK].code == V4L2_MBUS_FMT_SGRBG12_1X12) + isif->isif_cfg.bayer.buf_type = buf_type; + else + isif->isif_cfg.ycbcr.buf_type = buf_type; + + return 0; +} + +/* configure format in isif hardware */ +static int +isif_config_format(struct vpfe_device *vpfe_dev, unsigned int pad) +{ + struct vpfe_isif_device *vpfe_isif = &vpfe_dev->vpfe_isif; + enum isif_frmfmt frm_fmt = ISIF_FRMFMT_INTERLACED; + struct v4l2_pix_format format; + int ret = 0; + + v4l2_fill_pix_format(&format, &vpfe_dev->vpfe_isif.formats[pad]); + mbus_to_pix(&vpfe_dev->vpfe_isif.formats[pad], &format); + + if (isif_set_pixel_format(vpfe_isif, format.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Failed to set pixel format in isif\n"); + return -EINVAL; + } + + /* call for s_crop will override these values */ + vpfe_isif->crop.left = 0; + vpfe_isif->crop.top = 0; + vpfe_isif->crop.width = format.width; + vpfe_isif->crop.height = format.height; + + /* configure the image window */ + isif_set_image_window(vpfe_isif); + + switch (vpfe_dev->vpfe_isif.formats[pad].field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_INTERLEAVED); + break; + + case V4L2_FIELD_NONE: + frm_fmt = ISIF_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + + case V4L2_FIELD_SEQ_TB: + ret = isif_set_buftype(vpfe_isif, ISIF_BUFTYPE_FLD_SEPARATED); + break; + + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = isif_set_frame_format(vpfe_isif, frm_fmt); + + return ret; +} + +/* + * isif_try_format() - Try video format on a pad + * @isif: VPFE isif device + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + */ +static void +isif_try_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + unsigned int width = fmt->format.width; + unsigned int height = fmt->format.height; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(isif_fmts); i++) { + if (fmt->format.code == isif_fmts[i]) + break; + } + + /* If not found, use YUYV8_2x8 as default */ + if (i >= ARRAY_SIZE(isif_fmts)) + fmt->format.code = V4L2_MBUS_FMT_YUYV8_2X8; + + /* Clamp the size. */ + fmt->format.width = clamp_t(u32, width, 32, MAX_WIDTH); + fmt->format.height = clamp_t(u32, height, 32, MAX_HEIGHT); + + /* The data formatter truncates the number of horizontal output + * pixels to a multiple of 16. To avoid clipping data, allow + * callers to request an output size bigger than the input size + * up to the nearest multiple of 16. + */ + if (fmt->pad == ISIF_PAD_SOURCE) + fmt->format.width &= ~15; +} + +/* + * vpfe_isif_buffer_isr() - isif module non-progressive buffer scheduling isr + * @isif: Pointer to isif subdevice. + */ +void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + struct vpfe_video_device *video = &isif->video_out; + enum v4l2_field field; + int fid; + + if (!video->started) + return; + + field = video->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (video->cur_frm != video->next_frm) + vpfe_video_process_buffer_complete(video); + return; + } + + /* interlaced or TB capture check which field we + * are in hardware + */ + fid = vpfe_isif_get_fid(vpfe_dev); + + /* switch the software maintained field id */ + video->field_id ^= 1; + if (fid == video->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (video->cur_frm != video->next_frm) + vpfe_video_process_buffer_complete(video); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, + * reconfigure the ISIF memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_video_schedule_bottom_field(video); + return; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the + * empty queue if no frame is available hold on + * to the current buffer + */ + spin_lock(&video->dma_queue_lock); + if (!list_empty(&video->dma_queue) && + video->cur_frm == video->next_frm) + vpfe_video_schedule_next_buffer(video); + spin_unlock(&video->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video->field_id = fid; + } +} + +/* + * vpfe_isif_vidint1_isr() - ISIF module progressive buffer scheduling isr + * @isif: Pointer to isif subdevice. + */ +void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif) +{ + struct vpfe_video_device *video = &isif->video_out; + + if (!video->started) + return; + + spin_lock(&video->dma_queue_lock); + if (video->fmt.fmt.pix.field == V4L2_FIELD_NONE && + !list_empty(&video->dma_queue) && video->cur_frm == video->next_frm) + vpfe_video_schedule_next_buffer(video); + + spin_unlock(&video->dma_queue_lock); +} + +/* + * VPFE video operations + */ + +static int isif_video_queue(struct vpfe_device *vpfe_dev, unsigned long addr) +{ + struct vpfe_isif_device *isif = &vpfe_dev->vpfe_isif; + + isif_write(isif->isif_cfg.base_addr, (addr >> 21) & + ISIF_CADU_BITS, CADU); + isif_write(isif->isif_cfg.base_addr, (addr >> 5) & + ISIF_CADL_BITS, CADL); + + return 0; +} + +static const struct vpfe_video_operations isif_video_ops = { + .queue = isif_video_queue, +}; + +/* + * V4L2 subdev operations + */ + +/* Parameter operations */ +static int isif_get_params(struct v4l2_subdev *sd, void *params) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + + /* only raw module parameters can be set through the IOCTL */ + if (isif->formats[ISIF_PAD_SINK].code != V4L2_MBUS_FMT_SGRBG12_1X12) + return -EINVAL; + memcpy(params, &isif->isif_cfg.bayer.config_params, + sizeof(isif->isif_cfg.bayer.config_params)); + return 0; +} + +static int isif_validate_df_csc_params(struct vpfe_isif_df_csc *df_csc) +{ + struct vpfe_isif_color_space_conv *csc; + int err = -EINVAL; + int csc_df_en; + int i; + + if (!df_csc->df_or_csc) { + /* csc configuration */ + csc = &df_csc->csc; + if (csc->en) { + csc_df_en = 1; + for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++) + if (csc->coeff[i].integer > + ISIF_CSC_COEF_INTEG_MASK || + csc->coeff[i].decimal > + ISIF_CSC_COEF_DECIMAL_MASK) { + pr_err("Invalid CSC coefficients\n"); + return err; + } + } + } + if (df_csc->start_pix > ISIF_DF_CSC_SPH_MASK) { + pr_err("Invalid df_csc start pix value\n"); + return err; + } + + if (df_csc->num_pixels > ISIF_DF_NUMPIX) { + pr_err("Invalid df_csc num pixels value\n"); + return err; + } + + if (df_csc->start_line > ISIF_DF_CSC_LNH_MASK) { + pr_err("Invalid df_csc start_line value\n"); + return err; + } + + if (df_csc->num_lines > ISIF_DF_NUMLINES) { + pr_err("Invalid df_csc num_lines value\n"); + return err; + } + + return 0; +} + +#define DM365_ISIF_MAX_VDFLSFT 4 +#define DM365_ISIF_MAX_VDFSLV 4095 +#define DM365_ISIF_MAX_DFCMEM0 0x1fff +#define DM365_ISIF_MAX_DFCMEM1 0x1fff + +static int isif_validate_dfc_params(struct vpfe_isif_dfc *dfc) +{ + int err = -EINVAL; + int i; + + if (!dfc->en) + return 0; + + if (dfc->corr_whole_line > 1) { + pr_err("Invalid corr_whole_line value\n"); + return err; + } + + if (dfc->def_level_shift > DM365_ISIF_MAX_VDFLSFT) { + pr_err("Invalid def_level_shift value\n"); + return err; + } + + if (dfc->def_sat_level > DM365_ISIF_MAX_VDFSLV) { + pr_err("Invalid def_sat_level value\n"); + return err; + } + + if (!dfc->num_vdefects || + dfc->num_vdefects > VPFE_ISIF_VDFC_TABLE_SIZE) { + pr_err("Invalid num_vdefects value\n"); + return err; + } + + for (i = 0; i < VPFE_ISIF_VDFC_TABLE_SIZE; i++) { + if (dfc->table[i].pos_vert > DM365_ISIF_MAX_DFCMEM0) { + pr_err("Invalid pos_vert value\n"); + return err; + } + if (dfc->table[i].pos_horz > DM365_ISIF_MAX_DFCMEM1) { + pr_err("Invalid pos_horz value\n"); + return err; + } + } + + return 0; +} + +#define DM365_ISIF_MAX_CLVRV 0xfff +#define DM365_ISIF_MAX_CLDC 0x1fff +#define DM365_ISIF_MAX_CLHSH 0x1fff +#define DM365_ISIF_MAX_CLHSV 0x1fff +#define DM365_ISIF_MAX_CLVSH 0x1fff +#define DM365_ISIF_MAX_CLVSV 0x1fff +#define DM365_ISIF_MAX_HEIGHT_BLACK_REGION 0x1fff + +static int isif_validate_bclamp_params(struct vpfe_isif_black_clamp *bclamp) +{ + int err = -EINVAL; + + if (bclamp->dc_offset > DM365_ISIF_MAX_CLDC) { + pr_err("Invalid bclamp dc_offset value\n"); + return err; + } + if (!bclamp->en) + return 0; + if (bclamp->horz.clamp_pix_limit > 1) { + pr_err("Invalid bclamp horz clamp_pix_limit value\n"); + return err; + } + if (bclamp->horz.win_count_calc < 1 || + bclamp->horz.win_count_calc > 32) { + pr_err("Invalid bclamp horz win_count_calc value\n"); + return err; + } + if (bclamp->horz.win_start_h_calc > DM365_ISIF_MAX_CLHSH) { + pr_err("Invalid bclamp win_start_v_calc value\n"); + return err; + } + + if (bclamp->horz.win_start_v_calc > DM365_ISIF_MAX_CLHSV) { + pr_err("Invalid bclamp win_start_v_calc value\n"); + return err; + } + if (bclamp->vert.reset_clamp_val > DM365_ISIF_MAX_CLVRV) { + pr_err("Invalid bclamp reset_clamp_val value\n"); + return err; + } + if (bclamp->vert.ob_v_sz_calc > DM365_ISIF_MAX_HEIGHT_BLACK_REGION) { + pr_err("Invalid bclamp ob_v_sz_calc value\n"); + return err; + } + if (bclamp->vert.ob_start_h > DM365_ISIF_MAX_CLVSH) { + pr_err("Invalid bclamp ob_start_h value\n"); + return err; + } + if (bclamp->vert.ob_start_v > DM365_ISIF_MAX_CLVSV) { + pr_err("Invalid bclamp ob_start_h value\n"); + return err; + } + return 0; +} + +static int +isif_validate_raw_params(struct vpfe_isif_raw_config *params) +{ + int ret; + + ret = isif_validate_df_csc_params(¶ms->df_csc); + if (ret) + return ret; + ret = isif_validate_dfc_params(¶ms->dfc); + if (ret) + return ret; + ret = isif_validate_bclamp_params(¶ms->bclamp); + return ret; +} + +static int isif_set_params(struct v4l2_subdev *sd, void *params) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct vpfe_isif_raw_config isif_raw_params; + int ret = -EINVAL; + + /* only raw module parameters can be set through the IOCTL */ + if (isif->formats[ISIF_PAD_SINK].code != V4L2_MBUS_FMT_SGRBG12_1X12) + return ret; + + memcpy(&isif_raw_params, params, sizeof(isif_raw_params)); + if (!isif_validate_raw_params(&isif_raw_params)) { + memcpy(&isif->isif_cfg.bayer.config_params, &isif_raw_params, + sizeof(isif_raw_params)); + ret = 0; + } + return ret; +} +/* + * isif_ioctl() - isif module private ioctl's + * @sd: VPFE isif V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long isif_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + int ret; + + switch (cmd) { + case VIDIOC_VPFE_ISIF_S_RAW_PARAMS: + ret = isif_set_params(sd, arg); + break; + + case VIDIOC_VPFE_ISIF_G_RAW_PARAMS: + ret = isif_get_params(sd, arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +static void isif_config_gain_offset(struct vpfe_isif_device *isif) +{ + struct vpfe_isif_gain_offsets_adj *gain_off_ptr = + &isif->isif_cfg.bayer.config_params.gain_offset; + void *__iomem base = isif->isif_cfg.base_addr; + u32 val; + + val = ((gain_off_ptr->gain_sdram_en & 1) << GAIN_SDRAM_EN_SHIFT) | + ((gain_off_ptr->gain_ipipe_en & 1) << GAIN_IPIPE_EN_SHIFT) | + ((gain_off_ptr->gain_h3a_en & 1) << GAIN_H3A_EN_SHIFT) | + ((gain_off_ptr->offset_sdram_en & 1) << OFST_SDRAM_EN_SHIFT) | + ((gain_off_ptr->offset_ipipe_en & 1) << OFST_IPIPE_EN_SHIFT) | + ((gain_off_ptr->offset_h3a_en & 1) << OFST_H3A_EN_SHIFT); + isif_merge(base, GAIN_OFFSET_EN_MASK, val, CGAMMAWD); + + isif_write(base, isif->isif_cfg.isif_gain_params.cr_gain, CRGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cgr_gain, CGRGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cgb_gain, CGBGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.cb_gain, CBGAIN); + isif_write(base, isif->isif_cfg.isif_gain_params.offset & OFFSET_MASK, + COFSTA); + +} + +static void isif_config_bclamp(struct vpfe_isif_device *isif, + struct vpfe_isif_black_clamp *bc) +{ + u32 val; + + /** + * DC Offset is always added to image data irrespective of bc enable + * status + */ + val = bc->dc_offset & ISIF_BC_DCOFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLDCOFST); + + if (!bc->en) + return; + + val = (bc->bc_mode_color & ISIF_BC_MODE_COLOR_MASK) << + ISIF_BC_MODE_COLOR_SHIFT; + + /* Enable BC and horizontal clamp caculation paramaters */ + val = val | 1 | ((bc->horz.mode & ISIF_HORZ_BC_MODE_MASK) << + ISIF_HORZ_BC_MODE_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CLAMPCFG); + + if (bc->horz.mode != VPFE_ISIF_HORZ_BC_DISABLE) { + /* + * Window count for calculation + * Base window selection + * pixel limit + * Horizontal size of window + * vertical size of the window + * Horizontal start position of the window + * Vertical start position of the window + */ + val = (bc->horz.win_count_calc & ISIF_HORZ_BC_WIN_COUNT_MASK) | + ((bc->horz.base_win_sel_calc & 1) << + ISIF_HORZ_BC_WIN_SEL_SHIFT) | + ((bc->horz.clamp_pix_limit & 1) << + ISIF_HORZ_BC_PIX_LIMIT_SHIFT) | + ((bc->horz.win_h_sz_calc & + ISIF_HORZ_BC_WIN_H_SIZE_MASK) << + ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) | + ((bc->horz.win_v_sz_calc & + ISIF_HORZ_BC_WIN_V_SIZE_MASK) << + ISIF_HORZ_BC_WIN_V_SIZE_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CLHWIN0); + + val = bc->horz.win_start_h_calc & ISIF_HORZ_BC_WIN_START_H_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLHWIN1); + + val = bc->horz.win_start_v_calc & ISIF_HORZ_BC_WIN_START_V_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLHWIN2); + } + + /* vertical clamp caculation paramaters */ + /* OB H Valid */ + val = bc->vert.ob_h_sz_calc & ISIF_VERT_BC_OB_H_SZ_MASK; + + /* Reset clamp value sel for previous line */ + val |= (bc->vert.reset_val_sel & ISIF_VERT_BC_RST_VAL_SEL_MASK) << + ISIF_VERT_BC_RST_VAL_SEL_SHIFT; + + /* Line average coefficient */ + val |= bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN0); + + /* Configured reset value */ + if (bc->vert.reset_val_sel == VPFE_ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL) { + val = bc->vert.reset_clamp_val & ISIF_VERT_BC_RST_VAL_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVRV); + } + + /* Optical Black horizontal start position */ + val = bc->vert.ob_start_h & ISIF_VERT_BC_OB_START_HORZ_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN1); + + /* Optical Black vertical start position */ + val = bc->vert.ob_start_v & ISIF_VERT_BC_OB_START_VERT_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN2); + + val = bc->vert.ob_v_sz_calc & ISIF_VERT_BC_OB_VERT_SZ_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLVWIN3); + + /* Vertical start position for BC subtraction */ + val = bc->vert_start_sub & ISIF_BC_VERT_START_SUB_V_MASK; + isif_write(isif->isif_cfg.base_addr, val, CLSV); +} + +/* This function will configure the window size to be capture in ISIF reg */ +static void +isif_setwin(struct vpfe_isif_device *isif, struct v4l2_rect *image_win, + enum isif_frmfmt frm_fmt, int ppc, int mode) +{ + int horz_nr_pixels; + int vert_nr_lines; + int horz_start; + int vert_start; + int mid_img; + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + isif_write(isif->isif_cfg.base_addr, + horz_start & START_PX_HOR_MASK, SPH); + isif_write(isif->isif_cfg.base_addr, + horz_nr_pixels & NUM_PX_HOR_MASK, LNH); + vert_start = image_win->top; + + if (frm_fmt == ISIF_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + } else { + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + isif_write(isif->isif_cfg.base_addr, mid_img, VDINT1); + } + + if (!mode) + isif_write(isif->isif_cfg.base_addr, 0, VDINT0); + else + isif_write(isif->isif_cfg.base_addr, vert_nr_lines, VDINT0); + isif_write(isif->isif_cfg.base_addr, + vert_start & START_VER_ONE_MASK, SLV0); + isif_write(isif->isif_cfg.base_addr, + vert_start & START_VER_TWO_MASK, SLV1); + isif_write(isif->isif_cfg.base_addr, + vert_nr_lines & NUM_LINES_VER, LNV); +} + +#define DM365_ISIF_DFCMWR_MEMORY_WRITE 1 +#define DM365_ISIF_DFCMRD_MEMORY_READ 0x2 + +static void +isif_config_dfc(struct vpfe_isif_device *isif, struct vpfe_isif_dfc *vdfc) +{ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 count = DFC_WRITE_WAIT_COUNT; + u32 val; + int i; + + if (!vdfc->en) + return; + + /* Correction mode */ + val = (vdfc->corr_mode & ISIF_VDFC_CORR_MOD_MASK) << + ISIF_VDFC_CORR_MOD_SHIFT; + + /* Correct whole line or partial */ + if (vdfc->corr_whole_line) + val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT; + + /* level shift value */ + val |= (vdfc->def_level_shift & ISIF_VDFC_LEVEL_SHFT_MASK) << + ISIF_VDFC_LEVEL_SHFT_SHIFT; + + isif_write(isif->isif_cfg.base_addr, val, DFCCTL); + + /* Defect saturation level */ + val = vdfc->def_sat_level & ISIF_VDFC_SAT_LEVEL_MASK; + isif_write(isif->isif_cfg.base_addr, val, VDFSATLV); + + isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_vert & + ISIF_VDFC_POS_MASK, DFCMEM0); + isif_write(isif->isif_cfg.base_addr, vdfc->table[0].pos_horz & + ISIF_VDFC_POS_MASK, DFCMEM1); + if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL || + vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_at_pos, DFCMEM2); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_up_pixels, DFCMEM3); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[0].level_low_pixels, DFCMEM4); + } + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + /* set DFCMARST and set DFCMWR */ + val |= 1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT; + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL); + + while (count && (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x01)) + count--; + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + + for (i = 1; i < vdfc->num_vdefects; i++) { + isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_vert & + ISIF_VDFC_POS_MASK, DFCMEM0); + + isif_write(isif->isif_cfg.base_addr, vdfc->table[i].pos_horz & + ISIF_VDFC_POS_MASK, DFCMEM1); + + if (vdfc->corr_mode == VPFE_ISIF_VDFC_NORMAL || + vdfc->corr_mode == VPFE_ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_at_pos, DFCMEM2); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_up_pixels, DFCMEM3); + isif_write(isif->isif_cfg.base_addr, + vdfc->table[i].level_low_pixels, DFCMEM4); + } + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + /* clear DFCMARST and set DFCMWR */ + val &= ~(1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT); + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, DFCMEMCTL); + + count = DFC_WRITE_WAIT_COUNT; + while (count && (isif_read(isif->isif_cfg.base_addr, + DFCMEMCTL) & 0x01)) + count--; + + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + } + if (vdfc->num_vdefects < VPFE_ISIF_VDFC_TABLE_SIZE) { + /* Extra cycle needed */ + isif_write(isif->isif_cfg.base_addr, 0, DFCMEM0); + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_MAX_DFCMEM1, DFCMEM1); + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_DFCMWR_MEMORY_WRITE, DFCMEMCTL); + } + /* enable VDFC */ + isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT), + (1 << ISIF_VDFC_EN_SHIFT), DFCCTL); + + isif_merge(isif->isif_cfg.base_addr, (1 << ISIF_VDFC_EN_SHIFT), + (0 << ISIF_VDFC_EN_SHIFT), DFCCTL); + + isif_write(isif->isif_cfg.base_addr, 0x6, DFCMEMCTL); + for (i = 0 ; i < vdfc->num_vdefects; i++) { + count = DFC_WRITE_WAIT_COUNT; + while (count && + (isif_read(isif->isif_cfg.base_addr, DFCMEMCTL) & 0x2)) + count--; + val = isif_read(isif->isif_cfg.base_addr, DFCMEMCTL); + if (!count) { + pr_debug("defect table write timeout !!\n"); + return; + } + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_DFCMRD_MEMORY_READ, DFCMEMCTL); + } +} + +static void +isif_config_csc(struct vpfe_isif_device *isif, struct vpfe_isif_df_csc *df_csc) +{ + u32 val1; + u32 val2; + u32 i; + + if (!df_csc->csc.en) { + isif_write(isif->isif_cfg.base_addr, 0, CSCCTL); + return; + } + /* initialize all bits to 0 */ + val1 = 0; + for (i = 0; i < VPFE_ISIF_CSC_NUM_COEFF; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = ((df_csc->csc.coeff[i].integer & + ISIF_CSC_COEF_INTEG_MASK) << + ISIF_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + ISIF_CSC_COEF_DECIMAL_MASK)); + } else { + + /* CSCM - MSB */ + val2 = ((df_csc->csc.coeff[i].integer & + ISIF_CSC_COEF_INTEG_MASK) << + ISIF_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + ISIF_CSC_COEF_DECIMAL_MASK)); + val2 <<= ISIF_CSCM_MSB_SHIFT; + val2 |= val1; + isif_write(isif->isif_cfg.base_addr, val2, + (CSCM0 + ((i-1) << 1))); + } + } + /* program the active area */ + isif_write(isif->isif_cfg.base_addr, df_csc->start_pix & + ISIF_DF_CSC_SPH_MASK, FMTSPH); + /* + * one extra pixel as required for CSC. Actually number of + * pixel - 1 should be configured in this register. So we + * need to subtract 1 before writing to FMTSPH, but we will + * not do this since csc requires one extra pixel + */ + isif_write(isif->isif_cfg.base_addr, df_csc->num_pixels & + ISIF_DF_CSC_SPH_MASK, FMTLNH); + isif_write(isif->isif_cfg.base_addr, df_csc->start_line & + ISIF_DF_CSC_SPH_MASK, FMTSLV); + /* + * one extra line as required for CSC. See reason documented for + * num_pixels + */ + isif_write(isif->isif_cfg.base_addr, df_csc->num_lines & + ISIF_DF_CSC_SPH_MASK, FMTLNV); + /* Enable CSC */ + isif_write(isif->isif_cfg.base_addr, 1, CSCCTL); +} + +static void +isif_config_linearization(struct vpfe_isif_device *isif, + struct vpfe_isif_linearize *linearize) +{ + u32 val; + u32 i; + + if (!linearize->en) { + isif_write(isif->isif_cfg.base_addr, 0, LINCFG0); + return; + } + /* shift value for correction */ + val = (linearize->corr_shft & ISIF_LIN_CORRSFT_MASK) << + ISIF_LIN_CORRSFT_SHIFT; + /* enable */ + val |= 1; + isif_write(isif->isif_cfg.base_addr, val, LINCFG0); + /* Scale factor */ + val = (linearize->scale_fact.integer & 1) << + ISIF_LIN_SCALE_FACT_INTEG_SHIFT; + val |= linearize->scale_fact.decimal & ISIF_LIN_SCALE_FACT_DECIMAL_MASK; + isif_write(isif->isif_cfg.base_addr, val, LINCFG1); + + for (i = 0; i < VPFE_ISIF_LINEAR_TAB_SIZE; i++) { + val = linearize->table[i] & ISIF_LIN_ENTRY_MASK; + if (i%2) + isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 1); + else + isif_regw_lin_tbl(isif, val, ((i >> 1) << 2), 0); + } +} + +static void +isif_config_culling(struct vpfe_isif_device *isif, struct vpfe_isif_cul *cul) +{ + u32 val; + + /* Horizontal pattern */ + val = cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT; + val |= cul->hcpat_odd; + isif_write(isif->isif_cfg.base_addr, val, CULH); + /* vertical pattern */ + isif_write(isif->isif_cfg.base_addr, cul->vcpat, CULV); + /* LPF */ + isif_merge(isif->isif_cfg.base_addr, ISIF_LPF_MASK << ISIF_LPF_SHIFT, + cul->en_lpf << ISIF_LPF_SHIFT, MODESET); +} + +static int isif_get_pix_fmt(u32 mbus_code) +{ + switch (mbus_code) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return ISIF_PIXFMT_RAW; + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + case V4L2_MBUS_FMT_Y8_1X8: + return ISIF_PIXFMT_YCBCR_8BIT; + + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + return ISIF_PIXFMT_YCBCR_16BIT; + + default: + break; + } + return -EINVAL; +} + +#define ISIF_INTERLACE_INVERSE_MODE 0x4b6d +#define ISIF_INTERLACE_NON_INVERSE_MODE 0x0b6d +#define ISIF_PROGRESSIVE_INVERSE_MODE 0x4000 +#define ISIF_PROGRESSIVE_NON_INVERSE_MODE 0x0000 + +static int isif_config_raw(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct isif_params_raw *params = &isif->isif_cfg.bayer; + struct vpfe_isif_raw_config *module_params = + &isif->isif_cfg.bayer.config_params; + struct v4l2_mbus_framefmt *format; + int pix_fmt; + u32 val; + + format = &isif->formats[ISIF_PAD_SINK]; + + /* In case of user has set BT656IF earlier, it should be reset + * when configuring for raw input. + */ + isif_write(isif->isif_cfg.base_addr, 0, REC656IF); + /* Configure CCDCFG register + * Set CCD Not to swap input since input is RAW data + * Set FID detection function to Latch at V-Sync + * Set WENLOG - isif valid area + * Set TRGSEL + * Set EXTRG + * Packed to 8 or 16 bits + */ + val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC | + ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN | + ISIF_CCDCFG_EXTRG_DISABLE | (isif->isif_cfg.data_pack & + ISIF_DATA_PACK_MASK); + isif_write(isif->isif_cfg.base_addr, val, CCDCFG); + + pix_fmt = isif_get_pix_fmt(format->code); + if (pix_fmt < 0) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* + * Configure the vertical sync polarity(MODESET.VDPOL) + * Configure the horizontal sync polarity (MODESET.HDPOL) + * Configure frame id polarity (MODESET.FLDPOL) + * Configure data polarity + * Configure External WEN Selection + * Configure frame format(progressive or interlace) + * Configure pixel format (Input mode) + * Configure the data shift + */ + val = ISIF_VDHDOUT_INPUT | ((params->vd_pol & ISIF_VD_POL_MASK) << + ISIF_VD_POL_SHIFT) | ((params->hd_pol & ISIF_HD_POL_MASK) << + ISIF_HD_POL_SHIFT) | ((params->fid_pol & ISIF_FID_POL_MASK) << + ISIF_FID_POL_SHIFT) | ((ISIF_DATAPOL_NORMAL & + ISIF_DATAPOL_MASK) << ISIF_DATAPOL_SHIFT) | ((ISIF_EXWEN_DISABLE & + ISIF_EXWEN_MASK) << ISIF_EXWEN_SHIFT) | ((params->frm_fmt & + ISIF_FRM_FMT_MASK) << ISIF_FRM_FMT_SHIFT) | ((pix_fmt & + ISIF_INPUT_MASK) << ISIF_INPUT_SHIFT); + + /* currently only V4L2_MBUS_FMT_SGRBG12_1X12 is + * supported. shift appropriately depending on + * different MBUS fmt's added + */ + if (format->code == V4L2_MBUS_FMT_SGRBG12_1X12) + val |= ((VPFE_ISIF_NO_SHIFT & + ISIF_DATASFT_MASK) << ISIF_DATASFT_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, MODESET); + /* + * Configure GAMMAWD register + * CFA pattern setting + */ + val = (params->cfa_pat & ISIF_GAMMAWD_CFA_MASK) << + ISIF_GAMMAWD_CFA_SHIFT; + /* Gamma msb */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10ALAW8) + val = val | ISIF_ALAW_ENABLE; + + val = val | ((params->data_msb & ISIF_ALAW_GAMA_WD_MASK) << + ISIF_ALAW_GAMA_WD_SHIFT); + + isif_write(isif->isif_cfg.base_addr, val, CGAMMAWD); + /* Configure DPCM compression settings */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10DPCM8) { + val = 1 << ISIF_DPCM_EN_SHIFT; + val |= (params->dpcm_predictor & + ISIF_DPCM_PREDICTOR_MASK) << ISIF_DPCM_PREDICTOR_SHIFT; + } + isif_write(isif->isif_cfg.base_addr, val, MISC); + /* Configure Gain & Offset */ + isif_config_gain_offset(isif); + /* Configure Color pattern */ + if (format->code == V4L2_MBUS_FMT_SGRBG12_1X12) + val = isif_sgrbg_pattern; + else + /* default set to rggb */ + val = isif_srggb_pattern; + + isif_write(isif->isif_cfg.base_addr, val, CCOLP); + + /* Configure HSIZE register */ + val = (params->horz_flip_en & ISIF_HSIZE_FLIP_MASK) << + ISIF_HSIZE_FLIP_SHIFT; + + /* calculate line offset in 32 bytes based on pack value */ + if (isif->isif_cfg.data_pack == ISIF_PACK_8BIT) + val |= ((params->win.width + 31) >> 5) & ISIF_LINEOFST_MASK; + else if (isif->isif_cfg.data_pack == ISIF_PACK_12BIT) + val |= ((((params->win.width + (params->win.width >> 2)) + + 31) >> 5) & ISIF_LINEOFST_MASK); + else + val |= (((params->win.width * 2) + 31) >> 5) & + ISIF_LINEOFST_MASK; + isif_write(isif->isif_cfg.base_addr, val, HSIZE); + /* Configure SDOFST register */ + if (params->frm_fmt == ISIF_FRMFMT_INTERLACED) { + if (params->image_invert_en) + /* For interlace inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_INTERLACE_INVERSE_MODE, SDOFST); + else + /* For interlace non inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_INTERLACE_NON_INVERSE_MODE, SDOFST); + } else if (params->frm_fmt == ISIF_FRMFMT_PROGRESSIVE) { + if (params->image_invert_en) + isif_write(isif->isif_cfg.base_addr, + ISIF_PROGRESSIVE_INVERSE_MODE, SDOFST); + else + /* For progessive non inverse mode */ + isif_write(isif->isif_cfg.base_addr, + ISIF_PROGRESSIVE_NON_INVERSE_MODE, SDOFST); + } + /* Configure video window */ + isif_setwin(isif, ¶ms->win, params->frm_fmt, 1, mode); + /* Configure Black Clamp */ + isif_config_bclamp(isif, &module_params->bclamp); + /* Configure Vertical Defection Pixel Correction */ + isif_config_dfc(isif, &module_params->dfc); + if (!module_params->df_csc.df_or_csc) + /* Configure Color Space Conversion */ + isif_config_csc(isif, &module_params->df_csc); + + isif_config_linearization(isif, &module_params->linearize); + /* Configure Culling */ + isif_config_culling(isif, &module_params->culling); + /* Configure Horizontal and vertical offsets(DFC,LSC,Gain) */ + val = module_params->horz_offset & ISIF_DATA_H_OFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, DATAHOFST); + + val = module_params->vert_offset & ISIF_DATA_V_OFFSET_MASK; + isif_write(isif->isif_cfg.base_addr, val, DATAVOFST); + + return 0; +} + +#define DM365_ISIF_HSIZE_MASK 0xffffffe0 +#define DM365_ISIF_SDOFST_2_LINES 0x00000249 + +/* This function will configure ISIF for YCbCr parameters. */ +static int isif_config_ycbcr(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct isif_ycbcr_config *params = &isif->isif_cfg.ycbcr; + struct v4l2_mbus_framefmt *format; + int pix_fmt; + u32 modeset; + u32 ccdcfg; + + format = &isif->formats[ISIF_PAD_SINK]; + /* + * first reset the ISIF + * all registers have default values after reset + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + /* start with all bits zero */ + ccdcfg = modeset = 0; + pix_fmt = isif_get_pix_fmt(format->code); + if (pix_fmt < 0) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* configure pixel format or input mode */ + modeset = modeset | ((pix_fmt & ISIF_INPUT_MASK) << + ISIF_INPUT_SHIFT) | ((params->frm_fmt & ISIF_FRM_FMT_MASK) << + ISIF_FRM_FMT_SHIFT) | (((params->fid_pol & + ISIF_FID_POL_MASK) << ISIF_FID_POL_SHIFT)) | + (((params->hd_pol & ISIF_HD_POL_MASK) << ISIF_HD_POL_SHIFT)) | + (((params->vd_pol & ISIF_VD_POL_MASK) << ISIF_VD_POL_SHIFT)); + /* pack the data to 8-bit CCDCCFG */ + switch (format->code) { + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + modeset |= ((VPFE_PINPOL_NEGATIVE & ISIF_VD_POL_MASK) << + ISIF_VD_POL_SHIFT); + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR; + break; + + case V4L2_MBUS_FMT_YUYV10_2X10: + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* setup BT.656, embedded sync */ + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + /* enable 10 bit mode in ccdcfg */ + ccdcfg = ccdcfg | ISIF_PACK_8BIT | ISIF_YCINSWP_YCBCR | + ISIF_BW656_ENABLE; + break; + + case V4L2_MBUS_FMT_YUYV10_1X20: + if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + isif_write(isif->isif_cfg.base_addr, 3, REC656IF); + break; + + case V4L2_MBUS_FMT_Y8_1X8: + ccdcfg |= ISIF_PACK_8BIT; + ccdcfg |= ISIF_YCINSWP_YCBCR; + if (pix_fmt != ISIF_PIXFMT_YCBCR_8BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + + case V4L2_MBUS_FMT_YUYV8_1X16: + if (pix_fmt != ISIF_PIXFMT_YCBCR_16BIT) { + pr_debug("Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + + default: + /* should never come here */ + pr_debug("Invalid interface type\n"); + return -EINVAL; + } + isif_write(isif->isif_cfg.base_addr, modeset, MODESET); + /* Set up pix order */ + ccdcfg |= (params->pix_order & ISIF_PIX_ORDER_MASK) << + ISIF_PIX_ORDER_SHIFT; + isif_write(isif->isif_cfg.base_addr, ccdcfg, CCDCFG); + /* configure video window */ + if (format->code == V4L2_MBUS_FMT_YUYV10_1X20 || + format->code == V4L2_MBUS_FMT_YUYV8_1X16) + isif_setwin(isif, ¶ms->win, params->frm_fmt, 1, mode); + else + isif_setwin(isif, ¶ms->win, params->frm_fmt, 2, mode); + + /* + * configure the horizontal line offset + * this is done by rounding up width to a multiple of 16 pixels + * and multiply by two to account for y:cb:cr 4:2:2 data + */ + isif_write(isif->isif_cfg.base_addr, + ((((params->win.width * 2) + 31) & + DM365_ISIF_HSIZE_MASK) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->frm_fmt == ISIF_FRMFMT_INTERLACED && + params->buf_type == ISIF_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + isif_write(isif->isif_cfg.base_addr, + DM365_ISIF_SDOFST_2_LINES, SDOFST); + return 0; +} + +static int isif_configure(struct v4l2_subdev *sd, int mode) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = &isif->formats[ISIF_PAD_SINK]; + + switch (format->code) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return isif_config_raw(sd, mode); + + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + return isif_config_ycbcr(sd, mode); + + default: + break; + } + return -EINVAL; +} + +/* + * isif_set_stream() - Enable/Disable streaming on the ISIF module + * @sd: VPFE ISIF V4L2 subdevice + * @enable: Enable/disable stream + */ +static int isif_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + int ret; + + if (enable) { + ret = isif_configure(sd, + (isif->output == ISIF_OUTPUT_MEMORY) ? 0 : 1); + if (ret) + return ret; + if (isif->output == ISIF_OUTPUT_MEMORY) + isif_enable_output_to_sdram(isif, 1); + isif_enable(isif, 1); + } else { + isif_enable(isif, 0); + isif_enable_output_to_sdram(isif, 0); + } + + return 0; +} + +/* + * __isif_get_format() - helper function for getting isif format + * @isif: pointer to isif private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + */ +static struct v4l2_mbus_framefmt * +__isif_get_format(struct vpfe_isif_device *isif, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_subdev_format fmt; + + fmt.pad = pad; + fmt.which = which; + + return v4l2_subdev_get_try_format(fh, pad); + } + return &isif->formats[pad]; +} + +/* +* isif_set_format() - set format on pad +* @sd : VPFE ISIF device +* @fh : V4L2 subdev file handle +* @fmt : pointer to v4l2 subdev format structure +* +* Return 0 on success or -EINVAL if format or pad is invalid +*/ +static int +isif_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + struct v4l2_mbus_framefmt *format; + + format = __isif_get_format(isif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + isif_try_format(isif, fh, fmt); + memcpy(format, &fmt->format, sizeof(*format)); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == ISIF_PAD_SOURCE) + return isif_config_format(vpfe_dev, fmt->pad); + + return 0; +} + +/* + * isif_get_format() - Retrieve the video format on a pad + * @sd: VPFE ISIF V4L2 subdevice + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int +isif_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __isif_get_format(vpfe_isif, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + memcpy(&fmt->format, format, sizeof(fmt->format)); + + return 0; +} + +/* + * isif_enum_frame_size() - enum frame sizes on pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_frame_size_enum structure + */ +static int +isif_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + if (fse->index != 0) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = 1; + format.format.height = 1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + isif_try_format(isif, fh, &format); + fse->min_width = format.format.width; + fse->min_height = format.format.height; + + if (format.format.code != fse->code) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = -1; + format.format.height = -1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + isif_try_format(isif, fh, &format); + fse->max_width = format.format.width; + fse->max_height = format.format.height; + + return 0; +} + +/* + * isif_enum_mbus_code() - enum mbus codes for pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int +isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case ISIF_PAD_SINK: + case ISIF_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(isif_fmts)) + return -EINVAL; + code->code = isif_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * isif_pad_set_crop() - set crop rectangle on pad + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int +isif_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + /* check wether its a valid pad */ + if (crop->pad != ISIF_PAD_SINK) + return -EINVAL; + + format = __isif_get_format(vpfe_isif, fh, crop->pad, crop->which); + if (format == NULL) + return -EINVAL; + + /* check wether crop rect is within limits */ + if (crop->rect.top < 0 || crop->rect.left < 0 || + (crop->rect.left + crop->rect.width > + vpfe_isif->formats[ISIF_PAD_SINK].width) || + (crop->rect.top + crop->rect.height > + vpfe_isif->formats[ISIF_PAD_SINK].height)) { + crop->rect.left = 0; + crop->rect.top = 0; + crop->rect.width = format->width; + crop->rect.height = format->height; + } + /* adjust the width to 16 pixel boundry */ + crop->rect.width = ((crop->rect.width + 15) & ~0xf); + vpfe_isif->crop = crop->rect; + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + isif_set_image_window(vpfe_isif); + } else { + struct v4l2_rect *rect; + + rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); + memcpy(rect, &vpfe_isif->crop, sizeof(*rect)); + } + return 0; +} + +/* + * isif_pad_get_crop() - get crop rectangle on pad + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int +isif_pad_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); + + /* check wether its a valid pad */ + if (crop->pad != ISIF_PAD_SINK) + return -EINVAL; + + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_rect *rect; + rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); + memcpy(&crop->rect, rect, sizeof(*rect)); + } else { + crop->rect = vpfe_isif->crop; + } + + return 0; +} + +/* + * isif_init_formats() - Initialize formats on all pads + * @sd: VPFE isif V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +isif_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + struct v4l2_subdev_crop crop; + + memset(&format, 0, sizeof(format)); + format.pad = ISIF_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + isif_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = ISIF_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + isif_set_format(sd, fh, &format); + + memset(&crop, 0, sizeof(crop)); + crop.pad = ISIF_PAD_SINK; + crop.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + crop.rect.width = MAX_WIDTH; + crop.rect.height = MAX_HEIGHT; + isif_pad_set_crop(sd, fh, &crop); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops isif_v4l2_core_ops = { + .ioctl = isif_ioctl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops isif_v4l2_internal_ops = { + .open = isif_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops isif_v4l2_video_ops = { + .s_stream = isif_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops isif_v4l2_pad_ops = { + .enum_mbus_code = isif_enum_mbus_code, + .enum_frame_size = isif_enum_frame_size, + .get_fmt = isif_get_format, + .set_fmt = isif_set_format, + .set_crop = isif_pad_set_crop, + .get_crop = isif_pad_get_crop, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops isif_v4l2_ops = { + .core = &isif_v4l2_core_ops, + .video = &isif_v4l2_video_ops, + .pad = &isif_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * isif_link_setup() - Setup isif connections + * @entity: isif media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int +isif_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_isif_device *isif = v4l2_get_subdevdata(sd); + + switch (local->index | media_entity_type(remote->entity)) { + case ISIF_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from decoder/sensor */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + isif->input = ISIF_INPUT_NONE; + break; + } + if (isif->input != ISIF_INPUT_NONE) + return -EBUSY; + isif->input = ISIF_INPUT_PARALLEL; + break; + + case ISIF_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) + isif->output = ISIF_OUTPUT_MEMORY; + else + isif->output = ISIF_OUTPUT_NONE; + break; + + case ISIF_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (flags & MEDIA_LNK_FL_ENABLED) + isif->output = ISIF_OUTPUT_IPIPEIF; + else + isif->output = ISIF_OUTPUT_NONE; + break; + + default: + return -EINVAL; + } + + return 0; +} +static const struct media_entity_operations isif_media_ops = { + .link_setup = isif_link_setup, +}; + +/* + * vpfe_isif_unregister_entities() - isif unregister entity + * @isif - pointer to isif subdevice structure. + */ +void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif) +{ + vpfe_video_unregister(&isif->video_out); + /* cleanup entity */ + media_entity_cleanup(&isif->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&isif->subdev); +} + +static void isif_restore_defaults(struct vpfe_isif_device *isif) +{ + enum vpss_ccdc_source_sel source = VPSS_CCDCIN; + int i; + + memset(&isif->isif_cfg.bayer.config_params, 0, + sizeof(struct vpfe_isif_raw_config)); + + isif->isif_cfg.bayer.config_params.linearize.corr_shft = + VPFE_ISIF_NO_SHIFT; + isif->isif_cfg.bayer.config_params.linearize.scale_fact.integer = 1; + isif->isif_cfg.bayer.config_params.culling.hcpat_odd = + ISIF_CULLING_HCAPT_ODD; + isif->isif_cfg.bayer.config_params.culling.hcpat_even = + ISIF_CULLING_HCAPT_EVEN; + isif->isif_cfg.bayer.config_params.culling.vcpat = ISIF_CULLING_VCAPT; + /* Enable clock to ISIF, IPIPEIF and BL */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 1); + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + vpss_enable_clock(VPSS_BL_CLOCK, 1); + + /* set all registers to default value */ + for (i = 0; i <= 0x1f8; i += 4) + isif_write(isif->isif_cfg.base_addr, 0, i); + /* no culling support */ + isif_write(isif->isif_cfg.base_addr, 0xffff, CULH); + isif_write(isif->isif_cfg.base_addr, 0xff, CULV); + + /* Set default offset and gain */ + isif_config_gain_offset(isif); + vpss_select_ccdc_source(source); +} + +/* + * vpfe_isif_register_entities() - isif register entity + * @isif - pointer to isif subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_isif_register_entities(struct vpfe_isif_device *isif, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(isif); + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &isif->subdev); + if (ret < 0) + return ret; + + isif_restore_defaults(isif); + ret = vpfe_video_register(&isif->video_out, vdev); + if (ret) { + pr_err("Failed to register isif video out device\n"); + goto out_video_register; + } + isif->video_out.vpfe_dev = vpfe_dev; + flags = 0; + /* connect isif to video node */ + ret = media_entity_create_link(&isif->subdev.entity, 1, + &isif->video_out.video_dev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + return 0; +out_create_link: + vpfe_video_unregister(&isif->video_out); +out_video_register: + v4l2_device_unregister_subdev(&isif->subdev); + return ret; +} + +/* ------------------------------------------------------------------- + * V4L2 subdev control operations + */ + +static int vpfe_isif_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_isif_device *isif = + container_of(ctrl->handler, struct vpfe_isif_device, ctrls); + struct isif_oper_config *config = &isif->isif_cfg; + + switch (ctrl->id) { + case VPFE_CID_DPCM_PREDICTOR: + config->bayer.dpcm_predictor = ctrl->val; + break; + + case VPFE_ISIF_CID_CRGAIN: + config->isif_gain_params.cr_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CGRGAIN: + config->isif_gain_params.cgr_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CGBGAIN: + config->isif_gain_params.cgb_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_CBGAIN: + config->isif_gain_params.cb_gain = ctrl->val; + break; + + case VPFE_ISIF_CID_GAIN_OFFSET: + config->isif_gain_params.offset = ctrl->val; + break; + + default: + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops vpfe_isif_ctrl_ops = { + .s_ctrl = vpfe_isif_s_ctrl, +}; + +static const struct v4l2_ctrl_config vpfe_isif_dpcm_pred = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_CID_DPCM_PREDICTOR, + .name = "DPCM Predictor", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_crgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CRGAIN, + .name = "CRGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cgrgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CGRGAIN, + .name = "CGRGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cgbgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CGBGAIN, + .name = "CGBGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_cbgain = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_CBGAIN, + .name = "CBGAIN", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config vpfe_isif_gain_offset = { + .ops = &vpfe_isif_ctrl_ops, + .id = VPFE_ISIF_CID_GAIN_OFFSET, + .name = "Gain Offset", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = (1 << 12) - 1, + .step = 1, + .def = 0, +}; + +static void isif_remove(struct vpfe_isif_device *isif, + struct platform_device *pdev) +{ + struct resource *res; + int i = 0; + + iounmap(isif->isif_cfg.base_addr); + iounmap(isif->isif_cfg.linear_tbl0_addr); + iounmap(isif->isif_cfg.linear_tbl1_addr); + + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); + i++; + } +} + +static void isif_config_defaults(struct vpfe_isif_device *isif) +{ + isif->isif_cfg.ycbcr.v4l2_pix_fmt = V4L2_PIX_FMT_UYVY; + isif->isif_cfg.ycbcr.pix_fmt = ISIF_PIXFMT_YCBCR_8BIT; + isif->isif_cfg.ycbcr.frm_fmt = ISIF_FRMFMT_INTERLACED; + isif->isif_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.ycbcr.pix_order = ISIF_PIXORDER_CBYCRY; + isif->isif_cfg.ycbcr.buf_type = ISIF_BUFTYPE_FLD_INTERLEAVED; + + isif->isif_cfg.bayer.v4l2_pix_fmt = V4L2_PIX_FMT_SGRBG10ALAW8; + isif->isif_cfg.bayer.pix_fmt = ISIF_PIXFMT_RAW; + isif->isif_cfg.bayer.frm_fmt = ISIF_FRMFMT_PROGRESSIVE; + isif->isif_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE; + isif->isif_cfg.bayer.cfa_pat = ISIF_CFA_PAT_MOSAIC; + isif->isif_cfg.bayer.data_msb = ISIF_BIT_MSB_11; + isif->isif_cfg.data_pack = ISIF_PACK_8BIT; +} +/* + * vpfe_isif_init() - Initialize V4L2 subdev and media entity + * @isif: VPFE isif module + * @pdev: Pointer to platform device structure. + * Return 0 on success and a negative error code on failure. + */ +int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &isif->subdev; + struct media_pad *pads = &isif->pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + void *__iomem addr; + int status; + int i = 0; + + /* Get the ISIF base address, linearization table0 and table1 addr. */ + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + status = -ENOENT; + goto fail_nobase_res; + } + res_len = res->end - res->start + 1; + res = request_mem_region(res->start, res_len, res->name); + if (!res) { + status = -EBUSY; + goto fail_nobase_res; + } + addr = ioremap_nocache(res->start, res_len); + if (!addr) { + status = -EBUSY; + goto fail_base_iomap; + } + switch (i) { + case 0: + /* ISIF base address */ + isif->isif_cfg.base_addr = addr; + break; + case 1: + /* ISIF linear tbl0 address */ + isif->isif_cfg.linear_tbl0_addr = addr; + break; + default: + /* ISIF linear tbl0 address */ + isif->isif_cfg.linear_tbl1_addr = addr; + break; + } + i++; + } + davinci_cfg_reg(DM365_VIN_CAM_WEN); + davinci_cfg_reg(DM365_VIN_CAM_VD); + davinci_cfg_reg(DM365_VIN_CAM_HD); + davinci_cfg_reg(DM365_VIN_YIN4_7_EN); + davinci_cfg_reg(DM365_VIN_YIN0_3_EN); + + /* queue ops */ + isif->video_out.ops = &isif_video_ops; + v4l2_subdev_init(sd, &isif_v4l2_ops); + sd->internal_ops = &isif_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI ISIF", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, isif); + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + pads[ISIF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[ISIF_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + isif->input = ISIF_INPUT_NONE; + isif->output = ISIF_OUTPUT_NONE; + me->ops = &isif_media_ops; + status = media_entity_init(me, ISIF_PADS_NUM, pads, 0); + if (status) + goto isif_fail; + isif->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + status = vpfe_video_init(&isif->video_out, "ISIF"); + if (status) { + pr_err("Failed to init isif-out video device\n"); + goto isif_fail; + } + v4l2_ctrl_handler_init(&isif->ctrls, 6); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_crgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgrgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cgbgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_cbgain, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_gain_offset, NULL); + v4l2_ctrl_new_custom(&isif->ctrls, &vpfe_isif_dpcm_pred, NULL); + + v4l2_ctrl_handler_setup(&isif->ctrls); + sd->ctrl_handler = &isif->ctrls; + isif_config_defaults(isif); + return 0; +fail_base_iomap: + release_mem_region(res->start, res_len); + i--; +fail_nobase_res: + if (isif->isif_cfg.base_addr) + iounmap(isif->isif_cfg.base_addr); + if (isif->isif_cfg.linear_tbl0_addr) + iounmap(isif->isif_cfg.linear_tbl0_addr); + + while (i >= 0) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + release_mem_region(res->start, res_len); + i--; + } + return status; +isif_fail: + v4l2_ctrl_handler_free(&isif->ctrls); + isif_remove(isif, pdev); + return status; +} + +/* + * vpfe_isif_cleanup - isif module cleanup + * @isif: pointer to isif subdevice + * @dev: pointer to platform device structure + */ +void +vpfe_isif_cleanup(struct vpfe_isif_device *isif, struct platform_device *pdev) +{ + isif_remove(isif, pdev); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.h b/drivers/staging/media/davinci_vpfe/dm365_isif.h new file mode 100644 index 000000000000..473fd2cfe350 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_ISIF_H +#define _DAVINCI_VPFE_DM365_ISIF_H + +#include + +#include + +#include +#include +#include + +#include "davinci_vpfe_user.h" +#include "dm365_isif_regs.h" +#include "vpfe_video.h" + +#define ISIF_CULLING_HCAPT_ODD 0xff +#define ISIF_CULLING_HCAPT_EVEN 0xff +#define ISIF_CULLING_VCAPT 0xff + +#define ISIF_CADU_BITS 0x07ff +#define ISIF_CADL_BITS 0x0ffff + +enum isif_pixfmt { + ISIF_PIXFMT_RAW = 0, + ISIF_PIXFMT_YCBCR_16BIT = 1, + ISIF_PIXFMT_YCBCR_8BIT = 2, +}; + +enum isif_frmfmt { + ISIF_FRMFMT_PROGRESSIVE = 0, + ISIF_FRMFMT_INTERLACED = 1, +}; + +/* PIXEL ORDER IN MEMORY from LSB to MSB */ +/* only applicable for 8-bit input mode */ +enum isif_pixorder { + ISIF_PIXORDER_YCBYCR = 0, + ISIF_PIXORDER_CBYCRY = 1, +}; + +enum isif_buftype { + ISIF_BUFTYPE_FLD_INTERLEAVED = 0, + ISIF_BUFTYPE_FLD_SEPARATED = 1, +}; + +struct isif_ycbcr_config { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* isif pixel format */ + enum isif_pixfmt pix_fmt; + /* isif frame format */ + enum isif_frmfmt frm_fmt; + /* isif crop window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* isif pix order. Only used for ycbcr capture */ + enum isif_pixorder pix_order; + /* isif buffer type. Only used for ycbcr capture */ + enum isif_buftype buf_type; +}; + +enum isif_cfa_pattern { + ISIF_CFA_PAT_MOSAIC = 0, + ISIF_CFA_PAT_STRIPE = 1, +}; + +enum isif_data_msb { + /* MSB b15 */ + ISIF_BIT_MSB_15 = 0, + /* MSB b14 */ + ISIF_BIT_MSB_14 = 1, + /* MSB b13 */ + ISIF_BIT_MSB_13 = 2, + /* MSB b12 */ + ISIF_BIT_MSB_12 = 3, + /* MSB b11 */ + ISIF_BIT_MSB_11 = 4, + /* MSB b10 */ + ISIF_BIT_MSB_10 = 5, + /* MSB b9 */ + ISIF_BIT_MSB_9 = 6, + /* MSB b8 */ + ISIF_BIT_MSB_8 = 7, + /* MSB b7 */ + ISIF_BIT_MSB_7 = 8, +}; + +struct isif_params_raw { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* isif pixel format */ + enum isif_pixfmt pix_fmt; + /* isif frame format */ + enum isif_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* buffer type. Applicable for interlaced mode */ + enum isif_buftype buf_type; + /* cfa pattern */ + enum isif_cfa_pattern cfa_pat; + /* Data MSB position */ + enum isif_data_msb data_msb; + /* Enable horizontal flip */ + unsigned char horz_flip_en; + /* Enable image invert vertically */ + unsigned char image_invert_en; + unsigned char dpcm_predictor; + struct vpfe_isif_raw_config config_params; +}; + +enum isif_data_pack { + ISIF_PACK_16BIT = 0, + ISIF_PACK_12BIT = 1, + ISIF_PACK_8BIT = 2, +}; + +struct isif_gain_values { + unsigned int cr_gain; + unsigned int cgr_gain; + unsigned int cgb_gain; + unsigned int cb_gain; + unsigned int offset; +}; + +struct isif_oper_config { + struct isif_ycbcr_config ycbcr; + struct isif_params_raw bayer; + enum isif_data_pack data_pack; + struct isif_gain_values isif_gain_params; + void *__iomem base_addr; + void *__iomem linear_tbl0_addr; + void *__iomem linear_tbl1_addr; +}; + +#define ISIF_PAD_SINK 0 +#define ISIF_PAD_SOURCE 1 + +#define ISIF_PADS_NUM 2 + +enum isif_input_entity { + ISIF_INPUT_NONE = 0, + ISIF_INPUT_PARALLEL = 1, +}; + +#define ISIF_OUTPUT_NONE (0) +#define ISIF_OUTPUT_MEMORY (1 << 0) +#define ISIF_OUTPUT_IPIPEIF (1 << 1) + +struct vpfe_isif_device { + struct v4l2_subdev subdev; + struct media_pad pads[ISIF_PADS_NUM]; + struct v4l2_mbus_framefmt formats[ISIF_PADS_NUM]; + enum isif_input_entity input; + unsigned int output; + struct v4l2_ctrl_handler ctrls; + struct v4l2_rect crop; + struct isif_oper_config isif_cfg; + struct vpfe_video_device video_out; +}; + +enum v4l2_field vpfe_isif_get_fid(struct vpfe_device *vpfe_dev); +void vpfe_isif_unregister_entities(struct vpfe_isif_device *isif); +int vpfe_isif_register_entities(struct vpfe_isif_device *isif, + struct v4l2_device *dev); +int vpfe_isif_init(struct vpfe_isif_device *isif, struct platform_device *pdev); +void vpfe_isif_cleanup(struct vpfe_isif_device *vpfe_isif, + struct platform_device *pdev); +void vpfe_isif_vidint1_isr(struct vpfe_isif_device *isif); +void vpfe_isif_buffer_isr(struct vpfe_isif_device *isif); + +#endif /* _DAVINCI_VPFE_DM365_ISIF_H */ diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h new file mode 100644 index 000000000000..8aceabb43f8e --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_isif_regs.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_ISIF_REGS_H +#define _DAVINCI_VPFE_DM365_ISIF_REGS_H + +/* ISIF registers relative offsets */ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDW 0x08 +#define VDW 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define LNH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define LNV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define CADU 0x3c +#define CADL 0x40 +#define LINCFG0 0x44 +#define LINCFG1 0x48 +#define CCOLP 0x4c +#define CRGAIN 0x50 +#define CGRGAIN 0x54 +#define CGBGAIN 0x58 +#define CBGAIN 0x5c +#define COFSTA 0x60 +#define FLSHCFG0 0x64 +#define FLSHCFG1 0x68 +#define FLSHCFG2 0x6c +#define VDINT0 0x70 +#define VDINT1 0x74 +#define VDINT2 0x78 +#define MISC 0x7c +#define CGAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +/***************************************************** +* Defect Correction registers +*****************************************************/ +#define DFCCTL 0x8c +#define VDFSATLV 0x90 +#define DFCMEMCTL 0x94 +#define DFCMEM0 0x98 +#define DFCMEM1 0x9c +#define DFCMEM2 0xa0 +#define DFCMEM3 0xa4 +#define DFCMEM4 0xa8 +/**************************************************** +* Black Clamp registers +****************************************************/ +#define CLAMPCFG 0xac +#define CLDCOFST 0xb0 +#define CLSV 0xb4 +#define CLHWIN0 0xb8 +#define CLHWIN1 0xbc +#define CLHWIN2 0xc0 +#define CLVRV 0xc4 +#define CLVWIN0 0xc8 +#define CLVWIN1 0xcc +#define CLVWIN2 0xd0 +#define CLVWIN3 0xd4 +/**************************************************** +* Lense Shading Correction +****************************************************/ +#define DATAHOFST 0xd8 +#define DATAVOFST 0xdc +#define LSCHVAL 0xe0 +#define LSCVVAL 0xe4 +#define TWODLSCCFG 0xe8 +#define TWODLSCOFST 0xec +#define TWODLSCINI 0xf0 +#define TWODLSCGRBU 0xf4 +#define TWODLSCGRBL 0xf8 +#define TWODLSCGROF 0xfc +#define TWODLSCORBU 0x100 +#define TWODLSCORBL 0x104 +#define TWODLSCOROF 0x108 +#define TWODLSCIRQEN 0x10c +#define TWODLSCIRQST 0x110 +/**************************************************** +* Data formatter +****************************************************/ +#define FMTCFG 0x114 +#define FMTPLEN 0x118 +#define FMTSPH 0x11c +#define FMTLNH 0x120 +#define FMTSLV 0x124 +#define FMTLNV 0x128 +#define FMTRLEN 0x12c +#define FMTHCNT 0x130 +#define FMTAPTR_BASE 0x134 +/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */ +#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4)) +#define FMTPGMVF0 0x174 +#define FMTPGMVF1 0x178 +#define FMTPGMAPU0 0x17c +#define FMTPGMAPU1 0x180 +#define FMTPGMAPS0 0x184 +#define FMTPGMAPS1 0x188 +#define FMTPGMAPS2 0x18c +#define FMTPGMAPS3 0x190 +#define FMTPGMAPS4 0x194 +#define FMTPGMAPS5 0x198 +#define FMTPGMAPS6 0x19c +#define FMTPGMAPS7 0x1a0 +/************************************************ +* Color Space Converter +************************************************/ +#define CSCCTL 0x1a4 +#define CSCM0 0x1a8 +#define CSCM1 0x1ac +#define CSCM2 0x1b0 +#define CSCM3 0x1b4 +#define CSCM4 0x1b8 +#define CSCM5 0x1bc +#define CSCM6 0x1c0 +#define CSCM7 0x1c4 +#define OBWIN0 0x1c8 +#define OBWIN1 0x1cc +#define OBWIN2 0x1d0 +#define OBWIN3 0x1d4 +#define OBVAL0 0x1d8 +#define OBVAL1 0x1dc +#define OBVAL2 0x1e0 +#define OBVAL3 0x1e4 +#define OBVAL4 0x1e8 +#define OBVAL5 0x1ec +#define OBVAL6 0x1f0 +#define OBVAL7 0x1f4 +#define CLKCTL 0x1f8 + +/* Masks & Shifts below */ +#define START_PX_HOR_MASK 0x7fff +#define NUM_PX_HOR_MASK 0x7fff +#define START_VER_ONE_MASK 0x7fff +#define START_VER_TWO_MASK 0x7fff +#define NUM_LINES_VER 0x7fff + +/* gain - offset masks */ +#define OFFSET_MASK 0xfff +#define GAIN_SDRAM_EN_SHIFT 12 +#define GAIN_IPIPE_EN_SHIFT 13 +#define GAIN_H3A_EN_SHIFT 14 +#define OFST_SDRAM_EN_SHIFT 8 +#define OFST_IPIPE_EN_SHIFT 9 +#define OFST_H3A_EN_SHIFT 10 +#define GAIN_OFFSET_EN_MASK 0x7700 + +/* Culling */ +#define CULL_PAT_EVEN_LINE_SHIFT 8 + +/* CCDCFG register */ +#define ISIF_YCINSWP_RAW (0x00 << 4) +#define ISIF_YCINSWP_YCBCR (0x01 << 4) +#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6) +#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8) +#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9) +#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10) +#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15) +#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15) +#define ISIF_DATA_PACK_MASK 0x03 +#define ISIF_PIX_ORDER_SHIFT 11 +#define ISIF_PIX_ORDER_MASK 0x01 +#define ISIF_BW656_ENABLE (0x01 << 5) + +/* MODESET registers */ +#define ISIF_VDHDOUT_INPUT (0x00 << 0) +#define ISIF_INPUT_MASK 0x03 +#define ISIF_INPUT_SHIFT 12 +#define ISIF_FID_POL_MASK 0x01 +#define ISIF_FID_POL_SHIFT 4 +#define ISIF_HD_POL_MASK 0x01 +#define ISIF_HD_POL_SHIFT 3 +#define ISIF_VD_POL_MASK 0x01 +#define ISIF_VD_POL_SHIFT 2 +#define ISIF_DATAPOL_NORMAL 0x00 +#define ISIF_DATAPOL_MASK 0x01 +#define ISIF_DATAPOL_SHIFT 6 +#define ISIF_EXWEN_DISABLE 0x00 +#define ISIF_EXWEN_MASK 0x01 +#define ISIF_EXWEN_SHIFT 5 +#define ISIF_FRM_FMT_MASK 0x01 +#define ISIF_FRM_FMT_SHIFT 7 +#define ISIF_DATASFT_MASK 0x07 +#define ISIF_DATASFT_SHIFT 8 +#define ISIF_LPF_SHIFT 14 +#define ISIF_LPF_MASK 0x1 + +/* GAMMAWD registers */ +#define ISIF_ALAW_GAMA_WD_MASK 0xf +#define ISIF_ALAW_GAMA_WD_SHIFT 1 +#define ISIF_ALAW_ENABLE 0x01 +#define ISIF_GAMMAWD_CFA_MASK 0x01 +#define ISIF_GAMMAWD_CFA_SHIFT 5 + +/* HSIZE registers */ +#define ISIF_HSIZE_FLIP_MASK 0x01 +#define ISIF_HSIZE_FLIP_SHIFT 12 +#define ISIF_LINEOFST_MASK 0xfff + +/* MISC registers */ +#define ISIF_DPCM_EN_SHIFT 12 +#define ISIF_DPCM_PREDICTOR_SHIFT 13 +#define ISIF_DPCM_PREDICTOR_MASK 1 + +/* Black clamp related */ +#define ISIF_BC_DCOFFSET_MASK 0x1fff +#define ISIF_BC_MODE_COLOR_MASK 1 +#define ISIF_BC_MODE_COLOR_SHIFT 4 +#define ISIF_HORZ_BC_MODE_MASK 3 +#define ISIF_HORZ_BC_MODE_SHIFT 1 +#define ISIF_HORZ_BC_WIN_COUNT_MASK 0x1f +#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5 +#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6 +#define ISIF_HORZ_BC_WIN_H_SIZE_MASK 3 +#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8 +#define ISIF_HORZ_BC_WIN_V_SIZE_MASK 3 +#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12 +#define ISIF_HORZ_BC_WIN_START_H_MASK 0x1fff +#define ISIF_HORZ_BC_WIN_START_V_MASK 0x1fff +#define ISIF_VERT_BC_OB_H_SZ_MASK 7 +#define ISIF_VERT_BC_RST_VAL_SEL_MASK 3 +#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4 +#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8 +#define ISIF_VERT_BC_OB_START_HORZ_MASK 0x1fff +#define ISIF_VERT_BC_OB_START_VERT_MASK 0x1fff +#define ISIF_VERT_BC_OB_VERT_SZ_MASK 0x1fff +#define ISIF_VERT_BC_RST_VAL_MASK 0xfff +#define ISIF_BC_VERT_START_SUB_V_MASK 0x1fff + +/* VDFC registers */ +#define ISIF_VDFC_EN_SHIFT 4 +#define ISIF_VDFC_CORR_MOD_MASK 3 +#define ISIF_VDFC_CORR_MOD_SHIFT 5 +#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7 +#define ISIF_VDFC_LEVEL_SHFT_MASK 7 +#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8 +#define ISIF_VDFC_SAT_LEVEL_MASK 0xfff +#define ISIF_VDFC_POS_MASK 0x1fff +#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2 + +/* CSC registers */ +#define ISIF_CSC_COEF_INTEG_MASK 7 +#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f +#define ISIF_CSC_COEF_INTEG_SHIFT 5 +#define ISIF_CSCM_MSB_SHIFT 8 +#define ISIF_DF_CSC_SPH_MASK 0x1fff +#define ISIF_DF_CSC_LNH_MASK 0x1fff +#define ISIF_DF_CSC_SLV_MASK 0x1fff +#define ISIF_DF_CSC_LNV_MASK 0x1fff +#define ISIF_DF_NUMLINES 0x7fff +#define ISIF_DF_NUMPIX 0x1fff + +/* Offsets for LSC/DFC/Gain */ +#define ISIF_DATA_H_OFFSET_MASK 0x1fff +#define ISIF_DATA_V_OFFSET_MASK 0x1fff + +/* Linearization */ +#define ISIF_LIN_CORRSFT_MASK 7 +#define ISIF_LIN_CORRSFT_SHIFT 4 +#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10 +#define ISIF_LIN_SCALE_FACT_DECIMAL_MASK 0x3ff +#define ISIF_LIN_ENTRY_MASK 0x3ff + +/* masks and shifts*/ +#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0) +#define ISIF_SYNCEN_WEN_MASK (1 << 1) +#define ISIF_SYNCEN_WEN_SHIFT 1 + +#endif /* _DAVINCI_VPFE_DM365_ISIF_REGS_H */ From da43b6ccadcfe801e0316503334f8281b8679210 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:14:10 -0300 Subject: [PATCH 0296/4607] [media] davinci: vpfe: dm365: add IPIPE support for media controller driver Add the IPIPE subdevice to the DM365 vpfe driver. The IPIPE is the major sub IP in the DM365 capture VPFE hardware and implements black clamping, color space conversion, edge enhancements etc. the block is exposed as a subdevice and implements media controller based setup and a private IOCTL for fine grain control. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../staging/media/davinci_vpfe/dm365_ipipe.c | 1863 +++++++++++++++++ .../staging/media/davinci_vpfe/dm365_ipipe.h | 179 ++ 2 files changed, 2042 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe.c create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe.h diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c new file mode 100644 index 000000000000..92853539cc0a --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.c @@ -0,0 +1,1863 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + * + * + * IPIPE allows fine tuning of the input image using different + * tuning modules in IPIPE. Some examples :- Noise filter, Defect + * pixel correction etc. It essentially operate on Bayer Raw data + * or YUV raw data. To do image tuning, application call, + * + */ + +#include + +#include "dm365_ipipe.h" +#include "dm365_ipipe_hw.h" +#include "vpfe_mc_capture.h" + +#define MIN_OUT_WIDTH 32 +#define MIN_OUT_HEIGHT 32 + +/* ipipe input format's */ +static const unsigned int ipipe_input_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SGRBG12_1X12, + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, +}; + +/* ipipe output format's */ +static const unsigned int ipipe_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +}; + +static int ipipe_validate_lutdpc_params(struct vpfe_ipipe_lutdpc *lutdpc) +{ + int i; + + if (lutdpc->en > 1 || lutdpc->repl_white > 1 || + lutdpc->dpc_size > LUT_DPC_MAX_SIZE) + return -EINVAL; + + if (lutdpc->en && !lutdpc->table) + return -EINVAL; + + for (i = 0; i < lutdpc->dpc_size; i++) + if (lutdpc->table[i].horz_pos > LUT_DPC_H_POS_MASK || + lutdpc->table[i].vert_pos > LUT_DPC_V_POS_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc; + struct vpfe_ipipe_lutdpc *dpc_param; + struct device *dev; + + if (!param) { + memset((void *)lutdpc, 0, sizeof(struct vpfe_ipipe_lutdpc)); + goto success; + } + + dev = ipipe->subdev.v4l2_dev->dev; + dpc_param = (struct vpfe_ipipe_lutdpc *)param; + lutdpc->en = dpc_param->en; + lutdpc->repl_white = dpc_param->repl_white; + lutdpc->dpc_size = dpc_param->dpc_size; + memcpy(&lutdpc->table, &dpc_param->table, + (dpc_param->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry))); + if (ipipe_validate_lutdpc_params(lutdpc) < 0) + return -EINVAL; + +success: + ipipe_set_lutdpc_regs(ipipe->base_addr, ipipe->isp5_base_addr, lutdpc); + + return 0; +} + +static int ipipe_get_lutdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_lutdpc *lut_param = (struct vpfe_ipipe_lutdpc *)param; + struct vpfe_ipipe_lutdpc *lutdpc = &ipipe->config.lutdpc; + + lut_param->en = lutdpc->en; + lut_param->repl_white = lutdpc->repl_white; + lut_param->dpc_size = lutdpc->dpc_size; + memcpy(&lut_param->table, &lutdpc->table, + (lutdpc->dpc_size * sizeof(struct vpfe_ipipe_lutdpc_entry))); + + return 0; +} + +static int ipipe_set_input_config(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + + if (!param) + memset(config, 0, sizeof(struct vpfe_ipipe_input_config)); + else + memcpy(config, param, sizeof(struct vpfe_ipipe_input_config)); + return 0; +} + +static int ipipe_get_input_config(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + + if (!param) + return -EINVAL; + + memcpy(param, config, sizeof(struct vpfe_ipipe_input_config)); + + return 0; +} + +static int ipipe_validate_otfdpc_params(struct vpfe_ipipe_otfdpc *dpc_param) +{ + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0; + struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0; + + if (dpc_param->en > 1) + return -EINVAL; + + if (dpc_param->alg == VPFE_IPIPE_OTFDPC_2_0) { + dpc_2_0 = &dpc_param->alg_cfg.dpc_2_0; + if (dpc_2_0->det_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.b > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.b > OTFDPC_DPC2_THR_MASK) + return -EINVAL; + return 0; + } + + dpc_3_0 = &dpc_param->alg_cfg.dpc_3_0; + + if (dpc_3_0->act_adj_shf > OTF_DPC3_0_SHF_MASK || + dpc_3_0->det_thr > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->det_thr_min > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_thr_max > OTF_DPC3_0_DET_MASK || + dpc_3_0->corr_thr > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->corr_thr_min > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_thr_max > OTF_DPC3_0_CORR_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param; + struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc; + struct device *dev; + + if (!param) { + memset((void *)otfdpc, 0, sizeof(struct ipipe_otfdpc_2_0)); + goto success; + } + dev = ipipe->subdev.v4l2_dev->dev; + memcpy(otfdpc, dpc_param, sizeof(struct vpfe_ipipe_otfdpc)); + if (ipipe_validate_otfdpc_params(otfdpc) < 0) { + dev_err(dev, "Invalid otfdpc params\n"); + return -EINVAL; + } + +success: + ipipe_set_otfdpc_regs(ipipe->base_addr, otfdpc); + + return 0; +} + +static int ipipe_get_otfdpc_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_otfdpc *dpc_param = (struct vpfe_ipipe_otfdpc *)param; + struct vpfe_ipipe_otfdpc *otfdpc = &ipipe->config.otfdpc; + + memcpy(dpc_param, otfdpc, sizeof(struct vpfe_ipipe_otfdpc)); + return 0; +} + +static int ipipe_validate_nf_params(struct vpfe_ipipe_nf *nf_param) +{ + int i; + + if (nf_param->en > 1 || nf_param->shft_val > D2F_SHFT_VAL_MASK || + nf_param->spread_val > D2F_SPR_VAL_MASK || + nf_param->apply_lsc_gain > 1 || + nf_param->edge_det_min_thr > D2F_EDGE_DET_THR_MASK || + nf_param->edge_det_max_thr > D2F_EDGE_DET_THR_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_NF_THR_TABLE_SIZE; i++) + if (nf_param->thr[i] > D2F_THR_VAL_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_NF_STR_TABLE_SIZE; i++) + if (nf_param->str[i] > D2F_STR_VAL_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_nf_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param; + struct vpfe_ipipe_nf *nf = &ipipe->config.nf1; + struct device *dev; + + if (id == IPIPE_D2F_2ND) + nf = &ipipe->config.nf2; + + if (!nf_param) { + memset((void *)nf, 0, sizeof(struct vpfe_ipipe_nf)); + goto success; + } + + dev = ipipe->subdev.v4l2_dev->dev; + memcpy(nf, nf_param, sizeof(struct vpfe_ipipe_nf)); + if (ipipe_validate_nf_params(nf) < 0) { + dev_err(dev, "Invalid nf params\n"); + return -EINVAL; + } + +success: + ipipe_set_d2f_regs(ipipe->base_addr, id, nf); + + return 0; +} + +static int ipipe_set_nf1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_nf_params(ipipe, IPIPE_D2F_1ST, param); +} + +static int ipipe_set_nf2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_nf_params(ipipe, IPIPE_D2F_2ND, param); +} + +static int ipipe_get_nf_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_nf *nf_param = (struct vpfe_ipipe_nf *)param; + struct vpfe_ipipe_nf *nf = &ipipe->config.nf1; + + if (id == IPIPE_D2F_2ND) + nf = &ipipe->config.nf2; + + memcpy(nf_param, nf, sizeof(struct vpfe_ipipe_nf)); + + return 0; +} + +static int ipipe_get_nf1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_nf_params(ipipe, IPIPE_D2F_1ST, param); +} + +static int ipipe_get_nf2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_nf_params(ipipe, IPIPE_D2F_2ND, param); +} + +static int ipipe_validate_gic_params(struct vpfe_ipipe_gic *gic) +{ + if (gic->en > 1 || gic->gain > GIC_GAIN_MASK || + gic->thr > GIC_THR_MASK || gic->slope > GIC_SLOPE_MASK || + gic->apply_lsc_gain > 1 || + gic->nf2_thr_gain.integer > GIC_NFGAN_INT_MASK || + gic->nf2_thr_gain.decimal > GIC_NFGAN_DECI_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_gic_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_gic *gic = &ipipe->config.gic; + + if (!gic_param) { + memset((void *)gic, 0, sizeof(struct vpfe_ipipe_gic)); + goto success; + } + + memcpy(gic, gic_param, sizeof(struct vpfe_ipipe_gic)); + if (ipipe_validate_gic_params(gic) < 0) { + dev_err(dev, "Invalid gic params\n"); + return -EINVAL; + } + +success: + ipipe_set_gic_regs(ipipe->base_addr, gic); + + return 0; +} + +static int ipipe_get_gic_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gic *gic_param = (struct vpfe_ipipe_gic *)param; + struct vpfe_ipipe_gic *gic = &ipipe->config.gic; + + memcpy(gic_param, gic, sizeof(struct vpfe_ipipe_gic)); + + return 0; +} + +static int ipipe_validate_wb_params(struct vpfe_ipipe_wb *wbal) +{ + if (wbal->ofst_r > WB_OFFSET_MASK || + wbal->ofst_gr > WB_OFFSET_MASK || + wbal->ofst_gb > WB_OFFSET_MASK || + wbal->ofst_b > WB_OFFSET_MASK || + wbal->gain_r.integer > WB_GAIN_INT_MASK || + wbal->gain_r.decimal > WB_GAIN_DECI_MASK || + wbal->gain_gr.integer > WB_GAIN_INT_MASK || + wbal->gain_gr.decimal > WB_GAIN_DECI_MASK || + wbal->gain_gb.integer > WB_GAIN_INT_MASK || + wbal->gain_gb.decimal > WB_GAIN_DECI_MASK || + wbal->gain_b.integer > WB_GAIN_INT_MASK || + wbal->gain_b.decimal > WB_GAIN_DECI_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_wb_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param; + struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal; + + if (!wb_param) { + const struct vpfe_ipipe_wb wb_defaults = { + .gain_r = {2, 0x0}, + .gain_gr = {2, 0x0}, + .gain_gb = {2, 0x0}, + .gain_b = {2, 0x0} + }; + memcpy(wbal, &wb_defaults, sizeof(struct vpfe_ipipe_wb)); + goto success; + } + + memcpy(wbal, wb_param, sizeof(struct vpfe_ipipe_wb)); + if (ipipe_validate_wb_params(wbal) < 0) + return -EINVAL; + +success: + ipipe_set_wb_regs(ipipe->base_addr, wbal); + + return 0; +} + +static int ipipe_get_wb_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_wb *wb_param = (struct vpfe_ipipe_wb *)param; + struct vpfe_ipipe_wb *wbal = &ipipe->config.wbal; + + memcpy(wb_param, wbal, sizeof(struct vpfe_ipipe_wb)); + return 0; +} + +static int ipipe_validate_cfa_params(struct vpfe_ipipe_cfa *cfa) +{ + if (cfa->hpf_thr_2dir > CFA_HPF_THR_2DIR_MASK || + cfa->hpf_slp_2dir > CFA_HPF_SLOPE_2DIR_MASK || + cfa->hp_mix_thr_2dir > CFA_HPF_MIX_THR_2DIR_MASK || + cfa->hp_mix_slope_2dir > CFA_HPF_MIX_SLP_2DIR_MASK || + cfa->dir_thr_2dir > CFA_DIR_THR_2DIR_MASK || + cfa->dir_slope_2dir > CFA_DIR_SLP_2DIR_MASK || + cfa->nd_wt_2dir > CFA_ND_WT_2DIR_MASK || + cfa->hue_fract_daa > CFA_DAA_HUE_FRA_MASK || + cfa->edge_thr_daa > CFA_DAA_EDG_THR_MASK || + cfa->thr_min_daa > CFA_DAA_THR_MIN_MASK || + cfa->thr_slope_daa > CFA_DAA_THR_SLP_MASK || + cfa->slope_min_daa > CFA_DAA_SLP_MIN_MASK || + cfa->slope_slope_daa > CFA_DAA_SLP_SLP_MASK || + cfa->lp_wt_daa > CFA_DAA_LP_WT_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_cfa_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param; + struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa; + + if (!cfa_param) { + memset(cfa, 0, sizeof(struct vpfe_ipipe_cfa)); + cfa->alg = VPFE_IPIPE_CFA_ALG_2DIRAC; + goto success; + } + + memcpy(cfa, cfa_param, sizeof(struct vpfe_ipipe_cfa)); + if (ipipe_validate_cfa_params(cfa) < 0) + return -EINVAL; + +success: + ipipe_set_cfa_regs(ipipe->base_addr, cfa); + + return 0; +} + +static int ipipe_get_cfa_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cfa *cfa_param = (struct vpfe_ipipe_cfa *)param; + struct vpfe_ipipe_cfa *cfa = &ipipe->config.cfa; + + memcpy(cfa_param, cfa, sizeof(struct vpfe_ipipe_cfa)); + return 0; +} + +static int +ipipe_validate_rgb2rgb_params(struct vpfe_ipipe_rgb2rgb *rgb2rgb, + unsigned int id) +{ + u32 gain_int_upper = RGB2RGB_1_GAIN_INT_MASK; + u32 offset_upper = RGB2RGB_1_OFST_MASK; + + if (id == IPIPE_RGB2RGB_2) { + offset_upper = RGB2RGB_2_OFST_MASK; + gain_int_upper = RGB2RGB_2_GAIN_INT_MASK; + } + + if (rgb2rgb->coef_rr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_br.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_br.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->out_ofst_r > offset_upper || + rgb2rgb->out_ofst_g > offset_upper || + rgb2rgb->out_ofst_b > offset_upper) + return -EINVAL; + + return 0; +} + +static int ipipe_set_rgb2rgb_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_rgb2rgb *rgb2rgb_param; + + rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param; + + if (id == IPIPE_RGB2RGB_2) + rgb2rgb = &ipipe->config.rgb2rgb2; + + if (!rgb2rgb_param) { + const struct vpfe_ipipe_rgb2rgb rgb2rgb_defaults = { + .coef_rr = {1, 0}, /* 256 */ + .coef_gr = {0, 0}, + .coef_br = {0, 0}, + .coef_rg = {0, 0}, + .coef_gg = {1, 0}, /* 256 */ + .coef_bg = {0, 0}, + .coef_rb = {0, 0}, + .coef_gb = {0, 0}, + .coef_bb = {1, 0}, /* 256 */ + }; + /* Copy defaults for rgb2rgb conversion */ + memcpy(rgb2rgb, &rgb2rgb_defaults, + sizeof(struct vpfe_ipipe_rgb2rgb)); + goto success; + } + + memcpy(rgb2rgb, rgb2rgb_param, sizeof(struct vpfe_ipipe_rgb2rgb)); + if (ipipe_validate_rgb2rgb_params(rgb2rgb, id) < 0) { + dev_err(dev, "Invalid rgb2rgb params\n"); + return -EINVAL; + } + +success: + ipipe_set_rgb2rgb_regs(ipipe->base_addr, id, rgb2rgb); + + return 0; +} + +static int +ipipe_set_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param); +} + +static int +ipipe_set_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_set_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param); +} + +static int ipipe_get_rgb2rgb_params(struct vpfe_ipipe_device *ipipe, + unsigned int id, void *param) +{ + struct vpfe_ipipe_rgb2rgb *rgb2rgb = &ipipe->config.rgb2rgb1; + struct vpfe_ipipe_rgb2rgb *rgb2rgb_param; + + rgb2rgb_param = (struct vpfe_ipipe_rgb2rgb *)param; + + if (id == IPIPE_RGB2RGB_2) + rgb2rgb = &ipipe->config.rgb2rgb2; + + memcpy(rgb2rgb_param, rgb2rgb, sizeof(struct vpfe_ipipe_rgb2rgb)); + + return 0; +} + +static int +ipipe_get_rgb2rgb_1_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_1, param); +} + +static int +ipipe_get_rgb2rgb_2_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + return ipipe_get_rgb2rgb_params(ipipe, IPIPE_RGB2RGB_2, param); +} + +static int +ipipe_validate_gamma_entry(struct vpfe_ipipe_gamma_entry *table, int size) +{ + int i; + + if (!table) + return -EINVAL; + + for (i = 0; i < size; i++) + if (table[i].slope > GAMMA_MASK || + table[i].offset > GAMMA_MASK) + return -EINVAL; + + return 0; +} + +static int +ipipe_validate_gamma_params(struct vpfe_ipipe_gamma *gamma, struct device *dev) +{ + int table_size; + int err; + + if (gamma->bypass_r > 1 || + gamma->bypass_b > 1 || + gamma->bypass_g > 1) + return -EINVAL; + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return 0; + + table_size = gamma->tbl_size; + if (!gamma->bypass_r) { + err = ipipe_validate_gamma_entry(gamma->table_r, table_size); + if (err) { + dev_err(dev, "GAMMA R - table entry invalid\n"); + return err; + } + } + + if (!gamma->bypass_b) { + err = ipipe_validate_gamma_entry(gamma->table_b, table_size); + if (err) { + dev_err(dev, "GAMMA B - table entry invalid\n"); + return err; + } + } + + if (!gamma->bypass_g) { + err = ipipe_validate_gamma_entry(gamma->table_g, table_size); + if (err) { + dev_err(dev, "GAMMA G - table entry invalid\n"); + return err; + } + } + + return 0; +} + +static int +ipipe_set_gamma_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param; + struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + int table_size; + + if (!gamma_param) { + memset(gamma, 0, sizeof(struct vpfe_ipipe_gamma)); + gamma->tbl_sel = VPFE_IPIPE_GAMMA_TBL_ROM; + goto success; + } + + gamma->bypass_r = gamma_param->bypass_r; + gamma->bypass_b = gamma_param->bypass_b; + gamma->bypass_g = gamma_param->bypass_g; + gamma->tbl_sel = gamma_param->tbl_sel; + gamma->tbl_size = gamma_param->tbl_size; + + if (ipipe_validate_gamma_params(gamma, dev) < 0) + return -EINVAL; + + if (gamma_param->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + goto success; + + table_size = gamma->tbl_size; + if (!gamma_param->bypass_r) + memcpy(&gamma->table_r, &gamma_param->table_r, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma_param->bypass_b) + memcpy(&gamma->table_b, &gamma_param->table_b, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma_param->bypass_g) + memcpy(&gamma->table_g, &gamma_param->table_g, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + +success: + ipipe_set_gamma_regs(ipipe->base_addr, ipipe->isp5_base_addr, gamma); + + return 0; +} + +static int ipipe_get_gamma_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gamma *gamma_param = (struct vpfe_ipipe_gamma *)param; + struct vpfe_ipipe_gamma *gamma = &ipipe->config.gamma; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + int table_size; + + gamma_param->bypass_r = gamma->bypass_r; + gamma_param->bypass_g = gamma->bypass_g; + gamma_param->bypass_b = gamma->bypass_b; + gamma_param->tbl_sel = gamma->tbl_sel; + gamma_param->tbl_size = gamma->tbl_size; + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return 0; + + table_size = gamma->tbl_size; + + if (!gamma->bypass_r && !gamma_param->table_r) { + dev_err(dev, + "ipipe_get_gamma_params: table ptr empty for R\n"); + return -EINVAL; + } + memcpy(gamma_param->table_r, gamma->table_r, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma->bypass_g && !gamma_param->table_g) { + dev_err(dev, "ipipe_get_gamma_params: table ptr empty for G\n"); + return -EINVAL; + } + memcpy(gamma_param->table_g, gamma->table_g, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + if (!gamma->bypass_b && !gamma_param->table_b) { + dev_err(dev, "ipipe_get_gamma_params: table ptr empty for B\n"); + return -EINVAL; + } + memcpy(gamma_param->table_b, gamma->table_b, + (table_size * sizeof(struct vpfe_ipipe_gamma_entry))); + + return 0; +} + +static int ipipe_validate_3d_lut_params(struct vpfe_ipipe_3d_lut *lut) +{ + int i; + + if (!lut->en) + return 0; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) + if (lut->table[i].r > D3_LUT_ENTRY_MASK || + lut->table[i].g > D3_LUT_ENTRY_MASK || + lut->table[i].b > D3_LUT_ENTRY_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_get_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param; + struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + lut_param->en = lut->en; + if (!lut_param->table) { + dev_err(dev, "ipipe_get_3d_lut_params: Invalid table ptr\n"); + return -EINVAL; + } + + memcpy(lut_param->table, &lut->table, + (VPFE_IPIPE_MAX_SIZE_3D_LUT * + sizeof(struct vpfe_ipipe_3d_lut_entry))); + + return 0; +} + +static int +ipipe_set_3d_lut_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_3d_lut *lut_param = (struct vpfe_ipipe_3d_lut *)param; + struct vpfe_ipipe_3d_lut *lut = &ipipe->config.lut; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + if (!lut_param) { + memset(lut, 0, sizeof(struct vpfe_ipipe_3d_lut)); + goto success; + } + + memcpy(lut, lut_param, sizeof(struct vpfe_ipipe_3d_lut)); + if (ipipe_validate_3d_lut_params(lut) < 0) { + dev_err(dev, "Invalid 3D-LUT Params\n"); + return -EINVAL; + } + +success: + ipipe_set_3d_lut_regs(ipipe->base_addr, ipipe->isp5_base_addr, lut); + + return 0; +} + +static int ipipe_validate_rgb2yuv_params(struct vpfe_ipipe_rgb2yuv *rgb2yuv) +{ + if (rgb2yuv->coef_ry.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_ry.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gy.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gy.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_by.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_by.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_rcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_rcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_bcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_bcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_rcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_rcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_gcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_gcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->coef_bcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv->coef_bcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (rgb2yuv->out_ofst_y > RGB2YCBCR_OFST_MASK || + rgb2yuv->out_ofst_cb > RGB2YCBCR_OFST_MASK || + rgb2yuv->out_ofst_cr > RGB2YCBCR_OFST_MASK) + return -EINVAL; + + return 0; +} + +static int +ipipe_set_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_rgb2yuv *rgb2yuv_param; + + rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param; + if (!rgb2yuv_param) { + /* Defaults for rgb2yuv conversion */ + const struct vpfe_ipipe_rgb2yuv rgb2yuv_defaults = { + .coef_ry = {0, 0x4d}, + .coef_gy = {0, 0x96}, + .coef_by = {0, 0x1d}, + .coef_rcb = {0xf, 0xd5}, + .coef_gcb = {0xf, 0xab}, + .coef_bcb = {0, 0x80}, + .coef_rcr = {0, 0x80}, + .coef_gcr = {0xf, 0x95}, + .coef_bcr = {0xf, 0xeb}, + .out_ofst_cb = 0x80, + .out_ofst_cr = 0x80, + }; + /* Copy defaults for rgb2yuv conversion */ + memcpy(rgb2yuv, &rgb2yuv_defaults, + sizeof(struct vpfe_ipipe_rgb2yuv)); + goto success; + } + + memcpy(rgb2yuv, rgb2yuv_param, sizeof(struct vpfe_ipipe_rgb2yuv)); + if (ipipe_validate_rgb2yuv_params(rgb2yuv) < 0) { + dev_err(dev, "Invalid rgb2yuv params\n"); + return -EINVAL; + } + +success: + ipipe_set_rgb2ycbcr_regs(ipipe->base_addr, rgb2yuv); + + return 0; +} + +static int +ipipe_get_rgb2yuv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_rgb2yuv *rgb2yuv = &ipipe->config.rgb2yuv; + struct vpfe_ipipe_rgb2yuv *rgb2yuv_param; + + rgb2yuv_param = (struct vpfe_ipipe_rgb2yuv *)param; + memcpy(rgb2yuv_param, rgb2yuv, sizeof(struct vpfe_ipipe_rgb2yuv)); + return 0; +} + +static int ipipe_validate_gbce_params(struct vpfe_ipipe_gbce *gbce) +{ + u32 max = GBCE_Y_VAL_MASK; + int i; + + if (!gbce->en) + return 0; + + if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL) + max = GBCE_GAIN_VAL_MASK; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_GBCE_LUT; i++) + if (gbce->table[i] > max) + return -EINVAL; + + return 0; +} + +static int ipipe_set_gbce_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param; + struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + if (!gbce_param) { + memset(gbce, 0 , sizeof(struct vpfe_ipipe_gbce)); + } else { + memcpy(gbce, gbce_param, sizeof(struct vpfe_ipipe_gbce)); + if (ipipe_validate_gbce_params(gbce) < 0) { + dev_err(dev, "Invalid gbce params\n"); + return -EINVAL; + } + } + + ipipe_set_gbce_regs(ipipe->base_addr, ipipe->isp5_base_addr, gbce); + + return 0; +} + +static int ipipe_get_gbce_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_gbce *gbce_param = (struct vpfe_ipipe_gbce *)param; + struct vpfe_ipipe_gbce *gbce = &ipipe->config.gbce; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + gbce_param->en = gbce->en; + gbce_param->type = gbce->type; + if (!gbce_param->table) { + dev_err(dev, "ipipe_get_gbce_params: Invalid table ptr\n"); + return -EINVAL; + } + + memcpy(gbce_param->table, gbce->table, + (VPFE_IPIPE_MAX_SIZE_GBCE_LUT * sizeof(unsigned short))); + + return 0; +} + +static int +ipipe_validate_yuv422_conv_params(struct vpfe_ipipe_yuv422_conv *yuv422_conv) +{ + if (yuv422_conv->en_chrom_lpf > 1) + return -EINVAL; + + return 0; +} + +static int +ipipe_set_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv; + struct vpfe_ipipe_yuv422_conv *yuv422_conv_param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + + yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param; + if (!yuv422_conv_param) { + memset(yuv422_conv, 0, sizeof(struct vpfe_ipipe_yuv422_conv)); + yuv422_conv->chrom_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE; + } else { + memcpy(yuv422_conv, yuv422_conv_param, + sizeof(struct vpfe_ipipe_yuv422_conv)); + if (ipipe_validate_yuv422_conv_params(yuv422_conv) < 0) { + dev_err(dev, "Invalid yuv422 params\n"); + return -EINVAL; + } + } + + ipipe_set_yuv422_conv_regs(ipipe->base_addr, yuv422_conv); + + return 0; +} + +static int +ipipe_get_yuv422_conv_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yuv422_conv *yuv422_conv = &ipipe->config.yuv422_conv; + struct vpfe_ipipe_yuv422_conv *yuv422_conv_param; + + yuv422_conv_param = (struct vpfe_ipipe_yuv422_conv *)param; + memcpy(yuv422_conv_param, yuv422_conv, + sizeof(struct vpfe_ipipe_yuv422_conv)); + + return 0; +} + +static int ipipe_validate_yee_params(struct vpfe_ipipe_yee *yee) +{ + int i; + + if (yee->en > 1 || + yee->en_halo_red > 1 || + yee->hpf_shft > YEE_HPF_SHIFT_MASK) + return -EINVAL; + + if (yee->hpf_coef_00 > YEE_COEF_MASK || + yee->hpf_coef_01 > YEE_COEF_MASK || + yee->hpf_coef_02 > YEE_COEF_MASK || + yee->hpf_coef_10 > YEE_COEF_MASK || + yee->hpf_coef_11 > YEE_COEF_MASK || + yee->hpf_coef_12 > YEE_COEF_MASK || + yee->hpf_coef_20 > YEE_COEF_MASK || + yee->hpf_coef_21 > YEE_COEF_MASK || + yee->hpf_coef_22 > YEE_COEF_MASK) + return -EINVAL; + + if (yee->yee_thr > YEE_THR_MASK || + yee->es_gain > YEE_ES_GAIN_MASK || + yee->es_thr1 > YEE_ES_THR1_MASK || + yee->es_thr2 > YEE_THR_MASK || + yee->es_gain_grad > YEE_THR_MASK || + yee->es_ofst_grad > YEE_THR_MASK) + return -EINVAL; + + for (i = 0; i < VPFE_IPIPE_MAX_SIZE_YEE_LUT ; i++) + if (yee->table[i] > YEE_ENTRY_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_yee_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_yee *yee = &ipipe->config.yee; + + if (!yee_param) { + memset(yee, 0, sizeof(struct vpfe_ipipe_yee)); + } else { + memcpy(yee, yee_param, sizeof(struct vpfe_ipipe_yee)); + if (ipipe_validate_yee_params(yee) < 0) { + dev_err(dev, "Invalid yee params\n"); + return -EINVAL; + } + } + + ipipe_set_ee_regs(ipipe->base_addr, ipipe->isp5_base_addr, yee); + + return 0; +} + +static int ipipe_get_yee_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_yee *yee_param = (struct vpfe_ipipe_yee *)param; + struct vpfe_ipipe_yee *yee = &ipipe->config.yee; + + yee_param->en = yee->en; + yee_param->en_halo_red = yee->en_halo_red; + yee_param->merge_meth = yee->merge_meth; + yee_param->hpf_shft = yee->hpf_shft; + yee_param->hpf_coef_00 = yee->hpf_coef_00; + yee_param->hpf_coef_01 = yee->hpf_coef_01; + yee_param->hpf_coef_02 = yee->hpf_coef_02; + yee_param->hpf_coef_10 = yee->hpf_coef_10; + yee_param->hpf_coef_11 = yee->hpf_coef_11; + yee_param->hpf_coef_12 = yee->hpf_coef_12; + yee_param->hpf_coef_20 = yee->hpf_coef_20; + yee_param->hpf_coef_21 = yee->hpf_coef_21; + yee_param->hpf_coef_22 = yee->hpf_coef_22; + yee_param->yee_thr = yee->yee_thr; + yee_param->es_gain = yee->es_gain; + yee_param->es_thr1 = yee->es_thr1; + yee_param->es_thr2 = yee->es_thr2; + yee_param->es_gain_grad = yee->es_gain_grad; + yee_param->es_ofst_grad = yee->es_ofst_grad; + memcpy(yee_param->table, &yee->table, + (VPFE_IPIPE_MAX_SIZE_YEE_LUT * sizeof(short))); + + return 0; +} + +static int ipipe_validate_car_params(struct vpfe_ipipe_car *car) +{ + if (car->en > 1 || car->hpf_shft > CAR_HPF_SHIFT_MASK || + car->gain1.shft > CAR_GAIN1_SHFT_MASK || + car->gain1.gain_min > CAR_GAIN_MIN_MASK || + car->gain2.shft > CAR_GAIN2_SHFT_MASK || + car->gain2.gain_min > CAR_GAIN_MIN_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_car_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_car *car = &ipipe->config.car; + + if (!car_param) { + memset(car , 0, sizeof(struct vpfe_ipipe_car)); + } else { + memcpy(car, car_param, sizeof(struct vpfe_ipipe_car)); + if (ipipe_validate_car_params(car) < 0) { + dev_err(dev, "Invalid car params\n"); + return -EINVAL; + } + } + + ipipe_set_car_regs(ipipe->base_addr, car); + + return 0; +} + +static int ipipe_get_car_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_car *car_param = (struct vpfe_ipipe_car *)param; + struct vpfe_ipipe_car *car = &ipipe->config.car; + + memcpy(car_param, car, sizeof(struct vpfe_ipipe_car)); + return 0; +} + +static int ipipe_validate_cgs_params(struct vpfe_ipipe_cgs *cgs) +{ + if (cgs->en > 1 || cgs->h_shft > CAR_SHIFT_MASK) + return -EINVAL; + + return 0; +} + +static int ipipe_set_cgs_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param; + struct device *dev = ipipe->subdev.v4l2_dev->dev; + struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs; + + if (!cgs_param) { + memset(cgs, 0, sizeof(struct vpfe_ipipe_cgs)); + } else { + memcpy(cgs, cgs_param, sizeof(struct vpfe_ipipe_cgs)); + if (ipipe_validate_cgs_params(cgs) < 0) { + dev_err(dev, "Invalid cgs params\n"); + return -EINVAL; + } + } + + ipipe_set_cgs_regs(ipipe->base_addr, cgs); + + return 0; +} + +static int ipipe_get_cgs_params(struct vpfe_ipipe_device *ipipe, void *param) +{ + struct vpfe_ipipe_cgs *cgs_param = (struct vpfe_ipipe_cgs *)param; + struct vpfe_ipipe_cgs *cgs = &ipipe->config.cgs; + + memcpy(cgs_param, cgs, sizeof(struct vpfe_ipipe_cgs)); + + return 0; +} + +static const struct ipipe_module_if ipipe_modules[VPFE_IPIPE_MAX_MODULES] = { + /* VPFE_IPIPE_INPUT_CONFIG */ { + offsetof(struct ipipe_module_params, input_config), + FIELD_SIZEOF(struct ipipe_module_params, input_config), + offsetof(struct vpfe_ipipe_config, input_config), + ipipe_set_input_config, + ipipe_get_input_config, + }, /* VPFE_IPIPE_LUTDPC */ { + offsetof(struct ipipe_module_params, lutdpc), + FIELD_SIZEOF(struct ipipe_module_params, lutdpc), + offsetof(struct vpfe_ipipe_config, lutdpc), + ipipe_set_lutdpc_params, + ipipe_get_lutdpc_params, + }, /* VPFE_IPIPE_OTFDPC */ { + offsetof(struct ipipe_module_params, otfdpc), + FIELD_SIZEOF(struct ipipe_module_params, otfdpc), + offsetof(struct vpfe_ipipe_config, otfdpc), + ipipe_set_otfdpc_params, + ipipe_get_otfdpc_params, + }, /* VPFE_IPIPE_NF1 */ { + offsetof(struct ipipe_module_params, nf1), + FIELD_SIZEOF(struct ipipe_module_params, nf1), + offsetof(struct vpfe_ipipe_config, nf1), + ipipe_set_nf1_params, + ipipe_get_nf1_params, + }, /* VPFE_IPIPE_NF2 */ { + offsetof(struct ipipe_module_params, nf2), + FIELD_SIZEOF(struct ipipe_module_params, nf2), + offsetof(struct vpfe_ipipe_config, nf2), + ipipe_set_nf2_params, + ipipe_get_nf2_params, + }, /* VPFE_IPIPE_WB */ { + offsetof(struct ipipe_module_params, wbal), + FIELD_SIZEOF(struct ipipe_module_params, wbal), + offsetof(struct vpfe_ipipe_config, wbal), + ipipe_set_wb_params, + ipipe_get_wb_params, + }, /* VPFE_IPIPE_RGB2RGB_1 */ { + offsetof(struct ipipe_module_params, rgb2rgb1), + FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb1), + offsetof(struct vpfe_ipipe_config, rgb2rgb1), + ipipe_set_rgb2rgb_1_params, + ipipe_get_rgb2rgb_1_params, + }, /* VPFE_IPIPE_RGB2RGB_2 */ { + offsetof(struct ipipe_module_params, rgb2rgb2), + FIELD_SIZEOF(struct ipipe_module_params, rgb2rgb2), + offsetof(struct vpfe_ipipe_config, rgb2rgb2), + ipipe_set_rgb2rgb_2_params, + ipipe_get_rgb2rgb_2_params, + }, /* VPFE_IPIPE_GAMMA */ { + offsetof(struct ipipe_module_params, gamma), + FIELD_SIZEOF(struct ipipe_module_params, gamma), + offsetof(struct vpfe_ipipe_config, gamma), + ipipe_set_gamma_params, + ipipe_get_gamma_params, + }, /* VPFE_IPIPE_3D_LUT */ { + offsetof(struct ipipe_module_params, lut), + FIELD_SIZEOF(struct ipipe_module_params, lut), + offsetof(struct vpfe_ipipe_config, lut), + ipipe_set_3d_lut_params, + ipipe_get_3d_lut_params, + }, /* VPFE_IPIPE_RGB2YUV */ { + offsetof(struct ipipe_module_params, rgb2yuv), + FIELD_SIZEOF(struct ipipe_module_params, rgb2yuv), + offsetof(struct vpfe_ipipe_config, rgb2yuv), + ipipe_set_rgb2yuv_params, + ipipe_get_rgb2yuv_params, + }, /* VPFE_IPIPE_YUV422_CONV */ { + offsetof(struct ipipe_module_params, yuv422_conv), + FIELD_SIZEOF(struct ipipe_module_params, yuv422_conv), + offsetof(struct vpfe_ipipe_config, yuv422_conv), + ipipe_set_yuv422_conv_params, + ipipe_get_yuv422_conv_params, + }, /* VPFE_IPIPE_YEE */ { + offsetof(struct ipipe_module_params, yee), + FIELD_SIZEOF(struct ipipe_module_params, yee), + offsetof(struct vpfe_ipipe_config, yee), + ipipe_set_yee_params, + ipipe_get_yee_params, + }, /* VPFE_IPIPE_GIC */ { + offsetof(struct ipipe_module_params, gic), + FIELD_SIZEOF(struct ipipe_module_params, gic), + offsetof(struct vpfe_ipipe_config, gic), + ipipe_set_gic_params, + ipipe_get_gic_params, + }, /* VPFE_IPIPE_CFA */ { + offsetof(struct ipipe_module_params, cfa), + FIELD_SIZEOF(struct ipipe_module_params, cfa), + offsetof(struct vpfe_ipipe_config, cfa), + ipipe_set_cfa_params, + ipipe_get_cfa_params, + }, /* VPFE_IPIPE_CAR */ { + offsetof(struct ipipe_module_params, car), + FIELD_SIZEOF(struct ipipe_module_params, car), + offsetof(struct vpfe_ipipe_config, car), + ipipe_set_car_params, + ipipe_get_car_params, + }, /* VPFE_IPIPE_CGS */ { + offsetof(struct ipipe_module_params, cgs), + FIELD_SIZEOF(struct ipipe_module_params, cgs), + offsetof(struct vpfe_ipipe_config, cgs), + ipipe_set_cgs_params, + ipipe_get_cgs_params, + }, /* VPFE_IPIPE_GBCE */ { + offsetof(struct ipipe_module_params, gbce), + FIELD_SIZEOF(struct ipipe_module_params, gbce), + offsetof(struct vpfe_ipipe_config, gbce), + ipipe_set_gbce_params, + ipipe_get_gbce_params, + }, +}; + +static int ipipe_s_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + unsigned int i; + int rval = 0; + + for (i = 0; i < ARRAY_SIZE(ipipe_modules); i++) { + unsigned int bit = 1 << i; + if (cfg->flag & bit) { + const struct ipipe_module_if *module_if = + &ipipe_modules[i]; + struct ipipe_module_params *params; + void __user *from = *(void * __user *) + ((void *)cfg + module_if->config_offset); + size_t size; + void *to; + + params = kmalloc(sizeof(struct ipipe_module_params), + GFP_KERNEL); + to = (void *)params + module_if->param_offset; + size = module_if->param_size; + + if (to && from && size) { + if (copy_from_user(to, from, size)) { + rval = -EFAULT; + break; + } + rval = module_if->set(ipipe, to); + if (rval) + goto error; + } else if (to && !from && size) { + rval = module_if->set(ipipe, NULL); + if (rval) + goto error; + } + kfree(params); + } + } +error: + return rval; +} + +static int ipipe_g_config(struct v4l2_subdev *sd, struct vpfe_ipipe_config *cfg) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + unsigned int i; + int rval = 0; + + for (i = 1; i < ARRAY_SIZE(ipipe_modules); i++) { + unsigned int bit = 1 << i; + if (cfg->flag & bit) { + const struct ipipe_module_if *module_if = + &ipipe_modules[i]; + struct ipipe_module_params *params; + void __user *to = *(void * __user *) + ((void *)cfg + module_if->config_offset); + size_t size; + void *from; + + params = kmalloc(sizeof(struct ipipe_module_params), + GFP_KERNEL); + from = (void *)params + module_if->param_offset; + size = module_if->param_size; + + if (to && from && size) { + rval = module_if->get(ipipe, from); + if (rval) + goto error; + if (copy_to_user(to, from, size)) { + rval = -EFAULT; + break; + } + } + kfree(params); + } + } +error: + return rval; +} + +/* + * ipipe_ioctl() - Handle ipipe module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long ipipe_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + int ret = 0; + + switch (cmd) { + case VIDIOC_VPFE_IPIPE_S_CONFIG: + ret = ipipe_s_config(sd, arg); + break; + + case VIDIOC_VPFE_IPIPE_G_CONFIG: + ret = ipipe_g_config(sd, arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en) +{ + struct vpfe_ipipeif_device *ipipeif = &vpfe_dev->vpfe_ipipeif; + struct vpfe_ipipe_device *ipipe = &vpfe_dev->vpfe_ipipe; + unsigned char val; + + if (ipipe->input == IPIPE_INPUT_NONE) + return; + + /* ipipe is set to single shot */ + if (ipipeif->input == IPIPEIF_INPUT_MEMORY && en) { + /* for single-shot mode, need to wait for h/w to + * reset many register bits + */ + do { + val = regr_ip(vpfe_dev->vpfe_ipipe.base_addr, + IPIPE_SRC_EN); + } while (val); + } + regw_ip(vpfe_dev->vpfe_ipipe.base_addr, en, IPIPE_SRC_EN); +} + +/* + * ipipe_set_stream() - Enable/Disable streaming on the ipipe subdevice + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int ipipe_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + + if (enable && ipipe->input != IPIPE_INPUT_NONE && + ipipe->output != IPIPE_OUTPUT_NONE) { + if (config_ipipe_hw(ipipe) < 0) + return -EINVAL; + } + + vpfe_ipipe_enable(vpfe_dev, enable); + + return 0; +} + +/* + * __ipipe_get_format() - helper function for getting ipipe format + * @ipipe: pointer to ipipe private structure. + * @pad: pad number. + * @fh: V4L2 subdev file handle. + * @which: wanted subdev format. + * + */ +static struct v4l2_mbus_framefmt * +__ipipe_get_format(struct vpfe_ipipe_device *ipipe, + struct v4l2_subdev_fh *fh, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + + return &ipipe->formats[pad]; +} + +/* + * ipipe_try_format() - Handle try format by pad subdev method + * @ipipe: VPFE ipipe device. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which : wanted subdev format + */ +static void +ipipe_try_format(struct vpfe_ipipe_device *ipipe, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + if (pad == IPIPE_PAD_SINK) { + for (i = 0; i < ARRAY_SIZE(ipipe_input_fmts); i++) + if (fmt->code == ipipe_input_fmts[i]) + break; + + /* If not found, use SBGGR10 as default */ + if (i >= ARRAY_SIZE(ipipe_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SGRBG12_1X12; + } else if (pad == IPIPE_PAD_SOURCE) { + for (i = 0; i < ARRAY_SIZE(ipipe_output_fmts); i++) + if (fmt->code == ipipe_output_fmts[i]) + break; + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(ipipe_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + } + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width); + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height); +} + +/* + * ipipe_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int +ipipe_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ipipe_get_format(ipipe, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ipipe_try_format(ipipe, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == IPIPE_PAD_SINK && + (ipipe->input == IPIPE_INPUT_CCDC || + ipipe->input == IPIPE_INPUT_MEMORY)) + ipipe->formats[fmt->pad] = fmt->format; + else if (fmt->pad == IPIPE_PAD_SOURCE && + ipipe->output == IPIPE_OUTPUT_RESIZER) + ipipe->formats[fmt->pad] = fmt->format; + else + return -EINVAL; + + return 0; +} + +/* + * ipipe_get_format() - Handle get format by pads subdev method. + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fmt: pointer to v4l2 subdev format structure. + */ +static int +ipipe_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + fmt->format = ipipe->formats[fmt->pad]; + else + fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad)); + + return 0; +} + +/* + * ipipe_enum_frame_size() - enum frame sizes on pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fse: pointer to v4l2_subdev_frame_size_enum structure. + */ +static int +ipipe_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + ipipe_try_format(ipipe, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + ipipe_try_format(ipipe, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * ipipe_enum_mbus_code() - enum mbus codes for pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int +ipipe_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case IPIPE_PAD_SINK: + if (code->index >= ARRAY_SIZE(ipipe_input_fmts)) + return -EINVAL; + code->code = ipipe_input_fmts[code->index]; + break; + + case IPIPE_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(ipipe_output_fmts)) + return -EINVAL; + code->code = ipipe_output_fmts[code->index]; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipe_s_ctrl() - Handle set control subdev method + * @ctrl: pointer to v4l2 control structure + */ +static int ipipe_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct vpfe_ipipe_device *ipipe = + container_of(ctrl->handler, struct vpfe_ipipe_device, ctrls); + struct ipipe_lum_adj *lum_adj = &ipipe->config.lum_adj; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + lum_adj->brightness = ctrl->val; + ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj); + break; + + case V4L2_CID_CONTRAST: + lum_adj->contrast = ctrl->val; + ipipe_set_lum_adj_regs(ipipe->base_addr, lum_adj); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ipipe_init_formats() - Initialize formats on all pads + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int +ipipe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = IPIPE_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SGRBG12_1X12; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipe_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = IPIPE_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + ipipe_set_format(sd, fh, &format); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops ipipe_v4l2_core_ops = { + .ioctl = ipipe_ioctl, +}; + +static const struct v4l2_ctrl_ops ipipe_ctrl_ops = { + .s_ctrl = ipipe_s_ctrl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_internal_ops ipipe_v4l2_internal_ops = { + .open = ipipe_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops ipipe_v4l2_video_ops = { + .s_stream = ipipe_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops ipipe_v4l2_pad_ops = { + .enum_mbus_code = ipipe_enum_mbus_code, + .enum_frame_size = ipipe_enum_frame_size, + .get_fmt = ipipe_get_format, + .set_fmt = ipipe_set_format, +}; + +/* v4l2 subdev operation */ +static const struct v4l2_subdev_ops ipipe_v4l2_ops = { + .core = &ipipe_v4l2_core_ops, + .video = &ipipe_v4l2_video_ops, + .pad = &ipipe_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * ipipe_link_setup() - Setup ipipe connections + * @entity: ipipe media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int +ipipe_link_setup(struct media_entity *entity, const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_ipipe_device *ipipe = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + + switch (local->index | media_entity_type(remote->entity)) { + case IPIPE_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ipipe->input = IPIPE_INPUT_NONE; + break; + } + if (ipipe->input != IPIPE_INPUT_NONE) + return -EBUSY; + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY) + ipipe->input = IPIPE_INPUT_MEMORY; + else + ipipe->input = IPIPE_INPUT_CCDC; + break; + + case IPIPE_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + /* out to RESIZER */ + if (flags & MEDIA_LNK_FL_ENABLED) + ipipe->output = IPIPE_OUTPUT_RESIZER; + else + ipipe->output = IPIPE_OUTPUT_NONE; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations ipipe_media_ops = { + .link_setup = ipipe_link_setup, +}; + +/* + * vpfe_ipipe_unregister_entities() - ipipe unregister entity + * @vpfe_ipipe: pointer to ipipe subdevice structure. + */ +void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *vpfe_ipipe) +{ + /* cleanup entity */ + media_entity_cleanup(&vpfe_ipipe->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&vpfe_ipipe->subdev); +} + +/* + * vpfe_ipipe_register_entities() - ipipe register entity + * @ipipe: pointer to ipipe subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int +vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &ipipe->subdev); + if (ret) { + pr_err("Failed to register ipipe as v4l2 subdevice\n"); + return ret; + } + + return ret; +} + +#define IPIPE_CONTRAST_HIGH 0xff +#define IPIPE_BRIGHT_HIGH 0xff + +/* + * vpfe_ipipe_init() - ipipe module initialization. + * @ipipe: pointer to ipipe subdevice structure. + * @pdev: platform device pointer. + */ +int +vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, struct platform_device *pdev) +{ + struct media_pad *pads = &ipipe->pads[0]; + struct v4l2_subdev *sd = &ipipe->subdev; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 4); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + ipipe->base_addr = ioremap_nocache(res->start, res_len); + if (!ipipe->base_addr) + return -EBUSY; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 6); + if (!res) + return -ENOENT; + ipipe->isp5_base_addr = ioremap_nocache(res->start, res_len); + if (!ipipe->isp5_base_addr) + return -EBUSY; + + v4l2_subdev_init(sd, &ipipe_v4l2_ops); + sd->internal_ops = &ipipe_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI IPIPE", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, ipipe); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[IPIPE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[IPIPE_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ipipe->input = IPIPE_INPUT_NONE; + ipipe->output = IPIPE_OUTPUT_NONE; + + me->ops = &ipipe_media_ops; + v4l2_ctrl_handler_init(&ipipe->ctrls, 2); + v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, + IPIPE_BRIGHT_HIGH, 1, 16); + v4l2_ctrl_new_std(&ipipe->ctrls, &ipipe_ctrl_ops, + V4L2_CID_CONTRAST, 0, + IPIPE_CONTRAST_HIGH, 1, 16); + + + v4l2_ctrl_handler_setup(&ipipe->ctrls); + sd->ctrl_handler = &ipipe->ctrls; + + return media_entity_init(me, IPIPE_PADS_NUM, pads, 0); +} + +/* + * vpfe_ipipe_cleanup() - ipipe subdevice cleanup. + * @ipipe: pointer to ipipe subdevice + * @dev: pointer to platform device + */ +void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev) +{ + struct resource *res; + + v4l2_ctrl_handler_free(&ipipe->ctrls); + + iounmap(ipipe->base_addr); + iounmap(ipipe->isp5_base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 4); + if (res) + release_mem_region(res->start, res->end - res->start + 1); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h new file mode 100644 index 000000000000..cf4204603eb8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPE_H +#define _DAVINCI_VPFE_DM365_IPIPE_H + +#include + +#include +#include + +#include "davinci_vpfe_user.h" +#include "vpfe_video.h" + +#define CEIL(a, b) (((a) + (b-1)) / (b)) + +enum ipipe_noise_filter { + IPIPE_D2F_1ST = 0, + IPIPE_D2F_2ND = 1, +}; + +/* Used for driver storage */ +struct ipipe_otfdpc_2_0 { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + struct vpfe_ipipe_otfdpc_2_0_cfg otfdpc_2_0; +}; + +struct ipipe_otfdpc_3_0 { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum vpfe_ipipe_otfdpc_det_meth det_method; + /* Algorithm used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum vpfe_ipipe_otfdpc_alg alg; + struct vpfe_ipipe_otfdpc_3_0_cfg otfdpc_3_0; +}; + +/* Structure for configuring Luminance Adjustment module */ +struct ipipe_lum_adj { + /* Brightness adjustments */ + unsigned char brightness; + /* contrast adjustments */ + unsigned char contrast; +}; + +enum ipipe_rgb2rgb { + IPIPE_RGB2RGB_1 = 0, + IPIPE_RGB2RGB_2 = 1, +}; + +struct ipipe_module_params { + __u32 flag; + struct vpfe_ipipe_input_config input_config; + struct vpfe_ipipe_lutdpc lutdpc; + struct vpfe_ipipe_otfdpc otfdpc; + struct vpfe_ipipe_nf nf1; + struct vpfe_ipipe_nf nf2; + struct vpfe_ipipe_gic gic; + struct vpfe_ipipe_wb wbal; + struct vpfe_ipipe_cfa cfa; + struct vpfe_ipipe_rgb2rgb rgb2rgb1; + struct vpfe_ipipe_rgb2rgb rgb2rgb2; + struct vpfe_ipipe_gamma gamma; + struct vpfe_ipipe_3d_lut lut; + struct vpfe_ipipe_rgb2yuv rgb2yuv; + struct vpfe_ipipe_gbce gbce; + struct vpfe_ipipe_yuv422_conv yuv422_conv; + struct vpfe_ipipe_yee yee; + struct vpfe_ipipe_car car; + struct vpfe_ipipe_cgs cgs; + struct ipipe_lum_adj lum_adj; +}; + +#define IPIPE_PAD_SINK 0 +#define IPIPE_PAD_SOURCE 1 + +#define IPIPE_PADS_NUM 2 + +#define IPIPE_OUTPUT_NONE 0 +#define IPIPE_OUTPUT_RESIZER (1 << 0) + +enum ipipe_input_entity { + IPIPE_INPUT_NONE = 0, + IPIPE_INPUT_MEMORY = 1, + IPIPE_INPUT_CCDC = 2, +}; + + +struct vpfe_ipipe_device { + struct v4l2_subdev subdev; + struct media_pad pads[IPIPE_PADS_NUM]; + struct v4l2_mbus_framefmt formats[IPIPE_PADS_NUM]; + enum ipipe_input_entity input; + unsigned int output; + struct v4l2_ctrl_handler ctrls; + void *__iomem base_addr; + void *__iomem isp5_base_addr; + struct ipipe_module_params config; +}; + +struct ipipe_module_if { + unsigned int param_offset; + unsigned int param_size; + unsigned int config_offset; + int (*set)(struct vpfe_ipipe_device *ipipe, void *param); + int (*get)(struct vpfe_ipipe_device *ipipe, void *param); +}; + +/* data paths */ +enum ipipe_data_paths { + IPIPE_RAW2YUV, + /* Bayer RAW input to YCbCr output */ + IPIPE_RAW2RAW, + /* Bayer Raw to Bayer output */ + IPIPE_RAW2BOX, + /* Bayer Raw to Boxcar output */ + IPIPE_YUV2YUV + /* YUV Raw to YUV Raw output */ +}; + +#define IPIPE_COLPTN_R_Ye 0x0 +#define IPIPE_COLPTN_Gr_Cy 0x1 +#define IPIPE_COLPTN_Gb_G 0x2 +#define IPIPE_COLPTN_B_Mg 0x3 + +#define COLPAT_EE_SHIFT 0 +#define COLPAT_EO_SHIFT 2 +#define COLPAT_OE_SHIFT 4 +#define COLPAT_OO_SHIFT 6 + +#define ipipe_sgrbg_pattern \ + (IPIPE_COLPTN_Gr_Cy << COLPAT_EE_SHIFT | \ + IPIPE_COLPTN_R_Ye << COLPAT_EO_SHIFT | \ + IPIPE_COLPTN_B_Mg << COLPAT_OE_SHIFT | \ + IPIPE_COLPTN_Gb_G << COLPAT_OO_SHIFT) + +#define ipipe_srggb_pattern \ + (IPIPE_COLPTN_R_Ye << COLPAT_EE_SHIFT | \ + IPIPE_COLPTN_Gr_Cy << COLPAT_EO_SHIFT | \ + IPIPE_COLPTN_Gb_G << COLPAT_OE_SHIFT | \ + IPIPE_COLPTN_B_Mg << COLPAT_OO_SHIFT) + +int vpfe_ipipe_register_entities(struct vpfe_ipipe_device *ipipe, + struct v4l2_device *v4l2_dev); +int vpfe_ipipe_init(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev); +void vpfe_ipipe_unregister_entities(struct vpfe_ipipe_device *ipipe); +void vpfe_ipipe_cleanup(struct vpfe_ipipe_device *ipipe, + struct platform_device *pdev); +void vpfe_ipipe_enable(struct vpfe_device *vpfe_dev, int en); + +#endif /* _DAVINCI_VPFE_DM365_IPIPE_H */ From 26c0e9992c266ad05278da72902a5f9724962c65 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:16:35 -0300 Subject: [PATCH 0297/4607] [media] davinci: vpfe: dm365: add IPIPE hardware layer support IPIPE is the hardware IP which implements the functionality required for resizer, ipipe(colorspace converter) and the associated hardware support. This patch implements hardware setup including coefficient programming for various hardware filters, gamma, cfa and clock enabling. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/davinci_vpfe/dm365_ipipe_hw.c | 1048 +++++++++++++++++ .../media/davinci_vpfe/dm365_ipipe_hw.h | 559 +++++++++ 2 files changed, 1607 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c create mode 100644 drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c new file mode 100644 index 000000000000..e027b92b54ef --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.c @@ -0,0 +1,1048 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#include "dm365_ipipe_hw.h" + +#define IPIPE_MODE_CONTINUOUS 0 +#define IPIPE_MODE_SINGLE_SHOT 1 + +static void ipipe_clock_enable(void *__iomem base_addr) +{ + /* enable IPIPE MMR for register write access */ + regw_ip(base_addr, IPIPE_GCK_MMR_DEFAULT, IPIPE_GCK_MMR); + + /* enable the clock wb,cfa,dfc,d2f,pre modules */ + regw_ip(base_addr, IPIPE_GCK_PIX_DEFAULT, IPIPE_GCK_PIX); +} + +static void +rsz_set_common_params(void *__iomem rsz_base, struct resizer_params *params) +{ + struct rsz_common_params *rsz_common = ¶ms->rsz_common; + u32 val; + + /* Set mode */ + regw_rsz(rsz_base, params->oper_mode, RSZ_SRC_MODE); + + /* data source selection and bypass */ + val = (rsz_common->passthrough << RSZ_BYPASS_SHIFT) | + rsz_common->source; + regw_rsz(rsz_base, val, RSZ_SRC_FMT0); + + /* src image selection */ + val = (rsz_common->raw_flip & 1) | + (rsz_common->src_img_fmt << RSZ_SRC_IMG_FMT_SHIFT) | + ((rsz_common->y_c & 1) << RSZ_SRC_Y_C_SEL_SHIFT); + regw_rsz(rsz_base, val, RSZ_SRC_FMT1); + + regw_rsz(rsz_base, rsz_common->vps & IPIPE_RSZ_VPS_MASK, RSZ_SRC_VPS); + regw_rsz(rsz_base, rsz_common->hps & IPIPE_RSZ_HPS_MASK, RSZ_SRC_HPS); + regw_rsz(rsz_base, rsz_common->vsz & IPIPE_RSZ_VSZ_MASK, RSZ_SRC_VSZ); + regw_rsz(rsz_base, rsz_common->hsz & IPIPE_RSZ_HSZ_MASK, RSZ_SRC_HSZ); + regw_rsz(rsz_base, rsz_common->yuv_y_min, RSZ_YUV_Y_MIN); + regw_rsz(rsz_base, rsz_common->yuv_y_max, RSZ_YUV_Y_MAX); + regw_rsz(rsz_base, rsz_common->yuv_c_min, RSZ_YUV_C_MIN); + regw_rsz(rsz_base, rsz_common->yuv_c_max, RSZ_YUV_C_MAX); + /* chromatic position */ + regw_rsz(rsz_base, rsz_common->out_chr_pos, RSZ_YUV_PHS); +} + +static void +rsz_set_rsz_regs(void *__iomem rsz_base, unsigned int rsz_id, + struct resizer_params *params) +{ + struct resizer_scale_param *rsc_params; + struct rsz_ext_mem_param *ext_mem; + struct resizer_rgb *rgb; + u32 reg_base; + u32 val; + + rsc_params = ¶ms->rsz_rsc_param[rsz_id]; + rgb = ¶ms->rsz2rgb[rsz_id]; + ext_mem = ¶ms->ext_mem_param[rsz_id]; + + if (rsz_id == RSZ_A) { + val = rsc_params->h_flip << RSZA_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZA_V_FLIP_SHIFT; + reg_base = RSZ_EN_A; + } else { + val = rsc_params->h_flip << RSZB_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZB_V_FLIP_SHIFT; + reg_base = RSZ_EN_B; + } + /* update flip settings */ + regw_rsz(rsz_base, val, RSZ_SEQ); + + regw_rsz(rsz_base, params->oper_mode, reg_base + RSZ_MODE); + + val = (rsc_params->cen << RSZ_CEN_SHIFT) | rsc_params->yen; + regw_rsz(rsz_base, val, reg_base + RSZ_420); + + regw_rsz(rsz_base, rsc_params->i_vps & RSZ_VPS_MASK, + reg_base + RSZ_I_VPS); + regw_rsz(rsz_base, rsc_params->i_hps & RSZ_HPS_MASK, + reg_base + RSZ_I_HPS); + regw_rsz(rsz_base, rsc_params->o_vsz & RSZ_O_VSZ_MASK, + reg_base + RSZ_O_VSZ); + regw_rsz(rsz_base, rsc_params->o_hsz & RSZ_O_HSZ_MASK, + reg_base + RSZ_O_HSZ); + regw_rsz(rsz_base, rsc_params->v_phs_y & RSZ_V_PHS_MASK, + reg_base + RSZ_V_PHS_Y); + regw_rsz(rsz_base, rsc_params->v_phs_c & RSZ_V_PHS_MASK, + reg_base + RSZ_V_PHS_C); + + /* keep this additional adjustment to zero for now */ + regw_rsz(rsz_base, rsc_params->v_dif & RSZ_V_DIF_MASK, + reg_base + RSZ_V_DIF); + + val = (rsc_params->v_typ_y & 1) | + ((rsc_params->v_typ_c & 1) << RSZ_TYP_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_V_TYP); + + val = (rsc_params->v_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->v_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_V_LPF); + + regw_rsz(rsz_base, rsc_params->h_phs & + RSZ_H_PHS_MASK, reg_base + RSZ_H_PHS); + + regw_rsz(rsz_base, 0, reg_base + RSZ_H_PHS_ADJ); + regw_rsz(rsz_base, rsc_params->h_dif & + RSZ_H_DIF_MASK, reg_base + RSZ_H_DIF); + + val = (rsc_params->h_typ_y & 1) | + ((rsc_params->h_typ_c & 1) << RSZ_TYP_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_H_TYP); + + val = (rsc_params->h_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->h_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_H_LPF); + + regw_rsz(rsz_base, rsc_params->dscale_en & 1, reg_base + RSZ_DWN_EN); + + val = (rsc_params->h_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) | + ((rsc_params->v_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) << + RSZ_DWN_SCALE_AV_SZ_V_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_DWN_AV); + + /* setting rgb conversion parameters */ + regw_rsz(rsz_base, rgb->rgb_en, reg_base + RSZ_RGB_EN); + + val = (rgb->rgb_typ << RSZ_RGB_TYP_SHIFT) | + (rgb->rgb_msk0 << RSZ_RGB_MSK0_SHIFT) | + (rgb->rgb_msk1 << RSZ_RGB_MSK1_SHIFT); + regw_rsz(rsz_base, val, reg_base + RSZ_RGB_TYP); + + regw_rsz(rsz_base, rgb->rgb_alpha_val & RSZ_RGB_ALPHA_MASK, + reg_base + RSZ_RGB_BLD); + + /* setting external memory parameters */ + regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_y, reg_base + RSZ_SDR_Y_OFT); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_y, + reg_base + RSZ_SDR_Y_PTR_S); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_e_y, + reg_base + RSZ_SDR_Y_PTR_E); + regw_rsz(rsz_base, ext_mem->rsz_sdr_oft_c, reg_base + RSZ_SDR_C_OFT); + regw_rsz(rsz_base, ext_mem->rsz_sdr_ptr_s_c, + reg_base + RSZ_SDR_C_PTR_S); + regw_rsz(rsz_base, (ext_mem->rsz_sdr_ptr_e_c >> 1), + reg_base + RSZ_SDR_C_PTR_E); +} + +/*set the registers of either RSZ0 or RSZ1 */ +static void +ipipe_setup_resizer(void *__iomem rsz_base, struct resizer_params *params) +{ + /* enable MMR gate to write to Resizer */ + regw_rsz(rsz_base, 1, RSZ_GCK_MMR); + + /* Enable resizer if it is not in bypass mode */ + if (params->rsz_common.passthrough) + regw_rsz(rsz_base, 0, RSZ_GCK_SDR); + else + regw_rsz(rsz_base, 1, RSZ_GCK_SDR); + + rsz_set_common_params(rsz_base, params); + + regw_rsz(rsz_base, params->rsz_en[RSZ_A], RSZ_EN_A); + + if (params->rsz_en[RSZ_A]) + /*setting rescale parameters */ + rsz_set_rsz_regs(rsz_base, RSZ_A, params); + + regw_rsz(rsz_base, params->rsz_en[RSZ_B], RSZ_EN_B); + + if (params->rsz_en[RSZ_B]) + rsz_set_rsz_regs(rsz_base, RSZ_B, params); +} + +static u32 ipipe_get_color_pat(enum v4l2_mbus_pixelcode pix) +{ + switch (pix) { + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + return ipipe_sgrbg_pattern; + + default: + return ipipe_srggb_pattern; + } +} + +static int ipipe_get_data_path(struct vpfe_ipipe_device *ipipe) +{ + enum v4l2_mbus_pixelcode temp_pix_fmt; + + switch (ipipe->formats[IPIPE_PAD_SINK].code) { + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SGRBG12_1X12: + temp_pix_fmt = V4L2_MBUS_FMT_SGRBG12_1X12; + break; + + default: + temp_pix_fmt = V4L2_MBUS_FMT_UYVY8_2X8; + } + + if (temp_pix_fmt == V4L2_MBUS_FMT_SGRBG12_1X12) { + if (ipipe->formats[IPIPE_PAD_SOURCE].code == + V4L2_MBUS_FMT_SGRBG12_1X12) + return IPIPE_RAW2RAW; + return IPIPE_RAW2YUV; + } + + return IPIPE_YUV2YUV; +} + +static int get_ipipe_mode(struct vpfe_ipipe_device *ipipe) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(ipipe); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY) + return IPIPE_MODE_SINGLE_SHOT; + else if (ipipeif_sink == IPIPEIF_INPUT_ISIF) + return IPIPE_MODE_CONTINUOUS; + + return -EINVAL; +} + +int config_ipipe_hw(struct vpfe_ipipe_device *ipipe) +{ + struct vpfe_ipipe_input_config *config = &ipipe->config.input_config; + void __iomem *ipipe_base = ipipe->base_addr; + struct v4l2_mbus_framefmt *outformat; + u32 color_pat; + u32 ipipe_mode; + u32 data_path; + + /* enable clock to IPIPE */ + vpss_enable_clock(VPSS_IPIPE_CLOCK, 1); + ipipe_clock_enable(ipipe_base); + + if (ipipe->input == IPIPE_INPUT_NONE) { + regw_ip(ipipe_base, 0, IPIPE_SRC_EN); + return 0; + } + + ipipe_mode = get_ipipe_mode(ipipe); + if (ipipe < 0) { + pr_err("Failed to get ipipe mode"); + return -EINVAL; + } + regw_ip(ipipe_base, ipipe_mode, IPIPE_SRC_MODE); + + data_path = ipipe_get_data_path(ipipe); + regw_ip(ipipe_base, data_path, IPIPE_SRC_FMT); + + regw_ip(ipipe_base, config->vst & IPIPE_RSZ_VPS_MASK, IPIPE_SRC_VPS); + regw_ip(ipipe_base, config->hst & IPIPE_RSZ_HPS_MASK, IPIPE_SRC_HPS); + + outformat = &ipipe->formats[IPIPE_PAD_SOURCE]; + regw_ip(ipipe_base, (outformat->height + 1) & IPIPE_RSZ_VSZ_MASK, + IPIPE_SRC_VSZ); + regw_ip(ipipe_base, (outformat->width + 1) & IPIPE_RSZ_HSZ_MASK, + IPIPE_SRC_HSZ); + + if (data_path == IPIPE_RAW2YUV || + data_path == IPIPE_RAW2RAW) { + color_pat = + ipipe_get_color_pat(ipipe->formats[IPIPE_PAD_SINK].code); + regw_ip(ipipe_base, color_pat, IPIPE_SRC_COL); + } + + return 0; +} + +/* + * config_rsz_hw() - Performs hardware setup of resizer. + */ +int config_rsz_hw(struct vpfe_resizer_device *resizer, + struct resizer_params *config) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + void *__iomem ipipe_base = vpfe_dev->vpfe_ipipe.base_addr; + void *__iomem rsz_base = vpfe_dev->vpfe_resizer.base_addr; + + /* enable VPSS clock */ + vpss_enable_clock(VPSS_IPIPE_CLOCK, 1); + ipipe_clock_enable(ipipe_base); + + ipipe_setup_resizer(rsz_base, config); + + return 0; +} + +static void +rsz_set_y_address(void *__iomem rsz_base, unsigned int address, + unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADDR; + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_L); + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_L); + + val = (address & SET_HIGH_ADDR) >> 16; + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_BAD_H); + regw_rsz(rsz_base, val, offset + RSZ_SDR_Y_SAD_H); +} + +static void +rsz_set_c_address(void *__iomem rsz_base, unsigned int address, + unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADDR; + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_L); + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_L); + + val = (address & SET_HIGH_ADDR) >> 16; + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_BAD_H); + regw_rsz(rsz_base, val, offset + RSZ_SDR_C_SAD_H); +} + +/* + * resizer_set_outaddr() - set the address for given resize_no + * @rsz_base: resizer base address + * @params: pointer to ipipe_params structure + * @resize_no: 0 - Resizer-A, 1 - Resizer B + * @address: the address to set + */ +int +resizer_set_outaddr(void *__iomem rsz_base, struct resizer_params *params, + int resize_no, unsigned int address) +{ + struct resizer_scale_param *rsc_param; + struct rsz_ext_mem_param *mem_param; + struct rsz_common_params *rsz_common; + unsigned int rsz_start_add; + unsigned int val; + + if (resize_no != RSZ_A && resize_no != RSZ_B) + return -EINVAL; + + mem_param = ¶ms->ext_mem_param[resize_no]; + rsc_param = ¶ms->rsz_rsc_param[resize_no]; + rsz_common = ¶ms->rsz_common; + + if (resize_no == RSZ_A) + rsz_start_add = RSZ_EN_A; + else + rsz_start_add = RSZ_EN_B; + + /* y_c = 0 for y, = 1 for c */ + if (rsz_common->src_img_fmt == RSZ_IMG_420) { + if (rsz_common->y_c) { + /* C channel */ + val = address + mem_param->flip_ofst_c; + rsz_set_c_address(rsz_base, val, rsz_start_add); + } else { + val = address + mem_param->flip_ofst_y; + rsz_set_y_address(rsz_base, val, rsz_start_add); + } + } else { + if (rsc_param->cen && rsc_param->yen) { + /* 420 */ + val = address + mem_param->c_offset + + mem_param->flip_ofst_c + + mem_param->user_y_ofst + + mem_param->user_c_ofst; + if (resize_no == RSZ_B) + val += + params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set C address */ + rsz_set_c_address(rsz_base, val, rsz_start_add); + } + val = address + mem_param->flip_ofst_y + mem_param->user_y_ofst; + if (resize_no == RSZ_B) + val += params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set Y address */ + rsz_set_y_address(rsz_base, val, rsz_start_add); + } + /* resizer must be enabled */ + regw_rsz(rsz_base, params->rsz_en[resize_no], rsz_start_add); + + return 0; +} + +void +ipipe_set_lutdpc_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_lutdpc *dpc) +{ + u32 max_tbl_size = LUT_DPC_MAX_SIZE >> 1; + u32 lut_start_addr = DPC_TB0_START_ADDR; + u32 val; + u32 count; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, dpc->en, DPC_LUT_EN); + + if (dpc->en != 1) + return; + + val = LUTDPC_TBL_256_EN | (dpc->repl_white & 1); + regw_ip(base_addr, val, DPC_LUT_SEL); + regw_ip(base_addr, LUT_DPC_START_ADDR, DPC_LUT_ADR); + regw_ip(base_addr, dpc->dpc_size, DPC_LUT_SIZ & LUT_DPC_SIZE_MASK); + + if (dpc->table == NULL) + return; + + for (count = 0; count < dpc->dpc_size; count++) { + if (count >= max_tbl_size) + lut_start_addr = DPC_TB1_START_ADDR; + val = (dpc->table[count].horz_pos & LUT_DPC_H_POS_MASK) | + ((dpc->table[count].vert_pos & LUT_DPC_V_POS_MASK) << + LUT_DPC_V_POS_SHIFT) | (dpc->table[count].method << + LUT_DPC_CORR_METH_SHIFT); + w_ip_table(isp5_base_addr, val, (lut_start_addr + + ((count % max_tbl_size) << 2))); + } +} + +static void +set_dpc_thresholds(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_thr) +{ + regw_ip(base_addr, dpc_thr->corr_thr.r & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_R); + regw_ip(base_addr, dpc_thr->corr_thr.gr & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_GR); + regw_ip(base_addr, dpc_thr->corr_thr.gb & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_GB); + regw_ip(base_addr, dpc_thr->corr_thr.b & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2C_THR_B); + regw_ip(base_addr, dpc_thr->det_thr.r & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_R); + regw_ip(base_addr, dpc_thr->det_thr.gr & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_GR); + regw_ip(base_addr, dpc_thr->det_thr.gb & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_GB); + regw_ip(base_addr, dpc_thr->det_thr.b & OTFDPC_DPC2_THR_MASK, + DPC_OTF_2D_THR_B); +} + +void ipipe_set_otfdpc_regs(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc *otfdpc) +{ + struct vpfe_ipipe_otfdpc_2_0_cfg *dpc_2_0 = &otfdpc->alg_cfg.dpc_2_0; + struct vpfe_ipipe_otfdpc_3_0_cfg *dpc_3_0 = &otfdpc->alg_cfg.dpc_3_0; + u32 val; + + ipipe_clock_enable(base_addr); + + regw_ip(base_addr, (otfdpc->en & 1), DPC_OTF_EN); + if (!otfdpc->en) + return; + + /* dpc enabled */ + val = (otfdpc->det_method << OTF_DET_METHOD_SHIFT) | otfdpc->alg; + regw_ip(base_addr, val, DPC_OTF_TYP); + + if (otfdpc->det_method == VPFE_IPIPE_DPC_OTF_MIN_MAX) { + /* ALG= 0, TYP = 0, DPC_OTF_2D_THR_[x]=0 + * DPC_OTF_2C_THR_[x] = Maximum thresohld + * MinMax method + */ + dpc_2_0->det_thr.r = dpc_2_0->det_thr.gb = + dpc_2_0->det_thr.gr = dpc_2_0->det_thr.b = 0; + set_dpc_thresholds(base_addr, dpc_2_0); + return; + } + /* MinMax2 */ + if (otfdpc->alg == VPFE_IPIPE_OTFDPC_2_0) { + set_dpc_thresholds(base_addr, dpc_2_0); + return; + } + regw_ip(base_addr, dpc_3_0->act_adj_shf & + OTF_DPC3_0_SHF_MASK, DPC_OTF_3_SHF); + /* Detection thresholds */ + regw_ip(base_addr, ((dpc_3_0->det_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3D_THR); + regw_ip(base_addr, dpc_3_0->det_slp & + OTF_DPC3_0_SLP_MASK, DPC_OTF_3D_SLP); + regw_ip(base_addr, dpc_3_0->det_thr_min & + OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MIN); + regw_ip(base_addr, dpc_3_0->det_thr_max & + OTF_DPC3_0_DET_MASK, DPC_OTF_3D_MAX); + /* Correction thresholds */ + regw_ip(base_addr, ((dpc_3_0->corr_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3C_THR); + regw_ip(base_addr, dpc_3_0->corr_slp & + OTF_DPC3_0_SLP_MASK, DPC_OTF_3C_SLP); + regw_ip(base_addr, dpc_3_0->corr_thr_min & + OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MIN); + regw_ip(base_addr, dpc_3_0->corr_thr_max & + OTF_DPC3_0_CORR_MASK, DPC_OTF_3C_MAX); +} + +/* 2D Noise filter */ +void +ipipe_set_d2f_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_nf *noise_filter) +{ + + u32 offset = D2F_1ST; + int count; + u32 val; + + if (id == IPIPE_D2F_2ND) + offset = D2F_2ND; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, noise_filter->en & 1, offset + D2F_EN); + if (!noise_filter->en) + return; + + /*noise filter enabled */ + /* Combine all the fields to make D2F_CFG register of IPIPE */ + val = ((noise_filter->spread_val & D2F_SPR_VAL_MASK) << + D2F_SPR_VAL_SHIFT) | ((noise_filter->shft_val & + D2F_SHFT_VAL_MASK) << D2F_SHFT_VAL_SHIFT) | + (noise_filter->gr_sample_meth << D2F_SAMPLE_METH_SHIFT) | + ((noise_filter->apply_lsc_gain & 1) << + D2F_APPLY_LSC_GAIN_SHIFT) | D2F_USE_SPR_REG_VAL; + regw_ip(base_addr, val, offset + D2F_TYP); + + /* edge detection minimum */ + regw_ip(base_addr, noise_filter->edge_det_min_thr & + D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MIN); + + /* edge detection maximum */ + regw_ip(base_addr, noise_filter->edge_det_max_thr & + D2F_EDGE_DET_THR_MASK, offset + D2F_EDG_MAX); + + for (count = 0; count < VPFE_IPIPE_NF_STR_TABLE_SIZE; count++) + regw_ip(base_addr, + (noise_filter->str[count] & D2F_STR_VAL_MASK), + offset + D2F_STR + count * 4); + + for (count = 0; count < VPFE_IPIPE_NF_THR_TABLE_SIZE; count++) + regw_ip(base_addr, noise_filter->thr[count] & D2F_THR_VAL_MASK, + offset + D2F_THR + count * 4); +} + +#define IPIPE_U8Q5(decimal, integer) \ + (((decimal & 0x1f) | ((integer & 0x7) << 5))) + +/* Green Imbalance Correction */ +void ipipe_set_gic_regs(void *__iomem base_addr, struct vpfe_ipipe_gic *gic) +{ + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, gic->en & 1, GIC_EN); + + if (!gic->en) + return; + + /*gic enabled */ + val = (gic->wt_fn_type << GIC_TYP_SHIFT) | + (gic->thr_sel << GIC_THR_SEL_SHIFT) | + ((gic->apply_lsc_gain & 1) << GIC_APPLY_LSC_GAIN_SHIFT); + regw_ip(base_addr, val, GIC_TYP); + + regw_ip(base_addr, gic->gain & GIC_GAIN_MASK, GIC_GAN); + + if (gic->gic_alg != VPFE_IPIPE_GIC_ALG_ADAPT_GAIN) { + /* Constant Gain. Set threshold to maximum */ + regw_ip(base_addr, GIC_THR_MASK, GIC_THR); + return; + } + + if (gic->thr_sel == VPFE_IPIPE_GIC_THR_REG) { + regw_ip(base_addr, gic->thr & GIC_THR_MASK, GIC_THR); + regw_ip(base_addr, gic->slope & GIC_SLOPE_MASK, GIC_SLP); + } else { + /* Use NF thresholds */ + val = IPIPE_U8Q5(gic->nf2_thr_gain.decimal, + gic->nf2_thr_gain.integer); + regw_ip(base_addr, val, GIC_NFGAN); + } +} + +#define IPIPE_U13Q9(decimal, integer) \ + (((decimal & 0x1ff) | ((integer & 0xf) << 9))) +/* White balance */ +void ipipe_set_wb_regs(void *__iomem base_addr, struct vpfe_ipipe_wb *wb) +{ + u32 val; + + ipipe_clock_enable(base_addr); + /* Ofsets. S12 */ + regw_ip(base_addr, wb->ofst_r & WB_OFFSET_MASK, WB2_OFT_R); + regw_ip(base_addr, wb->ofst_gr & WB_OFFSET_MASK, WB2_OFT_GR); + regw_ip(base_addr, wb->ofst_gb & WB_OFFSET_MASK, WB2_OFT_GB); + regw_ip(base_addr, wb->ofst_b & WB_OFFSET_MASK, WB2_OFT_B); + + /* Gains. U13Q9 */ + val = IPIPE_U13Q9(wb->gain_r.decimal, wb->gain_r.integer); + regw_ip(base_addr, val, WB2_WGN_R); + + val = IPIPE_U13Q9(wb->gain_gr.decimal, wb->gain_gr.integer); + regw_ip(base_addr, val, WB2_WGN_GR); + + val = IPIPE_U13Q9(wb->gain_gb.decimal, wb->gain_gb.integer); + regw_ip(base_addr, val, WB2_WGN_GB); + + val = IPIPE_U13Q9(wb->gain_b.decimal, wb->gain_b.integer); + regw_ip(base_addr, val, WB2_WGN_B); +} + +/* CFA */ +void ipipe_set_cfa_regs(void *__iomem base_addr, struct vpfe_ipipe_cfa *cfa) +{ + ipipe_clock_enable(base_addr); + + regw_ip(base_addr, cfa->alg, CFA_MODE); + regw_ip(base_addr, cfa->hpf_thr_2dir & CFA_HPF_THR_2DIR_MASK, + CFA_2DIR_HPF_THR); + regw_ip(base_addr, cfa->hpf_slp_2dir & CFA_HPF_SLOPE_2DIR_MASK, + CFA_2DIR_HPF_SLP); + regw_ip(base_addr, cfa->hp_mix_thr_2dir & CFA_HPF_MIX_THR_2DIR_MASK, + CFA_2DIR_MIX_THR); + regw_ip(base_addr, cfa->hp_mix_slope_2dir & CFA_HPF_MIX_SLP_2DIR_MASK, + CFA_2DIR_MIX_SLP); + regw_ip(base_addr, cfa->dir_thr_2dir & CFA_DIR_THR_2DIR_MASK, + CFA_2DIR_DIR_THR); + regw_ip(base_addr, cfa->dir_slope_2dir & CFA_DIR_SLP_2DIR_MASK, + CFA_2DIR_DIR_SLP); + regw_ip(base_addr, cfa->nd_wt_2dir & CFA_ND_WT_2DIR_MASK, + CFA_2DIR_NDWT); + regw_ip(base_addr, cfa->hue_fract_daa & CFA_DAA_HUE_FRA_MASK, + CFA_MONO_HUE_FRA); + regw_ip(base_addr, cfa->edge_thr_daa & CFA_DAA_EDG_THR_MASK, + CFA_MONO_EDG_THR); + regw_ip(base_addr, cfa->thr_min_daa & CFA_DAA_THR_MIN_MASK, + CFA_MONO_THR_MIN); + regw_ip(base_addr, cfa->thr_slope_daa & CFA_DAA_THR_SLP_MASK, + CFA_MONO_THR_SLP); + regw_ip(base_addr, cfa->slope_min_daa & CFA_DAA_SLP_MIN_MASK, + CFA_MONO_SLP_MIN); + regw_ip(base_addr, cfa->slope_slope_daa & CFA_DAA_SLP_SLP_MASK, + CFA_MONO_SLP_SLP); + regw_ip(base_addr, cfa->lp_wt_daa & CFA_DAA_LP_WT_MASK, + CFA_MONO_LPWT); +} + +void +ipipe_set_rgb2rgb_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_rgb2rgb *rgb) +{ + u32 offset_mask = RGB2RGB_1_OFST_MASK; + u32 offset = RGB1_MUL_BASE; + u32 integ_mask = 0xf; + u32 val; + + ipipe_clock_enable(base_addr); + + if (id == IPIPE_RGB2RGB_2) { + /* For second RGB module, gain integer is 3 bits instead + of 4, offset has 11 bits insread of 13 */ + offset = RGB2_MUL_BASE; + integ_mask = 0x7; + offset_mask = RGB2RGB_2_OFST_MASK; + } + /* Gains */ + val = (rgb->coef_rr.decimal & 0xff) | + ((rgb->coef_rr.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RR); + val = (rgb->coef_gr.decimal & 0xff) | + ((rgb->coef_gr.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GR); + val = (rgb->coef_br.decimal & 0xff) | + ((rgb->coef_br.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BR); + val = (rgb->coef_rg.decimal & 0xff) | + ((rgb->coef_rg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RG); + val = (rgb->coef_gg.decimal & 0xff) | + ((rgb->coef_gg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GG); + val = (rgb->coef_bg.decimal & 0xff) | + ((rgb->coef_bg.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BG); + val = (rgb->coef_rb.decimal & 0xff) | + ((rgb->coef_rb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_RB); + val = (rgb->coef_gb.decimal & 0xff) | + ((rgb->coef_gb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_GB); + val = (rgb->coef_bb.decimal & 0xff) | + ((rgb->coef_bb.integer & integ_mask) << 8); + regw_ip(base_addr, val, offset + RGB_MUL_BB); + + /* Offsets */ + regw_ip(base_addr, rgb->out_ofst_r & offset_mask, offset + RGB_OFT_OR); + regw_ip(base_addr, rgb->out_ofst_g & offset_mask, offset + RGB_OFT_OG); + regw_ip(base_addr, rgb->out_ofst_b & offset_mask, offset + RGB_OFT_OB); +} + +static void +ipipe_update_gamma_tbl(void *__iomem isp5_base_addr, + struct vpfe_ipipe_gamma_entry *table, int size, u32 addr) +{ + int count; + u32 val; + + for (count = 0; count < size; count++) { + val = table[count].slope & GAMMA_MASK; + val |= (table[count].offset & GAMMA_MASK) << GAMMA_SHIFT; + w_ip_table(isp5_base_addr, val, (addr + (count * 4))); + } +} + +void +ipipe_set_gamma_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_gamma *gamma) +{ + int table_size; + u32 val; + + ipipe_clock_enable(base_addr); + val = (gamma->bypass_r << GAMMA_BYPR_SHIFT) | + (gamma->bypass_b << GAMMA_BYPG_SHIFT) | + (gamma->bypass_g << GAMMA_BYPB_SHIFT) | + (gamma->tbl_sel << GAMMA_TBL_SEL_SHIFT) | + (gamma->tbl_size << GAMMA_TBL_SIZE_SHIFT); + + regw_ip(base_addr, val, GMM_CFG); + + if (gamma->tbl_sel != VPFE_IPIPE_GAMMA_TBL_RAM) + return; + + table_size = gamma->tbl_size; + + if (!gamma->bypass_r && gamma->table_r != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_r, + table_size, GAMMA_R_START_ADDR); + if (!gamma->bypass_b && gamma->table_b != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_b, + table_size, GAMMA_B_START_ADDR); + if (!gamma->bypass_g && gamma->table_g != NULL) + ipipe_update_gamma_tbl(isp5_base_addr, gamma->table_g, + table_size, GAMMA_G_START_ADDR); +} + +void +ipipe_set_3d_lut_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_3d_lut *lut_3d) +{ + struct vpfe_ipipe_3d_lut_entry *tbl; + u32 bnk_index; + u32 tbl_index; + u32 val; + u32 i; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, lut_3d->en, D3LUT_EN); + + if (!lut_3d->en) + return; + + /* lut_3d enabled */ + if (!lut_3d->table) + return; + + /* valied table */ + tbl = lut_3d->table; + for (i = 0 ; i < VPFE_IPIPE_MAX_SIZE_3D_LUT; i++) { + /* Each entry has 0-9 (B), 10-19 (G) and + 20-29 R values */ + val = tbl[i].b & D3_LUT_ENTRY_MASK; + val |= (tbl[i].g & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_G_SHIFT; + val |= (tbl[i].r & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_R_SHIFT; + bnk_index = i % 4; + tbl_index = i >> 2; + tbl_index <<= 2; + if (bnk_index == 0) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB0_START_ADDR); + else if (bnk_index == 1) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB1_START_ADDR); + else if (bnk_index == 2) + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB2_START_ADDR); + else + w_ip_table(isp5_base_addr, val, + tbl_index + D3L_TB3_START_ADDR); + } +} + +/* Lumina adjustments */ +void +ipipe_set_lum_adj_regs(void *__iomem base_addr, struct ipipe_lum_adj *lum_adj) +{ + u32 val; + + ipipe_clock_enable(base_addr); + + /* combine fields of YUV_ADJ to set brightness and contrast */ + val = lum_adj->contrast << LUM_ADJ_CONTR_SHIFT | + lum_adj->brightness << LUM_ADJ_BRIGHT_SHIFT; + regw_ip(base_addr, val, YUV_ADJ); +} + +#define IPIPE_S12Q8(decimal, integer) \ + (((decimal & 0xff) | ((integer & 0xf) << 8))) + +void ipipe_set_rgb2ycbcr_regs(void *__iomem base_addr, + struct vpfe_ipipe_rgb2yuv *yuv) +{ + u32 val; + + /* S10Q8 */ + ipipe_clock_enable(base_addr); + val = IPIPE_S12Q8(yuv->coef_ry.decimal, yuv->coef_ry.integer); + regw_ip(base_addr, val, YUV_MUL_RY); + val = IPIPE_S12Q8(yuv->coef_gy.decimal, yuv->coef_gy.integer); + regw_ip(base_addr, val, YUV_MUL_GY); + val = IPIPE_S12Q8(yuv->coef_by.decimal, yuv->coef_by.integer); + regw_ip(base_addr, val, YUV_MUL_BY); + val = IPIPE_S12Q8(yuv->coef_rcb.decimal, yuv->coef_rcb.integer); + regw_ip(base_addr, val, YUV_MUL_RCB); + val = IPIPE_S12Q8(yuv->coef_gcb.decimal, yuv->coef_gcb.integer); + regw_ip(base_addr, val, YUV_MUL_GCB); + val = IPIPE_S12Q8(yuv->coef_bcb.decimal, yuv->coef_bcb.integer); + regw_ip(base_addr, val, YUV_MUL_BCB); + val = IPIPE_S12Q8(yuv->coef_rcr.decimal, yuv->coef_rcr.integer); + regw_ip(base_addr, val, YUV_MUL_RCR); + val = IPIPE_S12Q8(yuv->coef_gcr.decimal, yuv->coef_gcr.integer); + regw_ip(base_addr, val, YUV_MUL_GCR); + val = IPIPE_S12Q8(yuv->coef_bcr.decimal, yuv->coef_bcr.integer); + regw_ip(base_addr, val, YUV_MUL_BCR); + regw_ip(base_addr, yuv->out_ofst_y & RGB2YCBCR_OFST_MASK, YUV_OFT_Y); + regw_ip(base_addr, yuv->out_ofst_cb & RGB2YCBCR_OFST_MASK, YUV_OFT_CB); + regw_ip(base_addr, yuv->out_ofst_cr & RGB2YCBCR_OFST_MASK, YUV_OFT_CR); +} + +/* YUV 422 conversion */ +void +ipipe_set_yuv422_conv_regs(void *__iomem base_addr, + struct vpfe_ipipe_yuv422_conv *conv) +{ + u32 val; + + ipipe_clock_enable(base_addr); + + /* Combine all the fields to make YUV_PHS register of IPIPE */ + val = (conv->chrom_pos << 0) | (conv->en_chrom_lpf << 1); + regw_ip(base_addr, val, YUV_PHS); +} + +void +ipipe_set_gbce_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_gbce *gbce) +{ + unsigned int count; + u32 mask = GBCE_Y_VAL_MASK; + + if (gbce->type == VPFE_IPIPE_GBCE_GAIN_TBL) + mask = GBCE_GAIN_VAL_MASK; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, gbce->en & 1, GBCE_EN); + + if (!gbce->en) + return; + + regw_ip(base_addr, gbce->type, GBCE_TYP); + + if (!gbce->table) + return; + + for (count = 0; count < VPFE_IPIPE_MAX_SIZE_GBCE_LUT ; count += 2) + w_ip_table(isp5_base_addr, ((gbce->table[count + 1] & mask) << + GBCE_ENTRY_SHIFT) | (gbce->table[count] & mask), + ((count/2) << 2) + GBCE_TB_START_ADDR); +} + +void +ipipe_set_ee_regs(void *__iomem base_addr, void *__iomem isp5_base_addr, + struct vpfe_ipipe_yee *ee) +{ + unsigned int count; + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, ee->en, YEE_EN); + + if (!ee->en) + return; + + val = ee->en_halo_red & 1; + val |= ee->merge_meth << YEE_HALO_RED_EN_SHIFT; + regw_ip(base_addr, val, YEE_TYP); + + regw_ip(base_addr, ee->hpf_shft, YEE_SHF); + regw_ip(base_addr, ee->hpf_coef_00 & YEE_COEF_MASK, YEE_MUL_00); + regw_ip(base_addr, ee->hpf_coef_01 & YEE_COEF_MASK, YEE_MUL_01); + regw_ip(base_addr, ee->hpf_coef_02 & YEE_COEF_MASK, YEE_MUL_02); + regw_ip(base_addr, ee->hpf_coef_10 & YEE_COEF_MASK, YEE_MUL_10); + regw_ip(base_addr, ee->hpf_coef_11 & YEE_COEF_MASK, YEE_MUL_11); + regw_ip(base_addr, ee->hpf_coef_12 & YEE_COEF_MASK, YEE_MUL_12); + regw_ip(base_addr, ee->hpf_coef_20 & YEE_COEF_MASK, YEE_MUL_20); + regw_ip(base_addr, ee->hpf_coef_21 & YEE_COEF_MASK, YEE_MUL_21); + regw_ip(base_addr, ee->hpf_coef_22 & YEE_COEF_MASK, YEE_MUL_22); + regw_ip(base_addr, ee->yee_thr & YEE_THR_MASK, YEE_THR); + regw_ip(base_addr, ee->es_gain & YEE_ES_GAIN_MASK, YEE_E_GAN); + regw_ip(base_addr, ee->es_thr1 & YEE_ES_THR1_MASK, YEE_E_THR1); + regw_ip(base_addr, ee->es_thr2 & YEE_THR_MASK, YEE_E_THR2); + regw_ip(base_addr, ee->es_gain_grad & YEE_THR_MASK, YEE_G_GAN); + regw_ip(base_addr, ee->es_ofst_grad & YEE_THR_MASK, YEE_G_OFT); + + if (ee->table == NULL) + return; + + for (count = 0; count < VPFE_IPIPE_MAX_SIZE_YEE_LUT; count += 2) + w_ip_table(isp5_base_addr, ((ee->table[count + 1] & + YEE_ENTRY_MASK) << YEE_ENTRY_SHIFT) | + (ee->table[count] & YEE_ENTRY_MASK), + ((count/2) << 2) + YEE_TB_START_ADDR); +} + +/* Chromatic Artifact Correction. CAR */ +static void ipipe_set_mf(void *__iomem base_addr) +{ + /* typ to dynamic switch */ + regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP); + /* Set SW0 to maximum */ + regw_ip(base_addr, CAR_MF_THR, CAR_SW); +} + +static void +ipipe_set_gain_ctrl(void *__iomem base_addr, struct vpfe_ipipe_car *car) +{ + regw_ip(base_addr, VPFE_IPIPE_CAR_CHR_GAIN_CTRL, CAR_TYP); + regw_ip(base_addr, car->hpf, CAR_HPF_TYP); + regw_ip(base_addr, car->hpf_shft & CAR_HPF_SHIFT_MASK, CAR_HPF_SHF); + regw_ip(base_addr, car->hpf_thr, CAR_HPF_THR); + regw_ip(base_addr, car->gain1.gain, CAR_GN1_GAN); + regw_ip(base_addr, car->gain1.shft & CAR_GAIN1_SHFT_MASK, CAR_GN1_SHF); + regw_ip(base_addr, car->gain1.gain_min & CAR_GAIN_MIN_MASK, + CAR_GN1_MIN); + regw_ip(base_addr, car->gain2.gain, CAR_GN2_GAN); + regw_ip(base_addr, car->gain2.shft & CAR_GAIN2_SHFT_MASK, CAR_GN2_SHF); + regw_ip(base_addr, car->gain2.gain_min & CAR_GAIN_MIN_MASK, + CAR_GN2_MIN); +} + +void ipipe_set_car_regs(void *__iomem base_addr, struct vpfe_ipipe_car *car) +{ + u32 val; + + ipipe_clock_enable(base_addr); + regw_ip(base_addr, car->en, CAR_EN); + + if (!car->en) + return; + + switch (car->meth) { + case VPFE_IPIPE_CAR_MED_FLTR: + ipipe_set_mf(base_addr); + break; + + case VPFE_IPIPE_CAR_CHR_GAIN_CTRL: + ipipe_set_gain_ctrl(base_addr, car); + break; + + default: + /* Dynamic switch between MF and Gain Ctrl. */ + ipipe_set_mf(base_addr); + ipipe_set_gain_ctrl(base_addr, car); + /* Set the threshold for switching between + * the two Here we overwrite the MF SW0 value + */ + regw_ip(base_addr, VPFE_IPIPE_CAR_DYN_SWITCH, CAR_TYP); + val = car->sw1; + val <<= CAR_SW1_SHIFT; + val |= car->sw0; + regw_ip(base_addr, val, CAR_SW); + } +} + +/* Chromatic Gain Suppression */ +void ipipe_set_cgs_regs(void *__iomem base_addr, struct vpfe_ipipe_cgs *cgs) +{ + ipipe_clock_enable(base_addr); + regw_ip(base_addr, cgs->en, CGS_EN); + + if (!cgs->en) + return; + + /* Set the bright side parameters */ + regw_ip(base_addr, cgs->h_thr, CGS_GN1_H_THR); + regw_ip(base_addr, cgs->h_slope, CGS_GN1_H_GAN); + regw_ip(base_addr, cgs->h_shft & CAR_SHIFT_MASK, CGS_GN1_H_SHF); + regw_ip(base_addr, cgs->h_min, CGS_GN1_H_MIN); +} + +void rsz_src_enable(void *__iomem rsz_base, int enable) +{ + regw_rsz(rsz_base, enable, RSZ_SRC_EN); +} + +int rsz_enable(void *__iomem rsz_base, int rsz_id, int enable) +{ + if (rsz_id == RSZ_A) { + regw_rsz(rsz_base, enable, RSZ_EN_A); + /* We always enable RSZ_A. RSZ_B is enable upon request from + * application. So enable RSZ_SRC_EN along with RSZ_A + */ + regw_rsz(rsz_base, enable, RSZ_SRC_EN); + } else if (rsz_id == RSZ_B) { + regw_rsz(rsz_base, enable, RSZ_EN_B); + } else { + BUG(); + } + + return 0; +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h new file mode 100644 index 000000000000..010fdb247faf --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipe_hw.h @@ -0,0 +1,559 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_IPIPE_HW_H +#define _DAVINCI_VPFE_DM365_IPIPE_HW_H + +#include "vpfe_mc_capture.h" + +#define SET_LOW_ADDR 0x0000ffff +#define SET_HIGH_ADDR 0xffff0000 + +/* Below are the internal tables */ +#define DPC_TB0_START_ADDR 0x8000 +#define DPC_TB1_START_ADDR 0x8400 + +#define GAMMA_R_START_ADDR 0xa800 +#define GAMMA_G_START_ADDR 0xb000 +#define GAMMA_B_START_ADDR 0xb800 + +/* RAM table addresses for edge enhancement correction*/ +#define YEE_TB_START_ADDR 0x8800 + +/* RAM table address for GBC LUT */ +#define GBCE_TB_START_ADDR 0x9000 + +/* RAM table for 3D NF LUT */ +#define D3L_TB0_START_ADDR 0x9800 +#define D3L_TB1_START_ADDR 0x9c00 +#define D3L_TB2_START_ADDR 0xa000 +#define D3L_TB3_START_ADDR 0xa400 + +/* IPIPE Register Offsets from the base address */ +#define IPIPE_SRC_EN 0x0000 +#define IPIPE_SRC_MODE 0x0004 +#define IPIPE_SRC_FMT 0x0008 +#define IPIPE_SRC_COL 0x000c +#define IPIPE_SRC_VPS 0x0010 +#define IPIPE_SRC_VSZ 0x0014 +#define IPIPE_SRC_HPS 0x0018 +#define IPIPE_SRC_HSZ 0x001c + +#define IPIPE_SEL_SBU 0x0020 + +#define IPIPE_DMA_STA 0x0024 +#define IPIPE_GCK_MMR 0x0028 +#define IPIPE_GCK_PIX 0x002c +#define IPIPE_RESERVED0 0x0030 + +/* Defect Correction */ +#define DPC_LUT_EN 0x0034 +#define DPC_LUT_SEL 0x0038 +#define DPC_LUT_ADR 0x003c +#define DPC_LUT_SIZ 0x0040 +#define DPC_OTF_EN 0x0044 +#define DPC_OTF_TYP 0x0048 +#define DPC_OTF_2D_THR_R 0x004c +#define DPC_OTF_2D_THR_GR 0x0050 +#define DPC_OTF_2D_THR_GB 0x0054 +#define DPC_OTF_2D_THR_B 0x0058 +#define DPC_OTF_2C_THR_R 0x005c +#define DPC_OTF_2C_THR_GR 0x0060 +#define DPC_OTF_2C_THR_GB 0x0064 +#define DPC_OTF_2C_THR_B 0x0068 +#define DPC_OTF_3_SHF 0x006c +#define DPC_OTF_3D_THR 0x0070 +#define DPC_OTF_3D_SLP 0x0074 +#define DPC_OTF_3D_MIN 0x0078 +#define DPC_OTF_3D_MAX 0x007c +#define DPC_OTF_3C_THR 0x0080 +#define DPC_OTF_3C_SLP 0x0084 +#define DPC_OTF_3C_MIN 0x0088 +#define DPC_OTF_3C_MAX 0x008c + +/* Lense Shading Correction */ +#define LSC_VOFT 0x90 +#define LSC_VA2 0x94 +#define LSC_VA1 0x98 +#define LSC_VS 0x9c +#define LSC_HOFT 0xa0 +#define LSC_HA2 0xa4 +#define LSC_HA1 0xa8 +#define LSC_HS 0xac +#define LSC_GAIN_R 0xb0 +#define LSC_GAIN_GR 0xb4 +#define LSC_GAIN_GB 0xb8 +#define LSC_GAIN_B 0xbc +#define LSC_OFT_R 0xc0 +#define LSC_OFT_GR 0xc4 +#define LSC_OFT_GB 0xc8 +#define LSC_OFT_B 0xcc +#define LSC_SHF 0xd0 +#define LSC_MAX 0xd4 + +/* Noise Filter 1. Ofsets from start address given */ +#define D2F_1ST 0xd8 +#define D2F_EN 0x0 +#define D2F_TYP 0x4 +#define D2F_THR 0x8 +#define D2F_STR 0x28 +#define D2F_SPR 0x48 +#define D2F_EDG_MIN 0x68 +#define D2F_EDG_MAX 0x6c + +/* Noise Filter 2 */ +#define D2F_2ND 0x148 + +/* GIC */ +#define GIC_EN 0x1b8 +#define GIC_TYP 0x1bc +#define GIC_GAN 0x1c0 +#define GIC_NFGAN 0x1c4 +#define GIC_THR 0x1c8 +#define GIC_SLP 0x1cc + +/* White Balance */ +#define WB2_OFT_R 0x1d0 +#define WB2_OFT_GR 0x1d4 +#define WB2_OFT_GB 0x1d8 +#define WB2_OFT_B 0x1dc +#define WB2_WGN_R 0x1e0 +#define WB2_WGN_GR 0x1e4 +#define WB2_WGN_GB 0x1e8 +#define WB2_WGN_B 0x1ec + +/* CFA interpolation */ +#define CFA_MODE 0x1f0 +#define CFA_2DIR_HPF_THR 0x1f4 +#define CFA_2DIR_HPF_SLP 0x1f8 +#define CFA_2DIR_MIX_THR 0x1fc +#define CFA_2DIR_MIX_SLP 0x200 +#define CFA_2DIR_DIR_THR 0x204 +#define CFA_2DIR_DIR_SLP 0x208 +#define CFA_2DIR_NDWT 0x20c +#define CFA_MONO_HUE_FRA 0x210 +#define CFA_MONO_EDG_THR 0x214 +#define CFA_MONO_THR_MIN 0x218 +#define CFA_MONO_THR_SLP 0x21c +#define CFA_MONO_SLP_MIN 0x220 +#define CFA_MONO_SLP_SLP 0x224 +#define CFA_MONO_LPWT 0x228 + +/* RGB to RGB conversiona - 1st */ +#define RGB1_MUL_BASE 0x22c +/* Offsets from base */ +#define RGB_MUL_RR 0x0 +#define RGB_MUL_GR 0x4 +#define RGB_MUL_BR 0x8 +#define RGB_MUL_RG 0xc +#define RGB_MUL_GG 0x10 +#define RGB_MUL_BG 0x14 +#define RGB_MUL_RB 0x18 +#define RGB_MUL_GB 0x1c +#define RGB_MUL_BB 0x20 +#define RGB_OFT_OR 0x24 +#define RGB_OFT_OG 0x28 +#define RGB_OFT_OB 0x2c + +/* Gamma */ +#define GMM_CFG 0x25c + +/* RGB to RGB conversiona - 2nd */ +#define RGB2_MUL_BASE 0x260 + +/* 3D LUT */ +#define D3LUT_EN 0x290 + +/* RGB to YUV(YCbCr) conversion */ +#define YUV_ADJ 0x294 +#define YUV_MUL_RY 0x298 +#define YUV_MUL_GY 0x29c +#define YUV_MUL_BY 0x2a0 +#define YUV_MUL_RCB 0x2a4 +#define YUV_MUL_GCB 0x2a8 +#define YUV_MUL_BCB 0x2ac +#define YUV_MUL_RCR 0x2b0 +#define YUV_MUL_GCR 0x2b4 +#define YUV_MUL_BCR 0x2b8 +#define YUV_OFT_Y 0x2bc +#define YUV_OFT_CB 0x2c0 +#define YUV_OFT_CR 0x2c4 +#define YUV_PHS 0x2c8 + +/* Global Brightness and Contrast */ +#define GBCE_EN 0x2cc +#define GBCE_TYP 0x2d0 + +/* Edge Enhancer */ +#define YEE_EN 0x2d4 +#define YEE_TYP 0x2d8 +#define YEE_SHF 0x2dc +#define YEE_MUL_00 0x2e0 +#define YEE_MUL_01 0x2e4 +#define YEE_MUL_02 0x2e8 +#define YEE_MUL_10 0x2ec +#define YEE_MUL_11 0x2f0 +#define YEE_MUL_12 0x2f4 +#define YEE_MUL_20 0x2f8 +#define YEE_MUL_21 0x2fc +#define YEE_MUL_22 0x300 +#define YEE_THR 0x304 +#define YEE_E_GAN 0x308 +#define YEE_E_THR1 0x30c +#define YEE_E_THR2 0x310 +#define YEE_G_GAN 0x314 +#define YEE_G_OFT 0x318 + +/* Chroma Artifact Reduction */ +#define CAR_EN 0x31c +#define CAR_TYP 0x320 +#define CAR_SW 0x324 +#define CAR_HPF_TYP 0x328 +#define CAR_HPF_SHF 0x32c +#define CAR_HPF_THR 0x330 +#define CAR_GN1_GAN 0x334 +#define CAR_GN1_SHF 0x338 +#define CAR_GN1_MIN 0x33c +#define CAR_GN2_GAN 0x340 +#define CAR_GN2_SHF 0x344 +#define CAR_GN2_MIN 0x348 + +/* Chroma Gain Suppression */ +#define CGS_EN 0x34c +#define CGS_GN1_L_THR 0x350 +#define CGS_GN1_L_GAN 0x354 +#define CGS_GN1_L_SHF 0x358 +#define CGS_GN1_L_MIN 0x35c +#define CGS_GN1_H_THR 0x360 +#define CGS_GN1_H_GAN 0x364 +#define CGS_GN1_H_SHF 0x368 +#define CGS_GN1_H_MIN 0x36c +#define CGS_GN2_L_THR 0x370 +#define CGS_GN2_L_GAN 0x374 +#define CGS_GN2_L_SHF 0x378 +#define CGS_GN2_L_MIN 0x37c + +/* Resizer */ +#define RSZ_SRC_EN 0x0 +#define RSZ_SRC_MODE 0x4 +#define RSZ_SRC_FMT0 0x8 +#define RSZ_SRC_FMT1 0xc +#define RSZ_SRC_VPS 0x10 +#define RSZ_SRC_VSZ 0x14 +#define RSZ_SRC_HPS 0x18 +#define RSZ_SRC_HSZ 0x1c +#define RSZ_DMA_RZA 0x20 +#define RSZ_DMA_RZB 0x24 +#define RSZ_DMA_STA 0x28 +#define RSZ_GCK_MMR 0x2c +#define RSZ_RESERVED0 0x30 +#define RSZ_GCK_SDR 0x34 +#define RSZ_IRQ_RZA 0x38 +#define RSZ_IRQ_RZB 0x3c +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c +#define RSZ_YUV_PHS 0x50 +#define RSZ_SEQ 0x54 + +/* Resizer Rescale Parameters */ +#define RSZ_EN_A 0x58 +#define RSZ_EN_B 0xe8 +/* offset of the registers to be added with base register of + either RSZ0 or RSZ1 +*/ +#define RSZ_MODE 0x4 +#define RSZ_420 0x8 +#define RSZ_I_VPS 0xc +#define RSZ_I_HPS 0x10 +#define RSZ_O_VSZ 0x14 +#define RSZ_O_HSZ 0x18 +#define RSZ_V_PHS_Y 0x1c +#define RSZ_V_PHS_C 0x20 +#define RSZ_V_DIF 0x24 +#define RSZ_V_TYP 0x28 +#define RSZ_V_LPF 0x2c +#define RSZ_H_PHS 0x30 +#define RSZ_H_PHS_ADJ 0x34 +#define RSZ_H_DIF 0x38 +#define RSZ_H_TYP 0x3c +#define RSZ_H_LPF 0x40 +#define RSZ_DWN_EN 0x44 +#define RSZ_DWN_AV 0x48 + +/* Resizer RGB Conversion Parameters */ +#define RSZ_RGB_EN 0x4c +#define RSZ_RGB_TYP 0x50 +#define RSZ_RGB_BLD 0x54 + +/* Resizer External Memory Parameters */ +#define RSZ_SDR_Y_BAD_H 0x58 +#define RSZ_SDR_Y_BAD_L 0x5c +#define RSZ_SDR_Y_SAD_H 0x60 +#define RSZ_SDR_Y_SAD_L 0x64 +#define RSZ_SDR_Y_OFT 0x68 +#define RSZ_SDR_Y_PTR_S 0x6c +#define RSZ_SDR_Y_PTR_E 0x70 +#define RSZ_SDR_C_BAD_H 0x74 +#define RSZ_SDR_C_BAD_L 0x78 +#define RSZ_SDR_C_SAD_H 0x7c +#define RSZ_SDR_C_SAD_L 0x80 +#define RSZ_SDR_C_OFT 0x84 +#define RSZ_SDR_C_PTR_S 0x88 +#define RSZ_SDR_C_PTR_E 0x8c + +/* Macro for resizer */ +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c + +#define IPIPE_GCK_MMR_DEFAULT 1 +#define IPIPE_GCK_PIX_DEFAULT 0xe +#define RSZ_GCK_MMR_DEFAULT 1 +#define RSZ_GCK_SDR_DEFAULT 1 + +/* LUTDPC */ +#define LUTDPC_TBL_256_EN 0 +#define LUTDPC_INF_TBL_EN 1 +#define LUT_DPC_START_ADDR 0 +#define LUT_DPC_H_POS_MASK 0x1fff +#define LUT_DPC_V_POS_MASK 0x1fff +#define LUT_DPC_V_POS_SHIFT 13 +#define LUT_DPC_CORR_METH_SHIFT 26 +#define LUT_DPC_MAX_SIZE 256 +#define LUT_DPC_SIZE_MASK 0x3ff + +/* OTFDPC */ +#define OTFDPC_DPC2_THR_MASK 0xfff +#define OTF_DET_METHOD_SHIFT 1 +#define OTF_DPC3_0_SHF_MASK 3 +#define OTF_DPC3_0_THR_SHIFT 6 +#define OTF_DPC3_0_THR_MASK 0x3f +#define OTF_DPC3_0_SLP_MASK 0x3f +#define OTF_DPC3_0_DET_MASK 0xfff +#define OTF_DPC3_0_CORR_MASK 0xfff + +/* NF (D2F) */ +#define D2F_SPR_VAL_MASK 0x1f +#define D2F_SPR_VAL_SHIFT 0 +#define D2F_SHFT_VAL_MASK 3 +#define D2F_SHFT_VAL_SHIFT 5 +#define D2F_SAMPLE_METH_SHIFT 7 +#define D2F_APPLY_LSC_GAIN_SHIFT 8 +#define D2F_USE_SPR_REG_VAL 0 +#define D2F_STR_VAL_MASK 0x1f +#define D2F_THR_VAL_MASK 0x3ff +#define D2F_EDGE_DET_THR_MASK 0x7ff + +/* Green Imbalance Correction */ +#define GIC_TYP_SHIFT 0 +#define GIC_THR_SEL_SHIFT 1 +#define GIC_APPLY_LSC_GAIN_SHIFT 2 +#define GIC_GAIN_MASK 0xff +#define GIC_THR_MASK 0xfff +#define GIC_SLOPE_MASK 0xfff +#define GIC_NFGAN_INT_MASK 7 +#define GIC_NFGAN_DECI_MASK 0x1f + +/* WB */ +#define WB_OFFSET_MASK 0xfff +#define WB_GAIN_INT_MASK 0xf +#define WB_GAIN_DECI_MASK 0x1ff + +/* CFA */ +#define CFA_HPF_THR_2DIR_MASK 0x1fff +#define CFA_HPF_SLOPE_2DIR_MASK 0x3ff +#define CFA_HPF_MIX_THR_2DIR_MASK 0x1fff +#define CFA_HPF_MIX_SLP_2DIR_MASK 0x3ff +#define CFA_DIR_THR_2DIR_MASK 0x3ff +#define CFA_DIR_SLP_2DIR_MASK 0x7f +#define CFA_ND_WT_2DIR_MASK 0x3f +#define CFA_DAA_HUE_FRA_MASK 0x3f +#define CFA_DAA_EDG_THR_MASK 0xff +#define CFA_DAA_THR_MIN_MASK 0x3ff +#define CFA_DAA_THR_SLP_MASK 0x3ff +#define CFA_DAA_SLP_MIN_MASK 0x3ff +#define CFA_DAA_SLP_SLP_MASK 0x3ff +#define CFA_DAA_LP_WT_MASK 0x3f + +/* RGB2RGB */ +#define RGB2RGB_1_OFST_MASK 0x1fff +#define RGB2RGB_1_GAIN_INT_MASK 0xf +#define RGB2RGB_GAIN_DECI_MASK 0xff +#define RGB2RGB_2_OFST_MASK 0x7ff +#define RGB2RGB_2_GAIN_INT_MASK 0x7 + +/* Gamma */ +#define GAMMA_BYPR_SHIFT 0 +#define GAMMA_BYPG_SHIFT 1 +#define GAMMA_BYPB_SHIFT 2 +#define GAMMA_TBL_SEL_SHIFT 4 +#define GAMMA_TBL_SIZE_SHIFT 5 +#define GAMMA_MASK 0x3ff +#define GAMMA_SHIFT 10 + +/* 3D LUT */ +#define D3_LUT_ENTRY_MASK 0x3ff +#define D3_LUT_ENTRY_R_SHIFT 20 +#define D3_LUT_ENTRY_G_SHIFT 10 +#define D3_LUT_ENTRY_B_SHIFT 0 + +/* Lumina adj */ +#define LUM_ADJ_CONTR_SHIFT 0 +#define LUM_ADJ_BRIGHT_SHIFT 8 + +/* RGB2YCbCr */ +#define RGB2YCBCR_OFST_MASK 0x7ff +#define RGB2YCBCR_COEF_INT_MASK 0xf +#define RGB2YCBCR_COEF_DECI_MASK 0xff + +/* GBCE */ +#define GBCE_Y_VAL_MASK 0xff +#define GBCE_GAIN_VAL_MASK 0x3ff +#define GBCE_ENTRY_SHIFT 10 + +/* Edge Enhancements */ +#define YEE_HALO_RED_EN_SHIFT 1 +#define YEE_HPF_SHIFT_MASK 0xf +#define YEE_COEF_MASK 0x3ff +#define YEE_THR_MASK 0x3f +#define YEE_ES_GAIN_MASK 0xfff +#define YEE_ES_THR1_MASK 0xfff +#define YEE_ENTRY_SHIFT 9 +#define YEE_ENTRY_MASK 0x1ff + +/* CAR */ +#define CAR_MF_THR 0xff +#define CAR_SW1_SHIFT 8 +#define CAR_GAIN1_SHFT_MASK 7 +#define CAR_GAIN_MIN_MASK 0x1ff +#define CAR_GAIN2_SHFT_MASK 0xf +#define CAR_HPF_SHIFT_MASK 3 + +/* CGS */ +#define CAR_SHIFT_MASK 3 + +/* Resizer */ +#define RSZ_BYPASS_SHIFT 1 +#define RSZ_SRC_IMG_FMT_SHIFT 1 +#define RSZ_SRC_Y_C_SEL_SHIFT 2 +#define IPIPE_RSZ_VPS_MASK 0xffff +#define IPIPE_RSZ_HPS_MASK 0xffff +#define IPIPE_RSZ_VSZ_MASK 0x1fff +#define IPIPE_RSZ_HSZ_MASK 0x1fff +#define RSZ_HPS_MASK 0x1fff +#define RSZ_VPS_MASK 0x1fff +#define RSZ_O_HSZ_MASK 0x1fff +#define RSZ_O_VSZ_MASK 0x1fff +#define RSZ_V_PHS_MASK 0x3fff +#define RSZ_V_DIF_MASK 0x3fff + +#define RSZA_H_FLIP_SHIFT 0 +#define RSZA_V_FLIP_SHIFT 1 +#define RSZB_H_FLIP_SHIFT 2 +#define RSZB_V_FLIP_SHIFT 3 +#define RSZ_A 0 +#define RSZ_B 1 +#define RSZ_CEN_SHIFT 1 +#define RSZ_YEN_SHIFT 0 +#define RSZ_TYP_Y_SHIFT 0 +#define RSZ_TYP_C_SHIFT 1 +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_C_SHIFT 6 +#define RSZ_H_PHS_MASK 0x3fff +#define RSZ_H_DIF_MASK 0x3fff +#define RSZ_DIFF_DOWN_THR 256 +#define RSZ_DWN_SCALE_AV_SZ_V_SHIFT 3 +#define RSZ_DWN_SCALE_AV_SZ_MASK 7 +#define RSZ_RGB_MSK1_SHIFT 2 +#define RSZ_RGB_MSK0_SHIFT 1 +#define RSZ_RGB_TYP_SHIFT 0 +#define RSZ_RGB_ALPHA_MASK 0xff + +static inline u32 regr_ip(void *__iomem addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline void regw_ip(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); +} + +static inline u32 w_ip_table(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); + + return val; +} + +static inline u32 regr_rsz(void *__iomem addr, u32 offset) +{ + return readl(addr + offset); +} + +static inline u32 regw_rsz(void *__iomem addr, u32 val, u32 offset) +{ + writel(val, addr + offset); + + return val; +} + +int config_ipipe_hw(struct vpfe_ipipe_device *ipipe); +int resizer_set_outaddr(void *__iomem rsz_base, struct resizer_params *params, + int resize_no, unsigned int address); +int rsz_enable(void *__iomem rsz_base, int rsz_id, int enable); +void rsz_src_enable(void *__iomem rsz_base, int enable); +void rsz_set_in_pix_format(unsigned char y_c); +int config_rsz_hw(struct vpfe_resizer_device *resizer, + struct resizer_params *config); +void ipipe_set_d2f_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_nf *noise_filter); +void ipipe_set_rgb2rgb_regs(void *__iomem base_addr, unsigned int id, + struct vpfe_ipipe_rgb2rgb *rgb); +void ipipe_set_yuv422_conv_regs(void *__iomem base_addr, + struct vpfe_ipipe_yuv422_conv *conv); +void ipipe_set_lum_adj_regs(void *__iomem base_addr, + struct ipipe_lum_adj *lum_adj); +void ipipe_set_rgb2ycbcr_regs(void *__iomem base_addr, + struct vpfe_ipipe_rgb2yuv *yuv); +void ipipe_set_lutdpc_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_lutdpc *lutdpc); +void ipipe_set_otfdpc_regs(void *__iomem base_addr, + struct vpfe_ipipe_otfdpc *otfdpc); +void ipipe_set_3d_lut_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_3d_lut *lut_3d); +void ipipe_set_gamma_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_gamma *gamma); +void ipipe_set_ee_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_yee *ee); +void ipipe_set_gbce_regs(void *__iomem base_addr, + void *__iomem isp5_base_addr, struct vpfe_ipipe_gbce *gbce); +void ipipe_set_gic_regs(void *__iomem base_addr, struct vpfe_ipipe_gic *gic); +void ipipe_set_cfa_regs(void *__iomem base_addr, struct vpfe_ipipe_cfa *cfa); +void ipipe_set_car_regs(void *__iomem base_addr, struct vpfe_ipipe_car *car); +void ipipe_set_cgs_regs(void *__iomem base_addr, struct vpfe_ipipe_cgs *cgs); +void ipipe_set_wb_regs(void *__iomem base_addr, struct vpfe_ipipe_wb *wb); + +#endif /* _DAVINCI_VPFE_DM365_IPIPE_HW_H */ From 45e46b3bbe187a1bfc8f44c8d7ceaf851f0f4219 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:17:55 -0300 Subject: [PATCH 0298/4607] [media] davinci: vpfe: dm365: resizer driver based on media framework Add the video resizer driver with the v4l2 media controller framework which takes care of resizing the video frames with both up-scaling downscaling facility. The driver supports both continuous and single shot operations.The driver supports resizer as a subdevice and a media entity. It has support for 2 resizers - resizerA and resizerB. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- .../media/davinci_vpfe/dm365_resizer.c | 1999 +++++++++++++++++ .../media/davinci_vpfe/dm365_resizer.h | 244 ++ 2 files changed, 2243 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/dm365_resizer.c create mode 100644 drivers/staging/media/davinci_vpfe/dm365_resizer.h diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.c b/drivers/staging/media/davinci_vpfe/dm365_resizer.c new file mode 100644 index 000000000000..9cb0262b9b33 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.c @@ -0,0 +1,1999 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + * + * + * Resizer allows upscaling or downscaling a image to a desired + * resolution. There are 2 resizer modules. both operating on the + * same input image, but can have different output resolution. + */ + +#include "dm365_ipipe_hw.h" +#include "dm365_resizer.h" + +#define MIN_IN_WIDTH 32 +#define MIN_IN_HEIGHT 32 +#define MAX_IN_WIDTH 4095 +#define MAX_IN_HEIGHT 4095 +#define MIN_OUT_WIDTH 16 +#define MIN_OUT_HEIGHT 2 + +static const unsigned int resizer_input_formats[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_SGRBG12_1X12, +}; + +static const unsigned int resizer_output_formats[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_YDYUYDYV8_1X16, + V4L2_MBUS_FMT_SGRBG12_1X12, +}; + +/* resizer_calculate_line_length() - This function calculates the line length of + * various image planes at the input and + * output. + */ +static void +resizer_calculate_line_length(enum v4l2_mbus_pixelcode pix, int width, + int height, int *line_len, int *line_len_c) +{ + *line_len = 0; + *line_len_c = 0; + + if (pix == V4L2_MBUS_FMT_UYVY8_2X8 || + pix == V4L2_MBUS_FMT_SGRBG12_1X12) { + *line_len = width << 1; + } else if (pix == V4L2_MBUS_FMT_Y8_1X8 || + pix == V4L2_MBUS_FMT_UV8_1X8) { + *line_len = width; + *line_len_c = width; + } else { + /* YUV 420 */ + /* round width to upper 32 byte boundary */ + *line_len = width; + *line_len_c = width; + } + /* adjust the line len to be a multiple of 32 */ + *line_len += 31; + *line_len &= ~0x1f; + *line_len_c += 31; + *line_len_c &= ~0x1f; +} + +static inline int +resizer_validate_output_image_format(struct device *dev, + struct v4l2_mbus_framefmt *format, + int *in_line_len, int *in_line_len_c) +{ + if (format->code != V4L2_MBUS_FMT_UYVY8_2X8 && + format->code != V4L2_MBUS_FMT_Y8_1X8 && + format->code != V4L2_MBUS_FMT_UV8_1X8 && + format->code != V4L2_MBUS_FMT_YDYUYDYV8_1X16 && + format->code != V4L2_MBUS_FMT_SGRBG12_1X12) { + dev_err(dev, "Invalid Mbus format, %d\n", format->code); + return -EINVAL; + } + if (!format->width || !format->height) { + dev_err(dev, "invalid width or height\n"); + return -EINVAL; + } + resizer_calculate_line_length(format->code, format->width, + format->height, in_line_len, in_line_len_c); + return 0; +} + +static void +resizer_configure_passthru(struct vpfe_resizer_device *resizer, int bypass) +{ + struct resizer_params *param = &resizer->config; + + param->rsz_rsc_param[RSZ_A].cen = DISABLE; + param->rsz_rsc_param[RSZ_A].yen = DISABLE; + param->rsz_rsc_param[RSZ_A].v_phs_y = 0; + param->rsz_rsc_param[RSZ_A].v_phs_c = 0; + param->rsz_rsc_param[RSZ_A].v_dif = 256; + param->rsz_rsc_param[RSZ_A].v_lpf_int_y = 0; + param->rsz_rsc_param[RSZ_A].v_lpf_int_c = 0; + param->rsz_rsc_param[RSZ_A].h_phs = 0; + param->rsz_rsc_param[RSZ_A].h_dif = 256; + param->rsz_rsc_param[RSZ_A].h_lpf_int_y = 0; + param->rsz_rsc_param[RSZ_A].h_lpf_int_c = 0; + param->rsz_rsc_param[RSZ_A].dscale_en = DISABLE; + param->rsz2rgb[RSZ_A].rgb_en = DISABLE; + param->rsz_en[RSZ_A] = ENABLE; + param->rsz_en[RSZ_B] = DISABLE; + if (bypass) { + param->rsz_rsc_param[RSZ_A].i_vps = 0; + param->rsz_rsc_param[RSZ_A].i_hps = 0; + /* Raw Bypass */ + param->rsz_common.passthrough = BYPASS_ON; + } +} + +static void +configure_resizer_out_params(struct vpfe_resizer_device *resizer, int index, + void *output_spec, unsigned char partial, + unsigned flag) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *outformat; + struct vpfe_rsz_output_spec *output; + + if (index == RSZ_A && + resizer->resizer_a.output == RESIZER_OUTPUT_NONE) { + param->rsz_en[index] = DISABLE; + return; + } + if (index == RSZ_B && + resizer->resizer_b.output == RESIZER_OUTPUT_NONE) { + param->rsz_en[index] = DISABLE; + return; + } + output = (struct vpfe_rsz_output_spec *)output_spec; + param->rsz_en[index] = ENABLE; + if (partial) { + param->rsz_rsc_param[index].h_flip = output->h_flip; + param->rsz_rsc_param[index].v_flip = output->v_flip; + param->rsz_rsc_param[index].v_typ_y = output->v_typ_y; + param->rsz_rsc_param[index].v_typ_c = output->v_typ_c; + param->rsz_rsc_param[index].v_lpf_int_y = + output->v_lpf_int_y; + param->rsz_rsc_param[index].v_lpf_int_c = + output->v_lpf_int_c; + param->rsz_rsc_param[index].h_typ_y = output->h_typ_y; + param->rsz_rsc_param[index].h_typ_c = output->h_typ_c; + param->rsz_rsc_param[index].h_lpf_int_y = + output->h_lpf_int_y; + param->rsz_rsc_param[index].h_lpf_int_c = + output->h_lpf_int_c; + param->rsz_rsc_param[index].dscale_en = + output->en_down_scale; + param->rsz_rsc_param[index].h_dscale_ave_sz = + output->h_dscale_ave_sz; + param->rsz_rsc_param[index].v_dscale_ave_sz = + output->v_dscale_ave_sz; + param->ext_mem_param[index].user_y_ofst = + (output->user_y_ofst + 31) & ~0x1f; + param->ext_mem_param[index].user_c_ofst = + (output->user_c_ofst + 31) & ~0x1f; + return; + } + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + param->rsz_rsc_param[index].o_vsz = outformat->height - 1; + param->rsz_rsc_param[index].o_hsz = outformat->width - 1; + param->ext_mem_param[index].rsz_sdr_ptr_s_y = output->vst_y; + param->ext_mem_param[index].rsz_sdr_ptr_e_y = outformat->height; + param->ext_mem_param[index].rsz_sdr_ptr_s_c = output->vst_c; + param->ext_mem_param[index].rsz_sdr_ptr_e_c = outformat->height; + + if (!flag) + return; + /* update common parameters */ + param->rsz_rsc_param[index].h_flip = output->h_flip; + param->rsz_rsc_param[index].v_flip = output->v_flip; + param->rsz_rsc_param[index].v_typ_y = output->v_typ_y; + param->rsz_rsc_param[index].v_typ_c = output->v_typ_c; + param->rsz_rsc_param[index].v_lpf_int_y = output->v_lpf_int_y; + param->rsz_rsc_param[index].v_lpf_int_c = output->v_lpf_int_c; + param->rsz_rsc_param[index].h_typ_y = output->h_typ_y; + param->rsz_rsc_param[index].h_typ_c = output->h_typ_c; + param->rsz_rsc_param[index].h_lpf_int_y = output->h_lpf_int_y; + param->rsz_rsc_param[index].h_lpf_int_c = output->h_lpf_int_c; + param->rsz_rsc_param[index].dscale_en = output->en_down_scale; + param->rsz_rsc_param[index].h_dscale_ave_sz = output->h_dscale_ave_sz; + param->rsz_rsc_param[index].v_dscale_ave_sz = output->h_dscale_ave_sz; + param->ext_mem_param[index].user_y_ofst = + (output->user_y_ofst + 31) & ~0x1f; + param->ext_mem_param[index].user_c_ofst = + (output->user_c_ofst + 31) & ~0x1f; +} + +/* + * resizer_calculate_resize_ratios() - Calculates resize ratio for resizer + * A or B. This is called after setting + * the input size or output size. + * @resizer: Pointer to VPFE resizer subdevice. + * @index: index RSZ_A-resizer-A RSZ_B-resizer-B. + */ +void +resizer_calculate_resize_ratios(struct vpfe_resizer_device *resizer, int index) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *informat, *outformat; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + if (outformat->field != V4L2_FIELD_INTERLACED) + param->rsz_rsc_param[index].v_dif = + ((informat->height) * 256) / (outformat->height); + else + param->rsz_rsc_param[index].v_dif = + ((informat->height >> 1) * 256) / (outformat->height); + param->rsz_rsc_param[index].h_dif = + ((informat->width) * 256) / (outformat->width); +} + +void +static resizer_enable_422_420_conversion(struct resizer_params *param, + int index, bool en) +{ + param->rsz_rsc_param[index].cen = en; + param->rsz_rsc_param[index].yen = en; +} + +/* resizer_calculate_sdram_offsets() - This function calculates the offsets from + * start of buffer for the C plane when + * output format is YUV420SP. It also + * calculates the offsets from the start of + * the buffer when the image is flipped + * vertically or horizontally for ycbcr/y/c + * planes. + * @resizer: Pointer to resizer subdevice. + * @index: index RSZ_A-resizer-A RSZ_B-resizer-B. + */ +static int +resizer_calculate_sdram_offsets(struct vpfe_resizer_device *resizer, int index) +{ + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *outformat; + int bytesperpixel = 2; + int image_height; + int image_width; + int yuv_420 = 0; + int offset = 0; + + if (index == RSZ_A) + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + else + outformat = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + image_height = outformat->height + 1; + image_width = outformat->width + 1; + param->ext_mem_param[index].c_offset = 0; + param->ext_mem_param[index].flip_ofst_y = 0; + param->ext_mem_param[index].flip_ofst_c = 0; + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) { + /* YUV 420 */ + yuv_420 = 1; + bytesperpixel = 1; + } + + if (param->rsz_rsc_param[index].h_flip) + /* width * bytesperpixel - 1 */ + offset = (image_width * bytesperpixel) - 1; + if (param->rsz_rsc_param[index].v_flip) + offset += (image_height - 1) * + param->ext_mem_param[index].rsz_sdr_oft_y; + param->ext_mem_param[index].flip_ofst_y = offset; + if (!yuv_420) + return 0; + offset = 0; + /* half height for c-plane */ + if (param->rsz_rsc_param[index].h_flip) + /* width * bytesperpixel - 1 */ + offset = image_width - 1; + if (param->rsz_rsc_param[index].v_flip) + offset += (((image_height >> 1) - 1) * + param->ext_mem_param[index].rsz_sdr_oft_c); + param->ext_mem_param[index].flip_ofst_c = offset; + param->ext_mem_param[index].c_offset = + param->ext_mem_param[index].rsz_sdr_oft_y * image_height; + return 0; +} + +int resizer_configure_output_win(struct vpfe_resizer_device *resizer) +{ + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_output_spec output_specs; + struct v4l2_mbus_framefmt *outformat; + int line_len_c; + int line_len; + int ret; + + outformat = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + + output_specs.vst_y = param->user_config.vst; + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + output_specs.vst_c = param->user_config.vst; + + configure_resizer_out_params(resizer, RSZ_A, &output_specs, 0, 0); + resizer_calculate_line_length(outformat->code, + param->rsz_rsc_param[0].o_hsz + 1, + param->rsz_rsc_param[0].o_vsz + 1, + &line_len, &line_len_c); + param->ext_mem_param[0].rsz_sdr_oft_y = line_len; + param->ext_mem_param[0].rsz_sdr_oft_c = line_len_c; + resizer_calculate_resize_ratios(resizer, RSZ_A); + if (param->rsz_en[RSZ_B]) + resizer_calculate_resize_ratios(resizer, RSZ_B); + + if (outformat->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, RSZ_A, ENABLE); + else + resizer_enable_422_420_conversion(param, RSZ_A, DISABLE); + + ret = resizer_calculate_sdram_offsets(resizer, RSZ_A); + if (!ret && param->rsz_en[RSZ_B]) + ret = resizer_calculate_sdram_offsets(resizer, RSZ_B); + + if (ret) + pr_err("Error in calculating sdram offsets\n"); + return ret; +} + +static int +resizer_calculate_down_scale_f_div_param(struct device *dev, + int input_width, int output_width, + struct resizer_scale_param *param) +{ + /* rsz = R, input_width = H, output width = h in the equation */ + unsigned int two_power; + unsigned int upper_h1; + unsigned int upper_h2; + unsigned int val1; + unsigned int val; + unsigned int rsz; + unsigned int h1; + unsigned int h2; + unsigned int o; + unsigned int n; + + upper_h1 = input_width >> 1; + n = param->h_dscale_ave_sz; + /* 2 ^ (scale+1) */ + two_power = 1 << (n + 1); + upper_h1 = (upper_h1 >> (n + 1)) << (n + 1); + upper_h2 = input_width - upper_h1; + if (upper_h2 % two_power) { + dev_err(dev, "frame halves to be a multiple of 2 power n+1\n"); + return -EINVAL; + } + two_power = 1 << n; + rsz = (input_width << 8) / output_width; + val = rsz * two_power; + val = ((upper_h1 << 8) / val) + 1; + if (!(val % 2)) { + h1 = val; + } else { + val = upper_h1 << 8; + val >>= n + 1; + val -= rsz >> 1; + val /= rsz << 1; + val <<= 1; + val += 2; + h1 = val; + } + o = 10 + (two_power << 2); + if (((input_width << 7) / rsz) % 2) + o += (((CEIL(rsz, 1024)) << 1) << n); + h2 = output_width - h1; + /* phi */ + val = (h1 * rsz) - (((upper_h1 - (o - 10)) / two_power) << 8); + /* skip */ + val1 = ((val - 1024) >> 9) << 1; + param->f_div.num_passes = MAX_PASSES; + param->f_div.pass[0].o_hsz = h1 - 1; + param->f_div.pass[0].i_hps = 0; + param->f_div.pass[0].h_phs = 0; + param->f_div.pass[0].src_hps = 0; + param->f_div.pass[0].src_hsz = upper_h1 + o; + param->f_div.pass[1].o_hsz = h2 - 1; + param->f_div.pass[1].i_hps = 10 + (val1 * two_power); + param->f_div.pass[1].h_phs = (val - (val1 << 8)); + param->f_div.pass[1].src_hps = upper_h1 - o; + param->f_div.pass[1].src_hsz = upper_h2 + o; + + return 0; +} + +static int +resizer_configure_common_in_params(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_config_params *user_config; + struct v4l2_mbus_framefmt *informat; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + user_config = &resizer->config.user_config; + param->rsz_common.vps = param->user_config.vst; + param->rsz_common.hps = param->user_config.hst; + + if (vpfe_ipipeif_decimation_enabled(vpfe_dev)) + param->rsz_common.hsz = (((informat->width - 1) * + IPIPEIF_RSZ_CONST) / vpfe_ipipeif_get_rsz(vpfe_dev)); + else + param->rsz_common.hsz = informat->width - 1; + + if (informat->field == V4L2_FIELD_INTERLACED) + param->rsz_common.vsz = (informat->height - 1) >> 1; + else + param->rsz_common.vsz = informat->height - 1; + + param->rsz_common.raw_flip = 0; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF) + param->rsz_common.source = IPIPEIF_DATA; + else + param->rsz_common.source = IPIPE_DATA; + + switch (informat->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + param->rsz_common.src_img_fmt = RSZ_IMG_422; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_Y8_1X8: + param->rsz_common.src_img_fmt = RSZ_IMG_420; + /* Select y */ + param->rsz_common.y_c = 0; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_UV8_1X8: + param->rsz_common.src_img_fmt = RSZ_IMG_420; + /* Select y */ + param->rsz_common.y_c = 1; + param->rsz_common.raw_flip = 0; + break; + + case V4L2_MBUS_FMT_SGRBG12_1X12: + param->rsz_common.raw_flip = 1; + break; + + default: + param->rsz_common.src_img_fmt = RSZ_IMG_422; + param->rsz_common.source = IPIPE_DATA; + } + + param->rsz_common.yuv_y_min = user_config->yuv_y_min; + param->rsz_common.yuv_y_max = user_config->yuv_y_max; + param->rsz_common.yuv_c_min = user_config->yuv_c_min; + param->rsz_common.yuv_c_max = user_config->yuv_c_max; + param->rsz_common.out_chr_pos = user_config->out_chr_pos; + param->rsz_common.rsz_seq_crv = user_config->chroma_sample_even; + + return 0; +} +static int +resizer_configure_in_continious_mode(struct vpfe_resizer_device *resizer) +{ + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct resizer_params *param = &resizer->config; + struct vpfe_rsz_config_params *cont_config; + int line_len_c; + int line_len; + int ret; + + if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) { + dev_err(dev, "enable resizer - Resizer-A\n"); + return -EINVAL; + } + + cont_config = &resizer->config.user_config; + param->rsz_en[RSZ_A] = ENABLE; + configure_resizer_out_params(resizer, RSZ_A, + &cont_config->output1, 1, 0); + param->rsz_en[RSZ_B] = DISABLE; + param->oper_mode = RESIZER_MODE_CONTINIOUS; + + if (resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) { + struct v4l2_mbus_framefmt *outformat2; + + param->rsz_en[RSZ_B] = ENABLE; + outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + ret = resizer_validate_output_image_format(dev, outformat2, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_B, + &cont_config->output2, 0, 1); + if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_B, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_B, DISABLE); + } + resizer_configure_common_in_params(resizer); + ret = resizer_configure_output_win(resizer); + if (ret) + return ret; + + param->rsz_common.passthrough = cont_config->bypass; + if (cont_config->bypass) + resizer_configure_passthru(resizer, 1); + + return 0; +} + +static inline int +resizer_validate_input_image_format(struct device *dev, + enum v4l2_mbus_pixelcode pix, + int width, int height, int *line_len) +{ + int val; + + if (pix != V4L2_MBUS_FMT_UYVY8_2X8 && + pix != V4L2_MBUS_FMT_Y8_1X8 && + pix != V4L2_MBUS_FMT_UV8_1X8 && + pix != V4L2_MBUS_FMT_SGRBG12_1X12) { + dev_err(dev, + "resizer validate output: pix format not supported, %d\n", pix); + return -EINVAL; + } + + if (!width || !height) { + dev_err(dev, + "resizer validate input: invalid width or height\n"); + return -EINVAL; + } + + if (pix == V4L2_MBUS_FMT_UV8_1X8) + resizer_calculate_line_length(pix, width, + height, &val, line_len); + else + resizer_calculate_line_length(pix, width, + height, line_len, &val); + + return 0; +} + +static int +resizer_validate_decimation(struct device *dev, enum ipipeif_decimation dec_en, + unsigned char rsz, unsigned char frame_div_mode_en, + int width) +{ + if (dec_en && frame_div_mode_en) { + dev_err(dev, + "dec_en & frame_div_mode_en can not enabled simultaneously\n"); + return -EINVAL; + } + + if (frame_div_mode_en) { + dev_err(dev, "frame_div_mode mode not supported\n"); + return -EINVAL; + } + + if (!dec_en) + return 0; + + if (width <= VPFE_IPIPE_MAX_INPUT_WIDTH) { + dev_err(dev, + "image width to be more than %d for decimation\n", + VPFE_IPIPE_MAX_INPUT_WIDTH); + return -EINVAL; + } + + if (rsz < IPIPEIF_RSZ_MIN || rsz > IPIPEIF_RSZ_MAX) { + dev_err(dev, "rsz range is %d to %d\n", + IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); + return -EINVAL; + } + + return 0; +} + +/* resizer_calculate_normal_f_div_param() - Algorithm to calculate the frame + * division parameters for resizer. + * in normal mode. + */ +static int +resizer_calculate_normal_f_div_param(struct device *dev, int input_width, + int output_width, struct resizer_scale_param *param) +{ + /* rsz = R, input_width = H, output width = h in the equation */ + unsigned int val1; + unsigned int rsz; + unsigned int val; + unsigned int h1; + unsigned int h2; + unsigned int o; + + if (output_width > input_width) { + dev_err(dev, "frame div mode is used for scale down only\n"); + return -EINVAL; + } + + rsz = (input_width << 8) / output_width; + val = rsz << 1; + val = ((input_width << 8) / val) + 1; + o = 14; + if (!(val % 2)) { + h1 = val; + } else { + val = (input_width << 7); + val -= rsz >> 1; + val /= rsz << 1; + val <<= 1; + val += 2; + o += ((CEIL(rsz, 1024)) << 1); + h1 = val; + } + h2 = output_width - h1; + /* phi */ + val = (h1 * rsz) - (((input_width >> 1) - o) << 8); + /* skip */ + val1 = ((val - 1024) >> 9) << 1; + param->f_div.num_passes = MAX_PASSES; + param->f_div.pass[0].o_hsz = h1 - 1; + param->f_div.pass[0].i_hps = 0; + param->f_div.pass[0].h_phs = 0; + param->f_div.pass[0].src_hps = 0; + param->f_div.pass[0].src_hsz = (input_width >> 2) + o; + param->f_div.pass[1].o_hsz = h2 - 1; + param->f_div.pass[1].i_hps = val1; + param->f_div.pass[1].h_phs = (val - (val1 << 8)); + param->f_div.pass[1].src_hps = (input_width >> 2) - o; + param->f_div.pass[1].src_hsz = (input_width >> 2) + o; + + return 0; +} + +static int +resizer_configure_in_single_shot_mode(struct vpfe_resizer_device *resizer) +{ + struct vpfe_rsz_config_params *config = &resizer->config.user_config; + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct v4l2_mbus_framefmt *outformat1, *outformat2; + struct resizer_params *param = &resizer->config; + struct v4l2_mbus_framefmt *informat; + int decimation; + int line_len_c; + int line_len; + int rsz; + int ret; + + informat = &resizer->crop_resizer.formats[RESIZER_CROP_PAD_SINK]; + outformat1 = &resizer->resizer_a.formats[RESIZER_PAD_SOURCE]; + outformat2 = &resizer->resizer_b.formats[RESIZER_PAD_SOURCE]; + + decimation = vpfe_ipipeif_decimation_enabled(vpfe_dev); + rsz = vpfe_ipipeif_get_rsz(vpfe_dev); + if (decimation && param->user_config.frame_div_mode_en) { + dev_err(dev, + "dec_en & frame_div_mode_en cannot enabled simultaneously\n"); + return -EINVAL; + } + + ret = resizer_validate_decimation(dev, decimation, rsz, + param->user_config.frame_div_mode_en, informat->width); + if (ret) + return -EINVAL; + + ret = resizer_validate_input_image_format(dev, informat->code, + informat->width, informat->height, &line_len); + if (ret) + return -EINVAL; + + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + param->rsz_en[RSZ_A] = ENABLE; + ret = resizer_validate_output_image_format(dev, outformat1, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_A].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_A].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_A, + ¶m->user_config.output1, 0, 1); + + if (outformat1->code == V4L2_MBUS_FMT_SGRBG12_1X12) + param->rsz_common.raw_flip = 1; + else + param->rsz_common.raw_flip = 0; + + if (outformat1->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_A, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_A, DISABLE); + } + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + param->rsz_en[RSZ_B] = ENABLE; + ret = resizer_validate_output_image_format(dev, outformat2, + &line_len, &line_len_c); + if (ret) + return ret; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(resizer, RSZ_B, + ¶m->user_config.output2, 0, 1); + if (outformat2->code == V4L2_MBUS_FMT_YDYUYDYV8_1X16) + resizer_enable_422_420_conversion(param, + RSZ_B, ENABLE); + else + resizer_enable_422_420_conversion(param, + RSZ_B, DISABLE); + } + + resizer_configure_common_in_params(resizer); + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + resizer_calculate_resize_ratios(resizer, RSZ_A); + resizer_calculate_sdram_offsets(resizer, RSZ_A); + /* Overriding resize ratio calculation */ + if (informat->code == V4L2_MBUS_FMT_UV8_1X8) { + param->rsz_rsc_param[RSZ_A].v_dif = + (((informat->height + 1) * 2) * 256) / + (param->rsz_rsc_param[RSZ_A].o_vsz + 1); + } + } + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + resizer_calculate_resize_ratios(resizer, RSZ_B); + resizer_calculate_sdram_offsets(resizer, RSZ_B); + /* Overriding resize ratio calculation */ + if (informat->code == V4L2_MBUS_FMT_UV8_1X8) { + param->rsz_rsc_param[RSZ_B].v_dif = + (((informat->height + 1) * 2) * 256) / + (param->rsz_rsc_param[RSZ_B].o_vsz + 1); + } + } + if (param->user_config.frame_div_mode_en && + param->rsz_en[RSZ_A]) { + if (!param->rsz_rsc_param[RSZ_A].dscale_en) + ret = resizer_calculate_normal_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_A].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_A]); + else + ret = resizer_calculate_down_scale_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_A].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_A]); + if (ret) + return -EINVAL; + } + if (param->user_config.frame_div_mode_en && + param->rsz_en[RSZ_B]) { + if (!param->rsz_rsc_param[RSZ_B].dscale_en) + ret = resizer_calculate_normal_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_B].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_B]); + else + ret = resizer_calculate_down_scale_f_div_param(dev, + informat->width, + param->rsz_rsc_param[RSZ_B].o_vsz + 1, + ¶m->rsz_rsc_param[RSZ_B]); + if (ret) + return -EINVAL; + } + param->rsz_common.passthrough = config->bypass; + if (config->bypass) + resizer_configure_passthru(resizer, 1); + return 0; +} + +static void +resizer_set_defualt_configuration(struct vpfe_resizer_device *resizer) +{ +#define WIDTH_I 640 +#define HEIGHT_I 480 +#define WIDTH_O 640 +#define HEIGHT_O 480 + const struct resizer_params rsz_default_config = { + .oper_mode = RESIZER_MODE_ONE_SHOT, + .rsz_common = { + .vsz = HEIGHT_I - 1, + .hsz = WIDTH_I - 1, + .src_img_fmt = RSZ_IMG_422, + .raw_flip = 1, /* flip preserve Raw format */ + .source = IPIPE_DATA, + .passthrough = BYPASS_OFF, + .yuv_y_max = 255, + .yuv_c_max = 255, + .rsz_seq_crv = DISABLE, + .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE, + }, + .rsz_rsc_param = { + { + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + { + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + }, + .rsz2rgb = { + { + .rgb_en = DISABLE + }, + { + .rgb_en = DISABLE + } + }, + .ext_mem_param = { + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O >> 1, + }, + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O, + }, + }, + .rsz_en[0] = ENABLE, + .rsz_en[1] = DISABLE, + .user_config = { + .output1 = { + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .v_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + .output2 = { + .v_typ_y = VPFE_RSZ_INTP_CUBIC, + .v_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_typ_y = VPFE_RSZ_INTP_CUBIC, + .h_typ_c = VPFE_RSZ_INTP_CUBIC, + .h_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = + VPFE_IPIPE_DWN_SCALE_1_OVER_2, + }, + .yuv_y_max = 255, + .yuv_c_max = 255, + .out_chr_pos = VPFE_IPIPE_YUV422_CHR_POS_COSITE, + }, + }; + memset(&resizer->config, 0, sizeof(struct resizer_params)); + memcpy(&resizer->config, &rsz_default_config, + sizeof(struct resizer_params)); +} + +/* + * resizer_set_configuration() - set resizer config + * @resizer: vpfe resizer device pointer. + * @chan_config: resizer channel configuration. + */ +static int +resizer_set_configuration(struct vpfe_resizer_device *resizer, + struct vpfe_rsz_config *chan_config) +{ + if (!chan_config->config) + resizer_set_defualt_configuration(resizer); + else + if (copy_from_user(&resizer->config.user_config, + chan_config->config, sizeof(struct vpfe_rsz_config_params))) + return -EFAULT; + + return 0; +} + +/* + * resizer_get_configuration() - get resizer config + * @resizer: vpfe resizer device pointer. + * @channel: image processor logical channel. + * @chan_config: resizer channel configuration. + */ +static int +resizer_get_configuration(struct vpfe_resizer_device *resizer, + struct vpfe_rsz_config *chan_config) +{ + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + + if (!chan_config->config) { + dev_err(dev, "Resizer channel invalid pointer\n"); + return -EINVAL; + } + + if (copy_to_user((void *)chan_config->config, + (void *)&resizer->config.user_config, + sizeof(struct vpfe_rsz_config_params))) { + dev_err(dev, "resizer_get_configuration: Error in copy to user\n"); + return -EFAULT; + } + + return 0; +} + +/* + * VPFE video operations + */ + +/* + * resizer_a_video_out_queue() - RESIZER-A video out queue + * @vpfe_dev: vpfe device pointer. + * @addr: buffer address. + */ +static int resizer_a_video_out_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + + return resizer_set_outaddr(resizer->base_addr, + &resizer->config, RSZ_A, addr); +} + +/* + * resizer_b_video_out_queue() - RESIZER-B video out queue + * @vpfe_dev: vpfe device pointer. + * @addr: buffer address. + */ +static int resizer_b_video_out_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + + return resizer_set_outaddr(resizer->base_addr, + &resizer->config, RSZ_B, addr); +} + +static const struct vpfe_video_operations resizer_a_video_ops = { + .queue = resizer_a_video_out_queue, +}; + +static const struct vpfe_video_operations resizer_b_video_ops = { + .queue = resizer_b_video_out_queue, +}; + +static void resizer_enable(struct vpfe_resizer_device *resizer, int en) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + unsigned char val; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE) + return; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_IPIPEIF && + ipipeif_sink == IPIPEIF_INPUT_MEMORY) { + do { + val = regr_rsz(resizer->base_addr, RSZ_SRC_EN); + } while (val); + + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) { + do { + val = regr_rsz(resizer->base_addr, RSZ_A); + } while (val); + } + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) { + do { + val = regr_rsz(resizer->base_addr, RSZ_B); + } while (val); + } + } + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) + rsz_enable(resizer->base_addr, RSZ_A, en); + + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) + rsz_enable(resizer->base_addr, RSZ_B, en); +} + + +/* + * resizer_ss_isr() - resizer module single-shot buffer scheduling isr + * @resizer: vpfe resizer device pointer. + */ +static void resizer_ss_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &video_out->pipe; + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + u32 val; + + if (ipipeif_sink != IPIPEIF_INPUT_MEMORY) + return; + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + val = vpss_dma_complete_interrupt(); + if (val != 0 && val != 2) + return; + } + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_video_process_buffer_complete(video_out); + video_out->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + } + + /* If resizer B is enabled */ + if (pipe->output_num > 1 && resizer->resizer_b.output == + RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_video_process_buffer_complete(video_out2); + video_out2->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_video_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } + + /* start HW if buffers are queued */ + if (vpfe_video_is_pipe_ready(pipe) && + resizer->resizer_a.output == RESIZER_OUPUT_MEMORY) { + resizer_enable(resizer, 1); + vpfe_ipipe_enable(vpfe_dev, 1); + vpfe_ipipeif_enable(vpfe_dev); + } +} + +/* + * vpfe_resizer_buffer_isr() - resizer module buffer scheduling isr + * @resizer: vpfe resizer device pointer. + */ +void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_pipeline *pipe = &resizer->resizer_a.video_out.pipe; + enum v4l2_field field; + int fid; + + if (!video_out->started) + return; + + if (resizer->crop_resizer.input == RESIZER_CROP_INPUT_NONE) + return; + + field = video_out->fmt.fmt.pix.field; + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (video_out->cur_frm != video_out->next_frm) { + vpfe_video_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_video_process_buffer_complete(video_out2); + } + + video_out->skip_frame_count--; + if (!video_out->skip_frame_count) { + video_out->skip_frame_count = + video_out->skip_frame_count_init; + rsz_src_enable(resizer->base_addr, 1); + } else { + rsz_src_enable(resizer->base_addr, 0); + } + return; + } + + /* handle interlaced frame capture */ + fid = vpfe_isif_get_fid(vpfe_dev); + + /* switch the software maintained field id */ + video_out->field_id ^= 1; + if (fid == video_out->field_id) { + /* + * we are in-sync here,continue. + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (fid == 0 && video_out->cur_frm != video_out->next_frm) { + vpfe_video_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_video_process_buffer_complete(video_out2); + } + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video_out->field_id = fid; + } +} + +/* + * vpfe_resizer_dma_isr() - resizer module dma isr + * @resizer: vpfe resizer device pointer. + */ +void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out2 = &resizer->resizer_b.video_out; + struct vpfe_video_device *video_out = &resizer->resizer_a.video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &video_out->pipe; + int schedule_capture = 0; + enum v4l2_field field; + int fid; + + if (!video_out->started) + return; + + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) { + resizer_ss_isr(resizer); + return; + } + + field = video_out->fmt.fmt.pix.field; + if (field == V4L2_FIELD_NONE) { + if (!list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } else { + fid = vpfe_isif_get_fid(vpfe_dev); + if (fid == video_out->field_id) { + /* we are in-sync here,continue */ + if (fid == 1 && !list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } + } + + if (!schedule_capture) + return; + + spin_lock(&video_out->dma_queue_lock); + vpfe_video_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + if (pipe->output_num > 1) { + spin_lock(&video_out2->dma_queue_lock); + vpfe_video_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } +} + +/* + * V4L2 subdev operations + */ + +/* + * resizer_ioctl() - Handle resizer module private ioctl's + * @sd: pointer to v4l2 subdev structure + * @cmd: configuration command + * @arg: configuration argument + */ +static long resizer_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct device *dev = resizer->crop_resizer.subdev.v4l2_dev->dev; + struct vpfe_rsz_config *user_config; + int ret = -ENOIOCTLCMD; + + if (&resizer->crop_resizer.subdev != sd) + return ret; + + switch (cmd) { + case VIDIOC_VPFE_RSZ_S_CONFIG: + user_config = (struct vpfe_rsz_config *)arg; + ret = resizer_set_configuration(resizer, user_config); + break; + + case VIDIOC_VPFE_RSZ_G_CONFIG: + user_config = (struct vpfe_rsz_config *)arg; + if (!user_config->config) { + dev_err(dev, "error in VIDIOC_VPFE_RSZ_G_CONFIG\n"); + return -EINVAL; + } + ret = resizer_get_configuration(resizer, user_config); + break; + } + return ret; +} + +static int resizer_do_hw_setup(struct vpfe_resizer_device *resizer) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_sink = vpfe_dev->vpfe_ipipeif.input; + u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; + struct resizer_params *param = &resizer->config; + int ret = 0; + + if (resizer->resizer_a.output == RESIZER_OUPUT_MEMORY || + resizer->resizer_b.output == RESIZER_OUPUT_MEMORY) { + if (ipipeif_sink == IPIPEIF_INPUT_MEMORY && + ipipeif_source == IPIPEIF_OUTPUT_RESIZER) + ret = resizer_configure_in_single_shot_mode(resizer); + else + ret = resizer_configure_in_continious_mode(resizer); + if (ret) + return ret; + ret = config_rsz_hw(resizer, param); + } + return ret; +} + +/* + * resizer_set_stream() - Enable/Disable streaming on resizer subdev + * @sd: pointer to v4l2 subdev structure + * @enable: 1 == Enable, 0 == Disable + */ +static int resizer_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + + if (&resizer->crop_resizer.subdev != sd) + return 0; + + if (resizer->resizer_a.output != RESIZER_OUPUT_MEMORY) + return 0; + + switch (enable) { + case 1: + if (resizer_do_hw_setup(resizer) < 0) + return -EINVAL; + resizer_enable(resizer, enable); + break; + + case 0: + resizer_enable(resizer, enable); + break; + } + + return 0; +} + +/* + * __resizer_get_format() - helper function for getting resizer format + * @sd: pointer to subdev. + * @fh: V4L2 subdev file handle. + * @pad: pad number. + * @which: wanted subdev format. + * Retun wanted mbus frame format. + */ +static struct v4l2_mbus_framefmt * +__resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + if (&resizer->crop_resizer.subdev == sd) + return &resizer->crop_resizer.formats[pad]; + if (&resizer->resizer_a.subdev == sd) + return &resizer->resizer_a.formats[pad]; + if (&resizer->resizer_b.subdev == sd) + return &resizer->resizer_b.formats[pad]; + return NULL; +} + +/* + * resizer_try_format() - Handle try format by pad subdev method + * @sd: pointer to subdev. + * @fh: V4L2 subdev file handle. + * @pad: pad num. + * @fmt: pointer to v4l2 format structure. + * @which: wanted subdev format. + */ +static void +resizer_try_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + if ((&resizer->resizer_a.subdev == sd && pad == RESIZER_PAD_SINK) || + (&resizer->resizer_b.subdev == sd && pad == RESIZER_PAD_SINK) || + (&resizer->crop_resizer.subdev == sd && + (pad == RESIZER_CROP_PAD_SOURCE || + pad == RESIZER_CROP_PAD_SOURCE2 || pad == RESIZER_CROP_PAD_SINK))) { + for (i = 0; i < ARRAY_SIZE(resizer_input_formats); i++) { + if (fmt->code == resizer_input_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_input_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, + MAX_IN_WIDTH); + fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, + MAX_IN_HEIGHT); + } else if (&resizer->resizer_a.subdev == sd && + pad == RESIZER_PAD_SOURCE) { + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_A; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_A; + + for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) { + if (fmt->code == resizer_output_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_output_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } else if (&resizer->resizer_b.subdev == sd && + pad == RESIZER_PAD_SOURCE) { + max_out_width = IPIPE_MAX_OUTPUT_WIDTH_B; + max_out_height = IPIPE_MAX_OUTPUT_HEIGHT_B; + + for (i = 0; i < ARRAY_SIZE(resizer_output_formats); i++) { + if (fmt->code == resizer_output_formats[i]) + break; + } + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resizer_output_formats)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } +} + +/* + * resizer_set_format() - Handle set format by pads subdev method + * @sd: pointer to v4l2 subdev structure + * @fh: V4L2 subdev file handle + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + resizer_try_format(sd, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (&resizer->crop_resizer.subdev == sd) { + if (fmt->pad == RESIZER_CROP_PAD_SINK) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE && + resizer->crop_resizer.output == RESIZER_A) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + resizer->crop_resizer. + formats[RESIZER_CROP_PAD_SOURCE2] = fmt->format; + } else if (fmt->pad == RESIZER_CROP_PAD_SOURCE2 && + resizer->crop_resizer.output2 == RESIZER_B) { + resizer->crop_resizer.formats[fmt->pad] = fmt->format; + resizer->crop_resizer. + formats[RESIZER_CROP_PAD_SOURCE] = fmt->format; + } else { + return -EINVAL; + } + } else if (&resizer->resizer_a.subdev == sd) { + if (fmt->pad == RESIZER_PAD_SINK) + resizer->resizer_a.formats[fmt->pad] = fmt->format; + else if (fmt->pad == RESIZER_PAD_SOURCE) + resizer->resizer_a.formats[fmt->pad] = fmt->format; + else + return -EINVAL; + } else if (&resizer->resizer_b.subdev == sd) { + if (fmt->pad == RESIZER_PAD_SINK) + resizer->resizer_b.formats[fmt->pad] = fmt->format; + else if (fmt->pad == RESIZER_PAD_SOURCE) + resizer->resizer_b.formats[fmt->pad] = fmt->format; + else + return -EINVAL; + } else { + return -EINVAL; + } + + return 0; +} + +/* + * resizer_get_format() - Retrieve the video format on a pad + * @sd: pointer to v4l2 subdev structure. + * @fh: V4L2 subdev file handle. + * @fmt: pointer to v4l2 subdev format structure + * return -EINVAL or zero on success + */ +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(sd, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +/* + * resizer_enum_frame_size() - enum frame sizes on pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle. + * @code: pointer to v4l2_subdev_frame_size_enum structure. + */ +static int resizer_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + resizer_try_format(sd, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + resizer_try_format(sd, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * resizer_enum_mbus_code() - enum mbus codes for pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int resizer_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad == RESIZER_PAD_SINK) { + if (code->index >= ARRAY_SIZE(resizer_input_formats)) + return -EINVAL; + + code->code = resizer_input_formats[code->index]; + } else if (code->pad == RESIZER_PAD_SOURCE) { + if (code->index >= ARRAY_SIZE(resizer_output_formats)) + return -EINVAL; + + code->code = resizer_output_formats[code->index]; + } + + return 0; +} + +/* + * resizer_init_formats() - Initialize formats on all pads + * @sd: Pointer to subdevice. + * @fh: V4L2 subdev file handle. + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int resizer_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + __u32 which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + if (&resizer->crop_resizer.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_WIDTH; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_CROP_PAD_SOURCE2; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_WIDTH; + resizer_set_format(sd, fh, &format); + } else if (&resizer->resizer_a.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_A; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_A; + resizer_set_format(sd, fh, &format); + } else if (&resizer->resizer_b.subdev == sd) { + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SINK; + format.which = which; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SOURCE; + format.which = which; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = IPIPE_MAX_OUTPUT_WIDTH_B; + format.format.height = IPIPE_MAX_OUTPUT_HEIGHT_B; + resizer_set_format(sd, fh, &format); + } + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops resizer_v4l2_core_ops = { + .ioctl = resizer_ioctl, +}; + +/* subdev internal operations */ +static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = { + .open = resizer_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { + .s_stream = resizer_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { + .enum_mbus_code = resizer_enum_mbus_code, + .enum_frame_size = resizer_enum_frame_size, + .get_fmt = resizer_get_format, + .set_fmt = resizer_set_format, +}; + +/* subdev operations */ +static const struct v4l2_subdev_ops resizer_v4l2_ops = { + .core = &resizer_v4l2_core_ops, + .video = &resizer_v4l2_video_ops, + .pad = &resizer_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * resizer_link_setup() - Setup resizer connections + * @entity: Pointer to media entity structure + * @local: Pointer to local pad array + * @remote: Pointer to remote pad array + * @flags: Link flags + * return -EINVAL or zero on success + */ +static int resizer_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + u16 ipipeif_source = vpfe_dev->vpfe_ipipeif.output; + u16 ipipe_source = vpfe_dev->vpfe_ipipe.output; + + if (&resizer->crop_resizer.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_CROP_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_NONE; + break; + } + + if (resizer->crop_resizer.input != + RESIZER_CROP_INPUT_NONE) + return -EBUSY; + if (ipipeif_source == IPIPEIF_OUTPUT_RESIZER) + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_IPIPEIF; + else if (ipipe_source == IPIPE_OUTPUT_RESIZER) + resizer->crop_resizer.input = + RESIZER_CROP_INPUT_IPIPE; + else + return -EINVAL; + break; + + case RESIZER_CROP_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.output = + RESIZER_CROP_OUTPUT_NONE; + break; + } + if (resizer->crop_resizer.output != + RESIZER_CROP_OUTPUT_NONE) + return -EBUSY; + resizer->crop_resizer.output = RESIZER_A; + break; + + case RESIZER_CROP_PAD_SOURCE2 | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->crop_resizer.output2 = + RESIZER_CROP_OUTPUT_NONE; + break; + } + if (resizer->crop_resizer.output2 != + RESIZER_CROP_OUTPUT_NONE) + return -EBUSY; + resizer->crop_resizer.output2 = RESIZER_B; + break; + + default: + return -EINVAL; + } + } else if (&resizer->resizer_a.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_a.input = RESIZER_INPUT_NONE; + break; + } + if (resizer->resizer_a.input != RESIZER_INPUT_NONE) + return -EBUSY; + resizer->resizer_a.input = RESIZER_INPUT_CROP_RESIZER; + break; + + case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_a.output = RESIZER_OUTPUT_NONE; + break; + } + if (resizer->resizer_a.output != RESIZER_OUTPUT_NONE) + return -EBUSY; + resizer->resizer_a.output = RESIZER_OUPUT_MEMORY; + break; + + default: + return -EINVAL; + } + } else if (&resizer->resizer_b.subdev == sd) { + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_b.input = RESIZER_INPUT_NONE; + break; + } + if (resizer->resizer_b.input != RESIZER_INPUT_NONE) + return -EBUSY; + resizer->resizer_b.input = RESIZER_INPUT_CROP_RESIZER; + break; + + case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + resizer->resizer_b.output = RESIZER_OUTPUT_NONE; + break; + } + if (resizer->resizer_b.output != RESIZER_OUTPUT_NONE) + return -EBUSY; + resizer->resizer_b.output = RESIZER_OUPUT_MEMORY; + break; + + default: + return -EINVAL; + } + } else { + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations resizer_media_ops = { + .link_setup = resizer_link_setup, +}; + +/* + * vpfe_resizer_unregister_entities() - Unregister entity + * @vpfe_rsz - pointer to resizer subdevice structure. + */ +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz) +{ + /* unregister video devices */ + vpfe_video_unregister(&vpfe_rsz->resizer_a.video_out); + vpfe_video_unregister(&vpfe_rsz->resizer_b.video_out); + + /* cleanup entity */ + media_entity_cleanup(&vpfe_rsz->crop_resizer.subdev.entity); + media_entity_cleanup(&vpfe_rsz->resizer_a.subdev.entity); + media_entity_cleanup(&vpfe_rsz->resizer_b.subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&vpfe_rsz->crop_resizer.subdev); + v4l2_device_unregister_subdev(&vpfe_rsz->resizer_a.subdev); + v4l2_device_unregister_subdev(&vpfe_rsz->resizer_b.subdev); +} + +/* + * vpfe_resizer_register_entities() - Register entity + * @resizer - pointer to resizer devive. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + unsigned int flags = 0; + int ret; + + /* Register the crop resizer subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->crop_resizer.subdev); + if (ret < 0) { + pr_err("Failed to register crop resizer as v4l2-subdev\n"); + return ret; + } + /* Register Resizer-A subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->resizer_a.subdev); + if (ret < 0) { + pr_err("Failed to register resizer-a as v4l2-subdev\n"); + return ret; + } + /* Register Resizer-B subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->resizer_b.subdev); + if (ret < 0) { + pr_err("Failed to register resizer-b as v4l2-subdev\n"); + return ret; + } + /* Register video-out device for resizer-a */ + ret = vpfe_video_register(&resizer->resizer_a.video_out, vdev); + if (ret) { + pr_err("Failed to register RSZ-A video-out device\n"); + goto out_video_out2_register; + } + resizer->resizer_a.video_out.vpfe_dev = vpfe_dev; + + /* Register video-out device for resizer-b */ + ret = vpfe_video_register(&resizer->resizer_b.video_out, vdev); + if (ret) { + pr_err("Failed to register RSZ-B video-out device\n"); + goto out_video_out2_register; + } + resizer->resizer_b.video_out.vpfe_dev = vpfe_dev; + + /* create link between Resizer Crop----> Resizer A*/ + ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 1, + &resizer->resizer_a.subdev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer Crop----> Resizer B*/ + ret = media_entity_create_link(&resizer->crop_resizer.subdev.entity, 2, + &resizer->resizer_b.subdev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer A ----> video out */ + ret = media_entity_create_link(&resizer->resizer_a.subdev.entity, 1, + &resizer->resizer_a.video_out.video_dev.entity, 0, flags); + if (ret < 0) + goto out_create_link; + + /* create link between Resizer B ----> video out */ + ret = media_entity_create_link(&resizer->resizer_b.subdev.entity, 1, + &resizer->resizer_b.video_out.video_dev.entity, 0, flags); + if (ret < 0) + goto out_create_link; + + return 0; + +out_create_link: + vpfe_video_unregister(&resizer->resizer_b.video_out); +out_video_out2_register: + vpfe_video_unregister(&resizer->resizer_a.video_out); + media_entity_cleanup(&resizer->crop_resizer.subdev.entity); + media_entity_cleanup(&resizer->resizer_a.subdev.entity); + media_entity_cleanup(&resizer->resizer_b.subdev.entity); + v4l2_device_unregister_subdev(&resizer->crop_resizer.subdev); + v4l2_device_unregister_subdev(&resizer->resizer_a.subdev); + v4l2_device_unregister_subdev(&resizer->resizer_b.subdev); + return ret; +} + +/* + * vpfe_resizer_init() - resizer device initialization. + * @vpfe_rsz - pointer to resizer device + * @pdev: platform device pointer. + */ +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &vpfe_rsz->crop_resizer.subdev; + struct media_pad *pads = &vpfe_rsz->crop_resizer.pads[0]; + struct media_entity *me = &sd->entity; + static resource_size_t res_len; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 5); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + vpfe_rsz->base_addr = ioremap_nocache(res->start, res_len); + if (!vpfe_rsz->base_addr) + return -EBUSY; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER CROP", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_CROP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_CROP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + pads[RESIZER_CROP_PAD_SOURCE2].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->crop_resizer.input = RESIZER_CROP_INPUT_NONE; + vpfe_rsz->crop_resizer.output = RESIZER_CROP_OUTPUT_NONE; + vpfe_rsz->crop_resizer.output2 = RESIZER_CROP_OUTPUT_NONE; + vpfe_rsz->crop_resizer.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_CROP_PADS_NUM, pads, 0); + if (ret) + return ret; + + sd = &vpfe_rsz->resizer_a.subdev; + pads = &vpfe_rsz->resizer_a.pads[0]; + me = &sd->entity; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER A", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->resizer_a.input = RESIZER_INPUT_NONE; + vpfe_rsz->resizer_a.output = RESIZER_OUTPUT_NONE; + vpfe_rsz->resizer_a.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + if (ret) + return ret; + + sd = &vpfe_rsz->resizer_b.subdev; + pads = &vpfe_rsz->resizer_b.pads[0]; + me = &sd->entity; + + v4l2_subdev_init(sd, &resizer_v4l2_ops); + sd->internal_ops = &resizer_v4l2_internal_ops; + strlcpy(sd->name, "DAVINCI RESIZER B", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, vpfe_rsz); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + vpfe_rsz->resizer_b.input = RESIZER_INPUT_NONE; + vpfe_rsz->resizer_b.output = RESIZER_OUTPUT_NONE; + vpfe_rsz->resizer_b.rsz_device = vpfe_rsz; + me->ops = &resizer_media_ops; + ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + if (ret) + return ret; + + vpfe_rsz->resizer_a.video_out.ops = &resizer_a_video_ops; + vpfe_rsz->resizer_a.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->resizer_a.video_out, "RSZ-A"); + if (ret) { + pr_err("Failed to init RSZ video-out device\n"); + return ret; + } + vpfe_rsz->resizer_b.video_out.ops = &resizer_b_video_ops; + vpfe_rsz->resizer_b.video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->resizer_b.video_out, "RSZ-B"); + if (ret) { + pr_err("Failed to init RSZ video-out2 device\n"); + return ret; + } + memset(&vpfe_rsz->config, 0, sizeof(struct resizer_params)); + + return 0; +} + +void +vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev) +{ + struct resource *res; + + iounmap(vpfe_rsz->base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 5); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); +} diff --git a/drivers/staging/media/davinci_vpfe/dm365_resizer.h b/drivers/staging/media/davinci_vpfe/dm365_resizer.h new file mode 100644 index 000000000000..59a79422b914 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/dm365_resizer.h @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2012 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Prabhakar Lad + */ + +#ifndef _DAVINCI_VPFE_DM365_RESIZER_H +#define _DAVINCI_VPFE_DM365_RESIZER_H + +enum resizer_oper_mode { + RESIZER_MODE_CONTINIOUS = 0, + RESIZER_MODE_ONE_SHOT = 1, +}; + +struct f_div_pass { + unsigned int o_hsz; + unsigned int i_hps; + unsigned int h_phs; + unsigned int src_hps; + unsigned int src_hsz; +}; + +#define MAX_PASSES 2 + +struct f_div_param { + unsigned char en; + unsigned int num_passes; + struct f_div_pass pass[MAX_PASSES]; +}; + +/* Resizer Rescale Parameters*/ +struct resizer_scale_param { + bool h_flip; + bool v_flip; + bool cen; + bool yen; + unsigned short i_vps; + unsigned short i_hps; + unsigned short o_vsz; + unsigned short o_hsz; + unsigned short v_phs_y; + unsigned short v_phs_c; + unsigned short v_dif; + /* resize method - Luminance */ + enum vpfe_rsz_intp_t v_typ_y; + /* resize method - Chrominance */ + enum vpfe_rsz_intp_t v_typ_c; + /* vertical lpf intensity - Luminance */ + unsigned char v_lpf_int_y; + /* vertical lpf intensity - Chrominance */ + unsigned char v_lpf_int_c; + unsigned short h_phs; + unsigned short h_dif; + /* resize method - Luminance */ + enum vpfe_rsz_intp_t h_typ_y; + /* resize method - Chrominance */ + enum vpfe_rsz_intp_t h_typ_c; + /* horizontal lpf intensity - Luminance */ + unsigned char h_lpf_int_y; + /* horizontal lpf intensity - Chrominance */ + unsigned char h_lpf_int_c; + bool dscale_en; + enum vpfe_rsz_down_scale_ave_sz h_dscale_ave_sz; + enum vpfe_rsz_down_scale_ave_sz v_dscale_ave_sz; + /* store the calculated frame division parameter */ + struct f_div_param f_div; +}; + +enum resizer_rgb_t { + OUTPUT_32BIT, + OUTPUT_16BIT +}; + +enum resizer_rgb_msk_t { + NOMASK = 0, + MASKLAST2 = 1, +}; + +/* Resizer RGB Conversion Parameters */ +struct resizer_rgb { + bool rgb_en; + enum resizer_rgb_t rgb_typ; + enum resizer_rgb_msk_t rgb_msk0; + enum resizer_rgb_msk_t rgb_msk1; + unsigned int rgb_alpha_val; +}; + +/* Resizer External Memory Parameters */ +struct rsz_ext_mem_param { + unsigned int rsz_sdr_oft_y; + unsigned int rsz_sdr_ptr_s_y; + unsigned int rsz_sdr_ptr_e_y; + unsigned int rsz_sdr_oft_c; + unsigned int rsz_sdr_ptr_s_c; + unsigned int rsz_sdr_ptr_e_c; + /* offset to be added to buffer start when flipping for y/ycbcr */ + unsigned int flip_ofst_y; + /* offset to be added to buffer start when flipping for c */ + unsigned int flip_ofst_c; + /* c offset for YUV 420SP */ + unsigned int c_offset; + /* User Defined Y offset for YUV 420SP or YUV420ILE data */ + unsigned int user_y_ofst; + /* User Defined C offset for YUV 420SP data */ + unsigned int user_c_ofst; +}; + +enum rsz_data_source { + IPIPE_DATA, + IPIPEIF_DATA +}; + +enum rsz_src_img_fmt { + RSZ_IMG_422, + RSZ_IMG_420 +}; + +enum rsz_dpaths_bypass_t { + BYPASS_OFF = 0, + BYPASS_ON = 1, +}; + +struct rsz_common_params { + unsigned int vps; + unsigned int vsz; + unsigned int hps; + unsigned int hsz; + /* 420 or 422 */ + enum rsz_src_img_fmt src_img_fmt; + /* Y or C when src_fmt is 420, 0 - y, 1 - c */ + unsigned char y_c; + /* flip raw or ycbcr */ + unsigned char raw_flip; + /* IPIPE or IPIPEIF data */ + enum rsz_data_source source; + enum rsz_dpaths_bypass_t passthrough; + unsigned char yuv_y_min; + unsigned char yuv_y_max; + unsigned char yuv_c_min; + unsigned char yuv_c_max; + bool rsz_seq_crv; + enum vpfe_chr_pos out_chr_pos; +}; + +struct resizer_params { + enum resizer_oper_mode oper_mode; + struct rsz_common_params rsz_common; + struct resizer_scale_param rsz_rsc_param[2]; + struct resizer_rgb rsz2rgb[2]; + struct rsz_ext_mem_param ext_mem_param[2]; + bool rsz_en[2]; + struct vpfe_rsz_config_params user_config; +}; + +#define ENABLE 1 +#define DISABLE (!ENABLE) + +#define RESIZER_CROP_PAD_SINK 0 +#define RESIZER_CROP_PAD_SOURCE 1 +#define RESIZER_CROP_PAD_SOURCE2 2 + +#define RESIZER_CROP_PADS_NUM 3 + +enum resizer_crop_input_entity { + RESIZER_CROP_INPUT_NONE = 0, + RESIZER_CROP_INPUT_IPIPEIF = 1, + RESIZER_CROP_INPUT_IPIPE = 2, +}; + +enum resizer_crop_output_entity { + RESIZER_CROP_OUTPUT_NONE, + RESIZER_A, + RESIZER_B, +}; + +struct dm365_crop_resizer_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESIZER_CROP_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESIZER_CROP_PADS_NUM]; + enum resizer_crop_input_entity input; + enum resizer_crop_output_entity output; + enum resizer_crop_output_entity output2; + struct vpfe_resizer_device *rsz_device; +}; + +#define RESIZER_PAD_SINK 0 +#define RESIZER_PAD_SOURCE 1 + +#define RESIZER_PADS_NUM 2 + +enum resizer_input_entity { + RESIZER_INPUT_NONE = 0, + RESIZER_INPUT_CROP_RESIZER = 1, +}; + +enum resizer_output_entity { + RESIZER_OUTPUT_NONE = 0, + RESIZER_OUPUT_MEMORY = 1, +}; + +struct dm365_resizer_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESIZER_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESIZER_PADS_NUM]; + enum resizer_input_entity input; + enum resizer_output_entity output; + struct vpfe_video_device video_out; + struct vpfe_resizer_device *rsz_device; +}; + +struct vpfe_resizer_device { + struct dm365_crop_resizer_device crop_resizer; + struct dm365_resizer_device resizer_a; + struct dm365_resizer_device resizer_b; + struct resizer_params config; + void *__iomem base_addr; +}; + +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev); +int vpfe_resizer_register_entities(struct vpfe_resizer_device *vpfe_rsz, + struct v4l2_device *v4l2_dev); +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz); +void vpfe_resizer_cleanup(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev); +void vpfe_resizer_buffer_isr(struct vpfe_resizer_device *resizer); +void vpfe_resizer_dma_isr(struct vpfe_resizer_device *resizer); + +#endif /* _DAVINCI_VPFE_DM365_RESIZER_H */ From 44261e38059385ca1cd9c5018c579b4fc48b5a52 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:26:17 -0300 Subject: [PATCH 0299/4607] [media] davinci: vpfe: dm365: add build infrastructure for capture driver add build infrastructure for dm365 specific modules for VPFE capture driver. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/Kconfig | 2 ++ drivers/staging/media/Makefile | 1 + drivers/staging/media/davinci_vpfe/Kconfig | 9 +++++++++ drivers/staging/media/davinci_vpfe/Makefile | 3 +++ 4 files changed, 15 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/Kconfig create mode 100644 drivers/staging/media/davinci_vpfe/Makefile diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 427218b8b10f..ae0abc350e34 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -23,6 +23,8 @@ source "drivers/staging/media/as102/Kconfig" source "drivers/staging/media/cxd2099/Kconfig" +source "drivers/staging/media/davinci_vpfe/Kconfig" + source "drivers/staging/media/dt3155v4l/Kconfig" source "drivers/staging/media/go7007/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index aec6eb963940..2b97cae99499 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_SOLO6X10) += solo6x10/ obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/ obj-$(CONFIG_VIDEO_GO7007) += go7007/ +obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ diff --git a/drivers/staging/media/davinci_vpfe/Kconfig b/drivers/staging/media/davinci_vpfe/Kconfig new file mode 100644 index 000000000000..2e4a28b018e8 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/Kconfig @@ -0,0 +1,9 @@ +config VIDEO_DM365_VPFE + tristate "DM365 VPFE Media Controller Capture Driver" + depends on VIDEO_V4L2 && ARCH_DAVINCI_DM365 && !VIDEO_VPFE_CAPTURE + select VIDEOBUF2_DMA_CONTIG + help + Support for DM365 VPFE based Media Controller Capture driver. + + To compile this driver as a module, choose M here: the + module will be called vpfe-mc-capture. diff --git a/drivers/staging/media/davinci_vpfe/Makefile b/drivers/staging/media/davinci_vpfe/Makefile new file mode 100644 index 000000000000..c64515c644cd --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_VIDEO_DM365_VPFE) += \ + dm365_isif.o dm365_ipipe_hw.o dm365_ipipe.o \ + dm365_resizer.o dm365_ipipeif.o vpfe_mc_capture.o vpfe_video.o From 5a89fac7e90dd75b9783914fa351069d20fd8c54 Mon Sep 17 00:00:00 2001 From: Manjunath Hadli Date: Wed, 28 Nov 2012 02:29:46 -0300 Subject: [PATCH 0300/4607] [media] davinci: vpfe: Add documentation and TODO Add documentation on the Davinci VPFE driver. Document the subdevs, and private IOTCLs the driver implements. This patch also includes the TODO's to fit into drivers/media/ folder. Signed-off-by: Manjunath Hadli Signed-off-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/davinci_vpfe/TODO | 37 +++++ .../media/davinci_vpfe/davinci-vpfe-mc.txt | 154 ++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 drivers/staging/media/davinci_vpfe/TODO create mode 100644 drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt diff --git a/drivers/staging/media/davinci_vpfe/TODO b/drivers/staging/media/davinci_vpfe/TODO new file mode 100644 index 000000000000..7015ab35ded5 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/TODO @@ -0,0 +1,37 @@ +TODO (general): +================================== + +- User space interface refinement + - Controls should be used when possible rather than private ioctl + - No enums should be used + - Use of MC and V4L2 subdev APIs when applicable + - Single interface header might suffice + - Current interface forces to configure everything at once +- Get rid of the dm365_ipipe_hw.[ch] layer +- Active external sub-devices defined by link configuration; no strcmp + needed +- More generic platform data (i2c adapters) +- The driver should have no knowledge of possible external subdevs; see + struct vpfe_subdev_id +- Some of the hardware control should be refactorede +- Check proper serialisation (through mutexes and spinlocks) +- Names that are visible in kernel global namespace should have a common + prefix (or a few) +- While replacing the older driver in media folder, provide a compatibility + layer and compatibility tests that warrants (using the libv4l's LD_PRELOAD + approach) there is no regression for the users using the older driver. + +Building of uImage and Applications: +================================== + +As of now since the interface will undergo few changes all the include +files are present in staging itself, to build for dm365 follow below steps, + +- copy vpfe.h from drivers/staging/media/davinci_vpfe/ to + include/media/davinci/ folder for building the uImage. +- copy davinci_vpfe_user.h from drivers/staging/media/davinci_vpfe/ to + include/uapi/linux/davinci_vpfe.h, and add a entry in Kbuild (required + for building application). +- copy dm365_ipipeif_user.h from drivers/staging/media/davinci_vpfe/ to + include/uapi/linux/dm365_ipipeif.h and a entry in Kbuild (required + for building application). diff --git a/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt new file mode 100644 index 000000000000..1dbd56418704 --- /dev/null +++ b/drivers/staging/media/davinci_vpfe/davinci-vpfe-mc.txt @@ -0,0 +1,154 @@ +Davinci Video processing Front End (VPFE) driver + +Copyright (C) 2012 Texas Instruments Inc + +Contacts: Manjunath Hadli + Prabhakar Lad + + +Introduction +============ + +This file documents the Texas Instruments Davinci Video processing Front End +(VPFE) driver located under drivers/media/platform/davinci. The original driver +exists for Davinci VPFE, which is now being changed to Media Controller +Framework. + +Currently the driver has been successfully used on the following +version of Davinci: + + DM365/DM368 + +The driver implements V4L2, Media controller and v4l2_subdev interfaces. Sensor, +lens and flash drivers using the v4l2_subdev interface in the kernel are +supported. + + +Split to subdevs +================ + +The Davinci VPFE is split into V4L2 subdevs, each of the blocks inside the VPFE +having one subdev to represent it. Each of the subdevs provide a V4L2 subdev +interface to userspace. + + DAVINCI ISIF + DAVINCI IPIPEIF + DAVINCI IPIPE + DAVINCI CROP RESIZER + DAVINCI RESIZER A + DAVINCI RESIZER B + +Each possible link in the VPFE is modeled by a link in the Media controller +interface. For an example program see [1]. + + +ISIF, IPIPE, and RESIZER block IOCTLs +====================================== + +The Davinci Video processing Front End (VPFE) driver supports standard V4L2 +IOCTLs and controls where possible and practical. Much of the functions provided +by the VPFE, however, does not fall under the standard IOCTL's. + +In general, there is a private ioctl for configuring each of the blocks +containing hardware-dependent functions. + +The following private IOCTLs are supported: + + VIDIOC_VPFE_ISIF_[S/G]_RAW_PARAMS + VIDIOC_VPFE_IPIPE_[S/G]_CONFIG + VIDIOC_VPFE_RSZ_[S/G]_CONFIG + +The parameter structures used by these ioctl's are described in +include/uapi/linux/davinci_vpfe.h. + +The VIDIOC_VPFE_ISIF_S_RAW_PARAMS, VIDIOC_VPFE_IPIPE_S_CONFIG and +VIDIOC_VPFE_RSZ_S_CONFIG are used to configure, enable and disable functions in +the isif, ipipe and resizer blocks respectively. These IOCTL's control several +functions in the blocks they control. VIDIOC_VPFE_ISIF_S_RAW_PARAMS IOCTL +accepts a pointer to struct vpfe_isif_raw_config as its argument. Similarly +VIDIOC_VPFE_IPIPE_S_CONFIG accepts a pointer to struct vpfe_ipipe_config. And +VIDIOC_VPFE_RSZ_S_CONFIG accepts a pointer to struct vpfe_rsz_config as its +argument. Similarly VIDIOC_VPFE_ISIF_G_RAW_PARAMS, VIDIOC_VPFE_IPIPE_G_CONFIG +and VIDIOC_VPFE_RSZ_G_CONFIG are used to get the current configuration set in +the isif, ipipe and resizer blocks respectively. + +The detailed functions of the VPFE itself related to a given VPFE block is +described in the Technical Reference Manuals (TRMs) --- see the end of the +document for those. + + +IPIPEIF block IOCTLs +====================================== + +The following private IOCTLs are supported: + + VIDIOC_VPFE_IPIPEIF_[S/G]_CONFIG + +The parameter structures used by these ioctl's are described in +include/uapi/linux/dm365_ipipeif.h + +The VIDIOC_VPFE_IPIPEIF_S_CONFIG is used to configure the ipipeif +hardware block. The VIDIOC_VPFE_IPIPEIF_S_CONFIG and +VIDIOC_VPFE_IPIPEIF_G_CONFIG accepts a pointer to struct ipipeif_params +as its argument. + + +VPFE Operating Modes +========================================== + +a: Continuous Modes +------------------------ + +1: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> SDRAM + +2: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->| + | + <--------------------<----------------<---------------------<---| + | + V + DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM + +3: tvp514x/tvp7002/mt9p031---> DAVINCI ISIF---> DAVINCI IPIPEIF--->| + | + <--------------------<----------------<---------------------<---| + | + V + DAVINCI IPIPE---> DAVINCI CROP RESIZER--->DAVINCI RESIZER [A/B]---> SDRAM + +a: Single Shot Modes +------------------------ + +1: SDRAM---> DAVINCI IPIPEIF---> DAVINCI IPIPE---> DAVINCI CROP RESIZER--->| + | + <----------------<----------------<------------------<---------------<--| + | + V +DAVINCI RESIZER [A/B]---> SDRAM + +2: SDRAM---> DAVINCI IPIPEIF---> DAVINCI CROP RESIZER--->| + | + <----------------<----------------<---------------<---| + | + V +DAVINCI RESIZER [A/B]---> SDRAM + + +Technical reference manuals (TRMs) and other documentation +========================================================== + +Davinci DM365 TRM: + +Referenced MARCH 2009-REVISED JUNE 2011 + +Davinci DM368 TRM: + +Referenced APRIL 2010-REVISED JUNE 2011 + +Davinci Video Processing Front End (VPFE) DM36x + + + +References +========== + +[1] http://git.ideasonboard.org/?p=media-ctl.git;a=summary From caff80c35f923806b7e5ef312dce41663b5e99b9 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 20 Nov 2012 07:30:36 -0300 Subject: [PATCH 0301/4607] [media] davinci: vpbe: pass different platform names to handle different ip's The vpbe driver can handle different platforms DM644X, DM36X and DM355. To differentiate between this platforms venc_type/vpbe_type was passed as part of platform data which was incorrect. The correct way to differentiate to handle this case is by passing different platform names. This patch creates platform_device_id[] array supporting different platforms and assigns id_table to the platform driver, and finally in the probe gets the actual device by using platform_get_device_id() and gets the appropriate driver data for that platform. Taking this approach will also make the DT transition easier. Signed-off-by: Lad, Prabhakar Signed-off-by: Manjunath Hadli Acked-by: Sekhar Nori Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-davinci/board-dm644x-evm.c | 8 +-- arch/arm/mach-davinci/dm644x.c | 10 +-- drivers/media/platform/davinci/vpbe.c | 4 +- drivers/media/platform/davinci/vpbe_display.c | 2 +- drivers/media/platform/davinci/vpbe_osd.c | 35 +++++++--- drivers/media/platform/davinci/vpbe_venc.c | 65 +++++++++++++------ include/media/davinci/vpbe_osd.h | 5 +- include/media/davinci/vpbe_venc.h | 5 +- 8 files changed, 84 insertions(+), 50 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index f22572cee49d..b00ade498077 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -689,7 +689,7 @@ static struct vpbe_output dm644xevm_vpbe_outputs[] = { .std = VENC_STD_ALL, .capabilities = V4L2_OUT_CAP_STD, }, - .subdev_name = VPBE_VENC_SUBDEV_NAME, + .subdev_name = DM644X_VPBE_VENC_SUBDEV_NAME, .default_mode = "ntsc", .num_modes = ARRAY_SIZE(dm644xevm_enc_std_timing), .modes = dm644xevm_enc_std_timing, @@ -701,7 +701,7 @@ static struct vpbe_output dm644xevm_vpbe_outputs[] = { .type = V4L2_OUTPUT_TYPE_ANALOG, .capabilities = V4L2_OUT_CAP_DV_TIMINGS, }, - .subdev_name = VPBE_VENC_SUBDEV_NAME, + .subdev_name = DM644X_VPBE_VENC_SUBDEV_NAME, .default_mode = "480p59_94", .num_modes = ARRAY_SIZE(dm644xevm_enc_preset_timing), .modes = dm644xevm_enc_preset_timing, @@ -712,10 +712,10 @@ static struct vpbe_config dm644xevm_display_cfg = { .module_name = "dm644x-vpbe-display", .i2c_adapter_id = 1, .osd = { - .module_name = VPBE_OSD_SUBDEV_NAME, + .module_name = DM644X_VPBE_OSD_SUBDEV_NAME, }, .venc = { - .module_name = VPBE_VENC_SUBDEV_NAME, + .module_name = DM644X_VPBE_VENC_SUBDEV_NAME, }, .num_outputs = ARRAY_SIZE(dm644xevm_vpbe_outputs), .outputs = dm644xevm_vpbe_outputs, diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 14e9947bad6e..0849d5768d99 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -669,19 +669,14 @@ static struct resource dm644x_osd_resources[] = { }, }; -static struct osd_platform_data dm644x_osd_data = { - .vpbe_type = VPBE_VERSION_1, -}; - static struct platform_device dm644x_osd_dev = { - .name = VPBE_OSD_SUBDEV_NAME, + .name = DM644X_VPBE_OSD_SUBDEV_NAME, .id = -1, .num_resources = ARRAY_SIZE(dm644x_osd_resources), .resource = dm644x_osd_resources, .dev = { .dma_mask = &dm644x_video_dma_mask, .coherent_dma_mask = DMA_BIT_MASK(32), - .platform_data = &dm644x_osd_data, }, }; @@ -751,12 +746,11 @@ static struct platform_device dm644x_vpbe_display = { }; static struct venc_platform_data dm644x_venc_pdata = { - .venc_type = VPBE_VERSION_1, .setup_clock = dm644x_venc_setup_clock, }; static struct platform_device dm644x_venc_dev = { - .name = VPBE_VENC_SUBDEV_NAME, + .name = DM644X_VPBE_VENC_SUBDEV_NAME, .id = -1, .num_resources = ARRAY_SIZE(dm644x_venc_resources), .resource = dm644x_venc_resources, diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index 7f5cf9b347b2..dd670cdb2c2a 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -558,9 +558,9 @@ static int platform_device_get(struct device *dev, void *data) struct platform_device *pdev = to_platform_device(dev); struct vpbe_device *vpbe_dev = data; - if (strcmp("vpbe-osd", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-osd") != NULL) vpbe_dev->osd_device = platform_get_drvdata(pdev); - if (strcmp("vpbe-venc", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-venc") != NULL) vpbe_dev->venc_device = dev_get_platdata(&pdev->dev); return 0; diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 2bfde7958fef..3846890ea707 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1656,7 +1656,7 @@ static int vpbe_device_get(struct device *dev, void *data) if (strcmp("vpbe_controller", pdev->name) == 0) vpbe_disp->vpbe_dev = platform_get_drvdata(pdev); - if (strcmp("vpbe-osd", pdev->name) == 0) + if (strstr(pdev->name, "vpbe-osd") != NULL) vpbe_disp->osd_device = platform_get_drvdata(pdev); return 0; diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index 707f243f810d..12ad17c52ef3 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -39,7 +39,22 @@ #include #include "vpbe_osd_regs.h" -#define MODULE_NAME VPBE_OSD_SUBDEV_NAME +#define MODULE_NAME "davinci-vpbe-osd" + +static struct platform_device_id vpbe_osd_devtype[] = { + { + .name = DM644X_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_1, + }, { + .name = DM365_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_2, + }, { + .name = DM355_VPBE_OSD_SUBDEV_NAME, + .driver_data = VPBE_VERSION_3, + }, +}; + +MODULE_DEVICE_TABLE(platform, vpbe_osd_devtype); /* register access routines */ static inline u32 osd_read(struct osd_state *sd, u32 offset) @@ -129,7 +144,7 @@ static int _osd_dm6446_vid0_pingpong(struct osd_state *sd, struct osd_platform_data *pdata; pdata = (struct osd_platform_data *)sd->dev->platform_data; - if (pdata->field_inv_wa_enable) { + if (pdata != NULL && pdata->field_inv_wa_enable) { if (!field_inversion || !lconfig->interlaced) { osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); @@ -1526,7 +1541,7 @@ static const struct vpbe_osd_ops osd_ops = { static int osd_probe(struct platform_device *pdev) { - struct osd_platform_data *pdata; + const struct platform_device_id *pdev_id; struct osd_state *osd; struct resource *res; int ret = 0; @@ -1535,16 +1550,15 @@ static int osd_probe(struct platform_device *pdev) if (osd == NULL) return -ENOMEM; - osd->dev = &pdev->dev; - pdata = (struct osd_platform_data *)pdev->dev.platform_data; - osd->vpbe_type = (enum vpbe_version)pdata->vpbe_type; - if (NULL == pdev->dev.platform_data) { - dev_err(osd->dev, "No platform data defined for OSD" - " sub device\n"); - ret = -ENOENT; + pdev_id = platform_get_device_id(pdev); + if (!pdev_id) { + ret = -EINVAL; goto free_mem; } + osd->dev = &pdev->dev; + osd->vpbe_type = pdev_id->driver_data; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(osd->dev, "Unable to get OSD register address map\n"); @@ -1595,6 +1609,7 @@ static struct platform_driver osd_driver = { .name = MODULE_NAME, .owner = THIS_MODULE, }, + .id_table = vpbe_osd_devtype }; module_platform_driver(osd_driver); diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c index aed7369b962a..bdbebd59df98 100644 --- a/drivers/media/platform/davinci/vpbe_venc.c +++ b/drivers/media/platform/davinci/vpbe_venc.c @@ -38,7 +38,22 @@ #include "vpbe_venc_regs.h" -#define MODULE_NAME VPBE_VENC_SUBDEV_NAME +#define MODULE_NAME "davinci-vpbe-venc" + +static struct platform_device_id vpbe_venc_devtype[] = { + { + .name = DM644X_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_1, + }, { + .name = DM365_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_2, + }, { + .name = DM355_VPBE_VENC_SUBDEV_NAME, + .driver_data = VPBE_VERSION_3, + }, +}; + +MODULE_DEVICE_TABLE(platform, vpbe_venc_devtype); static int debug = 2; module_param(debug, int, 0644); @@ -54,6 +69,7 @@ struct venc_state { spinlock_t lock; void __iomem *venc_base; void __iomem *vdaccfg_reg; + enum vpbe_version venc_type; }; static inline struct venc_state *to_state(struct v4l2_subdev *sd) @@ -127,7 +143,7 @@ static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index) static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) { struct venc_state *venc = to_state(sd); - struct venc_platform_data *pdata = venc->pdata; + v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n"); if (benable) { @@ -159,7 +175,7 @@ static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) /* Disable LCD output control (accepting default polarity) */ venc_write(sd, VENC_LCDOUT, 0); - if (pdata->venc_type != VPBE_VERSION_3) + if (venc->venc_type != VPBE_VERSION_3) venc_write(sd, VENC_CMPNT, 0x100); venc_write(sd, VENC_HSPLS, 0); venc_write(sd, VENC_HINT, 0); @@ -203,11 +219,11 @@ static int venc_set_ntsc(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_3) { + if (venc->venc_type == VPBE_VERSION_3) { venc_write(sd, VENC_CLKCTL, 0x01); venc_write(sd, VENC_VIDCTL, 0); val = vdaccfg_write(sd, VDAC_CONFIG_SD_V3); - } else if (pdata->venc_type == VPBE_VERSION_2) { + } else if (venc->venc_type == VPBE_VERSION_2) { venc_write(sd, VENC_CLKCTL, 0x01); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V2); @@ -238,7 +254,6 @@ static int venc_set_ntsc(struct v4l2_subdev *sd) static int venc_set_pal(struct v4l2_subdev *sd) { struct venc_state *venc = to_state(sd); - struct venc_platform_data *pdata = venc->pdata; v4l2_dbg(debug, 2, sd, "venc_set_pal\n"); @@ -249,11 +264,11 @@ static int venc_set_pal(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_3) { + if (venc->venc_type == VPBE_VERSION_3) { venc_write(sd, VENC_CLKCTL, 0x1); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V3); - } else if (pdata->venc_type == VPBE_VERSION_2) { + } else if (venc->venc_type == VPBE_VERSION_2) { venc_write(sd, VENC_CLKCTL, 0x1); venc_write(sd, VENC_VIDCTL, 0); vdaccfg_write(sd, VDAC_CONFIG_SD_V2); @@ -293,8 +308,8 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd) struct venc_platform_data *pdata = venc->pdata; v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n"); - if ((pdata->venc_type != VPBE_VERSION_1) && - (pdata->venc_type != VPBE_VERSION_2)) + if (venc->venc_type != VPBE_VERSION_1 && + venc->venc_type != VPBE_VERSION_2) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ @@ -303,12 +318,12 @@ static int venc_set_480p59_94(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_2) + if (venc->venc_type == VPBE_VERSION_2) vdaccfg_write(sd, VDAC_CONFIG_HD_V2); venc_write(sd, VENC_OSDCLK0, 0); venc_write(sd, VENC_OSDCLK1, 1); - if (pdata->venc_type == VPBE_VERSION_1) { + if (venc->venc_type == VPBE_VERSION_1) { venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, VENC_VDPRO_DAFRQ); venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, @@ -341,8 +356,8 @@ static int venc_set_576p50(struct v4l2_subdev *sd) v4l2_dbg(debug, 2, sd, "venc_set_576p50\n"); - if ((pdata->venc_type != VPBE_VERSION_1) && - (pdata->venc_type != VPBE_VERSION_2)) + if (venc->venc_type != VPBE_VERSION_1 && + venc->venc_type != VPBE_VERSION_2) return -EINVAL; /* Setup clock at VPSS & VENC for SD */ if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) @@ -350,13 +365,13 @@ static int venc_set_576p50(struct v4l2_subdev *sd) venc_enabledigitaloutput(sd, 0); - if (pdata->venc_type == VPBE_VERSION_2) + if (venc->venc_type == VPBE_VERSION_2) vdaccfg_write(sd, VDAC_CONFIG_HD_V2); venc_write(sd, VENC_OSDCLK0, 0); venc_write(sd, VENC_OSDCLK1, 1); - if (pdata->venc_type == VPBE_VERSION_1) { + if (venc->venc_type == VPBE_VERSION_1) { venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, VENC_VDPRO_DAFRQ); venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, @@ -460,14 +475,14 @@ static int venc_s_dv_timings(struct v4l2_subdev *sd, else if (height == 480) return venc_set_480p59_94(sd); else if ((height == 720) && - (venc->pdata->venc_type == VPBE_VERSION_2)) { + (venc->venc_type == VPBE_VERSION_2)) { /* TBD setup internal 720p mode here */ ret = venc_set_720p60_internal(sd); /* for DM365 VPBE, there is DAC inside */ vdaccfg_write(sd, VDAC_CONFIG_HD_V2); return ret; } else if ((height == 1080) && - (venc->pdata->venc_type == VPBE_VERSION_2)) { + (venc->venc_type == VPBE_VERSION_2)) { /* TBD setup internal 1080i mode here */ ret = venc_set_1080i30_internal(sd); /* for DM365 VPBE, there is DAC inside */ @@ -556,7 +571,7 @@ static int venc_device_get(struct device *dev, void *data) struct platform_device *pdev = to_platform_device(dev); struct venc_state **venc = data; - if (strcmp(MODULE_NAME, pdev->name) == 0) + if (strstr(pdev->name, "vpbe-venc") != NULL) *venc = platform_get_drvdata(pdev); return 0; @@ -593,6 +608,7 @@ EXPORT_SYMBOL(venc_sub_dev_init); static int venc_probe(struct platform_device *pdev) { + const struct platform_device_id *pdev_id; struct venc_state *venc; struct resource *res; int ret; @@ -601,6 +617,12 @@ static int venc_probe(struct platform_device *pdev) if (venc == NULL) return -ENOMEM; + pdev_id = platform_get_device_id(pdev); + if (!pdev_id) { + ret = -EINVAL; + goto free_mem; + } + venc->venc_type = pdev_id->driver_data; venc->pdev = &pdev->dev; venc->pdata = pdev->dev.platform_data; if (NULL == venc->pdata) { @@ -630,7 +652,7 @@ static int venc_probe(struct platform_device *pdev) goto release_venc_mem_region; } - if (venc->pdata->venc_type != VPBE_VERSION_1) { + if (venc->venc_type != VPBE_VERSION_1) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { dev_err(venc->pdev, @@ -681,7 +703,7 @@ static int venc_remove(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap((void *)venc->venc_base); release_mem_region(res->start, resource_size(res)); - if (venc->pdata->venc_type != VPBE_VERSION_1) { + if (venc->venc_type != VPBE_VERSION_1) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); iounmap((void *)venc->vdaccfg_reg); release_mem_region(res->start, resource_size(res)); @@ -698,6 +720,7 @@ static struct platform_driver venc_driver = { .name = MODULE_NAME, .owner = THIS_MODULE, }, + .id_table = vpbe_venc_devtype }; module_platform_driver(venc_driver); diff --git a/include/media/davinci/vpbe_osd.h b/include/media/davinci/vpbe_osd.h index 5ab0d8d41f68..42628fcfe1bd 100644 --- a/include/media/davinci/vpbe_osd.h +++ b/include/media/davinci/vpbe_osd.h @@ -26,7 +26,9 @@ #include -#define VPBE_OSD_SUBDEV_NAME "vpbe-osd" +#define DM644X_VPBE_OSD_SUBDEV_NAME "dm644x,vpbe-osd" +#define DM365_VPBE_OSD_SUBDEV_NAME "dm365,vpbe-osd" +#define DM355_VPBE_OSD_SUBDEV_NAME "dm355,vpbe-osd" /** * enum osd_layer @@ -387,7 +389,6 @@ struct osd_state { }; struct osd_platform_data { - enum vpbe_version vpbe_type; int field_inv_wa_enable; }; diff --git a/include/media/davinci/vpbe_venc.h b/include/media/davinci/vpbe_venc.h index cc78c2eb16da..476fafc2f522 100644 --- a/include/media/davinci/vpbe_venc.h +++ b/include/media/davinci/vpbe_venc.h @@ -20,7 +20,9 @@ #include #include -#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" +#define DM644X_VPBE_VENC_SUBDEV_NAME "dm644x,vpbe-venc" +#define DM365_VPBE_VENC_SUBDEV_NAME "dm365,vpbe-venc" +#define DM355_VPBE_VENC_SUBDEV_NAME "dm355,vpbe-venc" /* venc events */ #define VENC_END_OF_FRAME BIT(0) @@ -28,7 +30,6 @@ #define VENC_SECOND_FIELD BIT(2) struct venc_platform_data { - enum vpbe_version venc_type; int (*setup_pinmux)(enum v4l2_mbus_pixelcode if_type, int field); int (*setup_clock)(enum vpbe_enc_timings_type type, From cfe9dbd8a76835abd33ba92060817e2699524b1e Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Wed, 28 Nov 2012 10:58:47 -0300 Subject: [PATCH 0302/4607] [media] media: davinci: vpbe: enable building of vpbe driver for DM355 and DM365 This patch allows enabling building of VPBE display driver for DM365 and DM355. This also removes unnecessary entry VIDEO_DM644X_VPBE in Kconfig, which could have been done with single entry, and appropriate changes in Makefile for building. Signed-off-by: Lad, Prabhakar Signed-off-by: Manjunath Hadli Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/Kconfig | 22 ++++++---------------- drivers/media/platform/davinci/Makefile | 4 ++-- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index 3c56037c82fc..ccfde4eb626a 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -97,25 +97,15 @@ config VIDEO_ISIF To compile this driver as a module, choose M here: the module will be called vpfe. -config VIDEO_DM644X_VPBE - tristate "DM644X VPBE HW module" - depends on ARCH_DAVINCI_DM644x +config VIDEO_DAVINCI_VPBE_DISPLAY + tristate "DM644X/DM365/DM355 VPBE HW module" + depends on ARCH_DAVINCI_DM644x || ARCH_DAVINCI_DM355 || ARCH_DAVINCI_DM365 select VIDEO_VPSS_SYSTEM select VIDEOBUF2_DMA_CONTIG help - Enables VPBE modules used for display on a DM644x - SoC. + Enables Davinci VPBE module used for display devices. + This module is common for following DM644x/DM365/DM355 + based display devices. To compile this driver as a module, choose M here: the module will be called vpbe. - - -config VIDEO_VPBE_DISPLAY - tristate "VPBE V4L2 Display driver" - depends on ARCH_DAVINCI_DM644x - select VIDEO_DM644X_VPBE - help - Enables VPBE V4L2 Display driver on a DM644x device - - To compile this driver as a module, choose M here: the - module will be called vpbe_display. diff --git a/drivers/media/platform/davinci/Makefile b/drivers/media/platform/davinci/Makefile index 74ed92d09257..f40f5219ca50 100644 --- a/drivers/media/platform/davinci/Makefile +++ b/drivers/media/platform/davinci/Makefile @@ -16,5 +16,5 @@ obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o obj-$(CONFIG_VIDEO_ISIF) += isif.o -obj-$(CONFIG_VIDEO_DM644X_VPBE) += vpbe.o vpbe_osd.o vpbe_venc.o -obj-$(CONFIG_VIDEO_VPBE_DISPLAY) += vpbe_display.o +obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpbe.o vpbe_osd.o \ + vpbe_venc.o vpbe_display.o From 4d22f1086d24f61e271e1e84c0c27db4ad495e8f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Dec 2012 06:18:35 -0300 Subject: [PATCH 0303/4607] [media] media: davinci: vpbe: fix return value check in vpbe_display_reqbufs() In case of error, the function vb2_dma_contig_init_ctx() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Acked-by: Prabhakar Lad Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 3846890ea707..0723c4694ced 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -1393,9 +1393,9 @@ static int vpbe_display_reqbufs(struct file *file, void *priv, } /* Initialize videobuf queue as per the buffer type */ layer->alloc_ctx = vb2_dma_contig_init_ctx(vpbe_dev->pdev); - if (!layer->alloc_ctx) { + if (IS_ERR(layer->alloc_ctx)) { v4l2_err(&vpbe_dev->v4l2_dev, "Failed to get the context\n"); - return -EINVAL; + return PTR_ERR(layer->alloc_ctx); } q = &layer->buffer_queue; memset(q, 0, sizeof(*q)); From e276f03b4f29fcc54d8e658d5de8dd953e4aef1e Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Dec 2012 22:50:47 -0300 Subject: [PATCH 0304/4607] [media] media: davinci: vpbe: return error code on error in vpbe_display_g_crop() We have assigned error code to 'ret' if crop->type is not V4L2_BUF_TYPE_VIDEO_OUTPUT, but never use it. We'd better return the error code on this error. Signed-off-by: Wei Yongjun Acked-by: Prabhakar Lad Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe_display.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index 0723c4694ced..d078738b35d6 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -791,7 +791,6 @@ static int vpbe_display_g_crop(struct file *file, void *priv, struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; struct osd_state *osd_device = fh->disp_dev->osd_device; struct v4l2_rect *rect = &crop->c; - int ret; v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_CROP, layer id = %d\n", @@ -799,7 +798,7 @@ static int vpbe_display_g_crop(struct file *file, void *priv, if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); - ret = -EINVAL; + return -EINVAL; } osd_device->ops.get_layer_config(osd_device, layer->layer_info.id, cfg); From cc91de5fad155fdfb40856ac65f29f080b9b42ab Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Dec 2012 22:53:44 -0300 Subject: [PATCH 0305/4607] [media] davinci: vpbe: remove unused variable in vpbe_initialize() The variable 'output_index' is initialized but never used otherwise, so remove the unused variable. Signed-off-by: Wei Yongjun Acked-by: Prabhakar Lad Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpbe.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c index dd670cdb2c2a..fe2b9ce0bce8 100644 --- a/drivers/media/platform/davinci/vpbe.c +++ b/drivers/media/platform/davinci/vpbe.c @@ -584,7 +584,6 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) struct v4l2_subdev **enc_subdev; struct osd_state *osd_device; struct i2c_adapter *i2c_adap; - int output_index; int num_encoders; int ret = 0; int err; @@ -731,7 +730,6 @@ static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) /* set the current encoder and output to that of venc by default */ vpbe_dev->current_sd_index = 0; vpbe_dev->current_out_index = 0; - output_index = 0; mutex_unlock(&vpbe_dev->lock); From 870f31cbf03e0c759900eea2feb276b2fe6558f4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 17 Dec 2012 22:10:00 -0300 Subject: [PATCH 0306/4607] [media] or51211: use %*ph[N] to dump small buffers Signed-off-by: Andy Shevchenko Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/or51211.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c index c625b57b4333..1af997eb0578 100644 --- a/drivers/media/dvb-frontends/or51211.c +++ b/drivers/media/dvb-frontends/or51211.c @@ -471,10 +471,7 @@ static int or51211_init(struct dvb_frontend* fe) i--; } } - dprintk("read_fwbits %x %x %x %x %x %x %x %x %x %x\n", - rec_buf[0], rec_buf[1], rec_buf[2], rec_buf[3], - rec_buf[4], rec_buf[5], rec_buf[6], rec_buf[7], - rec_buf[8], rec_buf[9]); + dprintk("read_fwbits %10ph\n", rec_buf); printk(KERN_INFO "or51211: ver TU%02x%02x%02x VSB mode %02x" " Status %02x\n", From aa735ee9dd92118488b30924b4710061b813193f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 17 Dec 2012 22:10:48 -0300 Subject: [PATCH 0307/4607] [media] ix2505v: use %*ph[N] to dump small buffers Signed-off-by: Andy Shevchenko Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ix2505v.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c index bc5a82082aaa..0e3387e00952 100644 --- a/drivers/media/dvb-frontends/ix2505v.c +++ b/drivers/media/dvb-frontends/ix2505v.c @@ -212,7 +212,7 @@ static int ix2505v_set_params(struct dvb_frontend *fe) lpf = 0xb; deb_info("Osc=%x b_w=%x lpf=%x\n", local_osc, b_w, lpf); - deb_info("Data 0=[%x%x%x%x]\n", data[0], data[1], data[2], data[3]); + deb_info("Data 0=[%4phN]\n", data); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); From bb9e31f3928dd9b1ecb66689890d1f5f3d19227c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 18 Dec 2012 10:20:28 -0300 Subject: [PATCH 0308/4607] [media] or51211: apply pr_fmt and use pr_* macros instead of printk Signed-off-by: Andy Shevchenko Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/or51211.c | 94 ++++++++++++--------------- 1 file changed, 43 insertions(+), 51 deletions(-) diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c index 1af997eb0578..10cfc0579168 100644 --- a/drivers/media/dvb-frontends/or51211.c +++ b/drivers/media/dvb-frontends/or51211.c @@ -22,6 +22,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ + /* * This driver needs external firmware. Please use the command * "/Documentation/dvb/get_dvb_firmware or51211" to @@ -44,9 +46,7 @@ static int debug; #define dprintk(args...) \ - do { \ - if (debug) printk(KERN_DEBUG "or51211: " args); \ - } while (0) + do { if (debug) pr_debug(args); } while (0) static u8 run_buf[] = {0x7f,0x01}; static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC @@ -80,8 +80,7 @@ static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf, msg.buf = (u8 *)buf; if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51211: i2c_writebytes error " - "(addr %02x, err == %i)\n", reg, err); + pr_warn("error (addr %02x, err == %i)\n", reg, err); return -EREMOTEIO; } @@ -98,8 +97,7 @@ static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len) msg.buf = buf; if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51211: i2c_readbytes error " - "(addr %02x, err == %i)\n", reg, err); + pr_warn("error (addr %02x, err == %i)\n", reg, err); return -EREMOTEIO; } @@ -118,11 +116,11 @@ static int or51211_load_firmware (struct dvb_frontend* fe, /* Get eprom data */ tudata[0] = 17; if (i2c_writebytes(state,0x50,tudata,1)) { - printk(KERN_WARNING "or51211:load_firmware error eprom addr\n"); + pr_warn("error eprom addr\n"); return -1; } if (i2c_readbytes(state,0x50,&tudata[145],192)) { - printk(KERN_WARNING "or51211: load_firmware error eprom\n"); + pr_warn("error eprom\n"); return -1; } @@ -136,32 +134,32 @@ static int or51211_load_firmware (struct dvb_frontend* fe, state->config->reset(fe); if (i2c_writebytes(state,state->config->demod_address,tudata,585)) { - printk(KERN_WARNING "or51211: load_firmware error 1\n"); + pr_warn("error 1\n"); return -1; } msleep(1); if (i2c_writebytes(state,state->config->demod_address, &fw->data[393],8125)) { - printk(KERN_WARNING "or51211: load_firmware error 2\n"); + pr_warn("error 2\n"); return -1; } msleep(1); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: load_firmware error 3\n"); + pr_warn("error 3\n"); return -1; } /* Wait at least 5 msec */ msleep(10); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: load_firmware error 4\n"); + pr_warn("error 4\n"); return -1; } msleep(10); - printk("or51211: Done.\n"); + pr_info("Done.\n"); return 0; }; @@ -173,14 +171,14 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) state->config->setmode(fe, mode); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: setmode error 1\n"); + pr_warn("error 1\n"); return -1; } /* Wait at least 5 msec */ msleep(10); if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { - printk(KERN_WARNING "or51211: setmode error 2\n"); + pr_warn("error 2\n"); return -1; } @@ -196,7 +194,7 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) * normal +/-150kHz Carrier acquisition range */ if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) { - printk(KERN_WARNING "or51211: setmode error 3\n"); + pr_warn("error 3\n"); return -1; } @@ -206,14 +204,14 @@ static int or51211_setmode(struct dvb_frontend* fe, int mode) rec_buf[3] = 0x00; msleep(20); if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) { - printk(KERN_WARNING "or51211: setmode error 5\n"); + pr_warn("error 5\n"); } msleep(3); if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) { - printk(KERN_WARNING "or51211: setmode error 6"); + pr_warn("error 6\n"); return -1; } - dprintk("setmode rec status %02x %02x\n",rec_buf[10],rec_buf[11]); + dprintk("rec status %02x %02x\n", rec_buf[10], rec_buf[11]); return 0; } @@ -248,15 +246,15 @@ static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status) /* Receiver Status */ if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { - printk(KERN_WARNING "or51132: read_status write error\n"); + pr_warn("write error\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: read_status read error\n"); + pr_warn("read error\n"); return -1; } - dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]); + dprintk("%x %x\n", rec_buf[0], rec_buf[1]); if (rec_buf[0] & 0x01) { /* Receiver Lock */ *status |= FE_HAS_SIGNAL; @@ -306,20 +304,18 @@ static int or51211_read_snr(struct dvb_frontend* fe, u16* snr) snd_buf[2] = 0x04; if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { - printk(KERN_WARNING "%s: error writing snr reg\n", - __func__); + pr_warn("error writing snr reg\n"); return -1; } if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "%s: read_status read error\n", - __func__); + pr_warn("read_status read error\n"); return -1; } state->snr = calculate_snr(rec_buf[0], 89599047); *snr = (state->snr) >> 16; - dprintk("%s: noise = 0x%02x, snr = %d.%02d dB\n", __func__, rec_buf[0], + dprintk("noise = 0x%02x, snr = %d.%02d dB\n", rec_buf[0], state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); return 0; @@ -375,25 +371,24 @@ static int or51211_init(struct dvb_frontend* fe) if (!state->initialized) { /* Request the firmware, this will block until it uploads */ - printk(KERN_INFO "or51211: Waiting for firmware upload " - "(%s)...\n", OR51211_DEFAULT_FIRMWARE); + pr_info("Waiting for firmware upload (%s)...\n", + OR51211_DEFAULT_FIRMWARE); ret = config->request_firmware(fe, &fw, OR51211_DEFAULT_FIRMWARE); - printk(KERN_INFO "or51211:Got Hotplug firmware\n"); + pr_info("Got Hotplug firmware\n"); if (ret) { - printk(KERN_WARNING "or51211: No firmware uploaded " - "(timeout or file not found?)\n"); + pr_warn("No firmware uploaded " + "(timeout or file not found?)\n"); return ret; } ret = or51211_load_firmware(fe, fw); release_firmware(fw); if (ret) { - printk(KERN_WARNING "or51211: Writing firmware to " - "device failed!\n"); + pr_warn("Writing firmware to device failed!\n"); return ret; } - printk(KERN_INFO "or51211: Firmware upload complete.\n"); + pr_info("Firmware upload complete.\n"); /* Set operation mode in Receiver 1 register; * type 1: @@ -406,7 +401,7 @@ static int or51211_init(struct dvb_frontend* fe) */ if (i2c_writebytes(state,state->config->demod_address, cmd_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error 5\n"); + pr_warn("Load DVR Error 5\n"); return -1; } @@ -419,13 +414,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(30); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error A\n"); + pr_warn("Load DVR Error A\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[10],2)) { - printk(KERN_WARNING "or51211: Load DVR Error B\n"); + pr_warn("Load DVR Error B\n"); return -1; } @@ -436,13 +431,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(20); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error C\n"); + pr_warn("Load DVR Error C\n"); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[12],2)) { - printk(KERN_WARNING "or51211: Load DVR Error D\n"); + pr_warn("Load DVR Error D\n"); return -1; } @@ -454,16 +449,14 @@ static int or51211_init(struct dvb_frontend* fe) get_ver_buf[4] = i+1; if (i2c_writebytes(state,state->config->demod_address, get_ver_buf,5)) { - printk(KERN_WARNING "or51211:Load DVR Error 6" - " - %d\n",i); + pr_warn("Load DVR Error 6 - %d\n", i); return -1; } msleep(3); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[i*2],2)) { - printk(KERN_WARNING "or51211:Load DVR Error 7" - " - %d\n",i); + pr_warn("Load DVR Error 7 - %d\n", i); return -1; } /* If we didn't receive the right index, try again */ @@ -473,10 +466,9 @@ static int or51211_init(struct dvb_frontend* fe) } dprintk("read_fwbits %10ph\n", rec_buf); - printk(KERN_INFO "or51211: ver TU%02x%02x%02x VSB mode %02x" - " Status %02x\n", - rec_buf[2], rec_buf[4],rec_buf[6], - rec_buf[12],rec_buf[10]); + pr_info("ver TU%02x%02x%02x VSB mode %02x Status %02x\n", + rec_buf[2], rec_buf[4], rec_buf[6], rec_buf[12], + rec_buf[10]); rec_buf[0] = 0x04; rec_buf[1] = 0x00; @@ -485,13 +477,13 @@ static int or51211_init(struct dvb_frontend* fe) msleep(20); if (i2c_writebytes(state,state->config->demod_address, rec_buf,3)) { - printk(KERN_WARNING "or51211: Load DVR Error 8\n"); + pr_warn("Load DVR Error 8\n"); return -1; } msleep(20); if (i2c_readbytes(state,state->config->demod_address, &rec_buf[8],2)) { - printk(KERN_WARNING "or51211: Load DVR Error 9\n"); + pr_warn("Load DVR Error 9\n"); return -1; } state->initialized = 1; From 41f55d57552b7d2236f94fccb5cdd07dbf2e8557 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 16 Dec 2012 19:37:11 -0300 Subject: [PATCH 0309/4607] [media] tda10071: make sure both tuner and demod i2c addresses are specified display an error message if either tuner_i2c_addr or demod_i2c_addr are not specified in the tda10071_config structure Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 18 +++++++++++++++--- drivers/media/dvb-frontends/tda10071.h | 4 ++-- drivers/media/pci/cx23885/cx23885-dvb.c | 2 +- drivers/media/usb/em28xx/em28xx-dvb.c | 3 ++- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 710362916f83..02f9234a5400 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -30,7 +30,7 @@ static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, u8 buf[len+1]; struct i2c_msg msg[1] = { { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = 0, .len = sizeof(buf), .buf = buf, @@ -59,12 +59,12 @@ static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, u8 buf[len]; struct i2c_msg msg[2] = { { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = 0, .len = 1, .buf = ®, }, { - .addr = priv->cfg.i2c_address, + .addr = priv->cfg.demod_i2c_addr, .flags = I2C_M_RD, .len = sizeof(buf), .buf = buf, @@ -1202,6 +1202,18 @@ struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, goto error; } + /* make sure demod i2c address is specified */ + if (!config->demod_i2c_addr) { + dev_dbg(&i2c->dev, "%s: invalid demod i2c address!\n", __func__); + goto error; + } + + /* make sure tuner i2c address is specified */ + if (!config->tuner_i2c_addr) { + dev_dbg(&i2c->dev, "%s: invalid tuner i2c address!\n", __func__); + goto error; + } + /* setup the priv */ priv->i2c = i2c; memcpy(&priv->cfg, config, sizeof(struct tda10071_config)); diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h index a20d5c41e048..bff1c38df802 100644 --- a/drivers/media/dvb-frontends/tda10071.h +++ b/drivers/media/dvb-frontends/tda10071.h @@ -28,10 +28,10 @@ struct tda10071_config { * Default: none, must set * Values: 0x55, */ - u8 i2c_address; + u8 demod_i2c_addr; /* Tuner I2C address. - * Default: 0x14 + * Default: none, must set * Values: 0x14, 0x54, ... */ u8 tuner_i2c_addr; diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index cf84c534f005..a1aae5633739 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -662,7 +662,7 @@ static struct mt2063_config terratec_mt2063_config[] = { }; static const struct tda10071_config hauppauge_tda10071_config = { - .i2c_address = 0x05, + .demod_i2c_addr = 0x05, .tuner_i2c_addr = 0x54, .i2c_wr_max = 64, .ts_mode = TDA10071_TS_SERIAL, diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 63f2e7070c00..e8008810b60c 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -714,7 +714,8 @@ static struct tda18271_config em28xx_cxd2820r_tda18271_config = { }; static const struct tda10071_config em28xx_tda10071_config = { - .i2c_address = 0x55, /* (0xaa >> 1) */ + .demod_i2c_addr = 0x55, /* (0xaa >> 1) */ + .tuner_i2c_addr = 0x14, .i2c_wr_max = 64, .ts_mode = TDA10071_TS_SERIAL, .spec_inv = 0, From 1c12bf8de7e1557afeedd55d9bcec6b6a6d7b5d1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 21 Dec 2012 14:43:19 -0200 Subject: [PATCH 0310/4607] [media] tda10071: fix a warning introduced by changeset 41f55d5755 The two new tests don't set the returned value. Cc: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda10071.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c index 02f9234a5400..2521f7e23018 100644 --- a/drivers/media/dvb-frontends/tda10071.c +++ b/drivers/media/dvb-frontends/tda10071.c @@ -1205,12 +1205,14 @@ struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, /* make sure demod i2c address is specified */ if (!config->demod_i2c_addr) { dev_dbg(&i2c->dev, "%s: invalid demod i2c address!\n", __func__); + ret = -EINVAL; goto error; } /* make sure tuner i2c address is specified */ if (!config->tuner_i2c_addr) { dev_dbg(&i2c->dev, "%s: invalid tuner i2c address!\n", __func__); + ret = -EINVAL; goto error; } From e5d85b9ac3133f67460ea5b2d4e33e0473d6eb4b Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Tue, 25 Nov 2008 10:57:30 -0300 Subject: [PATCH 0311/4607] [media] rc: Fix double free in gpio_ir_recv_probe() At the 'err_request_irq' label, rc_unregister_device(rcdev) frees its argument. So when we fall through to the 'err_gpio_request' label further down and call rc_free_device(rcdev) then that's a double free. Fix that by moving 'rcdev = NULL' from after the call to rc_free_device() to after rc_unregister_device(). That fixes the problem since rc_free_device() just does nothing if passed NULL and there's no further use of 'rcdev' after the call to rc_free_device() so it's not needed there. Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/gpio-ir-recv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index ba1a1eb356cf..32db5f59fbc3 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -129,12 +129,12 @@ static int __devinit gpio_ir_recv_probe(struct platform_device *pdev) err_request_irq: platform_set_drvdata(pdev, NULL); rc_unregister_device(rcdev); + rcdev = NULL; err_register_rc_device: err_gpio_direction_input: gpio_free(pdata->gpio_nr); err_gpio_request: rc_free_device(rcdev); - rcdev = NULL; err_allocate_device: kfree(gpio_dev); return rc; From bbe2a1d32f40c01ca1a7e7795e20ca06f87ffc9b Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Tue, 25 Nov 2008 10:57:54 -0300 Subject: [PATCH 0312/4607] [media] rc: Fix double free in gpio_ir_recv_remove() Since rc_unregister_device() frees its argument there's no need to subsequently call rc_free_device() on the same variable - in fact it's a double free bug. Easily fixed by just removing the rc_free_device() call. Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/gpio-ir-recv.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index 32db5f59fbc3..03e3cf6eb68f 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -148,7 +148,6 @@ static int __devexit gpio_ir_recv_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); rc_unregister_device(gpio_dev->rcdev); gpio_free(gpio_dev->gpio_nr); - rc_free_device(gpio_dev->rcdev); kfree(gpio_dev); return 0; } From 3f3f5c7f63dec5d6413075116cd6beee1e888d7b Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Mon, 29 Oct 2012 05:20:29 -0300 Subject: [PATCH 0313/4607] [media] media: coda: Fix H.264 header alignment Length of H.264 headers is variable and thus it might not be aligned for the coda to append the encoded frame. This causes the first frame to overwrite part of the H.264 PPS. In order to solve that, a filler NAL must be added between the headers and the first frame to preserve alignment. [mchehab@redhat.com: Fix a few CodingStyle issues] Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda.c | 36 ++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 7b8b547f2d51..8b7f5ac7fc11 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -178,6 +178,9 @@ struct coda_ctx { int idx; }; +static u8 coda_filler_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x0c, + 0xff, 0xff, 0xff, 0xff, 0xff}; + static inline void coda_write(struct coda_dev *dev, u32 data, u32 reg) { v4l2_dbg(1, coda_debug, &dev->v4l2_dev, @@ -944,6 +947,29 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d return 0; } +static int coda_h264_padding(int size, char *p) +{ + int size_align = size & ~0x3; + int filler_size = ARRAY_SIZE(coda_filler_nal); + int nal_size; + int diff; + + diff = size - size_align; + if (diff == 0) + return 0; + + nal_size = filler_size + 2 - diff; + if (nal_size > filler_size) + nal_size -= 4; + + memcpy(p, coda_filler_nal, nal_size); + + /* Add rbsp stop bit and trailing at the end */ + *(p + nal_size - 1) = 0x80; + + return nal_size; +} + static int coda_start_streaming(struct vb2_queue *q, unsigned int count) { struct coda_ctx *ctx = vb2_get_drv_priv(q); @@ -1171,7 +1197,15 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0), ctx->vpu_header_size[1]); - ctx->vpu_header_size[2] = 0; + /* + * Length of H.264 headers is variable and thus it might not be + * aligned for the coda to append the encoded frame. In that is + * the case a filler NAL must be added to header 2. + */ + ctx->vpu_header_size[2] = coda_h264_padding( + (ctx->vpu_header_size[0] + + ctx->vpu_header_size[1]), + ctx->vpu_header[2]); break; case V4L2_PIX_FMT_MPEG4: /* From 832fbb5aec6ec877ed9273a0b20520e3dc0b23b3 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Mon, 29 Oct 2012 08:34:59 -0300 Subject: [PATCH 0314/4607] [media] media: coda: Fix H.264 header alignment - v2 Length of H.264 headers is variable and thus it might not be aligned for the coda to append the encoded frame. This causes the first frame to overwrite part of the H.264 PPS. In order to solve that, a filler NAL must be added between the headers and the first frame to preserve alignment. [mchehab@redhat.com: applied only v2 diff here, as v1 ended by mistakenly being applied] Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/coda.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 8b7f5ac7fc11..2721f839852f 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -178,8 +178,9 @@ struct coda_ctx { int idx; }; -static u8 coda_filler_nal[] = { 0x00, 0x00, 0x00, 0x01, 0x0c, - 0xff, 0xff, 0xff, 0xff, 0xff}; +static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 }; +static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 }; static inline void coda_write(struct coda_dev *dev, u32 data, u32 reg) { @@ -949,19 +950,14 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d static int coda_h264_padding(int size, char *p) { - int size_align = size & ~0x3; - int filler_size = ARRAY_SIZE(coda_filler_nal); int nal_size; int diff; - diff = size - size_align; + diff = size - (size & ~0x7); if (diff == 0) return 0; - nal_size = filler_size + 2 - diff; - if (nal_size > filler_size) - nal_size -= 4; - + nal_size = coda_filler_size[diff]; memcpy(p, coda_filler_nal, nal_size); /* Add rbsp stop bit and trailing at the end */ From 37e310edf3f6090eb118b7fcafd00221c290ac6b Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Tue, 30 Oct 2012 11:09:41 -0300 Subject: [PATCH 0315/4607] [media] drivers/media/pci/saa7134/saa7134-dvb.c: Test if videobuf_dvb_get_frontend return NULL Based on commit: e66131cee501ee720b7b58a4b87073b8fbaaaba6 Not testing videobuf_dvb_get_frontend output may cause OOPS if it return NULL. This patch fixes this issue. The semantic patch that found this issue is(http://coccinelle.lip6.fr/): // @@ identifier i,a,b; statement S, S2; @@ i = videobuf_dvb_get_frontend(...); ... when != if (!i) S * if (i->a.b) S2 // Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7134/saa7134-dvb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c index b209de40a4f8..27915e501db9 100644 --- a/drivers/media/pci/saa7134/saa7134-dvb.c +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -607,6 +607,9 @@ static int configure_tda827x_fe(struct saa7134_dev *dev, /* Get the first frontend */ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); + if (!fe0) + return -EINVAL; + fe0->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap); if (fe0->dvb.frontend) { if (cdec_conf->i2c_gate) From 202724096816ebf5c6557719f6a2d6faf6371f9a Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 30 Oct 2012 11:12:32 -0300 Subject: [PATCH 0316/4607] [media] media: m2m-deinterlace: Do not set debugging flag to true Default value should be 'debugging disabled'. Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/m2m-deinterlace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 05c560f2ef06..ed77a645e992 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -28,7 +28,7 @@ MODULE_AUTHOR("Javier Martin Date: Tue, 30 Oct 2012 12:04:23 -0300 Subject: [PATCH 0317/4607] [media] media: ov7670: Allow 32x maximum gain for yuv422 4x gain ceiling is not enough to capture a decent image in conditions of total darkness and only a LED light source. Allow a maximum gain of 32x instead. This doesn't have any drawback since the image quality in 'normal' light conditions is the same. Signed-off-by: Javier Martin Acked-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index e7c82b297514..882ddf6f66d3 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -353,7 +353,7 @@ static struct regval_list ov7670_fmt_yuv422[] = { { REG_RGB444, 0 }, /* No RGB444 please */ { REG_COM1, 0 }, /* CCIR601 */ { REG_COM15, COM15_R00FF }, - { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ + { REG_COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */ { 0x4f, 0x80 }, /* "matrix coefficient 1" */ { 0x50, 0x80 }, /* "matrix coefficient 2" */ { 0x51, 0 }, /* vb */ From 6dc8f3823e52f3b9a127d79ca7b8bc782a5cfd7e Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Wed, 31 Oct 2012 11:52:45 -0300 Subject: [PATCH 0318/4607] [media] staging: media: Fix minor typo in staging/media Correct spelling typo in comment witin staging/media. Signed-off-by: Masanari Iida Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/as102/as10x_cmd_cfg.c | 2 +- drivers/staging/media/dt3155v4l/dt3155v4l.c | 2 +- drivers/staging/media/lirc/lirc_sasem.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/as102/as10x_cmd_cfg.c b/drivers/staging/media/as102/as10x_cmd_cfg.c index d2a4bce89623..4a2bbd766655 100644 --- a/drivers/staging/media/as102/as10x_cmd_cfg.c +++ b/drivers/staging/media/as102/as10x_cmd_cfg.c @@ -197,7 +197,7 @@ out: * @prsp: pointer to AS10x command response buffer * @proc_id: id of the command * - * Since the contex command reponse does not follow the common + * Since the contex command response does not follow the common * response, a specific parse function is required. * Return 0 on success or negative value in case of error. */ diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c index 54f1813aa1ef..0af8917ebc57 100644 --- a/drivers/staging/media/dt3155v4l/dt3155v4l.c +++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c @@ -785,7 +785,7 @@ dt3155_init_board(struct pci_dev *pdev) } write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */ - /* select chanel 1 for input and set sync level */ + /* select channel 1 for input and set sync level */ write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index f4e4d9003f38..101ac12eb381 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -891,7 +891,7 @@ exit: } /** - * Callback function for USB core API: disonnect + * Callback function for USB core API: disconnect */ static void sasem_disconnect(struct usb_interface *interface) { From cb31c7487580a0cfc6eb253e604c1e51ac8eb3c8 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Thu, 1 Nov 2012 16:24:30 -0300 Subject: [PATCH 0319/4607] [media] budget-av: only use t_state if initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building budget-av.o triggers this GCC warning: In file included from drivers/media/pci/ttpci/budget-av.c:44:0: drivers/media/dvb-frontends/tda8261_cfg.h: In function ‘tda8261_get_bandwidth’: drivers/media/dvb-frontends/tda8261_cfg.h:68:21: warning: ‘t_state.bandwidth’ may be used uninitialized in this function [-Wuninitialized] Move the printk() that uses t_state.bandwith to the location where it should be initialized to fix this. Signed-off-by: Paul Bolle Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/tda8261_cfg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/tda8261_cfg.h b/drivers/media/dvb-frontends/tda8261_cfg.h index 1af1ee49b542..46710744173b 100644 --- a/drivers/media/dvb-frontends/tda8261_cfg.h +++ b/drivers/media/dvb-frontends/tda8261_cfg.h @@ -78,7 +78,7 @@ static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) return err; } *bandwidth = t_state.bandwidth; + printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); } - printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); return 0; } From ed2e33011451f655052ff1d3aa9ee936057d508b Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Thu, 1 Nov 2012 17:00:09 -0300 Subject: [PATCH 0320/4607] [media] tda18212: tda18218: use 'val' if initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commits e666a44fa313cb9329c0381ad02fc6ee1e21cb31 ("[media] tda18212: silence compiler warning") and e0e52d4e9f5bce7ea887027c127473eb654a5a04 ("[media] tda18218: silence compiler warning") silenced warnings equivalent to these: drivers/media/tuners/tda18212.c: In function ‘tda18212_attach’: drivers/media/tuners/tda18212.c:299:2: warning: ‘val’ may be used uninitialized in this function [-Wmaybe-uninitialized] drivers/media/tuners/tda18218.c: In function ‘tda18218_attach’: drivers/media/tuners/tda18218.c:305:2: warning: ‘val’ may be used uninitialized in this function [-Wmaybe-uninitialized] But in both cases 'val' will still be used uninitialized if the calls of tda18212_rd_reg() or tda18218_rd_reg() fail. Fix this by only printing the "chip id" if the calls of those functions were successful. This allows to drop the uninitialized_var() stopgap measure. Also stop printing the return values of tda18212_rd_reg() or tda18218_rd_reg(), as these are not interesting. Signed-off-by: Paul Bolle Acked-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tda18212.c | 6 +++--- drivers/media/tuners/tda18218.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c index 5d9f02842501..e4a84ee231cf 100644 --- a/drivers/media/tuners/tda18212.c +++ b/drivers/media/tuners/tda18212.c @@ -277,7 +277,7 @@ struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, { struct tda18212_priv *priv = NULL; int ret; - u8 uninitialized_var(val); + u8 val; priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL); if (priv == NULL) @@ -296,8 +296,8 @@ struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ - dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret, - val); + if (!ret) + dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val); if (ret || val != 0xc7) { kfree(priv); return NULL; diff --git a/drivers/media/tuners/tda18218.c b/drivers/media/tuners/tda18218.c index 18198537be9f..2d31aeb6b088 100644 --- a/drivers/media/tuners/tda18218.c +++ b/drivers/media/tuners/tda18218.c @@ -277,7 +277,7 @@ struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct tda18218_config *cfg) { struct tda18218_priv *priv = NULL; - u8 uninitialized_var(val); + u8 val; int ret; /* chip default registers values */ static u8 def_regs[] = { @@ -302,8 +302,8 @@ struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, /* check if the tuner is there */ ret = tda18218_rd_reg(priv, R00_ID, &val); - dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret, - val); + if (!ret) + dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val); if (ret || val != def_regs[R00_ID]) { kfree(priv); return NULL; From 011b2aad587a770e758711f465312ac843440d2a Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Fri, 2 Nov 2012 04:48:48 -0300 Subject: [PATCH 0321/4607] [media] staging/media: Use dev_ printks in cxd2099/cxd2099.[ch] fixed below checkpatch warnings. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... Signed-off-by: YAMANE Toshiaki Tested-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/cxd2099/cxd2099.c | 29 +++++++++++++------------ drivers/staging/media/cxd2099/cxd2099.h | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c index 0ff19724992f..822c487592a4 100644 --- a/drivers/staging/media/cxd2099/cxd2099.c +++ b/drivers/staging/media/cxd2099/cxd2099.c @@ -66,8 +66,9 @@ static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n", - reg, adr); + dev_err(&adapter->dev, + "Failed to write to I2C register %02x@%02x!\n", + reg, adr); return -1; } return 0; @@ -79,7 +80,7 @@ static int i2c_write(struct i2c_adapter *adapter, u8 adr, struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len}; if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C!\n"); + dev_err(&adapter->dev, "Failed to write to I2C!\n"); return -1; } return 0; @@ -94,7 +95,7 @@ static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, .buf = val, .len = 1} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read_reg\n"); + dev_err(&adapter->dev, "error in i2c_read_reg\n"); return -1; } return 0; @@ -109,7 +110,7 @@ static int i2c_read(struct i2c_adapter *adapter, u8 adr, .buf = data, .len = n} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read\n"); + dev_err(&adapter->dev, "error in i2c_read\n"); return -1; } return 0; @@ -277,7 +278,7 @@ static void cam_mode(struct cxd *ci, int mode) #ifdef BUFFER_MODE if (!ci->en.read_data) return; - printk(KERN_INFO "enable cam buffer mode\n"); + dev_info(&ci->i2c->dev, "enable cam buffer mode\n"); /* write_reg(ci, 0x0d, 0x00); */ /* write_reg(ci, 0x0e, 0x01); */ write_regm(ci, 0x08, 0x40, 0x40); @@ -524,7 +525,7 @@ static int slot_reset(struct dvb_ca_en50221 *ca, int slot) msleep(10); #if 0 read_reg(ci, 0x06, &val); - printk(KERN_INFO "%d:%02x\n", i, val); + dev_info(&ci->i2c->dev, "%d:%02x\n", i, val); if (!(val&0x10)) break; #else @@ -542,7 +543,7 @@ static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) { struct cxd *ci = ca->data; - printk(KERN_INFO "slot_shutdown\n"); + dev_info(&ci->i2c->dev, "slot_shutdown\n"); mutex_lock(&ci->lock); write_regm(ci, 0x09, 0x08, 0x08); write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */ @@ -578,10 +579,10 @@ static int campoll(struct cxd *ci) if (istat&0x40) { ci->dr = 1; - printk(KERN_INFO "DR\n"); + dev_info(&ci->i2c->dev, "DR\n"); } if (istat&0x20) - printk(KERN_INFO "WC\n"); + dev_info(&ci->i2c->dev, "WC\n"); if (istat&2) { u8 slotstat; @@ -597,7 +598,7 @@ static int campoll(struct cxd *ci) if (ci->slot_stat) { ci->slot_stat = 0; write_regm(ci, 0x03, 0x00, 0x08); - printk(KERN_INFO "NO CAM\n"); + dev_info(&ci->i2c->dev, "NO CAM\n"); ci->ready = 0; } } @@ -634,7 +635,7 @@ static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) campoll(ci); mutex_unlock(&ci->lock); - printk(KERN_INFO "read_data\n"); + dev_info(&ci->i2c->dev, "read_data\n"); if (!ci->dr) return 0; @@ -687,7 +688,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, u8 val; if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) { - printk(KERN_INFO "No CXD2099 detected at %02x\n", cfg->adr); + dev_info(&i2c->dev, "No CXD2099 detected at %02x\n", cfg->adr); return NULL; } @@ -705,7 +706,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, ci->en = en_templ; ci->en.data = ci; init(ci); - printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->cfg.adr); + dev_info(&i2c->dev, "Attached CXD2099AR at %02x\n", ci->cfg.adr); return &ci->en; } EXPORT_SYMBOL(cxd2099_attach); diff --git a/drivers/staging/media/cxd2099/cxd2099.h b/drivers/staging/media/cxd2099/cxd2099.h index 19c588a59588..0eb607c5b423 100644 --- a/drivers/staging/media/cxd2099/cxd2099.h +++ b/drivers/staging/media/cxd2099/cxd2099.h @@ -43,7 +43,7 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, static inline struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, void *priv, struct i2c_adapter *i2c) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif From e3a8b4d22b47b3ee17baccdc5b351465f890671c Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Fri, 2 Nov 2012 09:10:30 -0300 Subject: [PATCH 0322/4607] [media] vivi: Optimize gen_text() I've noticed that vivi takes a lot of CPU to produce its frames. For example for 8 devices and 8 simple programs running, where each captures YUY2 640x480 and displays it to X via SDL, profile timing is as follows: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # Samples: 82K of event 'cycles' # Event count (approx.): 31551930117 # # Overhead Command Shared Object Symbol # ........ ............... .................... # 49.48% vivi-* [vivi] [k] gen_twopix 10.79% vivi-* [kernel.kallsyms] [k] memcpy 10.02% rawv libc-2.13.so [.] __memcpy_ssse3 8.35% vivi-* [vivi] [k] gen_text.constprop.6 5.06% Xorg [unknown] [.] 0xa73015f8 2.32% rawv [vivi] [k] gen_twopix 1.22% rawv [vivi] [k] precalculate_line 1.20% vivi-* [vivi] [k] vivi_fillbuff (rawv is display program, vivi-* is a combination of vivi-000 through vivi-007) so a lot of time is spent in gen_twopix() which as the follwing call-graph profile shows ... 49.48% vivi-* [vivi] [k] gen_twopix | --- gen_twopix | |--96.30%-- gen_text.constprop.6 | vivi_fillbuff | vivi_thread | kthread | ret_from_kernel_thread | --3.70%-- vivi_fillbuff vivi_thread kthread ret_from_kernel_thread ... is called mostly from gen_text(). If we'll look at gen_text(), in the inner loop, we'll see if (chr & (1 << (7 - i))) gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1); else gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1); which calls gen_twopix() for every character pixel, and that is very expensive, because gen_twopix() branches several times. Now, let's note, that we operate on only two colors - WHITE and TEXT_BLACK, and that pixel for that colors could be precomputed and gen_twopix() moved out of the inner loop. Also note, that for black and white colors even/odd does not make a difference for all supported pixel formats, so we could stop doing that `odd` gen_twopix() parameter game. So the first thing we are doing here is 1) moving gen_twopix() calls out of gen_text() into vivi_fillbuff(), to pregenerate black and white colors, just before printing starts. what we have next is that gen_text's font rendering loop, even with gen_twopix() calls moved out, was inefficient and branchy, so let's 2) rewrite gen_text() loop so it uses less variables + unroll char horizontal-rendering loop + instantiate 3 code paths for pixelsizes 2,3 and 4 so that in all inner loops we don't have to branch or make indirections (*). Done all above reworks, for gen_text() we get nice, non-branchy streamlined code (showing loop for pixelsize=2): ? cmp $0x2,%eax ? ? jne 26 ? mov -0x18(%ebp),%eax ? mov -0x20(%ebp),%edi ? imul -0x20(%ebp),%eax ? movzwl 0x3ffc(%ebx),%esi 0,08 ? movzwl 0x4000(%ebx),%ecx 0,04 ? add %edi,%edi ? mov 0x0,%ebx 0,51 ? mov %edi,-0x1c(%ebp) ? mov %ebx,-0x14(%ebp) ? movl $0x0,-0x10(%ebp) ? lea 0x20(%edx,%eax,2),%eax ? mov %eax,-0x18(%ebp) ? xchg %ax,%ax 0,04 ? a0: mov 0x8(%ebp),%ebx ? mov -0x18(%ebp),%eax 0,04 ? movzbl (%ebx),%edx 0,16 ? test %dl,%dl 0,04 ? ? je 128 0,08 ? lea 0x0(%esi),%esi 1,61 ? b0:???shl $0x4,%edx 1,02 ? ? mov -0x14(%ebp),%edi 2,04 ? ? add -0x10(%ebp),%edx 2,24 ? ? lea 0x1(%ebx),%ebx 0,27 ? ? movzbl (%edi,%edx,1),%edx 9,92 ? ? mov %esi,%edi 0,39 ? ? test %dl,%dl 2,04 ? ? cmovns %ecx,%edi 4,63 ? ? test $0x40,%dl 0,55 ? ? mov %di,(%eax) 3,76 ? ? mov %esi,%edi 0,71 ? ? cmove %ecx,%edi 3,41 ? ? test $0x20,%dl 0,75 ? ? mov %di,0x2(%eax) 2,43 ? ? mov %esi,%edi 0,59 ? ? cmove %ecx,%edi 4,59 ? ? test $0x10,%dl 0,67 ? ? mov %di,0x4(%eax) 2,55 ? ? mov %esi,%edi 0,78 ? ? cmove %ecx,%edi 4,31 ? ? test $0x8,%dl 0,67 ? ? mov %di,0x6(%eax) 5,76 ? ? mov %esi,%edi 1,80 ? ? cmove %ecx,%edi 4,20 ? ? test $0x4,%dl 0,86 ? ? mov %di,0x8(%eax) 2,98 ? ? mov %esi,%edi 1,37 ? ? cmove %ecx,%edi 4,67 ? ? test $0x2,%dl 0,20 ? ? mov %di,0xa(%eax) 2,78 ? ? mov %esi,%edi 0,75 ? ? cmove %ecx,%edi 3,92 ? ? and $0x1,%edx 0,75 ? ? mov %esi,%edx 2,59 ? ? mov %di,0xc(%eax) 0,59 ? ? cmove %ecx,%edx 3,10 ? ? mov %dx,0xe(%eax) 2,39 ? ? add $0x10,%eax 0,51 ? ? movzbl (%ebx),%edx 2,86 ? ? test %dl,%dl 2,31 ? ???jne b0 0,04 ?128: addl $0x1,-0x10(%ebp) 4,00 ? mov -0x1c(%ebp),%eax 0,04 ? add %eax,-0x18(%ebp) 0,08 ? cmpl $0x10,-0x10(%ebp) ? ? jne a0 which almost goes away from the profile: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # Samples: 49K of event 'cycles' # Event count (approx.): 16799780016 # # Overhead Command Shared Object Symbol # ........ ............... .................... # 27.51% rawv libc-2.13.so [.] __memcpy_ssse3 23.77% vivi-* [kernel.kallsyms] [k] memcpy 9.96% Xorg [unknown] [.] 0xa76f5e12 4.94% vivi-* [vivi] [k] gen_text.constprop.6 4.44% rawv [vivi] [k] gen_twopix 3.17% vivi-* [vivi] [k] vivi_fillbuff 2.45% rawv [vivi] [k] precalculate_line 1.20% swapper [kernel.kallsyms] [k] read_hpet i.e. gen_twopix() overhead dropped from 49% to 4% and gen_text() loops from ~8% to ~4%, and overal cycles count dropped from 31551930117 to 16799780016 which is ~1.9x whole workload speedup. (*) for RGB24 rendering I've introduced x24, which could be thought as synthetic u24 for simplifying the code. That's done because for memcpy used for conditional assignment, gcc generates suboptimal code with more indirections. Fortunately, in C struct assignment is builtin and that's all we need from pixeltype for font rendering. Signed-off-by: Kirill Smelkov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 59 +++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index c2f424f32450..3801a8d00946 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -240,6 +240,7 @@ struct vivi_dev { u8 line[MAX_WIDTH * 8]; unsigned int pixelsize; u8 alpha_component; + u32 textfg, textbg; }; /* ------------------------------------------------------------------ @@ -520,33 +521,54 @@ static void precalculate_line(struct vivi_dev *dev) } } +/* need this to do rgb24 rendering */ +typedef struct { u16 __; u8 _; } __attribute__((packed)) x24; + static void gen_text(struct vivi_dev *dev, char *basep, int y, int x, char *text) { int line; + unsigned int width = dev->width; /* Checks if it is possible to show string */ - if (y + 16 >= dev->height || x + strlen(text) * 8 >= dev->width) + if (y + 16 >= dev->height || x + strlen(text) * 8 >= width) return; /* Print stream time */ - for (line = y; line < y + 16; line++) { - int j = 0; - char *pos = basep + line * dev->width * dev->pixelsize + x * dev->pixelsize; - char *s; +#define PRINTSTR(PIXTYPE) do { \ + PIXTYPE fg; \ + PIXTYPE bg; \ + memcpy(&fg, &dev->textfg, sizeof(PIXTYPE)); \ + memcpy(&bg, &dev->textbg, sizeof(PIXTYPE)); \ + \ + for (line = 0; line < 16; line++) { \ + PIXTYPE *pos = (PIXTYPE *)( basep + ((y + line) * width + x) * sizeof(PIXTYPE) ); \ + u8 *s; \ + \ + for (s = text; *s; s++) { \ + u8 chr = font8x16[*s * 16 + line]; \ + \ + pos[0] = (chr & (0x01 << 7) ? fg : bg); \ + pos[1] = (chr & (0x01 << 6) ? fg : bg); \ + pos[2] = (chr & (0x01 << 5) ? fg : bg); \ + pos[3] = (chr & (0x01 << 4) ? fg : bg); \ + pos[4] = (chr & (0x01 << 3) ? fg : bg); \ + pos[5] = (chr & (0x01 << 2) ? fg : bg); \ + pos[6] = (chr & (0x01 << 1) ? fg : bg); \ + pos[7] = (chr & (0x01 << 0) ? fg : bg); \ + \ + pos += 8; \ + } \ + } \ +} while (0) - for (s = text; *s; s++) { - u8 chr = font8x16[*s * 16 + line - y]; - int i; - - for (i = 0; i < 7; i++, j++) { - /* Draw white font on black background */ - if (chr & (1 << (7 - i))) - gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1); - else - gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1); - } - } + switch (dev->pixelsize) { + case 2: + PRINTSTR(u16); break; + case 4: + PRINTSTR(u32); break; + case 3: + PRINTSTR(x24); break; } } @@ -570,6 +592,9 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) /* Updates stream time */ + gen_twopix(dev, (u8 *)&dev->textbg, TEXT_BLACK, /*odd=*/ 0); + gen_twopix(dev, (u8 *)&dev->textfg, WHITE, /*odd=*/ 0); + dev->ms += jiffies_to_msecs(jiffies - dev->jiffies); dev->jiffies = jiffies; ms = dev->ms; From 10ce84415836d3fbf907367b9eb595a0dfd6ccb1 Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Fri, 2 Nov 2012 09:10:31 -0300 Subject: [PATCH 0323/4607] [media] vivi: vivi_dev->line[] was not aligned Though dev->line[] is u8 array we work with it as with u16, u24 or u32 pixels, and also pass it to memcpy() and it's better to align it to at least 4. Before the patch, on x86 offsetof(vivi_dev, line) was 1003 and after patch it is 1004. There is slight performance increase, but I think is is slight, only because we start copying not from line[0]: ---- 8< ---- drivers/media/platform/vivi.c static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { ... for (h = 0; h < hmax; h++) memcpy(vbuf + h * wmax * dev->pixelsize, dev->line + (dev->mv_count % wmax) * dev->pixelsize, wmax * dev->pixelsize); before: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 49K of event 'cycles' # Event count (approx.): 16799780016 # # Overhead Command Shared Object # ........ ............... .................... # 27.51% rawv libc-2.13.so [.] __memcpy_ssse3 23.77% vivi-* [kernel.kallsyms] [k] memcpy 9.96% Xorg [unknown] [.] 0xa76f5e12 4.94% vivi-* [vivi] [k] gen_text.constprop.6 4.44% rawv [vivi] [k] gen_twopix 3.17% vivi-* [vivi] [k] vivi_fillbuff 2.45% rawv [vivi] [k] precalculate_line 1.20% swapper [kernel.kallsyms] [k] read_hpet 23.77% vivi-* [kernel.kallsyms] [k] memcpy | --- memcpy | |--99.28%-- vivi_fillbuff | vivi_thread | kthread | ret_from_kernel_thread --0.72%-- [...] after: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 49K of event 'cycles' # Event count (approx.): 16475832370 # # Overhead Command Shared Object # ........ ............... ...................... # 29.07% rawv libc-2.13.so [.] __memcpy_ssse3 20.57% vivi-* [kernel.kallsyms] [k] memcpy 10.20% Xorg [unknown] [.] 0xa7301494 5.16% vivi-* [vivi] [k] gen_text.constprop.6 4.43% rawv [vivi] [k] gen_twopix 4.36% vivi-* [vivi] [k] vivi_fillbuff 2.42% rawv [vivi] [k] precalculate_line 1.33% swapper [kernel.kallsyms] [k] read_hpet Signed-off-by: Kirill Smelkov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 3801a8d00946..a3090f0312c1 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -237,7 +237,7 @@ struct vivi_dev { unsigned int field_count; u8 bars[9][3]; - u8 line[MAX_WIDTH * 8]; + u8 line[MAX_WIDTH * 8] __attribute__((__aligned__(4))); unsigned int pixelsize; u8 alpha_component; u32 textfg, textbg; From 13908f330f91996b6d59355aa7860553dd1093d5 Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Fri, 2 Nov 2012 09:10:32 -0300 Subject: [PATCH 0324/4607] [media] vivi: Move computations out of vivi_fillbuf linecopy loop The "dev->mvcount % wmax" thing was showing high in profiles (we do it for each line which ~ 500 per frame) ? 000010c0 : ... 0,39 ? 70:???mov 0x3ff4(%edi),%esi 0,22 ? 76:? mov 0x2a0(%edi),%eax 0,30 ? ? mov -0x84(%ebp),%ebx 0,35 ? ? mov %eax,%edx 0,04 ? ? mov -0x7c(%ebp),%ecx 0,35 ? ? sar $0x1f,%edx 0,44 ? ? idivl -0x7c(%ebp) 21,68 ? ? imul %esi,%ecx 0,70 ? ? imul %esi,%ebx 0,52 ? ? add -0x88(%ebp),%ebx 1,65 ? ? mov %ebx,%eax 0,22 ? ? imul %edx,%esi 0,04 ? ? lea 0x3f4(%edi,%esi,1),%edx 2,18 ? ?? call vivi_fillbuff+0xa6 0,74 ? ? addl $0x1,-0x80(%ebp) 62,69 ? ? mov -0x7c(%ebp),%edx 1,18 ? ? mov -0x80(%ebp),%ecx 0,35 ? ? add %edx,-0x84(%ebp) 0,61 ? ? cmp %ecx,-0x8c(%ebp) 0,22 ? ???jne 70 so since all variables stay the same for all iterations let's move computations out of the loop: the abovementioned division and "width*pixelsize" too before: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 49K of event 'cycles' # Event count (approx.): 16475832370 # # Overhead Command Shared Object # ........ ............... ...................... # 29.07% rawv libc-2.13.so [.] __memcpy_ssse3 20.57% vivi-* [kernel.kallsyms] [k] memcpy 10.20% Xorg [unknown] [.] 0xa7301494 5.16% vivi-* [vivi] [k] gen_text.constprop.6 4.43% rawv [vivi] [k] gen_twopix 4.36% vivi-* [vivi] [k] vivi_fillbuff 2.42% rawv [vivi] [k] precalculate_line 1.33% swapper [kernel.kallsyms] [k] read_hpet after: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 46K of event 'cycles' # Event count (approx.): 15574200568 # # Overhead Command Shared Object # ........ ............... .................... # 27.99% rawv libc-2.13.so [.] __memcpy_ssse3 23.29% vivi-* [kernel.kallsyms] [k] memcpy 10.30% Xorg [unknown] [.] 0xa75c98f8 5.34% vivi-* [vivi] [k] gen_text.constprop.6 4.61% rawv [vivi] [k] gen_twopix 2.64% rawv [vivi] [k] precalculate_line 1.37% swapper [kernel.kallsyms] [k] read_hpet 0.79% Xorg [kernel.kallsyms] [k] read_hpet 0.64% Xorg [kernel.kallsyms] [k] unix_poll 0.45% Xorg [kernel.kallsyms] [k] fget_light 0.43% rawv libxcb.so.1.1.0 [.] 0x0000aae9 0.40% runsv [kernel.kallsyms] [k] ext2_try_to_allocate 0.36% Xorg [kernel.kallsyms] [k] _raw_spin_lock_irqsave 0.31% vivi-* [vivi] [k] vivi_fillbuff (i.e. vivi_fillbuff own overhead is almost gone) Signed-off-by: Kirill Smelkov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index a3090f0312c1..ec832e9461a8 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -574,21 +574,22 @@ static void gen_text(struct vivi_dev *dev, char *basep, static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { - int wmax = dev->width; + int stride = dev->width * dev->pixelsize; int hmax = dev->height; void *vbuf = vb2_plane_vaddr(&buf->vb, 0); unsigned ms; char str[100]; int h, line = 1; + u8 *linestart; s32 gain; if (!vbuf) return; + linestart = dev->line + (dev->mv_count % dev->width) * dev->pixelsize; + for (h = 0; h < hmax; h++) - memcpy(vbuf + h * wmax * dev->pixelsize, - dev->line + (dev->mv_count % wmax) * dev->pixelsize, - wmax * dev->pixelsize); + memcpy(vbuf + h * stride, linestart, stride); /* Updates stream time */ From d40fbf8d52ae6c9b7fe9d76eeab624afc3a3f1ea Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Fri, 2 Nov 2012 09:10:33 -0300 Subject: [PATCH 0325/4607] [media] vivi: Optimize precalculate_line() precalculate_line() is not very high on profile, but it calls expensive gen_twopix(), so let's polish it too: call gen_twopix() only once for every color bar and then distribute the result. before: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 46K of event 'cycles' # Event count (approx.): 15574200568 # # Overhead Command Shared Object # ........ ............... .................... # 27.99% rawv libc-2.13.so [.] __memcpy_ssse3 23.29% vivi-* [kernel.kallsyms] [k] memcpy 10.30% Xorg [unknown] [.] 0xa75c98f8 5.34% vivi-* [vivi] [k] gen_text.constprop.6 4.61% rawv [vivi] [k] gen_twopix 2.64% rawv [vivi] [k] precalculate_line 1.37% swapper [kernel.kallsyms] [k] read_hpet after: # cmdline : /home/kirr/local/perf/bin/perf record -g -a sleep 20 # # Samples: 45K of event 'cycles' # Event count (approx.): 15561769214 # # Overhead Command Shared Object # ........ ............... .................... # 30.73% rawv libc-2.13.so [.] __memcpy_ssse3 26.78% vivi-* [kernel.kallsyms] [k] memcpy 10.68% Xorg [unknown] [.] 0xa73015e9 5.55% vivi-* [vivi] [k] gen_text.constprop.6 1.36% swapper [kernel.kallsyms] [k] read_hpet 0.96% Xorg [kernel.kallsyms] [k] read_hpet ... 0.16% rawv [vivi] [k] precalculate_line ... 0.14% rawv [vivi] [k] gen_twopix (i.e. gen_twopix and precalculate_line overheads are almost gone) Signed-off-by: Kirill Smelkov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index ec832e9461a8..7abb046e0fa5 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -512,12 +512,22 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) static void precalculate_line(struct vivi_dev *dev) { - int w; + unsigned pixsize = dev->pixelsize; + unsigned pixsize2 = 2*pixsize; + int colorpos; + u8 *pos; - for (w = 0; w < dev->width * 2; w++) { - int colorpos = w / (dev->width / 8) % 8; + for (colorpos = 0; colorpos < 16; ++colorpos) { + u8 pix[8]; + int wstart = colorpos * dev->width / 8; + int wend = (colorpos+1) * dev->width / 8; + int w; - gen_twopix(dev, dev->line + w * dev->pixelsize, colorpos, w & 1); + gen_twopix(dev, &pix[0], colorpos % 8, 0); + gen_twopix(dev, &pix[pixsize], colorpos % 8, 1); + + for (w = wstart/2*2, pos = dev->line + w*pixsize; w < wend; w += 2, pos += pixsize2) + memcpy(pos, pix, pixsize2); } } From 70ef69915b1fba4ad85aebe530caf156a144c2e5 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 2 Nov 2012 09:13:54 -0300 Subject: [PATCH 0326/4607] [media] rc: Make probe cleanup goto labels more verbose Before, labels were simply numbered. Now, the labels are named after the cleanup action they'll perform (first), based on how the winbond-cir driver does it. This makes the code a bit more clear and makes changes in the ordering of labels easier to review. This change is applied only to the rc drivers that do significant cleanup in their probe functions: ati-remote, ene-ir, fintek-cir, gpio-ir-recv, ite-cir, nuvoton-cir. This commit should not change any code, it just renames goto labels. [mchehab@redhat.com: removed changes at gpio-ir-recv.c, due to merge conflicts] Signed-off-by: Matthijs Kooijman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ati_remote.c | 27 ++++++++++++++++----------- drivers/media/rc/ene_ir.c | 20 ++++++++++---------- drivers/media/rc/fintek-cir.c | 20 ++++++++++---------- drivers/media/rc/ite-cir.c | 18 +++++++++--------- drivers/media/rc/nuvoton-cir.c | 30 +++++++++++++++--------------- 5 files changed, 60 insertions(+), 55 deletions(-) diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index 2d6fb26a0170..4d6a63fe6c5e 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -872,11 +872,11 @@ static int ati_remote_probe(struct usb_interface *interface, ati_remote = kzalloc(sizeof (struct ati_remote), GFP_KERNEL); rc_dev = rc_allocate_device(); if (!ati_remote || !rc_dev) - goto fail1; + goto exit_free_dev_rdev; /* Allocate URB buffers, URBs */ if (ati_remote_alloc_buffers(udev, ati_remote)) - goto fail2; + goto exit_free_buffers; ati_remote->endpoint_in = endpoint_in; ati_remote->endpoint_out = endpoint_out; @@ -924,12 +924,12 @@ static int ati_remote_probe(struct usb_interface *interface, /* Device Hardware Initialization - fills in ati_remote->idev from udev. */ err = ati_remote_initialize(ati_remote); if (err) - goto fail3; + goto exit_kill_urbs; /* Set up and register rc device */ err = rc_register_device(ati_remote->rdev); if (err) - goto fail3; + goto exit_kill_urbs; /* use our delay for rc_dev */ ati_remote->rdev->input_dev->rep[REP_DELAY] = repeat_delay; @@ -939,7 +939,7 @@ static int ati_remote_probe(struct usb_interface *interface, input_dev = input_allocate_device(); if (!input_dev) { err = -ENOMEM; - goto fail4; + goto exit_unregister_device; } ati_remote->idev = input_dev; @@ -947,19 +947,24 @@ static int ati_remote_probe(struct usb_interface *interface, err = input_register_device(input_dev); if (err) - goto fail5; + goto exit_free_input_device; } usb_set_intfdata(interface, ati_remote); return 0; - fail5: input_free_device(input_dev); - fail4: rc_unregister_device(rc_dev); + exit_free_input_device: + input_free_device(input_dev); + exit_unregister_device: + rc_unregister_device(rc_dev); rc_dev = NULL; - fail3: usb_kill_urb(ati_remote->irq_urb); + exit_kill_urbs: + usb_kill_urb(ati_remote->irq_urb); usb_kill_urb(ati_remote->out_urb); - fail2: ati_remote_free_buffers(ati_remote); - fail1: rc_free_device(rc_dev); + exit_free_buffers: + ati_remote_free_buffers(ati_remote); + exit_free_dev_rdev: + rc_free_device(rc_dev); kfree(ati_remote); return err; } diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index 22231dd4f62b..f7fdfea49ab1 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1003,7 +1003,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL); rdev = rc_allocate_device(); if (!dev || !rdev) - goto failure; + goto exit_free_dev_rdev; /* validate resources */ error = -ENODEV; @@ -1014,10 +1014,10 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) if (!pnp_port_valid(pnp_dev, 0) || pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE) - goto failure; + goto exit_free_dev_rdev; if (!pnp_irq_valid(pnp_dev, 0)) - goto failure; + goto exit_free_dev_rdev; spin_lock_init(&dev->hw_lock); @@ -1033,7 +1033,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) /* detect hardware version and features */ error = ene_hw_detect(dev); if (error) - goto failure; + goto exit_free_dev_rdev; if (!dev->hw_learning_and_tx_capable && txsim) { dev->hw_learning_and_tx_capable = true; @@ -1078,27 +1078,27 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) /* claim the resources */ error = -EBUSY; if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) { - goto failure; + goto exit_free_dev_rdev; } dev->irq = pnp_irq(pnp_dev, 0); if (request_irq(dev->irq, ene_isr, IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) { - goto failure2; + goto exit_release_hw_io; } error = rc_register_device(rdev); if (error < 0) - goto failure3; + goto exit_free_irq; pr_notice("driver has been successfully loaded\n"); return 0; -failure3: +exit_free_irq: free_irq(dev->irq, dev); -failure2: +exit_release_hw_io: release_region(dev->hw_io, ENE_IO_SIZE); -failure: +exit_free_dev_rdev: rc_free_device(rdev); kfree(dev); return error; diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 936c3f79b62c..3d5e57cacf31 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -500,18 +500,18 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; ret = -ENODEV; /* validate pnp resources */ if (!pnp_port_valid(pdev, 0)) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "IR PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } fintek->cir_addr = pnp_port_start(pdev, 0); @@ -528,7 +528,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id ret = fintek_hw_detect(fintek); if (ret) - goto failure; + goto exit_free_dev_rdev; /* Initialize CIR & CIR Wake Logical Devices */ fintek_config_mode_enable(fintek); @@ -561,15 +561,15 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* now claim resources */ if (!request_region(fintek->cir_addr, fintek->cir_port_len, FINTEK_DRIVER_NAME)) - goto failure; + goto exit_free_dev_rdev; if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED, FINTEK_DRIVER_NAME, (void *)fintek)) - goto failure2; + goto exit_free_cir_addr; ret = rc_register_device(rdev); if (ret) - goto failure3; + goto exit_free_irq; device_init_wakeup(&pdev->dev, true); fintek->rdev = rdev; @@ -579,11 +579,11 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id return 0; -failure3: +exit_free_irq: free_irq(fintek->cir_irq, fintek); -failure2: +exit_free_cir_addr: release_region(fintek->cir_addr, fintek->cir_port_len); -failure: +exit_free_dev_rdev: rc_free_device(rdev); kfree(fintek); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 5e5a7f2b8184..8e0e661b5ef9 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1472,7 +1472,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; itdev->rdev = rdev; ret = -ENODEV; @@ -1498,12 +1498,12 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id if (!pnp_port_valid(pdev, io_rsrc_no) || pnp_port_len(pdev, io_rsrc_no) != dev_desc->io_region_size) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } /* store resource values */ @@ -1595,25 +1595,25 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id /* now claim resources */ if (!request_region(itdev->cir_addr, dev_desc->io_region_size, ITE_DRIVER_NAME)) - goto failure; + goto exit_free_dev_rdev; if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED, ITE_DRIVER_NAME, (void *)itdev)) - goto failure2; + goto exit_release_cir_addr; ret = rc_register_device(rdev); if (ret) - goto failure3; + goto exit_free_irq; ite_pr(KERN_NOTICE, "driver has been successfully loaded\n"); return 0; -failure3: +exit_free_irq: free_irq(itdev->cir_irq, itdev); -failure2: +exit_release_cir_addr: release_region(itdev->cir_addr, itdev->params.io_region_size); -failure: +exit_free_dev_rdev: rc_free_device(rdev); kfree(itdev); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index e4ea89a11eed..3477e231c182 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -986,25 +986,25 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* input device for IR remote (and tx) */ rdev = rc_allocate_device(); if (!rdev) - goto failure; + goto exit_free_dev_rdev; ret = -ENODEV; /* validate pnp resources */ if (!pnp_port_valid(pdev, 0) || pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) { dev_err(&pdev->dev, "IR PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_irq_valid(pdev, 0)) { dev_err(&pdev->dev, "PNP IRQ not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } if (!pnp_port_valid(pdev, 1) || pnp_port_len(pdev, 1) < CIR_IOREG_LENGTH) { dev_err(&pdev->dev, "Wake PNP Port not valid!\n"); - goto failure; + goto exit_free_dev_rdev; } nvt->cir_addr = pnp_port_start(pdev, 0); @@ -1027,7 +1027,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) ret = nvt_hw_detect(nvt); if (ret) - goto failure; + goto exit_free_dev_rdev; /* Initialize CIR & CIR Wake Logical Devices */ nvt_efm_enable(nvt); @@ -1070,23 +1070,23 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* now claim resources */ if (!request_region(nvt->cir_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto failure; + goto exit_free_dev_rdev; if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure2; + goto exit_release_cir_addr; if (!request_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto failure3; + goto exit_free_irq; if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure4; + goto exit_release_cir_wake_addr; ret = rc_register_device(rdev); if (ret) - goto failure5; + goto exit_free_wake_irq; device_init_wakeup(&pdev->dev, true); nvt->rdev = rdev; @@ -1098,15 +1098,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) return 0; -failure5: +exit_free_wake_irq: free_irq(nvt->cir_wake_irq, nvt); -failure4: +exit_release_cir_wake_addr: release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); -failure3: +exit_free_irq: free_irq(nvt->cir_irq, nvt); -failure2: +exit_release_cir_addr: release_region(nvt->cir_addr, CIR_IOREG_LENGTH); -failure: +exit_free_dev_rdev: rc_free_device(rdev); kfree(nvt); From d62b6818477704683d00c680335eff5833bd3906 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 2 Nov 2012 09:13:55 -0300 Subject: [PATCH 0327/4607] [media] rc: Set rdev before irq setup This fixes a problem in fintek-cir and nuvoton-cir where the irq handler would trigger during module load before the rdev member was set, causing a NULL pointer crash. It seems this crash is very reproducible (just bombard the receiver with IR signals during module load), probably because when request_irq is called, any pending intterupt is handled immediately, before request_irq returns and rdev can be set. This same crash was supposed to be fixed by commit 9ef449c6b31bb6a8e6dedc24de475a3b8c79be20 ("[media] rc: Postpone ISR registration"), but the crash was still observed on the nuvoton-cir driver. This commit was tested on nuvoton-cir only. Cc: Jarod Wilson Signed-off-by: Matthijs Kooijman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/fintek-cir.c | 4 +++- drivers/media/rc/nuvoton-cir.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 3d5e57cacf31..5eefe65760a4 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -557,6 +557,8 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */ rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD); + fintek->rdev = rdev; + ret = -EBUSY; /* now claim resources */ if (!request_region(fintek->cir_addr, @@ -572,7 +574,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id goto exit_free_irq; device_init_wakeup(&pdev->dev, true); - fintek->rdev = rdev; + fit_pr(KERN_NOTICE, "driver has been successfully loaded\n"); if (debug) cir_dump_regs(fintek); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 3477e231c182..c6441e690dd4 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1065,6 +1065,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* tx bits */ rdev->tx_resolution = XYZ; #endif + nvt->rdev = rdev; ret = -EBUSY; /* now claim resources */ @@ -1089,7 +1090,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) goto exit_free_wake_irq; device_init_wakeup(&pdev->dev, true); - nvt->rdev = rdev; + nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n"); if (debug) { cir_dump_regs(nvt); From 9fa35204dd19eb0e96ee870b7128a8f5da51dbfa Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 2 Nov 2012 09:13:56 -0300 Subject: [PATCH 0328/4607] [media] rc: Call rc_register_device before irq setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should fix a potential race condition, when the irq handler triggers while rc_register_device is still setting up the rdev->raw device. This crash has not been observed in practice, but there should be a very small window where it could occur. Since ir_raw_event_store_with_filter checks if rdev->raw is not NULL before using it, this bug is not triggered if the request_irq triggers a pending irq directly (since rdev->raw will still be NULL then). This commit was tested on nuvoton-cir only. Cc: Jarod Wilson Cc: Maxim Levitsky Cc: David Härdeman Signed-off-by: Matthijs Kooijman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ene_ir.c | 14 +++++++------- drivers/media/rc/ite-cir.c | 14 +++++++------- drivers/media/rc/nuvoton-cir.c | 14 +++++++------- drivers/media/rc/winbond-cir.c | 14 +++++++------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index f7fdfea49ab1..e601166c1edb 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1075,10 +1075,14 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) device_set_wakeup_capable(&pnp_dev->dev, true); device_set_wakeup_enable(&pnp_dev->dev, true); + error = rc_register_device(rdev); + if (error < 0) + goto exit_free_dev_rdev; + /* claim the resources */ error = -EBUSY; if (!request_region(dev->hw_io, ENE_IO_SIZE, ENE_DRIVER_NAME)) { - goto exit_free_dev_rdev; + goto exit_unregister_device; } dev->irq = pnp_irq(pnp_dev, 0); @@ -1087,17 +1091,13 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) goto exit_release_hw_io; } - error = rc_register_device(rdev); - if (error < 0) - goto exit_free_irq; - pr_notice("driver has been successfully loaded\n"); return 0; -exit_free_irq: - free_irq(dev->irq, dev); exit_release_hw_io: release_region(dev->hw_io, ENE_IO_SIZE); +exit_unregister_device: + rc_unregister_device(rdev); exit_free_dev_rdev: rc_free_device(rdev); kfree(dev); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 8e0e661b5ef9..e810846fada4 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1591,28 +1591,28 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id rdev->driver_name = ITE_DRIVER_NAME; rdev->map_name = RC_MAP_RC6_MCE; + ret = rc_register_device(rdev); + if (ret) + goto exit_free_dev_rdev; + ret = -EBUSY; /* now claim resources */ if (!request_region(itdev->cir_addr, dev_desc->io_region_size, ITE_DRIVER_NAME)) - goto exit_free_dev_rdev; + goto exit_unregister_device; if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED, ITE_DRIVER_NAME, (void *)itdev)) goto exit_release_cir_addr; - ret = rc_register_device(rdev); - if (ret) - goto exit_free_irq; - ite_pr(KERN_NOTICE, "driver has been successfully loaded\n"); return 0; -exit_free_irq: - free_irq(itdev->cir_irq, itdev); exit_release_cir_addr: release_region(itdev->cir_addr, itdev->params.io_region_size); +exit_unregister_device: + rc_unregister_device(rdev); exit_free_dev_rdev: rc_free_device(rdev); kfree(itdev); diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index c6441e690dd4..6cf43cc237eb 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1067,11 +1067,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) #endif nvt->rdev = rdev; + ret = rc_register_device(rdev); + if (ret) + goto exit_free_dev_rdev; + ret = -EBUSY; /* now claim resources */ if (!request_region(nvt->cir_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto exit_free_dev_rdev; + goto exit_unregister_device; if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) @@ -1085,10 +1089,6 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) NVT_DRIVER_NAME, (void *)nvt)) goto exit_release_cir_wake_addr; - ret = rc_register_device(rdev); - if (ret) - goto exit_free_wake_irq; - device_init_wakeup(&pdev->dev, true); nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n"); @@ -1099,14 +1099,14 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) return 0; -exit_free_wake_irq: - free_irq(nvt->cir_wake_irq, nvt); exit_release_cir_wake_addr: release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); exit_free_irq: free_irq(nvt->cir_irq, nvt); exit_release_cir_addr: release_region(nvt->cir_addr, CIR_IOREG_LENGTH); +exit_unregister_device: + rc_unregister_device(rdev); exit_free_dev_rdev: rc_free_device(rdev); kfree(nvt); diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 7f3c476dde05..553d1cdc439f 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1093,11 +1093,15 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) data->dev->rx_resolution = US_TO_NS(2); data->dev->allowed_protos = RC_BIT_ALL; + err = rc_register_device(data->dev); + if (err) + goto exit_free_rc; + if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) { dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", data->wbase, data->wbase + WAKEUP_IOMEM_LEN - 1); err = -EBUSY; - goto exit_free_rc; + goto exit_unregister_device; } if (!request_region(data->ebase, EHFUNC_IOMEM_LEN, DRVNAME)) { @@ -1122,24 +1126,20 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) goto exit_release_sbase; } - err = rc_register_device(data->dev); - if (err) - goto exit_free_irq; - device_init_wakeup(&device->dev, 1); wbcir_init_hw(data); return 0; -exit_free_irq: - free_irq(data->irq, device); exit_release_sbase: release_region(data->sbase, SP_IOMEM_LEN); exit_release_ebase: release_region(data->ebase, EHFUNC_IOMEM_LEN); exit_release_wbase: release_region(data->wbase, WAKEUP_IOMEM_LEN); +exit_unregister_device: + rc_unregister_device(data->dev); exit_free_rc: rc_free_device(data->dev); exit_unregister_led: From 6d569502b49849ae392a4453bfe3a2a973cc95d2 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Sun, 4 Nov 2012 16:40:22 -0300 Subject: [PATCH 0329/4607] [media] staging/media: Use dev_ printks in go7007/go7007-driver.c fixed below checkpatch warning. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-driver.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index ece2dd146487..c9dfc75d1d2c 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -201,7 +201,8 @@ static int init_i2c_module(struct i2c_adapter *adapter, const char *type, if (v4l2_i2c_new_subdev(v4l2_dev, adapter, type, addr, NULL)) return 0; - printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", type); + dev_info(&adapter->dev, + "go7007: probing for module i2c:%s failed\n", type); return -1; } @@ -217,7 +218,7 @@ int go7007_register_encoder(struct go7007 *go) { int i, ret; - printk(KERN_INFO "go7007: registering new %s\n", go->name); + dev_info(go->dev, "go7007: registering new %s\n", go->name); mutex_lock(&go->hw_lock); ret = go7007_init_encoder(go); From 6c629edcde1610635a7af93dbb181d5081afc194 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Sun, 4 Nov 2012 16:40:51 -0300 Subject: [PATCH 0330/4607] [media] staging/media: Use dev_ printks in go7007/wis-sony-tuner.c fixed below checkpatch warning. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... - WARNING: Prefer netdev_dbg(netdev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-sony-tuner.c | 86 +++++++++---------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/drivers/staging/media/go7007/wis-sony-tuner.c b/drivers/staging/media/go7007/wis-sony-tuner.c index 1291ab79d2af..5d7ff8c81d6d 100644 --- a/drivers/staging/media/go7007/wis-sony-tuner.c +++ b/drivers/staging/media/go7007/wis-sony-tuner.c @@ -95,8 +95,8 @@ static int set_freq(struct i2c_client *client, int freq) band_name = "UHF"; band_select = tun->UHF; } - printk(KERN_DEBUG "wis-sony-tuner: tuning to frequency %d.%04d (%s)\n", - freq / 16, (freq % 16) * 625, band_name); + dev_dbg(&client->dev, "tuning to frequency %d.%04d (%s)\n", + freq / 16, (freq % 16) * 625, band_name); n = freq + tun->IFPCoff; buffer[0] = n >> 8; @@ -288,16 +288,16 @@ static int mpx_setup(struct i2c_client *client) u8 buf1[3], buf2[2]; struct i2c_msg msgs[2]; - printk(KERN_DEBUG "wis-sony-tuner: MPX registers: %04x %04x " - "%04x %04x %04x %04x %04x %04x\n", - mpx_audio_modes[t->mpxmode].modus, - source, - mpx_audio_modes[t->mpxmode].acb, - mpx_audio_modes[t->mpxmode].fm_prescale, - mpx_audio_modes[t->mpxmode].nicam_prescale, - mpx_audio_modes[t->mpxmode].scart_prescale, - mpx_audio_modes[t->mpxmode].system, - mpx_audio_modes[t->mpxmode].volume); + dev_dbg(&client->dev, + "MPX registers: %04x %04x %04x %04x %04x %04x %04x %04x\n", + mpx_audio_modes[t->mpxmode].modus, + source, + mpx_audio_modes[t->mpxmode].acb, + mpx_audio_modes[t->mpxmode].fm_prescale, + mpx_audio_modes[t->mpxmode].nicam_prescale, + mpx_audio_modes[t->mpxmode].scart_prescale, + mpx_audio_modes[t->mpxmode].system, + mpx_audio_modes[t->mpxmode].volume); buf1[0] = 0x11; buf1[1] = 0x00; buf1[2] = 0x7e; @@ -310,14 +310,14 @@ static int mpx_setup(struct i2c_client *client) msgs[1].len = 2; msgs[1].buf = buf2; i2c_transfer(client->adapter, msgs, 2); - printk(KERN_DEBUG "wis-sony-tuner: MPX system: %02x%02x\n", - buf2[0], buf2[1]); + dev_dbg(&client->dev, "MPX system: %02x%02x\n", + buf2[0], buf2[1]); buf1[0] = 0x11; buf1[1] = 0x02; buf1[2] = 0x00; i2c_transfer(client->adapter, msgs, 2); - printk(KERN_DEBUG "wis-sony-tuner: MPX status: %02x%02x\n", - buf2[0], buf2[1]); + dev_dbg(&client->dev, "MPX status: %02x%02x\n", + buf2[0], buf2[1]); } #endif return 0; @@ -375,8 +375,7 @@ static int set_if(struct i2c_client *client) t->mpxmode = force_mpx_mode; else t->mpxmode = default_mpx_mode; - printk(KERN_DEBUG "wis-sony-tuner: setting MPX to mode %d\n", - t->mpxmode); + dev_dbg(&client->dev, "setting MPX to mode %d\n", t->mpxmode); mpx_setup(client); return 0; @@ -401,8 +400,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (t->type >= 0) { if (t->type != *type) - printk(KERN_ERR "wis-sony-tuner: type already " - "set to %d, ignoring request for %d\n", + dev_err(&client->dev, + "type already set to %d, ignoring request for %d\n", t->type, *type); break; } @@ -414,28 +413,28 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) case 'B': case 'g': case 'G': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-B/G bands\n"); + dev_info(&client->dev, + "forcing tuner to PAL-B/G bands\n"); force_band = V4L2_STD_PAL_BG; break; case 'i': case 'I': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-I band\n"); + dev_info(&client->dev, + "forcing tuner to PAL-I band\n"); force_band = V4L2_STD_PAL_I; break; case 'd': case 'D': case 'k': case 'K': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-D/K bands\n"); + dev_info(&client->dev, + "forcing tuner to PAL-D/K bands\n"); force_band = V4L2_STD_PAL_I; break; case 'l': case 'L': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to SECAM-L band\n"); + dev_info(&client->dev, + "forcing tuner to SECAM-L band\n"); force_band = V4L2_STD_SECAM_L; break; default: @@ -455,14 +454,15 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) t->std = V4L2_STD_NTSC_M; break; default: - printk(KERN_ERR "wis-sony-tuner: tuner type %d is not " - "supported by this module\n", *type); + dev_err(&client->dev, + "tuner type %d is not supported by this module\n", + *type); break; } if (type >= 0) - printk(KERN_INFO - "wis-sony-tuner: type set to %d (%s)\n", - t->type, sony_tuners[t->type - 200].name); + dev_info(&clinet->dev, + "type set to %d (%s)\n", + t->type, sony_tuners[t->type - 200].name); break; } #endif @@ -544,9 +544,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (force_band && (*std & force_band) != *std && *std != V4L2_STD_PAL && *std != V4L2_STD_SECAM) { - printk(KERN_DEBUG "wis-sony-tuner: ignoring " - "requested TV standard in " - "favor of force_band value\n"); + dev_dbg(&client->dev, + "ignoring requested TV standard in favor of force_band value\n"); t->std = force_band; } else if (*std & V4L2_STD_PAL_BG) { /* default */ t->std = V4L2_STD_PAL_BG; @@ -557,8 +556,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) } else if (*std & V4L2_STD_SECAM_L) { t->std = V4L2_STD_SECAM_L; } else { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ break; } @@ -567,15 +566,15 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) break; case TUNER_SONY_BTF_PK467Z: if (!(*std & V4L2_STD_NTSC_M_JP)) { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ } break; case TUNER_SONY_BTF_PB463Z: if (!(*std & V4L2_STD_NTSC_M)) { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); + dev_err(&client->dev, + "TV standard not supported\n"); *std = 0; /* hack to indicate EINVAL */ } break; @@ -673,8 +672,7 @@ static int wis_sony_tuner_probe(struct i2c_client *client, t->audmode = V4L2_TUNER_MODE_STEREO; i2c_set_clientdata(client, t); - printk(KERN_DEBUG - "wis-sony-tuner: initializing tuner at address %d on %s\n", + dev_dbg(&client->dev, "initializing tuner at address %d on %s\n", client->addr, adapter->name); return 0; From b11558a3026bd01436d08b7b8f92da301116c669 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Mon, 5 Nov 2012 07:34:42 -0300 Subject: [PATCH 0331/4607] [media] staging/media: Use dev_ printks in go7007/s2250-loader.c fixed below checkpatch warnings. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/s2250-loader.c | 35 +++++++++++---------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/staging/media/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c index f1bd159ac195..f57eb3beb341 100644 --- a/drivers/staging/media/go7007/s2250-loader.c +++ b/drivers/staging/media/go7007/s2250-loader.c @@ -55,16 +55,16 @@ static int s2250loader_probe(struct usb_interface *interface, usbdev = usb_get_dev(interface_to_usbdev(interface)); if (!usbdev) { - printk(KERN_ERR "Enter s2250loader_probe failed\n"); + dev_err(&interface->dev, "Enter s2250loader_probe failed\n"); return -1; } - printk(KERN_INFO "Enter s2250loader_probe 2.6 kernel\n"); - printk(KERN_INFO "vendor id 0x%x, device id 0x%x devnum:%d\n", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, - usbdev->devnum); + dev_info(&interface->dev, "Enter s2250loader_probe 2.6 kernel\n"); + dev_info(&interface->dev, "vendor id 0x%x, device id 0x%x devnum:%d\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, + usbdev->devnum); if (usbdev->descriptor.bNumConfigurations != 1) { - printk(KERN_ERR "can't handle multiple config\n"); + dev_err(&interface->dev, "can't handle multiple config\n"); return -1; } mutex_lock(&s2250_dev_table_mutex); @@ -75,31 +75,32 @@ static int s2250loader_probe(struct usb_interface *interface, } if (minor < 0 || minor >= MAX_DEVICES) { - printk(KERN_ERR "Invalid minor: %d\n", minor); + dev_err(&interface->dev, "Invalid minor: %d\n", minor); goto failed; } /* Allocate dev data structure */ s = kmalloc(sizeof(device_extension_t), GFP_KERNEL); if (s == NULL) { - printk(KERN_ERR "Out of memory\n"); + dev_err(&interface->dev, "Out of memory\n"); goto failed; } s2250_dev_table[minor] = s; - printk(KERN_INFO "s2250loader_probe: Device %d on Bus %d Minor %d\n", - usbdev->devnum, usbdev->bus->busnum, minor); + dev_info(&interface->dev, + "s2250loader_probe: Device %d on Bus %d Minor %d\n", + usbdev->devnum, usbdev->bus->busnum, minor); memset(s, 0, sizeof(device_extension_t)); s->usbdev = usbdev; - printk(KERN_INFO "loading 2250 loader\n"); + dev_info(&interface->dev, "loading 2250 loader\n"); kref_init(&(s->kref)); mutex_unlock(&s2250_dev_table_mutex); if (request_firmware(&fw, S2250_LOADER_FIRMWARE, &usbdev->dev)) { - printk(KERN_ERR + dev_err(&interface->dev, "s2250: unable to load firmware from file \"%s\"\n", S2250_LOADER_FIRMWARE); goto failed2; @@ -107,12 +108,12 @@ static int s2250loader_probe(struct usb_interface *interface, ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); release_firmware(fw); if (0 != ret) { - printk(KERN_ERR "loader download failed\n"); + dev_err(&interface->dev, "loader download failed\n"); goto failed2; } if (request_firmware(&fw, S2250_FIRMWARE, &usbdev->dev)) { - printk(KERN_ERR + dev_err(&interface->dev, "s2250: unable to load firmware from file \"%s\"\n", S2250_FIRMWARE); goto failed2; @@ -120,7 +121,7 @@ static int s2250loader_probe(struct usb_interface *interface, ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); release_firmware(fw); if (0 != ret) { - printk(KERN_ERR "firmware_s2250 download failed\n"); + dev_err(&interface->dev, "firmware_s2250 download failed\n"); goto failed2; } @@ -133,14 +134,14 @@ failed2: if (s) kref_put(&(s->kref), s2250loader_delete); - printk(KERN_ERR "probe failed\n"); + dev_err(&interface->dev, "probe failed\n"); return -1; } static void s2250loader_disconnect(struct usb_interface *interface) { pdevice_extension_t s; - printk(KERN_INFO "s2250: disconnect\n"); + dev_info(&interface->dev, "s2250: disconnect\n"); s = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); kref_put(&(s->kref), s2250loader_delete); From afc2e8a037885a8ce3a87684d742b9afb18e295c Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Mon, 5 Nov 2012 07:35:06 -0300 Subject: [PATCH 0332/4607] [media] staging/media: Use dev_ or pr_ printks in go7007/go7007-i2c.c fixed below checkpatch warnings. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_dbg(netdev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-i2c.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/go7007/go7007-i2c.c b/drivers/staging/media/go7007/go7007-i2c.c index 6bc82aaeef11..39456a36b2c6 100644 --- a/drivers/staging/media/go7007/go7007-i2c.c +++ b/drivers/staging/media/go7007/go7007-i2c.c @@ -60,10 +60,10 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, #ifdef GO7007_I2C_DEBUG if (read) - printk(KERN_DEBUG "go7007-i2c: reading 0x%02x on 0x%02x\n", + dev_dbg(go->dev, "go7007-i2c: reading 0x%02x on 0x%02x\n", command, addr); else - printk(KERN_DEBUG + dev_dbg(go->dev, "go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n", *data, command, addr); #endif @@ -85,7 +85,7 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, msleep(100); } if (i == 10) { - printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); + dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); goto i2c_done; } @@ -119,7 +119,7 @@ static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, msleep(100); } if (i == 10) { - printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); + dev_err(go->dev, "go7007-i2c: I2C adapter is hung\n"); goto i2c_done; } @@ -216,7 +216,7 @@ int go7007_i2c_init(struct go7007 *go) go->i2c_adapter.dev.parent = go->dev; i2c_set_adapdata(&go->i2c_adapter, go); if (i2c_add_adapter(&go->i2c_adapter) < 0) { - printk(KERN_ERR + dev_err(go->dev, "go7007-i2c: error: i2c_add_adapter failed\n"); return -1; } From afca99a2bf2cef3a6e5e574cb7bc0e0bdf5b3ffa Mon Sep 17 00:00:00 2001 From: Julian Scheel Date: Mon, 5 Nov 2012 11:51:05 -0300 Subject: [PATCH 0333/4607] [media] tm6000-dvb: Fix module unload dvb_unregister_frontend has to be called before detach. Otherwise the unregister call will segfault. This made tm6000-dvb module unload unusable. Signed-off-by: Julian Scheel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-dvb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c index e1f3f66e1e63..9fc1e940a82b 100644 --- a/drivers/media/usb/tm6000/tm6000-dvb.c +++ b/drivers/media/usb/tm6000/tm6000-dvb.c @@ -360,8 +360,8 @@ dvb_dmx_err: dvb_dmx_release(&dvb->demux); frontend_err: if (dvb->frontend) { - dvb_frontend_detach(dvb->frontend); dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); } adapter_err: dvb_unregister_adapter(&dvb->adapter); @@ -384,8 +384,8 @@ static void unregister_dvb(struct tm6000_core *dev) /* mutex_lock(&tm6000_driver.open_close_mutex); */ if (dvb->frontend) { - dvb_frontend_detach(dvb->frontend); dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); } dvb_dmxdev_release(&dvb->dmxdev); From 8ce21ecdf500edfa0123238f40d3fb592a44d9f3 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 08:33:26 -0300 Subject: [PATCH 0334/4607] [media] Staging/media: fixed spacing coding style in go7007/wis-tw9903.c fixed below checkpatch error. - ERROR: that open brace { should be on the previous line Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-tw9903.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/go7007/wis-tw9903.c b/drivers/staging/media/go7007/wis-tw9903.c index 94071def3bb4..854919e81547 100644 --- a/drivers/staging/media/go7007/wis-tw9903.c +++ b/drivers/staging/media/go7007/wis-tw9903.c @@ -31,8 +31,7 @@ struct wis_tw9903 { int hue; }; -static u8 initial_registers[] = -{ +static u8 initial_registers[] = { 0x02, 0x44, /* input 1, composite */ 0x03, 0x92, /* correct digital format */ 0x04, 0x00, From b2704e1540b297c06efa96851949ed060bd0d2bb Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 08:34:02 -0300 Subject: [PATCH 0335/4607] [media] Staging/media: Use dev_ printks in go7007/wis-tw9903.c fixed below checkpatch warning. - WARNING: Prefer netdev_dbg(netdev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-tw9903.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/staging/media/go7007/wis-tw9903.c b/drivers/staging/media/go7007/wis-tw9903.c index 854919e81547..684ca37f0382 100644 --- a/drivers/staging/media/go7007/wis-tw9903.c +++ b/drivers/staging/media/go7007/wis-tw9903.c @@ -127,8 +127,8 @@ static int wis_tw9903_command(struct i2c_client *client, 0x06, 0xc0, /* reset device */ 0, 0, }; - printk(KERN_DEBUG "vscale is %04x, hscale is %04x\n", - vscale, hscale); + dev_dbg(&client->dev, "vscale is %04x, hscale is %04x\n", + vscale, hscale); /*write_regs(client, regs);*/ break; } @@ -287,12 +287,11 @@ static int wis_tw9903_probe(struct i2c_client *client, dec->hue = 0; i2c_set_clientdata(client, dec); - printk(KERN_DEBUG - "wis-tw9903: initializing TW9903 at address %d on %s\n", + dev_dbg(&client->dev, "initializing TW9903 at address %d on %s\n", client->addr, adapter->name); if (write_regs(client, initial_registers) < 0) { - printk(KERN_ERR "wis-tw9903: error initializing TW9903\n"); + dev_err(&client->dev, "error initializing TW9903\n"); kfree(dec); return -ENODEV; } From 34047bac17471d47989b96450b784f22eaf96223 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 08:34:20 -0300 Subject: [PATCH 0336/4607] [media] Staging/media: Use dev_ printks in go7007/go7007-v4l2.c fixed below checkpatch warning. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-v4l2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index a78133b67de2..0d3713dceba5 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1811,8 +1811,8 @@ int go7007_v4l2_init(struct go7007 *go) } video_set_drvdata(go->video_dev, go); ++go->ref_count; - printk(KERN_INFO "%s: registered device %s [v4l2]\n", - go->video_dev->name, video_device_node_name(go->video_dev)); + dev_info(go->dev, "registered device %s [v4l2]\n", + video_device_node_name(go->video_dev)); return 0; } From dd442d4d799155b370593b3737956b3709fb8cbb Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 15:39:54 -0300 Subject: [PATCH 0337/4607] [media] Staging/media: Use dev_ printks in go7007/wis-uda1342.c fixed below checkpatch warning. - WARNING: Prefer netdev_dbg(netdev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-uda1342.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/go7007/wis-uda1342.c b/drivers/staging/media/go7007/wis-uda1342.c index 05ac798f35f7..582ea120a531 100644 --- a/drivers/staging/media/go7007/wis-uda1342.c +++ b/drivers/staging/media/go7007/wis-uda1342.c @@ -47,8 +47,8 @@ static int wis_uda1342_command(struct i2c_client *client, write_reg(client, 0x00, 0x1241); /* select input 1 */ break; default: - printk(KERN_ERR "wis-uda1342: input %d not supported\n", - *inp); + dev_err(&client->dev, "input %d not supported\n", + *inp); break; } break; @@ -67,8 +67,7 @@ static int wis_uda1342_probe(struct i2c_client *client, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - printk(KERN_DEBUG - "wis-uda1342: initializing UDA1342 at address %d on %s\n", + dev_dbg(&client->dev, "initializing UDA1342 at address %d on %s\n", client->addr, adapter->name); write_reg(client, 0x00, 0x8000); /* reset registers */ From b91c78e354711ffc8fe26b53be85706dc90918fa Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 15:40:16 -0300 Subject: [PATCH 0338/4607] [media] Staging/media: fixed spacing coding style in go7007/wis-uda1342.c fixed below checkpatch error. - ERROR: that open brace { should be on the previous line Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/s2250-board.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index b3974100c6cd..9ba61bd382ac 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -103,8 +103,7 @@ static u16 vid_regs_fp[] = { }; /* PAL specific values */ -static u16 vid_regs_fp_pal[] = -{ +static u16 vid_regs_fp_pal[] = { 0x120, 0x017, 0x121, 0xd22, 0x122, 0x122, From b441d03331cabcf14352465b7b0951f0c8d69481 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 15:41:26 -0300 Subject: [PATCH 0339/4607] [media] Staging/media: Use dev_ printks in go7007/wis-tw2804.c fixed below checkpatch warning. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_dbg(netdev, ... then dev_dbg(dev, ... then pr_debug(... to printk(KERN_DEBUG ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-tw2804.c | 24 ++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/staging/media/go7007/wis-tw2804.c b/drivers/staging/media/go7007/wis-tw2804.c index d6410ee01be8..290fd8c7bfef 100644 --- a/drivers/staging/media/go7007/wis-tw2804.c +++ b/drivers/staging/media/go7007/wis-tw2804.c @@ -128,30 +128,32 @@ static int wis_tw2804_command(struct i2c_client *client, int *input = arg; if (*input < 0 || *input > 3) { - printk(KERN_ERR "wis-tw2804: channel %d is not " - "between 0 and 3!\n", *input); + dev_err(&client->dev, + "channel %d is not between 0 and 3!\n", *input); return 0; } dec->channel = *input; - printk(KERN_DEBUG "wis-tw2804: initializing TW2804 " - "channel %d\n", dec->channel); + dev_dbg(&client->dev, "initializing TW2804 channel %d\n", + dec->channel); if (dec->channel == 0 && write_regs(client, global_registers, 0) < 0) { - printk(KERN_ERR "wis-tw2804: error initializing " - "TW2804 global registers\n"); + dev_err(&client->dev, + "error initializing TW2804 global registers\n"); return 0; } if (write_regs(client, channel_registers, dec->channel) < 0) { - printk(KERN_ERR "wis-tw2804: error initializing " - "TW2804 channel %d\n", dec->channel); + dev_err(&client->dev, + "error initializing TW2804 channel %d\n", + dec->channel); return 0; } return 0; } if (dec->channel < 0) { - printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until " - "channel number is set\n", cmd); + dev_dbg(&client->dev, + "ignoring command %08x until channel number is set\n", + cmd); return 0; } @@ -311,7 +313,7 @@ static int wis_tw2804_probe(struct i2c_client *client, dec->hue = 128; i2c_set_clientdata(client, dec); - printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n", + dev_dbg(&client->dev, "creating TW2804 at address %d on %s\n", client->addr, adapter->name); return 0; From 5820debccb530a8a5456f358e92d8272aa2902ae Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Tue, 6 Nov 2012 15:41:03 -0300 Subject: [PATCH 0340/4607] [media] Staging/media: Use dev_ printks in go7007/s2250-board.c fixed below checkpatch warning. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/s2250-board.c | 27 ++++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index 9ba61bd382ac..d60e06596375 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -173,7 +173,7 @@ static int write_reg(struct i2c_client *client, u8 reg, u8 value) usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -212,7 +212,7 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -230,13 +230,13 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) val_read = (buf[2] << 8) + buf[3]; kfree(buf); if (val_read != val) { - printk(KERN_INFO "invalid fp write %x %x\n", - val_read, val); + dev_info(&client->dev, "invalid fp write %x %x\n", + val_read, val); return -EFAULT; } if (subaddr != addr) { - printk(KERN_INFO "invalid fp write addr %x %x\n", - subaddr, addr); + dev_info(&client->dev, "invalid fp write addr %x %x\n", + subaddr, addr); return -EFAULT; } } else { @@ -274,7 +274,7 @@ static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) memset(buf, 0xcd, 6); usb = go->hpi_context; if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); + dev_info(&client->dev, "i2c lock failed\n"); kfree(buf); return -EINTR; } @@ -298,7 +298,7 @@ static int write_regs(struct i2c_client *client, u8 *regs) for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { if (write_reg(client, regs[i], regs[i+1]) < 0) { - printk(KERN_INFO "s2250: failed\n"); + dev_info(&client->dev, "failed\n"); return -1; } } @@ -311,7 +311,7 @@ static int write_regs_fp(struct i2c_client *client, u16 *regs) for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { if (write_reg_fp(client, regs[i], regs[i+1]) < 0) { - printk(KERN_INFO "s2250: failed fp\n"); + dev_info(&client->dev, "failed fp\n"); return -1; } } @@ -605,23 +605,20 @@ static int s2250_probe(struct i2c_client *client, /* initialize the audio */ if (write_regs(audio, aud_regs) < 0) { - printk(KERN_ERR - "s2250: error initializing audio\n"); + dev_err(&client->dev, "error initializing audio\n"); i2c_unregister_device(audio); kfree(state); return 0; } if (write_regs(client, vid_regs) < 0) { - printk(KERN_ERR - "s2250: error initializing decoder\n"); + dev_err(&client->dev, "error initializing decoder\n"); i2c_unregister_device(audio); kfree(state); return 0; } if (write_regs_fp(client, vid_regs_fp) < 0) { - printk(KERN_ERR - "s2250: error initializing decoder\n"); + dev_err(&client->dev, "error initializing decoder\n"); i2c_unregister_device(audio); kfree(state); return 0; From e6da76617fe79ab76ad8f78606c423e093a284f3 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Wed, 7 Nov 2012 15:26:19 -0300 Subject: [PATCH 0341/4607] [media] Staging/media: Use dev_ printks in solo6x10/p2m.c fixed below checkpatch warning. - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/solo6x10/p2m.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c index 56210f0fc5ec..58ab61b1f1d9 100644 --- a/drivers/staging/media/solo6x10/p2m.c +++ b/drivers/staging/media/solo6x10/p2m.c @@ -231,15 +231,15 @@ static void run_p2m_test(struct solo_dev *solo_dev) u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev); int i, d; - printk(KERN_WARNING "%s: Testing %u bytes of external ram\n", - SOLO6X10_NAME, size); + dev_warn(&solo_dev->pdev->dev, "Testing %u bytes of external ram\n", + size); for (i = 0; i < size; i += TEST_CHUNK_SIZE) for (d = 0; d < 4; d++) errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE); - printk(KERN_WARNING "%s: Found %llu errors during p2m test\n", - SOLO6X10_NAME, errs); + dev_warn(&solo_dev->pdev->dev, "Found %llu errors during p2m test\n", + errs); return; } From e174e6cac62419ab136506e48790ccd36854ed53 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:52:49 -0300 Subject: [PATCH 0342/4607] [media] staging/media: Use dev_ or pr_ printks in lirc/lirc_sasem.c fixed below checkpatch warnings. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_sasem.c | 65 +++++++++++++------------ 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index 101ac12eb381..b3fe21e7a90d 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -34,6 +34,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -171,7 +173,7 @@ static void delete_context(struct sasem_context *context) kfree(context); if (debug) - printk(KERN_INFO "%s: context deleted\n", __func__); + pr_info("%s: context deleted\n", __func__); } static void deregister_from_lirc(struct sasem_context *context) @@ -181,11 +183,10 @@ static void deregister_from_lirc(struct sasem_context *context) retval = lirc_unregister_driver(minor); if (retval) - printk(KERN_ERR "%s: unable to deregister from lirc (%d)\n", - __func__, retval); + pr_err("%s: unable to deregister from lirc (%d)\n", + __func__, retval); else - printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n", - minor); + pr_info("Deregistered Sasem driver (minor:%d)\n", minor); } @@ -206,8 +207,7 @@ static int vfd_open(struct inode *inode, struct file *file) subminor = iminor(inode); interface = usb_find_interface(&sasem_driver, subminor); if (!interface) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: could not find interface for minor %d\n", + pr_err("%s: could not find interface for minor %d\n", __func__, subminor); retval = -ENODEV; goto exit; @@ -252,8 +252,7 @@ static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -266,7 +265,7 @@ static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) context->vfd_contrast = (unsigned int)arg; break; default: - printk(KERN_INFO "Unknown IOCTL command\n"); + pr_info("Unknown IOCTL command\n"); mutex_unlock(&context->ctx_lock); return -ENOIOCTLCMD; /* not supported */ } @@ -287,8 +286,7 @@ static int vfd_close(struct inode *inode, struct file *file) context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -299,7 +297,7 @@ static int vfd_close(struct inode *inode, struct file *file) retval = -EIO; } else { context->vfd_isopen = 0; - printk(KERN_INFO "VFD port closed\n"); + dev_info(&context->dev->dev, "VFD port closed\n"); if (!context->dev_present && !context->ir_isopen) { /* Device disconnected before close and IR port is @@ -373,16 +371,14 @@ static ssize_t vfd_write(struct file *file, const char *buf, context = (struct sasem_context *) file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } mutex_lock(&context->ctx_lock); if (!context->dev_present) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no Sasem device present\n", __func__); + pr_err("%s: no Sasem device present\n", __func__); retval = -ENODEV; goto exit; } @@ -519,7 +515,7 @@ static int ir_open(void *data) __func__, retval); else { context->ir_isopen = 1; - printk(KERN_INFO "IR port opened\n"); + dev_info(&context->dev->dev, "IR port opened\n"); } exit: @@ -538,8 +534,7 @@ static void ir_close(void *data) context = (struct sasem_context *)data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return; } @@ -547,7 +542,7 @@ static void ir_close(void *data) usb_kill_urb(context->rx_urb); context->ir_isopen = 0; - printk(KERN_INFO "IR port closed\n"); + pr_info("IR port closed\n"); if (!context->dev_present) { @@ -584,8 +579,9 @@ static void incoming_packet(struct sasem_context *context, int i; if (len != 8) { - printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n", - __func__, len); + dev_warn(&context->dev->dev, + "%s: invalid incoming packet size (%d)\n", + __func__, len); return; } @@ -663,7 +659,7 @@ static void usb_rx_callback(struct urb *urb) break; default: - printk(KERN_WARNING "%s: status (%d): ignored", + dev_warn(&urb->dev->dev, "%s: status (%d): ignored", __func__, urb->status); break; } @@ -830,8 +826,9 @@ static int sasem_probe(struct usb_interface *interface, retval = lirc_minor; goto unlock; } else - printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n", - __func__, lirc_minor); + dev_info(&interface->dev, + "%s: Registered Sasem driver (minor:%d)\n", + __func__, lirc_minor); /* Needed while unregistering! */ driver->minor = lirc_minor; @@ -852,15 +849,18 @@ static int sasem_probe(struct usb_interface *interface, if (vfd_ep_found) { if (debug) - printk(KERN_INFO "Registering VFD with sysfs\n"); + dev_info(&interface->dev, + "Registering VFD with sysfs\n"); if (usb_register_dev(interface, &sasem_class)) /* Not a fatal error, so ignore */ - printk(KERN_INFO "%s: could not get a minor number " - "for VFD\n", __func__); + dev_info(&interface->dev, + "%s: could not get a minor number for VFD\n", + __func__); } - printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n", - __func__, dev->bus->busnum, dev->devnum); + dev_info(&interface->dev, + "%s: Sasem device on usb<%d:%d> initialized\n", + __func__, dev->bus->busnum, dev->devnum); unlock: mutex_unlock(&context->ctx_lock); @@ -903,7 +903,8 @@ static void sasem_disconnect(struct usb_interface *interface) context = usb_get_intfdata(interface); mutex_lock(&context->ctx_lock); - printk(KERN_INFO "%s: Sasem device disconnected\n", __func__); + dev_info(&interface->dev, "%s: Sasem device disconnected\n", + __func__); usb_set_intfdata(interface, NULL); context->dev_present = 0; From 014f00664267904585f58c3478e9a7bfc96d0f03 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:53:37 -0300 Subject: [PATCH 0343/4607] [media] staging/media: Use pr_ printks in lirc/lirc_sir.c fixed below checkpatch warnings. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_sir.c | 36 +++++++++++---------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c index 4afc3b419738..9a88f057c0bd 100644 --- a/drivers/staging/media/lirc/lirc_sir.c +++ b/drivers/staging/media/lirc/lirc_sir.c @@ -33,6 +33,8 @@ * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -495,7 +497,7 @@ static int init_chrdev(void) driver.dev = &lirc_sir_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); + pr_err("init_chrdev() failed.\n"); return -EIO; } return 0; @@ -604,7 +606,7 @@ static irqreturn_t sir_interrupt(int irq, void *dev_id) } if (status & UTSR0_TFS) - printk(KERN_ERR "transmit fifo not full, shouldn't happen\n"); + pr_err("transmit fifo not full, shouldn't happen\n"); /* We must clear certain bits. */ status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); @@ -787,7 +789,7 @@ static int init_hardware(void) #ifdef LIRC_ON_SA1100 #ifdef CONFIG_SA1100_BITSY if (machine_is_bitsy()) { - printk(KERN_INFO "Power on IR module\n"); + pr_info("Power on IR module\n"); set_bitsy_egpio(EGPIO_BITSY_IR_ON); } #endif @@ -885,8 +887,7 @@ static int init_hardware(void) udelay(1500); /* read previous control byte */ - printk(KERN_INFO LIRC_DRIVER_NAME - ": 0x%02x\n", sinp(UART_RX)); + pr_info("0x%02x\n", sinp(UART_RX)); /* Set DLAB 1. */ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); @@ -964,8 +965,7 @@ static int init_port(void) /* get I/O port access and IRQ line */ #ifndef LIRC_ON_SA1100 if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": i/o port 0x%.4x already in use.\n", io); + pr_err("i/o port 0x%.4x already in use.\n", io); return -EBUSY; } #endif @@ -975,15 +975,11 @@ static int init_port(void) # ifndef LIRC_ON_SA1100 release_region(io, 8); # endif - printk(KERN_ERR LIRC_DRIVER_NAME - ": IRQ %d already in use.\n", - irq); + pr_err("IRQ %d already in use.\n", irq); return retval; } #ifndef LIRC_ON_SA1100 - printk(KERN_INFO LIRC_DRIVER_NAME - ": I/O port 0x%.4x, IRQ %d.\n", - io, irq); + pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq); #endif init_timer(&timerlist); @@ -1213,8 +1209,7 @@ static int init_lirc_sir(void) if (retval < 0) return retval; init_hardware(); - printk(KERN_INFO LIRC_DRIVER_NAME - ": Installed.\n"); + pr_info("Installed.\n"); return 0; } @@ -1243,23 +1238,20 @@ static int __init lirc_sir_init(void) retval = platform_driver_register(&lirc_sir_driver); if (retval) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform driver register " - "failed!\n"); + pr_err("Platform driver register failed!\n"); return -ENODEV; } lirc_sir_dev = platform_device_alloc("lirc_dev", 0); if (!lirc_sir_dev) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device alloc " - "failed!\n"); + pr_err("Platform device alloc failed!\n"); retval = -ENOMEM; goto pdev_alloc_fail; } retval = platform_device_add(lirc_sir_dev); if (retval) { - printk(KERN_ERR LIRC_DRIVER_NAME ": Platform device add " - "failed!\n"); + pr_err("Platform device add failed!\n"); retval = -ENODEV; goto pdev_add_fail; } @@ -1292,7 +1284,7 @@ static void __exit lirc_sir_exit(void) drop_port(); platform_device_unregister(lirc_sir_dev); platform_driver_unregister(&lirc_sir_driver); - printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); + pr_info("Uninstalled.\n"); } module_init(lirc_sir_init); From f8a7df00210145d54f11855bac4023f59866efc4 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:54:09 -0300 Subject: [PATCH 0344/4607] [media] staging/media: Use pr_ printks in lirc/lirc_bt829.c fixed below checkpatch warnings. - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_bt829.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_bt829.c b/drivers/staging/media/lirc/lirc_bt829.c index 951007a3fc96..fa31ee7dd6a9 100644 --- a/drivers/staging/media/lirc/lirc_bt829.c +++ b/drivers/staging/media/lirc/lirc_bt829.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -72,20 +74,19 @@ static struct pci_dev *do_pci_probe(void) my_dev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_264VT, NULL); if (my_dev) { - printk(KERN_ERR DRIVER_NAME ": Using device: %s\n", - pci_name(my_dev)); + pr_err("Using device: %s\n", pci_name(my_dev)); pci_addr_phys = 0; if (my_dev->resource[0].flags & IORESOURCE_MEM) { pci_addr_phys = my_dev->resource[0].start; - printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X\n", + pr_info("memory at 0x%08X\n", (unsigned int)pci_addr_phys); } if (pci_addr_phys == 0) { - printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n"); + pr_err("no memory resource ?\n"); return NULL; } } else { - printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n"); + pr_err("pci_probe failed\n"); return NULL; } return my_dev; @@ -140,7 +141,7 @@ int init_module(void) atir_minor = lirc_register_driver(&atir_driver); if (atir_minor < 0) { - printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n"); + pr_err("failed to register driver!\n"); return atir_minor; } dprintk("driver is registered on minor %d\n", atir_minor); @@ -159,7 +160,7 @@ static int atir_init_start(void) { pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400); if (pci_addr_lin == 0) { - printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n"); + pr_info("pci mem must be mapped\n"); return 0; } return 1; From cc38b8e9f595c37677b5d5a3d72c985a6ee085c9 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:54:33 -0300 Subject: [PATCH 0345/4607] [media] staging/media: Use pr_ printks in lirc/lirc_parallel.c fixed below checkpatch warnings. - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... - WARNING: Prefer netdev_notice(netdev, ... then dev_notice(dev, ... then pr_notice(... to printk(KERN_NOTICE ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_parallel.c | 49 +++++++++------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c index dd2bca7b56fa..139920c290ca 100644 --- a/drivers/staging/media/lirc/lirc_parallel.c +++ b/drivers/staging/media/lirc/lirc_parallel.c @@ -22,6 +22,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + /*** Includes ***/ #include @@ -115,8 +117,7 @@ static void out(int offset, int value) parport_write_control(pport, value); break; case LIRC_LP_STATUS: - printk(KERN_INFO "%s: attempt to write to status register\n", - LIRC_DRIVER_NAME); + pr_info("attempt to write to status register\n"); break; } } @@ -166,27 +167,23 @@ static unsigned int init_lirc_timer(void) if (default_timer == 0) { /* autodetect timer */ newtimer = (1000000*count)/timeelapsed; - printk(KERN_INFO "%s: %u Hz timer detected\n", - LIRC_DRIVER_NAME, newtimer); + pr_info("%u Hz timer detected\n", newtimer); return newtimer; } else { newtimer = (1000000*count)/timeelapsed; if (abs(newtimer - default_timer) > default_timer/10) { /* bad timer */ - printk(KERN_NOTICE "%s: bad timer: %u Hz\n", - LIRC_DRIVER_NAME, newtimer); - printk(KERN_NOTICE "%s: using default timer: " - "%u Hz\n", - LIRC_DRIVER_NAME, default_timer); + pr_notice("bad timer: %u Hz\n", newtimer); + pr_notice("using default timer: %u Hz\n", + default_timer); return default_timer; } else { - printk(KERN_INFO "%s: %u Hz timer detected\n", - LIRC_DRIVER_NAME, newtimer); + pr_info("%u Hz timer detected\n", newtimer); return newtimer; /* use detected value */ } } } else { - printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME); + pr_notice("no timer detected\n"); return 0; } } @@ -194,13 +191,10 @@ static unsigned int init_lirc_timer(void) static int lirc_claim(void) { if (parport_claim(ppdevice) != 0) { - printk(KERN_WARNING "%s: could not claim port\n", - LIRC_DRIVER_NAME); - printk(KERN_WARNING "%s: waiting for port becoming available" - "\n", LIRC_DRIVER_NAME); + pr_warn("could not claim port\n"); + pr_warn("waiting for port becoming available\n"); if (parport_claim_or_block(ppdevice) < 0) { - printk(KERN_NOTICE "%s: could not claim port, giving" - " up\n", LIRC_DRIVER_NAME); + pr_notice("could not claim port, giving up\n"); return 0; } } @@ -219,7 +213,7 @@ static void rbuf_write(int signal) if (nwptr == rptr) { /* no new signals will be accepted */ lost_irqs++; - printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME); + pr_notice("buffer overrun\n"); return; } rbuf[wptr] = signal; @@ -290,7 +284,7 @@ static void irq_handler(void *blah) if (signal > timeout || (check_pselecd && (in(1) & LP_PSELECD))) { signal = 0; - printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME); + pr_notice("timeout\n"); break; } } while (lirc_get_signal()); @@ -644,8 +638,7 @@ static int __init lirc_parallel_init(void) result = platform_driver_register(&lirc_parallel_driver); if (result) { - printk(KERN_NOTICE "platform_driver_register" - " returned %d\n", result); + pr_notice("platform_driver_register returned %d\n", result); return result; } @@ -661,8 +654,7 @@ static int __init lirc_parallel_init(void) pport = parport_find_base(io); if (pport == NULL) { - printk(KERN_NOTICE "%s: no port at %x found\n", - LIRC_DRIVER_NAME, io); + pr_notice("no port at %x found\n", io); result = -ENXIO; goto exit_device_put; } @@ -670,8 +662,7 @@ static int __init lirc_parallel_init(void) pf, kf, irq_handler, 0, NULL); parport_put_port(pport); if (ppdevice == NULL) { - printk(KERN_NOTICE "%s: parport_register_device() failed\n", - LIRC_DRIVER_NAME); + pr_notice("parport_register_device() failed\n"); result = -ENXIO; goto exit_device_put; } @@ -706,14 +697,12 @@ static int __init lirc_parallel_init(void) driver.dev = &lirc_parallel_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_NOTICE "%s: register_chrdev() failed\n", - LIRC_DRIVER_NAME); + pr_notice("register_chrdev() failed\n"); parport_unregister_device(ppdevice); result = -EIO; goto exit_device_put; } - printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n", - LIRC_DRIVER_NAME, io, irq); + pr_info("installed using port 0x%04x irq %d\n", io, irq); return 0; exit_device_put: From df4f07b5066f3db4270e5b3240fd8292fd28aebb Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:55:09 -0300 Subject: [PATCH 0346/4607] [media] staging/media: Use pr_ printks in lirc/lirc_serial.c fixed below checkpatch warnings. - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_serial.c | 70 ++++++++++-------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c index 08cfaf6fb399..513b75ef5179 100644 --- a/drivers/staging/media/lirc/lirc_serial.c +++ b/drivers/staging/media/lirc/lirc_serial.c @@ -48,6 +48,8 @@ * Steve Davies July 2001 */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -667,8 +669,7 @@ static irqreturn_t irq_handler(int i, void *blah) counter++; status = sinp(UART_MSR); if (counter > RS_ISR_PASS_LIMIT) { - printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: " - "We're caught!\n"); + pr_warn("AIEEEE: We're caught!\n"); break; } if ((status & hardware[type].signal_pin_change) @@ -703,11 +704,10 @@ static irqreturn_t irq_handler(int i, void *blah) dcd = (status & hardware[type].signal_pin) ? 1 : 0; if (dcd == last_dcd) { - printk(KERN_WARNING LIRC_DRIVER_NAME - ": ignoring spike: %d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("ignoring spike: %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); continue; } @@ -715,25 +715,20 @@ static irqreturn_t irq_handler(int i, void *blah) if (tv.tv_sec < lasttv.tv_sec || (tv.tv_sec == lasttv.tv_sec && tv.tv_usec < lasttv.tv_usec)) { - printk(KERN_WARNING LIRC_DRIVER_NAME - ": AIEEEE: your clock just jumped " - "backwards\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": %d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("AIEEEE: your clock just jumped backwards\n"); + pr_warn("%d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); data = PULSE_MASK; } else if (deltv > 15) { data = PULSE_MASK; /* really long time */ if (!(dcd^sense)) { /* sanity check */ - printk(KERN_WARNING LIRC_DRIVER_NAME - ": AIEEEE: " - "%d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); + pr_warn("AIEEEE: %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); /* * detecting pulse while this * MUST be a space! @@ -776,8 +771,7 @@ static int hardware_init_port(void) soutp(UART_IER, scratch); if (scratch2 != 0 || scratch3 != 0x0f) { /* we fail, there's nothing here */ - printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test " - "failed, cannot continue\n"); + pr_err("port existence test failed, cannot continue\n"); return -ENODEV; } @@ -850,11 +844,9 @@ static int __devinit lirc_serial_probe(struct platform_device *dev) LIRC_DRIVER_NAME, (void *)&hardware); if (result < 0) { if (result == -EBUSY) - printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", - irq); + dev_err(&dev->dev, "IRQ %d busy\n", irq); else if (result == -EINVAL) - printk(KERN_ERR LIRC_DRIVER_NAME - ": Bad irq number or handler\n"); + dev_err(&dev->dev, "Bad irq number or handler\n"); return result; } @@ -869,14 +861,11 @@ static int __devinit lirc_serial_probe(struct platform_device *dev) LIRC_DRIVER_NAME) == NULL)) || ((iommap == 0) && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": port %04x already in use\n", io); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": use 'setserial /dev/ttySX uart none'\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": or compile the serial port driver as module and\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": make sure this module is loaded first\n"); + dev_err(&dev->dev, "port %04x already in use\n", io); + dev_warn(&dev->dev, "use 'setserial /dev/ttySX uart none'\n"); + dev_warn(&dev->dev, + "or compile the serial port driver as module and\n"); + dev_warn(&dev->dev, "make sure this module is loaded first\n"); result = -EBUSY; goto exit_free_irq; } @@ -907,11 +896,11 @@ static int __devinit lirc_serial_probe(struct platform_device *dev) msleep(40); } sense = (nlow >= nhigh ? 1 : 0); - printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active " - "%s receiver\n", sense ? "low" : "high"); + dev_info(&dev->dev, "auto-detected active %s receiver\n", + sense ? "low" : "high"); } else - printk(KERN_INFO LIRC_DRIVER_NAME ": Manually using active " - "%s receiver\n", sense ? "low" : "high"); + dev_info(&dev->dev, "Manually using active %s receiver\n", + sense ? "low" : "high"); dprintk("Interrupt %d, port %04x obtained\n", irq, io); return 0; @@ -1251,8 +1240,7 @@ static int __init lirc_serial_init_module(void) driver.dev = &lirc_serial_dev->dev; driver.minor = lirc_register_driver(&driver); if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": register_chrdev failed!\n"); + pr_err("register_chrdev failed!\n"); lirc_serial_exit(); return driver.minor; } From 5c77dc40c9fe957220b30c1334d8959be554e1d8 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:54:54 -0300 Subject: [PATCH 0347/4607] [media] staging/media: Use dev_ or pr_ printks in lirc/lirc_imon.c fixed below checkpatch warnings. - WARNING: Prefer netdev_info(netdev, ... then dev_info(dev, ... then pr_info(... to printk(KERN_INFO ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... and add pr_fmt. Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_imon.c | 28 ++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index 2944fde89f44..343c622f9387 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -20,6 +20,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -205,12 +207,12 @@ static void deregister_from_lirc(struct imon_context *context) retval = lirc_unregister_driver(minor); if (retval) - printk(KERN_ERR KBUILD_MODNAME - ": %s: unable to deregister from lirc(%d)", - __func__, retval); + dev_err(&context->usbdev->dev, + ": %s: unable to deregister from lirc(%d)", + __func__, retval); else - printk(KERN_INFO MOD_NAME ": Deregistered iMON driver " - "(minor:%d)\n", minor); + dev_info(&context->usbdev->dev, + "Deregistered iMON driver (minor:%d)\n", minor); } @@ -231,8 +233,7 @@ static int display_open(struct inode *inode, struct file *file) subminor = iminor(inode); interface = usb_find_interface(&imon_driver, subminor); if (!interface) { - printk(KERN_ERR KBUILD_MODNAME - ": %s: could not find interface for minor %d\n", + pr_err("%s: could not find interface for minor %d\n", __func__, subminor); retval = -ENODEV; goto exit; @@ -282,8 +283,7 @@ static int display_close(struct inode *inode, struct file *file) context = file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -391,8 +391,7 @@ static ssize_t vfd_write(struct file *file, const char __user *buf, context = file->private_data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return -ENODEV; } @@ -521,8 +520,7 @@ static void ir_close(void *data) context = (struct imon_context *)data; if (!context) { - printk(KERN_ERR KBUILD_MODNAME - "%s: no context for device\n", __func__); + pr_err("%s: no context for device\n", __func__); return; } @@ -1009,8 +1007,8 @@ static void imon_disconnect(struct usb_interface *interface) mutex_unlock(&driver_lock); - printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n", - __func__, ifnum); + dev_info(&interface->dev, "%s: iMON device (intf%d) disconnected\n", + __func__, ifnum); } static int imon_suspend(struct usb_interface *intf, pm_message_t message) From ce24c25b97b26c3f5a1634b2919b50a053eabc01 Mon Sep 17 00:00:00 2001 From: YAMANE Toshiaki Date: Thu, 8 Nov 2012 15:53:53 -0300 Subject: [PATCH 0348/4607] [media] staging/media: Use dev_ printks in lirc/igorplugusb.c fixed below checkpatch warnings. - WARNING: Prefer netdev_warn(netdev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... - WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: YAMANE Toshiaki Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/lirc/lirc_igorplugusb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/media/lirc/lirc_igorplugusb.c b/drivers/staging/media/lirc/lirc_igorplugusb.c index 939a801c23e4..2faa391006db 100644 --- a/drivers/staging/media/lirc/lirc_igorplugusb.c +++ b/drivers/staging/media/lirc/lirc_igorplugusb.c @@ -223,8 +223,8 @@ static int unregister_from_lirc(struct igorplug *ir) int devnum; if (!ir) { - printk(KERN_ERR "%s: called with NULL device struct!\n", - __func__); + dev_err(&ir->usbdev->dev, + "%s: called with NULL device struct!\n", __func__); return -EINVAL; } @@ -232,8 +232,8 @@ static int unregister_from_lirc(struct igorplug *ir) d = ir->d; if (!d) { - printk(KERN_ERR "%s: called with NULL lirc driver struct!\n", - __func__); + dev_err(&ir->usbdev->dev, + "%s: called with NULL lirc driver struct!\n", __func__); return -EINVAL; } @@ -347,8 +347,8 @@ static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf) if (ir->buf_in[2] == 0) send_fragment(ir, buf, DEVICE_HEADERLEN, ret); else { - printk(KERN_WARNING DRIVER_NAME - "[%d]: Device buffer overrun.\n", ir->devnum); + dev_warn(&ir->usbdev->dev, + "[%d]: Device buffer overrun.\n", ir->devnum); /* HHHNNNNNNNNNNNOOOOOOOO H = header <---[2]---> N = newer <---------ret--------> O = older */ From fc09931e10d08c2d3eb82c4992cb21fd98682cd8 Mon Sep 17 00:00:00 2001 From: Jonathan McDowell Date: Thu, 15 Nov 2012 21:55:12 -0300 Subject: [PATCH 0349/4607] [media] Autoselect more relevant frontends for EM28XX DVB stick I noticed that the EM28XX DVB driver doesn't auto select all of the appropriate DVB tuner modules required. In particular I needed DVB_LGDT3305 for my a340, but it looks like DVB_MT352 + DVB_S5H1409 were missing as well. Signed-Off-by: Jonathan McDowell Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index 7a5bd61bd3bb..617c6e4cce49 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -34,6 +34,7 @@ config VIDEO_EM28XX_DVB tristate "DVB/ATSC Support for em28xx based TV cards" depends on VIDEO_EM28XX && DVB_CORE select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT select DVB_S921 if MEDIA_SUBDRV_AUTOSELECT @@ -43,6 +44,8 @@ config VIDEO_EM28XX_DVB select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the From 37285bf2a516a808f1282540badcaf847340a154 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 21 Dec 2012 21:14:41 -0200 Subject: [PATCH 0350/4607] em28xx: add two missing tuners at the Kconfig file Those two tuners may also be needed. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index 617c6e4cce49..094c4ecf086d 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -46,6 +46,8 @@ config VIDEO_EM28XX_DVB select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the From 105e3687ada4ebe6dfbda7abc3b16106f86a787d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 15 Dec 2012 08:29:11 -0300 Subject: [PATCH 0351/4607] [media] em28xx: add support for NEC proto variants on em2874 and upper By disabling the NEC parity check, it is possible to handle all 3 NEC protocol variants (32, 24 or 16 bits). Change the driver in order to handle all of them. Unfortunately, em2860/em2863 provide only 16 bits for the IR scancode, even when NEC parity is disabled. So, this change should affect only em2874 and newer devices, with provides up to 32 bits for the scancode. Tested with one NEC-16, one NEC-24 and one RC5 IR. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 58 ++++++++++++++++--------- drivers/media/usb/em28xx/em28xx-reg.h | 1 + 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 660bf803c9e4..507370c5217d 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -57,8 +57,8 @@ MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); struct em28xx_ir_poll_result { unsigned int toggle_bit:1; unsigned int read_count:7; - u8 rc_address; - u8 rc_data[4]; /* 1 byte on em2860/2880, 4 on em2874 */ + + u32 scancode; }; struct em28xx_IR { @@ -72,6 +72,7 @@ struct em28xx_IR { struct delayed_work work; unsigned int full_code:1; unsigned int last_readcount; + u64 rc_type; int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; @@ -236,11 +237,8 @@ static int default_polling_getkey(struct em28xx_IR *ir, /* Infrared read count (Reg 0x45[6:0] */ poll_result->read_count = (msg[0] & 0x7f); - /* Remote Control Address (Reg 0x46) */ - poll_result->rc_address = msg[1]; - - /* Remote Control Data (Reg 0x47) */ - poll_result->rc_data[0] = msg[2]; + /* Remote Control Address/Data (Regs 0x46/0x47) */ + poll_result->scancode = msg[1] << 8 | msg[2]; return 0; } @@ -266,13 +264,32 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, /* Infrared read count (Reg 0x51[6:0] */ poll_result->read_count = (msg[0] & 0x7f); - /* Remote Control Address (Reg 0x52) */ - poll_result->rc_address = msg[1]; - - /* Remote Control Data (Reg 0x53-55) */ - poll_result->rc_data[0] = msg[2]; - poll_result->rc_data[1] = msg[3]; - poll_result->rc_data[2] = msg[4]; + /* + * Remote Control Address (Reg 0x52) + * Remote Control Data (Reg 0x53-0x55) + */ + switch (ir->rc_type) { + case RC_BIT_RC5: + poll_result->scancode = msg[1] << 8 | msg[2]; + break; + case RC_BIT_NEC: + if ((msg[3] ^ msg[4]) != 0xff) /* 32 bits NEC */ + poll_result->scancode = (msg[1] << 24) | + (msg[2] << 16) | + (msg[3] << 8) | + msg[4]; + else if ((msg[1] ^ msg[2]) != 0xff) /* 24 bits NEC */ + poll_result->scancode = (msg[1] << 16) | + (msg[2] << 8) | + msg[3]; + else /* Normal NEC */ + poll_result->scancode = msg[1] << 8 | msg[3]; + break; + default: + poll_result->scancode = (msg[1] << 24) | (msg[2] << 16) | + (msg[3] << 8) | msg[4]; + break; + } return 0; } @@ -294,17 +311,16 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) } if (unlikely(poll_result.read_count != ir->last_readcount)) { - dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__, + dprintk("%s: toggle: %d, count: %d, key 0x%04x\n", __func__, poll_result.toggle_bit, poll_result.read_count, - poll_result.rc_address, poll_result.rc_data[0]); + poll_result.scancode); if (ir->full_code) rc_keydown(ir->rc, - poll_result.rc_address << 8 | - poll_result.rc_data[0], + poll_result.scancode, poll_result.toggle_bit); else rc_keydown(ir->rc, - poll_result.rc_data[0], + poll_result.scancode & 0xff, poll_result.toggle_bit); if (ir->dev->chip_id == CHIP_ID_EM2874 || @@ -360,12 +376,14 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) *rc_type = RC_BIT_RC5; } else if (*rc_type & RC_BIT_NEC) { dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; - ir_config = EM2874_IR_NEC; + ir_config = EM2874_IR_NEC | EM2874_IR_NEC_NO_PARITY; ir->full_code = 1; *rc_type = RC_BIT_NEC; } else if (*rc_type != RC_BIT_UNKNOWN) rc = -EINVAL; + ir->rc_type = *rc_type; + em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 6ff368297f6e..2ad357354d81 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -177,6 +177,7 @@ /* em2874 IR config register (0x50) */ #define EM2874_IR_NEC 0x00 +#define EM2874_IR_NEC_NO_PARITY 0x01 #define EM2874_IR_RC5 0x04 #define EM2874_IR_RC6_MODE_0 0x08 #define EM2874_IR_RC6_MODE_6A 0x0b From 0dae88392395e228e67436cd08f084d395b39df5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 15 Dec 2012 08:29:12 -0300 Subject: [PATCH 0352/4607] [media] em28xx: add support for RC6 mode 0 on devices that support it Newer em28xx chipsets (em2874 and upper) are capable of supporting RC6 codes, on both mode 0 (command mode, 16 bits payload size, similar to RC5, also called "Philips mode") and mode 6a (OEM command mode, with offers a few alternatives with regards to the payload size). I don't have any mode 6a control ATM to test it, so, I opted to add support only to mode 0. After this patch, adding support to mode 6a should not be hard. Tested with a Philips television remote controller. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 93 ++++++++++++++++++++----- 1 file changed, 76 insertions(+), 17 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 507370c5217d..3899ea823336 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -285,6 +285,9 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, else /* Normal NEC */ poll_result->scancode = msg[1] << 8 | msg[3]; break; + case RC_BIT_RC6_0: + poll_result->scancode = msg[1] << 8 | msg[2]; + break; default: poll_result->scancode = (msg[1] << 24) | (msg[2] << 16) | (msg[3] << 8) | msg[4]; @@ -361,15 +364,42 @@ static void em28xx_ir_stop(struct rc_dev *rc) cancel_delayed_work_sync(&ir->work); } -static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +static int em2860_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +{ + struct em28xx_IR *ir = rc_dev->priv; + struct em28xx *dev = ir->dev; + + /* Adjust xclk based on IR table for RC5/NEC tables */ + if (*rc_type & RC_BIT_RC5) { + dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; + ir->full_code = 1; + *rc_type = RC_BIT_RC5; + } else if (*rc_type & RC_BIT_NEC) { + dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; + ir->full_code = 1; + *rc_type = RC_BIT_NEC; + } else if (*rc_type & RC_BIT_UNKNOWN) { + *rc_type = RC_BIT_UNKNOWN; + } else { + *rc_type = ir->rc_type; + return -EINVAL; + } + ir->get_key = default_polling_getkey; + em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, + EM28XX_XCLK_IR_RC5_MODE); + + ir->rc_type = *rc_type; + + return 0; +} + +static int em2874_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) { - int rc = 0; struct em28xx_IR *ir = rc_dev->priv; struct em28xx *dev = ir->dev; u8 ir_config = EM2874_IR_RC5; - /* Adjust xclk based o IR table for RC5/NEC tables */ - + /* Adjust xclk and set type based on IR table for RC5/NEC/RC6 tables */ if (*rc_type & RC_BIT_RC5) { dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; ir->full_code = 1; @@ -379,33 +409,47 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) ir_config = EM2874_IR_NEC | EM2874_IR_NEC_NO_PARITY; ir->full_code = 1; *rc_type = RC_BIT_NEC; - } else if (*rc_type != RC_BIT_UNKNOWN) - rc = -EINVAL; + } else if (*rc_type & RC_BIT_RC6_0) { + dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; + ir_config = EM2874_IR_RC6_MODE_0; + ir->full_code = 1; + *rc_type = RC_BIT_RC6_0; + } else if (*rc_type & RC_BIT_UNKNOWN) { + *rc_type = RC_BIT_UNKNOWN; + } else { + *rc_type = ir->rc_type; + return -EINVAL; + } - ir->rc_type = *rc_type; + ir->get_key = em2874_polling_getkey; + em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); + ir->rc_type = *rc_type; + + return 0; +} +static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) +{ + struct em28xx_IR *ir = rc_dev->priv; + struct em28xx *dev = ir->dev; + /* Setup the proper handler based on the chip */ switch (dev->chip_id) { case CHIP_ID_EM2860: case CHIP_ID_EM2883: - ir->get_key = default_polling_getkey; - break; + return em2860_ir_change_protocol(rc_dev, rc_type); case CHIP_ID_EM2884: case CHIP_ID_EM2874: case CHIP_ID_EM28174: - ir->get_key = em2874_polling_getkey; - em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); - break; + return em2874_ir_change_protocol(rc_dev, rc_type); default: printk("Unrecognized em28xx chip id 0x%02x: IR not supported\n", dev->chip_id); - rc = -EINVAL; + return -EINVAL; } - - return rc; } static void em28xx_register_i2c_ir(struct em28xx *dev) @@ -573,6 +617,21 @@ static int em28xx_ir_init(struct em28xx *dev) rc->open = em28xx_ir_start; rc->close = em28xx_ir_stop; + switch (dev->chip_id) { + case CHIP_ID_EM2860: + case CHIP_ID_EM2883: + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; + break; + case CHIP_ID_EM2884: + case CHIP_ID_EM2874: + case CHIP_ID_EM28174: + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | RC_BIT_RC6_0; + break; + default: + err = -ENODEV; + goto err_out_free; + } + /* By default, keep protocol field untouched */ rc_type = RC_BIT_UNKNOWN; err = em28xx_ir_change_protocol(rc, &rc_type); @@ -615,9 +674,9 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; - err_out_stop: +err_out_stop: dev->ir = NULL; - err_out_free: +err_out_free: rc_free_device(rc); kfree(ir); return err; From 2f5741aa6a71aea6bc8f186e8753f270ae8742f1 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 22 Dec 2012 10:13:38 -0300 Subject: [PATCH 0353/4607] [media] em28xx: input: fix oops on device removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When em28xx_ir_init() fails due to an configuration error, it frees the memory of struct em28xx_IR *ir, but doesn't set the corresponding pointer in the device struct to NULL. On device removal, em28xx_ir_fini() gets called, which then calls rc_unregister_device() with a pointer to freed memory. Fixes bug 26572 (http://bugzilla.kernel.org/show_bug.cgi?id=26572) Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 3899ea823336..3598221378ac 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -600,7 +600,7 @@ static int em28xx_ir_init(struct em28xx *dev) ir = kzalloc(sizeof(*ir), GFP_KERNEL); rc = rc_allocate_device(); if (!ir || !rc) - goto err_out_free; + goto error; /* record handles to ourself */ ir->dev = dev; @@ -629,14 +629,14 @@ static int em28xx_ir_init(struct em28xx *dev) break; default: err = -ENODEV; - goto err_out_free; + goto error; } /* By default, keep protocol field untouched */ rc_type = RC_BIT_UNKNOWN; err = em28xx_ir_change_protocol(rc, &rc_type); if (err) - goto err_out_free; + goto error; /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ @@ -661,7 +661,7 @@ static int em28xx_ir_init(struct em28xx *dev) /* all done */ err = rc_register_device(rc); if (err) - goto err_out_stop; + goto error; em28xx_register_i2c_ir(dev); @@ -674,9 +674,8 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; -err_out_stop: +error: dev->ir = NULL; -err_out_free: rc_free_device(rc); kfree(ir); return err; From c02ec71b014ea7bc6f50deb9db765bf57d593c53 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:33 -0300 Subject: [PATCH 0354/4607] [media] em28xx: fix wrong data offset for non-interlaced mode in em28xx_copy_video MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit em28xx_copy_video uses a wrong offset for the target buffer when copying the data from an USB isoc packet. This happens only for the second and all following lines in the packet. The reason why this bug doesn't cause image corruption with my test device (SilverCrest Webcam 1.3 MPix) is, that this device never sends any packets that cross the end of a line. I don't know if all devices behave like this, so this patch should be considered for stable. With the upcoming patches to add support for USB bulk transfers, em28xx_copy_video will be called once per URB, which will always trigger this bug. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 766ad125fc08..202e00bb2b6e 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -207,15 +207,10 @@ static void em28xx_copy_video(struct em28xx *dev, startread = p; remain = len; - if (dev->progressive) + if (dev->progressive || buf->top_field) fieldstart = outp; - else { - /* Interlaces two half frames */ - if (buf->top_field) - fieldstart = outp; - else - fieldstart = outp + bytesperline; - } + else /* interlaced mode, even nr. of lines */ + fieldstart = outp + bytesperline; linesdone = dma_q->pos / bytesperline; currlinedone = dma_q->pos % bytesperline; @@ -243,7 +238,10 @@ static void em28xx_copy_video(struct em28xx *dev, remain -= lencopy; while (remain > 0) { - startwrite += lencopy + bytesperline; + if (dev->progressive) + startwrite += lencopy; + else + startwrite += lencopy + bytesperline; startread += lencopy; if (bytesperline > remain) lencopy = remain; From 8c3015676f64289577c79c3b231b12acd0c2c62b Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:34 -0300 Subject: [PATCH 0355/4607] [media] em28xx: clarify meaning of field 'progressive' in struct em28xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 86e90d86da6d..ad9eec063275 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -497,7 +497,7 @@ struct em28xx { int sensor_xres, sensor_yres; int sensor_xtal; - /* Allows progressive (e. g. non-interlaced) mode */ + /* Progressive (non-interlaced) mode */ int progressive; /* Vinmode/Vinctl used at the driver */ From 515688a8985c023ba47cc89eb6a22564fab76694 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:35 -0300 Subject: [PATCH 0356/4607] [media] em28xx: rename isoc packet number constants and parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename EM28XX_NUM_PACKETS to EM28XX_NUM_ISOC_PACKETS and EM28XX_DVB_MAX_PACKETS to EM28XX_DVB_NUM_ISOC_PACKETS to clarify that these values are used only for isoc usb transfers. Also use the term num_packets instead of max_packets, as this is how these values are used and called in struct urb. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- drivers/media/usb/em28xx/em28xx-core.c | 8 ++++---- drivers/media/usb/em28xx/em28xx-dvb.c | 5 +++-- drivers/media/usb/em28xx/em28xx-video.c | 4 ++-- drivers/media/usb/em28xx/em28xx.h | 10 +++++----- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 619bffbab3bc..7cd2faf610b9 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3323,7 +3323,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (has_dvb) { /* pre-allocate DVB isoc transfer buffers */ retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_MAX_PACKETS, + EM28XX_DVB_NUM_ISOC_PACKETS, EM28XX_DVB_NUM_BUFS, dev->dvb_max_pkt_size); if (retval) { diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index bed07a6c33f8..2520a164bfa9 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1034,7 +1034,7 @@ EXPORT_SYMBOL_GPL(em28xx_stop_urbs); * Allocate URBs */ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size) + int num_packets, int num_bufs, int max_pkt_size) { struct em28xx_usb_isoc_bufs *isoc_bufs; int i; @@ -1069,7 +1069,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, } isoc_bufs->max_pkt_size = max_pkt_size; - isoc_bufs->num_packets = max_packets; + isoc_bufs->num_packets = num_packets; dev->isoc_ctl.vid_buf = NULL; dev->isoc_ctl.vbi_buf = NULL; @@ -1129,7 +1129,7 @@ EXPORT_SYMBOL_GPL(em28xx_alloc_isoc); * Allocate URBs and start IRQ */ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size, + int num_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) { struct em28xx_dmaqueue *dma_q = &dev->vidq; @@ -1153,7 +1153,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, } if (alloc) { - rc = em28xx_alloc_isoc(dev, mode, max_packets, + rc = em28xx_alloc_isoc(dev, mode, num_packets, num_bufs, max_pkt_size); if (rc) return rc; diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index e8008810b60c..581578f39133 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -173,11 +173,12 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) return max_dvb_packet_size; dprintk(1, "Using %d buffers each with %d x %d bytes\n", EM28XX_DVB_NUM_BUFS, - EM28XX_DVB_MAX_PACKETS, + EM28XX_DVB_NUM_ISOC_PACKETS, max_dvb_packet_size); return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS, + EM28XX_DVB_NUM_ISOC_PACKETS, + EM28XX_DVB_NUM_BUFS, max_dvb_packet_size, em28xx_dvb_isoc_copy); } diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 202e00bb2b6e..94b51da01af0 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -764,13 +764,13 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (urb_init) { if (em28xx_vbi_supported(dev) == 1) rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, + EM28XX_NUM_ISOC_PACKETS, EM28XX_NUM_BUFS, dev->max_pkt_size, em28xx_isoc_copy_vbi); else rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, + EM28XX_NUM_ISOC_PACKETS, EM28XX_NUM_BUFS, dev->max_pkt_size, em28xx_isoc_copy); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index ad9eec063275..36a786460e18 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -157,12 +157,12 @@ #define EM28XX_NUM_BUFS 5 #define EM28XX_DVB_NUM_BUFS 5 -/* number of packets for each buffer +/* isoc transfers: number of packets for each buffer windows requests only 64 packets .. so we better do the same this is what I found out for all alternate numbers there! */ -#define EM28XX_NUM_PACKETS 64 -#define EM28XX_DVB_MAX_PACKETS 64 +#define EM28XX_NUM_ISOC_PACKETS 64 +#define EM28XX_DVB_NUM_ISOC_PACKETS 64 #define EM28XX_INTERLACED_DEFAULT 1 @@ -667,9 +667,9 @@ int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size); + int num_packets, int num_bufs, int max_pkt_size); int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int max_packets, int num_bufs, int max_pkt_size, + int num_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); void em28xx_stop_urbs(struct em28xx *dev); From f0fa9936f577597dabd4a0140095bb3b02988814 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:36 -0300 Subject: [PATCH 0357/4607] [media] em28xx: rename struct em28xx_usb_isoc_bufs to em28xx_usb_bufs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be used for USB bulk transfers, too. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 8 ++++---- drivers/media/usb/em28xx/em28xx.h | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 2520a164bfa9..b250a63827d3 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -964,7 +964,7 @@ static void em28xx_irq_callback(struct urb *urb) void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) { struct urb *urb; - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *isoc_bufs; int i; em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); @@ -1012,7 +1012,7 @@ void em28xx_stop_urbs(struct em28xx *dev) { int i; struct urb *urb; - struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; + struct em28xx_usb_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); @@ -1036,7 +1036,7 @@ EXPORT_SYMBOL_GPL(em28xx_stop_urbs); int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, int num_packets, int num_bufs, int max_pkt_size) { - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *isoc_bufs; int i; int sb_size, pipe; struct urb *urb; @@ -1134,7 +1134,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, { struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - struct em28xx_usb_isoc_bufs *isoc_bufs; + struct em28xx_usb_bufs *isoc_bufs; int i; int rc; int alloc; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 36a786460e18..e062a2767d2f 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -203,7 +203,7 @@ enum em28xx_mode { struct em28xx; -struct em28xx_usb_isoc_bufs { +struct em28xx_usb_bufs { /* max packet size of isoc transaction */ int max_pkt_size; @@ -213,19 +213,19 @@ struct em28xx_usb_isoc_bufs { /* number of allocated urbs */ int num_bufs; - /* urb for isoc transfers */ + /* urb for isoc/bulk transfers */ struct urb **urb; - /* transfer buffers for isoc transfer */ + /* transfer buffers for isoc/bulk transfer */ char **transfer_buffer; }; struct em28xx_usb_isoc_ctl { /* isoc transfer buffers for analog mode */ - struct em28xx_usb_isoc_bufs analog_bufs; + struct em28xx_usb_bufs analog_bufs; /* isoc transfer buffers for digital mode */ - struct em28xx_usb_isoc_bufs digital_bufs; + struct em28xx_usb_bufs digital_bufs; /* Stores already requested buffers */ struct em28xx_buffer *vid_buf; From 74209dc06a7c27401de637cc371f54920d628ba8 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:37 -0300 Subject: [PATCH 0358/4607] [media] em28xx: rename struct em28xx_usb_isoc_ctl to em28xx_usb_ctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also rename the corresponding field isoc_ctl in struct em28xx to usb_ctl. We will use this struct for USB bulk transfers, too. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 24 ++++++++++++------------ drivers/media/usb/em28xx/em28xx-vbi.c | 4 ++-- drivers/media/usb/em28xx/em28xx-video.c | 24 ++++++++++++------------ drivers/media/usb/em28xx/em28xx.h | 12 ++++++------ 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index b250a63827d3..0892d9225d6a 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -941,7 +941,7 @@ static void em28xx_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->slock); - dev->isoc_ctl.isoc_copy(dev, urb); + dev->usb_ctl.urb_data_copy(dev, urb); spin_unlock(&dev->slock); /* Reset urb buffers */ @@ -970,9 +970,9 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->isoc_ctl.digital_bufs; + isoc_bufs = &dev->usb_ctl.digital_bufs; else - isoc_bufs = &dev->isoc_ctl.analog_bufs; + isoc_bufs = &dev->usb_ctl.analog_bufs; for (i = 0; i < isoc_bufs->num_bufs; i++) { urb = isoc_bufs->urb[i]; @@ -1012,7 +1012,7 @@ void em28xx_stop_urbs(struct em28xx *dev) { int i; struct urb *urb; - struct em28xx_usb_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; + struct em28xx_usb_bufs *isoc_bufs = &dev->usb_ctl.digital_bufs; em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); @@ -1045,9 +1045,9 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->isoc_ctl.digital_bufs; + isoc_bufs = &dev->usb_ctl.digital_bufs; else - isoc_bufs = &dev->isoc_ctl.analog_bufs; + isoc_bufs = &dev->usb_ctl.analog_bufs; /* De-allocates all pending stuff */ em28xx_uninit_isoc(dev, mode); @@ -1070,8 +1070,8 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, isoc_bufs->max_pkt_size = max_pkt_size; isoc_bufs->num_packets = num_packets; - dev->isoc_ctl.vid_buf = NULL; - dev->isoc_ctl.vbi_buf = NULL; + dev->usb_ctl.vid_buf = NULL; + dev->usb_ctl.vbi_buf = NULL; sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size; @@ -1079,7 +1079,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, for (i = 0; i < isoc_bufs->num_bufs; i++) { urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); if (!urb) { - em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); + em28xx_err("cannot alloc usb_ctl.urb %i\n", i); em28xx_uninit_isoc(dev, mode); return -ENOMEM; } @@ -1141,14 +1141,14 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode); - dev->isoc_ctl.isoc_copy = isoc_copy; + dev->usb_ctl.urb_data_copy = isoc_copy; if (mode == EM28XX_DIGITAL_MODE) { - isoc_bufs = &dev->isoc_ctl.digital_bufs; + isoc_bufs = &dev->usb_ctl.digital_bufs; /* no need to free/alloc isoc buffers in digital mode */ alloc = 0; } else { - isoc_bufs = &dev->isoc_ctl.analog_bufs; + isoc_bufs = &dev->usb_ctl.analog_bufs; alloc = 1; } diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c index 2b4c9cba2d67..d74713bd1d18 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -60,8 +60,8 @@ free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) VIDEOBUF_ACTIVE, it won't be, though. */ spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vbi_buf == buf) - dev->isoc_ctl.vbi_buf = NULL; + if (dev->usb_ctl.vbi_buf == buf) + dev->usb_ctl.vbi_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); videobuf_vmalloc_free(&buf->vb); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 94b51da01af0..4326b9b0e8c7 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -165,7 +165,7 @@ static inline void buffer_filled(struct em28xx *dev, buf->vb.field_count++; v4l2_get_timestamp(&buf->vb.ts); - dev->isoc_ctl.vid_buf = NULL; + dev->usb_ctl.vid_buf = NULL; list_del(&buf->vb.queue); wake_up(&buf->vb.done); @@ -182,7 +182,7 @@ static inline void vbi_buffer_filled(struct em28xx *dev, buf->vb.field_count++; v4l2_get_timestamp(&buf->vb.ts); - dev->isoc_ctl.vbi_buf = NULL; + dev->usb_ctl.vbi_buf = NULL; list_del(&buf->vb.queue); wake_up(&buf->vb.done); @@ -368,7 +368,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vid_buf = NULL; + dev->usb_ctl.vid_buf = NULL; *buf = NULL; return; } @@ -380,7 +380,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, outp = videobuf_to_vmalloc(&(*buf)->vb); memset(outp, 0, (*buf)->vb.size); - dev->isoc_ctl.vid_buf = *buf; + dev->usb_ctl.vid_buf = *buf; return; } @@ -396,7 +396,7 @@ static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vbi_buf = NULL; + dev->usb_ctl.vbi_buf = NULL; *buf = NULL; return; } @@ -407,7 +407,7 @@ static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, outp = videobuf_to_vmalloc(&(*buf)->vb); memset(outp, 0x00, (*buf)->vb.size); - dev->isoc_ctl.vbi_buf = *buf; + dev->usb_ctl.vbi_buf = *buf; return; } @@ -435,7 +435,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) return 0; } - buf = dev->isoc_ctl.vid_buf; + buf = dev->usb_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); @@ -531,11 +531,11 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) return 0; } - buf = dev->isoc_ctl.vid_buf; + buf = dev->usb_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); - vbi_buf = dev->isoc_ctl.vbi_buf; + vbi_buf = dev->usb_ctl.vbi_buf; if (vbi_buf != NULL) vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); @@ -725,8 +725,8 @@ static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) VIDEOBUF_ACTIVE, it won't be, though. */ spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vid_buf == buf) - dev->isoc_ctl.vid_buf = NULL; + if (dev->usb_ctl.vid_buf == buf) + dev->usb_ctl.vid_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); videobuf_vmalloc_free(&buf->vb); @@ -758,7 +758,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, goto fail; } - if (!dev->isoc_ctl.analog_bufs.num_bufs) + if (!dev->usb_ctl.analog_bufs.num_bufs) urb_init = 1; if (urb_init) { diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index e062a2767d2f..17310e6a51e2 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -220,19 +220,19 @@ struct em28xx_usb_bufs { char **transfer_buffer; }; -struct em28xx_usb_isoc_ctl { - /* isoc transfer buffers for analog mode */ +struct em28xx_usb_ctl { + /* isoc/bulk transfer buffers for analog mode */ struct em28xx_usb_bufs analog_bufs; - /* isoc transfer buffers for digital mode */ + /* isoc/bulk transfer buffers for digital mode */ struct em28xx_usb_bufs digital_bufs; /* Stores already requested buffers */ struct em28xx_buffer *vid_buf; struct em28xx_buffer *vbi_buf; - /* isoc urb callback */ - int (*isoc_copy) (struct em28xx *dev, struct urb *urb); + /* copy data from URB */ + int (*urb_data_copy) (struct em28xx *dev, struct urb *urb); }; @@ -582,7 +582,7 @@ struct em28xx { /* Isoc control struct */ struct em28xx_dmaqueue vidq; struct em28xx_dmaqueue vbiq; - struct em28xx_usb_isoc_ctl isoc_ctl; + struct em28xx_usb_ctl usb_ctl; spinlock_t slock; /* usb transfer */ From 89f84b9c2057fbfc85796d29d53d5b8e01002315 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:38 -0300 Subject: [PATCH 0359/4607] [media] em28xx: remove obsolete #define EM28XX_URB_TIMEOUT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It isn't used anymore and uses constants which no longer exist. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 17310e6a51e2..6773ca8d3647 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -187,10 +187,6 @@ Interval: 125us */ -/* time to wait when stopping the isoc transfer */ -#define EM28XX_URB_TIMEOUT \ - msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS) - /* time in msecs to wait for i2c writes to finish */ #define EM2800_I2C_WRITE_TIMEOUT 20 From 836e93bf6a7f0e5385f850f51d66cd1612e815aa Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:39 -0300 Subject: [PATCH 0360/4607] [media] em28xx: update description of em28xx_irq_callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit em28xx_irq_callback can be used for isoc and bulk transfers. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 0892d9225d6a..8f50f5c8d069 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -919,7 +919,7 @@ EXPORT_SYMBOL_GPL(em28xx_set_mode); ------------------------------------------------------------------*/ /* - * IRQ callback, called by URB callback + * URB completion handler for isoc/bulk transfers */ static void em28xx_irq_callback(struct urb *urb) { @@ -946,6 +946,7 @@ static void em28xx_irq_callback(struct urb *urb) /* Reset urb buffers */ for (i = 0; i < urb->number_of_packets; i++) { + /* isoc only (bulk: number_of_packets = 0) */ urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } From afb177e06563861bfe4d7795a9d4d3b52851813b Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:40 -0300 Subject: [PATCH 0361/4607] [media] em28xx: rename function em28xx_uninit_isoc to em28xx_uninit_usb_xfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function will be used to uninitialize USB bulk transfers, too. Also rename the local variable isoc_bufs to usb_bufs. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 4 +-- drivers/media/usb/em28xx/em28xx-core.c | 43 +++++++++++++------------ drivers/media/usb/em28xx/em28xx-video.c | 2 +- drivers/media/usb/em28xx/em28xx.h | 2 +- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 7cd2faf610b9..e474ccf07832 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3394,7 +3394,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) video_device_node_name(dev->vdev)); dev->state |= DEV_MISCONFIGURED; - em28xx_uninit_isoc(dev, dev->mode); + em28xx_uninit_usb_xfer(dev, dev->mode); dev->state |= DEV_DISCONNECTED; } else { dev->state |= DEV_DISCONNECTED; @@ -3402,7 +3402,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) } /* free DVB isoc buffers */ - em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE); + em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); mutex_unlock(&dev->lock); diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 8f50f5c8d069..a1ebd08c5bcd 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -962,49 +962,50 @@ static void em28xx_irq_callback(struct urb *urb) /* * Stop and Deallocate URBs */ -void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) +void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode) { struct urb *urb; - struct em28xx_usb_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; - em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode); + em28xx_isocdbg("em28xx: called em28xx_uninit_usb_xfer in mode %d\n", + mode); if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->usb_ctl.digital_bufs; + usb_bufs = &dev->usb_ctl.digital_bufs; else - isoc_bufs = &dev->usb_ctl.analog_bufs; + usb_bufs = &dev->usb_ctl.analog_bufs; - for (i = 0; i < isoc_bufs->num_bufs; i++) { - urb = isoc_bufs->urb[i]; + for (i = 0; i < usb_bufs->num_bufs; i++) { + urb = usb_bufs->urb[i]; if (urb) { if (!irqs_disabled()) usb_kill_urb(urb); else usb_unlink_urb(urb); - if (isoc_bufs->transfer_buffer[i]) { + if (usb_bufs->transfer_buffer[i]) { usb_free_coherent(dev->udev, urb->transfer_buffer_length, - isoc_bufs->transfer_buffer[i], + usb_bufs->transfer_buffer[i], urb->transfer_dma); } usb_free_urb(urb); - isoc_bufs->urb[i] = NULL; + usb_bufs->urb[i] = NULL; } - isoc_bufs->transfer_buffer[i] = NULL; + usb_bufs->transfer_buffer[i] = NULL; } - kfree(isoc_bufs->urb); - kfree(isoc_bufs->transfer_buffer); + kfree(usb_bufs->urb); + kfree(usb_bufs->transfer_buffer); - isoc_bufs->urb = NULL; - isoc_bufs->transfer_buffer = NULL; - isoc_bufs->num_bufs = 0; + usb_bufs->urb = NULL; + usb_bufs->transfer_buffer = NULL; + usb_bufs->num_bufs = 0; em28xx_capture_start(dev, 0); } -EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); +EXPORT_SYMBOL_GPL(em28xx_uninit_usb_xfer); /* * Stop URBs @@ -1051,7 +1052,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, isoc_bufs = &dev->usb_ctl.analog_bufs; /* De-allocates all pending stuff */ - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); isoc_bufs->num_bufs = num_bufs; @@ -1081,7 +1082,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); if (!urb) { em28xx_err("cannot alloc usb_ctl.urb %i\n", i); - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } isoc_bufs->urb[i] = urb; @@ -1093,7 +1094,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, " buffer %i%s\n", sb_size, i, in_interrupt() ? " while in int" : ""); - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } memset(isoc_bufs->transfer_buffer[i], 0, sb_size); @@ -1171,7 +1172,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, if (rc) { em28xx_err("submit of urb %i failed (error=%i)\n", i, rc); - em28xx_uninit_isoc(dev, mode); + em28xx_uninit_usb_xfer(dev, mode); return rc; } } diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 4326b9b0e8c7..d61d4dcca626 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2272,7 +2272,7 @@ static int em28xx_v4l2_close(struct file *filp) v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ - em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE); + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); em28xx_set_mode(dev, EM28XX_SUSPEND); /* set alternate 0 */ diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 6773ca8d3647..8fb350479e9e 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -667,7 +667,7 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, int num_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); -void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); +void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode); void em28xx_stop_urbs(struct em28xx *dev); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); From 6ddd89d0c90ec384d5a8058cb38679beb03c7eb7 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:41 -0300 Subject: [PATCH 0362/4607] [media] em28xx: create a common function for isoc and bulk URB allocation and setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the existing function for isoc transfers em28xx_init_isoc to em28xx_init_usb_xfer and extend it. URB allocation and setup is now done depending on the USB transfer type, which is selected with a new function parameter. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 6 +- drivers/media/usb/em28xx/em28xx-core.c | 99 ++++++++++++++----------- drivers/media/usb/em28xx/em28xx.h | 4 +- 3 files changed, 60 insertions(+), 49 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index e474ccf07832..bfce34d491c0 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3322,10 +3322,10 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (has_dvb) { /* pre-allocate DVB isoc transfer buffers */ - retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_NUM_ISOC_PACKETS, + retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, 0, EM28XX_DVB_NUM_BUFS, - dev->dvb_max_pkt_size); + dev->dvb_max_pkt_size, + EM28XX_DVB_NUM_ISOC_PACKETS); if (retval) { goto unlock_and_free; } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index a1ebd08c5bcd..42388dec5f32 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -5,6 +5,7 @@ Markus Rechberger Mauro Carvalho Chehab Sascha Sommer + Copyright (C) 2012 Frank Schäfer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1035,10 +1036,10 @@ EXPORT_SYMBOL_GPL(em28xx_stop_urbs); /* * Allocate URBs */ -int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int num_packets, int num_bufs, int max_pkt_size) +int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier) { - struct em28xx_usb_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; int sb_size, pipe; struct urb *urb; @@ -1047,49 +1048,52 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); if (mode == EM28XX_DIGITAL_MODE) - isoc_bufs = &dev->usb_ctl.digital_bufs; + usb_bufs = &dev->usb_ctl.digital_bufs; else - isoc_bufs = &dev->usb_ctl.analog_bufs; + usb_bufs = &dev->usb_ctl.analog_bufs; /* De-allocates all pending stuff */ em28xx_uninit_usb_xfer(dev, mode); - isoc_bufs->num_bufs = num_bufs; + usb_bufs->num_bufs = num_bufs; - isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!isoc_bufs->urb) { + usb_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!usb_bufs->urb) { em28xx_errdev("cannot alloc memory for usb buffers\n"); return -ENOMEM; } - isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + usb_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); - if (!isoc_bufs->transfer_buffer) { + if (!usb_bufs->transfer_buffer) { em28xx_errdev("cannot allocate memory for usb transfer\n"); - kfree(isoc_bufs->urb); + kfree(usb_bufs->urb); return -ENOMEM; } - isoc_bufs->max_pkt_size = max_pkt_size; - isoc_bufs->num_packets = num_packets; + usb_bufs->max_pkt_size = max_pkt_size; + if (xfer_bulk) + usb_bufs->num_packets = 0; + else + usb_bufs->num_packets = packet_multiplier; dev->usb_ctl.vid_buf = NULL; dev->usb_ctl.vbi_buf = NULL; - sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size; + sb_size = packet_multiplier * usb_bufs->max_pkt_size; /* allocate urbs and transfer buffers */ - for (i = 0; i < isoc_bufs->num_bufs; i++) { - urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL); + for (i = 0; i < usb_bufs->num_bufs; i++) { + urb = usb_alloc_urb(usb_bufs->num_packets, GFP_KERNEL); if (!urb) { em28xx_err("cannot alloc usb_ctl.urb %i\n", i); em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } - isoc_bufs->urb[i] = urb; + usb_bufs->urb[i] = urb; - isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, + usb_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev, sb_size, GFP_KERNEL, &urb->transfer_dma); - if (!isoc_bufs->transfer_buffer[i]) { + if (!usb_bufs->transfer_buffer[i]) { em28xx_err("unable to allocate %i bytes for transfer" " buffer %i%s\n", sb_size, i, @@ -1097,35 +1101,42 @@ int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_uninit_usb_xfer(dev, mode); return -ENOMEM; } - memset(isoc_bufs->transfer_buffer[i], 0, sb_size); + memset(usb_bufs->transfer_buffer[i], 0, sb_size); - /* FIXME: this is a hack - should be - 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' - should also be using 'desc.bInterval' - */ - pipe = usb_rcvisocpipe(dev->udev, - mode == EM28XX_ANALOG_MODE ? - EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL); - - usb_fill_int_urb(urb, dev->udev, pipe, - isoc_bufs->transfer_buffer[i], sb_size, - em28xx_irq_callback, dev, 1); - - urb->number_of_packets = isoc_bufs->num_packets; - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - - k = 0; - for (j = 0; j < isoc_bufs->num_packets; j++) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = - isoc_bufs->max_pkt_size; - k += isoc_bufs->max_pkt_size; + if (xfer_bulk) { /* bulk */ + pipe = usb_rcvbulkpipe(dev->udev, + mode == EM28XX_ANALOG_MODE ? + EM28XX_EP_ANALOG : + EM28XX_EP_DIGITAL); + usb_fill_bulk_urb(urb, dev->udev, pipe, + usb_bufs->transfer_buffer[i], sb_size, + em28xx_irq_callback, dev); + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + } else { /* isoc */ + pipe = usb_rcvisocpipe(dev->udev, + mode == EM28XX_ANALOG_MODE ? + EM28XX_EP_ANALOG : + EM28XX_EP_DIGITAL); + usb_fill_int_urb(urb, dev->udev, pipe, + usb_bufs->transfer_buffer[i], sb_size, + em28xx_irq_callback, dev, 1); + urb->transfer_flags = URB_ISO_ASAP | + URB_NO_TRANSFER_DMA_MAP; + k = 0; + for (j = 0; j < usb_bufs->num_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + usb_bufs->max_pkt_size; + k += usb_bufs->max_pkt_size; + } } + + urb->number_of_packets = usb_bufs->num_packets; } return 0; } -EXPORT_SYMBOL_GPL(em28xx_alloc_isoc); +EXPORT_SYMBOL_GPL(em28xx_alloc_urbs); /* * Allocate URBs and start IRQ @@ -1155,8 +1166,8 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, } if (alloc) { - rc = em28xx_alloc_isoc(dev, mode, num_packets, - num_bufs, max_pkt_size); + rc = em28xx_alloc_urbs(dev, mode, 0, num_bufs, + max_pkt_size, num_packets); if (rc) return rc; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 8fb350479e9e..7bc2ddd4dc0c 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -662,8 +662,8 @@ int em28xx_vbi_supported(struct em28xx *dev); int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); -int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode, - int num_packets, int num_bufs, int max_pkt_size); +int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier); int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, int num_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); From 057ca0da067c8c0c734088eba229ab06e21bc88c Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:42 -0300 Subject: [PATCH 0363/4607] [media] em28xx: create a common function for isoc and bulk USB transfer initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - rename em28xx_init_isoc to em28xx_init_usb_xfer - add parameter for isoc/bulk transfer selection which is passed to em28xx_alloc_urbs - rename local variable isoc_buf to usb_bufs Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 30 +++++++++++++------------ drivers/media/usb/em28xx/em28xx-dvb.c | 9 ++++---- drivers/media/usb/em28xx/em28xx-video.c | 20 ++++++++--------- drivers/media/usb/em28xx/em28xx.h | 8 ++++--- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 42388dec5f32..d8a8e8bbb870 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1141,33 +1141,35 @@ EXPORT_SYMBOL_GPL(em28xx_alloc_urbs); /* * Allocate URBs and start IRQ */ -int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int num_packets, int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) +int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, + int xfer_bulk, int num_bufs, int max_pkt_size, + int packet_multiplier, + int (*urb_data_copy) (struct em28xx *dev, struct urb *urb)) { struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - struct em28xx_usb_bufs *isoc_bufs; + struct em28xx_usb_bufs *usb_bufs; int i; int rc; int alloc; - em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode); + em28xx_isocdbg("em28xx: called em28xx_init_usb_xfer in mode %d\n", + mode); - dev->usb_ctl.urb_data_copy = isoc_copy; + dev->usb_ctl.urb_data_copy = urb_data_copy; if (mode == EM28XX_DIGITAL_MODE) { - isoc_bufs = &dev->usb_ctl.digital_bufs; - /* no need to free/alloc isoc buffers in digital mode */ + usb_bufs = &dev->usb_ctl.digital_bufs; + /* no need to free/alloc usb buffers in digital mode */ alloc = 0; } else { - isoc_bufs = &dev->usb_ctl.analog_bufs; + usb_bufs = &dev->usb_ctl.analog_bufs; alloc = 1; } if (alloc) { - rc = em28xx_alloc_urbs(dev, mode, 0, num_bufs, - max_pkt_size, num_packets); + rc = em28xx_alloc_urbs(dev, mode, xfer_bulk, num_bufs, + max_pkt_size, packet_multiplier); if (rc) return rc; } @@ -1178,8 +1180,8 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, em28xx_capture_start(dev, 1); /* submit urbs and enables IRQ */ - for (i = 0; i < isoc_bufs->num_bufs; i++) { - rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC); + for (i = 0; i < usb_bufs->num_bufs; i++) { + rc = usb_submit_urb(usb_bufs->urb[i], GFP_ATOMIC); if (rc) { em28xx_err("submit of urb %i failed (error=%i)\n", i, rc); @@ -1190,7 +1192,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, return 0; } -EXPORT_SYMBOL_GPL(em28xx_init_isoc); +EXPORT_SYMBOL_GPL(em28xx_init_usb_xfer); /* * em28xx_wake_i2c() diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 581578f39133..fe4d11c67a4e 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -176,10 +176,11 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) EM28XX_DVB_NUM_ISOC_PACKETS, max_dvb_packet_size); - return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE, - EM28XX_DVB_NUM_ISOC_PACKETS, - EM28XX_DVB_NUM_BUFS, - max_dvb_packet_size, em28xx_dvb_isoc_copy); + return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE, 0, + EM28XX_DVB_NUM_BUFS, + max_dvb_packet_size, + EM28XX_DVB_NUM_ISOC_PACKETS, + em28xx_dvb_isoc_copy); } static int em28xx_stop_streaming(struct em28xx_dvb *dvb) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index d61d4dcca626..a5c1a42f3923 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -763,17 +763,17 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (urb_init) { if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_ISOC_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy_vbi); + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + EM28XX_NUM_ISOC_PACKETS, + em28xx_isoc_copy_vbi); else - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_ISOC_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy); + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + EM28XX_NUM_ISOC_PACKETS, + em28xx_isoc_copy); if (rc < 0) goto fail; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 7bc2ddd4dc0c..950a71786a14 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -664,9 +664,11 @@ int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, int num_bufs, int max_pkt_size, int packet_multiplier); -int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, - int num_packets, int num_bufs, int max_pkt_size, - int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); +int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, + int xfer_bulk, + int num_bufs, int max_pkt_size, int packet_multiplier, + int (*urb_data_copy) + (struct em28xx *dev, struct urb *urb)); void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode); void em28xx_stop_urbs(struct em28xx *dev); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); From 337fe8dad58692ac468f4139ea19624ce464d953 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:43 -0300 Subject: [PATCH 0364/4607] [media] em28xx: clear USB halt/stall condition in em28xx_init_usb_xfer when using bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [mchehab@redhat.com: Fix a CodingStyle issue: don't break strings into separate lines] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index d8a8e8bbb870..6b3485d2266a 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1174,6 +1174,16 @@ int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode, return rc; } + if (xfer_bulk) { + rc = usb_clear_halt(dev->udev, usb_bufs->urb[0]->pipe); + if (rc < 0) { + em28xx_err("failed to clear USB bulk endpoint stall/halt condition (error=%i)\n", + rc); + em28xx_uninit_usb_xfer(dev, mode); + return rc; + } + } + init_waitqueue_head(&dma_q->wq); init_waitqueue_head(&vbi_dma_q->wq); From 1653cb0cb27fba2577933a5a2dd8df78a5bca906 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:44 -0300 Subject: [PATCH 0365/4607] [media] em28xx: remove double checks for urb->status == -ENOENT in urb_data_copy functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This check is already done in the URB handler em28xx_irq_callback before calling these functions. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-dvb.c | 5 +---- drivers/media/usb/em28xx/em28xx-video.c | 10 ++-------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index fe4d11c67a4e..7583cb74d53d 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -134,11 +134,8 @@ static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) return 0; - if (urb->status < 0) { + if (urb->status < 0) print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } for (i = 0; i < urb->number_of_packets; i++) { int status = urb->iso_frame_desc[i].status; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index a5c1a42f3923..6bb0b1d74e36 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -429,11 +429,8 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) return 0; - if (urb->status < 0) { + if (urb->status < 0) print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } buf = dev->usb_ctl.vid_buf; if (buf != NULL) @@ -525,11 +522,8 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) return 0; - if (urb->status < 0) { + if (urb->status < 0) print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; - } buf = dev->usb_ctl.vid_buf; if (buf != NULL) From 0fa4a4029025507feb6c0322cd4c4693fbad9aac Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:45 -0300 Subject: [PATCH 0366/4607] [media] em28xx: rename function em28xx_isoc_copy and extend for USB bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The URB data processing for bulk transfers is very similar to what is done with isoc transfers, so create a common function that works with both transfer types based on the existing isoc function. [mchehab@redhat.com: Fix a CodingStyle issue: don't break strings into separate lines] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 65 +++++++++++++++---------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 6bb0b1d74e36..87161eea257e 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -6,6 +6,7 @@ Markus Rechberger Mauro Carvalho Chehab Sascha Sommer + Copyright (C) 2012 Frank Schäfer Some parts based on SN9C10x PC Camera Controllers GPL driver made by Luca Risolia @@ -412,16 +413,14 @@ static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, return; } -/* - * Controls the isoc copy of each urb packet - */ -static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) +/* Processes and copies the URB data content to a frame buffer queue */ +static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; - unsigned char *outp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; + int xfer_bulk, num_packets, i, rc = 1; + unsigned int actual_length, len = 0; + unsigned char *p, *outp = NULL; if (!dev) return 0; @@ -432,32 +431,45 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) if (urb->status < 0) print_err_status(dev, -1, urb->status); + xfer_bulk = usb_pipebulk(urb->pipe); + buf = dev->usb_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + if (xfer_bulk) /* bulk */ + num_packets = 1; + else /* isoc */ + num_packets = urb->number_of_packets; - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) + for (i = 0; i < num_packets; i++) { + if (xfer_bulk) { /* bulk */ + actual_length = urb->actual_length; + + p = urb->transfer_buffer; + } else { /* isoc */ + if (urb->iso_frame_desc[i].status < 0) { + print_err_status(dev, i, + urb->iso_frame_desc[i].status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + actual_length = urb->iso_frame_desc[i].actual_length; + if (actual_length > dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); continue; + } + + p = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; } - len = urb->iso_frame_desc[i].actual_length - 4; - - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* em28xx_isocdbg("packet %d is empty",i); - spammy */ + if (actual_length <= 0) { + /* NOTE: happens very often with isoc transfers */ + /* em28xx_usbdbg("packet %d is empty",i); - spammy */ continue; } - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); - continue; - } - - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; /* FIXME: incomplete buffer checks where removed to make logic simpler. Impacts of those changes should be evaluated @@ -492,9 +504,12 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) } if (buf != NULL) { if (p[0] != 0x88 && p[0] != 0x22) { + /* NOTE: no intermediate data packet header + * 88 88 88 88 when using bulk transfers */ em28xx_isocdbg("frame is not complete\n"); - len += 4; + len = actual_length; } else { + len = actual_length - 4; p += 4; } em28xx_copy_video(dev, dma_q, buf, p, outp, len); @@ -767,7 +782,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, EM28XX_NUM_BUFS, dev->max_pkt_size, EM28XX_NUM_ISOC_PACKETS, - em28xx_isoc_copy); + em28xx_urb_data_copy); if (rc < 0) goto fail; } From 4601cc39773b33a336eda2010ea5a551aaf6d7f0 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:46 -0300 Subject: [PATCH 0367/4607] [media] em28xx: rename function em28xx_isoc_copy_vbi and extend for USB bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The URB data processing for bulk transfers is very similar to what is done with isoc transfers, so create a common function that works with both transfer types based on the existing isoc function. [mchehab@redhat.com: Fix a CodingStyle issue: don't break strings into separate lines] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 70 +++++++++++++++---------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 87161eea257e..df294f3858ea 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -518,18 +518,16 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) return rc; } -/* Version of isoc handler that takes into account a mixture of video and - VBI data */ -static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) +/* Version of the urb data handler that takes into account a mixture of + video and VBI data */ +static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf, *vbi_buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - unsigned char *outp = NULL; - unsigned char *vbioutp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; - int vbi_size; + int xfer_bulk, vbi_size, num_packets, i, rc = 1; + unsigned int actual_length, len = 0; + unsigned char *p, *outp = NULL, *vbioutp = NULL; if (!dev) return 0; @@ -540,6 +538,8 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) if (urb->status < 0) print_err_status(dev, -1, urb->status); + xfer_bulk = usb_pipebulk(urb->pipe); + buf = dev->usb_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); @@ -548,27 +548,39 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) if (vbi_buf != NULL) vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + if (xfer_bulk) /* bulk */ + num_packets = 1; + else /* isoc */ + num_packets = urb->number_of_packets; - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) + for (i = 0; i < num_packets; i++) { + if (xfer_bulk) { /* bulk */ + actual_length = urb->actual_length; + + p = urb->transfer_buffer; + } else { /* isoc */ + if (urb->iso_frame_desc[i].status < 0) { + print_err_status(dev, i, + urb->iso_frame_desc[i].status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + actual_length = urb->iso_frame_desc[i].actual_length; + if (actual_length > dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); continue; + } + + p = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; } - len = urb->iso_frame_desc[i].actual_length; - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* em28xx_isocdbg("packet %d is empty",i); - spammy */ + if (actual_length <= 0) { + /* NOTE: happens very often with isoc transfers */ + /* em28xx_usbdbg("packet %d is empty",i); - spammy */ continue; } - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); - continue; - } - - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; /* capture type 0 = vbi start capture type 1 = video start @@ -579,16 +591,20 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) em28xx_isocdbg("VBI START HEADER!!!\n"); dev->cur_field = p[2]; p += 4; - len -= 4; + len = actual_length - 4; } else if (p[0] == 0x88 && p[1] == 0x88 && p[2] == 0x88 && p[3] == 0x88) { /* continuation */ p += 4; - len -= 4; + len = actual_length - 4; } else if (p[0] == 0x22 && p[1] == 0x5a) { /* start video */ p += 4; - len -= 4; + len = actual_length - 4; + } else { + /* NOTE: With bulk transfers, intermediate data packets + * have no continuation header */ + len = actual_length; } vbi_size = dev->vbi_width * dev->vbi_height; @@ -776,7 +792,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, EM28XX_NUM_BUFS, dev->max_pkt_size, EM28XX_NUM_ISOC_PACKETS, - em28xx_isoc_copy_vbi); + em28xx_urb_data_copy_vbi); else rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, EM28XX_NUM_BUFS, From a950e4a75ea498f2f43c90a41173fdb4235752c9 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:47 -0300 Subject: [PATCH 0368/4607] [media] em28xx: rename function em28xx_dvb_isoc_copy and extend for USB bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The URB data processing for DVB bulk transfers is very similar to what is done with isoc transfers, so create a common function that works with both transfer types based on the existing isoc function. Tested with device Hauppauge HVR-930c. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-dvb.c | 44 +++++++++++++++++++-------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 7583cb74d53d..22ef6dd128e2 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -10,6 +10,8 @@ (c) 2008 Aidan Thornton + (c) 2012 Frank Schäfer + Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: (c) 2004, 2005 Chris Pascoe (c) 2004 Gerd Knorr [SuSE Labs] @@ -124,9 +126,9 @@ static inline void print_err_status(struct em28xx *dev, } } -static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) +static inline int em28xx_dvb_urb_data_copy(struct em28xx *dev, struct urb *urb) { - int i; + int xfer_bulk, num_packets, i; if (!dev) return 0; @@ -137,18 +139,34 @@ static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb) if (urb->status < 0) print_err_status(dev, -1, urb->status); - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + xfer_bulk = usb_pipebulk(urb->pipe); - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; + if (xfer_bulk) /* bulk */ + num_packets = 1; + else /* isoc */ + num_packets = urb->number_of_packets; + + for (i = 0; i < num_packets; i++) { + if (xfer_bulk) { + if (urb->status < 0) { + print_err_status(dev, i, urb->status); + if (urb->status != -EPROTO) + continue; + } + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, + urb->actual_length); + } else { + if (urb->iso_frame_desc[i].status < 0) { + print_err_status(dev, i, + urb->iso_frame_desc[i].status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + dvb_dmx_swfilter(&dev->dvb->demux, + urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); } - - dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer + - urb->iso_frame_desc[i].offset, - urb->iso_frame_desc[i].actual_length); } return 0; @@ -177,7 +195,7 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) EM28XX_DVB_NUM_BUFS, max_dvb_packet_size, EM28XX_DVB_NUM_ISOC_PACKETS, - em28xx_dvb_isoc_copy); + em28xx_dvb_urb_data_copy); } static int em28xx_stop_streaming(struct em28xx_dvb *dvb) From 0cf544a6cc66b493852d48517ce4833dfade5809 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:49 -0300 Subject: [PATCH 0369/4607] [media] em28xx: rename some USB parameter fields in struct em28xx to clarify their role MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also improve the comments. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 20 +++++++++++--------- drivers/media/usb/em28xx/em28xx-core.c | 8 ++++---- drivers/media/usb/em28xx/em28xx-dvb.c | 4 ++-- drivers/media/usb/em28xx/em28xx-video.c | 2 +- drivers/media/usb/em28xx/em28xx.h | 14 ++++++++------ 5 files changed, 26 insertions(+), 22 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index bfce34d491c0..873b52f71ab2 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3183,9 +3183,10 @@ static int em28xx_usb_probe(struct usb_interface *interface, } /* compute alternate max packet sizes */ - dev->alt_max_pkt_size = kmalloc(sizeof(dev->alt_max_pkt_size[0]) * + dev->alt_max_pkt_size_isoc = + kmalloc(sizeof(dev->alt_max_pkt_size_isoc[0]) * interface->num_altsetting, GFP_KERNEL); - if (dev->alt_max_pkt_size == NULL) { + if (dev->alt_max_pkt_size_isoc == NULL) { em28xx_errdev("out of memory!\n"); kfree(dev); retval = -ENOMEM; @@ -3216,13 +3217,14 @@ static int em28xx_usb_probe(struct usb_interface *interface, break; case EM28XX_EP_ANALOG: has_video = true; - dev->alt_max_pkt_size[i] = size; + dev->alt_max_pkt_size_isoc[i] = size; break; case EM28XX_EP_DIGITAL: has_dvb = true; - if (size > dev->dvb_max_pkt_size) { - dev->dvb_max_pkt_size = size; - dev->dvb_alt = i; + if (size > dev->dvb_max_pkt_size_isoc) { + dev->dvb_max_pkt_size_isoc = + size; + dev->dvb_alt_isoc = i; } break; } @@ -3324,7 +3326,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* pre-allocate DVB isoc transfer buffers */ retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, 0, EM28XX_DVB_NUM_BUFS, - dev->dvb_max_pkt_size, + dev->dvb_max_pkt_size_isoc, EM28XX_DVB_NUM_ISOC_PACKETS); if (retval) { goto unlock_and_free; @@ -3344,7 +3346,7 @@ unlock_and_free: mutex_unlock(&dev->lock); err_free: - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); kfree(dev); err: @@ -3409,7 +3411,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_close_extension(dev); if (!dev->users) { - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); kfree(dev); } } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 6b3485d2266a..3c40a1da69b6 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -830,14 +830,14 @@ int em28xx_set_alternate(struct em28xx *dev) for (i = 0; i < dev->num_alt; i++) { /* stop when the selected alt setting offers enough bandwidth */ - if (dev->alt_max_pkt_size[i] >= min_pkt_size) { + if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) { dev->alt = i; break; /* otherwise make sure that we end up with the maximum bandwidth because the min_pkt_size equation might be wrong... */ - } else if (dev->alt_max_pkt_size[i] > - dev->alt_max_pkt_size[dev->alt]) + } else if (dev->alt_max_pkt_size_isoc[i] > + dev->alt_max_pkt_size_isoc[dev->alt]) dev->alt = i; } @@ -845,7 +845,7 @@ set_alt: if (dev->alt != prev_alt) { em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", min_pkt_size, dev->alt); - dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; + dev->max_pkt_size = dev->alt_max_pkt_size_isoc[dev->alt]; em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt, dev->max_pkt_size); errCode = usb_set_interface(dev->udev, 0, dev->alt); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 22ef6dd128e2..24962520df80 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -178,12 +178,12 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) struct em28xx *dev = dvb->adapter.priv; int max_dvb_packet_size; - usb_set_interface(dev->udev, 0, dev->dvb_alt); + usb_set_interface(dev->udev, 0, dev->dvb_alt_isoc); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; - max_dvb_packet_size = dev->dvb_max_pkt_size; + max_dvb_packet_size = dev->dvb_max_pkt_size_isoc; if (max_dvb_packet_size < 0) return max_dvb_packet_size; dprintk(1, "Using %d buffers each with %d x %d bytes\n", diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index df294f3858ea..d9c15b6da1af 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2286,7 +2286,7 @@ static int em28xx_v4l2_close(struct file *filp) free the remaining resources */ if (dev->state & DEV_DISCONNECTED) { em28xx_release_resources(dev); - kfree(dev->alt_max_pkt_size); + kfree(dev->alt_max_pkt_size_isoc); mutex_unlock(&dev->lock); kfree(dev); kfree(fh); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 950a71786a14..6b8d3e6b6920 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -4,6 +4,7 @@ Copyright (C) 2005 Markus Rechberger Ludovico Cavedon Mauro Carvalho Chehab + Copyright (C) 2012 Frank Schäfer Based on the em2800 driver from Sascha Sommer @@ -583,12 +584,13 @@ struct em28xx { /* usb transfer */ struct usb_device *udev; /* the usb device */ - int alt; /* alternate */ - int max_pkt_size; /* max packet size of isoc transaction */ - int num_alt; /* Number of alternative settings */ - unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ - int dvb_alt; /* alternate for DVB */ - unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */ + int alt; /* alternate setting */ + int max_pkt_size; /* max packet size of the selected ep at alt */ + int num_alt; /* number of alternative settings */ + unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */ + int dvb_alt_isoc; /* alternate setting for DVB isoc transfers */ + unsigned int dvb_max_pkt_size_isoc; /* isoc max packet size of the + selected DVB ep at dvb_alt */ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ /* helper funcs that call usb_control_msg */ From 7312f2c9fa22614acc787c064a0865840888d662 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:50 -0300 Subject: [PATCH 0370/4607] [media] em28xx: add fields for analog and DVB USB transfer type selection to struct em28xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 6b8d3e6b6920..f5be5229f304 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -588,9 +588,13 @@ struct em28xx { int max_pkt_size; /* max packet size of the selected ep at alt */ int num_alt; /* number of alternative settings */ unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */ + unsigned int analog_xfer_bulk:1; /* use bulk instead of isoc + transfers for analog */ int dvb_alt_isoc; /* alternate setting for DVB isoc transfers */ unsigned int dvb_max_pkt_size_isoc; /* isoc max packet size of the selected DVB ep at dvb_alt */ + unsigned int dvb_xfer_bulk:1; /* use bulk instead of isoc + transfers for DVB */ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ /* helper funcs that call usb_control_msg */ From c8e9d95b41f2a441b2af0a1899448dd45ad7632d Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:51 -0300 Subject: [PATCH 0371/4607] [media] em28xx: set USB alternate settings for analog video bulk transfers properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend function em28xx_set_alternate: - use alternate setting 0 for bulk transfers as default - respect module parameter 'alt'=0 for bulk transfers - set max_packet_size to 512 bytes for bulk transfers [mchehab@redhat.com: Fix a CodingStyle issue: don't break strings into separate lines] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 3c40a1da69b6..cdf4cd24007d 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -805,21 +805,23 @@ int em28xx_resolution_set(struct em28xx *dev) return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } +/* Set USB alternate setting for analog video */ int em28xx_set_alternate(struct em28xx *dev) { int errCode, prev_alt = dev->alt; int i; unsigned int min_pkt_size = dev->width * 2 + 4; - /* - * alt = 0 is used only for control messages, so, only values - * greater than 0 can be used for streaming. - */ - if (alt && alt < dev->num_alt) { + /* NOTE: for isoc transfers, only alt settings > 0 are allowed + for bulk transfers, use alt=0 as default value */ + dev->alt = 0; + if ((alt > 0) && (alt < dev->num_alt)) { em28xx_coredbg("alternate forced to %d\n", dev->alt); dev->alt = alt; goto set_alt; } + if (dev->analog_xfer_bulk) + goto set_alt; /* When image size is bigger than a certain value, the frame size should be increased, otherwise, only @@ -843,9 +845,14 @@ int em28xx_set_alternate(struct em28xx *dev) set_alt: if (dev->alt != prev_alt) { - em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", - min_pkt_size, dev->alt); - dev->max_pkt_size = dev->alt_max_pkt_size_isoc[dev->alt]; + if (dev->analog_xfer_bulk) { + dev->max_pkt_size = 512; /* USB 2.0 spec */ + } else { /* isoc */ + em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); + dev->max_pkt_size = + dev->alt_max_pkt_size_isoc[dev->alt]; + } em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt, dev->max_pkt_size); errCode = usb_set_interface(dev->udev, 0, dev->alt); From c647a91a2558c4031eddd013e5860ca5a41363a7 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:52 -0300 Subject: [PATCH 0372/4607] [media] em28xx: improve USB endpoint logic, also use bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current enpoint logic ignores all bulk endpoints and uses a fixed mapping between endpint addresses and the supported data stream types (analog/audio/DVB): Ep 0x82, isoc => analog Ep 0x83, isoc => audio Ep 0x84, isoc => DVB Now that the code can also do bulk transfers, the endpoint logic has to be extended to also consider bulk endpoints. The new logic preserves backwards compatibility and reflects the endpoint configurations we have seen so far: Ep 0x82, isoc => analog Ep 0x82, bulk => analog Ep 0x83, isoc* => audio Ep 0x84, isoc => digital Ep 0x84, bulk => analog or digital** (*: audio should always be isoc) (**: analog, if ep 0x82 is isoc, otherwise digital) [mchehab@redhat.com: Fix a CodingStyle issue: don't break strings into separate lines] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 95 ++++++++++++++++++++----- drivers/media/usb/em28xx/em28xx-core.c | 32 +++++++-- drivers/media/usb/em28xx/em28xx-dvb.c | 34 ++++++--- drivers/media/usb/em28xx/em28xx-reg.h | 4 +- drivers/media/usb/em28xx/em28xx-video.c | 10 +-- drivers/media/usb/em28xx/em28xx.h | 12 ++++ 6 files changed, 147 insertions(+), 40 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 873b52f71ab2..a6f00181c246 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -6,6 +6,7 @@ Markus Rechberger Mauro Carvalho Chehab Sascha Sommer + Copyright (C) 2012 Frank Schäfer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -3209,26 +3210,67 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (udev->speed == USB_SPEED_HIGH) size = size * hb_mult(sizedescr); - if (usb_endpoint_xfer_isoc(e) && - usb_endpoint_dir_in(e)) { + if (usb_endpoint_dir_in(e)) { switch (e->bEndpointAddress) { - case EM28XX_EP_AUDIO: - has_audio = true; - break; - case EM28XX_EP_ANALOG: + case 0x82: has_video = true; - dev->alt_max_pkt_size_isoc[i] = size; + if (usb_endpoint_xfer_isoc(e)) { + dev->analog_ep_isoc = + e->bEndpointAddress; + dev->alt_max_pkt_size_isoc[i] = size; + } else if (usb_endpoint_xfer_bulk(e)) { + dev->analog_ep_bulk = + e->bEndpointAddress; + } break; - case EM28XX_EP_DIGITAL: - has_dvb = true; - if (size > dev->dvb_max_pkt_size_isoc) { - dev->dvb_max_pkt_size_isoc = - size; - dev->dvb_alt_isoc = i; + case 0x83: + if (usb_endpoint_xfer_isoc(e)) { + has_audio = true; + } else { + printk(KERN_INFO DRIVER_NAME + ": error: skipping audio endpoint 0x83, because it uses bulk transfers !\n"); + } + break; + case 0x84: + if (has_video && + (usb_endpoint_xfer_bulk(e))) { + dev->analog_ep_bulk = + e->bEndpointAddress; + } else { + has_dvb = true; + if (usb_endpoint_xfer_isoc(e)) { + dev->dvb_ep_isoc = e->bEndpointAddress; + if (size > dev->dvb_max_pkt_size_isoc) { + dev->dvb_max_pkt_size_isoc = size; + dev->dvb_alt_isoc = i; + } + } else { + dev->dvb_ep_bulk = e->bEndpointAddress; + } } break; } } + /* NOTE: + * Old logic with support for isoc transfers only was: + * 0x82 isoc => analog + * 0x83 isoc => audio + * 0x84 isoc => digital + * + * New logic with support for bulk transfers + * 0x82 isoc => analog + * 0x82 bulk => analog + * 0x83 isoc* => audio + * 0x84 isoc => digital + * 0x84 bulk => analog or digital** + * (*: audio should always be isoc) + * (**: analog, if ep 0x82 is isoc, otherwise digital) + * + * The new logic preserves backwards compatibility and + * reflects the endpoint configurations we have seen + * so far. But there might be devices for which this + * logic is not sufficient... + */ } } @@ -3289,6 +3331,12 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto err_free; } + /* Select USB transfer types to use */ + if (has_video && !dev->analog_ep_isoc) + dev->analog_xfer_bulk = 1; + if (has_dvb && !dev->dvb_ep_isoc) + dev->dvb_xfer_bulk = 1; + snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); dev->devno = nr; dev->model = id->driver_info; @@ -3323,12 +3371,23 @@ static int em28xx_usb_probe(struct usb_interface *interface, } if (has_dvb) { - /* pre-allocate DVB isoc transfer buffers */ - retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, 0, - EM28XX_DVB_NUM_BUFS, - dev->dvb_max_pkt_size_isoc, - EM28XX_DVB_NUM_ISOC_PACKETS); + /* pre-allocate DVB usb transfer buffers */ + if (dev->dvb_xfer_bulk) { + retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + 512, + EM28XX_DVB_BULK_PACKET_MULTIPLIER); + } else { + retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, + EM28XX_DVB_NUM_BUFS, + dev->dvb_max_pkt_size_isoc, + EM28XX_DVB_NUM_ISOC_PACKETS); + } if (retval) { + printk(DRIVER_NAME + ": Failed to pre-allocate USB transfer buffers for DVB.\n"); goto unlock_and_free; } } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index cdf4cd24007d..b10d959fefe5 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -847,11 +847,13 @@ set_alt: if (dev->alt != prev_alt) { if (dev->analog_xfer_bulk) { dev->max_pkt_size = 512; /* USB 2.0 spec */ + dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; } else { /* isoc */ em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", min_pkt_size, dev->alt); dev->max_pkt_size = dev->alt_max_pkt_size_isoc[dev->alt]; + dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; } em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", dev->alt, dev->max_pkt_size); @@ -1054,10 +1056,28 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode); - if (mode == EM28XX_DIGITAL_MODE) + /* Check mode and if we have an endpoint for the selected + transfer type, select buffer */ + if (mode == EM28XX_DIGITAL_MODE) { + if ((xfer_bulk && !dev->dvb_ep_bulk) || + (!xfer_bulk && !dev->dvb_ep_isoc)) { + em28xx_errdev("no endpoint for DVB mode and transfer type %d\n", + xfer_bulk > 0); + return -EINVAL; + } usb_bufs = &dev->usb_ctl.digital_bufs; - else + } else if (mode == EM28XX_ANALOG_MODE) { + if ((xfer_bulk && !dev->analog_ep_bulk) || + (!xfer_bulk && !dev->analog_ep_isoc)) { + em28xx_errdev("no endpoint for analog mode and transfer type %d\n", + xfer_bulk > 0); + return -EINVAL; + } usb_bufs = &dev->usb_ctl.analog_bufs; + } else { + em28xx_errdev("invalid mode selected\n"); + return -EINVAL; + } /* De-allocates all pending stuff */ em28xx_uninit_usb_xfer(dev, mode); @@ -1113,8 +1133,8 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, if (xfer_bulk) { /* bulk */ pipe = usb_rcvbulkpipe(dev->udev, mode == EM28XX_ANALOG_MODE ? - EM28XX_EP_ANALOG : - EM28XX_EP_DIGITAL); + dev->analog_ep_bulk : + dev->dvb_ep_bulk); usb_fill_bulk_urb(urb, dev->udev, pipe, usb_bufs->transfer_buffer[i], sb_size, em28xx_irq_callback, dev); @@ -1122,8 +1142,8 @@ int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk, } else { /* isoc */ pipe = usb_rcvisocpipe(dev->udev, mode == EM28XX_ANALOG_MODE ? - EM28XX_EP_ANALOG : - EM28XX_EP_DIGITAL); + dev->analog_ep_isoc : + dev->dvb_ep_isoc); usb_fill_int_urb(urb, dev->udev, pipe, usb_bufs->transfer_buffer[i], sb_size, em28xx_irq_callback, dev, 1); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 24962520df80..a70b19e07e37 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -176,25 +176,39 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) { int rc; struct em28xx *dev = dvb->adapter.priv; - int max_dvb_packet_size; + int dvb_max_packet_size, packet_multiplier, dvb_alt; - usb_set_interface(dev->udev, 0, dev->dvb_alt_isoc); + if (dev->dvb_xfer_bulk) { + if (!dev->dvb_ep_bulk) + return -ENODEV; + dvb_max_packet_size = 512; /* USB 2.0 spec */ + packet_multiplier = EM28XX_DVB_BULK_PACKET_MULTIPLIER; + dvb_alt = 0; + } else { /* isoc */ + if (!dev->dvb_ep_isoc) + return -ENODEV; + dvb_max_packet_size = dev->dvb_max_pkt_size_isoc; + if (dvb_max_packet_size < 0) + return dvb_max_packet_size; + packet_multiplier = EM28XX_DVB_NUM_ISOC_PACKETS; + dvb_alt = dev->dvb_alt_isoc; + } + + usb_set_interface(dev->udev, 0, dvb_alt); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; - max_dvb_packet_size = dev->dvb_max_pkt_size_isoc; - if (max_dvb_packet_size < 0) - return max_dvb_packet_size; dprintk(1, "Using %d buffers each with %d x %d bytes\n", EM28XX_DVB_NUM_BUFS, - EM28XX_DVB_NUM_ISOC_PACKETS, - max_dvb_packet_size); + packet_multiplier, + dvb_max_packet_size); - return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE, 0, + return em28xx_init_usb_xfer(dev, EM28XX_DIGITAL_MODE, + dev->dvb_xfer_bulk, EM28XX_DVB_NUM_BUFS, - max_dvb_packet_size, - EM28XX_DVB_NUM_ISOC_PACKETS, + dvb_max_packet_size, + packet_multiplier, em28xx_dvb_urb_data_copy); } diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 2ad357354d81..885089e22bcd 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -13,9 +13,9 @@ #define EM_GPO_3 (1 << 3) /* em28xx endpoints */ -#define EM28XX_EP_ANALOG 0x82 +/* 0x82: (always ?) analog */ #define EM28XX_EP_AUDIO 0x83 -#define EM28XX_EP_DIGITAL 0x84 +/* 0x84: digital or analog */ /* em2800 registers */ #define EM2800_R08_AUDIOSRC 0x08 diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index d9c15b6da1af..c7e23dd73b88 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -788,16 +788,18 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (urb_init) { if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, + dev->analog_xfer_bulk, EM28XX_NUM_BUFS, dev->max_pkt_size, - EM28XX_NUM_ISOC_PACKETS, + dev->packet_multiplier, em28xx_urb_data_copy_vbi); else - rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, 0, + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, + dev->analog_xfer_bulk, EM28XX_NUM_BUFS, dev->max_pkt_size, - EM28XX_NUM_ISOC_PACKETS, + dev->packet_multiplier, em28xx_urb_data_copy); if (rc < 0) goto fail; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index f5be5229f304..aa413bd76dec 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -165,6 +165,12 @@ #define EM28XX_NUM_ISOC_PACKETS 64 #define EM28XX_DVB_NUM_ISOC_PACKETS 64 +/* bulk transfers: transfer buffer size = packet size * packet multiplier + USB 2.0 spec says bulk packet size is always 512 bytes + */ +#define EM28XX_BULK_PACKET_MULTIPLIER 384 +#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384 + #define EM28XX_INTERLACED_DEFAULT 1 /* @@ -584,8 +590,14 @@ struct em28xx { /* usb transfer */ struct usb_device *udev; /* the usb device */ + u8 analog_ep_isoc; /* address of isoc endpoint for analog */ + u8 analog_ep_bulk; /* address of bulk endpoint for analog */ + u8 dvb_ep_isoc; /* address of isoc endpoint for DVB */ + u8 dvb_ep_bulk; /* address of bulk endpoint for DVC */ int alt; /* alternate setting */ int max_pkt_size; /* max packet size of the selected ep at alt */ + int packet_multiplier; /* multiplier for wMaxPacketSize, used for + URB buffer size definition */ int num_alt; /* number of alternative settings */ unsigned int *alt_max_pkt_size_isoc; /* array of isoc wMaxPacketSize */ unsigned int analog_xfer_bulk:1; /* use bulk instead of isoc From 454fe92f01f8d669669619a9b301d133eda9d173 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 8 Nov 2012 14:11:53 -0300 Subject: [PATCH 0373/4607] [media] em28xx: add module parameter for selection of the preferred USB transfer type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default, isoc transfers are used if possible. With the new module parameter, bulk can be selected as the preferred USB transfer type. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index a6f00181c246..bc63b1cee808 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -61,6 +61,11 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); +static unsigned int prefer_bulk; +module_param(prefer_bulk, int, 0644); +MODULE_PARM_DESC(prefer_bulk, "prefer USB bulk transfers"); + + /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ static unsigned long em28xx_devused; @@ -3332,9 +3337,11 @@ static int em28xx_usb_probe(struct usb_interface *interface, } /* Select USB transfer types to use */ - if (has_video && !dev->analog_ep_isoc) + if (has_video && + (!dev->analog_ep_isoc || (prefer_bulk && dev->analog_ep_bulk))) dev->analog_xfer_bulk = 1; - if (has_dvb && !dev->dvb_ep_isoc) + if (has_dvb && + (!dev->dvb_ep_isoc || (prefer_bulk && dev->dvb_ep_bulk))) dev->dvb_xfer_bulk = 1; snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); From b77e0c088f07817ecfc4e0a34d4d6a3f99a3ecaf Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 25 Nov 2012 06:37:32 -0300 Subject: [PATCH 0374/4607] [media] em28xx: fix video data start position calculation in em28xx_urb_data_copy_vbi() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header check/removal code at the end of function em28xx_urb_data_copy_vbi() is obsolete, because this is already done earlier in this function. In fact it is incomplete (doesn't check for vbi header) and causes trouble when the first data bytes are the same as header bytes (which is fortunately very unlikely). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index c7e23dd73b88..fec8847e84d8 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -678,24 +678,8 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) dma_q->pos = 0; } - if (buf != NULL && dev->capture_type == 2) { - if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - p += 4; - len -= 4; - } - if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, len=%i, %s\n", - p[2], len, (p[2] & 1) ? - "odd" : "even"); - p += 4; - len -= 4; - } - - if (len > 0) - em28xx_copy_video(dev, dma_q, buf, p, outp, - len); - } + if (buf != NULL && dev->capture_type == 2 && len > 0) + em28xx_copy_video(dev, dma_q, buf, p, outp, len); } return rc; } From 3610f58bb1d545fe3f3767ec8ca3251383e9c728 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 25 Nov 2012 06:37:33 -0300 Subject: [PATCH 0375/4607] [media] em28xx: make sure the packet size is >= 4 before checking for headers in em28xx_urb_data_copy_vbi() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 45 +++++++++++++------------ 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index fec8847e84d8..da31fd495466 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -576,7 +576,7 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) urb->iso_frame_desc[i].offset; } - if (actual_length <= 0) { + if (actual_length == 0) { /* NOTE: happens very often with isoc transfers */ /* em28xx_usbdbg("packet %d is empty",i); - spammy */ continue; @@ -585,27 +585,30 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) /* capture type 0 = vbi start capture type 1 = video start capture type 2 = video in progress */ - if (p[0] == 0x33 && p[1] == 0x95) { - dev->capture_type = 0; - dev->vbi_read = 0; - em28xx_isocdbg("VBI START HEADER!!!\n"); - dev->cur_field = p[2]; - p += 4; - len = actual_length - 4; - } else if (p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - /* continuation */ - p += 4; - len = actual_length - 4; - } else if (p[0] == 0x22 && p[1] == 0x5a) { - /* start video */ - p += 4; - len = actual_length - 4; - } else { - /* NOTE: With bulk transfers, intermediate data packets - * have no continuation header */ - len = actual_length; + len = actual_length; + if (len >= 4) { + /* NOTE: headers are always 4 bytes and + * never split across packets */ + if (p[0] == 0x33 && p[1] == 0x95) { + dev->capture_type = 0; + dev->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER!!!\n"); + dev->cur_field = p[2]; + p += 4; + len -= 4; + } else if (p[0] == 0x88 && p[1] == 0x88 && + p[2] == 0x88 && p[3] == 0x88) { + /* continuation */ + p += 4; + len -= 4; + } else if (p[0] == 0x22 && p[1] == 0x5a) { + /* start video */ + p += 4; + len -= 4; + } } + /* NOTE: with bulk transfers, intermediate data packets + * have no continuation header */ vbi_size = dev->vbi_width * dev->vbi_height; From 0455eebfbd6c286552f9d98bdc6614dfbdd63682 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 25 Nov 2012 06:37:34 -0300 Subject: [PATCH 0376/4607] [media] em28xx: fix capture type setting in em28xx_urb_data_copy_vbi() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set capture type to 1 (video start) when the video frame start header is detected. This bug didn't cause any trouble, because this type of header is never received in vbi mode. Fix it, because we want to use this function with disabled vbi in the future. Also start with capture type -1 to avoid processing of corrupted/incomplete frame data which is usually received at streaming start (especially when USB bulk transfers are used). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 26 ++++++++++--------------- drivers/media/usb/em28xx/em28xx.h | 2 +- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index da31fd495466..d4f230002747 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -593,7 +593,7 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) dev->capture_type = 0; dev->vbi_read = 0; em28xx_isocdbg("VBI START HEADER!!!\n"); - dev->cur_field = p[2]; + dev->top_field = !(p[2] & 1); p += 4; len -= 4; } else if (p[0] == 0x88 && p[1] == 0x88 && @@ -603,6 +603,8 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) len -= 4; } else if (p[0] == 0x22 && p[1] == 0x5a) { /* start video */ + dev->capture_type = 1; + dev->top_field = !(p[2] & 1); p += 4; len -= 4; } @@ -619,8 +621,7 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) em28xx_isocdbg("dev->vbi_read > vbi_size\n"); } else if ((dev->vbi_read + len) < vbi_size) { /* This entire frame is VBI data */ - if (dev->vbi_read == 0 && - (!(dev->cur_field & 1))) { + if (dev->vbi_read == 0 && dev->top_field) { /* Brand new frame */ if (vbi_buf != NULL) vbi_buffer_filled(dev, @@ -636,12 +637,8 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) if (dev->vbi_read == 0) { vbi_dma_q->pos = 0; - if (vbi_buf != NULL) { - if (dev->cur_field & 1) - vbi_buf->top_field = 0; - else - vbi_buf->top_field = 1; - } + if (vbi_buf != NULL) + vbi_buf->top_field = dev->top_field; } dev->vbi_read += len; @@ -662,7 +659,7 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) if (dev->capture_type == 1) { dev->capture_type = 2; - if (dev->progressive || !(dev->cur_field & 1)) { + if (dev->progressive || dev->top_field) { if (buf != NULL) buffer_filled(dev, dma_q, buf); get_next_buf(dma_q, &buf); @@ -671,12 +668,8 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) else outp = videobuf_to_vmalloc(&buf->vb); } - if (buf != NULL) { - if (dev->cur_field & 1) - buf->top_field = 0; - else - buf->top_field = 1; - } + if (buf != NULL) + buf->top_field = dev->top_field; dma_q->pos = 0; } @@ -774,6 +767,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, urb_init = 1; if (urb_init) { + dev->capture_type = -1; if (em28xx_vbi_supported(dev) == 1) rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, dev->analog_xfer_bulk, diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index aa413bd76dec..09df56a4570c 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -563,7 +563,7 @@ struct em28xx { /* vbi related state tracking */ int capture_type; int vbi_read; - unsigned char cur_field; + unsigned char top_field:1; unsigned int vbi_width; unsigned int vbi_height; /* lines per field */ From 79ff8697e98295606a55f7426930affe1322f9eb Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 25 Nov 2012 06:37:36 -0300 Subject: [PATCH 0377/4607] [media] em28xx: em28xx_urb_data_copy_vbi(): calculate vbi_size only if needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index d4f230002747..c397aa213601 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -525,7 +525,7 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) struct em28xx_buffer *buf, *vbi_buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - int xfer_bulk, vbi_size, num_packets, i, rc = 1; + int xfer_bulk, num_packets, i, rc = 1; unsigned int actual_length, len = 0; unsigned char *p, *outp = NULL, *vbioutp = NULL; @@ -612,9 +612,8 @@ static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) /* NOTE: with bulk transfers, intermediate data packets * have no continuation header */ - vbi_size = dev->vbi_width * dev->vbi_height; - if (dev->capture_type == 0) { + int vbi_size = dev->vbi_width * dev->vbi_height; if (dev->vbi_read >= vbi_size) { /* We've already read all the VBI data, so treat the rest as video */ From 960da93ba56f281261038e85c57ee3ec942dc734 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 25 Nov 2012 06:37:37 -0300 Subject: [PATCH 0378/4607] [media] em28xx: use common urb data copying function for vbi and non-vbi data streams MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit em28xx_urb_data_copy_vbi() is actually an extended version of em28xx_urb_data_copy(). With the preceding fixes and improvements, it works fine with both, vbi and non-vbi data streams without performance impacts. So rename em28xx_urb_data_copy_vbi() to em28xx_urb_data_copy(), delete the the old implementation of em28xx_urb_data_copy() and change the code to use this function for both data stream types. Tested with "SilverCrest 1.3 MPix webcam" (progressive, non-vbi) and "Hauppauge HVR-900 (65008/A1C0)" (interlaced, vbi enabled and disabled). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 129 ++---------------------- drivers/media/usb/em28xx/em28xx.h | 4 +- 2 files changed, 9 insertions(+), 124 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index c397aa213601..a1436a453bc8 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -376,7 +376,6 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, /* Get the next buffer */ *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - /* Cleans up buffer - Useful for testing for frame/URB loss */ outp = videobuf_to_vmalloc(&(*buf)->vb); memset(outp, 0, (*buf)->vb.size); @@ -413,114 +412,8 @@ static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, return; } -/* Processes and copies the URB data content to a frame buffer queue */ +/* Processes and copies the URB data content (video and VBI data) */ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) -{ - struct em28xx_buffer *buf; - struct em28xx_dmaqueue *dma_q = &dev->vidq; - int xfer_bulk, num_packets, i, rc = 1; - unsigned int actual_length, len = 0; - unsigned char *p, *outp = NULL; - - if (!dev) - return 0; - - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) - return 0; - - if (urb->status < 0) - print_err_status(dev, -1, urb->status); - - xfer_bulk = usb_pipebulk(urb->pipe); - - buf = dev->usb_ctl.vid_buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - - if (xfer_bulk) /* bulk */ - num_packets = 1; - else /* isoc */ - num_packets = urb->number_of_packets; - - for (i = 0; i < num_packets; i++) { - if (xfer_bulk) { /* bulk */ - actual_length = urb->actual_length; - - p = urb->transfer_buffer; - } else { /* isoc */ - if (urb->iso_frame_desc[i].status < 0) { - print_err_status(dev, i, - urb->iso_frame_desc[i].status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } - - actual_length = urb->iso_frame_desc[i].actual_length; - if (actual_length > dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); - continue; - } - - p = urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - } - - if (actual_length <= 0) { - /* NOTE: happens very often with isoc transfers */ - /* em28xx_usbdbg("packet %d is empty",i); - spammy */ - continue; - } - - /* FIXME: incomplete buffer checks where removed to make - logic simpler. Impacts of those changes should be evaluated - */ - if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) { - em28xx_isocdbg("VBI HEADER!!!\n"); - /* FIXME: Should add vbi copy */ - continue; - } - if (p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], - len, (p[2] & 1) ? "odd" : "even"); - - if (dev->progressive || !(p[2] & 1)) { - if (buf != NULL) - buffer_filled(dev, dma_q, buf); - get_next_buf(dma_q, &buf); - if (buf == NULL) - outp = NULL; - else - outp = videobuf_to_vmalloc(&buf->vb); - } - - if (buf != NULL) { - if (p[2] & 1) - buf->top_field = 0; - else - buf->top_field = 1; - } - - dma_q->pos = 0; - } - if (buf != NULL) { - if (p[0] != 0x88 && p[0] != 0x22) { - /* NOTE: no intermediate data packet header - * 88 88 88 88 when using bulk transfers */ - em28xx_isocdbg("frame is not complete\n"); - len = actual_length; - } else { - len = actual_length - 4; - p += 4; - } - em28xx_copy_video(dev, dma_q, buf, p, outp, len); - } - } - return rc; -} - -/* Version of the urb data handler that takes into account a mixture of - video and VBI data */ -static inline int em28xx_urb_data_copy_vbi(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf, *vbi_buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; @@ -767,20 +660,12 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, if (urb_init) { dev->capture_type = -1; - if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, - dev->analog_xfer_bulk, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - dev->packet_multiplier, - em28xx_urb_data_copy_vbi); - else - rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, - dev->analog_xfer_bulk, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - dev->packet_multiplier, - em28xx_urb_data_copy); + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, + dev->analog_xfer_bulk, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + dev->packet_multiplier, + em28xx_urb_data_copy); if (rc < 0) goto fail; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 09df56a4570c..304896de4879 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -560,10 +560,10 @@ struct em28xx { /* states */ enum em28xx_dev_state state; - /* vbi related state tracking */ + /* capture state tracking */ int capture_type; - int vbi_read; unsigned char top_field:1; + int vbi_read; unsigned int vbi_width; unsigned int vbi_height; /* lines per field */ From 24a6d8497f7e64a8870018ed1ed561755b2075ec Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:24 -0300 Subject: [PATCH 0379/4607] [media] em28xx: refactor get_next_buf() and use it for vbi data, too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_next_buf() and vbi_get_next_buf() do exactly the same just with a different dma queue and buffer. Saving the new buffer pointer back to the device struct in em28xx_urb_data_copy() instead of doing this from inside these functions makes it possible to get rid of one of them. Also refactor the function parameters and return type: - pass a pointer to struct em28xx as parameter (instead of obtaining the pointer from the dma queue pointer with the container_of macro) like we do it in all other functions - instead of using a pointer-pointer, return the pointer to the new buffer as return value of the function Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 58 +++++++------------------ 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index a1436a453bc8..db27499ecaa2 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -359,57 +359,26 @@ static inline void print_err_status(struct em28xx *dev, } /* - * video-buf generic routine to get the next available buffer + * get the next available buffer from dma queue */ -static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) +static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q) { - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + struct em28xx_buffer *buf; char *outp; if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->usb_ctl.vid_buf = NULL; - *buf = NULL; - return; + return NULL; } /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); + buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0, (*buf)->vb.size); + outp = videobuf_to_vmalloc(&buf->vb); + memset(outp, 0, buf->vb.size); - dev->usb_ctl.vid_buf = *buf; - - return; -} - -/* - * video-buf generic routine to get the next available VBI buffer - */ -static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) -{ - struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq); - char *outp; - - if (list_empty(&dma_q->active)) { - em28xx_isocdbg("No active queue to serve\n"); - dev->usb_ctl.vbi_buf = NULL; - *buf = NULL; - return; - } - - /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0x00, (*buf)->vb.size); - - dev->usb_ctl.vbi_buf = *buf; - - return; + return buf; } /* Processes and copies the URB data content (video and VBI data) */ @@ -519,7 +488,8 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) vbi_buffer_filled(dev, vbi_dma_q, vbi_buf); - vbi_get_next_buf(vbi_dma_q, &vbi_buf); + vbi_buf = get_next_buf(dev, vbi_dma_q); + dev->usb_ctl.vbi_buf = vbi_buf; if (vbi_buf == NULL) vbioutp = NULL; else @@ -530,7 +500,8 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) if (dev->vbi_read == 0) { vbi_dma_q->pos = 0; if (vbi_buf != NULL) - vbi_buf->top_field = dev->top_field; + vbi_buf->top_field + = dev->top_field; } dev->vbi_read += len; @@ -554,7 +525,8 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) if (dev->progressive || dev->top_field) { if (buf != NULL) buffer_filled(dev, dma_q, buf); - get_next_buf(dma_q, &buf); + buf = get_next_buf(dev, dma_q); + dev->usb_ctl.vid_buf = buf; if (buf == NULL) outp = NULL; else From 948a49aa692e12cc33558e407898c467b22bf9b4 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:25 -0300 Subject: [PATCH 0380/4607] [media] em28xx: use common function for video and vbi buffer completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 34 ++++--------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index db27499ecaa2..f9f24215b56c 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -154,37 +154,15 @@ static struct v4l2_queryctrl ac97_qctrl[] = { ------------------------------------------------------------------*/ /* - * Announces that a buffer were filled and request the next + * Finish the current buffer */ -static inline void buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) +static inline void finish_buffer(struct em28xx *dev, + struct em28xx_buffer *buf) { - /* Advice that buffer was filled */ em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; v4l2_get_timestamp(&buf->vb.ts); - - dev->usb_ctl.vid_buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); -} - -static inline void vbi_buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) -{ - /* Advice that buffer was filled */ - em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); - - dev->usb_ctl.vbi_buf = NULL; - list_del(&buf->vb.queue); wake_up(&buf->vb.done); } @@ -485,9 +463,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) if (dev->vbi_read == 0 && dev->top_field) { /* Brand new frame */ if (vbi_buf != NULL) - vbi_buffer_filled(dev, - vbi_dma_q, - vbi_buf); + finish_buffer(dev, vbi_buf); vbi_buf = get_next_buf(dev, vbi_dma_q); dev->usb_ctl.vbi_buf = vbi_buf; if (vbi_buf == NULL) @@ -524,7 +500,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) dev->capture_type = 2; if (dev->progressive || dev->top_field) { if (buf != NULL) - buffer_filled(dev, dma_q, buf); + finish_buffer(dev, buf); buf = get_next_buf(dev, dma_q); dev->usb_ctl.vid_buf = buf; if (buf == NULL) From cbe7f8a030f2056d5cee8c2729d5edd23ae61589 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:26 -0300 Subject: [PATCH 0381/4607] [media] em28xx: remove obsolete field 'frame' from struct em28xx_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 304896de4879..b3d72a92dd9b 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -252,7 +252,6 @@ struct em28xx_buffer { /* common v4l buffer stuff -- must be first */ struct videobuf_buffer vb; - struct list_head frame; int top_field; }; From 8732533b3284ca078e3ea4a4721e43627ff7fa8e Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:27 -0300 Subject: [PATCH 0382/4607] [media] em28xx: move field 'pos' from struct em28xx_dmaqueue to struct em28xx_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This field is used to keep track of the current memory position in the buffer, not in the dma queue, so move it to right place. This also allows us to get rid of the struct em28xx_dmaqueue pointer parameter in functions em28xx_copy_video() and em28xx_copy_vbi(). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 53 +++++++++++-------------- drivers/media/usb/em28xx/em28xx.h | 8 ++-- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index f9f24215b56c..565b6462f152 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -171,7 +171,6 @@ static inline void finish_buffer(struct em28xx *dev, * Identify the buffer header type and properly handles */ static void em28xx_copy_video(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, struct em28xx_buffer *buf, unsigned char *p, unsigned char *outp, unsigned long len) @@ -180,8 +179,8 @@ static void em28xx_copy_video(struct em28xx *dev, int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; + if (buf->pos + len > buf->vb.size) + len = buf->vb.size - buf->pos; startread = p; remain = len; @@ -191,8 +190,8 @@ static void em28xx_copy_video(struct em28xx *dev, else /* interlaced mode, even nr. of lines */ fieldstart = outp + bytesperline; - linesdone = dma_q->pos / bytesperline; - currlinedone = dma_q->pos % bytesperline; + linesdone = buf->pos / bytesperline; + currlinedone = buf->pos % bytesperline; if (dev->progressive) offset = linesdone * bytesperline + currlinedone; @@ -244,14 +243,13 @@ static void em28xx_copy_video(struct em28xx *dev, remain -= lencopy; } - dma_q->pos += len; + buf->pos += len; } static void em28xx_copy_vbi(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf, - unsigned char *p, - unsigned char *outp, unsigned long len) + struct em28xx_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) { void *startwrite, *startread; int offset; @@ -263,10 +261,6 @@ static void em28xx_copy_vbi(struct em28xx *dev, } bytesperline = dev->vbi_width; - if (dma_q == NULL) { - em28xx_isocdbg("dma_q is null\n"); - return; - } if (buf == NULL) { return; } @@ -279,13 +273,13 @@ static void em28xx_copy_vbi(struct em28xx *dev, return; } - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; + if (buf->pos + len > buf->vb.size) + len = buf->vb.size - buf->pos; startread = p; - startwrite = outp + dma_q->pos; - offset = dma_q->pos; + startwrite = outp + buf->pos; + offset = buf->pos; /* Make sure the bottom field populates the second half of the frame */ if (buf->top_field == 0) { @@ -294,7 +288,7 @@ static void em28xx_copy_vbi(struct em28xx *dev, } memcpy(startwrite, startread, len); - dma_q->pos += len; + buf->pos += len; } static inline void print_err_status(struct em28xx *dev, @@ -355,6 +349,7 @@ static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, /* Cleans up buffer - Useful for testing for frame/URB loss */ outp = videobuf_to_vmalloc(&buf->vb); memset(outp, 0, buf->vb.size); + buf->pos = 0; return buf; } @@ -474,22 +469,22 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } if (dev->vbi_read == 0) { - vbi_dma_q->pos = 0; - if (vbi_buf != NULL) + if (vbi_buf != NULL) { vbi_buf->top_field = dev->top_field; + vbi_buf->pos = 0; + } } dev->vbi_read += len; - em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, - vbioutp, len); + em28xx_copy_vbi(dev, vbi_buf, p, vbioutp, len); } else { /* Some of this frame is VBI data and some is video data */ int vbi_data_len = vbi_size - dev->vbi_read; dev->vbi_read += vbi_data_len; - em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, - vbioutp, vbi_data_len); + em28xx_copy_vbi(dev, vbi_buf, p, vbioutp, + vbi_data_len); dev->capture_type = 1; p += vbi_data_len; len -= vbi_data_len; @@ -508,14 +503,14 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) else outp = videobuf_to_vmalloc(&buf->vb); } - if (buf != NULL) + if (buf != NULL) { buf->top_field = dev->top_field; - - dma_q->pos = 0; + buf->pos = 0; + } } if (buf != NULL && dev->capture_type == 2 && len > 0) - em28xx_copy_video(dev, dma_q, buf, p, outp, len); + em28xx_copy_video(dev, buf, p, outp, len); } return rc; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index b3d72a92dd9b..7507aa6580d0 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -253,15 +253,17 @@ struct em28xx_buffer { struct videobuf_buffer vb; int top_field; + + /* counter to control buffer fill */ + unsigned int pos; + /* NOTE; in interlaced mode, this value is reset to zero at + * the start of each new field (not frame !) */ }; struct em28xx_dmaqueue { struct list_head active; wait_queue_head_t wq; - - /* Counters to control buffer fill */ - int pos; }; /* inputs */ From a48370158d134807f5b02655287b91cc000c45ca Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:28 -0300 Subject: [PATCH 0383/4607] [media] em28xx: refactor VBI data processing code in em28xx_urb_data_copy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a new frame header is detected in em28xx_urb_data_copy() and the data packet contains both, VBI data and video data, the prevoius VBI buffer doesn't get finished and is overwritten with the new VBI data. This bug is not triggered with isochronous USB transfers, because the data packetes are much smaller than the VBI data size. But when using USB bulk transfers, the whole data of an URB is treated as single packet, which is usually much larger then the VBI data size. Refactor the VBI data processing code to fix this bug, but also to simplify the code and make it similar to the video data processing code part (which allows further code abstraction/unification in the future). The changes have been tested with device "Hauppauge HVR-900". Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 75 ++++++++++++------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 565b6462f152..3a13a7138e73 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -418,8 +418,9 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } /* capture type 0 = vbi start - capture type 1 = video start - capture type 2 = video in progress */ + capture type 1 = vbi in progress + capture type 2 = video start + capture type 3 = video in progress */ len = actual_length; if (len >= 4) { /* NOTE: headers are always 4 bytes and @@ -438,7 +439,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) len -= 4; } else if (p[0] == 0x22 && p[1] == 0x5a) { /* start video */ - dev->capture_type = 1; + dev->capture_type = 2; dev->top_field = !(p[2] & 1); p += 4; len -= 4; @@ -448,51 +449,45 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) * have no continuation header */ if (dev->capture_type == 0) { + dev->capture_type = 1; + if (dev->top_field) { /* Brand new frame */ + if (vbi_buf != NULL) + finish_buffer(dev, vbi_buf); + vbi_buf = get_next_buf(dev, vbi_dma_q); + dev->usb_ctl.vbi_buf = vbi_buf; + if (vbi_buf == NULL) + vbioutp = NULL; + else + vbioutp = + videobuf_to_vmalloc(&vbi_buf->vb); + } + if (vbi_buf != NULL) { + vbi_buf->top_field = dev->top_field; + vbi_buf->pos = 0; + } + } + + if (dev->capture_type == 1) { int vbi_size = dev->vbi_width * dev->vbi_height; - if (dev->vbi_read >= vbi_size) { - /* We've already read all the VBI data, so - treat the rest as video */ - em28xx_isocdbg("dev->vbi_read > vbi_size\n"); - } else if ((dev->vbi_read + len) < vbi_size) { - /* This entire frame is VBI data */ - if (dev->vbi_read == 0 && dev->top_field) { - /* Brand new frame */ - if (vbi_buf != NULL) - finish_buffer(dev, vbi_buf); - vbi_buf = get_next_buf(dev, vbi_dma_q); - dev->usb_ctl.vbi_buf = vbi_buf; - if (vbi_buf == NULL) - vbioutp = NULL; - else - vbioutp = videobuf_to_vmalloc( - &vbi_buf->vb); - } + int vbi_data_len = ((dev->vbi_read + len) > vbi_size) ? + (vbi_size - dev->vbi_read) : len; - if (dev->vbi_read == 0) { - if (vbi_buf != NULL) { - vbi_buf->top_field - = dev->top_field; - vbi_buf->pos = 0; - } - } - - dev->vbi_read += len; - em28xx_copy_vbi(dev, vbi_buf, p, vbioutp, len); - } else { - /* Some of this frame is VBI data and some is - video data */ - int vbi_data_len = vbi_size - dev->vbi_read; - dev->vbi_read += vbi_data_len; + /* Copy VBI data */ + if (vbi_buf != NULL) em28xx_copy_vbi(dev, vbi_buf, p, vbioutp, vbi_data_len); - dev->capture_type = 1; + dev->vbi_read += vbi_data_len; + + if (vbi_data_len < len) { + /* Continue with copying video data */ + dev->capture_type = 2; p += vbi_data_len; len -= vbi_data_len; } } - if (dev->capture_type == 1) { - dev->capture_type = 2; + if (dev->capture_type == 2) { + dev->capture_type = 3; if (dev->progressive || dev->top_field) { if (buf != NULL) finish_buffer(dev, buf); @@ -509,7 +504,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } } - if (buf != NULL && dev->capture_type == 2 && len > 0) + if (buf != NULL && dev->capture_type == 3 && len > 0) em28xx_copy_video(dev, buf, p, outp, len); } return rc; From 4078d625c9610a362f571f7e5ff2521adadfff2b Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:29 -0300 Subject: [PATCH 0384/4607] [media] em28xx: move caching of pointer to vmalloc memory in videobuf to struct em28xx_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the current code em28xx_urb_data_copy() caches the pointer to the vmalloc memory in videobuf locally. The alternative would be to call videobuf_to_vmalloc() for each processed USB data packet (isoc USB transfers => 64 times per URB) in the em28xx_copy_*() functions. With the next commits, the data processing code will be split into functions for serveral reasons: - em28xx_urb_data_copy() is generally way to long, making it less readable - there is code duplication between VBI and video data processing - support for em25xx data processing (uses a different header and frame end signaling mechanism) will be added This would require extensive usage of pointer-pointers, which usually makes the code less readable and prone to bugs. The better solution is to cache the pointer in struct em28xx_buffer. This also improves consistency, because we already track the buffer fill count there. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 29 ++++++++----------------- drivers/media/usb/em28xx/em28xx.h | 3 +++ 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 3a13a7138e73..da59442d40a5 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -173,11 +173,12 @@ static inline void finish_buffer(struct em28xx *dev, static void em28xx_copy_video(struct em28xx *dev, struct em28xx_buffer *buf, unsigned char *p, - unsigned char *outp, unsigned long len) + unsigned long len) { void *fieldstart, *startwrite, *startread; int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; + unsigned char *outp = buf->vb_buf; if (buf->pos + len > buf->vb.size) len = buf->vb.size - buf->pos; @@ -249,11 +250,12 @@ static void em28xx_copy_video(struct em28xx *dev, static void em28xx_copy_vbi(struct em28xx *dev, struct em28xx_buffer *buf, unsigned char *p, - unsigned char *outp, unsigned long len) + unsigned long len) { void *startwrite, *startread; int offset; int bytesperline; + unsigned char *outp; if (dev == NULL) { em28xx_isocdbg("dev is null\n"); @@ -268,6 +270,7 @@ static void em28xx_copy_vbi(struct em28xx *dev, em28xx_isocdbg("p is null\n"); return; } + outp = buf->vb_buf; if (outp == NULL) { em28xx_isocdbg("outp is null\n"); return; @@ -350,6 +353,7 @@ static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, outp = videobuf_to_vmalloc(&buf->vb); memset(outp, 0, buf->vb.size); buf->pos = 0; + buf->vb_buf = outp; return buf; } @@ -362,7 +366,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; int xfer_bulk, num_packets, i, rc = 1; unsigned int actual_length, len = 0; - unsigned char *p, *outp = NULL, *vbioutp = NULL; + unsigned char *p; if (!dev) return 0; @@ -376,12 +380,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) xfer_bulk = usb_pipebulk(urb->pipe); buf = dev->usb_ctl.vid_buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - vbi_buf = dev->usb_ctl.vbi_buf; - if (vbi_buf != NULL) - vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); if (xfer_bulk) /* bulk */ num_packets = 1; @@ -455,11 +454,6 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) finish_buffer(dev, vbi_buf); vbi_buf = get_next_buf(dev, vbi_dma_q); dev->usb_ctl.vbi_buf = vbi_buf; - if (vbi_buf == NULL) - vbioutp = NULL; - else - vbioutp = - videobuf_to_vmalloc(&vbi_buf->vb); } if (vbi_buf != NULL) { vbi_buf->top_field = dev->top_field; @@ -474,8 +468,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) /* Copy VBI data */ if (vbi_buf != NULL) - em28xx_copy_vbi(dev, vbi_buf, p, vbioutp, - vbi_data_len); + em28xx_copy_vbi(dev, vbi_buf, p, vbi_data_len); dev->vbi_read += vbi_data_len; if (vbi_data_len < len) { @@ -493,10 +486,6 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) finish_buffer(dev, buf); buf = get_next_buf(dev, dma_q); dev->usb_ctl.vid_buf = buf; - if (buf == NULL) - outp = NULL; - else - outp = videobuf_to_vmalloc(&buf->vb); } if (buf != NULL) { buf->top_field = dev->top_field; @@ -505,7 +494,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } if (buf != NULL && dev->capture_type == 3 && len > 0) - em28xx_copy_video(dev, buf, p, outp, len); + em28xx_copy_video(dev, buf, p, len); } return rc; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 7507aa6580d0..062841e50722 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -258,6 +258,9 @@ struct em28xx_buffer { unsigned int pos; /* NOTE; in interlaced mode, this value is reset to zero at * the start of each new field (not frame !) */ + + /* pointer to vmalloc memory address in vb */ + char *vb_buf; }; struct em28xx_dmaqueue { From e04c00d985c62a6e1cc6c8048308f3216442f708 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:30 -0300 Subject: [PATCH 0385/4607] [media] em28xx: em28xx_urb_data_copy(): move duplicate code for capture_type=0 and capture_type=2 to a function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce code duplication by moving the duplicate code for dev->capture_type=0 (vbi start) and dev->capture_type=2 (video start) to a function. The same function will also be called by the (not yet existing) em25xx frame data processing code. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 45 ++++++++++++++----------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index da59442d40a5..a1f6de0f1b3b 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -358,6 +358,27 @@ static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, return buf; } +/* + * Finish the current buffer if completed and prepare for the next field + */ +static struct em28xx_buffer * +finish_field_prepare_next(struct em28xx *dev, + struct em28xx_buffer *buf, + struct em28xx_dmaqueue *dma_q) +{ + if (dev->progressive || dev->top_field) { /* Brand new frame */ + if (buf != NULL) + finish_buffer(dev, buf); + buf = get_next_buf(dev, dma_q); + } + if (buf != NULL) { + buf->top_field = dev->top_field; + buf->pos = 0; + } + + return buf; +} + /* Processes and copies the URB data content (video and VBI data) */ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) { @@ -448,17 +469,9 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) * have no continuation header */ if (dev->capture_type == 0) { + vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); + dev->usb_ctl.vbi_buf = vbi_buf; dev->capture_type = 1; - if (dev->top_field) { /* Brand new frame */ - if (vbi_buf != NULL) - finish_buffer(dev, vbi_buf); - vbi_buf = get_next_buf(dev, vbi_dma_q); - dev->usb_ctl.vbi_buf = vbi_buf; - } - if (vbi_buf != NULL) { - vbi_buf->top_field = dev->top_field; - vbi_buf->pos = 0; - } } if (dev->capture_type == 1) { @@ -480,17 +493,9 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } if (dev->capture_type == 2) { + buf = finish_field_prepare_next(dev, buf, dma_q); + dev->usb_ctl.vid_buf = buf; dev->capture_type = 3; - if (dev->progressive || dev->top_field) { - if (buf != NULL) - finish_buffer(dev, buf); - buf = get_next_buf(dev, dma_q); - dev->usb_ctl.vid_buf = buf; - } - if (buf != NULL) { - buf->top_field = dev->top_field; - buf->pos = 0; - } } if (buf != NULL && dev->capture_type == 3 && len > 0) From 227b7c90671624e0d143e324a3015726282981df Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:31 -0300 Subject: [PATCH 0386/4607] [media] em28xx: move the em2710/em2750/em28xx specific frame data processing code to a separate function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit em28xx_urb_data_copy() actually consists of two parts: USB urb processing (checks, data extraction) and frame data packet processing. Move the latter to a separate function and call it from em28xx_urb_data_copy() for each data packet. The em25xx, em2760, em2765 (and likely em277x) chip variants are using a different frame data format, for which support will be added later with another function. This reduces the size of em28xx_urb_data_copy() and makes the code much more readable. While we're at it, clean up the code a bit (rename some variables to something more meaningful, improve some comments etc.) Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 170 +++++++++++++----------- 1 file changed, 90 insertions(+), 80 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index a1f6de0f1b3b..133e6b0a5c7d 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -379,15 +379,90 @@ finish_field_prepare_next(struct em28xx *dev, return buf; } +/* + * Process data packet according to the em2710/em2750/em28xx frame data format + */ +static inline void process_frame_data_em28xx(struct em28xx *dev, + unsigned char *data_pkt, + unsigned int data_len) +{ + struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; + struct em28xx_buffer *vbi_buf = dev->usb_ctl.vbi_buf; + struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + + /* capture type 0 = vbi start + capture type 1 = vbi in progress + capture type 2 = video start + capture type 3 = video in progress */ + if (data_len >= 4) { + /* NOTE: Headers are always 4 bytes and + * never split across packets */ + if (data_pkt[0] == 0x88 && data_pkt[1] == 0x88 && + data_pkt[2] == 0x88 && data_pkt[3] == 0x88) { + /* Continuation */ + data_pkt += 4; + data_len -= 4; + } else if (data_pkt[0] == 0x33 && data_pkt[1] == 0x95) { + /* Field start (VBI mode) */ + dev->capture_type = 0; + dev->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER !!!\n"); + dev->top_field = !(data_pkt[2] & 1); + data_pkt += 4; + data_len -= 4; + } else if (data_pkt[0] == 0x22 && data_pkt[1] == 0x5a) { + /* Field start (VBI disabled) */ + dev->capture_type = 2; + em28xx_isocdbg("VIDEO START HEADER !!!\n"); + dev->top_field = !(data_pkt[2] & 1); + data_pkt += 4; + data_len -= 4; + } + } + /* NOTE: With bulk transfers, intermediate data packets + * have no continuation header */ + + if (dev->capture_type == 0) { + vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); + dev->usb_ctl.vbi_buf = vbi_buf; + dev->capture_type = 1; + } + + if (dev->capture_type == 1) { + int vbi_size = dev->vbi_width * dev->vbi_height; + int vbi_data_len = ((dev->vbi_read + data_len) > vbi_size) ? + (vbi_size - dev->vbi_read) : data_len; + + /* Copy VBI data */ + if (vbi_buf != NULL) + em28xx_copy_vbi(dev, vbi_buf, data_pkt, vbi_data_len); + dev->vbi_read += vbi_data_len; + + if (vbi_data_len < data_len) { + /* Continue with copying video data */ + dev->capture_type = 2; + data_pkt += vbi_data_len; + data_len -= vbi_data_len; + } + } + + if (dev->capture_type == 2) { + buf = finish_field_prepare_next(dev, buf, dma_q); + dev->usb_ctl.vid_buf = buf; + dev->capture_type = 3; + } + + if (dev->capture_type == 3 && buf != NULL && data_len > 0) + em28xx_copy_video(dev, buf, data_pkt, data_len); +} + /* Processes and copies the URB data content (video and VBI data) */ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) { - struct em28xx_buffer *buf, *vbi_buf; - struct em28xx_dmaqueue *dma_q = &dev->vidq; - struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - int xfer_bulk, num_packets, i, rc = 1; - unsigned int actual_length, len = 0; - unsigned char *p; + int xfer_bulk, num_packets, i; + unsigned char *usb_data_pkt; + unsigned int usb_data_len; if (!dev) return 0; @@ -400,9 +475,6 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) xfer_bulk = usb_pipebulk(urb->pipe); - buf = dev->usb_ctl.vid_buf; - vbi_buf = dev->usb_ctl.vbi_buf; - if (xfer_bulk) /* bulk */ num_packets = 1; else /* isoc */ @@ -410,9 +482,9 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) for (i = 0; i < num_packets; i++) { if (xfer_bulk) { /* bulk */ - actual_length = urb->actual_length; + usb_data_len = urb->actual_length; - p = urb->transfer_buffer; + usb_data_pkt = urb->transfer_buffer; } else { /* isoc */ if (urb->iso_frame_desc[i].status < 0) { print_err_status(dev, i, @@ -421,87 +493,25 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) continue; } - actual_length = urb->iso_frame_desc[i].actual_length; - if (actual_length > dev->max_pkt_size) { + usb_data_len = urb->iso_frame_desc[i].actual_length; + if (usb_data_len > dev->max_pkt_size) { em28xx_isocdbg("packet bigger than packet size"); continue; } - p = urb->transfer_buffer + - urb->iso_frame_desc[i].offset; + usb_data_pkt = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; } - if (actual_length == 0) { + if (usb_data_len == 0) { /* NOTE: happens very often with isoc transfers */ /* em28xx_usbdbg("packet %d is empty",i); - spammy */ continue; } - /* capture type 0 = vbi start - capture type 1 = vbi in progress - capture type 2 = video start - capture type 3 = video in progress */ - len = actual_length; - if (len >= 4) { - /* NOTE: headers are always 4 bytes and - * never split across packets */ - if (p[0] == 0x33 && p[1] == 0x95) { - dev->capture_type = 0; - dev->vbi_read = 0; - em28xx_isocdbg("VBI START HEADER!!!\n"); - dev->top_field = !(p[2] & 1); - p += 4; - len -= 4; - } else if (p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - /* continuation */ - p += 4; - len -= 4; - } else if (p[0] == 0x22 && p[1] == 0x5a) { - /* start video */ - dev->capture_type = 2; - dev->top_field = !(p[2] & 1); - p += 4; - len -= 4; - } - } - /* NOTE: with bulk transfers, intermediate data packets - * have no continuation header */ - - if (dev->capture_type == 0) { - vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); - dev->usb_ctl.vbi_buf = vbi_buf; - dev->capture_type = 1; - } - - if (dev->capture_type == 1) { - int vbi_size = dev->vbi_width * dev->vbi_height; - int vbi_data_len = ((dev->vbi_read + len) > vbi_size) ? - (vbi_size - dev->vbi_read) : len; - - /* Copy VBI data */ - if (vbi_buf != NULL) - em28xx_copy_vbi(dev, vbi_buf, p, vbi_data_len); - dev->vbi_read += vbi_data_len; - - if (vbi_data_len < len) { - /* Continue with copying video data */ - dev->capture_type = 2; - p += vbi_data_len; - len -= vbi_data_len; - } - } - - if (dev->capture_type == 2) { - buf = finish_field_prepare_next(dev, buf, dma_q); - dev->usb_ctl.vid_buf = buf; - dev->capture_type = 3; - } - - if (buf != NULL && dev->capture_type == 3 && len > 0) - em28xx_copy_video(dev, buf, p, len); + process_frame_data_em28xx(dev, usb_data_pkt, usb_data_len); } - return rc; + return 1; } From 36016a351d6245b2753138dff3022efba822e5e2 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 8 Dec 2012 11:31:32 -0300 Subject: [PATCH 0387/4607] [media] em28xx: clean up and unify functions em28xx_copy_vbi() em28xx_copy_video() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code in em28xx_vbi_copy can be simplified a lot. Also rename some variables to something more meaningful and fix+add the function descriptions. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 67 ++++++++----------------- 1 file changed, 21 insertions(+), 46 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 133e6b0a5c7d..4c1726d43374 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -168,28 +168,27 @@ static inline void finish_buffer(struct em28xx *dev, } /* - * Identify the buffer header type and properly handles + * Copy picture data from USB buffer to videobuf buffer */ static void em28xx_copy_video(struct em28xx *dev, struct em28xx_buffer *buf, - unsigned char *p, + unsigned char *usb_buf, unsigned long len) { void *fieldstart, *startwrite, *startread; int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; - unsigned char *outp = buf->vb_buf; if (buf->pos + len > buf->vb.size) len = buf->vb.size - buf->pos; - startread = p; + startread = usb_buf; remain = len; if (dev->progressive || buf->top_field) - fieldstart = outp; + fieldstart = buf->vb_buf; else /* interlaced mode, even nr. of lines */ - fieldstart = outp + bytesperline; + fieldstart = buf->vb_buf + bytesperline; linesdone = buf->pos / bytesperline; currlinedone = buf->pos % bytesperline; @@ -203,11 +202,12 @@ static void em28xx_copy_video(struct em28xx *dev, lencopy = bytesperline - currlinedone; lencopy = lencopy > remain ? remain : lencopy; - if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->vb.size) { em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", - ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - remain = (char *)outp + buf->vb.size - (char *)startwrite; + ((char *)startwrite + lencopy) - + ((char *)buf->vb_buf + buf->vb.size)); + remain = (char *)buf->vb_buf + buf->vb.size - + (char *)startwrite; lencopy = remain; } if (lencopy <= 0) @@ -227,13 +227,13 @@ static void em28xx_copy_video(struct em28xx *dev, else lencopy = bytesperline; - if ((char *)startwrite + lencopy > (char *)outp + + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->vb.size) { em28xx_isocdbg("Overflow of %zi bytes past buffer end" "(2)\n", ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - lencopy = remain = (char *)outp + buf->vb.size - + ((char *)buf->vb_buf + buf->vb.size)); + lencopy = remain = (char *)buf->vb_buf + buf->vb.size - (char *)startwrite; } if (lencopy <= 0) @@ -247,50 +247,25 @@ static void em28xx_copy_video(struct em28xx *dev, buf->pos += len; } +/* + * Copy VBI data from USB buffer to videobuf buffer + */ static void em28xx_copy_vbi(struct em28xx *dev, struct em28xx_buffer *buf, - unsigned char *p, + unsigned char *usb_buf, unsigned long len) { - void *startwrite, *startread; - int offset; - int bytesperline; - unsigned char *outp; - - if (dev == NULL) { - em28xx_isocdbg("dev is null\n"); - return; - } - bytesperline = dev->vbi_width; - - if (buf == NULL) { - return; - } - if (p == NULL) { - em28xx_isocdbg("p is null\n"); - return; - } - outp = buf->vb_buf; - if (outp == NULL) { - em28xx_isocdbg("outp is null\n"); - return; - } + unsigned int offset; if (buf->pos + len > buf->vb.size) len = buf->vb.size - buf->pos; - startread = p; - - startwrite = outp + buf->pos; offset = buf->pos; - /* Make sure the bottom field populates the second half of the frame */ - if (buf->top_field == 0) { - startwrite += bytesperline * dev->vbi_height; - offset += bytesperline * dev->vbi_height; - } + if (buf->top_field == 0) + offset += dev->vbi_width * dev->vbi_height; - memcpy(startwrite, startread, len); + memcpy(buf->vb_buf + offset, usb_buf, len); buf->pos += len; } From a6bad040dacb7c0c59173feba98001ae1d4811e4 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 16 Dec 2012 14:23:27 -0300 Subject: [PATCH 0388/4607] [media] em28xx: clean up the data type mess of the i2c transfer function parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 28 +++++++++++---------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 1683bd9d51ee..44533e4574ff 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -53,12 +53,11 @@ do { \ * em2800_i2c_send_max4() * send up to 4 bytes to the i2c device */ -static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em2800_i2c_send_max4(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { int ret; int write_timeout; - unsigned char b2[6]; + u8 b2[6]; BUG_ON(len < 1 || len > 4); b2[5] = 0x80 + len - 1; b2[4] = addr; @@ -89,15 +88,13 @@ static int em2800_i2c_send_max4(struct em28xx *dev, unsigned char addr, /* * em2800_i2c_send_bytes() */ -static int em2800_i2c_send_bytes(void *data, unsigned char addr, char *buf, - short len) +static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { - char *bufPtr = buf; + u8 *bufPtr = buf; int ret; int wrcount = 0; int count; int maxLen = 4; - struct em28xx *dev = (struct em28xx *)data; while (len > 0) { count = (len > maxLen) ? maxLen : len; ret = em2800_i2c_send_max4(dev, addr, bufPtr, count); @@ -115,9 +112,9 @@ static int em2800_i2c_send_bytes(void *data, unsigned char addr, char *buf, * em2800_i2c_check_for_device() * check if there is a i2c_device at the supplied address */ -static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr) +static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) { - char msg; + u8 msg; int ret; int write_timeout; msg = addr; @@ -150,8 +147,7 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr) * em2800_i2c_recv_bytes() * read from the i2c device */ -static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { int ret; /* check for the device and set i2c read address */ @@ -174,11 +170,10 @@ static int em2800_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, /* * em28xx_i2c_send_bytes() */ -static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf, - short len, int stop) +static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, + u16 len, int stop) { int wrcount = 0; - struct em28xx *dev = (struct em28xx *)data; int write_timeout, ret; wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); @@ -199,8 +194,7 @@ static int em28xx_i2c_send_bytes(void *data, unsigned char addr, char *buf, * em28xx_i2c_recv_bytes() * read a byte from the i2c device */ -static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, - char *buf, int len) +static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) { int ret; ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len); @@ -217,7 +211,7 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, unsigned char addr, * em28xx_i2c_check_for_device() * check if there is a i2c_device at the supplied address */ -static int em28xx_i2c_check_for_device(struct em28xx *dev, unsigned char addr) +static int em28xx_i2c_check_for_device(struct em28xx *dev, u16 addr) { int ret; From 871a069db9f29938071b3d214cb964980071ffbf Mon Sep 17 00:00:00 2001 From: Yang Zhang Date: Wed, 12 Dec 2012 13:05:12 +0800 Subject: [PATCH 0389/4607] KVM: remove a wrong hack of delivery PIT intr to vcpu0 This hack is wrong. The pin number of PIT is connected to 2 not 0. This means this hack never takes effect. So it is ok to remove it. Signed-off-by: Yang Zhang Signed-off-by: Gleb Natapov --- virt/kvm/ioapic.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c index cfb7e4d52dc2..f3abbef46c42 100644 --- a/virt/kvm/ioapic.c +++ b/virt/kvm/ioapic.c @@ -179,15 +179,6 @@ static int ioapic_deliver(struct kvm_ioapic *ioapic, int irq) irqe.level = 1; irqe.shorthand = 0; -#ifdef CONFIG_X86 - /* Always delivery PIT interrupt to vcpu 0 */ - if (irq == 0) { - irqe.dest_mode = 0; /* Physical mode. */ - /* need to read apic_id from apic regiest since - * it can be rewritten */ - irqe.dest_id = ioapic->kvm->bsp_vcpu_id; - } -#endif return kvm_irq_delivery_to_apic(ioapic->kvm, NULL, &irqe); } From 116c14c0191f3378e6567af296529ac287e85aa2 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 21 Dec 2012 08:20:16 -0700 Subject: [PATCH 0390/4607] kvm: Fix memory slot generation updates Previous patch "kvm: Minor memory slot optimization" (b7f69c555ca43) overlooked the generation field of the memory slots. Re-using the original memory slots left us with with two slightly different memory slots with the same generation. To fix this, make update_memslots() take a new parameter to specify the last generation. This also makes generation management more explicit to avoid such problems in the future. Reported-by: Takuya Yoshikawa Signed-off-by: Alex Williamson Signed-off-by: Gleb Natapov --- include/linux/kvm_host.h | 3 ++- virt/kvm/kvm_main.c | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 32fdc45ca35e..cbe0d683e2e5 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -428,7 +428,8 @@ void kvm_exit(void); void kvm_get_kvm(struct kvm *kvm); void kvm_put_kvm(struct kvm *kvm); -void update_memslots(struct kvm_memslots *slots, struct kvm_memory_slot *new); +void update_memslots(struct kvm_memslots *slots, struct kvm_memory_slot *new, + u64 last_generation); static inline struct kvm_memslots *kvm_memslots(struct kvm *kvm) { diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index bd31096e3698..14cbae83be64 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -672,7 +672,8 @@ static void sort_memslots(struct kvm_memslots *slots) slots->id_to_index[slots->memslots[i].id] = i; } -void update_memslots(struct kvm_memslots *slots, struct kvm_memory_slot *new) +void update_memslots(struct kvm_memslots *slots, struct kvm_memory_slot *new, + u64 last_generation) { if (new) { int id = new->id; @@ -684,7 +685,7 @@ void update_memslots(struct kvm_memslots *slots, struct kvm_memory_slot *new) sort_memslots(slots); } - slots->generation++; + slots->generation = last_generation + 1; } static int check_memory_region_flags(struct kvm_userspace_memory_region *mem) @@ -819,7 +820,7 @@ int __kvm_set_memory_region(struct kvm *kvm, slot = id_to_memslot(slots, mem->slot); slot->flags |= KVM_MEMSLOT_INVALID; - update_memslots(slots, NULL); + update_memslots(slots, NULL, kvm->memslots->generation); old_memslots = kvm->memslots; rcu_assign_pointer(kvm->memslots, slots); @@ -867,7 +868,7 @@ int __kvm_set_memory_region(struct kvm *kvm, memset(&new.arch, 0, sizeof(new.arch)); } - update_memslots(slots, &new); + update_memslots(slots, &new, kvm->memslots->generation); old_memslots = kvm->memslots; rcu_assign_pointer(kvm->memslots, slots); synchronize_srcu_expedited(&kvm->srcu); From 07f42f5f25dc214a33214159fc8b62b984b713eb Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Wed, 12 Dec 2012 19:10:49 +0200 Subject: [PATCH 0391/4607] KVM: VMX: cleanup rmode_segment_valid() Set segment fields explicitly instead of using binary operations. No behaviour changes. Reviewed-by: Marcelo Tosatti Signed-off-by: Gleb Natapov --- arch/x86/kvm/vmx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index b3101e368079..265fdd37b240 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3380,13 +3380,16 @@ static bool rmode_segment_valid(struct kvm_vcpu *vcpu, int seg) u32 ar; vmx_get_segment(vcpu, &var, seg); + var.dpl = 0x3; + var.g = 0; + var.db = 0; ar = vmx_segment_access_rights(&var); if (var.base != (var.selector << 4)) return false; if (var.limit < 0xffff) return false; - if (((ar | (3 << AR_DPL_SHIFT)) & ~(AR_G_MASK | AR_DB_MASK)) != 0xf3) + if (ar != 0xf3) return false; return true; From 0647f4aa8c58a7e5adb873b485c83e0c93d9c6d1 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Wed, 12 Dec 2012 19:10:50 +0200 Subject: [PATCH 0392/4607] KVM: VMX: relax check for CS register in rmode_segment_valid() rmode_segment_valid() checks if segment descriptor can be used to enter vm86 mode. VMX spec mandates that in vm86 mode CS register will be of type data, not code. Lets allow guest entry with vm86 mode if the only problem with CS register is incorrect type. Otherwise entire real mode will be emulated. Reviewed-by: Marcelo Tosatti Signed-off-by: Gleb Natapov --- arch/x86/kvm/vmx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 265fdd37b240..7c2c05406b60 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3383,6 +3383,8 @@ static bool rmode_segment_valid(struct kvm_vcpu *vcpu, int seg) var.dpl = 0x3; var.g = 0; var.db = 0; + if (seg == VCPU_SREG_CS) + var.type = 0x3; ar = vmx_segment_access_rights(&var); if (var.base != (var.selector << 4)) From c6ad115348035f474d6fb81005bb4764bdd128ef Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Wed, 12 Dec 2012 19:10:51 +0200 Subject: [PATCH 0393/4607] KVM: VMX: return correct segment limit and flags for CS/SS registers in real mode VMX without unrestricted mode cannot virtualize real mode, so if emulate_invalid_guest_state=0 kvm uses vm86 mode to approximate it. Sometimes, when guest moves from protected mode to real mode, it leaves segment descriptors in a state not suitable for use by vm86 mode virtualization, so we keep shadow copy of segment descriptors for internal use and load fake register to VMCS for guest entry to succeed. Till now we kept shadow for all segments except SS and CS (for SS and CS we returned parameters directly from VMCS), but since commit a5625189f6810 emulator enforces segment limits in real mode. This causes #GP during move from protected mode to real mode when emulator fetches first instruction after moving to real mode since it uses incorrect CS base and limit to linearize the %rip. Fix by keeping shadow for SS and CS too. Reviewed-by: Marcelo Tosatti Signed-off-by: Gleb Natapov --- arch/x86/kvm/vmx.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 7c2c05406b60..792d9cce8846 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2856,6 +2856,8 @@ static void enter_rmode(struct kvm_vcpu *vcpu) vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_DS], VCPU_SREG_DS); vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_FS], VCPU_SREG_FS); vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_GS], VCPU_SREG_GS); + vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_SS], VCPU_SREG_SS); + vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_CS], VCPU_SREG_CS); vmx->emulation_required = 1; vmx->rmode.vm86_active = 1; @@ -3171,10 +3173,7 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx = to_vmx(vcpu); u32 ar; - if (vmx->rmode.vm86_active - && (seg == VCPU_SREG_TR || seg == VCPU_SREG_ES - || seg == VCPU_SREG_DS || seg == VCPU_SREG_FS - || seg == VCPU_SREG_GS)) { + if (vmx->rmode.vm86_active && seg != VCPU_SREG_LDTR) { *var = vmx->rmode.segs[seg]; if (seg == VCPU_SREG_TR || var->selector == vmx_read_guest_seg_selector(vmx, seg)) From beb853ffeccbfa626f30038e816d61187103c455 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Wed, 12 Dec 2012 19:10:52 +0200 Subject: [PATCH 0394/4607] KVM: VMX: use fix_rmode_seg() to fix all code/data segments The code for SS and CS does the same thing fix_rmode_seg() is doing. Use it instead of hand crafted code. Reviewed-by: Marcelo Tosatti Signed-off-by: Gleb Natapov --- arch/x86/kvm/vmx.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 792d9cce8846..9e784c2c9782 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3315,30 +3315,8 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, * unrestricted guest like Westmere to older host that don't have * unrestricted guest like Nehelem. */ - if (vmx->rmode.vm86_active) { - switch (seg) { - case VCPU_SREG_CS: - vmcs_write32(GUEST_CS_AR_BYTES, 0xf3); - vmcs_write32(GUEST_CS_LIMIT, 0xffff); - if (vmcs_readl(GUEST_CS_BASE) == 0xffff0000) - vmcs_writel(GUEST_CS_BASE, 0xf0000); - vmcs_write16(GUEST_CS_SELECTOR, - vmcs_readl(GUEST_CS_BASE) >> 4); - break; - case VCPU_SREG_ES: - case VCPU_SREG_DS: - case VCPU_SREG_GS: - case VCPU_SREG_FS: - fix_rmode_seg(seg, &vmx->rmode.segs[seg]); - break; - case VCPU_SREG_SS: - vmcs_write16(GUEST_SS_SELECTOR, - vmcs_readl(GUEST_SS_BASE) >> 4); - vmcs_write32(GUEST_SS_LIMIT, 0xffff); - vmcs_write32(GUEST_SS_AR_BYTES, 0xf3); - break; - } - } + if (vmx->rmode.vm86_active && var->s) + fix_rmode_seg(seg, &vmx->rmode.segs[seg]); } static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l) From 39dcfb95def4646ecdb76ea7a7491e8420dce7a7 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Wed, 12 Dec 2012 19:10:53 +0200 Subject: [PATCH 0395/4607] KVM: VMX: remove redundant code from vmx_set_segment() Segment descriptor's base is fixed by call to fix_rmode_seg(). Not need to do it twice. Reviewed-by: Marcelo Tosatti Signed-off-by: Gleb Natapov --- arch/x86/kvm/vmx.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 9e784c2c9782..9b8edd2298e5 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3268,7 +3268,7 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, { struct vcpu_vmx *vmx = to_vmx(vcpu); const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; - u32 ar; + u32 ar = 0; vmx_segment_cache_clear(vmx); @@ -3280,15 +3280,9 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, vmcs_writel(sf->base, var->base); vmcs_write32(sf->limit, var->limit); vmcs_write16(sf->selector, var->selector); - if (vmx->rmode.vm86_active && var->s) { + if (vmx->rmode.vm86_active && var->s) vmx->rmode.segs[seg] = *var; - /* - * Hack real-mode segments into vm86 compatibility. - */ - if (var->base == 0xffff0000 && var->selector == 0xf000) - vmcs_writel(sf->base, 0xf0000); - ar = 0xf3; - } else + else ar = vmx_segment_access_rights(var); /* From 1ecd50a9474c4bfa3129d90cfcf1c1eb4fbf19e7 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Wed, 12 Dec 2012 19:10:54 +0200 Subject: [PATCH 0396/4607] KVM: VMX: clean-up vmx_set_segment() Move all vm86_active logic into one place. Reviewed-by: Marcelo Tosatti Signed-off-by: Gleb Natapov --- arch/x86/kvm/vmx.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 9b8edd2298e5..4bd1c22db390 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3271,19 +3271,21 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, u32 ar = 0; vmx_segment_cache_clear(vmx); + __clear_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail); - if (vmx->rmode.vm86_active && seg == VCPU_SREG_TR) { - vmcs_write16(sf->selector, var->selector); - vmx->rmode.segs[VCPU_SREG_TR] = *var; + if (vmx->rmode.vm86_active && seg != VCPU_SREG_LDTR) { + vmx->rmode.segs[seg] = *var; + if (seg == VCPU_SREG_TR) + vmcs_write16(sf->selector, var->selector); + else if (var->s) + fix_rmode_seg(seg, &vmx->rmode.segs[seg]); return; } + vmcs_writel(sf->base, var->base); vmcs_write32(sf->limit, var->limit); vmcs_write16(sf->selector, var->selector); - if (vmx->rmode.vm86_active && var->s) - vmx->rmode.segs[seg] = *var; - else - ar = vmx_segment_access_rights(var); + ar = vmx_segment_access_rights(var); /* * Fix the "Accessed" bit in AR field of segment registers for older @@ -3300,17 +3302,6 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, ar |= 0x1; /* Accessed */ vmcs_write32(sf->ar_bytes, ar); - __clear_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail); - - /* - * Fix segments for real mode guest in hosts that don't have - * "unrestricted_mode" or it was disabled. - * This is done to allow migration of the guests from hosts with - * unrestricted guest like Westmere to older host that don't have - * unrestricted guest like Nehelem. - */ - if (vmx->rmode.vm86_active && var->s) - fix_rmode_seg(seg, &vmx->rmode.segs[seg]); } static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l) From f924d66d278d5da890f3098805b0450a4ef66c32 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Wed, 12 Dec 2012 19:10:55 +0200 Subject: [PATCH 0397/4607] KVM: VMX: remove unneeded temporary variable from vmx_set_segment() Reviewed-by: Marcelo Tosatti Signed-off-by: Gleb Natapov --- arch/x86/kvm/vmx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 4bd1c22db390..23d5aec78073 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3268,7 +3268,6 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, { struct vcpu_vmx *vmx = to_vmx(vcpu); const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; - u32 ar = 0; vmx_segment_cache_clear(vmx); __clear_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail); @@ -3285,7 +3284,6 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, vmcs_writel(sf->base, var->base); vmcs_write32(sf->limit, var->limit); vmcs_write16(sf->selector, var->selector); - ar = vmx_segment_access_rights(var); /* * Fix the "Accessed" bit in AR field of segment registers for older @@ -3299,9 +3297,9 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, * kvm hack. */ if (enable_unrestricted_guest && (seg != VCPU_SREG_LDTR)) - ar |= 0x1; /* Accessed */ + var->type |= 0x1; /* Accessed */ - vmcs_write32(sf->ar_bytes, ar); + vmcs_write32(sf->ar_bytes, vmx_segment_access_rights(var)); } static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l) From b5cff595bd005dba8051e9125f0ed28b5ff05c89 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 23 Dec 2012 13:14:20 -0200 Subject: [PATCH 0398/4607] [media] em28xx: prefer_bulk parameter is read-only As the bulk mode is set at device's probe, it is not possible to change it later. So, change the parameter to be read only after modprobing. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index bc63b1cee808..2129560a7823 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -62,7 +62,7 @@ module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); static unsigned int prefer_bulk; -module_param(prefer_bulk, int, 0644); +module_param(prefer_bulk, int, 0444); MODULE_PARM_DESC(prefer_bulk, "prefer USB bulk transfers"); From aa51496b21542855e779a78bf33384002f01acb6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 23 Dec 2012 13:15:47 -0200 Subject: [PATCH 0399/4607] [media] em28xx: display the isoc/bulk mode As both bulk and isoc modes can be available, display what it was found for both DVB and analog. While here, also displays if audio is provided via USB Audio Class or via vendor's extension. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 31 ++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 2129560a7823..9a2cb6136728 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3310,19 +3310,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, ifnum, interface->altsetting->desc.bInterfaceNumber); - if (has_audio) - printk(KERN_INFO DRIVER_NAME - ": Audio Vendor Class interface %i found\n", - ifnum); - if (has_video) - printk(KERN_INFO DRIVER_NAME - ": Video interface %i found\n", - ifnum); - if (has_dvb) - printk(KERN_INFO DRIVER_NAME - ": DVB interface %i found\n", - ifnum); - /* * Make sure we have 480 Mbps of bandwidth, otherwise things like * video stream wouldn't likely work, since 12 Mbps is generally @@ -3361,6 +3348,24 @@ static int em28xx_usb_probe(struct usb_interface *interface, } } + if (has_audio) + printk(KERN_INFO DRIVER_NAME + ": Audio interface %i found %s\n", + ifnum, + dev->has_audio_class ? "(USB Audio Class)" : "(Vendor Class)"); + if (has_video) + printk(KERN_INFO DRIVER_NAME + ": Video interface %i found:%s%s\n", + ifnum, + dev->analog_ep_bulk ? " bulk" : "", + dev->analog_ep_isoc ? " isoc" : ""); + if (has_dvb) + printk(KERN_INFO DRIVER_NAME + ": DVB interface %i found:%s%s\n", + ifnum, + dev->dvb_ep_bulk ? " bulk" : "", + dev->dvb_ep_isoc ? " isoc" : ""); + dev->num_alt = interface->num_altsetting; if ((unsigned)card[nr] < em28xx_bcount) From a3efa1cc0e067675ffa2d2c357cbe1da0db4653b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 23 Dec 2012 16:32:03 -0200 Subject: [PATCH 0400/4607] [media] em28xx: make the logs reflect the specific chip name In order to make easier to analize the logs when multiple devices are plugged, change the device name accordingly with the chip version. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 46 ++++++++++++++++++------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 9a2cb6136728..1f90a8a8181b 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2957,6 +2957,8 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, int minor) { int retval; + static const char *default_chip_name = "em28xx"; + const char *chip_name = default_chip_name; dev->udev = udev; mutex_init(&dev->ctrl_urb_lock); @@ -2984,51 +2986,62 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, switch (dev->chip_id) { case CHIP_ID_EM2800: - em28xx_info("chip ID is em2800\n"); + chip_name = "em2800"; break; case CHIP_ID_EM2710: - em28xx_info("chip ID is em2710\n"); + chip_name = "em2710"; break; case CHIP_ID_EM2750: - em28xx_info("chip ID is em2750\n"); + chip_name = "em2750"; break; case CHIP_ID_EM2820: - em28xx_info("chip ID is em2820 (or em2710)\n"); + chip_name = "em2710/2820"; break; case CHIP_ID_EM2840: - em28xx_info("chip ID is em2840\n"); + chip_name = "em2840"; break; case CHIP_ID_EM2860: - em28xx_info("chip ID is em2860\n"); + chip_name = "em2860"; break; case CHIP_ID_EM2870: - em28xx_info("chip ID is em2870\n"); + chip_name = "em2870"; dev->wait_after_write = 0; break; case CHIP_ID_EM2874: - em28xx_info("chip ID is em2874\n"); + chip_name = "em2874"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; case CHIP_ID_EM28174: - em28xx_info("chip ID is em28174\n"); + chip_name = "em28174"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; case CHIP_ID_EM2883: - em28xx_info("chip ID is em2882/em2883\n"); + chip_name = "em2882/3"; dev->wait_after_write = 0; break; case CHIP_ID_EM2884: - em28xx_info("chip ID is em2884\n"); + chip_name = "em2884"; dev->reg_gpio_num = EM2874_R80_GPIO; dev->wait_after_write = 0; break; default: - em28xx_info("em28xx chip ID = %d\n", dev->chip_id); + printk(KERN_INFO DRIVER_NAME + ": unknown em28xx chip ID (%d)\n", dev->chip_id); } } + if (chip_name != default_chip_name) + printk(KERN_INFO DRIVER_NAME + ": chip ID is %s\n", chip_name); + + /* + * For em2820/em2710, the name may change latter, after checking + * if the device has a sensor (so, it is em2710) or not. + */ + snprintf(dev->name, sizeof(dev->name), "%s #%d", chip_name, dev->devno); + if (dev->is_audio_only) { retval = em28xx_audio_setup(dev); if (retval) @@ -3045,6 +3058,14 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, em28xx_pre_card_setup(dev); + if (dev->chip_id == CHIP_ID_EM2820) { + if (dev->board.is_webcam) + chip_name = "em2710"; + else + chip_name = "em2820"; + snprintf(dev->name, sizeof(dev->name), "%s #%d", chip_name, dev->devno); + } + if (!dev->board.is_em2800) { /* Resets I2C speed */ retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed); @@ -3331,7 +3352,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, (!dev->dvb_ep_isoc || (prefer_bulk && dev->dvb_ep_bulk))) dev->dvb_xfer_bulk = 1; - snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr); dev->devno = nr; dev->model = id->driver_info; dev->alt = -1; From 8b2aea7878f64814544d0527c659011949d52358 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 23 Dec 2012 13:25:38 -0200 Subject: [PATCH 0401/4607] [media] em28xx: prefer bulk mode on webcams Using bulk mode allows more than one webcam, as the maximum fps is low at 640x480 resolution. So, prefer it, if the device is a webcam. Tested with Silvercrest 1.3 Mpixel webcam (em2710) on both bulk and isoc modes. Tested analog with HVR-950 model 65201/A1C0 (em2883), where only ISOC endpoints are available for both DVB and Analog. Tested on Hauppauge WinTV USB 2 (em2840) on both bulk and isoc modes. It should be noticed that enabling bulk mode by default with TV boards is a bad idea; what happens is that, while with ISOC the USB logic will prevent the concurrent usage of two devices that spends more than 100% of the USB2 traffic, it doesn't care with bulk transfers. On my tests, I started two streams, one with a WinTV at 640x480x30fps and the other one with a Silvercrest webcam at 640x480, on a lower fps) both on bulk mode. One of the streams always silently failed. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 36 +++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 1f90a8a8181b..ad6c800d9a40 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -61,9 +61,9 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); -static unsigned int prefer_bulk; +static int prefer_bulk = -1; module_param(prefer_bulk, int, 0444); -MODULE_PARM_DESC(prefer_bulk, "prefer USB bulk transfers"); +MODULE_PARM_DESC(prefer_bulk, "prefer USB bulk transfers (-1 = auto, 0 = isoc, 1 = bulk)"); /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ @@ -3170,7 +3170,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, struct em28xx *dev = NULL; int retval; bool has_audio = false, has_video = false, has_dvb = false; - int i, nr; + int i, nr, try_bulk; const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; char *speed; @@ -3344,14 +3344,6 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto err_free; } - /* Select USB transfer types to use */ - if (has_video && - (!dev->analog_ep_isoc || (prefer_bulk && dev->analog_ep_bulk))) - dev->analog_xfer_bulk = 1; - if (has_dvb && - (!dev->dvb_ep_isoc || (prefer_bulk && dev->dvb_ep_bulk))) - dev->dvb_xfer_bulk = 1; - dev->devno = nr; dev->model = id->driver_info; dev->alt = -1; @@ -3402,7 +3394,29 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto unlock_and_free; } + if (prefer_bulk < 0) { + if (dev->board.is_webcam) + try_bulk = 1; + else + try_bulk = 0; + } else { + try_bulk = prefer_bulk > 0; + } + + /* Select USB transfer types to use */ + if (has_video) { + if (!dev->analog_ep_isoc || (try_bulk && dev->analog_ep_bulk)) + dev->analog_xfer_bulk = 1; + em28xx_info("analog set to %s mode.\n", + dev->analog_xfer_bulk ? "bulk" : "isoc"); + } if (has_dvb) { + if (!dev->dvb_ep_isoc || (try_bulk && dev->dvb_ep_bulk)) + dev->dvb_xfer_bulk = 1; + + em28xx_info("dvb set to %s mode.\n", + dev->dvb_xfer_bulk ? "bulk" : "isoc"); + /* pre-allocate DVB usb transfer buffers */ if (dev->dvb_xfer_bulk) { retval = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, From 36cb26a4a67cdc186a2a5ec5e49063ea635969ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Jes=C3=BAs=20Delaiti?= Date: Thu, 8 Nov 2012 16:14:50 -0300 Subject: [PATCH 0402/4607] [media] rc/keymaps: add RC keytable for MyGica X8507 Add RC-5 remote keytable definition for MyGica X8507. [mchehab@redhat.com: fixed whitespacing - it seems that Alfredo's emailer mangled it] Signed-off-by: Alfredo J. Delaiti Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 1 + .../rc/keymaps/rc-total-media-in-hand-02.c | 86 +++++++++++++++++++ include/media/rc-map.h | 1 + 3 files changed, 88 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-total-media-in-hand-02.c diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index ab84d66c67c1..778661971aed 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-tevii-nec.o \ rc-tivo.o \ rc-total-media-in-hand.o \ + rc-total-media-in-hand-02.o \ rc-trekstor.o \ rc-tt-1500.o \ rc-twinhan1027.o \ diff --git a/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c new file mode 100644 index 000000000000..47270f72ebf0 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-total-media-in-hand-02.c @@ -0,0 +1,86 @@ +/* + * Total Media In Hand_02 remote controller keytable for Mygica X8507 + * + * Copyright (C) 2012 Alfredo J. Delaiti + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include + + +static struct rc_map_table total_media_in_hand_02[] = { + { 0x0000, KEY_0 }, + { 0x0001, KEY_1 }, + { 0x0002, KEY_2 }, + { 0x0003, KEY_3 }, + { 0x0004, KEY_4 }, + { 0x0005, KEY_5 }, + { 0x0006, KEY_6 }, + { 0x0007, KEY_7 }, + { 0x0008, KEY_8 }, + { 0x0009, KEY_9 }, + { 0x000a, KEY_MUTE }, + { 0x000b, KEY_STOP }, /* Stop */ + { 0x000c, KEY_POWER2 }, /* Turn on/off application */ + { 0x000d, KEY_OK }, /* OK */ + { 0x000e, KEY_CAMERA }, /* Snapshot */ + { 0x000f, KEY_ZOOM }, /* Full Screen/Restore */ + { 0x0010, KEY_RIGHT }, /* Right arrow */ + { 0x0011, KEY_LEFT }, /* Left arrow */ + { 0x0012, KEY_CHANNELUP }, + { 0x0013, KEY_CHANNELDOWN }, + { 0x0014, KEY_SHUFFLE }, + { 0x0016, KEY_PAUSE }, + { 0x0017, KEY_PLAY }, /* Play */ + { 0x001e, KEY_TIME }, /* Time Shift */ + { 0x001f, KEY_RECORD }, + { 0x0020, KEY_UP }, + { 0x0021, KEY_DOWN }, + { 0x0025, KEY_POWER }, /* Turn off computer */ + { 0x0026, KEY_REWIND }, /* FR << */ + { 0x0027, KEY_FASTFORWARD }, /* FF >> */ + { 0x0029, KEY_ESC }, + { 0x002b, KEY_VOLUMEUP }, + { 0x002c, KEY_VOLUMEDOWN }, + { 0x002d, KEY_CHANNEL }, /* CH Surfing */ + { 0x0038, KEY_VIDEO }, /* TV/AV/S-Video/YPbPr */ +}; + +static struct rc_map_list total_media_in_hand_02_map = { + .map = { + .scan = total_media_in_hand_02, + .size = ARRAY_SIZE(total_media_in_hand_02), + .rc_type = RC_TYPE_RC5, + .name = RC_MAP_TOTAL_MEDIA_IN_HAND_02, + } +}; + +static int __init init_rc_map_total_media_in_hand_02(void) +{ + return rc_map_register(&total_media_in_hand_02_map); +} + +static void __exit exit_rc_map_total_media_in_hand_02(void) +{ + rc_map_unregister(&total_media_in_hand_02_map); +} + +module_init(init_rc_map_total_media_in_hand_02) +module_exit(exit_rc_map_total_media_in_hand_02) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR(" Alfredo J. Delaiti "); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 74f55a3f14eb..f74ee6f89711 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -182,6 +182,7 @@ void rc_map_init(void); #define RC_MAP_TEVII_NEC "rc-tevii-nec" #define RC_MAP_TIVO "rc-tivo" #define RC_MAP_TOTAL_MEDIA_IN_HAND "rc-total-media-in-hand" +#define RC_MAP_TOTAL_MEDIA_IN_HAND_02 "rc-total-media-in-hand-02" #define RC_MAP_TREKSTOR "rc-trekstor" #define RC_MAP_TT_1500 "rc-tt-1500" #define RC_MAP_TWINHAN_VP1027_DVBS "rc-twinhan1027" From e5f670b7f9685a850e94ba263948c676b1b06eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20Jes=C3=BAs=20Delaiti?= Date: Thu, 8 Nov 2012 15:50:25 -0300 Subject: [PATCH 0403/4607] [media] cx23885: add RC support for MyGica X8507 This series add remote control support for MyGica X8507. I test for 2 month under OpenSuse(X64) 11.4 and 12.2 with kernel 3.4, 3.5, 3.6 also 3.7-rc2 and rc3. [mchehab@redhat.com: fixed whitespacing - it seems that Alfredo's emailer mangled it] Signed-off-by: Alfredo J. Delaiti Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-cards.c | 3 +++ drivers/media/pci/cx23885/cx23885-input.c | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 7a79a17105c4..8d96ae4ebbdd 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -1408,6 +1408,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) break; case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_MYGICA_X8507: if (!enable_885_ir) break; dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE); @@ -1450,6 +1451,7 @@ void cx23885_ir_fini(struct cx23885_dev *dev) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: cx23885_irq_remove(dev, PCI_MSK_AV_CORE); /* sd_ir is a duplicate pointer to the AV Core, just clear it */ dev->sd_ir = NULL; @@ -1494,6 +1496,7 @@ void cx23885_ir_pci_int_enable(struct cx23885_dev *dev) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: if (dev->sd_ir) cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE); break; diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index 4f1055a194b5..7875dfbe09ff 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -89,6 +89,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: /* * The only boards we handle right now. However other boards * using the CX2388x integrated IR controller should be similar @@ -140,6 +141,7 @@ static int cx23885_input_ir_start(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: case CX23885_BOARD_HAUPPAUGE_HVR1250: + case CX23885_BOARD_MYGICA_X8507: /* * The IR controller on this board only returns pulse widths. * Any other mode setting will fail to set up the device. @@ -289,6 +291,13 @@ int cx23885_input_init(struct cx23885_dev *dev) /* A guess at the remote */ rc_map = RC_MAP_TEVII_NEC; break; + case CX23885_BOARD_MYGICA_X8507: + /* Integrated CX23885 IR controller */ + driver_type = RC_DRIVER_IR_RAW; + allowed_protos = RC_BIT_ALL; + /* A guess at the remote */ + rc_map = RC_MAP_TOTAL_MEDIA_IN_HAND_02; + break; default: return -ENODEV; } From 341068fcf8cc10145d38abe927fa9b9f0c461c66 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Thu, 8 Nov 2012 17:30:21 -0300 Subject: [PATCH 0404/4607] [media] it913x: fix correct endpoint size when pid filter on I kept the count as the hardware default with dvb-usb-v2, with 5, users can still run in to trouble with Video PIDs. I have traced it to an incorrect endpoint size when the PID filter is enabled. It also affected USB 2.0 with the filter on. Reported-by: Antti Palosaari Tested-by: Antti Palosaari Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/it913x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index 47204280b8b3..fbc0a84465eb 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -643,7 +643,8 @@ static int it913x_frontend_attach(struct dvb_usb_adapter *adap) struct it913x_state *st = d->priv; int ret = 0; u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5); - u16 ep_size = adap->stream.buf_size / 4; + u16 ep_size = (adap->pid_filtering) ? TS_BUFFER_SIZE_PID / 4 : + TS_BUFFER_SIZE_MAX / 4; u8 pkt_size = 0x80; if (d->udev->speed != USB_SPEED_HIGH) From 7ec4fb44962f611241bcb974fa97c5f49ddcb2f1 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 24 Dec 2012 17:49:30 +0200 Subject: [PATCH 0405/4607] KVM: move the code that installs new slots array to a separate function. Move repetitive code sequence to a separate function. Reviewed-by: Alex Williamson Signed-off-by: Gleb Natapov --- virt/kvm/kvm_main.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 14cbae83be64..e45c20ca422a 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -702,6 +702,17 @@ static int check_memory_region_flags(struct kvm_userspace_memory_region *mem) return 0; } +static struct kvm_memslots *install_new_memslots(struct kvm *kvm, + struct kvm_memslots *slots, struct kvm_memory_slot *new) +{ + struct kvm_memslots *old_memslots = kvm->memslots; + + update_memslots(slots, new, kvm->memslots->generation); + rcu_assign_pointer(kvm->memslots, slots); + synchronize_srcu_expedited(&kvm->srcu); + return old_memslots; +} + /* * Allocate some memory and give it an address in the guest physical address * space. @@ -820,11 +831,8 @@ int __kvm_set_memory_region(struct kvm *kvm, slot = id_to_memslot(slots, mem->slot); slot->flags |= KVM_MEMSLOT_INVALID; - update_memslots(slots, NULL, kvm->memslots->generation); + old_memslots = install_new_memslots(kvm, slots, NULL); - old_memslots = kvm->memslots; - rcu_assign_pointer(kvm->memslots, slots); - synchronize_srcu_expedited(&kvm->srcu); /* slot was deleted or moved, clear iommu mapping */ kvm_iommu_unmap_pages(kvm, &old); /* From this point no new shadow pages pointing to a deleted, @@ -868,10 +876,7 @@ int __kvm_set_memory_region(struct kvm *kvm, memset(&new.arch, 0, sizeof(new.arch)); } - update_memslots(slots, &new, kvm->memslots->generation); - old_memslots = kvm->memslots; - rcu_assign_pointer(kvm->memslots, slots); - synchronize_srcu_expedited(&kvm->srcu); + old_memslots = install_new_memslots(kvm, slots, &new); kvm_arch_commit_memory_region(kvm, mem, old, user_alloc); From 169743264f8100a39042a4d6bec44dabb2e2e624 Mon Sep 17 00:00:00 2001 From: "Yann E. MORIN" Date: Wed, 19 Dec 2012 19:17:00 +0100 Subject: [PATCH 0406/4607] kconfig: document use of CONFIG_ environment variable Signed-off-by: "Yann E. MORIN" --- Documentation/kbuild/kconfig.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/kbuild/kconfig.txt b/Documentation/kbuild/kconfig.txt index a09f1a6a830c..b8b77bbc784f 100644 --- a/Documentation/kbuild/kconfig.txt +++ b/Documentation/kbuild/kconfig.txt @@ -46,6 +46,12 @@ KCONFIG_OVERWRITECONFIG If you set KCONFIG_OVERWRITECONFIG in the environment, Kconfig will not break symlinks when .config is a symlink to somewhere else. +CONFIG_ +-------------------------------------------------- +If you set CONFIG_ in the environment, Kconfig will prefix all symbols +with its value when saving the configuration, instead of using the default, +"CONFIG_". + ______________________________________________________________________ Environment variables for '{allyes/allmod/allno/rand}config' From 87727d453b4677a21312712c8a4bb5dcd81a1ebe Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Mon, 17 Dec 2012 22:38:02 +0800 Subject: [PATCH 0407/4607] menuconfig:inputbox: support navigate input position This patch add support navigate input position *inside* the input field with LEFT/RIGHT, so it is possible to modify the text in place. Signed-off-by: Wang YanQing Tested-by: "Yann E. MORIN" Signed-off-by: "Yann E. MORIN" --- scripts/kconfig/lxdialog/inputbox.c | 123 +++++++++++++++++++++------- 1 file changed, 93 insertions(+), 30 deletions(-) diff --git a/scripts/kconfig/lxdialog/inputbox.c b/scripts/kconfig/lxdialog/inputbox.c index dd8e587c50e2..21404a04d7c3 100644 --- a/scripts/kconfig/lxdialog/inputbox.c +++ b/scripts/kconfig/lxdialog/inputbox.c @@ -45,7 +45,8 @@ int dialog_inputbox(const char *title, const char *prompt, int height, int width const char *init) { int i, x, y, box_y, box_x, box_width; - int input_x = 0, scroll = 0, key = 0, button = -1; + int input_x = 0, key = 0, button = -1; + int show_x, len, pos; char *instr = dialog_input_result; WINDOW *dialog; @@ -97,14 +98,17 @@ do_resize: wmove(dialog, box_y, box_x); wattrset(dialog, dlg.inputbox.atr); - input_x = strlen(instr); + len = strlen(instr); + pos = len; - if (input_x >= box_width) { - scroll = input_x - box_width + 1; + if (len >= box_width) { + show_x = len - box_width + 1; input_x = box_width - 1; for (i = 0; i < box_width - 1; i++) - waddch(dialog, instr[scroll + i]); + waddch(dialog, instr[show_x + i]); } else { + show_x = 0; + input_x = len; waddstr(dialog, instr); } @@ -121,45 +125,104 @@ do_resize: case KEY_UP: case KEY_DOWN: break; - case KEY_LEFT: - continue; - case KEY_RIGHT: - continue; case KEY_BACKSPACE: case 127: - if (input_x || scroll) { + if (pos) { wattrset(dialog, dlg.inputbox.atr); - if (!input_x) { - scroll = scroll < box_width - 1 ? 0 : scroll - (box_width - 1); - wmove(dialog, box_y, box_x); - for (i = 0; i < box_width; i++) - waddch(dialog, - instr[scroll + input_x + i] ? - instr[scroll + input_x + i] : ' '); - input_x = strlen(instr) - scroll; + if (input_x == 0) { + show_x--; } else input_x--; - instr[scroll + input_x] = '\0'; - mvwaddch(dialog, box_y, input_x + box_x, ' '); + + if (pos < len) { + for (i = pos - 1; i < len; i++) { + instr[i] = instr[i+1]; + } + } + + pos--; + len--; + instr[len] = '\0'; + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } wmove(dialog, box_y, input_x + box_x); wrefresh(dialog); } continue; + case KEY_LEFT: + if (pos > 0) { + if (input_x > 0) { + wmove(dialog, box_y, --input_x + box_x); + } else if (input_x == 0) { + show_x--; + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } + wmove(dialog, box_y, box_x); + } + pos--; + } + continue; + case KEY_RIGHT: + if (pos < len) { + if (input_x < box_width - 1) { + wmove(dialog, box_y, ++input_x + box_x); + } else if (input_x == box_width - 1) { + show_x++; + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } + wmove(dialog, box_y, input_x + box_x); + } + pos++; + } + continue; default: if (key < 0x100 && isprint(key)) { - if (scroll + input_x < MAX_LEN) { + if (len < MAX_LEN) { wattrset(dialog, dlg.inputbox.atr); - instr[scroll + input_x] = key; - instr[scroll + input_x + 1] = '\0'; - if (input_x == box_width - 1) { - scroll++; - wmove(dialog, box_y, box_x); - for (i = 0; i < box_width - 1; i++) - waddch(dialog, instr [scroll + i]); + if (pos < len) { + for (i = len; i > pos; i--) + instr[i] = instr[i-1]; + instr[pos] = key; } else { - wmove(dialog, box_y, input_x++ + box_x); - waddch(dialog, key); + instr[len] = key; } + pos++; + len++; + instr[len] = '\0'; + + if (input_x == box_width - 1) { + show_x++; + } else { + input_x++; + } + + wmove(dialog, box_y, box_x); + for (i = 0; i < box_width; i++) { + if (!instr[show_x + i]) { + waddch(dialog, ' '); + break; + } + waddch(dialog, instr[show_x + i]); + } + wmove(dialog, box_y, input_x + box_x); wrefresh(dialog); } else flash(); /* Alarm user about overflow */ From 899a179dbf5878fd01c0bd3b0e884ec526916afb Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 16 Nov 2012 00:55:28 -0300 Subject: [PATCH 0408/4607] [media] s5p-tv: Use devm_gpio_request in sii9234_drv.c devm_gpio_request is a device managed function and will make error handling and cleanup a bit simpler. Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/sii9234_drv.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c index 716d4846f8bd..4597342cdfbe 100644 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ b/drivers/media/platform/s5p-tv/sii9234_drv.c @@ -338,7 +338,7 @@ static int __devinit sii9234_probe(struct i2c_client *client, } ctx->gpio_n_reset = pdata->gpio_n_reset; - ret = gpio_request(ctx->gpio_n_reset, "MHL_RST"); + ret = devm_gpio_request(dev, ctx->gpio_n_reset, "MHL_RST"); if (ret) { dev_err(dev, "failed to acquire MHL_RST gpio\n"); return ret; @@ -370,7 +370,6 @@ fail_pm_get: fail_pm: pm_runtime_disable(dev); - gpio_free(ctx->gpio_n_reset); fail: dev_err(dev, "probe failed\n"); @@ -381,11 +380,8 @@ fail: static int __devexit sii9234_remove(struct i2c_client *client) { struct device *dev = &client->dev; - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct sii9234_context *ctx = sd_to_context(sd); pm_runtime_disable(dev); - gpio_free(ctx->gpio_n_reset); dev_info(dev, "remove successful\n"); From cb412a8da82233af001d13c28fc54f25a2001aef Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Fri, 16 Nov 2012 18:17:01 -0300 Subject: [PATCH 0409/4607] [media] staging/media/solo6x10/v4l2-enc.c: fix error-handling The return values of copy_to_user() and copy_from_user() cannot be negative. Found using the following semantich patch: @exists@ identifier ret; statement S; expression E; @@ ( * ret = copy_to_user(...); | * ret = copy_from_user(...); ) ... when != ret = E when != if (ret) { <+... ret = E; ...+> } * if (ret < 0) S Signed-off-by: Cyril Roelandt Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/solo6x10/v4l2-enc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c index f8f0da952288..4977e869d5b7 100644 --- a/drivers/staging/media/solo6x10/v4l2-enc.c +++ b/drivers/staging/media/solo6x10/v4l2-enc.c @@ -1619,6 +1619,8 @@ static int solo_s_ext_ctrls(struct file *file, void *priv, solo_enc->osd_text[OSD_TEXT_MAX] = '\0'; if (!err) err = solo_osd_print(solo_enc); + else + err = -EFAULT; } break; default: @@ -1654,6 +1656,8 @@ static int solo_g_ext_ctrls(struct file *file, void *priv, err = copy_to_user(ctrl->string, solo_enc->osd_text, OSD_TEXT_MAX); + if (err) + err = -EFAULT; } break; default: From fe0e990b22c24d53793c8cf246f0e535f31a4406 Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Tue, 23 Oct 2012 09:56:59 -0300 Subject: [PATCH 0410/4607] [media] vivi: Teach it to tune FPS I was testing my video-over-ethernet subsystem recently, and vivi seemed to be perfect video source for testing when one don't have lots of capture boards and cameras. Only its framerate was hardcoded to NTSC's 30fps, while in my country we usually use PAL (25 fps) and I needed that to precisely simulate bandwidth. That's why here is this patch with ->enum_frameintervals() and ->{g,s}_parm() implemented as suggested by Hans Verkuil which passes v4l2-compliance and manual testing through v4l2-ctl -P / -p . Regarding newly introduced __get_format(u32 pixelformat) I decided not to convert original get_format() to operate on fourcc codes, since >= 3 places in driver need to deal with v4l2_format and otherwise it won't be handy. [mchehab@redhat.com: Some CodingStyle fixes] Signed-off-by: Kirill Smelkov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 104 +++++++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index 7abb046e0fa5..ec6508909f02 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -36,9 +36,17 @@ #define VIVI_MODULE_NAME "vivi" -/* Wake up at about 30 fps */ -#define WAKE_NUMERATOR 30 -#define WAKE_DENOMINATOR 1001 +/* Maximum allowed frame rate + * + * Vivi will allow setting timeperframe in [1/FPS_MAX - FPS_MAX/1] range. + * + * Ideally FPS_MAX should be infinity, i.e. practically UINT_MAX, but that + * might hit application errors when they manipulate these values. + * + * Besides, for tpf < 1ms image-generation logic should be changed, to avoid + * producing frames with equal content. + */ +#define FPS_MAX 1000 #define MAX_WIDTH 1920 #define MAX_HEIGHT 1200 @@ -69,6 +77,12 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); /* Global font descriptor */ static const u8 *font8x16; +/* timeperframe: min/max and default */ +static const struct v4l2_fract + tpf_min = {.numerator = 1, .denominator = FPS_MAX}, + tpf_max = {.numerator = FPS_MAX, .denominator = 1}, + tpf_default = {.numerator = 1001, .denominator = 30000}; /* NTSC */ + #define dprintk(dev, level, fmt, arg...) \ v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg) @@ -150,14 +164,14 @@ static struct vivi_fmt formats[] = { }, }; -static struct vivi_fmt *get_format(struct v4l2_format *f) +static struct vivi_fmt *__get_format(u32 pixelformat) { struct vivi_fmt *fmt; unsigned int k; for (k = 0; k < ARRAY_SIZE(formats); k++) { fmt = &formats[k]; - if (fmt->fourcc == f->fmt.pix.pixelformat) + if (fmt->fourcc == pixelformat) break; } @@ -167,6 +181,11 @@ static struct vivi_fmt *get_format(struct v4l2_format *f) return &formats[k]; } +static struct vivi_fmt *get_format(struct v4l2_format *f) +{ + return __get_format(f->fmt.pix.pixelformat); +} + /* buffer for one video frame */ struct vivi_buffer { /* common v4l buffer stuff -- must be first */ @@ -232,6 +251,7 @@ struct vivi_dev { /* video capture */ struct vivi_fmt *fmt; + struct v4l2_fract timeperframe; unsigned int width, height; struct vb2_queue vb_vidq; unsigned int field_count; @@ -689,8 +709,8 @@ static void vivi_thread_tick(struct vivi_dev *dev) dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index); } -#define frames_to_ms(frames) \ - ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR) +#define frames_to_ms(dev, frames) \ + ((frames * dev->timeperframe.numerator * 1000) / dev->timeperframe.denominator) static void vivi_sleep(struct vivi_dev *dev) { @@ -706,7 +726,7 @@ static void vivi_sleep(struct vivi_dev *dev) goto stop_task; /* Calculate time to wake up */ - timeout = msecs_to_jiffies(frames_to_ms(1)); + timeout = msecs_to_jiffies(frames_to_ms(dev, 1)); vivi_thread_tick(dev); @@ -1078,6 +1098,70 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) return 0; } +/* timeperframe is arbitrary and continous */ +static int vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct vivi_fmt *fmt; + + if (fival->index) + return -EINVAL; + + fmt = __get_format(fival->pixel_format); + if (!fmt) + return -EINVAL; + + /* regarding width & height - we support any */ + + fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + + /* fill in stepwise (step=1.0 is requred by V4L2 spec) */ + fival->stepwise.min = tpf_min; + fival->stepwise.max = tpf_max; + fival->stepwise.step = (struct v4l2_fract) {1, 1}; + + return 0; +} + +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vivi_dev *dev = video_drvdata(file); + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe = dev->timeperframe; + parm->parm.capture.readbuffers = 1; + return 0; +} + +#define FRACT_CMP(a, OP, b) \ + ((u64)(a).numerator * (b).denominator OP (u64)(b).numerator * (a).denominator) + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct vivi_dev *dev = video_drvdata(file); + struct v4l2_fract tpf; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + tpf = parm->parm.capture.timeperframe; + + /* tpf: {*, 0} resets timing; clip to [min, max]*/ + tpf = tpf.denominator ? tpf : tpf_default; + tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; + tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; + + dev->timeperframe = tpf; + parm->parm.capture.timeperframe = tpf; + parm->parm.capture.readbuffers = 1; + return 0; +} + /* --- controls ---------------------------------------------- */ static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -1236,6 +1320,9 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, .vidioc_streamon = vb2_ioctl_streamon, .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = v4l2_ctrl_log_status, @@ -1294,6 +1381,7 @@ static int __init vivi_create_instance(int inst) goto free_dev; dev->fmt = &formats[0]; + dev->timeperframe = tpf_default; dev->width = 640; dev->height = 480; dev->pixelsize = dev->fmt->depth / 8; From fab0e8fa432e42d7b5c91a3d4c8af053f291a65a Mon Sep 17 00:00:00 2001 From: Scott Jiang Date: Tue, 20 Nov 2012 15:49:35 -0300 Subject: [PATCH 0411/4607] [media] v4l2: blackfin: convert ppi driver to a module Other drivers can make use of it. Signed-off-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/blackfin/Kconfig | 6 +++++- drivers/media/platform/blackfin/Makefile | 4 ++-- drivers/media/platform/blackfin/ppi.c | 7 +++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig index ecd5323768b7..519990e17122 100644 --- a/drivers/media/platform/blackfin/Kconfig +++ b/drivers/media/platform/blackfin/Kconfig @@ -2,9 +2,13 @@ config VIDEO_BLACKFIN_CAPTURE tristate "Blackfin Video Capture Driver" depends on VIDEO_V4L2 && BLACKFIN && I2C select VIDEOBUF2_DMA_CONTIG + select VIDEO_BLACKFIN_PPI help V4L2 bridge driver for Blackfin video capture device. Choose PPI or EPPI as its interface. To compile this driver as a module, choose M here: the - module will be called bfin_video_capture. + module will be called bfin_capture. + +config VIDEO_BLACKFIN_PPI + tristate diff --git a/drivers/media/platform/blackfin/Makefile b/drivers/media/platform/blackfin/Makefile index aa3a0a216387..30421bc23080 100644 --- a/drivers/media/platform/blackfin/Makefile +++ b/drivers/media/platform/blackfin/Makefile @@ -1,2 +1,2 @@ -bfin_video_capture-objs := bfin_capture.o ppi.o -obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o +obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_capture.o +obj-$(CONFIG_VIDEO_BLACKFIN_PPI) += ppi.o diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index d29592186b02..9374d676f63d 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -17,6 +17,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include @@ -263,9 +264,15 @@ struct ppi_if *ppi_create_instance(const struct ppi_info *info) pr_info("ppi probe success\n"); return ppi; } +EXPORT_SYMBOL(ppi_create_instance); void ppi_delete_instance(struct ppi_if *ppi) { peripheral_free_list(ppi->info->pin_req); kfree(ppi); } +EXPORT_SYMBOL(ppi_delete_instance); + +MODULE_DESCRIPTION("Analog Devices PPI driver"); +MODULE_AUTHOR("Scott Jiang "); +MODULE_LICENSE("GPL v2"); From 45b82596be0214f161c8176bd3e18f779e36eccd Mon Sep 17 00:00:00 2001 From: Scott Jiang Date: Tue, 20 Nov 2012 15:49:36 -0300 Subject: [PATCH 0412/4607] [media] v4l2: blackfin: add EPPI3 support Bf60x soc has a new PPI called Enhanced PPI version 3. HD video is supported now. To achieve this, we redesign ppi params and add dv timings feature. Signed-off-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/blackfin/bfin_capture.c | 148 ++++++++++++++++-- drivers/media/platform/blackfin/ppi.c | 72 +++++++-- include/media/blackfin/bfin_capture.h | 5 +- include/media/blackfin/ppi.h | 33 +++- 4 files changed, 222 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index d422d3c379e4..aa9f846a667e 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -52,6 +52,7 @@ struct bcap_format { u32 pixelformat; enum v4l2_mbus_pixelcode mbus_code; int bpp; /* bits per pixel */ + int dlen; /* data length for ppi in bits */ }; struct bcap_buffer { @@ -76,10 +77,14 @@ struct bcap_device { unsigned int cur_input; /* current selected standard */ v4l2_std_id std; + /* current selected dv_timings */ + struct v4l2_dv_timings dv_timings; /* used to store pixel format */ struct v4l2_pix_format fmt; /* bits per pixel*/ int bpp; + /* data length for ppi in bits */ + int dlen; /* used to store sensor supported format */ struct bcap_format *sensor_formats; /* number of sensor formats array */ @@ -116,24 +121,35 @@ static const struct bcap_format bcap_formats[] = { .pixelformat = V4L2_PIX_FMT_UYVY, .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, .bpp = 16, + .dlen = 8, }, { .desc = "YCbCr 4:2:2 Interleaved YUYV", .pixelformat = V4L2_PIX_FMT_YUYV, .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, .bpp = 16, + .dlen = 8, + }, + { + .desc = "YCbCr 4:2:2 Interleaved UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, + .bpp = 16, + .dlen = 16, }, { .desc = "RGB 565", .pixelformat = V4L2_PIX_FMT_RGB565, .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, .bpp = 16, + .dlen = 8, }, { .desc = "RGB 444", .pixelformat = V4L2_PIX_FMT_RGB444, .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, .bpp = 16, + .dlen = 8, }, }; @@ -366,9 +382,39 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) params.width = bcap_dev->fmt.width; params.height = bcap_dev->fmt.height; params.bpp = bcap_dev->bpp; + params.dlen = bcap_dev->dlen; params.ppi_control = bcap_dev->cfg->ppi_control; params.int_mask = bcap_dev->cfg->int_mask; - params.blank_clocks = bcap_dev->cfg->blank_clocks; + if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities + & V4L2_IN_CAP_CUSTOM_TIMINGS) { + struct v4l2_bt_timings *bt = &bcap_dev->dv_timings.bt; + + params.hdelay = bt->hsync + bt->hbackporch; + params.vdelay = bt->vsync + bt->vbackporch; + params.line = bt->hfrontporch + bt->hsync + + bt->hbackporch + bt->width; + params.frame = bt->vfrontporch + bt->vsync + + bt->vbackporch + bt->height; + if (bt->interlaced) + params.frame += bt->il_vfrontporch + bt->il_vsync + + bt->il_vbackporch; + } else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities + & V4L2_IN_CAP_STD) { + params.hdelay = 0; + params.vdelay = 0; + if (bcap_dev->std & V4L2_STD_525_60) { + params.line = 858; + params.frame = 525; + } else { + params.line = 864; + params.frame = 625; + } + } else { + params.hdelay = 0; + params.vdelay = 0; + params.line = params.width + bcap_dev->cfg->blank_pixels; + params.frame = params.height; + } ret = ppi->ops->set_params(ppi, ¶ms); if (ret < 0) { v4l2_err(&bcap_dev->v4l2_dev, @@ -600,6 +646,37 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std) return 0; } +static int bcap_g_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + int ret; + + ret = v4l2_subdev_call(bcap_dev->sd, video, + g_dv_timings, timings); + if (ret < 0) + return ret; + + bcap_dev->dv_timings = *timings; + return 0; +} + +static int bcap_s_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + int ret; + if (vb2_is_busy(&bcap_dev->buffer_queue)) + return -EBUSY; + + ret = v4l2_subdev_call(bcap_dev->sd, video, s_dv_timings, timings); + if (ret < 0) + return ret; + + bcap_dev->dv_timings = *timings; + return 0; +} + static int bcap_enum_input(struct file *file, void *priv, struct v4l2_input *input) { @@ -648,13 +725,15 @@ static int bcap_s_input(struct file *file, void *priv, unsigned int index) return ret; } bcap_dev->cur_input = index; + /* if this route has specific config, update ppi control */ + if (route->ppi_control) + config->ppi_control = route->ppi_control; return 0; } static int bcap_try_format(struct bcap_device *bcap, struct v4l2_pix_format *pixfmt, - enum v4l2_mbus_pixelcode *mbus_code, - int *bpp) + struct bcap_format *bcap_fmt) { struct bcap_format *sf = bcap->sensor_formats; struct bcap_format *fmt = NULL; @@ -669,16 +748,20 @@ static int bcap_try_format(struct bcap_device *bcap, if (i == bcap->num_sensor_formats) fmt = &sf[0]; - if (mbus_code) - *mbus_code = fmt->mbus_code; - if (bpp) - *bpp = fmt->bpp; v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code); ret = v4l2_subdev_call(bcap->sd, video, try_mbus_fmt, &mbus_fmt); if (ret < 0) return ret; v4l2_fill_pix_format(pixfmt, &mbus_fmt); + if (bcap_fmt) { + for (i = 0; i < bcap->num_sensor_formats; i++) { + fmt = &sf[i]; + if (mbus_fmt.code == fmt->mbus_code) + break; + } + *bcap_fmt = *fmt; + } pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8; pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; return 0; @@ -707,7 +790,7 @@ static int bcap_try_fmt_vid_cap(struct file *file, void *priv, struct bcap_device *bcap_dev = video_drvdata(file); struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - return bcap_try_format(bcap_dev, pixfmt, NULL, NULL); + return bcap_try_format(bcap_dev, pixfmt, NULL); } static int bcap_g_fmt_vid_cap(struct file *file, void *priv, @@ -724,24 +807,25 @@ static int bcap_s_fmt_vid_cap(struct file *file, void *priv, { struct bcap_device *bcap_dev = video_drvdata(file); struct v4l2_mbus_framefmt mbus_fmt; - enum v4l2_mbus_pixelcode mbus_code; + struct bcap_format bcap_fmt; struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - int ret, bpp; + int ret; if (vb2_is_busy(&bcap_dev->buffer_queue)) return -EBUSY; /* see if format works */ - ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp); + ret = bcap_try_format(bcap_dev, pixfmt, &bcap_fmt); if (ret < 0) return ret; - v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code); + v4l2_fill_mbus_format(&mbus_fmt, pixfmt, bcap_fmt.mbus_code); ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt); if (ret < 0) return ret; bcap_dev->fmt = *pixfmt; - bcap_dev->bpp = bpp; + bcap_dev->bpp = bcap_fmt.bpp; + bcap_dev->dlen = bcap_fmt.dlen; return 0; } @@ -832,6 +916,8 @@ static const struct v4l2_ioctl_ops bcap_ioctl_ops = { .vidioc_querystd = bcap_querystd, .vidioc_s_std = bcap_s_std, .vidioc_g_std = bcap_g_std, + .vidioc_s_dv_timings = bcap_s_dv_timings, + .vidioc_g_dv_timings = bcap_g_dv_timings, .vidioc_reqbufs = bcap_reqbufs, .vidioc_querybuf = bcap_querybuf, .vidioc_qbuf = bcap_qbuf, @@ -867,6 +953,7 @@ static int __devinit bcap_probe(struct platform_device *pdev) struct i2c_adapter *i2c_adap; struct bfin_capture_config *config; struct vb2_queue *q; + struct bcap_route *route; int ret; config = pdev->dev.platform_data; @@ -976,6 +1063,12 @@ static int __devinit bcap_probe(struct platform_device *pdev) NULL); if (bcap_dev->sd) { int i; + if (!config->num_inputs) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to work without input\n"); + goto err_unreg_vdev; + } + /* update tvnorms from the sub devices */ for (i = 0; i < config->num_inputs; i++) vfd->tvnorms |= config->inputs[i].std; @@ -987,8 +1080,24 @@ static int __devinit bcap_probe(struct platform_device *pdev) v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n"); + /* + * explicitly set input, otherwise some boards + * may not work at the state as we expected + */ + route = &config->routes[0]; + ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing, + route->input, route->output, 0); + if ((ret < 0) && (ret != -ENOIOCTLCMD)) { + v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n"); + goto err_unreg_vdev; + } + bcap_dev->cur_input = 0; + /* if this route has specific config, update ppi control */ + if (route->ppi_control) + config->ppi_control = route->ppi_control; + /* now we can probe the default state */ - if (vfd->tvnorms) { + if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) { v4l2_std_id std; ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std); if (ret) { @@ -998,6 +1107,17 @@ static int __devinit bcap_probe(struct platform_device *pdev) } bcap_dev->std = std; } + if (config->inputs[0].capabilities & V4L2_IN_CAP_CUSTOM_TIMINGS) { + struct v4l2_dv_timings dv_timings; + ret = v4l2_subdev_call(bcap_dev->sd, video, + g_dv_timings, &dv_timings); + if (ret) { + v4l2_err(&bcap_dev->v4l2_dev, + "Unable to get dv timings\n"); + goto err_unreg_vdev; + } + bcap_dev->dv_timings = dv_timings; + } ret = bcap_init_sensor_formats(bcap_dev); if (ret) { v4l2_err(&bcap_dev->v4l2_dev, diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index 9374d676f63d..1e24584605f2 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -68,6 +68,13 @@ static irqreturn_t ppi_irq_err(int irq, void *dev_id) bfin_write16(®->status, 0xffff); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + + bfin_write32(®->stat, 0xc0ff); + break; + } default: break; } @@ -129,6 +136,12 @@ static int ppi_start(struct ppi_if *ppi) bfin_write32(®->control, ppi->ppi_control); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + bfin_write32(®->ctl, ppi->ppi_control); + break; + } default: return -EINVAL; } @@ -156,6 +169,12 @@ static int ppi_stop(struct ppi_if *ppi) bfin_write32(®->control, ppi->ppi_control); break; } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + bfin_write32(®->ctl, ppi->ppi_control); + break; + } default: return -EINVAL; } @@ -172,17 +191,23 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) { const struct ppi_info *info = ppi->info; int dma32 = 0; - int dma_config, bytes_per_line, lines_per_frame; + int dma_config, bytes_per_line; + int hcount, hdelay, samples_per_line; bytes_per_line = params->width * params->bpp / 8; - lines_per_frame = params->height; + /* convert parameters unit from pixels to samples */ + hcount = params->width * params->bpp / params->dlen; + hdelay = params->hdelay * params->bpp / params->dlen; + samples_per_line = params->line * params->bpp / params->dlen; if (params->int_mask == 0xFFFFFFFF) ppi->err_int = false; else ppi->err_int = true; - dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN); + dma_config = (DMA_FLOW_STOP | RESTART | DMA2D | DI_EN_Y); ppi->ppi_control = params->ppi_control & ~PORT_EN; + if (!(ppi->ppi_control & PORT_DIR)) + dma_config |= WNR; switch (info->type) { case PPI_TYPE_PPI: { @@ -192,8 +217,8 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) dma32 = 1; bfin_write16(®->control, ppi->ppi_control); - bfin_write16(®->count, bytes_per_line - 1); - bfin_write16(®->frame, lines_per_frame); + bfin_write16(®->count, samples_per_line - 1); + bfin_write16(®->frame, params->frame); break; } case PPI_TYPE_EPPI: @@ -205,12 +230,31 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) dma32 = 1; bfin_write32(®->control, ppi->ppi_control); - bfin_write16(®->line, bytes_per_line + params->blank_clocks); - bfin_write16(®->frame, lines_per_frame); - bfin_write16(®->hdelay, 0); - bfin_write16(®->vdelay, 0); - bfin_write16(®->hcount, bytes_per_line); - bfin_write16(®->vcount, lines_per_frame); + bfin_write16(®->line, samples_per_line); + bfin_write16(®->frame, params->frame); + bfin_write16(®->hdelay, hdelay); + bfin_write16(®->vdelay, params->vdelay); + bfin_write16(®->hcount, hcount); + bfin_write16(®->vcount, params->height); + break; + } + case PPI_TYPE_EPPI3: + { + struct bfin_eppi3_regs *reg = info->base; + + if ((params->ppi_control & PACK_EN) + || (params->ppi_control & 0x70000) > DLEN_16) + dma32 = 1; + + bfin_write32(®->ctl, ppi->ppi_control); + bfin_write32(®->line, samples_per_line); + bfin_write32(®->frame, params->frame); + bfin_write32(®->hdly, hdelay); + bfin_write32(®->vdly, params->vdelay); + bfin_write32(®->hcnt, hcount); + bfin_write32(®->vcnt, params->height); + if (params->int_mask) + bfin_write32(®->imsk, params->int_mask & 0xFF); break; } default: @@ -218,17 +262,17 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) } if (dma32) { - dma_config |= WDSIZE_32; + dma_config |= WDSIZE_32 | PSIZE_32; set_dma_x_count(info->dma_ch, bytes_per_line >> 2); set_dma_x_modify(info->dma_ch, 4); set_dma_y_modify(info->dma_ch, 4); } else { - dma_config |= WDSIZE_16; + dma_config |= WDSIZE_16 | PSIZE_16; set_dma_x_count(info->dma_ch, bytes_per_line >> 1); set_dma_x_modify(info->dma_ch, 2); set_dma_y_modify(info->dma_ch, 2); } - set_dma_y_count(info->dma_ch, lines_per_frame); + set_dma_y_count(info->dma_ch, params->height); set_dma_config(info->dma_ch, dma_config); SSYNC(); diff --git a/include/media/blackfin/bfin_capture.h b/include/media/blackfin/bfin_capture.h index 2038a8a3f8aa..56b9ce4472fc 100644 --- a/include/media/blackfin/bfin_capture.h +++ b/include/media/blackfin/bfin_capture.h @@ -9,6 +9,7 @@ struct ppi_info; struct bcap_route { u32 input; u32 output; + u32 ppi_control; }; struct bfin_capture_config { @@ -30,8 +31,8 @@ struct bfin_capture_config { unsigned long ppi_control; /* ppi interrupt mask */ u32 int_mask; - /* horizontal blanking clocks */ - int blank_clocks; + /* horizontal blanking pixels */ + int blank_pixels; }; #endif diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h index 8f72f8a0b3d0..65c467576b31 100644 --- a/include/media/blackfin/ppi.h +++ b/include/media/blackfin/ppi.h @@ -21,22 +21,42 @@ #define _PPI_H_ #include +#include +#include +/* EPPI */ #ifdef EPPI_EN #define PORT_EN EPPI_EN +#define PORT_DIR EPPI_DIR #define DMA32 0 #define PACK_EN PACKEN #endif +/* EPPI3 */ +#ifdef EPPI0_CTL2 +#define PORT_EN EPPI_CTL_EN +#define PORT_DIR EPPI_CTL_DIR +#define PACK_EN EPPI_CTL_PACKEN +#define DMA32 0 +#define DLEN_8 EPPI_CTL_DLEN08 +#define DLEN_16 EPPI_CTL_DLEN16 +#endif + struct ppi_if; struct ppi_params { - int width; - int height; - int bpp; - unsigned long ppi_control; - u32 int_mask; - int blank_clocks; + u32 width; /* width in pixels */ + u32 height; /* height in lines */ + u32 hdelay; /* delay after the HSYNC in pixels */ + u32 vdelay; /* delay after the VSYNC in lines */ + u32 line; /* total pixels per line */ + u32 frame; /* total lines per frame */ + u32 hsync; /* HSYNC length in pixels */ + u32 vsync; /* VSYNC length in lines */ + int bpp; /* bits per pixel */ + int dlen; /* data length for ppi in bits */ + u32 ppi_control; /* ppi configuration */ + u32 int_mask; /* interrupt mask */ }; struct ppi_ops { @@ -51,6 +71,7 @@ struct ppi_ops { enum ppi_type { PPI_TYPE_PPI, PPI_TYPE_EPPI, + PPI_TYPE_EPPI3, }; struct ppi_info { From 30ebc5e44d057a1619ad63fe32c8c1670c37c4b8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 27 Nov 2012 13:35:09 -0300 Subject: [PATCH 0413/4607] [media] rc: unlock on error in show_protocols() We recently introduced a new return -ENODEV in this function but we need to unlock before returning. [mchehab@redhat.com: found two patches with the same fix. Merged SOB's/acks into one patch] Acked-by: Herton R. Krzesinski Signed-off-by: Dan Carpenter Cc: stable@vger.kernel.org Signed-off-by: Douglas Bagnall Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 601d1ac1c688..d593bc65b4ca 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -789,8 +789,10 @@ static ssize_t show_protocols(struct device *device, } else if (dev->raw) { enabled = dev->raw->enabled_protocols; allowed = ir_raw_get_allowed_protocols(); - } else + } else { + mutex_unlock(&dev->lock); return -ENODEV; + } IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n", (long long)allowed, From 923c7538236564c46ee80c253a416705321f13e3 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Thu, 27 Dec 2012 11:39:12 +0800 Subject: [PATCH 0414/4607] userns: Allow unprivileged reboot In a container with its own pid namespace and user namespace, rebooting the system won't reboot the host, but terminate all the processes in it and thus have the container shutdown, so it's safe. Signed-off-by: Li Zefan Signed-off-by: Eric W. Biederman --- kernel/sys.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/sys.c b/kernel/sys.c index 265b37690421..24d1ef56cd95 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -433,11 +433,12 @@ static DEFINE_MUTEX(reboot_mutex); SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) { + struct pid_namespace *pid_ns = task_active_pid_ns(current); char buffer[256]; int ret = 0; /* We only trust the superuser with rebooting the system. */ - if (!capable(CAP_SYS_BOOT)) + if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT)) return -EPERM; /* For safety, we require "magic" arguments. */ @@ -453,7 +454,7 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, * pid_namespace, the command is handled by reboot_pid_ns() which will * call do_exit(). */ - ret = reboot_pid_ns(task_active_pid_ns(current), cmd); + ret = reboot_pid_ns(pid_ns, cmd); if (ret) return ret; From f2ace93136cade6904a71d26e2f4768eef3b9aa3 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 27 Dec 2012 13:37:40 +0100 Subject: [PATCH 0415/4607] MIPS: sysmips: Use unreachable(). Signed-off-by: Ralf Baechle --- arch/mips/kernel/syscall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 201cb76b4df9..8c81f7d11df6 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -235,7 +235,7 @@ static inline int mips_atomic_set(struct pt_regs *regs, : "r" (regs)); /* unreached. Honestly. */ - while (1); + unreachable(); } save_static_function(sys_sysmips); From 12890d0f61fc4ed4c3afbb1982df382aa9905834 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 27 Dec 2012 16:23:12 +0100 Subject: [PATCH 0416/4607] MIPS: sysmips: Rewrite to use SYSCALL_DEFINE3(). Thanks to current_pt_regs() there is no need to use the dark MIPS magic. Signed-off-by: Ralf Baechle --- arch/mips/kernel/syscall.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 8c81f7d11df6..107307d583eb 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -138,10 +138,10 @@ SYSCALL_DEFINE1(set_thread_area, unsigned long, addr) return 0; } -static inline int mips_atomic_set(struct pt_regs *regs, - unsigned long addr, unsigned long new) +static inline int mips_atomic_set(unsigned long addr, unsigned long new) { unsigned long old, tmp; + struct pt_regs *regs; unsigned int err; if (unlikely(addr & 3)) @@ -222,6 +222,7 @@ static inline int mips_atomic_set(struct pt_regs *regs, if (unlikely(err)) return err; + regs = current_pt_regs(); regs->regs[2] = old; regs->regs[7] = 0; /* No error */ @@ -238,19 +239,11 @@ static inline int mips_atomic_set(struct pt_regs *regs, unreachable(); } -save_static_function(sys_sysmips); -static int __used noinline -_sys_sysmips(nabi_no_regargs struct pt_regs regs) +SYSCALL_DEFINE3(sysmips, long, cmd, long, arg1, long, arg2) { - long cmd, arg1, arg2; - - cmd = regs.regs[4]; - arg1 = regs.regs[5]; - arg2 = regs.regs[6]; - switch (cmd) { case MIPS_ATOMIC_SET: - return mips_atomic_set(®s, arg1, arg2); + return mips_atomic_set(arg1, arg2); case MIPS_FIXADE: if (arg1 & ~3) From afe5624b142279c6072ce1872811e309ad7e94be Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 27 Nov 2012 13:35:30 -0300 Subject: [PATCH 0417/4607] [media] rc: unlock on error in store_protocols() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This error path is missing the unlock. [mchehab@redhat.com: Merged two equal patches into one] Signed-off-by: Sasha Levin Acked-by: David Härdeman Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index d593bc65b4ca..759a40a42eaa 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -892,7 +892,8 @@ static ssize_t store_protocols(struct device *device, if (i == ARRAY_SIZE(proto_names)) { IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); - return -EINVAL; + ret = -EINVAL; + goto out; } count++; From 68c97bf39ad853063876f4a8449009c1620d972a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alf=20H=C3=B8gemark?= Date: Wed, 28 Nov 2012 14:29:16 -0300 Subject: [PATCH 0418/4607] [media] cx231xx : Add support for Elgato Video Capture V2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for the Elgato Video Capture, version 2, device. The device is added based on the code for CX231XX_BOARD_HAUPPAUGE_USBLIVE2, it is simply a copy of the code for that board, with the proper USB device info for the Elgato Video Capture V2 device. Signed-off-by: Alf Høgemark Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-cards.c | 29 +++++++++++++++++++++++ drivers/media/usb/cx231xx/cx231xx.h | 1 + 2 files changed, 30 insertions(+) diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index bbed1e40eeda..94b573ad96d5 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -603,6 +603,33 @@ struct cx231xx_board cx231xx_boards[] = { .gpio = NULL, } }, }, + [CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2] = { + .name = "Elgato Video Capture V2", + .tuner_type = TUNER_ABSENT, + .decoder = CX231XX_AVDECODER, + .output_mode = OUT_MODE_VIP11, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .norm = V4L2_STD_NTSC, + .no_alt_vanc = 1, + .external_av = 1, + .dont_use_port_3 = 1, + .input = {{ + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = NULL, + } }, + }, }; const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); @@ -642,6 +669,8 @@ struct usb_device_id cx231xx_id_table[] = { .driver_info = CX231XX_BOARD_KWORLD_UB430_USB_HYBRID}, {USB_DEVICE(0x1f4d, 0x0237), .driver_info = CX231XX_BOARD_ICONBIT_U100}, + {USB_DEVICE(0x0fd9, 0x0037), + .driver_info = CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2}, {}, }; diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index a89d020de948..3e11462be0d0 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -68,6 +68,7 @@ #define CX231XX_BOARD_ICONBIT_U100 13 #define CX231XX_BOARD_HAUPPAUGE_USB2_FM_PAL 14 #define CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC 15 +#define CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2 16 /* Limits minimum and default number of buffers */ #define CX231XX_MIN_BUF 4 From 5d92bbe634cc9d768db2b88ca7c303e6799ed5c0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Dec 2012 14:43:41 -0200 Subject: [PATCH 0419/4607] [media] ttpci: Fix a missing Kconfig dependency Fix a Kconfig warning that appears with allmodconfig on arm: warning: (DVB_USB_PCTV452E) selects TTPCI_EEPROM which has unmet direct dependencies (MEDIA_SUPPORT && MEDIA_PCI_SUPPORT && MEDIA_DIGITAL_TV_SUPPORT && I2C) Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 6 ++++++ drivers/media/pci/ttpci/Kconfig | 5 ----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 4ef0d80b57f4..4d9b4c21c199 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -135,6 +135,12 @@ config DVB_NET You may want to disable the network support on embedded devices. If unsure say Y. +# This Kconfig option is used by both PCI and USB drivers +config TTPCI_EEPROM + tristate + depends on I2C + default n + source "drivers/media/dvb-core/Kconfig" comment "Media drivers" diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig index 314e417addae..0dcb8cd77676 100644 --- a/drivers/media/pci/ttpci/Kconfig +++ b/drivers/media/pci/ttpci/Kconfig @@ -1,8 +1,3 @@ -config TTPCI_EEPROM - tristate - depends on I2C - default n - config DVB_AV7110 tristate "AV7110 cards" depends on DVB_CORE && PCI && I2C From 26711113c4e4e72a72d347bfc33590595f248df7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Dec 2012 14:46:55 -0200 Subject: [PATCH 0420/4607] [media] omap: Fix Kconfig dependencies on OMAP2 Resolves the following warning that appears with allmodconfig on -arm: warning: (VIDEO_OMAP2_VOUT && DRM_OMAP) selects OMAP2_DSS which has unmet direct dependencies (HAS_IOMEM && ARCH_OMAP2PLUS) Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig index 390ab094f9f2..37ad446b35b3 100644 --- a/drivers/media/platform/omap/Kconfig +++ b/drivers/media/platform/omap/Kconfig @@ -6,7 +6,7 @@ config VIDEO_OMAP2_VOUT depends on ARCH_OMAP2 || ARCH_OMAP3 select VIDEOBUF_GEN select VIDEOBUF_DMA_CONTIG - select OMAP2_DSS + select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB default n From 9d193b758edaad192d05ebcb8dc4cb72711bf618 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 29 Nov 2012 16:45:07 -0300 Subject: [PATCH 0421/4607] [media] s5p-fimc: convert struct spinlock to spinlock_t spinlock_t should always be used. Could not get this to build with allmodconfig: mcgrof@frijol ~/linux-next (git::(no branch))$ make C=1 M=drivers/media/platform/s5p-fimc/ WARNING: Symbol version dump /home/mcgrof/linux-next/Module.symvers is missing; modules will have no dependencies and modversions. LD drivers/media/platform/s5p-fimc/built-in.o Building modules, stage 2. MODPOST 0 modules Reported-by: Hauke Mehrtens Signed-off-by: Luis R. Rodriguez Cc: Kyungmin Park Cc: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 8a06f1402f37..076c29bd41c8 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -187,7 +187,7 @@ struct csis_state { const struct csis_pix_format *csis_fmt; struct v4l2_mbus_framefmt format; - struct spinlock slock; + spinlock_t slock; struct csis_pktbuf pkt_buf; struct s5pcsis_event events[S5PCSIS_NUM_EVENTS]; }; From a75831f3600c479054fc3f70cd11257ab07886e2 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Thu, 29 Nov 2012 16:45:08 -0300 Subject: [PATCH 0422/4607] [media] s5p-jpeg: convert struct spinlock to spinlock_t spinlock_t should always be used. Could not get this to build with allmodconfig: mcgrof@frijol ~/linux-next (git::(no branch))$ make C=1 M=drivers/media/platform/s5p-jpeg WARNING: Symbol version dump /home/mcgrof/linux-next/Module.symvers is missing; modules will have no dependencies and modversions. Building modules, stage 2. MODPOST 0 modules Reported-by: Hauke Mehrtens Signed-off-by: Luis R. Rodriguez Cc: Kyungmin Park Cc: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-jpeg/jpeg-core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h index 022b9b9baff9..8a4013e3aee7 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h @@ -62,7 +62,7 @@ */ struct s5p_jpeg { struct mutex lock; - struct spinlock slock; + spinlock_t slock; struct v4l2_device v4l2_dev; struct video_device *vfd_encoder; From 6ae23224557d797439d02f6ce5d10a82ab544b21 Mon Sep 17 00:00:00 2001 From: Juergen Lock Date: Sun, 23 Dec 2012 17:23:06 -0300 Subject: [PATCH 0423/4607] [media] dvb_frontend: fix ioctls failing if frontend open/closed too fast That likely fixes this MythTV ticket: http://code.mythtv.org/trac/ticket/10830 (which btw affects all usb tuners I tested as well, pctv452e, dib0700, af9015) pctv452e is still possibly broken with MythTV even after this fix; it does work with VDR here tho despite I2C errors. Reduced testcase: http://people.freebsd.org/~nox/tmp/ioctltst.c Thanx to devinheitmueller and crope from #linuxtv for helping with this fix! :) Signed-off-by: Juergen Lock Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 49d95040096a..9b4a47c104a1 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -603,6 +603,7 @@ static int dvb_frontend_thread(void *data) enum dvbfe_algo algo; bool re_tune = false; + bool semheld = false; dev_dbg(fe->dvb->device, "%s:\n", __func__); @@ -626,6 +627,8 @@ restart: if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) { /* got signal or quitting */ + if (!down_interruptible(&fepriv->sem)) + semheld = true; fepriv->exit = DVB_FE_NORMAL_EXIT; break; } @@ -741,6 +744,8 @@ restart: fepriv->exit = DVB_FE_NO_EXIT; mb(); + if (semheld) + up(&fepriv->sem); dvb_frontend_wakeup(fe); return 0; } @@ -1823,16 +1828,20 @@ static int dvb_frontend_ioctl(struct file *file, int err = -ENOTTY; dev_dbg(fe->dvb->device, "%s: (%d)\n", __func__, _IOC_NR(cmd)); - if (fepriv->exit != DVB_FE_NO_EXIT) + if (down_interruptible(&fepriv->sem)) + return -ERESTARTSYS; + + if (fepriv->exit != DVB_FE_NO_EXIT) { + up(&fepriv->sem); return -ENODEV; + } if ((file->f_flags & O_ACCMODE) == O_RDONLY && (_IOC_DIR(cmd) != _IOC_READ || cmd == FE_GET_EVENT || - cmd == FE_DISEQC_RECV_SLAVE_REPLY)) + cmd == FE_DISEQC_RECV_SLAVE_REPLY)) { + up(&fepriv->sem); return -EPERM; - - if (down_interruptible (&fepriv->sem)) - return -ERESTARTSYS; + } if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY)) err = dvb_frontend_ioctl_properties(file, cmd, parg); From 30ad64b8ac539459f8975aa186421ef3db0bb5cb Mon Sep 17 00:00:00 2001 From: Nikolaus Schulz Date: Sun, 23 Dec 2012 18:49:07 -0300 Subject: [PATCH 0424/4607] [media] dvb: push down ioctl lock in dvb_usercopy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since most dvb ioctls wrap their real work with dvb_usercopy, the static mutex used in dvb_usercopy effectively is a global lock for dvb ioctls. Unfortunately, frontend ioctls can be blocked by the frontend thread for several seconds; this leads to unacceptable lock contention. Mitigate that by pushing the mutex from dvb_usercopy down to the individual, device specific ioctls. There are 10 such ioctl functions using dvb_usercopy, either calling it directly, or via the trivial wrapper dvb_generic_ioctl. The following already employ their own locking and look safe: • dvb_demux_ioctl (as per dvb_demux_do_ioctl) • dvb_dvr_ioctl (as per dvb_dvr_do_ioctl) • dvb_osd_ioctl (as per single non-trivial callee) • fdtv_ca_ioctl (as per callees) • dvb_frontend_ioctl The following functions do not, and are thus changed to use a device specific mutex: • dvb_net_ioctl (as per dvb_net_do_ioctl) • dvb_ca_en50221_io_ioctl (as per dvb_ca_en50221_io_do_ioctl) • dvb_video_ioctl • dvb_audio_ioctl • dvb_ca_ioctl Signed-off-by: Nikolaus Schulz Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_ca_en50221.c | 9 ++++ drivers/media/dvb-core/dvb_net.c | 71 +++++++++++++++++-------- drivers/media/dvb-core/dvb_net.h | 1 + drivers/media/dvb-core/dvbdev.c | 2 - drivers/media/pci/ttpci/av7110.c | 2 + drivers/media/pci/ttpci/av7110.h | 2 + drivers/media/pci/ttpci/av7110_av.c | 8 +++ drivers/media/pci/ttpci/av7110_ca.c | 24 ++++++--- 8 files changed, 87 insertions(+), 32 deletions(-) diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 9be65a3b931f..190e5e0f48c7 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -156,6 +156,9 @@ struct dvb_ca_private { /* Slot to start looking for data to read from in the next user-space read operation */ int next_read_slot; + + /* mutex serializing ioctls */ + struct mutex ioctl_mutex; }; static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); @@ -1191,6 +1194,9 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, dprintk("%s\n", __func__); + if (mutex_lock_interruptible(&ca->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case CA_RESET: for (slot = 0; slot < ca->slot_count; slot++) { @@ -1241,6 +1247,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, break; } + mutex_unlock(&ca->ioctl_mutex); return err; } @@ -1695,6 +1702,8 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, mutex_init(&ca->slot_info[i].slot_lock); } + mutex_init(&ca->ioctl_mutex); + if (signal_pending(current)) { ret = -EINTR; goto error; diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index c2117688aa23..44225b186f6d 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -1345,26 +1345,35 @@ static int dvb_net_do_ioctl(struct file *file, { struct dvb_device *dvbdev = file->private_data; struct dvb_net *dvbnet = dvbdev->priv; + int ret = 0; if (((file->f_flags&O_ACCMODE)==O_RDONLY)) return -EPERM; + if (mutex_lock_interruptible(&dvbnet->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case NET_ADD_IF: { struct dvb_net_if *dvbnetif = parg; int result; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto ioctl_error; + } - if (!try_module_get(dvbdev->adapter->module)) - return -EPERM; + if (!try_module_get(dvbdev->adapter->module)) { + ret = -EPERM; + goto ioctl_error; + } result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype); if (result<0) { module_put(dvbdev->adapter->module); - return result; + ret = result; + goto ioctl_error; } dvbnetif->if_num=result; break; @@ -1376,8 +1385,10 @@ static int dvb_net_do_ioctl(struct file *file, struct dvb_net_if *dvbnetif = parg; if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || - !dvbnet->state[dvbnetif->if_num]) - return -EINVAL; + !dvbnet->state[dvbnetif->if_num]) { + ret = -EINVAL; + goto ioctl_error; + } netdev = dvbnet->device[dvbnetif->if_num]; @@ -1388,16 +1399,18 @@ static int dvb_net_do_ioctl(struct file *file, } case NET_REMOVE_IF: { - int ret; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) - return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto ioctl_error; + } + if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) { + ret = -EINVAL; + goto ioctl_error; + } ret = dvb_net_remove_if(dvbnet, (unsigned long) parg); if (!ret) module_put(dvbdev->adapter->module); - return ret; + break; } /* binary compatibility cruft */ @@ -1406,16 +1419,21 @@ static int dvb_net_do_ioctl(struct file *file, struct __dvb_net_if_old *dvbnetif = parg; int result; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto ioctl_error; + } - if (!try_module_get(dvbdev->adapter->module)) - return -EPERM; + if (!try_module_get(dvbdev->adapter->module)) { + ret = -EPERM; + goto ioctl_error; + } result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE); if (result<0) { module_put(dvbdev->adapter->module); - return result; + ret = result; + goto ioctl_error; } dvbnetif->if_num=result; break; @@ -1427,8 +1445,10 @@ static int dvb_net_do_ioctl(struct file *file, struct __dvb_net_if_old *dvbnetif = parg; if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || - !dvbnet->state[dvbnetif->if_num]) - return -EINVAL; + !dvbnet->state[dvbnetif->if_num]) { + ret = -EINVAL; + goto ioctl_error; + } netdev = dvbnet->device[dvbnetif->if_num]; @@ -1437,9 +1457,13 @@ static int dvb_net_do_ioctl(struct file *file, break; } default: - return -ENOTTY; + ret = -ENOTTY; + break; } - return 0; + +ioctl_error: + mutex_unlock(&dvbnet->ioctl_mutex); + return ret; } static long dvb_net_ioctl(struct file *file, @@ -1505,6 +1529,7 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, { int i; + mutex_init(&dvbnet->ioctl_mutex); dvbnet->demux = dmx; for (i=0; iioctl_mutex); + #if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) av7110_ir_init(av7110); #endif diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h index a378662b1dcf..ef3d9606b269 100644 --- a/drivers/media/pci/ttpci/av7110.h +++ b/drivers/media/pci/ttpci/av7110.h @@ -271,6 +271,8 @@ struct av7110 { struct dvb_frontend* fe; fe_status_t fe_status; + struct mutex ioctl_mutex; + /* crash recovery */ void (*recover)(struct av7110* av7110); fe_sec_voltage_t saved_voltage; diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c index 952b33dbac4f..301029ca4535 100644 --- a/drivers/media/pci/ttpci/av7110_av.c +++ b/drivers/media/pci/ttpci/av7110_av.c @@ -1109,6 +1109,9 @@ static int dvb_video_ioctl(struct file *file, } } + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case VIDEO_STOP: av7110->videostate.play_state = VIDEO_STOPPED; @@ -1297,6 +1300,7 @@ static int dvb_video_ioctl(struct file *file, break; } + mutex_unlock(&av7110->ioctl_mutex); return ret; } @@ -1314,6 +1318,9 @@ static int dvb_audio_ioctl(struct file *file, (cmd != AUDIO_GET_STATUS)) return -EPERM; + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case AUDIO_STOP: if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) @@ -1442,6 +1449,7 @@ static int dvb_audio_ioctl(struct file *file, ret = -ENOIOCTLCMD; } + mutex_unlock(&av7110->ioctl_mutex); return ret; } diff --git a/drivers/media/pci/ttpci/av7110_ca.c b/drivers/media/pci/ttpci/av7110_ca.c index 9fc1dd0ba4c3..a6079b90252a 100644 --- a/drivers/media/pci/ttpci/av7110_ca.c +++ b/drivers/media/pci/ttpci/av7110_ca.c @@ -253,12 +253,17 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) struct dvb_device *dvbdev = file->private_data; struct av7110 *av7110 = dvbdev->priv; unsigned long arg = (unsigned long) parg; + int ret = 0; dprintk(8, "av7110:%p\n",av7110); + if (mutex_lock_interruptible(&av7110->ioctl_mutex)) + return -ERESTARTSYS; + switch (cmd) { case CA_RESET: - return ci_ll_reset(&av7110->ci_wbuffer, file, arg, &av7110->ci_slot[0]); + ret = ci_ll_reset(&av7110->ci_wbuffer, file, arg, + &av7110->ci_slot[0]); break; case CA_GET_CAP: { @@ -277,8 +282,10 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) { ca_slot_info_t *info=(ca_slot_info_t *)parg; - if (info->num < 0 || info->num > 1) + if (info->num < 0 || info->num > 1) { + mutex_unlock(&av7110->ioctl_mutex); return -EINVAL; + } av7110->ci_slot[info->num].num = info->num; av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? CA_CI_LINK : CA_CI; @@ -306,10 +313,10 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) { ca_descr_t *descr = (ca_descr_t*) parg; - if (descr->index >= 16) - return -EINVAL; - if (descr->parity > 1) + if (descr->index >= 16 || descr->parity > 1) { + mutex_unlock(&av7110->ioctl_mutex); return -EINVAL; + } av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, (descr->index<<8)|descr->parity, (descr->cw[0]<<8)|descr->cw[1], @@ -320,9 +327,12 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) } default: - return -EINVAL; + ret = -EINVAL; + break; } - return 0; + + mutex_unlock(&av7110->ioctl_mutex); + return ret; } static ssize_t dvb_ca_write(struct file *file, const char __user *buf, From e8d4237325a475b02594d1fd85bb67983f7d57b9 Mon Sep 17 00:00:00 2001 From: Oleh Kravchenko Date: Sat, 8 Dec 2012 18:20:59 -0300 Subject: [PATCH 0425/4607] [media] Added support for AVerTV Hybrid Express Slim HC81R This patch provide only analog support. The device is based on AF9013 demodulator, XC3028 tuner and CX23885 chipset; subsystem id: 1461:d939 Signed-off-by: Oleh Kravchenko Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-cards.c | 75 +++++++++++++++++++++++ drivers/media/pci/cx23885/cx23885-video.c | 15 ++++- drivers/media/pci/cx23885/cx23885.h | 1 + 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index 8d96ae4ebbdd..7e923f8dd2f5 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -577,6 +577,35 @@ struct cx23885_board cx23885_boards[] = { .name = "Hauppauge WinTV-HVR4400", .portb = CX23885_MPEG_DVB, }, + [CX23885_BOARD_AVERMEDIA_HC81R] = { + .name = "AVerTV Hybrid Express Slim HC81R", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, /* 0xc2 >> 1 */ + .tuner_bus = 1, + .porta = CX23885_ANALOG_VIDEO, + .input = {{ + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_VIN2_CH1 | + CX25840_VIN5_CH2 | + CX25840_NONE0_CH3 | + CX25840_NONE1_CH3, + .amux = CX25840_AUDIO8, + }, { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_VIN8_CH1 | + CX25840_NONE_CH2 | + CX25840_VIN7_CH3 | + CX25840_SVIDEO_ON, + .amux = CX25840_AUDIO6, + }, { + .type = CX23885_VMUX_COMPONENT, + .vmux = CX25840_VIN1_CH1 | + CX25840_NONE_CH2 | + CX25840_NONE0_CH3 | + CX25840_NONE1_CH3, + .amux = CX25840_AUDIO6, + } }, + } }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -808,6 +837,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0070, .subdevice = 0xc1f8, .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + }, { + .subvendor = 0x1461, + .subdevice = 0xd939, + .card = CX23885_BOARD_AVERMEDIA_HC81R, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1032,6 +1065,10 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg) case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: altera_ci_tuner_reset(dev, port->nr); break; + case CX23885_BOARD_AVERMEDIA_HC81R: + /* XC3028L Reset Command */ + bitmask = 1 << 2; + break; } if (bitmask) { @@ -1331,6 +1368,32 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx23885_gpio_set(dev, GPIO_8); mdelay(100); break; + case CX23885_BOARD_AVERMEDIA_HC81R: + cx_clear(MC417_CTL, 1); + /* GPIO-0,1,2 setup direction as output */ + cx_set(GP0_IO, 0x00070000); + mdelay(10); + /* AF9013 demod reset */ + cx_set(GP0_IO, 0x00010001); + mdelay(10); + cx_clear(GP0_IO, 0x00010001); + mdelay(10); + cx_set(GP0_IO, 0x00010001); + mdelay(10); + /* demod tune? */ + cx_clear(GP0_IO, 0x00030003); + mdelay(10); + cx_set(GP0_IO, 0x00020002); + mdelay(10); + cx_set(GP0_IO, 0x00010001); + mdelay(10); + cx_clear(GP0_IO, 0x00020002); + /* XC3028L tuner reset */ + cx_set(GP0_IO, 0x00040004); + cx_clear(GP0_IO, 0x00040004); + cx_set(GP0_IO, 0x00040004); + mdelay(60); + break; } } @@ -1549,6 +1612,17 @@ void cx23885_card_setup(struct cx23885_dev *dev) } switch (dev->board) { + case CX23885_BOARD_AVERMEDIA_HC81R: + /* Defaults for VID B */ + ts1->gen_ctrl_val = 0x4; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + /* Defaults for VID C */ + /* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */ + ts2->gen_ctrl_val = 0x10e; + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ @@ -1675,6 +1749,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_MPX885: case CX23885_BOARD_MYGICA_X8507: case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: + case CX23885_BOARD_AVERMEDIA_HC81R: dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", 0x88 >> 1, NULL); diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 83975313e0f2..0e80ba4ca4dd 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -509,7 +509,8 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) || - (dev->board == CX23885_BOARD_MYGICA_X8507)) { + (dev->board == CX23885_BOARD_MYGICA_X8507) || + (dev->board == CX23885_BOARD_AVERMEDIA_HC81R)) { /* Configure audio routing */ v4l2_subdev_call(dev->sd_cx25840, audio, s_routing, INPUT(input)->amux, 0, 0); @@ -1878,6 +1879,18 @@ int cx23885_video_register(struct cx23885_dev *dev) }; v4l2_subdev_call(sd, tuner, s_config, &cfg); } + + if (dev->board == CX23885_BOARD_AVERMEDIA_HC81R) { + struct xc2028_ctrl ctrl = { + .fname = "xc3028L-v36.fw", + .max_len = 64 + }; + struct v4l2_priv_tun_config cfg = { + .tuner = dev->tuner_type, + .priv = &ctrl + }; + v4l2_subdev_call(sd, tuner, s_config, &cfg); + } } } diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index 61889b25a2af..59c322d870f2 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -92,6 +92,7 @@ #define CX23885_BOARD_HAUPPAUGE_HVR1255_22111 36 #define CX23885_BOARD_PROF_8000 37 #define CX23885_BOARD_HAUPPAUGE_HVR4400 38 +#define CX23885_BOARD_AVERMEDIA_HC81R 39 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 From 87739868944919beb4e6b3860c74355a114a54a1 Mon Sep 17 00:00:00 2001 From: Simon Farnsworth Date: Mon, 10 Dec 2012 08:35:09 -0300 Subject: [PATCH 0426/4607] [media] saa7134: Add pm_qos_request to fix video corruption The SAA7134 appears to have trouble buffering more than one line of video when doing DMA. Rather than try to fix the driver to cope (as has been done by Andy Walls for the cx18 driver), put in a pm_qos_request to limit deep sleep exit latencies. The visible effect of not having this is that seemingly random lines are only partly transferred - if you feed in a static image, you see a portion of the image "flicker" into place. [mchehab@redhat.com: Fix ABI breakage due to some renames at pm_qos] Signed-off-by: Simon Farnsworth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7134/saa7134-video.c | 13 +++++++++++++ drivers/media/pci/saa7134/saa7134.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 3abf52711e13..7c503fb68526 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -2248,6 +2248,17 @@ static int saa7134_streamon(struct file *file, void *priv, if (!res_get(dev, fh, res)) return -EBUSY; + /* The SAA7134 has a 1K FIFO; the datasheet suggests that when + * configured conservatively, there's 22 usec of buffering for video. + * We therefore request a DMA latency of 20 usec, giving us 2 usec of + * margin in case the FIFO is configured differently to the datasheet. + * Unfortunately, I lack register-level documentation to check the + * Linux FIFO setup and confirm the perfect value. + */ + pm_qos_add_request(&fh->qos_request, + PM_QOS_CPU_DMA_LATENCY, + 20); + return videobuf_streamon(saa7134_queue(fh)); } @@ -2259,6 +2270,8 @@ static int saa7134_streamoff(struct file *file, void *priv, struct saa7134_dev *dev = fh->dev; int res = saa7134_resource(fh); + pm_qos_remove_request(&fh->qos_request); + err = videobuf_streamoff(saa7134_queue(fh)); if (err < 0) return err; diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index c24b6512bd8f..0a3feaa8589b 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -469,6 +470,7 @@ struct saa7134_fh { enum v4l2_buf_type type; unsigned int resources; enum v4l2_priority prio; + struct pm_qos_request qos_request; /* video overlay */ struct v4l2_window win; From a214c55121e6746f21a32eea2a06a78a7a2d12ca Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:09 -0300 Subject: [PATCH 0427/4607] [media] dvb-usb: fix indentation of a for loop Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dvb-usb-init.c | 60 ++++++++++++------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/media/usb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c index 169196ec2d4e..1adf325012f7 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c @@ -38,41 +38,41 @@ static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) memcpy(&adap->props, &d->props.adapter[n], sizeof(struct dvb_usb_adapter_properties)); - for (o = 0; o < adap->props.num_frontends; o++) { - struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o]; - /* speed - when running at FULL speed we need a HW PID filter */ - if (d->udev->speed == USB_SPEED_FULL && !(props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) { - err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)"); - return -ENODEV; - } + for (o = 0; o < adap->props.num_frontends; o++) { + struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o]; + /* speed - when running at FULL speed we need a HW PID filter */ + if (d->udev->speed == USB_SPEED_FULL && !(props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) { + err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)"); + return -ENODEV; + } - if ((d->udev->speed == USB_SPEED_FULL && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) || - (props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { - info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count); - adap->fe_adap[o].pid_filtering = 1; - adap->fe_adap[o].max_feed_count = props->pid_filter_count; - } else { - info("will pass the complete MPEG2 transport stream to the software demuxer."); - adap->fe_adap[o].pid_filtering = 0; - adap->fe_adap[o].max_feed_count = 255; - } + if ((d->udev->speed == USB_SPEED_FULL && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) || + (props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { + info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count); + adap->fe_adap[o].pid_filtering = 1; + adap->fe_adap[o].max_feed_count = props->pid_filter_count; + } else { + info("will pass the complete MPEG2 transport stream to the software demuxer."); + adap->fe_adap[o].pid_filtering = 0; + adap->fe_adap[o].max_feed_count = 255; + } - if (!adap->fe_adap[o].pid_filtering && - dvb_usb_force_pid_filter_usage && - props->caps & DVB_USB_ADAP_HAS_PID_FILTER) { - info("pid filter enabled by module option."); - adap->fe_adap[o].pid_filtering = 1; - adap->fe_adap[o].max_feed_count = props->pid_filter_count; - } + if (!adap->fe_adap[o].pid_filtering && + dvb_usb_force_pid_filter_usage && + props->caps & DVB_USB_ADAP_HAS_PID_FILTER) { + info("pid filter enabled by module option."); + adap->fe_adap[o].pid_filtering = 1; + adap->fe_adap[o].max_feed_count = props->pid_filter_count; + } - if (props->size_of_priv > 0) { - adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL); - if (adap->fe_adap[o].priv == NULL) { - err("no memory for priv for adapter %d fe %d.", n, o); - return -ENOMEM; + if (props->size_of_priv > 0) { + adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL); + if (adap->fe_adap[o].priv == NULL) { + err("no memory for priv for adapter %d fe %d.", n, o); + return -ENOMEM; + } } } - } if (adap->props.size_of_priv > 0) { adap->priv = kzalloc(adap->props.size_of_priv, GFP_KERNEL); From 908498349562ff6614d570b40e904a20d397dafa Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:10 -0300 Subject: [PATCH 0428/4607] [media] m920x: fix a typo in a comment Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 661bb75be955..433696d14f4b 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -591,7 +591,7 @@ static struct m920x_inits tvwalkertwin_rc_init [] = { }; static struct m920x_inits pinnacle310e_init[] = { - /* without these the tuner don't work */ + /* without these the tuner doesn't work */ { 0xff20, 0x9b }, { 0xff22, 0x70 }, From 7543f344e9b06afe86b55a2620f5c11b38bd5642 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:11 -0300 Subject: [PATCH 0429/4607] [media] m920x: factor out a m920x_write_seq() function This is in preparation for the vp7049 frontend attach function which is going to set a sequence of registers as well. Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 433696d14f4b..23416fbb9f8c 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -63,6 +63,21 @@ static inline int m920x_write(struct usb_device *udev, u8 request, return ret; } +static inline int m920x_write_seq(struct usb_device *udev, u8 request, + struct m920x_inits *seq) +{ + int ret; + while (seq->address) { + ret = m920x_write(udev, request, seq->data, seq->address); + if (ret != 0) + return ret; + + seq++; + } + + return ret; +} + static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) { int ret = 0, i, epi, flags = 0; @@ -71,15 +86,10 @@ static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) /* Remote controller init. */ if (d->props.rc.legacy.rc_query) { deb("Initialising remote control\n"); - while (rc_seq->address) { - if ((ret = m920x_write(d->udev, M9206_CORE, - rc_seq->data, - rc_seq->address)) != 0) { - deb("Initialising remote control failed\n"); - return ret; - } - - rc_seq++; + ret = m920x_write_seq(d->udev, M9206_CORE, rc_seq); + if (ret != 0) { + deb("Initialising remote control failed\n"); + return ret; } deb("Initialising remote control success\n"); From f526e9e1dcb65c8967c61bbfa72933f4553958ee Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:12 -0300 Subject: [PATCH 0430/4607] [media] m920x: factor out a m920x_parse_rc_state() function This is in preparation to using RC core infrastructure for some devices, the RC button state parsing logic can be shared berween rc.legacy and rc.core callbacks as it is independent from the mechanism used for RC handling. Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 81 +++++++++++++++++-------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 23416fbb9f8c..581c5deffb6e 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -140,9 +140,50 @@ static int m920x_init_ep(struct usb_interface *intf) alt->desc.bAlternateSetting); } -static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +static inline void m920x_parse_rc_state(struct dvb_usb_device *d, u8 rc_state, + int *state) { struct m920x_state *m = d->priv; + + switch (rc_state) { + case 0x80: + *state = REMOTE_NO_KEY_PRESSED; + break; + + case 0x88: /* framing error or "invalid code" */ + case 0x99: + case 0xc0: + case 0xd8: + *state = REMOTE_NO_KEY_PRESSED; + m->rep_count = 0; + break; + + case 0x93: + case 0x92: + case 0x83: /* pinnacle PCTV310e */ + case 0x82: + m->rep_count = 0; + *state = REMOTE_KEY_PRESSED; + break; + + case 0x91: + case 0x81: /* pinnacle PCTV310e */ + /* prevent immediate auto-repeat */ + if (++m->rep_count > 2) + *state = REMOTE_KEY_REPEAT; + else + *state = REMOTE_NO_KEY_PRESSED; + break; + + default: + deb("Unexpected rc state %02x\n", rc_state); + *state = REMOTE_NO_KEY_PRESSED; + break; + } +} + +static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ int i, ret = 0; u8 *rc_state; @@ -159,42 +200,8 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) if (rc5_data(&d->props.rc.legacy.rc_map_table[i]) == rc_state[1]) { *event = d->props.rc.legacy.rc_map_table[i].keycode; - - switch(rc_state[0]) { - case 0x80: - *state = REMOTE_NO_KEY_PRESSED; - goto out; - - case 0x88: /* framing error or "invalid code" */ - case 0x99: - case 0xc0: - case 0xd8: - *state = REMOTE_NO_KEY_PRESSED; - m->rep_count = 0; - goto out; - - case 0x93: - case 0x92: - case 0x83: /* pinnacle PCTV310e */ - case 0x82: - m->rep_count = 0; - *state = REMOTE_KEY_PRESSED; - goto out; - - case 0x91: - case 0x81: /* pinnacle PCTV310e */ - /* prevent immediate auto-repeat */ - if (++m->rep_count > 2) - *state = REMOTE_KEY_REPEAT; - else - *state = REMOTE_NO_KEY_PRESSED; - goto out; - - default: - deb("Unexpected rc state %02x\n", rc_state[0]); - *state = REMOTE_NO_KEY_PRESSED; - goto out; - } + m920x_parse_rc_state(d, rc_state[0], state); + goto out; } if (rc_state[1] != 0) From 7a7ef4657e84b5038eace08a1db5b480854c893e Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:13 -0300 Subject: [PATCH 0431/4607] [media] m920x: avoid repeating RC state parsing at each keycode Parsing the RC press state is invariant wrt. the keycode, take it out of the keycode scanning loop. Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 581c5deffb6e..5f6ca753e293 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -197,10 +197,11 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0) goto out; + m920x_parse_rc_state(d, rc_state[0], state); + for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) if (rc5_data(&d->props.rc.legacy.rc_map_table[i]) == rc_state[1]) { *event = d->props.rc.legacy.rc_map_table[i].keycode; - m920x_parse_rc_state(d, rc_state[0], state); goto out; } From b677757cef208203426aa8486a91214809135e01 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:14 -0300 Subject: [PATCH 0432/4607] [media] m920x: introduce m920x_rc_core_query() Add an m920x_rc_core_query() function for drivers which want to use the linux RC core infrastructure. Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 5f6ca753e293..bddd7637fdaa 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -215,6 +215,38 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) return ret; } +static int m920x_rc_core_query(struct dvb_usb_device *d) +{ + int ret = 0; + u8 *rc_state; + int state; + + rc_state = kmalloc(2, GFP_KERNEL); + if (!rc_state) + return -ENOMEM; + + if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, &rc_state[0], 1)) != 0) + goto out; + + if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, &rc_state[1], 1)) != 0) + goto out; + + deb("state=0x%02x keycode=0x%02x\n", rc_state[0], rc_state[1]); + + m920x_parse_rc_state(d, rc_state[0], &state); + + if (state == REMOTE_NO_KEY_PRESSED) + rc_keyup(d->rc_dev); + else if (state == REMOTE_KEY_REPEAT) + rc_repeat(d->rc_dev); + else + rc_keydown(d->rc_dev, rc_state[1], 0); + +out: + kfree(rc_state); + return ret; +} + /* I2C */ static int m920x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { From 9d5394c2a19f8a74cb55ce83027bad1808b373b5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Dec 2012 16:32:58 -0200 Subject: [PATCH 0433/4607] [media] m920x: Fix CodingStyle issues Fix CodingStyle issues introduced by the last patch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index bddd7637fdaa..a70c5eaf0600 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -191,10 +191,14 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) if (!rc_state) return -ENOMEM; - if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, rc_state, 1)) != 0) + ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, + rc_state, 1); + if (ret != 0) goto out; - if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0) + ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, + rc_state + 1, 1); + if (ret != 0) goto out; m920x_parse_rc_state(d, rc_state[0], state); From 68511a7553b773a29808b5f9efe26e89df152c3d Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:15 -0300 Subject: [PATCH 0434/4607] [media] m920x: send the RC init sequence also when rc.core is used Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/m920x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index a70c5eaf0600..8888b8c2f8e5 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -84,7 +84,7 @@ static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) int adap_enabled[M9206_MAX_ADAPTERS] = { 0 }; /* Remote controller init. */ - if (d->props.rc.legacy.rc_query) { + if (d->props.rc.legacy.rc_query || d->props.rc.core.rc_query) { deb("Initialising remote control\n"); ret = m920x_write_seq(d->udev, M9206_CORE, rc_seq); if (ret != 0) { From f6b70ec509a393a966d1307f3e8a89341ee9e7f1 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:16 -0300 Subject: [PATCH 0435/4607] [media] get_dvb_firmware: add entry for the vp7049 firmware Add an entry to download the dvb-usb-vp7049-0.95.fw firmware for the Twinhan vp7049 and similar devices. Known devices of this kind are: Twinhan/Azurewave DTV-DVB UDTT7049 Digicom Digitune-S Think Xtra Hollywood DVB-T USB2.0 Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/get_dvb_firmware | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index 32bc56b13b1c..0cdb1570ffb4 100755 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -23,7 +23,7 @@ use IO::Handle; @components = ( "sp8870", "sp887x", "tda10045", "tda10046", "tda10046lifeview", "av7110", "dec2000t", "dec2540t", - "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004", + "dec3000s", "vp7041", "vp7049", "dibusb", "nxt2002", "nxt2004", "or51211", "or51132_qam", "or51132_vsb", "bluebird", "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718", "af9015", "ngene", "az6027", "lme2510_lg", "lme2510c_s7395", @@ -289,6 +289,19 @@ sub vp7041 { $outfile; } +sub vp7049 { + my $fwfile = "dvb-usb-vp7049-0.95.fw"; + my $url = "http://ao2.it/sites/default/files/blog/2012/11/06/linux-support-digicom-digitune-s-vp7049-udtt7049/$fwfile"; + my $hash = "5609fd295168aea88b25ff43a6f79c36"; + + checkstandard(); + + wgetfile($fwfile, $url); + verify($fwfile, $hash); + + $fwfile; +} + sub dibusb { my $url = "http://www.linuxtv.org/downloads/firmware/dvb-usb-dibusb-5.0.0.11.fw"; my $outfile = "dvb-dibusb-5.0.0.11.fw"; From de8ed820fd594a95582562d8f9f68148c972d1a4 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 10 Dec 2012 17:37:17 -0300 Subject: [PATCH 0436/4607] [media] m920x: add support for the VP-7049 Twinhan DVB-T USB Stick This device was originally made by Twinhan/Azurewave[1] and sometimes named DTV-DVB UDTT7049, it could be also found in Italy under the name of Digicom Digitune-S[2], or Think Xtra Hollywood DVB-T USB2.0[3]. Components: Usb bridge: ULi M9206 Frontend: MT352CG Tuner: MT2060F The firmware can be downloaded with: $ ./Documentation/dvb/get_dvb_firmware vp7049 [1] http://www.azurewave.com/Support_Utility_Driver.asp [2] http://www.digicom.it/digisit/driver_link.nsf/driverprodotto?openform&prodotto=DigiTuneS [3] http://www.txitalia.it/prodotto.asp?prodotto=txhollywooddvttv Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/usb/dvb-usb/m920x.c | 123 +++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 388c2eb4d747..c6720f2991ac 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -172,6 +172,7 @@ #define USB_PID_TWINHAN_VP7045_WARM 0x3206 #define USB_PID_TWINHAN_VP7021_COLD 0x3207 #define USB_PID_TWINHAN_VP7021_WARM 0x3208 +#define USB_PID_TWINHAN_VP7049 0x3219 #define USB_PID_TINYTWIN 0x3226 #define USB_PID_TINYTWIN_2 0xe402 #define USB_PID_TINYTWIN_3 0x9016 diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index 8888b8c2f8e5..92afeb20650f 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -16,6 +16,7 @@ #include "qt1010.h" #include "tda1004x.h" #include "tda827x.h" +#include "mt2060.h" #include #include "tuner-simple.h" @@ -550,6 +551,12 @@ static struct qt1010_config m920x_qt1010_config = { .i2c_address = 0x62 }; +static struct mt2060_config m920x_mt2060_config = { + .i2c_address = 0x60, /* 0xc0 */ + .clock_out = 0, +}; + + /* Callbacks for DVB USB */ static int m920x_mt352_frontend_attach(struct dvb_usb_adapter *adap) { @@ -564,6 +571,37 @@ static int m920x_mt352_frontend_attach(struct dvb_usb_adapter *adap) return 0; } +static int m920x_mt352_frontend_attach_vp7049(struct dvb_usb_adapter *adap) +{ + struct m920x_inits vp7049_fe_init_seq[] = { + /* XXX without these commands the frontend cannot be detected, + * they must be sent BEFORE the frontend is attached */ + { 0xff28, 0x00 }, + { 0xff23, 0x00 }, + { 0xff28, 0x00 }, + { 0xff23, 0x00 }, + { 0xff21, 0x20 }, + { 0xff21, 0x60 }, + { 0xff28, 0x00 }, + { 0xff22, 0x00 }, + { 0xff20, 0x30 }, + { 0xff20, 0x20 }, + { 0xff20, 0x30 }, + { } /* terminating entry */ + }; + int ret; + + deb("%s\n", __func__); + + ret = m920x_write_seq(adap->dev->udev, M9206_CORE, vp7049_fe_init_seq); + if (ret != 0) { + deb("Initialization of vp7049 frontend failed."); + return ret; + } + + return m920x_mt352_frontend_attach(adap); +} + static int m920x_tda10046_08_frontend_attach(struct dvb_usb_adapter *adap) { deb("%s\n",__func__); @@ -628,6 +666,18 @@ static int m920x_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) return 0; } +static int m920x_mt2060_tuner_attach(struct dvb_usb_adapter *adap) +{ + deb("%s\n", __func__); + + if (dvb_attach(mt2060_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, + &m920x_mt2060_config, 1220) == NULL) + return -ENODEV; + + return 0; +} + + /* device-specific initialization */ static struct m920x_inits megasky_rc_init [] = { { M9206_RC_INIT2, 0xa8 }, @@ -656,6 +706,15 @@ static struct m920x_inits pinnacle310e_init[] = { { } /* terminating entry */ }; +static struct m920x_inits vp7049_rc_init[] = { + { 0xff28, 0x00 }, + { 0xff23, 0x00 }, + { 0xff21, 0x70 }, + { M9206_RC_INIT2, 0x00 }, + { M9206_RC_INIT1, 0xff }, + { } /* terminating entry */ +}; + /* ir keymaps */ static struct rc_map_table rc_map_megasky_table[] = { { 0x0012, KEY_POWER }, @@ -758,6 +817,7 @@ static struct dvb_usb_device_properties digivox_mini_ii_properties; static struct dvb_usb_device_properties tvwalkertwin_properties; static struct dvb_usb_device_properties dposh_properties; static struct dvb_usb_device_properties pinnacle_pctv310e_properties; +static struct dvb_usb_device_properties vp7049_properties; static int m920x_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -810,6 +870,13 @@ static int m920x_probe(struct usb_interface *intf, goto found; } + ret = dvb_usb_device_init(intf, &vp7049_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) { + rc_init_seq = vp7049_rc_init; + goto found; + } + return ret; } else { /* Another interface on a multi-tuner device */ @@ -841,6 +908,7 @@ static struct usb_device_id m920x_table [] = { { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_COLD) }, { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_WARM) }, { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_PINNACLE_PCTV310E) }, + { USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_TWINHAN_VP7049) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, m920x_table); @@ -1133,6 +1201,61 @@ static struct dvb_usb_device_properties pinnacle_pctv310e_properties = { } }; +static struct dvb_usb_device_properties vp7049_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-vp7049-0.95.fw", + .download_firmware = m920x_firmware_download, + + .rc.core = { + .rc_interval = 150, + .rc_codes = RC_MAP_TWINHAN_VP1027_DVBS, + .rc_query = m920x_rc_core_query, + .allowed_protos = RC_TYPE_UNKNOWN, + }, + + .size_of_priv = sizeof(struct m920x_state), + + .identify_state = m920x_identify_state, + .num_adapters = 1, + .adapter = {{ + .num_frontends = 1, + .fe = {{ + + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m920x_pid_filter, + .pid_filter_ctrl = m920x_pid_filter_ctrl, + + .frontend_attach = m920x_mt352_frontend_attach_vp7049, + .tuner_attach = m920x_mt2060_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + } }, + } }, + .i2c_algo = &m920x_i2c_algo, + + .num_device_descs = 1, + .devices = { + { "DTV-DVB UDTT7049", + { &m920x_table[7], NULL }, + { NULL }, + } + } +}; + static struct usb_driver m920x_driver = { .name = "dvb_usb_m920x", .probe = m920x_probe, From 0e837fb985dadeff24824cef9ae86daaf0924494 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 10 Dec 2012 20:38:23 -0300 Subject: [PATCH 0437/4607] [media] MAINTAINERS: Add entries for Aptina sensor drivers Add an entry for the mt9m032, mt9p031, mt9t001 and mt9v032 Aptina sensor drivers. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 376117d5518d..8691752ad3e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5178,6 +5178,38 @@ L: platform-driver-x86@vger.kernel.org S: Supported F: drivers/platform/x86/msi-wmi.c +MT9M032 SENSOR DRIVER +M: Laurent Pinchart +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/mt9m032.c +F: include/media/mt9m032.h + +MT9P031 SENSOR DRIVER +M: Laurent Pinchart +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/mt9p031.c +F: include/media/mt9p031.h + +MT9T001 SENSOR DRIVER +M: Laurent Pinchart +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/mt9t001.c +F: include/media/mt9t001.h + +MT9V032 SENSOR DRIVER +M: Laurent Pinchart +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/mt9v032.c +F: include/media/mt9v032.h + MULTIFUNCTION DEVICES (MFD) M: Samuel Ortiz T: git git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6.git From 9d7005f9875460021f28df90783771875b694a32 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 10 Dec 2012 20:38:24 -0300 Subject: [PATCH 0438/4607] [media] MAINTAINERS: Add an entry for the ad3645a LED flash controller driver Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8691752ad3e6..c5de529677d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1300,6 +1300,14 @@ S: Maintained F: arch/arm64/ F: Documentation/arm64/ +AS3645A LED FLASH CONTROLLER DRIVER +M: Laurent Pinchart +L: linux-media@vger.kernel.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/i2c/as3645a.c +F: include/media/as3645a.h + ASC7621 HARDWARE MONITOR DRIVER M: George Joseph L: lm-sensors@lm-sensors.org From 21a73397c0cea688a37c532d1029fc8ecbd88fc6 Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Mon, 10 Dec 2012 23:05:28 -0300 Subject: [PATCH 0439/4607] [media] media: saa7146: don't use mutex_lock_interruptible() in device_release() Use uninterruptible mutex_lock in the release() file op to make sure all resources are properly freed when a process is being terminated. Returning -ERESTARTSYS has no effect for a terminating process and this may cause driver resources not to be released. This was found using the following semantic patch (http://coccinelle.lip6.fr/): @r@ identifier fops; identifier release_func; @@ static const struct v4l2_file_operations fops = { .release = release_func }; @depends on r@ identifier r.release_func; expression E; @@ static int release_func(...) { ... - if (mutex_lock_interruptible(E)) return -ERESTARTSYS; + mutex_lock(E); ... } Signed-off-by: Cyril Roelandt Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_fops.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index 2652f9155c34..eda01bc68ab2 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -265,8 +265,7 @@ static int fops_release(struct file *file) DEB_EE("file:%p\n", file); - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; + mutex_lock(vdev->lock); if (vdev->vfl_type == VFL_TYPE_VBI) { if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) From f7c3f5ce17a135610b114a17e917b5a53c4d07c4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 11 Dec 2012 09:11:52 -0300 Subject: [PATCH 0440/4607] [media] omap3isp: csiphy: Fix an uninitialized variable compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/platform/omap3isp/ispcsiphy.c: In function ‘csiphy_routing_cfg’: drivers/media/platform/omap3isp/ispcsiphy.c:71:57: warning: ‘shift’ may be used uninitialized in this function [-Wuninitialized] drivers/media/platform/omap3isp/ispcsiphy.c:40:6: note: ‘shift’ was declared here The warning is a false positive but the compiler is right in complaining. Fix it by using the correct enum data type for the iface argument and adding a default case in the switch statement. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/ispcsiphy.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c index 3d56b33f85e8..c09de32f986a 100644 --- a/drivers/media/platform/omap3isp/ispcsiphy.c +++ b/drivers/media/platform/omap3isp/ispcsiphy.c @@ -32,7 +32,8 @@ #include "ispreg.h" #include "ispcsiphy.h" -static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface, +static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, + enum isp_interface_type iface, bool ccp2_strobe) { u32 reg = isp_reg_readl( @@ -40,6 +41,8 @@ static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface, u32 shift, mode; switch (iface) { + default: + /* Should not happen in practice, but let's keep the compiler happy. */ case ISP_INTERFACE_CCP2B_PHY1: reg &= ~OMAP3630_CONTROL_CAMERA_PHY_CTRL_CSI1_RX_SEL_PHY2; shift = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_PHY1_SHIFT; @@ -59,9 +62,8 @@ static void csiphy_routing_cfg_3630(struct isp_csiphy *phy, u32 iface, } /* Select data/clock or data/strobe mode for CCP2 */ - switch (iface) { - case ISP_INTERFACE_CCP2B_PHY1: - case ISP_INTERFACE_CCP2B_PHY2: + if (iface == ISP_INTERFACE_CCP2B_PHY1 || + iface == ISP_INTERFACE_CCP2B_PHY2) { if (ccp2_strobe) mode = OMAP3630_CONTROL_CAMERA_PHY_CTRL_CAMMODE_CCP2_DATA_STROBE; else @@ -110,7 +112,8 @@ static void csiphy_routing_cfg_3430(struct isp_csiphy *phy, u32 iface, bool on, * and 3630, so they will not hold their contents in off-mode. This isn't an * issue since the MPU power domain is forced on whilst the ISP is in use. */ -static void csiphy_routing_cfg(struct isp_csiphy *phy, u32 iface, bool on, +static void csiphy_routing_cfg(struct isp_csiphy *phy, + enum isp_interface_type iface, bool on, bool ccp2_strobe) { if (phy->isp->mmio_base[OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL] From a4bb6f353e287f51a52a347670fd60938a566c25 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Fri, 14 Dec 2012 07:02:48 -0300 Subject: [PATCH 0441/4607] [media] media/rc: fix oops on unloading module rc-core During modiles initialization rc-core schedules work which calls request_module() several times to load ir-*-decoder modules, but it does not wait or cancel this work on module unloading. rc-core should use request_module_nowait() instead, because it anyway cannot load modules synchronously or cancel/wait pending work on unloading, because this leads to deadlock on modules_mutex between several "modprobe" processes. Signed-off-by: Konstantin Khlebnikov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-raw.c | 17 +---------------- drivers/media/rc/rc-core-priv.h | 16 ++++++++-------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index 97dc8d13b06b..17c94be9f24c 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -31,11 +31,6 @@ static DEFINE_MUTEX(ir_raw_handler_lock); static LIST_HEAD(ir_raw_handler_list); static u64 available_protocols; -#ifdef MODULE -/* Used to load the decoders */ -static struct work_struct wq_load; -#endif - static int ir_raw_event_thread(void *data) { struct ir_raw_event ev; @@ -347,8 +342,7 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) } EXPORT_SYMBOL(ir_raw_handler_unregister); -#ifdef MODULE -static void init_decoders(struct work_struct *work) +void ir_raw_init(void) { /* Load the decoder modules */ @@ -365,12 +359,3 @@ static void init_decoders(struct work_struct *work) it is needed to change the CONFIG_MODULE test at rc-core.h */ } -#endif - -void ir_raw_init(void) -{ -#ifdef MODULE - INIT_WORK(&wq_load, init_decoders); - schedule_work(&wq_load); -#endif -} diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 96f0a8bb39ea..5d87287ed372 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -165,56 +165,56 @@ void ir_raw_init(void); /* from ir-nec-decoder.c */ #ifdef CONFIG_IR_NEC_DECODER_MODULE -#define load_nec_decode() request_module("ir-nec-decoder") +#define load_nec_decode() request_module_nowait("ir-nec-decoder") #else static inline void load_nec_decode(void) { } #endif /* from ir-rc5-decoder.c */ #ifdef CONFIG_IR_RC5_DECODER_MODULE -#define load_rc5_decode() request_module("ir-rc5-decoder") +#define load_rc5_decode() request_module_nowait("ir-rc5-decoder") #else static inline void load_rc5_decode(void) { } #endif /* from ir-rc6-decoder.c */ #ifdef CONFIG_IR_RC6_DECODER_MODULE -#define load_rc6_decode() request_module("ir-rc6-decoder") +#define load_rc6_decode() request_module_nowait("ir-rc6-decoder") #else static inline void load_rc6_decode(void) { } #endif /* from ir-jvc-decoder.c */ #ifdef CONFIG_IR_JVC_DECODER_MODULE -#define load_jvc_decode() request_module("ir-jvc-decoder") +#define load_jvc_decode() request_module_nowait("ir-jvc-decoder") #else static inline void load_jvc_decode(void) { } #endif /* from ir-sony-decoder.c */ #ifdef CONFIG_IR_SONY_DECODER_MODULE -#define load_sony_decode() request_module("ir-sony-decoder") +#define load_sony_decode() request_module_nowait("ir-sony-decoder") #else static inline void load_sony_decode(void) { } #endif /* from ir-sanyo-decoder.c */ #ifdef CONFIG_IR_SANYO_DECODER_MODULE -#define load_sanyo_decode() request_module("ir-sanyo-decoder") +#define load_sanyo_decode() request_module_nowait("ir-sanyo-decoder") #else static inline void load_sanyo_decode(void) { } #endif /* from ir-mce_kbd-decoder.c */ #ifdef CONFIG_IR_MCE_KBD_DECODER_MODULE -#define load_mce_kbd_decode() request_module("ir-mce_kbd-decoder") +#define load_mce_kbd_decode() request_module_nowait("ir-mce_kbd-decoder") #else static inline void load_mce_kbd_decode(void) { } #endif /* from ir-lirc-codec.c */ #ifdef CONFIG_IR_LIRC_CODEC_MODULE -#define load_lirc_codec() request_module("ir-lirc-codec") +#define load_lirc_codec() request_module_nowait("ir-lirc-codec") #else static inline void load_lirc_codec(void) { } #endif From 77bdcfe5484fef0da899ec8e74b08dd21b031f66 Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Mon, 17 Dec 2012 22:37:51 +0800 Subject: [PATCH 0442/4607] kconfig:lxdialog: remove duplicate code dialog.h has two line the same below: extern char dialog_input_result[]; This patch remove one of them. Signed-off-by: Wang YanQing Reviewed-by: "Yann E. MORIN" Tested-by: "Yann E. MORIN" Signed-off-by: "Yann E. MORIN" --- scripts/kconfig/lxdialog/dialog.h | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/kconfig/lxdialog/dialog.h b/scripts/kconfig/lxdialog/dialog.h index ee17a5264d5b..307022a8beef 100644 --- a/scripts/kconfig/lxdialog/dialog.h +++ b/scripts/kconfig/lxdialog/dialog.h @@ -221,7 +221,6 @@ int dialog_menu(const char *title, const char *prompt, const void *selected, int *s_scroll); int dialog_checklist(const char *title, const char *prompt, int height, int width, int list_height); -extern char dialog_input_result[]; int dialog_inputbox(const char *title, const char *prompt, int height, int width, const char *init); From f698957aeaf3a711c2aa630a845b43426c02f339 Mon Sep 17 00:00:00 2001 From: Libin Yang Date: Sat, 15 Dec 2012 05:57:50 -0300 Subject: [PATCH 0443/4607] [media] marvell-ccic: use internal variable replace global frame stats variable This patch replaces the global frame stats variables by using internal variables in mcam_camera structure. Signed-off-by: Albert Wang Signed-off-by: Libin Yang Acked-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/marvell-ccic/mcam-core.c | 30 ++++++++----------- .../media/platform/marvell-ccic/mcam-core.h | 9 ++++++ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index ce2b7b4788d6..7012913f34a2 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -30,13 +30,6 @@ #include "mcam-core.h" -/* - * Basic frame stats - to be deleted shortly - */ -static int frames; -static int singles; -static int delivered; - #ifdef MCAM_MODE_VMALLOC /* * Internal DMA buffer management. Since the controller cannot do S/G I/O, @@ -367,10 +360,10 @@ static void mcam_frame_tasklet(unsigned long data) if (!test_bit(bufno, &cam->flags)) continue; if (list_empty(&cam->buffers)) { - singles++; + cam->frame_state.singles++; break; /* Leave it valid, hope for better later */ } - delivered++; + cam->frame_state.delivered++; clear_bit(bufno, &cam->flags); buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue); @@ -452,7 +445,7 @@ static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame) mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0)); set_bit(CF_SINGLE_BUFFER, &cam->flags); - singles++; + cam->frame_state.singles++; return; } /* @@ -485,7 +478,7 @@ static void mcam_dma_contig_done(struct mcam_camera *cam, int frame) struct mcam_vb_buffer *buf = cam->vb_bufs[frame]; if (!test_bit(CF_SINGLE_BUFFER, &cam->flags)) { - delivered++; + cam->frame_state.delivered++; mcam_buffer_done(cam, frame, &buf->vb_buf); } mcam_set_contig_buffer(cam, frame); @@ -578,13 +571,13 @@ static void mcam_dma_sg_done(struct mcam_camera *cam, int frame) */ } else { set_bit(CF_SG_RESTART, &cam->flags); - singles++; + cam->frame_state.singles++; cam->vb_bufs[0] = NULL; } /* * Now we can give the completed frame back to user space. */ - delivered++; + cam->frame_state.delivered++; mcam_buffer_done(cam, frame, &buf->vb_buf); } @@ -1545,7 +1538,9 @@ static int mcam_v4l_open(struct file *filp) filp->private_data = cam; - frames = singles = delivered = 0; + cam->frame_state.frames = 0; + cam->frame_state.singles = 0; + cam->frame_state.delivered = 0; mutex_lock(&cam->s_mutex); if (cam->users == 0) { ret = mcam_setup_vb2(cam); @@ -1566,8 +1561,9 @@ static int mcam_v4l_release(struct file *filp) { struct mcam_camera *cam = filp->private_data; - cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", frames, - singles, delivered); + cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", + cam->frame_state.frames, cam->frame_state.singles, + cam->frame_state.delivered); mutex_lock(&cam->s_mutex); (cam->users)--; if (cam->users == 0) { @@ -1660,7 +1656,7 @@ static void mcam_frame_complete(struct mcam_camera *cam, int frame) clear_bit(CF_DMA_ACTIVE, &cam->flags); cam->next_buf = frame; cam->buf_seq[frame] = ++(cam->sequence); - frames++; + cam->frame_state.frames++; /* * "This should never happen" */ diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index bd6acba9fb37..5e802c6b398e 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -73,6 +73,14 @@ static inline int mcam_buffer_mode_supported(enum mcam_buffer_mode mode) } } +/* + * Basic frame states + */ +struct mcam_frame_state { + unsigned int frames; + unsigned int singles; + unsigned int delivered; +}; /* * A description of one of our devices. @@ -108,6 +116,7 @@ struct mcam_camera { unsigned long flags; /* Buffer status, mainly (dev_lock) */ int users; /* How many open FDs */ + struct mcam_frame_state frame_state; /* Frame state counter */ /* * Subsystem structures. */ From e7c953d280cea9a79018ae36e2bc7cedc3678de3 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Sat, 15 Dec 2012 19:11:28 -0300 Subject: [PATCH 0444/4607] [media] drxd: allow functional gate control after, attach Previously, gate control didn't work until drxd_init() execution. Migrate necessary set of commands in drxd_attach to allow gate control to be used by tuner which are accessible through i2c gate. Reported-by: frederic.mantegazza@gbiloba.org Signed-off-by: Patrice Chotard Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/drxd_hard.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index e71cc60851e7..487c53b69bf3 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -2980,6 +2980,10 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config, sizeof(struct dvb_frontend_ops)); state->frontend.demodulator_priv = state; ConfigureMPEGOutput(state, 0); + /* add few initialization to allow gate control */ + CDRXD(state, state->config.IF ? state->config.IF : 36000000); + InitHI(state); + return &state->frontend; error: From 36a495a336c3fbbb2f4eeed2a94ab6d5be19d186 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Sat, 15 Dec 2012 19:11:43 -0300 Subject: [PATCH 0445/4607] [media] ngene: separate demodulator and tuner attach Previously, demodulator and tuner attach was done in the demod_attach callback. Migrate the tuner part in the tuner_attach callback in ngene_info to do thing in right place. Signed-off-by: Patrice Chotard Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ngene/ngene-cards.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index b38bce529566..2a4895b0706b 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -327,6 +327,14 @@ static int demod_attach_drxd(struct ngene_channel *chan) pr_err("No DRXD found!\n"); return -ENODEV; } + return 0; +} + +static int tuner_attach_dtt7520x(struct ngene_channel *chan) +{ + struct drxd_config *feconf; + + feconf = chan->dev->card_info->fe_config[chan->number]; if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address, &chan->i2c_adapter, From e19bc863f1f45223a85935f070177eda6b422f8f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 17 Dec 2012 04:52:48 -0300 Subject: [PATCH 0446/4607] [media] omap3isp: ispqueue: Fix uninitialized variable compiler warnings drivers/media/platform/omap3isp/ispqueue.c:399:18: warning: 'pa' may be used uninitialized in this function [-Wuninitialized] This is a false positive but the compiler has no way to know about it, so initialize the variable to 0. drivers/media/platform/omap3isp/ispqueue.c:445:6: warning: 'vm_page_prot' may be used uninitialized in this function [-Wuninitialized] This is a false positive and the compiler should know better. Use uninitialized_var(). Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/ispqueue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c index 6599963cdd9b..e15f01342058 100644 --- a/drivers/media/platform/omap3isp/ispqueue.c +++ b/drivers/media/platform/omap3isp/ispqueue.c @@ -366,7 +366,7 @@ static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf) unsigned long this_pfn; unsigned long start; unsigned long end; - dma_addr_t pa; + dma_addr_t pa = 0; int ret = -EFAULT; start = buf->vbuf.m.userptr; @@ -419,7 +419,7 @@ done: static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf) { struct vm_area_struct *vma; - pgprot_t vm_page_prot; + pgprot_t uninitialized_var(vm_page_prot); unsigned long start; unsigned long end; int ret = -EFAULT; From 1ca6ae8de8a563f69eebe114d023855b4f0bcb1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20T=C3=B6rnblom?= Date: Mon, 17 Dec 2012 08:53:54 -0300 Subject: [PATCH 0447/4607] [media] bttv: avoid flooding the kernel log when i2c debugging is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the bttv driver is running without i2c_debug being set, the kernel log is being flooded with the string ">". This string is really a part of a debug message that is logged using several substrings protected by a conditional check. This patch adds the same conditional check to the leaked substring. Signed-off-by: John Törnblom Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/bttv-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/bt8xx/bttv-i2c.c b/drivers/media/pci/bt8xx/bttv-i2c.c index 580c8e682392..da400dbe4a10 100644 --- a/drivers/media/pci/bt8xx/bttv-i2c.c +++ b/drivers/media/pci/bt8xx/bttv-i2c.c @@ -173,7 +173,7 @@ bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last) if (i2c_debug) pr_cont(" %02x", msg->buf[cnt]); } - if (!(xmit & BT878_I2C_NOSTOP)) + if (i2c_debug && !(xmit & BT878_I2C_NOSTOP)) pr_cont(">\n"); return msg->len; From 5344fe6e041c1ff867cde87d8088abf845645655 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 22 Dec 2012 07:48:58 -0300 Subject: [PATCH 0448/4607] [media] Improve media Kconfig menu I've always found it very confusing that the "Media ancillary drivers (tuners, sensors, i2c, frontends)" comment came after the "Autoselect" option. This patch moves it up and changes the "Autoselect" text to correspond more closely to the "Media ancillary drivers" comment. It also fixes two typos. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 4d9b4c21c199..84d85b921d6d 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -164,17 +164,20 @@ source "drivers/media/firewire/Kconfig" # Common driver options source "drivers/media/common/Kconfig" +comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" + # # Ancillary drivers (tuners, i2c, frontends) # config MEDIA_SUBDRV_AUTOSELECT - bool "Autoselect tuners and i2c modules to build" + bool "Autoselect ancillary drivers (tuners, sensors, i2c, frontends)" depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_CAMERA_SUPPORT default y help - By default, a media driver auto-selects all possible i2c - devices that are used by any of the supported devices. + By default, a media driver auto-selects all possible ancillary + devices such as tuners, sensors, video encoders/decoders and + frontends, that are used by any of the supported devices. This is generally the right thing to do, except when there are strict constraints with regards to the kernel size, @@ -183,12 +186,10 @@ config MEDIA_SUBDRV_AUTOSELECT Use this option with care, as deselecting ancillary drivers which are, in fact, necessary will result in the lack of the needed functionality for your device (it may not tune or may not have - the need demodulers). + the needed demodulators). If unsure say Y. -comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" - source "drivers/media/i2c/Kconfig" source "drivers/media/tuners/Kconfig" source "drivers/media/dvb-frontends/Kconfig" From 3724dde9c8c9f55c31ce8c7f8f2645733d6a59ac Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:05 -0300 Subject: [PATCH 0449/4607] [media] cx231xx: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-cards.c | 2 +- drivers/media/usb/cx231xx/cx231xx-video.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 94b573ad96d5..8d529565f163 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -736,7 +736,7 @@ static void cx231xx_sleep_s5h1432(struct cx231xx *dev) static inline void cx231xx_set_model(struct cx231xx *dev) { - memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board)); + dev->board = cx231xx_boards[dev->model]; } /* Since cx231xx_pre_card_setup() requires a proper dev->model, diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 239cb913be5c..93dfc182ec51 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -2627,8 +2627,7 @@ int cx231xx_register_analog_devices(struct cx231xx *dev) dev->name, video_device_node_name(dev->vdev)); /* Initialize VBI template */ - memcpy(&cx231xx_vbi_template, &cx231xx_video_template, - sizeof(cx231xx_vbi_template)); + cx231xx_vbi_template = cx231xx_video_template; strcpy(cx231xx_vbi_template.name, "cx231xx-vbi"); /* Allocate and fill vbi video_device struct */ From 8fe392b8fdb93ced86e1661bff91af95ee234cf9 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:06 -0300 Subject: [PATCH 0450/4607] [media] usbvision: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/usbvision/usbvision-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/usb/usbvision/usbvision-i2c.c b/drivers/media/usb/usbvision/usbvision-i2c.c index 89fec029e924..ba262a32bd3a 100644 --- a/drivers/media/usb/usbvision/usbvision-i2c.c +++ b/drivers/media/usb/usbvision/usbvision-i2c.c @@ -189,8 +189,7 @@ int usbvision_i2c_register(struct usb_usbvision *usbvision) if (usbvision->registered_i2c) return 0; - memcpy(&usbvision->i2c_adap, &i2c_adap_template, - sizeof(struct i2c_adapter)); + usbvision->i2c_adap = i2c_adap_template; sprintf(usbvision->i2c_adap.name, "%s-%d-%s", i2c_adap_template.name, usbvision->dev->bus->busnum, usbvision->dev->devpath); From 5869bb39f8e1f671746dcb5f070e3b1c38b23e5c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:07 -0300 Subject: [PATCH 0451/4607] [media] sn9c102: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/sn9c102/sn9c102_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c index 6bda81aebf87..c957e9aa6077 100644 --- a/drivers/media/usb/sn9c102/sn9c102_core.c +++ b/drivers/media/usb/sn9c102/sn9c102_core.c @@ -2827,7 +2827,7 @@ sn9c102_vidioc_querybuf(struct sn9c102_device* cam, void __user * arg) b.index >= cam->nbuffers || cam->io != IO_MMAP) return -EINVAL; - memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); + b = cam->frame[b.index].buf; if (cam->frame[b.index].vma_use_count) b.flags |= V4L2_BUF_FLAG_MAPPED; @@ -2930,7 +2930,7 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, f->state = F_UNUSED; - memcpy(&b, &f->buf, sizeof(b)); + b = f->buf; if (f->vma_use_count) b.flags |= V4L2_BUF_FLAG_MAPPED; From 5c2edefed74fb29b35634bce1b4c38ce1fdb2ce6 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:08 -0300 Subject: [PATCH 0452/4607] [media] pwc: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc-if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 21c15233c6ae..5ec15cb1ed26 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1008,7 +1008,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id } /* Init video_device structure */ - memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template)); + pdev->vdev = pwc_template; strcpy(pdev->vdev.name, name); pdev->vdev.queue = &pdev->vb_queue; pdev->vdev.queue->lock = &pdev->vb_queue_lock; From 5338c16905ed7e8145861e1dacdd4eadce18f2b9 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:09 -0300 Subject: [PATCH 0453/4607] [media] pvrusb2: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pvrusb2/pvrusb2-encoder.c | 3 +-- drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c | 4 ++-- drivers/media/usb/pvrusb2/pvrusb2-v4l2.c | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c index e046fdaec5ae..f7702aeeda3f 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c @@ -422,8 +422,7 @@ int pvr2_encoder_adjust(struct pvr2_hdw *hdw) pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Error from cx2341x module code=%d",ret); } else { - memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state, - sizeof(struct cx2341x_mpeg_params)); + hdw->enc_cur_state = hdw->enc_ctl_state; hdw->enc_cur_valid = !0; } return ret; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c index 9ab596c78a4e..b5e929f1bf82 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c @@ -649,8 +649,8 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) } // Configure the adapter and set up everything else related to it. - memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap)); - memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo)); + hdw->i2c_adap = pvr2_i2c_adap_template; + hdw->i2c_algo = pvr2_i2c_algo_template; strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name)); hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev; hdw->i2c_adap.algo = &hdw->i2c_algo; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index 6930676051e7..34c3b6e80e86 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -1339,7 +1339,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, return; } - memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); + dip->devbase = vdev_template; dip->devbase.release = pvr2_video_device_release; dip->devbase.ioctl_ops = &pvr2_ioctl_ops; { From d486b94b2636ce169f7f2bb1a4a7973843cc72e3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:10 -0300 Subject: [PATCH 0454/4607] [media] hdpvr: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hdpvr/hdpvr-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index 031cf024304c..6c5054f3f674 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -217,8 +217,7 @@ int hdpvr_register_i2c_adapter(struct hdpvr_device *dev) hdpvr_activate_ir(dev); - memcpy(&dev->i2c_adapter, &hdpvr_i2c_adapter_template, - sizeof(struct i2c_adapter)); + dev->i2c_adapter = hdpvr_i2c_adapter_template; dev->i2c_adapter.dev.parent = &dev->udev->dev; i2c_set_adapdata(&dev->i2c_adapter, dev); From 8ba6220fe3b1ffbb3106faae48eee3bcbd27b553 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:11 -0300 Subject: [PATCH 0455/4607] [media] cx25840: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/cx25840/cx25840-ir.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c index 38ce76ed1924..9ae977b5983a 100644 --- a/drivers/media/i2c/cx25840/cx25840-ir.c +++ b/drivers/media/i2c/cx25840/cx25840-ir.c @@ -1251,13 +1251,11 @@ int cx25840_ir_probe(struct v4l2_subdev *sd) cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0); mutex_init(&ir_state->rx_params_lock); - memcpy(&default_params, &default_rx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_rx_params; v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); mutex_init(&ir_state->tx_params_lock); - memcpy(&default_params, &default_tx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_tx_params; v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); return 0; From 37320d7bc0180979d49de21b90f30a97f57b3ee1 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:12 -0300 Subject: [PATCH 0456/4607] [media] zr36067: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/zoran/zoran_card.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c index fffc54b452c8..cea325d3e530 100644 --- a/drivers/media/pci/zoran/zoran_card.c +++ b/drivers/media/pci/zoran/zoran_card.c @@ -708,8 +708,7 @@ static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = { static int zoran_register_i2c (struct zoran *zr) { - memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template, - sizeof(struct i2c_algo_bit_data)); + zr->i2c_algo = zoran_i2c_bit_data_template; zr->i2c_algo.data = zr; strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), sizeof(zr->i2c_adapter.name)); From d3a950918446e201f0f9048995badc4fe8ba4e20 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:13 -0300 Subject: [PATCH 0457/4607] [media] dvb-usb/friio-fe: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/friio-fe.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/dvb-usb/friio-fe.c b/drivers/media/usb/dvb-usb/friio-fe.c index 90a70c66a96e..d56f927fc31a 100644 --- a/drivers/media/usb/dvb-usb/friio-fe.c +++ b/drivers/media/usb/dvb-usb/friio-fe.c @@ -421,11 +421,10 @@ struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d) /* setup the state */ state->i2c = &d->i2c_adap; - memcpy(&state->config, &friio_fe_config, sizeof(friio_fe_config)); + state->config = friio_fe_config; /* create dvb_frontend */ - memcpy(&state->frontend.ops, &jdvbt90502_ops, - sizeof(jdvbt90502_ops)); + state->frontend.ops = jdvbt90502_ops; state->frontend.demodulator_priv = state; if (jdvbt90502_init(&state->frontend) < 0) From f01e0ffd01eb0a8c6df71ac80234354ed716b488 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:14 -0300 Subject: [PATCH 0458/4607] [media] au0828: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/au0828/au0828-cards.c | 2 +- drivers/media/usb/au0828/au0828-i2c.c | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 88e35dfa33d4..dd32decb237d 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -193,7 +193,7 @@ void au0828_card_setup(struct au0828_dev *dev) dprintk(1, "%s()\n", __func__); - memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board)); + dev->board = au0828_boards[dev->boardnr]; if (dev->i2c_rc == 0) { dev->i2c_client.addr = 0xa0 >> 1; diff --git a/drivers/media/usb/au0828/au0828-i2c.c b/drivers/media/usb/au0828/au0828-i2c.c index 20d69b565255..17ec3651b10e 100644 --- a/drivers/media/usb/au0828/au0828-i2c.c +++ b/drivers/media/usb/au0828/au0828-i2c.c @@ -364,12 +364,9 @@ int au0828_i2c_register(struct au0828_dev *dev) { dprintk(1, "%s()\n", __func__); - memcpy(&dev->i2c_adap, &au0828_i2c_adap_template, - sizeof(dev->i2c_adap)); - memcpy(&dev->i2c_algo, &au0828_i2c_algo_template, - sizeof(dev->i2c_algo)); - memcpy(&dev->i2c_client, &au0828_i2c_client_template, - sizeof(dev->i2c_client)); + dev->i2c_adap = au0828_i2c_adap_template; + dev->i2c_algo = au0828_i2c_algo_template; + dev->i2c_client = au0828_i2c_client_template; dev->i2c_adap.dev.parent = &dev->usbdev->dev; From 36628731ec9ef46393d25d2fbdac40cc70c4d27a Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:15 -0300 Subject: [PATCH 0459/4607] [media] tuners/xc4000: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc4000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index 5c0fd787cc8f..2018befabb5a 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -1066,7 +1066,7 @@ check_device: goto fail; } - memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); + priv->cur_fw = new_fw; /* * By setting BASE in cur_fw.type only after successfully loading all From 03c420010f4c5ded38bd0fc909ccadc25c82d080 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:16 -0300 Subject: [PATCH 0460/4607] [media] tuners/xc2028: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tuner-xc2028.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 7bcb6b0ff1df..09451737c77e 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -870,7 +870,7 @@ check_device: } read_not_reliable: - memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); + priv->cur_fw = new_fw; /* * By setting BASE in cur_fw.type only after successfully loading all From 7f05b24536f068c0a5072929fb6c0fb2099d273c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:17 -0300 Subject: [PATCH 0461/4607] [media] tuners/tda18271: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/tda18271-maps.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/tuners/tda18271-maps.c b/drivers/media/tuners/tda18271-maps.c index fb881c667c94..b62e925f643f 100644 --- a/drivers/media/tuners/tda18271-maps.c +++ b/drivers/media/tuners/tda18271-maps.c @@ -1290,13 +1290,11 @@ int tda18271_assign_map_layout(struct dvb_frontend *fe) switch (priv->id) { case TDA18271HDC1: priv->maps = &tda18271c1_map_layout; - memcpy(&priv->std, &tda18271c1_std_map, - sizeof(struct tda18271_std_map)); + priv->std = tda18271c1_std_map; break; case TDA18271HDC2: priv->maps = &tda18271c2_map_layout; - memcpy(&priv->std, &tda18271c2_std_map, - sizeof(struct tda18271_std_map)); + priv->std = tda18271c2_std_map; break; default: ret = -EINVAL; From 01a5cbebce7bca910f50dff19b05177c2c8a8a76 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:18 -0300 Subject: [PATCH 0462/4607] [media] ivtv: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ivtv/ivtv-i2c.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c index 46e262becb67..a1811054fde4 100644 --- a/drivers/media/pci/ivtv/ivtv-i2c.c +++ b/drivers/media/pci/ivtv/ivtv-i2c.c @@ -719,13 +719,10 @@ int init_ivtv_i2c(struct ivtv *itv) return -ENODEV; } if (itv->options.newi2c > 0) { - memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template, - sizeof(struct i2c_adapter)); + itv->i2c_adap = ivtv_i2c_adap_hw_template; } else { - memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template, - sizeof(struct i2c_adapter)); - memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template, - sizeof(struct i2c_algo_bit_data)); + itv->i2c_adap = ivtv_i2c_adap_template; + itv->i2c_algo = ivtv_i2c_algo_template; } itv->i2c_algo.udelay = itv->options.i2c_clock_period / 2; itv->i2c_algo.data = itv; @@ -735,8 +732,7 @@ int init_ivtv_i2c(struct ivtv *itv) itv->instance); i2c_set_adapdata(&itv->i2c_adap, &itv->v4l2_dev); - memcpy(&itv->i2c_client, &ivtv_i2c_client_template, - sizeof(struct i2c_client)); + itv->i2c_client = ivtv_i2c_client_template; itv->i2c_client.adapter = &itv->i2c_adap; itv->i2c_adap.dev.parent = &itv->pdev->dev; From b52377475ae9dca21bcb1ae8ec0936fbae8595b2 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:19 -0300 Subject: [PATCH 0463/4607] [media] cx88: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88-cards.c | 2 +- drivers/media/pci/cx88/cx88-i2c.c | 3 +-- drivers/media/pci/cx88/cx88-vp3054-i2c.c | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c index 0c255248cbcd..e2e0b8faf7a4 100644 --- a/drivers/media/pci/cx88/cx88-cards.c +++ b/drivers/media/pci/cx88/cx88-cards.c @@ -3743,7 +3743,7 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) cx88_card_list(core, pci); } - memcpy(&core->board, &cx88_boards[core->boardnr], sizeof(core->board)); + core->board = cx88_boards[core->boardnr]; if (!core->board.num_frontends && (core->board.mpeg & CX88_MPEG_DVB)) core->board.num_frontends = 1; diff --git a/drivers/media/pci/cx88/cx88-i2c.c b/drivers/media/pci/cx88/cx88-i2c.c index de0f1af74e41..cf2d69615838 100644 --- a/drivers/media/pci/cx88/cx88-i2c.c +++ b/drivers/media/pci/cx88/cx88-i2c.c @@ -139,8 +139,7 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) if (i2c_udelay<5) i2c_udelay=5; - memcpy(&core->i2c_algo, &cx8800_i2c_algo_template, - sizeof(core->i2c_algo)); + core->i2c_algo = cx8800_i2c_algo_template; core->i2c_adap.dev.parent = &pci->dev; diff --git a/drivers/media/pci/cx88/cx88-vp3054-i2c.c b/drivers/media/pci/cx88/cx88-vp3054-i2c.c index d77f8ecab9d7..deede6e25d94 100644 --- a/drivers/media/pci/cx88/cx88-vp3054-i2c.c +++ b/drivers/media/pci/cx88/cx88-vp3054-i2c.c @@ -118,8 +118,7 @@ int vp3054_i2c_probe(struct cx8802_dev *dev) return -ENOMEM; dev->vp3054 = vp3054_i2c; - memcpy(&vp3054_i2c->algo, &vp3054_i2c_algo_template, - sizeof(vp3054_i2c->algo)); + vp3054_i2c->algo = vp3054_i2c_algo_template; vp3054_i2c->adap.dev.parent = &dev->pci->dev; strlcpy(vp3054_i2c->adap.name, core->name, From 3618acab2ccefe292a3b1a1d7295f1368023b71a Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:20 -0300 Subject: [PATCH 0464/4607] [media] cx23885: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Reviewed-by: Andy Walls Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx23885/cx23885-video.c | 3 +-- drivers/media/pci/cx23885/cx23888-ir.c | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c index 0e80ba4ca4dd..5991bc8dc158 100644 --- a/drivers/media/pci/cx23885/cx23885-video.c +++ b/drivers/media/pci/cx23885/cx23885-video.c @@ -1819,8 +1819,7 @@ int cx23885_video_register(struct cx23885_dev *dev) spin_lock_init(&dev->slock); /* Initialize VBI template */ - memcpy(&cx23885_vbi_template, &cx23885_video_template, - sizeof(cx23885_vbi_template)); + cx23885_vbi_template = cx23885_video_template; strcpy(cx23885_vbi_template.name, "cx23885-vbi"); dev->tvnorm = cx23885_video_template.current_norm; diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c index c4bd1e95d33f..d51eed051d59 100644 --- a/drivers/media/pci/cx23885/cx23888-ir.c +++ b/drivers/media/pci/cx23885/cx23888-ir.c @@ -1237,13 +1237,11 @@ int cx23888_ir_probe(struct cx23885_dev *dev) cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0); mutex_init(&state->rx_params_lock); - memcpy(&default_params, &default_rx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_rx_params; v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); mutex_init(&state->tx_params_lock); - memcpy(&default_params, &default_tx_params, - sizeof(struct v4l2_subdev_ir_parameters)); + default_params = default_tx_params; v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); } else { kfifo_free(&state->rx_kfifo); From 2e814af502e0fc5983cbb96fc8c0c64fe49a9340 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:21 -0300 Subject: [PATCH 0465/4607] [media] cx18: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-i2c.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/pci/cx18/cx18-i2c.c b/drivers/media/pci/cx18/cx18-i2c.c index 4908eb7bcf6c..d61ac6393e7e 100644 --- a/drivers/media/pci/cx18/cx18-i2c.c +++ b/drivers/media/pci/cx18/cx18-i2c.c @@ -240,15 +240,13 @@ int init_cx18_i2c(struct cx18 *cx) for (i = 0; i < 2; i++) { /* Setup algorithm for adapter */ - memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template, - sizeof(struct i2c_algo_bit_data)); + cx->i2c_algo[i] = cx18_i2c_algo_template; cx->i2c_algo_cb_data[i].cx = cx; cx->i2c_algo_cb_data[i].bus_index = i; cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i]; /* Setup adapter */ - memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, - sizeof(struct i2c_adapter)); + cx->i2c_adap[i] = cx18_i2c_adap_template; cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name), " #%d-%d", cx->instance, i); From b4b1d04006540ebeb5c85222292cdcb2f95f217b Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:22 -0300 Subject: [PATCH 0466/4607] [media] bttv: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/bttv-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/pci/bt8xx/bttv-i2c.c b/drivers/media/pci/bt8xx/bttv-i2c.c index da400dbe4a10..99865f531199 100644 --- a/drivers/media/pci/bt8xx/bttv-i2c.c +++ b/drivers/media/pci/bt8xx/bttv-i2c.c @@ -366,8 +366,7 @@ int __devinit init_bttv_i2c(struct bttv *btv) strlcpy(btv->c.i2c_adap.name, "bttv", sizeof(btv->c.i2c_adap.name)); - memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template, - sizeof(bttv_i2c_algo_bit_template)); + btv->i2c_algo = bttv_i2c_algo_bit_template; btv->i2c_algo.udelay = i2c_udelay; btv->i2c_algo.data = btv; btv->c.i2c_adap.algo_data = &btv->i2c_algo; From b9b1b3a8f7b76035140912bc9e3a325e58fc6d58 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:23 -0300 Subject: [PATCH 0467/4607] [media] dvb-core: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 9b4a47c104a1..dd35fa972067 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -2255,7 +2255,7 @@ static int dvb_frontend_ioctl_legacy(struct file *file, printk("%s switch command: 0x%04lx\n", __func__, swcmd); do_gettimeofday(&nexttime); if (dvb_frontend_debug) - memcpy(&tv[0], &nexttime, sizeof(struct timeval)); + tv[0] = nexttime; /* before sending a command, initialize by sending * a 32ms 18V to the switch */ From ee45ddc1e03afc221afad273503b6c2fc0683008 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:24 -0300 Subject: [PATCH 0468/4607] [media] dvb-frontends: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/cx24116.c | 2 +- drivers/media/dvb-frontends/drxd_hard.c | 5 ++--- drivers/media/dvb-frontends/stv0299.c | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c index b48879186537..2916d7c74a1d 100644 --- a/drivers/media/dvb-frontends/cx24116.c +++ b/drivers/media/dvb-frontends/cx24116.c @@ -819,7 +819,7 @@ static int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) static void cx24116_clone_params(struct dvb_frontend *fe) { struct cx24116_state *state = fe->demodulator_priv; - memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur)); + state->dcur = state->dnxt; } /* Wait for LNB */ diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index 487c53b69bf3..9a2134792cfa 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -2965,7 +2965,7 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config, return NULL; memset(state, 0, sizeof(*state)); - memcpy(&state->ops, &drxd_ops, sizeof(struct dvb_frontend_ops)); + state->ops = drxd_ops; state->dev = dev; state->config = *config; state->i2c = i2c; @@ -2976,8 +2976,7 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config, if (Read16(state, 0, 0, 0) < 0) goto error; - memcpy(&state->frontend.ops, &drxd_ops, - sizeof(struct dvb_frontend_ops)); + state->frontend.ops = drxd_ops; state->frontend.demodulator_priv = state; ConfigureMPEGOutput(state, 0); /* add few initialization to allow gate control */ diff --git a/drivers/media/dvb-frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c index 92a6075cd82f..b57ecf42e75a 100644 --- a/drivers/media/dvb-frontends/stv0299.c +++ b/drivers/media/dvb-frontends/stv0299.c @@ -420,7 +420,7 @@ static int stv0299_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long do_gettimeofday (&nexttime); if (debug_legacy_dish_switch) - memcpy (&tv[0], &nexttime, sizeof (struct timeval)); + tv[0] = nexttime; stv0299_writeregI (state, 0x0c, reg0x0c | 0x50); /* set LNB to 18V */ dvb_frontend_sleep_until(&nexttime, 32000); From 2b9b325d73dc092e1b888c381b98dde57d394194 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:25 -0300 Subject: [PATCH 0469/4607] [media] radio-wl1273: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-wl1273.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 9b0c9fa0beb8..6e55e93c0cf6 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -2084,8 +2084,7 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev) } /* V4L2 configuration */ - memcpy(&radio->videodev, &wl1273_viddev_template, - sizeof(wl1273_viddev_template)); + radio->videodev = wl1273_viddev_template; radio->videodev.v4l2_dev = &radio->v4l2dev; From c84401c200423e4855bc990d8460cc0c3a2bb664 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 23 Oct 2012 15:57:26 -0300 Subject: [PATCH 0470/4607] [media] wl128x: Replace memcpy with struct assignment This kind of memcpy() is error-prone. Its replacement with a struct assignment is prefered because it's type-safe and much easier to read. Found by coccinelle. Hand patched and reviewed. Tested by compilation only. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier struct_name; struct struct_name to; struct struct_name from; expression E; @@ -memcpy(&(to), &(from), E); +to = from; // Signed-off-by: Peter Senna Tschudin Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/wl128x/fmdrv_common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/radio/wl128x/fmdrv_common.c b/drivers/media/radio/wl128x/fmdrv_common.c index 602ef7ac8c24..a002234ed5de 100644 --- a/drivers/media/radio/wl128x/fmdrv_common.c +++ b/drivers/media/radio/wl128x/fmdrv_common.c @@ -1563,8 +1563,7 @@ int fmc_prepare(struct fmdev *fmdev) fmdev->irq_info.mask = FM_MAL_EVENT; /* Region info */ - memcpy(&fmdev->rx.region, ®ion_configs[default_radio_region], - sizeof(struct region_info)); + fmdev->rx.region = region_configs[default_radio_region]; fmdev->rx.mute_mode = FM_MUTE_OFF; fmdev->rx.rf_depend_mute = FM_RX_RF_DEPENDENT_MUTE_OFF; From 9a3323aef4e32585a76ace5f53973404e7e5afee Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Thu, 20 Dec 2012 15:11:18 -0300 Subject: [PATCH 0471/4607] [media] m2m-deinterlace: use correct check for kzalloc failure There is no point in PTR_ERR()ing a NULL pointer, use a real error instead. Signed-off-by: Sasha Levin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/m2m-deinterlace.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index ed77a645e992..6c4db9b98989 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -917,10 +917,8 @@ static int deinterlace_open(struct file *file) ctx->xt = kzalloc(sizeof(struct dma_async_tx_descriptor) + sizeof(struct data_chunk), GFP_KERNEL); if (!ctx->xt) { - int ret = PTR_ERR(ctx->xt); - kfree(ctx); - return ret; + return -ENOMEM; } ctx->colorspace = V4L2_COLORSPACE_REC709; From 10a5c9148ee6841004cb7e6f4b09022eba94c7be Mon Sep 17 00:00:00 2001 From: Eddi De Pieri Date: Sat, 22 Dec 2012 09:41:49 -0300 Subject: [PATCH 0472/4607] [media] it913x: add support for Avermedia A835B Add support for Avermedia A835B and variants. Signed-off-by: Eddi De Pieri Cc: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb-usb-ids.h | 4 ++++ drivers/media/usb/dvb-usb-v2/it913x.c | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index c6720f2991ac..7e1597dad568 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -234,6 +234,10 @@ #define USB_PID_AVERMEDIA_A815M 0x815a #define USB_PID_AVERMEDIA_A835 0xa835 #define USB_PID_AVERMEDIA_B835 0xb835 +#define USB_PID_AVERMEDIA_A835B_1835 0x1835 +#define USB_PID_AVERMEDIA_A835B_2835 0x2835 +#define USB_PID_AVERMEDIA_A835B_3835 0x3835 +#define USB_PID_AVERMEDIA_A835B_4835 0x4835 #define USB_PID_AVERMEDIA_1867 0x1867 #define USB_PID_AVERMEDIA_A867 0xa867 #define USB_PID_AVERMEDIA_TWINSTAR 0x0825 diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index fbc0a84465eb..744c9f9f7680 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -780,6 +780,18 @@ static const struct usb_device_id it913x_id_table[] = { { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9006, &it913x_properties, "ITE 9135(9006) Generic", RC_MAP_IT913X_V1) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_1835, + &it913x_properties, "Avermedia A835B(1835)", + RC_MAP_IT913X_V2) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_2835, + &it913x_properties, "Avermedia A835B(2835)", + RC_MAP_IT913X_V2) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_3835, + &it913x_properties, "Avermedia A835B(3835)", + RC_MAP_IT913X_V2) }, + { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835B_4835, + &it913x_properties, "Avermedia A835B(4835)", + RC_MAP_IT913X_V2) }, {} /* Terminating entry */ }; From c1965eae65f0db2eee574f72aab4e8b34ecf8f9c Mon Sep 17 00:00:00 2001 From: Konstantin Dimitrov Date: Sun, 23 Dec 2012 19:25:09 -0300 Subject: [PATCH 0473/4607] [media] ds3000: remove ts2020 tuner related code remove ts2020 tuner related code from ds3000 driver prepare ds3000 driver for using external tuner driver Signed-off-by: Konstantin Dimitrov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ds3000.c | 242 +++------------------------ drivers/media/dvb-frontends/ds3000.h | 8 +- 2 files changed, 27 insertions(+), 223 deletions(-) diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index 60a529e3833f..cd84fbd78a5b 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -1,8 +1,8 @@ /* - Montage Technology DS3000/TS2020 - DVBS/S2 Demodulator/Tuner driver - Copyright (C) 2009 Konstantin Dimitrov + Montage Technology DS3000 - DVBS/S2 Demodulator driver + Copyright (C) 2009-2012 Konstantin Dimitrov - Copyright (C) 2009 TurboSight.com + Copyright (C) 2009-2012 TurboSight.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,7 +42,6 @@ static int debug; #define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw" #define DS3000_SAMPLE_RATE 96000 /* in kHz */ -#define DS3000_XTAL_FREQ 27000 /* in kHz */ /* Register values to initialise the demod in DVB-S mode */ static u8 ds3000_dvbs_init_tab[] = { @@ -256,22 +255,14 @@ static int ds3000_writereg(struct ds3000_state *state, int reg, int data) return 0; } -static int ds3000_tuner_writereg(struct ds3000_state *state, int reg, int data) +static int ds3000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { - u8 buf[] = { reg, data }; - struct i2c_msg msg = { .addr = 0x60, - .flags = 0, .buf = buf, .len = 2 }; - int err; + struct ds3000_state *state = fe->demodulator_priv; - dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); - - ds3000_writereg(state, 0x03, 0x11); - err = i2c_transfer(state->i2c, &msg, 1); - if (err != 1) { - printk("%s: writereg error(err == %i, reg == 0x%02x," - " value == 0x%02x)\n", __func__, err, reg, data); - return -EREMOTEIO; - } + if (enable) + ds3000_writereg(state, 0x03, 0x12); + else + ds3000_writereg(state, 0x03, 0x02); return 0; } @@ -348,38 +339,6 @@ static int ds3000_readreg(struct ds3000_state *state, u8 reg) return b1[0]; } -static int ds3000_tuner_readreg(struct ds3000_state *state, u8 reg) -{ - int ret; - u8 b0[] = { reg }; - u8 b1[] = { 0 }; - struct i2c_msg msg[] = { - { - .addr = 0x60, - .flags = 0, - .buf = b0, - .len = 1 - }, { - .addr = 0x60, - .flags = I2C_M_RD, - .buf = b1, - .len = 1 - } - }; - - ds3000_writereg(state, 0x03, 0x12); - ret = i2c_transfer(state->i2c, msg, 2); - - if (ret != 2) { - printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); - return ret; - } - - dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); - - return b1[0]; -} - static int ds3000_load_firmware(struct dvb_frontend *fe, const struct firmware *fw); @@ -568,37 +527,6 @@ static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) return 0; } -/* read TS2020 signal strength */ -static int ds3000_read_signal_strength(struct dvb_frontend *fe, - u16 *signal_strength) -{ - struct ds3000_state *state = fe->demodulator_priv; - u16 sig_reading, sig_strength; - u8 rfgain, bbgain; - - dprintk("%s()\n", __func__); - - rfgain = ds3000_tuner_readreg(state, 0x3d) & 0x1f; - bbgain = ds3000_tuner_readreg(state, 0x21) & 0x1f; - - if (rfgain > 15) - rfgain = 15; - if (bbgain > 13) - bbgain = 13; - - sig_reading = rfgain * 2 + bbgain * 3; - - sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; - - /* cook the value to be suitable for szap-s2 human readable output */ - *signal_strength = sig_strength * 1000; - - dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, - sig_reading, *signal_strength); - - return 0; -} - /* calculate DS3000 snr value in dB */ static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr) { @@ -952,133 +880,17 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) int i; fe_status_t status; - u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; s32 offset_khz; - u16 value, ndiv; - u32 f3db; + u32 frequency; + u16 value; dprintk("%s() ", __func__); if (state->config->set_ts_params) state->config->set_ts_params(fe, 0); /* Tune */ - /* unknown */ - ds3000_tuner_writereg(state, 0x07, 0x02); - ds3000_tuner_writereg(state, 0x10, 0x00); - ds3000_tuner_writereg(state, 0x60, 0x79); - ds3000_tuner_writereg(state, 0x08, 0x01); - ds3000_tuner_writereg(state, 0x00, 0x01); - div4 = 0; - - /* calculate and set freq divider */ - if (c->frequency < 1146000) { - ds3000_tuner_writereg(state, 0x10, 0x11); - div4 = 1; - ndiv = ((c->frequency * (6 + 8) * 4) + - (DS3000_XTAL_FREQ / 2)) / - DS3000_XTAL_FREQ - 1024; - } else { - ds3000_tuner_writereg(state, 0x10, 0x01); - ndiv = ((c->frequency * (6 + 8) * 2) + - (DS3000_XTAL_FREQ / 2)) / - DS3000_XTAL_FREQ - 1024; - } - - ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8); - ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff); - - /* set pll */ - ds3000_tuner_writereg(state, 0x03, 0x06); - ds3000_tuner_writereg(state, 0x51, 0x0f); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x10); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - /* unknown */ - ds3000_tuner_writereg(state, 0x51, 0x17); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x08); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - value = ds3000_tuner_readreg(state, 0x3d); - value &= 0x0f; - if ((value > 4) && (value < 15)) { - value -= 3; - if (value < 4) - value = 4; - value = ((value << 3) | 0x01) & 0x79; - } - - ds3000_tuner_writereg(state, 0x60, value); - ds3000_tuner_writereg(state, 0x51, 0x17); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x08); - ds3000_tuner_writereg(state, 0x50, 0x00); - - /* set low-pass filter period */ - ds3000_tuner_writereg(state, 0x04, 0x2e); - ds3000_tuner_writereg(state, 0x51, 0x1b); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x04); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - f3db = ((c->symbol_rate / 1000) << 2) / 5 + 2000; - if ((c->symbol_rate / 1000) < 5000) - f3db += 3000; - if (f3db < 7000) - f3db = 7000; - if (f3db > 40000) - f3db = 40000; - - /* set low-pass filter baseband */ - value = ds3000_tuner_readreg(state, 0x26); - mlpf = 0x2e * 207 / ((value << 1) + 151); - mlpf_max = mlpf * 135 / 100; - mlpf_min = mlpf * 78 / 100; - if (mlpf_max > 63) - mlpf_max = 63; - - /* rounded to the closest integer */ - nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2)) - / (2766 * DS3000_XTAL_FREQ); - if (nlpf > 23) - nlpf = 23; - if (nlpf < 1) - nlpf = 1; - - /* rounded to the closest integer */ - mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + - (1000 * f3db / 2)) / (1000 * f3db); - - if (mlpf_new < mlpf_min) { - nlpf++; - mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + - (1000 * f3db / 2)) / (1000 * f3db); - } - - if (mlpf_new > mlpf_max) - mlpf_new = mlpf_max; - - ds3000_tuner_writereg(state, 0x04, mlpf_new); - ds3000_tuner_writereg(state, 0x06, nlpf); - ds3000_tuner_writereg(state, 0x51, 0x1b); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x04); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(5); - - /* unknown */ - ds3000_tuner_writereg(state, 0x51, 0x1e); - ds3000_tuner_writereg(state, 0x51, 0x1f); - ds3000_tuner_writereg(state, 0x50, 0x01); - ds3000_tuner_writereg(state, 0x50, 0x00); - msleep(60); - - offset_khz = (ndiv - ndiv % 2 + 1024) * DS3000_XTAL_FREQ - / (6 + 8) / (div4 + 1) / 2 - c->frequency; + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); /* ds3000 global reset */ ds3000_writereg(state, 0x07, 0x80); @@ -1186,7 +998,11 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) /* start ds3000 build-in uC */ ds3000_writereg(state, 0xb2, 0x00); - ds3000_set_carrier_offset(fe, offset_khz); + if (fe->ops.tuner_ops.get_frequency) { + fe->ops.tuner_ops.get_frequency(fe, &frequency); + offset_khz = frequency - c->frequency; + ds3000_set_carrier_offset(fe, offset_khz); + } for (i = 0; i < 30 ; i++) { ds3000_read_status(fe, &status); @@ -1237,10 +1053,6 @@ static int ds3000_initfe(struct dvb_frontend *fe) ds3000_writereg(state, 0x08, 0x01 | ds3000_readreg(state, 0x08)); msleep(1); - /* TS2020 init */ - ds3000_tuner_writereg(state, 0x42, 0x73); - ds3000_tuner_writereg(state, 0x05, 0x01); - ds3000_tuner_writereg(state, 0x62, 0xf5); /* Load the firmware if required */ ret = ds3000_firmware_ondemand(fe); if (ret != 0) { @@ -1251,17 +1063,10 @@ static int ds3000_initfe(struct dvb_frontend *fe) return 0; } -/* Put device to sleep */ -static int ds3000_sleep(struct dvb_frontend *fe) -{ - dprintk("%s()\n", __func__); - return 0; -} - static struct dvb_frontend_ops ds3000_ops = { - .delsys = { SYS_DVBS, SYS_DVBS2}, + .delsys = { SYS_DVBS, SYS_DVBS2 }, .info = { - .name = "Montage Technology DS3000/TS2020", + .name = "Montage Technology DS3000", .frequency_min = 950000, .frequency_max = 2150000, .frequency_stepsize = 1011, /* kHz for QPSK frontends */ @@ -1279,10 +1084,9 @@ static struct dvb_frontend_ops ds3000_ops = { .release = ds3000_release, .init = ds3000_initfe, - .sleep = ds3000_sleep, + .i2c_gate_ctrl = ds3000_i2c_gate_ctrl, .read_status = ds3000_read_status, .read_ber = ds3000_read_ber, - .read_signal_strength = ds3000_read_signal_strength, .read_snr = ds3000_read_snr, .read_ucblocks = ds3000_read_ucblocks, .set_voltage = ds3000_set_voltage, @@ -1299,7 +1103,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); MODULE_DESCRIPTION("DVB Frontend module for Montage Technology " - "DS3000/TS2020 hardware"); -MODULE_AUTHOR("Konstantin Dimitrov"); + "DS3000 hardware"); +MODULE_AUTHOR("Konstantin Dimitrov "); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(DS3000_DEFAULT_FIRMWARE); diff --git a/drivers/media/dvb-frontends/ds3000.h b/drivers/media/dvb-frontends/ds3000.h index 1b736888ea37..67eeaf9bdbce 100644 --- a/drivers/media/dvb-frontends/ds3000.h +++ b/drivers/media/dvb-frontends/ds3000.h @@ -1,8 +1,8 @@ /* - Montage Technology DS3000/TS2020 - DVBS/S2 Satellite demod/tuner driver - Copyright (C) 2009 Konstantin Dimitrov + Montage Technology DS3000 - DVBS/S2 Demodulator driver + Copyright (C) 2009-2012 Konstantin Dimitrov - Copyright (C) 2009 TurboSight.com + Copyright (C) 2009-2012 TurboSight.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ + */ #ifndef DS3000_H #define DS3000_H From 6fef4fc71e79282b673d7613cfc63da6bdeec5bd Mon Sep 17 00:00:00 2001 From: Konstantin Dimitrov Date: Sun, 23 Dec 2012 19:25:27 -0300 Subject: [PATCH 0474/4607] [media] ts2020: add ts2020 tuner driver add separate ts2020 tuner driver Signed-off-by: Konstantin Dimitrov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/Kconfig | 7 + drivers/media/dvb-frontends/Makefile | 1 + drivers/media/dvb-frontends/ts2020.c | 323 +++++++++++++++++++++++++++ drivers/media/dvb-frontends/ts2020.h | 49 ++++ 4 files changed, 380 insertions(+) create mode 100644 drivers/media/dvb-frontends/ts2020.c create mode 100644 drivers/media/dvb-frontends/ts2020.h diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 5efec73a32d2..6f809a70c78e 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -207,6 +207,13 @@ config DVB_SI21XX help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_TS2020 + tristate "Montage Tehnology TS2020 based tuners" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner. + config DVB_DS3000 tristate "Montage Tehnology DS3000 based" depends on DVB_CORE && I2C diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile index 7eb73bbd2e26..cebc0faffab5 100644 --- a/drivers/media/dvb-frontends/Makefile +++ b/drivers/media/dvb-frontends/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_DVB_ISL6423) += isl6423.o obj-$(CONFIG_DVB_EC100) += ec100.o obj-$(CONFIG_DVB_HD29L2) += hd29l2.o obj-$(CONFIG_DVB_DS3000) += ds3000.o +obj-$(CONFIG_DVB_TS2020) += ts2020.o obj-$(CONFIG_DVB_MB86A16) += mb86a16.o obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o obj-$(CONFIG_DVB_IX2505V) += ix2505v.o diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c new file mode 100644 index 000000000000..8dce4ae55f09 --- /dev/null +++ b/drivers/media/dvb-frontends/ts2020.c @@ -0,0 +1,323 @@ +/* + Montage Technology TS2020 - Silicon Tuner driver + Copyright (C) 2009-2012 Konstantin Dimitrov + + Copyright (C) 2009-2012 TurboSight.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dvb_frontend.h" +#include "ts2020.h" + +#define TS2020_XTAL_FREQ 27000 /* in kHz */ + +struct ts2020_state { + u8 tuner_address; + struct i2c_adapter *i2c; +}; + +static int ts2020_readreg(struct dvb_frontend *fe, u8 reg) +{ + struct ts2020_state *state = fe->tuner_priv; + + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = state->tuner_address, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = state->tuner_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = i2c_transfer(state->i2c, msg, 2); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 2) { + printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); + return ret; + } + + return b1[0]; +} + +static int ts2020_writereg(struct dvb_frontend *fe, int reg, int data) +{ + struct ts2020_state *state = fe->tuner_priv; + + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->tuner_address, + .flags = 0, .buf = buf, .len = 2 }; + int err; + + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + err = i2c_transfer(state->i2c, &msg, 1); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (err != 1) { + printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," + " value == 0x%02x)\n", __func__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +static int ts2020_init(struct dvb_frontend *fe) +{ + ts2020_writereg(fe, 0x42, 0x73); + ts2020_writereg(fe, 0x05, 0x01); + ts2020_writereg(fe, 0x62, 0xf5); + return 0; +} + +static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + u16 ndiv, div4; + + div4 = (ts2020_readreg(fe, 0x10) & 0x10) >> 4; + + ndiv = ts2020_readreg(fe, 0x01); + ndiv &= 0x0f; + ndiv <<= 8; + ndiv |= ts2020_readreg(fe, 0x02); + + /* actual tuned frequency, i.e. including the offset */ + *frequency = (ndiv - ndiv % 2 + 1024) * TS2020_XTAL_FREQ + / (6 + 8) / (div4 + 1) / 2; + + return 0; +} + +static int ts2020_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; + u16 value, ndiv; + u32 srate = 0, f3db; + + ts2020_init(fe); + + /* unknown */ + ts2020_writereg(fe, 0x07, 0x02); + ts2020_writereg(fe, 0x10, 0x00); + ts2020_writereg(fe, 0x60, 0x79); + ts2020_writereg(fe, 0x08, 0x01); + ts2020_writereg(fe, 0x00, 0x01); + div4 = 0; + + /* calculate and set freq divider */ + if (c->frequency < 1146000) { + ts2020_writereg(fe, 0x10, 0x11); + div4 = 1; + ndiv = ((c->frequency * (6 + 8) * 4) + + (TS2020_XTAL_FREQ / 2)) / + TS2020_XTAL_FREQ - 1024; + } else { + ts2020_writereg(fe, 0x10, 0x01); + ndiv = ((c->frequency * (6 + 8) * 2) + + (TS2020_XTAL_FREQ / 2)) / + TS2020_XTAL_FREQ - 1024; + } + + ts2020_writereg(fe, 0x01, (ndiv & 0x0f00) >> 8); + ts2020_writereg(fe, 0x02, ndiv & 0x00ff); + + /* set pll */ + ts2020_writereg(fe, 0x03, 0x06); + ts2020_writereg(fe, 0x51, 0x0f); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x10); + ts2020_writereg(fe, 0x50, 0x00); + msleep(5); + + /* unknown */ + ts2020_writereg(fe, 0x51, 0x17); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x08); + ts2020_writereg(fe, 0x50, 0x00); + msleep(5); + + value = ts2020_readreg(fe, 0x3d); + value &= 0x0f; + if ((value > 4) && (value < 15)) { + value -= 3; + if (value < 4) + value = 4; + value = ((value << 3) | 0x01) & 0x79; + } + + ts2020_writereg(fe, 0x60, value); + ts2020_writereg(fe, 0x51, 0x17); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x08); + ts2020_writereg(fe, 0x50, 0x00); + + /* set low-pass filter period */ + ts2020_writereg(fe, 0x04, 0x2e); + ts2020_writereg(fe, 0x51, 0x1b); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x04); + ts2020_writereg(fe, 0x50, 0x00); + msleep(5); + + srate = c->symbol_rate / 1000; + + f3db = (srate << 2) / 5 + 2000; + if (srate < 5000) + f3db += 3000; + if (f3db < 7000) + f3db = 7000; + if (f3db > 40000) + f3db = 40000; + + /* set low-pass filter baseband */ + value = ts2020_readreg(fe, 0x26); + mlpf = 0x2e * 207 / ((value << 1) + 151); + mlpf_max = mlpf * 135 / 100; + mlpf_min = mlpf * 78 / 100; + if (mlpf_max > 63) + mlpf_max = 63; + + /* rounded to the closest integer */ + nlpf = ((mlpf * f3db * 1000) + (2766 * TS2020_XTAL_FREQ / 2)) + / (2766 * TS2020_XTAL_FREQ); + if (nlpf > 23) + nlpf = 23; + if (nlpf < 1) + nlpf = 1; + + /* rounded to the closest integer */ + mlpf_new = ((TS2020_XTAL_FREQ * nlpf * 2766) + + (1000 * f3db / 2)) / (1000 * f3db); + + if (mlpf_new < mlpf_min) { + nlpf++; + mlpf_new = ((TS2020_XTAL_FREQ * nlpf * 2766) + + (1000 * f3db / 2)) / (1000 * f3db); + } + + if (mlpf_new > mlpf_max) + mlpf_new = mlpf_max; + + ts2020_writereg(fe, 0x04, mlpf_new); + ts2020_writereg(fe, 0x06, nlpf); + ts2020_writereg(fe, 0x51, 0x1b); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x04); + ts2020_writereg(fe, 0x50, 0x00); + msleep(5); + + /* unknown */ + ts2020_writereg(fe, 0x51, 0x1e); + ts2020_writereg(fe, 0x51, 0x1f); + ts2020_writereg(fe, 0x50, 0x01); + ts2020_writereg(fe, 0x50, 0x00); + msleep(60); + + return 0; +} + +static int ts2020_release(struct dvb_frontend *fe) +{ + struct ts2020_state *state = fe->tuner_priv; + + fe->tuner_priv = NULL; + kfree(state); + + return 0; +} + +int ts2020_get_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + u16 sig_reading, sig_strength; + u8 rfgain, bbgain; + + rfgain = ts2020_readreg(fe, 0x3d) & 0x1f; + bbgain = ts2020_readreg(fe, 0x21) & 0x1f; + + if (rfgain > 15) + rfgain = 15; + if (bbgain > 13) + bbgain = 13; + + sig_reading = rfgain * 2 + bbgain * 3; + + sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; + + /* cook the value to be suitable for szap-s2 human readable output */ + *signal_strength = sig_strength * 1000; + + return 0; +} + +static struct dvb_tuner_ops ts2020_ops = { + .info = { + .name = "Montage Technology TS2020 Silicon Tuner", + .frequency_min = 950000, + .frequency_max = 2150000, + }, + + .init = ts2020_init, + .release = ts2020_release, + .set_params = ts2020_set_params, + .get_frequency = ts2020_get_frequency, + .get_rf_strength = ts2020_get_signal_strength +}; + +struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, + const struct ts2020_config *config, struct i2c_adapter *i2c) +{ + struct ts2020_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct ts2020_state), GFP_KERNEL); + if (!state) + return NULL; + + /* setup the state */ + state->tuner_address = config->tuner_address; + state->i2c = i2c; + fe->tuner_priv = state; + fe->ops.tuner_ops = ts2020_ops; + fe->ops.read_signal_strength = fe->ops.tuner_ops.get_rf_strength; + + return fe; +} +EXPORT_SYMBOL(ts2020_attach); + +MODULE_AUTHOR("Konstantin Dimitrov "); +MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h new file mode 100644 index 000000000000..13a172dfd582 --- /dev/null +++ b/drivers/media/dvb-frontends/ts2020.h @@ -0,0 +1,49 @@ +/* + Montage Technology TS2020 - Silicon Tuner driver + Copyright (C) 2009-2012 Konstantin Dimitrov + + Copyright (C) 2009-2012 TurboSight.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef TS2020_H +#define TS2020_H + +#include + +struct ts2020_config { + u8 tuner_address; +}; + +#if defined(CONFIG_DVB_TS2020) || \ + (defined(CONFIG_DVB_TS2020_MODULE) && defined(MODULE)) + +extern struct dvb_frontend *ts2020_attach( + struct dvb_frontend *fe, + const struct ts2020_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *ts2020_attach( + struct dvb_frontend *fe, + const struct ts2020_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* TS2020_H */ From 73f0af44a9137cc2ab18e181f68f59d2ad3fe3f7 Mon Sep 17 00:00:00 2001 From: Konstantin Dimitrov Date: Sun, 23 Dec 2012 19:25:38 -0300 Subject: [PATCH 0475/4607] [media] make the other drivers take use of the new ts2020 driver make the other drivers take use of the separate ts2020 driver Signed-off-by: Konstantin Dimitrov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ds3000.c | 1 + drivers/media/pci/cx23885/Kconfig | 1 + drivers/media/pci/cx23885/cx23885-dvb.c | 10 +++++++++- drivers/media/pci/cx88/Kconfig | 2 ++ drivers/media/pci/cx88/cx88-dvb.c | 10 +++++++++- drivers/media/pci/dm1105/Kconfig | 1 + drivers/media/pci/dm1105/dm1105.c | 10 +++++++++- drivers/media/usb/dvb-usb/Kconfig | 1 + drivers/media/usb/dvb-usb/dw2102.c | 17 +++++++++++++++++ 9 files changed, 50 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index cd84fbd78a5b..bc17e29b54d1 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -27,6 +27,7 @@ #include #include "dvb_frontend.h" +#include "ts2020.h" #include "ds3000.h" static int debug; diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index 733d6c8ebe4c..b3688aa8acc3 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -25,6 +25,7 @@ config VIDEO_CX23885 select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index a1aae5633739..a2ed0f759b0a 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -57,6 +57,7 @@ #include "netup-init.h" #include "lgdt3305.h" #include "atbm8830.h" +#include "ts2020.h" #include "ds3000.h" #include "cx23885-f300.h" #include "altera-ci.h" @@ -471,6 +472,10 @@ static struct ds3000_config tevii_ds3000_config = { .demod_address = 0x68, }; +static struct ts2020_config tevii_ts2020_config = { + .tuner_address = 0x60, +}; + static struct cx24116_config dvbworld_cx24116_config = { .demod_address = 0x05, }; @@ -1027,8 +1032,11 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend = dvb_attach(ds3000_attach, &tevii_ds3000_config, &i2c_bus->i2c_adap); - if (fe0->dvb.frontend != NULL) + if (fe0->dvb.frontend != NULL) { + dvb_attach(ts2020_attach, fe0->dvb.frontend, + &tevii_ts2020_config, &i2c_bus->i2c_adap); fe0->dvb.frontend->ops.set_voltage = f300_set_voltage; + } break; case CX23885_BOARD_DVBWORLD_2005: diff --git a/drivers/media/pci/cx88/Kconfig b/drivers/media/pci/cx88/Kconfig index d27fccbf03c4..bb05eca2da29 100644 --- a/drivers/media/pci/cx88/Kconfig +++ b/drivers/media/pci/cx88/Kconfig @@ -62,6 +62,8 @@ config VIDEO_CX88_DVB select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT + select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT ---help--- This adds support for DVB/ATSC cards based on the diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 666f83b2f3c0..e085851d6872 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -58,6 +58,7 @@ #include "stb6100.h" #include "stb6100_proc.h" #include "mb86a16.h" +#include "ts2020.h" #include "ds3000.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); @@ -700,6 +701,10 @@ static struct ds3000_config tevii_ds3000_config = { .set_ts_params = ds3000_set_ts_param, }; +static struct ts2020_config tevii_ts2020_config = { + .tuner_address = 0x60, +}; + static const struct stv0900_config prof_7301_stv0900_config = { .demod_address = 0x6a, /* demod_mode = 0,*/ @@ -1466,9 +1471,12 @@ static int dvb_register(struct cx8802_dev *dev) fe0->dvb.frontend = dvb_attach(ds3000_attach, &tevii_ds3000_config, &core->i2c_adap); - if (fe0->dvb.frontend != NULL) + if (fe0->dvb.frontend != NULL) { + dvb_attach(ts2020_attach, fe0->dvb.frontend, + &tevii_ts2020_config, &core->i2c_adap); fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; + } break; case CX88_BOARD_OMICOM_SS4_PCI: case CX88_BOARD_TBS_8920: diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig index 013df4e015cd..173daf0c0847 100644 --- a/drivers/media/pci/dm1105/Kconfig +++ b/drivers/media/pci/dm1105/Kconfig @@ -8,6 +8,7 @@ config DVB_DM1105 select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT depends on RC_CORE help Support for cards based on the SDMC DM1105 PCI chip like diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index f288ffcc4b6b..79fe74aae1f9 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -45,6 +45,7 @@ #include "si21xx.h" #include "cx24116.h" #include "z0194a.h" +#include "ts2020.h" #include "ds3000.h" #define MODULE_NAME "dm1105" @@ -849,6 +850,10 @@ static struct ds3000_config dvbworld_ds3000_config = { .demod_address = 0x68, }; +static struct ts2020_config dvbworld_ts2020_config = { + .tuner_address = 0x60, +}; + static int __devinit frontend_init(struct dm1105_dev *dev) { int ret; @@ -898,8 +903,11 @@ static int __devinit frontend_init(struct dm1105_dev *dev) dev->fe = dvb_attach( ds3000_attach, &dvbworld_ds3000_config, &dev->i2c_adap); - if (dev->fe) + if (dev->fe) { + dvb_attach(ts2020_attach, dev->fe, + &dvbworld_ts2020_config, &dev->i2c_adap); dev->fe->ops.set_voltage = dm1105_set_voltage; + } break; case DM1105_BOARD_DVBWORLD_2002: diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index fa0b2931d305..dac7204c8020 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -267,6 +267,7 @@ config DVB_USB_DW2102 select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 937c744217c8..f61c5e367ea1 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -22,6 +22,7 @@ #include "tda1002x.h" #include "mt312.h" #include "zl10039.h" +#include "ts2020.h" #include "ds3000.h" #include "stv0900.h" #include "stv6110.h" @@ -941,6 +942,10 @@ static struct ds3000_config dw2104_ds3000_config = { .demod_address = 0x68, }; +static struct ts2020_config dw2104_ts2020_config = { + .tuner_address = 0x60, +}; + static struct stv0900_config dw2104a_stv0900_config = { .demod_address = 0x6a, .demod_mode = 0, @@ -992,6 +997,10 @@ static struct ds3000_config su3000_ds3000_config = { .ci_mode = 1, }; +static struct ts2020_config su3000_ts2020_config = { + .tuner_address = 0x60, +}; + static int dw2104_frontend_attach(struct dvb_usb_adapter *d) { struct dvb_tuner_ops *tuner_ops = NULL; @@ -1042,6 +1051,8 @@ static int dw2104_frontend_attach(struct dvb_usb_adapter *d) d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, &d->dev->i2c_adap); if (d->fe_adap[0].fe != NULL) { + dvb_attach(ts2020_attach, d->fe_adap[0].fe, + &dw2104_ts2020_config, &d->dev->i2c_adap); d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; info("Attached DS3000!\n"); return 0; @@ -1154,6 +1165,9 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d) if (d->fe_adap[0].fe == NULL) return -EIO; + dvb_attach(ts2020_attach, d->fe_adap[0].fe, &dw2104_ts2020_config, + &d->dev->i2c_adap); + st->old_set_voltage = d->fe_adap[0].fe->ops.set_voltage; d->fe_adap[0].fe->ops.set_voltage = s660_set_voltage; @@ -1214,6 +1228,9 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) if (d->fe_adap[0].fe == NULL) return -EIO; + dvb_attach(ts2020_attach, d->fe_adap[0].fe, &su3000_ts2020_config, + &d->dev->i2c_adap); + info("Attached DS3000!\n"); return 0; From f8c30b6f3bbea7eb8b25db7df61f8a7c24072480 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Dec 2012 19:35:44 -0200 Subject: [PATCH 0476/4607] [media] ts2020: fix two warnings added by changeset 73f0af4 drivers/media/dvb-frontends/ts2020.c: In function 'ts2020_set_params': drivers/media/dvb-frontends/ts2020.c:126:47: warning: variable 'div4' set but not used [-Wunused-but-set-variable] drivers/media/dvb-frontends/ts2020.c: At top level: drivers/media/dvb-frontends/ts2020.c:262:5: warning: no previous prototype for 'ts2020_get_signal_strength' [-Wmissing-prototypes] Cc: Konstantin Dimitrov Cc: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ts2020.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 8dce4ae55f09..73010ecb9866 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -123,7 +123,7 @@ static int ts2020_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; + u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf; u16 value, ndiv; u32 srate = 0, f3db; @@ -135,12 +135,10 @@ static int ts2020_set_params(struct dvb_frontend *fe) ts2020_writereg(fe, 0x60, 0x79); ts2020_writereg(fe, 0x08, 0x01); ts2020_writereg(fe, 0x00, 0x01); - div4 = 0; /* calculate and set freq divider */ if (c->frequency < 1146000) { ts2020_writereg(fe, 0x10, 0x11); - div4 = 1; ndiv = ((c->frequency * (6 + 8) * 4) + (TS2020_XTAL_FREQ / 2)) / TS2020_XTAL_FREQ - 1024; @@ -259,7 +257,7 @@ static int ts2020_release(struct dvb_frontend *fe) return 0; } -int ts2020_get_signal_strength(struct dvb_frontend *fe, +static int ts2020_get_signal_strength(struct dvb_frontend *fe, u16 *signal_strength) { u16 sig_reading, sig_strength; From 7e5d74ee116d9622fc4ef9f5100692485ae286cf Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Wed, 26 Dec 2012 15:12:37 -0300 Subject: [PATCH 0477/4607] [media] em28xx: rename module parameter prefer_bulk to usb_xfer_mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we have now 3 modes (auto/isoc/bulk), usb_xfer_mode is more suitable than prefer_bulk. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index ad6c800d9a40..f5cac47d099a 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -61,9 +61,10 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); -static int prefer_bulk = -1; -module_param(prefer_bulk, int, 0444); -MODULE_PARM_DESC(prefer_bulk, "prefer USB bulk transfers (-1 = auto, 0 = isoc, 1 = bulk)"); +static int usb_xfer_mode = -1; +module_param(usb_xfer_mode, int, 0444); +MODULE_PARM_DESC(usb_xfer_mode, + "USB transfer mode for frame data (-1 = auto, 0 = prefer isoc, 1 = prefer bulk)"); /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */ @@ -3394,13 +3395,13 @@ static int em28xx_usb_probe(struct usb_interface *interface, goto unlock_and_free; } - if (prefer_bulk < 0) { + if (usb_xfer_mode < 0) { if (dev->board.is_webcam) try_bulk = 1; else try_bulk = 0; } else { - try_bulk = prefer_bulk > 0; + try_bulk = usb_xfer_mode > 0; } /* Select USB transfer types to use */ From d58f4f27282e10b25daee53045a7e839bd4178a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Cardona?= Date: Fri, 28 Sep 2012 08:59:29 -0300 Subject: [PATCH 0478/4607] [media] ds3000: bail out early on i2c failures during firmware load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - if kmalloc() returns NULL, we can return immediately without trying to kfree() a NULL pointer. - if i2c_transfer() fails, error out immediately instead of trying to upload the remaining bytes of the firmware. - the error code is then properly propagated down to ds3000_initfe(). Signed-off-by: Rémi Cardona Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ds3000.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index bc17e29b54d1..fded9b67456c 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -272,15 +272,14 @@ static int ds3000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) static int ds3000_writeFW(struct ds3000_state *state, int reg, const u8 *data, u16 len) { - int i, ret = -EREMOTEIO; + int i, ret = 0; struct i2c_msg msg; u8 *buf; buf = kmalloc(33, GFP_KERNEL); if (buf == NULL) { printk(KERN_ERR "Unable to kmalloc\n"); - ret = -ENOMEM; - goto error; + return -ENOMEM; } *(buf) = reg; @@ -300,8 +299,10 @@ static int ds3000_writeFW(struct ds3000_state *state, int reg, printk(KERN_ERR "%s: write error(err == %i, " "reg == 0x%02x\n", __func__, ret, reg); ret = -EREMOTEIO; + goto error; } } + ret = 0; error: kfree(buf); @@ -384,6 +385,7 @@ static int ds3000_load_firmware(struct dvb_frontend *fe, const struct firmware *fw) { struct ds3000_state *state = fe->demodulator_priv; + int ret = 0; dprintk("%s\n", __func__); dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", @@ -396,10 +398,10 @@ static int ds3000_load_firmware(struct dvb_frontend *fe, /* Begin the firmware load process */ ds3000_writereg(state, 0xb2, 0x01); /* write the entire firmware */ - ds3000_writeFW(state, 0xb0, fw->data, fw->size); + ret = ds3000_writeFW(state, 0xb0, fw->data, fw->size); ds3000_writereg(state, 0xb2, 0x00); - return 0; + return ret; } static int ds3000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) From 955d00ac7a193e9c29a897cd5d731a84e3850217 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 8 May 2012 03:53:17 -0300 Subject: [PATCH 0479/4607] [media] TeVii DVB-S s421 and s632 cards support DVB-S chip is Montage m88rs2000, so initial patch is simple. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dw2102.c | 106 +++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 7 deletions(-) diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index f61c5e367ea1..5ae3529f81ad 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -1,9 +1,9 @@ /* DVB USB framework compliant Linux driver for the * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, - * TeVii S600, S630, S650, S660, S480, + * TeVii S600, S630, S650, S660, S480, S421, S632 * Prof 1100, 7500, * Geniatech SU3000 Cards - * Copyright (C) 2008-2011 Igor M. Liplianin (liplianin@me.by) + * Copyright (C) 2008-2012 Igor M. Liplianin (liplianin@me.by) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -28,6 +28,7 @@ #include "stv6110.h" #include "stb6100.h" #include "stb6100_proc.h" +#include "m88rs2000.h" #ifndef USB_PID_DW2102 #define USB_PID_DW2102 0x2102 @@ -69,6 +70,14 @@ #define USB_PID_PROF_1100 0xb012 #endif +#ifndef USB_PID_TEVII_S421 +#define USB_PID_TEVII_S421 0xd421 +#endif + +#ifndef USB_PID_TEVII_S632 +#define USB_PID_TEVII_S632 0xd632 +#endif + #define DW210X_READ_MSG 0 #define DW210X_WRITE_MSG 1 @@ -544,7 +553,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], } /*case 0x55: cx24116 case 0x6a: stv0903 - case 0x68: ds3000, stv0903 + case 0x68: ds3000, stv0903, rs2000 case 0x60: ts2020, stv6110, stb6100 case 0xa0: eeprom */ default: { @@ -1001,6 +1010,38 @@ static struct ts2020_config su3000_ts2020_config = { .tuner_address = 0x60, }; +static u8 m88rs2000_inittab[] = { + DEMOD_WRITE, 0x9a, 0x30, + DEMOD_WRITE, 0x00, 0x01, + WRITE_DELAY, 0x19, 0x00, + DEMOD_WRITE, 0x00, 0x00, + DEMOD_WRITE, 0x9a, 0xb0, + DEMOD_WRITE, 0x81, 0xc1, + TUNER_WRITE, 0x42, 0x73, + TUNER_WRITE, 0x05, 0x07, + TUNER_WRITE, 0x20, 0x27, + TUNER_WRITE, 0x07, 0x02, + TUNER_WRITE, 0x11, 0xff, + TUNER_WRITE, 0x60, 0xf9, + TUNER_WRITE, 0x08, 0x01, + TUNER_WRITE, 0x00, 0x41, + DEMOD_WRITE, 0x81, 0x81, + DEMOD_WRITE, 0x86, 0xc6, + DEMOD_WRITE, 0x9a, 0x30, + DEMOD_WRITE, 0xf0, 0x80, + DEMOD_WRITE, 0xf1, 0xbf, + DEMOD_WRITE, 0xb0, 0x45, + DEMOD_WRITE, 0xb2, 0x01, + DEMOD_WRITE, 0x9a, 0xb0, + 0xff, 0xaa, 0xff +}; + +static struct m88rs2000_config s421_m88rs2000_config = { + .demod_addr = 0x68, + .tuner_addr = 0x60, + .inittab = m88rs2000_inittab, +}; + static int dw2104_frontend_attach(struct dvb_usb_adapter *d) { struct dvb_tuner_ops *tuner_ops = NULL; @@ -1236,6 +1277,24 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) return 0; } +static int m88rs2000_frontend_attach(struct dvb_usb_adapter *d) +{ + u8 obuf[] = { 0x51 }; + u8 ibuf[] = { 0 }; + + if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0) + err("command 0x51 transfer failed."); + + d->fe_adap[0].fe = dvb_attach(m88rs2000_attach, &s421_m88rs2000_config, + &d->dev->i2c_adap); + if (d->fe_adap[0].fe == NULL) + return -EIO; + + info("Attached m88rs2000!\n"); + + return 0; +} + static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) { dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, @@ -1473,6 +1532,8 @@ enum dw2102_table_entry { TEVII_S480_1, TEVII_S480_2, X3M_SPC1400HD, + TEVII_S421, + TEVII_S632, }; static struct usb_device_id dw2102_table[] = { @@ -1491,6 +1552,8 @@ static struct usb_device_id dw2102_table[] = { [TEVII_S480_1] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_1)}, [TEVII_S480_2] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_2)}, [X3M_SPC1400HD] = {USB_DEVICE(0x1f4d, 0x3100)}, + [TEVII_S421] = {USB_DEVICE(0x9022, USB_PID_TEVII_S421)}, + [TEVII_S632] = {USB_DEVICE(0x9022, USB_PID_TEVII_S632)}, { } }; @@ -1839,6 +1902,19 @@ static struct dvb_usb_device_description d7500 = { {NULL}, }; +struct dvb_usb_device_properties *s421; +static struct dvb_usb_device_description d421 = { + "TeVii S421 PCI", + {&dw2102_table[TEVII_S421], NULL}, + {NULL}, +}; + +static struct dvb_usb_device_description d632 = { + "TeVii S632 USB", + {&dw2102_table[TEVII_S632], NULL}, + {NULL}, +}; + static struct dvb_usb_device_properties su3000_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, .usb_ctrl = DEVICE_SPECIFIC, @@ -1936,6 +2012,20 @@ static int dw2102_probe(struct usb_interface *intf, p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); p7500->adapter->fe[0].frontend_attach = prof_7500_frontend_attach; + + s421 = kmemdup(&su3000_properties, + sizeof(struct dvb_usb_device_properties), GFP_KERNEL); + if (!s421) { + kfree(p1100); + kfree(s660); + kfree(p7500); + return -ENOMEM; + } + s421->num_device_descs = 2; + s421->devices[0] = d421; + s421->devices[1] = d632; + s421->adapter->fe[0].frontend_attach = m88rs2000_frontend_attach; + if (0 == dvb_usb_device_init(intf, &dw2102_properties, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &dw2104_properties, @@ -1950,6 +2040,8 @@ static int dw2102_probe(struct usb_interface *intf, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, p7500, THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, s421, + THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &su3000_properties, THIS_MODULE, NULL, adapter_nr)) return 0; @@ -1968,10 +2060,10 @@ module_usb_driver(dw2102_driver); MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," - " DVB-C 3101 USB2.0," - " TeVii S600, S630, S650, S660, S480," - " Prof 1100, 7500 USB2.0," - " Geniatech SU3000 devices"); + " DVB-C 3101 USB2.0," + " TeVii S600, S630, S650, S660, S480, S421, S632" + " Prof 1100, 7500 USB2.0," + " Geniatech SU3000 devices"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(DW2101_FIRMWARE); From 081416e62d516a6412225751c9c4a3807b2374b9 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Tue, 8 May 2012 04:08:04 -0300 Subject: [PATCH 0480/4607] [media] TeVii DVB-S s421 and s632 cards support, rs2000 part One register needs to be changed to TS to work. So we use separate inittab. Signed-off-by: Igor M. Liplianin Acked-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/m88rs2000.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 633815ed90ca..5d3d5dd62daa 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -458,7 +458,11 @@ static int m88rs2000_init(struct dvb_frontend *fe) deb_info("m88rs2000: init chip\n"); /* Setup frontend from shutdown/cold */ - ret = m88rs2000_tab_set(state, m88rs2000_setup); + if (state->config->inittab) + ret = m88rs2000_tab_set(state, + (struct inittab *)state->config->inittab); + else + ret = m88rs2000_tab_set(state, m88rs2000_setup); return ret; } From 1d6ca29db8b1f213de880b10ac28aed0a19c1d4a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 2 Dec 2012 06:43:13 -0300 Subject: [PATCH 0481/4607] [media] mantis: cleanup NULL checking in mantis_ca_exit() Smatch complainst that the call to mantis_evmgr_exit() dereferences "ca" but then we check it for NULL on the next line. I've moved the NULL check forward to avoid that. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/mantis/mantis_ca.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/mantis/mantis_ca.c b/drivers/media/pci/mantis/mantis_ca.c index 3d7046909009..60c6c2f24066 100644 --- a/drivers/media/pci/mantis/mantis_ca.c +++ b/drivers/media/pci/mantis/mantis_ca.c @@ -198,11 +198,12 @@ void mantis_ca_exit(struct mantis_pci *mantis) struct mantis_ca *ca = mantis->mantis_ca; dprintk(MANTIS_DEBUG, 1, "Mantis CA exit"); + if (!ca) + return; mantis_evmgr_exit(ca); dprintk(MANTIS_ERROR, 1, "Unregistering EN50221 device"); - if (ca) - dvb_ca_en50221_release(&ca->en50221); + dvb_ca_en50221_release(&ca->en50221); kfree(ca); } From 250539a36f69aa51e4c68a1f92c4ed2bcc042a1a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 3 Dec 2012 02:44:31 -0300 Subject: [PATCH 0482/4607] [media] s3c-camif: Add missing version.h header file versioncheck script complains about missing linux/version.h header file. Signed-off-by: Sachin Kamat Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s3c-camif/camif-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index 0dd65376c067..37c9b6faae26 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include From e669c8d379567d42d62989f890f926f7b1b8f2a1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 27 Dec 2012 21:21:14 -0200 Subject: [PATCH 0483/4607] [media] blackfin Kconfig: select is evil; use, instead depends on Select is evil as it has issues with dependencies. Better to convert it to use depends on. That fixes a breakage with out-of-tree compilation of the media tree. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/blackfin/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig index 519990e17122..cc239972fa2c 100644 --- a/drivers/media/platform/blackfin/Kconfig +++ b/drivers/media/platform/blackfin/Kconfig @@ -2,7 +2,6 @@ config VIDEO_BLACKFIN_CAPTURE tristate "Blackfin Video Capture Driver" depends on VIDEO_V4L2 && BLACKFIN && I2C select VIDEOBUF2_DMA_CONTIG - select VIDEO_BLACKFIN_PPI help V4L2 bridge driver for Blackfin video capture device. Choose PPI or EPPI as its interface. @@ -12,3 +11,5 @@ config VIDEO_BLACKFIN_CAPTURE config VIDEO_BLACKFIN_PPI tristate + depends on VIDEO_BLACKFIN_CAPTURE + default VIDEO_BLACKFIN_CAPTURE From 4834f4d1ff1dc574024e1a6de920ea99571090ff Mon Sep 17 00:00:00 2001 From: Alexey Klimov Date: Mon, 12 Nov 2012 02:56:37 -0300 Subject: [PATCH 0484/4607] [media] media: add driver for Masterkit MA901 usb radio This patch creates a new usb-radio driver, radio-ma901.c, that supports Masterkit MA 901 USB FM radio devices. This device plugs into both the USB and an analog audio input or headphones, so this thing only deals with initialization and frequency setting. Signed-off-by: Alexey Klimov Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/Kconfig | 12 + drivers/media/radio/Makefile | 1 + drivers/media/radio/radio-ma901.c | 460 ++++++++++++++++++++++++++++++ 3 files changed, 473 insertions(+) create mode 100644 drivers/media/radio/radio-ma901.c diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 8090b87b3066..ead992853730 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -124,6 +124,18 @@ config USB_KEENE To compile this driver as a module, choose M here: the module will be called radio-keene. +config USB_MA901 + tristate "Masterkit MA901 USB FM radio support" + depends on USB && VIDEO_V4L2 + ---help--- + Say Y here if you want to connect this type of radio to your + computer's USB port. Note that the audio is not digital, and + you must connect the line out connector to a sound card or a + set of speakers or headphones. + + To compile this driver as a module, choose M here: the + module will be called radio-ma901. + config RADIO_TEA5764 tristate "TEA5764 I2C FM radio support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index c03ce4fe74e9..303eaebdb85a 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ obj-$(CONFIG_USB_MR800) += radio-mr800.o obj-$(CONFIG_USB_KEENE) += radio-keene.o +obj-$(CONFIG_USB_MA901) += radio-ma901.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o obj-$(CONFIG_RADIO_TEF6862) += tef6862.o diff --git a/drivers/media/radio/radio-ma901.c b/drivers/media/radio/radio-ma901.c new file mode 100644 index 000000000000..c61f590029ad --- /dev/null +++ b/drivers/media/radio/radio-ma901.c @@ -0,0 +1,460 @@ +/* + * Driver for the MasterKit MA901 USB FM radio. This device plugs + * into the USB port and an analog audio input or headphones, so this thing + * only deals with initialization, frequency setting, volume. + * + * Copyright (c) 2012 Alexey Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_AUTHOR "Alexey Klimov " +#define DRIVER_DESC "Masterkit MA901 USB FM radio driver" +#define DRIVER_VERSION "0.0.1" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +#define USB_MA901_VENDOR 0x16c0 +#define USB_MA901_PRODUCT 0x05df + +/* dev_warn macro with driver name */ +#define MA901_DRIVER_NAME "radio-ma901" +#define ma901radio_dev_warn(dev, fmt, arg...) \ + dev_warn(dev, MA901_DRIVER_NAME " - " fmt, ##arg) + +#define ma901radio_dev_err(dev, fmt, arg...) \ + dev_err(dev, MA901_DRIVER_NAME " - " fmt, ##arg) + +/* Probably USB_TIMEOUT should be modified in module parameter */ +#define BUFFER_LENGTH 8 +#define USB_TIMEOUT 500 + +#define FREQ_MIN 87.5 +#define FREQ_MAX 108.0 +#define FREQ_MUL 16000 + +#define MA901_VOLUME_MAX 16 +#define MA901_VOLUME_MIN 0 + +/* Commands that device should understand + * List isn't full and will be updated with implementation of new functions + */ +#define MA901_RADIO_SET_FREQ 0x03 +#define MA901_RADIO_SET_VOLUME 0x04 +#define MA901_RADIO_SET_MONO_STEREO 0x05 + +/* Comfortable defines for ma901radio_set_stereo */ +#define MA901_WANT_STEREO 0x50 +#define MA901_WANT_MONO 0xd0 + +/* module parameter */ +static int radio_nr = -1; +module_param(radio_nr, int, 0); +MODULE_PARM_DESC(radio_nr, "Radio file number"); + +/* Data for one (physical) device */ +struct ma901radio_device { + /* reference to USB and video device */ + struct usb_device *usbdev; + struct usb_interface *intf; + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; + + u8 *buffer; + struct mutex lock; /* buffer locking */ + int curfreq; + u16 volume; + int stereo; + bool muted; +}; + +static inline struct ma901radio_device *to_ma901radio_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct ma901radio_device, v4l2_dev); +} + +/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ +static int ma901radio_set_freq(struct ma901radio_device *radio, int freq) +{ + unsigned int freq_send = 0x300 + (freq >> 5) / 25; + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_FREQ; + radio->buffer[2] = ((freq_send >> 8) & 0xff) + 0x80; + radio->buffer[3] = freq_send & 0xff; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + if (retval < 0) + return retval; + + radio->curfreq = freq; + return 0; +} + +static int ma901radio_set_volume(struct ma901radio_device *radio, u16 vol_to_set) +{ + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_VOLUME; + radio->buffer[2] = 0xc2; + radio->buffer[3] = vol_to_set + 0x20; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + if (retval < 0) + return retval; + + radio->volume = vol_to_set; + return retval; +} + +static int ma901_set_stereo(struct ma901radio_device *radio, u8 stereo) +{ + int retval; + + radio->buffer[0] = 0x0a; + radio->buffer[1] = MA901_RADIO_SET_MONO_STEREO; + radio->buffer[2] = stereo; + radio->buffer[3] = 0x00; + radio->buffer[4] = 0x00; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), + 9, 0x21, 0x0300, 0, + radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); + + if (retval < 0) + return retval; + + if (stereo == MA901_WANT_STEREO) + radio->stereo = V4L2_TUNER_MODE_STEREO; + else + radio->stereo = V4L2_TUNER_MODE_MONO; + + return retval; +} + +/* Handle unplugging the device. + * We call video_unregister_device in any case. + * The last function called in this procedure is + * usb_ma901radio_device_release. + */ +static void usb_ma901radio_disconnect(struct usb_interface *intf) +{ + struct ma901radio_device *radio = to_ma901radio_dev(usb_get_intfdata(intf)); + + mutex_lock(&radio->lock); + video_unregister_device(&radio->vdev); + usb_set_intfdata(intf, NULL); + v4l2_device_disconnect(&radio->v4l2_dev); + mutex_unlock(&radio->lock); + v4l2_device_put(&radio->v4l2_dev); +} + +/* vidioc_querycap - query device capabilities */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + strlcpy(v->driver, "radio-ma901", sizeof(v->driver)); + strlcpy(v->card, "Masterkit MA901 USB FM Radio", sizeof(v->card)); + usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); + v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; +} + +/* vidioc_g_tuner - get tuner attributes */ +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (v->index > 0) + return -EINVAL; + + v->signal = 0; + + /* TODO: the same words like in _probe() goes here. + * When receiving of stats will be implemented then we can call + * ma901radio_get_stat(). + * retval = ma901radio_get_stat(radio, &is_stereo, &v->signal); + */ + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = FREQ_MIN * FREQ_MUL; + v->rangehigh = FREQ_MAX * FREQ_MUL; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + /* v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; */ + v->audmode = radio->stereo ? + V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; + return 0; +} + +/* vidioc_s_tuner - set tuner attributes */ +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (v->index > 0) + return -EINVAL; + + /* mono/stereo selector */ + switch (v->audmode) { + case V4L2_TUNER_MODE_MONO: + return ma901_set_stereo(radio, MA901_WANT_MONO); + default: + return ma901_set_stereo(radio, MA901_WANT_STEREO); + } +} + +/* vidioc_s_frequency - set tuner radio frequency */ +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + + return ma901radio_set_freq(radio, clamp_t(unsigned, f->frequency, + FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); +} + +/* vidioc_g_frequency - get tuner radio frequency */ +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct ma901radio_device *radio = video_drvdata(file); + + if (f->tuner != 0) + return -EINVAL; + f->frequency = radio->curfreq; + + return 0; +} + +static int usb_ma901radio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ma901radio_device *radio = + container_of(ctrl->handler, struct ma901radio_device, hdl); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: /* set volume */ + return ma901radio_set_volume(radio, (u16)ctrl->val); + } + + return -EINVAL; +} + +/* TODO: Should we really need to implement suspend and resume functions? + * Radio has it's own memory and will continue playing if power is present + * on usb port and on resume it will start to play again based on freq, volume + * values in device memory. + */ +static int usb_ma901radio_suspend(struct usb_interface *intf, pm_message_t message) +{ + return 0; +} + +static int usb_ma901radio_resume(struct usb_interface *intf) +{ + return 0; +} + +static const struct v4l2_ctrl_ops usb_ma901radio_ctrl_ops = { + .s_ctrl = usb_ma901radio_s_ctrl, +}; + +/* File system interface */ +static const struct v4l2_file_operations usb_ma901radio_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops usb_ma901radio_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void usb_ma901radio_release(struct v4l2_device *v4l2_dev) +{ + struct ma901radio_device *radio = to_ma901radio_dev(v4l2_dev); + + v4l2_ctrl_handler_free(&radio->hdl); + v4l2_device_unregister(&radio->v4l2_dev); + kfree(radio->buffer); + kfree(radio); +} + +/* check if the device is present and register with v4l and usb if it is */ +static int usb_ma901radio_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct ma901radio_device *radio; + int retval = 0; + + radio = kzalloc(sizeof(struct ma901radio_device), GFP_KERNEL); + if (!radio) { + dev_err(&intf->dev, "kzalloc for ma901radio_device failed\n"); + retval = -ENOMEM; + goto err; + } + + radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); + if (!radio->buffer) { + dev_err(&intf->dev, "kmalloc for radio->buffer failed\n"); + retval = -ENOMEM; + goto err_nobuf; + } + + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); + if (retval < 0) { + dev_err(&intf->dev, "couldn't register v4l2_device\n"); + goto err_v4l2; + } + + v4l2_ctrl_handler_init(&radio->hdl, 1); + + /* TODO:It looks like this radio doesn't have mute/unmute control + * and windows program just emulate it using volume control. + * Let's plan to do the same in this driver. + * + * v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, + * V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + */ + + v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, MA901_VOLUME_MIN, + MA901_VOLUME_MAX, 1, MA901_VOLUME_MAX); + + if (radio->hdl.error) { + retval = radio->hdl.error; + dev_err(&intf->dev, "couldn't register control\n"); + goto err_ctrl; + } + mutex_init(&radio->lock); + + radio->v4l2_dev.ctrl_handler = &radio->hdl; + radio->v4l2_dev.release = usb_ma901radio_release; + strlcpy(radio->vdev.name, radio->v4l2_dev.name, + sizeof(radio->vdev.name)); + radio->vdev.v4l2_dev = &radio->v4l2_dev; + radio->vdev.fops = &usb_ma901radio_fops; + radio->vdev.ioctl_ops = &usb_ma901radio_ioctl_ops; + radio->vdev.release = video_device_release_empty; + radio->vdev.lock = &radio->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); + + radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; + usb_set_intfdata(intf, &radio->v4l2_dev); + radio->curfreq = 95.21 * FREQ_MUL; + + video_set_drvdata(&radio->vdev, radio); + + /* TODO: we can get some statistics (freq, volume) from device + * but it's not implemented yet. After insertion in usb-port radio + * setups frequency and starts playing without any initialization. + * So we don't call usb_ma901radio_init/get_stat() here. + * retval = usb_ma901radio_init(radio); + */ + + retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, + radio_nr); + if (retval < 0) { + dev_err(&intf->dev, "could not register video device\n"); + goto err_vdev; + } + + return 0; + +err_vdev: + v4l2_ctrl_handler_free(&radio->hdl); +err_ctrl: + v4l2_device_unregister(&radio->v4l2_dev); +err_v4l2: + kfree(radio->buffer); +err_nobuf: + kfree(radio); +err: + return retval; +} + +/* USB Device ID List */ +static struct usb_device_id usb_ma901radio_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(USB_MA901_VENDOR, USB_MA901_PRODUCT, + USB_CLASS_HID, 0, 0) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_ma901radio_device_table); + +/* USB subsystem interface */ +static struct usb_driver usb_ma901radio_driver = { + .name = MA901_DRIVER_NAME, + .probe = usb_ma901radio_probe, + .disconnect = usb_ma901radio_disconnect, + .suspend = usb_ma901radio_suspend, + .resume = usb_ma901radio_resume, + .reset_resume = usb_ma901radio_resume, + .id_table = usb_ma901radio_device_table, +}; + +module_usb_driver(usb_ma901radio_driver); From 0322bd3980b3ebf7dde8474e22614cb443d6479a Mon Sep 17 00:00:00 2001 From: Alexey Klimov Date: Mon, 12 Nov 2012 02:57:03 -0300 Subject: [PATCH 0485/4607] [hid] usb hid quirks for Masterkit MA901 usb radio Don't let Masterkit MA901 USB radio be handled by usb hid drivers. This device will be handled by radio-ma901.c driver. Signed-off-by: Alexey Klimov Acked-by: Hans Verkuil Acked-by: Jiri Kosina Signed-off-by: Mauro Carvalho Chehab --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index eb2ee11b6412..ccca9ac34dca 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2070,6 +2070,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_BEATPAD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MASTERKIT, USB_DEVICE_ID_MASTERKIT_MA901RADIO) }, { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) }, { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4dfa605e2d14..f39be08c1358 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -551,6 +551,9 @@ #define USB_VENDOR_ID_MADCATZ 0x0738 #define USB_DEVICE_ID_MADCATZ_BEATPAD 0x4540 +#define USB_VENDOR_ID_MASTERKIT 0x16c0 +#define USB_DEVICE_ID_MASTERKIT_MA901RADIO 0x05df + #define USB_VENDOR_ID_MCC 0x09db #define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 #define USB_DEVICE_ID_MCC_PMD1208LS 0x007a From c19bec500168108bf28710fae304523679ffb40f Mon Sep 17 00:00:00 2001 From: Kirill Smelkov Date: Wed, 26 Dec 2012 12:23:26 -0300 Subject: [PATCH 0486/4607] [media] vivi: Constify structures Most of *_ops and other structures in vivi.c were already declared const but some have not. Constify and code/data will take less space: $ size drivers/media/platform/vivi.o text data bss dec hex filename before: 12569 248 8 12825 3219 drivers/media/platform/vivi.o after: 12308 20 8 12336 3030 drivers/media/platform/vivi.o i.e. vivi.o is now ~500 bytes less. Signed-off-by: Kirill Smelkov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/vivi.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c index ec6508909f02..8a33a712f480 100644 --- a/drivers/media/platform/vivi.c +++ b/drivers/media/platform/vivi.c @@ -91,13 +91,13 @@ static const struct v4l2_fract ------------------------------------------------------------------*/ struct vivi_fmt { - char *name; + const char *name; u32 fourcc; /* v4l2 format id */ u8 depth; bool is_yuv; }; -static struct vivi_fmt formats[] = { +static const struct vivi_fmt formats[] = { { .name = "4:2:2, packed, YUYV", .fourcc = V4L2_PIX_FMT_YUYV, @@ -164,9 +164,9 @@ static struct vivi_fmt formats[] = { }, }; -static struct vivi_fmt *__get_format(u32 pixelformat) +static const struct vivi_fmt *__get_format(u32 pixelformat) { - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; unsigned int k; for (k = 0; k < ARRAY_SIZE(formats); k++) { @@ -181,7 +181,7 @@ static struct vivi_fmt *__get_format(u32 pixelformat) return &formats[k]; } -static struct vivi_fmt *get_format(struct v4l2_format *f) +static const struct vivi_fmt *get_format(struct v4l2_format *f) { return __get_format(f->fmt.pix.pixelformat); } @@ -191,7 +191,7 @@ struct vivi_buffer { /* common v4l buffer stuff -- must be first */ struct vb2_buffer vb; struct list_head list; - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; }; struct vivi_dmaqueue { @@ -250,7 +250,7 @@ struct vivi_dev { int input; /* video capture */ - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; struct v4l2_fract timeperframe; unsigned int width, height; struct vb2_queue vb_vidq; @@ -297,7 +297,7 @@ struct bar_std { /* Maximum number of bars are 10 - otherwise, the input print code should be modified */ -static struct bar_std bars[] = { +static const struct bar_std bars[] = { { /* Standard ITU-R color bar sequence */ { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN, COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK } @@ -926,7 +926,7 @@ static void vivi_unlock(struct vb2_queue *vq) } -static struct vb2_ops vivi_video_qops = { +static const struct vb2_ops vivi_video_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, @@ -957,7 +957,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; if (f->index >= ARRAY_SIZE(formats)) return -EINVAL; @@ -993,7 +993,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vivi_dev *dev = video_drvdata(file); - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; fmt = get_format(f); if (!fmt) { @@ -1102,7 +1102,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_enum_frameintervals(struct file *file, void *priv, struct v4l2_frmivalenum *fival) { - struct vivi_fmt *fmt; + const struct vivi_fmt *fmt; if (fival->index) return -EINVAL; @@ -1330,7 +1330,7 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = { .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -static struct video_device vivi_template = { +static const struct video_device vivi_template = { .name = "vivi", .fops = &vivi_fops, .ioctl_ops = &vivi_ioctl_ops, From a3e7ad256cae84dfbb8d016a5838c72f3b695883 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 28 Dec 2012 19:40:04 -0300 Subject: [PATCH 0487/4607] [media] dw2102: autoselect DVB_M88RS2000 Patch to select rs2000 module to compile automatically for TeVii S421 and S632 cards. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index dac7204c8020..c423eb80c3a5 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -271,6 +271,7 @@ config DVB_USB_DW2102 select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT + select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0 receivers. From 38f7889cea9d5754493fa601a2d466ba33f13f55 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 28 Dec 2012 19:40:16 -0300 Subject: [PATCH 0488/4607] [media] m88rs2000: SNR, BER implemented Trivial patch to implement SNR, BER, UCB counter for Montage rs2000 demod. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/m88rs2000.c | 44 ++++++++++++++++++++----- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 5d3d5dd62daa..df9e7dd6fe74 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -492,12 +492,29 @@ static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status) return 0; } -/* Extact code for these unknown but lmedm04 driver uses interupt callbacks */ - static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) { - deb_info("m88rs2000_read_ber %d\n", *ber); - *ber = 0; + struct m88rs2000_state *state = fe->demodulator_priv; + u8 tmp0, tmp1; + + m88rs2000_demod_write(state, 0x9a, 0x30); + tmp0 = m88rs2000_demod_read(state, 0xd8); + if ((tmp0 & 0x10) != 0) { + m88rs2000_demod_write(state, 0x9a, 0xb0); + *ber = 0xffffffff; + return 0; + } + + *ber = (m88rs2000_demod_read(state, 0xd7) << 8) | + m88rs2000_demod_read(state, 0xd6); + + tmp1 = m88rs2000_demod_read(state, 0xd9); + m88rs2000_demod_write(state, 0xd9, (tmp1 & ~7) | 4); + /* needs twice */ + m88rs2000_demod_write(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_demod_write(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_demod_write(state, 0x9a, 0xb0); + return 0; } @@ -510,15 +527,26 @@ static int m88rs2000_read_signal_strength(struct dvb_frontend *fe, static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) { - deb_info("m88rs2000_read_snr %d\n", *snr); - *snr = 0; + struct m88rs2000_state *state = fe->demodulator_priv; + + *snr = 512 * m88rs2000_demod_read(state, 0x65); + return 0; } static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { - deb_info("m88rs2000_read_ber %d\n", *ucblocks); - *ucblocks = 0; + struct m88rs2000_state *state = fe->demodulator_priv; + u8 tmp; + + *ucblocks = (m88rs2000_demod_read(state, 0xd5) << 8) | + m88rs2000_demod_read(state, 0xd4); + tmp = m88rs2000_demod_read(state, 0xd8); + m88rs2000_demod_write(state, 0xd8, tmp & ~0x20); + /* needs two times */ + m88rs2000_demod_write(state, 0xd8, tmp | 0x20); + m88rs2000_demod_write(state, 0xd8, tmp | 0x20); + return 0; } From 43385c8a645a25ddef7a45df8786ff26806f7e5d Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 28 Dec 2012 19:40:24 -0300 Subject: [PATCH 0489/4607] [media] ds3000: lock led procedure added TeVii s660 and others have LED for lock indication. Let's use it in right order. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ds3000.c | 12 ++++++++++++ drivers/media/dvb-frontends/ds3000.h | 2 ++ drivers/media/usb/dvb-usb/dw2102.c | 7 ++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index fded9b67456c..d128f85844e7 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -460,6 +460,9 @@ static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status) return 1; } + if (state->config->set_lock_led) + state->config->set_lock_led(fe, *status == 0 ? 0 : 1); + dprintk("%s: status = 0x%02x\n", __func__, lock); return 0; @@ -809,6 +812,10 @@ static int ds3000_diseqc_send_burst(struct dvb_frontend *fe, static void ds3000_release(struct dvb_frontend *fe) { struct ds3000_state *state = fe->demodulator_priv; + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + dprintk("%s\n", __func__); kfree(state); } @@ -1037,6 +1044,11 @@ static int ds3000_tune(struct dvb_frontend *fe, static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) { + struct ds3000_state *state = fe->demodulator_priv; + + if (state->config->set_lock_led) + state->config->set_lock_led(fe, 0); + dprintk("%s()\n", __func__); return DVBFE_ALGO_HW; } diff --git a/drivers/media/dvb-frontends/ds3000.h b/drivers/media/dvb-frontends/ds3000.h index 67eeaf9bdbce..478ad66c63d7 100644 --- a/drivers/media/dvb-frontends/ds3000.h +++ b/drivers/media/dvb-frontends/ds3000.h @@ -30,6 +30,8 @@ struct ds3000_config { u8 ci_mode; /* Set device param to start dma */ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); + /* Hook for Lock LED */ + void (*set_lock_led)(struct dvb_frontend *fe, int offon); }; #if defined(CONFIG_DVB_DS3000) || \ diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index 5ae3529f81ad..d8a5ebb43626 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -955,6 +955,11 @@ static struct ts2020_config dw2104_ts2020_config = { .tuner_address = 0x60, }; +static struct ds3000_config s660_ds3000_config = { + .demod_address = 0x68, + .set_lock_led = dw210x_led_ctrl, +}; + static struct stv0900_config dw2104a_stv0900_config = { .demod_address = 0x6a, .demod_mode = 0, @@ -1200,7 +1205,7 @@ static int ds3000_frontend_attach(struct dvb_usb_adapter *d) struct s6x0_state *st = (struct s6x0_state *)d->dev->priv; u8 obuf[] = {7, 1}; - d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, + d->fe_adap[0].fe = dvb_attach(ds3000_attach, &s660_ds3000_config, &d->dev->i2c_adap); if (d->fe_adap[0].fe == NULL) From b858c331cdf402853be2c48c8f4f77173ef04da8 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Fri, 28 Dec 2012 19:40:33 -0300 Subject: [PATCH 0490/4607] [media] m88rs2000: make use ts2020 Tuner part of Montage rs2000 chip is similar to ts2020 tuner. Patch to use ts2020 code. [mchehab@redhat.com: a few CodingStyle fixes] Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/m88rs2000.c | 408 ++++++------------------ drivers/media/dvb-frontends/m88rs2000.h | 6 - drivers/media/dvb-frontends/ts2020.c | 367 ++++++++++++--------- drivers/media/dvb-frontends/ts2020.h | 1 + drivers/media/pci/cx23885/cx23885-dvb.c | 27 +- drivers/media/pci/cx88/cx88-dvb.c | 1 + drivers/media/pci/dm1105/dm1105.c | 1 + drivers/media/usb/dvb-usb-v2/Kconfig | 1 + drivers/media/usb/dvb-usb-v2/lmedm04.c | 9 +- drivers/media/usb/dvb-usb/dw2102.c | 49 +-- 10 files changed, 365 insertions(+), 505 deletions(-) diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index df9e7dd6fe74..283c90fee374 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -60,15 +60,13 @@ MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); #define info(format, arg...) \ printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg) -static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner, +static int m88rs2000_writereg(struct m88rs2000_state *state, u8 reg, u8 data) { int ret; - u8 addr = (tuner == 0) ? state->config->tuner_addr : - state->config->demod_addr; u8 buf[] = { reg, data }; struct i2c_msg msg = { - .addr = addr, + .addr = state->config->demod_addr, .flags = 0, .buf = buf, .len = 2 @@ -83,44 +81,20 @@ static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner, return (ret != 1) ? -EREMOTEIO : 0; } -static int m88rs2000_demod_write(struct m88rs2000_state *state, u8 reg, u8 data) -{ - return m88rs2000_writereg(state, 1, reg, data); -} - -static int m88rs2000_tuner_write(struct m88rs2000_state *state, u8 reg, u8 data) -{ - m88rs2000_demod_write(state, 0x81, 0x84); - udelay(10); - return m88rs2000_writereg(state, 0, reg, data); - -} - -static int m88rs2000_write(struct dvb_frontend *fe, const u8 buf[], int len) -{ - struct m88rs2000_state *state = fe->demodulator_priv; - - if (len != 2) - return -EINVAL; - - return m88rs2000_writereg(state, 1, buf[0], buf[1]); -} - -static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg) +static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg) { int ret; u8 b0[] = { reg }; u8 b1[] = { 0 }; - u8 addr = (tuner == 0) ? state->config->tuner_addr : - state->config->demod_addr; + struct i2c_msg msg[] = { { - .addr = addr, + .addr = state->config->demod_addr, .flags = 0, .buf = b0, .len = 1 }, { - .addr = addr, + .addr = state->config->demod_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 @@ -136,18 +110,6 @@ static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg) return b1[0]; } -static u8 m88rs2000_demod_read(struct m88rs2000_state *state, u8 reg) -{ - return m88rs2000_readreg(state, 1, reg); -} - -static u8 m88rs2000_tuner_read(struct m88rs2000_state *state, u8 reg) -{ - m88rs2000_demod_write(state, 0x81, 0x85); - udelay(10); - return m88rs2000_readreg(state, 0, reg); -} - static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) { struct m88rs2000_state *state = fe->demodulator_priv; @@ -166,9 +128,9 @@ static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) b[0] = (u8) (temp >> 16) & 0xff; b[1] = (u8) (temp >> 8) & 0xff; b[2] = (u8) temp & 0xff; - ret = m88rs2000_demod_write(state, 0x93, b[2]); - ret |= m88rs2000_demod_write(state, 0x94, b[1]); - ret |= m88rs2000_demod_write(state, 0x95, b[0]); + ret = m88rs2000_writereg(state, 0x93, b[2]); + ret |= m88rs2000_writereg(state, 0x94, b[1]); + ret |= m88rs2000_writereg(state, 0x95, b[0]); deb_info("m88rs2000: m88rs2000_set_symbolrate\n"); return ret; @@ -182,37 +144,37 @@ static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe, int i; u8 reg; deb_info("%s\n", __func__); - m88rs2000_demod_write(state, 0x9a, 0x30); - reg = m88rs2000_demod_read(state, 0xb2); + m88rs2000_writereg(state, 0x9a, 0x30); + reg = m88rs2000_readreg(state, 0xb2); reg &= 0x3f; - m88rs2000_demod_write(state, 0xb2, reg); + m88rs2000_writereg(state, 0xb2, reg); for (i = 0; i < m->msg_len; i++) - m88rs2000_demod_write(state, 0xb3 + i, m->msg[i]); + m88rs2000_writereg(state, 0xb3 + i, m->msg[i]); - reg = m88rs2000_demod_read(state, 0xb1); + reg = m88rs2000_readreg(state, 0xb1); reg &= 0x87; reg |= ((m->msg_len - 1) << 3) | 0x07; reg &= 0x7f; - m88rs2000_demod_write(state, 0xb1, reg); + m88rs2000_writereg(state, 0xb1, reg); for (i = 0; i < 15; i++) { - if ((m88rs2000_demod_read(state, 0xb1) & 0x40) == 0x0) + if ((m88rs2000_readreg(state, 0xb1) & 0x40) == 0x0) break; msleep(20); } - reg = m88rs2000_demod_read(state, 0xb1); + reg = m88rs2000_readreg(state, 0xb1); if ((reg & 0x40) > 0x0) { reg &= 0x7f; reg |= 0x40; - m88rs2000_demod_write(state, 0xb1, reg); + m88rs2000_writereg(state, 0xb1, reg); } - reg = m88rs2000_demod_read(state, 0xb2); + reg = m88rs2000_readreg(state, 0xb2); reg &= 0x3f; reg |= 0x80; - m88rs2000_demod_write(state, 0xb2, reg); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0xb2, reg); + m88rs2000_writereg(state, 0x9a, 0xb0); return 0; @@ -224,14 +186,14 @@ static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe, struct m88rs2000_state *state = fe->demodulator_priv; u8 reg0, reg1; deb_info("%s\n", __func__); - m88rs2000_demod_write(state, 0x9a, 0x30); + m88rs2000_writereg(state, 0x9a, 0x30); msleep(50); - reg0 = m88rs2000_demod_read(state, 0xb1); - reg1 = m88rs2000_demod_read(state, 0xb2); + reg0 = m88rs2000_readreg(state, 0xb1); + reg1 = m88rs2000_readreg(state, 0xb2); /* TODO complete this section */ - m88rs2000_demod_write(state, 0xb2, reg1); - m88rs2000_demod_write(state, 0xb1, reg0); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0xb2, reg1); + m88rs2000_writereg(state, 0xb1, reg0); + m88rs2000_writereg(state, 0x9a, 0xb0); return 0; } @@ -240,9 +202,9 @@ static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) { struct m88rs2000_state *state = fe->demodulator_priv; u8 reg0, reg1; - m88rs2000_demod_write(state, 0x9a, 0x30); - reg0 = m88rs2000_demod_read(state, 0xb1); - reg1 = m88rs2000_demod_read(state, 0xb2); + m88rs2000_writereg(state, 0x9a, 0x30); + reg0 = m88rs2000_readreg(state, 0xb1); + reg1 = m88rs2000_readreg(state, 0xb2); reg1 &= 0x3f; @@ -257,9 +219,9 @@ static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) default: break; } - m88rs2000_demod_write(state, 0xb2, reg1); - m88rs2000_demod_write(state, 0xb1, reg0); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0xb2, reg1); + m88rs2000_writereg(state, 0xb1, reg0); + m88rs2000_writereg(state, 0x9a, 0xb0); return 0; } @@ -276,14 +238,6 @@ struct inittab m88rs2000_setup[] = { {DEMOD_WRITE, 0x00, 0x00}, {DEMOD_WRITE, 0x9a, 0xb0}, {DEMOD_WRITE, 0x81, 0xc1}, - {TUNER_WRITE, 0x42, 0x73}, - {TUNER_WRITE, 0x05, 0x07}, - {TUNER_WRITE, 0x20, 0x27}, - {TUNER_WRITE, 0x07, 0x02}, - {TUNER_WRITE, 0x11, 0xff}, - {TUNER_WRITE, 0x60, 0xf9}, - {TUNER_WRITE, 0x08, 0x01}, - {TUNER_WRITE, 0x00, 0x41}, {DEMOD_WRITE, 0x81, 0x81}, {DEMOD_WRITE, 0x86, 0xc6}, {DEMOD_WRITE, 0x9a, 0x30}, @@ -301,23 +255,10 @@ struct inittab m88rs2000_shutdown[] = { {DEMOD_WRITE, 0xf1, 0x89}, {DEMOD_WRITE, 0x00, 0x01}, {DEMOD_WRITE, 0x9a, 0xb0}, - {TUNER_WRITE, 0x00, 0x40}, {DEMOD_WRITE, 0x81, 0x81}, {0xff, 0xaa, 0xff} }; -struct inittab tuner_reset[] = { - {TUNER_WRITE, 0x42, 0x73}, - {TUNER_WRITE, 0x05, 0x07}, - {TUNER_WRITE, 0x20, 0x27}, - {TUNER_WRITE, 0x07, 0x02}, - {TUNER_WRITE, 0x11, 0xff}, - {TUNER_WRITE, 0x60, 0xf9}, - {TUNER_WRITE, 0x08, 0x01}, - {TUNER_WRITE, 0x00, 0x41}, - {0xff, 0xaa, 0xff} -}; - struct inittab fe_reset[] = { {DEMOD_WRITE, 0x00, 0x01}, {DEMOD_WRITE, 0xf1, 0xbf}, @@ -389,11 +330,7 @@ static int m88rs2000_tab_set(struct m88rs2000_state *state, for (i = 0; i < 255; i++) { switch (tab[i].cmd) { case 0x01: - ret = m88rs2000_demod_write(state, tab[i].reg, - tab[i].val); - break; - case 0x02: - ret = m88rs2000_tuner_write(state, tab[i].reg, + ret = m88rs2000_writereg(state, tab[i].reg, tab[i].val); break; case 0x10: @@ -419,7 +356,7 @@ static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) struct m88rs2000_state *state = fe->demodulator_priv; u8 data; - data = m88rs2000_demod_read(state, 0xb2); + data = m88rs2000_readreg(state, 0xb2); data |= 0x03; /* bit0 V/H, bit1 off/on */ switch (volt) { @@ -434,23 +371,11 @@ static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) break; } - m88rs2000_demod_write(state, 0xb2, data); + m88rs2000_writereg(state, 0xb2, data); return 0; } -static int m88rs2000_startup(struct m88rs2000_state *state) -{ - int ret = 0; - u8 reg; - - reg = m88rs2000_tuner_read(state, 0x00); - if ((reg & 0x40) == 0) - ret = -ENODEV; - - return ret; -} - static int m88rs2000_init(struct dvb_frontend *fe) { struct m88rs2000_state *state = fe->demodulator_priv; @@ -479,7 +404,7 @@ static int m88rs2000_sleep(struct dvb_frontend *fe) static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct m88rs2000_state *state = fe->demodulator_priv; - u8 reg = m88rs2000_demod_read(state, 0x8c); + u8 reg = m88rs2000_readreg(state, 0x8c); *status = 0; @@ -497,23 +422,23 @@ static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) struct m88rs2000_state *state = fe->demodulator_priv; u8 tmp0, tmp1; - m88rs2000_demod_write(state, 0x9a, 0x30); - tmp0 = m88rs2000_demod_read(state, 0xd8); + m88rs2000_writereg(state, 0x9a, 0x30); + tmp0 = m88rs2000_readreg(state, 0xd8); if ((tmp0 & 0x10) != 0) { - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0x9a, 0xb0); *ber = 0xffffffff; return 0; } - *ber = (m88rs2000_demod_read(state, 0xd7) << 8) | - m88rs2000_demod_read(state, 0xd6); + *ber = (m88rs2000_readreg(state, 0xd7) << 8) | + m88rs2000_readreg(state, 0xd6); - tmp1 = m88rs2000_demod_read(state, 0xd9); - m88rs2000_demod_write(state, 0xd9, (tmp1 & ~7) | 4); + tmp1 = m88rs2000_readreg(state, 0xd9); + m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4); /* needs twice */ - m88rs2000_demod_write(state, 0xd8, (tmp0 & ~8) | 0x30); - m88rs2000_demod_write(state, 0xd8, (tmp0 & ~8) | 0x30); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); + m88rs2000_writereg(state, 0x9a, 0xb0); return 0; } @@ -529,7 +454,7 @@ static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) { struct m88rs2000_state *state = fe->demodulator_priv; - *snr = 512 * m88rs2000_demod_read(state, 0x65); + *snr = 512 * m88rs2000_readreg(state, 0x65); return 0; } @@ -539,166 +464,17 @@ static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) struct m88rs2000_state *state = fe->demodulator_priv; u8 tmp; - *ucblocks = (m88rs2000_demod_read(state, 0xd5) << 8) | - m88rs2000_demod_read(state, 0xd4); - tmp = m88rs2000_demod_read(state, 0xd8); - m88rs2000_demod_write(state, 0xd8, tmp & ~0x20); + *ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) | + m88rs2000_readreg(state, 0xd4); + tmp = m88rs2000_readreg(state, 0xd8); + m88rs2000_writereg(state, 0xd8, tmp & ~0x20); /* needs two times */ - m88rs2000_demod_write(state, 0xd8, tmp | 0x20); - m88rs2000_demod_write(state, 0xd8, tmp | 0x20); + m88rs2000_writereg(state, 0xd8, tmp | 0x20); + m88rs2000_writereg(state, 0xd8, tmp | 0x20); return 0; } -static int m88rs2000_tuner_gate_ctrl(struct m88rs2000_state *state, u8 offset) -{ - int ret; - ret = m88rs2000_tuner_write(state, 0x51, 0x1f - offset); - ret |= m88rs2000_tuner_write(state, 0x51, 0x1f); - ret |= m88rs2000_tuner_write(state, 0x50, offset); - ret |= m88rs2000_tuner_write(state, 0x50, 0x00); - msleep(20); - return ret; -} - -static int m88rs2000_set_tuner_rf(struct dvb_frontend *fe) -{ - struct m88rs2000_state *state = fe->demodulator_priv; - int reg; - reg = m88rs2000_tuner_read(state, 0x3d); - reg &= 0x7f; - if (reg < 0x16) - reg = 0xa1; - else if (reg == 0x16) - reg = 0x99; - else - reg = 0xf9; - - m88rs2000_tuner_write(state, 0x60, reg); - reg = m88rs2000_tuner_gate_ctrl(state, 0x08); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - return reg; -} - -static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset) -{ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct m88rs2000_state *state = fe->demodulator_priv; - int ret; - u32 frequency = c->frequency; - s32 offset_khz; - s32 tmp; - u32 symbol_rate = (c->symbol_rate / 1000); - u32 f3db, gdiv28; - u16 value, ndiv, lpf_coeff; - u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf; - u8 lo = 0x01, div4 = 0x0; - - /* Reset Tuner */ - ret = m88rs2000_tab_set(state, tuner_reset); - - /* Calculate frequency divider */ - if (frequency < 1060000) { - lo |= 0x10; - div4 = 0x1; - ndiv = (frequency * 14 * 4) / FE_CRYSTAL_KHZ; - } else - ndiv = (frequency * 14 * 2) / FE_CRYSTAL_KHZ; - ndiv = ndiv + ndiv % 2; - ndiv = ndiv - 1024; - - ret = m88rs2000_tuner_write(state, 0x10, 0x80 | lo); - - /* Set frequency divider */ - ret |= m88rs2000_tuner_write(state, 0x01, (ndiv >> 8) & 0xf); - ret |= m88rs2000_tuner_write(state, 0x02, ndiv & 0xff); - - ret |= m88rs2000_tuner_write(state, 0x03, 0x06); - ret |= m88rs2000_tuner_gate_ctrl(state, 0x10); - if (ret < 0) - return -ENODEV; - - /* Tuner Frequency Range */ - ret = m88rs2000_tuner_write(state, 0x10, lo); - - ret |= m88rs2000_tuner_gate_ctrl(state, 0x08); - - /* Tuner RF */ - ret |= m88rs2000_set_tuner_rf(fe); - - gdiv28 = (FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000; - ret |= m88rs2000_tuner_write(state, 0x04, gdiv28 & 0xff); - ret |= m88rs2000_tuner_gate_ctrl(state, 0x04); - if (ret < 0) - return -ENODEV; - - value = m88rs2000_tuner_read(state, 0x26); - - f3db = (symbol_rate * 135) / 200 + 2000; - f3db += FREQ_OFFSET_LOW_SYM_RATE; - if (f3db < 7000) - f3db = 7000; - if (f3db > 40000) - f3db = 40000; - - gdiv28 = gdiv28 * 207 / (value * 2 + 151); - mlpf_max = gdiv28 * 135 / 100; - mlpf_min = gdiv28 * 78 / 100; - if (mlpf_max > 63) - mlpf_max = 63; - - lpf_coeff = 2766; - - nlpf = (f3db * gdiv28 * 2 / lpf_coeff / - (FE_CRYSTAL_KHZ / 1000) + 1) / 2; - if (nlpf > 23) - nlpf = 23; - if (nlpf < 1) - nlpf = 1; - - lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000) - * lpf_coeff * 2 / f3db + 1) / 2; - - if (lpf_mxdiv < mlpf_min) { - nlpf++; - lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000) - * lpf_coeff * 2 / f3db + 1) / 2; - } - - if (lpf_mxdiv > mlpf_max) - lpf_mxdiv = mlpf_max; - - ret = m88rs2000_tuner_write(state, 0x04, lpf_mxdiv); - ret |= m88rs2000_tuner_write(state, 0x06, nlpf); - - ret |= m88rs2000_tuner_gate_ctrl(state, 0x04); - - ret |= m88rs2000_tuner_gate_ctrl(state, 0x01); - - msleep(80); - /* calculate offset assuming 96000kHz*/ - offset_khz = (ndiv - ndiv % 2 + 1024) * FE_CRYSTAL_KHZ - / 14 / (div4 + 1) / 2; - - offset_khz -= frequency; - - tmp = offset_khz; - tmp *= 65536; - - tmp = (2 * tmp + 96000) / (2 * 96000); - if (tmp < 0) - tmp += 65536; - - *offset = tmp & 0xffff; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - return (ret < 0) ? -EINVAL : 0; -} - static int m88rs2000_set_fec(struct m88rs2000_state *state, fe_code_rate_t fec) { @@ -724,7 +500,7 @@ static int m88rs2000_set_fec(struct m88rs2000_state *state, default: fec_set = 0x08; } - m88rs2000_demod_write(state, 0x76, fec_set); + m88rs2000_writereg(state, 0x76, fec_set); return 0; } @@ -733,9 +509,9 @@ static int m88rs2000_set_fec(struct m88rs2000_state *state, static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state) { u8 reg; - m88rs2000_demod_write(state, 0x9a, 0x30); - reg = m88rs2000_demod_read(state, 0x76); - m88rs2000_demod_write(state, 0x9a, 0xb0); + m88rs2000_writereg(state, 0x9a, 0x30); + reg = m88rs2000_readreg(state, 0x76); + m88rs2000_writereg(state, 0x9a, 0xb0); switch (reg) { case 0x88: @@ -761,7 +537,9 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) struct m88rs2000_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; fe_status_t status; - int i, ret; + int i, ret = 0; + s32 tmp; + u32 tuner_freq; u16 offset = 0; u8 reg; @@ -775,17 +553,37 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) } /* Set Tuner */ - ret = m88rs2000_set_tuner(fe, &offset); + if (fe->ops.tuner_ops.set_params) + ret = fe->ops.tuner_ops.set_params(fe); + if (ret < 0) return -ENODEV; - ret = m88rs2000_demod_write(state, 0x9a, 0x30); + if (fe->ops.tuner_ops.get_frequency) + ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq); + + if (ret < 0) + return -ENODEV; + + offset = tuner_freq - c->frequency; + + /* calculate offset assuming 96000kHz*/ + tmp = offset; + tmp *= 65536; + + tmp = (2 * tmp + 96000) / (2 * 96000); + if (tmp < 0) + tmp += 65536; + + offset = tmp & 0xffff; + + ret = m88rs2000_writereg(state, 0x9a, 0x30); /* Unknown usually 0xc6 sometimes 0xc1 */ - reg = m88rs2000_demod_read(state, 0x86); - ret |= m88rs2000_demod_write(state, 0x86, reg); + reg = m88rs2000_readreg(state, 0x86); + ret |= m88rs2000_writereg(state, 0x86, reg); /* Offset lower nibble always 0 */ - ret |= m88rs2000_demod_write(state, 0x9c, (offset >> 8)); - ret |= m88rs2000_demod_write(state, 0x9d, offset & 0xf0); + ret |= m88rs2000_writereg(state, 0x9c, (offset >> 8)); + ret |= m88rs2000_writereg(state, 0x9d, offset & 0xf0); /* Reset Demod */ @@ -794,16 +592,16 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) return -ENODEV; /* Unknown */ - reg = m88rs2000_demod_read(state, 0x70); - ret = m88rs2000_demod_write(state, 0x70, reg); + reg = m88rs2000_readreg(state, 0x70); + ret = m88rs2000_writereg(state, 0x70, reg); /* Set FEC */ ret |= m88rs2000_set_fec(state, c->fec_inner); - ret |= m88rs2000_demod_write(state, 0x85, 0x1); - ret |= m88rs2000_demod_write(state, 0x8a, 0xbf); - ret |= m88rs2000_demod_write(state, 0x8d, 0x1e); - ret |= m88rs2000_demod_write(state, 0x90, 0xf1); - ret |= m88rs2000_demod_write(state, 0x91, 0x08); + ret |= m88rs2000_writereg(state, 0x85, 0x1); + ret |= m88rs2000_writereg(state, 0x8a, 0xbf); + ret |= m88rs2000_writereg(state, 0x8d, 0x1e); + ret |= m88rs2000_writereg(state, 0x90, 0xf1); + ret |= m88rs2000_writereg(state, 0x91, 0x08); if (ret < 0) return -ENODEV; @@ -819,27 +617,25 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) return -ENODEV; for (i = 0; i < 25; i++) { - reg = m88rs2000_demod_read(state, 0x8c); + reg = m88rs2000_readreg(state, 0x8c); if ((reg & 0x7) == 0x7) { status = FE_HAS_LOCK; break; } state->no_lock_count++; if (state->no_lock_count == 15) { - reg = m88rs2000_demod_read(state, 0x70); + reg = m88rs2000_readreg(state, 0x70); reg ^= 0x4; - m88rs2000_demod_write(state, 0x70, reg); + m88rs2000_writereg(state, 0x70, reg); state->no_lock_count = 0; } - if (state->no_lock_count == 20) - m88rs2000_set_tuner_rf(fe); msleep(20); } if (status & FE_HAS_LOCK) { state->fec_inner = m88rs2000_get_fec(state); /* Uknown suspect SNR level */ - reg = m88rs2000_demod_read(state, 0x65); + reg = m88rs2000_readreg(state, 0x65); } state->tuner_frequency = c->frequency; @@ -862,9 +658,9 @@ static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) struct m88rs2000_state *state = fe->demodulator_priv; if (enable) - m88rs2000_demod_write(state, 0x81, 0x84); + m88rs2000_writereg(state, 0x81, 0x84); else - m88rs2000_demod_write(state, 0x81, 0x81); + m88rs2000_writereg(state, 0x81, 0x81); udelay(10); return 0; } @@ -895,7 +691,6 @@ static struct dvb_frontend_ops m88rs2000_ops = { .release = m88rs2000_release, .init = m88rs2000_init, .sleep = m88rs2000_sleep, - .write = m88rs2000_write, .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl, .read_status = m88rs2000_read_status, .read_ber = m88rs2000_read_ber, @@ -928,9 +723,6 @@ struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config, state->symbol_rate = 0; state->fec_inner = 0; - if (m88rs2000_startup(state) < 0) - goto error; - /* create dvb_frontend */ memcpy(&state->frontend.ops, &m88rs2000_ops, sizeof(struct dvb_frontend_ops)); diff --git a/drivers/media/dvb-frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h index 59acdb696873..5a8023e5a4b8 100644 --- a/drivers/media/dvb-frontends/m88rs2000.h +++ b/drivers/media/dvb-frontends/m88rs2000.h @@ -26,8 +26,6 @@ struct m88rs2000_config { /* Demodulator i2c address */ u8 demod_addr; - /* Tuner address */ - u8 tuner_addr; u8 *inittab; @@ -55,12 +53,8 @@ static inline struct dvb_frontend *m88rs2000_attach( } #endif /* CONFIG_DVB_M88RS2000 */ -#define FE_CRYSTAL_KHZ 27000 -#define FREQ_OFFSET_LOW_SYM_RATE 3000 - enum { DEMOD_WRITE = 0x1, - TUNER_WRITE, WRITE_DELAY = 0x10, }; #endif /* M88RS2000_H */ diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 73010ecb9866..94e3fe21eefb 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -23,27 +23,68 @@ #include "ts2020.h" #define TS2020_XTAL_FREQ 27000 /* in kHz */ +#define FREQ_OFFSET_LOW_SYM_RATE 3000 -struct ts2020_state { - u8 tuner_address; +struct ts2020_priv { + /* i2c details */ + int i2c_address; struct i2c_adapter *i2c; + u8 clk_out_div; + u32 frequency; }; +static int ts2020_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int ts2020_writereg(struct dvb_frontend *fe, int reg, int data) +{ + struct ts2020_priv *priv = fe->tuner_priv; + u8 buf[] = { reg, data }; + struct i2c_msg msg[] = { + { + .addr = priv->i2c_address, + .flags = 0, + .buf = buf, + .len = 2 + } + }; + int err; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + err = i2c_transfer(priv->i2c, msg, 1); + if (err != 1) { + printk(KERN_ERR + "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n", + __func__, err, reg, data); + return -EREMOTEIO; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + static int ts2020_readreg(struct dvb_frontend *fe, u8 reg) { - struct ts2020_state *state = fe->tuner_priv; - + struct ts2020_priv *priv = fe->tuner_priv; int ret; u8 b0[] = { reg }; u8 b1[] = { 0 }; struct i2c_msg msg[] = { { - .addr = state->tuner_address, + .addr = priv->i2c_address, .flags = 0, .buf = b0, .len = 1 }, { - .addr = state->tuner_address, + .addr = priv->i2c_address, .flags = I2C_M_RD, .buf = b1, .len = 1 @@ -53,212 +94,202 @@ static int ts2020_readreg(struct dvb_frontend *fe, u8 reg) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - ret = i2c_transfer(state->i2c, msg, 2); + ret = i2c_transfer(priv->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", + __func__, reg, ret); + return ret; + } if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); - if (ret != 2) { - printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); - return ret; - } - return b1[0]; } -static int ts2020_writereg(struct dvb_frontend *fe, int reg, int data) +static int ts2020_sleep(struct dvb_frontend *fe) { - struct ts2020_state *state = fe->tuner_priv; - - u8 buf[] = { reg, data }; - struct i2c_msg msg = { .addr = state->tuner_address, - .flags = 0, .buf = buf, .len = 2 }; - int err; - + struct ts2020_priv *priv = fe->tuner_priv; + int ret; + u8 buf[] = { 10, 0 }; + struct i2c_msg msg = { + .addr = priv->i2c_address, + .flags = 0, + .buf = buf, + .len = 2 + }; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - err = i2c_transfer(state->i2c, &msg, 1); + ret = i2c_transfer(priv->i2c, &msg, 1); + if (ret != 1) + printk(KERN_ERR "%s: i2c error\n", __func__); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); - if (err != 1) { - printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," - " value == 0x%02x)\n", __func__, err, reg, data); - return -EREMOTEIO; - } - - return 0; + return (ret == 1) ? 0 : ret; } static int ts2020_init(struct dvb_frontend *fe) { + struct ts2020_priv *priv = fe->tuner_priv; + ts2020_writereg(fe, 0x42, 0x73); - ts2020_writereg(fe, 0x05, 0x01); - ts2020_writereg(fe, 0x62, 0xf5); + ts2020_writereg(fe, 0x05, priv->clk_out_div); + ts2020_writereg(fe, 0x20, 0x27); + ts2020_writereg(fe, 0x07, 0x02); + ts2020_writereg(fe, 0x11, 0xff); + ts2020_writereg(fe, 0x60, 0xf9); + ts2020_writereg(fe, 0x08, 0x01); + ts2020_writereg(fe, 0x00, 0x41); + return 0; } -static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) +static int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset) { - u16 ndiv, div4; + int ret; + ret = ts2020_writereg(fe, 0x51, 0x1f - offset); + ret |= ts2020_writereg(fe, 0x51, 0x1f); + ret |= ts2020_writereg(fe, 0x50, offset); + ret |= ts2020_writereg(fe, 0x50, 0x00); + msleep(20); + return ret; +} - div4 = (ts2020_readreg(fe, 0x10) & 0x10) >> 4; +static int ts2020_set_tuner_rf(struct dvb_frontend *fe) +{ + int reg; - ndiv = ts2020_readreg(fe, 0x01); - ndiv &= 0x0f; - ndiv <<= 8; - ndiv |= ts2020_readreg(fe, 0x02); + reg = ts2020_readreg(fe, 0x3d); + reg &= 0x7f; + if (reg < 0x16) + reg = 0xa1; + else if (reg == 0x16) + reg = 0x99; + else + reg = 0xf9; - /* actual tuned frequency, i.e. including the offset */ - *frequency = (ndiv - ndiv % 2 + 1024) * TS2020_XTAL_FREQ - / (6 + 8) / (div4 + 1) / 2; + ts2020_writereg(fe, 0x60, reg); + reg = ts2020_tuner_gate_ctrl(fe, 0x08); - return 0; + return reg; } static int ts2020_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct ts2020_priv *priv = fe->demodulator_priv; + int ret; + u32 frequency = c->frequency; + s32 offset_khz; + u32 symbol_rate = (c->symbol_rate / 1000); + u32 f3db, gdiv28; + u16 value, ndiv, lpf_coeff; + u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf; + u8 lo = 0x01, div4 = 0x0; - u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf; - u16 value, ndiv; - u32 srate = 0, f3db; + /* Calculate frequency divider */ + if (frequency < 1060000) { + lo |= 0x10; + div4 = 0x1; + ndiv = (frequency * 14 * 4) / TS2020_XTAL_FREQ; + } else + ndiv = (frequency * 14 * 2) / TS2020_XTAL_FREQ; + ndiv = ndiv + ndiv % 2; + ndiv = ndiv - 1024; - ts2020_init(fe); + ret = ts2020_writereg(fe, 0x10, 0x80 | lo); - /* unknown */ - ts2020_writereg(fe, 0x07, 0x02); - ts2020_writereg(fe, 0x10, 0x00); - ts2020_writereg(fe, 0x60, 0x79); - ts2020_writereg(fe, 0x08, 0x01); - ts2020_writereg(fe, 0x00, 0x01); + /* Set frequency divider */ + ret |= ts2020_writereg(fe, 0x01, (ndiv >> 8) & 0xf); + ret |= ts2020_writereg(fe, 0x02, ndiv & 0xff); - /* calculate and set freq divider */ - if (c->frequency < 1146000) { - ts2020_writereg(fe, 0x10, 0x11); - ndiv = ((c->frequency * (6 + 8) * 4) + - (TS2020_XTAL_FREQ / 2)) / - TS2020_XTAL_FREQ - 1024; - } else { - ts2020_writereg(fe, 0x10, 0x01); - ndiv = ((c->frequency * (6 + 8) * 2) + - (TS2020_XTAL_FREQ / 2)) / - TS2020_XTAL_FREQ - 1024; - } + ret |= ts2020_writereg(fe, 0x03, 0x06); + ret |= ts2020_tuner_gate_ctrl(fe, 0x10); + if (ret < 0) + return -ENODEV; - ts2020_writereg(fe, 0x01, (ndiv & 0x0f00) >> 8); - ts2020_writereg(fe, 0x02, ndiv & 0x00ff); + /* Tuner Frequency Range */ + ret = ts2020_writereg(fe, 0x10, lo); - /* set pll */ - ts2020_writereg(fe, 0x03, 0x06); - ts2020_writereg(fe, 0x51, 0x0f); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x10); - ts2020_writereg(fe, 0x50, 0x00); - msleep(5); + ret |= ts2020_tuner_gate_ctrl(fe, 0x08); - /* unknown */ - ts2020_writereg(fe, 0x51, 0x17); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x08); - ts2020_writereg(fe, 0x50, 0x00); - msleep(5); + /* Tuner RF */ + ret |= ts2020_set_tuner_rf(fe); - value = ts2020_readreg(fe, 0x3d); - value &= 0x0f; - if ((value > 4) && (value < 15)) { - value -= 3; - if (value < 4) - value = 4; - value = ((value << 3) | 0x01) & 0x79; - } + gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000; + ret |= ts2020_writereg(fe, 0x04, gdiv28 & 0xff); + ret |= ts2020_tuner_gate_ctrl(fe, 0x04); + if (ret < 0) + return -ENODEV; - ts2020_writereg(fe, 0x60, value); - ts2020_writereg(fe, 0x51, 0x17); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x08); - ts2020_writereg(fe, 0x50, 0x00); + value = ts2020_readreg(fe, 0x26); - /* set low-pass filter period */ - ts2020_writereg(fe, 0x04, 0x2e); - ts2020_writereg(fe, 0x51, 0x1b); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x04); - ts2020_writereg(fe, 0x50, 0x00); - msleep(5); - - srate = c->symbol_rate / 1000; - - f3db = (srate << 2) / 5 + 2000; - if (srate < 5000) - f3db += 3000; + f3db = (symbol_rate * 135) / 200 + 2000; + f3db += FREQ_OFFSET_LOW_SYM_RATE; if (f3db < 7000) f3db = 7000; if (f3db > 40000) f3db = 40000; - /* set low-pass filter baseband */ - value = ts2020_readreg(fe, 0x26); - mlpf = 0x2e * 207 / ((value << 1) + 151); - mlpf_max = mlpf * 135 / 100; - mlpf_min = mlpf * 78 / 100; + gdiv28 = gdiv28 * 207 / (value * 2 + 151); + mlpf_max = gdiv28 * 135 / 100; + mlpf_min = gdiv28 * 78 / 100; if (mlpf_max > 63) mlpf_max = 63; - /* rounded to the closest integer */ - nlpf = ((mlpf * f3db * 1000) + (2766 * TS2020_XTAL_FREQ / 2)) - / (2766 * TS2020_XTAL_FREQ); + lpf_coeff = 2766; + + nlpf = (f3db * gdiv28 * 2 / lpf_coeff / + (TS2020_XTAL_FREQ / 1000) + 1) / 2; if (nlpf > 23) nlpf = 23; if (nlpf < 1) nlpf = 1; - /* rounded to the closest integer */ - mlpf_new = ((TS2020_XTAL_FREQ * nlpf * 2766) + - (1000 * f3db / 2)) / (1000 * f3db); + lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) + * lpf_coeff * 2 / f3db + 1) / 2; - if (mlpf_new < mlpf_min) { + if (lpf_mxdiv < mlpf_min) { nlpf++; - mlpf_new = ((TS2020_XTAL_FREQ * nlpf * 2766) + - (1000 * f3db / 2)) / (1000 * f3db); + lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) + * lpf_coeff * 2 / f3db + 1) / 2; } - if (mlpf_new > mlpf_max) - mlpf_new = mlpf_max; + if (lpf_mxdiv > mlpf_max) + lpf_mxdiv = mlpf_max; - ts2020_writereg(fe, 0x04, mlpf_new); - ts2020_writereg(fe, 0x06, nlpf); - ts2020_writereg(fe, 0x51, 0x1b); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x04); - ts2020_writereg(fe, 0x50, 0x00); - msleep(5); + ret = ts2020_writereg(fe, 0x04, lpf_mxdiv); + ret |= ts2020_writereg(fe, 0x06, nlpf); - /* unknown */ - ts2020_writereg(fe, 0x51, 0x1e); - ts2020_writereg(fe, 0x51, 0x1f); - ts2020_writereg(fe, 0x50, 0x01); - ts2020_writereg(fe, 0x50, 0x00); - msleep(60); + ret |= ts2020_tuner_gate_ctrl(fe, 0x04); - return 0; + ret |= ts2020_tuner_gate_ctrl(fe, 0x01); + + msleep(80); + /* calculate offset assuming 96000kHz*/ + offset_khz = (ndiv - ndiv % 2 + 1024) * TS2020_XTAL_FREQ + / (6 + 8) / (div4 + 1) / 2; + + priv->frequency = offset_khz; + + return (ret < 0) ? -EINVAL : 0; } -static int ts2020_release(struct dvb_frontend *fe) +static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) { - struct ts2020_state *state = fe->tuner_priv; - - fe->tuner_priv = NULL; - kfree(state); - + struct ts2020_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; return 0; } -static int ts2020_get_signal_strength(struct dvb_frontend *fe, - u16 *signal_strength) +/* read TS2020 signal strength */ +static int ts2020_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) { u16 sig_reading, sig_strength; u8 rfgain, bbgain; @@ -281,35 +312,57 @@ static int ts2020_get_signal_strength(struct dvb_frontend *fe, return 0; } -static struct dvb_tuner_ops ts2020_ops = { +static struct dvb_tuner_ops ts2020_tuner_ops = { .info = { - .name = "Montage Technology TS2020 Silicon Tuner", + .name = "TS2020", .frequency_min = 950000, - .frequency_max = 2150000, + .frequency_max = 2150000 }, - .init = ts2020_init, .release = ts2020_release, + .sleep = ts2020_sleep, .set_params = ts2020_set_params, .get_frequency = ts2020_get_frequency, - .get_rf_strength = ts2020_get_signal_strength + .get_rf_strength = ts2020_read_signal_strength, }; struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, - const struct ts2020_config *config, struct i2c_adapter *i2c) + const struct ts2020_config *config, + struct i2c_adapter *i2c) { - struct ts2020_state *state = NULL; + struct ts2020_priv *priv = NULL; + u8 buf; - /* allocate memory for the internal state */ - state = kzalloc(sizeof(struct ts2020_state), GFP_KERNEL); - if (!state) + priv = kzalloc(sizeof(struct ts2020_priv), GFP_KERNEL); + if (priv == NULL) return NULL; - /* setup the state */ - state->tuner_address = config->tuner_address; - state->i2c = i2c; - fe->tuner_priv = state; - fe->ops.tuner_ops = ts2020_ops; + priv->i2c_address = config->tuner_address; + priv->i2c = i2c; + priv->clk_out_div = config->clk_out_div; + fe->tuner_priv = priv; + + /* Wake Up the tuner */ + if ((0x03 & ts2020_readreg(fe, 0x00)) == 0x00) { + ts2020_writereg(fe, 0x00, 0x01); + msleep(2); + } + + ts2020_writereg(fe, 0x00, 0x03); + msleep(2); + + /* Check the tuner version */ + buf = ts2020_readreg(fe, 0x00); + if ((buf == 0x01) || (buf == 0x41) || (buf == 0x81)) + printk(KERN_INFO "%s: Find tuner TS2020!\n", __func__); + else { + printk(KERN_ERR "%s: Read tuner reg[0] = %d\n", __func__, buf); + kfree(priv); + return NULL; + } + + memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops, + sizeof(struct dvb_tuner_ops)); fe->ops.read_signal_strength = fe->ops.tuner_ops.get_rf_strength; return fe; diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h index 13a172dfd582..c7e64afa614a 100644 --- a/drivers/media/dvb-frontends/ts2020.h +++ b/drivers/media/dvb-frontends/ts2020.h @@ -26,6 +26,7 @@ struct ts2020_config { u8 tuner_address; + u8 clk_out_div; }; #if defined(CONFIG_DVB_TS2020) || \ diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index a2ed0f759b0a..9c5ed10b2c5e 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -474,6 +474,7 @@ static struct ds3000_config tevii_ds3000_config = { static struct ts2020_config tevii_ts2020_config = { .tuner_address = 0x60, + .clk_out_div = 1, }; static struct cx24116_config dvbworld_cx24116_config = { @@ -500,20 +501,20 @@ static struct xc5000_config mygica_x8506_xc5000_config = { }; static struct stv090x_config prof_8000_stv090x_config = { - .device = STV0903, - .demod_mode = STV090x_SINGLE, - .clk_mode = STV090x_CLK_EXT, - .xtal = 27000000, - .address = 0x6A, - .ts1_mode = STV090x_TSMODE_PARALLEL_PUNCTURED, - .repeater_level = STV090x_RPTLEVEL_64, - .adc1_range = STV090x_ADC_2Vpp, - .diseqc_envelope_mode = false, + .device = STV0903, + .demod_mode = STV090x_SINGLE, + .clk_mode = STV090x_CLK_EXT, + .xtal = 27000000, + .address = 0x6A, + .ts1_mode = STV090x_TSMODE_PARALLEL_PUNCTURED, + .repeater_level = STV090x_RPTLEVEL_64, + .adc1_range = STV090x_ADC_2Vpp, + .diseqc_envelope_mode = false, - .tuner_get_frequency = stb6100_get_frequency, - .tuner_set_frequency = stb6100_set_frequency, - .tuner_set_bandwidth = stb6100_set_bandwidth, - .tuner_get_bandwidth = stb6100_get_bandwidth, + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, }; static struct stb6100_config prof_8000_stb6100_config = { diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index e085851d6872..50b5ac5b3983 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -703,6 +703,7 @@ static struct ds3000_config tevii_ds3000_config = { static struct ts2020_config tevii_ts2020_config = { .tuner_address = 0x60, + .clk_out_div = 1, }; static const struct stv0900_config prof_7301_stv0900_config = { diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index 79fe74aae1f9..c789e7c67e4e 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -852,6 +852,7 @@ static struct ds3000_config dvbworld_ds3000_config = { static struct ts2020_config dvbworld_ts2020_config = { .tuner_address = 0x60, + .clk_out_div = 1, }; static int __devinit frontend_init(struct dm1105_dev *dev) diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 834bfecbed73..3240d559ef7b 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -120,6 +120,7 @@ config DVB_USB_LME2510 select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT help Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 6427ac359f21..b5e1f736eb7e 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -81,6 +81,7 @@ #include "dvb-pll.h" #include "z0194a.h" #include "m88rs2000.h" +#include "ts2020.h" #define LME2510_C_S7395 "dvb-usb-lme2510c-s7395.fw"; @@ -944,10 +945,14 @@ static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe, static struct m88rs2000_config m88rs2000_config = { .demod_addr = 0xd0, - .tuner_addr = 0xc0, .set_ts_params = dm04_rs2000_set_ts_param, }; +static struct ts2020_config ts2020_config = { + .tuner_address = 0x60, + .clk_out_div = 7, +}; + static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { @@ -1097,6 +1102,8 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) if (adap->fe[0]) { info("FE Found M88RS2000"); + dvb_attach(ts2020_attach, adap->fe[0], &ts2020_config, + &d->i2c_adap); st->i2c_tuner_gate_w = 5; st->i2c_tuner_gate_r = 5; st->i2c_tuner_addr = 0xc0; diff --git a/drivers/media/usb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c index d8a5ebb43626..9578a6761f1b 100644 --- a/drivers/media/usb/dvb-usb/dw2102.c +++ b/drivers/media/usb/dvb-usb/dw2102.c @@ -29,6 +29,7 @@ #include "stb6100.h" #include "stb6100_proc.h" #include "m88rs2000.h" +#include "ts2020.h" #ifndef USB_PID_DW2102 #define USB_PID_DW2102 0x2102 @@ -953,10 +954,12 @@ static struct ds3000_config dw2104_ds3000_config = { static struct ts2020_config dw2104_ts2020_config = { .tuner_address = 0x60, + .clk_out_div = 1, }; static struct ds3000_config s660_ds3000_config = { .demod_address = 0x68, + .ci_mode = 1, .set_lock_led = dw210x_led_ctrl, }; @@ -1009,10 +1012,7 @@ static struct stv0900_config prof_7500_stv0900_config = { static struct ds3000_config su3000_ds3000_config = { .demod_address = 0x68, .ci_mode = 1, -}; - -static struct ts2020_config su3000_ts2020_config = { - .tuner_address = 0x60, + .set_lock_led = dw210x_led_ctrl, }; static u8 m88rs2000_inittab[] = { @@ -1022,14 +1022,6 @@ static u8 m88rs2000_inittab[] = { DEMOD_WRITE, 0x00, 0x00, DEMOD_WRITE, 0x9a, 0xb0, DEMOD_WRITE, 0x81, 0xc1, - TUNER_WRITE, 0x42, 0x73, - TUNER_WRITE, 0x05, 0x07, - TUNER_WRITE, 0x20, 0x27, - TUNER_WRITE, 0x07, 0x02, - TUNER_WRITE, 0x11, 0xff, - TUNER_WRITE, 0x60, 0xf9, - TUNER_WRITE, 0x08, 0x01, - TUNER_WRITE, 0x00, 0x41, DEMOD_WRITE, 0x81, 0x81, DEMOD_WRITE, 0x86, 0xc6, DEMOD_WRITE, 0x9a, 0x30, @@ -1043,7 +1035,6 @@ static u8 m88rs2000_inittab[] = { static struct m88rs2000_config s421_m88rs2000_config = { .demod_addr = 0x68, - .tuner_addr = 0x60, .inittab = m88rs2000_inittab, }; @@ -1250,6 +1241,14 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) err("command 0x0e transfer failed."); + obuf[0] = 0xe; + obuf[1] = 0x02; + obuf[2] = 1; + + if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) + err("command 0x0e transfer failed."); + msleep(300); + obuf[0] = 0xe; obuf[1] = 0x83; obuf[2] = 0; @@ -1274,12 +1273,15 @@ static int su3000_frontend_attach(struct dvb_usb_adapter *d) if (d->fe_adap[0].fe == NULL) return -EIO; - dvb_attach(ts2020_attach, d->fe_adap[0].fe, &su3000_ts2020_config, - &d->dev->i2c_adap); + if (dvb_attach(ts2020_attach, d->fe_adap[0].fe, + &dw2104_ts2020_config, + &d->dev->i2c_adap)) { + info("Attached DS3000/TS2020!\n"); + return 0; + } - info("Attached DS3000!\n"); - - return 0; + info("Failed to attach DS3000/TS2020!\n"); + return -EIO; } static int m88rs2000_frontend_attach(struct dvb_usb_adapter *d) @@ -1292,12 +1294,19 @@ static int m88rs2000_frontend_attach(struct dvb_usb_adapter *d) d->fe_adap[0].fe = dvb_attach(m88rs2000_attach, &s421_m88rs2000_config, &d->dev->i2c_adap); + if (d->fe_adap[0].fe == NULL) return -EIO; - info("Attached m88rs2000!\n"); + if (dvb_attach(ts2020_attach, d->fe_adap[0].fe, + &dw2104_ts2020_config, + &d->dev->i2c_adap)) { + info("Attached RS2000/TS2020!\n"); + return 0; + } - return 0; + info("Failed to attach RS2000/TS2020!\n"); + return -EIO; } static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) From e003ae399c160e00c1a882dc6dd4f0ef855ae616 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 23 Nov 2012 08:12:35 -0300 Subject: [PATCH 0491/4607] [media] stk1160: Replace BUG_ON with WARN_ON This situation is not even an error condition so it's stupid to BUG_ON. Learn the lesson: http://permalink.gmane.org/gmane.linux.kernel/1347333 Signed-off-by: Ezequiel Garcia Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/stk1160/stk1160-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index 0a4ee85f4399..39f1aae209bc 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -78,7 +78,7 @@ struct stk1160_buffer *stk1160_next_buffer(struct stk1160 *dev) unsigned long flags = 0; /* Current buffer must be NULL when this functions gets called */ - BUG_ON(dev->isoc_ctl.buf); + WARN_ON(dev->isoc_ctl.buf); spin_lock_irqsave(&dev->buf_lock, flags); if (!list_empty(&dev->avail_bufs)) { From 71dc98becc3ddc9775f6e54485929927dd106b6e Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sat, 29 Dec 2012 07:34:24 -0300 Subject: [PATCH 0492/4607] [media] lmedm04: correct I2C values to 7 bit addressing The separation the lmedm04 fails on the ts2020 portion because the correct I2C addressing. So, it's time to correct the addressing in the remainder of lmedm04. Tested all tuners. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/lmedm04.c | 29 +++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index b5e1f736eb7e..f30c58cecbba 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -627,8 +627,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], gate = 5; for (i = 0; i < num; i++) { - read_o = 1 & (msg[i].flags & I2C_M_RD); - read = i+1 < num && (msg[i+1].flags & I2C_M_RD); + read_o = msg[i].flags & I2C_M_RD; + read = i + 1 < num && msg[i + 1].flags & I2C_M_RD; read |= read_o; gate = (msg[i].addr == st->i2c_tuner_addr) ? (read) ? st->i2c_tuner_gate_r @@ -641,7 +641,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], else obuf[1] = msg[i].len + read + 1; - obuf[2] = msg[i].addr; + obuf[2] = msg[i].addr << 1; + if (read) { if (read_o) len = 3; @@ -895,27 +896,27 @@ static int lme2510_kill_urb(struct usb_data_stream *stream) } static struct tda10086_config tda10086_config = { - .demod_address = 0x1c, + .demod_address = 0x0e, .invert = 0, .diseqc_tone = 1, .xtal_freq = TDA10086_XTAL_16M, }; static struct stv0288_config lme_config = { - .demod_address = 0xd0, + .demod_address = 0x68, .min_delay_ms = 15, .inittab = s7395_inittab, }; static struct ix2505v_config lme_tuner = { - .tuner_address = 0xc0, + .tuner_address = 0x60, .min_delay_ms = 100, .tuner_gain = 0x0, .tuner_chargepump = 0x3, }; static struct stv0299_config sharp_z0194_config = { - .demod_address = 0xd0, + .demod_address = 0x68, .inittab = sharp_z0194a_inittab, .mclk = 88000000UL, .invert = 0, @@ -944,7 +945,7 @@ static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe, } static struct m88rs2000_config m88rs2000_config = { - .demod_addr = 0xd0, + .demod_addr = 0x68, .set_ts_params = dm04_rs2000_set_ts_param, }; @@ -1054,7 +1055,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) info("TUN Found Frontend TDA10086"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 4; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_LG; if (st->dvb_usb_lme2510_firmware != TUNER_LG) { st->dvb_usb_lme2510_firmware = TUNER_LG; @@ -1070,7 +1071,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) info("FE Found Stv0299"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 5; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_S0194; if (st->dvb_usb_lme2510_firmware != TUNER_S0194) { st->dvb_usb_lme2510_firmware = TUNER_S0194; @@ -1087,7 +1088,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) info("FE Found Stv0288"); st->i2c_tuner_gate_w = 4; st->i2c_tuner_gate_r = 5; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_S7395; if (st->dvb_usb_lme2510_firmware != TUNER_S7395) { st->dvb_usb_lme2510_firmware = TUNER_S7395; @@ -1106,7 +1107,7 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) &d->i2c_adap); st->i2c_tuner_gate_w = 5; st->i2c_tuner_gate_r = 5; - st->i2c_tuner_addr = 0xc0; + st->i2c_tuner_addr = 0x60; st->tuner_config = TUNER_RS2000; st->fe_set_voltage = adap->fe[0]->ops.set_voltage; @@ -1151,7 +1152,7 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) switch (st->tuner_config) { case TUNER_LG: - if (dvb_attach(tda826x_attach, adap->fe[0], 0xc0, + if (dvb_attach(tda826x_attach, adap->fe[0], 0x60, &d->i2c_adap, 1)) ret = st->tuner_config; break; @@ -1161,7 +1162,7 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) ret = st->tuner_config; break; case TUNER_S0194: - if (dvb_attach(dvb_pll_attach , adap->fe[0], 0xc0, + if (dvb_attach(dvb_pll_attach , adap->fe[0], 0x60, &d->i2c_adap, DVB_PLL_OPERA1)) ret = st->tuner_config; break; From 8cd7085ff460ead3aba6174052a408f4ad52ac36 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Tue, 1 Jan 2013 08:54:26 -0300 Subject: [PATCH 0493/4607] [media] get_dvb_firmware: Fix the location of firmware for Terratec HTC Fix firmware download for the Terratec Cinergy HTC Stick HD. The file was moved on the server. Signed-off-by: Martin Blumenstingl Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/get_dvb_firmware | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index 0cdb1570ffb4..5d5ee4c13fa6 100755 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -690,7 +690,7 @@ sub drxk_terratec_h5 { } sub drxk_terratec_htc_stick { - my $url = "http://ftp.terratec.de/Receiver/Cinergy_HTC_Stick/Updates/"; + my $url = "http://ftp.terratec.de/Receiver/Cinergy_HTC_Stick/Updates/History/"; my $zipfile = "Cinergy_HTC_Stick_Drv_5.09.1202.00_XP_Vista_7.exe"; my $hash = "6722a2442a05423b781721fbc069ed5e"; my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 0); From 11393a077dcfa7fb827d957f0305fc369d402a5e Mon Sep 17 00:00:00 2001 From: Jesse Larrew Date: Mon, 10 Dec 2012 15:31:51 -0600 Subject: [PATCH 0494/4607] x86: kvm_para: fix typo in hypercall comments Correct a typo in the comment explaining hypercalls. Signed-off-by: Jesse Larrew Signed-off-by: Marcelo Tosatti --- arch/x86/include/asm/kvm_para.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/kvm_para.h b/arch/x86/include/asm/kvm_para.h index eb3e9d85e1f1..f49c16d47581 100644 --- a/arch/x86/include/asm/kvm_para.h +++ b/arch/x86/include/asm/kvm_para.h @@ -122,7 +122,7 @@ static inline bool kvm_check_and_clear_guest_paused(void) * * Up to four arguments may be passed in rbx, rcx, rdx, and rsi respectively. * The hypercall number should be placed in rax and the return value will be - * placed in rax. No other registers will be clobbered unless explicited + * placed in rax. No other registers will be clobbered unless explicitly * noted by the particular hypercall. */ From 3a78a4f46302bfc83602a53dfa4dcbe76a7a1f5f Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Thu, 20 Dec 2012 16:57:42 +0200 Subject: [PATCH 0495/4607] KVM: emulator: drop RPL check from linearize() function According to Intel SDM Vol3 Section 5.5 "Privilege Levels" and 5.6 "Privilege Level Checking When Accessing Data Segments" RPL checking is done during loading of a segment selector, not during data access. We already do checking during segment selector loading, so drop the check during data access. Checking RPL during data access triggers #GP if after transition from real mode to protected mode RPL bits in a segment selector are set. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index c7547b3fd527..a3d31e3d9875 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -665,7 +665,7 @@ static int __linearize(struct x86_emulate_ctxt *ctxt, ulong la; u32 lim; u16 sel; - unsigned cpl, rpl; + unsigned cpl; la = seg_base(ctxt, addr.seg) + addr.ea; switch (ctxt->mode) { @@ -699,11 +699,6 @@ static int __linearize(struct x86_emulate_ctxt *ctxt, goto bad; } cpl = ctxt->ops->cpl(ctxt); - if (ctxt->mode == X86EMUL_MODE_REAL) - rpl = 0; - else - rpl = sel & 3; - cpl = max(cpl, rpl); if (!(desc.type & 8)) { /* data segment */ if (cpl > desc.dpl) From 045a282ca41505184e8fc805335d1f5aae0c8a03 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Thu, 20 Dec 2012 16:57:43 +0200 Subject: [PATCH 0496/4607] KVM: emulator: implement fninit, fnstsw, fnstcw Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 126 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index a3d31e3d9875..53c5ad6851d1 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -115,6 +115,7 @@ #define GroupDual (2<<15) /* Alternate decoding of mod == 3 */ #define Prefix (3<<15) /* Instruction varies with 66/f2/f3 prefix */ #define RMExt (4<<15) /* Opcode extension in ModRM r/m if mod == 3 */ +#define Escape (5<<15) /* Escape to coprocessor instruction */ #define Sse (1<<18) /* SSE Vector instruction */ /* Generic ModRM decode. */ #define ModRM (1<<19) @@ -166,6 +167,7 @@ struct opcode { const struct opcode *group; const struct group_dual *gdual; const struct gprefix *gprefix; + const struct escape *esc; } u; int (*check_perm)(struct x86_emulate_ctxt *ctxt); }; @@ -182,6 +184,11 @@ struct gprefix { struct opcode pfx_f3; }; +struct escape { + struct opcode op[8]; + struct opcode high[64]; +}; + /* EFLAGS bit definitions. */ #define EFLG_ID (1<<21) #define EFLG_VIP (1<<20) @@ -991,6 +998,53 @@ static void write_mmx_reg(struct x86_emulate_ctxt *ctxt, u64 *data, int reg) ctxt->ops->put_fpu(ctxt); } +static int em_fninit(struct x86_emulate_ctxt *ctxt) +{ + if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM)) + return emulate_nm(ctxt); + + ctxt->ops->get_fpu(ctxt); + asm volatile("fninit"); + ctxt->ops->put_fpu(ctxt); + return X86EMUL_CONTINUE; +} + +static int em_fnstcw(struct x86_emulate_ctxt *ctxt) +{ + u16 fcw; + + if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM)) + return emulate_nm(ctxt); + + ctxt->ops->get_fpu(ctxt); + asm volatile("fnstcw %0": "+m"(fcw)); + ctxt->ops->put_fpu(ctxt); + + /* force 2 byte destination */ + ctxt->dst.bytes = 2; + ctxt->dst.val = fcw; + + return X86EMUL_CONTINUE; +} + +static int em_fnstsw(struct x86_emulate_ctxt *ctxt) +{ + u16 fsw; + + if (ctxt->ops->get_cr(ctxt, 0) & (X86_CR0_TS | X86_CR0_EM)) + return emulate_nm(ctxt); + + ctxt->ops->get_fpu(ctxt); + asm volatile("fnstsw %0": "+m"(fsw)); + ctxt->ops->put_fpu(ctxt); + + /* force 2 byte destination */ + ctxt->dst.bytes = 2; + ctxt->dst.val = fsw; + + return X86EMUL_CONTINUE; +} + static void decode_register_operand(struct x86_emulate_ctxt *ctxt, struct operand *op) { @@ -3590,6 +3644,7 @@ static int check_perm_out(struct x86_emulate_ctxt *ctxt) #define EXT(_f, _e) { .flags = ((_f) | RMExt), .u.group = (_e) } #define G(_f, _g) { .flags = ((_f) | Group | ModRM), .u.group = (_g) } #define GD(_f, _g) { .flags = ((_f) | GroupDual | ModRM), .u.gdual = (_g) } +#define E(_f, _e) { .flags = ((_f) | Escape | ModRM), .u.esc = (_e) } #define I(_f, _e) { .flags = (_f), .u.execute = (_e) } #define II(_f, _e, _i) \ { .flags = (_f), .u.execute = (_e), .intercept = x86_intercept_##_i } @@ -3725,6 +3780,69 @@ static const struct gprefix pfx_vmovntpx = { I(0, em_mov), N, N, N, }; +static const struct escape escape_d9 = { { + N, N, N, N, N, N, N, I(DstMem, em_fnstcw), +}, { + /* 0xC0 - 0xC7 */ + N, N, N, N, N, N, N, N, + /* 0xC8 - 0xCF */ + N, N, N, N, N, N, N, N, + /* 0xD0 - 0xC7 */ + N, N, N, N, N, N, N, N, + /* 0xD8 - 0xDF */ + N, N, N, N, N, N, N, N, + /* 0xE0 - 0xE7 */ + N, N, N, N, N, N, N, N, + /* 0xE8 - 0xEF */ + N, N, N, N, N, N, N, N, + /* 0xF0 - 0xF7 */ + N, N, N, N, N, N, N, N, + /* 0xF8 - 0xFF */ + N, N, N, N, N, N, N, N, +} }; + +static const struct escape escape_db = { { + N, N, N, N, N, N, N, N, +}, { + /* 0xC0 - 0xC7 */ + N, N, N, N, N, N, N, N, + /* 0xC8 - 0xCF */ + N, N, N, N, N, N, N, N, + /* 0xD0 - 0xC7 */ + N, N, N, N, N, N, N, N, + /* 0xD8 - 0xDF */ + N, N, N, N, N, N, N, N, + /* 0xE0 - 0xE7 */ + N, N, N, I(ImplicitOps, em_fninit), N, N, N, N, + /* 0xE8 - 0xEF */ + N, N, N, N, N, N, N, N, + /* 0xF0 - 0xF7 */ + N, N, N, N, N, N, N, N, + /* 0xF8 - 0xFF */ + N, N, N, N, N, N, N, N, +} }; + +static const struct escape escape_dd = { { + N, N, N, N, N, N, N, I(DstMem, em_fnstsw), +}, { + /* 0xC0 - 0xC7 */ + N, N, N, N, N, N, N, N, + /* 0xC8 - 0xCF */ + N, N, N, N, N, N, N, N, + /* 0xD0 - 0xC7 */ + N, N, N, N, N, N, N, N, + /* 0xD8 - 0xDF */ + N, N, N, N, N, N, N, N, + /* 0xE0 - 0xE7 */ + N, N, N, N, N, N, N, N, + /* 0xE8 - 0xEF */ + N, N, N, N, N, N, N, N, + /* 0xF0 - 0xF7 */ + N, N, N, N, N, N, N, N, + /* 0xF8 - 0xFF */ + N, N, N, N, N, N, N, N, +} }; + static const struct opcode opcode_table[256] = { /* 0x00 - 0x07 */ I6ALU(Lock, em_add), @@ -3821,7 +3939,7 @@ static const struct opcode opcode_table[256] = { D2bv(DstMem | SrcOne | ModRM), D2bv(DstMem | ModRM), N, I(DstAcc | SrcImmByte | No64, em_aad), N, N, /* 0xD8 - 0xDF */ - N, N, N, N, N, N, N, N, + N, E(0, &escape_d9), N, E(0, &escape_db), N, E(0, &escape_dd), N, N, /* 0xE0 - 0xE7 */ X3(I(SrcImmByte, em_loop)), I(SrcImmByte, em_jcxz), @@ -4246,6 +4364,12 @@ done_prefixes: case 0xf3: opcode = opcode.u.gprefix->pfx_f3; break; } break; + case Escape: + if (ctxt->modrm > 0xbf) + opcode = opcode.u.esc->high[ctxt->modrm - 0xc0]; + else + opcode = opcode.u.esc->op[(ctxt->modrm >> 3) & 7]; + break; default: return EMULATION_FAILED; } From 89efbed02cfd7e9ce3324de0b44a70ee1c716fac Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Thu, 20 Dec 2012 16:57:44 +0200 Subject: [PATCH 0497/4607] KVM: VMX: make rmode_segment_valid() more strict. Currently it allows entering vm86 mode if segment limit is greater than 0xffff and db bit is set. Both of those can cause incorrect execution of instruction by cpu since in vm86 mode limit will be set to 0xffff and db will be forced to 0. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 23d5aec78073..7ebcac25725b 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3341,15 +3341,13 @@ static bool rmode_segment_valid(struct kvm_vcpu *vcpu, int seg) vmx_get_segment(vcpu, &var, seg); var.dpl = 0x3; - var.g = 0; - var.db = 0; if (seg == VCPU_SREG_CS) var.type = 0x3; ar = vmx_segment_access_rights(&var); if (var.base != (var.selector << 4)) return false; - if (var.limit < 0xffff) + if (var.limit != 0xffff) return false; if (ar != 0xf3) return false; From d99e415275dd3f757b75981adad8645cdc26da45 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Thu, 20 Dec 2012 16:57:45 +0200 Subject: [PATCH 0498/4607] KVM: VMX: fix emulation of invalid guest state. Currently when emulation of invalid guest state is enable (emulate_invalid_guest_state=1) segment registers are still fixed for entry to vm86 mode some times. Segment register fixing is avoided in enter_rmode(), but vmx_set_segment() still does it unconditionally. The patch fixes it. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 122 +++++++++++++++++++++++++-------------------- 1 file changed, 68 insertions(+), 54 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 7ebcac25725b..9dff310a2e95 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -624,6 +624,8 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); static void vmx_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); +static bool guest_state_valid(struct kvm_vcpu *vcpu); +static u32 vmx_segment_access_rights(struct kvm_segment *var); static DEFINE_PER_CPU(struct vmcs *, vmxarea); static DEFINE_PER_CPU(struct vmcs *, current_vmcs); @@ -2758,18 +2760,23 @@ static __exit void hardware_unsetup(void) free_kvm_area(); } -static void fix_pmode_dataseg(struct kvm_vcpu *vcpu, int seg, struct kvm_segment *save) +static void fix_pmode_dataseg(struct kvm_vcpu *vcpu, int seg, + struct kvm_segment *save) { - const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; - struct kvm_segment tmp = *save; - - if (!(vmcs_readl(sf->base) == tmp.base && tmp.s)) { - tmp.base = vmcs_readl(sf->base); - tmp.selector = vmcs_read16(sf->selector); - tmp.dpl = tmp.selector & SELECTOR_RPL_MASK; - tmp.s = 1; + if (!emulate_invalid_guest_state) { + /* + * CS and SS RPL should be equal during guest entry according + * to VMX spec, but in reality it is not always so. Since vcpu + * is in the middle of the transition from real mode to + * protected mode it is safe to assume that RPL 0 is a good + * default value. + */ + if (seg == VCPU_SREG_CS || seg == VCPU_SREG_SS) + save->selector &= ~SELECTOR_RPL_MASK; + save->dpl = save->selector & SELECTOR_RPL_MASK; + save->s = 1; } - vmx_set_segment(vcpu, &tmp, seg); + vmx_set_segment(vcpu, save, seg); } static void enter_pmode(struct kvm_vcpu *vcpu) @@ -2777,6 +2784,17 @@ static void enter_pmode(struct kvm_vcpu *vcpu) unsigned long flags; struct vcpu_vmx *vmx = to_vmx(vcpu); + /* + * Update real mode segment cache. It may be not up-to-date if sement + * register was written while vcpu was in a guest mode. + */ + vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_ES], VCPU_SREG_ES); + vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_DS], VCPU_SREG_DS); + vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_FS], VCPU_SREG_FS); + vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_GS], VCPU_SREG_GS); + vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_SS], VCPU_SREG_SS); + vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_CS], VCPU_SREG_CS); + vmx->emulation_required = 1; vmx->rmode.vm86_active = 0; @@ -2794,22 +2812,12 @@ static void enter_pmode(struct kvm_vcpu *vcpu) update_exception_bitmap(vcpu); - if (emulate_invalid_guest_state) - return; - + fix_pmode_dataseg(vcpu, VCPU_SREG_CS, &vmx->rmode.segs[VCPU_SREG_CS]); + fix_pmode_dataseg(vcpu, VCPU_SREG_SS, &vmx->rmode.segs[VCPU_SREG_SS]); fix_pmode_dataseg(vcpu, VCPU_SREG_ES, &vmx->rmode.segs[VCPU_SREG_ES]); fix_pmode_dataseg(vcpu, VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]); fix_pmode_dataseg(vcpu, VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]); fix_pmode_dataseg(vcpu, VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]); - - vmx_segment_cache_clear(vmx); - - vmcs_write16(GUEST_SS_SELECTOR, 0); - vmcs_write32(GUEST_SS_AR_BYTES, 0x93); - - vmcs_write16(GUEST_CS_SELECTOR, - vmcs_read16(GUEST_CS_SELECTOR) & ~SELECTOR_RPL_MASK); - vmcs_write32(GUEST_CS_AR_BYTES, 0x9b); } static gva_t rmode_tss_base(struct kvm *kvm) @@ -2831,22 +2839,40 @@ static gva_t rmode_tss_base(struct kvm *kvm) static void fix_rmode_seg(int seg, struct kvm_segment *save) { const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; + struct kvm_segment var = *save; - vmcs_write16(sf->selector, save->base >> 4); - vmcs_write32(sf->base, save->base & 0xffff0); - vmcs_write32(sf->limit, 0xffff); - vmcs_write32(sf->ar_bytes, 0xf3); - if (save->base & 0xf) - printk_once(KERN_WARNING "kvm: segment base is not paragraph" - " aligned when entering protected mode (seg=%d)", - seg); + var.dpl = 0x3; + if (seg == VCPU_SREG_CS) + var.type = 0x3; + + if (!emulate_invalid_guest_state) { + var.selector = var.base >> 4; + var.base = var.base & 0xffff0; + var.limit = 0xffff; + var.g = 0; + var.db = 0; + var.present = 1; + var.s = 1; + var.l = 0; + var.unusable = 0; + var.type = 0x3; + var.avl = 0; + if (save->base & 0xf) + printk_once(KERN_WARNING "kvm: segment base is not " + "paragraph aligned when entering " + "protected mode (seg=%d)", seg); + } + + vmcs_write16(sf->selector, var.selector); + vmcs_write32(sf->base, var.base); + vmcs_write32(sf->limit, var.limit); + vmcs_write32(sf->ar_bytes, vmx_segment_access_rights(&var)); } static void enter_rmode(struct kvm_vcpu *vcpu) { unsigned long flags; struct vcpu_vmx *vmx = to_vmx(vcpu); - struct kvm_segment var; if (enable_unrestricted_guest) return; @@ -2862,7 +2888,6 @@ static void enter_rmode(struct kvm_vcpu *vcpu) vmx->emulation_required = 1; vmx->rmode.vm86_active = 1; - /* * Very old userspace does not call KVM_SET_TSS_ADDR before entering * vcpu. Call it here with phys address pointing 16M below 4G. @@ -2890,28 +2915,13 @@ static void enter_rmode(struct kvm_vcpu *vcpu) vmcs_writel(GUEST_CR4, vmcs_readl(GUEST_CR4) | X86_CR4_VME); update_exception_bitmap(vcpu); - if (emulate_invalid_guest_state) - goto continue_rmode; + fix_rmode_seg(VCPU_SREG_SS, &vmx->rmode.segs[VCPU_SREG_SS]); + fix_rmode_seg(VCPU_SREG_CS, &vmx->rmode.segs[VCPU_SREG_CS]); + fix_rmode_seg(VCPU_SREG_ES, &vmx->rmode.segs[VCPU_SREG_ES]); + fix_rmode_seg(VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]); + fix_rmode_seg(VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]); + fix_rmode_seg(VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]); - vmx_get_segment(vcpu, &var, VCPU_SREG_SS); - vmx_set_segment(vcpu, &var, VCPU_SREG_SS); - - vmx_get_segment(vcpu, &var, VCPU_SREG_CS); - vmx_set_segment(vcpu, &var, VCPU_SREG_CS); - - vmx_get_segment(vcpu, &var, VCPU_SREG_ES); - vmx_set_segment(vcpu, &var, VCPU_SREG_ES); - - vmx_get_segment(vcpu, &var, VCPU_SREG_DS); - vmx_set_segment(vcpu, &var, VCPU_SREG_DS); - - vmx_get_segment(vcpu, &var, VCPU_SREG_GS); - vmx_set_segment(vcpu, &var, VCPU_SREG_GS); - - vmx_get_segment(vcpu, &var, VCPU_SREG_FS); - vmx_set_segment(vcpu, &var, VCPU_SREG_FS); - -continue_rmode: kvm_mmu_reset_context(vcpu); } @@ -3278,7 +3288,7 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, vmcs_write16(sf->selector, var->selector); else if (var->s) fix_rmode_seg(seg, &vmx->rmode.segs[seg]); - return; + goto out; } vmcs_writel(sf->base, var->base); @@ -3300,6 +3310,10 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, var->type |= 0x1; /* Accessed */ vmcs_write32(sf->ar_bytes, vmx_segment_access_rights(var)); + +out: + if (!vmx->emulation_required) + vmx->emulation_required = !guest_state_valid(vcpu); } static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l) From d54d07b2ca19a2908aa89e0c67715ca2e8e62a4c Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Thu, 20 Dec 2012 16:57:46 +0200 Subject: [PATCH 0499/4607] KVM: VMX: Do not fix segment register during vcpu initialization. Segment registers will be fixed according to current emulation policy during switching to real mode for the first time. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 9dff310a2e95..a101dd488f23 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3621,12 +3621,9 @@ static void seg_setup(int seg) vmcs_write16(sf->selector, 0); vmcs_writel(sf->base, 0); vmcs_write32(sf->limit, 0xffff); - if (enable_unrestricted_guest) { - ar = 0x93; - if (seg == VCPU_SREG_CS) - ar |= 0x08; /* code segment */ - } else - ar = 0xf3; + ar = 0x93; + if (seg == VCPU_SREG_CS) + ar |= 0x08; /* code segment */ vmcs_write32(sf->ar_bytes, ar); } @@ -3967,14 +3964,9 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu) vmx_segment_cache_clear(vmx); seg_setup(VCPU_SREG_CS); - /* - * GUEST_CS_BASE should really be 0xffff0000, but VT vm86 mode - * insists on having GUEST_CS_BASE == GUEST_CS_SELECTOR << 4. Sigh. - */ - if (kvm_vcpu_is_bsp(&vmx->vcpu)) { + if (kvm_vcpu_is_bsp(&vmx->vcpu)) vmcs_write16(GUEST_CS_SELECTOR, 0xf000); - vmcs_writel(GUEST_CS_BASE, 0x000f0000); - } else { + else { vmcs_write16(GUEST_CS_SELECTOR, vmx->vcpu.arch.sipi_vector << 8); vmcs_writel(GUEST_CS_BASE, vmx->vcpu.arch.sipi_vector << 12); } From 0ca1b4f4ba3a9f75bb099ccaf6c4bd8bb6db7a74 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Thu, 20 Dec 2012 16:57:47 +0200 Subject: [PATCH 0500/4607] KVM: VMX: handle IO when emulation is due to #GP in real mode. With emulate_invalid_guest_state=0 if a vcpu is in real mode VMX can enter the vcpu with smaller segment limit than guest configured. If the guest tries to access pass this limit it will get #GP at which point instruction will be emulated with correct segment limit applied. If during the emulation IO is detected it is not handled correctly. Vcpu thread should exit to userspace to serve the IO, but it returns to the guest instead. Since emulation is not completed till userspace completes the IO the faulty instruction is re-executed ad infinitum. The patch fixes that by exiting to userspace if IO happens during instruction emulation. Reported-by: Alex Williamson Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 75 +++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index a101dd488f23..55dfc375f1ab 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -4230,28 +4230,9 @@ static int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr) return 0; } -static int handle_rmode_exception(struct kvm_vcpu *vcpu, - int vec, u32 err_code) +static bool rmode_exception(struct kvm_vcpu *vcpu, int vec) { - /* - * Instruction with address size override prefix opcode 0x67 - * Cause the #SS fault with 0 error code in VM86 mode. - */ - if (((vec == GP_VECTOR) || (vec == SS_VECTOR)) && err_code == 0) - if (emulate_instruction(vcpu, 0) == EMULATE_DONE) - return 1; - /* - * Forward all other exceptions that are valid in real mode. - * FIXME: Breaks guest debugging in real mode, needs to be fixed with - * the required debugging infrastructure rework. - */ switch (vec) { - case DB_VECTOR: - if (vcpu->guest_debug & - (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP)) - return 0; - kvm_queue_exception(vcpu, vec); - return 1; case BP_VECTOR: /* * Update instruction length as we may reinject the exception @@ -4260,7 +4241,12 @@ static int handle_rmode_exception(struct kvm_vcpu *vcpu, to_vmx(vcpu)->vcpu.arch.event_exit_inst_len = vmcs_read32(VM_EXIT_INSTRUCTION_LEN); if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP) - return 0; + return false; + /* fall through */ + case DB_VECTOR: + if (vcpu->guest_debug & + (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP)) + return false; /* fall through */ case DE_VECTOR: case OF_VECTOR: @@ -4270,10 +4256,37 @@ static int handle_rmode_exception(struct kvm_vcpu *vcpu, case SS_VECTOR: case GP_VECTOR: case MF_VECTOR: - kvm_queue_exception(vcpu, vec); - return 1; + return true; + break; } - return 0; + return false; +} + +static int handle_rmode_exception(struct kvm_vcpu *vcpu, + int vec, u32 err_code) +{ + /* + * Instruction with address size override prefix opcode 0x67 + * Cause the #SS fault with 0 error code in VM86 mode. + */ + if (((vec == GP_VECTOR) || (vec == SS_VECTOR)) && err_code == 0) { + if (emulate_instruction(vcpu, 0) == EMULATE_DONE) { + if (vcpu->arch.halt_request) { + vcpu->arch.halt_request = 0; + return kvm_emulate_halt(vcpu); + } + return 1; + } + return 0; + } + + /* + * Forward all other exceptions that are valid in real mode. + * FIXME: Breaks guest debugging in real mode, needs to be fixed with + * the required debugging infrastructure rework. + */ + kvm_queue_exception(vcpu, vec); + return 1; } /* @@ -4361,17 +4374,11 @@ static int handle_exception(struct kvm_vcpu *vcpu) return kvm_mmu_page_fault(vcpu, cr2, error_code, NULL, 0); } - if (vmx->rmode.vm86_active && - handle_rmode_exception(vcpu, intr_info & INTR_INFO_VECTOR_MASK, - error_code)) { - if (vcpu->arch.halt_request) { - vcpu->arch.halt_request = 0; - return kvm_emulate_halt(vcpu); - } - return 1; - } - ex_no = intr_info & INTR_INFO_VECTOR_MASK; + + if (vmx->rmode.vm86_active && rmode_exception(vcpu, ex_no)) + return handle_rmode_exception(vcpu, ex_no, error_code); + switch (ex_no) { case DB_VECTOR: dr6 = vmcs_readl(EXIT_QUALIFICATION); From 9a32299394d8cce79ca7d0098dc32c4f14032dcd Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Fri, 12 Oct 2012 17:52:45 +0200 Subject: [PATCH 0501/4607] powerpc, dma: move bestcomm driver from arch/powerpc/sysdev to drivers/dma The bestcomm dma hardware, and some of its users like the FEC ethernet component, is used in different FreeScale parts, including non-powerpc parts like the ColdFire MCF547x & MCF548x families. Don't keep the driver hidden in arch/powerpc where it is inaccessible for other arches. .c files are moved to drivers/dma/bestcomm, while .h files are moved to include/linux/fsl/bestcomm. Makefiles, Kconfigs and #include directives are updated for the new file locations. Tested by recompiling for MPC5200 with all bestcomm users enabled. Signed-off-by: Philippe De Muyter Signed-off-by: Anatolij Gustschin --- arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c | 6 +++--- arch/powerpc/platforms/Kconfig | 2 -- arch/powerpc/sysdev/Makefile | 1 - drivers/Makefile | 2 +- drivers/ata/pata_mpc52xx.c | 6 +++--- drivers/dma/Kconfig | 2 ++ drivers/dma/Makefile | 1 + {arch/powerpc/sysdev => drivers/dma}/bestcomm/Kconfig | 0 {arch/powerpc/sysdev => drivers/dma}/bestcomm/Makefile | 0 {arch/powerpc/sysdev => drivers/dma}/bestcomm/ata.c | 6 +++--- .../powerpc/sysdev => drivers/dma}/bestcomm/bcom_ata_task.c | 0 .../sysdev => drivers/dma}/bestcomm/bcom_fec_rx_task.c | 0 .../sysdev => drivers/dma}/bestcomm/bcom_fec_tx_task.c | 0 .../sysdev => drivers/dma}/bestcomm/bcom_gen_bd_rx_task.c | 0 .../sysdev => drivers/dma}/bestcomm/bcom_gen_bd_tx_task.c | 0 {arch/powerpc/sysdev => drivers/dma}/bestcomm/bestcomm.c | 6 +++--- {arch/powerpc/sysdev => drivers/dma}/bestcomm/fec.c | 6 +++--- {arch/powerpc/sysdev => drivers/dma}/bestcomm/gen_bd.c | 6 +++--- {arch/powerpc/sysdev => drivers/dma}/bestcomm/sram.c | 2 +- drivers/net/ethernet/freescale/fec_mpc52xx.c | 4 ++-- {arch/powerpc/sysdev => include/linux/fsl}/bestcomm/ata.h | 0 .../sysdev => include/linux/fsl}/bestcomm/bestcomm.h | 0 .../sysdev => include/linux/fsl}/bestcomm/bestcomm_priv.h | 0 {arch/powerpc/sysdev => include/linux/fsl}/bestcomm/fec.h | 0 .../powerpc/sysdev => include/linux/fsl}/bestcomm/gen_bd.h | 0 {arch/powerpc/sysdev => include/linux/fsl}/bestcomm/sram.h | 0 sound/soc/fsl/mpc5200_dma.c | 4 ++-- 27 files changed, 27 insertions(+), 27 deletions(-) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/Kconfig (100%) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/Makefile (100%) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/ata.c (97%) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/bcom_ata_task.c (100%) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/bcom_fec_rx_task.c (100%) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/bcom_fec_tx_task.c (100%) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/bcom_gen_bd_rx_task.c (100%) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/bcom_gen_bd_tx_task.c (100%) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/bestcomm.c (99%) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/fec.c (98%) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/gen_bd.c (98%) rename {arch/powerpc/sysdev => drivers/dma}/bestcomm/sram.c (99%) rename {arch/powerpc/sysdev => include/linux/fsl}/bestcomm/ata.h (100%) rename {arch/powerpc/sysdev => include/linux/fsl}/bestcomm/bestcomm.h (100%) rename {arch/powerpc/sysdev => include/linux/fsl}/bestcomm/bestcomm_priv.h (100%) rename {arch/powerpc/sysdev => include/linux/fsl}/bestcomm/fec.h (100%) rename {arch/powerpc/sysdev => include/linux/fsl}/bestcomm/gen_bd.h (100%) rename {arch/powerpc/sysdev => include/linux/fsl}/bestcomm/sram.h (100%) diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c index 16150fa430f9..93fa645904e6 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c @@ -20,9 +20,9 @@ #include #include -#include -#include -#include +#include +#include +#include MODULE_AUTHOR("Grant Likely "); MODULE_DESCRIPTION("MPC5200 LocalPlus FIFO device driver"); diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 48a920d51489..52de8bccfb30 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -352,8 +352,6 @@ config OF_RTC Uses information from the OF or flattened device tree to instantiate platform devices for direct mapped RTC chips like the DS1742 or DS1743. -source "arch/powerpc/sysdev/bestcomm/Kconfig" - config SIMPLE_GPIO bool "Support for simple, memory-mapped GPIO controllers" depends on PPC diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index a57600b3a4e3..3884776600fd 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -26,7 +26,6 @@ obj-$(CONFIG_SIMPLE_GPIO) += simple_gpio.o obj-$(CONFIG_FSL_RIO) += fsl_rio.o fsl_rmu.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ -obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ mv64x60-$(CONFIG_PCI) += mv64x60_pci.o obj-$(CONFIG_MV64X60) += $(mv64x60-y) mv64x60_pic.o mv64x60_dev.o \ mv64x60_udbg.o diff --git a/drivers/Makefile b/drivers/Makefile index 7863b9fee50b..d8372ab2e4cf 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -29,7 +29,7 @@ obj-$(CONFIG_PNP) += pnp/ obj-y += amba/ # Many drivers will want to use DMA so this has to be made available # really early. -obj-$(CONFIG_DMA_ENGINE) += dma/ +obj-$(CONFIG_DMADEVICES) += dma/ obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_XEN) += xen/ diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index ec67f54dc56f..0b363e9d42a5 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -26,9 +26,9 @@ #include #include -#include -#include -#include +#include +#include +#include #define DRV_NAME "mpc52xx_ata" diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index d4c12180c654..40179e749f08 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -125,6 +125,8 @@ config MPC512X_DMA ---help--- Enable support for the Freescale MPC512x built-in DMA engine. +source "drivers/dma/bestcomm/Kconfig" + config MV_XOR bool "Marvell XOR engine support" depends on PLAT_ORION diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 7428feaa8705..642d96736cf5 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o +obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/ obj-$(CONFIG_MV_XOR) += mv_xor.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o obj-$(CONFIG_AT_HDMAC) += at_hdmac.o diff --git a/arch/powerpc/sysdev/bestcomm/Kconfig b/drivers/dma/bestcomm/Kconfig similarity index 100% rename from arch/powerpc/sysdev/bestcomm/Kconfig rename to drivers/dma/bestcomm/Kconfig diff --git a/arch/powerpc/sysdev/bestcomm/Makefile b/drivers/dma/bestcomm/Makefile similarity index 100% rename from arch/powerpc/sysdev/bestcomm/Makefile rename to drivers/dma/bestcomm/Makefile diff --git a/arch/powerpc/sysdev/bestcomm/ata.c b/drivers/dma/bestcomm/ata.c similarity index 97% rename from arch/powerpc/sysdev/bestcomm/ata.c rename to drivers/dma/bestcomm/ata.c index 901c9f91e5dd..2fd87f83cf90 100644 --- a/arch/powerpc/sysdev/bestcomm/ata.c +++ b/drivers/dma/bestcomm/ata.c @@ -18,9 +18,9 @@ #include #include -#include "bestcomm.h" -#include "bestcomm_priv.h" -#include "ata.h" +#include +#include +#include /* ======================================================================== */ diff --git a/arch/powerpc/sysdev/bestcomm/bcom_ata_task.c b/drivers/dma/bestcomm/bcom_ata_task.c similarity index 100% rename from arch/powerpc/sysdev/bestcomm/bcom_ata_task.c rename to drivers/dma/bestcomm/bcom_ata_task.c diff --git a/arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c b/drivers/dma/bestcomm/bcom_fec_rx_task.c similarity index 100% rename from arch/powerpc/sysdev/bestcomm/bcom_fec_rx_task.c rename to drivers/dma/bestcomm/bcom_fec_rx_task.c diff --git a/arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c b/drivers/dma/bestcomm/bcom_fec_tx_task.c similarity index 100% rename from arch/powerpc/sysdev/bestcomm/bcom_fec_tx_task.c rename to drivers/dma/bestcomm/bcom_fec_tx_task.c diff --git a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c b/drivers/dma/bestcomm/bcom_gen_bd_rx_task.c similarity index 100% rename from arch/powerpc/sysdev/bestcomm/bcom_gen_bd_rx_task.c rename to drivers/dma/bestcomm/bcom_gen_bd_rx_task.c diff --git a/arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c b/drivers/dma/bestcomm/bcom_gen_bd_tx_task.c similarity index 100% rename from arch/powerpc/sysdev/bestcomm/bcom_gen_bd_tx_task.c rename to drivers/dma/bestcomm/bcom_gen_bd_tx_task.c diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.c b/drivers/dma/bestcomm/bestcomm.c similarity index 99% rename from arch/powerpc/sysdev/bestcomm/bestcomm.c rename to drivers/dma/bestcomm/bestcomm.c index b3fbb271be87..67371fb820d3 100644 --- a/arch/powerpc/sysdev/bestcomm/bestcomm.c +++ b/drivers/dma/bestcomm/bestcomm.c @@ -23,9 +23,9 @@ #include #include -#include "sram.h" -#include "bestcomm_priv.h" -#include "bestcomm.h" +#include +#include +#include "linux/fsl/bestcomm/bestcomm.h" #define DRIVER_NAME "bestcomm-core" diff --git a/arch/powerpc/sysdev/bestcomm/fec.c b/drivers/dma/bestcomm/fec.c similarity index 98% rename from arch/powerpc/sysdev/bestcomm/fec.c rename to drivers/dma/bestcomm/fec.c index 957a988d23ea..7f1fb1c999e4 100644 --- a/arch/powerpc/sysdev/bestcomm/fec.c +++ b/drivers/dma/bestcomm/fec.c @@ -16,9 +16,9 @@ #include #include -#include "bestcomm.h" -#include "bestcomm_priv.h" -#include "fec.h" +#include +#include +#include /* ======================================================================== */ diff --git a/arch/powerpc/sysdev/bestcomm/gen_bd.c b/drivers/dma/bestcomm/gen_bd.c similarity index 98% rename from arch/powerpc/sysdev/bestcomm/gen_bd.c rename to drivers/dma/bestcomm/gen_bd.c index e0a53e3147b2..1a5b22d88127 100644 --- a/arch/powerpc/sysdev/bestcomm/gen_bd.c +++ b/drivers/dma/bestcomm/gen_bd.c @@ -21,9 +21,9 @@ #include #include -#include "bestcomm.h" -#include "bestcomm_priv.h" -#include "gen_bd.h" +#include +#include +#include /* ======================================================================== */ diff --git a/arch/powerpc/sysdev/bestcomm/sram.c b/drivers/dma/bestcomm/sram.c similarity index 99% rename from arch/powerpc/sysdev/bestcomm/sram.c rename to drivers/dma/bestcomm/sram.c index b6db23e085fb..5e2ed30ba2c4 100644 --- a/arch/powerpc/sysdev/bestcomm/sram.c +++ b/drivers/dma/bestcomm/sram.c @@ -23,7 +23,7 @@ #include #include -#include "sram.h" +#include /* Struct keeping our 'state' */ diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c index 817d081d2cd8..85e776d500a6 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c @@ -40,8 +40,8 @@ #include #include -#include -#include +#include +#include #include "fec_mpc52xx.h" diff --git a/arch/powerpc/sysdev/bestcomm/ata.h b/include/linux/fsl/bestcomm/ata.h similarity index 100% rename from arch/powerpc/sysdev/bestcomm/ata.h rename to include/linux/fsl/bestcomm/ata.h diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm.h b/include/linux/fsl/bestcomm/bestcomm.h similarity index 100% rename from arch/powerpc/sysdev/bestcomm/bestcomm.h rename to include/linux/fsl/bestcomm/bestcomm.h diff --git a/arch/powerpc/sysdev/bestcomm/bestcomm_priv.h b/include/linux/fsl/bestcomm/bestcomm_priv.h similarity index 100% rename from arch/powerpc/sysdev/bestcomm/bestcomm_priv.h rename to include/linux/fsl/bestcomm/bestcomm_priv.h diff --git a/arch/powerpc/sysdev/bestcomm/fec.h b/include/linux/fsl/bestcomm/fec.h similarity index 100% rename from arch/powerpc/sysdev/bestcomm/fec.h rename to include/linux/fsl/bestcomm/fec.h diff --git a/arch/powerpc/sysdev/bestcomm/gen_bd.h b/include/linux/fsl/bestcomm/gen_bd.h similarity index 100% rename from arch/powerpc/sysdev/bestcomm/gen_bd.h rename to include/linux/fsl/bestcomm/gen_bd.h diff --git a/arch/powerpc/sysdev/bestcomm/sram.h b/include/linux/fsl/bestcomm/sram.h similarity index 100% rename from arch/powerpc/sysdev/bestcomm/sram.h rename to include/linux/fsl/bestcomm/sram.h diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index 9997c039bb24..2a847ca494b5 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -14,8 +14,8 @@ #include -#include -#include +#include +#include #include #include "mpc5200_dma.h" From b7869ba17cfb5553a33e11f18c7c45d988e4c455 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 3 Jan 2013 15:28:34 -0700 Subject: [PATCH 0502/4607] x86/PCI: Remove unused pci_root_bus pci_root_bus is unused, so remove all references to it. Signed-off-by: Bjorn Helgaas --- arch/x86/include/asm/pci_x86.h | 1 - arch/x86/pci/common.c | 1 - arch/x86/pci/legacy.c | 2 +- arch/x86/pci/numaq_32.c | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index 73e8eeff22ee..0126f104f0a5 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -54,7 +54,6 @@ void pcibios_set_cache_line_size(void); /* pci-pc.c */ extern int pcibios_last_bus; -extern struct pci_bus *pci_root_bus; extern struct pci_ops pci_root_ops; void pcibios_scan_specific_bus(int busn); diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 412e1286d1fc..505731b139f4 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -34,7 +34,6 @@ int noioapicreroute = 1; #endif int pcibios_last_bus = -1; unsigned long pirq_table_addr; -struct pci_bus *pci_root_bus; const struct pci_raw_ops *__read_mostly raw_pci_ops; const struct pci_raw_ops *__read_mostly raw_pci_ext_ops; diff --git a/arch/x86/pci/legacy.c b/arch/x86/pci/legacy.c index a1df191129d3..a9e83083fb85 100644 --- a/arch/x86/pci/legacy.c +++ b/arch/x86/pci/legacy.c @@ -30,7 +30,7 @@ int __init pci_legacy_init(void) } printk("PCI: Probing PCI hardware\n"); - pci_root_bus = pcibios_scan_root(0); + pcibios_scan_root(0); return 0; } diff --git a/arch/x86/pci/numaq_32.c b/arch/x86/pci/numaq_32.c index 83e125b95ca6..00edfe652b72 100644 --- a/arch/x86/pci/numaq_32.c +++ b/arch/x86/pci/numaq_32.c @@ -152,7 +152,7 @@ int __init pci_numaq_init(void) raw_pci_ops = &pci_direct_conf1_mq; - pci_root_bus = pcibios_scan_root(0); + pcibios_scan_root(0); if (num_online_nodes() > 1) for_each_online_node(quad) { if (quad == 0) From d069015e268bcac6c8bd8997b3235f5f977d3ab6 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 19 Nov 2012 15:33:51 +0800 Subject: [PATCH 0503/4607] Introduce THERMAL_TREND_RAISE_FULL and THERMAL_TREND_DROP_FULL These two new thermal_trend types are used to tell the governor that the temeprature is raising/dropping quickly. Thermal cooling governors should handle this situation and make proper decisions, e.g. set cooling state to upper/lower limit directly instead of one step each time. Signed-off-by: Zhang Rui --- include/linux/thermal.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index fe82022478e7..883bcda7e1e4 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -74,6 +74,8 @@ enum thermal_trend { THERMAL_TREND_STABLE, /* temperature is stable */ THERMAL_TREND_RAISING, /* temperature is raising */ THERMAL_TREND_DROPPING, /* temperature is dropping */ + THERMAL_TREND_RAISE_FULL, /* apply highest cooling action */ + THERMAL_TREND_DROP_FULL, /* apply lowest cooling action */ }; /* Events supported by Thermal Netlink */ From 3dbfff3dfe6714aeefb615c65bec0800dc5a4c51 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 19 Nov 2012 16:10:20 +0800 Subject: [PATCH 0504/4607] Introduce THERMAL_TREND_RAISE/DROP_FULL support for step_wise governor step_wise governor should set the device cooling state to upper/lower limit directly when THERMAL_TREND_RAISE/DROP_FULL. Signed-off-by: Zhang Rui --- drivers/thermal/step_wise.c | 64 ++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index 0cd5e9fbab1c..f45dd0f768f0 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -35,21 +35,54 @@ * state for this trip point * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling * state for this trip point + * c. if the trend is THERMAL_TREND_RAISE_FULL, use upper limit + * for this trip point + * d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit + * for this trip point + * If the temperature is lower than a trip point, + * a. if the trend is THERMAL_TREND_RAISING, do nothing + * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling + * state for this trip point, if the cooling state already + * equals lower limit, deactivate the thermal instance + * c. if the trend is THERMAL_TREND_RAISE_FULL, do nothing + * d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit, + * if the cooling state already equals lower limit, + * deactive the thermal instance */ static unsigned long get_target_state(struct thermal_instance *instance, - enum thermal_trend trend) + enum thermal_trend trend, bool throttle) { struct thermal_cooling_device *cdev = instance->cdev; unsigned long cur_state; cdev->ops->get_cur_state(cdev, &cur_state); - if (trend == THERMAL_TREND_RAISING) { - cur_state = cur_state < instance->upper ? - (cur_state + 1) : instance->upper; - } else if (trend == THERMAL_TREND_DROPPING) { - cur_state = cur_state > instance->lower ? - (cur_state - 1) : instance->lower; + switch (trend) { + case THERMAL_TREND_RAISING: + if (throttle) + cur_state = cur_state < instance->upper ? + (cur_state + 1) : instance->upper; + break; + case THERMAL_TREND_RAISE_FULL: + if (throttle) + cur_state = instance->upper; + break; + case THERMAL_TREND_DROPPING: + if (cur_state == instance->lower) { + if (!throttle) + cur_state = -1; + } else + cur_state -= 1; + break; + case THERMAL_TREND_DROP_FULL: + if (cur_state == instance->lower) { + if (!throttle) + cur_state = -1; + } else + cur_state = instance->lower; + break; + default: + break; } return cur_state; @@ -76,7 +109,7 @@ static void update_instance_for_throttle(struct thermal_zone_device *tz, if (instance->trip != trip) continue; - instance->target = get_target_state(instance, trend); + instance->target = get_target_state(instance, trend, true); /* Activate a passive thermal instance */ if (instance->target == THERMAL_NO_TARGET) @@ -87,28 +120,23 @@ static void update_instance_for_throttle(struct thermal_zone_device *tz, } static void update_instance_for_dethrottle(struct thermal_zone_device *tz, - int trip, enum thermal_trip_type trip_type) + int trip, enum thermal_trip_type trip_type, + enum thermal_trend trend) { struct thermal_instance *instance; - struct thermal_cooling_device *cdev; - unsigned long cur_state; list_for_each_entry(instance, &tz->thermal_instances, tz_node) { if (instance->trip != trip || instance->target == THERMAL_NO_TARGET) continue; - cdev = instance->cdev; - cdev->ops->get_cur_state(cdev, &cur_state); - - instance->target = cur_state > instance->lower ? - (cur_state - 1) : THERMAL_NO_TARGET; + instance->target = get_target_state(instance, trend, false); /* Deactivate a passive thermal instance */ if (instance->target == THERMAL_NO_TARGET) update_passive_instance(tz, trip_type, -1); - cdev->updated = false; /* cdev needs update */ + instance->cdev->updated = false; /* cdev needs update */ } } @@ -133,7 +161,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) if (tz->temperature >= trip_temp) update_instance_for_throttle(tz, trip, trip_type, trend); else - update_instance_for_dethrottle(tz, trip, trip_type); + update_instance_for_dethrottle(tz, trip, trip_type, trend); mutex_unlock(&tz->lock); } From b8bb6cb999858043489c1ddef08eed2127559169 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 22 Nov 2012 15:45:02 +0800 Subject: [PATCH 0505/4607] step_wise: Unify the code for both throttle and dethrottle Signed-off-by: Zhang Rui --- drivers/thermal/step_wise.c | 70 +++++++++++++------------------------ 1 file changed, 25 insertions(+), 45 deletions(-) diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index f45dd0f768f0..407cde3211c1 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -99,52 +99,14 @@ static void update_passive_instance(struct thermal_zone_device *tz, tz->passive += value; } -static void update_instance_for_throttle(struct thermal_zone_device *tz, - int trip, enum thermal_trip_type trip_type, - enum thermal_trend trend) -{ - struct thermal_instance *instance; - - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip) - continue; - - instance->target = get_target_state(instance, trend, true); - - /* Activate a passive thermal instance */ - if (instance->target == THERMAL_NO_TARGET) - update_passive_instance(tz, trip_type, 1); - - instance->cdev->updated = false; /* cdev needs update */ - } -} - -static void update_instance_for_dethrottle(struct thermal_zone_device *tz, - int trip, enum thermal_trip_type trip_type, - enum thermal_trend trend) -{ - struct thermal_instance *instance; - - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip || - instance->target == THERMAL_NO_TARGET) - continue; - - instance->target = get_target_state(instance, trend, false); - - /* Deactivate a passive thermal instance */ - if (instance->target == THERMAL_NO_TARGET) - update_passive_instance(tz, trip_type, -1); - - instance->cdev->updated = false; /* cdev needs update */ - } -} - static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) { long trip_temp; enum thermal_trip_type trip_type; enum thermal_trend trend; + struct thermal_instance *instance; + bool throttle = false; + int old_target; if (trip == THERMAL_TRIPS_NONE) { trip_temp = tz->forced_passive; @@ -156,12 +118,30 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) trend = get_tz_trend(tz, trip); + if (tz->temperature >= trip_temp) + throttle = true; + mutex_lock(&tz->lock); - if (tz->temperature >= trip_temp) - update_instance_for_throttle(tz, trip, trip_type, trend); - else - update_instance_for_dethrottle(tz, trip, trip_type, trend); + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + if (instance->trip != trip) + continue; + + old_target = instance->target; + instance->target = get_target_state(instance, trend, throttle); + + /* Activate a passive thermal instance */ + if (old_target == THERMAL_NO_TARGET && + instance->target != THERMAL_NO_TARGET) + update_passive_instance(tz, trip_type, 1); + /* Deactivate a passive thermal instance */ + else if (old_target != THERMAL_NO_TARGET && + instance->target == THERMAL_NO_TARGET) + update_passive_instance(tz, trip_type, -1); + + + instance->cdev->updated = false; /* cdev needs update */ + } mutex_unlock(&tz->lock); } From bbf63be4f331358173da26b888a10583fcc92ec0 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Wed, 21 Nov 2012 13:31:01 +0900 Subject: [PATCH 0506/4607] Thermal: exynos: Add sysfs node supporting exynos's emulation mode. This patch supports exynos's emulation mode with newly created sysfs node. Exynos 4x12 (4212, 4412) and 5 series provide emulation mode for thermal management unit. Thermal emulation mode supports software debug for TMU's operation. User can set temperature manually with software code and TMU will read current temperature from user value not from sensor's value. This patch includes also documentary placed under Documentation/thermal/. Signed-off-by: Jonghwa Lee Signed-off-by: Zhang Rui --- .../thermal/exynos_thermal_emulation | 53 +++++++++ drivers/thermal/Kconfig | 9 ++ drivers/thermal/exynos_thermal.c | 103 ++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 Documentation/thermal/exynos_thermal_emulation diff --git a/Documentation/thermal/exynos_thermal_emulation b/Documentation/thermal/exynos_thermal_emulation new file mode 100644 index 000000000000..b73bbfb697bb --- /dev/null +++ b/Documentation/thermal/exynos_thermal_emulation @@ -0,0 +1,53 @@ +EXYNOS EMULATION MODE +======================== + +Copyright (C) 2012 Samsung Electronics + +Written by Jonghwa Lee + +Description +----------- + +Exynos 4x12 (4212, 4412) and 5 series provide emulation mode for thermal management unit. +Thermal emulation mode supports software debug for TMU's operation. User can set temperature +manually with software code and TMU will read current temperature from user value not from +sensor's value. + +Enabling CONFIG_EXYNOS_THERMAL_EMUL option will make this support in available. +When it's enabled, sysfs node will be created under +/sys/bus/platform/devices/'exynos device name'/ with name of 'emulation'. + +The sysfs node, 'emulation', will contain value 0 for the initial state. When you input any +temperature you want to update to sysfs node, it automatically enable emulation mode and +current temperature will be changed into it. +(Exynos also supports user changable delay time which would be used to delay of + changing temperature. However, this node only uses same delay of real sensing time, 938us.) + +Exynos emulation mode requires synchronous of value changing and enabling. It means when you +want to update the any value of delay or next temperature, then you have to enable emulation +mode at the same time. (Or you have to keep the mode enabling.) If you don't, it fails to +change the value to updated one and just use last succeessful value repeatedly. That's why +this node gives users the right to change termerpature only. Just one interface makes it more +simply to use. + +Disabling emulation mode only requires writing value 0 to sysfs node. + + +TEMP 120 | + | + 100 | + | + 80 | + | +----------- + 60 | | | + | +-------------| | + 40 | | | | + | | | | + 20 | | | +---------- + | | | | | + 0 |______________|_____________|__________|__________|_________ + A A A A TIME + |<----->| |<----->| |<----->| | + | 938us | | | | | | +emulation : 0 50 | 70 | 20 | 0 +current temp : sensor 50 70 20 sensor diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index c2c77d1ac499..c31b9e4451a3 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -101,6 +101,15 @@ config EXYNOS_THERMAL If you say yes here you get support for TMU (Thermal Management Unit) on SAMSUNG EXYNOS series of SoC. +config EXYNOS_THERMAL_EMUL + bool "EXYNOS TMU emulation mode support" + depends on EXYNOS_THERMAL + help + Exynos 4412 and 4414 and 5 series has emulation mode on TMU. + Enable this option will be make sysfs node in exynos thermal platform + device directory to support emulation mode. With emulation mode sysfs + node, you can manually input temperature to TMU for simulation purpose. + config DB8500_THERMAL bool "DB8500 thermal management" depends on ARCH_U8500 diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index 7772d1603769..e2ef8a3531ac 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -99,6 +99,14 @@ #define IDLE_INTERVAL 10000 #define MCELSIUS 1000 +#ifdef CONFIG_EXYNOS_THERMAL_EMUL +#define EXYNOS_EMUL_TIME 0x57F0 +#define EXYNOS_EMUL_TIME_SHIFT 16 +#define EXYNOS_EMUL_DATA_SHIFT 8 +#define EXYNOS_EMUL_DATA_MASK 0xFF +#define EXYNOS_EMUL_ENABLE 0x1 +#endif /* CONFIG_EXYNOS_THERMAL_EMUL */ + /* CPU Zone information */ #define PANIC_ZONE 4 #define WARN_ZONE 3 @@ -832,6 +840,94 @@ static inline struct exynos_tmu_platform_data *exynos_get_driver_data( return (struct exynos_tmu_platform_data *) platform_get_device_id(pdev)->driver_data; } + +#ifdef CONFIG_EXYNOS_THERMAL_EMUL +static ssize_t exynos_tmu_emulation_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct exynos_tmu_data *data = platform_get_drvdata(pdev); + unsigned int reg; + u8 temp_code; + int temp = 0; + + if (data->soc == SOC_ARCH_EXYNOS4210) + goto out; + + mutex_lock(&data->lock); + clk_enable(data->clk); + reg = readl(data->base + EXYNOS_EMUL_CON); + clk_disable(data->clk); + mutex_unlock(&data->lock); + + if (reg & EXYNOS_EMUL_ENABLE) { + reg >>= EXYNOS_EMUL_DATA_SHIFT; + temp_code = reg & EXYNOS_EMUL_DATA_MASK; + temp = code_to_temp(data, temp_code); + } +out: + return sprintf(buf, "%d\n", temp * MCELSIUS); +} + +static ssize_t exynos_tmu_emulation_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct exynos_tmu_data *data = platform_get_drvdata(pdev); + unsigned int reg; + int temp; + + if (data->soc == SOC_ARCH_EXYNOS4210) + goto out; + + if (!sscanf(buf, "%d\n", &temp) || temp < 0) + return -EINVAL; + + mutex_lock(&data->lock); + clk_enable(data->clk); + + reg = readl(data->base + EXYNOS_EMUL_CON); + + if (temp) { + /* Both CELSIUS and MCELSIUS type are available for input */ + if (temp > MCELSIUS) + temp /= MCELSIUS; + + reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) | + (temp_to_code(data, (temp / MCELSIUS)) + << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE; + } else { + reg &= ~EXYNOS_EMUL_ENABLE; + } + + writel(reg, data->base + EXYNOS_EMUL_CON); + + clk_disable(data->clk); + mutex_unlock(&data->lock); + +out: + return count; +} + +static DEVICE_ATTR(emulation, 0644, exynos_tmu_emulation_show, + exynos_tmu_emulation_store); +static int create_emulation_sysfs(struct device *dev) +{ + return device_create_file(dev, &dev_attr_emulation); +} +static void remove_emulation_sysfs(struct device *dev) +{ + device_remove_file(dev, &dev_attr_emulation); +} +#else +static inline int create_emulation_sysfs(struct device *dev) { return 0; } +static inline void remove_emulation_sysfs(struct device *dev) {} +#endif + static int __devinit exynos_tmu_probe(struct platform_device *pdev) { struct exynos_tmu_data *data; @@ -930,6 +1026,11 @@ static int __devinit exynos_tmu_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to register thermal interface\n"); goto err_clk; } + + ret = create_emulation_sysfs(&pdev->dev); + if (ret) + dev_err(&pdev->dev, "Failed to create emulation mode sysfs node\n"); + return 0; err_clk: platform_set_drvdata(pdev, NULL); @@ -941,6 +1042,8 @@ static int __devexit exynos_tmu_remove(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); + remove_emulation_sysfs(&pdev->dev); + exynos_tmu_control(pdev, false); exynos_unregister_thermal(); From d2a73e225d113fdccd80373ad9aeb2b58b32a30b Mon Sep 17 00:00:00 2001 From: "kuninori.morimoto.gx@renesas.com" Date: Sun, 2 Dec 2012 18:48:41 -0800 Subject: [PATCH 0507/4607] thermal: rcar: add .get_trip_type/temp and .notify support This patch adds .get_trip_type(), .get_trip_temp(), and .notify() on rcar_thermal_zone_ops. Driver will try platform power OFF if it reached to critical temperature. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 68 ++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 90db951725da..89979ff10e27 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -22,10 +22,13 @@ #include #include #include +#include #include #include #include +#define IDLE_INTERVAL 5000 + #define THSCR 0x2c #define THSSR 0x30 @@ -176,8 +179,66 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, return 0; } +static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, + int trip, enum thermal_trip_type *type) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + /* see rcar_thermal_get_temp() */ + switch (trip) { + case 0: /* +90 <= temp */ + *type = THERMAL_TRIP_CRITICAL; + break; + default: + dev_err(priv->dev, "rcar driver trip error\n"); + return -EINVAL; + } + + return 0; +} + +static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, + int trip, unsigned long *temp) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + /* see rcar_thermal_get_temp() */ + switch (trip) { + case 0: /* +90 <= temp */ + *temp = MCELSIUS(90); + break; + default: + dev_err(priv->dev, "rcar driver trip error\n"); + return -EINVAL; + } + + return 0; +} + +static int rcar_thermal_notify(struct thermal_zone_device *zone, + int trip, enum thermal_trip_type type) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + switch (type) { + case THERMAL_TRIP_CRITICAL: + /* FIXME */ + dev_warn(priv->dev, + "Thermal reached to critical temperature\n"); + machine_power_off(); + break; + default: + break; + } + + return 0; +} + static struct thermal_zone_device_ops rcar_thermal_zone_ops = { - .get_temp = rcar_thermal_get_temp, + .get_temp = rcar_thermal_get_temp, + .get_trip_type = rcar_thermal_get_trip_type, + .get_trip_temp = rcar_thermal_get_trip_temp, + .notify = rcar_thermal_notify, }; /* @@ -211,8 +272,9 @@ static int rcar_thermal_probe(struct platform_device *pdev) return -ENOMEM; } - zone = thermal_zone_device_register("rcar_thermal", 0, 0, priv, - &rcar_thermal_zone_ops, NULL, 0, 0); + zone = thermal_zone_device_register("rcar_thermal", 1, 0, priv, + &rcar_thermal_zone_ops, NULL, 0, + IDLE_INTERVAL); if (IS_ERR(zone)) { dev_err(&pdev->dev, "thermal zone device is NULL\n"); return PTR_ERR(zone); From 03b79bda8d061d80a6106e257c072a2754141bab Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 7 Dec 2012 11:29:32 +0100 Subject: [PATCH 0508/4607] drivers/thermal/spear_thermal.c: use devm_clk_get devm_clk_get allocates a resource that is released when a driver detaches. This patch uses devm_clk_get for data that is allocated in the probe function of a platform device and is only released in the remove function. Signed-off-by: Julia Lawall Signed-off-by: Zhang Rui --- drivers/thermal/spear_thermal.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index 6b2d8b21aaee..3c5ee5607977 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -131,7 +131,7 @@ static int spear_thermal_probe(struct platform_device *pdev) return -ENOMEM; } - stdev->clk = clk_get(&pdev->dev, NULL); + stdev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(stdev->clk)) { dev_err(&pdev->dev, "Can't get clock\n"); return PTR_ERR(stdev->clk); @@ -140,7 +140,7 @@ static int spear_thermal_probe(struct platform_device *pdev) ret = clk_enable(stdev->clk); if (ret) { dev_err(&pdev->dev, "Can't enable clock\n"); - goto put_clk; + return ret; } stdev->flags = val; @@ -163,8 +163,6 @@ static int spear_thermal_probe(struct platform_device *pdev) disable_clk: clk_disable(stdev->clk); -put_clk: - clk_put(stdev->clk); return ret; } @@ -183,7 +181,6 @@ static int spear_thermal_exit(struct platform_device *pdev) writel_relaxed(actual_mask & ~stdev->flags, stdev->thermal_base); clk_disable(stdev->clk); - clk_put(stdev->clk); return 0; } From caa5cbd5a18a72bb6f5a5211ddeade71fdb2282d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 12 Dec 2012 15:54:24 +0530 Subject: [PATCH 0509/4607] thermal: exynos: Use of_match_ptr() macro This eliminates having an #ifdef returning NULL for the case when OF is disabled. Signed-off-by: Sachin Kamat Signed-off-by: Zhang Rui --- drivers/thermal/exynos_thermal.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index e2ef8a3531ac..327102a4596a 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -808,8 +808,6 @@ static const struct of_device_id exynos_tmu_match[] = { {}, }; MODULE_DEVICE_TABLE(of, exynos_tmu_match); -#else -#define exynos_tmu_match NULL #endif static struct platform_device_id exynos_tmu_driver_ids[] = { @@ -1085,7 +1083,7 @@ static struct platform_driver exynos_tmu_driver = { .name = "exynos-tmu", .owner = THIS_MODULE, .pm = EXYNOS_TMU_PM, - .of_match_table = exynos_tmu_match, + .of_match_table = of_match_ptr(exynos_tmu_match), }, .probe = exynos_tmu_probe, .remove = __devexit_p(exynos_tmu_remove), From c313637641c3e33388903e16cfde97b2e67adb9e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 19 Dec 2012 14:20:58 +0530 Subject: [PATCH 0510/4607] thermal: db8500: Use of_match_ptr() macro in db8500_thermal.c This eliminates having an #ifdef returning NULL for the case when OF is disabled. Signed-off-by: Sachin Kamat Reviewed-by: Hongbo Zhang Signed-off-by: Zhang Rui --- drivers/thermal/db8500_thermal.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c index ec71ade3e317..61ce60a35921 100644 --- a/drivers/thermal/db8500_thermal.c +++ b/drivers/thermal/db8500_thermal.c @@ -508,15 +508,13 @@ static const struct of_device_id db8500_thermal_match[] = { { .compatible = "stericsson,db8500-thermal" }, {}, }; -#else -#define db8500_thermal_match NULL #endif static struct platform_driver db8500_thermal_driver = { .driver = { .owner = THIS_MODULE, .name = "db8500-thermal", - .of_match_table = db8500_thermal_match, + .of_match_table = of_match_ptr(db8500_thermal_match), }, .probe = db8500_thermal_probe, .suspend = db8500_thermal_suspend, From c076fc42a4d6c51d6422aaf9f6626bf1adf983e7 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 19 Dec 2012 14:20:59 +0530 Subject: [PATCH 0511/4607] thermal: db8500: Use of_match_ptr() macro in db8500_cpufreq_cooling.c This eliminates having an #ifdef returning NULL for the case when OF is disabled. Signed-off-by: Sachin Kamat Reviewed-by: Hongbo Zhang Signed-off-by: Zhang Rui --- drivers/thermal/db8500_cpufreq_cooling.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c index 4cf8e72af90a..21419851fc02 100644 --- a/drivers/thermal/db8500_cpufreq_cooling.c +++ b/drivers/thermal/db8500_cpufreq_cooling.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -73,15 +74,13 @@ static const struct of_device_id db8500_cpufreq_cooling_match[] = { { .compatible = "stericsson,db8500-cpufreq-cooling" }, {}, }; -#else -#define db8500_cpufreq_cooling_match NULL #endif static struct platform_driver db8500_cpufreq_cooling_driver = { .driver = { .owner = THIS_MODULE, .name = "db8500-cpufreq-cooling", - .of_match_table = db8500_cpufreq_cooling_match, + .of_match_table = of_match_ptr(db8500_cpufreq_cooling_match), }, .probe = db8500_cpufreq_cooling_probe, .suspend = db8500_cpufreq_cooling_suspend, From ca75e032a51013b5b9de16b7aec3674ce29b544f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 3 Jan 2013 15:30:09 -0700 Subject: [PATCH 0512/4607] frv/PCI: Remove unused pci_root_bus pci_root_bus is unused, so remove all references to it. Signed-off-by: Bjorn Helgaas --- arch/frv/mb93090-mb00/pci-frv.h | 1 - arch/frv/mb93090-mb00/pci-vdk.c | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/frv/mb93090-mb00/pci-frv.h b/arch/frv/mb93090-mb00/pci-frv.h index 089eeba4f3bc..76c4e73d643d 100644 --- a/arch/frv/mb93090-mb00/pci-frv.h +++ b/arch/frv/mb93090-mb00/pci-frv.h @@ -31,7 +31,6 @@ void pcibios_resource_survey(void); /* pci-vdk.c */ extern int __nongpreldata pcibios_last_bus; -extern struct pci_bus *__nongpreldata pci_root_bus; extern struct pci_ops *__nongpreldata pci_root_ops; /* pci-irq.c */ diff --git a/arch/frv/mb93090-mb00/pci-vdk.c b/arch/frv/mb93090-mb00/pci-vdk.c index 71e9bcf58105..1152a1e3cabb 100644 --- a/arch/frv/mb93090-mb00/pci-vdk.c +++ b/arch/frv/mb93090-mb00/pci-vdk.c @@ -26,7 +26,6 @@ unsigned int __nongpreldata pci_probe = 1; int __nongpreldata pcibios_last_bus = -1; -struct pci_bus *__nongpreldata pci_root_bus; struct pci_ops *__nongpreldata pci_root_ops; /* @@ -416,8 +415,7 @@ int __init pcibios_init(void) printk("PCI: Probing PCI hardware\n"); pci_add_resource(&resources, &pci_ioport_resource); pci_add_resource(&resources, &pci_iomem_resource); - pci_root_bus = pci_scan_root_bus(NULL, 0, pci_root_ops, NULL, - &resources); + pci_scan_root_bus(NULL, 0, pci_root_ops, NULL, &resources); pcibios_irq_init(); pcibios_fixup_peer_bridges(); From 7f0d21f937939d05649b95c2ff34c53f6726d11e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 3 Jan 2013 15:30:21 -0700 Subject: [PATCH 0513/4607] mn10300/PCI: Remove unused pci_root_bus pci_root_bus is unused, so remove all references to it. Signed-off-by: Bjorn Helgaas --- arch/mn10300/unit-asb2305/pci-asb2305.h | 1 - arch/mn10300/unit-asb2305/pci.c | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.h b/arch/mn10300/unit-asb2305/pci-asb2305.h index 1194fe486b01..7fa66a0e4624 100644 --- a/arch/mn10300/unit-asb2305/pci-asb2305.h +++ b/arch/mn10300/unit-asb2305/pci-asb2305.h @@ -36,7 +36,6 @@ extern void pcibios_resource_survey(void); /* pci.c */ extern int pcibios_last_bus; -extern struct pci_bus *pci_root_bus; extern struct pci_ops *pci_root_ops; extern struct irq_routing_table *pcibios_get_irq_routing_table(void); diff --git a/arch/mn10300/unit-asb2305/pci.c b/arch/mn10300/unit-asb2305/pci.c index e2059486d3f8..426bf51605f3 100644 --- a/arch/mn10300/unit-asb2305/pci.c +++ b/arch/mn10300/unit-asb2305/pci.c @@ -24,7 +24,6 @@ unsigned int pci_probe = 1; int pcibios_last_bus = -1; -struct pci_bus *pci_root_bus; struct pci_ops *pci_root_ops; /* @@ -377,8 +376,7 @@ static int __init pcibios_init(void) pci_add_resource_offset(&resources, &pci_ioport_resource, io_offset); pci_add_resource_offset(&resources, &pci_iomem_resource, mem_offset); - pci_root_bus = pci_scan_root_bus(NULL, 0, &pci_direct_ampci, NULL, - &resources); + pci_scan_root_bus(NULL, 0, &pci_direct_ampci, NULL, &resources); pcibios_irq_init(); pcibios_fixup_irqs(); From f7ffe19a6fab14df70bd0acca9a4836aa80b15f5 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2013 12:13:15 -0700 Subject: [PATCH 0514/4607] PCI: Use "unsigned long" for __pci_enable_device_flags to match ioport.h __pci_enable_device_flags() takes values like IORESOURCE_IO and IORESOURCE_MEM, which are values for struct resource.flags, which is "unsigned long", not "resource_size_t". Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5cb5820fae40..d574fbdbf4b0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1157,7 +1157,7 @@ int pci_reenable_device(struct pci_dev *dev) } static int __pci_enable_device_flags(struct pci_dev *dev, - resource_size_t flags) + unsigned long flags) { int err; int i, bars = 0; From b4b4fbba46a7a638878e58f303317e7904edfa13 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2013 12:12:55 -0700 Subject: [PATCH 0515/4607] PCI: Drop "__" prefix on __pci_enable_device_flags() Drop the useless "__" prefix on __pci_enable_device_flags(). Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d574fbdbf4b0..a2f30394c091 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1156,8 +1156,7 @@ int pci_reenable_device(struct pci_dev *dev) return 0; } -static int __pci_enable_device_flags(struct pci_dev *dev, - unsigned long flags) +static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) { int err; int i, bars = 0; @@ -1201,7 +1200,7 @@ static int __pci_enable_device_flags(struct pci_dev *dev, */ int pci_enable_device_io(struct pci_dev *dev) { - return __pci_enable_device_flags(dev, IORESOURCE_IO); + return pci_enable_device_flags(dev, IORESOURCE_IO); } /** @@ -1214,7 +1213,7 @@ int pci_enable_device_io(struct pci_dev *dev) */ int pci_enable_device_mem(struct pci_dev *dev) { - return __pci_enable_device_flags(dev, IORESOURCE_MEM); + return pci_enable_device_flags(dev, IORESOURCE_MEM); } /** @@ -1230,7 +1229,7 @@ int pci_enable_device_mem(struct pci_dev *dev) */ int pci_enable_device(struct pci_dev *dev) { - return __pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO); + return pci_enable_device_flags(dev, IORESOURCE_MEM | IORESOURCE_IO); } /* From 8303dc9952758ab3060a3ee9a19ecb6fec83c600 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jan 2013 17:27:47 -0300 Subject: [PATCH 0516/4607] [media] em28xx: initialize button/I2C IR earlier The em28xx-input is used by 3 different types of input devices: - devices with buttons (like cameras and grabber devices); - devices with I2C remotes; - em2860 or latter chips with RC support embedded. When the device has an I2C remote, all it needs to do is to call the proper I2C driver (ir-i2c-kbd), passing the proper data to it, and just leave the code. Also, button devices have its own init code that doesn't depend on having an IR or not (as a general rule, they don't have). So, move its init code to fix bugs introduced by earlier patches that prevent them to work. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 3598221378ac..2a1b3d277db1 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -590,6 +590,17 @@ static int em28xx_ir_init(struct em28xx *dev) int err = -ENOMEM; u64 rc_type; + if (dev->board.has_snapshot_button) + em28xx_register_snapshot_button(dev); + + if (dev->board.has_ir_i2c) { + em28xx_register_i2c_ir(dev); +#if defined(CONFIG_MODULES) && defined(MODULE) + request_module("ir-kbd-i2c"); +#endif + return 0; + } + if (dev->board.ir_codes == NULL) { /* No remote control support */ em28xx_warn("Remote control support is not available for " @@ -663,15 +674,6 @@ static int em28xx_ir_init(struct em28xx *dev) if (err) goto error; - em28xx_register_i2c_ir(dev); - -#if defined(CONFIG_MODULES) && defined(MODULE) - if (dev->board.has_ir_i2c) - request_module("ir-kbd-i2c"); -#endif - if (dev->board.has_snapshot_button) - em28xx_register_snapshot_button(dev); - return 0; error: From 728f9778e273a11a65926ac21574e6ca8d911ebf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jan 2013 17:32:58 -0300 Subject: [PATCH 0517/4607] [media] em28xx: autoload em28xx-rc if the device has an I2C IR If the device has an I2C IR, em28xx-rc should be loaded by default, except if the user explicitly requested to not load, via modprobe option. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index f5cac47d099a..e17be073c0c3 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2912,7 +2912,7 @@ static void request_module_async(struct work_struct *work) if (dev->board.has_dvb) request_module("em28xx-dvb"); - if (dev->board.ir_codes && !disable_ir) + if ((dev->board.ir_codes || dev->board.has_ir_i2c) && !disable_ir) request_module("em28xx-rc"); #endif /* CONFIG_MODULES */ } From d40580e7c043cdfbf472e76052a4606fea16642d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jan 2013 17:37:26 -0300 Subject: [PATCH 0518/4607] [media] em28xx: simplify IR names on I2C devices The ir-i2c-kbd already adds I2C IR before the name. The way it is, the devices are named as: "i2c IR (i2c IR (EM2840 Hauppaug" With is ugly and incorrect. After this patch, it is now properly displayed as: "i2c IR (WinTV USB2)" Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 2a1b3d277db1..ebbb0aa51dfa 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -472,22 +472,22 @@ static void em28xx_register_i2c_ir(struct em28xx *dev) case EM2820_BOARD_TERRATEC_CINERGY_250: dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; dev->init_data.get_key = em28xx_get_key_terratec; - dev->init_data.name = "i2c IR (EM28XX Terratec)"; + dev->init_data.name = "Terratec Cinergy 200/250"; break; case EM2820_BOARD_PINNACLE_USB_2: dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; + dev->init_data.name = "Pinnacle USB2"; break; case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; dev->init_data.get_key = em28xx_get_key_em_haup; - dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; + dev->init_data.name = "WinTV USB2"; break; case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; - dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; + dev->init_data.name = "Winfast TV USBII Deluxe"; break; } From 3ac693c40a08703f3c456d8f45940a48c1f8d93f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 4 Jan 2013 18:01:59 -0300 Subject: [PATCH 0519/4607] [media] em28xx: tell ir-kbd-i2c that WinTV uses an RC5 protocol Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index ebbb0aa51dfa..5b3292c8310b 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -483,6 +483,7 @@ static void em28xx_register_i2c_ir(struct em28xx *dev) dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; dev->init_data.get_key = em28xx_get_key_em_haup; dev->init_data.name = "WinTV USB2"; + dev->init_data.type = RC_BIT_RC5; break; case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; From d1a6f4f19728d6e90480e53601a90fc9f6a348ad Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Fri, 4 Jan 2013 19:49:31 -0500 Subject: [PATCH 0520/4607] block: delete super ancient PC-XT driver for 1980's hardware This driver was for the 8 bit ISA cards that were installed in the PC-XT machines of 1980 vintage. They supported the dual ribbon cable MFM drives of 10-20MB capacity, and ran at a 3:1 interleave, giving performance on the order of 128kB/s. By the introduction of the PC-AT (286) these controllers were already scrapped in favour of 16 bit controllers with some onboard RAM that could support a 1:1 interleave. The git history doesn't show any evidence of runtime fixes that would reflect active usage; instead just the usual tree-wide API type changes/cleanups. Going back to in-source changelogs, the last "runtime" fix that is evident is something I did over a dozen years ago[1] -- and even back then, the hardware was long since unavailable, so that ancient fix was also not runtime tested. The time is long overdue for this to get flushed, so lets get rid of it before anyone wastes more time doing builds and sparse checks etc. on long since dead code. [1] http://lkml.indiana.edu/hypermail/linux/kernel/0102.2/0027.html Signed-off-by: Paul Gortmaker --- drivers/block/Kconfig | 13 - drivers/block/Makefile | 1 - drivers/block/xd.c | 1123 ---------------------------------------- drivers/block/xd.h | 134 ----- 4 files changed, 1271 deletions(-) delete mode 100644 drivers/block/xd.c delete mode 100644 drivers/block/xd.h diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 824e09c4d0d7..e29a44e78e15 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -63,19 +63,6 @@ config AMIGA_Z2RAM To compile this driver as a module, choose M here: the module will be called z2ram. -config BLK_DEV_XD - tristate "XT hard disk support" - depends on ISA && ISA_DMA_API - select CHECK_SIGNATURE - help - Very old 8 bit hard disk controllers used in the IBM XT computer - will be supported if you say Y here. - - To compile this driver as a module, choose M here: the - module will be called xd. - - It's pretty unlikely that you have one of these: say N. - config GDROM tristate "SEGA Dreamcast GD-ROM drive" depends on SH_DREAMCAST diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 17e82df3df74..5195c1f61b2a 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o obj-$(CONFIG_BLK_DEV_RAM) += brd.o obj-$(CONFIG_BLK_DEV_LOOP) += loop.o -obj-$(CONFIG_BLK_DEV_XD) += xd.o obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o diff --git a/drivers/block/xd.c b/drivers/block/xd.c deleted file mode 100644 index ff540520bada..000000000000 --- a/drivers/block/xd.c +++ /dev/null @@ -1,1123 +0,0 @@ -/* - * This file contains the driver for an XT hard disk controller - * (at least the DTC 5150X) for Linux. - * - * Author: Pat Mackinlay, pat@it.com.au - * Date: 29/09/92 - * - * Revised: 01/01/93, ... - * - * Ref: DTC 5150X Controller Specification (thanks to Kevin Fowler, - * kevinf@agora.rain.com) - * Also thanks to: Salvador Abreu, Dave Thaler, Risto Kankkunen and - * Wim Van Dorst. - * - * Revised: 04/04/94 by Risto Kankkunen - * Moved the detection code from xd_init() to xd_geninit() as it needed - * interrupts enabled and Linus didn't want to enable them in that first - * phase. xd_geninit() is the place to do these kinds of things anyway, - * he says. - * - * Modularized: 04/10/96 by Todd Fries, tfries@umr.edu - * - * Revised: 13/12/97 by Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl - * Fixed some problems with disk initialization and module initiation. - * Added support for manual geometry setting (except Seagate controllers) - * in form: - * xd_geo=,,[,,,] - * Recovered DMA access. Abridged messages. Added support for DTC5051CX, - * WD1002-27X & XEBEC controllers. Driver uses now some jumper settings. - * Extended ioctl() support. - * - * Bugfix: 15/02/01, Paul G. - inform queue layer of tiny xd_maxsect. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "xd.h" - -static DEFINE_MUTEX(xd_mutex); -static void __init do_xd_setup (int *integers); -#ifdef MODULE -static int xd[5] = { -1,-1,-1,-1, }; -#endif - -#define XD_DONT_USE_DMA 0 /* Initial value. may be overriden using - "nodma" module option */ -#define XD_INIT_DISK_DELAY (30) /* 30 ms delay during disk initialization */ - -/* Above may need to be increased if a problem with the 2nd drive detection - (ST11M controller) or resetting a controller (WD) appears */ - -static XD_INFO xd_info[XD_MAXDRIVES]; - -/* If you try this driver and find that your card is not detected by the driver at bootup, you need to add your BIOS - signature and details to the following list of signatures. A BIOS signature is a string embedded into the first - few bytes of your controller's on-board ROM BIOS. To find out what yours is, use something like MS-DOS's DEBUG - command. Run DEBUG, and then you can examine your BIOS signature with: - - d xxxx:0000 - - where xxxx is the segment of your controller (like C800 or D000 or something). On the ASCII dump at the right, you should - be able to see a string mentioning the manufacturer's copyright etc. Add this string into the table below. The parameters - in the table are, in order: - - offset ; this is the offset (in bytes) from the start of your ROM where the signature starts - signature ; this is the actual text of the signature - xd_?_init_controller ; this is the controller init routine used by your controller - xd_?_init_drive ; this is the drive init routine used by your controller - - The controllers directly supported at the moment are: DTC 5150x, WD 1004A27X, ST11M/R and override. If your controller is - made by the same manufacturer as one of these, try using the same init routines as they do. If that doesn't work, your - best bet is to use the "override" routines. These routines use a "portable" method of getting the disk's geometry, and - may work with your card. If none of these seem to work, try sending me some email and I'll see what I can do . - - NOTE: You can now specify your XT controller's parameters from the command line in the form xd=TYPE,IRQ,IO,DMA. The driver - should be able to detect your drive's geometry from this info. (eg: xd=0,5,0x320,3 is the "standard"). */ - -#include -#define xd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,get_order(size)) -#define xd_dma_mem_free(addr, size) free_pages(addr, get_order(size)) -static char *xd_dma_buffer; - -static XD_SIGNATURE xd_sigs[] __initdata = { - { 0x0000,"Override geometry handler",NULL,xd_override_init_drive,"n unknown" }, /* Pat Mackinlay, pat@it.com.au */ - { 0x0008,"[BXD06 (C) DTC 17-MAY-1985]",xd_dtc_init_controller,xd_dtc5150cx_init_drive," DTC 5150CX" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ - { 0x000B,"CRD18A Not an IBM rom. (C) Copyright Data Technology Corp. 05/31/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Todd Fries, tfries@umr.edu */ - { 0x000B,"CXD23A Not an IBM ROM (C)Copyright Data Technology Corp 12/03/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Pat Mackinlay, pat@it.com.au */ - { 0x0008,"07/15/86(C) Copyright 1986 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Dig. 1002-27X" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ - { 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Dig. WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */ - { 0x0015,"SEAGATE ST11 BIOS REVISION",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Salvador Abreu, spa@fct.unl.pt */ - { 0x0010,"ST11R BIOS",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */ - { 0x0010,"ST11 BIOS v1.7",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11R" }, /* Alan Hourihane, alanh@fairlite.demon.co.uk */ - { 0x1000,"(c)Copyright 1987 SMS",xd_omti_init_controller,xd_omti_init_drive,"n OMTI 5520" }, /* Dirk Melchers, dirk@merlin.nbg.sub.org */ - { 0x0006,"COPYRIGHT XEBEC (C) 1984",xd_xebec_init_controller,xd_xebec_init_drive," XEBEC" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */ - { 0x0008,"(C) Copyright 1984 Western Digital Corp", xd_wd_init_controller, xd_wd_init_drive," Western Dig. 1002s-wx2" }, - { 0x0008,"(C) Copyright 1986 Western Digital Corporation", xd_wd_init_controller, xd_wd_init_drive," 1986 Western Digital" }, /* jfree@sovereign.org */ -}; - -static unsigned int xd_bases[] __initdata = -{ - 0xC8000, 0xCA000, 0xCC000, - 0xCE000, 0xD0000, 0xD2000, - 0xD4000, 0xD6000, 0xD8000, - 0xDA000, 0xDC000, 0xDE000, - 0xE0000 -}; - -static DEFINE_SPINLOCK(xd_lock); - -static struct gendisk *xd_gendisk[2]; - -static int xd_getgeo(struct block_device *bdev, struct hd_geometry *geo); - -static const struct block_device_operations xd_fops = { - .owner = THIS_MODULE, - .ioctl = xd_ioctl, - .getgeo = xd_getgeo, -}; -static DECLARE_WAIT_QUEUE_HEAD(xd_wait_int); -static u_char xd_drives, xd_irq = 5, xd_dma = 3, xd_maxsectors; -static u_char xd_override __initdata = 0, xd_type __initdata = 0; -static u_short xd_iobase = 0x320; -static int xd_geo[XD_MAXDRIVES*3] __initdata = { 0, }; - -static volatile int xdc_busy; -static struct timer_list xd_watchdog_int; - -static volatile u_char xd_error; -static bool nodma = XD_DONT_USE_DMA; - -static struct request_queue *xd_queue; - -/* xd_init: register the block device number and set up pointer tables */ -static int __init xd_init(void) -{ - u_char i,controller; - unsigned int address; - int err; - -#ifdef MODULE - { - u_char count = 0; - for (i = 4; i > 0; i--) - if (((xd[i] = xd[i-1]) >= 0) && !count) - count = i; - if ((xd[0] = count)) - do_xd_setup(xd); - } -#endif - - init_timer (&xd_watchdog_int); xd_watchdog_int.function = xd_watchdog; - - err = -EBUSY; - if (register_blkdev(XT_DISK_MAJOR, "xd")) - goto out1; - - err = -ENOMEM; - xd_queue = blk_init_queue(do_xd_request, &xd_lock); - if (!xd_queue) - goto out1a; - - if (xd_detect(&controller,&address)) { - - printk("Detected a%s controller (type %d) at address %06x\n", - xd_sigs[controller].name,controller,address); - if (!request_region(xd_iobase,4,"xd")) { - printk("xd: Ports at 0x%x are not available\n", - xd_iobase); - goto out2; - } - if (controller) - xd_sigs[controller].init_controller(address); - xd_drives = xd_initdrives(xd_sigs[controller].init_drive); - - printk("Detected %d hard drive%s (using IRQ%d & DMA%d)\n", - xd_drives,xd_drives == 1 ? "" : "s",xd_irq,xd_dma); - } - - /* - * With the drive detected, xd_maxsectors should now be known. - * If xd_maxsectors is 0, nothing was detected and we fall through - * to return -ENODEV - */ - if (!xd_dma_buffer && xd_maxsectors) { - xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200); - if (!xd_dma_buffer) { - printk(KERN_ERR "xd: Out of memory.\n"); - goto out3; - } - } - - err = -ENODEV; - if (!xd_drives) - goto out3; - - for (i = 0; i < xd_drives; i++) { - XD_INFO *p = &xd_info[i]; - struct gendisk *disk = alloc_disk(64); - if (!disk) - goto Enomem; - p->unit = i; - disk->major = XT_DISK_MAJOR; - disk->first_minor = i<<6; - sprintf(disk->disk_name, "xd%c", i+'a'); - disk->fops = &xd_fops; - disk->private_data = p; - disk->queue = xd_queue; - set_capacity(disk, p->heads * p->cylinders * p->sectors); - printk(" %s: CHS=%d/%d/%d\n", disk->disk_name, - p->cylinders, p->heads, p->sectors); - xd_gendisk[i] = disk; - } - - err = -EBUSY; - if (request_irq(xd_irq,xd_interrupt_handler, 0, "XT hard disk", NULL)) { - printk("xd: unable to get IRQ%d\n",xd_irq); - goto out4; - } - - if (request_dma(xd_dma,"xd")) { - printk("xd: unable to get DMA%d\n",xd_dma); - goto out5; - } - - /* xd_maxsectors depends on controller - so set after detection */ - blk_queue_max_hw_sectors(xd_queue, xd_maxsectors); - - for (i = 0; i < xd_drives; i++) - add_disk(xd_gendisk[i]); - - return 0; - -out5: - free_irq(xd_irq, NULL); -out4: - for (i = 0; i < xd_drives; i++) - put_disk(xd_gendisk[i]); -out3: - if (xd_maxsectors) - release_region(xd_iobase,4); - - if (xd_dma_buffer) - xd_dma_mem_free((unsigned long)xd_dma_buffer, - xd_maxsectors * 0x200); -out2: - blk_cleanup_queue(xd_queue); -out1a: - unregister_blkdev(XT_DISK_MAJOR, "xd"); -out1: - return err; -Enomem: - err = -ENOMEM; - while (i--) - put_disk(xd_gendisk[i]); - goto out3; -} - -/* xd_detect: scan the possible BIOS ROM locations for the signature strings */ -static u_char __init xd_detect (u_char *controller, unsigned int *address) -{ - int i, j; - - if (xd_override) - { - *controller = xd_type; - *address = 0; - return(1); - } - - for (i = 0; i < ARRAY_SIZE(xd_bases); i++) { - void __iomem *p = ioremap(xd_bases[i], 0x2000); - if (!p) - continue; - for (j = 1; j < ARRAY_SIZE(xd_sigs); j++) { - const char *s = xd_sigs[j].string; - if (check_signature(p + xd_sigs[j].offset, s, strlen(s))) { - *controller = j; - xd_type = j; - *address = xd_bases[i]; - iounmap(p); - return 1; - } - } - iounmap(p); - } - return 0; -} - -/* do_xd_request: handle an incoming request */ -static void do_xd_request (struct request_queue * q) -{ - struct request *req; - - if (xdc_busy) - return; - - req = blk_fetch_request(q); - while (req) { - unsigned block = blk_rq_pos(req); - unsigned count = blk_rq_cur_sectors(req); - XD_INFO *disk = req->rq_disk->private_data; - int res = -EIO; - int retry; - - if (req->cmd_type != REQ_TYPE_FS) - goto done; - if (block + count > get_capacity(req->rq_disk)) - goto done; - for (retry = 0; (retry < XD_RETRIES) && !res; retry++) - res = xd_readwrite(rq_data_dir(req), disk, req->buffer, - block, count); - done: - /* wrap up, 0 = success, -errno = fail */ - if (!__blk_end_request_cur(req, res)) - req = blk_fetch_request(q); - } -} - -static int xd_getgeo(struct block_device *bdev, struct hd_geometry *geo) -{ - XD_INFO *p = bdev->bd_disk->private_data; - - geo->heads = p->heads; - geo->sectors = p->sectors; - geo->cylinders = p->cylinders; - return 0; -} - -/* xd_ioctl: handle device ioctl's */ -static int xd_locked_ioctl(struct block_device *bdev, fmode_t mode, u_int cmd, u_long arg) -{ - switch (cmd) { - case HDIO_SET_DMA: - if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (xdc_busy) return -EBUSY; - nodma = !arg; - if (nodma && xd_dma_buffer) { - xd_dma_mem_free((unsigned long)xd_dma_buffer, - xd_maxsectors * 0x200); - xd_dma_buffer = NULL; - } else if (!nodma && !xd_dma_buffer) { - xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200); - if (!xd_dma_buffer) { - nodma = XD_DONT_USE_DMA; - return -ENOMEM; - } - } - return 0; - case HDIO_GET_DMA: - return put_user(!nodma, (long __user *) arg); - case HDIO_GET_MULTCOUNT: - return put_user(xd_maxsectors, (long __user *) arg); - default: - return -EINVAL; - } -} - -static int xd_ioctl(struct block_device *bdev, fmode_t mode, - unsigned int cmd, unsigned long param) -{ - int ret; - - mutex_lock(&xd_mutex); - ret = xd_locked_ioctl(bdev, mode, cmd, param); - mutex_unlock(&xd_mutex); - - return ret; -} - -/* xd_readwrite: handle a read/write request */ -static int xd_readwrite (u_char operation,XD_INFO *p,char *buffer,u_int block,u_int count) -{ - int drive = p->unit; - u_char cmdblk[6],sense[4]; - u_short track,cylinder; - u_char head,sector,control,mode = PIO_MODE,temp; - char **real_buffer; - register int i; - -#ifdef DEBUG_READWRITE - printk("xd_readwrite: operation = %s, drive = %d, buffer = 0x%X, block = %d, count = %d\n",operation == READ ? "read" : "write",drive,buffer,block,count); -#endif /* DEBUG_READWRITE */ - - spin_unlock_irq(&xd_lock); - - control = p->control; - if (!xd_dma_buffer) - xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200); - while (count) { - temp = count < xd_maxsectors ? count : xd_maxsectors; - - track = block / p->sectors; - head = track % p->heads; - cylinder = track / p->heads; - sector = block % p->sectors; - -#ifdef DEBUG_READWRITE - printk("xd_readwrite: drive = %d, head = %d, cylinder = %d, sector = %d, count = %d\n",drive,head,cylinder,sector,temp); -#endif /* DEBUG_READWRITE */ - - if (xd_dma_buffer) { - mode = xd_setup_dma(operation == READ ? DMA_MODE_READ : DMA_MODE_WRITE,(u_char *)(xd_dma_buffer),temp * 0x200); - real_buffer = &xd_dma_buffer; - for (i=0; i < (temp * 0x200); i++) - xd_dma_buffer[i] = buffer[i]; - } - else - real_buffer = &buffer; - - xd_build(cmdblk,operation == READ ? CMD_READ : CMD_WRITE,drive,head,cylinder,sector,temp & 0xFF,control); - - switch (xd_command(cmdblk,mode,(u_char *)(*real_buffer),(u_char *)(*real_buffer),sense,XD_TIMEOUT)) { - case 1: - printk("xd%c: %s timeout, recalibrating drive\n",'a'+drive,(operation == READ ? "read" : "write")); - xd_recalibrate(drive); - spin_lock_irq(&xd_lock); - return -EIO; - case 2: - if (sense[0] & 0x30) { - printk("xd%c: %s - ",'a'+drive,(operation == READ ? "reading" : "writing")); - switch ((sense[0] & 0x30) >> 4) { - case 0: printk("drive error, code = 0x%X",sense[0] & 0x0F); - break; - case 1: printk("controller error, code = 0x%X",sense[0] & 0x0F); - break; - case 2: printk("command error, code = 0x%X",sense[0] & 0x0F); - break; - case 3: printk("miscellaneous error, code = 0x%X",sense[0] & 0x0F); - break; - } - } - if (sense[0] & 0x80) - printk(" - CHS = %d/%d/%d\n",((sense[2] & 0xC0) << 2) | sense[3],sense[1] & 0x1F,sense[2] & 0x3F); - /* reported drive number = (sense[1] & 0xE0) >> 5 */ - else - printk(" - no valid disk address\n"); - spin_lock_irq(&xd_lock); - return -EIO; - } - if (xd_dma_buffer) - for (i=0; i < (temp * 0x200); i++) - buffer[i] = xd_dma_buffer[i]; - - count -= temp, buffer += temp * 0x200, block += temp; - } - spin_lock_irq(&xd_lock); - return 0; -} - -/* xd_recalibrate: recalibrate a given drive and reset controller if necessary */ -static void xd_recalibrate (u_char drive) -{ - u_char cmdblk[6]; - - xd_build(cmdblk,CMD_RECALIBRATE,drive,0,0,0,0,0); - if (xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT * 8)) - printk("xd%c: warning! error recalibrating, controller may be unstable\n", 'a'+drive); -} - -/* xd_interrupt_handler: interrupt service routine */ -static irqreturn_t xd_interrupt_handler(int irq, void *dev_id) -{ - if (inb(XD_STATUS) & STAT_INTERRUPT) { /* check if it was our device */ -#ifdef DEBUG_OTHER - printk("xd_interrupt_handler: interrupt detected\n"); -#endif /* DEBUG_OTHER */ - outb(0,XD_CONTROL); /* acknowledge interrupt */ - wake_up(&xd_wait_int); /* and wake up sleeping processes */ - return IRQ_HANDLED; - } - else - printk("xd: unexpected interrupt\n"); - return IRQ_NONE; -} - -/* xd_setup_dma: set up the DMA controller for a data transfer */ -static u_char xd_setup_dma (u_char mode,u_char *buffer,u_int count) -{ - unsigned long f; - - if (nodma) - return (PIO_MODE); - if (((unsigned long) buffer & 0xFFFF0000) != (((unsigned long) buffer + count) & 0xFFFF0000)) { -#ifdef DEBUG_OTHER - printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n"); -#endif /* DEBUG_OTHER */ - return (PIO_MODE); - } - - f=claim_dma_lock(); - disable_dma(xd_dma); - clear_dma_ff(xd_dma); - set_dma_mode(xd_dma,mode); - set_dma_addr(xd_dma, (unsigned long) buffer); - set_dma_count(xd_dma,count); - - release_dma_lock(f); - - return (DMA_MODE); /* use DMA and INT */ -} - -/* xd_build: put stuff into an array in a format suitable for the controller */ -static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control) -{ - cmdblk[0] = command; - cmdblk[1] = ((drive & 0x07) << 5) | (head & 0x1F); - cmdblk[2] = ((cylinder & 0x300) >> 2) | (sector & 0x3F); - cmdblk[3] = cylinder & 0xFF; - cmdblk[4] = count; - cmdblk[5] = control; - - return (cmdblk); -} - -static void xd_watchdog (unsigned long unused) -{ - xd_error = 1; - wake_up(&xd_wait_int); -} - -/* xd_waitport: waits until port & mask == flags or a timeout occurs. return 1 for a timeout */ -static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout) -{ - u_long expiry = jiffies + timeout; - int success; - - xdc_busy = 1; - while ((success = ((inb(port) & mask) != flags)) && time_before(jiffies, expiry)) - schedule_timeout_uninterruptible(1); - xdc_busy = 0; - return (success); -} - -static inline u_int xd_wait_for_IRQ (void) -{ - unsigned long flags; - xd_watchdog_int.expires = jiffies + 8 * HZ; - add_timer(&xd_watchdog_int); - - flags=claim_dma_lock(); - enable_dma(xd_dma); - release_dma_lock(flags); - - sleep_on(&xd_wait_int); - del_timer(&xd_watchdog_int); - xdc_busy = 0; - - flags=claim_dma_lock(); - disable_dma(xd_dma); - release_dma_lock(flags); - - if (xd_error) { - printk("xd: missed IRQ - command aborted\n"); - xd_error = 0; - return (1); - } - return (0); -} - -/* xd_command: handle all data transfers necessary for a single command */ -static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout) -{ - u_char cmdblk[6],csb,complete = 0; - -#ifdef DEBUG_COMMAND - printk("xd_command: command = 0x%X, mode = 0x%X, indata = 0x%X, outdata = 0x%X, sense = 0x%X\n",command,mode,indata,outdata,sense); -#endif /* DEBUG_COMMAND */ - - outb(0,XD_SELECT); - outb(mode,XD_CONTROL); - - if (xd_waitport(XD_STATUS,STAT_SELECT,STAT_SELECT,timeout)) - return (1); - - while (!complete) { - if (xd_waitport(XD_STATUS,STAT_READY,STAT_READY,timeout)) - return (1); - - switch (inb(XD_STATUS) & (STAT_COMMAND | STAT_INPUT)) { - case 0: - if (mode == DMA_MODE) { - if (xd_wait_for_IRQ()) - return (1); - } else - outb(outdata ? *outdata++ : 0,XD_DATA); - break; - case STAT_INPUT: - if (mode == DMA_MODE) { - if (xd_wait_for_IRQ()) - return (1); - } else - if (indata) - *indata++ = inb(XD_DATA); - else - inb(XD_DATA); - break; - case STAT_COMMAND: - outb(command ? *command++ : 0,XD_DATA); - break; - case STAT_COMMAND | STAT_INPUT: - complete = 1; - break; - } - } - csb = inb(XD_DATA); - - if (xd_waitport(XD_STATUS,0,STAT_SELECT,timeout)) /* wait until deselected */ - return (1); - - if (csb & CSB_ERROR) { /* read sense data if error */ - xd_build(cmdblk,CMD_SENSE,(csb & CSB_LUN) >> 5,0,0,0,0,0); - if (xd_command(cmdblk,0,sense,NULL,NULL,XD_TIMEOUT)) - printk("xd: warning! sense command failed!\n"); - } - -#ifdef DEBUG_COMMAND - printk("xd_command: completed with csb = 0x%X\n",csb); -#endif /* DEBUG_COMMAND */ - - return (csb & CSB_ERROR); -} - -static u_char __init xd_initdrives (void (*init_drive)(u_char drive)) -{ - u_char cmdblk[6],i,count = 0; - - for (i = 0; i < XD_MAXDRIVES; i++) { - xd_build(cmdblk,CMD_TESTREADY,i,0,0,0,0,0); - if (!xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT*8)) { - msleep_interruptible(XD_INIT_DISK_DELAY); - - init_drive(count); - count++; - - msleep_interruptible(XD_INIT_DISK_DELAY); - } - } - return (count); -} - -static void __init xd_manual_geo_set (u_char drive) -{ - xd_info[drive].heads = (u_char)(xd_geo[3 * drive + 1]); - xd_info[drive].cylinders = (u_short)(xd_geo[3 * drive]); - xd_info[drive].sectors = (u_char)(xd_geo[3 * drive + 2]); -} - -static void __init xd_dtc_init_controller (unsigned int address) -{ - switch (address) { - case 0x00000: - case 0xC8000: break; /*initial: 0x320 */ - case 0xCA000: xd_iobase = 0x324; - case 0xD0000: /*5150CX*/ - case 0xD8000: break; /*5150CX & 5150XL*/ - default: printk("xd_dtc_init_controller: unsupported BIOS address %06x\n",address); - break; - } - xd_maxsectors = 0x01; /* my card seems to have trouble doing multi-block transfers? */ - - outb(0,XD_RESET); /* reset the controller */ -} - - -static void __init xd_dtc5150cx_init_drive (u_char drive) -{ - /* values from controller's BIOS - BIOS chip may be removed */ - static u_short geometry_table[][4] = { - {0x200,8,0x200,0x100}, - {0x267,2,0x267,0x267}, - {0x264,4,0x264,0x80}, - {0x132,4,0x132,0x0}, - {0x132,2,0x80, 0x132}, - {0x177,8,0x177,0x0}, - {0x132,8,0x84, 0x0}, - {}, /* not used */ - {0x132,6,0x80, 0x100}, - {0x200,6,0x100,0x100}, - {0x264,2,0x264,0x80}, - {0x280,4,0x280,0x100}, - {0x2B9,3,0x2B9,0x2B9}, - {0x2B9,5,0x2B9,0x2B9}, - {0x280,6,0x280,0x100}, - {0x132,4,0x132,0x0}}; - u_char n; - - n = inb(XD_JUMPER); - n = (drive ? n : (n >> 2)) & 0x33; - n = (n | (n >> 2)) & 0x0F; - if (xd_geo[3*drive]) - xd_manual_geo_set(drive); - else - if (n != 7) { - xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */ - xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */ - xd_info[drive].sectors = 17; /* sectors */ -#if 0 - xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */ - xd_info[drive].precomp = geometry_table[n][3] /* write precomp */ - xd_info[drive].ecc = 0x0B; /* ecc length */ -#endif /* 0 */ - } - else { - printk("xd%c: undetermined drive geometry\n",'a'+drive); - return; - } - xd_info[drive].control = 5; /* control byte */ - xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B); - xd_recalibrate(drive); -} - -static void __init xd_dtc_init_drive (u_char drive) -{ - u_char cmdblk[6],buf[64]; - - xd_build(cmdblk,CMD_DTCGETGEOM,drive,0,0,0,0,0); - if (!xd_command(cmdblk,PIO_MODE,buf,NULL,NULL,XD_TIMEOUT * 2)) { - xd_info[drive].heads = buf[0x0A]; /* heads */ - xd_info[drive].cylinders = ((u_short *) (buf))[0x04]; /* cylinders */ - xd_info[drive].sectors = 17; /* sectors */ - if (xd_geo[3*drive]) - xd_manual_geo_set(drive); -#if 0 - xd_info[drive].rwrite = ((u_short *) (buf + 1))[0x05]; /* reduced write */ - xd_info[drive].precomp = ((u_short *) (buf + 1))[0x06]; /* write precomp */ - xd_info[drive].ecc = buf[0x0F]; /* ecc length */ -#endif /* 0 */ - xd_info[drive].control = 0; /* control byte */ - - xd_setparam(CMD_DTCSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,((u_short *) (buf + 1))[0x05],((u_short *) (buf + 1))[0x06],buf[0x0F]); - xd_build(cmdblk,CMD_DTCSETSTEP,drive,0,0,0,0,7); - if (xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT * 2)) - printk("xd_dtc_init_drive: error setting step rate for xd%c\n", 'a'+drive); - } - else - printk("xd_dtc_init_drive: error reading geometry for xd%c\n", 'a'+drive); -} - -static void __init xd_wd_init_controller (unsigned int address) -{ - switch (address) { - case 0x00000: - case 0xC8000: break; /*initial: 0x320 */ - case 0xCA000: xd_iobase = 0x324; break; - case 0xCC000: xd_iobase = 0x328; break; - case 0xCE000: xd_iobase = 0x32C; break; - case 0xD0000: xd_iobase = 0x328; break; /* ? */ - case 0xD8000: xd_iobase = 0x32C; break; /* ? */ - default: printk("xd_wd_init_controller: unsupported BIOS address %06x\n",address); - break; - } - xd_maxsectors = 0x01; /* this one doesn't wrap properly either... */ - - outb(0,XD_RESET); /* reset the controller */ - - msleep(XD_INIT_DISK_DELAY); -} - -static void __init xd_wd_init_drive (u_char drive) -{ - /* values from controller's BIOS - BIOS may be disabled */ - static u_short geometry_table[][4] = { - {0x264,4,0x1C2,0x1C2}, /* common part */ - {0x132,4,0x099,0x0}, - {0x267,2,0x1C2,0x1C2}, - {0x267,4,0x1C2,0x1C2}, - - {0x334,6,0x335,0x335}, /* 1004 series RLL */ - {0x30E,4,0x30F,0x3DC}, - {0x30E,2,0x30F,0x30F}, - {0x267,4,0x268,0x268}, - - {0x3D5,5,0x3D6,0x3D6}, /* 1002 series RLL */ - {0x3DB,7,0x3DC,0x3DC}, - {0x264,4,0x265,0x265}, - {0x267,4,0x268,0x268}}; - - u_char cmdblk[6],buf[0x200]; - u_char n = 0,rll,jumper_state,use_jumper_geo; - u_char wd_1002 = (xd_sigs[xd_type].string[7] == '6'); - - jumper_state = ~(inb(0x322)); - if (jumper_state & 0x40) - xd_irq = 9; - rll = (jumper_state & 0x30) ? (0x04 << wd_1002) : 0; - xd_build(cmdblk,CMD_READ,drive,0,0,0,1,0); - if (!xd_command(cmdblk,PIO_MODE,buf,NULL,NULL,XD_TIMEOUT * 2)) { - xd_info[drive].heads = buf[0x1AF]; /* heads */ - xd_info[drive].cylinders = ((u_short *) (buf + 1))[0xD6]; /* cylinders */ - xd_info[drive].sectors = 17; /* sectors */ - if (xd_geo[3*drive]) - xd_manual_geo_set(drive); -#if 0 - xd_info[drive].rwrite = ((u_short *) (buf))[0xD8]; /* reduced write */ - xd_info[drive].wprecomp = ((u_short *) (buf))[0xDA]; /* write precomp */ - xd_info[drive].ecc = buf[0x1B4]; /* ecc length */ -#endif /* 0 */ - xd_info[drive].control = buf[0x1B5]; /* control byte */ - use_jumper_geo = !(xd_info[drive].heads) || !(xd_info[drive].cylinders); - if (xd_geo[3*drive]) { - xd_manual_geo_set(drive); - xd_info[drive].control = rll ? 7 : 5; - } - else if (use_jumper_geo) { - n = (((jumper_state & 0x0F) >> (drive << 1)) & 0x03) | rll; - xd_info[drive].cylinders = geometry_table[n][0]; - xd_info[drive].heads = (u_char)(geometry_table[n][1]); - xd_info[drive].control = rll ? 7 : 5; -#if 0 - xd_info[drive].rwrite = geometry_table[n][2]; - xd_info[drive].wprecomp = geometry_table[n][3]; - xd_info[drive].ecc = 0x0B; -#endif /* 0 */ - } - if (!wd_1002) { - if (use_jumper_geo) - xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders, - geometry_table[n][2],geometry_table[n][3],0x0B); - else - xd_setparam(CMD_WDSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders, - ((u_short *) (buf))[0xD8],((u_short *) (buf))[0xDA],buf[0x1B4]); - } - /* 1002 based RLL controller requests converted addressing, but reports physical - (physical 26 sec., logical 17 sec.) - 1004 based ???? */ - if (rll & wd_1002) { - if ((xd_info[drive].cylinders *= 26, - xd_info[drive].cylinders /= 17) > 1023) - xd_info[drive].cylinders = 1023; /* 1024 ? */ -#if 0 - xd_info[drive].rwrite *= 26; - xd_info[drive].rwrite /= 17; - xd_info[drive].wprecomp *= 26 - xd_info[drive].wprecomp /= 17; -#endif /* 0 */ - } - } - else - printk("xd_wd_init_drive: error reading geometry for xd%c\n",'a'+drive); - -} - -static void __init xd_seagate_init_controller (unsigned int address) -{ - switch (address) { - case 0x00000: - case 0xC8000: break; /*initial: 0x320 */ - case 0xD0000: xd_iobase = 0x324; break; - case 0xD8000: xd_iobase = 0x328; break; - case 0xE0000: xd_iobase = 0x32C; break; - default: printk("xd_seagate_init_controller: unsupported BIOS address %06x\n",address); - break; - } - xd_maxsectors = 0x40; - - outb(0,XD_RESET); /* reset the controller */ -} - -static void __init xd_seagate_init_drive (u_char drive) -{ - u_char cmdblk[6],buf[0x200]; - - xd_build(cmdblk,CMD_ST11GETGEOM,drive,0,0,0,1,0); - if (!xd_command(cmdblk,PIO_MODE,buf,NULL,NULL,XD_TIMEOUT * 2)) { - xd_info[drive].heads = buf[0x04]; /* heads */ - xd_info[drive].cylinders = (buf[0x02] << 8) | buf[0x03]; /* cylinders */ - xd_info[drive].sectors = buf[0x05]; /* sectors */ - xd_info[drive].control = 0; /* control byte */ - } - else - printk("xd_seagate_init_drive: error reading geometry from xd%c\n", 'a'+drive); -} - -/* Omti support courtesy Dirk Melchers */ -static void __init xd_omti_init_controller (unsigned int address) -{ - switch (address) { - case 0x00000: - case 0xC8000: break; /*initial: 0x320 */ - case 0xD0000: xd_iobase = 0x324; break; - case 0xD8000: xd_iobase = 0x328; break; - case 0xE0000: xd_iobase = 0x32C; break; - default: printk("xd_omti_init_controller: unsupported BIOS address %06x\n",address); - break; - } - - xd_maxsectors = 0x40; - - outb(0,XD_RESET); /* reset the controller */ -} - -static void __init xd_omti_init_drive (u_char drive) -{ - /* gets infos from drive */ - xd_override_init_drive(drive); - - /* set other parameters, Hardcoded, not that nice :-) */ - xd_info[drive].control = 2; -} - -/* Xebec support (AK) */ -static void __init xd_xebec_init_controller (unsigned int address) -{ -/* iobase may be set manually in range 0x300 - 0x33C - irq may be set manually to 2(9),3,4,5,6,7 - dma may be set manually to 1,2,3 - (How to detect them ???) -BIOS address may be set manually in range 0x0 - 0xF8000 -If you need non-standard settings use the xd=... command */ - - switch (address) { - case 0x00000: - case 0xC8000: /* initially: xd_iobase==0x320 */ - case 0xD0000: - case 0xD2000: - case 0xD4000: - case 0xD6000: - case 0xD8000: - case 0xDA000: - case 0xDC000: - case 0xDE000: - case 0xE0000: break; - default: printk("xd_xebec_init_controller: unsupported BIOS address %06x\n",address); - break; - } - - xd_maxsectors = 0x01; - outb(0,XD_RESET); /* reset the controller */ - - msleep(XD_INIT_DISK_DELAY); -} - -static void __init xd_xebec_init_drive (u_char drive) -{ - /* values from controller's BIOS - BIOS chip may be removed */ - static u_short geometry_table[][5] = { - {0x132,4,0x080,0x080,0x7}, - {0x132,4,0x080,0x080,0x17}, - {0x264,2,0x100,0x100,0x7}, - {0x264,2,0x100,0x100,0x17}, - {0x132,8,0x080,0x080,0x7}, - {0x132,8,0x080,0x080,0x17}, - {0x264,4,0x100,0x100,0x6}, - {0x264,4,0x100,0x100,0x17}, - {0x2BC,5,0x2BC,0x12C,0x6}, - {0x3A5,4,0x3A5,0x3A5,0x7}, - {0x26C,6,0x26C,0x26C,0x7}, - {0x200,8,0x200,0x100,0x17}, - {0x400,5,0x400,0x400,0x7}, - {0x400,6,0x400,0x400,0x7}, - {0x264,8,0x264,0x200,0x17}, - {0x33E,7,0x33E,0x200,0x7}}; - u_char n; - - n = inb(XD_JUMPER) & 0x0F; /* BIOS's drive number: same geometry - is assumed for BOTH drives */ - if (xd_geo[3*drive]) - xd_manual_geo_set(drive); - else { - xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */ - xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */ - xd_info[drive].sectors = 17; /* sectors */ -#if 0 - xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */ - xd_info[drive].precomp = geometry_table[n][3] /* write precomp */ - xd_info[drive].ecc = 0x0B; /* ecc length */ -#endif /* 0 */ - } - xd_info[drive].control = geometry_table[n][4]; /* control byte */ - xd_setparam(CMD_XBSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B); - xd_recalibrate(drive); -} - -/* xd_override_init_drive: this finds disk geometry in a "binary search" style, narrowing in on the "correct" number of heads - etc. by trying values until it gets the highest successful value. Idea courtesy Salvador Abreu (spa@fct.unl.pt). */ -static void __init xd_override_init_drive (u_char drive) -{ - u_short min[] = { 0,0,0 },max[] = { 16,1024,64 },test[] = { 0,0,0 }; - u_char cmdblk[6],i; - - if (xd_geo[3*drive]) - xd_manual_geo_set(drive); - else { - for (i = 0; i < 3; i++) { - while (min[i] != max[i] - 1) { - test[i] = (min[i] + max[i]) / 2; - xd_build(cmdblk,CMD_SEEK,drive,(u_char) test[0],(u_short) test[1],(u_char) test[2],0,0); - if (!xd_command(cmdblk,PIO_MODE,NULL,NULL,NULL,XD_TIMEOUT * 2)) - min[i] = test[i]; - else - max[i] = test[i]; - } - test[i] = min[i]; - } - xd_info[drive].heads = (u_char) min[0] + 1; - xd_info[drive].cylinders = (u_short) min[1] + 1; - xd_info[drive].sectors = (u_char) min[2] + 1; - } - xd_info[drive].control = 0; -} - -/* xd_setup: initialise controller from command line parameters */ -static void __init do_xd_setup (int *integers) -{ - switch (integers[0]) { - case 4: if (integers[4] < 0) - nodma = 1; - else if (integers[4] < 8) - xd_dma = integers[4]; - case 3: if ((integers[3] > 0) && (integers[3] <= 0x3FC)) - xd_iobase = integers[3]; - case 2: if ((integers[2] > 0) && (integers[2] < 16)) - xd_irq = integers[2]; - case 1: xd_override = 1; - if ((integers[1] >= 0) && (integers[1] < ARRAY_SIZE(xd_sigs))) - xd_type = integers[1]; - case 0: break; - default:printk("xd: too many parameters for xd\n"); - } - xd_maxsectors = 0x01; -} - -/* xd_setparam: set the drive characteristics */ -static void __init xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc) -{ - u_char cmdblk[14]; - - xd_build(cmdblk,command,drive,0,0,0,0,0); - cmdblk[6] = (u_char) (cylinders >> 8) & 0x03; - cmdblk[7] = (u_char) (cylinders & 0xFF); - cmdblk[8] = heads & 0x1F; - cmdblk[9] = (u_char) (rwrite >> 8) & 0x03; - cmdblk[10] = (u_char) (rwrite & 0xFF); - cmdblk[11] = (u_char) (wprecomp >> 8) & 0x03; - cmdblk[12] = (u_char) (wprecomp & 0xFF); - cmdblk[13] = ecc; - - /* Some controllers require geometry info as data, not command */ - - if (xd_command(cmdblk,PIO_MODE,NULL,&cmdblk[6],NULL,XD_TIMEOUT * 2)) - printk("xd: error setting characteristics for xd%c\n", 'a'+drive); -} - - -#ifdef MODULE - -module_param_array(xd, int, NULL, 0); -module_param_array(xd_geo, int, NULL, 0); -module_param(nodma, bool, 0); - -MODULE_LICENSE("GPL"); - -void cleanup_module(void) -{ - int i; - unregister_blkdev(XT_DISK_MAJOR, "xd"); - for (i = 0; i < xd_drives; i++) { - del_gendisk(xd_gendisk[i]); - put_disk(xd_gendisk[i]); - } - blk_cleanup_queue(xd_queue); - release_region(xd_iobase,4); - if (xd_drives) { - free_irq(xd_irq, NULL); - free_dma(xd_dma); - if (xd_dma_buffer) - xd_dma_mem_free((unsigned long)xd_dma_buffer, xd_maxsectors * 0x200); - } -} -#else - -static int __init xd_setup (char *str) -{ - int ints[5]; - get_options (str, ARRAY_SIZE (ints), ints); - do_xd_setup (ints); - return 1; -} - -/* xd_manual_geo_init: initialise drive geometry from command line parameters - (used only for WD drives) */ -static int __init xd_manual_geo_init (char *str) -{ - int i, integers[1 + 3*XD_MAXDRIVES]; - - get_options (str, ARRAY_SIZE (integers), integers); - if (integers[0]%3 != 0) { - printk("xd: incorrect number of parameters for xd_geo\n"); - return 1; - } - for (i = 0; (i < integers[0]) && (i < 3*XD_MAXDRIVES); i++) - xd_geo[i] = integers[i+1]; - return 1; -} - -__setup ("xd=", xd_setup); -__setup ("xd_geo=", xd_manual_geo_init); - -#endif /* MODULE */ - -module_init(xd_init); -MODULE_ALIAS_BLOCKDEV_MAJOR(XT_DISK_MAJOR); diff --git a/drivers/block/xd.h b/drivers/block/xd.h deleted file mode 100644 index 37cacef16e93..000000000000 --- a/drivers/block/xd.h +++ /dev/null @@ -1,134 +0,0 @@ -#ifndef _LINUX_XD_H -#define _LINUX_XD_H - -/* - * This file contains the definitions for the IO ports and errors etc. for XT hard disk controllers (at least the DTC 5150X). - * - * Author: Pat Mackinlay, pat@it.com.au - * Date: 29/09/92 - * - * Revised: 01/01/93, ... - * - * Ref: DTC 5150X Controller Specification (thanks to Kevin Fowler, kevinf@agora.rain.com) - * Also thanks to: Salvador Abreu, Dave Thaler, Risto Kankkunen and Wim Van Dorst. - */ - -#include - -/* XT hard disk controller registers */ -#define XD_DATA (xd_iobase + 0x00) /* data RW register */ -#define XD_RESET (xd_iobase + 0x01) /* reset WO register */ -#define XD_STATUS (xd_iobase + 0x01) /* status RO register */ -#define XD_SELECT (xd_iobase + 0x02) /* select WO register */ -#define XD_JUMPER (xd_iobase + 0x02) /* jumper RO register */ -#define XD_CONTROL (xd_iobase + 0x03) /* DMAE/INTE WO register */ -#define XD_RESERVED (xd_iobase + 0x03) /* reserved */ - -/* XT hard disk controller commands (incomplete list) */ -#define CMD_TESTREADY 0x00 /* test drive ready */ -#define CMD_RECALIBRATE 0x01 /* recalibrate drive */ -#define CMD_SENSE 0x03 /* request sense */ -#define CMD_FORMATDRV 0x04 /* format drive */ -#define CMD_VERIFY 0x05 /* read verify */ -#define CMD_FORMATTRK 0x06 /* format track */ -#define CMD_FORMATBAD 0x07 /* format bad track */ -#define CMD_READ 0x08 /* read */ -#define CMD_WRITE 0x0A /* write */ -#define CMD_SEEK 0x0B /* seek */ - -/* Controller specific commands */ -#define CMD_DTCSETPARAM 0x0C /* set drive parameters (DTC 5150X & CX only?) */ -#define CMD_DTCGETECC 0x0D /* get ecc error length (DTC 5150X only?) */ -#define CMD_DTCREADBUF 0x0E /* read sector buffer (DTC 5150X only?) */ -#define CMD_DTCWRITEBUF 0x0F /* write sector buffer (DTC 5150X only?) */ -#define CMD_DTCREMAPTRK 0x11 /* assign alternate track (DTC 5150X only?) */ -#define CMD_DTCGETPARAM 0xFB /* get drive parameters (DTC 5150X only?) */ -#define CMD_DTCSETSTEP 0xFC /* set step rate (DTC 5150X only?) */ -#define CMD_DTCSETGEOM 0xFE /* set geometry data (DTC 5150X only?) */ -#define CMD_DTCGETGEOM 0xFF /* get geometry data (DTC 5150X only?) */ -#define CMD_ST11GETGEOM 0xF8 /* get geometry data (Seagate ST11R/M only?) */ -#define CMD_WDSETPARAM 0x0C /* set drive parameters (WD 1004A27X only?) */ -#define CMD_XBSETPARAM 0x0C /* set drive parameters (XEBEC only?) */ - -/* Bits for command status byte */ -#define CSB_ERROR 0x02 /* error */ -#define CSB_LUN 0x20 /* logical Unit Number */ - -/* XT hard disk controller status bits */ -#define STAT_READY 0x01 /* controller is ready */ -#define STAT_INPUT 0x02 /* data flowing from controller to host */ -#define STAT_COMMAND 0x04 /* controller in command phase */ -#define STAT_SELECT 0x08 /* controller is selected */ -#define STAT_REQUEST 0x10 /* controller requesting data */ -#define STAT_INTERRUPT 0x20 /* controller requesting interrupt */ - -/* XT hard disk controller control bits */ -#define PIO_MODE 0x00 /* control bits to set for PIO */ -#define DMA_MODE 0x03 /* control bits to set for DMA & interrupt */ - -#define XD_MAXDRIVES 2 /* maximum 2 drives */ -#define XD_TIMEOUT HZ /* 1 second timeout */ -#define XD_RETRIES 4 /* maximum 4 retries */ - -#undef DEBUG /* define for debugging output */ - -#ifdef DEBUG - #define DEBUG_STARTUP /* debug driver initialisation */ - #define DEBUG_OVERRIDE /* debug override geometry detection */ - #define DEBUG_READWRITE /* debug each read/write command */ - #define DEBUG_OTHER /* debug misc. interrupt/DMA stuff */ - #define DEBUG_COMMAND /* debug each controller command */ -#endif /* DEBUG */ - -/* this structure defines the XT drives and their types */ -typedef struct { - u_char heads; - u_short cylinders; - u_char sectors; - u_char control; - int unit; -} XD_INFO; - -/* this structure defines a ROM BIOS signature */ -typedef struct { - unsigned int offset; - const char *string; - void (*init_controller)(unsigned int address); - void (*init_drive)(u_char drive); - const char *name; -} XD_SIGNATURE; - -#ifndef MODULE -static int xd_manual_geo_init (char *command); -#endif /* MODULE */ -static u_char xd_detect (u_char *controller, unsigned int *address); -static u_char xd_initdrives (void (*init_drive)(u_char drive)); - -static void do_xd_request (struct request_queue * q); -static int xd_ioctl (struct block_device *bdev,fmode_t mode,unsigned int cmd,unsigned long arg); -static int xd_readwrite (u_char operation,XD_INFO *disk,char *buffer,u_int block,u_int count); -static void xd_recalibrate (u_char drive); - -static irqreturn_t xd_interrupt_handler(int irq, void *dev_id); -static u_char xd_setup_dma (u_char opcode,u_char *buffer,u_int count); -static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control); -static void xd_watchdog (unsigned long unused); -static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout); -static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout); - -/* card specific setup and geometry gathering code */ -static void xd_dtc_init_controller (unsigned int address); -static void xd_dtc5150cx_init_drive (u_char drive); -static void xd_dtc_init_drive (u_char drive); -static void xd_wd_init_controller (unsigned int address); -static void xd_wd_init_drive (u_char drive); -static void xd_seagate_init_controller (unsigned int address); -static void xd_seagate_init_drive (u_char drive); -static void xd_omti_init_controller (unsigned int address); -static void xd_omti_init_drive (u_char drive); -static void xd_xebec_init_controller (unsigned int address); -static void xd_xebec_init_drive (u_char drive); -static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc); -static void xd_override_init_drive (u_char drive); - -#endif /* _LINUX_XD_H */ From a9d79fe581f4019209ddc121b114dc28b88cdab0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 6 Sep 2012 07:31:04 -0300 Subject: [PATCH 0521/4607] [media] em28xx: fix querycap Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 42 +++++++++++-------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 4c1726d43374..fb9ee4677130 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1577,6 +1577,7 @@ static int vidioc_streamoff(struct file *file, void *priv, static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { + struct video_device *vdev = video_devdata(file); struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; @@ -1584,20 +1585,28 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = - V4L2_CAP_SLICED_VBI_CAPTURE | - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - - if (dev->vbi_dev) - cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + cap->device_caps = V4L2_CAP_READWRITE | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else if (vdev->vfl_type == VFL_TYPE_RADIO) + cap->device_caps = V4L2_CAP_RADIO; + else + cap->device_caps = V4L2_CAP_READWRITE | + V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE; if (dev->audio_mode.has_audio) - cap->capabilities |= V4L2_CAP_AUDIO; + cap->device_caps |= V4L2_CAP_AUDIO; if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; + cap->device_caps |= V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | + V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + if (dev->vbi_dev) + cap->capabilities |= + V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE; + if (dev->radio_dev) + cap->capabilities |= V4L2_CAP_RADIO; return 0; } @@ -1831,19 +1840,6 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) /* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; - - strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); - strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - cap->capabilities = V4L2_CAP_TUNER; - return 0; -} - static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -2281,7 +2277,7 @@ static const struct v4l2_file_operations radio_fops = { }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, + .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_enum_input = radio_enum_input, .vidioc_g_audio = radio_g_audio, From dd5a4363224614489f6fef25272328ba949a4121 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 6 Sep 2012 09:23:03 -0300 Subject: [PATCH 0522/4607] [media] em28xx: remove bogus input/audio ioctls for the radio device Radio devices should not implement those ioctls. Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 35 ------------------------- 1 file changed, 35 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index fb9ee4677130..f025440bf953 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1856,26 +1856,6 @@ static int radio_g_tuner(struct file *file, void *priv, return 0; } -static int radio_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} - -static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; - - strcpy(a->name, "Radio"); - return 0; -} - static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1889,17 +1869,6 @@ static int radio_s_tuner(struct file *file, void *priv, return 0; } -static int radio_s_audio(struct file *file, void *fh, - const struct v4l2_audio *a) -{ - return 0; -} - -static int radio_s_input(struct file *file, void *fh, unsigned int i) -{ - return 0; -} - static int radio_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *qc) { @@ -2279,11 +2248,7 @@ static const struct v4l2_file_operations radio_fops = { static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, .vidioc_queryctrl = radio_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, From 319a55fbe4c1d3bbe8abe3900e6dc91440ec9b0b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 6 Sep 2012 09:53:08 -0300 Subject: [PATCH 0523/4607] [media] em28xx: fix VIDIOC_DBG_G_CHIP_IDENT compliance errors Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index f025440bf953..b71df42aa7bb 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1403,6 +1403,14 @@ static int vidioc_g_chip_ident(struct file *file, void *priv, chip->ident = V4L2_IDENT_NONE; chip->revision = 0; + if (chip->match.type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(&chip->match)) + chip->ident = V4L2_IDENT_NONE; + return 0; + } + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip); From 20deebfe17b20ded00ba404adbcd014eb2b024c1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 6 Sep 2012 10:07:25 -0300 Subject: [PATCH 0524/4607] [media] em28xx: fix tuner/frequency handling v4l2-compliance found problems with frequency clamping that wasn't reported correctly and missing tuner index checks. Also removed unnecessary tuner type checks (these are now done by the v4l2 core). Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index b71df42aa7bb..89cbfaf17bda 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1322,7 +1322,6 @@ static int vidioc_g_tuner(struct file *file, void *priv, return -EINVAL; strcpy(t->name, "Tuner"); - t->type = V4L2_TUNER_ANALOG_TV; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); return 0; @@ -1352,7 +1351,9 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + if (0 != f->tuner) + return -EINVAL; + f->frequency = dev->ctl_freq; return 0; } @@ -1371,13 +1372,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (0 != f->tuner) return -EINVAL; - if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) - return -EINVAL; - if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) - return -EINVAL; - - dev->ctl_freq = f->frequency; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); + dev->ctl_freq = f->frequency; return 0; } From 8ac7a9493a4380a8a886fbfe311ab00bc424ca0f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 04:46:39 -0300 Subject: [PATCH 0525/4607] [media] v4l2-ctrls: add a notify callback Sometimes platform/bridge drivers need to be notified when a control from a sub-device changes value. In order to support this a notify callback was added. [dheitmueller@kernellabs.com: fix merge conflict in v4l2-ctrls.c] Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-controls.txt | 22 +++++++++++------- drivers/media/v4l2-core/v4l2-ctrls.c | 18 +++++++++++++++ include/media/v4l2-ctrls.h | 25 +++++++++++++++++++++ 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index cfe52c798d74..676f87366025 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -715,14 +715,20 @@ a control of this type whenever the first control belonging to a new control class is added. -Proposals for Extensions -======================== +Adding Notify Callbacks +======================= -Some ideas for future extensions to the spec: +Sometimes the platform or bridge driver needs to be notified when a control +from a sub-device driver changes. You can set a notify callback by calling +this function: -1) Add a V4L2_CTRL_FLAG_HEX to have values shown as hexadecimal instead of -decimal. Useful for e.g. video_mute_yuv. +void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, + void (*notify)(struct v4l2_ctrl *ctrl, void *priv), void *priv); -2) It is possible to mark in the controls array which controls have been -successfully written and which failed by for example adding a bit to the -control ID. Not sure if it is worth the effort, though. +Whenever the give control changes value the notify callback will be called +with a pointer to the control and the priv pointer that was passed with +v4l2_ctrl_notify. Note that the control's handler lock is held when the +notify function is called. + +There can be only one notify function per control handler. Any attempt +to set another notify function will cause a WARN_ON. diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index f6ee201d9347..fa02363e7db4 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1204,6 +1204,8 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, send_event(fh, ctrl, (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) | (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0)); + if (ctrl->call_notify && changed && ctrl->handler->notify) + ctrl->handler->notify(ctrl, ctrl->handler->notify_priv); } } @@ -2725,6 +2727,22 @@ int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) } EXPORT_SYMBOL(v4l2_ctrl_s_ctrl_int64); +void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv) +{ + if (ctrl == NULL) + return; + if (notify == NULL) { + ctrl->call_notify = 0; + return; + } + if (WARN_ON(ctrl->handler->notify && ctrl->handler->notify != notify)) + return; + ctrl->handler->notify = notify; + ctrl->handler->notify_priv = priv; + ctrl->call_notify = 1; +} +EXPORT_SYMBOL(v4l2_ctrl_notify); + static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) { struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 96509119f28f..c4cc04136074 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -53,6 +53,8 @@ struct v4l2_ctrl_ops { int (*s_ctrl)(struct v4l2_ctrl *ctrl); }; +typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); + /** struct v4l2_ctrl - The control structure. * @node: The list node. * @ev_subs: The list of control event subscriptions. @@ -72,6 +74,8 @@ struct v4l2_ctrl_ops { * set this flag directly. * @has_volatiles: If set, then one or more members of the cluster are volatile. * Drivers should never touch this flag. + * @call_notify: If set, then call the handler's notify function whenever the + * control's value changes. * @manual_mode_value: If the is_auto flag is set, then this is the value * of the auto control that determines if that control is in * manual mode. So if the value of the auto control equals this @@ -119,6 +123,7 @@ struct v4l2_ctrl { unsigned int is_private:1; unsigned int is_auto:1; unsigned int has_volatiles:1; + unsigned int call_notify:1; unsigned int manual_mode_value:8; const struct v4l2_ctrl_ops *ops; @@ -177,6 +182,10 @@ struct v4l2_ctrl_ref { * control is needed multiple times, so this is a simple * optimization. * @buckets: Buckets for the hashing. Allows for quick control lookup. + * @notify: A notify callback that is called whenever the control changes value. + * Note that the handler's lock is held when the notify function + * is called! + * @notify_priv: Passed as argument to the v4l2_ctrl notify callback. * @nr_of_buckets: Total number of buckets in the array. * @error: The error code of the first failed control addition. */ @@ -187,6 +196,8 @@ struct v4l2_ctrl_handler { struct list_head ctrl_refs; struct v4l2_ctrl_ref *cached; struct v4l2_ctrl_ref **buckets; + v4l2_ctrl_notify_fnc notify; + void *notify_priv; u16 nr_of_buckets; int error; }; @@ -525,6 +536,20 @@ static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl) mutex_unlock(ctrl->handler->lock); } +/** v4l2_ctrl_notify() - Function to set a notify callback for a control. + * @ctrl: The control. + * @notify: The callback function. + * @priv: The callback private handle, passed as argument to the callback. + * + * This function sets a callback function for the control. If @ctrl is NULL, + * then it will do nothing. If @notify is NULL, then the notify callback will + * be removed. + * + * There can be only one notify. If another already exists, then a WARN_ON + * will be issued and the function will do nothing. + */ +void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv); + /** v4l2_ctrl_g_ctrl() - Helper function to get the control's value from within a driver. * @ctrl: The control. * From 081b945ed74c9bd37da2ee928f9ad281222a6477 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 05:43:59 -0300 Subject: [PATCH 0526/4607] [media] em28xx: convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 24 +++ drivers/media/usb/em28xx/em28xx-video.c | 264 +++--------------------- drivers/media/usb/em28xx/em28xx.h | 6 + 3 files changed, 61 insertions(+), 233 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index e17be073c0c3..1aca98f30a8d 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2941,6 +2941,8 @@ void em28xx_release_resources(struct em28xx *dev) em28xx_i2c_unregister(dev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_device_unregister(&dev->v4l2_dev); usb_put_dev(dev->udev); @@ -2957,6 +2959,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, struct usb_interface *interface, int minor) { + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; int retval; static const char *default_chip_name = "em28xx"; const char *chip_name = default_chip_name; @@ -3084,6 +3087,9 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, return retval; } + v4l2_ctrl_handler_init(hdl, 4); + dev->v4l2_dev.ctrl_handler = hdl; + /* register i2c bus */ retval = em28xx_i2c_register(dev); if (retval < 0) { @@ -3109,6 +3115,18 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, __func__, retval); goto fail; } + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 0x1f, 1, 0x1f); + } else { + /* install the em28xx notify callback */ + v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_MUTE), + em28xx_ctrl_notify, dev); + v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_VOLUME), + em28xx_ctrl_notify, dev); + } /* wake i2c devices */ em28xx_wake_i2c(dev); @@ -3138,6 +3156,11 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, msleep(3); } + v4l2_ctrl_handler_setup(&dev->ctrl_handler); + retval = dev->ctrl_handler.error; + if (retval) + goto fail; + retval = em28xx_register_analog_devices(dev); if (retval < 0) { goto fail; @@ -3150,6 +3173,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, fail: em28xx_i2c_unregister(dev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); unregister_dev: v4l2_device_unregister(&dev->v4l2_dev); diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 89cbfaf17bda..ebbf775fcaba 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -125,30 +125,6 @@ static struct em28xx_fmt format[] = { }, }; -/* supported controls */ -/* Common to all boards */ -static struct v4l2_queryctrl ac97_qctrl[] = { - { - .id = V4L2_CID_AUDIO_VOLUME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Volume", - .minimum = 0x0, - .maximum = 0x1f, - .step = 0x1, - .default_value = 0x1f, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_AUDIO_MUTE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - .flags = 0, - } -}; - /* ------------------------------------------------------------------ DMA and thread functions ------------------------------------------------------------------*/ @@ -718,76 +694,48 @@ static int get_ressource(struct em28xx_fh *fh) } } -/* - * ac97_queryctrl() - * return the ac97 supported controls - */ -static int ac97_queryctrl(struct v4l2_queryctrl *qc) +void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv) { - int i; + struct em28xx *dev = priv; - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (qc->id && qc->id == ac97_qctrl[i].id) { - memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); - return 0; - } - } - - /* Control is not ac97 related */ - return 1; -} - -/* - * ac97_get_ctrl() - * return the current values for ac97 mute and volume - */ -static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) -{ + /* + * In the case of non-AC97 volume controls, we still need + * to do some setups at em28xx, in order to mute/unmute + * and to adjust audio volume. However, the value ranges + * should be checked by the corresponding V4L subdriver. + */ switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->mute; - return 0; - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = dev->volume; - return 0; - default: - /* Control is not ac97 related */ - return 1; - } -} - -/* - * ac97_set_ctrl() - * set values for ac97 mute and volume - */ -static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) - if (ctrl->id == ac97_qctrl[i].id) - goto handle; - - /* Announce that hasn't handle it */ - return 1; - -handle: - if (ctrl->value < ac97_qctrl[i].minimum || - ctrl->value > ac97_qctrl[i].maximum) - return -ERANGE; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - dev->mute = ctrl->value; + dev->mute = ctrl->val; + em28xx_audio_analog_set(dev); break; case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; + dev->volume = ctrl->val; + em28xx_audio_analog_set(dev); + break; + } +} + +static int em28xx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct em28xx *dev = container_of(ctrl->handler, struct em28xx, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + dev->mute = ctrl->val; + break; + case V4L2_CID_AUDIO_VOLUME: + dev->volume = ctrl->val; break; } return em28xx_audio_analog_set(dev); } +const struct v4l2_ctrl_ops em28xx_ctrl_ops = { + .s_ctrl = em28xx_s_ctrl, +}; + static int check_dev(struct em28xx *dev) { if (dev->state & DEV_DISCONNECTED) { @@ -1182,131 +1130,6 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int id = qc->id; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - memset(qc, 0, sizeof(*qc)); - - qc->id = id; - - /* enumerate AC97 controls */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - rc = ac97_queryctrl(qc); - if (!rc) - return 0; - } - - /* enumerate V4L2 device controls */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); - - if (qc->type) - return 0; - else - return -EINVAL; -} - -/* - * FIXME: This is an indirect way to check if a control exists at a - * subdev. Instead of that hack, maybe the better would be to change all - * subdevs to return -ENOIOCTLCMD, if an ioctl is not supported. - */ -static int check_subdev_ctrl(struct em28xx *dev, int id) -{ - struct v4l2_queryctrl qc; - - memset(&qc, 0, sizeof(qc)); - qc.id = id; - - /* enumerate V4L2 device controls */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, &qc); - - if (qc.type) - return 0; - else - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - rc = 0; - - /* Set an AC97 control */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) - rc = ac97_get_ctrl(dev, ctrl); - else - rc = 1; - - /* It were not an AC97 control. Sends it to the v4l2 dev interface */ - if (rc == 1) { - if (check_subdev_ctrl(dev, ctrl->id)) - return -EINVAL; - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - rc = 0; - } - - return rc; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - /* Set an AC97 control */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) - rc = ac97_set_ctrl(dev, ctrl); - else - rc = 1; - - /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ - if (rc == 1) { - rc = check_subdev_ctrl(dev, ctrl->id); - if (!rc) - v4l2_device_call_all(&dev->v4l2_dev, 0, - core, s_ctrl, ctrl); - /* - * In the case of non-AC97 volume controls, we still need - * to do some setups at em28xx, in order to mute/unmute - * and to adjust audio volume. However, the value ranges - * should be checked by the corresponding V4L subdriver. - */ - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - dev->mute = ctrl->value; - rc = em28xx_audio_analog_set(dev); - break; - case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; - rc = em28xx_audio_analog_set(dev); - } - } - return (rc < 0) ? rc : 0; -} - static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1874,25 +1697,6 @@ static int radio_s_tuner(struct file *file, void *priv, return 0; } -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - int i; - - if (qc->id < V4L2_CID_BASE || - qc->id >= V4L2_CID_LASTP1) - return -EINVAL; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (qc->id && qc->id == ac97_qctrl[i].id) { - memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); - return 0; - } - } - - return -EINVAL; -} - /* * em28xx_v4l2_open() * inits the device and starts isoc transfer @@ -2218,9 +2022,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_tuner = vidioc_g_tuner, @@ -2254,9 +2055,6 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_s_tuner = radio_s_tuner, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -2300,7 +2098,7 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, int em28xx_register_analog_devices(struct em28xx *dev) { - u8 val; + u8 val; int ret; unsigned int maxw; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 062841e50722..707319eabe2d 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) @@ -497,6 +498,9 @@ struct em28xx { int audio_ifnum; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + /* provides ac97 mute and volume overrides */ + struct v4l2_ctrl_handler ac97_ctrl_handler; struct em28xx_board board; /* Webcam specific fields */ @@ -705,6 +709,8 @@ void em28xx_close_extension(struct em28xx *dev); /* Provided by em28xx-video.c */ int em28xx_register_analog_devices(struct em28xx *dev); void em28xx_release_analog_resources(struct em28xx *dev); +void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv); +extern const struct v4l2_ctrl_ops em28xx_ctrl_ops; /* Provided by em28xx-cards.c */ extern int em2800_variant_detect(struct usb_device *udev, int model); From 69a61642ac60e84647394b4cf0f322579701d218 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 05:52:40 -0300 Subject: [PATCH 0527/4607] [media] em28xx: convert to v4l2_fh, fix priority handling Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 5 +++++ drivers/media/usb/em28xx/em28xx.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index ebbf775fcaba..c67ff8d96d22 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1735,6 +1735,7 @@ static int em28xx_v4l2_open(struct file *filp) mutex_unlock(&dev->lock); return -ENOMEM; } + v4l2_fh_init(&fh->fh, vdev); fh->dev = dev; fh->radio = radio; fh->type = fh_type; @@ -1774,6 +1775,7 @@ static int em28xx_v4l2_open(struct file *filp) V4L2_FIELD_SEQ_TB, sizeof(struct em28xx_buffer), fh, &dev->lock); mutex_unlock(&dev->lock); + v4l2_fh_add(&fh->fh); return errCode; } @@ -1867,6 +1869,8 @@ static int em28xx_v4l2_close(struct file *filp) "0 (error=%i)\n", errCode); } } + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); videobuf_mmap_free(&fh->vb_vidq); videobuf_mmap_free(&fh->vb_vbiq); @@ -2088,6 +2092,7 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 707319eabe2d..7432be483c22 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) @@ -477,6 +478,7 @@ struct em28xx_audio { struct em28xx; struct em28xx_fh { + struct v4l2_fh fh; struct em28xx *dev; int radio; unsigned int resources; From 50fdf40f696106e0e3c9fa0ee2f6f1457209e81e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 06:10:12 -0300 Subject: [PATCH 0528/4607] [media] em28xx: add support for control events Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 36 +++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index c67ff8d96d22..acdb4340399f 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -40,6 +40,7 @@ #include "em28xx.h" #include #include +#include #include #include #include @@ -1927,24 +1928,33 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, static unsigned int em28xx_poll(struct file *filp, poll_table *wait) { struct em28xx_fh *fh = filp->private_data; + unsigned long req_events = poll_requested_events(wait); struct em28xx *dev = fh->dev; + unsigned int res = 0; int rc; rc = check_dev(dev); if (rc < 0) - return rc; + return DEFAULT_POLLMASK; - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) - return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VBI)) - return POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); - } else { - return POLLERR; + if (v4l2_event_pending(&fh->fh)) + res = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(filp, &fh->fh.wait, wait); + + if (req_events & (POLLIN | POLLRDNORM)) { + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) + return res | POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + return res | POLLERR; + return res | videobuf_poll_stream(filp, &fh->vb_vbiq, wait); + } } + return res; } static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) @@ -2032,6 +2042,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, @@ -2061,6 +2073,8 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_s_tuner = radio_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, From 86ff7f1d4be99080d740fd88495154717cd39e2b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 06:16:03 -0300 Subject: [PATCH 0529/4607] [media] em28xx: fill in readbuffers and fix incorrect return code g/s_parm should fill in readbuffers. For non-webcams s_parm should return -ENOTTY instead of -EINVAL. Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index acdb4340399f..a91a2484bc98 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -972,6 +972,7 @@ static int vidioc_g_parm(struct file *file, void *priv, if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + p->parm.capture.readbuffers = EM28XX_MIN_BUF; if (dev->board.is_webcam) rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, g_parm, p); @@ -989,11 +990,12 @@ static int vidioc_s_parm(struct file *file, void *priv, struct em28xx *dev = fh->dev; if (!dev->board.is_webcam) - return -EINVAL; + return -ENOTTY; if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + p->parm.capture.readbuffers = EM28XX_MIN_BUF; return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); } From 7f529794847bc2ff509967e5ad54fce7d885de93 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 06:34:26 -0300 Subject: [PATCH 0530/4607] [media] tvp5150: remove compat control ops No longer needed now that em28xx has been converted to the control framework. Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 31104a960652..5967e1a0c809 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1096,13 +1096,6 @@ static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { static const struct v4l2_subdev_core_ops tvp5150_core_ops = { .log_status = tvp5150_log_status, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, .s_std = tvp5150_s_std, .reset = tvp5150_reset, .g_chip_ident = tvp5150_g_chip_ident, From d8c95c08ef1127c8777dc3a1177143cf8a5b86ef Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 07:31:54 -0300 Subject: [PATCH 0531/4607] [media] em28xx: std fixes: don't implement in webcam mode, and fix std changes When in webcam mode the STD API shouldn't be implemented. When changing the standard the resolution wasn't updated, and there was no check against streaming-in-progress. Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index a91a2484bc98..7000e22e11a8 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -909,6 +909,8 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) struct em28xx *dev = fh->dev; int rc; + if (dev->board.is_webcam) + return -ENOTTY; rc = check_dev(dev); if (rc < 0) return rc; @@ -924,6 +926,8 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm) struct em28xx *dev = fh->dev; int rc; + if (dev->board.is_webcam) + return -ENOTTY; rc = check_dev(dev); if (rc < 0) return rc; @@ -940,15 +944,24 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) struct v4l2_format f; int rc; + if (dev->board.is_webcam) + return -ENOTTY; + if (*norm == dev->norm) + return 0; rc = check_dev(dev); if (rc < 0) return rc; + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + em28xx_errdev("%s queue busy\n", __func__); + return -EBUSY; + } + dev->norm = *norm; /* Adjusts width/height, if needed */ - f.fmt.pix.width = dev->width; - f.fmt.pix.height = dev->height; + f.fmt.pix.width = 720; + f.fmt.pix.height = (*norm & V4L2_STD_525_60) ? 480 : 576; vidioc_try_fmt_vid_cap(file, priv, &f); /* set new image size */ @@ -1034,6 +1047,9 @@ static int vidioc_enum_input(struct file *file, void *priv, i->type = V4L2_INPUT_TYPE_TUNER; i->std = dev->vdev->tvnorms; + /* webcams do not have the STD API */ + if (dev->board.is_webcam) + i->capabilities = 0; return 0; } @@ -2059,7 +2075,6 @@ static const struct video_device em28xx_video_template = { .ioctl_ops = &video_ioctl_ops, .tvnorms = V4L2_STD_ALL, - .current_norm = V4L2_STD_PAL, }; static const struct v4l2_file_operations radio_fops = { @@ -2109,6 +2124,8 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, vfd->debug = video_debug; vfd->lock = &dev->lock; set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + if (dev->board.is_webcam) + vfd->tvnorms = 0; snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); @@ -2127,7 +2144,7 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->name, EM28XX_VERSION); /* set default norm */ - dev->norm = em28xx_video_template.current_norm; + dev->norm = V4L2_STD_PAL; v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); dev->interlaced = EM28XX_INTERLACED_DEFAULT; From 1d179eeedc8cb48712bc236ec82ec6c63af42008 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 08:45:10 -0300 Subject: [PATCH 0532/4607] [media] em28xx: remove sliced VBI support The sliced VBI support in the tvp5150 is completely broken. And there is no support for the saa7115 sliced VBI implementation in the em28xx driver. So we remove the sliced VBI support completely. It should be possible to get it to work with the tvp5150, but that will require someone to really dig into that driver. Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 49 +------------------------ 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 7000e22e11a8..971046861f7f 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1438,8 +1438,7 @@ static int vidioc_querycap(struct file *file, void *priv, else if (vdev->vfl_type == VFL_TYPE_RADIO) cap->device_caps = V4L2_CAP_RADIO; else - cap->device_caps = V4L2_CAP_READWRITE | - V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE; + cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; if (dev->audio_mode.has_audio) cap->device_caps |= V4L2_CAP_AUDIO; @@ -1450,8 +1449,7 @@ static int vidioc_querycap(struct file *file, void *priv, cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; if (dev->vbi_dev) - cap->capabilities |= - V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE; + cap->capabilities |= V4L2_CAP_VBI_CAPTURE; if (dev->radio_dev) cap->capabilities |= V4L2_CAP_RADIO; return 0; @@ -1508,46 +1506,6 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, return 0; } -/* Sliced VBI ioctls */ -static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - f->fmt.sliced.service_set = 0; - v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - rc = -EINVAL; - - return rc; -} - -static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - return -EINVAL; - - return 0; -} - /* RAW VBI ioctls */ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, @@ -2038,9 +1996,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, - .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, - .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, From 2a221d34b646c7e1f44a1b1d8af4eee18015fbda Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 08:51:32 -0300 Subject: [PATCH 0533/4607] [media] em28xx: zero vbi_format reserved array and add try_vbi_fmt Signed-off-by: Hans Verkuil Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 971046861f7f..e26e01422a2d 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1521,6 +1521,7 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; format->fmt.vbi.count[0] = dev->vbi_height; format->fmt.vbi.count[1] = dev->vbi_height; + memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved)); /* Varies by video standard (NTSC, PAL, etc.) */ if (dev->norm & V4L2_STD_525_60) { @@ -1549,6 +1550,7 @@ static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; format->fmt.vbi.count[0] = dev->vbi_height; format->fmt.vbi.count[1] = dev->vbi_height; + memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved)); /* Varies by video standard (NTSC, PAL, etc.) */ if (dev->norm & V4L2_STD_525_60) { @@ -1991,6 +1993,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_g_audio = vidioc_g_audio, From d3829fadc4611e96aa360b8ead5adefdf61f45ea Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Fri, 4 Jan 2013 16:16:24 -0300 Subject: [PATCH 0534/4607] [media] em28xx: convert to videobuf2 This patch converts the em28xx driver over to videobuf2. It is likely that em28xx_fh can go away entirely, but that will come in a separate patch. [mchehab@redhat.com: fix a non-trivial merge conflict with some VBI patches; CodingStyle fixes] Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/Kconfig | 3 +- drivers/media/usb/em28xx/em28xx-cards.c | 7 +- drivers/media/usb/em28xx/em28xx-dvb.c | 4 +- drivers/media/usb/em28xx/em28xx-vbi.c | 123 ++-- drivers/media/usb/em28xx/em28xx-video.c | 768 +++++++++--------------- drivers/media/usb/em28xx/em28xx.h | 30 +- 6 files changed, 342 insertions(+), 593 deletions(-) diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index 094c4ecf086d..c754a80a8d8b 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -3,7 +3,7 @@ config VIDEO_EM28XX depends on VIDEO_DEV && I2C select VIDEO_TUNER select VIDEO_TVEEPROM - select VIDEOBUF_VMALLOC + select VIDEOBUF2_VMALLOC select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TVP5150 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT @@ -48,7 +48,6 @@ config VIDEO_EM28XX_DVB select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT - select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the Empiatech em28xx chips. diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 1aca98f30a8d..a4d11a46fd4f 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -57,7 +57,7 @@ module_param(disable_usb_speed_check, int, 0444); MODULE_PARM_DESC(disable_usb_speed_check, "override min bandwidth requirement of 480M bps"); -static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); @@ -2965,6 +2965,8 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, const char *chip_name = default_chip_name; dev->udev = udev; + mutex_init(&dev->vb_queue_lock); + mutex_init(&dev->vb_vbi_queue_lock); mutex_init(&dev->ctrl_urb_lock); spin_lock_init(&dev->slock); @@ -3411,6 +3413,9 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); + /* initialize videobuf2 stuff */ + em28xx_vb2_setup(dev); + /* allocate device struct */ mutex_init(&dev->lock); mutex_lock(&dev->lock); diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index a70b19e07e37..01bb8006f659 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -27,7 +27,9 @@ #include "em28xx.h" #include -#include +#include +#include +#include #include #include "tuner-simple.h" #include diff --git a/drivers/media/usb/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c index d74713bd1d18..39f39c527c13 100644 --- a/drivers/media/usb/em28xx/em28xx-vbi.c +++ b/drivers/media/usb/em28xx/em28xx-vbi.c @@ -41,105 +41,72 @@ MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); /* ------------------------------------------------------------------ */ -static void -free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); + struct em28xx *dev = vb2_get_drv_priv(vq); + unsigned long size; - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) + if (fmt) + size = fmt->fmt.pix.sizeimage; + else + size = dev->vbi_width * dev->vbi_height * 2; - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->usb_ctl.vbi_buf == buf) - dev->usb_ctl.vbi_buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); + if (0 == *nbuffers) + *nbuffers = 32; + if (*nbuffers < 2) + *nbuffers = 2; + if (*nbuffers > 32) + *nbuffers = 32; - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} + *nplanes = 1; + sizes[0] = size; -static int -vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) -{ - struct em28xx_fh *fh = q->priv_data; - struct em28xx *dev = fh->dev; - - *size = dev->vbi_width * dev->vbi_height * 2; - - if (0 == *count) - *count = vbibufs; - if (*count < 2) - *count = 2; - if (*count > 32) - *count = 32; return 0; } -static int -vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static int vbi_buffer_prepare(struct vb2_buffer *vb) { - struct em28xx_fh *fh = q->priv_data; - struct em28xx *dev = fh->dev; + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - int rc = 0; + unsigned long size; - buf->vb.size = dev->vbi_width * dev->vbi_height * 2; + size = dev->vbi_width * dev->vbi_height * 2; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + if (vb2_plane_size(vb, 0) < size) { + printk(KERN_INFO "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); return -EINVAL; - - buf->vb.width = dev->vbi_width; - buf->vb.height = dev->vbi_height; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(q, &buf->vb, NULL); - if (rc < 0) - goto fail; } + vb2_set_plane_payload(&buf->vb, 0, size); - buf->vb.state = VIDEOBUF_PREPARED; return 0; - -fail: - free_buffer(q, buf); - return rc; } static void -vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct em28xx_dmaqueue *vbiq = &dev->vbiq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vbiq->active); -} - -static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +vbi_buffer_queue(struct vb2_buffer *vb) { + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - free_buffer(q, buf); + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + unsigned long flags = 0; + + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); + + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &vbiq->active); + spin_unlock_irqrestore(&dev->slock, flags); } -struct videobuf_queue_ops em28xx_vbi_qops = { - .buf_setup = vbi_setup, - .buf_prepare = vbi_prepare, - .buf_queue = vbi_queue, - .buf_release = vbi_release, + +struct vb2_ops em28xx_vbi_qops = { + .queue_setup = vbi_queue_setup, + .buf_prepare = vbi_buffer_prepare, + .buf_queue = vbi_buffer_queue, + .start_streaming = em28xx_start_analog_streaming, + .stop_streaming = em28xx_stop_vbi_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index e26e01422a2d..57510c0d383c 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -76,9 +76,9 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_VERSION(EM28XX_VERSION); -static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; +static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; +static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; module_param_array(video_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); @@ -136,12 +136,13 @@ static struct em28xx_fmt format[] = { static inline void finish_buffer(struct em28xx *dev, struct em28xx_buffer *buf) { - em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field); + + buf->vb.v4l2_buf.sequence = dev->field_count++; + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); } /* @@ -156,8 +157,8 @@ static void em28xx_copy_video(struct em28xx *dev, int linesdone, currlinedone, offset, lencopy, remain; int bytesperline = dev->width << 1; - if (buf->pos + len > buf->vb.size) - len = buf->vb.size - buf->pos; + if (buf->pos + len > buf->length) + len = buf->length - buf->pos; startread = usb_buf; remain = len; @@ -179,11 +180,11 @@ static void em28xx_copy_video(struct em28xx *dev, lencopy = bytesperline - currlinedone; lencopy = lencopy > remain ? remain : lencopy; - if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->vb.size) { + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->length) { em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", ((char *)startwrite + lencopy) - - ((char *)buf->vb_buf + buf->vb.size)); - remain = (char *)buf->vb_buf + buf->vb.size - + ((char *)buf->vb_buf + buf->length)); + remain = (char *)buf->vb_buf + buf->length - (char *)startwrite; lencopy = remain; } @@ -205,13 +206,13 @@ static void em28xx_copy_video(struct em28xx *dev, lencopy = bytesperline; if ((char *)startwrite + lencopy > (char *)buf->vb_buf + - buf->vb.size) { + buf->length) { em28xx_isocdbg("Overflow of %zi bytes past buffer end" "(2)\n", ((char *)startwrite + lencopy) - - ((char *)buf->vb_buf + buf->vb.size)); - lencopy = remain = (char *)buf->vb_buf + buf->vb.size - - (char *)startwrite; + ((char *)buf->vb_buf + buf->length)); + lencopy = remain = (char *)buf->vb_buf + buf->length - + (char *)startwrite; } if (lencopy <= 0) break; @@ -234,8 +235,8 @@ static void em28xx_copy_vbi(struct em28xx *dev, { unsigned int offset; - if (buf->pos + len > buf->vb.size) - len = buf->vb.size - buf->pos; + if (buf->pos + len > buf->length) + len = buf->length - buf->pos; offset = buf->pos; /* Make sure the bottom field populates the second half of the frame */ @@ -292,7 +293,6 @@ static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, struct em28xx_dmaqueue *dma_q) { struct em28xx_buffer *buf; - char *outp; if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); @@ -300,12 +300,11 @@ static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, } /* Get the next buffer */ - buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); + buf = list_entry(dma_q->active.next, struct em28xx_buffer, list); /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&buf->vb); - memset(outp, 0, buf->vb.size); + list_del(&buf->list); buf->pos = 0; - buf->vb_buf = outp; + buf->vb_buf = buf->mem; return buf; } @@ -467,92 +466,118 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) } +static int get_ressource(enum v4l2_buf_type f_type) +{ + switch (f_type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return EM28XX_RESOURCE_VIDEO; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return EM28XX_RESOURCE_VBI; + default: + BUG(); + return 0; + } +} + +/* Usage lock check functions */ +static int res_get(struct em28xx *dev, enum v4l2_buf_type f_type) +{ + int res_type = get_ressource(f_type); + + /* is it free? */ + if (dev->resources & res_type) { + /* no, someone else uses it */ + return -EBUSY; + } + + /* it's free, grab it */ + dev->resources |= res_type; + em28xx_videodbg("res: get %d\n", res_type); + return 0; +} + +static void res_free(struct em28xx *dev, enum v4l2_buf_type f_type) +{ + int res_type = get_ressource(f_type); + + dev->resources &= ~res_type; + em28xx_videodbg("res: put %d\n", res_type); +} + /* ------------------------------------------------------------------ - Videobuf operations + Videobuf2 operations ------------------------------------------------------------------*/ -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct v4l2_frequency f; + struct em28xx *dev = vb2_get_drv_priv(vq); + unsigned long size; - *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) - >> 3; + if (fmt) + size = fmt->fmt.pix.sizeimage; + else + size = (dev->width * dev->height * dev->format->depth + 7) >> 3; - if (0 == *count) - *count = EM28XX_DEF_BUF; + if (size == 0) + return -EINVAL; - if (*count < EM28XX_MIN_BUF) - *count = EM28XX_MIN_BUF; + if (0 == *nbuffers) + *nbuffers = 32; - /* Ask tuner to go to analog or radio mode */ - memset(&f, 0, sizeof(f)); - f.frequency = dev->ctl_freq; - f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + *nplanes = 1; + sizes[0] = size; return 0; } -/* This is called *without* dev->slock held; please keep it that way */ -static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +static int +buffer_prepare(struct vb2_buffer *vb) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + unsigned long size; - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) + em28xx_videodbg("%s, field=%d\n", __func__, vb->v4l2_buf.field); - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->usb_ctl.vid_buf == buf) - dev->usb_ctl.vid_buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); + size = (dev->width * dev->height * dev->format->depth + 7) >> 3; - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + if (vb2_plane_size(vb, 0) < size) { + em28xx_videodbg("%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(&buf->vb, 0, size); + + return 0; } -static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) +int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - struct em28xx *dev = fh->dev; - int rc = 0, urb_init = 0; + struct em28xx *dev = vb2_get_drv_priv(vq); + struct v4l2_frequency f; + int rc = 0; - buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth - + 7) >> 3; + em28xx_videodbg("%s\n", __func__); - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; + /* Make sure streaming is not already in progress for this type + of filehandle (e.g. video, vbi) */ + rc = res_get(dev, vq->type); + if (rc) + return rc; - buf->vb.width = dev->width; - buf->vb.height = dev->height; - buf->vb.field = field; + if (dev->streaming_users++ == 0) { + /* First active streaming user, so allocate all the URBs */ - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } + /* Allocate the USB bandwidth */ + em28xx_set_alternate(dev); - if (!dev->usb_ctl.analog_bufs.num_bufs) - urb_init = 1; + /* Needed, since GPIO might have disabled power of + some i2c device + */ + em28xx_wake_i2c(dev); - if (urb_init) { dev->capture_type = -1; rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, dev->analog_xfer_bulk, @@ -562,52 +587,144 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, em28xx_urb_data_copy); if (rc < 0) goto fail; + + /* + * djh: it's not clear whether this code is still needed. I'm + * leaving it in here for now entirely out of concern for + * backward compatibility (the old code did it) + */ + + /* Ask tuner to go to analog or radio mode */ + memset(&f, 0, sizeof(f)); + f.frequency = dev->ctl_freq; + if (vq->owner && vq->owner->vdev->vfl_type == VFL_TYPE_RADIO) + f.type = V4L2_TUNER_RADIO; + else + f.type = V4L2_TUNER_ANALOG_TV; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); } - buf->vb.state = VIDEOBUF_PREPARED; - return 0; - fail: - free_buffer(vq, buf); return rc; } +int em28xx_stop_streaming(struct vb2_queue *vq) +{ + struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_dmaqueue *vidq = &dev->vidq; + unsigned long flags = 0; + + em28xx_videodbg("%s\n", __func__); + + res_free(dev, vq->type); + + if (dev->streaming_users-- == 1) { + /* Last active user, so shutdown all the URBS */ + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + } + + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&vidq->active)) { + struct em28xx_buffer *buf; + buf = list_entry(vidq->active.next, struct em28xx_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + dev->usb_ctl.vid_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + return 0; +} + +int em28xx_stop_vbi_streaming(struct vb2_queue *vq) +{ + struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + unsigned long flags = 0; + + em28xx_videodbg("%s\n", __func__); + + res_free(dev, vq->type); + + if (dev->streaming_users-- == 1) { + /* Last active user, so shutdown all the URBS */ + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + } + + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&vbiq->active)) { + struct em28xx_buffer *buf; + buf = list_entry(vbiq->active.next, struct em28xx_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + dev->usb_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + return 0; +} + static void -buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +buffer_queue(struct vb2_buffer *vb) { - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct em28xx_dmaqueue *vidq = &dev->vidq; + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx_dmaqueue *vidq = &dev->vidq; + unsigned long flags = 0; - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); + em28xx_videodbg("%s\n", __func__); + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&dev->slock, flags); } -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = (struct em28xx *)fh->dev; - - em28xx_isocdbg("em28xx: called buffer_release\n"); - - free_buffer(vq, buf); -} - -static struct videobuf_queue_ops em28xx_video_qops = { - .buf_setup = buffer_setup, +static struct vb2_ops em28xx_video_qops = { + .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .start_streaming = em28xx_start_analog_streaming, + .stop_streaming = em28xx_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; +int em28xx_vb2_setup(struct em28xx *dev) +{ + int rc; + struct vb2_queue *q; + + /* Setup Videobuf2 for Video capture */ + q = &dev->vb_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct em28xx_buffer); + q->ops = &em28xx_video_qops; + q->mem_ops = &vb2_vmalloc_memops; + + rc = vb2_queue_init(q); + if (rc < 0) + return rc; + + /* Setup Videobuf2 for VBI capture */ + q = &dev->vb_vbiq; + q->type = V4L2_BUF_TYPE_VBI_CAPTURE; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct em28xx_buffer); + q->ops = &em28xx_vbi_qops; + q->mem_ops = &vb2_vmalloc_memops; + + rc = vb2_queue_init(q); + if (rc < 0) + return rc; + + return 0; +} + /********************* v4l2 interface **************************************/ static void video_mux(struct em28xx *dev, int index) @@ -640,61 +757,6 @@ static void video_mux(struct em28xx *dev, int index) em28xx_audio_analog_set(dev); } -/* Usage lock check functions */ -static int res_get(struct em28xx_fh *fh, unsigned int bit) -{ - struct em28xx *dev = fh->dev; - - if (fh->resources & bit) - /* have it already allocated */ - return 1; - - /* is it free? */ - if (dev->resources & bit) { - /* no, someone else uses it */ - return 0; - } - /* it's free, grab it */ - fh->resources |= bit; - dev->resources |= bit; - em28xx_videodbg("res: get %d\n", bit); - return 1; -} - -static int res_check(struct em28xx_fh *fh, unsigned int bit) -{ - return fh->resources & bit; -} - -static int res_locked(struct em28xx *dev, unsigned int bit) -{ - return dev->resources & bit; -} - -static void res_free(struct em28xx_fh *fh, unsigned int bits) -{ - struct em28xx *dev = fh->dev; - - BUG_ON((fh->resources & bits) != bits); - - fh->resources &= ~bits; - dev->resources &= ~bits; - em28xx_videodbg("res: put %d\n", bits); -} - -static int get_ressource(struct em28xx_fh *fh) -{ - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return EM28XX_RESOURCE_VIDEO; - case V4L2_BUF_TYPE_VBI_CAPTURE: - return EM28XX_RESOURCE_VBI; - default: - BUG(); - return 0; - } -} - void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv) { struct em28xx *dev = priv; @@ -828,8 +890,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* the em2800 can only scale down to 50% */ height = height > (3 * maxh / 4) ? maxh : maxh / 2; width = width > (3 * maxw / 4) ? maxw : maxw / 2; - /* MaxPacketSize for em2800 is too small to capture at full resolution - * use half of maxw as the scaler can only scale to 50% */ + /* + * MaxPacketSize for em2800 is too small to capture at full + * resolution use half of maxw as the scaler can only scale + * to 50% + */ if (width == maxw && height == maxh) width /= 2; } else { @@ -875,7 +940,6 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, /* set new image size */ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); - em28xx_set_alternate(dev); em28xx_resolution_set(dev); return 0; @@ -884,21 +948,13 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; + struct em28xx *dev = video_drvdata(file); - rc = check_dev(dev); - if (rc < 0) - return rc; + if (dev->streaming_users > 0) + return -EBUSY; vidioc_try_fmt_vid_cap(file, priv, f); - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - em28xx_errdev("%s queue busy\n", __func__); - return -EBUSY; - } - return em28xx_set_video_format(dev, f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height); } @@ -952,10 +1008,8 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) if (rc < 0) return rc; - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - em28xx_errdev("%s queue busy\n", __func__); + if (dev->streaming_users > 0) return -EBUSY; - } dev->norm = *norm; @@ -1358,69 +1412,6 @@ static int vidioc_cropcap(struct file *file, void *priv, return 0; } -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc = -EINVAL; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (unlikely(type != fh->type)) - return -EINVAL; - - em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (unlikely(!res_get(fh, get_ressource(fh)))) - return -EBUSY; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_streamon(&fh->vb_vidq); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_streamon(&fh->vb_vbiq); - - return rc; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - if (type != fh->type) - return -EINVAL; - - em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { - videobuf_streamoff(&fh->vb_vidq); - res_free(fh, EM28XX_RESOURCE_VIDEO); - } - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (res_check(fh, EM28XX_RESOURCE_VBI)) { - videobuf_streamoff(&fh->vb_vbiq); - res_free(fh, EM28XX_RESOURCE_VBI); - } - } - - return 0; -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1566,83 +1557,6 @@ static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, return 0; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_reqbufs(&fh->vb_vidq, rb); - else - return videobuf_reqbufs(&fh->vb_vbiq, rb); -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_querybuf(&fh->vb_vidq, b); - else { - /* FIXME: I'm not sure yet whether this is a bug in zvbi or - the videobuf framework, but we probably shouldn't be - returning a buffer larger than that which was asked for. - At a minimum, it causes a crash in zvbi since it does - a memcpy based on the source buffer length */ - int result = videobuf_querybuf(&fh->vb_vbiq, b); - b->length = dev->vbi_width * dev->vbi_height * 2; - - return result; - } -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_qbuf(&fh->vb_vidq, b); - else - return videobuf_qbuf(&fh->vb_vbiq, b); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & - O_NONBLOCK); - else - return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & - O_NONBLOCK); -} - /* ----------------------------------------------------------- */ /* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ @@ -1682,12 +1596,10 @@ static int radio_s_tuner(struct file *file, void *priv, */ static int em28xx_v4l2_open(struct file *filp) { - int errCode = 0, radio = 0; struct video_device *vdev = video_devdata(filp); struct em28xx *dev = video_drvdata(filp); enum v4l2_buf_type fh_type = 0; struct em28xx_fh *fh; - enum v4l2_field field; switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: @@ -1696,9 +1608,6 @@ static int em28xx_v4l2_open(struct file *filp) case VFL_TYPE_VBI: fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; break; - case VFL_TYPE_RADIO: - radio = 1; - break; } em28xx_videodbg("open dev=%s type=%s users=%d\n", @@ -1716,13 +1625,11 @@ static int em28xx_v4l2_open(struct file *filp) } v4l2_fh_init(&fh->fh, vdev); fh->dev = dev; - fh->radio = radio; fh->type = fh_type; filp->private_data = fh; if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { em28xx_set_mode(dev, EM28XX_ANALOG_MODE); - em28xx_set_alternate(dev); em28xx_resolution_set(dev); /* Needed, since GPIO might have disabled power of @@ -1731,32 +1638,18 @@ static int em28xx_v4l2_open(struct file *filp) em28xx_wake_i2c(dev); } - if (fh->radio) { + + if (vdev->vfl_type == VFL_TYPE_RADIO) { em28xx_videodbg("video_open: setting radio device\n"); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); } dev->users++; - if (dev->progressive) - field = V4L2_FIELD_NONE; - else - field = V4L2_FIELD_INTERLACED; - - videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, field, - sizeof(struct em28xx_buffer), fh, &dev->lock); - - videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, - sizeof(struct em28xx_buffer), fh, &dev->lock); mutex_unlock(&dev->lock); v4l2_fh_add(&fh->fh); - return errCode; + return 0; } /* @@ -1810,15 +1703,7 @@ static int em28xx_v4l2_close(struct file *filp) em28xx_videodbg("users=%d\n", dev->users); mutex_lock(&dev->lock); - if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { - videobuf_stop(&fh->vb_vidq); - res_free(fh, EM28XX_RESOURCE_VIDEO); - } - - if (res_check(fh, EM28XX_RESOURCE_VBI)) { - videobuf_stop(&fh->vb_vbiq); - res_free(fh, EM28XX_RESOURCE_VBI); - } + vb2_fop_release(filp); if (dev->users == 1) { /* the device is already disconnect, @@ -1828,7 +1713,6 @@ static int em28xx_v4l2_close(struct file *filp) kfree(dev->alt_max_pkt_size_isoc); mutex_unlock(&dev->lock); kfree(dev); - kfree(fh); return 0; } @@ -1836,7 +1720,6 @@ static int em28xx_v4l2_close(struct file *filp) v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ - em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); em28xx_set_mode(dev, EM28XX_SUSPEND); /* set alternate 0 */ @@ -1848,141 +1731,19 @@ static int em28xx_v4l2_close(struct file *filp) "0 (error=%i)\n", errCode); } } - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); - videobuf_mmap_free(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vbiq); - kfree(fh); dev->users--; mutex_unlock(&dev->lock); return 0; } -/* - * em28xx_v4l2_read() - * will allocate buffers when called for the first time - */ -static ssize_t -em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, - loff_t *pos) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - /* FIXME: read() is not prepared to allow changing the video - resolution while streaming. Seems a bug at em28xx_set_fmt - */ - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_locked(dev, EM28XX_RESOURCE_VIDEO)) - rc = -EBUSY; - else - rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VBI)) - rc = -EBUSY; - else - rc = videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } - mutex_unlock(&dev->lock); - - return rc; -} - -/* - * em28xx_poll() - * will allocate buffers when called for the first time - */ -static unsigned int em28xx_poll(struct file *filp, poll_table *wait) -{ - struct em28xx_fh *fh = filp->private_data; - unsigned long req_events = poll_requested_events(wait); - struct em28xx *dev = fh->dev; - unsigned int res = 0; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return DEFAULT_POLLMASK; - - if (v4l2_event_pending(&fh->fh)) - res = POLLPRI; - else if (req_events & POLLPRI) - poll_wait(filp, &fh->fh.wait, wait); - - if (req_events & (POLLIN | POLLRDNORM)) { - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) - return res | POLLERR; - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); - } - if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VBI)) - return res | POLLERR; - return res | videobuf_poll_stream(filp, &fh->vb_vbiq, wait); - } - } - return res; -} - -static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - unsigned int res; - - mutex_lock(&dev->lock); - res = em28xx_poll(filp, wait); - mutex_unlock(&dev->lock); - return res; -} - -/* - * em28xx_v4l2_mmap() - */ -static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); - mutex_unlock(&dev->lock); - - em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, - rc); - - return rc; -} - static const struct v4l2_file_operations em28xx_v4l_fops = { .owner = THIS_MODULE, .open = em28xx_v4l2_open, .release = em28xx_v4l2_close, - .read = em28xx_v4l2_read, - .poll = em28xx_v4l2_poll, - .mmap = em28xx_v4l2_mmap, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, .unlocked_ioctl = video_ioctl2, }; @@ -2000,10 +1761,13 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_g_std = vidioc_g_std, .vidioc_querystd = vidioc_querystd, .vidioc_s_std = vidioc_s_std, @@ -2012,8 +1776,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, @@ -2029,7 +1793,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { static const struct video_device em28xx_video_template = { .fops = &em28xx_v4l_fops, - .release = video_device_release, + .release = video_device_release_empty, .ioctl_ops = &video_ioctl_ops, .tvnorms = V4L2_STD_ALL, @@ -2078,7 +1842,6 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, *vfd = *template; vfd->v4l2_dev = &dev->v4l2_dev; - vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); @@ -2110,10 +1873,10 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->format = &format[0]; maxw = norm_maxw(dev); - /* MaxPacketSize for em2800 is too small to capture at full resolution - * use half of maxw as the scaler can only scale to 50% */ - if (dev->board.is_em2800) - maxw /= 2; + /* MaxPacketSize for em2800 is too small to capture at full resolution + * use half of maxw as the scaler can only scale to 50% */ + if (dev->board.is_em2800) + maxw /= 2; em28xx_set_video_format(dev, format[0].fourcc, maxw, norm_maxh(dev)); @@ -2139,6 +1902,8 @@ int em28xx_register_analog_devices(struct em28xx *dev) em28xx_errdev("cannot allocate video_device.\n"); return -ENODEV; } + dev->vdev->queue = &dev->vb_vidq; + dev->vdev->queue->lock = &dev->vb_queue_lock; /* register v4l2 video video_device */ ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, @@ -2154,6 +1919,9 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); + dev->vbi_dev->queue = &dev->vb_vbiq; + dev->vbi_dev->queue->lock = &dev->vb_vbi_queue_lock; + /* register v4l2 vbi video_device */ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 7432be483c22..bf0a790f7eb9 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -31,15 +31,12 @@ #include #include -#include +#include #include #include #include #include #include -#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) -#include -#endif #include "tuner-xc2028.h" #include "xc5000.h" #include "em28xx-reg.h" @@ -252,8 +249,11 @@ struct em28xx_fmt { /* buffer for one video frame */ struct em28xx_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_buffer vb; + struct list_head list; + void *mem; + unsigned int length; int top_field; /* counter to control buffer fill */ @@ -480,11 +480,6 @@ struct em28xx; struct em28xx_fh { struct v4l2_fh fh; struct em28xx *dev; - int radio; - unsigned int resources; - - struct videobuf_queue vb_vidq; - struct videobuf_queue vb_vbiq; enum v4l2_buf_type type; }; @@ -545,6 +540,7 @@ struct em28xx { struct i2c_client i2c_client; /* video for linux */ int users; /* user count for exclusive use */ + int streaming_users; /* Number of actively streaming users */ struct video_device *vdev; /* video for linux device struct */ v4l2_std_id norm; /* selected tv norm */ int ctl_freq; /* selected frequency */ @@ -587,6 +583,12 @@ struct em28xx { struct video_device *vbi_dev; struct video_device *radio_dev; + /* Videobuf2 */ + struct vb2_queue vb_vidq; + struct vb2_queue vb_vbiq; + struct mutex vb_queue_lock; + struct mutex vb_vbi_queue_lock; + /* resources in use */ unsigned int resources; @@ -598,6 +600,9 @@ struct em28xx { struct em28xx_usb_ctl usb_ctl; spinlock_t slock; + unsigned int field_count; + unsigned int vbi_field_count; + /* usb transfer */ struct usb_device *udev; /* the usb device */ u8 analog_ep_isoc; /* address of isoc endpoint for analog */ @@ -709,9 +714,12 @@ void em28xx_init_extension(struct em28xx *dev); void em28xx_close_extension(struct em28xx *dev); /* Provided by em28xx-video.c */ +int em28xx_vb2_setup(struct em28xx *dev); int em28xx_register_analog_devices(struct em28xx *dev); void em28xx_release_analog_resources(struct em28xx *dev); void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv); +int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count); +int em28xx_stop_vbi_streaming(struct vb2_queue *vq); extern const struct v4l2_ctrl_ops em28xx_ctrl_ops; /* Provided by em28xx-cards.c */ @@ -723,7 +731,7 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg); void em28xx_release_resources(struct em28xx *dev); /* Provided by em28xx-vbi.c */ -extern struct videobuf_queue_ops em28xx_vbi_qops; +extern struct vb2_ops em28xx_vbi_qops; /* printk macros */ From 2665c2995d6a6026cfc9ec118908dfccb74fb5e0 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 27 Dec 2012 19:02:43 -0300 Subject: [PATCH 0535/4607] [media] em28xx: simplify device state tracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEV_INITIALIZED of enum em28xx_dev_state state is used nowhere and there is no need for DEV_MISCONFIGURED, so remove this enum and use a boolean field 'disconnected' in the device struct instead. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 5 ++--- drivers/media/usb/em28xx/em28xx-core.c | 4 ++-- drivers/media/usb/em28xx/em28xx-dvb.c | 4 ++-- drivers/media/usb/em28xx/em28xx-video.c | 12 +++--------- drivers/media/usb/em28xx/em28xx.h | 12 ++---------- 5 files changed, 11 insertions(+), 26 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index a4d11a46fd4f..99f2da661c32 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3530,11 +3530,10 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) "deallocation are deferred on close.\n", video_device_node_name(dev->vdev)); - dev->state |= DEV_MISCONFIGURED; em28xx_uninit_usb_xfer(dev, dev->mode); - dev->state |= DEV_DISCONNECTED; + dev->disconnected = 1; } else { - dev->state |= DEV_DISCONNECTED; + dev->disconnected = 1; em28xx_release_resources(dev); } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index b10d959fefe5..6916e87bc624 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -77,7 +77,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, int ret; int pipe = usb_rcvctrlpipe(dev->udev, 0); - if (dev->state & DEV_DISCONNECTED) + if (dev->disconnected) return -ENODEV; if (len > URB_MAX_CTRL_SIZE) @@ -153,7 +153,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, int ret; int pipe = usb_sndctrlpipe(dev->udev, 0); - if (dev->state & DEV_DISCONNECTED) + if (dev->disconnected) return -ENODEV; if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 01bb8006f659..a81ec2e8cc9b 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -135,7 +135,7 @@ static inline int em28xx_dvb_urb_data_copy(struct em28xx *dev, struct urb *urb) if (!dev) return 0; - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + if (dev->disconnected) return 0; if (urb->status < 0) @@ -1322,7 +1322,7 @@ static int em28xx_dvb_fini(struct em28xx *dev) if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; - if (dev->state & DEV_DISCONNECTED) { + if (dev->disconnected) { /* We cannot tell the device to sleep * once it has been unplugged. */ if (dvb->fe[0]) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 57510c0d383c..40b96d17ccf8 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -418,7 +418,7 @@ static inline int em28xx_urb_data_copy(struct em28xx *dev, struct urb *urb) if (!dev) return 0; - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + if (dev->disconnected) return 0; if (urb->status < 0) @@ -801,16 +801,10 @@ const struct v4l2_ctrl_ops em28xx_ctrl_ops = { static int check_dev(struct em28xx *dev) { - if (dev->state & DEV_DISCONNECTED) { + if (dev->disconnected) { em28xx_errdev("v4l2 ioctl: device not present\n"); return -ENODEV; } - - if (dev->state & DEV_MISCONFIGURED) { - em28xx_errdev("v4l2 ioctl: device is misconfigured; " - "close and open it again\n"); - return -EIO; - } return 0; } @@ -1708,7 +1702,7 @@ static int em28xx_v4l2_close(struct file *filp) if (dev->users == 1) { /* the device is already disconnect, free the remaining resources */ - if (dev->state & DEV_DISCONNECTED) { + if (dev->disconnected) { em28xx_release_resources(dev); kfree(dev->alt_max_pkt_size_isoc); mutex_unlock(&dev->lock); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index bf0a790f7eb9..f891a28706f5 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -439,13 +439,6 @@ struct em28xx_eeprom { u8 string_idx_table; }; -/* device states */ -enum em28xx_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - #define EM28XX_AUDIO_BUFS 5 #define EM28XX_NUM_AUDIO_PACKETS 64 #define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ @@ -492,6 +485,8 @@ struct em28xx { int devno; /* marks the number of this device */ enum em28xx_chip_id chip_id; + unsigned char disconnected:1; /* device has been diconnected */ + int audio_ifnum; struct v4l2_device v4l2_dev; @@ -563,9 +558,6 @@ struct em28xx { struct em28xx_audio adev; - /* states */ - enum em28xx_dev_state state; - /* capture state tracking */ int capture_type; unsigned char top_field:1; From 05fe2175cf87da8a5475aed422bd636475ab0412 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 27 Dec 2012 19:02:44 -0300 Subject: [PATCH 0536/4607] [media] em28xx: refactor the code in em28xx_usb_disconnect() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main purpose of this patch is to move the call of em28xx_release_resources() after the call of em28xx_close_extension(). This is necessary, because some resources might be needed/used by the extensions fini() functions when they get closed. Also mark the device as disconnected earlier in this function and unify the em28xx_uninit_usb_xfer() calls for analog and digital mode. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 26 +++++++++++-------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 99f2da661c32..4d849bfa2db3 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3507,6 +3507,8 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) if (!dev) return; + dev->disconnected = 1; + if (dev->is_audio_only) { mutex_lock(&dev->lock); em28xx_close_extension(dev); @@ -3518,32 +3520,26 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) flush_request_modules(dev); - /* wait until all current v4l2 io is finished then deallocate - resources */ mutex_lock(&dev->lock); v4l2_device_disconnect(&dev->v4l2_dev); if (dev->users) { - em28xx_warn - ("device %s is open! Deregistration and memory " - "deallocation are deferred on close.\n", - video_device_node_name(dev->vdev)); + em28xx_warn("device %s is open! Deregistration and memory deallocation are deferred on close.\n", + video_device_node_name(dev->vdev)); - em28xx_uninit_usb_xfer(dev, dev->mode); - dev->disconnected = 1; - } else { - dev->disconnected = 1; - em28xx_release_resources(dev); + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); } - /* free DVB isoc buffers */ - em28xx_uninit_usb_xfer(dev, EM28XX_DIGITAL_MODE); + em28xx_close_extension(dev); + /* NOTE: must be called BEFORE the resources are released */ + + if (!dev->users) + em28xx_release_resources(dev); mutex_unlock(&dev->lock); - em28xx_close_extension(dev); - if (!dev->users) { kfree(dev->alt_max_pkt_size_isoc); kfree(dev); From 6ea887efadec30ec830ed9466073715b7d339d2b Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 27 Dec 2012 19:02:47 -0300 Subject: [PATCH 0537/4607] [media] em28xx: IR RC: move assignment of get_key functions from *_change_protocol() functions to em28xx_ir_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The get_key functions are independent from the selected protocol, so assign them once only at device initialization. [mchehab@redhat.com: fix a merge conflict] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 5b3292c8310b..07f6030d7455 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -384,7 +384,6 @@ static int em2860_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) *rc_type = ir->rc_type; return -EINVAL; } - ir->get_key = default_polling_getkey; em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); @@ -420,10 +419,7 @@ static int em2874_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) *rc_type = ir->rc_type; return -EINVAL; } - - ir->get_key = em2874_polling_getkey; em28xx_write_regs(dev, EM2874_R50_IR_CONFIG, &ir_config, 1); - em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); @@ -633,10 +629,12 @@ static int em28xx_ir_init(struct em28xx *dev) case CHIP_ID_EM2860: case CHIP_ID_EM2883: rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; + ir->get_key = default_polling_getkey; break; case CHIP_ID_EM2884: case CHIP_ID_EM2874: case CHIP_ID_EM28174: + ir->get_key = em2874_polling_getkey; rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | RC_BIT_RC6_0; break; default: From f5ae371aca34bd0660a75f8838198466e9d5166c Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 3 Jan 2013 14:27:02 -0300 Subject: [PATCH 0538/4607] [media] em28xx: respect the message size constraints for i2c transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The em2800 can transfer up to 4 bytes per i2c message. All other em25xx/em27xx/28xx chips can transfer at least 64 bytes per message. I2C adapters should never split messages transferred via the I2C subsystem into multiple message transfers, because the result will almost always NOT be the same as when the whole data is transferred to the I2C client in a single message. If the message size exceeds the capabilities of the I2C adapter, -EOPNOTSUPP should be returned. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 44 +++++++++++---------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 44533e4574ff..c508c1297a26 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -50,14 +50,18 @@ do { \ } while (0) /* - * em2800_i2c_send_max4() - * send up to 4 bytes to the i2c device + * em2800_i2c_send_bytes() + * send up to 4 bytes to the em2800 i2c device */ -static int em2800_i2c_send_max4(struct em28xx *dev, u8 addr, u8 *buf, u16 len) +static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { int ret; int write_timeout; u8 b2[6]; + + if (len < 1 || len > 4) + return -EOPNOTSUPP; + BUG_ON(len < 1 || len > 4); b2[5] = 0x80 + len - 1; b2[4] = addr; @@ -85,29 +89,6 @@ static int em2800_i2c_send_max4(struct em28xx *dev, u8 addr, u8 *buf, u16 len) return -EIO; } -/* - * em2800_i2c_send_bytes() - */ -static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) -{ - u8 *bufPtr = buf; - int ret; - int wrcount = 0; - int count; - int maxLen = 4; - while (len > 0) { - count = (len > maxLen) ? maxLen : len; - ret = em2800_i2c_send_max4(dev, addr, bufPtr, count); - if (ret > 0) { - len -= count; - bufPtr += count; - wrcount += count; - } else - return (ret < 0) ? ret : -EFAULT; - } - return wrcount; -} - /* * em2800_i2c_check_for_device() * check if there is a i2c_device at the supplied address @@ -150,6 +131,10 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { int ret; + + if (len < 1 || len > 4) + return -EOPNOTSUPP; + /* check for the device and set i2c read address */ ret = em2800_i2c_check_for_device(dev, addr); if (ret) { @@ -176,6 +161,9 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, int wrcount = 0; int write_timeout, ret; + if (len < 1 || len > 64) + return -EOPNOTSUPP; + wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); /* Seems to be required after a write */ @@ -197,6 +185,10 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) { int ret; + + if (len < 1 || len > 64) + return -EOPNOTSUPP; + ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len); if (ret < 0) { em28xx_warn("reading i2c device failed (error=%i)\n", ret); From 2fcc82d8831a74afd55c3cb898beb9fde5f2a1fd Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 3 Jan 2013 14:27:03 -0300 Subject: [PATCH 0539/4607] [media] em28xx: fix two severe bugs in function em2800_i2c_recv_bytes() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function em2800_i2c_recv_bytes() has 2 severe bugs: 1) It does not wait for the i2c read to complete before reading the received message content from the bridge registers. 2) Reading more than 1 byte doesn't work The former can result in data corruption, the latter always does. The rewritten code also superseds the content of function em2800_i2c_check_for_device(). Tested with device "Terratec Cinergy 200 USB". [mchehab@redhat.com: Fix CodingStyle issues] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 110 ++++++++++++++------------ drivers/media/usb/em28xx/em28xx.h | 2 +- 2 files changed, 61 insertions(+), 51 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index c508c1297a26..67a8e623dd8c 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -73,12 +73,14 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) if (len > 3) b2[0] = buf[3]; + /* trigger write */ ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len); if (ret != 2 + len) { em28xx_warn("writing to i2c device failed (error=%i)\n", ret); return -EIO; } - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; + /* wait for completion */ + for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; write_timeout -= 5) { ret = dev->em28xx_read_reg(dev, 0x05); if (ret == 0x80 + len - 1) @@ -89,69 +91,77 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) return -EIO; } -/* - * em2800_i2c_check_for_device() - * check if there is a i2c_device at the supplied address - */ -static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) -{ - u8 msg; - int ret; - int write_timeout; - msg = addr; - ret = dev->em28xx_write_regs(dev, 0x04, &msg, 1); - if (ret < 0) { - em28xx_warn("setting i2c device address failed (error=%i)\n", - ret); - return ret; - } - msg = 0x84; - ret = dev->em28xx_write_regs(dev, 0x05, &msg, 1); - if (ret < 0) { - em28xx_warn("preparing i2c read failed (error=%i)\n", ret); - return ret; - } - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; - write_timeout -= 5) { - unsigned reg = dev->em28xx_read_reg(dev, 0x5); - - if (reg == 0x94) - return -ENODEV; - else if (reg == 0x84) - return 0; - msleep(5); - } - return -ENODEV; -} - /* * em2800_i2c_recv_bytes() - * read from the i2c device + * read up to 4 bytes from the em2800 i2c device */ static int em2800_i2c_recv_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) { + u8 buf2[4]; int ret; + int read_timeout; + int i; if (len < 1 || len > 4) return -EOPNOTSUPP; - /* check for the device and set i2c read address */ - ret = em2800_i2c_check_for_device(dev, addr); - if (ret) { - em28xx_warn - ("preparing read at i2c address 0x%x failed (error=%i)\n", - addr, ret); - return ret; + /* trigger read */ + buf2[1] = 0x84 + len - 1; + buf2[0] = addr; + ret = dev->em28xx_write_regs(dev, 0x04, buf2, 2); + if (ret != 2) { + em28xx_warn("failed to trigger read from i2c address 0x%x " + "(error=%i)\n", addr, ret); + return (ret < 0) ? ret : -EIO; } - ret = dev->em28xx_read_reg_req_len(dev, 0x0, 0x3, buf, len); - if (ret < 0) { - em28xx_warn("reading from i2c device at 0x%x failed (error=%i)", - addr, ret); - return ret; + + /* wait for completion */ + for (read_timeout = EM2800_I2C_XFER_TIMEOUT; read_timeout > 0; + read_timeout -= 5) { + ret = dev->em28xx_read_reg(dev, 0x05); + if (ret == 0x84 + len - 1) { + break; + } else if (ret == 0x94 + len - 1) { + return -ENODEV; + } else if (ret < 0) { + em28xx_warn("failed to get i2c transfer status from " + "bridge register (error=%i)\n", ret); + return ret; + } + msleep(5); } + if (ret != 0x84 + len - 1) + em28xx_warn("read from i2c device at 0x%x timed out\n", addr); + + /* get the received message */ + ret = dev->em28xx_read_reg_req_len(dev, 0x00, 4-len, buf2, len); + if (ret != len) { + em28xx_warn("reading from i2c device at 0x%x failed: " + "couldn't get the received message from the bridge " + "(error=%i)\n", addr, ret); + return (ret < 0) ? ret : -EIO; + } + for (i = 0; i < len; i++) + buf[i] = buf2[len - 1 - i]; + return ret; } +/* + * em2800_i2c_check_for_device() + * check if there is an i2c device at the supplied address + */ +static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) +{ + u8 buf; + int ret; + + ret = em2800_i2c_recv_bytes(dev, addr, &buf, 1); + if (ret == 1) + return 0; + return (ret < 0) ? ret : -EIO; +} + /* * em28xx_i2c_send_bytes() */ @@ -167,7 +177,7 @@ static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); /* Seems to be required after a write */ - for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; + for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; write_timeout -= 5) { ret = dev->em28xx_read_reg(dev, 0x05); if (!ret) diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index f891a28706f5..2aa4b8472a62 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -194,7 +194,7 @@ */ /* time in msecs to wait for i2c writes to finish */ -#define EM2800_I2C_WRITE_TIMEOUT 20 +#define EM2800_I2C_XFER_TIMEOUT 20 enum em28xx_mode { EM28XX_SUSPEND, From eaf33c404cd60ba4b442324766abbb5da8c94381 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 3 Jan 2013 14:27:04 -0300 Subject: [PATCH 0540/4607] [media] em28xx: fix the i2c adapter functionality flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I2C_FUNC_SMBUS_EMUL includes flag I2C_FUNC_SMBUS_WRITE_BLOCK_DATA which signals that up to 31 data bytes can be written to the ic2 client. But the EM2800 supports only i2c messages with max. 4 data bytes. I2C_FUNC_IC2 should be set if a master_xfer function pointer is provided in struct i2c_algorithm. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 67a8e623dd8c..4a24ed08e46a 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -445,7 +445,11 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) */ static u32 functionality(struct i2c_adapter *adap) { - return I2C_FUNC_SMBUS_EMUL; + struct em28xx *dev = adap->algo_data; + u32 func_flags = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + if (dev->board.is_em2800) + func_flags &= ~I2C_FUNC_SMBUS_WRITE_BLOCK_DATA; + return func_flags; } static struct i2c_algorithm em28xx_algo = { From 45f04e82d035006afe5023850393e9b3b74b85c2 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 3 Jan 2013 14:27:05 -0300 Subject: [PATCH 0541/4607] [media] em28xx: fix+improve+unify i2c error handling, debug messages and code comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - do not pass USB specific error codes to userspace/i2c-subsystem - unify the returned error codes and make them compliant with the i2c subsystem spec - check number of actually transferred bytes (via USB) everywehere - fix/improve debug messages - improve code comments Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 5 +- drivers/media/usb/em28xx/em28xx-i2c.c | 116 ++++++++++++++++++------- 2 files changed, 89 insertions(+), 32 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 6916e87bc624..80f87bbbd554 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -101,7 +101,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, if (reg_debug) printk(" failed!\n"); mutex_unlock(&dev->ctrl_urb_lock); - return ret; + return usb_translate_errors(ret); } if (len) @@ -182,6 +182,9 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, 0x0000, reg, dev->urb_buf, len, HZ); mutex_unlock(&dev->ctrl_urb_lock); + if (ret < 0) + return usb_translate_errors(ret); + if (dev->wait_after_write) msleep(dev->wait_after_write); diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 4a24ed08e46a..f784b516ae1c 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -76,18 +76,26 @@ static int em2800_i2c_send_bytes(struct em28xx *dev, u8 addr, u8 *buf, u16 len) /* trigger write */ ret = dev->em28xx_write_regs(dev, 4 - len, &b2[4 - len], 2 + len); if (ret != 2 + len) { - em28xx_warn("writing to i2c device failed (error=%i)\n", ret); - return -EIO; + em28xx_warn("failed to trigger write to i2c address 0x%x " + "(error=%i)\n", addr, ret); + return (ret < 0) ? ret : -EIO; } /* wait for completion */ for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; write_timeout -= 5) { ret = dev->em28xx_read_reg(dev, 0x05); - if (ret == 0x80 + len - 1) + if (ret == 0x80 + len - 1) { return len; + } else if (ret == 0x94 + len - 1) { + return -ENODEV; + } else if (ret < 0) { + em28xx_warn("failed to get i2c transfer status from " + "bridge register (error=%i)\n", ret); + return ret; + } msleep(5); } - em28xx_warn("i2c write timed out\n"); + em28xx_warn("write to i2c device at 0x%x timed out\n", addr); return -EIO; } @@ -168,24 +176,48 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, u8 addr) static int em28xx_i2c_send_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len, int stop) { - int wrcount = 0; int write_timeout, ret; if (len < 1 || len > 64) return -EOPNOTSUPP; + /* NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected */ - wrcount = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); + /* Write to i2c device */ + ret = dev->em28xx_write_regs_req(dev, stop ? 2 : 3, addr, buf, len); + if (ret != len) { + if (ret < 0) { + em28xx_warn("writing to i2c device at 0x%x failed " + "(error=%i)\n", addr, ret); + return ret; + } else { + em28xx_warn("%i bytes write to i2c device at 0x%x " + "requested, but %i bytes written\n", + len, addr, ret); + return -EIO; + } + } - /* Seems to be required after a write */ + /* Check success of the i2c operation */ for (write_timeout = EM2800_I2C_XFER_TIMEOUT; write_timeout > 0; write_timeout -= 5) { ret = dev->em28xx_read_reg(dev, 0x05); - if (!ret) - break; + if (ret == 0) { /* success */ + return len; + } else if (ret == 0x10) { + return -ENODEV; + } else if (ret < 0) { + em28xx_warn("failed to read i2c transfer status from " + "bridge (error=%i)\n", ret); + return ret; + } msleep(5); + /* NOTE: do we really have to wait for success ? + Never seen anything else than 0x00 or 0x10 + (even with high payload) ... */ } - - return wrcount; + em28xx_warn("write to i2c device at 0x%x timed out\n", addr); + return -EIO; } /* @@ -198,15 +230,40 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) if (len < 1 || len > 64) return -EOPNOTSUPP; + /* NOTE: limited by the USB ctrl message constraints + * Zero length reads always succeed, even if no device is connected */ + /* Read data from i2c device */ ret = dev->em28xx_read_reg_req_len(dev, 2, addr, buf, len); + if (ret != len) { + if (ret < 0) { + em28xx_warn("reading from i2c device at 0x%x failed " + "(error=%i)\n", addr, ret); + return ret; + } else { + em28xx_warn("%i bytes requested from i2c device at " + "0x%x, but %i bytes received\n", + len, addr, ret); + return -EIO; + } + } + + /* Check success of the i2c operation */ + ret = dev->em28xx_read_reg(dev, 0x05); if (ret < 0) { - em28xx_warn("reading i2c device failed (error=%i)\n", ret); + em28xx_warn("failed to read i2c transfer status from " + "bridge (error=%i)\n", ret); return ret; } - if (dev->em28xx_read_reg(dev, 0x5) != 0) - return -ENODEV; - return ret; + if (ret > 0) { + if (ret == 0x10) { + return -ENODEV; + } else { + em28xx_warn("unknown i2c error (status=%i)\n", ret); + return -EIO; + } + } + return len; } /* @@ -216,15 +273,12 @@ static int em28xx_i2c_recv_bytes(struct em28xx *dev, u16 addr, u8 *buf, u16 len) static int em28xx_i2c_check_for_device(struct em28xx *dev, u16 addr) { int ret; + u8 buf; - ret = dev->em28xx_read_reg_req(dev, 2, addr); - if (ret < 0) { - em28xx_warn("reading from i2c device failed (error=%i)\n", ret); - return ret; - } - if (dev->em28xx_read_reg(dev, 0x5) != 0) - return -ENODEV; - return 0; + ret = em28xx_i2c_recv_bytes(dev, addr, &buf, 1); + if (ret == 1) + return 0; + return (ret < 0) ? ret : -EIO; } /* @@ -249,11 +303,11 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, rc = em2800_i2c_check_for_device(dev, addr); else rc = em28xx_i2c_check_for_device(dev, addr); - if (rc < 0) { - dprintk2(2, " no device\n"); + if (rc == -ENODEV) { + if (i2c_debug >= 2) + printk(" no device\n"); return rc; } - } else if (msgs[i].flags & I2C_M_RD) { /* read bytes */ if (dev->board.is_em2800) @@ -284,16 +338,16 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, msgs[i].len, i == num - 1); } - if (rc < 0) - goto err; + if (rc < 0) { + if (i2c_debug >= 2) + printk(" ERROR: %i\n", rc); + return rc; + } if (i2c_debug >= 2) printk("\n"); } return num; -err: - dprintk2(2, " ERROR: %i\n", rc); - return rc; } /* based on linux/sunrpc/svcauth.h and linux/hash.h From 90271964c96f89862b3e06e184e770e16cca7c8f Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 3 Jan 2013 14:27:06 -0300 Subject: [PATCH 0542/4607] [media] em28xx: consider the message length limitation of the i2c adapter when reading the eeprom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EEPROMs are currently read in blocks of 16 bytes, but the em2800 is limited to 4 bytes per read. All other chip variants support reading of max. 64 bytes at once (according to the em258x datasheet; also verified with em2710, em2882, and em28174). Since em2800_i2c_recv_bytes() has been fixed to return with -EOPNOTSUPP when more than 4 bytes are requested, EEPROM reading with this chip is broken. It was actually broken before that change, too, it just didn't throw an error because the i2c adapter silently returned trash data (for all reads >1 byte !). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index f784b516ae1c..9ae8f6051d8e 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -379,7 +379,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) { unsigned char buf, *p = eedata; struct em28xx_eeprom *em_eeprom = (void *)eedata; - int i, err, size = len, block; + int i, err, size = len, block, block_max; if (dev->chip_id == CHIP_ID_EM2874 || dev->chip_id == CHIP_ID_EM28174 || @@ -412,9 +412,15 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) dev->name, err); return err; } + + if (dev->board.is_em2800) + block_max = 4; + else + block_max = 64; + while (size > 0) { - if (size > 16) - block = 16; + if (size > block_max) + block = block_max; else block = size; From 317efce991620adc589b3005b9baed433dcb2a56 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 24 Nov 2012 21:35:48 -0300 Subject: [PATCH 0543/4607] [media] v4l: Reset subdev v4l2_dev field to NULL if registration fails When subdev registration fails the subdev v4l2_dev field is left to a non-NULL value. Later calls to v4l2_device_unregister_subdev() will consider the subdev as registered and will module_put() the subdev module without any matching module_get(). Fix this by setting the subdev v4l2_dev field to NULL in v4l2_device_register_subdev() when the function fails. Signed-off-by: Laurent Pinchart Cc: stable@vger.kernel.org Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-device.c | 30 +++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 513969fa695d..98a7f5e9cb1b 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -159,31 +159,21 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, sd->v4l2_dev = v4l2_dev; if (sd->internal_ops && sd->internal_ops->registered) { err = sd->internal_ops->registered(sd); - if (err) { - module_put(sd->owner); - return err; - } + if (err) + goto error_module; } /* This just returns 0 if either of the two args is NULL */ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL); - if (err) { - if (sd->internal_ops && sd->internal_ops->unregistered) - sd->internal_ops->unregistered(sd); - module_put(sd->owner); - return err; - } + if (err) + goto error_unregister; #if defined(CONFIG_MEDIA_CONTROLLER) /* Register the entity. */ if (v4l2_dev->mdev) { err = media_device_register_entity(v4l2_dev->mdev, entity); - if (err < 0) { - if (sd->internal_ops && sd->internal_ops->unregistered) - sd->internal_ops->unregistered(sd); - module_put(sd->owner); - return err; - } + if (err < 0) + goto error_unregister; } #endif @@ -192,6 +182,14 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, spin_unlock(&v4l2_dev->lock); return 0; + +error_unregister: + if (sd->internal_ops && sd->internal_ops->unregistered) + sd->internal_ops->unregistered(sd); +error_module: + module_put(sd->owner); + sd->v4l2_dev = NULL; + return err; } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); From 3a799c27b3faebaf5a7a6149dfe8f705451b241f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2013 01:27:21 -0200 Subject: [PATCH 0544/4607] [media] em28xx: declare em28xx_stop_streaming as static That fixes the following warning: drivers/media/usb/em28xx/em28xx-video.c:611:5: warning: no previous prototype for 'em28xx_stop_streaming' [-Wmissing-prototypes] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 40b96d17ccf8..75027e390738 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -608,7 +608,7 @@ fail: return rc; } -int em28xx_stop_streaming(struct vb2_queue *vq) +static int em28xx_stop_streaming(struct vb2_queue *vq) { struct em28xx *dev = vb2_get_drv_priv(vq); struct em28xx_dmaqueue *vidq = &dev->vidq; From daf16bab1eaf5a82217697bfb91eb7d9c9745d0d Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 28 Nov 2012 23:56:15 -0300 Subject: [PATCH 0545/4607] [media] mt9v022: fix potential NULL pointer dereference in mt9v022_probe() The dereference to 'icl' should be moved below the NULL test. Reported-by: Fengguang Wu Signed-off-by: Wei Yongjun Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/mt9v022.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index d40a8858be01..75098024d477 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -875,7 +875,7 @@ static int mt9v022_probe(struct i2c_client *client, struct mt9v022 *mt9v022; struct soc_camera_link *icl = soc_camera_i2c_to_link(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct mt9v022_platform_data *pdata = icl->priv; + struct mt9v022_platform_data *pdata; int ret; if (!icl) { @@ -893,6 +893,7 @@ static int mt9v022_probe(struct i2c_client *client, if (!mt9v022) return -ENOMEM; + pdata = icl->priv; v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); v4l2_ctrl_handler_init(&mt9v022->hdl, 6); v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, From 7d051b35d5196ad6011a17e751dbd3d180abb046 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 20 Dec 2012 13:02:51 -0300 Subject: [PATCH 0546/4607] [media] soc-camera: properly fix camera probing races The recently introduced host_lock causes lockdep warnings, besides, list enumeration in scan_add_host() must be protected by holdint the list_lock. OTOH, holding .video_lock in soc_camera_open() isn't enough to protect the host during its building of the pipeline, because .video_lock is per soc-camera device. If, e.g. more than one sensor can be attached to a host and the user tries to open both device nodes simultaneously, host's .add() method can be called simultaneously for both sensors. Fix these problems by holding list_lock instead of .host_lock in scan_add_host() and taking it shortly at the beginning of soc_camera_open(), and using .host_lock to protect host's .add() and .remove() operations only. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/soc_camera/soc_camera.c | 22 ++++++++++++++++--- include/media/soc_camera.h | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index a8ca956e7a40..1070a0c496d8 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -517,7 +517,14 @@ static int soc_camera_open(struct file *file) /* No device driver attached */ return -ENODEV; + /* + * Don't mess with the host during probe: wait until the loop in + * scan_add_host() completes + */ + if (mutex_lock_interruptible(&list_lock)) + return -ERESTARTSYS; ici = to_soc_camera_host(icd->parent); + mutex_unlock(&list_lock); if (mutex_lock_interruptible(&icd->video_lock)) return -ERESTARTSYS; @@ -548,7 +555,6 @@ static int soc_camera_open(struct file *file) if (icl->reset) icl->reset(icd->pdev); - /* Don't mess with the host during probe */ mutex_lock(&ici->host_lock); ret = ici->ops->add(icd); mutex_unlock(&ici->host_lock); @@ -602,7 +608,9 @@ esfmt: eresume: __soc_camera_power_off(icd); epower: + mutex_lock(&ici->host_lock); ici->ops->remove(icd); + mutex_unlock(&ici->host_lock); eiciadd: icd->use_count--; module_put(ici->ops->owner); @@ -625,7 +633,9 @@ static int soc_camera_close(struct file *file) if (ici->ops->init_videobuf2) vb2_queue_release(&icd->vb2_vidq); + mutex_lock(&ici->host_lock); ici->ops->remove(icd); + mutex_unlock(&ici->host_lock); __soc_camera_power_off(icd); } @@ -1052,7 +1062,7 @@ static void scan_add_host(struct soc_camera_host *ici) { struct soc_camera_device *icd; - mutex_lock(&ici->host_lock); + mutex_lock(&list_lock); list_for_each_entry(icd, &devices, list) { if (icd->iface == ici->nr) { @@ -1061,7 +1071,7 @@ static void scan_add_host(struct soc_camera_host *ici) } } - mutex_unlock(&ici->host_lock); + mutex_unlock(&list_lock); } #ifdef CONFIG_I2C_BOARDINFO @@ -1148,7 +1158,9 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (icl->reset) icl->reset(icd->pdev); + mutex_lock(&ici->host_lock); ret = ici->ops->add(icd); + mutex_unlock(&ici->host_lock); if (ret < 0) goto eadd; @@ -1220,7 +1232,9 @@ static int soc_camera_probe(struct soc_camera_device *icd) icd->field = mf.field; } + mutex_lock(&ici->host_lock); ici->ops->remove(icd); + mutex_unlock(&ici->host_lock); mutex_unlock(&icd->video_lock); @@ -1242,7 +1256,9 @@ eadddev: video_device_release(icd->vdev); icd->vdev = NULL; evdc: + mutex_lock(&ici->host_lock); ici->ops->remove(icd); + mutex_unlock(&ici->host_lock); eadd: ereg: v4l2_ctrl_handler_free(&icd->ctrl_handler); diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 6442edc2a151..0370a9517282 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -62,7 +62,7 @@ struct soc_camera_device { struct soc_camera_host { struct v4l2_device v4l2_dev; struct list_head list; - struct mutex host_lock; /* Protect during probing */ + struct mutex host_lock; /* Protect pipeline modifications */ unsigned char nr; /* Host number */ u32 capabilities; void *priv; From 8a97d4c11756ab6bab8582126d0f1b5c00b067ad Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 3 Jan 2013 11:21:02 -0300 Subject: [PATCH 0547/4607] [media] soc-camera: fix repeated regulator requesting Currently devm_regulator_bulk_get() is called by soc-camera during host driver probing, but regulators are attached to the camera platform device, that is staying, independent whether the host probed successfully or not. This can lead to repeated regulator requesting, if the host driver is re-probed. Move the call to platform device probing to avoid this. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/soc_camera.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 1070a0c496d8..6552856ea59b 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1149,11 +1149,6 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) return ret; - ret = devm_regulator_bulk_get(icd->pdev, icl->num_regulators, - icl->regulators); - if (ret < 0) - goto ereg; - /* The camera could have been already on, try to reset */ if (icl->reset) icl->reset(icd->pdev); @@ -1260,7 +1255,6 @@ evdc: ici->ops->remove(icd); mutex_unlock(&ici->host_lock); eadd: -ereg: v4l2_ctrl_handler_free(&icd->ctrl_handler); return ret; } @@ -1549,6 +1543,7 @@ static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) { struct soc_camera_link *icl = pdev->dev.platform_data; struct soc_camera_device *icd; + int ret; if (!icl) return -EINVAL; @@ -1557,6 +1552,11 @@ static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) if (!icd) return -ENOMEM; + ret = devm_regulator_bulk_get(&pdev->dev, icl->num_regulators, + icl->regulators); + if (ret < 0) + return ret; + icd->iface = icl->bus_id; icd->link = icl; icd->pdev = &pdev->dev; From dd669e907cbe1cf33f9cbbff79af2b5c271cdd89 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 24 Dec 2012 09:31:33 -0300 Subject: [PATCH 0548/4607] [media] soc-camera: remove struct soc_camera_device::video_lock Currently soc-camera has a per-device node lock, used for video operations and a per-host lock for code paths, modifying host's pipeline. Manipulating the two locks increases complexity and doesn't bring any advantages. This patch removes the per-device lock and uses the per-host lock for all operations. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/atmel-isi.c | 4 +- .../media/platform/soc_camera/mx1_camera.c | 3 +- .../media/platform/soc_camera/mx2_camera.c | 1 - .../media/platform/soc_camera/mx3_camera.c | 4 +- .../media/platform/soc_camera/omap1_camera.c | 4 +- .../media/platform/soc_camera/pxa_camera.c | 6 +-- .../soc_camera/sh_mobile_ceu_camera.c | 4 +- .../media/platform/soc_camera/soc_camera.c | 51 ++++++++----------- include/media/soc_camera.h | 3 +- 9 files changed, 35 insertions(+), 45 deletions(-) diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index c8d748a31944..eba1f6b2b0fb 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -745,7 +745,7 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, return formats; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static int isi_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -770,7 +770,7 @@ static int isi_camera_add_device(struct soc_camera_device *icd) icd->devnum); return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void isi_camera_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index 674ded646b66..4b661e87d8b1 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -373,7 +372,7 @@ static void mx1_camera_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct mx1_buffer), icd, &icd->video_lock); + sizeof(struct mx1_buffer), icd, &icd->host_lock); } static int mclk_get_divisor(struct mx1_camera_dev *pcdev) diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 28d5c84eef82..bf2740f7c4d1 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 574d12522f95..37d0d0ef8adf 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -510,7 +510,7 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, clk_set_rate(mx3_cam->clk, rate); } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static int mx3_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -530,7 +530,7 @@ static int mx3_camera_add_device(struct soc_camera_device *icd) return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void mx3_camera_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index 8f9c1f44544c..dcf7be814776 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -1383,12 +1383,12 @@ static void omap1_cam_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &icd->video_lock); + sizeof(struct omap1_cam_buf), icd, &icd->host_lock); else videobuf_queue_sg_init(q, &omap1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &icd->video_lock); + sizeof(struct omap1_cam_buf), icd, &icd->host_lock); /* use videobuf mode (auto)selected with the module parameter */ pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG; diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 8ff961eec39d..f3c1b6202425 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -842,7 +842,7 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q, */ videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct pxa_buffer), icd, &icd->video_lock); + sizeof(struct pxa_buffer), icd, &icd->host_lock); } static u32 mclk_get_divisor(struct platform_device *pdev, @@ -958,7 +958,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) /* * The following two functions absolutely depend on the fact, that * there can be only one camera on PXA quick capture interface - * Called with .video_lock held + * Called with .host_lock held */ static int pxa_camera_add_device(struct soc_camera_device *icd) { @@ -978,7 +978,7 @@ static int pxa_camera_add_device(struct soc_camera_device *icd) return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void pxa_camera_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 9f021043cfe6..cdf173bbcc93 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -543,7 +543,7 @@ static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev) return NULL; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -587,7 +587,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) return 0; } -/* Called with .video_lock held */ +/* Called with .host_lock held */ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 6552856ea59b..ae89f980e82b 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -383,7 +383,7 @@ static int soc_camera_prepare_buf(struct file *file, void *priv, return vb2_prepare_buf(&icd->vb2_vidq, b); } -/* Always entered with .video_lock held */ +/* Always entered with .host_lock held */ static int soc_camera_init_user_formats(struct soc_camera_device *icd) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); @@ -450,7 +450,7 @@ egfmt: return ret; } -/* Always entered with .video_lock held */ +/* Always entered with .host_lock held */ static void soc_camera_free_user_formats(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); @@ -526,7 +526,7 @@ static int soc_camera_open(struct file *file) ici = to_soc_camera_host(icd->parent); mutex_unlock(&list_lock); - if (mutex_lock_interruptible(&icd->video_lock)) + if (mutex_lock_interruptible(&ici->host_lock)) return -ERESTARTSYS; if (!try_module_get(ici->ops->owner)) { dev_err(icd->pdev, "Couldn't lock capture bus driver.\n"); @@ -555,9 +555,7 @@ static int soc_camera_open(struct file *file) if (icl->reset) icl->reset(icd->pdev); - mutex_lock(&ici->host_lock); ret = ici->ops->add(icd); - mutex_unlock(&ici->host_lock); if (ret < 0) { dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); goto eiciadd; @@ -576,7 +574,7 @@ static int soc_camera_open(struct file *file) * Try to configure with default parameters. Notice: this is the * very first open, so, we cannot race against other calls, * apart from someone else calling open() simultaneously, but - * .video_lock is protecting us against it. + * .host_lock is protecting us against it. */ ret = soc_camera_set_fmt(icd, &f); if (ret < 0) @@ -591,7 +589,7 @@ static int soc_camera_open(struct file *file) } v4l2_ctrl_handler_setup(&icd->ctrl_handler); } - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); file->private_data = icd; dev_dbg(icd->pdev, "camera device open\n"); @@ -599,7 +597,7 @@ static int soc_camera_open(struct file *file) return 0; /* - * First four errors are entered with the .video_lock held + * First four errors are entered with the .host_lock held * and use_count == 1 */ einitvb: @@ -608,14 +606,12 @@ esfmt: eresume: __soc_camera_power_off(icd); epower: - mutex_lock(&ici->host_lock); ici->ops->remove(icd); - mutex_unlock(&ici->host_lock); eiciadd: icd->use_count--; module_put(ici->ops->owner); emodule: - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); return ret; } @@ -625,7 +621,7 @@ static int soc_camera_close(struct file *file) struct soc_camera_device *icd = file->private_data; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - mutex_lock(&icd->video_lock); + mutex_lock(&ici->host_lock); icd->use_count--; if (!icd->use_count) { pm_runtime_suspend(&icd->vdev->dev); @@ -633,16 +629,14 @@ static int soc_camera_close(struct file *file) if (ici->ops->init_videobuf2) vb2_queue_release(&icd->vb2_vidq); - mutex_lock(&ici->host_lock); ici->ops->remove(icd); - mutex_unlock(&ici->host_lock); __soc_camera_power_off(icd); } if (icd->streamer == file) icd->streamer = NULL; - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); module_put(ici->ops->owner); @@ -679,13 +673,13 @@ static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) if (icd->streamer != file) return -EBUSY; - if (mutex_lock_interruptible(&icd->video_lock)) + if (mutex_lock_interruptible(&ici->host_lock)) return -ERESTARTSYS; if (ici->ops->init_videobuf) err = videobuf_mmap_mapper(&icd->vb_vidq, vma); else err = vb2_mmap(&icd->vb2_vidq, vma); - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, @@ -704,26 +698,28 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt) if (icd->streamer != file) return POLLERR; - mutex_lock(&icd->video_lock); + mutex_lock(&ici->host_lock); if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream)) dev_err(icd->pdev, "Trying to poll with no queued buffers!\n"); else res = ici->ops->poll(file, pt); - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); return res; } void soc_camera_lock(struct vb2_queue *vq) { struct soc_camera_device *icd = vb2_get_drv_priv(vq); - mutex_lock(&icd->video_lock); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + mutex_lock(&ici->host_lock); } EXPORT_SYMBOL(soc_camera_lock); void soc_camera_unlock(struct vb2_queue *vq) { struct soc_camera_device *icd = vb2_get_drv_priv(vq); - mutex_unlock(&icd->video_lock); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + mutex_unlock(&ici->host_lock); } EXPORT_SYMBOL(soc_camera_unlock); @@ -1213,7 +1209,7 @@ static int soc_camera_probe(struct soc_camera_device *icd) * itself is protected against concurrent open() calls, but we also have * to protect our data. */ - mutex_lock(&icd->video_lock); + mutex_lock(&ici->host_lock); ret = soc_camera_video_start(icd); if (ret < 0) @@ -1227,16 +1223,14 @@ static int soc_camera_probe(struct soc_camera_device *icd) icd->field = mf.field; } - mutex_lock(&ici->host_lock); ici->ops->remove(icd); - mutex_unlock(&ici->host_lock); - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); return 0; evidstart: - mutex_unlock(&icd->video_lock); + mutex_unlock(&ici->host_lock); soc_camera_free_user_formats(icd); eiufmt: ectrl: @@ -1451,7 +1445,6 @@ static int soc_camera_device_register(struct soc_camera_device *icd) icd->devnum = num; icd->use_count = 0; icd->host_priv = NULL; - mutex_init(&icd->video_lock); list_add_tail(&icd->list, &devices); @@ -1509,7 +1502,7 @@ static int video_dev_create(struct soc_camera_device *icd) vdev->release = video_device_release; vdev->tvnorms = V4L2_STD_UNKNOWN; vdev->ctrl_handler = &icd->ctrl_handler; - vdev->lock = &icd->video_lock; + vdev->lock = &ici->host_lock; icd->vdev = vdev; @@ -1517,7 +1510,7 @@ static int video_dev_create(struct soc_camera_device *icd) } /* - * Called from soc_camera_probe() above (with .video_lock held???) + * Called from soc_camera_probe() above with .host_lock held */ static int soc_camera_video_start(struct soc_camera_device *icd) { diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 0370a9517282..5a662c981484 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -46,9 +46,8 @@ struct soc_camera_device { int num_user_formats; enum v4l2_field field; /* Preserve field over close() */ void *host_priv; /* Per-device host private data */ - /* soc_camera.c private count. Only accessed with .video_lock held */ + /* soc_camera.c private count. Only accessed with .host_lock held */ int use_count; - struct mutex video_lock; /* Protects device data */ struct file *streamer; /* stream owner */ union { struct videobuf_queue vb_vidq; From 25a348110078cefa99b0b079938dd930cfc3a0be Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 21 Dec 2012 08:11:48 -0300 Subject: [PATCH 0549/4607] [media] soc-camera: split struct soc_camera_link into host and subdevice parts struct soc_camera_link currently contains fields, used both by sensor and bridge drivers. To make subdevice driver re-use simpler, split it into a host and a subdevice parts. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/imx074.c | 14 +-- drivers/media/i2c/soc_camera/mt9m001.c | 38 +++---- drivers/media/i2c/soc_camera/mt9m111.c | 21 ++-- drivers/media/i2c/soc_camera/mt9t031.c | 22 ++-- drivers/media/i2c/soc_camera/mt9t112.c | 18 ++-- drivers/media/i2c/soc_camera/mt9v022.c | 36 +++---- drivers/media/i2c/soc_camera/ov2640.c | 12 +-- drivers/media/i2c/soc_camera/ov5642.c | 16 +-- drivers/media/i2c/soc_camera/ov6650.c | 16 +-- drivers/media/i2c/soc_camera/ov772x.c | 14 +-- drivers/media/i2c/soc_camera/ov9640.c | 12 +-- drivers/media/i2c/soc_camera/ov9740.c | 14 +-- drivers/media/i2c/soc_camera/rj54n1cb0c.c | 24 ++--- drivers/media/i2c/soc_camera/tw9910.c | 18 ++-- .../media/platform/soc_camera/soc_camera.c | 98 +++++++++-------- .../platform/soc_camera/soc_camera_platform.c | 2 +- include/media/soc_camera.h | 102 ++++++++++++++---- include/media/soc_camera_platform.h | 10 +- 18 files changed, 279 insertions(+), 208 deletions(-) diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c index f8534eec9de9..1c065717cdd1 100644 --- a/drivers/media/i2c/soc_camera/imx074.c +++ b/drivers/media/i2c/soc_camera/imx074.c @@ -271,9 +271,9 @@ static int imx074_g_chip_ident(struct v4l2_subdev *sd, static int imx074_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int imx074_g_mbus_config(struct v4l2_subdev *sd, @@ -430,10 +430,10 @@ static int imx074_probe(struct i2c_client *client, { struct imx074 *priv; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "IMX074: missing platform data!\n"); return -EINVAL; } @@ -464,10 +464,10 @@ static int imx074_probe(struct i2c_client *client, static int imx074_remove(struct i2c_client *client) { struct imx074 *priv = to_imx074(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); kfree(priv); return 0; diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index 19f8a07764f9..9ae7066798eb 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -23,7 +23,7 @@ /* * mt9m001 i2c address 0x5d * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_link + * from struct soc_camera_host_desc */ /* mt9m001 selected register addresses */ @@ -380,9 +380,9 @@ static int mt9m001_s_register(struct v4l2_subdev *sd, static int mt9m001_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -482,7 +482,7 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9m001_video_probe(struct soc_camera_link *icl, +static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); @@ -526,8 +526,8 @@ static int mt9m001_video_probe(struct soc_camera_link *icl, * The platform may support different bus widths due to * different routing of the data lines. */ - if (icl->query_bus_param) - flags = icl->query_bus_param(icl); + if (ssdd->query_bus_param) + flags = ssdd->query_bus_param(ssdd); else flags = SOCAM_DATAWIDTH_10; @@ -558,10 +558,10 @@ done: return ret; } -static void mt9m001_video_remove(struct soc_camera_link *icl) +static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd) { - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); } static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) @@ -605,14 +605,14 @@ static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); /* MT9M001 has all capture_format parameters fixed */ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -621,12 +621,12 @@ static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { const struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct mt9m001 *mt9m001 = to_mt9m001(client); unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; - if (icl->set_bus_param) - return icl->set_bus_param(icl, 1 << (bps - 1)); + if (ssdd->set_bus_param) + return ssdd->set_bus_param(ssdd, 1 << (bps - 1)); /* * Without board specific bus width settings we only support the @@ -663,10 +663,10 @@ static int mt9m001_probe(struct i2c_client *client, { struct mt9m001 *mt9m001; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "MT9M001 driver needs platform data\n"); return -EINVAL; } @@ -713,7 +713,7 @@ static int mt9m001_probe(struct i2c_client *client, mt9m001->rect.width = MT9M001_MAX_WIDTH; mt9m001->rect.height = MT9M001_MAX_HEIGHT; - ret = mt9m001_video_probe(icl, client); + ret = mt9m001_video_probe(ssdd, client); if (ret) { v4l2_ctrl_handler_free(&mt9m001->hdl); kfree(mt9m001); @@ -725,11 +725,11 @@ static int mt9m001_probe(struct i2c_client *client, static int mt9m001_remove(struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); v4l2_device_unregister_subdev(&mt9m001->subdev); v4l2_ctrl_handler_free(&mt9m001->hdl); - mt9m001_video_remove(icl); + mt9m001_video_remove(ssdd); kfree(mt9m001); return 0; diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index 62fd94af599b..78511669bab0 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -24,7 +24,8 @@ /* * MT9M111, MT9M112 and MT9M131: * i2c address is 0x48 or 0x5d (depending on SADDR pin) - * The platform has to define i2c_board_info and call i2c_register_board_info() + * The platform has to define struct i2c_board_info objects and link to them + * from struct soc_camera_host_desc */ /* @@ -799,17 +800,17 @@ static int mt9m111_init(struct mt9m111 *mt9m111) static int mt9m111_power_on(struct mt9m111 *mt9m111) { struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; ret = mt9m111_resume(mt9m111); if (ret < 0) { dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret); - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } return ret; @@ -818,10 +819,10 @@ static int mt9m111_power_on(struct mt9m111 *mt9m111) static void mt9m111_power_off(struct mt9m111 *mt9m111) { struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); mt9m111_suspend(mt9m111); - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } static int mt9m111_s_power(struct v4l2_subdev *sd, int on) @@ -879,13 +880,13 @@ static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -956,10 +957,10 @@ static int mt9m111_probe(struct i2c_client *client, { struct mt9m111 *mt9m111; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "mt9m111: driver needs platform data\n"); return -EINVAL; } diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index 40800b10a080..9ca6d65cefaf 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -31,8 +31,8 @@ /* * mt9t031 i2c address 0x5d - * The platform has to define i2c_board_info and link to it from - * struct soc_camera_link + * The platform has to define struct i2c_board_info objects and link to them + * from struct soc_camera_host_desc */ /* mt9t031 selected register addresses */ @@ -608,18 +608,18 @@ static struct device_type mt9t031_dev_type = { static int mt9t031_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct video_device *vdev = soc_camera_i2c_to_vdev(client); int ret; if (on) { - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; vdev->dev.type = &mt9t031_dev_type; } else { vdev->dev.type = NULL; - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } return 0; @@ -707,13 +707,13 @@ static int mt9t031_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -722,9 +722,9 @@ static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (soc_camera_apply_board_flags(icl, cfg) & + if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_FALLING) return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); else @@ -758,11 +758,11 @@ static int mt9t031_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9t031 *mt9t031; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "MT9T031 driver needs platform data\n"); return -EINVAL; } diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index de7cd836b0a2..92bb65de6cd9 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -779,9 +779,9 @@ static int mt9t112_s_register(struct v4l2_subdev *sd, static int mt9t112_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { @@ -991,13 +991,13 @@ static int mt9t112_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1006,10 +1006,10 @@ static int mt9t112_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct mt9t112_priv *priv = to_mt9t112(client); - if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) + if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) priv->flags |= PCLK_RISING; return 0; @@ -1078,7 +1078,7 @@ static int mt9t112_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9t112_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct v4l2_rect rect = { .width = VGA_WIDTH, .height = VGA_HEIGHT, @@ -1087,7 +1087,7 @@ static int mt9t112_probe(struct i2c_client *client, }; int ret; - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "mt9t112: missing platform data!\n"); return -EINVAL; } @@ -1096,7 +1096,7 @@ static int mt9t112_probe(struct i2c_client *client, if (!priv) return -ENOMEM; - priv->info = icl->priv; + priv->info = ssdd->drv_priv; v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index 75098024d477..33fb5c3b52b4 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -25,7 +25,7 @@ /* * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c * The platform has to define struct i2c_board_info objects and link to them - * from struct soc_camera_link + * from struct soc_camera_host_desc */ static char *sensor_type; @@ -508,9 +508,9 @@ static int mt9v022_s_register(struct v4l2_subdev *sd, static int mt9v022_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) @@ -655,7 +655,7 @@ static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) static int mt9v022_video_probe(struct i2c_client *client) { struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); s32 data; int ret; unsigned long flags; @@ -715,8 +715,8 @@ static int mt9v022_video_probe(struct i2c_client *client) * The platform may support different bus widths due to * different routing of the data lines. */ - if (icl->query_bus_param) - flags = icl->query_bus_param(icl); + if (ssdd->query_bus_param) + flags = ssdd->query_bus_param(ssdd); else flags = SOCAM_DATAWIDTH_10; @@ -784,7 +784,7 @@ static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | @@ -792,7 +792,7 @@ static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -801,15 +801,15 @@ static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct mt9v022 *mt9v022 = to_mt9v022(client); - unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; int ret; u16 pixclk = 0; - if (icl->set_bus_param) { - ret = icl->set_bus_param(icl, 1 << (bps - 1)); + if (ssdd->set_bus_param) { + ret = ssdd->set_bus_param(ssdd, 1 << (bps - 1)); if (ret) return ret; } else if (bps != 10) { @@ -873,12 +873,12 @@ static int mt9v022_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9v022 *mt9v022; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct mt9v022_platform_data *pdata; int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "MT9V022 driver needs platform data\n"); return -EINVAL; } @@ -893,7 +893,7 @@ static int mt9v022_probe(struct i2c_client *client, if (!mt9v022) return -ENOMEM; - pdata = icl->priv; + pdata = ssdd->drv_priv; v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); v4l2_ctrl_handler_init(&mt9v022->hdl, 6); v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, @@ -961,11 +961,11 @@ static int mt9v022_probe(struct i2c_client *client, static int mt9v022_remove(struct i2c_client *client) { struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); v4l2_device_unregister_subdev(&mt9v022->subdev); - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); v4l2_ctrl_handler_free(&mt9v022->hdl); kfree(mt9v022); diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c index 66698a83bda2..c57a5095edef 100644 --- a/drivers/media/i2c/soc_camera/ov2640.c +++ b/drivers/media/i2c/soc_camera/ov2640.c @@ -771,9 +771,9 @@ static int ov2640_s_register(struct v4l2_subdev *sd, static int ov2640_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } /* Select the nearest higher resolution for capture */ @@ -1046,13 +1046,13 @@ static int ov2640_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1080,11 +1080,11 @@ static int ov2640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov2640_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - if (!icl) { + if (!ssdd) { dev_err(&adapter->dev, "OV2640: Missing platform_data for driver\n"); return -EINVAL; diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index 8577e0cfb7fe..b892d01e57ae 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -934,13 +934,13 @@ static int ov5642_g_mbus_config(struct v4l2_subdev *sd, static int ov5642_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; if (!on) - return soc_camera_power_off(&client->dev, icl); + return soc_camera_power_off(&client->dev, ssdd); - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; @@ -1020,10 +1020,10 @@ static int ov5642_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov5642 *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "OV5642: missing platform data!\n"); return -EINVAL; } @@ -1057,10 +1057,10 @@ error: static int ov5642_remove(struct i2c_client *client) { struct ov5642 *priv = to_ov5642(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); kfree(priv); return 0; diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index e87feb0881e3..1ae8b8dd268b 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -435,9 +435,9 @@ static int ov6650_set_register(struct v4l2_subdev *sd, static int ov6650_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) @@ -892,7 +892,7 @@ static int ov6650_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | @@ -900,7 +900,7 @@ static int ov6650_g_mbus_config(struct v4l2_subdev *sd, V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -910,8 +910,8 @@ static int ov6650_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); + unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); int ret; if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) @@ -963,10 +963,10 @@ static int ov6650_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov6650 *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; } diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index e4a10751894d..5172ce9d8de1 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -667,9 +667,9 @@ static int ov772x_s_register(struct v4l2_subdev *sd, static int ov772x_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) @@ -1019,13 +1019,13 @@ static int ov772x_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1054,11 +1054,11 @@ static int ov772x_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov772x_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "OV772X: missing platform data!\n"); return -EINVAL; } @@ -1074,7 +1074,7 @@ static int ov772x_probe(struct i2c_client *client, if (!priv) return -ENOMEM; - priv->info = icl->priv; + priv->info = ssdd->drv_priv; v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); v4l2_ctrl_handler_init(&priv->hdl, 3); diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c index b323684eaf77..0ce212437bc1 100644 --- a/drivers/media/i2c/soc_camera/ov9640.c +++ b/drivers/media/i2c/soc_camera/ov9640.c @@ -336,9 +336,9 @@ static int ov9640_set_register(struct v4l2_subdev *sd, static int ov9640_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } /* select nearest higher resolution for capture */ @@ -657,13 +657,13 @@ static int ov9640_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -690,10 +690,10 @@ static int ov9640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9640_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; } diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c index 7a55889e397b..cdaaf5e140f8 100644 --- a/drivers/media/i2c/soc_camera/ov9740.c +++ b/drivers/media/i2c/soc_camera/ov9740.c @@ -787,12 +787,12 @@ static int ov9740_g_chip_ident(struct v4l2_subdev *sd, static int ov9740_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct ov9740_priv *priv = to_ov9740(sd); int ret; if (on) { - ret = soc_camera_power_on(&client->dev, icl); + ret = soc_camera_power_on(&client->dev, ssdd); if (ret < 0) return ret; @@ -806,7 +806,7 @@ static int ov9740_s_power(struct v4l2_subdev *sd, int on) priv->current_enable = true; } - soc_camera_power_off(&client->dev, icl); + soc_camera_power_off(&client->dev, ssdd); } return 0; @@ -905,13 +905,13 @@ static int ov9740_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -951,10 +951,10 @@ static int ov9740_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9740_priv *priv; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl) { + if (!ssdd) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; } diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c index 02f0400051d9..297e28817e45 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -1183,9 +1183,9 @@ static int rj54n1_s_register(struct v4l2_subdev *sd, static int rj54n1_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) @@ -1245,14 +1245,14 @@ static int rj54n1_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -1261,10 +1261,10 @@ static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ - if (soc_camera_apply_board_flags(icl, cfg) & + if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); else @@ -1334,17 +1334,17 @@ static int rj54n1_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct rj54n1 *rj54n1; - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct rj54n1_pdata *rj54n1_priv; int ret; - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); return -EINVAL; } - rj54n1_priv = icl->priv; + rj54n1_priv = ssdd->drv_priv; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_warn(&adapter->dev, @@ -1398,11 +1398,11 @@ static int rj54n1_probe(struct i2c_client *client, static int rj54n1_remove(struct i2c_client *client) { struct rj54n1 *rj54n1 = to_rj54n1(client); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); v4l2_device_unregister_subdev(&rj54n1->subdev); - if (icl->free_bus) - icl->free_bus(icl); + if (ssdd->free_bus) + ssdd->free_bus(ssdd); v4l2_ctrl_handler_free(&rj54n1->hdl); kfree(rj54n1); diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index 140716e71a15..cc34c5956419 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -569,9 +569,9 @@ static int tw9910_s_register(struct v4l2_subdev *sd, static int tw9910_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - return soc_camera_set_power(&client->dev, icl, on); + return soc_camera_set_power(&client->dev, ssdd, on); } static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height) @@ -847,14 +847,14 @@ static int tw9910_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | V4L2_MBUS_DATA_ACTIVE_HIGH; cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(icl, cfg); + cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); return 0; } @@ -863,9 +863,9 @@ static int tw9910_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); u8 val = VSSL_VVALID | HSSL_DVALID; - unsigned long flags = soc_camera_apply_board_flags(icl, cfg); + unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); /* * set OUTCTR1 @@ -911,15 +911,15 @@ static int tw9910_probe(struct i2c_client *client, struct tw9910_video_info *info; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; - if (!icl || !icl->priv) { + if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "TW9910: missing platform data!\n"); return -EINVAL; } - info = icl->priv; + info = ssdd->drv_priv; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index ae89f980e82b..dd057f19dbc3 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -50,22 +50,22 @@ static LIST_HEAD(hosts); static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */ -int soc_camera_power_on(struct device *dev, struct soc_camera_link *icl) +int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd) { - int ret = regulator_bulk_enable(icl->num_regulators, - icl->regulators); + int ret = regulator_bulk_enable(ssdd->num_regulators, + ssdd->regulators); if (ret < 0) { dev_err(dev, "Cannot enable regulators\n"); return ret; } - if (icl->power) { - ret = icl->power(dev, 1); + if (ssdd->power) { + ret = ssdd->power(dev, 1); if (ret < 0) { dev_err(dev, "Platform failed to power-on the camera.\n"); - regulator_bulk_disable(icl->num_regulators, - icl->regulators); + regulator_bulk_disable(ssdd->num_regulators, + ssdd->regulators); } } @@ -73,13 +73,13 @@ int soc_camera_power_on(struct device *dev, struct soc_camera_link *icl) } EXPORT_SYMBOL(soc_camera_power_on); -int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl) +int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd) { int ret = 0; int err; - if (icl->power) { - err = icl->power(dev, 0); + if (ssdd->power) { + err = ssdd->power(dev, 0); if (err < 0) { dev_err(dev, "Platform failed to power-off the camera.\n"); @@ -87,8 +87,8 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl) } } - err = regulator_bulk_disable(icl->num_regulators, - icl->regulators); + err = regulator_bulk_disable(ssdd->num_regulators, + ssdd->regulators); if (err < 0) { dev_err(dev, "Cannot disable regulators\n"); ret = ret ? : err; @@ -136,29 +136,29 @@ EXPORT_SYMBOL(soc_camera_xlate_by_fourcc); /** * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags - * @icl: camera platform parameters + * @ssdd: camera platform parameters * @cfg: media bus configuration * @return: resulting flags */ -unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl, +unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, const struct v4l2_mbus_config *cfg) { unsigned long f, flags = cfg->flags; /* If only one of the two polarities is supported, switch to the opposite */ - if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) { + if (ssdd->flags & SOCAM_SENSOR_INVERT_HSYNC) { f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW); if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW) flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW; } - if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) { + if (ssdd->flags & SOCAM_SENSOR_INVERT_VSYNC) { f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW); if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW) flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW; } - if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) { + if (ssdd->flags & SOCAM_SENSOR_INVERT_PCLK) { f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING); if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING) flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; @@ -509,7 +509,7 @@ static int soc_camera_open(struct file *file) { struct video_device *vdev = video_devdata(file); struct soc_camera_device *icd = dev_get_drvdata(vdev->parent); - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); struct soc_camera_host *ici; int ret; @@ -552,8 +552,8 @@ static int soc_camera_open(struct file *file) }; /* The camera could have been already on, try to reset */ - if (icl->reset) - icl->reset(icd->pdev); + if (sdesc->subdev_desc.reset) + sdesc->subdev_desc.reset(icd->pdev); ret = ici->ops->add(icd); if (ret < 0) { @@ -1072,23 +1072,24 @@ static void scan_add_host(struct soc_camera_host *ici) #ifdef CONFIG_I2C_BOARDINFO static int soc_camera_init_i2c(struct soc_camera_device *icd, - struct soc_camera_link *icl) + struct soc_camera_desc *sdesc) { struct i2c_client *client; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id); + struct soc_camera_host_desc *shd = &sdesc->host_desc; + struct i2c_adapter *adap = i2c_get_adapter(shd->i2c_adapter_id); struct v4l2_subdev *subdev; if (!adap) { dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", - icl->i2c_adapter_id); + shd->i2c_adapter_id); goto ei2cga; } - icl->board_info->platform_data = icl; + shd->board_info->platform_data = &sdesc->subdev_desc; subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, - icl->board_info, NULL); + shd->board_info, NULL); if (!subdev) goto ei2cnd; @@ -1116,7 +1117,7 @@ static void soc_camera_free_i2c(struct soc_camera_device *icd) i2c_put_adapter(adap); } #else -#define soc_camera_init_i2c(icd, icl) (-ENODEV) +#define soc_camera_init_i2c(icd, sdesc) (-ENODEV) #define soc_camera_free_i2c(icd) do {} while (0) #endif @@ -1126,7 +1127,9 @@ static int video_dev_create(struct soc_camera_device *icd); static int soc_camera_probe(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_host_desc *shd = &sdesc->host_desc; + struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; struct device *control = NULL; struct v4l2_subdev *sd; struct v4l2_mbus_framefmt mf; @@ -1146,8 +1149,8 @@ static int soc_camera_probe(struct soc_camera_device *icd) return ret; /* The camera could have been already on, try to reset */ - if (icl->reset) - icl->reset(icd->pdev); + if (ssdd->reset) + ssdd->reset(icd->pdev); mutex_lock(&ici->host_lock); ret = ici->ops->add(icd); @@ -1161,18 +1164,18 @@ static int soc_camera_probe(struct soc_camera_device *icd) goto evdc; /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ - if (icl->board_info) { - ret = soc_camera_init_i2c(icd, icl); + if (shd->board_info) { + ret = soc_camera_init_i2c(icd, sdesc); if (ret < 0) goto eadddev; - } else if (!icl->add_device || !icl->del_device) { + } else if (!shd->add_device || !shd->del_device) { ret = -EINVAL; goto eadddev; } else { - if (icl->module_name) - ret = request_module(icl->module_name); + if (shd->module_name) + ret = request_module(shd->module_name); - ret = icl->add_device(icd); + ret = shd->add_device(icd); if (ret < 0) goto eadddev; @@ -1183,7 +1186,7 @@ static int soc_camera_probe(struct soc_camera_device *icd) control = to_soc_camera_control(icd); if (!control || !control->driver || !dev_get_drvdata(control) || !try_module_get(control->driver->owner)) { - icl->del_device(icd); + shd->del_device(icd); ret = -ENODEV; goto enodrv; } @@ -1234,10 +1237,10 @@ evidstart: soc_camera_free_user_formats(icd); eiufmt: ectrl: - if (icl->board_info) { + if (shd->board_info) { soc_camera_free_i2c(icd); } else { - icl->del_device(icd); + shd->del_device(icd); module_put(control->driver->owner); } enodrv: @@ -1259,7 +1262,7 @@ eadd: */ static int soc_camera_remove(struct soc_camera_device *icd) { - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); struct video_device *vdev = icd->vdev; BUG_ON(!icd->parent); @@ -1270,12 +1273,12 @@ static int soc_camera_remove(struct soc_camera_device *icd) icd->vdev = NULL; } - if (icl->board_info) { + if (sdesc->host_desc.board_info) { soc_camera_free_i2c(icd); } else { struct device_driver *drv = to_soc_camera_control(icd)->driver; if (drv) { - icl->del_device(icd); + sdesc->host_desc.del_device(icd); module_put(drv->owner); } } @@ -1534,24 +1537,25 @@ static int soc_camera_video_start(struct soc_camera_device *icd) static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) { - struct soc_camera_link *icl = pdev->dev.platform_data; + struct soc_camera_desc *sdesc = pdev->dev.platform_data; + struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; struct soc_camera_device *icd; int ret; - if (!icl) + if (!sdesc) return -EINVAL; icd = devm_kzalloc(&pdev->dev, sizeof(*icd), GFP_KERNEL); if (!icd) return -ENOMEM; - ret = devm_regulator_bulk_get(&pdev->dev, icl->num_regulators, - icl->regulators); + ret = devm_regulator_bulk_get(&pdev->dev, ssdd->num_regulators, + ssdd->regulators); if (ret < 0) return ret; - icd->iface = icl->bus_id; - icd->link = icl; + icd->iface = sdesc->host_desc.bus_id; + icd->sdesc = sdesc; icd->pdev = &pdev->dev; platform_set_drvdata(pdev, icd); diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index 7cf7fd16481f..51e29d19b1b4 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -54,7 +54,7 @@ static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - return soc_camera_set_power(p->icd->control, p->icd->link, on); + return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, on); } static struct v4l2_subdev_core_ops platform_subdev_core_ops = { diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 5a662c981484..2cc70cf318bf 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -23,11 +23,11 @@ #include struct file; -struct soc_camera_link; +struct soc_camera_desc; struct soc_camera_device { struct list_head list; /* list of all registered devices */ - struct soc_camera_link *link; + struct soc_camera_desc *sdesc; struct device *pdev; /* Platform device */ struct device *parent; /* Camera host device */ struct device *control; /* E.g., the i2c client */ @@ -116,26 +116,72 @@ struct soc_camera_host_ops { struct i2c_board_info; struct regulator_bulk_data; -struct soc_camera_link { - /* Camera bus id, used to match a camera and a bus */ - int bus_id; +struct soc_camera_subdev_desc { /* Per camera SOCAM_SENSOR_* bus flags */ unsigned long flags; - int i2c_adapter_id; - struct i2c_board_info *board_info; - const char *module_name; - void *priv; + + /* sensor driver private platform data */ + void *drv_priv; /* Optional regulators that have to be managed on power on/off events */ struct regulator_bulk_data *regulators; int num_regulators; + /* Optional callbacks to power on or off and reset the sensor */ + int (*power)(struct device *, int); + int (*reset)(struct device *); + + /* + * some platforms may support different data widths than the sensors + * native ones due to different data line routing. Let the board code + * overwrite the width flags. + */ + int (*set_bus_param)(struct soc_camera_subdev_desc *, unsigned long flags); + unsigned long (*query_bus_param)(struct soc_camera_subdev_desc *); + void (*free_bus)(struct soc_camera_subdev_desc *); +}; + +struct soc_camera_host_desc { + /* Camera bus id, used to match a camera and a bus */ + int bus_id; + int i2c_adapter_id; + struct i2c_board_info *board_info; + const char *module_name; + /* * For non-I2C devices platform has to provide methods to add a device * to the system and to remove it */ int (*add_device)(struct soc_camera_device *); void (*del_device)(struct soc_camera_device *); +}; + +/* + * This MUST be kept binary-identical to struct soc_camera_link below, until + * it is completely replaced by this one, after which we can split it into its + * two components. + */ +struct soc_camera_desc { + struct soc_camera_subdev_desc subdev_desc; + struct soc_camera_host_desc host_desc; +}; + +/* Prepare to replace this struct: don't change its layout any more! */ +struct soc_camera_link { + /* + * Subdevice part - keep at top and compatible to + * struct soc_camera_subdev_desc + */ + + /* Per camera SOCAM_SENSOR_* bus flags */ + unsigned long flags; + + void *priv; + + /* Optional regulators that have to be managed on power on/off events */ + struct regulator_bulk_data *regulators; + int num_regulators; + /* Optional callbacks to power on or off and reset the sensor */ int (*power)(struct device *, int); int (*reset)(struct device *); @@ -147,6 +193,24 @@ struct soc_camera_link { int (*set_bus_param)(struct soc_camera_link *, unsigned long flags); unsigned long (*query_bus_param)(struct soc_camera_link *); void (*free_bus)(struct soc_camera_link *); + + /* + * Host part - keep at bottom and compatible to + * struct soc_camera_host_desc + */ + + /* Camera bus id, used to match a camera and a bus */ + int bus_id; + int i2c_adapter_id; + struct i2c_board_info *board_info; + const char *module_name; + + /* + * For non-I2C devices platform has to provide methods to add a device + * to the system and to remove it + */ + int (*add_device)(struct soc_camera_device *); + void (*del_device)(struct soc_camera_device *); }; static inline struct soc_camera_host *to_soc_camera_host( @@ -157,10 +221,10 @@ static inline struct soc_camera_host *to_soc_camera_host( return container_of(v4l2_dev, struct soc_camera_host, v4l2_dev); } -static inline struct soc_camera_link *to_soc_camera_link( +static inline struct soc_camera_desc *to_soc_camera_desc( const struct soc_camera_device *icd) { - return icd->link; + return icd->sdesc; } static inline struct device *to_soc_camera_control( @@ -250,19 +314,17 @@ static inline void soc_camera_limit_side(int *start, int *length, *start = start_min + length_max - *length; } -unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl, - unsigned long flags); -unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl, +unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, const struct v4l2_mbus_config *cfg); -int soc_camera_power_on(struct device *dev, struct soc_camera_link *icl); -int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl); +int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd); +int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd); static inline int soc_camera_set_power(struct device *dev, - struct soc_camera_link *icl, bool on) + struct soc_camera_subdev_desc *ssdd, bool on) { - return on ? soc_camera_power_on(dev, icl) - : soc_camera_power_off(dev, icl); + return on ? soc_camera_power_on(dev, ssdd) + : soc_camera_power_off(dev, ssdd); } /* This is only temporary here - until v4l2-subdev begins to link to video_device */ @@ -274,7 +336,7 @@ static inline struct video_device *soc_camera_i2c_to_vdev(const struct i2c_clien return icd ? icd->vdev : NULL; } -static inline struct soc_camera_link *soc_camera_i2c_to_link(const struct i2c_client *client) +static inline struct soc_camera_subdev_desc *soc_camera_i2c_to_desc(const struct i2c_client *client) { return client->dev.platform_data; } diff --git a/include/media/soc_camera_platform.h b/include/media/soc_camera_platform.h index 8aa4200a0b1d..1e5065dab430 100644 --- a/include/media/soc_camera_platform.h +++ b/include/media/soc_camera_platform.h @@ -38,10 +38,12 @@ static inline int soc_camera_platform_add(struct soc_camera_device *icd, void (*release)(struct device *dev), int id) { - struct soc_camera_platform_info *info = plink->priv; + struct soc_camera_subdev_desc *ssdd = + (struct soc_camera_subdev_desc *)plink; + struct soc_camera_platform_info *info = ssdd->drv_priv; int ret; - if (icd->link != plink) + if (&icd->sdesc->subdev_desc != ssdd) return -ENODEV; if (*pdev) @@ -70,7 +72,9 @@ static inline void soc_camera_platform_del(const struct soc_camera_device *icd, struct platform_device *pdev, const struct soc_camera_link *plink) { - if (icd->link != plink || !pdev) + const struct soc_camera_subdev_desc *ssdd = + (const struct soc_camera_subdev_desc *)plink; + if (&icd->sdesc->subdev_desc != ssdd || !pdev) return; platform_device_unregister(pdev); From 70e176a5a9839ea22f0fbcfa21d1c8ae952a0dd2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 21 Dec 2012 10:28:43 -0300 Subject: [PATCH 0550/4607] [media] soc-camera: use devm_kzalloc in subdevice drivers I2C drivers can use devm_kzalloc() too in their .probe() methods. Doing so simplifies their clean up paths. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/imx074.c | 13 ++--------- drivers/media/i2c/soc_camera/mt9m001.c | 14 ++++-------- drivers/media/i2c/soc_camera/mt9m111.c | 15 ++++--------- drivers/media/i2c/soc_camera/mt9t031.c | 14 ++++-------- drivers/media/i2c/soc_camera/mt9t112.c | 9 ++------ drivers/media/i2c/soc_camera/mt9v022.c | 8 ++----- drivers/media/i2c/soc_camera/ov2640.c | 17 +++++--------- drivers/media/i2c/soc_camera/ov5642.c | 15 ++----------- drivers/media/i2c/soc_camera/ov6650.c | 14 ++++-------- drivers/media/i2c/soc_camera/ov772x.c | 22 ++++++------------- drivers/media/i2c/soc_camera/ov9640.c | 15 ++++--------- drivers/media/i2c/soc_camera/ov9740.c | 15 ++++--------- drivers/media/i2c/soc_camera/rj54n1cb0c.c | 15 ++++--------- drivers/media/i2c/soc_camera/tw9910.c | 12 ++-------- .../platform/soc_camera/soc_camera_platform.c | 4 +--- 15 files changed, 51 insertions(+), 151 deletions(-) diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c index 1c065717cdd1..a2a5cbbdbe28 100644 --- a/drivers/media/i2c/soc_camera/imx074.c +++ b/drivers/media/i2c/soc_camera/imx074.c @@ -431,7 +431,6 @@ static int imx074_probe(struct i2c_client *client, struct imx074 *priv; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; if (!ssdd) { dev_err(&client->dev, "IMX074: missing platform data!\n"); @@ -444,7 +443,7 @@ static int imx074_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(struct imx074), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -452,23 +451,15 @@ static int imx074_probe(struct i2c_client *client, priv->fmt = &imx074_colour_fmts[0]; - ret = imx074_video_probe(client); - if (ret < 0) { - kfree(priv); - return ret; - } - - return ret; + return imx074_video_probe(client); } static int imx074_remove(struct i2c_client *client) { - struct imx074 *priv = to_imx074(client); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); if (ssdd->free_bus) ssdd->free_bus(ssdd); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index 9ae7066798eb..bcdc86175549 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -677,7 +677,7 @@ static int mt9m001_probe(struct i2c_client *client, return -EIO; } - mt9m001 = kzalloc(sizeof(struct mt9m001), GFP_KERNEL); + mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL); if (!mt9m001) return -ENOMEM; @@ -697,12 +697,9 @@ static int mt9m001_probe(struct i2c_client *client, &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); mt9m001->subdev.ctrl_handler = &mt9m001->hdl; - if (mt9m001->hdl.error) { - int err = mt9m001->hdl.error; + if (mt9m001->hdl.error) + return mt9m001->hdl.error; - kfree(mt9m001); - return err; - } v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure, V4L2_EXPOSURE_MANUAL, true); @@ -714,10 +711,8 @@ static int mt9m001_probe(struct i2c_client *client, mt9m001->rect.height = MT9M001_MAX_HEIGHT; ret = mt9m001_video_probe(ssdd, client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&mt9m001->hdl); - kfree(mt9m001); - } return ret; } @@ -730,7 +725,6 @@ static int mt9m001_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&mt9m001->subdev); v4l2_ctrl_handler_free(&mt9m001->hdl); mt9m001_video_remove(ssdd); - kfree(mt9m001); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c index 78511669bab0..bbc4ff99603c 100644 --- a/drivers/media/i2c/soc_camera/mt9m111.c +++ b/drivers/media/i2c/soc_camera/mt9m111.c @@ -971,7 +971,7 @@ static int mt9m111_probe(struct i2c_client *client, return -EIO; } - mt9m111 = kzalloc(sizeof(struct mt9m111), GFP_KERNEL); + mt9m111 = devm_kzalloc(&client->dev, sizeof(struct mt9m111), GFP_KERNEL); if (!mt9m111) return -ENOMEM; @@ -989,12 +989,8 @@ static int mt9m111_probe(struct i2c_client *client, &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO); mt9m111->subdev.ctrl_handler = &mt9m111->hdl; - if (mt9m111->hdl.error) { - int err = mt9m111->hdl.error; - - kfree(mt9m111); - return err; - } + if (mt9m111->hdl.error) + return mt9m111->hdl.error; /* Second stage probe - when a capture adapter is there */ mt9m111->rect.left = MT9M111_MIN_DARK_COLS; @@ -1006,10 +1002,8 @@ static int mt9m111_probe(struct i2c_client *client, mutex_init(&mt9m111->power_lock); ret = mt9m111_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&mt9m111->hdl); - kfree(mt9m111); - } return ret; } @@ -1020,7 +1014,6 @@ static int mt9m111_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&mt9m111->subdev); v4l2_ctrl_handler_free(&mt9m111->hdl); - kfree(mt9m111); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index 9ca6d65cefaf..d80d044ebf15 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -773,7 +773,7 @@ static int mt9t031_probe(struct i2c_client *client, return -EIO; } - mt9t031 = kzalloc(sizeof(struct mt9t031), GFP_KERNEL); + mt9t031 = devm_kzalloc(&client->dev, sizeof(struct mt9t031), GFP_KERNEL); if (!mt9t031) return -ENOMEM; @@ -797,12 +797,9 @@ static int mt9t031_probe(struct i2c_client *client, V4L2_CID_EXPOSURE, 1, 255, 1, 255); mt9t031->subdev.ctrl_handler = &mt9t031->hdl; - if (mt9t031->hdl.error) { - int err = mt9t031->hdl.error; + if (mt9t031->hdl.error) + return mt9t031->hdl.error; - kfree(mt9t031); - return err; - } v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure, V4L2_EXPOSURE_MANUAL, true); @@ -816,10 +813,8 @@ static int mt9t031_probe(struct i2c_client *client, mt9t031->yskip = 1; ret = mt9t031_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&mt9t031->hdl); - kfree(mt9t031); - } return ret; } @@ -830,7 +825,6 @@ static int mt9t031_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&mt9t031->subdev); v4l2_ctrl_handler_free(&mt9t031->hdl); - kfree(mt9t031); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index 92bb65de6cd9..c75d83168700 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -1092,7 +1092,7 @@ static int mt9t112_probe(struct i2c_client *client, return -EINVAL; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1101,10 +1101,8 @@ static int mt9t112_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); ret = mt9t112_camera_probe(client); - if (ret) { - kfree(priv); + if (ret) return ret; - } /* Cannot fail: using the default supported pixel code */ mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8); @@ -1114,9 +1112,6 @@ static int mt9t112_probe(struct i2c_client *client, static int mt9t112_remove(struct i2c_client *client) { - struct mt9t112_priv *priv = to_mt9t112(client); - - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index 33fb5c3b52b4..a5e65d6a0781 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -889,7 +889,7 @@ static int mt9v022_probe(struct i2c_client *client, return -EIO; } - mt9v022 = kzalloc(sizeof(struct mt9v022), GFP_KERNEL); + mt9v022 = devm_kzalloc(&client->dev, sizeof(struct mt9v022), GFP_KERNEL); if (!mt9v022) return -ENOMEM; @@ -930,7 +930,6 @@ static int mt9v022_probe(struct i2c_client *client, int err = mt9v022->hdl.error; dev_err(&client->dev, "control initialisation err %d\n", err); - kfree(mt9v022); return err; } v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, @@ -950,10 +949,8 @@ static int mt9v022_probe(struct i2c_client *client, mt9v022->rect.height = MT9V022_MAX_HEIGHT; ret = mt9v022_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&mt9v022->hdl); - kfree(mt9v022); - } return ret; } @@ -967,7 +964,6 @@ static int mt9v022_remove(struct i2c_client *client) if (ssdd->free_bus) ssdd->free_bus(ssdd); v4l2_ctrl_handler_free(&mt9v022->hdl); - kfree(mt9v022); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c index c57a5095edef..0f520f693b6e 100644 --- a/drivers/media/i2c/soc_camera/ov2640.c +++ b/drivers/media/i2c/soc_camera/ov2640.c @@ -1096,7 +1096,7 @@ static int ov2640_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(struct ov2640_priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov2640_priv), GFP_KERNEL); if (!priv) { dev_err(&adapter->dev, "Failed to allocate memory for private data!\n"); @@ -1110,20 +1110,14 @@ static int ov2640_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; - - kfree(priv); - return err; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov2640_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } else { + else dev_info(&adapter->dev, "OV2640 Probed\n"); - } return ret; } @@ -1134,7 +1128,6 @@ static int ov2640_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index b892d01e57ae..9d53309619d2 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -1021,14 +1021,13 @@ static int ov5642_probe(struct i2c_client *client, { struct ov5642 *priv; struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; if (!ssdd) { dev_err(&client->dev, "OV5642: missing platform data!\n"); return -EINVAL; } - priv = kzalloc(sizeof(struct ov5642), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov5642), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1043,25 +1042,15 @@ static int ov5642_probe(struct i2c_client *client, priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; priv->total_height = BLANKING_MIN_HEIGHT; - ret = ov5642_video_probe(client); - if (ret < 0) - goto error; - - return 0; - -error: - kfree(priv); - return ret; + return ov5642_video_probe(client); } static int ov5642_remove(struct i2c_client *client) { - struct ov5642 *priv = to_ov5642(client); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); if (ssdd->free_bus) ssdd->free_bus(ssdd); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index 1ae8b8dd268b..dbe4f564e6b2 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -971,7 +971,7 @@ static int ov6650_probe(struct i2c_client *client, return -EINVAL; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "Failed to allocate memory for private data!\n"); @@ -1009,12 +1009,9 @@ static int ov6650_probe(struct i2c_client *client, V4L2_CID_GAMMA, 0, 0xff, 1, 0x12); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; + if (priv->hdl.error) + return priv->hdl.error; - kfree(priv); - return err; - } v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true); v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true); v4l2_ctrl_auto_cluster(2, &priv->autoexposure, @@ -1029,10 +1026,8 @@ static int ov6650_probe(struct i2c_client *client, priv->colorspace = V4L2_COLORSPACE_JPEG; ret = ov6650_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } return ret; } @@ -1043,7 +1038,6 @@ static int ov6650_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index 5172ce9d8de1..fbeb5b2f3ae5 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -1070,7 +1070,7 @@ static int ov772x_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1085,22 +1085,15 @@ static int ov772x_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - ret = priv->hdl.error; - goto done; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov772x_video_probe(priv); - if (ret < 0) - goto done; - - priv->cfmt = &ov772x_cfmts[0]; - priv->win = &ov772x_win_sizes[0]; - -done: - if (ret) { + if (ret < 0) { v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); + } else { + priv->cfmt = &ov772x_cfmts[0]; + priv->win = &ov772x_win_sizes[0]; } return ret; } @@ -1111,7 +1104,6 @@ static int ov772x_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c index 0ce212437bc1..05993041be31 100644 --- a/drivers/media/i2c/soc_camera/ov9640.c +++ b/drivers/media/i2c/soc_camera/ov9640.c @@ -698,7 +698,7 @@ static int ov9640_probe(struct i2c_client *client, return -EINVAL; } - priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov9640_priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "Failed to allocate memory for private data!\n"); @@ -713,19 +713,13 @@ static int ov9640_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; - - kfree(priv); - return err; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov9640_video_probe(client); - if (ret) { + if (ret) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } return ret; } @@ -737,7 +731,6 @@ static int ov9640_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c index cdaaf5e140f8..2f236da80165 100644 --- a/drivers/media/i2c/soc_camera/ov9740.c +++ b/drivers/media/i2c/soc_camera/ov9740.c @@ -959,7 +959,7 @@ static int ov9740_probe(struct i2c_client *client, return -EINVAL; } - priv = kzalloc(sizeof(struct ov9740_priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(struct ov9740_priv), GFP_KERNEL); if (!priv) { dev_err(&client->dev, "Failed to allocate private data!\n"); return -ENOMEM; @@ -972,18 +972,12 @@ static int ov9740_probe(struct i2c_client *client, v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); priv->subdev.ctrl_handler = &priv->hdl; - if (priv->hdl.error) { - int err = priv->hdl.error; - - kfree(priv); - return err; - } + if (priv->hdl.error) + return priv->hdl.error; ret = ov9740_video_probe(client); - if (ret < 0) { + if (ret < 0) v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); - } return ret; } @@ -994,7 +988,6 @@ static int ov9740_remove(struct i2c_client *client) v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); - kfree(priv); return 0; } diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c index 297e28817e45..5c92679bfefb 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -1352,7 +1352,7 @@ static int rj54n1_probe(struct i2c_client *client, return -EIO; } - rj54n1 = kzalloc(sizeof(struct rj54n1), GFP_KERNEL); + rj54n1 = devm_kzalloc(&client->dev, sizeof(struct rj54n1), GFP_KERNEL); if (!rj54n1) return -ENOMEM; @@ -1367,12 +1367,8 @@ static int rj54n1_probe(struct i2c_client *client, v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); rj54n1->subdev.ctrl_handler = &rj54n1->hdl; - if (rj54n1->hdl.error) { - int err = rj54n1->hdl.error; - - kfree(rj54n1); - return err; - } + if (rj54n1->hdl.error) + return rj54n1->hdl.error; rj54n1->clk_div = clk_div; rj54n1->rect.left = RJ54N1_COLUMN_SKIP; @@ -1387,10 +1383,8 @@ static int rj54n1_probe(struct i2c_client *client, (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); ret = rj54n1_video_probe(client, rj54n1_priv); - if (ret < 0) { + if (ret < 0) v4l2_ctrl_handler_free(&rj54n1->hdl); - kfree(rj54n1); - } return ret; } @@ -1404,7 +1398,6 @@ static int rj54n1_remove(struct i2c_client *client) if (ssdd->free_bus) ssdd->free_bus(ssdd); v4l2_ctrl_handler_free(&rj54n1->hdl); - kfree(rj54n1); return 0; } diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index cc34c5956419..7d2074601881 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -912,7 +912,6 @@ static int tw9910_probe(struct i2c_client *client, struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - int ret; if (!ssdd || !ssdd->drv_priv) { dev_err(&client->dev, "TW9910: missing platform data!\n"); @@ -928,7 +927,7 @@ static int tw9910_probe(struct i2c_client *client, return -EIO; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -936,18 +935,11 @@ static int tw9910_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); - ret = tw9910_video_probe(client); - if (ret) - kfree(priv); - - return ret; + return tw9910_video_probe(client); } static int tw9910_remove(struct i2c_client *client) { - struct tw9910_priv *priv = to_tw9910(client); - - kfree(priv); return 0; } diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index 51e29d19b1b4..ce3b1d6a4734 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -148,7 +148,7 @@ static int soc_camera_platform_probe(struct platform_device *pdev) return -EINVAL; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -173,7 +173,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev) evdrs: platform_set_drvdata(pdev, NULL); - kfree(priv); return ret; } @@ -185,7 +184,6 @@ static int soc_camera_platform_remove(struct platform_device *pdev) p->icd->control = NULL; v4l2_device_unregister_subdev(&priv->subdev); platform_set_drvdata(pdev, NULL); - kfree(priv); return 0; } From 9b556953224080d81754bfe764ab1899fc069edc Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 30 Oct 2012 11:28:59 -0300 Subject: [PATCH 0551/4607] [media] mx2_camera: Remove i.mx25 support i.MX25 support has been broken for several releases now and nobody seems to care about it. Signed-off-by: Javier Martin [g.liakhovetski@gmx.de: rebased on top of cpu_is_mx27() removal] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 7 +- .../media/platform/soc_camera/mx2_camera.c | 488 +++++------------- 2 files changed, 121 insertions(+), 374 deletions(-) diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index cb6791e62bd4..b139b525bb16 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -70,13 +70,12 @@ config VIDEO_MX2_HOSTSUPPORT bool config VIDEO_MX2 - tristate "i.MX27/i.MX25 Camera Sensor Interface driver" - depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || (ARCH_MX25 && BROKEN)) + tristate "i.MX27 Camera Sensor Interface driver" + depends on VIDEO_DEV && SOC_CAMERA && MACH_MX27 select VIDEOBUF2_DMA_CONTIG select VIDEO_MX2_HOSTSUPPORT ---help--- - This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor - Interface + This is a v4l2 driver for the i.MX27 Camera Sensor Interface config VIDEO_ATMEL_ISI tristate "ATMEL Image Sensor Interface (ISI) support" diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index bf2740f7c4d1..c0511c001be1 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -1,5 +1,5 @@ /* - * V4L2 Driver for i.MX27/i.MX25 camera host + * V4L2 Driver for i.MX27 camera host * * Copyright (C) 2008, Sascha Hauer, Pengutronix * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography @@ -63,9 +63,7 @@ #define CSICR1_RF_OR_INTEN (1 << 24) #define CSICR1_STATFF_LEVEL (3 << 22) #define CSICR1_STATFF_INTEN (1 << 21) -#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) /* MX27 */ -#define CSICR1_FB2_DMA_INTEN (1 << 20) /* MX25 */ -#define CSICR1_FB1_DMA_INTEN (1 << 19) /* MX25 */ +#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) #define CSICR1_RXFF_INTEN (1 << 18) #define CSICR1_SOF_POL (1 << 17) #define CSICR1_SOF_INTEN (1 << 16) @@ -87,45 +85,15 @@ #define SHIFT_RXFF_LEVEL 19 #define SHIFT_MCLKDIV 12 -/* control reg 3 */ -#define CSICR3_FRMCNT (0xFFFF << 16) -#define CSICR3_FRMCNT_RST (1 << 15) -#define CSICR3_DMA_REFLASH_RFF (1 << 14) -#define CSICR3_DMA_REFLASH_SFF (1 << 13) -#define CSICR3_DMA_REQ_EN_RFF (1 << 12) -#define CSICR3_DMA_REQ_EN_SFF (1 << 11) -#define CSICR3_RXFF_LEVEL(l) (((l) & 7) << 4) /* MX25 */ -#define CSICR3_CSI_SUP (1 << 3) -#define CSICR3_ZERO_PACK_EN (1 << 2) -#define CSICR3_ECC_INT_EN (1 << 1) -#define CSICR3_ECC_AUTO_EN (1 << 0) - #define SHIFT_FRMCNT 16 -/* csi status reg */ -#define CSISR_SFF_OR_INT (1 << 25) -#define CSISR_RFF_OR_INT (1 << 24) -#define CSISR_STATFF_INT (1 << 21) -#define CSISR_DMA_TSF_FB2_INT (1 << 20) /* MX25 */ -#define CSISR_DMA_TSF_FB1_INT (1 << 19) /* MX25 */ -#define CSISR_RXFF_INT (1 << 18) -#define CSISR_EOF_INT (1 << 17) -#define CSISR_SOF_INT (1 << 16) -#define CSISR_F2_INT (1 << 15) -#define CSISR_F1_INT (1 << 14) -#define CSISR_COF_INT (1 << 13) -#define CSISR_ECC_INT (1 << 1) -#define CSISR_DRDY (1 << 0) - #define CSICR1 0x00 #define CSICR2 0x04 -#define CSISR_IMX25 0x18 -#define CSISR_IMX27 0x08 +#define CSISR 0x08 #define CSISTATFIFO 0x0c #define CSIRFIFO 0x10 #define CSIRXCNT 0x14 -#define CSICR3_IMX25 0x08 -#define CSICR3_IMX27 0x1c +#define CSICR3 0x1c #define CSIDMASA_STATFIFO 0x20 #define CSIDMATA_STATFIFO 0x24 #define CSIDMASA_FB1 0x28 @@ -269,7 +237,6 @@ struct mx2_buffer { }; enum mx2_camera_type { - IMX25_CAMERA, IMX27_CAMERA, }; @@ -297,8 +264,6 @@ struct mx2_camera_dev { struct mx2_buffer *fb2_active; u32 csicr1; - u32 reg_csisr; - u32 reg_csicr3; enum mx2_camera_type devtype; struct mx2_buf_internal buf_discard[2]; @@ -314,9 +279,6 @@ struct mx2_camera_dev { static struct platform_device_id mx2_camera_devtype[] = { { - .name = "imx25-camera", - .driver_data = IMX25_CAMERA, - }, { .name = "imx27-camera", .driver_data = IMX27_CAMERA, }, { @@ -325,16 +287,6 @@ static struct platform_device_id mx2_camera_devtype[] = { }; MODULE_DEVICE_TABLE(platform, mx2_camera_devtype); -static inline int is_imx25_camera(struct mx2_camera_dev *pcdev) -{ - return pcdev->devtype == IMX25_CAMERA; -} - -static inline int is_imx27_camera(struct mx2_camera_dev *pcdev) -{ - return pcdev->devtype == IMX27_CAMERA; -} - static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf) { return container_of(int_buf, struct mx2_buffer, internal); @@ -462,21 +414,10 @@ static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev, static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) { - unsigned long flags; - clk_disable_unprepare(pcdev->clk_csi_ahb); clk_disable_unprepare(pcdev->clk_csi_per); writel(0, pcdev->base_csi + CSICR1); - if (is_imx27_camera(pcdev)) { - writel(0, pcdev->base_emma + PRP_CNTL); - } else if (is_imx25_camera(pcdev)) { - spin_lock_irqsave(&pcdev->lock, flags); - pcdev->fb1_active = NULL; - pcdev->fb2_active = NULL; - writel(0, pcdev->base_csi + CSIDMASA_FB1); - writel(0, pcdev->base_csi + CSIDMASA_FB2); - spin_unlock_irqrestore(&pcdev->lock, flags); - } + writel(0, pcdev->base_emma + PRP_CNTL); } /* @@ -501,11 +442,8 @@ static int mx2_camera_add_device(struct soc_camera_device *icd) if (ret < 0) goto exit_csi_ahb; - csicr1 = CSICR1_MCLKEN; - - if (is_imx27_camera(pcdev)) - csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC | - CSICR1_RXFF_LEVEL(0); + csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC | + CSICR1_RXFF_LEVEL(0); pcdev->csicr1 = csicr1; writel(pcdev->csicr1, pcdev->base_csi + CSICR1); @@ -539,65 +477,6 @@ static void mx2_camera_remove_device(struct soc_camera_device *icd) pcdev->icd = NULL; } -static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb, - int state) -{ - struct vb2_buffer *vb; - struct mx2_buffer *buf; - struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active : - &pcdev->fb2_active; - u32 fb_reg = fb == 1 ? CSIDMASA_FB1 : CSIDMASA_FB2; - unsigned long flags; - - spin_lock_irqsave(&pcdev->lock, flags); - - if (*fb_active == NULL) - goto out; - - vb = &(*fb_active)->vb; - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - vb->v4l2_buf.sequence++; - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - - if (list_empty(&pcdev->capture)) { - buf = NULL; - writel(0, pcdev->base_csi + fb_reg); - } else { - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - vb = &buf->vb; - list_del(&buf->internal.queue); - buf->state = MX2_STATE_ACTIVE; - writel(vb2_dma_contig_plane_dma_addr(vb, 0), - pcdev->base_csi + fb_reg); - } - - *fb_active = buf; - -out: - spin_unlock_irqrestore(&pcdev->lock, flags); -} - -static irqreturn_t mx25_camera_irq(int irq_csi, void *data) -{ - struct mx2_camera_dev *pcdev = data; - u32 status = readl(pcdev->base_csi + pcdev->reg_csisr); - - if (status & CSISR_DMA_TSF_FB1_INT) - mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE); - else if (status & CSISR_DMA_TSF_FB2_INT) - mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE); - - /* FIXME: handle CSISR_RFF_OR_INT */ - - writel(status, pcdev->base_csi + pcdev->reg_csisr); - - return IRQ_HANDLED; -} - /* * Videobuf operations */ @@ -678,54 +557,17 @@ static void mx2_videobuf_queue(struct vb2_buffer *vb) buf->state = MX2_STATE_QUEUED; list_add_tail(&buf->internal.queue, &pcdev->capture); - if (is_imx25_camera(pcdev)) { - u32 csicr3, dma_inten = 0; - - if (pcdev->fb1_active == NULL) { - writel(vb2_dma_contig_plane_dma_addr(vb, 0), - pcdev->base_csi + CSIDMASA_FB1); - pcdev->fb1_active = buf; - dma_inten = CSICR1_FB1_DMA_INTEN; - } else if (pcdev->fb2_active == NULL) { - writel(vb2_dma_contig_plane_dma_addr(vb, 0), - pcdev->base_csi + CSIDMASA_FB2); - pcdev->fb2_active = buf; - dma_inten = CSICR1_FB2_DMA_INTEN; - } - - if (dma_inten) { - list_del(&buf->internal.queue); - buf->state = MX2_STATE_ACTIVE; - - csicr3 = readl(pcdev->base_csi + pcdev->reg_csicr3); - - /* Reflash DMA */ - writel(csicr3 | CSICR3_DMA_REFLASH_RFF, - pcdev->base_csi + pcdev->reg_csicr3); - - /* clear & enable interrupts */ - writel(dma_inten, pcdev->base_csi + pcdev->reg_csisr); - pcdev->csicr1 |= dma_inten; - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - - /* enable DMA */ - csicr3 |= CSICR3_DMA_REQ_EN_RFF | CSICR3_RXFF_LEVEL(1); - writel(csicr3, pcdev->base_csi + pcdev->reg_csicr3); - } - } - spin_unlock_irqrestore(&pcdev->lock, flags); } static void mx2_videobuf_release(struct vb2_buffer *vb) { +#ifdef DEBUG struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); - unsigned long flags; -#ifdef DEBUG dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); @@ -744,29 +586,11 @@ static void mx2_videobuf_release(struct vb2_buffer *vb) #endif /* - * Terminate only queued but inactive buffers. Active buffers are - * released when they become inactive after videobuf_waiton(). - * * FIXME: implement forced termination of active buffers for mx27 and * mx27 eMMA, so that the user won't get stuck in an uninterruptible * state. This requires a specific handling for each of the these DMA * types. */ - - spin_lock_irqsave(&pcdev->lock, flags); - if (is_imx25_camera(pcdev) && buf->state == MX2_STATE_ACTIVE) { - if (pcdev->fb1_active == buf) { - pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN; - writel(0, pcdev->base_csi + CSIDMASA_FB1); - pcdev->fb1_active = NULL; - } else if (pcdev->fb2_active == buf) { - pcdev->csicr1 &= ~CSICR1_FB2_DMA_INTEN; - writel(0, pcdev->base_csi + CSIDMASA_FB2); - pcdev->fb2_active = NULL; - } - writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - } - spin_unlock_irqrestore(&pcdev->lock, flags); } static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, @@ -876,92 +700,90 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) struct mx2_buffer *buf; unsigned long phys; int bytesperline; + unsigned long flags; - if (is_imx27_camera(pcdev)) { - unsigned long flags; - if (count < 2) - return -EINVAL; + if (count < 2) + return -EINVAL; - spin_lock_irqsave(&pcdev->lock, flags); + spin_lock_irqsave(&pcdev->lock, flags); - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 0; - vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 0; + vb = &buf->vb; + buf->state = MX2_STATE_ACTIVE; - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - buf = list_first_entry(&pcdev->capture, struct mx2_buffer, - internal.queue); - buf->internal.bufnum = 1; - vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; + buf = list_first_entry(&pcdev->capture, struct mx2_buffer, + internal.queue); + buf->internal.bufnum = 1; + vb = &buf->vb; + buf->state = MX2_STATE_ACTIVE; - phys = vb2_dma_contig_plane_dma_addr(vb, 0); - mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); - list_move_tail(pcdev->capture.next, &pcdev->active_bufs); + phys = vb2_dma_contig_plane_dma_addr(vb, 0); + mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); + list_move_tail(pcdev->capture.next, &pcdev->active_bufs); - bytesperline = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytesperline < 0) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return bytesperline; - } - - /* - * I didn't manage to properly enable/disable the prp - * on a per frame basis during running transfers, - * thus we allocate a buffer here and use it to - * discard frames when no buffer is available. - * Feel free to work on this ;) - */ - pcdev->discard_size = icd->user_height * bytesperline; - pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, &pcdev->discard_buffer_dma, - GFP_ATOMIC); - if (!pcdev->discard_buffer) { - spin_unlock_irqrestore(&pcdev->lock, flags); - return -ENOMEM; - } - - pcdev->buf_discard[0].discard = true; - list_add_tail(&pcdev->buf_discard[0].queue, - &pcdev->discard); - - pcdev->buf_discard[1].discard = true; - list_add_tail(&pcdev->buf_discard[1].queue, - &pcdev->discard); - - mx2_prp_resize_commit(pcdev); - - mx27_camera_emma_buf_init(icd, bytesperline); - - if (prp->cfg.channel == 1) { - writel(PRP_CNTL_CH1EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH1_LEN | - PRP_CNTL_CH1BYP | - PRP_CNTL_CH1_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } else { - writel(PRP_CNTL_CH2EN | - PRP_CNTL_CSIEN | - prp->cfg.in_fmt | - prp->cfg.out_fmt | - PRP_CNTL_CH2_LEN | - PRP_CNTL_CH2_TSKIP(0) | - PRP_CNTL_IN_TSKIP(0), - pcdev->base_emma + PRP_CNTL); - } + bytesperline = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + if (bytesperline < 0) { spin_unlock_irqrestore(&pcdev->lock, flags); + return bytesperline; } + /* + * I didn't manage to properly enable/disable the prp + * on a per frame basis during running transfers, + * thus we allocate a buffer here and use it to + * discard frames when no buffer is available. + * Feel free to work on this ;) + */ + pcdev->discard_size = icd->user_height * bytesperline; + pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, + &pcdev->discard_buffer_dma, GFP_ATOMIC); + if (!pcdev->discard_buffer) { + spin_unlock_irqrestore(&pcdev->lock, flags); + return -ENOMEM; + } + + pcdev->buf_discard[0].discard = true; + list_add_tail(&pcdev->buf_discard[0].queue, + &pcdev->discard); + + pcdev->buf_discard[1].discard = true; + list_add_tail(&pcdev->buf_discard[1].queue, + &pcdev->discard); + + mx2_prp_resize_commit(pcdev); + + mx27_camera_emma_buf_init(icd, bytesperline); + + if (prp->cfg.channel == 1) { + writel(PRP_CNTL_CH1EN | + PRP_CNTL_CSIEN | + prp->cfg.in_fmt | + prp->cfg.out_fmt | + PRP_CNTL_CH1_LEN | + PRP_CNTL_CH1BYP | + PRP_CNTL_CH1_TSKIP(0) | + PRP_CNTL_IN_TSKIP(0), + pcdev->base_emma + PRP_CNTL); + } else { + writel(PRP_CNTL_CH2EN | + PRP_CNTL_CSIEN | + prp->cfg.in_fmt | + prp->cfg.out_fmt | + PRP_CNTL_CH2_LEN | + PRP_CNTL_CH2_TSKIP(0) | + PRP_CNTL_IN_TSKIP(0), + pcdev->base_emma + PRP_CNTL); + } + spin_unlock_irqrestore(&pcdev->lock, flags); + return 0; } @@ -976,29 +798,27 @@ static int mx2_stop_streaming(struct vb2_queue *q) void *b; u32 cntl; - if (is_imx27_camera(pcdev)) { - spin_lock_irqsave(&pcdev->lock, flags); + spin_lock_irqsave(&pcdev->lock, flags); - cntl = readl(pcdev->base_emma + PRP_CNTL); - if (prp->cfg.channel == 1) { - writel(cntl & ~PRP_CNTL_CH1EN, - pcdev->base_emma + PRP_CNTL); - } else { - writel(cntl & ~PRP_CNTL_CH2EN, - pcdev->base_emma + PRP_CNTL); - } - INIT_LIST_HEAD(&pcdev->capture); - INIT_LIST_HEAD(&pcdev->active_bufs); - INIT_LIST_HEAD(&pcdev->discard); - - b = pcdev->discard_buffer; - pcdev->discard_buffer = NULL; - - spin_unlock_irqrestore(&pcdev->lock, flags); - - dma_free_coherent(ici->v4l2_dev.dev, - pcdev->discard_size, b, pcdev->discard_buffer_dma); + cntl = readl(pcdev->base_emma + PRP_CNTL); + if (prp->cfg.channel == 1) { + writel(cntl & ~PRP_CNTL_CH1EN, + pcdev->base_emma + PRP_CNTL); + } else { + writel(cntl & ~PRP_CNTL_CH2EN, + pcdev->base_emma + PRP_CNTL); } + INIT_LIST_HEAD(&pcdev->capture); + INIT_LIST_HEAD(&pcdev->active_bufs); + INIT_LIST_HEAD(&pcdev->discard); + + b = pcdev->discard_buffer; + pcdev->discard_buffer = NULL; + + spin_unlock_irqrestore(&pcdev->lock, flags); + + dma_free_coherent(ici->v4l2_dev.dev, + pcdev->discard_size, b, pcdev->discard_buffer_dma); return 0; } @@ -1128,16 +948,9 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) if (bytesperline < 0) return bytesperline; - if (is_imx27_camera(pcdev)) { - ret = mx27_camera_emma_prp_reset(pcdev); - if (ret) - return ret; - } else if (is_imx25_camera(pcdev)) { - writel((bytesperline * icd->user_height) >> 2, - pcdev->base_csi + CSIRXCNT); - writel((bytesperline << 16) | icd->user_height, - pcdev->base_csi + CSIIMAG_PARA); - } + ret = mx27_camera_emma_prp_reset(pcdev); + if (ret) + return ret; writel(pcdev->csicr1, pcdev->base_csi + CSICR1); @@ -1424,7 +1237,6 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct mx2_fmt_cfg *emma_prp; - unsigned int width_limit; int ret; dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", @@ -1436,44 +1248,11 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, return -EINVAL; } - /* limit to MX25 hardware capabilities */ - if (is_imx25_camera(pcdev)) { - if (xlate->host_fmt->bits_per_sample <= 8) - width_limit = 0xffff * 4; - else - width_limit = 0xffff * 2; - /* CSIIMAG_PARA limit */ - if (pix->width > width_limit) - pix->width = width_limit; - if (pix->height > 0xffff) - pix->height = 0xffff; - - pix->bytesperline = soc_mbus_bytes_per_line(pix->width, - xlate->host_fmt); - if (pix->bytesperline < 0) - return pix->bytesperline; - pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, - pix->bytesperline, pix->height); - /* Check against the CSIRXCNT limit */ - if (pix->sizeimage > 4 * 0x3ffff) { - /* Adjust geometry, preserve aspect ratio */ - unsigned int new_height = int_sqrt(div_u64(0x3ffffULL * - 4 * pix->height, pix->bytesperline)); - pix->width = new_height * pix->width / pix->height; - pix->height = new_height; - pix->bytesperline = soc_mbus_bytes_per_line(pix->width, - xlate->host_fmt); - BUG_ON(pix->bytesperline < 0); - pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, - pix->bytesperline, pix->height); - } - } else { - /* - * Width must be a multiple of 8 as requested by the CSI. - * (Table 39-2 in the i.MX27 Reference Manual). - */ - pix->width &= ~0x7; - } + /* + * limit to MX27 hardware capabilities: width must be a multiple of 8 as + * requested by the CSI. (Table 39-2 in the i.MX27 Reference Manual). + */ + pix->width &= ~0x7; /* limit to sensor capabilities */ mf.width = pix->width; @@ -1491,7 +1270,7 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, /* If the sensor does not support image size try PrP resizing */ emma_prp = mx27_emma_prp_get_format(xlate->code, - xlate->host_fmt->fourcc); + xlate->host_fmt->fourcc); if ((mf.width != pix->width || mf.height != pix->height) && emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { @@ -1777,20 +1556,6 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) goto exit; } - pcdev->devtype = pdev->id_entry->driver_data; - switch (pcdev->devtype) { - case IMX25_CAMERA: - pcdev->reg_csisr = CSISR_IMX25; - pcdev->reg_csicr3 = CSICR3_IMX25; - break; - case IMX27_CAMERA: - pcdev->reg_csisr = CSISR_IMX27; - pcdev->reg_csicr3 = CSICR3_IMX27; - break; - default: - break; - } - pcdev->clk_csi_ahb = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(pcdev->clk_csi_ahb)) { dev_err(&pdev->dev, "Could not get csi ahb clock\n"); @@ -1836,20 +1601,9 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->dev = &pdev->dev; platform_set_drvdata(pdev, pcdev); - if (is_imx25_camera(pcdev)) { - err = devm_request_irq(&pdev->dev, irq_csi, mx25_camera_irq, 0, - MX2_CAM_DRV_NAME, pcdev); - if (err) { - dev_err(pcdev->dev, "Camera interrupt register failed \n"); - goto exit; - } - } - - if (is_imx27_camera(pcdev)) { - err = mx27_camera_emma_init(pdev); - if (err) - goto exit; - } + err = mx27_camera_emma_init(pdev); + if (err) + goto exit; /* * We're done with drvdata here. Clear the pointer so that @@ -1862,8 +1616,6 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->soc_host.priv = pcdev; pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; - if (is_imx25_camera(pcdev)) - pcdev->soc_host.capabilities = SOCAM_HOST_CAP_STRIDE; pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(pcdev->alloc_ctx)) { @@ -1882,10 +1634,8 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) exit_free_emma: vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); eallocctx: - if (is_imx27_camera(pcdev)) { - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); - } + clk_disable_unprepare(pcdev->clk_emma_ipg); + clk_disable_unprepare(pcdev->clk_emma_ahb); exit: return err; } @@ -1900,10 +1650,8 @@ static int __devexit mx2_camera_remove(struct platform_device *pdev) vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); - if (is_imx27_camera(pcdev)) { - clk_disable_unprepare(pcdev->clk_emma_ipg); - clk_disable_unprepare(pcdev->clk_emma_ahb); - } + clk_disable_unprepare(pcdev->clk_emma_ipg); + clk_disable_unprepare(pcdev->clk_emma_ahb); dev_info(&pdev->dev, "MX2 Camera driver unloaded\n"); @@ -1932,7 +1680,7 @@ static void __exit mx2_camera_exit(void) module_init(mx2_camera_init); module_exit(mx2_camera_exit); -MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver"); +MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver"); MODULE_AUTHOR("Sascha Hauer "); MODULE_LICENSE("GPL"); MODULE_VERSION(MX2_CAM_VERSION); From 22f9a477a23b2e4fe85017920aaf4fd86a8a6660 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 30 Oct 2012 11:29:01 -0300 Subject: [PATCH 0552/4607] [media] mx2_camera: Remove 'buf_cleanup' callback All necessary tasks to end the streaming properly are already implemented in mx2_stop_streaming() and nothing remains to be done in this callback. Furthermore, it only included debug messages so it can be removed. Signed-off-by: Javier Martin Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/soc_camera/mx2_camera.c | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index c0511c001be1..c93e790594ef 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -560,39 +560,6 @@ static void mx2_videobuf_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&pcdev->lock, flags); } -static void mx2_videobuf_release(struct vb2_buffer *vb) -{ -#ifdef DEBUG - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx2_camera_dev *pcdev = ici->priv; - struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb); - - dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, - vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - - switch (buf->state) { - case MX2_STATE_ACTIVE: - dev_info(icd->parent, "%s (active)\n", __func__); - break; - case MX2_STATE_QUEUED: - dev_info(icd->parent, "%s (queued)\n", __func__); - break; - default: - dev_info(icd->parent, "%s (unknown) %d\n", __func__, - buf->state); - break; - } -#endif - - /* - * FIXME: implement forced termination of active buffers for mx27 and - * mx27 eMMA, so that the user won't get stuck in an uninterruptible - * state. This requires a specific handling for each of the these DMA - * types. - */ -} - static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, int bytesperline) { @@ -827,7 +794,6 @@ static struct vb2_ops mx2_videobuf_ops = { .queue_setup = mx2_videobuf_setup, .buf_prepare = mx2_videobuf_prepare, .buf_queue = mx2_videobuf_queue, - .buf_cleanup = mx2_videobuf_release, .start_streaming = mx2_start_streaming, .stop_streaming = mx2_stop_streaming, }; From 3fdd97973b357255c291beaad5cdf6255d3ff936 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 30 Oct 2012 11:29:02 -0300 Subject: [PATCH 0553/4607] [media] mx2_camera: Remove buffer states After removing i.mx25 support and buf_cleanup() callback, buffer states are not used in the code any longer. Signed-off-by: Javier Martin Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx2_camera.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index c93e790594ef..188541ac355a 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -216,12 +216,6 @@ struct mx2_fmt_cfg { struct mx2_prp_cfg cfg; }; -enum mx2_buffer_state { - MX2_STATE_QUEUED, - MX2_STATE_ACTIVE, - MX2_STATE_DONE, -}; - struct mx2_buf_internal { struct list_head queue; int bufnum; @@ -232,7 +226,6 @@ struct mx2_buf_internal { struct mx2_buffer { /* common v4l buffer stuff -- must be first */ struct vb2_buffer vb; - enum mx2_buffer_state state; struct mx2_buf_internal internal; }; @@ -554,7 +547,6 @@ static void mx2_videobuf_queue(struct vb2_buffer *vb) spin_lock_irqsave(&pcdev->lock, flags); - buf->state = MX2_STATE_QUEUED; list_add_tail(&buf->internal.queue, &pcdev->capture); spin_unlock_irqrestore(&pcdev->lock, flags); @@ -678,7 +670,6 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) internal.queue); buf->internal.bufnum = 0; vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; phys = vb2_dma_contig_plane_dma_addr(vb, 0); mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); @@ -688,7 +679,6 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) internal.queue); buf->internal.bufnum = 1; vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; phys = vb2_dma_contig_plane_dma_addr(vb, 0); mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum); @@ -1382,7 +1372,6 @@ static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev, list_move_tail(pcdev->capture.next, &pcdev->active_bufs); vb = &buf->vb; - buf->state = MX2_STATE_ACTIVE; phys = vb2_dma_contig_plane_dma_addr(vb, 0); mx27_update_emma_buf(pcdev, phys, bufnum); From 39793c6900b89c22cf30e0c83207df8c0a5458df Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 30 Nov 2012 12:06:21 -0300 Subject: [PATCH 0554/4607] [media] mx2_camera: Convert it to platform driver Converting it to platform code can make the code smaller. Signed-off-by: Fabio Estevam Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx2_camera.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 188541ac355a..168e062164e9 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -1619,21 +1619,10 @@ static struct platform_driver mx2_camera_driver = { }, .id_table = mx2_camera_devtype, .remove = __devexit_p(mx2_camera_remove), + .probe = mx2_camera_probe, }; - -static int __init mx2_camera_init(void) -{ - return platform_driver_probe(&mx2_camera_driver, &mx2_camera_probe); -} - -static void __exit mx2_camera_exit(void) -{ - return platform_driver_unregister(&mx2_camera_driver); -} - -module_init(mx2_camera_init); -module_exit(mx2_camera_exit); +module_platform_driver(mx2_camera_driver); MODULE_DESCRIPTION("i.MX27 SoC Camera Host driver"); MODULE_AUTHOR("Sascha Hauer "); From 2c46bb119f13a3ebc62461dac498a7057f5b4a94 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jan 2013 02:03:14 -0200 Subject: [PATCH 0555/4607] [media] ngene: fix commit 36a495a336c3fbbb2f4eeed2a94ab6d5be19d186 The above commit were applied only partially; it broke tuner and demod attach, but the part that added it to ngene_info was missing. Not sure what happened there, but, without this patch, a regression would be happening. Also, gcc complains about a defined but not used symbol. So, apply manually the missing part. Cc: Patrice Chotard Cc: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ngene/ngene-cards.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index 2a4895b0706b..82318d11696f 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -732,6 +732,7 @@ static struct ngene_info ngene_info_terratec = { .name = "Terratec Integra/Cinergy2400i Dual DVB-T", .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, .demod_attach = {demod_attach_drxd, demod_attach_drxd}, + .tuner_attach = {tuner_attach_dtt7520x, tuner_attach_dtt7520x}, .fe_config = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1}, .i2c_access = 1, }; From 3ff59bcee7be7796ce566dfef0604eda013cc8c2 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:01 -0700 Subject: [PATCH 0556/4607] crypto: omap-sham - Remove unnecessary pr_info noise Remove the unnecessary pr_info() call in omap_sham_mod_init(). CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 1d75e6f95a58..2df7a54fc3b9 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -1286,8 +1286,6 @@ static struct platform_driver omap_sham_driver = { static int __init omap_sham_mod_init(void) { - pr_info("loading %s driver\n", "omap-sham"); - return platform_driver_register(&omap_sham_driver); } From b359f034c8bf6c6ae4785c1172786ce73eccf9f2 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:02 -0700 Subject: [PATCH 0557/4607] crypto: omap-sham - Convert to use pm_runtime API Convert the omap-sham crypto driver to use the pm_runtime API instead of the clk API. CC: Kevin Hilman CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 2df7a54fc3b9..777eb9fdd096 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -22,12 +22,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -140,7 +140,6 @@ struct omap_sham_dev { struct device *dev; void __iomem *io_base; int irq; - struct clk *iclk; spinlock_t lock; int err; int dma; @@ -237,7 +236,7 @@ static void omap_sham_copy_ready_hash(struct ahash_request *req) static int omap_sham_hw_init(struct omap_sham_dev *dd) { - clk_enable(dd->iclk); + pm_runtime_get_sync(dd->dev); if (!test_bit(FLAGS_INIT, &dd->flags)) { omap_sham_write_mask(dd, SHA_REG_MASK, @@ -652,7 +651,8 @@ static void omap_sham_finish_req(struct ahash_request *req, int err) /* atomic operation is not needed here */ dd->flags &= ~(BIT(FLAGS_BUSY) | BIT(FLAGS_FINAL) | BIT(FLAGS_CPU) | BIT(FLAGS_DMA_READY) | BIT(FLAGS_OUTPUT_READY)); - clk_disable(dd->iclk); + + pm_runtime_put_sync(dd->dev); if (req->base.complete) req->base.complete(&req->base, err); @@ -1197,14 +1197,6 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) if (err) goto dma_err; - /* Initializing the clock */ - dd->iclk = clk_get(dev, "ick"); - if (IS_ERR(dd->iclk)) { - dev_err(dev, "clock intialization failed.\n"); - err = PTR_ERR(dd->iclk); - goto clk_err; - } - dd->io_base = ioremap(dd->phys_base, SZ_4K); if (!dd->io_base) { dev_err(dev, "can't ioremap\n"); @@ -1212,11 +1204,14 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) goto io_err; } - clk_enable(dd->iclk); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + dev_info(dev, "hw accel on OMAP rev %u.%u\n", (omap_sham_read(dd, SHA_REG_REV) & SHA_REG_REV_MAJOR) >> 4, omap_sham_read(dd, SHA_REG_REV) & SHA_REG_REV_MINOR); - clk_disable(dd->iclk); + + pm_runtime_put_sync(&pdev->dev); spin_lock(&sham.lock); list_add_tail(&dd->list, &sham.dev_list); @@ -1234,9 +1229,8 @@ err_algs: for (j = 0; j < i; j++) crypto_unregister_ahash(&algs[j]); iounmap(dd->io_base); + pm_runtime_disable(dev); io_err: - clk_put(dd->iclk); -clk_err: omap_sham_dma_cleanup(dd); dma_err: if (dd->irq >= 0) @@ -1265,7 +1259,7 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) crypto_unregister_ahash(&algs[i]); tasklet_kill(&dd->done_task); iounmap(dd->io_base); - clk_put(dd->iclk); + pm_runtime_disable(&pdev->dev); omap_sham_dma_cleanup(dd); if (dd->irq >= 0) free_irq(dd->irq, dd); From 3b3f440023b3809c8eabec681768a4bcee15f2b4 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:03 -0700 Subject: [PATCH 0558/4607] crypto: omap-sham - Add suspend/resume support Add suspend/resume support to the OMAP SHAM driver. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 777eb9fdd096..9c3b096e15e8 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -1269,12 +1269,31 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int omap_sham_suspend(struct device *dev) +{ + pm_runtime_put_sync(dev); + return 0; +} + +static int omap_sham_resume(struct device *dev) +{ + pm_runtime_get_sync(dev); + return 0; +} +#endif + +static const struct dev_pm_ops omap_sham_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap_sham_suspend, omap_sham_resume) +}; + static struct platform_driver omap_sham_driver = { .probe = omap_sham_probe, .remove = omap_sham_remove, .driver = { .name = "omap-sham", .owner = THIS_MODULE, + .pm = &omap_sham_pm_ops, }, }; From dfd061d5a8f5a6d89c77d23693a63038ff8cbcd8 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:04 -0700 Subject: [PATCH 0559/4607] crypto: omap-sham - Add code to use dmaengine API Add code to use the new dmaengine API alongside the existing DMA code that uses the private OMAP DMA API. The API to use is chosen by defining or undefining 'OMAP_SHAM_DMA_PRIVATE'. This is a transitional change and the code that uses the private DMA API will be removed in an upcoming commit. CC: Russell King CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 154 ++++++++++++++++++++++++++++++++++--- 1 file changed, 145 insertions(+), 9 deletions(-) diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 9c3b096e15e8..f54ceb8f5b24 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -13,6 +13,8 @@ * Some ideas are from old omap-sha1-md5.c driver. */ +#define OMAP_SHAM_DMA_PRIVATE + #define pr_fmt(fmt) "%s: " fmt, __func__ #include @@ -27,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -37,15 +41,15 @@ #include #include -#include -#include - #define SHA_REG_DIGEST(x) (0x00 + ((x) * 0x04)) #define SHA_REG_DIN(x) (0x1C + ((x) * 0x04)) #define SHA1_MD5_BLOCK_SIZE SHA1_BLOCK_SIZE #define MD5_DIGEST_SIZE 16 +#define DST_MAXBURST 16 +#define DMA_MIN (DST_MAXBURST * sizeof(u32)) + #define SHA_REG_DIGCNT 0x14 #define SHA_REG_CTRL 0x18 @@ -109,6 +113,9 @@ struct omap_sham_reqctx { /* walk state */ struct scatterlist *sg; +#ifndef OMAP_SHAM_DMA_PRIVATE + struct scatterlist sgl; +#endif unsigned int offset; /* offset in current sg */ unsigned int total; /* total request */ @@ -142,8 +149,12 @@ struct omap_sham_dev { int irq; spinlock_t lock; int err; +#ifdef OMAP_SHAM_DMA_PRIVATE int dma; int dma_lch; +#else + struct dma_chan *dma_lch; +#endif struct tasklet_struct done_task; unsigned long flags; @@ -313,15 +324,32 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf, return -EINPROGRESS; } +#ifndef OMAP_SHAM_DMA_PRIVATE +static void omap_sham_dma_callback(void *param) +{ + struct omap_sham_dev *dd = param; + + set_bit(FLAGS_DMA_READY, &dd->flags); + tasklet_schedule(&dd->done_task); +} +#endif + static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, - size_t length, int final) + size_t length, int final, int is_sg) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); +#ifdef OMAP_SHAM_DMA_PRIVATE int len32; +#else + struct dma_async_tx_descriptor *tx; + struct dma_slave_config cfg; + int len32, ret; +#endif dev_dbg(dd->dev, "xmit_dma: digcnt: %d, length: %d, final: %d\n", ctx->digcnt, length, final); +#ifdef OMAP_SHAM_DMA_PRIVATE len32 = DIV_ROUND_UP(length, sizeof(u32)); omap_set_dma_transfer_params(dd->dma_lch, OMAP_DMA_DATA_TYPE_S32, len32, @@ -331,6 +359,50 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, omap_set_dma_src_params(dd->dma_lch, 0, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0); +#else + memset(&cfg, 0, sizeof(cfg)); + + cfg.dst_addr = dd->phys_base + SHA_REG_DIN(0); + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_maxburst = DST_MAXBURST; + + ret = dmaengine_slave_config(dd->dma_lch, &cfg); + if (ret) { + pr_err("omap-sham: can't configure dmaengine slave: %d\n", ret); + return ret; + } + + len32 = DIV_ROUND_UP(length, DMA_MIN) * DMA_MIN; + + if (is_sg) { + /* + * The SG entry passed in may not have the 'length' member + * set correctly so use a local SG entry (sgl) with the + * proper value for 'length' instead. If this is not done, + * the dmaengine may try to DMA the incorrect amount of data. + */ + sg_init_table(&ctx->sgl, 1); + ctx->sgl.page_link = ctx->sg->page_link; + ctx->sgl.offset = ctx->sg->offset; + sg_dma_len(&ctx->sgl) = len32; + sg_dma_address(&ctx->sgl) = sg_dma_address(ctx->sg); + + tx = dmaengine_prep_slave_sg(dd->dma_lch, &ctx->sgl, 1, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + } else { + tx = dmaengine_prep_slave_single(dd->dma_lch, dma_addr, len32, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + } + + if (!tx) { + dev_err(dd->dev, "prep_slave_sg/single() failed\n"); + return -EINVAL; + } + + tx->callback = omap_sham_dma_callback; + tx->callback_param = dd; +#endif + omap_sham_write_ctrl(dd, length, final, 1); ctx->digcnt += length; @@ -340,7 +412,12 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, set_bit(FLAGS_DMA_ACTIVE, &dd->flags); +#ifdef OMAP_SHAM_DMA_PRIVATE omap_start_dma(dd->dma_lch); +#else + dmaengine_submit(tx); + dma_async_issue_pending(dd->dma_lch); +#endif return -EINPROGRESS; } @@ -387,6 +464,8 @@ static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd, struct omap_sham_reqctx *ctx, size_t length, int final) { + int ret; + ctx->dma_addr = dma_map_single(dd->dev, ctx->buffer, ctx->buflen, DMA_TO_DEVICE); if (dma_mapping_error(dd->dev, ctx->dma_addr)) { @@ -396,8 +475,12 @@ static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd, ctx->flags &= ~BIT(FLAGS_SG); - /* next call does not fail... so no unmap in the case of error */ - return omap_sham_xmit_dma(dd, ctx->dma_addr, length, final); + ret = omap_sham_xmit_dma(dd, ctx->dma_addr, length, final, 0); + if (ret) + dma_unmap_single(dd->dev, ctx->dma_addr, ctx->buflen, + DMA_TO_DEVICE); + + return ret; } static int omap_sham_update_dma_slow(struct omap_sham_dev *dd) @@ -432,6 +515,7 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); unsigned int length, final, tail; struct scatterlist *sg; + int ret; if (!ctx->total) return 0; @@ -439,6 +523,17 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) if (ctx->bufcnt || ctx->offset) return omap_sham_update_dma_slow(dd); +#ifndef OMAP_SHAM_DMA_PRIVATE + /* + * Don't use the sg interface when the transfer size is less + * than the number of elements in a DMA frame. Otherwise, + * the dmaengine infrastructure will calculate that it needs + * to transfer 0 frames which ultimately fails. + */ + if (ctx->total < (DST_MAXBURST * sizeof(u32))) + return omap_sham_update_dma_slow(dd); +#endif + dev_dbg(dd->dev, "fast: digcnt: %d, bufcnt: %u, total: %u\n", ctx->digcnt, ctx->bufcnt, ctx->total); @@ -476,8 +571,11 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) final = (ctx->flags & BIT(FLAGS_FINUP)) && !ctx->total; - /* next call does not fail... so no unmap in the case of error */ - return omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final); + ret = omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final, 1); + if (ret) + dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE); + + return ret; } static int omap_sham_update_cpu(struct omap_sham_dev *dd) @@ -496,7 +594,12 @@ static int omap_sham_update_dma_stop(struct omap_sham_dev *dd) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); +#ifdef OMAP_SHAM_DMA_PRIVATE omap_stop_dma(dd->dma_lch); +#else + dmaengine_terminate_all(dd->dma_lch); +#endif + if (ctx->flags & BIT(FLAGS_SG)) { dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE); if (ctx->sg->length == ctx->offset) { @@ -583,7 +686,7 @@ static int omap_sham_final_req(struct omap_sham_dev *dd) struct omap_sham_reqctx *ctx = ahash_request_ctx(req); int err = 0, use_dma = 1; - if (ctx->bufcnt <= 64) + if (ctx->bufcnt <= DMA_MIN) /* faster to handle last block with cpu */ use_dma = 0; @@ -699,6 +802,7 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd, if (err) goto err1; +#ifdef OMAP_SHAM_DMA_PRIVATE omap_set_dma_dest_params(dd->dma_lch, 0, OMAP_DMA_AMODE_CONSTANT, dd->phys_base + SHA_REG_DIN(0), 0, 16); @@ -708,6 +812,7 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd, omap_set_dma_src_burst_mode(dd->dma_lch, OMAP_DMA_DATA_BURST_4); +#endif if (ctx->digcnt) /* request has changed - restore hash */ @@ -1099,6 +1204,7 @@ static irqreturn_t omap_sham_irq(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef OMAP_SHAM_DMA_PRIVATE static void omap_sham_dma_callback(int lch, u16 ch_status, void *data) { struct omap_sham_dev *dd = data; @@ -1136,12 +1242,17 @@ static void omap_sham_dma_cleanup(struct omap_sham_dev *dd) dd->dma_lch = -1; } } +#endif static int __devinit omap_sham_probe(struct platform_device *pdev) { struct omap_sham_dev *dd; struct device *dev = &pdev->dev; struct resource *res; +#ifndef OMAP_SHAM_DMA_PRIVATE + dma_cap_mask_t mask; + unsigned dma_chan; +#endif int err, i, j; dd = kzalloc(sizeof(struct omap_sham_dev), GFP_KERNEL); @@ -1176,7 +1287,11 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) err = -ENODEV; goto res_err; } +#ifdef OMAP_SHAM_DMA_PRIVATE dd->dma = res->start; +#else + dma_chan = res->start; +#endif /* Get the IRQ */ dd->irq = platform_get_irq(pdev, 0); @@ -1193,9 +1308,22 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) goto res_err; } +#ifdef OMAP_SHAM_DMA_PRIVATE err = omap_sham_dma_init(dd); if (err) goto dma_err; +#else + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dd->dma_lch = dma_request_channel(mask, omap_dma_filter_fn, &dma_chan); + if (!dd->dma_lch) { + dev_err(dev, "unable to obtain RX DMA engine channel %u\n", + dma_chan); + err = -ENXIO; + goto dma_err; + } +#endif dd->io_base = ioremap(dd->phys_base, SZ_4K); if (!dd->io_base) { @@ -1231,7 +1359,11 @@ err_algs: iounmap(dd->io_base); pm_runtime_disable(dev); io_err: +#ifdef OMAP_SHAM_DMA_PRIVATE omap_sham_dma_cleanup(dd); +#else + dma_release_channel(dd->dma_lch); +#endif dma_err: if (dd->irq >= 0) free_irq(dd->irq, dd); @@ -1260,7 +1392,11 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) tasklet_kill(&dd->done_task); iounmap(dd->io_base); pm_runtime_disable(&pdev->dev); +#ifdef OMAP_SHAM_DMA_PRIVATE omap_sham_dma_cleanup(dd); +#else + dma_release_channel(dd->dma_lch); +#endif if (dd->irq >= 0) free_irq(dd->irq, dd); kfree(dd); From dd49a69e8eb1423e4d434081a7785bc1b8b8948a Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:05 -0700 Subject: [PATCH 0560/4607] crypto: omap-sham - Remove usage of private DMA API Remove usage of the private OMAP DMA API. The dmaengine API will be used instead. CC: Russell King CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 109 ------------------------------------- 1 file changed, 109 deletions(-) diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index f54ceb8f5b24..f6b270ed7d62 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -13,8 +13,6 @@ * Some ideas are from old omap-sha1-md5.c driver. */ -#define OMAP_SHAM_DMA_PRIVATE - #define pr_fmt(fmt) "%s: " fmt, __func__ #include @@ -113,9 +111,7 @@ struct omap_sham_reqctx { /* walk state */ struct scatterlist *sg; -#ifndef OMAP_SHAM_DMA_PRIVATE struct scatterlist sgl; -#endif unsigned int offset; /* offset in current sg */ unsigned int total; /* total request */ @@ -149,12 +145,7 @@ struct omap_sham_dev { int irq; spinlock_t lock; int err; -#ifdef OMAP_SHAM_DMA_PRIVATE - int dma; - int dma_lch; -#else struct dma_chan *dma_lch; -#endif struct tasklet_struct done_task; unsigned long flags; @@ -324,7 +315,6 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf, return -EINPROGRESS; } -#ifndef OMAP_SHAM_DMA_PRIVATE static void omap_sham_dma_callback(void *param) { struct omap_sham_dev *dd = param; @@ -332,34 +322,18 @@ static void omap_sham_dma_callback(void *param) set_bit(FLAGS_DMA_READY, &dd->flags); tasklet_schedule(&dd->done_task); } -#endif static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, size_t length, int final, int is_sg) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); -#ifdef OMAP_SHAM_DMA_PRIVATE - int len32; -#else struct dma_async_tx_descriptor *tx; struct dma_slave_config cfg; int len32, ret; -#endif dev_dbg(dd->dev, "xmit_dma: digcnt: %d, length: %d, final: %d\n", ctx->digcnt, length, final); -#ifdef OMAP_SHAM_DMA_PRIVATE - len32 = DIV_ROUND_UP(length, sizeof(u32)); - - omap_set_dma_transfer_params(dd->dma_lch, OMAP_DMA_DATA_TYPE_S32, len32, - 1, OMAP_DMA_SYNC_PACKET, dd->dma, - OMAP_DMA_DST_SYNC_PREFETCH); - - omap_set_dma_src_params(dd->dma_lch, 0, OMAP_DMA_AMODE_POST_INC, - dma_addr, 0, 0); - -#else memset(&cfg, 0, sizeof(cfg)); cfg.dst_addr = dd->phys_base + SHA_REG_DIN(0); @@ -401,7 +375,6 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, tx->callback = omap_sham_dma_callback; tx->callback_param = dd; -#endif omap_sham_write_ctrl(dd, length, final, 1); @@ -412,12 +385,8 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, set_bit(FLAGS_DMA_ACTIVE, &dd->flags); -#ifdef OMAP_SHAM_DMA_PRIVATE - omap_start_dma(dd->dma_lch); -#else dmaengine_submit(tx); dma_async_issue_pending(dd->dma_lch); -#endif return -EINPROGRESS; } @@ -523,7 +492,6 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) if (ctx->bufcnt || ctx->offset) return omap_sham_update_dma_slow(dd); -#ifndef OMAP_SHAM_DMA_PRIVATE /* * Don't use the sg interface when the transfer size is less * than the number of elements in a DMA frame. Otherwise, @@ -532,7 +500,6 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) */ if (ctx->total < (DST_MAXBURST * sizeof(u32))) return omap_sham_update_dma_slow(dd); -#endif dev_dbg(dd->dev, "fast: digcnt: %d, bufcnt: %u, total: %u\n", ctx->digcnt, ctx->bufcnt, ctx->total); @@ -594,11 +561,7 @@ static int omap_sham_update_dma_stop(struct omap_sham_dev *dd) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); -#ifdef OMAP_SHAM_DMA_PRIVATE - omap_stop_dma(dd->dma_lch); -#else dmaengine_terminate_all(dd->dma_lch); -#endif if (ctx->flags & BIT(FLAGS_SG)) { dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE); @@ -802,18 +765,6 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd, if (err) goto err1; -#ifdef OMAP_SHAM_DMA_PRIVATE - omap_set_dma_dest_params(dd->dma_lch, 0, - OMAP_DMA_AMODE_CONSTANT, - dd->phys_base + SHA_REG_DIN(0), 0, 16); - - omap_set_dma_dest_burst_mode(dd->dma_lch, - OMAP_DMA_DATA_BURST_16); - - omap_set_dma_src_burst_mode(dd->dma_lch, - OMAP_DMA_DATA_BURST_4); -#endif - if (ctx->digcnt) /* request has changed - restore hash */ omap_sham_copy_hash(req, 0); @@ -1204,55 +1155,13 @@ static irqreturn_t omap_sham_irq(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef OMAP_SHAM_DMA_PRIVATE -static void omap_sham_dma_callback(int lch, u16 ch_status, void *data) -{ - struct omap_sham_dev *dd = data; - - if (ch_status != OMAP_DMA_BLOCK_IRQ) { - pr_err("omap-sham DMA error status: 0x%hx\n", ch_status); - dd->err = -EIO; - clear_bit(FLAGS_INIT, &dd->flags);/* request to re-initialize */ - } - - set_bit(FLAGS_DMA_READY, &dd->flags); - tasklet_schedule(&dd->done_task); -} - -static int omap_sham_dma_init(struct omap_sham_dev *dd) -{ - int err; - - dd->dma_lch = -1; - - err = omap_request_dma(dd->dma, dev_name(dd->dev), - omap_sham_dma_callback, dd, &dd->dma_lch); - if (err) { - dev_err(dd->dev, "Unable to request DMA channel\n"); - return err; - } - - return 0; -} - -static void omap_sham_dma_cleanup(struct omap_sham_dev *dd) -{ - if (dd->dma_lch >= 0) { - omap_free_dma(dd->dma_lch); - dd->dma_lch = -1; - } -} -#endif - static int __devinit omap_sham_probe(struct platform_device *pdev) { struct omap_sham_dev *dd; struct device *dev = &pdev->dev; struct resource *res; -#ifndef OMAP_SHAM_DMA_PRIVATE dma_cap_mask_t mask; unsigned dma_chan; -#endif int err, i, j; dd = kzalloc(sizeof(struct omap_sham_dev), GFP_KERNEL); @@ -1287,11 +1196,7 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) err = -ENODEV; goto res_err; } -#ifdef OMAP_SHAM_DMA_PRIVATE - dd->dma = res->start; -#else dma_chan = res->start; -#endif /* Get the IRQ */ dd->irq = platform_get_irq(pdev, 0); @@ -1308,11 +1213,6 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) goto res_err; } -#ifdef OMAP_SHAM_DMA_PRIVATE - err = omap_sham_dma_init(dd); - if (err) - goto dma_err; -#else dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -1323,7 +1223,6 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) err = -ENXIO; goto dma_err; } -#endif dd->io_base = ioremap(dd->phys_base, SZ_4K); if (!dd->io_base) { @@ -1359,11 +1258,7 @@ err_algs: iounmap(dd->io_base); pm_runtime_disable(dev); io_err: -#ifdef OMAP_SHAM_DMA_PRIVATE - omap_sham_dma_cleanup(dd); -#else dma_release_channel(dd->dma_lch); -#endif dma_err: if (dd->irq >= 0) free_irq(dd->irq, dd); @@ -1392,11 +1287,7 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) tasklet_kill(&dd->done_task); iounmap(dd->io_base); pm_runtime_disable(&pdev->dev); -#ifdef OMAP_SHAM_DMA_PRIVATE - omap_sham_dma_cleanup(dd); -#else dma_release_channel(dd->dma_lch); -#endif if (dd->irq >= 0) free_irq(dd->irq, dd); kfree(dd); From 03feec9cc67eaa21e9aa0d3aede0dfed0629f468 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:06 -0700 Subject: [PATCH 0561/4607] crypto: omap-sham - Add Device Tree Support Add Device Tree suport to the omap-sham crypto driver. Currently, only support for OMAP2 and OMAP3 is being added but support for OMAP4 will be added in a subsequent patch. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 141 ++++++++++++++++++++++++++++--------- 1 file changed, 107 insertions(+), 34 deletions(-) diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index f6b270ed7d62..860cad866a36 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -30,6 +30,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -145,6 +149,7 @@ struct omap_sham_dev { int irq; spinlock_t lock; int err; + unsigned int dma; struct dma_chan *dma_lch; struct tasklet_struct done_task; @@ -1155,13 +1160,99 @@ static irqreturn_t omap_sham_irq(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_OF +static const struct of_device_id omap_sham_of_match[] = { + { + .compatible = "ti,omap2-sham", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_sham_of_match); + +static int omap_sham_get_res_of(struct omap_sham_dev *dd, + struct device *dev, struct resource *res) +{ + struct device_node *node = dev->of_node; + const struct of_device_id *match; + int err = 0; + + match = of_match_device(of_match_ptr(omap_sham_of_match), dev); + if (!match) { + dev_err(dev, "no compatible OF match\n"); + err = -EINVAL; + goto err; + } + + err = of_address_to_resource(node, 0, res); + if (err < 0) { + dev_err(dev, "can't translate OF node address\n"); + err = -EINVAL; + goto err; + } + + dd->irq = of_irq_to_resource(node, 0, NULL); + if (!dd->irq) { + dev_err(dev, "can't translate OF irq value\n"); + err = -EINVAL; + goto err; + } + + dd->dma = -1; /* Dummy value that's unused */ + +err: + return err; +} +#else +static int omap_sham_get_res_dev(struct omap_sham_dev *dd, + struct device *dev, struct resource *res) +{ + return -EINVAL; +} +#endif + +static int omap_sham_get_res_pdev(struct omap_sham_dev *dd, + struct platform_device *pdev, struct resource *res) +{ + struct device *dev = &pdev->dev; + struct resource *r; + int err = 0; + + /* Get the base address */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(dev, "no MEM resource info\n"); + err = -ENODEV; + goto err; + } + memcpy(res, r, sizeof(*res)); + + /* Get the IRQ */ + dd->irq = platform_get_irq(pdev, 0); + if (dd->irq < 0) { + dev_err(dev, "no IRQ resource info\n"); + err = dd->irq; + goto err; + } + + /* Get the DMA */ + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!r) { + dev_err(dev, "no DMA resource info\n"); + err = -ENODEV; + goto err; + } + dd->dma = r->start; + +err: + return err; +} + static int __devinit omap_sham_probe(struct platform_device *pdev) { struct omap_sham_dev *dd; struct device *dev = &pdev->dev; - struct resource *res; + struct resource res; dma_cap_mask_t mask; - unsigned dma_chan; int err, i, j; dd = kzalloc(sizeof(struct omap_sham_dev), GFP_KERNEL); @@ -1178,33 +1269,18 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) tasklet_init(&dd->done_task, omap_sham_done_task, (unsigned long)dd); crypto_init_queue(&dd->queue, OMAP_SHAM_QUEUE_LENGTH); - dd->irq = -1; + err = (dev->of_node) ? omap_sham_get_res_of(dd, dev, &res) : + omap_sham_get_res_pdev(dd, pdev, &res); + if (err) + goto res_err; - /* Get the base address */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "no MEM resource info\n"); - err = -ENODEV; - goto res_err; - } - dd->phys_base = res->start; - - /* Get the DMA */ - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(dev, "no DMA resource info\n"); - err = -ENODEV; - goto res_err; - } - dma_chan = res->start; - - /* Get the IRQ */ - dd->irq = platform_get_irq(pdev, 0); - if (dd->irq < 0) { - dev_err(dev, "no IRQ resource info\n"); - err = dd->irq; + dd->io_base = devm_request_and_ioremap(dev, &res); + if (!dd->io_base) { + dev_err(dev, "can't ioremap\n"); + err = -ENOMEM; goto res_err; } + dd->phys_base = res.start; err = request_irq(dd->irq, omap_sham_irq, IRQF_TRIGGER_LOW, dev_name(dev), dd); @@ -1216,10 +1292,10 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - dd->dma_lch = dma_request_channel(mask, omap_dma_filter_fn, &dma_chan); + dd->dma_lch = dma_request_channel(mask, omap_dma_filter_fn, &dd->dma); if (!dd->dma_lch) { dev_err(dev, "unable to obtain RX DMA engine channel %u\n", - dma_chan); + dd->dma); err = -ENXIO; goto dma_err; } @@ -1255,13 +1331,11 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) err_algs: for (j = 0; j < i; j++) crypto_unregister_ahash(&algs[j]); - iounmap(dd->io_base); pm_runtime_disable(dev); io_err: dma_release_channel(dd->dma_lch); dma_err: - if (dd->irq >= 0) - free_irq(dd->irq, dd); + free_irq(dd->irq, dd); res_err: kfree(dd); dd = NULL; @@ -1285,11 +1359,9 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(algs); i++) crypto_unregister_ahash(&algs[i]); tasklet_kill(&dd->done_task); - iounmap(dd->io_base); pm_runtime_disable(&pdev->dev); dma_release_channel(dd->dma_lch); - if (dd->irq >= 0) - free_irq(dd->irq, dd); + free_irq(dd->irq, dd); kfree(dd); dd = NULL; @@ -1321,6 +1393,7 @@ static struct platform_driver omap_sham_driver = { .name = "omap-sham", .owner = THIS_MODULE, .pm = &omap_sham_pm_ops, + .of_match_table = omap_sham_of_match, }, }; From 0e87e73f4abe1ada69cf780fe2550c6361a1b53b Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:07 -0700 Subject: [PATCH 0562/4607] crypto: omap-sham - Convert to dma_request_slave_channel_compat() Use the dma_request_slave_channel_compat() call instead of the dma_request_channel() call to request a DMA channel. This allows the omap-sham driver use different DMA engines. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 860cad866a36..8074bd9947d1 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -1292,7 +1292,8 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - dd->dma_lch = dma_request_channel(mask, omap_dma_filter_fn, &dd->dma); + dd->dma_lch = dma_request_slave_channel_compat(mask, omap_dma_filter_fn, + &dd->dma, dev, "rx"); if (!dd->dma_lch) { dev_err(dev, "unable to obtain RX DMA engine channel %u\n", dd->dma); From 0d373d603202b8bfecc87b9b3602e6ffbf9e4feb Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:08 -0700 Subject: [PATCH 0563/4607] crypto: omap-sham - Add OMAP4/AM33XX SHAM Support Add support for the OMAP4 version of the SHAM module that is present on OMAP4 and AM33xx SoCs. The modules have several differences including register offsets, hardware XORing, and how DMA is triggered. To handle these differences, a platform_data structure is defined and contains routine pointers, register offsets, bit shifts within registers, and flags to indicate whether the hardware supports XORing and provides SHA1 results in big or little endian. OMAP2/OMAP3-specific routines are suffixed with '_omap2' and OMAP4/AM33xx routines are suffixed with '_omap4'. Note: The code being integrated is from the TI AM33xx SDK and was written by Greg Turner and Herman Schuurman (current email unknown) while at TI. CC: Greg Turner CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 422 +++++++++++++++++++++++++++++-------- 1 file changed, 335 insertions(+), 87 deletions(-) diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 8074bd9947d1..fab0af488b83 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -5,6 +5,7 @@ * * Copyright (c) 2010 Nokia Corporation * Author: Dmitry Kasatkin + * Copyright (c) 2011 Texas Instruments Incorporated * * 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 @@ -43,16 +44,17 @@ #include #include -#define SHA_REG_DIGEST(x) (0x00 + ((x) * 0x04)) -#define SHA_REG_DIN(x) (0x1C + ((x) * 0x04)) - #define SHA1_MD5_BLOCK_SIZE SHA1_BLOCK_SIZE #define MD5_DIGEST_SIZE 16 #define DST_MAXBURST 16 #define DMA_MIN (DST_MAXBURST * sizeof(u32)) -#define SHA_REG_DIGCNT 0x14 +#define SHA_REG_IDIGEST(dd, x) ((dd)->pdata->idigest_ofs + ((x)*0x04)) +#define SHA_REG_DIN(dd, x) ((dd)->pdata->din_ofs + ((x) * 0x04)) +#define SHA_REG_DIGCNT(dd) ((dd)->pdata->digcnt_ofs) + +#define SHA_REG_ODIGEST(x) (0x00 + ((x) * 0x04)) #define SHA_REG_CTRL 0x18 #define SHA_REG_CTRL_LENGTH (0xFFFFFFFF << 5) @@ -62,19 +64,40 @@ #define SHA_REG_CTRL_INPUT_READY (1 << 1) #define SHA_REG_CTRL_OUTPUT_READY (1 << 0) -#define SHA_REG_REV 0x5C -#define SHA_REG_REV_MAJOR 0xF0 -#define SHA_REG_REV_MINOR 0x0F +#define SHA_REG_REV(dd) ((dd)->pdata->rev_ofs) -#define SHA_REG_MASK 0x60 +#define SHA_REG_MASK(dd) ((dd)->pdata->mask_ofs) #define SHA_REG_MASK_DMA_EN (1 << 3) #define SHA_REG_MASK_IT_EN (1 << 2) #define SHA_REG_MASK_SOFTRESET (1 << 1) #define SHA_REG_AUTOIDLE (1 << 0) -#define SHA_REG_SYSSTATUS 0x64 +#define SHA_REG_SYSSTATUS(dd) ((dd)->pdata->sysstatus_ofs) #define SHA_REG_SYSSTATUS_RESETDONE (1 << 0) +#define SHA_REG_MODE 0x44 +#define SHA_REG_MODE_HMAC_OUTER_HASH (1 << 7) +#define SHA_REG_MODE_HMAC_KEY_PROC (1 << 5) +#define SHA_REG_MODE_CLOSE_HASH (1 << 4) +#define SHA_REG_MODE_ALGO_CONSTANT (1 << 3) +#define SHA_REG_MODE_ALGO_MASK (3 << 1) +#define SHA_REG_MODE_ALGO_MD5_128 (0 << 1) +#define SHA_REG_MODE_ALGO_SHA1_160 (1 << 1) + +#define SHA_REG_LENGTH 0x48 + +#define SHA_REG_IRQSTATUS 0x118 +#define SHA_REG_IRQSTATUS_CTX_RDY (1 << 3) +#define SHA_REG_IRQSTATUS_PARTHASH_RDY (1 << 2) +#define SHA_REG_IRQSTATUS_INPUT_RDY (1 << 1) +#define SHA_REG_IRQSTATUS_OUTPUT_RDY (1 << 0) + +#define SHA_REG_IRQENA 0x11C +#define SHA_REG_IRQENA_CTX_RDY (1 << 3) +#define SHA_REG_IRQENA_PARTHASH_RDY (1 << 2) +#define SHA_REG_IRQENA_INPUT_RDY (1 << 1) +#define SHA_REG_IRQENA_OUTPUT_RDY (1 << 0) + #define DEFAULT_TIMEOUT_INTERVAL HZ /* mostly device flags */ @@ -85,20 +108,29 @@ #define FLAGS_INIT 4 #define FLAGS_CPU 5 #define FLAGS_DMA_READY 6 +#define FLAGS_AUTO_XOR 7 +#define FLAGS_BE32_SHA1 8 /* context flags */ #define FLAGS_FINUP 16 #define FLAGS_SG 17 -#define FLAGS_SHA1 18 -#define FLAGS_HMAC 19 -#define FLAGS_ERROR 20 -#define OP_UPDATE 1 -#define OP_FINAL 2 +#define FLAGS_MODE_SHIFT 18 +#define FLAGS_MODE_MASK (SHA_REG_MODE_ALGO_MASK \ + << (FLAGS_MODE_SHIFT - 1)) +#define FLAGS_MODE_MD5 (SHA_REG_MODE_ALGO_MD5_128 \ + << (FLAGS_MODE_SHIFT - 1)) +#define FLAGS_MODE_SHA1 (SHA_REG_MODE_ALGO_SHA1_160 \ + << (FLAGS_MODE_SHIFT - 1)) +#define FLAGS_HMAC 20 +#define FLAGS_ERROR 21 + +#define OP_UPDATE 1 +#define OP_FINAL 2 #define OMAP_ALIGN_MASK (sizeof(u32)-1) #define OMAP_ALIGNED __attribute__((aligned(sizeof(u32)))) -#define BUFLEN PAGE_SIZE +#define BUFLEN PAGE_SIZE struct omap_sham_dev; @@ -107,7 +139,7 @@ struct omap_sham_reqctx { unsigned long flags; unsigned long op; - u8 digest[SHA1_DIGEST_SIZE] OMAP_ALIGNED; + u8 digest[SHA256_DIGEST_SIZE] OMAP_ALIGNED; size_t digcnt; size_t bufcnt; size_t buflen; @@ -124,8 +156,8 @@ struct omap_sham_reqctx { struct omap_sham_hmac_ctx { struct crypto_shash *shash; - u8 ipad[SHA1_MD5_BLOCK_SIZE]; - u8 opad[SHA1_MD5_BLOCK_SIZE]; + u8 ipad[SHA1_MD5_BLOCK_SIZE] OMAP_ALIGNED; + u8 opad[SHA1_MD5_BLOCK_SIZE] OMAP_ALIGNED; }; struct omap_sham_ctx { @@ -141,6 +173,31 @@ struct omap_sham_ctx { #define OMAP_SHAM_QUEUE_LENGTH 1 +struct omap_sham_pdata { + unsigned long flags; + int digest_size; + + void (*copy_hash)(struct ahash_request *req, int out); + void (*write_ctrl)(struct omap_sham_dev *dd, size_t length, + int final, int dma); + void (*trigger)(struct omap_sham_dev *dd, size_t length); + int (*poll_irq)(struct omap_sham_dev *dd); + irqreturn_t (*intr_hdlr)(int irq, void *dev_id); + + u32 odigest_ofs; + u32 idigest_ofs; + u32 din_ofs; + u32 digcnt_ofs; + u32 rev_ofs; + u32 mask_ofs; + u32 sysstatus_ofs; + + u32 major_mask; + u32 major_shift; + u32 minor_mask; + u32 minor_shift; +}; + struct omap_sham_dev { struct list_head list; unsigned long phys_base; @@ -156,6 +213,8 @@ struct omap_sham_dev { unsigned long flags; struct crypto_queue queue; struct ahash_request *req; + + const struct omap_sham_pdata *pdata; }; struct omap_sham_drv { @@ -203,42 +262,76 @@ static inline int omap_sham_wait(struct omap_sham_dev *dd, u32 offset, u32 bit) return 0; } -static void omap_sham_copy_hash(struct ahash_request *req, int out) +static void omap_sham_copy_hash_omap2(struct ahash_request *req, int out) { struct omap_sham_reqctx *ctx = ahash_request_ctx(req); + struct omap_sham_dev *dd = ctx->dd; u32 *hash = (u32 *)ctx->digest; int i; - /* MD5 is almost unused. So copy sha1 size to reduce code */ - for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++) { + for (i = 0; i < dd->pdata->digest_size / sizeof(u32); i++) { if (out) - hash[i] = omap_sham_read(ctx->dd, - SHA_REG_DIGEST(i)); + hash[i] = omap_sham_read(dd, SHA_REG_IDIGEST(dd, i)); else - omap_sham_write(ctx->dd, - SHA_REG_DIGEST(i), hash[i]); + omap_sham_write(dd, SHA_REG_IDIGEST(dd, i), hash[i]); } } +static void omap_sham_copy_hash_omap4(struct ahash_request *req, int out) +{ + struct omap_sham_reqctx *ctx = ahash_request_ctx(req); + struct omap_sham_dev *dd = ctx->dd; + int i; + + if (ctx->flags & BIT(FLAGS_HMAC)) { + struct crypto_ahash *tfm = crypto_ahash_reqtfm(dd->req); + struct omap_sham_ctx *tctx = crypto_ahash_ctx(tfm); + struct omap_sham_hmac_ctx *bctx = tctx->base; + u32 *opad = (u32 *)bctx->opad; + + for (i = 0; i < dd->pdata->digest_size / sizeof(u32); i++) { + if (out) + opad[i] = omap_sham_read(dd, + SHA_REG_ODIGEST(i)); + else + omap_sham_write(dd, SHA_REG_ODIGEST(i), + opad[i]); + } + } + + omap_sham_copy_hash_omap2(req, out); +} + static void omap_sham_copy_ready_hash(struct ahash_request *req) { struct omap_sham_reqctx *ctx = ahash_request_ctx(req); u32 *in = (u32 *)ctx->digest; u32 *hash = (u32 *)req->result; - int i; + int i, d, big_endian = 0; if (!hash) return; - if (likely(ctx->flags & BIT(FLAGS_SHA1))) { - /* SHA1 results are in big endian */ - for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++) - hash[i] = be32_to_cpu(in[i]); - } else { - /* MD5 results are in little endian */ - for (i = 0; i < MD5_DIGEST_SIZE / sizeof(u32); i++) - hash[i] = le32_to_cpu(in[i]); + switch (ctx->flags & FLAGS_MODE_MASK) { + case FLAGS_MODE_MD5: + d = MD5_DIGEST_SIZE / sizeof(u32); + break; + case FLAGS_MODE_SHA1: + /* OMAP2 SHA1 is big endian */ + if (test_bit(FLAGS_BE32_SHA1, &ctx->dd->flags)) + big_endian = 1; + d = SHA1_DIGEST_SIZE / sizeof(u32); + break; + default: + d = 0; } + + if (big_endian) + for (i = 0; i < d; i++) + hash[i] = be32_to_cpu(in[i]); + else + for (i = 0; i < d; i++) + hash[i] = le32_to_cpu(in[i]); } static int omap_sham_hw_init(struct omap_sham_dev *dd) @@ -246,13 +339,6 @@ static int omap_sham_hw_init(struct omap_sham_dev *dd) pm_runtime_get_sync(dd->dev); if (!test_bit(FLAGS_INIT, &dd->flags)) { - omap_sham_write_mask(dd, SHA_REG_MASK, - SHA_REG_MASK_SOFTRESET, SHA_REG_MASK_SOFTRESET); - - if (omap_sham_wait(dd, SHA_REG_SYSSTATUS, - SHA_REG_SYSSTATUS_RESETDONE)) - return -ETIMEDOUT; - set_bit(FLAGS_INIT, &dd->flags); dd->err = 0; } @@ -260,23 +346,23 @@ static int omap_sham_hw_init(struct omap_sham_dev *dd) return 0; } -static void omap_sham_write_ctrl(struct omap_sham_dev *dd, size_t length, +static void omap_sham_write_ctrl_omap2(struct omap_sham_dev *dd, size_t length, int final, int dma) { struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); u32 val = length << 5, mask; if (likely(ctx->digcnt)) - omap_sham_write(dd, SHA_REG_DIGCNT, ctx->digcnt); + omap_sham_write(dd, SHA_REG_DIGCNT(dd), ctx->digcnt); - omap_sham_write_mask(dd, SHA_REG_MASK, + omap_sham_write_mask(dd, SHA_REG_MASK(dd), SHA_REG_MASK_IT_EN | (dma ? SHA_REG_MASK_DMA_EN : 0), SHA_REG_MASK_IT_EN | SHA_REG_MASK_DMA_EN); /* * Setting ALGO_CONST only for the first iteration * and CLOSE_HASH only for the last one. */ - if (ctx->flags & BIT(FLAGS_SHA1)) + if ((ctx->flags & FLAGS_MODE_MASK) == FLAGS_MODE_SHA1) val |= SHA_REG_CTRL_ALGO; if (!ctx->digcnt) val |= SHA_REG_CTRL_ALGO_CONST; @@ -289,6 +375,81 @@ static void omap_sham_write_ctrl(struct omap_sham_dev *dd, size_t length, omap_sham_write_mask(dd, SHA_REG_CTRL, val, mask); } +static void omap_sham_trigger_omap2(struct omap_sham_dev *dd, size_t length) +{ +} + +static int omap_sham_poll_irq_omap2(struct omap_sham_dev *dd) +{ + return omap_sham_wait(dd, SHA_REG_CTRL, SHA_REG_CTRL_INPUT_READY); +} + +static void omap_sham_write_n(struct omap_sham_dev *dd, u32 offset, + u32 *value, int count) +{ + for (; count--; value++, offset += 4) + omap_sham_write(dd, offset, *value); +} + +static void omap_sham_write_ctrl_omap4(struct omap_sham_dev *dd, size_t length, + int final, int dma) +{ + struct omap_sham_reqctx *ctx = ahash_request_ctx(dd->req); + u32 val, mask; + + /* + * Setting ALGO_CONST only for the first iteration and + * CLOSE_HASH only for the last one. Note that flags mode bits + * correspond to algorithm encoding in mode register. + */ + val = (ctx->flags & FLAGS_MODE_MASK) >> (FLAGS_MODE_SHIFT - 1); + if (!ctx->digcnt) { + struct crypto_ahash *tfm = crypto_ahash_reqtfm(dd->req); + struct omap_sham_ctx *tctx = crypto_ahash_ctx(tfm); + struct omap_sham_hmac_ctx *bctx = tctx->base; + + val |= SHA_REG_MODE_ALGO_CONSTANT; + + if (ctx->flags & BIT(FLAGS_HMAC)) { + val |= SHA_REG_MODE_HMAC_KEY_PROC; + omap_sham_write_n(dd, SHA_REG_ODIGEST(0), + (u32 *)bctx->ipad, + SHA1_BLOCK_SIZE / sizeof(u32)); + ctx->digcnt += SHA1_BLOCK_SIZE; + } + } + + if (final) { + val |= SHA_REG_MODE_CLOSE_HASH; + + if (ctx->flags & BIT(FLAGS_HMAC)) + val |= SHA_REG_MODE_HMAC_OUTER_HASH; + } + + mask = SHA_REG_MODE_ALGO_CONSTANT | SHA_REG_MODE_CLOSE_HASH | + SHA_REG_MODE_ALGO_MASK | SHA_REG_MODE_HMAC_OUTER_HASH | + SHA_REG_MODE_HMAC_KEY_PROC; + + dev_dbg(dd->dev, "ctrl: %08x, flags: %08lx\n", val, ctx->flags); + omap_sham_write_mask(dd, SHA_REG_MODE, val, mask); + omap_sham_write(dd, SHA_REG_IRQENA, SHA_REG_IRQENA_OUTPUT_RDY); + omap_sham_write_mask(dd, SHA_REG_MASK(dd), + SHA_REG_MASK_IT_EN | + (dma ? SHA_REG_MASK_DMA_EN : 0), + SHA_REG_MASK_IT_EN | SHA_REG_MASK_DMA_EN); +} + +static void omap_sham_trigger_omap4(struct omap_sham_dev *dd, size_t length) +{ + omap_sham_write(dd, SHA_REG_LENGTH, length); +} + +static int omap_sham_poll_irq_omap4(struct omap_sham_dev *dd) +{ + return omap_sham_wait(dd, SHA_REG_IRQSTATUS, + SHA_REG_IRQSTATUS_INPUT_RDY); +} + static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf, size_t length, int final) { @@ -299,12 +460,13 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf, dev_dbg(dd->dev, "xmit_cpu: digcnt: %d, length: %d, final: %d\n", ctx->digcnt, length, final); - omap_sham_write_ctrl(dd, length, final, 0); + dd->pdata->write_ctrl(dd, length, final, 0); + dd->pdata->trigger(dd, length); /* should be non-zero before next lines to disable clocks later */ ctx->digcnt += length; - if (omap_sham_wait(dd, SHA_REG_CTRL, SHA_REG_CTRL_INPUT_READY)) + if (dd->pdata->poll_irq(dd)) return -ETIMEDOUT; if (final) @@ -315,7 +477,7 @@ static int omap_sham_xmit_cpu(struct omap_sham_dev *dd, const u8 *buf, len32 = DIV_ROUND_UP(length, sizeof(u32)); for (count = 0; count < len32; count++) - omap_sham_write(dd, SHA_REG_DIN(count), buffer[count]); + omap_sham_write(dd, SHA_REG_DIN(dd, count), buffer[count]); return -EINPROGRESS; } @@ -341,7 +503,7 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, memset(&cfg, 0, sizeof(cfg)); - cfg.dst_addr = dd->phys_base + SHA_REG_DIN(0); + cfg.dst_addr = dd->phys_base + SHA_REG_DIN(dd, 0); cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.dst_maxburst = DST_MAXBURST; @@ -381,7 +543,7 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, tx->callback = omap_sham_dma_callback; tx->callback_param = dd; - omap_sham_write_ctrl(dd, length, final, 1); + dd->pdata->write_ctrl(dd, length, final, 1); ctx->digcnt += length; @@ -393,6 +555,8 @@ static int omap_sham_xmit_dma(struct omap_sham_dev *dd, dma_addr_t dma_addr, dmaengine_submit(tx); dma_async_issue_pending(dd->dma_lch); + dd->pdata->trigger(dd, length); + return -EINPROGRESS; } @@ -450,7 +614,7 @@ static int omap_sham_xmit_dma_map(struct omap_sham_dev *dd, ctx->flags &= ~BIT(FLAGS_SG); ret = omap_sham_xmit_dma(dd, ctx->dma_addr, length, final, 0); - if (ret) + if (ret != -EINPROGRESS) dma_unmap_single(dd->dev, ctx->dma_addr, ctx->buflen, DMA_TO_DEVICE); @@ -544,7 +708,7 @@ static int omap_sham_update_dma_start(struct omap_sham_dev *dd) final = (ctx->flags & BIT(FLAGS_FINUP)) && !ctx->total; ret = omap_sham_xmit_dma(dd, sg_dma_address(ctx->sg), length, final, 1); - if (ret) + if (ret != -EINPROGRESS) dma_unmap_sg(dd->dev, ctx->sg, 1, DMA_TO_DEVICE); return ret; @@ -609,18 +773,27 @@ static int omap_sham_init(struct ahash_request *req) dev_dbg(dd->dev, "init: digest size: %d\n", crypto_ahash_digestsize(tfm)); - if (crypto_ahash_digestsize(tfm) == SHA1_DIGEST_SIZE) - ctx->flags |= BIT(FLAGS_SHA1); + switch (crypto_ahash_digestsize(tfm)) { + case MD5_DIGEST_SIZE: + ctx->flags |= FLAGS_MODE_MD5; + break; + case SHA1_DIGEST_SIZE: + ctx->flags |= FLAGS_MODE_SHA1; + break; + } ctx->bufcnt = 0; ctx->digcnt = 0; ctx->buflen = BUFLEN; if (tctx->flags & BIT(FLAGS_HMAC)) { - struct omap_sham_hmac_ctx *bctx = tctx->base; + if (!test_bit(FLAGS_AUTO_XOR, &dd->flags)) { + struct omap_sham_hmac_ctx *bctx = tctx->base; + + memcpy(ctx->buffer, bctx->ipad, SHA1_MD5_BLOCK_SIZE); + ctx->bufcnt = SHA1_MD5_BLOCK_SIZE; + } - memcpy(ctx->buffer, bctx->ipad, SHA1_MD5_BLOCK_SIZE); - ctx->bufcnt = SHA1_MD5_BLOCK_SIZE; ctx->flags |= BIT(FLAGS_HMAC); } @@ -697,7 +870,8 @@ static int omap_sham_finish(struct ahash_request *req) if (ctx->digcnt) { omap_sham_copy_ready_hash(req); - if (ctx->flags & BIT(FLAGS_HMAC)) + if ((ctx->flags & BIT(FLAGS_HMAC)) && + !test_bit(FLAGS_AUTO_XOR, &dd->flags)) err = omap_sham_finish_hmac(req); } @@ -712,7 +886,7 @@ static void omap_sham_finish_req(struct ahash_request *req, int err) struct omap_sham_dev *dd = ctx->dd; if (!err) { - omap_sham_copy_hash(req, 1); + dd->pdata->copy_hash(req, 1); if (test_bit(FLAGS_FINAL, &dd->flags)) err = omap_sham_finish(req); } else { @@ -772,7 +946,7 @@ static int omap_sham_handle_queue(struct omap_sham_dev *dd, if (ctx->digcnt) /* request has changed - restore hash */ - omap_sham_copy_hash(req, 0); + dd->pdata->copy_hash(req, 0); if (ctx->op == OP_UPDATE) { err = omap_sham_update_req(dd); @@ -911,7 +1085,21 @@ static int omap_sham_setkey(struct crypto_ahash *tfm, const u8 *key, struct omap_sham_hmac_ctx *bctx = tctx->base; int bs = crypto_shash_blocksize(bctx->shash); int ds = crypto_shash_digestsize(bctx->shash); + struct omap_sham_dev *dd = NULL, *tmp; int err, i; + + spin_lock_bh(&sham.lock); + if (!tctx->dd) { + list_for_each_entry(tmp, &sham.dev_list, list) { + dd = tmp; + break; + } + tctx->dd = dd; + } else { + dd = tctx->dd; + } + spin_unlock_bh(&sham.lock); + err = crypto_shash_setkey(tctx->fallback, key, keylen); if (err) return err; @@ -928,11 +1116,14 @@ static int omap_sham_setkey(struct crypto_ahash *tfm, const u8 *key, } memset(bctx->ipad + keylen, 0, bs - keylen); - memcpy(bctx->opad, bctx->ipad, bs); - for (i = 0; i < bs; i++) { - bctx->ipad[i] ^= 0x36; - bctx->opad[i] ^= 0x5c; + if (!test_bit(FLAGS_AUTO_XOR, &dd->flags)) { + memcpy(bctx->opad, bctx->ipad, bs); + + for (i = 0; i < bs; i++) { + bctx->ipad[i] ^= 0x36; + bctx->opad[i] ^= 0x5c; + } } return err; @@ -1137,7 +1328,19 @@ finish: omap_sham_finish_req(dd->req, err); } -static irqreturn_t omap_sham_irq(int irq, void *dev_id) +static irqreturn_t omap_sham_irq_common(struct omap_sham_dev *dd) +{ + if (!test_bit(FLAGS_BUSY, &dd->flags)) { + dev_warn(dd->dev, "Interrupt when no active requests.\n"); + } else { + set_bit(FLAGS_OUTPUT_READY, &dd->flags); + tasklet_schedule(&dd->done_task); + } + + return IRQ_HANDLED; +} + +static irqreturn_t omap_sham_irq_omap2(int irq, void *dev_id) { struct omap_sham_dev *dd = dev_id; @@ -1149,21 +1352,67 @@ static irqreturn_t omap_sham_irq(int irq, void *dev_id) SHA_REG_CTRL_OUTPUT_READY); omap_sham_read(dd, SHA_REG_CTRL); - if (!test_bit(FLAGS_BUSY, &dd->flags)) { - dev_warn(dd->dev, "Interrupt when no active requests.\n"); - return IRQ_HANDLED; - } - - set_bit(FLAGS_OUTPUT_READY, &dd->flags); - tasklet_schedule(&dd->done_task); - - return IRQ_HANDLED; + return omap_sham_irq_common(dd); } +static irqreturn_t omap_sham_irq_omap4(int irq, void *dev_id) +{ + struct omap_sham_dev *dd = dev_id; + + omap_sham_write_mask(dd, SHA_REG_MASK(dd), 0, SHA_REG_MASK_IT_EN); + + return omap_sham_irq_common(dd); +} + +static const struct omap_sham_pdata omap_sham_pdata_omap2 = { + .flags = BIT(FLAGS_BE32_SHA1), + .digest_size = SHA1_DIGEST_SIZE, + .copy_hash = omap_sham_copy_hash_omap2, + .write_ctrl = omap_sham_write_ctrl_omap2, + .trigger = omap_sham_trigger_omap2, + .poll_irq = omap_sham_poll_irq_omap2, + .intr_hdlr = omap_sham_irq_omap2, + .idigest_ofs = 0x00, + .din_ofs = 0x1c, + .digcnt_ofs = 0x14, + .rev_ofs = 0x5c, + .mask_ofs = 0x60, + .sysstatus_ofs = 0x64, + .major_mask = 0xf0, + .major_shift = 4, + .minor_mask = 0x0f, + .minor_shift = 0, +}; + #ifdef CONFIG_OF +static const struct omap_sham_pdata omap_sham_pdata_omap4 = { + .flags = BIT(FLAGS_AUTO_XOR), + .digest_size = SHA256_DIGEST_SIZE, + .copy_hash = omap_sham_copy_hash_omap4, + .write_ctrl = omap_sham_write_ctrl_omap4, + .trigger = omap_sham_trigger_omap4, + .poll_irq = omap_sham_poll_irq_omap4, + .intr_hdlr = omap_sham_irq_omap4, + .idigest_ofs = 0x020, + .din_ofs = 0x080, + .digcnt_ofs = 0x040, + .rev_ofs = 0x100, + .mask_ofs = 0x110, + .sysstatus_ofs = 0x114, + .major_mask = 0x0700, + .major_shift = 8, + .minor_mask = 0x003f, + .minor_shift = 0, +}; + static const struct of_device_id omap_sham_of_match[] = { { .compatible = "ti,omap2-sham", + .data = &omap_sham_pdata_omap2, + }, + { + .compatible = "ti,omap4-sham", + .data = &omap_sham_pdata_omap4, }, {}, }; @@ -1198,6 +1447,7 @@ static int omap_sham_get_res_of(struct omap_sham_dev *dd, } dd->dma = -1; /* Dummy value that's unused */ + dd->pdata = match->data; err: return err; @@ -1243,6 +1493,9 @@ static int omap_sham_get_res_pdev(struct omap_sham_dev *dd, } dd->dma = r->start; + /* Only OMAP2/3 can be non-DT */ + dd->pdata = &omap_sham_pdata_omap2; + err: return err; } @@ -1254,6 +1507,7 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) struct resource res; dma_cap_mask_t mask; int err, i, j; + u32 rev; dd = kzalloc(sizeof(struct omap_sham_dev), GFP_KERNEL); if (dd == NULL) { @@ -1282,8 +1536,8 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) } dd->phys_base = res.start; - err = request_irq(dd->irq, omap_sham_irq, - IRQF_TRIGGER_LOW, dev_name(dev), dd); + err = request_irq(dd->irq, dd->pdata->intr_hdlr, IRQF_TRIGGER_LOW, + dev_name(dev), dd); if (err) { dev_err(dev, "unable to request irq.\n"); goto res_err; @@ -1301,21 +1555,16 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) goto dma_err; } - dd->io_base = ioremap(dd->phys_base, SZ_4K); - if (!dd->io_base) { - dev_err(dev, "can't ioremap\n"); - err = -ENOMEM; - goto io_err; - } + dd->flags |= dd->pdata->flags; pm_runtime_enable(dev); pm_runtime_get_sync(dev); + rev = omap_sham_read(dd, SHA_REG_REV(dd)); + pm_runtime_put_sync(&pdev->dev); dev_info(dev, "hw accel on OMAP rev %u.%u\n", - (omap_sham_read(dd, SHA_REG_REV) & SHA_REG_REV_MAJOR) >> 4, - omap_sham_read(dd, SHA_REG_REV) & SHA_REG_REV_MINOR); - - pm_runtime_put_sync(&pdev->dev); + (rev & dd->pdata->major_mask) >> dd->pdata->major_shift, + (rev & dd->pdata->minor_mask) >> dd->pdata->minor_shift); spin_lock(&sham.lock); list_add_tail(&dd->list, &sham.dev_list); @@ -1333,7 +1582,6 @@ err_algs: for (j = 0; j < i; j++) crypto_unregister_ahash(&algs[j]); pm_runtime_disable(dev); -io_err: dma_release_channel(dd->dma_lch); dma_err: free_irq(dd->irq, dd); From d20fb18be246d196225ed151c126832b2dab6506 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 21 Dec 2012 10:04:09 -0700 Subject: [PATCH 0564/4607] crypto: omap-sham - Add SHA224 and SHA256 Support The OMAP4/AM33xx version of the SHAM crypto module supports SHA224 and SHA256 in addition to MD5 and SHA1 that the OMAP2 version of the module supports. To add this support, use the platform_data introduced in an ealier commit to hold the list of algorithms supported by the current module. The probe routine will use that list to register the correct algorithms. Note: The code being integrated is from the TI AM33xx SDK and was written by Greg Turner and Herman Schuurman (current email unknown) while at TI. CC: Greg Turner CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 183 +++++++++++++++++++++++++++++++++++-- 1 file changed, 173 insertions(+), 10 deletions(-) diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index fab0af488b83..edff981edfb1 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -83,6 +83,8 @@ #define SHA_REG_MODE_ALGO_MASK (3 << 1) #define SHA_REG_MODE_ALGO_MD5_128 (0 << 1) #define SHA_REG_MODE_ALGO_SHA1_160 (1 << 1) +#define SHA_REG_MODE_ALGO_SHA2_224 (2 << 1) +#define SHA_REG_MODE_ALGO_SHA2_256 (3 << 1) #define SHA_REG_LENGTH 0x48 @@ -121,6 +123,10 @@ << (FLAGS_MODE_SHIFT - 1)) #define FLAGS_MODE_SHA1 (SHA_REG_MODE_ALGO_SHA1_160 \ << (FLAGS_MODE_SHIFT - 1)) +#define FLAGS_MODE_SHA224 (SHA_REG_MODE_ALGO_SHA2_224 \ + << (FLAGS_MODE_SHIFT - 1)) +#define FLAGS_MODE_SHA256 (SHA_REG_MODE_ALGO_SHA2_256 \ + << (FLAGS_MODE_SHIFT - 1)) #define FLAGS_HMAC 20 #define FLAGS_ERROR 21 @@ -173,7 +179,15 @@ struct omap_sham_ctx { #define OMAP_SHAM_QUEUE_LENGTH 1 +struct omap_sham_algs_info { + struct ahash_alg *algs_list; + unsigned int size; + unsigned int registered; +}; + struct omap_sham_pdata { + struct omap_sham_algs_info *algs_info; + unsigned int algs_info_size; unsigned long flags; int digest_size; @@ -322,6 +336,12 @@ static void omap_sham_copy_ready_hash(struct ahash_request *req) big_endian = 1; d = SHA1_DIGEST_SIZE / sizeof(u32); break; + case FLAGS_MODE_SHA224: + d = SHA224_DIGEST_SIZE / sizeof(u32); + break; + case FLAGS_MODE_SHA256: + d = SHA256_DIGEST_SIZE / sizeof(u32); + break; default: d = 0; } @@ -780,6 +800,12 @@ static int omap_sham_init(struct ahash_request *req) case SHA1_DIGEST_SIZE: ctx->flags |= FLAGS_MODE_SHA1; break; + case SHA224_DIGEST_SIZE: + ctx->flags |= FLAGS_MODE_SHA224; + break; + case SHA256_DIGEST_SIZE: + ctx->flags |= FLAGS_MODE_SHA256; + break; } ctx->bufcnt = 0; @@ -1173,6 +1199,16 @@ static int omap_sham_cra_sha1_init(struct crypto_tfm *tfm) return omap_sham_cra_init_alg(tfm, "sha1"); } +static int omap_sham_cra_sha224_init(struct crypto_tfm *tfm) +{ + return omap_sham_cra_init_alg(tfm, "sha224"); +} + +static int omap_sham_cra_sha256_init(struct crypto_tfm *tfm) +{ + return omap_sham_cra_init_alg(tfm, "sha256"); +} + static int omap_sham_cra_md5_init(struct crypto_tfm *tfm) { return omap_sham_cra_init_alg(tfm, "md5"); @@ -1191,7 +1227,7 @@ static void omap_sham_cra_exit(struct crypto_tfm *tfm) } } -static struct ahash_alg algs[] = { +static struct ahash_alg algs_sha1_md5[] = { { .init = omap_sham_init, .update = omap_sham_update, @@ -1290,6 +1326,102 @@ static struct ahash_alg algs[] = { } }; +/* OMAP4 has some algs in addition to what OMAP2 has */ +static struct ahash_alg algs_sha224_sha256[] = { +{ + .init = omap_sham_init, + .update = omap_sham_update, + .final = omap_sham_final, + .finup = omap_sham_finup, + .digest = omap_sham_digest, + .halg.digestsize = SHA224_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha224", + .cra_driver_name = "omap-sha224", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct omap_sham_ctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_init = omap_sham_cra_init, + .cra_exit = omap_sham_cra_exit, + } +}, +{ + .init = omap_sham_init, + .update = omap_sham_update, + .final = omap_sham_final, + .finup = omap_sham_finup, + .digest = omap_sham_digest, + .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.base = { + .cra_name = "sha256", + .cra_driver_name = "omap-sha256", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct omap_sham_ctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_init = omap_sham_cra_init, + .cra_exit = omap_sham_cra_exit, + } +}, +{ + .init = omap_sham_init, + .update = omap_sham_update, + .final = omap_sham_final, + .finup = omap_sham_finup, + .digest = omap_sham_digest, + .setkey = omap_sham_setkey, + .halg.digestsize = SHA224_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha224)", + .cra_driver_name = "omap-hmac-sha224", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct omap_sham_ctx) + + sizeof(struct omap_sham_hmac_ctx), + .cra_alignmask = OMAP_ALIGN_MASK, + .cra_module = THIS_MODULE, + .cra_init = omap_sham_cra_sha224_init, + .cra_exit = omap_sham_cra_exit, + } +}, +{ + .init = omap_sham_init, + .update = omap_sham_update, + .final = omap_sham_final, + .finup = omap_sham_finup, + .digest = omap_sham_digest, + .setkey = omap_sham_setkey, + .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.base = { + .cra_name = "hmac(sha256)", + .cra_driver_name = "omap-hmac-sha256", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct omap_sham_ctx) + + sizeof(struct omap_sham_hmac_ctx), + .cra_alignmask = OMAP_ALIGN_MASK, + .cra_module = THIS_MODULE, + .cra_init = omap_sham_cra_sha256_init, + .cra_exit = omap_sham_cra_exit, + } +}, +}; + static void omap_sham_done_task(unsigned long data) { struct omap_sham_dev *dd = (struct omap_sham_dev *)data; @@ -1364,7 +1496,16 @@ static irqreturn_t omap_sham_irq_omap4(int irq, void *dev_id) return omap_sham_irq_common(dd); } +static struct omap_sham_algs_info omap_sham_algs_info_omap2[] = { + { + .algs_list = algs_sha1_md5, + .size = ARRAY_SIZE(algs_sha1_md5), + }, +}; + static const struct omap_sham_pdata omap_sham_pdata_omap2 = { + .algs_info = omap_sham_algs_info_omap2, + .algs_info_size = ARRAY_SIZE(omap_sham_algs_info_omap2), .flags = BIT(FLAGS_BE32_SHA1), .digest_size = SHA1_DIGEST_SIZE, .copy_hash = omap_sham_copy_hash_omap2, @@ -1385,7 +1526,20 @@ static const struct omap_sham_pdata omap_sham_pdata_omap2 = { }; #ifdef CONFIG_OF +static struct omap_sham_algs_info omap_sham_algs_info_omap4[] = { + { + .algs_list = algs_sha1_md5, + .size = ARRAY_SIZE(algs_sha1_md5), + }, + { + .algs_list = algs_sha224_sha256, + .size = ARRAY_SIZE(algs_sha224_sha256), + }, +}; + static const struct omap_sham_pdata omap_sham_pdata_omap4 = { + .algs_info = omap_sham_algs_info_omap4, + .algs_info_size = ARRAY_SIZE(omap_sham_algs_info_omap4), .flags = BIT(FLAGS_AUTO_XOR), .digest_size = SHA256_DIGEST_SIZE, .copy_hash = omap_sham_copy_hash_omap4, @@ -1570,17 +1724,24 @@ static int __devinit omap_sham_probe(struct platform_device *pdev) list_add_tail(&dd->list, &sham.dev_list); spin_unlock(&sham.lock); - for (i = 0; i < ARRAY_SIZE(algs); i++) { - err = crypto_register_ahash(&algs[i]); - if (err) - goto err_algs; + for (i = 0; i < dd->pdata->algs_info_size; i++) { + for (j = 0; j < dd->pdata->algs_info[i].size; j++) { + err = crypto_register_ahash( + &dd->pdata->algs_info[i].algs_list[j]); + if (err) + goto err_algs; + + dd->pdata->algs_info[i].registered++; + } } return 0; err_algs: - for (j = 0; j < i; j++) - crypto_unregister_ahash(&algs[j]); + for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) + for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) + crypto_unregister_ahash( + &dd->pdata->algs_info[i].algs_list[j]); pm_runtime_disable(dev); dma_release_channel(dd->dma_lch); dma_err: @@ -1597,7 +1758,7 @@ data_err: static int __devexit omap_sham_remove(struct platform_device *pdev) { static struct omap_sham_dev *dd; - int i; + int i, j; dd = platform_get_drvdata(pdev); if (!dd) @@ -1605,8 +1766,10 @@ static int __devexit omap_sham_remove(struct platform_device *pdev) spin_lock(&sham.lock); list_del(&dd->list); spin_unlock(&sham.lock); - for (i = 0; i < ARRAY_SIZE(algs); i++) - crypto_unregister_ahash(&algs[i]); + for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) + for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) + crypto_unregister_ahash( + &dd->pdata->algs_info[i].algs_list[j]); tasklet_kill(&dd->done_task); pm_runtime_disable(&pdev->dev); dma_release_channel(dd->dma_lch); From ef85cd9cd5474e94638248955d035598789fc737 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 6 Jan 2013 00:34:22 -0200 Subject: [PATCH 0565/4607] [media] em28xx: enable DMABUF Now that it uses videobuf2, em28xx can support DMABUF. Tested with an HVR-950 on analog mode and a 2gen i5core machine with an i915 graphics adapter. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 75027e390738..2eabf2a94752 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -699,7 +699,7 @@ int em28xx_vb2_setup(struct em28xx *dev) /* Setup Videobuf2 for Video capture */ q = &dev->vb_vidq; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; q->drv_priv = dev; q->buf_struct_size = sizeof(struct em28xx_buffer); q->ops = &em28xx_video_qops; From e713ad1549209c10a8440d943a05056874a96015 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 2 Dec 2012 18:47:00 -0300 Subject: [PATCH 0566/4607] [media] af9033: add support for Fitipower FC0012 tuner Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033.c | 4 +++ drivers/media/dvb-frontends/af9033.h | 1 + drivers/media/dvb-frontends/af9033_priv.h | 43 +++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 464ad878490b..27638a9bb4a6 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -318,6 +318,10 @@ static int af9033_init(struct dvb_frontend *fe) len = ARRAY_SIZE(tuner_init_fc2580); init = tuner_init_fc2580; break; + case AF9033_TUNER_FC0012: + len = ARRAY_SIZE(tuner_init_fc0012); + init = tuner_init_fc0012; + break; default: dev_dbg(&state->i2c->dev, "%s: unsupported tuner ID=%d\n", __func__, state->cfg.tuner); diff --git a/drivers/media/dvb-frontends/af9033.h b/drivers/media/dvb-frontends/af9033.h index bfa4313fde21..82bd8c1513b6 100644 --- a/drivers/media/dvb-frontends/af9033.h +++ b/drivers/media/dvb-frontends/af9033.h @@ -40,6 +40,7 @@ struct af9033_config { */ #define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */ #define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */ +#define AF9033_TUNER_FC0012 0x2e /* Fitipower FC0012 */ #define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */ #define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */ #define AF9033_TUNER_FC2580 0x32 /* FCI FC2580 */ diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index 34dddcd77538..288cd4524547 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -397,6 +397,49 @@ static const struct reg_val tuner_init_fc0011[] = { { 0x80F1E6, 0x00 }, }; +/* Fitipower FC0012 tuner init + AF9033_TUNER_FC0012 = 0x2e */ +static const struct reg_val tuner_init_fc0012[] = { + { 0x800046, 0x2e }, + { 0x800057, 0x00 }, + { 0x800058, 0x01 }, + { 0x800059, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x80006d, 0x00 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800075, 0x03 }, + { 0x800076, 0x02 }, + { 0x800077, 0x01 }, + { 0x800078, 0x00 }, + { 0x800079, 0x00 }, + { 0x80007a, 0x90 }, + { 0x80007b, 0x90 }, + { 0x800093, 0x00 }, + { 0x800094, 0x01 }, + { 0x800095, 0x02 }, + { 0x800096, 0x01 }, + { 0x800098, 0x0a }, + { 0x80009b, 0x05 }, + { 0x80009c, 0x80 }, + { 0x8000b3, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000c9, 0x5d }, + { 0x80f007, 0x00 }, + { 0x80f01f, 0xa0 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x00 }, +}; + /* MaxLinear MxL5007T tuner init AF9033_TUNER_MXL5007T = 0xa0 */ static const struct reg_val tuner_init_mxl5007t[] = { From 7e0bc2960397c43019757aadc76c89da27120bea Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 2 Dec 2012 20:12:29 -0300 Subject: [PATCH 0567/4607] [media] af9035: support for Fitipower FC0012 tuner devices Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 26 ++++++++++++++++++++++++++ drivers/media/usb/dvb-usb-v2/af9035.h | 1 + 2 files changed, 27 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 61ae7f9d0b27..c1ec18ccb0ba 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -514,6 +514,7 @@ static int af9035_read_config(struct dvb_usb_device *d) case AF9033_TUNER_MXL5007T: case AF9033_TUNER_TDA18218: case AF9033_TUNER_FC2580: + case AF9033_TUNER_FC0012: state->af9033_config[i].spec_inv = 1; break; default: @@ -907,6 +908,31 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) fe = dvb_attach(fc2580_attach, adap->fe[0], &d->i2c_adap, &af9035_fc2580_config); break; + case AF9033_TUNER_FC0012: + /* + * AF9035 gpiot2 = FC0012 enable + * XXX: there seems to be something on gpioh8 too, but on my + * my test I didn't find any difference. + */ + + /* configure gpiot2 as output and high */ + ret = af9035_wr_reg_mask(d, 0xd8eb, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ec, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ed, 0x01, 0x01); + if (ret < 0) + goto err; + + usleep_range(10000, 50000); + + fe = dvb_attach(fc0012_attach, adap->fe[0], &d->i2c_adap, 0x63, + 1, FC_XTAL_36_MHZ); + break; default: fe = NULL; } diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index 75ef1ec13fbf..f509d35a3b00 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -26,6 +26,7 @@ #include "af9033.h" #include "tua9001.h" #include "fc0011.h" +#include "fc0012.h" #include "mxl5007t.h" #include "tda18218.h" #include "fc2580.h" From 9805992ffc59ec0271ec037bfb02fe8111691284 Mon Sep 17 00:00:00 2001 From: Jose Alberto Reguero Date: Sun, 23 Sep 2012 16:48:47 -0300 Subject: [PATCH 0568/4607] [media] af9035: dual mode support Adds initial support for af9035 dual mode designs. Signed-off-by: Jose Alberto Reguero [crope@iki.fi: fix merge conflict] Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033.c | 12 ++ drivers/media/usb/dvb-usb-v2/af9035.c | 155 ++++++++++++++++++-------- drivers/media/usb/dvb-usb-v2/af9035.h | 3 + 3 files changed, 123 insertions(+), 47 deletions(-) diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 27638a9bb4a6..745d2fa3fbdc 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -335,6 +335,18 @@ static int af9033_init(struct dvb_frontend *fe) goto err; } + if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { + ret = af9033_wr_reg_mask(state, 0x00d91c, 0x01, 0x01); + if (ret < 0) + goto err; + ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); + if (ret < 0) + goto err; + ret = af9033_wr_reg_mask(state, 0x00d916, 0x00, 0x01); + if (ret < 0) + goto err; + } + state->bandwidth_hz = 0; /* force to program all parameters */ return 0; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index c1ec18ccb0ba..15625eb28b56 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -209,10 +209,14 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (msg[0].len > 40 || msg[1].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; - } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { + } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) || + (msg[0].addr == state->af9033_config[1].i2c_addr)) { /* integrated demod */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; + if (state->af9033_config[1].i2c_addr && + (msg[0].addr == state->af9033_config[1].i2c_addr)) + reg |= 0x100000; ret = af9035_rd_regs(d, reg, &msg[1].buf[0], msg[1].len); } else { @@ -220,8 +224,9 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, u8 buf[5 + msg[0].len]; struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf), buf, msg[1].len, msg[1].buf }; + req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[1].len; - buf[1] = msg[0].addr << 1; + buf[1] = (u8)(msg[0].addr << 1); buf[2] = 0x00; /* reg addr len */ buf[3] = 0x00; /* reg addr MSB */ buf[4] = 0x00; /* reg addr LSB */ @@ -232,10 +237,14 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, if (msg[0].len > 40) { /* TODO: correct limits > 40 */ ret = -EOPNOTSUPP; - } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { + } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) || + (msg[0].addr == state->af9033_config[1].i2c_addr)) { /* integrated demod */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; + if (state->af9033_config[1].i2c_addr && + (msg[0].addr == state->af9033_config[1].i2c_addr)) + reg |= 0x100000; ret = af9035_wr_regs(d, reg, &msg[0].buf[3], msg[0].len - 3); } else { @@ -243,8 +252,9 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, u8 buf[5 + msg[0].len]; struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf, 0, NULL }; + req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[0].len; - buf[1] = msg[0].addr << 1; + buf[1] = (u8)(msg[0].addr << 1); buf[2] = 0x00; /* reg addr len */ buf[3] = 0x00; /* reg addr MSB */ buf[4] = 0x00; /* reg addr LSB */ @@ -283,9 +293,30 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) int ret; u8 wbuf[1] = { 1 }; u8 rbuf[4]; + u8 tmp; struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf, sizeof(rbuf), rbuf }; + /* check if there is dual tuners */ + ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); + if (ret < 0) + goto err; + + if (tmp) { + /* read 2nd demodulator I2C address */ + ret = af9035_rd_reg(d, EEPROM_2WIREADDR, &tmp); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00417f, tmp); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00d81a, 1); + if (ret < 0) + goto err; + } + ret = af9035_ctrl_msg(d, &req); if (ret < 0) goto err; @@ -498,6 +529,15 @@ static int af9035_read_config(struct dvb_usb_device *d) dev_dbg(&d->udev->dev, "%s: dual mode=%d\n", __func__, state->dual_mode); + if (state->dual_mode) { + /* read 2nd demodulator I2C address */ + ret = af9035_rd_reg(d, EEPROM_2WIREADDR, &tmp); + if (ret < 0) + goto err; + state->af9033_config[1].i2c_addr = tmp; + pr_debug("%s: 2nd demod I2C addr:%02x\n", __func__, tmp); + } + for (i = 0; i < state->dual_mode + 1; i++) { /* tuner */ ret = af9035_rd_reg(d, EEPROM_1_TUNER_ID + eeprom_shift, &tmp); @@ -731,6 +771,12 @@ static int af9035_frontend_callback(void *adapter_priv, int component, return 0; } +static int af9035_get_adapter_count(struct dvb_usb_device *d) +{ + struct state *state = d_to_priv(d); + return state->dual_mode + 1; +} + static int af9035_frontend_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); @@ -786,13 +832,22 @@ static const struct fc0011_config af9035_fc0011_config = { .i2c_address = 0x60, }; -static struct mxl5007t_config af9035_mxl5007t_config = { - .xtal_freq_hz = MxL_XTAL_24_MHZ, - .if_freq_hz = MxL_IF_4_57_MHZ, - .invert_if = 0, - .loop_thru_enable = 0, - .clk_out_enable = 0, - .clk_out_amp = MxL_CLKOUT_AMP_0_94V, +static struct mxl5007t_config af9035_mxl5007t_config[] = { + { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_4_57_MHZ, + .invert_if = 0, + .loop_thru_enable = 0, + .clk_out_enable = 0, + .clk_out_amp = MxL_CLKOUT_AMP_0_94V, + }, { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_4_57_MHZ, + .invert_if = 0, + .loop_thru_enable = 1, + .clk_out_enable = 1, + .clk_out_amp = MxL_CLKOUT_AMP_0_94V, + } }; static struct tda18218_config af9035_tda18218_config = { @@ -843,46 +898,52 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) &d->i2c_adap, &af9035_fc0011_config); break; case AF9033_TUNER_MXL5007T: - ret = af9035_wr_reg(d, 0x00d8e0, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8e1, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8df, 0); - if (ret < 0) - goto err; + state->tuner_address[adap->id] = 0x60; + /* hack, use b[7] to carry used I2C-bus */ + state->tuner_address[adap->id] |= (adap->id << 7); + if (adap->id == 0) { + ret = af9035_wr_reg(d, 0x00d8e0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8e1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8df, 0); + if (ret < 0) + goto err; - msleep(30); + msleep(30); - ret = af9035_wr_reg(d, 0x00d8df, 1); - if (ret < 0) - goto err; + ret = af9035_wr_reg(d, 0x00d8df, 1); + if (ret < 0) + goto err; - msleep(300); + msleep(300); - ret = af9035_wr_reg(d, 0x00d8c0, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8c1, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8bf, 0); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8b4, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8b5, 1); - if (ret < 0) - goto err; - ret = af9035_wr_reg(d, 0x00d8b3, 1); - if (ret < 0) - goto err; + ret = af9035_wr_reg(d, 0x00d8c0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8c1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8bf, 0); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8b4, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8b5, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(d, 0x00d8b3, 1); + if (ret < 0) + goto err; + } /* attach tuner */ fe = dvb_attach(mxl5007t_attach, adap->fe[0], - &d->i2c_adap, 0x60, &af9035_mxl5007t_config); + &d->i2c_adap, state->tuner_address[adap->id], + &af9035_mxl5007t_config[adap->id]); break; case AF9033_TUNER_TDA18218: /* attach tuner */ @@ -971,8 +1032,8 @@ static int af9035_init(struct dvb_usb_device *d) { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff}, { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff}, { 0x00dd0d, packet_size, 0xff }, - { 0x80f9a3, 0x00, 0x01 }, - { 0x80f9cd, 0x00, 0x01 }, + { 0x80f9a3, state->dual_mode, 0x01 }, + { 0x80f9cd, state->dual_mode, 0x01 }, { 0x80f99d, 0x00, 0x01 }, { 0x80f9a4, 0x00, 0x01 }, }; @@ -1094,7 +1155,7 @@ static const struct dvb_usb_device_properties af9035_props = { .init = af9035_init, .get_rc_config = af9035_get_rc_config, - .num_adapters = 1, + .get_adapter_count = af9035_get_adapter_count, .adapter = { { .stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188), diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index f509d35a3b00..e26e04d076aa 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -56,6 +56,8 @@ struct state { bool dual_mode; struct af9033_config af9033_config[2]; + + u8 tuner_address[2]; }; u32 clock_lut[] = { @@ -92,6 +94,7 @@ u32 clock_lut_it9135[] = { /* EEPROM locations */ #define EEPROM_IR_MODE 0x430d #define EEPROM_DUAL_MODE 0x4326 +#define EEPROM_2WIREADDR 0x4327 #define EEPROM_IR_TYPE 0x4329 #define EEPROM_1_IFFREQ_L 0x432d #define EEPROM_1_IFFREQ_H 0x432e From bf97b6373bb10bbde7c0b485b8fc829fec5a4bcf Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 8 Dec 2012 22:51:19 -0300 Subject: [PATCH 0569/4607] [media] af9035: dual mode related changes Various small changes and fixes releated to dual mode. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033.c | 2 + drivers/media/usb/dvb-usb-v2/af9035.c | 140 ++++++++++++++++++-------- drivers/media/usb/dvb-usb-v2/af9035.h | 5 +- 3 files changed, 99 insertions(+), 48 deletions(-) diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c index 745d2fa3fbdc..c9cad989b8b9 100644 --- a/drivers/media/dvb-frontends/af9033.c +++ b/drivers/media/dvb-frontends/af9033.c @@ -339,9 +339,11 @@ static int af9033_init(struct dvb_frontend *fe) ret = af9033_wr_reg_mask(state, 0x00d91c, 0x01, 0x01); if (ret < 0) goto err; + ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); if (ret < 0) goto err; + ret = af9033_wr_reg_mask(state, 0x00d916, 0x00, 0x01); if (ret < 0) goto err; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 15625eb28b56..d1beb7ffe72c 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -211,12 +211,13 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, ret = -EOPNOTSUPP; } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) || (msg[0].addr == state->af9033_config[1].i2c_addr)) { - /* integrated demod */ + /* demod access via firmware interface */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; - if (state->af9033_config[1].i2c_addr && - (msg[0].addr == state->af9033_config[1].i2c_addr)) + + if (msg[0].addr == state->af9033_config[1].i2c_addr) reg |= 0x100000; + ret = af9035_rd_regs(d, reg, &msg[1].buf[0], msg[1].len); } else { @@ -226,7 +227,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, buf, msg[1].len, msg[1].buf }; req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[1].len; - buf[1] = (u8)(msg[0].addr << 1); + buf[1] = msg[0].addr << 1; buf[2] = 0x00; /* reg addr len */ buf[3] = 0x00; /* reg addr MSB */ buf[4] = 0x00; /* reg addr LSB */ @@ -239,12 +240,13 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, ret = -EOPNOTSUPP; } else if ((msg[0].addr == state->af9033_config[0].i2c_addr) || (msg[0].addr == state->af9033_config[1].i2c_addr)) { - /* integrated demod */ + /* demod access via firmware interface */ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | msg[0].buf[2]; - if (state->af9033_config[1].i2c_addr && - (msg[0].addr == state->af9033_config[1].i2c_addr)) + + if (msg[0].addr == state->af9033_config[1].i2c_addr) reg |= 0x100000; + ret = af9035_wr_regs(d, reg, &msg[0].buf[3], msg[0].len - 3); } else { @@ -254,7 +256,7 @@ static int af9035_i2c_master_xfer(struct i2c_adapter *adap, 0, NULL }; req.mbox |= ((msg[0].addr & 0x80) >> 3); buf[0] = msg[0].len; - buf[1] = (u8)(msg[0].addr << 1); + buf[1] = msg[0].addr << 1; buf[2] = 0x00; /* reg addr len */ buf[3] = 0x00; /* reg addr MSB */ buf[4] = 0x00; /* reg addr LSB */ @@ -293,30 +295,9 @@ static int af9035_identify_state(struct dvb_usb_device *d, const char **name) int ret; u8 wbuf[1] = { 1 }; u8 rbuf[4]; - u8 tmp; struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf, sizeof(rbuf), rbuf }; - /* check if there is dual tuners */ - ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); - if (ret < 0) - goto err; - - if (tmp) { - /* read 2nd demodulator I2C address */ - ret = af9035_rd_reg(d, EEPROM_2WIREADDR, &tmp); - if (ret < 0) - goto err; - - ret = af9035_wr_reg(d, 0x00417f, tmp); - if (ret < 0) - goto err; - - ret = af9035_wr_reg(d, 0x00d81a, 1); - if (ret < 0) - goto err; - } - ret = af9035_ctrl_msg(d, &req); if (ret < 0) goto err; @@ -344,11 +325,56 @@ static int af9035_download_firmware(struct dvb_usb_device *d, struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL }; struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; - u8 hdr_core; + u8 hdr_core, tmp; u16 hdr_addr, hdr_data_len, hdr_checksum; #define MAX_DATA 58 #define HDR_SIZE 7 + /* + * In case of dual tuner configuration we need to do some extra + * initialization in order to download firmware to slave demod too, + * which is done by master demod. + * Master feeds also clock and controls power via GPIO. + */ + ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); + if (ret < 0) + goto err; + + if (tmp) { + /* configure gpioh1, reset & power slave demod */ + ret = af9035_wr_reg_mask(d, 0x00d8b0, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0x00d8b1, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0x00d8af, 0x00, 0x01); + if (ret < 0) + goto err; + + usleep_range(10000, 50000); + + ret = af9035_wr_reg_mask(d, 0x00d8af, 0x01, 0x01); + if (ret < 0) + goto err; + + /* tell the slave I2C address */ + ret = af9035_rd_reg(d, EEPROM_2ND_DEMOD_ADDR, &tmp); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0x00417f, tmp); + if (ret < 0) + goto err; + + /* enable clock out */ + ret = af9035_wr_reg_mask(d, 0x00d81a, 0x01, 0x01); + if (ret < 0) + goto err; + } + /* * Thanks to Daniel Glöckner about that info! * @@ -520,22 +546,27 @@ static int af9035_read_config(struct dvb_usb_device *d) u8 tmp; u16 tmp16; + /* demod I2C "address" */ + state->af9033_config[0].i2c_addr = 0x38; + /* check if there is dual tuners */ ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); if (ret < 0) goto err; state->dual_mode = tmp; - dev_dbg(&d->udev->dev, "%s: dual mode=%d\n", - __func__, state->dual_mode); + dev_dbg(&d->udev->dev, "%s: dual mode=%d\n", __func__, + state->dual_mode); if (state->dual_mode) { /* read 2nd demodulator I2C address */ - ret = af9035_rd_reg(d, EEPROM_2WIREADDR, &tmp); + ret = af9035_rd_reg(d, EEPROM_2ND_DEMOD_ADDR, &tmp); if (ret < 0) goto err; + state->af9033_config[1].i2c_addr = tmp; - pr_debug("%s: 2nd demod I2C addr:%02x\n", __func__, tmp); + dev_dbg(&d->udev->dev, "%s: 2nd demod I2C addr=%02x\n", + __func__, tmp); } for (i = 0; i < state->dual_mode + 1; i++) { @@ -563,6 +594,16 @@ static int af9035_read_config(struct dvb_usb_device *d) KBUILD_MODNAME, tmp); } + /* disable dual mode if driver does not support it */ + if (i == 1) + switch (tmp) { + default: + state->dual_mode = false; + dev_info(&d->udev->dev, "%s: driver does not " \ + "support 2nd tuner and will " \ + "disable it", KBUILD_MODNAME); + } + /* tuner IF frequency */ ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp); if (ret < 0) @@ -798,15 +839,14 @@ static int af9035_frontend_attach(struct dvb_usb_adapter *adap) if (ret < 0) goto err; - ret = af9035_wr_reg(d, 0x00d81a, - state->dual_mode); + ret = af9035_wr_reg(d, 0x00d81a, state->dual_mode); if (ret < 0) goto err; } /* attach demodulator */ - adap->fe[0] = dvb_attach(af9033_attach, - &state->af9033_config[adap->id], &d->i2c_adap); + adap->fe[0] = dvb_attach(af9033_attach, &state->af9033_config[adap->id], + &d->i2c_adap); if (adap->fe[0] == NULL) { ret = -ENODEV; goto err; @@ -866,6 +906,11 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) struct dvb_usb_device *d = adap_to_d(adap); int ret; struct dvb_frontend *fe; + u8 tuner_addr; + /* + * XXX: Hack used in that function: we abuse unused I2C address bit [7] + * to carry info about used I2C bus for dual tuner configuration. + */ switch (state->af9033_config[adap->id].tuner) { case AF9033_TUNER_TUA9001: @@ -898,16 +943,15 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) &d->i2c_adap, &af9035_fc0011_config); break; case AF9033_TUNER_MXL5007T: - state->tuner_address[adap->id] = 0x60; - /* hack, use b[7] to carry used I2C-bus */ - state->tuner_address[adap->id] |= (adap->id << 7); if (adap->id == 0) { ret = af9035_wr_reg(d, 0x00d8e0, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8e1, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8df, 0); if (ret < 0) goto err; @@ -923,27 +967,35 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) ret = af9035_wr_reg(d, 0x00d8c0, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8c1, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8bf, 0); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8b4, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8b5, 1); if (ret < 0) goto err; + ret = af9035_wr_reg(d, 0x00d8b3, 1); if (ret < 0) goto err; + + tuner_addr = 0x60; + } else { + tuner_addr = 0x60 | 0x80; /* I2C bus hack */ } /* attach tuner */ - fe = dvb_attach(mxl5007t_attach, adap->fe[0], - &d->i2c_adap, state->tuner_address[adap->id], - &af9035_mxl5007t_config[adap->id]); + fe = dvb_attach(mxl5007t_attach, adap->fe[0], &d->i2c_adap, + tuner_addr, &af9035_mxl5007t_config[adap->id]); break; case AF9033_TUNER_TDA18218: /* attach tuner */ diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h index e26e04d076aa..29f3eec22c2c 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.h +++ b/drivers/media/usb/dvb-usb-v2/af9035.h @@ -54,10 +54,7 @@ struct usb_req { struct state { u8 seq; /* packet sequence number */ bool dual_mode; - struct af9033_config af9033_config[2]; - - u8 tuner_address[2]; }; u32 clock_lut[] = { @@ -94,7 +91,7 @@ u32 clock_lut_it9135[] = { /* EEPROM locations */ #define EEPROM_IR_MODE 0x430d #define EEPROM_DUAL_MODE 0x4326 -#define EEPROM_2WIREADDR 0x4327 +#define EEPROM_2ND_DEMOD_ADDR 0x4327 #define EEPROM_IR_TYPE 0x4329 #define EEPROM_1_IFFREQ_L 0x432d #define EEPROM_1_IFFREQ_H 0x432e From ad3a758bb30ab7c71b67930ae7dcc794d517dd6b Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sat, 8 Dec 2012 23:27:49 -0300 Subject: [PATCH 0570/4607] [media] fc0012: use struct for driver config I need even more configuration options and overloading dvb_attach() for all those sounds quite stupid. Due to that switch struct and make room for new options. Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012.c | 9 ++++----- drivers/media/tuners/fc0012.h | 20 ++++++++++++++++---- drivers/media/usb/dvb-usb-v2/af9035.c | 10 ++++++++-- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 7 ++++++- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 308135abd54c..5ede0c049bc0 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -436,8 +436,7 @@ static const struct dvb_tuner_ops fc0012_tuner_ops = { }; struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, u8 i2c_address, int dual_master, - enum fc001x_xtal_freq xtal_freq) + struct i2c_adapter *i2c, const struct fc0012_config *cfg) { struct fc0012_priv *priv = NULL; @@ -446,9 +445,9 @@ struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, return NULL; priv->i2c = i2c; - priv->dual_master = dual_master; - priv->addr = i2c_address; - priv->xtal_freq = xtal_freq; + priv->dual_master = cfg->dual_master; + priv->addr = cfg->i2c_address; + priv->xtal_freq = cfg->xtal_freq; info("Fitipower FC0012 successfully attached."); diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 4dbd5efe8845..41946f82d02b 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -24,17 +24,29 @@ #include "dvb_frontend.h" #include "fc001x-common.h" +struct fc0012_config { + /* + * I2C address + */ + u8 i2c_address; + + /* + * clock + */ + enum fc001x_xtal_freq xtal_freq; + + int dual_master; +}; + #if defined(CONFIG_MEDIA_TUNER_FC0012) || \ (defined(CONFIG_MEDIA_TUNER_FC0012_MODULE) && defined(MODULE)) extern struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - u8 i2c_address, int dual_master, - enum fc001x_xtal_freq xtal_freq); + const struct fc0012_config *cfg); #else static inline struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, - u8 i2c_address, int dual_master, - enum fc001x_xtal_freq xtal_freq) + const struct fc0012_config *cfg) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index d1beb7ffe72c..6cf9ad5ef8c1 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -900,6 +900,12 @@ static const struct fc2580_config af9035_fc2580_config = { .clock = 16384000, }; +static const struct fc0012_config af9035_fc0012_config = { + .i2c_address = 0x63, + .xtal_freq = FC_XTAL_36_MHZ, + .dual_master = 1, +}; + static int af9035_tuner_attach(struct dvb_usb_adapter *adap) { struct state *state = adap_to_priv(adap); @@ -1043,8 +1049,8 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) usleep_range(10000, 50000); - fe = dvb_attach(fc0012_attach, adap->fe[0], &d->i2c_adap, 0x63, - 1, FC_XTAL_36_MHZ); + fe = dvb_attach(fc0012_attach, adap->fe[0], &d->i2c_adap, + &af9035_fc0012_config); break; default: fe = NULL; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index a4c302d0aa37..eddda6958c19 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -835,6 +835,11 @@ static struct tua9001_config rtl2832u_tua9001_config = { .i2c_addr = 0x60, }; +static const struct fc0012_config rtl2832u_fc0012_config = { + .i2c_address = 0x63, /* 0xc6 >> 1 */ + .xtal_freq = FC_XTAL_28_8_MHZ, +}; + static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) { int ret; @@ -847,7 +852,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) switch (priv->tuner) { case TUNER_RTL2832_FC0012: fe = dvb_attach(fc0012_attach, adap->fe[0], - &d->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ); + &d->i2c_adap, &rtl2832u_fc0012_config); /* since fc0012 includs reading the signal strength delegate * that to the tuner driver */ From 71b1e82794bbae7b23409e013f7249dd2f382160 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 00:30:08 -0300 Subject: [PATCH 0571/4607] [media] fc0012: add RF loop through Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012-priv.h | 1 + drivers/media/tuners/fc0012.c | 7 +++++++ drivers/media/tuners/fc0012.h | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/drivers/media/tuners/fc0012-priv.h b/drivers/media/tuners/fc0012-priv.h index 4577c917e616..1195ee9b9cf7 100644 --- a/drivers/media/tuners/fc0012-priv.h +++ b/drivers/media/tuners/fc0012-priv.h @@ -32,6 +32,7 @@ struct fc0012_priv { struct i2c_adapter *i2c; + const struct fc0012_config *cfg; u8 addr; u8 dual_master; u8 xtal_freq; diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 5ede0c049bc0..636f951219db 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -101,6 +101,9 @@ static int fc0012_init(struct dvb_frontend *fe) if (priv->dual_master) reg[0x0c] |= 0x02; + if (priv->cfg->loop_through) + reg[0x09] |= 0x01; + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ @@ -445,6 +448,7 @@ struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, return NULL; priv->i2c = i2c; + priv->cfg = cfg; priv->dual_master = cfg->dual_master; priv->addr = cfg->i2c_address; priv->xtal_freq = cfg->xtal_freq; @@ -453,6 +457,9 @@ struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, fe->tuner_priv = priv; + if (priv->cfg->loop_through) + fc0012_writereg(priv, 0x09, 0x6f); + memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, sizeof(struct dvb_tuner_ops)); diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 41946f82d02b..891d66d5af3b 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -36,6 +36,11 @@ struct fc0012_config { enum fc001x_xtal_freq xtal_freq; int dual_master; + + /* + * RF loop-through + */ + bool loop_through; }; #if defined(CONFIG_MEDIA_TUNER_FC0012) || \ From 3b0d51afa026b87784d81e6a88522271a69ca7b9 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 11:46:40 -0300 Subject: [PATCH 0572/4607] [media] fc0012: enable clock output on attach() We need feed clock to slave demodulator at the very beginning in case of dual tuner configuration. I am not sure if that configuration changes clock output divider or enable clock output itself... Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012.c | 7 +++++++ drivers/media/tuners/fc0012.h | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 636f951219db..1a52b766360f 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -460,6 +460,13 @@ struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, if (priv->cfg->loop_through) fc0012_writereg(priv, 0x09, 0x6f); + /* + * TODO: Clock out en or div? + * For dual tuner configuration clearing bit [0] is required. + */ + if (priv->cfg->clock_out) + fc0012_writereg(priv, 0x0b, 0x82); + memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, sizeof(struct dvb_tuner_ops)); diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 891d66d5af3b..83a98e732502 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -41,6 +41,11 @@ struct fc0012_config { * RF loop-through */ bool loop_through; + + /* + * clock output + */ + bool clock_out; }; #if defined(CONFIG_MEDIA_TUNER_FC0012) || \ From 0bb3d8ac87188a106f98e5d257f56f3ffe066147 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 12:01:41 -0300 Subject: [PATCH 0573/4607] [media] af9035: add support for fc0012 dual tuner configuration That adds support for AF9035 dual devices having FC0012 tuners. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 56 ++++++++++++++++++++------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 6cf9ad5ef8c1..1c7fe5ab5b9b 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -597,6 +597,8 @@ static int af9035_read_config(struct dvb_usb_device *d) /* disable dual mode if driver does not support it */ if (i == 1) switch (tmp) { + case AF9033_TUNER_FC0012: + break; default: state->dual_mode = false; dev_info(&d->udev->dev, "%s: driver does not " \ @@ -900,10 +902,18 @@ static const struct fc2580_config af9035_fc2580_config = { .clock = 16384000, }; -static const struct fc0012_config af9035_fc0012_config = { - .i2c_address = 0x63, - .xtal_freq = FC_XTAL_36_MHZ, - .dual_master = 1, +static const struct fc0012_config af9035_fc0012_config[] = { + { + .i2c_address = 0x63, + .xtal_freq = FC_XTAL_36_MHZ, + .dual_master = 1, + .loop_through = true, + .clock_out = true, + }, { + .i2c_address = 0x63 | 0x80, /* I2C bus select hack */ + .xtal_freq = FC_XTAL_36_MHZ, + .dual_master = 1, + } }; static int af9035_tuner_attach(struct dvb_usb_adapter *adap) @@ -912,6 +922,7 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) struct dvb_usb_device *d = adap_to_d(adap); int ret; struct dvb_frontend *fe; + struct i2c_msg msg[1]; u8 tuner_addr; /* * XXX: Hack used in that function: we abuse unused I2C address bit [7] @@ -1034,23 +1045,38 @@ static int af9035_tuner_attach(struct dvb_usb_adapter *adap) * my test I didn't find any difference. */ - /* configure gpiot2 as output and high */ - ret = af9035_wr_reg_mask(d, 0xd8eb, 0x01, 0x01); - if (ret < 0) - goto err; + if (adap->id == 0) { + /* configure gpiot2 as output and high */ + ret = af9035_wr_reg_mask(d, 0xd8eb, 0x01, 0x01); + if (ret < 0) + goto err; - ret = af9035_wr_reg_mask(d, 0xd8ec, 0x01, 0x01); - if (ret < 0) - goto err; + ret = af9035_wr_reg_mask(d, 0xd8ec, 0x01, 0x01); + if (ret < 0) + goto err; - ret = af9035_wr_reg_mask(d, 0xd8ed, 0x01, 0x01); - if (ret < 0) - goto err; + ret = af9035_wr_reg_mask(d, 0xd8ed, 0x01, 0x01); + if (ret < 0) + goto err; + } else { + /* + * FIXME: That belongs for the FC0012 driver. + * Write 02 to FC0012 master tuner register 0d directly + * in order to make slave tuner working. + */ + msg[0].addr = 0x63; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = "\x0d\x02"; + ret = i2c_transfer(&d->i2c_adap, msg, 1); + if (ret < 0) + goto err; + } usleep_range(10000, 50000); fe = dvb_attach(fc0012_attach, adap->fe[0], &d->i2c_adap, - &af9035_fc0012_config); + &af9035_fc0012_config[adap->id]); break; default: fe = NULL; From 3a98477200b44328e50a5c0830f92fd5cdc1ea9b Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 12:33:04 -0300 Subject: [PATCH 0574/4607] [media] fc0012: use config directly from the config struct No need to copy config to the driver state. Those are coming from the const struct and could be used directly. Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012-priv.h | 3 --- drivers/media/tuners/fc0012.c | 17 ++++++++--------- drivers/media/tuners/fc0012.h | 2 +- drivers/media/usb/dvb-usb-v2/af9035.c | 4 ++-- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/drivers/media/tuners/fc0012-priv.h b/drivers/media/tuners/fc0012-priv.h index 1195ee9b9cf7..3b98bf971fb3 100644 --- a/drivers/media/tuners/fc0012-priv.h +++ b/drivers/media/tuners/fc0012-priv.h @@ -33,9 +33,6 @@ struct fc0012_priv { struct i2c_adapter *i2c; const struct fc0012_config *cfg; - u8 addr; - u8 dual_master; - u8 xtal_freq; u32 frequency; u32 bandwidth; diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 1a52b766360f..01f5e406282c 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -25,7 +25,7 @@ static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) { u8 buf[2] = {reg, val}; struct i2c_msg msg = { - .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 + .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 }; if (i2c_transfer(priv->i2c, &msg, 1) != 1) { @@ -38,8 +38,10 @@ static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val) { struct i2c_msg msg[2] = { - { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, - { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, + { .addr = priv->cfg->i2c_address, .flags = 0, + .buf = ®, .len = 1 }, + { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, + .buf = val, .len = 1 }, }; if (i2c_transfer(priv->i2c, msg, 2) != 2) { @@ -88,7 +90,7 @@ static int fc0012_init(struct dvb_frontend *fe) 0x04, /* reg. 0x15: Enable LNA COMPS */ }; - switch (priv->xtal_freq) { + switch (priv->cfg->xtal_freq) { case FC_XTAL_27_MHZ: case FC_XTAL_28_8_MHZ: reg[0x07] |= 0x20; @@ -98,7 +100,7 @@ static int fc0012_init(struct dvb_frontend *fe) break; } - if (priv->dual_master) + if (priv->cfg->dual_master) reg[0x0c] |= 0x02; if (priv->cfg->loop_through) @@ -147,7 +149,7 @@ static int fc0012_set_params(struct dvb_frontend *fe) goto exit; } - switch (priv->xtal_freq) { + switch (priv->cfg->xtal_freq) { case FC_XTAL_27_MHZ: xtal_freq_khz_2 = 27000 / 2; break; @@ -449,9 +451,6 @@ struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, priv->i2c = i2c; priv->cfg = cfg; - priv->dual_master = cfg->dual_master; - priv->addr = cfg->i2c_address; - priv->xtal_freq = cfg->xtal_freq; info("Fitipower FC0012 successfully attached."); diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 83a98e732502..3fb53b86813a 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -35,7 +35,7 @@ struct fc0012_config { */ enum fc001x_xtal_freq xtal_freq; - int dual_master; + bool dual_master; /* * RF loop-through diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 1c7fe5ab5b9b..68e0e80416aa 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -906,13 +906,13 @@ static const struct fc0012_config af9035_fc0012_config[] = { { .i2c_address = 0x63, .xtal_freq = FC_XTAL_36_MHZ, - .dual_master = 1, + .dual_master = true, .loop_through = true, .clock_out = true, }, { .i2c_address = 0x63 | 0x80, /* I2C bus select hack */ .xtal_freq = FC_XTAL_36_MHZ, - .dual_master = 1, + .dual_master = true, } }; From 44ff69cd95308c115134f0546317b584fe1bf5b2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 13:28:45 -0300 Subject: [PATCH 0575/4607] [media] fc0012: rework attach() to check chip id and I/O errors Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012.c | 59 +++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 01f5e406282c..feb15941cc5b 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -443,32 +443,71 @@ static const struct dvb_tuner_ops fc0012_tuner_ops = { struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct fc0012_config *cfg) { - struct fc0012_priv *priv = NULL; + struct fc0012_priv *priv; + int ret; + u8 chip_id; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); priv = kzalloc(sizeof(struct fc0012_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; + if (!priv) { + ret = -ENOMEM; + dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME); + goto err; + } - priv->i2c = i2c; priv->cfg = cfg; + priv->i2c = i2c; - info("Fitipower FC0012 successfully attached."); + /* check if the tuner is there */ + ret = fc0012_readreg(priv, 0x00, &chip_id); + if (ret < 0) + goto err; - fe->tuner_priv = priv; + dev_dbg(&i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id); - if (priv->cfg->loop_through) - fc0012_writereg(priv, 0x09, 0x6f); + switch (chip_id) { + case 0xa1: + break; + default: + ret = -ENODEV; + goto err; + } + + dev_info(&i2c->dev, "%s: Fitipower FC0012 successfully identified\n", + KBUILD_MODNAME); + + if (priv->cfg->loop_through) { + ret = fc0012_writereg(priv, 0x09, 0x6f); + if (ret < 0) + goto err; + } /* * TODO: Clock out en or div? * For dual tuner configuration clearing bit [0] is required. */ - if (priv->cfg->clock_out) - fc0012_writereg(priv, 0x0b, 0x82); + if (priv->cfg->clock_out) { + ret = fc0012_writereg(priv, 0x0b, 0x82); + if (ret < 0) + goto err; + } + fe->tuner_priv = priv; memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, sizeof(struct dvb_tuner_ops)); +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret) { + dev_dbg(&i2c->dev, "%s: failed: %d\n", __func__, ret); + kfree(priv); + return NULL; + } + return fe; } EXPORT_SYMBOL(fc0012_attach); From 4562620159c6c0c3c11913d518138a27e6ecd957 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 13:42:25 -0300 Subject: [PATCH 0576/4607] [media] fc0012: use Kernel dev_foo() logging Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012-priv.h | 9 --------- drivers/media/tuners/fc0012.c | 20 ++++++++++++++------ drivers/media/tuners/fc0012.h | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/media/tuners/fc0012-priv.h b/drivers/media/tuners/fc0012-priv.h index 3b98bf971fb3..1a86ce1d3fcf 100644 --- a/drivers/media/tuners/fc0012-priv.h +++ b/drivers/media/tuners/fc0012-priv.h @@ -21,15 +21,6 @@ #ifndef _FC0012_PRIV_H_ #define _FC0012_PRIV_H_ -#define LOG_PREFIX "fc0012" - -#undef err -#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) -#undef info -#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) -#undef warn -#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) - struct fc0012_priv { struct i2c_adapter *i2c; const struct fc0012_config *cfg; diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index feb15941cc5b..4491f063e211 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -29,7 +29,9 @@ static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) }; if (i2c_transfer(priv->i2c, &msg, 1) != 1) { - err("I2C write reg failed, reg: %02x, val: %02x", reg, val); + dev_err(&priv->i2c->dev, + "%s: I2C write reg failed, reg: %02x, val: %02x\n", + KBUILD_MODNAME, reg, val); return -EREMOTEIO; } return 0; @@ -45,7 +47,9 @@ static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val) }; if (i2c_transfer(priv->i2c, msg, 2) != 2) { - err("I2C read reg failed, reg: %02x", reg); + dev_err(&priv->i2c->dev, + "%s: I2C read reg failed, reg: %02x\n", + KBUILD_MODNAME, reg); return -EREMOTEIO; } return 0; @@ -119,7 +123,8 @@ static int fc0012_init(struct dvb_frontend *fe) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ if (ret) - err("fc0012_writereg failed: %d", ret); + dev_err(&priv->i2c->dev, "%s: fc0012_writereg failed: %d\n", + KBUILD_MODNAME, ret); return ret; } @@ -261,7 +266,8 @@ static int fc0012_set_params(struct dvb_frontend *fe) break; } } else { - err("%s: modulation type not supported!", __func__); + dev_err(&priv->i2c->dev, "%s: modulation type not supported!\n", + KBUILD_MODNAME); return -EINVAL; } @@ -323,7 +329,8 @@ exit: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ if (ret) - warn("%s: failed: %d", __func__, ret); + dev_warn(&priv->i2c->dev, "%s: %s failed: %d\n", + KBUILD_MODNAME, __func__, ret); return ret; } @@ -413,7 +420,8 @@ err: fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ exit: if (ret) - warn("%s: failed: %d", __func__, ret); + dev_warn(&priv->i2c->dev, "%s: %s failed: %d\n", + KBUILD_MODNAME, __func__, ret); return ret; } diff --git a/drivers/media/tuners/fc0012.h b/drivers/media/tuners/fc0012.h index 3fb53b86813a..54508fcc3469 100644 --- a/drivers/media/tuners/fc0012.h +++ b/drivers/media/tuners/fc0012.h @@ -58,7 +58,7 @@ static inline struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct fc0012_config *cfg) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + pr_warn("%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif From c5dc3b98fffaf183bb3d5bf690bfab9f6705c5e1 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 13:45:42 -0300 Subject: [PATCH 0577/4607] [media] fc0012: remove unused callback and correct one comment There is no need to keep dummy sleep() callback implementation as DVB-core checks existence of it before calls callback. Due to that we can remove it. FC0012 is based of direct-conversion receiver architecture (aka Zero-IF) where is no IF used. Due to that IF is always 0 Hz. Fix comment to point that. Signed-off-by: Antti Palosaari Acked-by: Hans-Frieder Vogt Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0012.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 4491f063e211..f4d0e797a6cc 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -129,12 +129,6 @@ static int fc0012_init(struct dvb_frontend *fe) return ret; } -static int fc0012_sleep(struct dvb_frontend *fe) -{ - /* nothing to do here */ - return 0; -} - static int fc0012_set_params(struct dvb_frontend *fe) { struct fc0012_priv *priv = fe->tuner_priv; @@ -343,8 +337,7 @@ static int fc0012_get_frequency(struct dvb_frontend *fe, u32 *frequency) static int fc0012_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) { - /* CHECK: always ? */ - *frequency = 0; + *frequency = 0; /* Zero-IF */ return 0; } @@ -437,7 +430,6 @@ static const struct dvb_tuner_ops fc0012_tuner_ops = { .release = fc0012_release, .init = fc0012_init, - .sleep = fc0012_sleep, .set_params = fc0012_set_params, From d267d2709196b2a2ef27850abd9189c9ed5e537a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 14:46:45 -0300 Subject: [PATCH 0578/4607] [media] af9033: update demod init sequence Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033_priv.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index 288cd4524547..d96d128622a1 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -199,10 +199,9 @@ static const struct reg_val ofsm_init[] = { { 0x8000a6, 0x01 }, { 0x8000a9, 0x00 }, { 0x8000aa, 0x01 }, - { 0x8000ab, 0x01 }, { 0x8000b0, 0x01 }, - { 0x8000c0, 0x05 }, - { 0x8000c4, 0x19 }, + { 0x8000c4, 0x05 }, + { 0x8000c8, 0x19 }, { 0x80f000, 0x0f }, { 0x80f016, 0x10 }, { 0x80f017, 0x04 }, From 2c37d37fc635a562753e93628217c578d298609e Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 15:38:03 -0300 Subject: [PATCH 0579/4607] [media] af9033: update tua9001 init sequence Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033_priv.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index d96d128622a1..e0be0409435e 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -321,8 +321,9 @@ static const struct reg_val tuner_init_tua9001[] = { { 0x80009b, 0x05 }, { 0x80009c, 0x80 }, { 0x8000b3, 0x00 }, - { 0x8000c1, 0x01 }, - { 0x8000c2, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000c9, 0x5d }, { 0x80f007, 0x00 }, { 0x80f01f, 0x82 }, { 0x80f020, 0x00 }, From 0353d6b1cd23acf88327fa6bdb28a48f29c080a3 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 15:45:26 -0300 Subject: [PATCH 0580/4607] [media] af9033: update fc0011 init sequence Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033_priv.h | 72 +++++++++++------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index e0be0409435e..1fb84a230b46 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -339,14 +339,14 @@ static const struct reg_val tuner_init_tua9001[] = { /* Fitipower fc0011 tuner init AF9033_TUNER_FC0011 = 0x28 */ static const struct reg_val tuner_init_fc0011[] = { - { 0x800046, AF9033_TUNER_FC0011 }, + { 0x800046, 0x28 }, { 0x800057, 0x00 }, { 0x800058, 0x01 }, { 0x80005f, 0x00 }, { 0x800060, 0x00 }, { 0x800068, 0xa5 }, { 0x80006e, 0x01 }, - { 0x800071, 0x0A }, + { 0x800071, 0x0a }, { 0x800072, 0x02 }, { 0x800074, 0x01 }, { 0x800079, 0x01 }, @@ -354,7 +354,7 @@ static const struct reg_val tuner_init_fc0011[] = { { 0x800094, 0x00 }, { 0x800095, 0x00 }, { 0x800096, 0x00 }, - { 0x80009b, 0x2D }, + { 0x80009b, 0x2d }, { 0x80009c, 0x60 }, { 0x80009d, 0x23 }, { 0x8000a4, 0x50 }, @@ -362,39 +362,39 @@ static const struct reg_val tuner_init_fc0011[] = { { 0x8000b3, 0x01 }, { 0x8000b7, 0x88 }, { 0x8000b8, 0xa6 }, - { 0x8000c3, 0x01 }, - { 0x8000c4, 0x01 }, - { 0x8000c7, 0x69 }, - { 0x80F007, 0x00 }, - { 0x80F00A, 0x1B }, - { 0x80F00B, 0x1B }, - { 0x80F00C, 0x1B }, - { 0x80F00D, 0x1B }, - { 0x80F00E, 0xFF }, - { 0x80F00F, 0x01 }, - { 0x80F010, 0x00 }, - { 0x80F011, 0x02 }, - { 0x80F012, 0xFF }, - { 0x80F013, 0x01 }, - { 0x80F014, 0x00 }, - { 0x80F015, 0x02 }, - { 0x80F01B, 0xEF }, - { 0x80F01C, 0x01 }, - { 0x80F01D, 0x0f }, - { 0x80F01E, 0x02 }, - { 0x80F01F, 0x6E }, - { 0x80F020, 0x00 }, - { 0x80F025, 0xDE }, - { 0x80F026, 0x00 }, - { 0x80F027, 0x0A }, - { 0x80F028, 0x03 }, - { 0x80F029, 0x6E }, - { 0x80F02A, 0x00 }, - { 0x80F047, 0x00 }, - { 0x80F054, 0x00 }, - { 0x80F055, 0x00 }, - { 0x80F077, 0x01 }, - { 0x80F1E6, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x01 }, + { 0x8000c9, 0x69 }, + { 0x80f007, 0x00 }, + { 0x80f00a, 0x1b }, + { 0x80f00b, 0x1b }, + { 0x80f00c, 0x1b }, + { 0x80f00d, 0x1b }, + { 0x80f00e, 0xff }, + { 0x80f00f, 0x01 }, + { 0x80f010, 0x00 }, + { 0x80f011, 0x02 }, + { 0x80f012, 0xff }, + { 0x80f013, 0x01 }, + { 0x80f014, 0x00 }, + { 0x80f015, 0x02 }, + { 0x80f01b, 0xef }, + { 0x80f01c, 0x01 }, + { 0x80f01d, 0x0f }, + { 0x80f01e, 0x02 }, + { 0x80f01f, 0x6e }, + { 0x80f020, 0x00 }, + { 0x80f025, 0xde }, + { 0x80f026, 0x00 }, + { 0x80f027, 0x0a }, + { 0x80f028, 0x03 }, + { 0x80f029, 0x6e }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x00 }, }; /* Fitipower FC0012 tuner init From 864c714392ee966b8d50af0f480f5855ca3b5334 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 16:07:05 -0300 Subject: [PATCH 0581/4607] [media] af9033: update fc2580 init sequence Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/af9033_priv.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h index 1fb84a230b46..e9bd78265543 100644 --- a/drivers/media/dvb-frontends/af9033_priv.h +++ b/drivers/media/dvb-frontends/af9033_priv.h @@ -525,11 +525,12 @@ static const struct reg_val tuner_init_fc2580[] = { { 0x800095, 0x00 }, { 0x800096, 0x05 }, { 0x8000b3, 0x01 }, - { 0x8000c3, 0x01 }, - { 0x8000c4, 0x00 }, + { 0x8000c5, 0x01 }, + { 0x8000c6, 0x00 }, + { 0x8000d1, 0x01 }, { 0x80f007, 0x00 }, { 0x80f00c, 0x19 }, - { 0x80f00d, 0x1A }, + { 0x80f00d, 0x1a }, { 0x80f00e, 0x00 }, { 0x80f00f, 0x02 }, { 0x80f010, 0x00 }, From ff4e3fe86f6c5e8c746f07e232a89330cd3cf1a9 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 16:25:08 -0300 Subject: [PATCH 0582/4607] [media] af9035: print warning when firmware is bad Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 68e0e80416aa..ea37b5c3c33f 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -437,6 +437,10 @@ static int af9035_download_firmware(struct dvb_usb_device *d, __func__, fw->size - i); } + /* print warn if firmware is bad, continue and see what happens */ + if (i) + dev_warn(&d->udev->dev, "%s: bad firmware\n", KBUILD_MODNAME); + /* firmware loaded, request boot */ req.cmd = CMD_FW_BOOT; ret = af9035_ctrl_msg(d, &req); From 6612545ffb3c14ccb5fa265992cc1b40db3ff463 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 5 Dec 2012 13:52:00 -0300 Subject: [PATCH 0583/4607] [media] s5p-fimc: Avoid possible NULL pointer dereference in set_fmt op This fixes following issue found with a static analysis tool: Pointer 'ffmt' returned from call to function 'fimc_capture_try_format' at line 1522 may be NULL and may be dereferenced at line 1535. Although it shouldn't happen in practice, add the NULL pointer check to be on the safe side. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 95e6a7820b5e..aad0850d0c01 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -1561,6 +1561,10 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, *mf = fmt->format; return 0; } + /* There must be a bug in the driver if this happens */ + if (WARN_ON(ffmt == NULL)) + return -EINVAL; + /* Update RGB Alpha control state and value range */ fimc_alpha_ctrl_update(ctx); From a8697ec8c7f3ab7331bc3210c3b89563356f8de5 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 5 Dec 2012 13:40:04 -0300 Subject: [PATCH 0584/4607] [media] s5p-fimc: Prevent potential buffer overflow Replace the hard coded csi_sensors[] array size with a relevant constant to make sure we don't iterate beyond the actual array. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 8b43f982c12d..e77dba7a97b8 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -627,7 +627,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) */ static int fimc_md_create_links(struct fimc_md *fmd) { - struct v4l2_subdev *csi_sensors[2] = { NULL }; + struct v4l2_subdev *csi_sensors[CSIS_MAX_ENTITIES] = { NULL }; struct v4l2_subdev *sensor, *csis; struct s5p_fimc_isp_info *pdata; struct fimc_sensor_info *s_info; @@ -692,7 +692,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) pad, link_mask); } - for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) { + for (i = 0; i < CSIS_MAX_ENTITIES; i++) { if (fmd->csis[i].sd == NULL) continue; source = &fmd->csis[i].sd->entity; From dc3ae328799bfc5c352174e95162dc5716e209ff Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Tue, 18 Dec 2012 05:28:40 -0300 Subject: [PATCH 0585/4607] [media] s5p-fimc: Fix incorrect usage of IS_ERR_OR_NULL Replace IS_ERR_OR_NULL with IS_ERR on clk_get results. Signed-off-by: Tony Prisk Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index e77dba7a97b8..25055cb43993 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -732,7 +732,7 @@ static int fimc_md_get_clocks(struct fimc_md *fmd) for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i); clock = clk_get(NULL, clk_name); - if (IS_ERR_OR_NULL(clock)) { + if (IS_ERR(clock)) { v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s", clk_name); return -ENXIO; From 740ad921f8a72ed76d20c88225a2fa71e8290904 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 6 Dec 2012 10:26:19 -0300 Subject: [PATCH 0586/4607] [media] s5p-fimc: Prevent AB-BA deadlock during links reconfiguration This patch patch eliminates potential AB-BA deadlock when one process calls open(), or VIDIOC_S/TRY_FMT ioctl on the FIMC capture video node, while other thread is reconfiguring media links via media device node: /dev/video? open() /dev/media? MEDIA_IOC_SETUP_LINK ioctl mutex_lock(video_lock) mutex_lock(graph_lock) fimc_pipeline_open() fimc_md_link_notify() mutex_lock(graph_lock) mutex_lock(video_lock) ... ... The deadlock is avoided by always taking the graph mutex first in video node open() or an ioctl, before the video lock is acquired. Reversed order seems impossible, since media device driver's link_notify callback is called with media graph mutex already held. To ensure proper locking order VIDIOC_S_FMT and VIDIOC_TRY_FMT ioctls are not serialized in the v4l2-core and the driver takes care of it itself. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/s5p-fimc/fimc-capture.c | 54 ++++++++--- drivers/media/platform/s5p-fimc/fimc-lite.c | 6 +- .../media/platform/s5p-fimc/fimc-mdevice.c | 94 +++++++------------ 3 files changed, 78 insertions(+), 76 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index aad0850d0c01..18a70e4a0b0f 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -510,8 +510,8 @@ static int fimc_capture_open(struct file *file) dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; + fimc_md_graph_lock(fimc); + mutex_lock(&fimc->lock); if (fimc_m2m_active(fimc)) goto unlock; @@ -546,6 +546,7 @@ static int fimc_capture_open(struct file *file) } unlock: mutex_unlock(&fimc->lock); + fimc_md_graph_unlock(fimc); return ret; } @@ -962,6 +963,10 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, struct fimc_ctx *ctx = fimc->vid_cap.ctx; struct v4l2_mbus_framefmt mf; struct fimc_fmt *ffmt = NULL; + int ret = 0; + + fimc_md_graph_lock(fimc); + mutex_lock(&fimc->lock); if (fimc_jpeg_fourcc(pix->pixelformat)) { fimc_capture_try_format(ctx, &pix->width, &pix->height, @@ -973,16 +978,16 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, NULL, &pix->pixelformat, FIMC_SD_PAD_SOURCE); - if (!ffmt) - return -EINVAL; + if (!ffmt) { + ret = -EINVAL; + goto unlock; + } if (!fimc->vid_cap.user_subdev_api) { mf.width = pix->width; mf.height = pix->height; mf.code = ffmt->mbus_code; - fimc_md_graph_lock(fimc); fimc_pipeline_try_format(ctx, &mf, &ffmt, false); - fimc_md_graph_unlock(fimc); pix->width = mf.width; pix->height = mf.height; if (ffmt) @@ -994,8 +999,11 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, if (ffmt->flags & FMT_FLAGS_COMPRESSED) fimc_get_sensor_frame_desc(fimc->pipeline.subdevs[IDX_SENSOR], pix->plane_fmt, ffmt->memplanes, true); +unlock: + mutex_unlock(&fimc->lock); + fimc_md_graph_unlock(fimc); - return 0; + return ret; } static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, @@ -1012,7 +1020,8 @@ static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state); } -static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) +static int __fimc_capture_set_format(struct fimc_dev *fimc, + struct v4l2_format *f) { struct fimc_ctx *ctx = fimc->vid_cap.ctx; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; @@ -1047,12 +1056,10 @@ static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) mf->code = ff->fmt->mbus_code; mf->width = pix->width; mf->height = pix->height; - - fimc_md_graph_lock(fimc); ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true); - fimc_md_graph_unlock(fimc); if (ret) return ret; + pix->width = mf->width; pix->height = mf->height; } @@ -1091,8 +1098,23 @@ static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct fimc_dev *fimc = video_drvdata(file); + int ret; - return fimc_capture_set_format(fimc, f); + fimc_md_graph_lock(fimc); + mutex_lock(&fimc->lock); + /* + * The graph is walked within __fimc_capture_set_format() to set + * the format at subdevs thus the graph mutex needs to be held at + * this point and acquired before the video mutex, to avoid AB-BA + * deadlock when fimc_md_link_notify() is called by other thread. + * Ideally the graph walking and setting format at the whole pipeline + * should be removed from this driver and handled in userspace only. + */ + ret = __fimc_capture_set_format(fimc, f); + + mutex_unlock(&fimc->lock); + fimc_md_graph_unlock(fimc); + return ret; } static int fimc_cap_enum_input(struct file *file, void *priv, @@ -1727,7 +1749,7 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc) }, }; - return fimc_capture_set_format(fimc, &fmt); + return __fimc_capture_set_format(fimc, &fmt); } /* fimc->lock must be already initialized */ @@ -1789,6 +1811,12 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0); if (ret) goto err_ent; + /* + * For proper order of acquiring/releasing the video + * and the graph mutex. + */ + v4l2_disable_ioctl_locking(vfd, VIDIOC_TRY_FMT); + v4l2_disable_ioctl_locking(vfd, VIDIOC_S_FMT); ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); if (ret) diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index 765b8e4cbf4e..ef31c3919fe9 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -459,11 +459,12 @@ static void fimc_lite_clear_event_counters(struct fimc_lite *fimc) static int fimc_lite_open(struct file *file) { struct fimc_lite *fimc = video_drvdata(file); + struct media_entity *me = &fimc->vfd.entity; int ret; - if (mutex_lock_interruptible(&fimc->lock)) - return -ERESTARTSYS; + mutex_lock(&me->parent->graph_mutex); + mutex_lock(&fimc->lock); if (fimc->out_path != FIMC_IO_DMA) { ret = -EBUSY; goto done; @@ -492,6 +493,7 @@ static int fimc_lite_open(struct file *file) } done: mutex_unlock(&fimc->lock); + mutex_unlock(&me->parent->graph_mutex); return ret; } diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 25055cb43993..62f3a71c4f6d 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -142,7 +142,7 @@ static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state) * @me: media entity to start graph walk with * @prep: true to acquire sensor (and csis) subdevs * - * This function must be called with the graph mutex held. + * Called with the graph mutex held. */ static int __fimc_pipeline_open(struct fimc_pipeline *p, struct media_entity *me, bool prep) @@ -162,30 +162,19 @@ static int __fimc_pipeline_open(struct fimc_pipeline *p, return fimc_pipeline_s_power(p, 1); } -static int fimc_pipeline_open(struct fimc_pipeline *p, - struct media_entity *me, bool prep) -{ - int ret; - - mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_open(p, me, prep); - mutex_unlock(&me->parent->graph_mutex); - - return ret; -} - /** * __fimc_pipeline_close - disable the sensor clock and pipeline power * @fimc: fimc device terminating the pipeline * - * Disable power of all subdevs in the pipeline and turn off the external - * sensor clock. - * Called with the graph mutex held. + * Disable power of all subdevs and turn the external sensor clock off. */ static int __fimc_pipeline_close(struct fimc_pipeline *p) { int ret = 0; + if (!p || !p->subdevs[IDX_SENSOR]) + return -EINVAL; + if (p->subdevs[IDX_SENSOR]) { ret = fimc_pipeline_s_power(p, 0); fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false); @@ -193,28 +182,12 @@ static int __fimc_pipeline_close(struct fimc_pipeline *p) return ret == -ENXIO ? 0 : ret; } -static int fimc_pipeline_close(struct fimc_pipeline *p) -{ - struct media_entity *me; - int ret; - - if (!p || !p->subdevs[IDX_SENSOR]) - return -EINVAL; - - me = &p->subdevs[IDX_SENSOR]->entity; - mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_close(p); - mutex_unlock(&me->parent->graph_mutex); - - return ret; -} - /** - * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs + * __fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs * @pipeline: video pipeline structure * @on: passed as the s_stream call argument */ -static int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) +static int __fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) { int i, ret; @@ -236,9 +209,9 @@ static int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) /* Media pipeline operations for the FIMC/FIMC-LITE video device driver */ static const struct fimc_pipeline_ops fimc_pipeline_ops = { - .open = fimc_pipeline_open, - .close = fimc_pipeline_close, - .set_stream = fimc_pipeline_s_stream, + .open = __fimc_pipeline_open, + .close = __fimc_pipeline_close, + .set_stream = __fimc_pipeline_s_stream, }; /* @@ -822,7 +795,9 @@ static int fimc_md_link_notify(struct media_pad *source, struct fimc_dev *fimc = NULL; struct fimc_pipeline *pipeline; struct v4l2_subdev *sd; + struct mutex *lock; int ret = 0; + int ref_count; if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return 0; @@ -832,26 +807,31 @@ static int fimc_md_link_notify(struct media_pad *source, switch (sd->grp_id) { case GRP_ID_FLITE: fimc_lite = v4l2_get_subdevdata(sd); + if (WARN_ON(fimc_lite == NULL)) + return 0; pipeline = &fimc_lite->pipeline; + lock = &fimc_lite->lock; break; case GRP_ID_FIMC: fimc = v4l2_get_subdevdata(sd); + if (WARN_ON(fimc == NULL)) + return 0; pipeline = &fimc->pipeline; + lock = &fimc->lock; break; default: return 0; } if (!(flags & MEDIA_LNK_FL_ENABLED)) { + int i; + mutex_lock(lock); ret = __fimc_pipeline_close(pipeline); - pipeline->subdevs[IDX_SENSOR] = NULL; - pipeline->subdevs[IDX_CSIS] = NULL; - - if (fimc) { - mutex_lock(&fimc->lock); + for (i = 0; i < IDX_MAX; i++) + pipeline->subdevs[i] = NULL; + if (fimc) fimc_ctrls_delete(fimc->vid_cap.ctx); - mutex_unlock(&fimc->lock); - } + mutex_unlock(lock); return ret; } /* @@ -859,23 +839,15 @@ static int fimc_md_link_notify(struct media_pad *source, * pipeline is already in use, i.e. its video node is opened. * Recreate the controls destroyed during the link deactivation. */ - if (fimc) { - mutex_lock(&fimc->lock); - if (fimc->vid_cap.refcnt > 0) { - ret = __fimc_pipeline_open(pipeline, - source->entity, true); - if (!ret) - ret = fimc_capture_ctrls_create(fimc); - } - mutex_unlock(&fimc->lock); - } else { - mutex_lock(&fimc_lite->lock); - if (fimc_lite->ref_count > 0) { - ret = __fimc_pipeline_open(pipeline, - source->entity, true); - } - mutex_unlock(&fimc_lite->lock); - } + mutex_lock(lock); + + ref_count = fimc ? fimc->vid_cap.refcnt : fimc_lite->ref_count; + if (ref_count > 0) + ret = __fimc_pipeline_open(pipeline, source->entity, true); + if (!ret && fimc) + ret = fimc_capture_ctrls_create(fimc); + + mutex_unlock(lock); return ret ? -EPIPE : ret; } From cf48f56c27ecfe94fbea363db6e8e0bacbd525ed Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 24 Sep 2012 06:00:37 -0300 Subject: [PATCH 0587/4607] [media] s5p-tv: Fix return value in sdo_probe() on error paths Use proper return value test for clk_get() and devm_regulator_get() functions and propagate any errors from the clock and the regulator subsystem to the driver core. In two cases a proper error code is now returned rather than 0. Reported-by: Peter Senna Tschudin Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/sdo_drv.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c index ad68bbed014e..2d1a65488f2d 100644 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ b/drivers/media/platform/s5p-tv/sdo_drv.c @@ -341,47 +341,50 @@ static int __devinit sdo_probe(struct platform_device *pdev) /* acquire clocks */ sdev->sclk_dac = clk_get(dev, "sclk_dac"); - if (IS_ERR_OR_NULL(sdev->sclk_dac)) { + if (IS_ERR(sdev->sclk_dac)) { dev_err(dev, "failed to get clock 'sclk_dac'\n"); - ret = -ENXIO; + ret = PTR_ERR(sdev->sclk_dac); goto fail; } sdev->dac = clk_get(dev, "dac"); - if (IS_ERR_OR_NULL(sdev->dac)) { + if (IS_ERR(sdev->dac)) { dev_err(dev, "failed to get clock 'dac'\n"); - ret = -ENXIO; + ret = PTR_ERR(sdev->dac); goto fail_sclk_dac; } sdev->dacphy = clk_get(dev, "dacphy"); - if (IS_ERR_OR_NULL(sdev->dacphy)) { + if (IS_ERR(sdev->dacphy)) { dev_err(dev, "failed to get clock 'dacphy'\n"); - ret = -ENXIO; + ret = PTR_ERR(sdev->dacphy); goto fail_dac; } sclk_vpll = clk_get(dev, "sclk_vpll"); - if (IS_ERR_OR_NULL(sclk_vpll)) { + if (IS_ERR(sclk_vpll)) { dev_err(dev, "failed to get clock 'sclk_vpll'\n"); - ret = -ENXIO; + ret = PTR_ERR(sclk_vpll); goto fail_dacphy; } clk_set_parent(sdev->sclk_dac, sclk_vpll); clk_put(sclk_vpll); sdev->fout_vpll = clk_get(dev, "fout_vpll"); - if (IS_ERR_OR_NULL(sdev->fout_vpll)) { + if (IS_ERR(sdev->fout_vpll)) { dev_err(dev, "failed to get clock 'fout_vpll'\n"); + ret = PTR_ERR(sdev->fout_vpll); goto fail_dacphy; } dev_info(dev, "fout_vpll.rate = %lu\n", clk_get_rate(sclk_vpll)); /* acquire regulator */ sdev->vdac = devm_regulator_get(dev, "vdd33a_dac"); - if (IS_ERR_OR_NULL(sdev->vdac)) { + if (IS_ERR(sdev->vdac)) { dev_err(dev, "failed to get regulator 'vdac'\n"); + ret = PTR_ERR(sdev->vdac); goto fail_fout_vpll; } sdev->vdet = devm_regulator_get(dev, "vdet"); - if (IS_ERR_OR_NULL(sdev->vdet)) { + if (IS_ERR(sdev->vdet)) { dev_err(dev, "failed to get regulator 'vdet'\n"); + ret = PTR_ERR(sdev->vdet); goto fail_fout_vpll; } From aeae3db1c267759661609c76d6c7791231ae438a Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Tue, 18 Dec 2012 04:28:39 -0300 Subject: [PATCH 0588/4607] [media] s5p-tv: Fix incorrect usage of IS_ERR_OR_NULL Replace IS_ERR_OR_NULL with IS_ERR on clk_get results. Signed-off-by: Tony Prisk Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/hdmi_drv.c | 10 +++++----- drivers/media/platform/s5p-tv/mixer_drv.c | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 8a9cf43018f6..1c48ca5e419f 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -781,27 +781,27 @@ static int hdmi_resources_init(struct hdmi_device *hdev) /* get clocks, power */ res->hdmi = clk_get(dev, "hdmi"); - if (IS_ERR_OR_NULL(res->hdmi)) { + if (IS_ERR(res->hdmi)) { dev_err(dev, "failed to get clock 'hdmi'\n"); goto fail; } res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); - if (IS_ERR_OR_NULL(res->sclk_hdmi)) { + if (IS_ERR(res->sclk_hdmi)) { dev_err(dev, "failed to get clock 'sclk_hdmi'\n"); goto fail; } res->sclk_pixel = clk_get(dev, "sclk_pixel"); - if (IS_ERR_OR_NULL(res->sclk_pixel)) { + if (IS_ERR(res->sclk_pixel)) { dev_err(dev, "failed to get clock 'sclk_pixel'\n"); goto fail; } res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy"); - if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) { + if (IS_ERR(res->sclk_hdmiphy)) { dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n"); goto fail; } res->hdmiphy = clk_get(dev, "hdmiphy"); - if (IS_ERR_OR_NULL(res->hdmiphy)) { + if (IS_ERR(res->hdmiphy)) { dev_err(dev, "failed to get clock 'hdmiphy'\n"); goto fail; } diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c index ca0f29717448..c1b2e0ed20d2 100644 --- a/drivers/media/platform/s5p-tv/mixer_drv.c +++ b/drivers/media/platform/s5p-tv/mixer_drv.c @@ -240,27 +240,27 @@ static int mxr_acquire_clocks(struct mxr_device *mdev) struct device *dev = mdev->dev; res->mixer = clk_get(dev, "mixer"); - if (IS_ERR_OR_NULL(res->mixer)) { + if (IS_ERR(res->mixer)) { mxr_err(mdev, "failed to get clock 'mixer'\n"); goto fail; } res->vp = clk_get(dev, "vp"); - if (IS_ERR_OR_NULL(res->vp)) { + if (IS_ERR(res->vp)) { mxr_err(mdev, "failed to get clock 'vp'\n"); goto fail; } res->sclk_mixer = clk_get(dev, "sclk_mixer"); - if (IS_ERR_OR_NULL(res->sclk_mixer)) { + if (IS_ERR(res->sclk_mixer)) { mxr_err(mdev, "failed to get clock 'sclk_mixer'\n"); goto fail; } res->sclk_hdmi = clk_get(dev, "sclk_hdmi"); - if (IS_ERR_OR_NULL(res->sclk_hdmi)) { + if (IS_ERR(res->sclk_hdmi)) { mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n"); goto fail; } res->sclk_dac = clk_get(dev, "sclk_dac"); - if (IS_ERR_OR_NULL(res->sclk_dac)) { + if (IS_ERR(res->sclk_dac)) { mxr_err(mdev, "failed to get clock 'sclk_dac'\n"); goto fail; } From 80f0dee21c7ec39f76f90548c9f08a0e7ec9b3fa Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:00 -0300 Subject: [PATCH 0589/4607] [media] s5p-tv: Add missing braces around sizeof in sdo_drv.c Silences the following checkpatch warnings: WARNING: sizeof *sdev should be sizeof(*sdev) FILE: media/platform/s5p-tv/sdo_drv.c:304: sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL); WARNING: sizeof sdev->sd.name should be sizeof(sdev->sd.name) FILE: media/platform/s5p-tv/sdo_drv.c:394: strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name); Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/sdo_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c index 2d1a65488f2d..35320f08c399 100644 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ b/drivers/media/platform/s5p-tv/sdo_drv.c @@ -301,7 +301,7 @@ static int __devinit sdo_probe(struct platform_device *pdev) struct clk *sclk_vpll; dev_info(dev, "probe start\n"); - sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL); + sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL); if (!sdev) { dev_err(dev, "not enough memory.\n"); ret = -ENOMEM; @@ -397,7 +397,7 @@ static int __devinit sdo_probe(struct platform_device *pdev) /* configuration of interface subdevice */ v4l2_subdev_init(&sdev->sd, &sdo_sd_ops); sdev->sd.owner = THIS_MODULE; - strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name); + strlcpy(sdev->sd.name, "s5p-sdo", sizeof(sdev->sd.name)); /* set default format */ sdev->fmt = sdo_find_format(SDO_DEFAULT_STD); From c0d5120429959de122ddab338fbac838a053387a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:01 -0300 Subject: [PATCH 0590/4607] [media] s5p-tv: Add missing braces around sizeof in mixer_video.c Silences several checkpatch warnings of the type: WARNING: sizeof *out should be sizeof(*out) FILE: media/platform/s5p-tv/mixer_video.c:98: out = kzalloc(sizeof *out, GFP_KERNEL); Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_video.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index 7379e77bf4e1..80ca14b7c4d3 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -95,7 +95,7 @@ int __devinit mxr_acquire_video(struct mxr_device *mdev, /* trying to register next output */ if (sd == NULL) continue; - out = kzalloc(sizeof *out, GFP_KERNEL); + out = kzalloc(sizeof(*out), GFP_KERNEL); if (out == NULL) { mxr_err(mdev, "no memory for '%s'\n", conf->output_name); @@ -127,7 +127,7 @@ fail_output: /* kfree is NULL-safe */ for (i = 0; i < mdev->output_cnt; ++i) kfree(mdev->output[i]); - memset(mdev->output, 0, sizeof mdev->output); + memset(mdev->output, 0, sizeof(mdev->output)); fail_vb2_allocator: /* freeing allocator context */ @@ -160,8 +160,8 @@ static int mxr_querycap(struct file *file, void *priv, mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__); - strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver); - strlcpy(cap->card, layer->vfd.name, sizeof cap->card); + strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof(cap->driver)); + strlcpy(cap->card, layer->vfd.name, sizeof(cap->card)); sprintf(cap->bus_info, "%d", layer->idx); cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT_MPLANE; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; @@ -192,7 +192,7 @@ static void mxr_layer_default_geo(struct mxr_layer *layer) struct mxr_device *mdev = layer->mdev; struct v4l2_mbus_framefmt mbus_fmt; - memset(&layer->geo, 0, sizeof layer->geo); + memset(&layer->geo, 0, sizeof(layer->geo)); mxr_get_mbus_fmt(mdev, &mbus_fmt); @@ -425,7 +425,7 @@ static int mxr_s_selection(struct file *file, void *fh, struct mxr_geometry tmp; struct v4l2_rect res; - memset(&res, 0, sizeof res); + memset(&res, 0, sizeof(res)); mxr_dbg(layer->mdev, "%s: rect: %dx%d@%d,%d\n", __func__, s->r.width, s->r.height, s->r.left, s->r.top); @@ -464,7 +464,7 @@ static int mxr_s_selection(struct file *file, void *fh, /* apply change and update geometry if needed */ if (target) { /* backup current geometry if setup fails */ - memcpy(&tmp, geo, sizeof tmp); + memcpy(&tmp, geo, sizeof(tmp)); /* apply requested selection */ target->x_offset = s->r.left; @@ -496,7 +496,7 @@ static int mxr_s_selection(struct file *file, void *fh, fail: /* restore old geometry, which is not touched if target is NULL */ if (target) - memcpy(geo, &tmp, sizeof tmp); + memcpy(geo, &tmp, sizeof(tmp)); return -ERANGE; } @@ -1071,7 +1071,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, { struct mxr_layer *layer; - layer = kzalloc(sizeof *layer, GFP_KERNEL); + layer = kzalloc(sizeof(*layer), GFP_KERNEL); if (layer == NULL) { mxr_err(mdev, "not enough memory for layer.\n"); goto fail; From ad84291c6af11e07d8c55eb4ea4d64ade054e2a5 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:02 -0300 Subject: [PATCH 0591/4607] [media] s5p-tv: Add missing braces around sizeof in mixer_reg.c Silences checkpatch warnings of the type: WARNING: sizeof filter_y_horiz_tap8 should be sizeof(filter_y_horiz_tap8) FILE: media/platform/s5p-tv/mixer_reg.c:473: filter_y_horiz_tap8, sizeof filter_y_horiz_tap8); Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_reg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/s5p-tv/mixer_reg.c b/drivers/media/platform/s5p-tv/mixer_reg.c index 3b1670a045f4..b713403024ef 100644 --- a/drivers/media/platform/s5p-tv/mixer_reg.c +++ b/drivers/media/platform/s5p-tv/mixer_reg.c @@ -470,11 +470,11 @@ static inline void mxr_reg_vp_filter_set(struct mxr_device *mdev, static void mxr_reg_vp_default_filter(struct mxr_device *mdev) { mxr_reg_vp_filter_set(mdev, VP_POLY8_Y0_LL, - filter_y_horiz_tap8, sizeof filter_y_horiz_tap8); + filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8)); mxr_reg_vp_filter_set(mdev, VP_POLY4_Y0_LL, - filter_y_vert_tap4, sizeof filter_y_vert_tap4); + filter_y_vert_tap4, sizeof(filter_y_vert_tap4)); mxr_reg_vp_filter_set(mdev, VP_POLY4_C0_LL, - filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4); + filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4)); } static void mxr_reg_mxr_dump(struct mxr_device *mdev) From dc03398528c83357e1e95481cd447f6bf1036b76 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:03 -0300 Subject: [PATCH 0592/4607] [media] s5p-tv: Add missing braces around sizeof in mixer_drv.c Silences checkpatch warnings of type: WARNING: sizeof mdev->res should be sizeof(mdev->res) FILE: media/platform/s5p-tv/mixer_drv.c:301: memset(&mdev->res, 0, sizeof mdev->res); WARNING: sizeof *mdev should be sizeof(*mdev) FILE: media/platform/s5p-tv/mixer_drv.c:385: mdev = kzalloc(sizeof *mdev, GFP_KERNEL); Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c index c1b2e0ed20d2..d5cf603551b2 100644 --- a/drivers/media/platform/s5p-tv/mixer_drv.c +++ b/drivers/media/platform/s5p-tv/mixer_drv.c @@ -298,7 +298,7 @@ static void mxr_release_resources(struct mxr_device *mdev) { mxr_release_clocks(mdev); mxr_release_plat_resources(mdev); - memset(&mdev->res, 0, sizeof mdev->res); + memset(&mdev->res, 0, sizeof(mdev->res)); } static void mxr_release_layers(struct mxr_device *mdev) @@ -382,7 +382,7 @@ static int __devinit mxr_probe(struct platform_device *pdev) /* mdev does not exist yet so no mxr_dbg is used */ dev_info(dev, "probe start\n"); - mdev = kzalloc(sizeof *mdev, GFP_KERNEL); + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); if (!mdev) { dev_err(dev, "not enough memory.\n"); ret = -ENOMEM; From 45b56d572cd8b4f118ed3a006aa33527fd966389 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:04 -0300 Subject: [PATCH 0593/4607] [media] s5p-tv: Add missing braces around sizeof in hdmiphy_drv.c Fixes the following checkpatch warning: WARNING: sizeof *ctx should be sizeof(*ctx) FILE: media/platform/s5p-tv/hdmiphy_drv.c:287: ctx = kzalloc(sizeof *ctx, GFP_KERNEL); Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/hdmiphy_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c index f67b38631801..94c2a13c3b3a 100644 --- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c @@ -284,7 +284,7 @@ static int __devinit hdmiphy_probe(struct i2c_client *client, { struct hdmiphy_ctx *ctx; - ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; From d322bb915425b65a51c48b90e912af55762cc745 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 26 Nov 2012 01:49:05 -0300 Subject: [PATCH 0594/4607] [media] s5p-tv: Add missing braces around sizeof in hdmi_drv.c Fixes the following checkpatch warnings: WARNING: sizeof *fmt should be sizeof(*fmt) WARNING: sizeof *res should be sizeof(*res) WARNING: sizeof *res should be sizeof(*res) WARNING: sizeof sd->name should be sizeof(sd->name) Signed-off-by: Sachin Kamat Acked-by: Tomasz Stanislawski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/hdmi_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 1c48ca5e419f..c0d0f84e5b86 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -656,7 +656,7 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, dev_dbg(hdev->dev, "%s\n", __func__); if (!hdev->cur_conf) return -EINVAL; - memset(fmt, 0, sizeof *fmt); + memset(fmt, 0, sizeof(*fmt)); fmt->width = t->hact.end - t->hact.beg; fmt->height = t->vact[0].end - t->vact[0].beg; fmt->code = V4L2_MBUS_FMT_FIXED; /* means RGB888 */ @@ -760,7 +760,7 @@ static void hdmi_resources_cleanup(struct hdmi_device *hdev) clk_put(res->sclk_hdmi); if (!IS_ERR_OR_NULL(res->hdmi)) clk_put(res->hdmi); - memset(res, 0, sizeof *res); + memset(res, 0, sizeof(*res)); } static int hdmi_resources_init(struct hdmi_device *hdev) @@ -777,7 +777,7 @@ static int hdmi_resources_init(struct hdmi_device *hdev) dev_dbg(dev, "HDMI resource init\n"); - memset(res, 0, sizeof *res); + memset(res, 0, sizeof(*res)); /* get clocks, power */ res->hdmi = clk_get(dev, "hdmi"); @@ -955,7 +955,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev) v4l2_subdev_init(sd, &hdmi_sd_ops); sd->owner = THIS_MODULE; - strlcpy(sd->name, "s5p-hdmi", sizeof sd->name); + strlcpy(sd->name, "s5p-hdmi", sizeof(sd->name)); hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET; /* FIXME: missing fail preset is not supported */ hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset); From 15514fb567728b236dfbedeb360f55791302bf6e Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Tue, 18 Dec 2012 05:28:41 -0300 Subject: [PATCH 0595/4607] [media] s5p-g2d: Fix incorrect usage of IS_ERR_OR_NULL Replace IS_ERR_OR_NULL with IS_ERR on clk_get results. Signed-off-by: Tony Prisk Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 1bfbc325836b..dcd53357704d 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -715,7 +715,7 @@ static int g2d_probe(struct platform_device *pdev) } dev->clk = clk_get(&pdev->dev, "sclk_fimg2d"); - if (IS_ERR_OR_NULL(dev->clk)) { + if (IS_ERR(dev->clk)) { dev_err(&pdev->dev, "failed to get g2d clock\n"); return -ENXIO; } @@ -727,7 +727,7 @@ static int g2d_probe(struct platform_device *pdev) } dev->gate = clk_get(&pdev->dev, "fimg2d"); - if (IS_ERR_OR_NULL(dev->gate)) { + if (IS_ERR(dev->gate)) { dev_err(&pdev->dev, "failed to get g2d clock gate\n"); ret = -ENXIO; goto unprep_clk; From 371a664eea4e2c0d2acc1df082f7e08693506f89 Mon Sep 17 00:00:00 2001 From: Shaik Ameer Basha Date: Fri, 7 Dec 2012 08:28:55 -0300 Subject: [PATCH 0596/4607] [media] exynos-gsc: Support dmabuf export buffer This patch adds the dmabuf export buffer feature to the Exynos G-Scaler driver. Signed-off-by: Shaik Ameer Basha Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-m2m.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 0d06d6c6f373..386c0a7a3a52 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -373,6 +373,13 @@ static int gsc_m2m_reqbufs(struct file *file, void *fh, return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } +static int gsc_m2m_expbuf(struct file *file, void *fh, + struct v4l2_exportbuffer *eb) +{ + struct gsc_ctx *ctx = fh_to_ctx(fh); + return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); +} + static int gsc_m2m_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { @@ -554,6 +561,7 @@ static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = { .vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane, .vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane, .vidioc_reqbufs = gsc_m2m_reqbufs, + .vidioc_expbuf = gsc_m2m_expbuf, .vidioc_querybuf = gsc_m2m_querybuf, .vidioc_qbuf = gsc_m2m_qbuf, .vidioc_dqbuf = gsc_m2m_dqbuf, @@ -571,7 +579,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(src_vq, 0, sizeof(*src_vq)); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; src_vq->drv_priv = ctx; src_vq->ops = &gsc_m2m_qops; src_vq->mem_ops = &vb2_dma_contig_memops; @@ -583,7 +591,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(dst_vq, 0, sizeof(*dst_vq)); dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; dst_vq->drv_priv = ctx; dst_vq->ops = &gsc_m2m_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; From b27a23be0d1de44ab2cc01495b4f9149f4f762d5 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Thu, 25 Oct 2012 05:24:14 -0300 Subject: [PATCH 0597/4607] [media] s5p-mfc: Add device tree support This patch will add the device tree support for MFC driver. Signed-off-by: Arun Kumar K Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 100 +++++++++++++++++++---- 1 file changed, 84 insertions(+), 16 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 3afe879d54d7..e2fe64efb756 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "s5p_mfc_common.h" #include "s5p_mfc_ctrl.h" @@ -1027,6 +1028,8 @@ static int match_child(struct device *dev, void *data) return !strcmp(dev_name(dev), (char *)data); } +static void *mfc_get_drv_data(struct platform_device *pdev); + /* MFC probe function */ static int s5p_mfc_probe(struct platform_device *pdev) { @@ -1034,6 +1037,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) struct video_device *vfd; struct resource *res; int ret; + unsigned int mem_info[2]; pr_debug("%s++\n", __func__); dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -1050,8 +1054,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) return -ENODEV; } - dev->variant = (struct s5p_mfc_variant *) - platform_get_device_id(pdev)->driver_data; + dev->variant = mfc_get_drv_data(pdev); ret = s5p_mfc_init_pm(dev); if (ret < 0) { @@ -1081,20 +1084,55 @@ static int s5p_mfc_probe(struct platform_device *pdev) goto err_res; } - dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l", - match_child); - if (!dev->mem_dev_l) { - mfc_err("Mem child (L) device get failed\n"); - ret = -ENODEV; - goto err_res; - } + if (pdev->dev.of_node) { + dev->mem_dev_l = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!dev->mem_dev_l) { + mfc_err("Not enough memory\n"); + ret = -ENOMEM; + goto err_res; + } + of_property_read_u32_array(pdev->dev.of_node, "samsung,mfc-l", + mem_info, 2); + if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0], + mem_info[0], mem_info[1], + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { + mfc_err("Failed to declare coherent memory for\n" + "MFC device\n"); + ret = -ENOMEM; + goto err_res; + } - dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r", - match_child); - if (!dev->mem_dev_r) { - mfc_err("Mem child (R) device get failed\n"); - ret = -ENODEV; - goto err_res; + dev->mem_dev_r = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!dev->mem_dev_r) { + mfc_err("Not enough memory\n"); + ret = -ENOMEM; + goto err_res; + } + of_property_read_u32_array(pdev->dev.of_node, "samsung,mfc-r", + mem_info, 2); + if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0], + mem_info[0], mem_info[1], + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { + pr_err("Failed to declare coherent memory for\n" + "MFC device\n"); + ret = -ENOMEM; + goto err_res; + } + } else { + dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, + "s5p-mfc-l", match_child); + if (!dev->mem_dev_l) { + mfc_err("Mem child (L) device get failed\n"); + ret = -ENODEV; + goto err_res; + } + dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, + "s5p-mfc-r", match_child); + if (!dev->mem_dev_r) { + mfc_err("Mem child (R) device get failed\n"); + ret = -ENODEV; + goto err_res; + } } dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l); @@ -1366,6 +1404,35 @@ static struct platform_device_id mfc_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, mfc_driver_ids); +static const struct of_device_id exynos_mfc_match[] = { + { + .compatible = "samsung,mfc-v5", + .data = &mfc_drvdata_v5, + }, { + .compatible = "samsung,mfc-v6", + .data = &mfc_drvdata_v6, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_mfc_match); + +static void *mfc_get_drv_data(struct platform_device *pdev) +{ + struct s5p_mfc_variant *driver_data = NULL; + + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(of_match_ptr(exynos_mfc_match), + pdev->dev.of_node); + if (match) + driver_data = (struct s5p_mfc_variant *)match->data; + } else { + driver_data = (struct s5p_mfc_variant *) + platform_get_device_id(pdev)->driver_data; + } + return driver_data; +} + static struct platform_driver s5p_mfc_driver = { .probe = s5p_mfc_probe, .remove = __devexit_p(s5p_mfc_remove), @@ -1373,7 +1440,8 @@ static struct platform_driver s5p_mfc_driver = { .driver = { .name = S5P_MFC_NAME, .owner = THIS_MODULE, - .pm = &s5p_mfc_pm_ops + .pm = &s5p_mfc_pm_ops, + .of_match_table = exynos_mfc_match, }, }; From 20fe1cf081ae861e66611b3a8f289fabd8d56a8f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Dec 2012 08:17:08 -0300 Subject: [PATCH 0598/4607] [media] s5p-mfc: remove unused variable The variable index is initialized but never used otherwise, so remove the unused variable. Signed-off-by: Wei Yongjun Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 5 ----- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 6 ------ 2 files changed, 11 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index e2fe64efb756..a49d0e5db93a 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -274,7 +274,6 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) struct s5p_mfc_buf *dst_buf; size_t dspl_y_addr; unsigned int frame_type; - unsigned int index; dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev); frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev); @@ -311,7 +310,6 @@ static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) vb2_buffer_done(dst_buf->b, err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - index = dst_buf->b->v4l2_buf.index; break; } } @@ -327,8 +325,6 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, unsigned long flags; unsigned int res_change; - unsigned int index; - dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev) & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK; res_change = (s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev) @@ -388,7 +384,6 @@ static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, mfc_debug(2, "Running again the same buffer\n"); ctx->after_packed_pb = 1; } else { - index = src_buf->b->v4l2_buf.index; mfc_debug(2, "MFC needs next buffer\n"); ctx->consumed_stream = 0; list_del(&src_buf->list); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 3a8cfd9fc1bd..bf4d2f44f009 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1408,7 +1408,6 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx) struct s5p_mfc_buf *temp_vb; unsigned long flags; int last_frame = 0; - unsigned int index; spin_lock_irqsave(&dev->irqlock, flags); @@ -1427,8 +1426,6 @@ static inline int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx) temp_vb->b->v4l2_planes[0].bytesused); spin_unlock_irqrestore(&dev->irqlock, flags); - index = temp_vb->b->v4l2_buf.index; - dev->curr_ctx = ctx->num; s5p_mfc_clean_ctx_int_flags(ctx); if (temp_vb->b->v4l2_planes[0].bytesused == 0) { @@ -1452,7 +1449,6 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) unsigned int src_y_size, src_c_size; */ unsigned int dst_size; - unsigned int index; spin_lock_irqsave(&dev->irqlock, flags); @@ -1487,8 +1483,6 @@ static inline int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx) spin_unlock_irqrestore(&dev->irqlock, flags); - index = src_mb->b->v4l2_buf.index; - dev->curr_ctx = ctx->num; s5p_mfc_clean_ctx_int_flags(ctx); s5p_mfc_encode_one_frame_v6(ctx); From 8f23cc0222a9fe9c43f679dcb3d38604b30cf7c8 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Thu, 22 Nov 2012 06:15:55 -0300 Subject: [PATCH 0599/4607] [media] s5p-mfc: Flush DPB buffers during stream off Flushing of delay DPB buffers have to be done during stream off. In MFC v6, it is done with a risc to host command. Signed-off-by: Arun Kumar K Signed-off-by: Arun Mankuzhi Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 6 ++++++ drivers/media/platform/s5p-mfc/s5p_mfc_common.h | 1 + drivers/media/platform/s5p-mfc/s5p_mfc_dec.c | 15 +++++++++++++-- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 17 +++++++++++------ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index a49d0e5db93a..3930177db125 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -686,6 +686,12 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv) s5p_mfc_handle_stream_complete(ctx, reason, err); break; + case S5P_MFC_R2H_CMD_DPB_FLUSH_RET: + clear_work_bit(ctx); + ctx->state = MFCINST_RUNNING; + wake_up(&ctx->queue); + goto irq_cleanup_hw; + default: mfc_debug(2, "Unknown int reason\n"); s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index f02e0497ca98..3b9b600a6182 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -145,6 +145,7 @@ enum s5p_mfc_inst_state { MFCINST_RETURN_INST, MFCINST_ERROR, MFCINST_ABORT, + MFCINST_FLUSH, MFCINST_RES_CHANGE_INIT, MFCINST_RES_CHANGE_FLUSH, MFCINST_RES_CHANGE_END, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index 6dad9a74f61c..4582473978ca 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -991,24 +991,35 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q) S5P_MFC_R2H_CMD_FRAME_DONE_RET, 0); aborted = 1; } - spin_lock_irqsave(&dev->irqlock, flags); if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + spin_lock_irqsave(&dev->irqlock, flags); s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->dst_queue, &ctx->vq_dst); INIT_LIST_HEAD(&ctx->dst_queue); ctx->dst_queue_cnt = 0; ctx->dpb_flush_flag = 1; ctx->dec_dst_flag = 0; + spin_unlock_irqrestore(&dev->irqlock, flags); + if (IS_MFCV6(dev) && (ctx->state == MFCINST_RUNNING)) { + ctx->state = MFCINST_FLUSH; + set_work_bit_irqsave(ctx); + s5p_mfc_clean_ctx_int_flags(ctx); + s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); + if (s5p_mfc_wait_for_done_ctx(ctx, + S5P_MFC_R2H_CMD_DPB_FLUSH_RET, 0)) + mfc_err("Err flushing buffers\n"); + } } if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + spin_lock_irqsave(&dev->irqlock, flags); s5p_mfc_hw_call(dev->mfc_ops, cleanup_queue, &ctx->src_queue, &ctx->vq_src); INIT_LIST_HEAD(&ctx->src_queue); ctx->src_queue_cnt = 0; + spin_unlock_irqrestore(&dev->irqlock, flags); } if (aborted) ctx->state = MFCINST_RUNNING; - spin_unlock_irqrestore(&dev->irqlock, flags); return 0; } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index bf4d2f44f009..5f9a5e073456 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1253,12 +1253,14 @@ int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) static inline void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush) { struct s5p_mfc_dev *dev = ctx->dev; - unsigned int dpb; - if (flush) - dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) | (1 << 14); - else - dpb = READL(S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & ~(1 << 14); - WRITEL(dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL); + + if (flush) { + dev->curr_ctx = ctx->num; + s5p_mfc_clean_ctx_int_flags(ctx); + WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6); + s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev, + S5P_FIMV_H2R_CMD_FLUSH_V6, NULL); + } } /* Decode a single frame */ @@ -1650,6 +1652,9 @@ void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev) case MFCINST_HEAD_PARSED: ret = s5p_mfc_run_init_dec_buffers(ctx); break; + case MFCINST_FLUSH: + s5p_mfc_set_flush(ctx, ctx->dpb_flush_flag); + break; case MFCINST_RES_CHANGE_INIT: s5p_mfc_run_dec_last_frames(ctx); break; From 55e2cf34ba290a18782c64786f0e1842b8394c45 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 28 Dec 2012 06:18:27 -0300 Subject: [PATCH 0600/4607] [media] s5p-mfc: Remove redundant 'break' The code returns before this statement. Hence not required. Silences the following smatch message: drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c:525 s5p_mfc_set_dec_frame_buffer_v5() info: ignoring unreachable code. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index bf7d010a4107..2f099c44cb54 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -523,7 +523,6 @@ int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) mfc_err("Unknown codec for decoding (%x)\n", ctx->codec_mode); return -EINVAL; - break; } frame_size = ctx->luma_size; frame_size_ch = ctx->chroma_size; From b311ea4bf18153b78d26e62304a30434447dbbf0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 2 Jan 2013 05:13:33 -0300 Subject: [PATCH 0601/4607] [media] s5p-mfc: Fix a typo in error message in s5p_mfc_pm.c Fixed a trivial typo. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c index 2895333866fc..6aa38a56aaf2 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c @@ -46,7 +46,7 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev) ret = clk_prepare(pm->clock_gate); if (ret) { - mfc_err("Failed to preapre clock-gating control\n"); + mfc_err("Failed to prepare clock-gating control\n"); goto err_p_ip_clk; } From 4a9c85aa495a35593eb7f3fd3dc259fd020308b4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 2 Jan 2013 06:45:49 -0300 Subject: [PATCH 0602/4607] [media] s5p-mfc: Fix an error check Checking unsigned variable for negative value always returns false. Hence make this value signed as we expect it to be negative too. Fixes the following smatch warning: drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c:572 s5p_mfc_set_enc_ref_buffer_v6() warn: unsigned 'buf_size1' is never less than zero. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 5f9a5e073456..91d508729d43 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -535,8 +535,8 @@ void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx, int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; - size_t buf_addr1, buf_size1; - int i; + size_t buf_addr1; + int i, buf_size1; mfc_debug_enter(); From 2e731e443fcc8e4553201ad0573c1d5571e906ac Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 3 Jan 2013 11:02:07 -0300 Subject: [PATCH 0603/4607] [media] s5p-mfc: Move firmware allocation point to avoid allocation problems Move firmware allocation from open to probe to avoid problems when using CMA for allocation. In certain circumstances CMA may allocate buffer that is not in the beginning of the MFC memory area. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 29 ++-- .../media/platform/s5p-mfc/s5p_mfc_common.h | 10 +- drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c | 149 ++++++++---------- drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h | 3 +- 4 files changed, 94 insertions(+), 97 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 3930177db125..9a679fab73e2 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -793,14 +793,16 @@ static int s5p_mfc_open(struct file *file) goto err_pwr_enable; } s5p_mfc_clock_on(); - ret = s5p_mfc_alloc_and_load_firmware(dev); - if (ret) - goto err_alloc_fw; + ret = s5p_mfc_load_firmware(dev); + if (ret) { + s5p_mfc_clock_off(); + goto err_load_fw; + } /* Init the FW */ ret = s5p_mfc_init_hw(dev); + s5p_mfc_clock_off(); if (ret) goto err_init_hw; - s5p_mfc_clock_off(); } /* Init videobuf2 queue for CAPTURE */ q = &ctx->vq_dst; @@ -849,17 +851,16 @@ static int s5p_mfc_open(struct file *file) return ret; /* Deinit when failure occured */ err_queue_init: + if (dev->num_inst == 1) + s5p_mfc_deinit_hw(dev); err_init_hw: - s5p_mfc_release_firmware(dev); -err_alloc_fw: +err_load_fw: dev->ctx[ctx->num] = NULL; del_timer_sync(&dev->watchdog_timer); - s5p_mfc_clock_off(); err_pwr_enable: if (dev->num_inst == 1) { if (s5p_mfc_power_off() < 0) mfc_err("power off failed\n"); - s5p_mfc_release_firmware(dev); } err_ctrls_setup: s5p_mfc_dec_ctrls_delete(ctx); @@ -917,11 +918,8 @@ static int s5p_mfc_release(struct file *file) clear_bit(0, &dev->hw_lock); dev->num_inst--; if (dev->num_inst == 0) { - mfc_debug(2, "Last instance - release firmware\n"); - /* reset <-> F/W release */ - s5p_mfc_reset(dev); + mfc_debug(2, "Last instance\n"); s5p_mfc_deinit_hw(dev); - s5p_mfc_release_firmware(dev); del_timer_sync(&dev->watchdog_timer); if (s5p_mfc_power_off() < 0) mfc_err("Power off failed\n"); @@ -1149,6 +1147,10 @@ static int s5p_mfc_probe(struct platform_device *pdev) mutex_init(&dev->mfc_mutex); + ret = s5p_mfc_alloc_firmware(dev); + if (ret) + goto err_alloc_fw; + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) goto err_v4l2_dev_reg; @@ -1230,6 +1232,8 @@ err_dec_reg: err_dec_alloc: v4l2_device_unregister(&dev->v4l2_dev); err_v4l2_dev_reg: + s5p_mfc_release_firmware(dev); +err_alloc_fw: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); err_mem_init_ctx_1: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); @@ -1255,6 +1259,7 @@ static int __devexit s5p_mfc_remove(struct platform_device *pdev) video_unregister_device(dev->vfd_enc); video_unregister_device(dev->vfd_dec); v4l2_device_unregister(&dev->v4l2_dev); + s5p_mfc_release_firmware(dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 3b9b600a6182..0df6454e407e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -278,8 +278,9 @@ struct s5p_mfc_priv_buf { * @int_err: error number for last interrupt * @queue: waitqueue for waiting for completion of device commands * @fw_size: size of firmware - * @bank1: address of the beggining of bank 1 memory - * @bank2: address of the beggining of bank 2 memory + * @fw_virt_addr: virtual firmware address + * @bank1: address of the beginning of bank 1 memory + * @bank2: address of the beginning of bank 2 memory * @hw_lock: used for hardware locking * @ctx: array of driver contexts * @curr_ctx: number of the currently running context @@ -318,8 +319,9 @@ struct s5p_mfc_dev { unsigned int int_err; wait_queue_head_t queue; size_t fw_size; - size_t bank1; - size_t bank2; + void *fw_virt_addr; + dma_addr_t bank1; + dma_addr_t bank2; unsigned long hw_lock; struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS]; int curr_ctx; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index 585b7b0ed8ec..1682271c2453 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -22,16 +22,64 @@ #include "s5p_mfc_opr.h" #include "s5p_mfc_pm.h" -static void *s5p_mfc_bitproc_buf; -static size_t s5p_mfc_bitproc_phys; -static unsigned char *s5p_mfc_bitproc_virt; +/* Allocate memory for firmware */ +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev) +{ + void *bank2_virt; + dma_addr_t bank2_dma_addr; -/* Allocate and load firmware */ -int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) + dev->fw_size = dev->variant->buf_size->fw; + + if (dev->fw_virt_addr) { + mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n"); + return -ENOMEM; + } + + dev->fw_virt_addr = dma_alloc_coherent(dev->mem_dev_l, dev->fw_size, + &dev->bank1, GFP_KERNEL); + + if (IS_ERR(dev->fw_virt_addr)) { + dev->fw_virt_addr = NULL; + mfc_err("Allocating bitprocessor buffer failed\n"); + return -ENOMEM; + } + + dev->bank1 = dev->bank1; + + if (HAS_PORTNUM(dev) && IS_TWOPORT(dev)) { + bank2_virt = dma_alloc_coherent(dev->mem_dev_r, 1 << MFC_BASE_ALIGN_ORDER, + &bank2_dma_addr, GFP_KERNEL); + + if (IS_ERR(dev->fw_virt_addr)) { + mfc_err("Allocating bank2 base failed\n"); + dma_free_coherent(dev->mem_dev_l, dev->fw_size, + dev->fw_virt_addr, dev->bank1); + dev->fw_virt_addr = NULL; + return -ENOMEM; + } + + /* Valid buffers passed to MFC encoder with LAST_FRAME command + * should not have address of bank2 - MFC will treat it as a null frame. + * To avoid such situation we set bank2 address below the pool address. + */ + dev->bank2 = bank2_dma_addr - (1 << MFC_BASE_ALIGN_ORDER); + + dma_free_coherent(dev->mem_dev_r, 1 << MFC_BASE_ALIGN_ORDER, + bank2_virt, bank2_dma_addr); + + } else { + /* In this case bank2 can point to the same address as bank1. + * Firmware will always occupy the beggining of this area so it is + * impossible having a video frame buffer with zero address. */ + dev->bank2 = dev->bank1; + } + return 0; +} + +/* Load firmware */ +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev) { struct firmware *fw_blob; - size_t bank2_base_phys; - void *b_base; int err; /* Firmare has to be present as a separate file or compiled @@ -44,77 +92,17 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n"); return -EINVAL; } - dev->fw_size = dev->variant->buf_size->fw; if (fw_blob->size > dev->fw_size) { mfc_err("MFC firmware is too big to be loaded\n"); release_firmware(fw_blob); return -ENOMEM; } - if (s5p_mfc_bitproc_buf) { - mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n"); + if (!dev->fw_virt_addr) { + mfc_err("MFC firmware is not allocated\n"); release_firmware(fw_blob); - return -ENOMEM; + return -EINVAL; } - s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size); - if (IS_ERR(s5p_mfc_bitproc_buf)) { - s5p_mfc_bitproc_buf = NULL; - mfc_err("Allocating bitprocessor buffer failed\n"); - release_firmware(fw_blob); - return -ENOMEM; - } - s5p_mfc_bitproc_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], s5p_mfc_bitproc_buf); - if (s5p_mfc_bitproc_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) { - mfc_err("The base memory for bank 1 is not aligned to 128KB\n"); - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - release_firmware(fw_blob); - return -EIO; - } - s5p_mfc_bitproc_virt = vb2_dma_contig_memops.vaddr(s5p_mfc_bitproc_buf); - if (!s5p_mfc_bitproc_virt) { - mfc_err("Bitprocessor memory remap failed\n"); - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - release_firmware(fw_blob); - return -EIO; - } - dev->bank1 = s5p_mfc_bitproc_phys; - if (HAS_PORTNUM(dev) && IS_TWOPORT(dev)) { - b_base = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], - 1 << MFC_BASE_ALIGN_ORDER); - if (IS_ERR(b_base)) { - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - mfc_err("Allocating bank2 base failed\n"); - release_firmware(fw_blob); - return -ENOMEM; - } - bank2_base_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], b_base); - vb2_dma_contig_memops.put(b_base); - if (bank2_base_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) { - mfc_err("The base memory for bank 2 is not aligned to 128KB\n"); - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; - release_firmware(fw_blob); - return -EIO; - } - /* Valid buffers passed to MFC encoder with LAST_FRAME command - * should not have address of bank2 - MFC will treat it as a null frame. - * To avoid such situation we set bank2 address below the pool address. - */ - dev->bank2 = bank2_base_phys - (1 << MFC_BASE_ALIGN_ORDER); - } else { - dev->bank2 = dev->bank1; - } - memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size); + memcpy(dev->fw_virt_addr, fw_blob->data, fw_blob->size); wmb(); release_firmware(fw_blob); mfc_debug_leave(); @@ -142,12 +130,12 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev) release_firmware(fw_blob); return -ENOMEM; } - if (s5p_mfc_bitproc_buf == NULL || s5p_mfc_bitproc_phys == 0) { - mfc_err("MFC firmware is not allocated or was not mapped correctly\n"); + if (dev->fw_virt_addr) { + mfc_err("MFC firmware is not allocated\n"); release_firmware(fw_blob); return -EINVAL; } - memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size); + memcpy(dev->fw_virt_addr, fw_blob->data, fw_blob->size); wmb(); release_firmware(fw_blob); mfc_debug_leave(); @@ -159,12 +147,11 @@ int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev) { /* Before calling this function one has to make sure * that MFC is no longer processing */ - if (!s5p_mfc_bitproc_buf) + if (!dev->fw_virt_addr) return -EINVAL; - vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_virt = NULL; - s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = NULL; + dma_free_coherent(dev->mem_dev_l, dev->fw_size, dev->fw_virt_addr, + dev->bank1); + dev->fw_virt_addr = NULL; return 0; } @@ -257,8 +244,10 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev) int ret; mfc_debug_enter(); - if (!s5p_mfc_bitproc_buf) + if (!dev->fw_virt_addr) { + mfc_err("Firmware memory is not allocated.\n"); return -EINVAL; + } /* 0. MFC reset */ mfc_debug(2, "MFC reset..\n"); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h index 90aa9b9886d5..6a9b6f8606bb 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h @@ -16,7 +16,8 @@ #include "s5p_mfc_common.h" int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev); -int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev); +int s5p_mfc_alloc_firmware(struct s5p_mfc_dev *dev); +int s5p_mfc_load_firmware(struct s5p_mfc_dev *dev); int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev); int s5p_mfc_init_hw(struct s5p_mfc_dev *dev); From ef89fff874758c0f08501bdc2932b7e77421cfc6 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 3 Jan 2013 07:06:03 -0300 Subject: [PATCH 0604/4607] [media] s5p-mfc: Correct check of vb2_dma_contig_init_ctx return value vb2_dma_contig_init_ctx returns an error if failed, NULL check is not necessary. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 9a679fab73e2..4fcd075e8699 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1135,12 +1135,12 @@ static int s5p_mfc_probe(struct platform_device *pdev) } dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l); - if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) { + if (IS_ERR(dev->alloc_ctx[0])) { ret = PTR_ERR(dev->alloc_ctx[0]); goto err_res; } dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r); - if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) { + if (IS_ERR(dev->alloc_ctx[1])) { ret = PTR_ERR(dev->alloc_ctx[1]); goto err_mem_init_ctx_1; } From 317b4ca4982ea2429b75d0acd10445ec9475aa86 Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 3 Jan 2013 07:06:04 -0300 Subject: [PATCH 0605/4607] [media] s5p-mfc: Change internal buffer allocation from vb2 ops to dma_alloc_coherent Change internal buffer allocation from vb2 memory ops call to direct calls of dma_alloc_coherent. This change shortens the code and makes it much more readable. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/s5p-mfc/s5p_mfc_common.h | 20 +- drivers/media/platform/s5p-mfc/s5p_mfc_opr.c | 30 +++ drivers/media/platform/s5p-mfc/s5p_mfc_opr.h | 5 + .../media/platform/s5p-mfc/s5p_mfc_opr_v5.c | 196 ++++++------------ .../media/platform/s5p-mfc/s5p_mfc_opr_v6.c | 121 ++++------- 5 files changed, 143 insertions(+), 229 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h index 0df6454e407e..202d1d7a37a8 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h @@ -496,15 +496,9 @@ struct s5p_mfc_codec_ops { * flushed * @head_processed: flag mentioning whether the header data is processed * completely or not - * @bank1_buf: handle to memory allocated for temporary buffers from + * @bank1: handle to memory allocated for temporary buffers from * memory bank 1 - * @bank1_phys: address of the temporary buffers from memory bank 1 - * @bank1_size: size of the memory allocated for temporary buffers from - * memory bank 1 - * @bank2_buf: handle to memory allocated for temporary buffers from - * memory bank 2 - * @bank2_phys: address of the temporary buffers from memory bank 2 - * @bank2_size: size of the memory allocated for temporary buffers from + * @bank2: handle to memory allocated for temporary buffers from * memory bank 2 * @capture_state: state of the capture buffers queue * @output_state: state of the output buffers queue @@ -584,14 +578,8 @@ struct s5p_mfc_ctx { unsigned int dpb_flush_flag; unsigned int head_processed; - /* Buffers */ - void *bank1_buf; - size_t bank1_phys; - size_t bank1_size; - - void *bank2_buf; - size_t bank2_phys; - size_t bank2_size; + struct s5p_mfc_priv_buf bank1; + struct s5p_mfc_priv_buf bank2; enum s5p_mfc_queue_state capture_state; enum s5p_mfc_queue_state output_state; diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c index 6932e90d4065..b4c194331d8c 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c @@ -12,6 +12,7 @@ * published by the Free Software Foundation. */ +#include "s5p_mfc_debug.h" #include "s5p_mfc_opr.h" #include "s5p_mfc_opr_v5.h" #include "s5p_mfc_opr_v6.h" @@ -29,3 +30,32 @@ void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev) } dev->mfc_ops = s5p_mfc_ops; } + +int s5p_mfc_alloc_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b) +{ + + mfc_debug(3, "Allocating priv: %d\n", b->size); + + b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL); + + if (!b->virt) { + mfc_err("Allocating private buffer failed\n"); + return -ENOMEM; + } + + mfc_debug(3, "Allocated addr %p %08x\n", b->virt, b->dma); + return 0; +} + +void s5p_mfc_release_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b) +{ + if (b->virt) { + dma_free_coherent(dev, b->size, b->virt, b->dma); + b->virt = 0; + b->dma = 0; + b->size = 0; + } +} + diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h index 420abecafec0..754c540e7a7e 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h @@ -80,5 +80,10 @@ struct s5p_mfc_hw_ops { }; void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev); +int s5p_mfc_alloc_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b); +void s5p_mfc_release_priv_buf(struct device *dev, + struct s5p_mfc_priv_buf *b); + #endif /* S5P_MFC_OPR_H_ */ diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c index 2f099c44cb54..f61dba837899 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c @@ -38,39 +38,26 @@ int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv; + int ret; - ctx->dsc.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], - buf_size->dsc); - if (IS_ERR_VALUE((int)ctx->dsc.alloc)) { - ctx->dsc.alloc = NULL; - mfc_err("Allocating DESC buffer failed\n"); - return -ENOMEM; + ctx->dsc.size = buf_size->dsc; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->dsc); + if (ret) { + mfc_err("Failed to allocate temporary buffer\n"); + return ret; } - ctx->dsc.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->dsc.alloc); + BUG_ON(ctx->dsc.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); - ctx->dsc.virt = vb2_dma_contig_memops.vaddr(ctx->dsc.alloc); - if (ctx->dsc.virt == NULL) { - vb2_dma_contig_memops.put(ctx->dsc.alloc); - ctx->dsc.dma = 0; - ctx->dsc.alloc = NULL; - mfc_err("Remapping DESC buffer failed\n"); - return -ENOMEM; - } - memset(ctx->dsc.virt, 0, buf_size->dsc); + memset(ctx->dsc.virt, 0, ctx->dsc.size); wmb(); return 0; } + /* Release temporary buffers for decoding */ void s5p_mfc_release_dec_desc_buffer_v5(struct s5p_mfc_ctx *ctx) { - if (ctx->dsc.dma) { - vb2_dma_contig_memops.put(ctx->dsc.alloc); - ctx->dsc.alloc = NULL; - ctx->dsc.dma = 0; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->dsc); } /* Allocate codec buffers */ @@ -80,6 +67,7 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) unsigned int enc_ref_y_size = 0; unsigned int enc_ref_c_size = 0; unsigned int guard_width, guard_height; + int ret; if (ctx->type == MFCINST_DECODER) { mfc_debug(2, "Luma size:%d Chroma size:%d MV size:%d\n", @@ -113,100 +101,93 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) /* Codecs have different memory requirements */ switch (ctx->codec_mode) { case S5P_MFC_CODEC_H264_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_NB_IP_SIZE + S5P_FIMV_DEC_VERT_NB_MV_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = ctx->total_dpb_count * ctx->mv_size; + ctx->bank2.size = ctx->total_dpb_count * ctx->mv_size; break; case S5P_MFC_CODEC_MPEG4_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE + S5P_FIMV_DEC_UPNB_MV_SIZE + S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE + S5P_FIMV_DEC_STX_PARSER_SIZE + S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_VC1RCV_DEC: case S5P_MFC_CODEC_VC1_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE + S5P_FIMV_DEC_UPNB_MV_SIZE + S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE + S5P_FIMV_DEC_NB_DCAC_SIZE + 3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_MPEG2_DEC: - ctx->bank1_size = 0; - ctx->bank2_size = 0; + ctx->bank1.size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H263_DEC: - ctx->bank1_size = + ctx->bank1.size = ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE + S5P_FIMV_DEC_UPNB_MV_SIZE + S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE + S5P_FIMV_DEC_NB_DCAC_SIZE, S5P_FIMV_DEC_BUF_ALIGN); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H264_ENC: - ctx->bank1_size = (enc_ref_y_size * 2) + + ctx->bank1.size = (enc_ref_y_size * 2) + S5P_FIMV_ENC_UPMV_SIZE + S5P_FIMV_ENC_COLFLG_SIZE + S5P_FIMV_ENC_INTRAMD_SIZE + S5P_FIMV_ENC_NBORINFO_SIZE; - ctx->bank2_size = (enc_ref_y_size * 2) + + ctx->bank2.size = (enc_ref_y_size * 2) + (enc_ref_c_size * 4) + S5P_FIMV_ENC_INTRAPRED_SIZE; break; case S5P_MFC_CODEC_MPEG4_ENC: - ctx->bank1_size = (enc_ref_y_size * 2) + + ctx->bank1.size = (enc_ref_y_size * 2) + S5P_FIMV_ENC_UPMV_SIZE + S5P_FIMV_ENC_COLFLG_SIZE + S5P_FIMV_ENC_ACDCCOEF_SIZE; - ctx->bank2_size = (enc_ref_y_size * 2) + + ctx->bank2.size = (enc_ref_y_size * 2) + (enc_ref_c_size * 4); break; case S5P_MFC_CODEC_H263_ENC: - ctx->bank1_size = (enc_ref_y_size * 2) + + ctx->bank1.size = (enc_ref_y_size * 2) + S5P_FIMV_ENC_UPMV_SIZE + S5P_FIMV_ENC_ACDCCOEF_SIZE; - ctx->bank2_size = (enc_ref_y_size * 2) + + ctx->bank2.size = (enc_ref_y_size * 2) + (enc_ref_c_size * 4); break; default: break; } /* Allocate only if memory from bank 1 is necessary */ - if (ctx->bank1_size > 0) { - ctx->bank1_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size); - if (IS_ERR(ctx->bank1_buf)) { - ctx->bank1_buf = NULL; - printk(KERN_ERR - "Buf alloc for decoding failed (port A)\n"); - return -ENOMEM; + if (ctx->bank1.size > 0) { + + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1); + if (ret) { + mfc_err("Failed to allocate Bank1 temporary buffer\n"); + return ret; } - ctx->bank1_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf); - BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); + BUG_ON(ctx->bank1.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); } /* Allocate only if memory from bank 2 is necessary */ - if (ctx->bank2_size > 0) { - ctx->bank2_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size); - if (IS_ERR(ctx->bank2_buf)) { - ctx->bank2_buf = NULL; - mfc_err("Buf alloc for decoding failed (port B)\n"); - return -ENOMEM; + if (ctx->bank2.size > 0) { + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_r, &ctx->bank2); + if (ret) { + mfc_err("Failed to allocate Bank2 temporary buffer\n"); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); + return ret; } - ctx->bank2_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_buf); - BUG_ON(ctx->bank2_phys & ((1 << MFC_BANK2_ALIGN_ORDER) - 1)); + BUG_ON(ctx->bank2.dma & ((1 << MFC_BANK2_ALIGN_ORDER) - 1)); } return 0; } @@ -214,18 +195,8 @@ int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx) /* Release buffers allocated for codec */ void s5p_mfc_release_codec_buffers_v5(struct s5p_mfc_ctx *ctx) { - if (ctx->bank1_buf) { - vb2_dma_contig_memops.put(ctx->bank1_buf); - ctx->bank1_buf = NULL; - ctx->bank1_phys = 0; - ctx->bank1_size = 0; - } - if (ctx->bank2_buf) { - vb2_dma_contig_memops.put(ctx->bank2_buf); - ctx->bank2_buf = NULL; - ctx->bank2_phys = 0; - ctx->bank2_size = 0; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_r, &ctx->bank2); } /* Allocate memory for instance data buffer */ @@ -233,58 +204,38 @@ int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v5 *buf_size = dev->variant->buf_size->priv; + int ret; if (ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || ctx->codec_mode == S5P_MFC_CODEC_H264_ENC) ctx->ctx.size = buf_size->h264_ctx; else ctx->ctx.size = buf_size->non_h264_ctx; - ctx->ctx.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.size); - if (IS_ERR(ctx->ctx.alloc)) { - mfc_err("Allocating context buffer failed\n"); - ctx->ctx.alloc = NULL; - return -ENOMEM; + + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx); + if (ret) { + mfc_err("Failed to allocate instance buffer\n"); + return ret; } - ctx->ctx.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.alloc); - BUG_ON(ctx->ctx.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); ctx->ctx.ofs = OFFSETA(ctx->ctx.dma); - ctx->ctx.virt = vb2_dma_contig_memops.vaddr(ctx->ctx.alloc); - if (!ctx->ctx.virt) { - mfc_err("Remapping instance buffer failed\n"); - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.ofs = 0; - ctx->ctx.dma = 0; - return -ENOMEM; - } + /* Zero content of the allocated memory */ memset(ctx->ctx.virt, 0, ctx->ctx.size); wmb(); /* Initialize shared memory */ - ctx->shm.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], buf_size->shm); - if (IS_ERR(ctx->shm.alloc)) { - mfc_err("failed to allocate shared memory\n"); - return PTR_ERR(ctx->shm.alloc); + ctx->shm.size = buf_size->shm; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->shm); + if (ret) { + mfc_err("Failed to allocate shared memory buffer\n"); + return ret; } + /* shared memory offset only keeps the offset from base (port a) */ - ctx->shm.ofs = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->shm.alloc) - - dev->bank1; + ctx->shm.ofs = ctx->shm.dma - dev->bank1; BUG_ON(ctx->shm.ofs & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); - ctx->shm.virt = vb2_dma_contig_memops.vaddr(ctx->shm.alloc); - if (!ctx->shm.virt) { - vb2_dma_contig_memops.put(ctx->shm.alloc); - ctx->shm.alloc = NULL; - ctx->shm.ofs = 0; - mfc_err("failed to virt addr of shared memory\n"); - return -ENOMEM; - } - memset((void *)ctx->shm.virt, 0, buf_size->shm); + memset(ctx->shm.virt, 0, buf_size->shm); wmb(); return 0; } @@ -292,19 +243,8 @@ int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx) /* Release instance buffer */ void s5p_mfc_release_instance_buffer_v5(struct s5p_mfc_ctx *ctx) { - if (ctx->ctx.alloc) { - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.ofs = 0; - ctx->ctx.virt = NULL; - ctx->ctx.dma = 0; - } - if (ctx->shm.alloc) { - vb2_dma_contig_memops.put(ctx->shm.alloc); - ctx->shm.alloc = NULL; - ctx->shm.ofs = 0; - ctx->shm.virt = NULL; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->shm); } int s5p_mfc_alloc_dev_context_buffer_v5(struct s5p_mfc_dev *dev) @@ -443,10 +383,10 @@ int s5p_mfc_set_dec_frame_buffer_v5(struct s5p_mfc_ctx *ctx) size_t buf_addr1, buf_addr2; int buf_size1, buf_size2; - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; - buf_addr2 = ctx->bank2_phys; - buf_size2 = ctx->bank2_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; + buf_addr2 = ctx->bank2.dma; + buf_size2 = ctx->bank2.size; dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) & ~S5P_FIMV_DPB_COUNT_MASK; mfc_write(dev, ctx->total_dpb_count | dpb, @@ -606,10 +546,10 @@ int s5p_mfc_set_enc_ref_buffer_v5(struct s5p_mfc_ctx *ctx) unsigned int guard_width, guard_height; int i; - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; - buf_addr2 = ctx->bank2_phys; - buf_size2 = ctx->bank2_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; + buf_addr2 = ctx->bank2.dma; + buf_size2 = ctx->bank2.size; enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN) * ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN); enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 91d508729d43..beb6dbacebd9 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -73,6 +73,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; unsigned int mb_width, mb_height; + int ret; mb_width = MB_WIDTH(ctx->img_width); mb_height = MB_HEIGHT(ctx->img_height); @@ -112,7 +113,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = + ctx->bank1.size = ctx->scratch_buf_size + (ctx->mv_count * ctx->mv_size); break; @@ -123,7 +124,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_VC1RCV_DEC: case S5P_MFC_CODEC_VC1_DEC: @@ -133,11 +134,11 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_MPEG2_DEC: - ctx->bank1_size = 0; - ctx->bank2_size = 0; + ctx->bank1.size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_H263_DEC: ctx->scratch_buf_size = @@ -146,7 +147,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_VP8_DEC: ctx->scratch_buf_size = @@ -155,7 +156,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = ctx->scratch_buf_size; + ctx->bank1.size = ctx->scratch_buf_size; break; case S5P_MFC_CODEC_H264_ENC: ctx->scratch_buf_size = @@ -164,11 +165,11 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = + ctx->bank1.size = ctx->scratch_buf_size + ctx->tmv_buffer_size + (ctx->dpb_count * (ctx->luma_dpb_size + ctx->chroma_dpb_size + ctx->me_buffer_size)); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; case S5P_MFC_CODEC_MPEG4_ENC: case S5P_MFC_CODEC_H263_ENC: @@ -178,28 +179,24 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) mb_height); ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size, S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6); - ctx->bank1_size = + ctx->bank1.size = ctx->scratch_buf_size + ctx->tmv_buffer_size + (ctx->dpb_count * (ctx->luma_dpb_size + ctx->chroma_dpb_size + ctx->me_buffer_size)); - ctx->bank2_size = 0; + ctx->bank2.size = 0; break; default: break; } /* Allocate only if memory from bank 1 is necessary */ - if (ctx->bank1_size > 0) { - ctx->bank1_buf = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size); - if (IS_ERR(ctx->bank1_buf)) { - ctx->bank1_buf = 0; - pr_err("Buf alloc for decoding failed (port A)\n"); - return -ENOMEM; + if (ctx->bank1.size > 0) { + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1); + if (ret) { + mfc_err("Failed to allocate Bank1 memory\n"); + return ret; } - ctx->bank1_phys = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf); - BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); + BUG_ON(ctx->bank1.dma & ((1 << MFC_BANK1_ALIGN_ORDER) - 1)); } return 0; @@ -208,12 +205,7 @@ int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx) /* Release buffers allocated for codec */ void s5p_mfc_release_codec_buffers_v6(struct s5p_mfc_ctx *ctx) { - if (ctx->bank1_buf) { - vb2_dma_contig_memops.put(ctx->bank1_buf); - ctx->bank1_buf = 0; - ctx->bank1_phys = 0; - ctx->bank1_size = 0; - } + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1); } /* Allocate memory for instance data buffer */ @@ -221,6 +213,7 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; + int ret; mfc_debug_enter(); @@ -250,25 +243,10 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) break; } - ctx->ctx.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.size); - if (IS_ERR(ctx->ctx.alloc)) { - mfc_err("Allocating context buffer failed.\n"); - return PTR_ERR(ctx->ctx.alloc); - } - - ctx->ctx.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx.alloc); - - ctx->ctx.virt = vb2_dma_contig_memops.vaddr(ctx->ctx.alloc); - if (!ctx->ctx.virt) { - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.dma = 0; - ctx->ctx.virt = NULL; - - mfc_err("Remapping context buffer failed.\n"); - return -ENOMEM; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx); + if (ret) { + mfc_err("Failed to allocate instance buffer\n"); + return ret; } memset(ctx->ctx.virt, 0, ctx->ctx.size); @@ -282,44 +260,22 @@ int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx) /* Release instance buffer */ void s5p_mfc_release_instance_buffer_v6(struct s5p_mfc_ctx *ctx) { - mfc_debug_enter(); - - if (ctx->ctx.alloc) { - vb2_dma_contig_memops.put(ctx->ctx.alloc); - ctx->ctx.alloc = NULL; - ctx->ctx.dma = 0; - ctx->ctx.virt = NULL; - } - - mfc_debug_leave(); + s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->ctx); } /* Allocate context buffers for SYS_INIT */ int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) { struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv; + int ret; mfc_debug_enter(); - dev->ctx_buf.alloc = vb2_dma_contig_memops.alloc( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], buf_size->dev_ctx); - if (IS_ERR(dev->ctx_buf.alloc)) { - mfc_err("Allocating DESC buffer failed.\n"); - return PTR_ERR(dev->ctx_buf.alloc); - } - - dev->ctx_buf.dma = s5p_mfc_mem_cookie( - dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], - dev->ctx_buf.alloc); - - dev->ctx_buf.virt = vb2_dma_contig_memops.vaddr(dev->ctx_buf.alloc); - if (!dev->ctx_buf.virt) { - vb2_dma_contig_memops.put(dev->ctx_buf.alloc); - dev->ctx_buf.alloc = NULL; - dev->ctx_buf.dma = 0; - - mfc_err("Remapping DESC buffer failed.\n"); - return -ENOMEM; + dev->ctx_buf.size = buf_size->dev_ctx; + ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &dev->ctx_buf); + if (ret) { + mfc_err("Failed to allocate device context buffer\n"); + return ret; } memset(dev->ctx_buf.virt, 0, buf_size->dev_ctx); @@ -333,12 +289,7 @@ int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev) /* Release context buffers for SYS_INIT */ void s5p_mfc_release_dev_context_buffer_v6(struct s5p_mfc_dev *dev) { - if (dev->ctx_buf.alloc) { - vb2_dma_contig_memops.put(dev->ctx_buf.alloc); - dev->ctx_buf.alloc = NULL; - dev->ctx_buf.dma = 0; - dev->ctx_buf.virt = NULL; - } + s5p_mfc_release_priv_buf(dev->mem_dev_l, &dev->ctx_buf); } static int calc_plane(int width, int height) @@ -417,8 +368,8 @@ int s5p_mfc_set_dec_frame_buffer_v6(struct s5p_mfc_ctx *ctx) int buf_size1; int align_gap; - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1); mfc_debug(2, "Total DPB COUNT: %d\n", ctx->total_dpb_count); @@ -540,8 +491,8 @@ int s5p_mfc_set_enc_ref_buffer_v6(struct s5p_mfc_ctx *ctx) mfc_debug_enter(); - buf_addr1 = ctx->bank1_phys; - buf_size1 = ctx->bank1_size; + buf_addr1 = ctx->bank1.dma; + buf_size1 = ctx->bank1.size; mfc_debug(2, "Buf1: %p (%d)\n", (void *)buf_addr1, buf_size1); From 1b73ba0be4aad87a0d195a6d433bb59ffe81e99a Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Thu, 22 Nov 2012 10:00:28 -0300 Subject: [PATCH 0606/4607] [media] s5p-mfc: Context handling in open() bugfix Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 4fcd075e8699..b1d7f9a9b996 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -855,16 +855,16 @@ err_queue_init: s5p_mfc_deinit_hw(dev); err_init_hw: err_load_fw: - dev->ctx[ctx->num] = NULL; - del_timer_sync(&dev->watchdog_timer); err_pwr_enable: if (dev->num_inst == 1) { if (s5p_mfc_power_off() < 0) mfc_err("power off failed\n"); + del_timer_sync(&dev->watchdog_timer); } err_ctrls_setup: s5p_mfc_dec_ctrls_delete(ctx); err_bad_node: + dev->ctx[ctx->num] = NULL; err_no_ctx: v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); From 24b9f50170f55a3179c6f6d51022eb7d50502d05 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 3 Jan 2013 12:30:30 -0300 Subject: [PATCH 0607/4607] [media] V4L: Remove deprecated image centering controls It has been over 3 years since the V4L2_CID_[HV]CENTER were deprecated. Clean up the DocBook and remove the V4L2_CID_VCENTER_DEPRECATED, V4L2_CID_VCENTER_DEPRECATED control related paragraphs. Remove the V4L2_CID_[HV]CENTER controls definitions from v4l2-controls.h, these controls are not used by any driver in the mainline now. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 23 -------------------- drivers/media/v4l2-core/v4l2-ctrls.c | 2 -- include/uapi/linux/v4l2-controls.h | 4 ---- 3 files changed, 29 deletions(-) diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 7fe5be1d3bbb..9e8f85498678 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -203,29 +203,6 @@ and should not be used in new drivers and applications. boolean Mirror the picture vertically. - - V4L2_CID_HCENTER_DEPRECATED (formerly V4L2_CID_HCENTER) - integer - Horizontal image centering. This control is -deprecated. New drivers and applications should use the Camera class controls -V4L2_CID_PAN_ABSOLUTE, -V4L2_CID_PAN_RELATIVE and -V4L2_CID_PAN_RESET instead. - - - V4L2_CID_VCENTER_DEPRECATED - (formerly V4L2_CID_VCENTER) - integer - Vertical image centering. Centering is intended to -physically adjust cameras. For image cropping see -, for clipping . This -control is deprecated. New drivers and applications should use the -Camera class controls -V4L2_CID_TILT_ABSOLUTE, -V4L2_CID_TILT_RELATIVE and -V4L2_CID_TILT_RESET instead. - V4L2_CID_POWER_LINE_FREQUENCY enum diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index fa02363e7db4..7b486ac3f4d9 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -577,8 +577,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_GAIN: return "Gain"; case V4L2_CID_HFLIP: return "Horizontal Flip"; case V4L2_CID_VFLIP: return "Vertical Flip"; - case V4L2_CID_HCENTER: return "Horizontal Center"; - case V4L2_CID_VCENTER: return "Vertical Center"; case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency"; case V4L2_CID_HUE_AUTO: return "Hue, Automatic"; case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature"; diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index f56c945cecd4..4dc0822700fe 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -88,10 +88,6 @@ #define V4L2_CID_HFLIP (V4L2_CID_BASE+20) #define V4L2_CID_VFLIP (V4L2_CID_BASE+21) -/* Deprecated; use V4L2_CID_PAN_RESET and V4L2_CID_TILT_RESET */ -#define V4L2_CID_HCENTER (V4L2_CID_BASE+22) -#define V4L2_CID_VCENTER (V4L2_CID_BASE+23) - #define V4L2_CID_POWER_LINE_FREQUENCY (V4L2_CID_BASE+24) enum v4l2_power_line_frequency { V4L2_CID_POWER_LINE_FREQUENCY_DISABLED = 0, From 0c87c66aa383b045c437e7cf456eef28a8aa7b66 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Thu, 29 Nov 2012 00:05:35 -0300 Subject: [PATCH 0608/4607] [media] dvb_usb_v2: make remote controller optional Make it possible to compile dvb_usb_v2 driver without the remote controller (RC-core). Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/Kconfig | 3 ++- drivers/media/usb/dvb-usb-v2/dvb_usb.h | 9 +++++++++ drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 12 ++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig index 3240d559ef7b..7b5773fe6367 100644 --- a/drivers/media/usb/dvb-usb-v2/Kconfig +++ b/drivers/media/usb/dvb-usb-v2/Kconfig @@ -1,6 +1,6 @@ config DVB_USB_V2 tristate "Support for various USB DVB devices v2" - depends on DVB_CORE && USB && I2C && RC_CORE + depends on DVB_CORE && USB && I2C help By enabling this you will be able to choose the various supported USB1.1 and USB2.0 DVB devices. @@ -113,6 +113,7 @@ config DVB_USB_IT913X config DVB_USB_LME2510 tristate "LME DM04/QQBOX DVB-S USB2.0 support" depends on DVB_USB_V2 + depends on RC_CORE select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 059291b892b8..e2678a78db4c 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -400,4 +400,13 @@ extern int dvb_usbv2_reset_resume(struct usb_interface *); extern int dvb_usbv2_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16); extern int dvb_usbv2_generic_write(struct dvb_usb_device *, u8 *, u16); +/* stub implementations that will be never called when RC-core is disabled */ +#if !defined(CONFIG_RC_CORE) && !defined(CONFIG_RC_CORE_MODULE) +#define rc_repeat(args...) +#define rc_keydown(args...) +#define rc_keydown_notimeout(args...) +#define rc_keyup(args...) +#define rc_g_keycode_from_table(args...) 0 +#endif + #endif diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 671b4fa232b4..94f134c4e942 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -102,6 +102,7 @@ static int dvb_usbv2_i2c_exit(struct dvb_usb_device *d) return 0; } +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) static void dvb_usb_read_remote_control(struct work_struct *work) { struct dvb_usb_device *d = container_of(work, @@ -202,6 +203,17 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) return 0; } +#else +static int dvb_usbv2_remote_init(struct dvb_usb_device *d) +{ + return 0; +} + +static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) +{ + return 0; +} +#endif static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buf, size_t len) From cedda37015e90e58458fd91635ff8ef5d144d3f5 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:06:00 -0300 Subject: [PATCH 0609/4607] [media] rtl28xxu: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index eddda6958c19..9a6890392e90 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1125,7 +1125,7 @@ err: return ret; } - +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) static int rtl2831u_rc_query(struct dvb_usb_device *d) { int ret, i; @@ -1208,7 +1208,11 @@ static int rtl2831u_get_rc_config(struct dvb_usb_device *d, return 0; } +#else + #define rtl2831u_get_rc_config NULL +#endif +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) static int rtl2832u_rc_query(struct dvb_usb_device *d) { int ret, i; @@ -1280,6 +1284,9 @@ static int rtl2832u_get_rc_config(struct dvb_usb_device *d, return 0; } +#else + #define rtl2832u_get_rc_config NULL +#endif static const struct dvb_usb_device_properties rtl2831u_props = { .driver_name = KBUILD_MODNAME, From d5c6209068aeca3d464f4161006e637a6087008c Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:12:07 -0300 Subject: [PATCH 0610/4607] [media] anysee: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/anysee.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index d05c5b563dac..5f450374cd03 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -1019,6 +1019,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) return ret; } +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) static int anysee_rc_query(struct dvb_usb_device *d) { u8 buf[] = {CMD_GET_IR_CODE}; @@ -1054,6 +1055,9 @@ static int anysee_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) return 0; } +#else + #define anysee_get_rc_config NULL +#endif static int anysee_ci_read_attribute_mem(struct dvb_ca_en50221 *ci, int slot, int addr) From b6215596b5912c205ed48c11deb1cbdf0aa3ac25 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:15:47 -0300 Subject: [PATCH 0611/4607] [media] af9015: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9015.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index 943d93423705..51505d198f44 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -1156,6 +1156,7 @@ error: return ret; } +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) struct af9015_rc_setup { unsigned int id; char *rc_codes; @@ -1312,6 +1313,9 @@ static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) return 0; } +#else + #define af9015_get_rc_config NULL +#endif /* interface 0 is used by DVB-T receiver and interface 1 is for remote controller (HID) */ From eed5670a31c511e196fd50fc591689ffdab61f80 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:18:31 -0300 Subject: [PATCH 0612/4607] [media] af9035: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9035.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index ea37b5c3c33f..19b139494448 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1146,6 +1146,7 @@ err: return ret; } +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) static int af9035_rc_query(struct dvb_usb_device *d) { unsigned int key; @@ -1220,6 +1221,9 @@ err: return ret; } +#else + #define af9035_get_rc_config NULL +#endif /* interface 0 is used by DVB-T receiver and interface 1 is for remote controller (HID) */ From 7b75322af1bd3b365ce3a5f2ac6df329bf725656 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:23:22 -0300 Subject: [PATCH 0613/4607] [media] az6007: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Acked-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/az6007.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index d75dbf27e99e..3b33f1ec2295 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -189,6 +189,7 @@ static int az6007_streaming_ctrl(struct dvb_frontend *fe, int onoff) return az6007_write(d, 0xbc, onoff, 0, NULL, 0); } +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) /* remote control stuff (does not work with my box) */ static int az6007_rc_query(struct dvb_usb_device *d) { @@ -215,6 +216,20 @@ static int az6007_rc_query(struct dvb_usb_device *d) return 0; } +static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + pr_debug("Getting az6007 Remote Control properties\n"); + + rc->allowed_protos = RC_BIT_NEC; + rc->query = az6007_rc_query; + rc->interval = 400; + + return 0; +} +#else + #define az6007_get_rc_config NULL +#endif + static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) @@ -822,17 +837,6 @@ static void az6007_usb_disconnect(struct usb_interface *intf) dvb_usbv2_disconnect(intf); } -static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) -{ - pr_debug("Getting az6007 Remote Control properties\n"); - - rc->allowed_protos = RC_BIT_NEC; - rc->query = az6007_rc_query; - rc->interval = 400; - - return 0; -} - static int az6007_download_firmware(struct dvb_usb_device *d, const struct firmware *fw) { From b963c2083ae3ec67052dfc6e2ec8216c479843d8 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:27:59 -0300 Subject: [PATCH 0614/4607] [media] it913x: make remote controller optional Do not compile remote controller when RC-core is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/it913x.c | 36 +++++++++++++++------------ 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index 744c9f9f7680..b8593bda725c 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -308,6 +308,7 @@ static struct i2c_algorithm it913x_i2c_algo = { }; /* Callbacks for DVB USB */ +#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) #define IT913X_POLL 250 static int it913x_rc_query(struct dvb_usb_device *d) { @@ -334,6 +335,25 @@ static int it913x_rc_query(struct dvb_usb_device *d) return ret; } +static int it913x_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) +{ + struct it913x_state *st = d->priv; + + if (st->proprietary_ir == false) { + rc->map_name = NULL; + return 0; + } + + rc->allowed_protos = RC_BIT_NEC; + rc->query = it913x_rc_query; + rc->interval = 250; + + return 0; +} +#else + #define it913x_get_rc_config NULL +#endif + /* Firmware sets raw */ static const char fw_it9135_v1[] = FW_IT9135_V1; static const char fw_it9135_v2[] = FW_IT9135_V2; @@ -696,22 +716,6 @@ static int it913x_frontend_attach(struct dvb_usb_adapter *adap) } /* DVB USB Driver */ -static int it913x_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc) -{ - struct it913x_state *st = d->priv; - - if (st->proprietary_ir == false) { - rc->map_name = NULL; - return 0; - } - - rc->allowed_protos = RC_BIT_NEC; - rc->query = it913x_rc_query; - rc->interval = 250; - - return 0; -} - static int it913x_get_adapter_count(struct dvb_usb_device *d) { struct it913x_state *st = d->priv; From 21ca20303671f77c34380105fdce849793ba92aa Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:32:09 -0300 Subject: [PATCH 0615/4607] [media] it913x: remove unused define and increase module version Signed-off-by: Antti Palosaari Acked-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/it913x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index b8593bda725c..511c2aa00b97 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -309,7 +309,6 @@ static struct i2c_algorithm it913x_i2c_algo = { /* Callbacks for DVB USB */ #if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) -#define IT913X_POLL 250 static int it913x_rc_query(struct dvb_usb_device *d) { u8 ibuf[4]; @@ -814,7 +813,7 @@ module_usb_driver(it913x_driver); MODULE_AUTHOR("Malcolm Priestley "); MODULE_DESCRIPTION("it913x USB 2 Driver"); -MODULE_VERSION("1.32"); +MODULE_VERSION("1.33"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(FW_IT9135_V1); MODULE_FIRMWARE(FW_IT9135_V2); From 21354c6de395e2e3cf896cf72ac243d5773f7773 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:41:49 -0300 Subject: [PATCH 0616/4607] [media] dvb_usb_v2: remove rc-core stub implementations Those are not needed anymore as all dvb-usb-v2 drivers has proper dependency checks for RC-core. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index e2678a78db4c..059291b892b8 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -400,13 +400,4 @@ extern int dvb_usbv2_reset_resume(struct usb_interface *); extern int dvb_usbv2_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16); extern int dvb_usbv2_generic_write(struct dvb_usb_device *, u8 *, u16); -/* stub implementations that will be never called when RC-core is disabled */ -#if !defined(CONFIG_RC_CORE) && !defined(CONFIG_RC_CORE_MODULE) -#define rc_repeat(args...) -#define rc_keydown(args...) -#define rc_keydown_notimeout(args...) -#define rc_keyup(args...) -#define rc_g_keycode_from_table(args...) 0 -#endif - #endif From ef3824029d0f5887c065ea461157bdf8b2287bf8 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 20:46:46 -0300 Subject: [PATCH 0617/4607] [media] dvb_usb_v2: use dummy function defines instead stub functions I think it is better (cheaper) to use dummy defines for functions that has no meaning when remote controller is disabled by Kconfig. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 94f134c4e942..1330c644dd8d 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -204,15 +204,8 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) return 0; } #else -static int dvb_usbv2_remote_init(struct dvb_usb_device *d) -{ - return 0; -} - -static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) -{ - return 0; -} + #define dvb_usbv2_remote_init(args...) 0 + #define dvb_usbv2_remote_exit(args...) #endif static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buf, From ac1c86c857368eb727b7ca2c7a48bd6e373fa628 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 9 Dec 2012 21:23:30 -0300 Subject: [PATCH 0618/4607] [media] dvb_usb_v2: change rc polling active/deactive logic Use own flag to mark when rc polling is active/deactive and make decisions, like start/stop polling on suspend/resume, against that. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/dvb_usb.h | 3 ++- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 059291b892b8..3cac8bd0b116 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -347,6 +347,7 @@ struct dvb_usb_adapter { * @props: device properties * @name: device name * @rc_map: name of rc codes table + * @rc_polling_active: set when RC polling is active * @udev: pointer to the device's struct usb_device * @intf: pointer to the device's usb interface * @rc: remote controller configuration @@ -364,7 +365,7 @@ struct dvb_usb_device { const struct dvb_usb_device_properties *props; const char *name; const char *rc_map; - + bool rc_polling_active; struct usb_device *udev; struct usb_interface *intf; struct dvb_usb_rc rc; diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 1330c644dd8d..95968d39c1ca 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -113,13 +113,16 @@ static void dvb_usb_read_remote_control(struct work_struct *work) * When the parameter has been set to 1 via sysfs while the * driver was running, or when bulk mode is enabled after IR init. */ - if (dvb_usbv2_disable_rc_polling || d->rc.bulk_mode) + if (dvb_usbv2_disable_rc_polling || d->rc.bulk_mode) { + d->rc_polling_active = false; return; + } ret = d->rc.query(d); if (ret < 0) { dev_err(&d->udev->dev, "%s: rc.query() failed=%d\n", KBUILD_MODNAME, ret); + d->rc_polling_active = false; return; /* stop polling */ } @@ -183,6 +186,7 @@ static int dvb_usbv2_remote_init(struct dvb_usb_device *d) d->rc.interval); schedule_delayed_work(&d->rc_query_work, msecs_to_jiffies(d->rc.interval)); + d->rc_polling_active = true; } return 0; @@ -964,7 +968,7 @@ int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg) dev_dbg(&d->udev->dev, "%s:\n", __func__); /* stop remote controller poll */ - if (d->rc.query && !d->rc.bulk_mode) + if (d->rc_polling_active) cancel_delayed_work_sync(&d->rc_query_work); for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) { @@ -1011,7 +1015,7 @@ static int dvb_usbv2_resume_common(struct dvb_usb_device *d) } /* start remote controller poll */ - if (d->rc.query && !d->rc.bulk_mode) + if (d->rc_polling_active) schedule_delayed_work(&d->rc_query_work, msecs_to_jiffies(d->rc.interval)); From 37b44a0f04184998073633887d2f1e724aee130a Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 4 Jan 2013 15:21:26 -0300 Subject: [PATCH 0619/4607] [media] dvb_usb_v2: use IS_ENABLED() macro replace: #if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) with: #if IS_ENABLED(CONFIG_RC_CORE) Reported-by: Fabio Estevam Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/af9015.c | 2 +- drivers/media/usb/dvb-usb-v2/af9035.c | 2 +- drivers/media/usb/dvb-usb-v2/anysee.c | 2 +- drivers/media/usb/dvb-usb-v2/az6007.c | 2 +- drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 2 +- drivers/media/usb/dvb-usb-v2/it913x.c | 2 +- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c index 51505d198f44..b86d0f27a398 100644 --- a/drivers/media/usb/dvb-usb-v2/af9015.c +++ b/drivers/media/usb/dvb-usb-v2/af9015.c @@ -1156,7 +1156,7 @@ error: return ret; } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) struct af9015_rc_setup { unsigned int id; char *rc_codes; diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 19b139494448..f11cc42454f0 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -1146,7 +1146,7 @@ err: return ret; } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static int af9035_rc_query(struct dvb_usb_device *d) { unsigned int key; diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c index 5f450374cd03..a20d691d0b63 100644 --- a/drivers/media/usb/dvb-usb-v2/anysee.c +++ b/drivers/media/usb/dvb-usb-v2/anysee.c @@ -1019,7 +1019,7 @@ static int anysee_tuner_attach(struct dvb_usb_adapter *adap) return ret; } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static int anysee_rc_query(struct dvb_usb_device *d) { u8 buf[] = {CMD_GET_IR_CODE}; diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c index 3b33f1ec2295..70ec80d8be71 100644 --- a/drivers/media/usb/dvb-usb-v2/az6007.c +++ b/drivers/media/usb/dvb-usb-v2/az6007.c @@ -189,7 +189,7 @@ static int az6007_streaming_ctrl(struct dvb_frontend *fe, int onoff) return az6007_write(d, 0xbc, onoff, 0, NULL, 0); } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) /* remote control stuff (does not work with my box) */ static int az6007_rc_query(struct dvb_usb_device *d) { diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 95968d39c1ca..086792055912 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -102,7 +102,7 @@ static int dvb_usbv2_i2c_exit(struct dvb_usb_device *d) return 0; } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static void dvb_usb_read_remote_control(struct work_struct *work) { struct dvb_usb_device *d = container_of(work, diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c index 511c2aa00b97..833847995c65 100644 --- a/drivers/media/usb/dvb-usb-v2/it913x.c +++ b/drivers/media/usb/dvb-usb-v2/it913x.c @@ -308,7 +308,7 @@ static struct i2c_algorithm it913x_i2c_algo = { }; /* Callbacks for DVB USB */ -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static int it913x_rc_query(struct dvb_usb_device *d) { u8 ibuf[4]; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 9a6890392e90..2d024716d630 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1125,7 +1125,7 @@ err: return ret; } -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static int rtl2831u_rc_query(struct dvb_usb_device *d) { int ret, i; @@ -1212,7 +1212,7 @@ static int rtl2831u_get_rc_config(struct dvb_usb_device *d, #define rtl2831u_get_rc_config NULL #endif -#if defined(CONFIG_RC_CORE) || defined(CONFIG_RC_CORE_MODULE) +#if IS_ENABLED(CONFIG_RC_CORE) static int rtl2832u_rc_query(struct dvb_usb_device *d) { int ret, i; From 3971e79a8707cc529f474c1d25e5502ddfa33ebc Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Sun, 16 Dec 2012 11:47:07 -0300 Subject: [PATCH 0620/4607] [media] rtl28xxu: [1b80:d3a8] ASUS My Cinema-U3100Mini Plus V2 RF-tuner is Fitipower FC0013 Reported-by: Renato Gallo Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 2d024716d630..ca54674e0f43 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1364,6 +1364,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "Dexatek DK mini DVB-T Dongle", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d7, &rtl2832u_props, "TerraTec Cinergy T Stick+", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd3a8, + &rtl2832u_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); From 78a5e70919e9416344409e523e53221613776d80 Mon Sep 17 00:00:00 2001 From: Alexander Inyukhin Date: Fri, 4 Jan 2013 18:19:02 -0300 Subject: [PATCH 0621/4607] [media] rtl28xxu: add Gigabyte U7300 DVB-T Dongle Device with ID 1b80:d393 is the Gigabyte U7300 DVB-T dongle. It contains decoder Realtek RTL2832U and tuner Fitipower FC0012. [crope@iki.fi: fix trivial merge conflict] Signed-off-by: Alexander Inyukhin Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index ca54674e0f43..39e2d7c3ed0d 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1366,6 +1366,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "TerraTec Cinergy T Stick+", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd3a8, &rtl2832u_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) }, + { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd393, + &rtl2832u_props, "Gigabyte U7300 DVB-T Dongle", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); From 9db15638e1bd4a1cfdb0a42907308ad441030a80 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 4 Jan 2013 18:35:00 -0300 Subject: [PATCH 0622/4607] [media] rtl28xxu: correct some device names Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 39e2d7c3ed0d..4ab0dd8dd9a8 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1345,13 +1345,13 @@ static const struct usb_device_id rtl28xxu_id_table[] = { { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838, &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1, - &rtl2832u_props, "Terratec Cinergy T Stick Black", NULL) }, + &rtl2832u_props, "TerraTec Cinergy T Stick Black", NULL) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT, &rtl2832u_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK, - &rtl2832u_props, "NOXON DAB/DAB+ USB dongle", NULL) }, + &rtl2832u_props, "TerraTec NOXON DAB Stick", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK_REV2, - &rtl2832u_props, "NOXON DAB/DAB+ USB dongle (rev 2)", NULL) }, + &rtl2832u_props, "TerraTec NOXON DAB Stick (rev 2)", NULL) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TREKSTOR_TERRES_2_0, &rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) }, { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1101, @@ -1367,7 +1367,7 @@ static const struct usb_device_id rtl28xxu_id_table[] = { { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd3a8, &rtl2832u_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd393, - &rtl2832u_props, "Gigabyte U7300 DVB-T Dongle", NULL) }, + &rtl2832u_props, "GIGABYTE U7300", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); From 1c612e9a1cf712bd7845e3adb4961e3ef8eaf8aa Mon Sep 17 00:00:00 2001 From: Prabhakar Lad Date: Wed, 2 Jan 2013 02:08:15 -0300 Subject: [PATCH 0623/4607] [media] s5p-fimc: Fix typo of URL pointing to Media Controller API's Signed-off-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/fimc.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/video4linux/fimc.txt b/Documentation/video4linux/fimc.txt index fd02d9a4930a..25f4d3402722 100644 --- a/Documentation/video4linux/fimc.txt +++ b/Documentation/video4linux/fimc.txt @@ -58,7 +58,7 @@ Not currently supported: 4.1. Media device interface The driver supports Media Controller API as defined at -http://http://linuxtv.org/downloads/v4l-dvb-apis/media_common.html +http://linuxtv.org/downloads/v4l-dvb-apis/media_common.html The media device driver name is "SAMSUNG S5P FIMC". The purpose of this interface is to allow changing assignment of FIMC instances From a96fbe0429ace98bf3d92340ab9caa03c80db88c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 3 Jan 2013 12:53:53 -0300 Subject: [PATCH 0624/4607] [media] dib0700: do not lock interruptible on tear-down paths When mutex_lock_interruptible is used on paths where a signal can be pending, the device is not closed properly and cannot be reused. This usually happens when you start tzap for example and send it a TERM signal. The signal is pending while tear-down routines are called. Hence streaming is not properly stopped in that case. And the device stops working from that moment on. Signed-off-by: Jiri Slaby Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dib0700_core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index 19b5ed2825d7..bf2a908d74cf 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -561,10 +561,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) } } - if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) { - err("could not acquire lock"); - return -EINTR; - } + mutex_lock(&adap->dev->usb_mutex); st->buf[0] = REQUEST_ENABLE_VIDEO; /* this bit gives a kind of command, From 9898df6482f71fa0d27b029789ef2f37988c06b3 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Thu, 3 Jan 2013 16:37:25 -0300 Subject: [PATCH 0625/4607] [media] ts2020.c: ts2020_set_params [BUG] point to fe->tuner_priv Fixes corruption of fe->demodulator_priv Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ts2020.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index 94e3fe21eefb..f50e237e1464 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -182,7 +182,7 @@ static int ts2020_set_tuner_rf(struct dvb_frontend *fe) static int ts2020_set_params(struct dvb_frontend *fe) { struct dtv_frontend_properties *c = &fe->dtv_property_cache; - struct ts2020_priv *priv = fe->demodulator_priv; + struct ts2020_priv *priv = fe->tuner_priv; int ret; u32 frequency = c->frequency; s32 offset_khz; From c4fe29a32ffa16c1166edacad1edc2dcf0aaa08c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 4 Jan 2013 14:56:02 -0300 Subject: [PATCH 0626/4607] [media] dvb: unlock on error in dvb_ca_en50221_io_do_ioctl() We recently pushed the locking down into this function, but there was an error path where the unlock was missed. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_ca_en50221.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index 190e5e0f48c7..0aac3096728e 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -1227,8 +1227,10 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, case CA_GET_SLOT_INFO: { struct ca_slot_info *info = parg; - if ((info->num > ca->slot_count) || (info->num < 0)) - return -EINVAL; + if ((info->num > ca->slot_count) || (info->num < 0)) { + err = -EINVAL; + goto out_unlock; + } info->type = CA_CI_LINK; info->flags = 0; @@ -1247,6 +1249,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, break; } +out_unlock: mutex_unlock(&ca->ioctl_mutex); return err; } From 5e8d02bb346d6240b029f1990ddc295d7d59685b Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sat, 5 Jan 2013 08:44:09 -0300 Subject: [PATCH 0627/4607] [media] em28xx: fix audio input for TV mode of device Terratec Cinergy 250 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remy Blank reported that audio over USB can be made working for the television input if .amux is changed from EM28XX_AMUX_LINE_IN to EM28XX_AMUX_VIDEO. An examination of his devices shows, that it is indeed supplied with an EM202 AC97 audio IC. We also use this setting for the Cinergy 200. Remy Blank also provided the original version of this patch (many thanks !). Fixes bug 14126 (see bug report for further device details). Signed-off-by: Frank Schäfer Signed-off-by: Remy Blank Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 4d849bfa2db3..0a5aa6223adf 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -493,7 +493,7 @@ struct em28xx_board em28xx_boards[] = { .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, From 668a8b3b57e26a14a5172c84da0d861fb9f697d9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 5 Jan 2013 18:56:10 -0300 Subject: [PATCH 0628/4607] [media] v4l: Don't compile v4l2-int-device unless really needed Add a configuration option for v4l2-int-device so it is only compiled when necessary, which is only by omap24xxcam and tcm825x drivers. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 2 +- drivers/media/platform/Kconfig | 2 +- drivers/media/v4l2-core/Kconfig | 11 +++++++++++ drivers/media/v4l2-core/Makefile | 3 ++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 24d78e28e493..1e4b2d0f23ce 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -477,7 +477,7 @@ config VIDEO_MT9V032 config VIDEO_TCM825X tristate "TCM825x camera sensor support" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_INT_DEVICE depends on MEDIA_CAMERA_SUPPORT ---help--- This is a driver for the Toshiba TCM825x VGA camera sensor. diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index c071b079de23..2433e2bbeee4 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -92,7 +92,7 @@ config VIDEO_M32R_AR_M64278 config VIDEO_OMAP2 tristate "OMAP2 Camera Capture Interface driver" - depends on VIDEO_DEV && ARCH_OMAP2 + depends on VIDEO_DEV && ARCH_OMAP2 && VIDEO_V4L2_INT_DEVICE select VIDEOBUF_DMA_SG ---help--- This is a v4l2 driver for the TI OMAP2 camera capture interface diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig index 65875c3aba1b..976d029e9925 100644 --- a/drivers/media/v4l2-core/Kconfig +++ b/drivers/media/v4l2-core/Kconfig @@ -82,3 +82,14 @@ config VIDEOBUF2_DMA_SG #depends on HAS_DMA select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS + +config VIDEO_V4L2_INT_DEVICE + tristate "V4L2 int device (DEPRECATED)" + depends on VIDEO_V4L2 + ---help--- + An early framework for a hardware-independent interface for + image sensors and bridges etc. Currently used by omap24xxcam and + tcm825x drivers that should be converted to V4L2 subdev. + + Do not use for new developments. + diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index c2d61d4f03d1..a9d355230e8e 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -10,7 +10,8 @@ ifeq ($(CONFIG_COMPAT),y) videodev-objs += v4l2-compat-ioctl32.o endif -obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o +obj-$(CONFIG_VIDEO_DEV) += videodev.o +obj-$(CONFIG_VIDEO_V4L2_INT_DEVICE) += v4l2-int-device.o obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o obj-$(CONFIG_VIDEO_TUNER) += tuner.o From a0a030bdbe612b7d8a941fba672300f7fc21b275 Mon Sep 17 00:00:00 2001 From: Malcolm Priestley Date: Sun, 6 Jan 2013 08:40:42 -0300 Subject: [PATCH 0629/4607] [media] ts2020: call get_rf_strength from frontend Restore ds3000.c read_signal_strength. Call tuner get_rf_strength from frontend read_signal_strength. We are able to do a NULL check and doesn't limit the tuner attach to the frontend attach area. At the moment the lmedm04 tuner attach is stuck in frontend attach area. Signed-off-by: Malcolm Priestley Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/ds3000.c | 10 ++++++++++ drivers/media/dvb-frontends/m88rs2000.c | 4 +++- drivers/media/dvb-frontends/ts2020.c | 1 - 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index d128f85844e7..1e344b033277 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -533,6 +533,15 @@ static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) return 0; } +static int ds3000_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + if (fe->ops.tuner_ops.get_rf_strength) + fe->ops.tuner_ops.get_rf_strength(fe, signal_strength); + + return 0; +} + /* calculate DS3000 snr value in dB */ static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr) { @@ -1102,6 +1111,7 @@ static struct dvb_frontend_ops ds3000_ops = { .i2c_gate_ctrl = ds3000_i2c_gate_ctrl, .read_status = ds3000_read_status, .read_ber = ds3000_read_ber, + .read_signal_strength = ds3000_read_signal_strength, .read_snr = ds3000_read_snr, .read_ucblocks = ds3000_read_ucblocks, .set_voltage = ds3000_set_voltage, diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 283c90fee374..4da5272075cb 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -446,7 +446,9 @@ static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) static int m88rs2000_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { - *strength = 0; + if (fe->ops.tuner_ops.get_rf_strength) + fe->ops.tuner_ops.get_rf_strength(fe, strength); + return 0; } diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index f50e237e1464..ad7ad857ab2a 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -363,7 +363,6 @@ struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops, sizeof(struct dvb_tuner_ops)); - fe->ops.read_signal_strength = fe->ops.tuner_ops.get_rf_strength; return fe; } From 55ee64b30a38d688232e5eb2860467dddc493573 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 16 Dec 2012 16:04:46 -0300 Subject: [PATCH 0630/4607] [media] omap_vout: find_vma() needs ->mmap_sem held Walking rbtree while it's modified is a Bad Idea(tm); besides, the result of find_vma() can be freed just as it's getting returned to caller. Fortunately, it's easy to fix - just take ->mmap_sem a bit earlier (and don't bother with find_vma() at all if virtp >= PAGE_OFFSET - in that case we don't even look at its result). While we are at it, what prevents VIDIOC_PREPARE_BUF calling v4l_prepare_buf() -> (e.g) vb2_ioctl_prepare_buf() -> vb2_prepare_buf() -> __buf_prepare() -> __qbuf_userptr() -> vb2_vmalloc_get_userptr() -> find_vma(), AFAICS without having taken ->mmap_sem anywhere in process? The code flow is bloody convoluted and depends on a bunch of things done by initialization, so I certainly might've missed something... Cc: stable@vger.kernel.org [2.6.35] Signed-off-by: Al Viro Cc: Sakari Ailus Cc: Laurent Pinchart Cc: Archit Taneja Cc: Prabhakar Lad Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap/omap_vout.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index dade3ceab092..96c4a17e4280 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -205,19 +205,21 @@ static u32 omap_vout_uservirt_to_phys(u32 virtp) struct vm_area_struct *vma; struct mm_struct *mm = current->mm; - vma = find_vma(mm, virtp); /* For kernel direct-mapped memory, take the easy way */ - if (virtp >= PAGE_OFFSET) { - physp = virt_to_phys((void *) virtp); - } else if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { + if (virtp >= PAGE_OFFSET) + return virt_to_phys((void *) virtp); + + down_read(¤t->mm->mmap_sem); + vma = find_vma(mm, virtp); + if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) { /* this will catch, kernel-allocated, mmaped-to-usermode addresses */ physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); + up_read(¤t->mm->mmap_sem); } else { /* otherwise, use get_user_pages() for general userland pages */ int res, nr_pages = 1; struct page *pages; - down_read(¤t->mm->mmap_sem); res = get_user_pages(current, current->mm, virtp, nr_pages, 1, 0, &pages, NULL); From 73ec66c000e9816806c7380ca3420f4e0638c40e Mon Sep 17 00:00:00 2001 From: Evgeny Plehov Date: Thu, 29 Nov 2012 15:27:38 -0300 Subject: [PATCH 0631/4607] [media] stv0900: Multistream support Multistream support for stv0900. For Netup Dual S2 CI with STV0900BAC/AAC. Signed-off-by: Evgeny Plehov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/stv0900_core.c | 26 ++++++++++++++++++++++ drivers/media/dvb-frontends/stv0900_reg.h | 3 +++ 2 files changed, 29 insertions(+) diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index b551ca350e00..0fb34e1909d1 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -1558,6 +1558,27 @@ static int stv0900_status(struct stv0900_internal *intp, return locked; } +static int stv0900_set_mis(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, int mis) +{ + enum fe_stv0900_error error = STV0900_NO_ERROR; + + dprintk("%s\n", __func__); + + if (mis < 0 || mis > 255) { + dprintk("Disable MIS filtering\n"); + stv0900_write_bits(intp, FILTER_EN, 0); + } else { + dprintk("Enable MIS filtering - %d\n", mis); + stv0900_write_bits(intp, FILTER_EN, 1); + stv0900_write_reg(intp, ISIENTRY, mis); + stv0900_write_reg(intp, ISIBITENA, 0xff); + } + + return error; +} + + static enum dvbfe_search stv0900_search(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; @@ -1578,6 +1599,8 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe) if (state->config->set_ts_params) state->config->set_ts_params(fe, 0); + stv0900_set_mis(intp, demod, c->stream_id); + p_result.locked = FALSE; p_search.path = demod; p_search.frequency = c->frequency; @@ -1935,6 +1958,9 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, if (err_stv0900) goto error; + if (state->internal->chip_id >= 0x30) + state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM; + break; default: goto error; diff --git a/drivers/media/dvb-frontends/stv0900_reg.h b/drivers/media/dvb-frontends/stv0900_reg.h index 731afe93a823..511ed2a2d987 100644 --- a/drivers/media/dvb-frontends/stv0900_reg.h +++ b/drivers/media/dvb-frontends/stv0900_reg.h @@ -3446,8 +3446,11 @@ extern s32 shiftx(s32 x, int demod, s32 shift); #define R0900_P1_PDELCTRL1 0xf550 #define PDELCTRL1 REGx(R0900_P1_PDELCTRL1) #define F0900_P1_INV_MISMASK 0xf5500080 +#define INV_MISMASK FLDx(F0900_P1_INV_MISMASK) #define F0900_P1_FILTER_EN 0xf5500020 +#define FILTER_EN FLDx(F0900_P1_FILTER_EN) #define F0900_P1_EN_MIS00 0xf5500002 +#define EN_MIS00 FLDx(F0900_P1_EN_MIS00) #define F0900_P1_ALGOSWRST 0xf5500001 #define ALGOSWRST FLDx(F0900_P1_ALGOSWRST) From 9a6cecc846169159bfce511f4c0034bb96eea1ca Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 14 Sep 2012 17:41:57 -0500 Subject: [PATCH 0632/4607] dmaengine: add helper function to request a slave DMA channel Currently slave DMA channels are requested by calling dma_request_channel() and requires DMA clients to pass various filter parameters to obtain the appropriate channel. With device-tree being used by architectures such as arm and the addition of device-tree helper functions to extract the relevant DMA client information from device-tree, add a new function to request a slave DMA channel using device-tree. This function is currently a simple wrapper that calls the device-tree of_dma_request_slave_channel() function. Cc: Nicolas Ferre Cc: Benoit Cousson Cc: Stephen Warren Cc: Grant Likely Cc: Russell King Cc: Rob Herring Cc: Arnd Bergmann Cc: Vinod Koul Cc: Dan Williams Acked-by: Arnd Bergmann Signed-off-by: Jon Hunter Reviewed-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 16 ++++++++++++++++ include/linux/dmaengine.h | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index a815d44c70a4..d37cf95987b6 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -62,6 +62,7 @@ #include #include #include +#include static DEFINE_MUTEX(dma_list_mutex); static DEFINE_IDR(dma_idr); @@ -546,6 +547,21 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v } EXPORT_SYMBOL_GPL(__dma_request_channel); +/** + * dma_request_slave_channel - try to allocate an exclusive slave channel + * @dev: pointer to client device structure + * @name: slave channel name + */ +struct dma_chan *dma_request_slave_channel(struct device *dev, char *name) +{ + /* If device-tree is present get slave info from here */ + if (dev->of_node) + return of_dma_request_slave_channel(dev->of_node, name); + + return NULL; +} +EXPORT_SYMBOL_GPL(dma_request_slave_channel); + void dma_release_channel(struct dma_chan *chan) { mutex_lock(&dma_list_mutex); diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index d3201e438d16..8cd0e2556d04 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -974,6 +974,7 @@ enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie); enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx); void dma_issue_pending_all(void); struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, void *fn_param); +struct dma_chan *dma_request_slave_channel(struct device *dev, char *name); void dma_release_channel(struct dma_chan *chan); #else static inline enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx) @@ -988,6 +989,11 @@ static inline struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, { return NULL; } +static inline struct dma_chan *dma_request_slave_channel(struct device *dev, + char *name) +{ + return NULL +} static inline void dma_release_channel(struct dma_chan *chan) { } From aa3da644c76d1c2083d085e453c18f7c51f19bc4 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 14 Sep 2012 17:41:56 -0500 Subject: [PATCH 0633/4607] of: Add generic device tree DMA helpers This is based upon the work by Benoit Cousson [1] and Nicolas Ferre [2] to add some basic helpers to retrieve a DMA controller device_node and the DMA request/channel information. Aim of DMA helpers - The purpose of device-tree is to describe the capabilites of the hardware. Thinking about DMA controllers purely from the context of the hardware to begin with, we can describe a device in terms of a DMA controller as follows ... 1. Number of DMA controllers 2. Number of channels (maybe physical or logical) 3. Mapping of DMA requests signals to DMA controller 4. Number of DMA interrupts 5. Mapping of DMA interrupts to channels - With the above in mind the aim of the DT DMA helper functions is to extract the above information from the DT and provide to the appropriate driver. However, due to the vast number of DMA controllers and not all are using a common driver (such as DMA Engine) it has been seen that this is not a trivial task. In previous discussions on this topic the following concerns have been raised ... 1. How does the binding support devices with multiple DMA controllers? 2. How to support both legacy DMA controllers not using DMA Engine as well as those that support DMA Engine. 3. When using with DMA Engine how do we support the various implementations where the opaque filter function parameter differs between implementations? 4. How do we handle DMA channels that are identified with a string versus a integer? - Hence the design of the DMA helpers has to accomodate the above or align on an agreement what can be or should be supported. Design of DMA helpers 1. Registering DMA controllers In the case of DMA controllers that are using DMA Engine, requesting a channel is performed by calling the following function. struct dma_chan *dma_request_channel(dma_cap_mask_t mask, dma_filter_fn filter_fn, void *filter_param); The mask variable is used to match a type of the device controller in a list of controllers. The filter_fn and filter_param are used to identify the required dma channel and return a handle to the dma channel of type dma_chan. From the examples I have seen, the mask and filter_fn are constant for a given DMA controller and therefore, we can specify these as controller specific data when registering the DMA controller with the device-tree DMA helpers. The filter_param variable is of an unknown type and is typically specific to the DMA engine implementation for a given DMA controller. To allow some flexibility in the type and formating of this filter_param we employ an xlate to translate the device-tree binding information into the appropriate format. The xlate function used for a DMA controller can also be specified when registering the DMA controller with the device-tree DMA helpers. Based upon the above, a function for registering the DMA controller with the DMA helpers now looks like the below. The data variable is used to pass a pointer to DMA controller specific data used by the xlate function. int of_dma_controller_register(struct device_node *np, struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *), void *data) For example, in the case where DMA engine is used, we define the following structure (that stores the DMA engine capability mask and filter function) and pass this to the data variable in the above function. struct of_dma_filter_info { dma_cap_mask_t dma_cap; dma_filter_fn filter_fn; }; 2. Representing and requesting channel information Please see the dma binding documentation included in this patch for a description of how DMA controllers and client information should be represented with device-tree. For more information on how this binding came about please see [3]. In addition to this, feedback received from the Linux kernel summit showed a consensus (among those who attended) to use a name to identify DMA client information [4]. A DMA channel can be requested by calling the following function, where name is a required parameter used for identifying a DMA channel. This function has been designed to return a structure of type dma_chan to work with the DMA engine driver. Note that if DMA engine is used then drivers should be using the DMA engine API dma_request_slave_channel() (implemented in part 2 of this series, "dmaengine: add helper function to request a slave DMA channel") which will in turn call the below function if device-tree is present. The aim being to have a common DMA engine interface regardless of whether device tree is being used. struct dma_chan *of_dma_request_slave_channel(struct device_node *np, char *name) 3. Supporting legacy devices not using DMA Engine These devices present a problem, as there may not be a uniform way to easily support them with regard to device tree. Ideally, these should be migrated to DMA engine. However, if this is not possible, then they should still be able to use this binding, the only constaint imposed by this implementation is that when requesting a DMA channel via of_dma_request_slave_channel(), it will return a type of dma_chan. This implementation has been tested on OMAP4430 using the kernel v3.6-rc5. I have validated that MMC is working on the PANDA board with this implementation. My development branch for testing on OMAP can be found here [5]. v6: - minor corrections in DMA binding documentation v5: - minor update to binding documentation - added loop to exhaustively search for a slave channel in the case where there could be alternative channels available v4: - revert the removal of xlate function from v3 - update the proposed binding format and APIs based upon discussions [3] v3: - avoid passing an xlate function and instead pass DMA engine parameters - define number of dma channels and requests in dma-controller node v2: - remove of_dma_to_resource API - make property #dma-cells required (no fallback anymore) - another check in of_dma_xlate_onenumbercell() function [1] http://article.gmane.org/gmane.linux.drivers.devicetree/12022 [2] http://article.gmane.org/gmane.linux.ports.arm.omap/73622 [3] http://marc.info/?l=linux-omap&m=133582085008539&w=2 [4] http://pad.linaro.org/arm-mini-summit-2012 [5] https://github.com/jonhunter/linux/tree/dev-dt-dma Cc: Nicolas Ferre Cc: Benoit Cousson Cc: Stephen Warren Cc: Grant Likely Cc: Russell King Cc: Rob Herring Cc: Arnd Bergmann Cc: Vinod Koul Cc: Dan Williams Reviewed-by: Arnd Bergmann Reviewed-by: Nicolas Ferre Signed-off-by: Jon Hunter Reviewed-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/dma.txt | 81 +++++++ drivers/of/Makefile | 2 +- drivers/of/dma.c | 219 ++++++++++++++++++ include/linux/of_dma.h | 45 ++++ 4 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/dma/dma.txt create mode 100644 drivers/of/dma.c create mode 100644 include/linux/of_dma.h diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt new file mode 100644 index 000000000000..a4f59a5967d4 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/dma.txt @@ -0,0 +1,81 @@ +* Generic DMA Controller and DMA request bindings + +Generic binding to provide a way for a driver using DMA Engine to retrieve the +DMA request or channel information that goes from a hardware device to a DMA +controller. + + +* DMA controller + +Required property: +- #dma-cells: Must be at least 1. Used to provide DMA controller + specific information. See DMA client binding below for + more details. + +Optional properties: +- #dma-channels: Number of DMA channels supported by the controller. +- #dma-requests: Number of DMA requests signals supported by the + controller. + +Example: + + dma: dma@48000000 { + compatible = "ti,omap-sdma" + reg = <0x48000000 0x1000>; + interrupts = <0 12 0x4 + 0 13 0x4 + 0 14 0x4 + 0 15 0x4>; + #dma-cells = <1>; + #dma-channels = <32>; + #dma-requests = <127>; + }; + + +* DMA client + +Client drivers should specify the DMA property using a phandle to the controller +followed by DMA controller specific data. + +Required property: +- dmas: List of one or more DMA specifiers, each consisting of + - A phandle pointing to DMA controller node + - A number of integer cells, as determined by the + #dma-cells property in the node referenced by phandle + containing DMA controller specific information. This + typically contains a DMA request line number or a + channel number, but can contain any data that is used + required for configuring a channel. +- dma-names: Contains one identifier string for each DMA specifier in + the dmas property. The specific strings that can be used + are defined in the binding of the DMA client device. + Multiple DMA specifiers can be used to represent + alternatives and in this case the dma-names for those + DMA specifiers must be identical (see examples). + +Examples: + +1. A device with one DMA read channel, one DMA write channel: + + i2c1: i2c@1 { + ... + dmas = <&dma 2 /* read channel */ + &dma 3>; /* write channel */ + dma-names = "rx", "tx" + ... + }; + +2. A single read-write channel with three alternative DMA controllers: + + dmas = <&dma1 5 + &dma2 7 + &dma3 2>; + dma-names = "rx-tx", "rx-tx", "rx-tx" + +3. A device with three channels, one of which has two alternatives: + + dmas = <&dma1 2 /* read channel */ + &dma1 3 /* write channel */ + &dma2 0 /* error read */ + &dma3 0>; /* alternative error read */ + dma-names = "rx", "tx", "error", "error"; diff --git a/drivers/of/Makefile b/drivers/of/Makefile index e027f444d10c..eafa107aed40 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,4 +1,4 @@ -obj-y = base.o +obj-y = base.o dma.o obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o diff --git a/drivers/of/dma.c b/drivers/of/dma.c new file mode 100644 index 000000000000..19ad37c066f5 --- /dev/null +++ b/drivers/of/dma.c @@ -0,0 +1,219 @@ +/* + * Device tree helpers for DMA request / controller + * + * Based on of_gpio.c + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(of_dma_list); + +/** + * of_dma_find_controller - Find a DMA controller in DT DMA helpers list + * @np: device node of DMA controller + */ +static struct of_dma *of_dma_find_controller(struct device_node *np) +{ + struct of_dma *ofdma; + + if (list_empty(&of_dma_list)) { + pr_err("empty DMA controller list\n"); + return NULL; + } + + list_for_each_entry_rcu(ofdma, &of_dma_list, of_dma_controllers) + if (ofdma->of_node == np) + return ofdma; + + return NULL; +} + +/** + * of_dma_controller_register - Register a DMA controller to DT DMA helpers + * @np: device node of DMA controller + * @of_dma_xlate: translation function which converts a phandle + * arguments list into a dma_chan structure + * @data pointer to controller specific data to be used by + * translation function + * + * Returns 0 on success or appropriate errno value on error. + * + * Allocated memory should be freed with appropriate of_dma_controller_free() + * call. + */ +int of_dma_controller_register(struct device_node *np, + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *), + void *data) +{ + struct of_dma *ofdma; + int nbcells; + + if (!np || !of_dma_xlate) { + pr_err("%s: not enough information provided\n", __func__); + return -EINVAL; + } + + ofdma = kzalloc(sizeof(*ofdma), GFP_KERNEL); + if (!ofdma) + return -ENOMEM; + + nbcells = be32_to_cpup(of_get_property(np, "#dma-cells", NULL)); + if (!nbcells) { + pr_err("%s: #dma-cells property is missing or invalid\n", + __func__); + return -EINVAL; + } + + ofdma->of_node = np; + ofdma->of_dma_nbcells = nbcells; + ofdma->of_dma_xlate = of_dma_xlate; + ofdma->of_dma_data = data; + + /* Now queue of_dma controller structure in list */ + list_add_tail_rcu(&ofdma->of_dma_controllers, &of_dma_list); + + return 0; +} +EXPORT_SYMBOL_GPL(of_dma_controller_register); + +/** + * of_dma_controller_free - Remove a DMA controller from DT DMA helpers list + * @np: device node of DMA controller + * + * Memory allocated by of_dma_controller_register() is freed here. + */ +void of_dma_controller_free(struct device_node *np) +{ + struct of_dma *ofdma; + + ofdma = of_dma_find_controller(np); + if (ofdma) { + list_del_rcu(&ofdma->of_dma_controllers); + kfree(ofdma); + } +} +EXPORT_SYMBOL_GPL(of_dma_controller_free); + +/** + * of_dma_find_channel - Find a DMA channel by name + * @np: device node to look for DMA channels + * @name: name of desired channel + * @dma_spec: pointer to DMA specifier as found in the device tree + * + * Find a DMA channel by the name. Returns 0 on success or appropriate + * errno value on error. + */ +static int of_dma_find_channel(struct device_node *np, char *name, + struct of_phandle_args *dma_spec) +{ + int count, i; + const char *s; + + count = of_property_count_strings(np, "dma-names"); + if (count < 0) + return count; + + for (i = 0; i < count; i++) { + if (of_property_read_string_index(np, "dma-names", i, &s)) + continue; + + if (strcmp(name, s)) + continue; + + if (!of_parse_phandle_with_args(np, "dmas", "#dma-cells", i, + dma_spec)) + return 0; + } + + return -ENODEV; +} + +/** + * of_dma_request_slave_channel - Get the DMA slave channel + * @np: device node to get DMA request from + * @name: name of desired channel + * + * Returns pointer to appropriate dma channel on success or NULL on error. + */ +struct dma_chan *of_dma_request_slave_channel(struct device_node *np, + char *name) +{ + struct of_phandle_args dma_spec; + struct of_dma *ofdma; + struct dma_chan *chan; + int r; + + if (!np || !name) { + pr_err("%s: not enough information provided\n", __func__); + return NULL; + } + + do { + r = of_dma_find_channel(np, name, &dma_spec); + if (r) { + pr_err("%s: can't find DMA channel\n", np->full_name); + return NULL; + } + + ofdma = of_dma_find_controller(dma_spec.np); + if (!ofdma) { + pr_debug("%s: can't find DMA controller %s\n", + np->full_name, dma_spec.np->full_name); + continue; + } + + if (dma_spec.args_count != ofdma->of_dma_nbcells) { + pr_debug("%s: wrong #dma-cells for %s\n", np->full_name, + dma_spec.np->full_name); + continue; + } + + chan = ofdma->of_dma_xlate(&dma_spec, ofdma); + + of_node_put(dma_spec.np); + + } while (!chan); + + return chan; +} + +/** + * of_dma_simple_xlate - Simple DMA engine translation function + * @dma_spec: pointer to DMA specifier as found in the device tree + * @of_dma: pointer to DMA controller data + * + * A simple translation function for devices that use a 32-bit value for the + * filter_param when calling the DMA engine dma_request_channel() function. + * Note that this translation function requires that #dma-cells is equal to 1 + * and the argument of the dma specifier is the 32-bit filter_param. Returns + * pointer to appropriate dma channel on success or NULL on error. + */ +struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct of_dma_filter_info *info = ofdma->of_dma_data; + + if (!info || !info->filter_fn) + return NULL; + + if (count != 1) + return NULL; + + return dma_request_channel(info->dma_cap, info->filter_fn, + &dma_spec->args[0]); +} +EXPORT_SYMBOL_GPL(of_dma_simple_xlate); diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h new file mode 100644 index 000000000000..337823dc6b90 --- /dev/null +++ b/include/linux/of_dma.h @@ -0,0 +1,45 @@ +/* + * OF helpers for DMA request / controller + * + * Based on of_gpio.h + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 __LINUX_OF_DMA_H +#define __LINUX_OF_DMA_H + +#include +#include + +struct device_node; + +struct of_dma { + struct list_head of_dma_controllers; + struct device_node *of_node; + int of_dma_nbcells; + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *); + void *of_dma_data; +}; + +struct of_dma_filter_info { + dma_cap_mask_t dma_cap; + dma_filter_fn filter_fn; +}; + +extern int of_dma_controller_register(struct device_node *np, + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *), + void *data); +extern void of_dma_controller_free(struct device_node *np); +extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np, + char *name); +extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma); + +#endif /* __LINUX_OF_DMA_H */ From 4c26bc601d20fa67eefcb27477feda130c10790e Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 25 Sep 2012 09:57:36 +0530 Subject: [PATCH 0634/4607] of: dma- fix build break for !CONFIG_OF Signed-off-by: Vinod Koul --- include/linux/of_dma.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index 337823dc6b90..67158ddd1f3e 100644 --- a/include/linux/of_dma.h +++ b/include/linux/of_dma.h @@ -32,6 +32,7 @@ struct of_dma_filter_info { dma_filter_fn filter_fn; }; +#ifdef CONFIG_OF extern int of_dma_controller_register(struct device_node *np, struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *), @@ -41,5 +42,31 @@ extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np, char *name); extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma); +#else +static int of_dma_controller_register(struct device_node *np, + struct dma_chan *(*of_dma_xlate) + (struct of_phandle_args *, struct of_dma *), + void *data) +{ + return -ENODEV; +} + +static void of_dma_controller_free(struct device_node *np) +{ +} + +static struct dma_chan *of_dma_request_slave_channel(struct device_node *np, + char *name) +{ + return NULL; +} + +static struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + return NULL; +} + +#endif #endif /* __LINUX_OF_DMA_H */ From deef12443785add808af39ff7f836b54bc5c4253 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Wed, 19 Sep 2012 10:49:48 -0400 Subject: [PATCH 0635/4607] of: dma: fix typos in generic dma binding definition Some semicolons were left out in the examples. The #dma-channels and #dma-requests properties have a prefix that is, by convention, reserved for cell size properties. Rename those properties to dma-channels and dma-requests. Signed-off-by: Matt Porter Acked-by: Arnd Bergmann Acked-by: Jon Hunter Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/dma.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/dma.txt b/Documentation/devicetree/bindings/dma/dma.txt index a4f59a5967d4..8f504e6bae14 100644 --- a/Documentation/devicetree/bindings/dma/dma.txt +++ b/Documentation/devicetree/bindings/dma/dma.txt @@ -13,22 +13,22 @@ Required property: more details. Optional properties: -- #dma-channels: Number of DMA channels supported by the controller. -- #dma-requests: Number of DMA requests signals supported by the +- dma-channels: Number of DMA channels supported by the controller. +- dma-requests: Number of DMA requests signals supported by the controller. Example: dma: dma@48000000 { - compatible = "ti,omap-sdma" + compatible = "ti,omap-sdma"; reg = <0x48000000 0x1000>; interrupts = <0 12 0x4 0 13 0x4 0 14 0x4 0 15 0x4>; #dma-cells = <1>; - #dma-channels = <32>; - #dma-requests = <127>; + dma-channels = <32>; + dma-requests = <127>; }; @@ -61,7 +61,7 @@ Examples: ... dmas = <&dma 2 /* read channel */ &dma 3>; /* write channel */ - dma-names = "rx", "tx" + dma-names = "rx", "tx"; ... }; @@ -70,7 +70,7 @@ Examples: dmas = <&dma1 5 &dma2 7 &dma3 2>; - dma-names = "rx-tx", "rx-tx", "rx-tx" + dma-names = "rx-tx", "rx-tx", "rx-tx"; 3. A device with three channels, one of which has two alternatives: From d18d5f59797009d86a0ee46feb2a56d53707455a Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 25 Sep 2012 16:18:55 +0530 Subject: [PATCH 0636/4607] dmaengine: fix build failure due to missing semi-colon Reported-by: Fengguang Wu Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 8cd0e2556d04..c88f302d91c9 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -992,7 +992,7 @@ static inline struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, static inline struct dma_chan *dma_request_slave_channel(struct device *dev, char *name) { - return NULL + return NULL; } static inline void dma_release_channel(struct dma_chan *chan) { From c6a0aec9216064f756c4eda80be0720707c898d5 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 23 Oct 2012 13:01:54 -0700 Subject: [PATCH 0637/4607] drivers/dma: remove CONFIG_EXPERIMENTAL This config item has not carried much meaning for a while now and is almost always enabled by default. As agreed during the Linux kernel summit, remove it. CC: Vinod Koul CC: Dan Williams Signed-off-by: Kees Cook Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index d4c12180c654..93ea87e59fdf 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -51,7 +51,7 @@ config ASYNC_TX_ENABLE_CHANNEL_SWITCH config AMBA_PL08X bool "ARM PrimeCell PL080 or PL081 support" - depends on ARM_AMBA && EXPERIMENTAL + depends on ARM_AMBA select DMA_ENGINE select DMA_VIRTUAL_CHANNELS help From 53b9989bc75e185d0bf946e939ebe8534c8a9740 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 10 Oct 2012 21:04:58 +0800 Subject: [PATCH 0638/4607] pch_dma: use module_pci_driver to simplify the code Use the module_pci_driver() macro to make the code simpler by eliminating module_init and module_exit calls. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: Vinod Koul --- drivers/dma/pch_dma.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index eca1c4ddf039..aed6e0c117ed 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -1029,18 +1029,7 @@ static struct pci_driver pch_dma_driver = { #endif }; -static int __init pch_dma_init(void) -{ - return pci_register_driver(&pch_dma_driver); -} - -static void __exit pch_dma_exit(void) -{ - pci_unregister_driver(&pch_dma_driver); -} - -module_init(pch_dma_init); -module_exit(pch_dma_exit); +module_pci_driver(pch_dma_driver); MODULE_DESCRIPTION("Intel EG20T PCH / LAPIS Semicon ML7213/ML7223/ML7831 IOH " "DMA controller driver"); From 177d2bf5c7d3ab41bfb4ce2597dde668225958dd Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 16 Oct 2012 09:49:16 +0530 Subject: [PATCH 0639/4607] dmaengine: dw_dmac: Update documentation style comments for dw_dma_platform_data Documentation style comments were missing for few fields in struct dw_dma_platform_data. Add these. Signed-off-by: Viresh Kumar Reviewed-by: Andy Shevchenko Signed-off-by: Vinod Koul --- include/linux/dw_dmac.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index e1c8c9e919ac..62a6190dee22 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -19,6 +19,8 @@ * @nr_channels: Number of channels supported by hardware (max 8) * @is_private: The device channels should be marked as private and not for * by the general purpose DMA channel allocator. + * @chan_allocation_order: Allocate channels starting from 0 or 7 + * @chan_priority: Set channel priority increasing from 0 to 7 or 7 to 0. * @block_size: Maximum block size supported by the controller * @nr_masters: Number of AHB masters supported by the controller * @data_width: Maximum data width supported by hardware per AHB master From 3ecd9d01f7dffd5a2291267ba0410e1826c87530 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Thu, 20 Dec 2012 14:11:23 -0500 Subject: [PATCH 0640/4607] PCI: cpqphp: Cleanup and remove unreachable paths Remove redundant checks and unreachable paths. Signed-off-by: Sasha Levin Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/cpqphp_ctrl.c | 57 +++++++++++-------------------- 1 file changed, 20 insertions(+), 37 deletions(-) diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index 36112fe212d3..d282019cda5f 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -1900,8 +1900,7 @@ static void interrupt_event_handler(struct controller *ctrl) dbg("power fault\n"); } else { /* refresh notification */ - if (p_slot) - update_slot_info(ctrl, p_slot); + update_slot_info(ctrl, p_slot); } ctrl->event_queue[loop].event_type = 0; @@ -2520,44 +2519,28 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func /* If we have IO resources copy them and fill in the bridge's * IO range registers */ - if (io_node) { - memcpy(hold_IO_node, io_node, sizeof(struct pci_resource)); - io_node->next = NULL; + memcpy(hold_IO_node, io_node, sizeof(struct pci_resource)); + io_node->next = NULL; - /* set IO base and Limit registers */ - temp_byte = io_node->base >> 8; - rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte); + /* set IO base and Limit registers */ + temp_byte = io_node->base >> 8; + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte); - temp_byte = (io_node->base + io_node->length - 1) >> 8; - rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); - } else { - kfree(hold_IO_node); - hold_IO_node = NULL; - } + temp_byte = (io_node->base + io_node->length - 1) >> 8; + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); - /* If we have memory resources copy them and fill in the - * bridge's memory range registers. Otherwise, fill in the - * range registers with values that disable them. */ - if (mem_node) { - memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); - mem_node->next = NULL; + /* Copy the memory resources and fill in the bridge's memory + * range registers. + */ + memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); + mem_node->next = NULL; - /* set Mem base and Limit registers */ - temp_word = mem_node->base >> 16; - rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + /* set Mem base and Limit registers */ + temp_word = mem_node->base >> 16; + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); - temp_word = (mem_node->base + mem_node->length - 1) >> 16; - rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); - } else { - temp_word = 0xFFFF; - rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); - - temp_word = 0x0000; - rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); - - kfree(hold_mem_node); - hold_mem_node = NULL; - } + temp_word = (mem_node->base + mem_node->length - 1) >> 16; + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource)); p_mem_node->next = NULL; @@ -2627,7 +2610,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func /* Return unused bus resources * First use the temporary node to store information for * the board */ - if (hold_bus_node && bus_node && temp_resources.bus_head) { + if (bus_node && temp_resources.bus_head) { hold_bus_node->length = bus_node->base - hold_bus_node->base; hold_bus_node->next = func->bus_head; @@ -2751,7 +2734,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func } /* If we have prefetchable memory space available and there * is some left at the end, return the unused portion */ - if (hold_p_mem_node && temp_resources.p_mem_head) { + if (temp_resources.p_mem_head) { p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head), &hold_p_mem_node, 0x100000); From f7ac356dc3da1f69dc52cb6273e08e53b85b4884 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:24 -0700 Subject: [PATCH 0641/4607] x86/PCI: Factor out pcibios_allocate_bridge_resources() Thus pcibios_allocate_bus_resources() could more simple and clean. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 46 +++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index dd8ca6f7223b..980036286909 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -193,34 +193,36 @@ EXPORT_SYMBOL(pcibios_align_resource); * as well. */ -static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +static void __init pcibios_allocate_bridge_resources(struct pci_dev *dev) { - struct pci_bus *bus; - struct pci_dev *dev; int idx; struct resource *r; + for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { + r = &dev->resource[idx]; + if (!r->flags) + continue; + if (!r->start || pci_claim_resource(dev, idx) < 0) { + /* + * Something is wrong with the region. + * Invalidate the resource to prevent + * child resource allocations in this + * range. + */ + r->start = r->end = 0; + r->flags = 0; + } + } +} + +static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct pci_bus *bus; + /* Depth-First Search on bus tree */ list_for_each_entry(bus, bus_list, node) { - if ((dev = bus->self)) { - for (idx = PCI_BRIDGE_RESOURCES; - idx < PCI_NUM_RESOURCES; idx++) { - r = &dev->resource[idx]; - if (!r->flags) - continue; - if (!r->start || - pci_claim_resource(dev, idx) < 0) { - /* - * Something is wrong with the region. - * Invalidate the resource to prevent - * child resource allocations in this - * range. - */ - r->start = r->end = 0; - r->flags = 0; - } - } - } + if (bus->self) + pcibios_allocate_bridge_resources(bus->self); pcibios_allocate_bus_resources(&bus->children); } } From c7f4bbc92feee2986212ef3b42c806e2257197dc Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:25 -0700 Subject: [PATCH 0642/4607] x86/PCI: Factor out pcibios_allocate_dev_resources() Factor pcibios_allocate_dev_resources() out of pcibios_allocate_resources(). Currently we only allocate these resources at boot-time with a for_each_pci_dev() loop. Eventually we'll use pcibios_allocate_dev_resources() for hot-added devices, too. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 980036286909..5817cf235d9a 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -232,9 +232,8 @@ struct pci_check_idx_range { int end; }; -static void __init pcibios_allocate_resources(int pass) +static void __init pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) { - struct pci_dev *dev = NULL; int idx, disabled, i; u16 command; struct resource *r; @@ -246,14 +245,13 @@ static void __init pcibios_allocate_resources(int pass) #endif }; - for_each_pci_dev(dev) { - pci_read_config_word(dev, PCI_COMMAND, &command); - for (i = 0; i < ARRAY_SIZE(idx_range); i++) + pci_read_config_word(dev, PCI_COMMAND, &command); + for (i = 0; i < ARRAY_SIZE(idx_range); i++) for (idx = idx_range[i].start; idx <= idx_range[i].end; idx++) { r = &dev->resource[idx]; - if (r->parent) /* Already allocated */ + if (r->parent) /* Already allocated */ continue; - if (!r->start) /* Address not assigned at all */ + if (!r->start) /* Address not assigned at all */ continue; if (r->flags & IORESOURCE_IO) disabled = !(command & PCI_COMMAND_IO); @@ -272,23 +270,29 @@ static void __init pcibios_allocate_resources(int pass) } } } - if (!pass) { - r = &dev->resource[PCI_ROM_RESOURCE]; - if (r->flags & IORESOURCE_ROM_ENABLE) { - /* Turn the ROM off, leave the resource region, - * but keep it unregistered. */ - u32 reg; - dev_dbg(&dev->dev, "disabling ROM %pR\n", r); - r->flags &= ~IORESOURCE_ROM_ENABLE; - pci_read_config_dword(dev, - dev->rom_base_reg, ®); - pci_write_config_dword(dev, dev->rom_base_reg, + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & IORESOURCE_ROM_ENABLE) { + /* Turn the ROM off, leave the resource region, + * but keep it unregistered. */ + u32 reg; + dev_dbg(&dev->dev, "disabling ROM %pR\n", r); + r->flags &= ~IORESOURCE_ROM_ENABLE; + pci_read_config_dword(dev, dev->rom_base_reg, ®); + pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE); - } } } } +static void __init pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev = NULL; + + for_each_pci_dev(dev) + pcibios_allocate_dev_resources(dev, pass); +} + static int __init pcibios_assign_resources(void) { struct pci_dev *dev = NULL; From 77975357956c6450dd7ac3dfe572c1a8f0014c54 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 20 Dec 2012 15:32:06 +0100 Subject: [PATCH 0643/4607] KVM: s390: Constify intercept handler tables. These tables are never modified. Reviewed-by: Alexander Graf Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- arch/s390/kvm/intercept.c | 2 +- arch/s390/kvm/priv.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index 22798ec33fd1..0c08b3f1c1c5 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -104,7 +104,7 @@ static int handle_lctl(struct kvm_vcpu *vcpu) return 0; } -static intercept_handler_t instruction_handlers[256] = { +static const intercept_handler_t instruction_handlers[256] = { [0x01] = kvm_s390_handle_01, [0x83] = kvm_s390_handle_diag, [0xae] = kvm_s390_handle_sigp, diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index d768906f15c8..1aeb9335f9e2 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -297,7 +297,7 @@ out_fail: return 0; } -static intercept_handler_t priv_handlers[256] = { +static const intercept_handler_t priv_handlers[256] = { [0x02] = handle_stidp, [0x10] = handle_set_prefix, [0x11] = handle_store_prefix, @@ -405,7 +405,7 @@ static int handle_sckpf(struct kvm_vcpu *vcpu) return 0; } -static intercept_handler_t x01_handlers[256] = { +static const intercept_handler_t x01_handlers[256] = { [0x07] = handle_sckpf, }; From b1c571a50dfacf25a24c23271e9b8bf18ff6b102 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 20 Dec 2012 15:32:07 +0100 Subject: [PATCH 0644/4607] KVM: s390: Decoding helper functions. Introduce helper functions for decoding the various base/displacement instruction formats. Reviewed-by: Alexander Graf Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- arch/s390/kvm/intercept.c | 21 ++++++-------------- arch/s390/kvm/kvm-s390.h | 37 ++++++++++++++++++++++++++++++++++ arch/s390/kvm/priv.c | 42 ++++++++++----------------------------- arch/s390/kvm/sigp.c | 6 +----- 4 files changed, 55 insertions(+), 51 deletions(-) diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index 0c08b3f1c1c5..df6c0ad085aa 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -26,9 +26,6 @@ static int handle_lctlg(struct kvm_vcpu *vcpu) { int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4; int reg3 = vcpu->arch.sie_block->ipa & 0x000f; - int base2 = vcpu->arch.sie_block->ipb >> 28; - int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16) + - ((vcpu->arch.sie_block->ipb & 0xff00) << 4); u64 useraddr; int reg, rc; @@ -36,17 +33,15 @@ static int handle_lctlg(struct kvm_vcpu *vcpu) if ((vcpu->arch.sie_block->ipb & 0xff) != 0x2f) return -EOPNOTSUPP; - useraddr = disp2; - if (base2) - useraddr += vcpu->run->s.regs.gprs[base2]; + useraddr = kvm_s390_get_base_disp_rsy(vcpu); if (useraddr & 7) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); reg = reg1; - VCPU_EVENT(vcpu, 5, "lctlg r1:%x, r3:%x,b2:%x,d2:%x", reg1, reg3, base2, - disp2); + VCPU_EVENT(vcpu, 5, "lctlg r1:%x, r3:%x, addr:%llx", reg1, reg3, + useraddr); trace_kvm_s390_handle_lctl(vcpu, 1, reg1, reg3, useraddr); do { @@ -68,23 +63,19 @@ static int handle_lctl(struct kvm_vcpu *vcpu) { int reg1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4; int reg3 = vcpu->arch.sie_block->ipa & 0x000f; - int base2 = vcpu->arch.sie_block->ipb >> 28; - int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); u64 useraddr; u32 val = 0; int reg, rc; vcpu->stat.instruction_lctl++; - useraddr = disp2; - if (base2) - useraddr += vcpu->run->s.regs.gprs[base2]; + useraddr = kvm_s390_get_base_disp_rs(vcpu); if (useraddr & 3) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); - VCPU_EVENT(vcpu, 5, "lctl r1:%x, r3:%x,b2:%x,d2:%x", reg1, reg3, base2, - disp2); + VCPU_EVENT(vcpu, 5, "lctl r1:%x, r3:%x, addr:%llx", reg1, reg3, + useraddr); trace_kvm_s390_handle_lctl(vcpu, 0, reg1, reg3, useraddr); reg = reg1; diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index d75bc5e92c5b..dccc0242b7ca 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -65,6 +65,43 @@ static inline void kvm_s390_set_prefix(struct kvm_vcpu *vcpu, u32 prefix) vcpu->arch.sie_block->ihcpu = 0xffff; } +static inline u64 kvm_s390_get_base_disp_s(struct kvm_vcpu *vcpu) +{ + int base2 = vcpu->arch.sie_block->ipb >> 28; + int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); + + return (base2 ? vcpu->run->s.regs.gprs[base2] : 0) + disp2; +} + +static inline void kvm_s390_get_base_disp_sse(struct kvm_vcpu *vcpu, + u64 *address1, u64 *address2) +{ + int base1 = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28; + int disp1 = (vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16; + int base2 = (vcpu->arch.sie_block->ipb & 0xf000) >> 12; + int disp2 = vcpu->arch.sie_block->ipb & 0x0fff; + + *address1 = (base1 ? vcpu->run->s.regs.gprs[base1] : 0) + disp1; + *address2 = (base2 ? vcpu->run->s.regs.gprs[base2] : 0) + disp2; +} + +static inline u64 kvm_s390_get_base_disp_rsy(struct kvm_vcpu *vcpu) +{ + int base2 = vcpu->arch.sie_block->ipb >> 28; + int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16) + + ((vcpu->arch.sie_block->ipb & 0xff00) << 4); + + return (base2 ? vcpu->run->s.regs.gprs[base2] : 0) + disp2; +} + +static inline u64 kvm_s390_get_base_disp_rs(struct kvm_vcpu *vcpu) +{ + int base2 = vcpu->arch.sie_block->ipb >> 28; + int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); + + return (base2 ? vcpu->run->s.regs.gprs[base2] : 0) + disp2; +} + int kvm_s390_handle_wait(struct kvm_vcpu *vcpu); enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer); void kvm_s390_tasklet(unsigned long parm); diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 1aeb9335f9e2..d715842f56ca 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -24,17 +24,13 @@ static int handle_set_prefix(struct kvm_vcpu *vcpu) { - int base2 = vcpu->arch.sie_block->ipb >> 28; - int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); u64 operand2; u32 address = 0; u8 tmp; vcpu->stat.instruction_spx++; - operand2 = disp2; - if (base2) - operand2 += vcpu->run->s.regs.gprs[base2]; + operand2 = kvm_s390_get_base_disp_s(vcpu); /* must be word boundary */ if (operand2 & 3) { @@ -67,15 +63,12 @@ out: static int handle_store_prefix(struct kvm_vcpu *vcpu) { - int base2 = vcpu->arch.sie_block->ipb >> 28; - int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); u64 operand2; u32 address; vcpu->stat.instruction_stpx++; - operand2 = disp2; - if (base2) - operand2 += vcpu->run->s.regs.gprs[base2]; + + operand2 = kvm_s390_get_base_disp_s(vcpu); /* must be word boundary */ if (operand2 & 3) { @@ -100,15 +93,12 @@ out: static int handle_store_cpu_address(struct kvm_vcpu *vcpu) { - int base2 = vcpu->arch.sie_block->ipb >> 28; - int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); u64 useraddr; int rc; vcpu->stat.instruction_stap++; - useraddr = disp2; - if (base2) - useraddr += vcpu->run->s.regs.gprs[base2]; + + useraddr = kvm_s390_get_base_disp_s(vcpu); if (useraddr & 1) { kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); @@ -178,15 +168,12 @@ static int handle_stfl(struct kvm_vcpu *vcpu) static int handle_stidp(struct kvm_vcpu *vcpu) { - int base2 = vcpu->arch.sie_block->ipb >> 28; - int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); u64 operand2; int rc; vcpu->stat.instruction_stidp++; - operand2 = disp2; - if (base2) - operand2 += vcpu->run->s.regs.gprs[base2]; + + operand2 = kvm_s390_get_base_disp_s(vcpu); if (operand2 & 7) { kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); @@ -240,17 +227,13 @@ static int handle_stsi(struct kvm_vcpu *vcpu) int fc = (vcpu->run->s.regs.gprs[0] & 0xf0000000) >> 28; int sel1 = vcpu->run->s.regs.gprs[0] & 0xff; int sel2 = vcpu->run->s.regs.gprs[1] & 0xffff; - int base2 = vcpu->arch.sie_block->ipb >> 28; - int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); u64 operand2; unsigned long mem; vcpu->stat.instruction_stsi++; VCPU_EVENT(vcpu, 4, "stsi: fc: %x sel1: %x sel2: %x", fc, sel1, sel2); - operand2 = disp2; - if (base2) - operand2 += vcpu->run->s.regs.gprs[base2]; + operand2 = kvm_s390_get_base_disp_s(vcpu); if (operand2 & 0xfff && fc > 0) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); @@ -335,17 +318,14 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu) static int handle_tprot(struct kvm_vcpu *vcpu) { - int base1 = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28; - int disp1 = (vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16; - int base2 = (vcpu->arch.sie_block->ipb & 0xf000) >> 12; - int disp2 = vcpu->arch.sie_block->ipb & 0x0fff; - u64 address1 = disp1 + base1 ? vcpu->run->s.regs.gprs[base1] : 0; - u64 address2 = disp2 + base2 ? vcpu->run->s.regs.gprs[base2] : 0; + u64 address1, address2; struct vm_area_struct *vma; unsigned long user_address; vcpu->stat.instruction_tprot++; + kvm_s390_get_base_disp_sse(vcpu, &address1, &address2); + /* we only handle the Linux memory detection case: * access key == 0 * guest DAT == off diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c index 566ddf6e8dfb..461e84179db1 100644 --- a/arch/s390/kvm/sigp.c +++ b/arch/s390/kvm/sigp.c @@ -324,8 +324,6 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu) { int r1 = (vcpu->arch.sie_block->ipa & 0x00f0) >> 4; int r3 = vcpu->arch.sie_block->ipa & 0x000f; - int base2 = vcpu->arch.sie_block->ipb >> 28; - int disp2 = ((vcpu->arch.sie_block->ipb & 0x0fff0000) >> 16); u32 parameter; u16 cpu_addr = vcpu->run->s.regs.gprs[r3]; u8 order_code; @@ -336,9 +334,7 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu) return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OPERATION); - order_code = disp2; - if (base2) - order_code += vcpu->run->s.regs.gprs[base2]; + order_code = kvm_s390_get_base_disp_rs(vcpu); if (r1 % 2) parameter = vcpu->run->s.regs.gprs[r1]; From d8346b7d9bab37e6cc712ff1622c65ff98bdfef8 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 20 Dec 2012 15:32:08 +0100 Subject: [PATCH 0645/4607] KVM: s390: Support for I/O interrupts. Add support for handling I/O interrupts (standard, subchannel-related ones and rudimentary adapter interrupts). The subchannel-identifying parameters are encoded into the interrupt type. I/O interrupts are floating, so they can't be injected on a specific vcpu. Reviewed-by: Alexander Graf Reviewed-by: Marcelo Tosatti Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- Documentation/virtual/kvm/api.txt | 4 ++ arch/s390/include/asm/kvm_host.h | 2 + arch/s390/kvm/interrupt.c | 103 +++++++++++++++++++++++++++++- include/uapi/linux/kvm.h | 9 +++ 4 files changed, 116 insertions(+), 2 deletions(-) diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index a4df5535996b..83bd92b52936 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2069,6 +2069,10 @@ KVM_S390_INT_VIRTIO (vm) - virtio external interrupt; external interrupt KVM_S390_INT_SERVICE (vm) - sclp external interrupt; sclp parameter in parm KVM_S390_INT_EMERGENCY (vcpu) - sigp emergency; source cpu in parm KVM_S390_INT_EXTERNAL_CALL (vcpu) - sigp external call; source cpu in parm +KVM_S390_INT_IO(ai,cssid,ssid,schid) (vm) - compound value to indicate an + I/O interrupt (ai - adapter interrupt; cssid,ssid,schid - subchannel); + I/O interruption parameters in parm (subchannel) and parm64 (intparm, + interruption subclass) Note that the vcpu ioctl is asynchronous to vcpu execution. diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 711c5ab391cf..a8e35c43df78 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -74,6 +74,7 @@ struct kvm_s390_sie_block { __u64 epoch; /* 0x0038 */ __u8 reserved40[4]; /* 0x0040 */ #define LCTL_CR0 0x8000 +#define LCTL_CR6 0x0200 __u16 lctl; /* 0x0044 */ __s16 icpua; /* 0x0046 */ __u32 ictl; /* 0x0048 */ @@ -125,6 +126,7 @@ struct kvm_vcpu_stat { u32 deliver_prefix_signal; u32 deliver_restart_signal; u32 deliver_program_int; + u32 deliver_io_int; u32 exit_wait_state; u32 instruction_stidp; u32 instruction_spx; diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index c30615e605ac..52cdf20906ab 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -21,11 +21,26 @@ #include "gaccess.h" #include "trace-s390.h" +#define IOINT_SCHID_MASK 0x0000ffff +#define IOINT_SSID_MASK 0x00030000 +#define IOINT_CSSID_MASK 0x03fc0000 +#define IOINT_AI_MASK 0x04000000 + +static int is_ioint(u64 type) +{ + return ((type & 0xfffe0000u) != 0xfffe0000u); +} + static int psw_extint_disabled(struct kvm_vcpu *vcpu) { return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT); } +static int psw_ioint_disabled(struct kvm_vcpu *vcpu) +{ + return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO); +} + static int psw_interrupts_disabled(struct kvm_vcpu *vcpu) { if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) || @@ -67,7 +82,15 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu, case KVM_S390_SIGP_SET_PREFIX: case KVM_S390_RESTART: return 1; + case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: + if (psw_ioint_disabled(vcpu)) + return 0; + if (vcpu->arch.sie_block->gcr[6] & inti->io.io_int_word) + return 1; + return 0; default: + printk(KERN_WARNING "illegal interrupt type %llx\n", + inti->type); BUG(); } return 0; @@ -116,6 +139,12 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu, case KVM_S390_SIGP_STOP: __set_cpuflag(vcpu, CPUSTAT_STOP_INT); break; + case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: + if (psw_ioint_disabled(vcpu)) + __set_cpuflag(vcpu, CPUSTAT_IO_INT); + else + vcpu->arch.sie_block->lctl |= LCTL_CR6; + break; default: BUG(); } @@ -297,6 +326,47 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu, exception = 1; break; + case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: + { + __u32 param0 = ((__u32)inti->io.subchannel_id << 16) | + inti->io.subchannel_nr; + __u64 param1 = ((__u64)inti->io.io_int_parm << 32) | + inti->io.io_int_word; + VCPU_EVENT(vcpu, 4, "interrupt: I/O %llx", inti->type); + vcpu->stat.deliver_io_int++; + trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, + param0, param1); + rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_ID, + inti->io.subchannel_id); + if (rc == -EFAULT) + exception = 1; + + rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_NR, + inti->io.subchannel_nr); + if (rc == -EFAULT) + exception = 1; + + rc = put_guest_u32(vcpu, __LC_IO_INT_PARM, + inti->io.io_int_parm); + if (rc == -EFAULT) + exception = 1; + + rc = put_guest_u32(vcpu, __LC_IO_INT_WORD, + inti->io.io_int_word); + if (rc == -EFAULT) + exception = 1; + + rc = copy_to_guest(vcpu, __LC_IO_OLD_PSW, + &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); + if (rc == -EFAULT) + exception = 1; + + rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, + __LC_IO_NEW_PSW, sizeof(psw_t)); + if (rc == -EFAULT) + exception = 1; + break; + } default: BUG(); } @@ -545,7 +615,7 @@ int kvm_s390_inject_vm(struct kvm *kvm, { struct kvm_s390_local_interrupt *li; struct kvm_s390_float_interrupt *fi; - struct kvm_s390_interrupt_info *inti; + struct kvm_s390_interrupt_info *inti, *iter; int sigcpu; inti = kzalloc(sizeof(*inti), GFP_KERNEL); @@ -569,6 +639,22 @@ int kvm_s390_inject_vm(struct kvm *kvm, case KVM_S390_SIGP_STOP: case KVM_S390_INT_EXTERNAL_CALL: case KVM_S390_INT_EMERGENCY: + kfree(inti); + return -EINVAL; + case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: + if (s390int->type & IOINT_AI_MASK) + VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)"); + else + VM_EVENT(kvm, 5, "inject: I/O css %x ss %x schid %04x", + s390int->type & IOINT_CSSID_MASK, + s390int->type & IOINT_SSID_MASK, + s390int->type & IOINT_SCHID_MASK); + inti->type = s390int->type; + inti->io.subchannel_id = s390int->parm >> 16; + inti->io.subchannel_nr = s390int->parm & 0x0000ffffu; + inti->io.io_int_parm = s390int->parm64 >> 32; + inti->io.io_int_word = s390int->parm64 & 0x00000000ffffffffull; + break; default: kfree(inti); return -EINVAL; @@ -579,7 +665,19 @@ int kvm_s390_inject_vm(struct kvm *kvm, mutex_lock(&kvm->lock); fi = &kvm->arch.float_int; spin_lock(&fi->lock); - list_add_tail(&inti->list, &fi->list); + if (!is_ioint(inti->type)) + list_add_tail(&inti->list, &fi->list); + else { + /* Keep I/O interrupts sorted in isc order. */ + list_for_each_entry(iter, &fi->list, list) { + if (!is_ioint(iter->type)) + continue; + if (iter->io.io_int_word <= inti->io.io_int_word) + continue; + break; + } + list_add_tail(&inti->list, &iter->list); + } atomic_set(&fi->active, 1); sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS); if (sigcpu == KVM_MAX_VCPUS) { @@ -653,6 +751,7 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, break; case KVM_S390_INT_VIRTIO: case KVM_S390_INT_SERVICE: + case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: default: kfree(inti); return -EINVAL; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index e6e5d4b13708..54540bdd3340 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -401,6 +401,15 @@ struct kvm_s390_psw { #define KVM_S390_INT_SERVICE 0xffff2401u #define KVM_S390_INT_EMERGENCY 0xffff1201u #define KVM_S390_INT_EXTERNAL_CALL 0xffff1202u +/* Anything below 0xfffe0000u is taken by INT_IO */ +#define KVM_S390_INT_IO(ai,cssid,ssid,schid) \ + (((schid)) | \ + ((ssid) << 16) | \ + ((cssid) << 18) | \ + ((ai) << 26)) +#define KVM_S390_INT_IO_MIN 0x00000000u +#define KVM_S390_INT_IO_MAX 0xfffdffffu + struct kvm_s390_interrupt { __u32 type; From 48a3e950f4cee6a345ffbe9baf599f1e9a54c479 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 20 Dec 2012 15:32:09 +0100 Subject: [PATCH 0646/4607] KVM: s390: Add support for machine checks. Add support for injecting machine checks (only repressible conditions for now). This is a bit more involved than I/O interrupts, for these reasons: - Machine checks come in both floating and cpu varieties. - We don't have a bit for machine checks enabling, but have to use a roundabout approach with trapping PSW changing instructions and watching for opened machine checks. Reviewed-by: Alexander Graf Reviewed-by: Marcelo Tosatti Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- Documentation/virtual/kvm/api.txt | 4 + arch/s390/include/asm/kvm_host.h | 8 ++ arch/s390/kvm/intercept.c | 2 + arch/s390/kvm/interrupt.c | 112 +++++++++++++++++++++++++ arch/s390/kvm/kvm-s390.h | 3 + arch/s390/kvm/priv.c | 135 ++++++++++++++++++++++++++++++ arch/s390/kvm/trace-s390.h | 6 +- include/uapi/linux/kvm.h | 1 + 8 files changed, 268 insertions(+), 3 deletions(-) diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 83bd92b52936..8a0de309932f 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2073,6 +2073,10 @@ KVM_S390_INT_IO(ai,cssid,ssid,schid) (vm) - compound value to indicate an I/O interrupt (ai - adapter interrupt; cssid,ssid,schid - subchannel); I/O interruption parameters in parm (subchannel) and parm64 (intparm, interruption subclass) +KVM_S390_MCHK (vm, vcpu) - machine check interrupt; cr 14 bits in parm, + machine check interrupt code in parm64 (note that + machine checks needing further payload are not + supported by this ioctl) Note that the vcpu ioctl is asynchronous to vcpu execution. diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index a8e35c43df78..29363d155cd5 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -75,8 +75,10 @@ struct kvm_s390_sie_block { __u8 reserved40[4]; /* 0x0040 */ #define LCTL_CR0 0x8000 #define LCTL_CR6 0x0200 +#define LCTL_CR14 0x0002 __u16 lctl; /* 0x0044 */ __s16 icpua; /* 0x0046 */ +#define ICTL_LPSW 0x00400000 __u32 ictl; /* 0x0048 */ __u32 eca; /* 0x004c */ __u8 icptcode; /* 0x0050 */ @@ -187,6 +189,11 @@ struct kvm_s390_emerg_info { __u16 code; }; +struct kvm_s390_mchk_info { + __u64 cr14; + __u64 mcic; +}; + struct kvm_s390_interrupt_info { struct list_head list; u64 type; @@ -197,6 +204,7 @@ struct kvm_s390_interrupt_info { struct kvm_s390_emerg_info emerg; struct kvm_s390_extcall_info extcall; struct kvm_s390_prefix_info prefix; + struct kvm_s390_mchk_info mchk; }; }; diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index df6c0ad085aa..950c13ecaf60 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -97,10 +97,12 @@ static int handle_lctl(struct kvm_vcpu *vcpu) static const intercept_handler_t instruction_handlers[256] = { [0x01] = kvm_s390_handle_01, + [0x82] = kvm_s390_handle_lpsw, [0x83] = kvm_s390_handle_diag, [0xae] = kvm_s390_handle_sigp, [0xb2] = kvm_s390_handle_b2, [0xb7] = handle_lctl, + [0xb9] = kvm_s390_handle_b9, [0xe5] = kvm_s390_handle_e5, [0xeb] = handle_lctlg, }; diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 52cdf20906ab..b3b4748485ee 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -41,6 +41,11 @@ static int psw_ioint_disabled(struct kvm_vcpu *vcpu) return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO); } +static int psw_mchk_disabled(struct kvm_vcpu *vcpu) +{ + return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_MCHECK); +} + static int psw_interrupts_disabled(struct kvm_vcpu *vcpu) { if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) || @@ -82,6 +87,12 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu, case KVM_S390_SIGP_SET_PREFIX: case KVM_S390_RESTART: return 1; + case KVM_S390_MCHK: + if (psw_mchk_disabled(vcpu)) + return 0; + if (vcpu->arch.sie_block->gcr[14] & inti->mchk.cr14) + return 1; + return 0; case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: if (psw_ioint_disabled(vcpu)) return 0; @@ -116,6 +127,7 @@ static void __reset_intercept_indicators(struct kvm_vcpu *vcpu) CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT, &vcpu->arch.sie_block->cpuflags); vcpu->arch.sie_block->lctl = 0x0000; + vcpu->arch.sie_block->ictl &= ~ICTL_LPSW; } static void __set_cpuflag(struct kvm_vcpu *vcpu, u32 flag) @@ -139,6 +151,12 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu, case KVM_S390_SIGP_STOP: __set_cpuflag(vcpu, CPUSTAT_STOP_INT); break; + case KVM_S390_MCHK: + if (psw_mchk_disabled(vcpu)) + vcpu->arch.sie_block->ictl |= ICTL_LPSW; + else + vcpu->arch.sie_block->lctl |= LCTL_CR14; + break; case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: if (psw_ioint_disabled(vcpu)) __set_cpuflag(vcpu, CPUSTAT_IO_INT); @@ -326,6 +344,32 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu, exception = 1; break; + case KVM_S390_MCHK: + VCPU_EVENT(vcpu, 4, "interrupt: machine check mcic=%llx", + inti->mchk.mcic); + trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, + inti->mchk.cr14, + inti->mchk.mcic); + rc = kvm_s390_vcpu_store_status(vcpu, + KVM_S390_STORE_STATUS_PREFIXED); + if (rc == -EFAULT) + exception = 1; + + rc = put_guest_u64(vcpu, __LC_MCCK_CODE, inti->mchk.mcic); + if (rc == -EFAULT) + exception = 1; + + rc = copy_to_guest(vcpu, __LC_MCK_OLD_PSW, + &vcpu->arch.sie_block->gpsw, sizeof(psw_t)); + if (rc == -EFAULT) + exception = 1; + + rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw, + __LC_MCK_NEW_PSW, sizeof(psw_t)); + if (rc == -EFAULT) + exception = 1; + break; + case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: { __u32 param0 = ((__u32)inti->io.subchannel_id << 16) | @@ -588,6 +632,61 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu) } } +void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu) +{ + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + struct kvm_s390_float_interrupt *fi = vcpu->arch.local_int.float_int; + struct kvm_s390_interrupt_info *n, *inti = NULL; + int deliver; + + __reset_intercept_indicators(vcpu); + if (atomic_read(&li->active)) { + do { + deliver = 0; + spin_lock_bh(&li->lock); + list_for_each_entry_safe(inti, n, &li->list, list) { + if ((inti->type == KVM_S390_MCHK) && + __interrupt_is_deliverable(vcpu, inti)) { + list_del(&inti->list); + deliver = 1; + break; + } + __set_intercept_indicator(vcpu, inti); + } + if (list_empty(&li->list)) + atomic_set(&li->active, 0); + spin_unlock_bh(&li->lock); + if (deliver) { + __do_deliver_interrupt(vcpu, inti); + kfree(inti); + } + } while (deliver); + } + + if (atomic_read(&fi->active)) { + do { + deliver = 0; + spin_lock(&fi->lock); + list_for_each_entry_safe(inti, n, &fi->list, list) { + if ((inti->type == KVM_S390_MCHK) && + __interrupt_is_deliverable(vcpu, inti)) { + list_del(&inti->list); + deliver = 1; + break; + } + __set_intercept_indicator(vcpu, inti); + } + if (list_empty(&fi->list)) + atomic_set(&fi->active, 0); + spin_unlock(&fi->lock); + if (deliver) { + __do_deliver_interrupt(vcpu, inti); + kfree(inti); + } + } while (deliver); + } +} + int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code) { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; @@ -641,6 +740,13 @@ int kvm_s390_inject_vm(struct kvm *kvm, case KVM_S390_INT_EMERGENCY: kfree(inti); return -EINVAL; + case KVM_S390_MCHK: + VM_EVENT(kvm, 5, "inject: machine check parm64:%llx", + s390int->parm64); + inti->type = s390int->type; + inti->mchk.cr14 = s390int->parm; /* upper bits are not used */ + inti->mchk.mcic = s390int->parm64; + break; case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: if (s390int->type & IOINT_AI_MASK) VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)"); @@ -749,6 +855,12 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, inti->type = s390int->type; inti->emerg.code = s390int->parm; break; + case KVM_S390_MCHK: + VCPU_EVENT(vcpu, 5, "inject: machine check parm64:%llx", + s390int->parm64); + inti->type = s390int->type; + inti->mchk.mcic = s390int->parm64; + break; case KVM_S390_INT_VIRTIO: case KVM_S390_INT_SERVICE: case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index dccc0242b7ca..1f7cc6ccf102 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -106,6 +106,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu); enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer); void kvm_s390_tasklet(unsigned long parm); void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu); +void kvm_s390_deliver_pending_machine_checks(struct kvm_vcpu *vcpu); int kvm_s390_inject_vm(struct kvm *kvm, struct kvm_s390_interrupt *s390int); int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, @@ -117,6 +118,8 @@ int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action); int kvm_s390_handle_b2(struct kvm_vcpu *vcpu); int kvm_s390_handle_e5(struct kvm_vcpu *vcpu); int kvm_s390_handle_01(struct kvm_vcpu *vcpu); +int kvm_s390_handle_b9(struct kvm_vcpu *vcpu); +int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu); /* implemented in sigp.c */ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu); diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index d715842f56ca..d3cbcd3c9ada 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include "gaccess.h" #include "kvm-s390.h" #include "trace.h" @@ -166,6 +168,99 @@ static int handle_stfl(struct kvm_vcpu *vcpu) return 0; } +static void handle_new_psw(struct kvm_vcpu *vcpu) +{ + /* Check whether the new psw is enabled for machine checks. */ + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_MCHECK) + kvm_s390_deliver_pending_machine_checks(vcpu); +} + +#define PSW_MASK_ADDR_MODE (PSW_MASK_EA | PSW_MASK_BA) +#define PSW_MASK_UNASSIGNED 0xb80800fe7fffffffUL +#define PSW_ADDR_24 0x00000000000fffffUL +#define PSW_ADDR_31 0x000000007fffffffUL + +int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu) +{ + u64 addr; + psw_compat_t new_psw; + + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, + PGM_PRIVILEGED_OPERATION); + + addr = kvm_s390_get_base_disp_s(vcpu); + + if (addr & 7) { + kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + goto out; + } + + if (copy_from_guest(vcpu, &new_psw, addr, sizeof(new_psw))) { + kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); + goto out; + } + + if (!(new_psw.mask & PSW32_MASK_BASE)) { + kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + goto out; + } + + vcpu->arch.sie_block->gpsw.mask = + (new_psw.mask & ~PSW32_MASK_BASE) << 32; + vcpu->arch.sie_block->gpsw.addr = new_psw.addr; + + if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_UNASSIGNED) || + (!(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_ADDR_MODE) && + (vcpu->arch.sie_block->gpsw.addr & ~PSW_ADDR_24)) || + ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_ADDR_MODE) == + PSW_MASK_EA)) { + kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + goto out; + } + + handle_new_psw(vcpu); +out: + return 0; +} + +static int handle_lpswe(struct kvm_vcpu *vcpu) +{ + u64 addr; + psw_t new_psw; + + addr = kvm_s390_get_base_disp_s(vcpu); + + if (addr & 7) { + kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + goto out; + } + + if (copy_from_guest(vcpu, &new_psw, addr, sizeof(new_psw))) { + kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); + goto out; + } + + vcpu->arch.sie_block->gpsw.mask = new_psw.mask; + vcpu->arch.sie_block->gpsw.addr = new_psw.addr; + + if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_UNASSIGNED) || + (((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_ADDR_MODE) == + PSW_MASK_BA) && + (vcpu->arch.sie_block->gpsw.addr & ~PSW_ADDR_31)) || + (!(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_ADDR_MODE) && + (vcpu->arch.sie_block->gpsw.addr & ~PSW_ADDR_24)) || + ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_ADDR_MODE) == + PSW_MASK_EA)) { + kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); + goto out; + } + + handle_new_psw(vcpu); +out: + return 0; +} + static int handle_stidp(struct kvm_vcpu *vcpu) { u64 operand2; @@ -292,6 +387,7 @@ static const intercept_handler_t priv_handlers[256] = { [0x5f] = handle_chsc, [0x7d] = handle_stsi, [0xb1] = handle_stfl, + [0xb2] = handle_lpswe, }; int kvm_s390_handle_b2(struct kvm_vcpu *vcpu) @@ -316,6 +412,45 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu) return -EOPNOTSUPP; } +static int handle_epsw(struct kvm_vcpu *vcpu) +{ + int reg1, reg2; + + reg1 = (vcpu->arch.sie_block->ipb & 0x00f00000) >> 24; + reg2 = (vcpu->arch.sie_block->ipb & 0x000f0000) >> 16; + + /* This basically extracts the mask half of the psw. */ + vcpu->run->s.regs.gprs[reg1] &= 0xffffffff00000000; + vcpu->run->s.regs.gprs[reg1] |= vcpu->arch.sie_block->gpsw.mask >> 32; + if (reg2) { + vcpu->run->s.regs.gprs[reg2] &= 0xffffffff00000000; + vcpu->run->s.regs.gprs[reg2] |= + vcpu->arch.sie_block->gpsw.mask & 0x00000000ffffffff; + } + return 0; +} + +static const intercept_handler_t b9_handlers[256] = { + [0x8d] = handle_epsw, +}; + +int kvm_s390_handle_b9(struct kvm_vcpu *vcpu) +{ + intercept_handler_t handler; + + /* This is handled just as for the B2 instructions. */ + handler = b9_handlers[vcpu->arch.sie_block->ipa & 0x00ff]; + if (handler) { + if ((handler != handle_epsw) && + (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)) + return kvm_s390_inject_program_int(vcpu, + PGM_PRIVILEGED_OPERATION); + else + return handler(vcpu); + } + return -EOPNOTSUPP; +} + static int handle_tprot(struct kvm_vcpu *vcpu) { u64 address1, address2; diff --git a/arch/s390/kvm/trace-s390.h b/arch/s390/kvm/trace-s390.h index 90fdf85b5ff7..95fbc1ab88dc 100644 --- a/arch/s390/kvm/trace-s390.h +++ b/arch/s390/kvm/trace-s390.h @@ -141,13 +141,13 @@ TRACE_EVENT(kvm_s390_inject_vcpu, * Trace point for the actual delivery of interrupts. */ TRACE_EVENT(kvm_s390_deliver_interrupt, - TP_PROTO(unsigned int id, __u64 type, __u32 data0, __u64 data1), + TP_PROTO(unsigned int id, __u64 type, __u64 data0, __u64 data1), TP_ARGS(id, type, data0, data1), TP_STRUCT__entry( __field(int, id) __field(__u32, inttype) - __field(__u32, data0) + __field(__u64, data0) __field(__u64, data1) ), @@ -159,7 +159,7 @@ TRACE_EVENT(kvm_s390_deliver_interrupt, ), TP_printk("deliver interrupt (vcpu %d): type:%x (%s) " \ - "data:%08x %016llx", + "data:%08llx %016llx", __entry->id, __entry->inttype, __print_symbolic(__entry->inttype, kvm_s390_int_type), __entry->data0, __entry->data1) diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 54540bdd3340..80bb3b801116 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -397,6 +397,7 @@ struct kvm_s390_psw { #define KVM_S390_PROGRAM_INT 0xfffe0001u #define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u #define KVM_S390_RESTART 0xfffe0003u +#define KVM_S390_MCHK 0xfffe1000u #define KVM_S390_INT_VIRTIO 0xffff2603u #define KVM_S390_INT_SERVICE 0xffff2401u #define KVM_S390_INT_EMERGENCY 0xffff1201u From f379aae558b8daff0f7a1c5fc225af5d35c741f7 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 20 Dec 2012 15:32:10 +0100 Subject: [PATCH 0647/4607] KVM: s390: In-kernel handling of I/O instructions. Explicitely catch all channel I/O related instructions intercepts in the kernel and set condition code 3 for them. This paves the way for properly handling these instructions later on. Note: This is not architecture compliant (the previous code wasn't either) since setting cc 3 is not the correct thing to do for some of these instructions. For Linux guests, however, it still has the intended effect of stopping css probing. Reviewed-by: Marcelo Tosatti Reviewed-by: Alexander Graf Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- arch/s390/kvm/intercept.c | 19 ++++++++++--- arch/s390/kvm/kvm-s390.h | 1 + arch/s390/kvm/priv.c | 56 +++++++++++++++++++++++++++------------ 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index 950c13ecaf60..71af87dbb42c 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -30,8 +30,6 @@ static int handle_lctlg(struct kvm_vcpu *vcpu) int reg, rc; vcpu->stat.instruction_lctlg++; - if ((vcpu->arch.sie_block->ipb & 0xff) != 0x2f) - return -EOPNOTSUPP; useraddr = kvm_s390_get_base_disp_rsy(vcpu); @@ -95,6 +93,21 @@ static int handle_lctl(struct kvm_vcpu *vcpu) return 0; } +static const intercept_handler_t eb_handlers[256] = { + [0x2f] = handle_lctlg, + [0x8a] = kvm_s390_handle_priv_eb, +}; + +static int handle_eb(struct kvm_vcpu *vcpu) +{ + intercept_handler_t handler; + + handler = eb_handlers[vcpu->arch.sie_block->ipb & 0xff]; + if (handler) + return handler(vcpu); + return -EOPNOTSUPP; +} + static const intercept_handler_t instruction_handlers[256] = { [0x01] = kvm_s390_handle_01, [0x82] = kvm_s390_handle_lpsw, @@ -104,7 +117,7 @@ static const intercept_handler_t instruction_handlers[256] = { [0xb7] = handle_lctl, [0xb9] = kvm_s390_handle_b9, [0xe5] = kvm_s390_handle_e5, - [0xeb] = handle_lctlg, + [0xeb] = handle_eb, }; static int handle_noop(struct kvm_vcpu *vcpu) diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 1f7cc6ccf102..211b340385a7 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -120,6 +120,7 @@ int kvm_s390_handle_e5(struct kvm_vcpu *vcpu); int kvm_s390_handle_01(struct kvm_vcpu *vcpu); int kvm_s390_handle_b9(struct kvm_vcpu *vcpu); int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu); +int kvm_s390_handle_priv_eb(struct kvm_vcpu *vcpu); /* implemented in sigp.c */ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu); diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index d3cbcd3c9ada..8ad776f87856 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -127,20 +127,9 @@ static int handle_skey(struct kvm_vcpu *vcpu) return 0; } -static int handle_stsch(struct kvm_vcpu *vcpu) +static int handle_io_inst(struct kvm_vcpu *vcpu) { - vcpu->stat.instruction_stsch++; - VCPU_EVENT(vcpu, 4, "%s", "store subchannel - CC3"); - /* condition code 3 */ - vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); - vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44; - return 0; -} - -static int handle_chsc(struct kvm_vcpu *vcpu) -{ - vcpu->stat.instruction_chsc++; - VCPU_EVENT(vcpu, 4, "%s", "channel subsystem call - CC3"); + VCPU_EVENT(vcpu, 4, "%s", "I/O instruction"); /* condition code 3 */ vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44; @@ -375,7 +364,7 @@ out_fail: return 0; } -static const intercept_handler_t priv_handlers[256] = { +static const intercept_handler_t b2_handlers[256] = { [0x02] = handle_stidp, [0x10] = handle_set_prefix, [0x11] = handle_store_prefix, @@ -383,8 +372,22 @@ static const intercept_handler_t priv_handlers[256] = { [0x29] = handle_skey, [0x2a] = handle_skey, [0x2b] = handle_skey, - [0x34] = handle_stsch, - [0x5f] = handle_chsc, + [0x30] = handle_io_inst, + [0x31] = handle_io_inst, + [0x32] = handle_io_inst, + [0x33] = handle_io_inst, + [0x34] = handle_io_inst, + [0x35] = handle_io_inst, + [0x36] = handle_io_inst, + [0x37] = handle_io_inst, + [0x38] = handle_io_inst, + [0x39] = handle_io_inst, + [0x3a] = handle_io_inst, + [0x3b] = handle_io_inst, + [0x3c] = handle_io_inst, + [0x5f] = handle_io_inst, + [0x74] = handle_io_inst, + [0x76] = handle_io_inst, [0x7d] = handle_stsi, [0xb1] = handle_stfl, [0xb2] = handle_lpswe, @@ -401,7 +404,7 @@ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu) * state bit and (a) handle the instruction or (b) send a code 2 * program check. * Anything else goes to userspace.*/ - handler = priv_handlers[vcpu->arch.sie_block->ipa & 0x00ff]; + handler = b2_handlers[vcpu->arch.sie_block->ipa & 0x00ff]; if (handler) { if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) return kvm_s390_inject_program_int(vcpu, @@ -432,6 +435,7 @@ static int handle_epsw(struct kvm_vcpu *vcpu) static const intercept_handler_t b9_handlers[256] = { [0x8d] = handle_epsw, + [0x9c] = handle_io_inst, }; int kvm_s390_handle_b9(struct kvm_vcpu *vcpu) @@ -451,6 +455,24 @@ int kvm_s390_handle_b9(struct kvm_vcpu *vcpu) return -EOPNOTSUPP; } +static const intercept_handler_t eb_handlers[256] = { + [0x8a] = handle_io_inst, +}; + +int kvm_s390_handle_priv_eb(struct kvm_vcpu *vcpu) +{ + intercept_handler_t handler; + + /* All eb instructions that end up here are privileged. */ + if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) + return kvm_s390_inject_program_int(vcpu, + PGM_PRIVILEGED_OPERATION); + handler = eb_handlers[vcpu->arch.sie_block->ipb & 0xff]; + if (handler) + return handler(vcpu); + return -EOPNOTSUPP; +} + static int handle_tprot(struct kvm_vcpu *vcpu) { u64 address1, address2; From d6712df95bcfea597fc3ea2405ec13e8b69a7b8c Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 20 Dec 2012 15:32:11 +0100 Subject: [PATCH 0648/4607] KVM: s390: Base infrastructure for enabling capabilities. Make s390 support KVM_ENABLE_CAP. Reviewed-by: Marcelo Tosatti Acked-by: Alexander Graf Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- Documentation/virtual/kvm/api.txt | 2 +- arch/s390/kvm/kvm-s390.c | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 8a0de309932f..73bd159c5559 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -913,7 +913,7 @@ documentation when it pops into existence). 4.37 KVM_ENABLE_CAP Capability: KVM_CAP_ENABLE_CAP -Architectures: ppc +Architectures: ppc, s390 Type: vcpu ioctl Parameters: struct kvm_enable_cap (in) Returns: 0 on success; -1 on error diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index f718bc65835c..5ff26033825c 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -140,6 +140,7 @@ int kvm_dev_ioctl_check_extension(long ext) #endif case KVM_CAP_SYNC_REGS: case KVM_CAP_ONE_REG: + case KVM_CAP_ENABLE_CAP: r = 1; break; case KVM_CAP_NR_VCPUS: @@ -808,6 +809,22 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr) return 0; } +static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, + struct kvm_enable_cap *cap) +{ + int r; + + if (cap->flags) + return -EINVAL; + + switch (cap->cap) { + default: + r = -EINVAL; + break; + } + return r; +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -894,6 +911,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp, r = 0; break; } + case KVM_ENABLE_CAP: + { + struct kvm_enable_cap cap; + r = -EFAULT; + if (copy_from_user(&cap, argp, sizeof(cap))) + break; + r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap); + break; + } default: r = -ENOTTY; } From fa6b7fe9928d50444c29b29c8563746c6b0c6299 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 20 Dec 2012 15:32:12 +0100 Subject: [PATCH 0649/4607] KVM: s390: Add support for channel I/O instructions. Add a new capability, KVM_CAP_S390_CSS_SUPPORT, which will pass intercepts for channel I/O instructions to userspace. Only I/O instructions interacting with I/O interrupts need to be handled in-kernel: - TEST PENDING INTERRUPTION (tpi) dequeues and stores pending interrupts entirely in-kernel. - TEST SUBCHANNEL (tsch) dequeues pending interrupts in-kernel and exits via KVM_EXIT_S390_TSCH to userspace for subchannel- related processing. Reviewed-by: Marcelo Tosatti Reviewed-by: Alexander Graf Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- Documentation/virtual/kvm/api.txt | 30 ++++++++++ arch/s390/include/asm/kvm_host.h | 1 + arch/s390/kvm/intercept.c | 1 + arch/s390/kvm/interrupt.c | 37 +++++++++++++ arch/s390/kvm/kvm-s390.c | 12 ++++ arch/s390/kvm/kvm-s390.h | 2 + arch/s390/kvm/priv.c | 91 +++++++++++++++++++++++++++++-- arch/s390/kvm/trace-s390.h | 20 +++++++ include/trace/events/kvm.h | 2 +- include/uapi/linux/kvm.h | 11 ++++ 10 files changed, 202 insertions(+), 5 deletions(-) diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 73bd159c5559..f2d6391178b9 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2350,6 +2350,22 @@ The possible hypercalls are defined in the Power Architecture Platform Requirements (PAPR) document available from www.power.org (free developer registration required to access it). + /* KVM_EXIT_S390_TSCH */ + struct { + __u16 subchannel_id; + __u16 subchannel_nr; + __u32 io_int_parm; + __u32 io_int_word; + __u32 ipb; + __u8 dequeued; + } s390_tsch; + +s390 specific. This exit occurs when KVM_CAP_S390_CSS_SUPPORT has been enabled +and TEST SUBCHANNEL was intercepted. If dequeued is set, a pending I/O +interrupt for the target subchannel has been dequeued and subchannel_id, +subchannel_nr, io_int_parm and io_int_word contain the parameters for that +interrupt. ipb is needed for instruction parameter decoding. + /* Fix the size of the union. */ char padding[256]; }; @@ -2471,3 +2487,17 @@ For mmu types KVM_MMU_FSL_BOOKE_NOHV and KVM_MMU_FSL_BOOKE_HV: where "num_sets" is the tlb_sizes[] value divided by the tlb_ways[] value. - The tsize field of mas1 shall be set to 4K on TLB0, even though the hardware ignores this value for TLB0. + +6.4 KVM_CAP_S390_CSS_SUPPORT + +Architectures: s390 +Parameters: none +Returns: 0 on success; -1 on error + +This capability enables support for handling of channel I/O instructions. + +TEST PENDING INTERRUPTION and the interrupt portion of TEST SUBCHANNEL are +handled in-kernel, while the other I/O instructions are passed to userspace. + +When this capability is enabled, KVM_EXIT_S390_TSCH will occur on TEST +SUBCHANNEL intercepts. diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 29363d155cd5..16bd5d169cdb 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -262,6 +262,7 @@ struct kvm_arch{ debug_info_t *dbf; struct kvm_s390_float_interrupt float_int; struct gmap *gmap; + int css_support; }; extern int sie64a(struct kvm_s390_sie_block *, u64 *); diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index 71af87dbb42c..f26ff1e31bdb 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -264,6 +264,7 @@ static const intercept_handler_t intercept_funcs[] = { [0x0C >> 2] = handle_instruction_and_prog, [0x10 >> 2] = handle_noop, [0x14 >> 2] = handle_noop, + [0x18 >> 2] = handle_noop, [0x1C >> 2] = kvm_s390_handle_wait, [0x20 >> 2] = handle_validity, [0x28 >> 2] = handle_stop, diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index b3b4748485ee..9a128357fd15 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -709,6 +709,43 @@ int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code) return 0; } +struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, + u64 cr6, u64 schid) +{ + struct kvm_s390_float_interrupt *fi; + struct kvm_s390_interrupt_info *inti, *iter; + + if ((!schid && !cr6) || (schid && cr6)) + return NULL; + mutex_lock(&kvm->lock); + fi = &kvm->arch.float_int; + spin_lock(&fi->lock); + inti = NULL; + list_for_each_entry(iter, &fi->list, list) { + if (!is_ioint(iter->type)) + continue; + if (cr6 && ((cr6 & iter->io.io_int_word) == 0)) + continue; + if (schid) { + if (((schid & 0x00000000ffff0000) >> 16) != + iter->io.subchannel_id) + continue; + if ((schid & 0x000000000000ffff) != + iter->io.subchannel_nr) + continue; + } + inti = iter; + break; + } + if (inti) + list_del_init(&inti->list); + if (list_empty(&fi->list)) + atomic_set(&fi->active, 0); + spin_unlock(&fi->lock); + mutex_unlock(&kvm->lock); + return inti; +} + int kvm_s390_inject_vm(struct kvm *kvm, struct kvm_s390_interrupt *s390int) { diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 5ff26033825c..5b01f0953900 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -141,6 +141,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_SYNC_REGS: case KVM_CAP_ONE_REG: case KVM_CAP_ENABLE_CAP: + case KVM_CAP_S390_CSS_SUPPORT: r = 1; break; case KVM_CAP_NR_VCPUS: @@ -235,6 +236,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) if (!kvm->arch.gmap) goto out_nogmap; } + + kvm->arch.css_support = 0; + return 0; out_nogmap: debug_unregister(kvm->arch.dbf); @@ -658,6 +662,7 @@ rerun_vcpu: case KVM_EXIT_INTR: case KVM_EXIT_S390_RESET: case KVM_EXIT_S390_UCONTROL: + case KVM_EXIT_S390_TSCH: break; default: BUG(); @@ -818,6 +823,13 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, return -EINVAL; switch (cap->cap) { + case KVM_CAP_S390_CSS_SUPPORT: + if (!vcpu->kvm->arch.css_support) { + vcpu->kvm->arch.css_support = 1; + trace_kvm_s390_enable_css(vcpu->kvm); + } + r = 0; + break; default: r = -EINVAL; break; diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 211b340385a7..3e05deff21b6 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -113,6 +113,8 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_interrupt *s390int); int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code); int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action); +struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, + u64 cr6, u64 schid); /* implemented in priv.c */ int kvm_s390_handle_b2(struct kvm_vcpu *vcpu); diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 8ad776f87856..0ef9894606e5 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -127,13 +127,96 @@ static int handle_skey(struct kvm_vcpu *vcpu) return 0; } +static int handle_tpi(struct kvm_vcpu *vcpu) +{ + u64 addr; + struct kvm_s390_interrupt_info *inti; + int cc; + + addr = kvm_s390_get_base_disp_s(vcpu); + + inti = kvm_s390_get_io_int(vcpu->kvm, vcpu->run->s.regs.crs[6], 0); + if (inti) { + if (addr) { + /* + * Store the two-word I/O interruption code into the + * provided area. + */ + put_guest_u16(vcpu, addr, inti->io.subchannel_id); + put_guest_u16(vcpu, addr + 2, inti->io.subchannel_nr); + put_guest_u32(vcpu, addr + 4, inti->io.io_int_parm); + } else { + /* + * Store the three-word I/O interruption code into + * the appropriate lowcore area. + */ + put_guest_u16(vcpu, 184, inti->io.subchannel_id); + put_guest_u16(vcpu, 186, inti->io.subchannel_nr); + put_guest_u32(vcpu, 188, inti->io.io_int_parm); + put_guest_u32(vcpu, 192, inti->io.io_int_word); + } + cc = 1; + } else + cc = 0; + kfree(inti); + /* Set condition code and we're done. */ + vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); + vcpu->arch.sie_block->gpsw.mask |= (cc & 3ul) << 44; + return 0; +} + +static int handle_tsch(struct kvm_vcpu *vcpu) +{ + struct kvm_s390_interrupt_info *inti; + + inti = kvm_s390_get_io_int(vcpu->kvm, 0, + vcpu->run->s.regs.gprs[1]); + + /* + * Prepare exit to userspace. + * We indicate whether we dequeued a pending I/O interrupt + * so that userspace can re-inject it if the instruction gets + * a program check. While this may re-order the pending I/O + * interrupts, this is no problem since the priority is kept + * intact. + */ + vcpu->run->exit_reason = KVM_EXIT_S390_TSCH; + vcpu->run->s390_tsch.dequeued = !!inti; + if (inti) { + vcpu->run->s390_tsch.subchannel_id = inti->io.subchannel_id; + vcpu->run->s390_tsch.subchannel_nr = inti->io.subchannel_nr; + vcpu->run->s390_tsch.io_int_parm = inti->io.io_int_parm; + vcpu->run->s390_tsch.io_int_word = inti->io.io_int_word; + } + vcpu->run->s390_tsch.ipb = vcpu->arch.sie_block->ipb; + kfree(inti); + return -EREMOTE; +} + static int handle_io_inst(struct kvm_vcpu *vcpu) { VCPU_EVENT(vcpu, 4, "%s", "I/O instruction"); - /* condition code 3 */ - vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); - vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44; - return 0; + + if (vcpu->kvm->arch.css_support) { + /* + * Most I/O instructions will be handled by userspace. + * Exceptions are tpi and the interrupt portion of tsch. + */ + if (vcpu->arch.sie_block->ipa == 0xb236) + return handle_tpi(vcpu); + if (vcpu->arch.sie_block->ipa == 0xb235) + return handle_tsch(vcpu); + /* Handle in userspace. */ + return -EOPNOTSUPP; + } else { + /* + * Set condition code 3 to stop the guest from issueing channel + * I/O instructions. + */ + vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44); + vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44; + return 0; + } } static int handle_stfl(struct kvm_vcpu *vcpu) diff --git a/arch/s390/kvm/trace-s390.h b/arch/s390/kvm/trace-s390.h index 95fbc1ab88dc..13f30f58a2df 100644 --- a/arch/s390/kvm/trace-s390.h +++ b/arch/s390/kvm/trace-s390.h @@ -204,6 +204,26 @@ TRACE_EVENT(kvm_s390_stop_request, ); +/* + * Trace point for enabling channel I/O instruction support. + */ +TRACE_EVENT(kvm_s390_enable_css, + TP_PROTO(void *kvm), + TP_ARGS(kvm), + + TP_STRUCT__entry( + __field(void *, kvm) + ), + + TP_fast_assign( + __entry->kvm = kvm; + ), + + TP_printk("enabling channel I/O support (kvm @ %p)\n", + __entry->kvm) + ); + + #endif /* _TRACE_KVMS390_H */ /* This part must be outside protection */ diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h index 7ef9e759f499..a23f47c884cf 100644 --- a/include/trace/events/kvm.h +++ b/include/trace/events/kvm.h @@ -14,7 +14,7 @@ ERSN(SHUTDOWN), ERSN(FAIL_ENTRY), ERSN(INTR), ERSN(SET_TPR), \ ERSN(TPR_ACCESS), ERSN(S390_SIEIC), ERSN(S390_RESET), ERSN(DCR),\ ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI), ERSN(PAPR_HCALL), \ - ERSN(S390_UCONTROL) + ERSN(S390_UCONTROL), ERSN(S390_TSCH) TRACE_EVENT(kvm_userspace_exit, TP_PROTO(__u32 reason, int errno), diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 80bb3b801116..8bb0bf83afc5 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -168,6 +168,7 @@ struct kvm_pit_config { #define KVM_EXIT_PAPR_HCALL 19 #define KVM_EXIT_S390_UCONTROL 20 #define KVM_EXIT_WATCHDOG 21 +#define KVM_EXIT_S390_TSCH 22 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -285,6 +286,15 @@ struct kvm_run { __u64 ret; __u64 args[9]; } papr_hcall; + /* KVM_EXIT_S390_TSCH */ + struct { + __u16 subchannel_id; + __u16 subchannel_nr; + __u32 io_int_parm; + __u32 io_int_word; + __u32 ipb; + __u8 dequeued; + } s390_tsch; /* Fix the size of the union. */ char padding[256]; }; @@ -645,6 +655,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_IRQFD_RESAMPLE 82 #define KVM_CAP_PPC_BOOKE_WATCHDOG 83 #define KVM_CAP_PPC_HTAB_FD 84 +#define KVM_CAP_S390_CSS_SUPPORT 85 #ifdef KVM_CAP_IRQ_ROUTING From ee04e0cea8b170ae9c8542162091c716de36666b Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Tue, 25 Dec 2012 14:34:06 +0200 Subject: [PATCH 0650/4607] KVM: mmu: remove unused trace event trace_kvm_mmu_delay_free_pages() is no longer used. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/mmutrace.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/x86/kvm/mmutrace.h b/arch/x86/kvm/mmutrace.h index cd6e98333ba3..b8f6172f4174 100644 --- a/arch/x86/kvm/mmutrace.h +++ b/arch/x86/kvm/mmutrace.h @@ -195,12 +195,6 @@ DEFINE_EVENT(kvm_mmu_page_class, kvm_mmu_prepare_zap_page, TP_ARGS(sp) ); -DEFINE_EVENT(kvm_mmu_page_class, kvm_mmu_delay_free_pages, - TP_PROTO(struct kvm_mmu_page *sp), - - TP_ARGS(sp) -); - TRACE_EVENT( mark_mmio_spte, TP_PROTO(u64 *sptep, gfn_t gfn, unsigned access), From 908e7d7999bcce70ac52e7f390a8f5cbc55948de Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Thu, 27 Dec 2012 14:44:58 +0200 Subject: [PATCH 0651/4607] KVM: MMU: simplify folding of dirty bit into accessed_dirty MMU code tries to avoid if()s HW is not able to predict reliably by using bitwise operation to streamline code execution, but in case of a dirty bit folding this gives us nothing since write_fault is checked right before the folding code. Lets just piggyback onto the if() to make code more clear. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/paging_tmpl.h | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index 891eb6d93b8b..a7b24cf59a3c 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -249,16 +249,12 @@ retry_walk: if (!write_fault) protect_clean_gpte(&pte_access, pte); - - /* - * On a write fault, fold the dirty bit into accessed_dirty by shifting it one - * place right. - * - * On a read fault, do nothing. - */ - shift = write_fault >> ilog2(PFERR_WRITE_MASK); - shift *= PT_DIRTY_SHIFT - PT_ACCESSED_SHIFT; - accessed_dirty &= pte >> shift; + else + /* + * On a write fault, fold the dirty bit into accessed_dirty by + * shifting it one place right. + */ + accessed_dirty &= pte >> (PT_DIRTY_SHIFT - PT_ACCESSED_SHIFT); if (unlikely(!accessed_dirty)) { ret = FNAME(update_accessed_dirty_bits)(vcpu, mmu, walker, write_fault); From 83edc87ce8b284a3d60ab8072e55041c76a68277 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:26 -0700 Subject: [PATCH 0652/4607] x86/PCI: Allocate resources on a per-bus basis for hot-adding root buses Previously pcibios_allocate_resources() allocated resources at boot-time for all PCI devices using for_each_pci_dev(). This patch changes pcibios_allocate_resources() so we can specify a bus, so we can do similar allocation when hot-adding a root bus. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 5817cf235d9a..84696ed1f0e9 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -215,16 +215,15 @@ static void __init pcibios_allocate_bridge_resources(struct pci_dev *dev) } } -static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +static void __init pcibios_allocate_bus_resources(struct pci_bus *bus) { - struct pci_bus *bus; + struct pci_bus *child; /* Depth-First Search on bus tree */ - list_for_each_entry(bus, bus_list, node) { - if (bus->self) - pcibios_allocate_bridge_resources(bus->self); - pcibios_allocate_bus_resources(&bus->children); - } + if (bus->self) + pcibios_allocate_bridge_resources(bus->self); + list_for_each_entry(child, &bus->children, node) + pcibios_allocate_bus_resources(child); } struct pci_check_idx_range { @@ -285,12 +284,18 @@ static void __init pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) } } -static void __init pcibios_allocate_resources(int pass) +static void __init pcibios_allocate_resources(struct pci_bus *bus, int pass) { - struct pci_dev *dev = NULL; + struct pci_dev *dev; + struct pci_bus *child; - for_each_pci_dev(dev) + list_for_each_entry(dev, &bus->devices, bus_list) { pcibios_allocate_dev_resources(dev, pass); + + child = dev->subordinate; + if (child) + pcibios_allocate_resources(child, pass); + } } static int __init pcibios_assign_resources(void) @@ -323,10 +328,17 @@ static int __init pcibios_assign_resources(void) void __init pcibios_resource_survey(void) { + struct pci_bus *bus; + DBG("PCI: Allocating resources\n"); - pcibios_allocate_bus_resources(&pci_root_buses); - pcibios_allocate_resources(0); - pcibios_allocate_resources(1); + + list_for_each_entry(bus, &pci_root_buses, node) + pcibios_allocate_bus_resources(bus); + + list_for_each_entry(bus, &pci_root_buses, node) + pcibios_allocate_resources(bus, 0); + list_for_each_entry(bus, &pci_root_buses, node) + pcibios_allocate_resources(bus, 1); e820_reserve_resources_late(); /* From dc2f56fa8400677ef4852d5128f03b795cf57e7b Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:27 -0700 Subject: [PATCH 0653/4607] x86/PCI: Factor out pcibios_allocate_dev_rom_resource() Factor pcibios_allocate_rom_resources() and pcibios_allocate_dev_rom_resource() out of pcibios_assign_resources(). This will allow us to allocate ROM resources for hot-added root buses. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 52 ++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 84696ed1f0e9..42dd75552351 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -298,27 +298,45 @@ static void __init pcibios_allocate_resources(struct pci_bus *bus, int pass) } } -static int __init pcibios_assign_resources(void) +static void __init pcibios_allocate_dev_rom_resource(struct pci_dev *dev) { - struct pci_dev *dev = NULL; struct resource *r; - if (!(pci_probe & PCI_ASSIGN_ROMS)) { - /* - * Try to use BIOS settings for ROMs, otherwise let - * pci_assign_unassigned_resources() allocate the new - * addresses. - */ - for_each_pci_dev(dev) { - r = &dev->resource[PCI_ROM_RESOURCE]; - if (!r->flags || !r->start) - continue; - if (pci_claim_resource(dev, PCI_ROM_RESOURCE) < 0) { - r->end -= r->start; - r->start = 0; - } - } + /* + * Try to use BIOS settings for ROMs, otherwise let + * pci_assign_unassigned_resources() allocate the new + * addresses. + */ + r = &dev->resource[PCI_ROM_RESOURCE]; + if (!r->flags || !r->start) + return; + + if (pci_claim_resource(dev, PCI_ROM_RESOURCE) < 0) { + r->end -= r->start; + r->start = 0; } +} +static void __init pcibios_allocate_rom_resources(struct pci_bus *bus) +{ + struct pci_dev *dev; + struct pci_bus *child; + + list_for_each_entry(dev, &bus->devices, bus_list) { + pcibios_allocate_dev_rom_resource(dev); + + child = dev->subordinate; + if (child) + pcibios_allocate_rom_resources(child); + } +} + +static int __init pcibios_assign_resources(void) +{ + struct pci_bus *bus; + + if (!(pci_probe & PCI_ASSIGN_ROMS)) + list_for_each_entry(bus, &pci_root_buses, node) + pcibios_allocate_rom_resources(bus); pci_assign_unassigned_resources(); pcibios_fw_addr_list_del(); From cf02820041668b14cbfa0fbd2bab45ac79bd6174 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 19 Dec 2012 16:07:25 +0000 Subject: [PATCH 0654/4607] UAPI: (Scripted) Disintegrate include/scsi/fc Signed-off-by: David Howells Acked-by: Arnd Bergmann Acked-by: Thomas Gleixner Acked-by: Michael Kerrisk Acked-by: Paul E. McKenney Acked-by: Dave Jones --- include/scsi/fc/Kbuild | 4 ---- include/uapi/scsi/fc/Kbuild | 4 ++++ include/{ => uapi}/scsi/fc/fc_els.h | 0 include/{ => uapi}/scsi/fc/fc_fs.h | 0 include/{ => uapi}/scsi/fc/fc_gs.h | 0 include/{ => uapi}/scsi/fc/fc_ns.h | 0 6 files changed, 4 insertions(+), 4 deletions(-) rename include/{ => uapi}/scsi/fc/fc_els.h (100%) rename include/{ => uapi}/scsi/fc/fc_fs.h (100%) rename include/{ => uapi}/scsi/fc/fc_gs.h (100%) rename include/{ => uapi}/scsi/fc/fc_ns.h (100%) diff --git a/include/scsi/fc/Kbuild b/include/scsi/fc/Kbuild index 56603813c6cd..e69de29bb2d1 100644 --- a/include/scsi/fc/Kbuild +++ b/include/scsi/fc/Kbuild @@ -1,4 +0,0 @@ -header-y += fc_els.h -header-y += fc_fs.h -header-y += fc_gs.h -header-y += fc_ns.h diff --git a/include/uapi/scsi/fc/Kbuild b/include/uapi/scsi/fc/Kbuild index aafaa5aa54d4..5ead9fac265c 100644 --- a/include/uapi/scsi/fc/Kbuild +++ b/include/uapi/scsi/fc/Kbuild @@ -1 +1,5 @@ # UAPI Header export list +header-y += fc_els.h +header-y += fc_fs.h +header-y += fc_gs.h +header-y += fc_ns.h diff --git a/include/scsi/fc/fc_els.h b/include/uapi/scsi/fc/fc_els.h similarity index 100% rename from include/scsi/fc/fc_els.h rename to include/uapi/scsi/fc/fc_els.h diff --git a/include/scsi/fc/fc_fs.h b/include/uapi/scsi/fc/fc_fs.h similarity index 100% rename from include/scsi/fc/fc_fs.h rename to include/uapi/scsi/fc/fc_fs.h diff --git a/include/scsi/fc/fc_gs.h b/include/uapi/scsi/fc/fc_gs.h similarity index 100% rename from include/scsi/fc/fc_gs.h rename to include/uapi/scsi/fc/fc_gs.h diff --git a/include/scsi/fc/fc_ns.h b/include/uapi/scsi/fc/fc_ns.h similarity index 100% rename from include/scsi/fc/fc_ns.h rename to include/uapi/scsi/fc/fc_ns.h From 745216025de0354eea23493d994e3fc0ab7369fc Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:28 -0700 Subject: [PATCH 0655/4607] x86/PCI: Don't track firmware-assigned BAR values for hot-added devices The BIOS doesn't assign BAR values for hot-added devices, so don't bother saving the original values when we enumerate these devices. [bhelgaas: changelog, return constant 0 in pcibios_retrieve_fw_addr] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 42dd75552351..1bd672ab0084 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -51,6 +51,7 @@ struct pcibios_fwaddrmap { static LIST_HEAD(pcibios_fwaddrmappings); static DEFINE_SPINLOCK(pcibios_fwaddrmap_lock); +static bool pcibios_fw_addr_done; /* Must be called with 'pcibios_fwaddrmap_lock' lock held. */ static struct pcibios_fwaddrmap *pcibios_fwaddrmap_lookup(struct pci_dev *dev) @@ -72,6 +73,9 @@ pcibios_save_fw_addr(struct pci_dev *dev, int idx, resource_size_t fw_addr) unsigned long flags; struct pcibios_fwaddrmap *map; + if (pcibios_fw_addr_done) + return; + spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); map = pcibios_fwaddrmap_lookup(dev); if (!map) { @@ -97,6 +101,9 @@ resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) struct pcibios_fwaddrmap *map; resource_size_t fw_addr = 0; + if (pcibios_fw_addr_done) + return 0; + spin_lock_irqsave(&pcibios_fwaddrmap_lock, flags); map = pcibios_fwaddrmap_lookup(dev); if (map) @@ -106,7 +113,7 @@ resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx) return fw_addr; } -static void pcibios_fw_addr_list_del(void) +static void __init pcibios_fw_addr_list_del(void) { unsigned long flags; struct pcibios_fwaddrmap *entry, *next; @@ -118,6 +125,7 @@ static void pcibios_fw_addr_list_del(void) kfree(entry); } spin_unlock_irqrestore(&pcibios_fwaddrmap_lock, flags); + pcibios_fw_addr_done = true; } static int From b95168e010a405add13aa010d7c45b55dc4026c7 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:29 -0700 Subject: [PATCH 0656/4607] x86/PCI: Keep resource allocation functions after boot The PCI resource allocation functions will be used for hot-added devices, so keep them around. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 1bd672ab0084..8656ea88cf66 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -201,7 +201,7 @@ EXPORT_SYMBOL(pcibios_align_resource); * as well. */ -static void __init pcibios_allocate_bridge_resources(struct pci_dev *dev) +static void pcibios_allocate_bridge_resources(struct pci_dev *dev) { int idx; struct resource *r; @@ -223,7 +223,7 @@ static void __init pcibios_allocate_bridge_resources(struct pci_dev *dev) } } -static void __init pcibios_allocate_bus_resources(struct pci_bus *bus) +static void pcibios_allocate_bus_resources(struct pci_bus *bus) { struct pci_bus *child; @@ -239,7 +239,7 @@ struct pci_check_idx_range { int end; }; -static void __init pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) +static void pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) { int idx, disabled, i; u16 command; @@ -292,7 +292,7 @@ static void __init pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) } } -static void __init pcibios_allocate_resources(struct pci_bus *bus, int pass) +static void pcibios_allocate_resources(struct pci_bus *bus, int pass) { struct pci_dev *dev; struct pci_bus *child; @@ -306,7 +306,7 @@ static void __init pcibios_allocate_resources(struct pci_bus *bus, int pass) } } -static void __init pcibios_allocate_dev_rom_resource(struct pci_dev *dev) +static void pcibios_allocate_dev_rom_resource(struct pci_dev *dev) { struct resource *r; @@ -324,7 +324,7 @@ static void __init pcibios_allocate_dev_rom_resource(struct pci_dev *dev) r->start = 0; } } -static void __init pcibios_allocate_rom_resources(struct pci_bus *bus) +static void pcibios_allocate_rom_resources(struct pci_bus *bus) { struct pci_dev *dev; struct pci_bus *child; From 3c449ed0075994b3f3371f8254560428ba787efc Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:31 -0700 Subject: [PATCH 0657/4607] PCI/ACPI: Reserve firmware-allocated resources for hot-added root buses Firmware may have assigned PCI BARs for hot-added devices, so reserve those resources before trying to allocate more. [bhelgaas: move empty weak definition here] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_root.c | 4 +++- drivers/pci/bus.c | 2 ++ include/linux/pci.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 7928d4dc7056..dcbe9660e756 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -650,8 +650,10 @@ static int acpi_pci_root_start(struct acpi_device *device) struct acpi_pci_root *root = acpi_driver_data(device); struct acpi_pci_driver *driver; - if (system_state != SYSTEM_BOOTING) + if (system_state != SYSTEM_BOOTING) { + pcibios_resource_survey_bus(root->bus); pci_assign_unassigned_bus_resources(root->bus); + } mutex_lock(&acpi_pci_root_lock); list_for_each_entry(driver, &acpi_pci_drivers, node) diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index ad6a8b635692..847f3ca47bb8 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -158,6 +158,8 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, return ret; } +void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { } + /** * pci_bus_add_device - add a single device * @dev: device to add diff --git a/include/linux/pci.h b/include/linux/pci.h index 15472d691ee6..907b455ab603 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -674,6 +674,7 @@ extern struct list_head pci_root_buses; /* list of all known PCI buses */ /* Some device drivers need know if pci is initiated */ extern int no_pci_devices(void); +void pcibios_resource_survey_bus(struct pci_bus *bus); void pcibios_fixup_bus(struct pci_bus *); int __must_check pcibios_enable_device(struct pci_dev *, int mask); /* Architecture specific versions may override this (weak) */ From b3e65e1f9185a2eb034defe4270ba178ba70b9a9 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 3 Nov 2012 21:39:30 -0700 Subject: [PATCH 0658/4607] x86/PCI: Implement pcibios_resource_survey_bus() During testing remove/rescan root bus 00, found [ 338.142574] bus: 'pci': really_probe: probing driver ata_piix with device 0000:00:01.1 [ 338.146788] ata_piix 0000:00:01.1: device not available (can't reserve [io 0x01f0-0x01f7]) [ 338.150565] ata_piix: probe of 0000:00:01.1 failed with error -22 because that fixed resource is not claimed. For bootint path it is claimed in from arch/x86/pci/i386.c::pcibios_allocate_resources() Claim those resources, so on the remove/rescan will still use old resources. It is some kind honoring FW setting in the registers during hot add. esp root-bus hot add is through acpi, BIOS has chance to set some registers before handing over. [bhelgaas: move weak definition to patch that uses it] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/x86/pci/i386.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 8656ea88cf66..94919e307f8e 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -352,6 +352,19 @@ static int __init pcibios_assign_resources(void) return 0; } +void pcibios_resource_survey_bus(struct pci_bus *bus) +{ + dev_printk(KERN_DEBUG, &bus->dev, "Allocating resources\n"); + + pcibios_allocate_bus_resources(bus); + + pcibios_allocate_resources(bus, 0); + pcibios_allocate_resources(bus, 1); + + if (!(pci_probe & PCI_ASSIGN_ROMS)) + pcibios_allocate_rom_resources(bus); +} + void __init pcibios_resource_survey(void) { struct pci_bus *bus; From a9ddb575d6d6c58c39e8c44a22b84445fedb0521 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 16 Oct 2012 09:49:17 +0530 Subject: [PATCH 0659/4607] dmaengine: dw_dmac: Enhance device tree support dw_dmac driver already supports device tree but it used to have its platform data passed the non-DT way. This patch does following changes: - pass platform data via DT, non-DT way still takes precedence if both are used. - create generic filter routine - Earlier slave information was made available by slave specific filter routines in chan->private field. Now, this information would be passed from within dmac DT node. Slave drivers would now be required to pass bus_id (a string) as parameter to this generic filter(), which would be compared against the slave data passed from DT, by the generic filter routine. - Update binding document Signed-off-by: Viresh Kumar Reviewed-by: Andy Shevchenko [Fixed __devinit usage] Signed-off-by: Vinod Koul --- .../devicetree/bindings/dma/snps-dma.txt | 44 ++++++ drivers/dma/dw_dmac.c | 134 ++++++++++++++++++ drivers/dma/dw_dmac_regs.h | 4 + include/linux/dw_dmac.h | 43 +++--- 4 files changed, 208 insertions(+), 17 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/snps-dma.txt b/Documentation/devicetree/bindings/dma/snps-dma.txt index c0d85dbcada5..5bb3dfb6f1d8 100644 --- a/Documentation/devicetree/bindings/dma/snps-dma.txt +++ b/Documentation/devicetree/bindings/dma/snps-dma.txt @@ -6,6 +6,26 @@ Required properties: - interrupt-parent: Should be the phandle for the interrupt controller that services interrupts for this device - interrupt: Should contain the DMAC interrupt number +- nr_channels: Number of channels supported by hardware +- is_private: The device channels should be marked as private and not for by the + general purpose DMA channel allocator. False if not passed. +- chan_allocation_order: order of allocation of channel, 0 (default): ascending, + 1: descending +- chan_priority: priority of channels. 0 (default): increase from chan 0->n, 1: + increase from chan n->0 +- block_size: Maximum block size supported by the controller +- nr_masters: Number of AHB masters supported by the controller +- data_width: Maximum data width supported by hardware per AHB master + (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) +- slave_info: + - bus_id: name of this device channel, not just a device name since + devices may have more than one channel e.g. "foo_tx". For using the + dw_generic_filter(), slave drivers must pass exactly this string as + param to filter function. + - cfg_hi: Platform-specific initializer for the CFG_HI register + - cfg_lo: Platform-specific initializer for the CFG_LO register + - src_master: src master for transfers on allocated channel. + - dst_master: dest master for transfers on allocated channel. Example: @@ -14,4 +34,28 @@ Example: reg = <0xfc000000 0x1000>; interrupt-parent = <&vic1>; interrupts = <12>; + + nr_channels = <8>; + chan_allocation_order = <1>; + chan_priority = <1>; + block_size = <0xfff>; + nr_masters = <2>; + data_width = <3 3 0 0>; + + slave_info { + uart0-tx { + bus_id = "uart0-tx"; + cfg_hi = <0x4000>; /* 0x8 << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <1>; + }; + spi0-tx { + bus_id = "spi0-tx"; + cfg_hi = <0x2000>; /* 0x4 << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <0>; + }; + }; }; diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 8f0b111af4de..27b8e1d1845e 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1179,6 +1179,50 @@ static void dwc_free_chan_resources(struct dma_chan *chan) dev_vdbg(chan2dev(chan), "%s: done\n", __func__); } +bool dw_dma_generic_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma *dw = to_dw_dma(chan->device); + static struct dw_dma *last_dw; + static char *last_bus_id; + int i = -1; + + /* + * dmaengine framework calls this routine for all channels of all dma + * controller, until true is returned. If 'param' bus_id is not + * registered with a dma controller (dw), then there is no need of + * running below function for all channels of dw. + * + * This block of code does this by saving the parameters of last + * failure. If dw and param are same, i.e. trying on same dw with + * different channel, return false. + */ + if ((last_dw == dw) && (last_bus_id == param)) + return false; + /* + * Return true: + * - If dw_dma's platform data is not filled with slave info, then all + * dma controllers are fine for transfer. + * - Or if param is NULL + */ + if (!dw->sd || !param) + return true; + + while (++i < dw->sd_count) { + if (!strcmp(dw->sd[i].bus_id, param)) { + chan->private = &dw->sd[i]; + last_dw = NULL; + last_bus_id = NULL; + + return true; + } + } + + last_dw = dw; + last_bus_id = param; + return false; +} +EXPORT_SYMBOL(dw_dma_generic_filter); + /* --------------------- Cyclic DMA API extensions -------------------- */ /** @@ -1462,6 +1506,91 @@ static void dw_dma_off(struct dw_dma *dw) dw->chan[i].initialized = false; } +#ifdef CONFIG_OF +static struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + struct device_node *sn, *cn, *np = pdev->dev.of_node; + struct dw_dma_platform_data *pdata; + struct dw_dma_slave *sd; + u32 tmp, arr[4]; + + if (!np) { + dev_err(&pdev->dev, "Missing DT data\n"); + return NULL; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_u32(np, "nr_channels", &pdata->nr_channels)) + return NULL; + + if (of_property_read_bool(np, "is_private")) + pdata->is_private = true; + + if (!of_property_read_u32(np, "chan_allocation_order", &tmp)) + pdata->chan_allocation_order = (unsigned char)tmp; + + if (!of_property_read_u32(np, "chan_priority", &tmp)) + pdata->chan_priority = tmp; + + if (!of_property_read_u32(np, "block_size", &tmp)) + pdata->block_size = tmp; + + if (!of_property_read_u32(np, "nr_masters", &tmp)) { + if (tmp > 4) + return NULL; + + pdata->nr_masters = tmp; + } + + if (!of_property_read_u32_array(np, "data_width", arr, + pdata->nr_masters)) + for (tmp = 0; tmp < pdata->nr_masters; tmp++) + pdata->data_width[tmp] = arr[tmp]; + + /* parse slave data */ + sn = of_find_node_by_name(np, "slave_info"); + if (!sn) + return pdata; + + /* calculate number of slaves */ + tmp = of_get_child_count(sn); + if (!tmp) + return NULL; + + sd = devm_kzalloc(&pdev->dev, sizeof(*sd) * tmp, GFP_KERNEL); + if (!sd) + return NULL; + + pdata->sd = sd; + pdata->sd_count = tmp; + + for_each_child_of_node(sn, cn) { + sd->dma_dev = &pdev->dev; + of_property_read_string(cn, "bus_id", &sd->bus_id); + of_property_read_u32(cn, "cfg_hi", &sd->cfg_hi); + of_property_read_u32(cn, "cfg_lo", &sd->cfg_lo); + if (!of_property_read_u32(cn, "src_master", &tmp)) + sd->src_master = tmp; + + if (!of_property_read_u32(cn, "dst_master", &tmp)) + sd->dst_master = tmp; + sd++; + } + + return pdata; +} +#else +static inline struct dw_dma_platform_data * +dw_dma_parse_dt(struct platform_device *pdev) +{ + return NULL; +} +#endif + static int dw_probe(struct platform_device *pdev) { struct dw_dma_platform_data *pdata; @@ -1478,6 +1607,9 @@ static int dw_probe(struct platform_device *pdev) int i; pdata = dev_get_platdata(&pdev->dev); + if (!pdata) + pdata = dw_dma_parse_dt(pdev); + if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) return -EINVAL; @@ -1512,6 +1644,8 @@ static int dw_probe(struct platform_device *pdev) clk_prepare_enable(dw->clk); dw->regs = regs; + dw->sd = pdata->sd; + dw->sd_count = pdata->sd_count; /* get hardware configuration parameters */ if (autocfg) { diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 88965597b7d0..88a069f66b89 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -239,6 +239,10 @@ struct dw_dma { struct tasklet_struct tasklet; struct clk *clk; + /* slave information */ + struct dw_dma_slave *sd; + unsigned int sd_count; + u8 all_chan_mask; /* hardware configuration */ diff --git a/include/linux/dw_dmac.h b/include/linux/dw_dmac.h index 62a6190dee22..41766de66e33 100644 --- a/include/linux/dw_dmac.h +++ b/include/linux/dw_dmac.h @@ -14,6 +14,26 @@ #include +/** + * struct dw_dma_slave - Controller-specific information about a slave + * + * @dma_dev: required DMA master device. Depricated. + * @bus_id: name of this device channel, not just a device name since + * devices may have more than one channel e.g. "foo_tx" + * @cfg_hi: Platform-specific initializer for the CFG_HI register + * @cfg_lo: Platform-specific initializer for the CFG_LO register + * @src_master: src master for transfers on allocated channel. + * @dst_master: dest master for transfers on allocated channel. + */ +struct dw_dma_slave { + struct device *dma_dev; + const char *bus_id; + u32 cfg_hi; + u32 cfg_lo; + u8 src_master; + u8 dst_master; +}; + /** * struct dw_dma_platform_data - Controller configuration parameters * @nr_channels: Number of channels supported by hardware (max 8) @@ -25,6 +45,8 @@ * @nr_masters: Number of AHB masters supported by the controller * @data_width: Maximum data width supported by hardware per AHB master * (0 - 8bits, 1 - 16bits, ..., 5 - 256bits) + * @sd: slave specific data. Used for configuring channels + * @sd_count: count of slave data structures passed. */ struct dw_dma_platform_data { unsigned int nr_channels; @@ -38,6 +60,9 @@ struct dw_dma_platform_data { unsigned short block_size; unsigned char nr_masters; unsigned char data_width[4]; + + struct dw_dma_slave *sd; + unsigned int sd_count; }; /* bursts size */ @@ -52,23 +77,6 @@ enum dw_dma_msize { DW_DMA_MSIZE_256, }; -/** - * struct dw_dma_slave - Controller-specific information about a slave - * - * @dma_dev: required DMA master device - * @cfg_hi: Platform-specific initializer for the CFG_HI register - * @cfg_lo: Platform-specific initializer for the CFG_LO register - * @src_master: src master for transfers on allocated channel. - * @dst_master: dest master for transfers on allocated channel. - */ -struct dw_dma_slave { - struct device *dma_dev; - u32 cfg_hi; - u32 cfg_lo; - u8 src_master; - u8 dst_master; -}; - /* Platform-configurable bits in CFG_HI */ #define DWC_CFGH_FCMODE (1 << 0) #define DWC_CFGH_FIFO_MODE (1 << 1) @@ -106,5 +114,6 @@ void dw_dma_cyclic_stop(struct dma_chan *chan); dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan); dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan); +bool dw_dma_generic_filter(struct dma_chan *chan, void *param); #endif /* DW_DMAC_H */ From f9965aa20706860077cfa093d04a6351c0c1e940 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 16 Oct 2012 09:49:18 +0530 Subject: [PATCH 0660/4607] ARM: SPEAr13xx: Pass DW DMAC platform data from DT This patch adds dw_dmac's platform data to DT node. It also creates slave info node for SPEAr13xx, for the devices which were using dw_dmac. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- arch/arm/boot/dts/spear1340.dtsi | 19 +++++++ arch/arm/boot/dts/spear13xx.dtsi | 38 ++++++++++++++ arch/arm/mach-spear13xx/include/mach/spear.h | 2 - arch/arm/mach-spear13xx/spear1310.c | 5 +- arch/arm/mach-spear13xx/spear1340.c | 33 ++++-------- arch/arm/mach-spear13xx/spear13xx.c | 55 ++------------------ 6 files changed, 72 insertions(+), 80 deletions(-) diff --git a/arch/arm/boot/dts/spear1340.dtsi b/arch/arm/boot/dts/spear1340.dtsi index 34da11aa6795..b2d41b7502bd 100644 --- a/arch/arm/boot/dts/spear1340.dtsi +++ b/arch/arm/boot/dts/spear1340.dtsi @@ -88,6 +88,25 @@ status = "disabled"; }; + dma@ea800000 { + slave_info { + uart1_tx { + bus_id = "uart1_tx"; + cfg_hi = <0x6000>; /* 0xC << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <1>; + }; + uart1_tx { + bus_id = "uart1_tx"; + cfg_hi = <0x680>; /* 0xD << 7 */ + cfg_lo = <0>; + src_master = <1>; + dst_master = <0>; + }; + }; + }; + spi1: spi@5d400000 { compatible = "arm,pl022", "arm,primecell"; reg = <0x5d400000 0x1000>; diff --git a/arch/arm/boot/dts/spear13xx.dtsi b/arch/arm/boot/dts/spear13xx.dtsi index b4ca60f4eb42..585f64157ea4 100644 --- a/arch/arm/boot/dts/spear13xx.dtsi +++ b/arch/arm/boot/dts/spear13xx.dtsi @@ -105,6 +105,37 @@ reg = <0xea800000 0x1000>; interrupts = <0 19 0x4>; status = "disabled"; + + nr_channels = <8>; + chan_allocation_order = <1>; + chan_priority = <1>; + block_size = <0xfff>; + nr_masters = <2>; + data_width = <3 3 0 0>; + + slave_info { + ssp0_tx { + bus_id = "ssp0_tx"; + cfg_hi = <0x2000>; /* 0x4 << 11 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <0>; + }; + ssp0_rx { + bus_id = "ssp0_rx"; + cfg_hi = <0x280>; /* 0x5 << 7 */ + cfg_lo = <0>; + src_master = <0>; + dst_master = <0>; + }; + cf { + bus_id = "cf"; + cfg_hi = <0>; + cfg_lo = <0>; + src_master = <0>; + dst_master = <0>; + }; + }; }; dma@eb000000 { @@ -112,6 +143,13 @@ reg = <0xeb000000 0x1000>; interrupts = <0 59 0x4>; status = "disabled"; + + nr_channels = <8>; + chan_allocation_order = <1>; + chan_priority = <1>; + block_size = <0xfff>; + nr_masters = <2>; + data_width = <3 3 0 0>; }; fsmc: flash@b0000000 { diff --git a/arch/arm/mach-spear13xx/include/mach/spear.h b/arch/arm/mach-spear13xx/include/mach/spear.h index 7cfa6818865a..972a151df34c 100644 --- a/arch/arm/mach-spear13xx/include/mach/spear.h +++ b/arch/arm/mach-spear13xx/include/mach/spear.h @@ -43,8 +43,6 @@ #define VA_L2CC_BASE IOMEM(UL(0xFB000000)) /* others */ -#define DMAC0_BASE UL(0xEA800000) -#define DMAC1_BASE UL(0xEB000000) #define MCIF_CF_BASE UL(0xB2800000) /* Debug uart for linux, will be used for debug and uncompress messages */ diff --git a/arch/arm/mach-spear13xx/spear1310.c b/arch/arm/mach-spear13xx/spear1310.c index 02f4724bb0d4..ec72c47c0e08 100644 --- a/arch/arm/mach-spear13xx/spear1310.c +++ b/arch/arm/mach-spear13xx/spear1310.c @@ -36,7 +36,7 @@ static struct arasan_cf_pdata cf_pdata = { .cf_if_clk = CF_IF_CLK_166M, .quirk = CF_BROKEN_UDMA, - .dma_priv = &cf_dma_priv, + .dma_priv = "cf", }; /* ssp device registration */ @@ -47,10 +47,7 @@ static struct pl022_ssp_controller ssp1_plat_data = { /* Add SPEAr1310 auxdata to pass platform data */ static struct of_dev_auxdata spear1310_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_pdata), - OF_DEV_AUXDATA("snps,dma-spear1340", DMAC0_BASE, NULL, &dmac_plat_data), - OF_DEV_AUXDATA("snps,dma-spear1340", DMAC1_BASE, NULL, &dmac_plat_data), OF_DEV_AUXDATA("arm,pl022", SSP_BASE, NULL, &pl022_plat_data), - OF_DEV_AUXDATA("arm,pl022", SPEAR1310_SSP1_BASE, NULL, &ssp1_plat_data), {} }; diff --git a/arch/arm/mach-spear13xx/spear1340.c b/arch/arm/mach-spear13xx/spear1340.c index 081014fb314a..69c8f72a9ca2 100644 --- a/arch/arm/mach-spear13xx/spear1340.c +++ b/arch/arm/mach-spear13xx/spear1340.c @@ -18,9 +18,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -78,26 +78,16 @@ (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ SPEAR1340_MIPHY_PLL_RATIO_TOP(25)) -static struct dw_dma_slave uart1_dma_param[] = { - { - /* Tx */ - .cfg_hi = DWC_CFGH_DST_PER(SPEAR1340_DMA_REQ_UART1_TX), - .cfg_lo = 0, - .src_master = DMA_MASTER_MEMORY, - .dst_master = SPEAR1340_DMA_MASTER_UART1, - }, { - /* Rx */ - .cfg_hi = DWC_CFGH_SRC_PER(SPEAR1340_DMA_REQ_UART1_RX), - .cfg_lo = 0, - .src_master = SPEAR1340_DMA_MASTER_UART1, - .dst_master = DMA_MASTER_MEMORY, - } +static struct amba_pl011_data uart1_data = { + .dma_filter = dw_dma_generic_filter, + .dma_tx_param = "uart1_tx", + .dma_rx_param = "uart1_rx", }; -static struct amba_pl011_data uart1_data = { - .dma_filter = dw_dma_filter, - .dma_tx_param = &uart1_dma_param[0], - .dma_rx_param = &uart1_dma_param[1], +static struct arasan_cf_pdata cf_pdata = { + .cf_if_clk = CF_IF_CLK_166M, + .quirk = CF_BROKEN_UDMA, + .dma_priv = "cf", }; /* SATA device registration */ @@ -158,11 +148,8 @@ static struct ahci_platform_data sata_pdata = { /* Add SPEAr1340 auxdata to pass platform data */ static struct of_dev_auxdata spear1340_auxdata_lookup[] __initdata = { - OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_dma_priv), - OF_DEV_AUXDATA("snps,dma-spear1340", DMAC0_BASE, NULL, &dmac_plat_data), - OF_DEV_AUXDATA("snps,dma-spear1340", DMAC1_BASE, NULL, &dmac_plat_data), + OF_DEV_AUXDATA("arasan,cf-spear1340", MCIF_CF_BASE, NULL, &cf_pdata), OF_DEV_AUXDATA("arm,pl022", SSP_BASE, NULL, &pl022_plat_data), - OF_DEV_AUXDATA("snps,spear-ahci", SPEAR1340_SATA_BASE, NULL, &sata_pdata), OF_DEV_AUXDATA("arm,pl011", SPEAR1340_UART1_BASE, NULL, &uart1_data), diff --git a/arch/arm/mach-spear13xx/spear13xx.c b/arch/arm/mach-spear13xx/spear13xx.c index c4af775a8451..b074db8b109c 100644 --- a/arch/arm/mach-spear13xx/spear13xx.c +++ b/arch/arm/mach-spear13xx/spear13xx.c @@ -22,63 +22,16 @@ #include #include #include -#include #include #include -/* common dw_dma filter routine to be used by peripherals */ -bool dw_dma_filter(struct dma_chan *chan, void *slave) -{ - struct dw_dma_slave *dws = (struct dw_dma_slave *)slave; - - if (chan->device->dev == dws->dma_dev) { - chan->private = slave; - return true; - } else { - return false; - } -} - /* ssp device registration */ -static struct dw_dma_slave ssp_dma_param[] = { - { - /* Tx */ - .cfg_hi = DWC_CFGH_DST_PER(DMA_REQ_SSP0_TX), - .cfg_lo = 0, - .src_master = DMA_MASTER_MEMORY, - .dst_master = DMA_MASTER_SSP0, - }, { - /* Rx */ - .cfg_hi = DWC_CFGH_SRC_PER(DMA_REQ_SSP0_RX), - .cfg_lo = 0, - .src_master = DMA_MASTER_SSP0, - .dst_master = DMA_MASTER_MEMORY, - } -}; - struct pl022_ssp_controller pl022_plat_data = { .enable_dma = 1, - .dma_filter = dw_dma_filter, - .dma_rx_param = &ssp_dma_param[1], - .dma_tx_param = &ssp_dma_param[0], -}; - -/* CF device registration */ -struct dw_dma_slave cf_dma_priv = { - .cfg_hi = 0, - .cfg_lo = 0, - .src_master = 0, - .dst_master = 0, -}; - -/* dmac device registeration */ -struct dw_dma_platform_data dmac_plat_data = { - .nr_channels = 8, - .chan_allocation_order = CHAN_ALLOCATION_DESCENDING, - .chan_priority = CHAN_PRIORITY_DESCENDING, - .block_size = 4095U, - .nr_masters = 2, - .data_width = { 3, 3, 0, 0 }, + .dma_filter = dw_dma_generic_filter, + .dma_rx_param = "ssp0_rx", + .dma_tx_param = "ssp0_tx", + .num_chipselect = 3, }; void __init spear13xx_l2x0_init(void) From b73d6c0ae4d74588a2653835a2452944722cc64b Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 18 Oct 2012 17:34:07 +0300 Subject: [PATCH 0661/4607] dmaengine: dw_dmac: remove CLK dependency This driver could be used on different platforms. Thus, the HAVE_CLK dependency is dropped away. Signed-off-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Reviewed-by: Felipe Balbi Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 93ea87e59fdf..6761cc8a59d5 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -83,7 +83,6 @@ config INTEL_IOP_ADMA config DW_DMAC tristate "Synopsys DesignWare AHB DMA support" - depends on HAVE_CLK select DMA_ENGINE default y if CPU_AT32AP7000 help From b801479bb6f5b66f5a35feaa72451ac5ac152ef5 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 18 Oct 2012 17:34:08 +0300 Subject: [PATCH 0662/4607] dmaengine: dw_dmac: amend description and indentation The driver will be used as a core part for various implementations of the DesignWare DMA device. The patch adjusts description on the top and corrects paragraph indentation in few places across the code. Signed-off-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 27b8e1d1845e..62bd85c8a517 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1,6 +1,5 @@ /* - * Driver for the Synopsys DesignWare DMA Controller (aka DMACA on - * AVR32 systems.) + * Core driver for the Synopsys DesignWare DMA Controller * * Copyright (C) 2007-2008 Atmel Corporation * Copyright (C) 2010-2011 ST Microelectronics @@ -9,6 +8,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + #include #include #include @@ -222,7 +222,6 @@ static inline void dwc_dump_chan_regs(struct dw_dma_chan *dwc) channel_readl(dwc, CTL_LO)); } - static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) { channel_clear_bit(dw, CH_EN, dwc->mask); @@ -1813,6 +1812,7 @@ static int dw_resume_noirq(struct device *dev) clk_prepare_enable(dw->clk); dma_writel(dw, CFG, DW_CFG_DMA_EN); + return 0; } From 21d43f49cb5e132e74bde9e34ac0760f79ab85a9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:09 +0300 Subject: [PATCH 0663/4607] dw_dmac: change dev_printk() to corresponding macros Change printk(KERN_INFO ..., dev_name(...), ...) to dev_info() as well. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 62bd85c8a517..155d3f108ab4 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -456,9 +456,8 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) static inline void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) { - dev_printk(KERN_CRIT, chan2dev(&dwc->chan), - " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", - lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); + dev_crit(chan2dev(&dwc->chan), " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", + lli->sar, lli->dar, lli->llp, lli->ctlhi, lli->ctllo); } static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) @@ -492,10 +491,8 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) * controller flagged an error instead of scribbling over * random memory locations. */ - dev_printk(KERN_CRIT, chan2dev(&dwc->chan), - "Bad descriptor submitted for DMA!\n"); - dev_printk(KERN_CRIT, chan2dev(&dwc->chan), - " cookie: %d\n", bad_desc->txd.cookie); + dev_crit(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n"); + dev_crit(chan2dev(&dwc->chan), " cookie: %d\n", bad_desc->txd.cookie); dwc_dump_lli(dwc, &bad_desc->lli); list_for_each_entry(child, &bad_desc->tx_list, desc_node) dwc_dump_lli(dwc, &child->lli); @@ -1759,8 +1756,8 @@ static int dw_probe(struct platform_device *pdev) dma_writel(dw, CFG, DW_CFG_DMA_EN); - printk(KERN_INFO "%s: DesignWare DMA Controller, %d channels\n", - dev_name(&pdev->dev), nr_channels); + dev_info(&pdev->dev, "DesignWare DMA Controller, %d channels\n", + nr_channels); dma_async_device_register(&dw->dma); From 6168d5670bd764557b5e06b1842964a44cf34a45 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:10 +0300 Subject: [PATCH 0664/4607] dw_dmac: don't call platform_get_drvdata twice There is no need to call platform_get_drvdata twice as we have it already in dw variable. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 155d3f108ab4..d60ef9c7dd68 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1787,7 +1787,7 @@ static void dw_shutdown(struct platform_device *pdev) { struct dw_dma *dw = platform_get_drvdata(pdev); - dw_dma_off(platform_get_drvdata(pdev)); + dw_dma_off(dw); clk_disable_unprepare(dw->clk); } @@ -1796,7 +1796,7 @@ static int dw_suspend_noirq(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct dw_dma *dw = platform_get_drvdata(pdev); - dw_dma_off(platform_get_drvdata(pdev)); + dw_dma_off(dw); clk_disable_unprepare(dw->clk); return 0; From ba84bd7146b9244de0ce04cdc668521a73f5336f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:11 +0300 Subject: [PATCH 0665/4607] dw_dmac: change dev_crit to dev_WARN in dwc_handle_error In case of handling a bad descriptor the dwc_handle_error() will dump a stack as well. It's a lot more verbose and more likely to get user's attention. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index d60ef9c7dd68..9a27fb70c950 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -485,14 +485,14 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) dwc_dostart(dwc, dwc_first_active(dwc)); /* - * KERN_CRITICAL may seem harsh, but since this only happens + * WARN may seem harsh, but since this only happens * when someone submits a bad physical address in a * descriptor, we should consider ourselves lucky that the * controller flagged an error instead of scribbling over * random memory locations. */ - dev_crit(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n"); - dev_crit(chan2dev(&dwc->chan), " cookie: %d\n", bad_desc->txd.cookie); + dev_WARN(chan2dev(&dwc->chan), "Bad descriptor submitted for DMA!\n" + " cookie: %d\n", bad_desc->txd.cookie); dwc_dump_lli(dwc, &bad_desc->lli); list_for_each_entry(child, &bad_desc->tx_list, desc_node) dwc_dump_lli(dwc, &child->lli); From e63a47a361e03eaf79e0f2f6cdaca8e7679d1867 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 18 Oct 2012 17:34:12 +0300 Subject: [PATCH 0666/4607] dw_dmac: introduce to_dw_desc() macro The to_dw_desc() macro helps to retrieve the dw_desc node from the corresponding list_head structure. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Reviewed-by: Felipe Balbi Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 ++---- drivers/dma/dw_dmac_regs.h | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 9a27fb70c950..476e9c8fb6ca 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -93,7 +93,7 @@ static struct device *chan2parent(struct dma_chan *chan) static struct dw_desc *dwc_first_active(struct dw_dma_chan *dwc) { - return list_entry(dwc->active_list.next, struct dw_desc, desc_node); + return to_dw_desc(dwc->active_list.next); } static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) @@ -600,9 +600,7 @@ static void dw_dma_tasklet(unsigned long data) if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { if (dwc->tx_node_active != dwc->tx_list) { struct dw_desc *desc = - list_entry(dwc->tx_node_active, - struct dw_desc, - desc_node); + to_dw_desc(dwc->tx_node_active); dma_writel(dw, CLEAR.XFER, dwc->mask); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 88a069f66b89..8881e9b277a3 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -299,6 +299,8 @@ struct dw_desc { size_t len; }; +#define to_dw_desc(h) list_entry(h, struct dw_desc, desc_node) + static inline struct dw_desc * txd_to_dw_desc(struct dma_async_tx_descriptor *txd) { From e5a087fdc1ebe5bba40bcecb53c28a0af70e3b47 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 26 Oct 2012 23:35:15 +0900 Subject: [PATCH 0667/4607] dmaengine: use for_each_set_bit Use for_each_set_bit() to implement for_each_dma_cap_mask() and remove unused first_dma_cap() and next_dma_cap(). Signed-off-by: Akinobu Mita Cc: Vinod Koul Cc: Dan Williams Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index c88f302d91c9..4c8643794e0d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -849,20 +849,6 @@ static inline bool async_tx_test_ack(struct dma_async_tx_descriptor *tx) return (tx->flags & DMA_CTRL_ACK) == DMA_CTRL_ACK; } -#define first_dma_cap(mask) __first_dma_cap(&(mask)) -static inline int __first_dma_cap(const dma_cap_mask_t *srcp) -{ - return min_t(int, DMA_TX_TYPE_END, - find_first_bit(srcp->bits, DMA_TX_TYPE_END)); -} - -#define next_dma_cap(n, mask) __next_dma_cap((n), &(mask)) -static inline int __next_dma_cap(int n, const dma_cap_mask_t *srcp) -{ - return min_t(int, DMA_TX_TYPE_END, - find_next_bit(srcp->bits, DMA_TX_TYPE_END, n+1)); -} - #define dma_cap_set(tx, mask) __dma_cap_set((tx), &(mask)) static inline void __dma_cap_set(enum dma_transaction_type tx_type, dma_cap_mask_t *dstp) @@ -891,9 +877,7 @@ __dma_has_cap(enum dma_transaction_type tx_type, dma_cap_mask_t *srcp) } #define for_each_dma_cap_mask(cap, mask) \ - for ((cap) = first_dma_cap(mask); \ - (cap) < DMA_TX_TYPE_END; \ - (cap) = next_dma_cap((cap), (mask))) + for_each_set_bit(cap, mask.bits, DMA_TX_TYPE_END) /** * dma_async_issue_pending - flush pending transactions to HW From 91998261dd0d5aefb56d87cef84f7810c32f6194 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Oct 2012 00:49:31 +0900 Subject: [PATCH 0668/4607] dma: amba-pl08x: use vchan_dma_desc_free_list vchan_dma_desc_free_list() iterates through each virt_dma_desc in the specified list_head and calls vchan->desc_free(). We can use it instead of repeated execution of pl08x_desc_free() for each virt_dma_desc in the list_head. Because vchan->desc_free callback is set as pl08x_desc_free() for amba-pl08x driver. Signed-off-by: Akinobu Mita Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index d1cc5791476b..6eb6a5c210bb 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1096,15 +1096,9 @@ static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x, struct pl08x_dma_chan *plchan) { LIST_HEAD(head); - struct pl08x_txd *txd; vchan_get_all_descriptors(&plchan->vc, &head); - - while (!list_empty(&head)) { - txd = list_first_entry(&head, struct pl08x_txd, vd.node); - list_del(&txd->vd.node); - pl08x_desc_free(&txd->vd); - } + vchan_dma_desc_free_list(&plchan->vc, &head); } /* From 8be9e32b310cd8c4302991c8ff6692689c7d9d76 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Oct 2012 00:49:32 +0900 Subject: [PATCH 0669/4607] dmatest: adjust invalid module parameters for number of source buffers DMA Engine test module has module parameters to set the number of source buffers for xor and pq operations. We can set these values larger than the maximum number of sources that the device can support. These values are not adjusted and the unsupported number of source buffers are passed to the device. But most drivers don't check it, so unexpected results will happen. This makes an appropriate adjustment for these module parameters before use. Signed-off-by: Akinobu Mita Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 64b048d7fba7..3a8047b1f108 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -242,6 +242,13 @@ static inline void unmap_dst(struct device *dev, dma_addr_t *addr, size_t len, dma_unmap_single(dev, addr[count], len, DMA_BIDIRECTIONAL); } +static unsigned int min_odd(unsigned int x, unsigned int y) +{ + unsigned int val = min(x, y); + + return val % 2 ? val : val - 1; +} + /* * This function repeatedly tests DMA transfers of various lengths and * offsets for a given operation type until it is told to exit by @@ -262,6 +269,7 @@ static int dmatest_func(void *data) struct dmatest_thread *thread = data; struct dmatest_done done = { .wait = &done_wait }; struct dma_chan *chan; + struct dma_device *dev; const char *thread_name; unsigned int src_off, dst_off, len; unsigned int error_count; @@ -283,13 +291,16 @@ static int dmatest_func(void *data) smp_rmb(); chan = thread->chan; + dev = chan->device; if (thread->type == DMA_MEMCPY) src_cnt = dst_cnt = 1; else if (thread->type == DMA_XOR) { - src_cnt = xor_sources | 1; /* force odd to ensure dst = src */ + /* force odd to ensure dst = src */ + src_cnt = min_odd(xor_sources | 1, dev->max_xor); dst_cnt = 1; } else if (thread->type == DMA_PQ) { - src_cnt = pq_sources | 1; /* force odd to ensure dst = src */ + /* force odd to ensure dst = src */ + src_cnt = min_odd(pq_sources | 1, dma_maxpq(dev, 0)); dst_cnt = 2; for (i = 0; i < src_cnt; i++) pq_coefs[i] = 1; @@ -327,7 +338,6 @@ static int dmatest_func(void *data) while (!kthread_should_stop() && !(iterations && total_tests >= iterations)) { - struct dma_device *dev = chan->device; struct dma_async_tx_descriptor *tx = NULL; dma_addr_t dma_srcs[src_cnt]; dma_addr_t dma_dsts[dst_cnt]; From 1ba151cdf5ac120fc829ee6524fefedc6828947f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 28 Oct 2012 01:05:44 -0700 Subject: [PATCH 0670/4607] dma: Convert dev_printk(KERN_ to dev_( dev_ calls take less code than dev_printk(KERN_ and reducing object size is good. Coalesce formats for easier grep. Signed-off-by: Joe Perches Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac_regs.h | 8 +++---- drivers/dma/iop-adma.c | 45 ++++++++++++++++++------------------- drivers/dma/mv_xor.c | 40 +++++++++++++-------------------- 3 files changed, 42 insertions(+), 51 deletions(-) diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 116e4adffb08..0eb3c1388667 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -369,10 +369,10 @@ static void vdbg_dump_regs(struct at_dma_chan *atchan) {} static void atc_dump_lli(struct at_dma_chan *atchan, struct at_lli *lli) { - dev_printk(KERN_CRIT, chan2dev(&atchan->chan_common), - " desc: s0x%x d0x%x ctrl0x%x:0x%x l0x%x\n", - lli->saddr, lli->daddr, - lli->ctrla, lli->ctrlb, lli->dscr); + dev_crit(chan2dev(&atchan->chan_common), + " desc: s0x%x d0x%x ctrl0x%x:0x%x l0x%x\n", + lli->saddr, lli->daddr, + lli->ctrla, lli->ctrlb, lli->dscr); } diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 9072e173b860..62a59fd65723 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -936,7 +936,7 @@ static irqreturn_t iop_adma_err_handler(int irq, void *data) struct iop_adma_chan *chan = data; unsigned long status = iop_chan_get_status(chan); - dev_printk(KERN_ERR, chan->device->common.dev, + dev_err(chan->device->common.dev, "error ( %s%s%s%s%s%s%s)\n", iop_is_err_int_parity(status, chan) ? "int_parity " : "", iop_is_err_mcu_abort(status, chan) ? "mcu_abort " : "", @@ -1017,7 +1017,7 @@ static int iop_adma_memcpy_self_test(struct iop_adma_device *device) if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test copy timed out, disabling\n"); err = -ENODEV; goto free_resources; @@ -1027,7 +1027,7 @@ static int iop_adma_memcpy_self_test(struct iop_adma_device *device) dma_sync_single_for_cpu(&iop_chan->device->pdev->dev, dest_dma, IOP_ADMA_TEST_SIZE, DMA_FROM_DEVICE); if (memcmp(src, dest, IOP_ADMA_TEST_SIZE)) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test copy failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1117,7 +1117,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test xor timed out, disabling\n"); err = -ENODEV; goto free_resources; @@ -1129,7 +1129,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) { u32 *ptr = page_address(dest); if (ptr[i] != cmp_word) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test xor failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1163,14 +1163,14 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) msleep(8); if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test zero sum timed out, disabling\n"); err = -ENODEV; goto free_resources; } if (zero_sum_result != 0) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test zero sum failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1187,7 +1187,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) msleep(8); if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test memset timed out, disabling\n"); err = -ENODEV; goto free_resources; @@ -1196,7 +1196,7 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) for (i = 0; i < PAGE_SIZE/sizeof(u32); i++) { u32 *ptr = page_address(dest); if (ptr[i]) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test memset failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1219,14 +1219,14 @@ iop_adma_xor_val_self_test(struct iop_adma_device *device) msleep(8); if (iop_adma_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test non-zero sum timed out, disabling\n"); err = -ENODEV; goto free_resources; } if (zero_sum_result != 1) { - dev_printk(KERN_ERR, dma_chan->device->dev, + dev_err(dma_chan->device->dev, "Self-test non-zero sum failed compare, disabling\n"); err = -ENODEV; goto free_resources; @@ -1579,15 +1579,14 @@ static int iop_adma_probe(struct platform_device *pdev) goto err_free_iop_chan; } - dev_printk(KERN_INFO, &pdev->dev, "Intel(R) IOP: " - "( %s%s%s%s%s%s%s)\n", - dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "pq " : "", - dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "", - dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", - dma_has_cap(DMA_XOR_VAL, dma_dev->cap_mask) ? "xor_val " : "", - dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", - dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", - dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); + dev_info(&pdev->dev, "Intel(R) IOP: ( %s%s%s%s%s%s%s)\n", + dma_has_cap(DMA_PQ, dma_dev->cap_mask) ? "pq " : "", + dma_has_cap(DMA_PQ_VAL, dma_dev->cap_mask) ? "pq_val " : "", + dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", + dma_has_cap(DMA_XOR_VAL, dma_dev->cap_mask) ? "xor_val " : "", + dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", + dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", + dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); dma_async_device_register(dma_dev); goto out; @@ -1651,8 +1650,8 @@ static void iop_chan_start_null_memcpy(struct iop_adma_chan *iop_chan) /* run the descriptor */ iop_chan_enable(iop_chan); } else - dev_printk(KERN_ERR, iop_chan->device->common.dev, - "failed to allocate null descriptor\n"); + dev_err(iop_chan->device->common.dev, + "failed to allocate null descriptor\n"); spin_unlock_bh(&iop_chan->lock); } @@ -1704,7 +1703,7 @@ static void iop_chan_start_null_xor(struct iop_adma_chan *iop_chan) /* run the descriptor */ iop_chan_enable(iop_chan); } else - dev_printk(KERN_ERR, iop_chan->device->common.dev, + dev_err(iop_chan->device->common.dev, "failed to allocate null descriptor\n"); spin_unlock_bh(&iop_chan->lock); } diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index ac71f555dd72..e26de4f680e1 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -210,7 +210,7 @@ static void mv_set_mode(struct mv_xor_chan *chan, break; default: dev_err(mv_chan_to_devp(chan), - "error: unsupported operation %d.\n", + "error: unsupported operation %d\n", type); BUG(); return; @@ -828,28 +828,22 @@ static void mv_dump_xor_regs(struct mv_xor_chan *chan) u32 val; val = __raw_readl(XOR_CONFIG(chan)); - dev_err(mv_chan_to_devp(chan), - "config 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "config 0x%08x\n", val); val = __raw_readl(XOR_ACTIVATION(chan)); - dev_err(mv_chan_to_devp(chan), - "activation 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "activation 0x%08x\n", val); val = __raw_readl(XOR_INTR_CAUSE(chan)); - dev_err(mv_chan_to_devp(chan), - "intr cause 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "intr cause 0x%08x\n", val); val = __raw_readl(XOR_INTR_MASK(chan)); - dev_err(mv_chan_to_devp(chan), - "intr mask 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "intr mask 0x%08x\n", val); val = __raw_readl(XOR_ERROR_CAUSE(chan)); - dev_err(mv_chan_to_devp(chan), - "error cause 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "error cause 0x%08x\n", val); val = __raw_readl(XOR_ERROR_ADDR(chan)); - dev_err(mv_chan_to_devp(chan), - "error addr 0x%08x.\n", val); + dev_err(mv_chan_to_devp(chan), "error addr 0x%08x\n", val); } static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan, @@ -862,7 +856,7 @@ static void mv_xor_err_interrupt_handler(struct mv_xor_chan *chan, } dev_err(mv_chan_to_devp(chan), - "error on chan %d. intr cause 0x%08x.\n", + "error on chan %d. intr cause 0x%08x\n", chan->idx, intr_cause); mv_dump_xor_regs(chan); @@ -1052,9 +1046,8 @@ mv_xor_xor_self_test(struct mv_xor_chan *mv_chan) u32 *ptr = page_address(dest); if (ptr[i] != cmp_word) { dev_err(dma_chan->device->dev, - "Self-test xor failed compare, disabling." - " index %d, data %x, expected %x\n", i, - ptr[i], cmp_word); + "Self-test xor failed compare, disabling. index %d, data %x, expected %x\n", + i, ptr[i], cmp_word); err = -ENODEV; goto free_resources; } @@ -1194,12 +1187,11 @@ mv_xor_channel_add(struct mv_xor_device *xordev, goto err_free_irq; } - dev_info(&pdev->dev, "Marvell XOR: " - "( %s%s%s%s)\n", - dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", - dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", - dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", - dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); + dev_info(&pdev->dev, "Marvell XOR: ( %s%s%s%s)\n", + dma_has_cap(DMA_XOR, dma_dev->cap_mask) ? "xor " : "", + dma_has_cap(DMA_MEMSET, dma_dev->cap_mask) ? "fill " : "", + dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask) ? "cpy " : "", + dma_has_cap(DMA_INTERRUPT, dma_dev->cap_mask) ? "intr " : ""); dma_async_device_register(dma_dev); return mv_chan; @@ -1253,7 +1245,7 @@ static int mv_xor_probe(struct platform_device *pdev) struct resource *res; int i, ret; - dev_notice(&pdev->dev, "Marvell XOR driver\n"); + dev_notice(&pdev->dev, "Marvell shared XOR driver\n"); xordev = devm_kzalloc(&pdev->dev, sizeof(*xordev), GFP_KERNEL); if (!xordev) From 2c88ae90939c2ef8ae80b07713b898c577b81598 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Sun, 28 Oct 2012 00:49:33 +0900 Subject: [PATCH 0671/4607] async_tx: use memchr_inv Use memchr_inv() to check the specified page is filled with zero. Signed-off-by: Akinobu Mita Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Vinod Koul --- crypto/async_tx/async_xor.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crypto/async_tx/async_xor.c b/crypto/async_tx/async_xor.c index 154cc84381c2..8ade0a0481c6 100644 --- a/crypto/async_tx/async_xor.c +++ b/crypto/async_tx/async_xor.c @@ -230,9 +230,7 @@ EXPORT_SYMBOL_GPL(async_xor); static int page_is_zero(struct page *p, unsigned int offset, size_t len) { - char *a = page_address(p) + offset; - return ((*(u32 *) a) == 0 && - memcmp(a, a + 4, len - 4) == 0); + return !memchr_inv(page_address(p) + offset, 0, len); } static inline struct dma_chan * From 35fa4dbc8c877c69144736cfe144a95a1e7ccc1a Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:12 +0000 Subject: [PATCH 0672/4607] async_tx: add missing DMA unmap to async_memcpy() Do DMA unmap on ->device_prep_dma_memcpy failure. Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- crypto/async_tx/async_memcpy.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crypto/async_tx/async_memcpy.c b/crypto/async_tx/async_memcpy.c index 361b5e8239bc..9e62feffb374 100644 --- a/crypto/async_tx/async_memcpy.c +++ b/crypto/async_tx/async_memcpy.c @@ -67,6 +67,12 @@ async_memcpy(struct page *dest, struct page *src, unsigned int dest_offset, tx = device->device_prep_dma_memcpy(chan, dma_dest, dma_src, len, dma_prep_flags); + if (!tx) { + dma_unmap_page(device->dev, dma_dest, len, + DMA_FROM_DEVICE); + dma_unmap_page(device->dev, dma_src, len, + DMA_TO_DEVICE); + } } if (tx) { From 522d974451743abcf674cbebd7c29d44fbd63586 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:13 +0000 Subject: [PATCH 0673/4607] ioat: add missing DMA unmap to ioat_dma_self_test() Make ioat_dma_self_test() do DMA unmapping itself and fix handling of failure cases. Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/dma/ioat/dma.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 73b2b65cb1de..464138a8a782 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -833,14 +833,14 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) dma_src = dma_map_single(dev, src, IOAT_TEST_SIZE, DMA_TO_DEVICE); dma_dest = dma_map_single(dev, dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); - flags = DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_DEST_UNMAP_SINGLE | + flags = DMA_COMPL_SKIP_SRC_UNMAP | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT; tx = device->common.device_prep_dma_memcpy(dma_chan, dma_dest, dma_src, IOAT_TEST_SIZE, flags); if (!tx) { dev_err(dev, "Self-test prep failed, disabling\n"); err = -ENODEV; - goto free_resources; + goto unmap_dma; } async_tx_ack(tx); @@ -851,7 +851,7 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test setup failed, disabling\n"); err = -ENODEV; - goto free_resources; + goto unmap_dma; } dma->device_issue_pending(dma_chan); @@ -862,7 +862,7 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) != DMA_SUCCESS) { dev_err(dev, "Self-test copy timed out, disabling\n"); err = -ENODEV; - goto free_resources; + goto unmap_dma; } if (memcmp(src, dest, IOAT_TEST_SIZE)) { dev_err(dev, "Self-test copy failed compare, disabling\n"); @@ -870,6 +870,9 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) goto free_resources; } +unmap_dma: + dma_unmap_single(dev, dma_src, IOAT_TEST_SIZE, DMA_TO_DEVICE); + dma_unmap_single(dev, dma_dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); free_resources: dma->device_free_chan_resources(dma_chan); out: From d1806a5c4d2248d2799f4367dbdb1800be94a26f Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:14 +0000 Subject: [PATCH 0674/4607] mtd: fsmc_nand: add missing DMA unmap to dma_xfer() Make dma_xfer() do DMA unmapping itself and fix handling of failure cases. Cc: David Woodhouse Cc: Vipin Kumar Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/mtd/nand/fsmc_nand.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 1d7446434b0e..a66576863e53 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -573,23 +573,22 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, dma_dev = chan->device; dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction); + flags |= DMA_COMPL_SKIP_SRC_UNMAP | DMA_COMPL_SKIP_DEST_UNMAP; + if (direction == DMA_TO_DEVICE) { dma_src = dma_addr; dma_dst = host->data_pa; - flags |= DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_SKIP_DEST_UNMAP; } else { dma_src = host->data_pa; dma_dst = dma_addr; - flags |= DMA_COMPL_DEST_UNMAP_SINGLE | DMA_COMPL_SKIP_SRC_UNMAP; } tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, len, flags); - if (!tx) { dev_err(host->dev, "device_prep_dma_memcpy error\n"); - dma_unmap_single(dma_dev->dev, dma_addr, len, direction); - return -EIO; + ret = -EIO; + goto unmap_dma; } tx->callback = dma_complete; @@ -599,7 +598,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, ret = dma_submit_error(cookie); if (ret) { dev_err(host->dev, "dma_submit_error %d\n", cookie); - return ret; + goto unmap_dma; } dma_async_issue_pending(chan); @@ -610,10 +609,17 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, if (ret <= 0) { chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); dev_err(host->dev, "wait_for_completion_timeout\n"); - return ret ? ret : -ETIMEDOUT; + if (!ret) + ret = -ETIMEDOUT; + goto unmap_dma; } - return 0; + ret = 0; + +unmap_dma: + dma_unmap_single(dma_dev->dev, dma_addr, len, direction); + + return ret; } /* From bfc191ea568a9c00ab652750686f83ad2daf92a8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:15 +0000 Subject: [PATCH 0675/4607] carma-fpga: pass correct flags to ->device_prep_dma_memcpy() DMA unmapping is handled by a driver so tell fsldma.c driver (which is the DMA engine driver used by carma-fpga) to skip unmapping destination and source buffers. Cc: Ira W. Snyder Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/misc/carma/carma-fpga.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 8835eabb3b87..6b43f8c7b3be 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -631,6 +631,8 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf) struct dma_async_tx_descriptor *tx; dma_cookie_t cookie; dma_addr_t dst, src; + unsigned long dma_flags = DMA_COMPL_SKIP_DEST_UNMAP | + DMA_COMPL_SKIP_SRC_UNMAP; dst_sg = buf->vb.sglist; dst_nents = buf->vb.sglen; @@ -666,7 +668,7 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf) src = SYS_FPGA_BLOCK; tx = chan->device->device_prep_dma_memcpy(chan, dst, src, REG_BLOCK_SIZE, - 0); + dma_flags); if (!tx) { dev_err(priv->dev, "unable to prep SYS-FPGA DMA\n"); return -ENOMEM; From 5ca7c109e8eee8d97267d3308548509bb80d8bf0 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 25 Sep 2012 13:59:31 -0500 Subject: [PATCH 0676/4607] of: dma: fix potential deadlock when requesting a slave channel In the latest version of the OF dma handlers I added support (rather hastily) to exhaustively search for an available dma slave channel, for the use-case where we have alternative slave channels that can be used. In the current implementation a deadlock scenario can occur causing the CPU to loop forever. The scenario is as follows ... 1. There are alternative channels avaialble 2. The first channel that is found by calling of_dma_find_channel() is not available and so the call to the xlate function returns NULL. In this case we will call of_dma_find_channel() again but we will return the same channel that we found the first time and hence, again the xlate will return NULL and we will loop here forever. Fix this potential deadlock by just using a single for-loop and not a for-loop nested in a do-while loop. This change also replaces the function of_dma_find_channel() with of_dma_match_channel() which performs a simple check to see if a DMA channel matches the name specified. I have tested this implementation on an OMAP4 panda board by adding a dummy DMA specifier, that will cause the xlate function to return NULL, to the beginning of a list of DMA specifiers for a DMA client. Cc: Nicolas Ferre Cc: Benoit Cousson Cc: Stephen Warren Cc: Grant Likely Cc: Russell King Cc: Rob Herring Cc: Arnd Bergmann Cc: Vinod Koul Cc: Dan Williams Signed-off-by: Jon Hunter Signed-off-by: Vinod Koul --- drivers/of/dma.c | 60 ++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/of/dma.c b/drivers/of/dma.c index 19ad37c066f5..4bed490a69e4 100644 --- a/drivers/of/dma.c +++ b/drivers/of/dma.c @@ -108,37 +108,32 @@ void of_dma_controller_free(struct device_node *np) EXPORT_SYMBOL_GPL(of_dma_controller_free); /** - * of_dma_find_channel - Find a DMA channel by name + * of_dma_match_channel - Check if a DMA specifier matches name * @np: device node to look for DMA channels - * @name: name of desired channel + * @name: channel name to be matched + * @index: index of DMA specifier in list of DMA specifiers * @dma_spec: pointer to DMA specifier as found in the device tree * - * Find a DMA channel by the name. Returns 0 on success or appropriate - * errno value on error. + * Check if the DMA specifier pointed to by the index in a list of DMA + * specifiers, matches the name provided. Returns 0 if the name matches and + * a valid pointer to the DMA specifier is found. Otherwise returns -ENODEV. */ -static int of_dma_find_channel(struct device_node *np, char *name, - struct of_phandle_args *dma_spec) +static int of_dma_match_channel(struct device_node *np, char *name, int index, + struct of_phandle_args *dma_spec) { - int count, i; const char *s; - count = of_property_count_strings(np, "dma-names"); - if (count < 0) - return count; + if (of_property_read_string_index(np, "dma-names", index, &s)) + return -ENODEV; - for (i = 0; i < count; i++) { - if (of_property_read_string_index(np, "dma-names", i, &s)) - continue; + if (strcmp(name, s)) + return -ENODEV; - if (strcmp(name, s)) - continue; + if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index, + dma_spec)) + return -ENODEV; - if (!of_parse_phandle_with_args(np, "dmas", "#dma-cells", i, - dma_spec)) - return 0; - } - - return -ENODEV; + return 0; } /** @@ -154,19 +149,22 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, struct of_phandle_args dma_spec; struct of_dma *ofdma; struct dma_chan *chan; - int r; + int count, i; if (!np || !name) { pr_err("%s: not enough information provided\n", __func__); return NULL; } - do { - r = of_dma_find_channel(np, name, &dma_spec); - if (r) { - pr_err("%s: can't find DMA channel\n", np->full_name); - return NULL; - } + count = of_property_count_strings(np, "dma-names"); + if (count < 0) { + pr_err("%s: dma-names property missing or empty\n", __func__); + return NULL; + } + + for (i = 0; i < count; i++) { + if (of_dma_match_channel(np, name, i, &dma_spec)) + continue; ofdma = of_dma_find_controller(dma_spec.np); if (!ofdma) { @@ -185,9 +183,11 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, of_node_put(dma_spec.np); - } while (!chan); + if (chan) + return chan; + } - return chan; + return NULL; } /** From 9743a3b62dee8c9d8af1319f8d1c1ff39130267d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 11 Oct 2012 14:43:01 -0500 Subject: [PATCH 0677/4607] of: dma: fix protection of DMA controller data stored by DMA helpers In the current implementation of the OF DMA helpers, read-copy-update (RCU) linked lists are being used for storing and accessing the DMA controller data. This part of implementation is based upon V2 of the DMA helpers by Nicolas [1]. During a recent review of RCU, it became apparent that the code is missing the required rcu_read_lock()/unlock() calls as well as synchronisation calls before freeing any memory protected by RCU. Having looked into adding the appropriate RCU calls to protect the DMA data it became apparent that with the current DMA helper implementation, using RCU is not as attractive as it may have been before. The main reasons being that ... 1. We need to protect the DMA data around calls to the xlate function. 2. The of_dma_simple_xlate() function calls the DMA engine function dma_request_channel() which employs a mutex and so could sleep. 3. The RCU read-side critical sections must not sleep and so we cannot hold an RCU read lock around the xlate function. Therefore, instead of using RCU, an alternative for this use-case is to employ a simple spinlock inconjunction with a usage count variable to keep track of how many current users of the DMA data structure there are. With this implementation, the DMA data cannot be freed until all current users of the DMA data are finished. This patch is based upon the DMA helpers fix for potential deadlock [2]. [1] http://article.gmane.org/gmane.linux.ports.arm.omap/73622 [2] http://marc.info/?l=linux-arm-kernel&m=134859982520984&w=2 Signed-off-by: Jon Hunter Signed-off-by: Vinod Koul --- drivers/of/dma.c | 89 +++++++++++++++++++++++++++++++----------- include/linux/of_dma.h | 5 ++- 2 files changed, 70 insertions(+), 24 deletions(-) diff --git a/drivers/of/dma.c b/drivers/of/dma.c index 4bed490a69e4..59631b2c4666 100644 --- a/drivers/of/dma.c +++ b/drivers/of/dma.c @@ -19,27 +19,60 @@ #include static LIST_HEAD(of_dma_list); +static DEFINE_SPINLOCK(of_dma_lock); /** - * of_dma_find_controller - Find a DMA controller in DT DMA helpers list - * @np: device node of DMA controller + * of_dma_get_controller - Get a DMA controller in DT DMA helpers list + * @dma_spec: pointer to DMA specifier as found in the device tree + * + * Finds a DMA controller with matching device node and number for dma cells + * in a list of registered DMA controllers. If a match is found the use_count + * variable is increased and a valid pointer to the DMA data stored is retuned. + * A NULL pointer is returned if no match is found. */ -static struct of_dma *of_dma_find_controller(struct device_node *np) +static struct of_dma *of_dma_get_controller(struct of_phandle_args *dma_spec) { struct of_dma *ofdma; + spin_lock(&of_dma_lock); + if (list_empty(&of_dma_list)) { - pr_err("empty DMA controller list\n"); + spin_unlock(&of_dma_lock); return NULL; } - list_for_each_entry_rcu(ofdma, &of_dma_list, of_dma_controllers) - if (ofdma->of_node == np) + list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers) + if ((ofdma->of_node == dma_spec->np) && + (ofdma->of_dma_nbcells == dma_spec->args_count)) { + ofdma->use_count++; + spin_unlock(&of_dma_lock); return ofdma; + } + + spin_unlock(&of_dma_lock); + + pr_debug("%s: can't find DMA controller %s\n", __func__, + dma_spec->np->full_name); return NULL; } +/** + * of_dma_put_controller - Decrement use count for a registered DMA controller + * @of_dma: pointer to DMA controller data + * + * Decrements the use_count variable in the DMA data structure. This function + * should be called only when a valid pointer is returned from + * of_dma_get_controller() and no further accesses to data referenced by that + * pointer are needed. + */ +static void of_dma_put_controller(struct of_dma *ofdma) +{ + spin_lock(&of_dma_lock); + ofdma->use_count--; + spin_unlock(&of_dma_lock); +} + /** * of_dma_controller_register - Register a DMA controller to DT DMA helpers * @np: device node of DMA controller @@ -81,9 +114,10 @@ int of_dma_controller_register(struct device_node *np, ofdma->of_dma_nbcells = nbcells; ofdma->of_dma_xlate = of_dma_xlate; ofdma->of_dma_data = data; + ofdma->use_count = 0; /* Now queue of_dma controller structure in list */ - list_add_tail_rcu(&ofdma->of_dma_controllers, &of_dma_list); + list_add_tail(&ofdma->of_dma_controllers, &of_dma_list); return 0; } @@ -95,15 +129,32 @@ EXPORT_SYMBOL_GPL(of_dma_controller_register); * * Memory allocated by of_dma_controller_register() is freed here. */ -void of_dma_controller_free(struct device_node *np) +int of_dma_controller_free(struct device_node *np) { struct of_dma *ofdma; - ofdma = of_dma_find_controller(np); - if (ofdma) { - list_del_rcu(&ofdma->of_dma_controllers); - kfree(ofdma); + spin_lock(&of_dma_lock); + + if (list_empty(&of_dma_list)) { + spin_unlock(&of_dma_lock); + return -ENODEV; } + + list_for_each_entry(ofdma, &of_dma_list, of_dma_controllers) + if (ofdma->of_node == np) { + if (ofdma->use_count) { + spin_unlock(&of_dma_lock); + return -EBUSY; + } + + list_del(&ofdma->of_dma_controllers); + spin_unlock(&of_dma_lock); + kfree(ofdma); + return 0; + } + + spin_unlock(&of_dma_lock); + return -ENODEV; } EXPORT_SYMBOL_GPL(of_dma_controller_free); @@ -166,21 +217,15 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, if (of_dma_match_channel(np, name, i, &dma_spec)) continue; - ofdma = of_dma_find_controller(dma_spec.np); - if (!ofdma) { - pr_debug("%s: can't find DMA controller %s\n", - np->full_name, dma_spec.np->full_name); - continue; - } + ofdma = of_dma_get_controller(&dma_spec); - if (dma_spec.args_count != ofdma->of_dma_nbcells) { - pr_debug("%s: wrong #dma-cells for %s\n", np->full_name, - dma_spec.np->full_name); + if (!ofdma) continue; - } chan = ofdma->of_dma_xlate(&dma_spec, ofdma); + of_dma_put_controller(ofdma); + of_node_put(dma_spec.np); if (chan) diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index 67158ddd1f3e..84b64f857e23 100644 --- a/include/linux/of_dma.h +++ b/include/linux/of_dma.h @@ -25,6 +25,7 @@ struct of_dma { struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *); void *of_dma_data; + int use_count; }; struct of_dma_filter_info { @@ -37,7 +38,7 @@ extern int of_dma_controller_register(struct device_node *np, struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *), void *data); -extern void of_dma_controller_free(struct device_node *np); +extern int of_dma_controller_free(struct device_node *np); extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np, char *name); extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, @@ -51,7 +52,7 @@ static int of_dma_controller_register(struct device_node *np, return -ENODEV; } -static void of_dma_controller_free(struct device_node *np) +static int of_dma_controller_free(struct device_node *np) { } From 91f8aecc501e456c97a6f49f7ea3797e874d5d89 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 29 Nov 2012 12:17:22 +0530 Subject: [PATCH 0678/4607] dmaengine: fix !of_dma compilation warning Reported-by: Stephen Rothwell Reported-by: Fengguang Wu Signed-off-by: Vinod Koul --- include/linux/of_dma.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/linux/of_dma.h b/include/linux/of_dma.h index 84b64f857e23..d15073e080dd 100644 --- a/include/linux/of_dma.h +++ b/include/linux/of_dma.h @@ -44,7 +44,7 @@ extern struct dma_chan *of_dma_request_slave_channel(struct device_node *np, extern struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma); #else -static int of_dma_controller_register(struct device_node *np, +static inline int of_dma_controller_register(struct device_node *np, struct dma_chan *(*of_dma_xlate) (struct of_phandle_args *, struct of_dma *), void *data) @@ -52,17 +52,18 @@ static int of_dma_controller_register(struct device_node *np, return -ENODEV; } -static int of_dma_controller_free(struct device_node *np) +static inline int of_dma_controller_free(struct device_node *np) { + return -ENODEV; } -static struct dma_chan *of_dma_request_slave_channel(struct device_node *np, +static inline struct dma_chan *of_dma_request_slave_channel(struct device_node *np, char *name) { return NULL; } -static struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, +static inline struct dma_chan *of_dma_simple_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { return NULL; From f7d935dcc34fae9ad4a39f2cf8e2a96199c48948 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Thu, 1 Nov 2012 22:54:43 +0800 Subject: [PATCH 0679/4607] dmaengine: sirf: enable the driver support new SiRFmarco SoC The driver supports old up SiRFprimaII SoCs, this patch makes it support the new SiRFmarco as well. SiRFmarco, as a SMP SoC, adds new DMA_INT_EN_CLR and DMA_CH_LOOP_CTRL_CLR registers, to disable IRQ/Channel, we should write 1 to the corresponding bit in the two CLEAR register. Tested on SiRFmarco using SPI driver: $ /mnt/spidev-sirftest -D /dev/spidev32766.0 spi mode: 0 bits per word: 8 max speed: 500000 Hz (500 KHz) 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 $ cat /proc/interrupts CPU0 CPU1 32: 1593 0 GIC sirfsoc_timer0 33: 0 3533 GIC sirfsoc_timer1 44: 0 0 GIC sirfsoc_dma 45: 16 0 GIC sirfsoc_dma 47: 6 0 GIC sirfsoc_spi 50: 5654 0 GIC sirfsoc-uart ... Signed-off-by: Barry Song Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 4 ++-- drivers/dma/sirf-dma.c | 25 +++++++++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 6761cc8a59d5..0b408bbb6a17 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -212,8 +212,8 @@ config TIMB_DMA Enable support for the Timberdale FPGA DMA engine. config SIRF_DMA - tristate "CSR SiRFprimaII DMA support" - depends on ARCH_PRIMA2 + tristate "CSR SiRFprimaII/SiRFmarco DMA support" + depends on ARCH_SIRF select DMA_ENGINE help Enable support for the CSR SiRFprimaII DMA engine. diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index c3de6edb9651..3c210ba9f938 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -32,7 +32,9 @@ #define SIRFSOC_DMA_CH_VALID 0x140 #define SIRFSOC_DMA_CH_INT 0x144 #define SIRFSOC_DMA_INT_EN 0x148 +#define SIRFSOC_DMA_INT_EN_CLR 0x14C #define SIRFSOC_DMA_CH_LOOP_CTRL 0x150 +#define SIRFSOC_DMA_CH_LOOP_CTRL_CLR 0x15C #define SIRFSOC_DMA_MODE_CTRL_BIT 4 #define SIRFSOC_DMA_DIR_CTRL_BIT 5 @@ -76,6 +78,7 @@ struct sirfsoc_dma { struct sirfsoc_dma_chan channels[SIRFSOC_DMA_CHANNELS]; void __iomem *base; int irq; + bool is_marco; }; #define DRV_NAME "sirfsoc_dma" @@ -288,13 +291,19 @@ static int sirfsoc_dma_terminate_all(struct sirfsoc_dma_chan *schan) int cid = schan->chan.chan_id; unsigned long flags; - writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) & - ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN); - writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID); - - writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL) - & ~((1 << cid) | 1 << (cid + 16)), + if (!sdma->is_marco) { + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_INT_EN) & + ~(1 << cid), sdma->base + SIRFSOC_DMA_INT_EN); + writel_relaxed(readl_relaxed(sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL) + & ~((1 << cid) | 1 << (cid + 16)), sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL); + } else { + writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_INT_EN_CLR); + writel_relaxed((1 << cid) | 1 << (cid + 16), + sdma->base + SIRFSOC_DMA_CH_LOOP_CTRL_CLR); + } + + writel_relaxed(1 << cid, sdma->base + SIRFSOC_DMA_CH_VALID); spin_lock_irqsave(&schan->lock, flags); list_splice_tail_init(&schan->active, &schan->free); @@ -568,6 +577,9 @@ static int sirfsoc_dma_probe(struct platform_device *op) return -ENOMEM; } + if (of_device_is_compatible(dn, "sirf,marco-dmac")) + sdma->is_marco = true; + if (of_property_read_u32(dn, "cell-index", &id)) { dev_err(dev, "Fail to get DMAC index\n"); return -ENODEV; @@ -668,6 +680,7 @@ static int __devexit sirfsoc_dma_remove(struct platform_device *op) static struct of_device_id sirfsoc_dma_match[] = { { .compatible = "sirf,prima2-dmac", }, + { .compatible = "sirf,marco-dmac", }, {}, }; From a14acb4ac2a1486f6633c55eb7f7ded07f3ec9fc Mon Sep 17 00:00:00 2001 From: Barry Song Date: Tue, 6 Nov 2012 21:32:39 +0800 Subject: [PATCH 0680/4607] DMAEngine: add dmaengine_prep_interleaved_dma wrapper for interleaved api commit b14dab792dee(DMAEngine: Define interleaved transfer request api) adds interleaved request api, this patch adds the dmaengine_prep_interleaved_dma just like we have dmaengine_prep_ for other modes to avoid drivers call: xxx_chan->device->device_prep_interleaved_dma(). Signed-off-by: Barry Song Cc: Jassi Brar Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 4c8643794e0d..3aa76dbed166 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -660,6 +660,13 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic( period_len, dir, flags, NULL); } +static inline struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma( + struct dma_chan *chan, struct dma_interleaved_template *xt, + unsigned long flags) +{ + return chan->device->device_prep_interleaved_dma(chan, xt, flags); +} + static inline int dmaengine_terminate_all(struct dma_chan *chan) { return dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0); From e4d43c1764bc3ee1150f24e530db2b5b23e91425 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 15 Nov 2012 06:27:50 +0000 Subject: [PATCH 0681/4607] DMA: PL330: Use devm_* functions devm_* functions are device managed and make the code and error handling a bit simpler. Cc: Jassi Brar Signed-off-by: Sachin Kamat Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 95555f37ea6d..f7edb6f0ee87 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2866,7 +2866,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pdat = adev->dev.platform_data; /* Allocate a new DMAC and its Channels */ - pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL); + pdmac = devm_kzalloc(&adev->dev, sizeof(*pdmac), GFP_KERNEL); if (!pdmac) { dev_err(&adev->dev, "unable to allocate mem\n"); return -ENOMEM; @@ -2878,13 +2878,9 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0; res = &adev->res; - request_mem_region(res->start, resource_size(res), "dma-pl330"); - - pi->base = ioremap(res->start, resource_size(res)); - if (!pi->base) { - ret = -ENXIO; - goto probe_err1; - } + pi->base = devm_request_and_ioremap(&adev->dev, res); + if (!pi->base) + return -ENXIO; amba_set_drvdata(adev, pdmac); @@ -2892,11 +2888,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ret = request_irq(irq, pl330_irq_handler, 0, dev_name(&adev->dev), pi); if (ret) - goto probe_err2; + return ret; ret = pl330_add(pi); if (ret) - goto probe_err3; + goto probe_err1; INIT_LIST_HEAD(&pdmac->desc_pool); spin_lock_init(&pdmac->pool_lock); @@ -2918,7 +2914,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) if (!pdmac->peripherals) { ret = -ENOMEM; dev_err(&adev->dev, "unable to allocate pdmac->peripherals\n"); - goto probe_err4; + goto probe_err2; } for (i = 0; i < num_chan; i++) { @@ -2962,7 +2958,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) ret = dma_async_device_register(pd); if (ret) { dev_err(&adev->dev, "unable to register DMAC\n"); - goto probe_err4; + goto probe_err2; } dev_info(&adev->dev, @@ -2975,15 +2971,10 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) return 0; -probe_err4: - pl330_del(pi); -probe_err3: - free_irq(irq, pi); probe_err2: - iounmap(pi->base); + pl330_del(pi); probe_err1: - release_mem_region(res->start, resource_size(res)); - kfree(pdmac); + free_irq(irq, pi); return ret; } @@ -2993,7 +2984,6 @@ static int __devexit pl330_remove(struct amba_device *adev) struct dma_pl330_dmac *pdmac = amba_get_drvdata(adev); struct dma_pl330_chan *pch, *_p; struct pl330_info *pi; - struct resource *res; int irq; if (!pdmac) @@ -3020,13 +3010,6 @@ static int __devexit pl330_remove(struct amba_device *adev) irq = adev->irq[0]; free_irq(irq, pi); - iounmap(pi->base); - - res = &adev->res; - release_mem_region(res->start, resource_size(res)); - - kfree(pdmac); - return 0; } From 944ea4dd38b8575e30a5699633c81945bff1864d Mon Sep 17 00:00:00 2001 From: Jon Mason Date: Sun, 11 Nov 2012 23:03:20 +0000 Subject: [PATCH 0682/4607] dmatest: Fix NULL pointer dereference on ioat device_control is an optional and not implemented in all DMA drivers. Any calls to these will result in a NULL pointer dereference. dmatest makes two of these calls when completing the kernel thread and removing the module. These are corrected by calling the dmaengine_device_control wrapper and checking for a non-existant device_control function pointer there. Signed-off-by: Jon Mason CC: Vinod Koul CC: Dan Williams Reviewed-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 4 ++-- include/linux/dmaengine.h | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 3a8047b1f108..99a75e5d66be 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -536,7 +536,7 @@ err_srcs: thread_name, total_tests, failed_tests, ret); /* terminate all transfers on specified channels */ - chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(chan); if (iterations > 0) while (!kthread_should_stop()) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit); @@ -561,7 +561,7 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc) } /* terminate all transfers on specified channels */ - dtc->chan->device->device_control(dtc->chan, DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(dtc->chan); kfree(dtc); } diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 3aa76dbed166..be6e95395b11 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -608,7 +608,10 @@ static inline int dmaengine_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { - return chan->device->device_control(chan, cmd, arg); + if (chan->device->device_control) + return chan->device->device_control(chan, cmd, arg); + else + return -ENOSYS; } static inline int dmaengine_slave_config(struct dma_chan *chan, From 7c1119bdd650fa58dad8157bc75c5fcf6ed97843 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 28 Nov 2012 06:49:47 +0000 Subject: [PATCH 0683/4607] dma: sh: Don't use ENODEV for failing slave lookup If dmaengine driver's .device_alloc_chan_resources() method returns -ENODEV, dma_request_channel() will decide, that the driver has been removed and will remove the device from its list. To prevent this use ENXIO if a slave lookup fails. Reported-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Cc: stable@vger.kernel.org Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index 8201bb4e0cd7..62eff4800521 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -326,7 +326,7 @@ static int sh_dmae_set_slave(struct shdma_chan *schan, shdma_chan); const struct sh_dmae_slave_config *cfg = dmae_find_slave(sh_chan, slave_id); if (!cfg) - return -ENODEV; + return -ENXIO; if (!try) sh_chan->config = cfg; From 5e034f7b659be9d94e64aaaa985ab530dd847fdb Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Fri, 9 Nov 2012 15:26:29 +0000 Subject: [PATCH 0684/4607] dmaengine/dmatest: terminate transfers only in case of errors dmatest erroneously terminated transfers in normal cases also leading to test failures for multiple threads over a channel. Fix this and terminate transfers only in case of errors. Signed-off-by: Shiraz Hashim Signed-off-by: Deepak Sikri Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 99a75e5d66be..a2c8904b63ea 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -536,7 +536,9 @@ err_srcs: thread_name, total_tests, failed_tests, ret); /* terminate all transfers on specified channels */ - dmaengine_terminate_all(chan); + if (ret) + dmaengine_terminate_all(chan); + if (iterations > 0) while (!kthread_should_stop()) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit); From 7369f56e3e7193583576ec705d95647b04838b05 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:19 +0000 Subject: [PATCH 0685/4607] ioat3: add missing DMA unmap to ioat_xor_val_self_test() Make ioat_xor_val_self_test() do DMA unmapping itself and fix handling of failure cases. Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/dma/ioat/dma_v3.c | 76 ++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index f7f1dc62c15c..6456f7d38e13 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -863,6 +863,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) unsigned long tmo; struct device *dev = &device->pdev->dev; struct dma_device *dma = &device->common; + u8 op = 0; dev_dbg(dev, "%s\n", __func__); @@ -908,18 +909,22 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) } /* test xor */ + op = IOAT_OP_XOR; + dest_dma = dma_map_page(dev, dest, 0, PAGE_SIZE, DMA_FROM_DEVICE); for (i = 0; i < IOAT_NUM_SRC_TEST; i++) dma_srcs[i] = dma_map_page(dev, xor_srcs[i], 0, PAGE_SIZE, DMA_TO_DEVICE); tx = dma->device_prep_dma_xor(dma_chan, dest_dma, dma_srcs, IOAT_NUM_SRC_TEST, PAGE_SIZE, - DMA_PREP_INTERRUPT); + DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test xor prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -930,7 +935,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test xor setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -939,9 +944,13 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test xor timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + dma_unmap_page(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < IOAT_NUM_SRC_TEST; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); + dma_sync_single_for_cpu(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); for (i = 0; i < (PAGE_SIZE / sizeof(u32)); i++) { u32 *ptr = page_address(dest); @@ -957,6 +966,8 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (!dma_has_cap(DMA_XOR_VAL, dma_chan->device->cap_mask)) goto free_resources; + op = IOAT_OP_XOR_VAL; + /* validate the sources with the destintation page */ for (i = 0; i < IOAT_NUM_SRC_TEST; i++) xor_val_srcs[i] = xor_srcs[i]; @@ -969,11 +980,13 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) DMA_TO_DEVICE); tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs, IOAT_NUM_SRC_TEST + 1, PAGE_SIZE, - &xor_val_result, DMA_PREP_INTERRUPT); + &xor_val_result, DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test zero prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -984,7 +997,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test zero setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -993,9 +1006,12 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test validate timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); + if (xor_val_result != 0) { dev_err(dev, "Self-test validate failed compare\n"); err = -ENODEV; @@ -1007,14 +1023,18 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) goto free_resources; /* test memset */ + op = IOAT_OP_FILL; + dma_addr = dma_map_page(dev, dest, 0, PAGE_SIZE, DMA_FROM_DEVICE); tx = dma->device_prep_dma_memset(dma_chan, dma_addr, 0, PAGE_SIZE, - DMA_PREP_INTERRUPT); + DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test memset prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -1025,7 +1045,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test memset setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -1034,9 +1054,11 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test memset timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < PAGE_SIZE/sizeof(u32); i++) { u32 *ptr = page_address(dest); if (ptr[i]) { @@ -1047,17 +1069,21 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) } /* test for non-zero parity sum */ + op = IOAT_OP_XOR_VAL; + xor_val_result = 0; for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) dma_srcs[i] = dma_map_page(dev, xor_val_srcs[i], 0, PAGE_SIZE, DMA_TO_DEVICE); tx = dma->device_prep_dma_xor_val(dma_chan, dma_srcs, IOAT_NUM_SRC_TEST + 1, PAGE_SIZE, - &xor_val_result, DMA_PREP_INTERRUPT); + &xor_val_result, DMA_PREP_INTERRUPT | + DMA_COMPL_SKIP_SRC_UNMAP | + DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { dev_err(dev, "Self-test 2nd zero prep failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } async_tx_ack(tx); @@ -1068,7 +1094,7 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test 2nd zero setup failed\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } dma->device_issue_pending(dma_chan); @@ -1077,15 +1103,31 @@ static int __devinit ioat_xor_val_self_test(struct ioatdma_device *device) if (dma->device_tx_status(dma_chan, cookie, NULL) != DMA_SUCCESS) { dev_err(dev, "Self-test 2nd validate timed out\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } if (xor_val_result != SUM_CHECK_P_RESULT) { dev_err(dev, "Self-test validate failed compare\n"); err = -ENODEV; - goto free_resources; + goto dma_unmap; } + for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, DMA_TO_DEVICE); + + goto free_resources; +dma_unmap: + if (op == IOAT_OP_XOR) { + dma_unmap_page(dev, dest_dma, PAGE_SIZE, DMA_FROM_DEVICE); + for (i = 0; i < IOAT_NUM_SRC_TEST; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, + DMA_TO_DEVICE); + } else if (op == IOAT_OP_XOR_VAL) { + for (i = 0; i < IOAT_NUM_SRC_TEST + 1; i++) + dma_unmap_page(dev, dma_srcs[i], PAGE_SIZE, + DMA_TO_DEVICE); + } else if (op == IOAT_OP_FILL) + dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_FROM_DEVICE); free_resources: dma->device_free_chan_resources(dma_chan); out: From 06eeb114026804a9a9cb83eaeb00e24aaa40ba0b Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 5 Nov 2012 10:00:20 +0000 Subject: [PATCH 0686/4607] async_tx: fix build for async_memset Add missing include. Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- crypto/async_tx/async_memset.c | 1 + 1 file changed, 1 insertion(+) diff --git a/crypto/async_tx/async_memset.c b/crypto/async_tx/async_memset.c index 58e4a8752aee..05a4d1e00148 100644 --- a/crypto/async_tx/async_memset.c +++ b/crypto/async_tx/async_memset.c @@ -25,6 +25,7 @@ */ #include #include +#include #include #include #include From b9ee86830f34737a08deead93872a79a37419a13 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 8 Nov 2012 09:59:54 +0000 Subject: [PATCH 0687/4607] dmaengine: remove dma_async_memcpy_pending() macro Just use dma_async_issue_pending() directly. Cc: Vinod Koul Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/misc/carma/carma-fpga-program.c | 2 +- drivers/misc/carma/carma-fpga.c | 2 +- include/linux/dmaengine.h | 2 -- net/ipv4/tcp.c | 6 +++--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c index eaddfe9db149..736c7714f565 100644 --- a/drivers/misc/carma/carma-fpga-program.c +++ b/drivers/misc/carma/carma-fpga-program.c @@ -546,7 +546,7 @@ static noinline int fpga_program_dma(struct fpga_dev *priv) goto out_dma_unmap; } - dma_async_memcpy_issue_pending(chan); + dma_async_issue_pending(chan); /* Set the total byte count */ fpga_set_byte_count(priv->regs, priv->bytes); diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 6b43f8c7b3be..7508cafff103 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c @@ -751,7 +751,7 @@ static irqreturn_t data_irq(int irq, void *dev_id) submitted = true; /* Start the DMA Engine */ - dma_async_memcpy_issue_pending(priv->chan); + dma_async_issue_pending(priv->chan); out: /* If no DMA was submitted, re-enable interrupts */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index be6e95395b11..cd15958d4d1d 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -901,8 +901,6 @@ static inline void dma_async_issue_pending(struct dma_chan *chan) chan->device->device_issue_pending(chan); } -#define dma_async_memcpy_issue_pending(chan) dma_async_issue_pending(chan) - /** * dma_async_is_tx_complete - poll for transaction completion * @chan: DMA channel diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 1ca253635f7a..cf949a119a54 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1406,7 +1406,7 @@ static void tcp_service_net_dma(struct sock *sk, bool wait) return; last_issued = tp->ucopy.dma_cookie; - dma_async_memcpy_issue_pending(tp->ucopy.dma_chan); + dma_async_issue_pending(tp->ucopy.dma_chan); do { if (dma_async_memcpy_complete(tp->ucopy.dma_chan, @@ -1744,7 +1744,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, tcp_service_net_dma(sk, true); tcp_cleanup_rbuf(sk, copied); } else - dma_async_memcpy_issue_pending(tp->ucopy.dma_chan); + dma_async_issue_pending(tp->ucopy.dma_chan); } #endif if (copied >= target) { @@ -1837,7 +1837,7 @@ do_prequeue: break; } - dma_async_memcpy_issue_pending(tp->ucopy.dma_chan); + dma_async_issue_pending(tp->ucopy.dma_chan); if ((offset + used) == skb->len) copied_early = true; From e239345f642e6a255d0ba1e3d92c2f9ec5a44fbe Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 8 Nov 2012 10:01:01 +0000 Subject: [PATCH 0688/4607] dmaengine: remove dma_async_memcpy_complete() macro Just use dma_async_is_tx_complete() directly. Cc: Vinod Koul Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- include/linux/dmaengine.h | 5 +---- net/ipv4/tcp.c | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index cd15958d4d1d..4ca9cf73ad3f 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -926,16 +926,13 @@ static inline enum dma_status dma_async_is_tx_complete(struct dma_chan *chan, return status; } -#define dma_async_memcpy_complete(chan, cookie, last, used)\ - dma_async_is_tx_complete(chan, cookie, last, used) - /** * dma_async_is_complete - test a cookie against chan state * @cookie: transaction identifier to test status of * @last_complete: last know completed transaction * @last_used: last cookie value handed out * - * dma_async_is_complete() is used in dma_async_memcpy_complete() + * dma_async_is_complete() is used in dma_async_is_tx_complete() * the test logic is separated for lightweight testing of multiple cookies */ static inline enum dma_status dma_async_is_complete(dma_cookie_t cookie, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index cf949a119a54..db0856ad70cb 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1409,7 +1409,7 @@ static void tcp_service_net_dma(struct sock *sk, bool wait) dma_async_issue_pending(tp->ucopy.dma_chan); do { - if (dma_async_memcpy_complete(tp->ucopy.dma_chan, + if (dma_async_is_tx_complete(tp->ucopy.dma_chan, last_issued, &done, &used) == DMA_SUCCESS) { /* Safe to free early-copied skbs now */ From 2cbe7feba1ac521b5668609c35b94536bbbcd52f Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 8 Nov 2012 10:02:07 +0000 Subject: [PATCH 0689/4607] dmaengine: add cpu_relax() to busy-loop in dma_sync_wait() Removal of the busy-loop from dma_sync_wait() is not a trivial task so just add cpu_relax() to the loop for now. Cc: Vinod Koul Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- drivers/dma/dmaengine.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index d37cf95987b6..242b8c0a3de8 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -267,7 +267,10 @@ enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) pr_err("%s: timeout!\n", __func__); return DMA_ERROR; } - } while (status == DMA_IN_PROGRESS); + if (status != DMA_IN_PROGRESS) + break; + cpu_relax(); + } while (1); return status; } From 7d283397ade3c9e51de644676a6593e1f724ac00 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 8 Nov 2012 10:03:16 +0000 Subject: [PATCH 0690/4607] async_tx: fix checking of dma_wait_for_async_tx() return value dma_wait_for_async_tx() can also return DMA_PAUSED (which should be considered as error). Cc: Vinod Koul Cc: Dan Williams Cc: Tomasz Figa Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Kyungmin Park Signed-off-by: Dan Williams --- crypto/async_tx/async_tx.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crypto/async_tx/async_tx.c b/crypto/async_tx/async_tx.c index 842120979374..7be34248b450 100644 --- a/crypto/async_tx/async_tx.c +++ b/crypto/async_tx/async_tx.c @@ -128,8 +128,8 @@ async_tx_channel_switch(struct dma_async_tx_descriptor *depend_tx, } device->device_issue_pending(chan); } else { - if (dma_wait_for_async_tx(depend_tx) == DMA_ERROR) - panic("%s: DMA_ERROR waiting for depend_tx\n", + if (dma_wait_for_async_tx(depend_tx) != DMA_SUCCESS) + panic("%s: DMA error waiting for depend_tx\n", __func__); tx->tx_submit(tx); } @@ -280,8 +280,9 @@ void async_tx_quiesce(struct dma_async_tx_descriptor **tx) * we are referring to the correct operation */ BUG_ON(async_tx_test_ack(*tx)); - if (dma_wait_for_async_tx(*tx) == DMA_ERROR) - panic("DMA_ERROR waiting for transaction\n"); + if (dma_wait_for_async_tx(*tx) != DMA_SUCCESS) + panic("%s: DMA error waiting for transaction\n", + __func__); async_tx_ack(*tx); *tx = NULL; } From 1a363068dcc2269931daef360ed14d2a262f19f7 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Mon, 3 Dec 2012 16:08:37 -0700 Subject: [PATCH 0691/4607] ioat: Add alignment workaround for IVB platforms The PCI IDs for IvyBridge IOAT DMA needs to go into a header file since dma_v3.c looks them up for certain hardware workarounds. Need to add to the alignment workaround for IOAT 3.2 since it wasn't fixed in IVB. Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/dma/ioat/dma_v3.c | 22 +++++++++++++++++++++- drivers/dma/ioat/hw.h | 11 +++++++++++ drivers/dma/ioat/pci.c | 11 ----------- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 6456f7d38e13..9d6f3bbf0e53 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -1229,6 +1229,26 @@ static bool is_snb_ioat(struct pci_dev *pdev) } } +static bool is_ivb_ioat(struct pci_dev *pdev) +{ + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_IOAT_IVB0: + case PCI_DEVICE_ID_INTEL_IOAT_IVB1: + case PCI_DEVICE_ID_INTEL_IOAT_IVB2: + case PCI_DEVICE_ID_INTEL_IOAT_IVB3: + case PCI_DEVICE_ID_INTEL_IOAT_IVB4: + case PCI_DEVICE_ID_INTEL_IOAT_IVB5: + case PCI_DEVICE_ID_INTEL_IOAT_IVB6: + case PCI_DEVICE_ID_INTEL_IOAT_IVB7: + case PCI_DEVICE_ID_INTEL_IOAT_IVB8: + case PCI_DEVICE_ID_INTEL_IOAT_IVB9: + return true; + default: + return false; + } + +} + int __devinit ioat3_dma_probe(struct ioatdma_device *device, int dca) { struct pci_dev *pdev = device->pdev; @@ -1249,7 +1269,7 @@ int __devinit ioat3_dma_probe(struct ioatdma_device *device, int dca) dma->device_alloc_chan_resources = ioat2_alloc_chan_resources; dma->device_free_chan_resources = ioat2_free_chan_resources; - if (is_jf_ioat(pdev) || is_snb_ioat(pdev)) + if (is_jf_ioat(pdev) || is_snb_ioat(pdev) || is_ivb_ioat(pdev)) dma->copy_align = 6; dma_cap_set(DMA_INTERRUPT, dma->cap_mask); diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h index d2ff3fda0b18..7cb74c62c719 100644 --- a/drivers/dma/ioat/hw.h +++ b/drivers/dma/ioat/hw.h @@ -35,6 +35,17 @@ #define IOAT_VER_3_0 0x30 /* Version 3.0 */ #define IOAT_VER_3_2 0x32 /* Version 3.2 */ +#define PCI_DEVICE_ID_INTEL_IOAT_IVB0 0x0e20 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB1 0x0e21 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB2 0x0e22 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB3 0x0e23 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB4 0x0e24 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB5 0x0e25 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB6 0x0e26 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB7 0x0e27 +#define PCI_DEVICE_ID_INTEL_IOAT_IVB8 0x0e2e +#define PCI_DEVICE_ID_INTEL_IOAT_IVB9 0x0e2f + int system_has_dca_enabled(struct pci_dev *pdev); struct ioat_dma_descriptor { diff --git a/drivers/dma/ioat/pci.c b/drivers/dma/ioat/pci.c index bfa9a3536e09..f4e6163a1e73 100644 --- a/drivers/dma/ioat/pci.c +++ b/drivers/dma/ioat/pci.c @@ -40,17 +40,6 @@ MODULE_VERSION(IOAT_DMA_VERSION); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Intel Corporation"); -#define PCI_DEVICE_ID_INTEL_IOAT_IVB0 0x0e20 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB1 0x0e21 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB2 0x0e22 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB3 0x0e23 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB4 0x0e24 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB5 0x0e25 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB6 0x0e26 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB7 0x0e27 -#define PCI_DEVICE_ID_INTEL_IOAT_IVB8 0x0e2e -#define PCI_DEVICE_ID_INTEL_IOAT_IVB9 0x0e2f - static struct pci_device_id ioat_pci_tbl[] = { /* I/OAT v1 platforms */ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IOAT) }, From 6decffd5f6afaf55722d9c85b8739621dca63d0f Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Tue, 27 Nov 2012 15:16:08 -0700 Subject: [PATCH 0692/4607] ioat: remove chanerr mask setting for IOAT v3.x The existing code set a value in the PCI_CHANERRMSK_INT register for a workaround to address a pre-silicon bug on the Intel 5520 IO hub that has been fixed when the hardware was released. There is no need for this code. Signed-off-by: Dave Jiang Signed-off-by: Dan Williams --- drivers/dma/ioat/dma_v3.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 9d6f3bbf0e53..e52cf1eb6839 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -1168,12 +1168,7 @@ static int ioat3_reset_hw(struct ioat_chan_common *chan) chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); writel(chanerr, chan->reg_base + IOAT_CHANERR_OFFSET); - /* -= IOAT ver.3 workarounds =- */ - /* Write CHANERRMSK_INT with 3E07h to mask out the errors - * that can cause stability issues for IOAT ver.3, and clear any - * pending errors - */ - pci_write_config_dword(pdev, IOAT_PCI_CHANERRMASK_INT_OFFSET, 0x3e07); + /* clear any pending errors */ err = pci_read_config_dword(pdev, IOAT_PCI_CHANERR_INT_OFFSET, &chanerr); if (err) { dev_err(&pdev->dev, "channel error register unreachable\n"); From c419fcfd071cf34ba00f9f65282583772d2655e7 Mon Sep 17 00:00:00 2001 From: Maciej Sosnowski Date: Wed, 23 May 2012 17:27:07 +0200 Subject: [PATCH 0693/4607] dca: check against empty dca_domains list before unregister provider When providers get blocked unregister_dca_providers() is called ending up with dca_providers and dca_domain lists emptied. Dca should be prevented from trying to unregister any provider if dca_domain list is found empty. Cc: Reported-by: Jiang Liu Tested-by: Gaohuai Han Signed-off-by: Maciej Sosnowski Signed-off-by: Dan Williams --- drivers/dca/dca-core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c index bc6f5faa1e9e..819dfda88236 100644 --- a/drivers/dca/dca-core.c +++ b/drivers/dca/dca-core.c @@ -420,6 +420,11 @@ void unregister_dca_provider(struct dca_provider *dca, struct device *dev) raw_spin_lock_irqsave(&dca_lock, flags); + if (list_empty(&dca_domains)) { + raw_spin_unlock_irqrestore(&dca_lock, flags); + return; + } + list_del(&dca->node); pci_rc = dca_pci_rc_from_dev(dev); From e65f32ca21faed30ce37cd6480271697fe671f74 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 6 Sep 2012 09:19:35 +0200 Subject: [PATCH 0694/4607] dma: ipu: Drop unused spinlock I was checking why this spinlock was never initialized, but it turns out it's not used anywhere, so we can drop it. Signed-off-by: Jean Delvare Cc: Vinod Koul Signed-off-by: Dan Williams --- drivers/dma/ipu/ipu_irq.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c index a5ee37d5320f..2e284a4438bc 100644 --- a/drivers/dma/ipu/ipu_irq.c +++ b/drivers/dma/ipu/ipu_irq.c @@ -44,7 +44,6 @@ static void ipu_write_reg(struct ipu *ipu, u32 value, unsigned long reg) struct ipu_irq_bank { unsigned int control; unsigned int status; - spinlock_t lock; struct ipu *ipu; }; From 1b140908c4cda43c653bb080c244d112e619008f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 21:52:02 +0530 Subject: [PATCH 0695/4607] dma: tegra: add support for channel wise pause NVIDIA's some SoCs like Tegra114 support the channel wise pause control inplace of global pause which pauses all DMA channels. When SoCs support the channel wise pause control then it uses the global pause for clock gating for register access as well as all DMA channel pause. Hence DMA registers are not accessible if DMAs are globally paused on these new SoCs. Add support for channel wise pause feature if SoCs support it. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 43 +++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index efdfffa13349..2c46ac46e7ad 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -62,6 +62,9 @@ #define TEGRA_APBDMA_STATUS_COUNT_SHIFT 2 #define TEGRA_APBDMA_STATUS_COUNT_MASK 0xFFFC +#define TEGRA_APBDMA_CHAN_CSRE 0x00C +#define TEGRA_APBDMA_CHAN_CSRE_PAUSE (1 << 31) + /* AHB memory address */ #define TEGRA_APBDMA_CHAN_AHBPTR 0x010 @@ -112,10 +115,12 @@ struct tegra_dma; * tegra_dma_chip_data Tegra chip specific DMA data * @nr_channels: Number of channels available in the controller. * @max_dma_count: Maximum DMA transfer count supported by DMA controller. + * @support_channel_pause: Support channel wise pause of dma. */ struct tegra_dma_chip_data { int nr_channels; int max_dma_count; + bool support_channel_pause; }; /* DMA channel registers */ @@ -353,6 +358,32 @@ static void tegra_dma_global_resume(struct tegra_dma_channel *tdc) spin_unlock(&tdma->global_lock); } +static void tegra_dma_pause(struct tegra_dma_channel *tdc, + bool wait_for_burst_complete) +{ + struct tegra_dma *tdma = tdc->tdma; + + if (tdma->chip_data->support_channel_pause) { + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSRE, + TEGRA_APBDMA_CHAN_CSRE_PAUSE); + if (wait_for_burst_complete) + udelay(TEGRA_APBDMA_BURST_COMPLETE_TIME); + } else { + tegra_dma_global_pause(tdc, wait_for_burst_complete); + } +} + +static void tegra_dma_resume(struct tegra_dma_channel *tdc) +{ + struct tegra_dma *tdma = tdc->tdma; + + if (tdma->chip_data->support_channel_pause) { + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSRE, 0); + } else { + tegra_dma_global_resume(tdc); + } +} + static void tegra_dma_stop(struct tegra_dma_channel *tdc) { u32 csr; @@ -408,7 +439,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, * If there is already IEC status then interrupt handler need to * load new configuration. */ - tegra_dma_global_pause(tdc, false); + tegra_dma_pause(tdc, false); status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); /* @@ -418,7 +449,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { dev_err(tdc2dev(tdc), "Skipping new configuration as interrupt is pending\n"); - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); return; } @@ -429,7 +460,7 @@ static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, nsg_req->ch_regs.csr | TEGRA_APBDMA_CSR_ENB); nsg_req->configured = true; - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); } static void tdc_start_head_req(struct tegra_dma_channel *tdc) @@ -690,7 +721,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc) goto skip_dma_stop; /* Pause DMA before checking the queue status */ - tegra_dma_global_pause(tdc, true); + tegra_dma_pause(tdc, true); status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { @@ -708,7 +739,7 @@ static void tegra_dma_terminate_all(struct dma_chan *dc) sgreq->dma_desc->bytes_transferred += get_current_xferred_count(tdc, sgreq, status); } - tegra_dma_global_resume(tdc); + tegra_dma_resume(tdc); skip_dma_stop: tegra_dma_abort_all(tdc); @@ -1175,6 +1206,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc) static const struct tegra_dma_chip_data tegra20_dma_chip_data = { .nr_channels = 16, .max_dma_count = 1024UL * 64, + .support_channel_pause = false, }; #if defined(CONFIG_OF) @@ -1182,6 +1214,7 @@ static const struct tegra_dma_chip_data tegra20_dma_chip_data = { static const struct tegra_dma_chip_data tegra30_dma_chip_data = { .nr_channels = 32, .max_dma_count = 1024UL * 64, + .support_channel_pause = false, }; static const struct of_device_id tegra_dma_of_match[] __devinitconst = { From 5ea7caf30debefc1c4319f77146288fd5e92a803 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 21:52:03 +0530 Subject: [PATCH 0696/4607] dma: tegra: add support for Tegra114 SoC NVIDIA's Tegra114 has APB DMA controller which has 32 dma channels and support support channel wise pause control. Add support for Tegra114 which uses the channel wise pause control hardware feature. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 2c46ac46e7ad..6c144814a896 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1217,8 +1217,19 @@ static const struct tegra_dma_chip_data tegra30_dma_chip_data = { .support_channel_pause = false, }; -static const struct of_device_id tegra_dma_of_match[] __devinitconst = { +/* Tegra114 specific DMA controller information */ +static const struct tegra_dma_chip_data tegra114_dma_chip_data = { + .nr_channels = 32, + .max_dma_count = 1024UL * 64, + .support_channel_pause = true, +}; + + +static const struct of_device_id tegra_dma_of_match[] = { { + .compatible = "nvidia,tegra114-apbdma", + .data = &tegra114_dma_chip_data, + }, { .compatible = "nvidia,tegra30-apbdma", .data = &tegra30_dma_chip_data, }, { From f2ad6992546674e5915a34a1bc36dcdd8fb29bd2 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 7 Jan 2013 23:48:39 -0200 Subject: [PATCH 0697/4607] dma: mxs-dma: Fix build warnings with W=1 Fix the following warnings when building with W=1 option: drivers/dma/mxs-dma.c: In function 'mxs_dma_alloc_chan_resources': drivers/dma/mxs-dma.c:368:25: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c: In function 'mxs_dma_prep_slave_sg': drivers/dma/mxs-dma.c:481:17: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c:494:3: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c:515:14: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] drivers/dma/mxs-dma.c: In function 'mxs_dma_prep_dma_cyclic': drivers/dma/mxs-dma.c:563:13: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] Signed-off-by: Fabio Estevam Signed-off-by: Vinod Koul --- drivers/dma/mxs-dma.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 9f02e794b12b..8f6d30d37c45 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -109,7 +109,7 @@ struct mxs_dma_chan { struct dma_chan chan; struct dma_async_tx_descriptor desc; struct tasklet_struct tasklet; - int chan_irq; + unsigned int chan_irq; struct mxs_dma_ccw *ccw; dma_addr_t ccw_phys; int desc_count; @@ -441,7 +441,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; struct mxs_dma_ccw *ccw; struct scatterlist *sg; - int i, j; + u32 i, j; u32 *pio; bool append = flags & DMA_PREP_INTERRUPT; int idx = append ? mxs_chan->desc_count : 0; @@ -537,8 +537,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; - int num_periods = buf_len / period_len; - int i = 0, buf = 0; + u32 num_periods = buf_len / period_len; + u32 i = 0, buf = 0; if (mxs_chan->status == DMA_IN_PROGRESS) return NULL; From 9421bade0765d8ffb86b8a99213b611278a3542a Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 8 Jan 2013 16:36:42 +0100 Subject: [PATCH 0698/4607] pwm: atmel: add Timer Counter Block PWM driver This patch adds a PWM driver based on Atmel Timer Counter Block. The Timer Counter Block is used in Waveform generator mode. A Timer Counter Block provides up to 6 PWM devices grouped by 2: * group 0 = PWM 0 and 1 * group 1 = PWM 2 and 3 * group 2 = PMW 4 and 5 PWM devices in a given group must be configured with the same period value. If a PWM device in a group tries to change the period value and the other device is already configured with a different value an error will be returned. This driver requires device tree support. The Timer Counter Block number used to create a PWM chip is given by the tc-block field in an "atmel,tcb-pwm" compatible node. This patch was tested on kizbox board (at91sam9g20 SoC) with pwm-leds. Signed-off-by: Boris BREZILLON Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/atmel-tcb-pwm.txt | 18 + drivers/pwm/Kconfig | 12 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-atmel-tcb.c | 445 ++++++++++++++++++ 4 files changed, 476 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt create mode 100644 drivers/pwm/pwm-atmel-tcb.c diff --git a/Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt b/Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt new file mode 100644 index 000000000000..de0eaed86651 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/atmel-tcb-pwm.txt @@ -0,0 +1,18 @@ +Atmel TCB PWM controller + +Required properties: +- compatible: should be "atmel,tcb-pwm" +- #pwm-cells: Should be 3. The first cell specifies the per-chip index + of the PWM to use, the second cell is the period in nanoseconds and + bit 0 in the third cell is used to encode the polarity of PWM output. + Set bit 0 of the third cell in PWM specifier to 1 for inverse polarity & + set to 0 for normal polarity. +- tc-block: The Timer Counter block to use as a PWM chip. + +Example: + +pwm { + compatible = "atmel,tcb-pwm"; + #pwm-cells = <3>; + tc-block = <1>; +}; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e513cd998170..10b6afc94bc3 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -37,6 +37,18 @@ config PWM_AB8500 To compile this driver as a module, choose M here: the module will be called pwm-ab8500. +config PWM_ATMEL_TCB + tristate "TC Block PWM" + depends on ATMEL_TCLIB && OF + help + Generic PWM framework driver for Atmel Timer Counter Block. + + A Timer Counter Block provides 6 PWM devices grouped by 2. + Devices in a given group must have the same period. + + To compile this driver as a module, choose M here: the module + will be called pwm-atmel-tcb. + config PWM_BFIN tristate "Blackfin PWM support" depends on BFIN_GPTIMERS diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 62a2963cfe58..94ba21e24bd6 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o +obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c new file mode 100644 index 000000000000..16cb53092857 --- /dev/null +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -0,0 +1,445 @@ +/* + * Copyright (C) Overkiz SAS 2012 + * + * Author: Boris BREZILLON + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NPWM 6 + +#define ATMEL_TC_ACMR_MASK (ATMEL_TC_ACPA | ATMEL_TC_ACPC | \ + ATMEL_TC_AEEVT | ATMEL_TC_ASWTRG) + +#define ATMEL_TC_BCMR_MASK (ATMEL_TC_BCPB | ATMEL_TC_BCPC | \ + ATMEL_TC_BEEVT | ATMEL_TC_BSWTRG) + +struct atmel_tcb_pwm_device { + enum pwm_polarity polarity; /* PWM polarity */ + unsigned div; /* PWM clock divider */ + unsigned duty; /* PWM duty expressed in clk cycles */ + unsigned period; /* PWM period expressed in clk cycles */ +}; + +struct atmel_tcb_pwm_chip { + struct pwm_chip chip; + spinlock_t lock; + struct atmel_tc *tc; + struct atmel_tcb_pwm_device *pwms[NPWM]; +}; + +static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct atmel_tcb_pwm_chip, chip); +} + +static int atmel_tcb_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + + tcbpwm->polarity = polarity; + + return 0; +} + +static int atmel_tcb_pwm_request(struct pwm_chip *chip, + struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm; + struct atmel_tc *tc = tcbpwmc->tc; + void __iomem *regs = tc->regs; + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + unsigned cmr; + int ret; + + tcbpwm = devm_kzalloc(chip->dev, sizeof(*tcbpwm), GFP_KERNEL); + if (!tcbpwm) + return -ENOMEM; + + ret = clk_enable(tc->clk[group]); + if (ret) { + devm_kfree(chip->dev, tcbpwm); + return ret; + } + + pwm_set_chip_data(pwm, tcbpwm); + tcbpwm->polarity = PWM_POLARITY_NORMAL; + tcbpwm->duty = 0; + tcbpwm->period = 0; + tcbpwm->div = 0; + + spin_lock(&tcbpwmc->lock); + cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + /* + * Get init config from Timer Counter registers if + * Timer Counter is already configured as a PWM generator. + */ + if (cmr & ATMEL_TC_WAVE) { + if (index == 0) + tcbpwm->duty = + __raw_readl(regs + ATMEL_TC_REG(group, RA)); + else + tcbpwm->duty = + __raw_readl(regs + ATMEL_TC_REG(group, RB)); + + tcbpwm->div = cmr & ATMEL_TC_TCCLKS; + tcbpwm->period = __raw_readl(regs + ATMEL_TC_REG(group, RC)); + cmr &= (ATMEL_TC_TCCLKS | ATMEL_TC_ACMR_MASK | + ATMEL_TC_BCMR_MASK); + } else + cmr = 0; + + cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0; + __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); + spin_unlock(&tcbpwmc->lock); + + tcbpwmc->pwms[pwm->hwpwm] = tcbpwm; + + return 0; +} + +static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + struct atmel_tc *tc = tcbpwmc->tc; + + clk_disable(tc->clk[pwm->hwpwm / 2]); + tcbpwmc->pwms[pwm->hwpwm] = NULL; + devm_kfree(chip->dev, tcbpwm); +} + +static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + struct atmel_tc *tc = tcbpwmc->tc; + void __iomem *regs = tc->regs; + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + unsigned cmr; + enum pwm_polarity polarity = tcbpwm->polarity; + + /* + * If duty is 0 the timer will be stopped and we have to + * configure the output correctly on software trigger: + * - set output to high if PWM_POLARITY_INVERSED + * - set output to low if PWM_POLARITY_NORMAL + * + * This is why we're reverting polarity in this case. + */ + if (tcbpwm->duty == 0) + polarity = !polarity; + + spin_lock(&tcbpwmc->lock); + cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + + /* flush old setting and set the new one */ + if (index == 0) { + cmr &= ~ATMEL_TC_ACMR_MASK; + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_ASWTRG_CLEAR; + else + cmr |= ATMEL_TC_ASWTRG_SET; + } else { + cmr &= ~ATMEL_TC_BCMR_MASK; + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_BSWTRG_CLEAR; + else + cmr |= ATMEL_TC_BSWTRG_SET; + } + + __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); + + /* + * Use software trigger to apply the new setting. + * If both PWM devices in this group are disabled we stop the clock. + */ + if (!(cmr & (ATMEL_TC_ACPC | ATMEL_TC_BCPC))) + __raw_writel(ATMEL_TC_SWTRG | ATMEL_TC_CLKDIS, + regs + ATMEL_TC_REG(group, CCR)); + else + __raw_writel(ATMEL_TC_SWTRG, regs + + ATMEL_TC_REG(group, CCR)); + + spin_unlock(&tcbpwmc->lock); +} + +static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + struct atmel_tc *tc = tcbpwmc->tc; + void __iomem *regs = tc->regs; + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + u32 cmr; + enum pwm_polarity polarity = tcbpwm->polarity; + + /* + * If duty is 0 the timer will be stopped and we have to + * configure the output correctly on software trigger: + * - set output to high if PWM_POLARITY_INVERSED + * - set output to low if PWM_POLARITY_NORMAL + * + * This is why we're reverting polarity in this case. + */ + if (tcbpwm->duty == 0) + polarity = !polarity; + + spin_lock(&tcbpwmc->lock); + cmr = __raw_readl(regs + ATMEL_TC_REG(group, CMR)); + + /* flush old setting and set the new one */ + cmr &= ~ATMEL_TC_TCCLKS; + + if (index == 0) { + cmr &= ~ATMEL_TC_ACMR_MASK; + + /* Set CMR flags according to given polarity */ + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_ASWTRG_CLEAR; + else + cmr |= ATMEL_TC_ASWTRG_SET; + } else { + cmr &= ~ATMEL_TC_BCMR_MASK; + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_BSWTRG_CLEAR; + else + cmr |= ATMEL_TC_BSWTRG_SET; + } + + /* + * If duty is 0 or equal to period there's no need to register + * a specific action on RA/RB and RC compare. + * The output will be configured on software trigger and keep + * this config till next config call. + */ + if (tcbpwm->duty != tcbpwm->period && tcbpwm->duty > 0) { + if (index == 0) { + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_ACPA_SET | ATMEL_TC_ACPC_CLEAR; + else + cmr |= ATMEL_TC_ACPA_CLEAR | ATMEL_TC_ACPC_SET; + } else { + if (polarity == PWM_POLARITY_INVERSED) + cmr |= ATMEL_TC_BCPB_SET | ATMEL_TC_BCPC_CLEAR; + else + cmr |= ATMEL_TC_BCPB_CLEAR | ATMEL_TC_BCPC_SET; + } + } + + __raw_writel(cmr, regs + ATMEL_TC_REG(group, CMR)); + + if (index == 0) + __raw_writel(tcbpwm->duty, regs + ATMEL_TC_REG(group, RA)); + else + __raw_writel(tcbpwm->duty, regs + ATMEL_TC_REG(group, RB)); + + __raw_writel(tcbpwm->period, regs + ATMEL_TC_REG(group, RC)); + + /* Use software trigger to apply the new setting */ + __raw_writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, + regs + ATMEL_TC_REG(group, CCR)); + spin_unlock(&tcbpwmc->lock); + return 0; +} + +static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); + struct atmel_tcb_pwm_device *tcbpwm = pwm_get_chip_data(pwm); + unsigned group = pwm->hwpwm / 2; + unsigned index = pwm->hwpwm % 2; + struct atmel_tcb_pwm_device *atcbpwm = NULL; + struct atmel_tc *tc = tcbpwmc->tc; + int i; + int slowclk = 0; + unsigned period; + unsigned duty; + unsigned rate = clk_get_rate(tc->clk[group]); + unsigned long long min; + unsigned long long max; + + /* + * Find best clk divisor: + * the smallest divisor which can fulfill the period_ns requirements. + */ + for (i = 0; i < 5; ++i) { + if (atmel_tc_divisors[i] == 0) { + slowclk = i; + continue; + } + min = div_u64((u64)NSEC_PER_SEC * atmel_tc_divisors[i], rate); + max = min << tc->tcb_config->counter_width; + if (max >= period_ns) + break; + } + + /* + * If none of the divisor are small enough to represent period_ns + * take slow clock (32KHz). + */ + if (i == 5) { + i = slowclk; + rate = 32768; + min = div_u64(NSEC_PER_SEC, rate); + max = min << 16; + + /* If period is too big return ERANGE error */ + if (max < period_ns) + return -ERANGE; + } + + duty = div_u64(duty_ns, min); + period = div_u64(period_ns, min); + + if (index == 0) + atcbpwm = tcbpwmc->pwms[pwm->hwpwm + 1]; + else + atcbpwm = tcbpwmc->pwms[pwm->hwpwm - 1]; + + /* + * PWM devices provided by TCB driver are grouped by 2: + * - group 0: PWM 0 & 1 + * - group 1: PWM 2 & 3 + * - group 2: PWM 4 & 5 + * + * PWM devices in a given group must be configured with the + * same period_ns. + * + * We're checking the period value of the second PWM device + * in this group before applying the new config. + */ + if ((atcbpwm && atcbpwm->duty > 0 && + atcbpwm->duty != atcbpwm->period) && + (atcbpwm->div != i || atcbpwm->period != period)) { + dev_err(chip->dev, + "failed to configure period_ns: PWM group already configured with a different value\n"); + return -EINVAL; + } + + tcbpwm->period = period; + tcbpwm->div = i; + tcbpwm->duty = duty; + + /* If the PWM is enabled, call enable to apply the new conf */ + if (test_bit(PWMF_ENABLED, &pwm->flags)) + atmel_tcb_pwm_enable(chip, pwm); + + return 0; +} + +static const struct pwm_ops atmel_tcb_pwm_ops = { + .request = atmel_tcb_pwm_request, + .free = atmel_tcb_pwm_free, + .config = atmel_tcb_pwm_config, + .set_polarity = atmel_tcb_pwm_set_polarity, + .enable = atmel_tcb_pwm_enable, + .disable = atmel_tcb_pwm_disable, +}; + +static int atmel_tcb_pwm_probe(struct platform_device *pdev) +{ + struct atmel_tcb_pwm_chip *tcbpwm; + struct device_node *np = pdev->dev.of_node; + struct atmel_tc *tc; + int err; + int tcblock; + + err = of_property_read_u32(np, "tc-block", &tcblock); + if (err < 0) { + dev_err(&pdev->dev, + "failed to get Timer Counter Block number from device tree (error: %d)\n", + err); + return err; + } + + tc = atmel_tc_alloc(tcblock, "tcb-pwm"); + if (tc == NULL) { + dev_err(&pdev->dev, "failed to allocate Timer Counter Block\n"); + return -ENOMEM; + } + + tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL); + if (tcbpwm == NULL) { + atmel_tc_free(tc); + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + tcbpwm->chip.dev = &pdev->dev; + tcbpwm->chip.ops = &atmel_tcb_pwm_ops; + tcbpwm->chip.of_xlate = of_pwm_xlate_with_flags; + tcbpwm->chip.of_pwm_n_cells = 3; + tcbpwm->chip.base = -1; + tcbpwm->chip.npwm = NPWM; + tcbpwm->tc = tc; + + spin_lock_init(&tcbpwm->lock); + + err = pwmchip_add(&tcbpwm->chip); + if (err < 0) { + atmel_tc_free(tc); + return err; + } + + platform_set_drvdata(pdev, tcbpwm); + + return 0; +} + +static int atmel_tcb_pwm_remove(struct platform_device *pdev) +{ + struct atmel_tcb_pwm_chip *tcbpwm = platform_get_drvdata(pdev); + int err; + + err = pwmchip_remove(&tcbpwm->chip); + if (err < 0) + return err; + + atmel_tc_free(tcbpwm->tc); + + return 0; +} + +static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { + { .compatible = "atmel,tcb-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids); + +static struct platform_driver atmel_tcb_pwm_driver = { + .driver = { + .name = "atmel-tcb-pwm", + .of_match_table = atmel_tcb_pwm_dt_ids, + }, + .probe = atmel_tcb_pwm_probe, + .remove = atmel_tcb_pwm_remove, +}; +module_platform_driver(atmel_tcb_pwm_driver); + +MODULE_AUTHOR("Boris BREZILLON "); +MODULE_DESCRIPTION("Atmel Timer Counter Pulse Width Modulation Driver"); +MODULE_LICENSE("GPL v2"); From b0cfeb5dedf9109b8a8bd594abe60791b135a643 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Tue, 8 Jan 2013 10:49:00 +0200 Subject: [PATCH 0699/4607] KVM: x86: remove unused variable from walk_addr_generic() Fix compilation warning. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/paging_tmpl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index a7b24cf59a3c..2ad76b981119 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -151,7 +151,7 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, pt_element_t pte; pt_element_t __user *uninitialized_var(ptep_user); gfn_t table_gfn; - unsigned index, pt_access, pte_access, accessed_dirty, shift; + unsigned index, pt_access, pte_access, accessed_dirty; gpa_t pte_gpa; int offset; const int write_fault = access & PFERR_WRITE_MASK; From b09408d00fd82be80289a329dd94d1a0d6b77dc2 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Mon, 7 Jan 2013 19:27:06 -0200 Subject: [PATCH 0700/4607] KVM: VMX: fix incorrect cached cpl value with real/v8086 modes CPL is always 0 when in real mode, and always 3 when virtual 8086 mode. Using values other than those can cause failures on operations that check CPL. Reviewed-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 55dfc375f1ab..dd2a85c1c6f0 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -1696,7 +1696,6 @@ static unsigned long vmx_get_rflags(struct kvm_vcpu *vcpu) static void vmx_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags) { __set_bit(VCPU_EXREG_RFLAGS, (ulong *)&vcpu->arch.regs_avail); - __clear_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail); to_vmx(vcpu)->rflags = rflags; if (to_vmx(vcpu)->rmode.vm86_active) { to_vmx(vcpu)->rmode.save_rflags = rflags; @@ -3110,7 +3109,6 @@ static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) vmcs_writel(CR0_READ_SHADOW, cr0); vmcs_writel(GUEST_CR0, hw_cr0); vcpu->arch.cr0 = cr0; - __clear_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail); } static u64 construct_eptp(unsigned long root_hpa) @@ -3220,8 +3218,10 @@ static u64 vmx_get_segment_base(struct kvm_vcpu *vcpu, int seg) return vmx_read_guest_seg_base(to_vmx(vcpu), seg); } -static int __vmx_get_cpl(struct kvm_vcpu *vcpu) +static int vmx_get_cpl(struct kvm_vcpu *vcpu) { + struct vcpu_vmx *vmx = to_vmx(vcpu); + if (!is_protmode(vcpu)) return 0; @@ -3229,13 +3229,6 @@ static int __vmx_get_cpl(struct kvm_vcpu *vcpu) && (kvm_get_rflags(vcpu) & X86_EFLAGS_VM)) /* if virtual 8086 */ return 3; - return vmx_read_guest_seg_selector(to_vmx(vcpu), VCPU_SREG_CS) & 3; -} - -static int vmx_get_cpl(struct kvm_vcpu *vcpu) -{ - struct vcpu_vmx *vmx = to_vmx(vcpu); - /* * If we enter real mode with cs.sel & 3 != 0, the normal CPL calculations * fail; use the cache instead. @@ -3246,7 +3239,7 @@ static int vmx_get_cpl(struct kvm_vcpu *vcpu) if (!test_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail)) { __set_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail); - vmx->cpl = __vmx_get_cpl(vcpu); + vmx->cpl = vmx_read_guest_seg_selector(vmx, VCPU_SREG_CS) & 3; } return vmx->cpl; From 8ab432caa46413c9f3ca81d82ea9fa5bae07c3c1 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Thu, 3 Jan 2013 08:44:15 +1300 Subject: [PATCH 0701/4607] pwm: vt8500: Register write busy test performed incorrectly Correct operation for register writes is to perform a busy-wait after writing the register. Currently the busy wait it performed before, meaning subsequent register writes to bitfields may occur before the previous field has been updated. Also, all registers are defined as 32-bit read/write. Change pwm_busy_wait() to use readl rather than readb. Improve readability of code with defines for registers and bitfields. Signed-off-by: Tony Prisk Signed-off-by: Thierry Reding --- drivers/pwm/pwm-vt8500.c | 64 ++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index b0ba2d403439..bbc37504103a 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -36,6 +36,25 @@ */ #define VT8500_NR_PWMS 2 +#define REG_CTRL(pwm) (((pwm) << 4) + 0x00) +#define REG_SCALAR(pwm) (((pwm) << 4) + 0x04) +#define REG_PERIOD(pwm) (((pwm) << 4) + 0x08) +#define REG_DUTY(pwm) (((pwm) << 4) + 0x0C) +#define REG_STATUS 0x40 + +#define CTRL_ENABLE BIT(0) +#define CTRL_INVERT BIT(1) +#define CTRL_AUTOLOAD BIT(2) +#define CTRL_STOP_IMM BIT(3) +#define CTRL_LOAD_PRESCALE BIT(4) +#define CTRL_LOAD_PERIOD BIT(5) + +#define STATUS_CTRL_UPDATE BIT(0) +#define STATUS_SCALAR_UPDATE BIT(1) +#define STATUS_PERIOD_UPDATE BIT(2) +#define STATUS_DUTY_UPDATE BIT(3) +#define STATUS_ALL_UPDATE 0x0F + struct vt8500_chip { struct pwm_chip chip; void __iomem *base; @@ -45,15 +64,17 @@ struct vt8500_chip { #define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip) #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) -static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask) +static inline void pwm_busy_wait(struct vt8500_chip *vt8500, int nr, u8 bitmask) { int loops = msecs_to_loops(10); - while ((readb(reg) & bitmask) && --loops) + u32 mask = bitmask << (nr << 8); + + while ((readl(vt8500->base + REG_STATUS) & mask) && --loops) cpu_relax(); if (unlikely(!loops)) - pr_warn("Waiting for status bits 0x%x to clear timed out\n", - bitmask); + dev_warn(vt8500->chip.dev, "Waiting for status bits 0x%x to clear timed out\n", + mask); } static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, @@ -63,6 +84,7 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long c; unsigned long period_cycles, prescale, pv, dc; int err; + u32 val; err = clk_enable(vt8500->clk); if (err < 0) { @@ -91,14 +113,19 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, do_div(c, period_ns); dc = c; - pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 1)); - writel(prescale, vt8500->base + 0x4 + (pwm->hwpwm << 4)); + writel(prescale, vt8500->base + REG_SCALAR(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_SCALAR_UPDATE); - pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 2)); - writel(pv, vt8500->base + 0x8 + (pwm->hwpwm << 4)); + writel(pv, vt8500->base + REG_PERIOD(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_PERIOD_UPDATE); - pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 3)); - writel(dc, vt8500->base + 0xc + (pwm->hwpwm << 4)); + writel(dc, vt8500->base + REG_DUTY(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_DUTY_UPDATE); + + val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); + val |= CTRL_AUTOLOAD; + writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); clk_disable(vt8500->clk); return 0; @@ -106,8 +133,9 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { - int err; struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + int err; + u32 val; err = clk_enable(vt8500->clk); if (err < 0) { @@ -115,17 +143,23 @@ static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return err; } - pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); - writel(5, vt8500->base + (pwm->hwpwm << 4)); + val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); + val |= CTRL_ENABLE; + writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + return 0; } static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + u32 val; - pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); - writel(0, vt8500->base + (pwm->hwpwm << 4)); + val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); + val &= ~CTRL_ENABLE; + writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); clk_disable(vt8500->clk); } From 3ccb1c1702ed4bb07006d20c8173899a69dae242 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Thu, 3 Jan 2013 08:44:16 +1300 Subject: [PATCH 0702/4607] pwm: vt8500: Add polarity support Add support to set polarity on PWM devices, allowing for inverted duty cycles. Also update the binding document to #pwm-cells = <3> to allow passing the flags from devicetree. Signed-off-by: Tony Prisk Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/vt8500-pwm.txt | 9 +++++--- drivers/pwm/pwm-vt8500.c | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt index bcc63678a9a5..d21d82d29855 100644 --- a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt +++ b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt @@ -3,14 +3,17 @@ VIA/Wondermedia VT8500/WM8xxx series SoC PWM controller Required properties: - compatible: should be "via,vt8500-pwm" - reg: physical base address and length of the controller's registers -- #pwm-cells: should be 2. The first cell specifies the per-chip index - of the PWM to use and the second cell is the period in nanoseconds. +- #pwm-cells: Should be 3. Number of cells being used to specify PWM property. + First cell specifies the per-chip index of the PWM to use, the second + cell is the period in nanoseconds and bit 0 in the third cell is used to + encode the polarity of PWM output. Set bit 0 of the third in PWM specifier + to 1 for inverse polarity & set to 0 for normal polarity. - clocks: phandle to the PWM source clock Example: pwm1: pwm@d8220000 { - #pwm-cells = <2>; + #pwm-cells = <3>; compatible = "via,vt8500-pwm"; reg = <0xd8220000 0x1000>; clocks = <&clkpwm>; diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index bbc37504103a..98d79e9f0144 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -164,10 +164,31 @@ static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) clk_disable(vt8500->clk); } +static int vt8500_pwm_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct vt8500_chip *vt8500 = to_vt8500_chip(chip); + u32 val; + + val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); + + if (polarity == PWM_POLARITY_INVERSED) + val |= CTRL_INVERT; + else + val &= ~CTRL_INVERT; + + writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); + pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + + return 0; +} + static struct pwm_ops vt8500_pwm_ops = { .enable = vt8500_pwm_enable, .disable = vt8500_pwm_disable, .config = vt8500_pwm_config, + .set_polarity = vt8500_pwm_set_polarity, .owner = THIS_MODULE, }; @@ -197,6 +218,8 @@ static int vt8500_pwm_probe(struct platform_device *pdev) chip->chip.dev = &pdev->dev; chip->chip.ops = &vt8500_pwm_ops; + chip->chip.of_xlate = of_pwm_xlate_with_flags; + chip->chip.of_pwm_n_cells = 3; chip->chip.base = -1; chip->chip.npwm = VT8500_NR_PWMS; From 123de543414bce42da9729071962d4a9512612c8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:01 +0200 Subject: [PATCH 0703/4607] dw_dmac: absence of pdata isn't critical when autocfg is set The patch allows to probe the device when platform data is absent and hardware auto configuration is enabled. In that case the default platform data is used where the channel allocation order is set to ascending, channel priority is set to ascending, and private property is set to true. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 476e9c8fb6ca..c0c9370ddd3f 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1600,13 +1600,6 @@ static int dw_probe(struct platform_device *pdev) int err; int i; - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) - pdata = dw_dma_parse_dt(pdev); - - if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) - return -EINVAL; - io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) return -EINVAL; @@ -1622,6 +1615,22 @@ static int dw_probe(struct platform_device *pdev) dw_params = dma_read_byaddr(regs, DW_PARAMS); autocfg = dw_params >> DW_PARAMS_EN & 0x1; + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) + pdata = dw_dma_parse_dt(pdev); + + if (!pdata && autocfg) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + /* Fill platform data with the default values */ + pdata->is_private = true; + pdata->chan_allocation_order = CHAN_ALLOCATION_ASCENDING; + pdata->chan_priority = CHAN_PRIORITY_ASCENDING; + } else if (!pdata || pdata->nr_channels > DW_DMA_MAX_NR_CHANNELS) + return -EINVAL; + if (autocfg) nr_channels = (dw_params >> DW_PARAMS_NR_CHAN & 0x7) + 1; else From cbd65312ba6b508e994d40729e84a51301870bcc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:11 +0200 Subject: [PATCH 0704/4607] dw_dmac: check for mapping errors Otherwise we get a warning in case of CONFIG_DMA_API_DEBUG=y [ 45.775943] WARNING: at lib/dma-debug.c:933 check_unmap+0x5d6/0x6ac() [ 45.782369] dw_dmac dw_dmac.0: DMA-API: device driver failed to check map error[device address=0x00000000356efcc0] [size=28 bytes] [mapped as single] Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index c0c9370ddd3f..dd7a8192d5a0 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1087,6 +1087,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) struct dw_desc *desc; int i; unsigned long flags; + int ret; dev_vdbg(chan2dev(chan), "%s\n", __func__); @@ -1110,12 +1111,8 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) spin_unlock_irqrestore(&dwc->lock, flags); desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); - if (!desc) { - dev_info(chan2dev(chan), - "only allocated %d descriptors\n", i); - spin_lock_irqsave(&dwc->lock, flags); - break; - } + if (!desc) + goto err_desc_alloc; INIT_LIST_HEAD(&desc->tx_list); dma_async_tx_descriptor_init(&desc->txd, chan); @@ -1123,6 +1120,10 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) desc->txd.flags = DMA_CTRL_ACK; desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli, sizeof(desc->lli), DMA_TO_DEVICE); + ret = dma_mapping_error(chan2parent(chan), desc->txd.phys); + if (ret) + goto err_desc_alloc; + dwc_desc_put(dwc, desc); spin_lock_irqsave(&dwc->lock, flags); @@ -1133,6 +1134,13 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); + return i; + +err_desc_alloc: + kfree(desc); + + dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); + return i; } From 21e93c1e7dae0e8b1914a522c331f0f7763fa89d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:12 +0200 Subject: [PATCH 0705/4607] dw_dmac: remove redundant check There is no need to check the callback_required parameter, due to we check the callback pointer to be a non-NULL. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index dd7a8192d5a0..c74a7ec9bb9f 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -347,7 +347,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, spin_unlock_irqrestore(&dwc->lock, flags); - if (callback_required && callback) + if (callback) callback(param); } From f5c6a7df35b04db906577e90fa5e133e56433bcf Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:13 +0200 Subject: [PATCH 0706/4607] dw_dmac: update tx_node_active in dwc_do_single_block The "else" keyword in the dw_dma_tasklet is removed as well. All together simplifies the logic of the code and understanding of what is happening there. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index c74a7ec9bb9f..d6b322a1f565 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -247,6 +247,9 @@ static inline void dwc_do_single_block(struct dw_dma_chan *dwc, channel_writel(dwc, CTL_LO, ctllo); channel_writel(dwc, CTL_HI, desc->lli.ctlhi); channel_set_bit(dw, CH_EN, dwc->mask); + + /* Move pointer to next descriptor */ + dwc->tx_node_active = dwc->tx_node_active->next; } /* Called with dwc->lock held and bh disabled */ @@ -278,7 +281,7 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) dwc_initialize(dwc); dwc->tx_list = &first->tx_list; - dwc->tx_node_active = first->tx_list.next; + dwc->tx_node_active = &first->tx_list; dwc_do_single_block(dwc, first); @@ -604,18 +607,13 @@ static void dw_dma_tasklet(unsigned long data) dma_writel(dw, CLEAR.XFER, dwc->mask); - /* move pointer to next descriptor */ - dwc->tx_node_active = - dwc->tx_node_active->next; - dwc_do_single_block(dwc, desc); spin_unlock_irqrestore(&dwc->lock, flags); continue; - } else { - /* we are done here */ - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); } + /* we are done here */ + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); } spin_unlock_irqrestore(&dwc->lock, flags); From 21fe3c5245647d200a7ba25d42b80d21c578a8dc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Jan 2013 10:17:14 +0200 Subject: [PATCH 0707/4607] dma: dw_dmac: add dwc_chan_pause and dwc_chan_resume We will use at least the dwc_chan_resume() later. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index d6b322a1f565..6c9e20a7ff51 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -988,6 +988,26 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) return 0; } +static inline void dwc_chan_pause(struct dw_dma_chan *dwc) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); + while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY)) + cpu_relax(); + + dwc->paused = true; +} + +static inline void dwc_chan_resume(struct dw_dma_chan *dwc) +{ + u32 cfglo = channel_readl(dwc, CFG_LO); + + channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); + + dwc->paused = false; +} + static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { @@ -995,18 +1015,13 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, struct dw_dma *dw = to_dw_dma(chan->device); struct dw_desc *desc, *_desc; unsigned long flags; - u32 cfglo; LIST_HEAD(list); if (cmd == DMA_PAUSE) { spin_lock_irqsave(&dwc->lock, flags); - cfglo = channel_readl(dwc, CFG_LO); - channel_writel(dwc, CFG_LO, cfglo | DWC_CFGL_CH_SUSP); - while (!(channel_readl(dwc, CFG_LO) & DWC_CFGL_FIFO_EMPTY)) - cpu_relax(); + dwc_chan_pause(dwc); - dwc->paused = true; spin_unlock_irqrestore(&dwc->lock, flags); } else if (cmd == DMA_RESUME) { if (!dwc->paused) @@ -1014,9 +1029,7 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, spin_lock_irqsave(&dwc->lock, flags); - cfglo = channel_readl(dwc, CFG_LO); - channel_writel(dwc, CFG_LO, cfglo & ~DWC_CFGL_CH_SUSP); - dwc->paused = false; + dwc_chan_resume(dwc); spin_unlock_irqrestore(&dwc->lock, flags); } else if (cmd == DMA_TERMINATE_ALL) { From 3bf10fea3ba3804090e82aa59fabf25f43fa74c2 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 3 Oct 2012 16:56:56 -0400 Subject: [PATCH 0708/4607] cfq-iosched: Properly name all references to IO class Currently CFQ has three IO classes, RT, BE and IDLE. At many a places we are calling workloads belonging to these classes as "prio". This gets very confusing as one starts to associate it with ioprio. So this patch just does bunch of renaming so that reading code becomes easier. All reference to RT, BE and IDLE workload are done using keyword "class" and all references to subclass, SYNC, SYNC-IDLE, ASYNC are made using keyword "type". This makes me feel much better while I am reading the code. There is no functionality change due to this patch. Signed-off-by: Vivek Goyal Acked-by: Jeff Moyer Acked-by: Tejun Heo Signed-off-by: Tejun Heo --- block/cfq-iosched.c | 67 +++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index e62e9205b80a..7646dfddba03 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -155,7 +155,7 @@ struct cfq_queue { * First index in the service_trees. * IDLE is handled separately, so it has negative index */ -enum wl_prio_t { +enum wl_class_t { BE_WORKLOAD = 0, RT_WORKLOAD = 1, IDLE_WORKLOAD = 2, @@ -250,7 +250,7 @@ struct cfq_group { unsigned long saved_workload_slice; enum wl_type_t saved_workload; - enum wl_prio_t saved_serving_prio; + enum wl_class_t saved_serving_class; /* number of requests that are on the dispatch list or inside driver */ int dispatched; @@ -280,7 +280,7 @@ struct cfq_data { /* * The priority currently being served */ - enum wl_prio_t serving_prio; + enum wl_class_t serving_class; enum wl_type_t serving_type; unsigned long workload_expires; struct cfq_group *serving_group; @@ -354,16 +354,16 @@ struct cfq_data { static struct cfq_group *cfq_get_next_cfqg(struct cfq_data *cfqd); static struct cfq_rb_root *service_tree_for(struct cfq_group *cfqg, - enum wl_prio_t prio, + enum wl_class_t class, enum wl_type_t type) { if (!cfqg) return NULL; - if (prio == IDLE_WORKLOAD) + if (class == IDLE_WORKLOAD) return &cfqg->service_tree_idle; - return &cfqg->service_trees[prio][type]; + return &cfqg->service_trees[class][type]; } enum cfqq_state_flags { @@ -732,7 +732,7 @@ static inline bool iops_mode(struct cfq_data *cfqd) return false; } -static inline enum wl_prio_t cfqq_prio(struct cfq_queue *cfqq) +static inline enum wl_class_t cfqq_class(struct cfq_queue *cfqq) { if (cfq_class_idle(cfqq)) return IDLE_WORKLOAD; @@ -751,16 +751,16 @@ static enum wl_type_t cfqq_type(struct cfq_queue *cfqq) return SYNC_WORKLOAD; } -static inline int cfq_group_busy_queues_wl(enum wl_prio_t wl, +static inline int cfq_group_busy_queues_wl(enum wl_class_t wl_class, struct cfq_data *cfqd, struct cfq_group *cfqg) { - if (wl == IDLE_WORKLOAD) + if (wl_class == IDLE_WORKLOAD) return cfqg->service_tree_idle.count; - return cfqg->service_trees[wl][ASYNC_WORKLOAD].count - + cfqg->service_trees[wl][SYNC_NOIDLE_WORKLOAD].count - + cfqg->service_trees[wl][SYNC_WORKLOAD].count; + return cfqg->service_trees[wl_class][ASYNC_WORKLOAD].count + + cfqg->service_trees[wl_class][SYNC_NOIDLE_WORKLOAD].count + + cfqg->service_trees[wl_class][SYNC_WORKLOAD].count; } static inline int cfqg_busy_async_queues(struct cfq_data *cfqd, @@ -1304,7 +1304,7 @@ static void cfq_group_served(struct cfq_data *cfqd, struct cfq_group *cfqg, cfqg->saved_workload_slice = cfqd->workload_expires - jiffies; cfqg->saved_workload = cfqd->serving_type; - cfqg->saved_serving_prio = cfqd->serving_prio; + cfqg->saved_serving_class = cfqd->serving_class; } else cfqg->saved_workload_slice = 0; @@ -1616,7 +1616,7 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq, int left; int new_cfqq = 1; - service_tree = service_tree_for(cfqq->cfqg, cfqq_prio(cfqq), + service_tree = service_tree_for(cfqq->cfqg, cfqq_class(cfqq), cfqq_type(cfqq)); if (cfq_class_idle(cfqq)) { rb_key = CFQ_IDLE_DELAY; @@ -2030,8 +2030,8 @@ static void __cfq_set_active_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) { if (cfqq) { - cfq_log_cfqq(cfqd, cfqq, "set_active wl_prio:%d wl_type:%d", - cfqd->serving_prio, cfqd->serving_type); + cfq_log_cfqq(cfqd, cfqq, "set_active wl_class:%d wl_type:%d", + cfqd->serving_class, cfqd->serving_type); cfqg_stats_update_avg_queue_size(cfqq->cfqg); cfqq->slice_start = 0; cfqq->dispatch_start = jiffies; @@ -2118,7 +2118,7 @@ static inline void cfq_slice_expired(struct cfq_data *cfqd, bool timed_out) static struct cfq_queue *cfq_get_next_queue(struct cfq_data *cfqd) { struct cfq_rb_root *service_tree = - service_tree_for(cfqd->serving_group, cfqd->serving_prio, + service_tree_for(cfqd->serving_group, cfqd->serving_class, cfqd->serving_type); if (!cfqd->rq_queued) @@ -2285,7 +2285,7 @@ static struct cfq_queue *cfq_close_cooperator(struct cfq_data *cfqd, static bool cfq_should_idle(struct cfq_data *cfqd, struct cfq_queue *cfqq) { - enum wl_prio_t prio = cfqq_prio(cfqq); + enum wl_class_t wl_class = cfqq_class(cfqq); struct cfq_rb_root *service_tree = cfqq->service_tree; BUG_ON(!service_tree); @@ -2295,7 +2295,7 @@ static bool cfq_should_idle(struct cfq_data *cfqd, struct cfq_queue *cfqq) return false; /* We never do for idle class queues. */ - if (prio == IDLE_WORKLOAD) + if (wl_class == IDLE_WORKLOAD) return false; /* We do for queues that were marked with idle window flag. */ @@ -2495,7 +2495,7 @@ static void cfq_setup_merge(struct cfq_queue *cfqq, struct cfq_queue *new_cfqq) } static enum wl_type_t cfq_choose_wl(struct cfq_data *cfqd, - struct cfq_group *cfqg, enum wl_prio_t prio) + struct cfq_group *cfqg, enum wl_class_t wl_class) { struct cfq_queue *queue; int i; @@ -2505,7 +2505,7 @@ static enum wl_type_t cfq_choose_wl(struct cfq_data *cfqd, for (i = 0; i <= SYNC_WORKLOAD; ++i) { /* select the one with lowest rb_key */ - queue = cfq_rb_first(service_tree_for(cfqg, prio, i)); + queue = cfq_rb_first(service_tree_for(cfqg, wl_class, i)); if (queue && (!key_valid || time_before(queue->rb_key, lowest_key))) { lowest_key = queue->rb_key; @@ -2523,20 +2523,20 @@ static void choose_service_tree(struct cfq_data *cfqd, struct cfq_group *cfqg) unsigned count; struct cfq_rb_root *st; unsigned group_slice; - enum wl_prio_t original_prio = cfqd->serving_prio; + enum wl_class_t original_class = cfqd->serving_class; /* Choose next priority. RT > BE > IDLE */ if (cfq_group_busy_queues_wl(RT_WORKLOAD, cfqd, cfqg)) - cfqd->serving_prio = RT_WORKLOAD; + cfqd->serving_class = RT_WORKLOAD; else if (cfq_group_busy_queues_wl(BE_WORKLOAD, cfqd, cfqg)) - cfqd->serving_prio = BE_WORKLOAD; + cfqd->serving_class = BE_WORKLOAD; else { - cfqd->serving_prio = IDLE_WORKLOAD; + cfqd->serving_class = IDLE_WORKLOAD; cfqd->workload_expires = jiffies + 1; return; } - if (original_prio != cfqd->serving_prio) + if (original_class != cfqd->serving_class) goto new_workload; /* @@ -2544,7 +2544,7 @@ static void choose_service_tree(struct cfq_data *cfqd, struct cfq_group *cfqg) * (SYNC, SYNC_NOIDLE, ASYNC), and to compute a workload * expiration time */ - st = service_tree_for(cfqg, cfqd->serving_prio, cfqd->serving_type); + st = service_tree_for(cfqg, cfqd->serving_class, cfqd->serving_type); count = st->count; /* @@ -2556,8 +2556,8 @@ static void choose_service_tree(struct cfq_data *cfqd, struct cfq_group *cfqg) new_workload: /* otherwise select new workload type */ cfqd->serving_type = - cfq_choose_wl(cfqd, cfqg, cfqd->serving_prio); - st = service_tree_for(cfqg, cfqd->serving_prio, cfqd->serving_type); + cfq_choose_wl(cfqd, cfqg, cfqd->serving_class); + st = service_tree_for(cfqg, cfqd->serving_class, cfqd->serving_type); count = st->count; /* @@ -2568,8 +2568,9 @@ new_workload: group_slice = cfq_group_slice(cfqd, cfqg); slice = group_slice * count / - max_t(unsigned, cfqg->busy_queues_avg[cfqd->serving_prio], - cfq_group_busy_queues_wl(cfqd->serving_prio, cfqd, cfqg)); + max_t(unsigned, cfqg->busy_queues_avg[cfqd->serving_class], + cfq_group_busy_queues_wl(cfqd->serving_class, cfqd, + cfqg)); if (cfqd->serving_type == ASYNC_WORKLOAD) { unsigned int tmp; @@ -2620,7 +2621,7 @@ static void cfq_choose_cfqg(struct cfq_data *cfqd) if (cfqg->saved_workload_slice) { cfqd->workload_expires = jiffies + cfqg->saved_workload_slice; cfqd->serving_type = cfqg->saved_workload; - cfqd->serving_prio = cfqg->saved_serving_prio; + cfqd->serving_class = cfqg->saved_serving_class; } else cfqd->workload_expires = jiffies - 1; @@ -3645,7 +3646,7 @@ static void cfq_completed_request(struct request_queue *q, struct request *rq) service_tree = cfqq->service_tree; else service_tree = service_tree_for(cfqq->cfqg, - cfqq_prio(cfqq), cfqq_type(cfqq)); + cfqq_class(cfqq), cfqq_type(cfqq)); service_tree->ttime.last_end_request = now; if (!time_after(rq->start_time + cfqd->cfq_fifo_expire[1], now)) cfqd->last_delayed_sync = now; From 4d2ceea4cb86060b03b2aa4826b365320bc78651 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 3 Oct 2012 16:56:57 -0400 Subject: [PATCH 0709/4607] cfq-iosched: More renaming to better represent wl_class and wl_type Some more renaming. Again making the code uniform w.r.t use of wl_class/class to represent IO class (RT, BE, IDLE) and using wl_type/type to represent subclass (SYNC, SYNC-IDLE, ASYNC). At places this patch shortens the string "workload" to "wl". Renamed "saved_workload" to "saved_wl_type". Renamed "saved_serving_class" to "saved_wl_class". For uniformity with "saved_wl_*" variables, renamed "serving_class" to "serving_wl_class" and renamed "serving_type" to "serving_wl_type". Again, just trying to improve upon code uniformity and improve readability. No functional change. v2: - Restored the usage of keyword "service" based on Jeff Moyer's feedback. Signed-off-by: Vivek Goyal Signed-off-by: Tejun Heo --- block/cfq-iosched.c | 64 +++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 7646dfddba03..8f890bfba8fd 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -248,9 +248,9 @@ struct cfq_group { struct cfq_rb_root service_trees[2][3]; struct cfq_rb_root service_tree_idle; - unsigned long saved_workload_slice; - enum wl_type_t saved_workload; - enum wl_class_t saved_serving_class; + unsigned long saved_wl_slice; + enum wl_type_t saved_wl_type; + enum wl_class_t saved_wl_class; /* number of requests that are on the dispatch list or inside driver */ int dispatched; @@ -280,8 +280,8 @@ struct cfq_data { /* * The priority currently being served */ - enum wl_class_t serving_class; - enum wl_type_t serving_type; + enum wl_class_t serving_wl_class; + enum wl_type_t serving_wl_type; unsigned long workload_expires; struct cfq_group *serving_group; @@ -1241,7 +1241,7 @@ cfq_group_notify_queue_del(struct cfq_data *cfqd, struct cfq_group *cfqg) cfq_log_cfqg(cfqd, cfqg, "del_from_rr group"); cfq_group_service_tree_del(st, cfqg); - cfqg->saved_workload_slice = 0; + cfqg->saved_wl_slice = 0; cfqg_stats_update_dequeue(cfqg); } @@ -1301,12 +1301,12 @@ static void cfq_group_served(struct cfq_data *cfqd, struct cfq_group *cfqg, /* This group is being expired. Save the context */ if (time_after(cfqd->workload_expires, jiffies)) { - cfqg->saved_workload_slice = cfqd->workload_expires + cfqg->saved_wl_slice = cfqd->workload_expires - jiffies; - cfqg->saved_workload = cfqd->serving_type; - cfqg->saved_serving_class = cfqd->serving_class; + cfqg->saved_wl_type = cfqd->serving_wl_type; + cfqg->saved_wl_class = cfqd->serving_wl_class; } else - cfqg->saved_workload_slice = 0; + cfqg->saved_wl_slice = 0; cfq_log_cfqg(cfqd, cfqg, "served: vt=%llu min_vt=%llu", cfqg->vdisktime, st->min_vdisktime); @@ -2031,7 +2031,7 @@ static void __cfq_set_active_queue(struct cfq_data *cfqd, { if (cfqq) { cfq_log_cfqq(cfqd, cfqq, "set_active wl_class:%d wl_type:%d", - cfqd->serving_class, cfqd->serving_type); + cfqd->serving_wl_class, cfqd->serving_wl_type); cfqg_stats_update_avg_queue_size(cfqq->cfqg); cfqq->slice_start = 0; cfqq->dispatch_start = jiffies; @@ -2118,8 +2118,8 @@ static inline void cfq_slice_expired(struct cfq_data *cfqd, bool timed_out) static struct cfq_queue *cfq_get_next_queue(struct cfq_data *cfqd) { struct cfq_rb_root *service_tree = - service_tree_for(cfqd->serving_group, cfqd->serving_class, - cfqd->serving_type); + service_tree_for(cfqd->serving_group, cfqd->serving_wl_class, + cfqd->serving_wl_type); if (!cfqd->rq_queued) return NULL; @@ -2523,20 +2523,20 @@ static void choose_service_tree(struct cfq_data *cfqd, struct cfq_group *cfqg) unsigned count; struct cfq_rb_root *st; unsigned group_slice; - enum wl_class_t original_class = cfqd->serving_class; + enum wl_class_t original_class = cfqd->serving_wl_class; /* Choose next priority. RT > BE > IDLE */ if (cfq_group_busy_queues_wl(RT_WORKLOAD, cfqd, cfqg)) - cfqd->serving_class = RT_WORKLOAD; + cfqd->serving_wl_class = RT_WORKLOAD; else if (cfq_group_busy_queues_wl(BE_WORKLOAD, cfqd, cfqg)) - cfqd->serving_class = BE_WORKLOAD; + cfqd->serving_wl_class = BE_WORKLOAD; else { - cfqd->serving_class = IDLE_WORKLOAD; + cfqd->serving_wl_class = IDLE_WORKLOAD; cfqd->workload_expires = jiffies + 1; return; } - if (original_class != cfqd->serving_class) + if (original_class != cfqd->serving_wl_class) goto new_workload; /* @@ -2544,7 +2544,8 @@ static void choose_service_tree(struct cfq_data *cfqd, struct cfq_group *cfqg) * (SYNC, SYNC_NOIDLE, ASYNC), and to compute a workload * expiration time */ - st = service_tree_for(cfqg, cfqd->serving_class, cfqd->serving_type); + st = service_tree_for(cfqg, cfqd->serving_wl_class, + cfqd->serving_wl_type); count = st->count; /* @@ -2555,9 +2556,10 @@ static void choose_service_tree(struct cfq_data *cfqd, struct cfq_group *cfqg) new_workload: /* otherwise select new workload type */ - cfqd->serving_type = - cfq_choose_wl(cfqd, cfqg, cfqd->serving_class); - st = service_tree_for(cfqg, cfqd->serving_class, cfqd->serving_type); + cfqd->serving_wl_type = cfq_choose_wl(cfqd, cfqg, + cfqd->serving_wl_class); + st = service_tree_for(cfqg, cfqd->serving_wl_class, + cfqd->serving_wl_type); count = st->count; /* @@ -2568,11 +2570,11 @@ new_workload: group_slice = cfq_group_slice(cfqd, cfqg); slice = group_slice * count / - max_t(unsigned, cfqg->busy_queues_avg[cfqd->serving_class], - cfq_group_busy_queues_wl(cfqd->serving_class, cfqd, + max_t(unsigned, cfqg->busy_queues_avg[cfqd->serving_wl_class], + cfq_group_busy_queues_wl(cfqd->serving_wl_class, cfqd, cfqg)); - if (cfqd->serving_type == ASYNC_WORKLOAD) { + if (cfqd->serving_wl_type == ASYNC_WORKLOAD) { unsigned int tmp; /* @@ -2618,10 +2620,10 @@ static void cfq_choose_cfqg(struct cfq_data *cfqd) cfqd->serving_group = cfqg; /* Restore the workload type data */ - if (cfqg->saved_workload_slice) { - cfqd->workload_expires = jiffies + cfqg->saved_workload_slice; - cfqd->serving_type = cfqg->saved_workload; - cfqd->serving_class = cfqg->saved_serving_class; + if (cfqg->saved_wl_slice) { + cfqd->workload_expires = jiffies + cfqg->saved_wl_slice; + cfqd->serving_wl_type = cfqg->saved_wl_type; + cfqd->serving_wl_class = cfqg->saved_wl_class; } else cfqd->workload_expires = jiffies - 1; @@ -3404,7 +3406,7 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, return true; /* Allow preemption only if we are idling on sync-noidle tree */ - if (cfqd->serving_type == SYNC_NOIDLE_WORKLOAD && + if (cfqd->serving_wl_type == SYNC_NOIDLE_WORKLOAD && cfqq_type(new_cfqq) == SYNC_NOIDLE_WORKLOAD && new_cfqq->service_tree->count == 2 && RB_EMPTY_ROOT(&cfqq->sort_list)) @@ -3456,7 +3458,7 @@ static void cfq_preempt_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq) * doesn't happen */ if (old_type != cfqq_type(cfqq)) - cfqq->cfqg->saved_workload_slice = 0; + cfqq->cfqg->saved_wl_slice = 0; /* * Put the new queue at the front of the of the current list, From 34b98d03bd6e3f3c67af1e4933aaf19887a61192 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 3 Oct 2012 16:56:58 -0400 Subject: [PATCH 0710/4607] cfq-iosched: Rename "service_tree" to "st" at some places At quite a few places we use the keyword "service_tree". At some places, especially local variables, I have abbreviated it to "st". Also at couple of places moved binary operator "+" from beginning of line to end of previous line, as per Tejun's feedback. v2: Reverted most of the service tree name change based on Jeff Moyer's feedback. Signed-off-by: Vivek Goyal Signed-off-by: Tejun Heo --- block/cfq-iosched.c | 77 +++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 8f890bfba8fd..db4a1a52c3d9 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -353,7 +353,7 @@ struct cfq_data { static struct cfq_group *cfq_get_next_cfqg(struct cfq_data *cfqd); -static struct cfq_rb_root *service_tree_for(struct cfq_group *cfqg, +static struct cfq_rb_root *st_for(struct cfq_group *cfqg, enum wl_class_t class, enum wl_type_t type) { @@ -758,16 +758,16 @@ static inline int cfq_group_busy_queues_wl(enum wl_class_t wl_class, if (wl_class == IDLE_WORKLOAD) return cfqg->service_tree_idle.count; - return cfqg->service_trees[wl_class][ASYNC_WORKLOAD].count - + cfqg->service_trees[wl_class][SYNC_NOIDLE_WORKLOAD].count - + cfqg->service_trees[wl_class][SYNC_WORKLOAD].count; + return cfqg->service_trees[wl_class][ASYNC_WORKLOAD].count + + cfqg->service_trees[wl_class][SYNC_NOIDLE_WORKLOAD].count + + cfqg->service_trees[wl_class][SYNC_WORKLOAD].count; } static inline int cfqg_busy_async_queues(struct cfq_data *cfqd, struct cfq_group *cfqg) { - return cfqg->service_trees[RT_WORKLOAD][ASYNC_WORKLOAD].count - + cfqg->service_trees[BE_WORKLOAD][ASYNC_WORKLOAD].count; + return cfqg->service_trees[RT_WORKLOAD][ASYNC_WORKLOAD].count + + cfqg->service_trees[BE_WORKLOAD][ASYNC_WORKLOAD].count; } static void cfq_dispatch_insert(struct request_queue *, struct request *); @@ -1612,15 +1612,14 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq, struct rb_node **p, *parent; struct cfq_queue *__cfqq; unsigned long rb_key; - struct cfq_rb_root *service_tree; + struct cfq_rb_root *st; int left; int new_cfqq = 1; - service_tree = service_tree_for(cfqq->cfqg, cfqq_class(cfqq), - cfqq_type(cfqq)); + st = st_for(cfqq->cfqg, cfqq_class(cfqq), cfqq_type(cfqq)); if (cfq_class_idle(cfqq)) { rb_key = CFQ_IDLE_DELAY; - parent = rb_last(&service_tree->rb); + parent = rb_last(&st->rb); if (parent && parent != &cfqq->rb_node) { __cfqq = rb_entry(parent, struct cfq_queue, rb_node); rb_key += __cfqq->rb_key; @@ -1638,7 +1637,7 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq, cfqq->slice_resid = 0; } else { rb_key = -HZ; - __cfqq = cfq_rb_first(service_tree); + __cfqq = cfq_rb_first(st); rb_key += __cfqq ? __cfqq->rb_key : jiffies; } @@ -1647,8 +1646,7 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq, /* * same position, nothing more to do */ - if (rb_key == cfqq->rb_key && - cfqq->service_tree == service_tree) + if (rb_key == cfqq->rb_key && cfqq->service_tree == st) return; cfq_rb_erase(&cfqq->rb_node, cfqq->service_tree); @@ -1657,8 +1655,8 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq, left = 1; parent = NULL; - cfqq->service_tree = service_tree; - p = &service_tree->rb.rb_node; + cfqq->service_tree = st; + p = &st->rb.rb_node; while (*p) { struct rb_node **n; @@ -1679,12 +1677,12 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq, } if (left) - service_tree->left = &cfqq->rb_node; + st->left = &cfqq->rb_node; cfqq->rb_key = rb_key; rb_link_node(&cfqq->rb_node, parent, p); - rb_insert_color(&cfqq->rb_node, &service_tree->rb); - service_tree->count++; + rb_insert_color(&cfqq->rb_node, &st->rb); + st->count++; if (add_front || !new_cfqq) return; cfq_group_notify_queue_add(cfqd, cfqq->cfqg); @@ -2117,19 +2115,18 @@ static inline void cfq_slice_expired(struct cfq_data *cfqd, bool timed_out) */ static struct cfq_queue *cfq_get_next_queue(struct cfq_data *cfqd) { - struct cfq_rb_root *service_tree = - service_tree_for(cfqd->serving_group, cfqd->serving_wl_class, - cfqd->serving_wl_type); + struct cfq_rb_root *st = st_for(cfqd->serving_group, + cfqd->serving_wl_class, cfqd->serving_wl_type); if (!cfqd->rq_queued) return NULL; /* There is nothing to dispatch */ - if (!service_tree) + if (!st) return NULL; - if (RB_EMPTY_ROOT(&service_tree->rb)) + if (RB_EMPTY_ROOT(&st->rb)) return NULL; - return cfq_rb_first(service_tree); + return cfq_rb_first(st); } static struct cfq_queue *cfq_get_next_queue_forced(struct cfq_data *cfqd) @@ -2286,10 +2283,10 @@ static struct cfq_queue *cfq_close_cooperator(struct cfq_data *cfqd, static bool cfq_should_idle(struct cfq_data *cfqd, struct cfq_queue *cfqq) { enum wl_class_t wl_class = cfqq_class(cfqq); - struct cfq_rb_root *service_tree = cfqq->service_tree; + struct cfq_rb_root *st = cfqq->service_tree; - BUG_ON(!service_tree); - BUG_ON(!service_tree->count); + BUG_ON(!st); + BUG_ON(!st->count); if (!cfqd->cfq_slice_idle) return false; @@ -2307,11 +2304,10 @@ static bool cfq_should_idle(struct cfq_data *cfqd, struct cfq_queue *cfqq) * Otherwise, we do only if they are the last ones * in their service tree. */ - if (service_tree->count == 1 && cfq_cfqq_sync(cfqq) && - !cfq_io_thinktime_big(cfqd, &service_tree->ttime, false)) + if (st->count == 1 && cfq_cfqq_sync(cfqq) && + !cfq_io_thinktime_big(cfqd, &st->ttime, false)) return true; - cfq_log_cfqq(cfqd, cfqq, "Not idling. st->count:%d", - service_tree->count); + cfq_log_cfqq(cfqd, cfqq, "Not idling. st->count:%d", st->count); return false; } @@ -2505,7 +2501,7 @@ static enum wl_type_t cfq_choose_wl(struct cfq_data *cfqd, for (i = 0; i <= SYNC_WORKLOAD; ++i) { /* select the one with lowest rb_key */ - queue = cfq_rb_first(service_tree_for(cfqg, wl_class, i)); + queue = cfq_rb_first(st_for(cfqg, wl_class, i)); if (queue && (!key_valid || time_before(queue->rb_key, lowest_key))) { lowest_key = queue->rb_key; @@ -2544,8 +2540,7 @@ static void choose_service_tree(struct cfq_data *cfqd, struct cfq_group *cfqg) * (SYNC, SYNC_NOIDLE, ASYNC), and to compute a workload * expiration time */ - st = service_tree_for(cfqg, cfqd->serving_wl_class, - cfqd->serving_wl_type); + st = st_for(cfqg, cfqd->serving_wl_class, cfqd->serving_wl_type); count = st->count; /* @@ -2558,8 +2553,7 @@ new_workload: /* otherwise select new workload type */ cfqd->serving_wl_type = cfq_choose_wl(cfqd, cfqg, cfqd->serving_wl_class); - st = service_tree_for(cfqg, cfqd->serving_wl_class, - cfqd->serving_wl_type); + st = st_for(cfqg, cfqd->serving_wl_class, cfqd->serving_wl_type); count = st->count; /* @@ -3640,16 +3634,17 @@ static void cfq_completed_request(struct request_queue *q, struct request *rq) cfqd->rq_in_flight[cfq_cfqq_sync(cfqq)]--; if (sync) { - struct cfq_rb_root *service_tree; + struct cfq_rb_root *st; RQ_CIC(rq)->ttime.last_end_request = now; if (cfq_cfqq_on_rr(cfqq)) - service_tree = cfqq->service_tree; + st = cfqq->service_tree; else - service_tree = service_tree_for(cfqq->cfqg, - cfqq_class(cfqq), cfqq_type(cfqq)); - service_tree->ttime.last_end_request = now; + st = st_for(cfqq->cfqg, cfqq_class(cfqq), + cfqq_type(cfqq)); + + st->ttime.last_end_request = now; if (!time_after(rq->start_time + cfqd->cfq_fifo_expire[1], now)) cfqd->last_delayed_sync = now; } From 6d816ec7c83871da7c2472af7479d2438e641052 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 3 Oct 2012 16:56:59 -0400 Subject: [PATCH 0711/4607] cfq-iosched: Rename few functions related to selecting workload choose_service_tree() selects/sets both wl_class and wl_type. Rename it to choose_wl_class_and_type() to make it very clear. cfq_choose_wl() only selects and sets wl_type. It is easy to confuse it with choose_st(). So rename it to cfq_choose_wl_type() to make it clear what does it do. Just renaming. No functionality change. Signed-off-by: Vivek Goyal Acked-by: Jeff Moyer Signed-off-by: Tejun Heo --- block/cfq-iosched.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index db4a1a52c3d9..e34e142b6dbb 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -2490,7 +2490,7 @@ static void cfq_setup_merge(struct cfq_queue *cfqq, struct cfq_queue *new_cfqq) } } -static enum wl_type_t cfq_choose_wl(struct cfq_data *cfqd, +static enum wl_type_t cfq_choose_wl_type(struct cfq_data *cfqd, struct cfq_group *cfqg, enum wl_class_t wl_class) { struct cfq_queue *queue; @@ -2513,7 +2513,8 @@ static enum wl_type_t cfq_choose_wl(struct cfq_data *cfqd, return cur_best; } -static void choose_service_tree(struct cfq_data *cfqd, struct cfq_group *cfqg) +static void +choose_wl_class_and_type(struct cfq_data *cfqd, struct cfq_group *cfqg) { unsigned slice; unsigned count; @@ -2551,7 +2552,7 @@ static void choose_service_tree(struct cfq_data *cfqd, struct cfq_group *cfqg) new_workload: /* otherwise select new workload type */ - cfqd->serving_wl_type = cfq_choose_wl(cfqd, cfqg, + cfqd->serving_wl_type = cfq_choose_wl_type(cfqd, cfqg, cfqd->serving_wl_class); st = st_for(cfqg, cfqd->serving_wl_class, cfqd->serving_wl_type); count = st->count; @@ -2621,7 +2622,7 @@ static void cfq_choose_cfqg(struct cfq_data *cfqd) } else cfqd->workload_expires = jiffies - 1; - choose_service_tree(cfqd, cfqg); + choose_wl_class_and_type(cfqd, cfqg); } /* From 1f23f12151ab508728dd5fca12180e2fce6c6949 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 3 Oct 2012 16:57:00 -0400 Subject: [PATCH 0712/4607] cfq-iosched: Get rid of unnecessary local variable Use of local varibale "n" seems to be unnecessary. Remove it. This brings it inline with function __cfq_group_st_add(), which is also doing the similar operation of adding a group to a rb tree. No functionality change here. Signed-off-by: Vivek Goyal Acked-by: Jeff Moyer Signed-off-by: Tejun Heo --- block/cfq-iosched.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index e34e142b6dbb..5ad4cae1beb2 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1658,8 +1658,6 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq, cfqq->service_tree = st; p = &st->rb.rb_node; while (*p) { - struct rb_node **n; - parent = *p; __cfqq = rb_entry(parent, struct cfq_queue, rb_node); @@ -1667,13 +1665,11 @@ static void cfq_service_tree_add(struct cfq_data *cfqd, struct cfq_queue *cfqq, * sort by key, that represents service time. */ if (time_before(rb_key, __cfqq->rb_key)) - n = &(*p)->rb_left; + p = &parent->rb_left; else { - n = &(*p)->rb_right; + p = &parent->rb_right; left = 0; } - - p = n; } if (left) From b226e5c411759eec29308f0ea38e918aa695dc7f Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 3 Oct 2012 16:57:01 -0400 Subject: [PATCH 0713/4607] cfq-iosched: Print sync-noidle information in blktrace messages Currently we attach a character "S" or "A" to the cfqq, to represent whether queues is sync or async. Add one more character "N" to represent whether it is sync-noidle queue or sync queue. So now three different type of queues will look as follows. cfq1234S --> sync queus cfq1234SN --> sync noidle queue cfq1234A --> Async queue Previously S/A classification was being printed only if group scheduling was enabled. This patch also makes sure that this classification is displayed even if group idling is disabled. Signed-off-by: Vivek Goyal Acked-by: Jeff Moyer Signed-off-by: Tejun Heo --- block/cfq-iosched.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 5ad4cae1beb2..bc076f45ca46 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -586,8 +586,9 @@ static inline void cfqg_put(struct cfq_group *cfqg) char __pbuf[128]; \ \ blkg_path(cfqg_to_blkg((cfqq)->cfqg), __pbuf, sizeof(__pbuf)); \ - blk_add_trace_msg((cfqd)->queue, "cfq%d%c %s " fmt, (cfqq)->pid, \ - cfq_cfqq_sync((cfqq)) ? 'S' : 'A', \ + blk_add_trace_msg((cfqd)->queue, "cfq%d%c%c %s " fmt, (cfqq)->pid, \ + cfq_cfqq_sync((cfqq)) ? 'S' : 'A', \ + cfqq_type((cfqq)) == SYNC_NOIDLE_WORKLOAD ? 'N' : ' ',\ __pbuf, ##args); \ } while (0) @@ -675,7 +676,10 @@ static inline void cfqg_get(struct cfq_group *cfqg) { } static inline void cfqg_put(struct cfq_group *cfqg) { } #define cfq_log_cfqq(cfqd, cfqq, fmt, args...) \ - blk_add_trace_msg((cfqd)->queue, "cfq%d " fmt, (cfqq)->pid, ##args) + blk_add_trace_msg((cfqd)->queue, "cfq%d%c%c " fmt, (cfqq)->pid, \ + cfq_cfqq_sync((cfqq)) ? 'S' : 'A', \ + cfqq_type((cfqq)) == SYNC_NOIDLE_WORKLOAD ? 'N' : ' ',\ + ##args) #define cfq_log_cfqg(cfqd, cfqg, fmt, args...) do {} while (0) static inline void cfqg_stats_update_io_add(struct cfq_group *cfqg, From 356d2e581032b686da0854c7f17de2027c872762 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:10 -0800 Subject: [PATCH 0714/4607] blkcg: fix minor bug in blkg_alloc() blkg_alloc() was mistakenly checking blkcg_policy_enabled() twice. The latter test should have been on whether pol->pd_init_fn() exists. This doesn't cause actual problems because both blkcg policies implement pol->pd_init_fn(). Fix it. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index b8858fb0cafa..7ef747b7f056 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -114,7 +114,7 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, pd->blkg = blkg; /* invoke per-policy init */ - if (blkcg_policy_enabled(blkg->q, pol)) + if (pol->pd_init_fn) pol->pd_init_fn(blkg); } From 86cde6b62313dd221c2e5935db02de7234cb4164 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:10 -0800 Subject: [PATCH 0715/4607] blkcg: reorganize blkg_lookup_create() and friends Reorganize such that * __blkg_lookup() takes bool param @update_hint to determine whether to update hint. * __blkg_lookup_create() no longer performs lookup before trying to create. Renamed to blkg_create(). * blkg_lookup_create() now performs lookup and then invokes blkg_create() if lookup fails. * root_blkg creation in blkcg_activate_policy() updated accordingly. Note that blkcg_activate_policy() no longer updates lookup hint if root_blkg already exists. Except for the last lookup hint bit which is immaterial, this is pure reorganization and doesn't introduce any visible behavior change. This is to prepare for proper hierarchy support. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 75 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 7ef747b7f056..201275467d8b 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -126,7 +126,7 @@ err_free: } static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, - struct request_queue *q) + struct request_queue *q, bool update_hint) { struct blkcg_gq *blkg; @@ -135,14 +135,19 @@ static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, return blkg; /* - * Hint didn't match. Look up from the radix tree. Note that we - * may not be holding queue_lock and thus are not sure whether - * @blkg from blkg_tree has already been removed or not, so we - * can't update hint to the lookup result. Leave it to the caller. + * Hint didn't match. Look up from the radix tree. Note that the + * hint can only be updated under queue_lock as otherwise @blkg + * could have already been removed from blkg_tree. The caller is + * responsible for grabbing queue_lock if @update_hint. */ blkg = radix_tree_lookup(&blkcg->blkg_tree, q->id); - if (blkg && blkg->q == q) + if (blkg && blkg->q == q) { + if (update_hint) { + lockdep_assert_held(q->queue_lock); + rcu_assign_pointer(blkcg->blkg_hint, blkg); + } return blkg; + } return NULL; } @@ -162,7 +167,7 @@ struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, struct request_queue *q) if (unlikely(blk_queue_bypass(q))) return NULL; - return __blkg_lookup(blkcg, q); + return __blkg_lookup(blkcg, q, false); } EXPORT_SYMBOL_GPL(blkg_lookup); @@ -170,9 +175,9 @@ EXPORT_SYMBOL_GPL(blkg_lookup); * If @new_blkg is %NULL, this function tries to allocate a new one as * necessary using %GFP_ATOMIC. @new_blkg is always consumed on return. */ -static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg, - struct request_queue *q, - struct blkcg_gq *new_blkg) +static struct blkcg_gq *blkg_create(struct blkcg *blkcg, + struct request_queue *q, + struct blkcg_gq *new_blkg) { struct blkcg_gq *blkg; int ret; @@ -180,13 +185,6 @@ static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg, WARN_ON_ONCE(!rcu_read_lock_held()); lockdep_assert_held(q->queue_lock); - /* lookup and update hint on success, see __blkg_lookup() for details */ - blkg = __blkg_lookup(blkcg, q); - if (blkg) { - rcu_assign_pointer(blkcg->blkg_hint, blkg); - goto out_free; - } - /* blkg holds a reference to blkcg */ if (!css_tryget(&blkcg->css)) { blkg = ERR_PTR(-EINVAL); @@ -223,16 +221,39 @@ out_free: return blkg; } +/** + * blkg_lookup_create - lookup blkg, try to create one if not there + * @blkcg: blkcg of interest + * @q: request_queue of interest + * + * Lookup blkg for the @blkcg - @q pair. If it doesn't exist, try to + * create one. This function should be called under RCU read lock and + * @q->queue_lock. + * + * Returns pointer to the looked up or created blkg on success, ERR_PTR() + * value on error. If @q is dead, returns ERR_PTR(-EINVAL). If @q is not + * dead and bypassing, returns ERR_PTR(-EBUSY). + */ struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg, struct request_queue *q) { + struct blkcg_gq *blkg; + + WARN_ON_ONCE(!rcu_read_lock_held()); + lockdep_assert_held(q->queue_lock); + /* * This could be the first entry point of blkcg implementation and * we shouldn't allow anything to go through for a bypassing queue. */ if (unlikely(blk_queue_bypass(q))) return ERR_PTR(blk_queue_dying(q) ? -EINVAL : -EBUSY); - return __blkg_lookup_create(blkcg, q, NULL); + + blkg = __blkg_lookup(blkcg, q, true); + if (blkg) + return blkg; + + return blkg_create(blkcg, q, NULL); } EXPORT_SYMBOL_GPL(blkg_lookup_create); @@ -777,7 +798,7 @@ int blkcg_activate_policy(struct request_queue *q, const struct blkcg_policy *pol) { LIST_HEAD(pds); - struct blkcg_gq *blkg; + struct blkcg_gq *blkg, *new_blkg; struct blkg_policy_data *pd, *n; int cnt = 0, ret; bool preloaded; @@ -786,19 +807,27 @@ int blkcg_activate_policy(struct request_queue *q, return 0; /* preallocations for root blkg */ - blkg = blkg_alloc(&blkcg_root, q, GFP_KERNEL); - if (!blkg) + new_blkg = blkg_alloc(&blkcg_root, q, GFP_KERNEL); + if (!new_blkg) return -ENOMEM; preloaded = !radix_tree_preload(GFP_KERNEL); blk_queue_bypass_start(q); - /* make sure the root blkg exists and count the existing blkgs */ + /* + * Make sure the root blkg exists and count the existing blkgs. As + * @q is bypassing at this point, blkg_lookup_create() can't be + * used. Open code it. + */ spin_lock_irq(q->queue_lock); rcu_read_lock(); - blkg = __blkg_lookup_create(&blkcg_root, q, blkg); + blkg = __blkg_lookup(&blkcg_root, q, false); + if (blkg) + blkg_free(new_blkg); + else + blkg = blkg_create(&blkcg_root, q, new_blkg); rcu_read_unlock(); if (preloaded) From 93e6d5d8f5c909479623c6ab4427f038c6c3f63f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:10 -0800 Subject: [PATCH 0716/4607] blkcg: cosmetic updates to blkg_create() * Rename out_* labels to err_*. * Do ERR_PTR() conversion once in the error return path. This patch is cosmetic and to prepare for the hierarchy support. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 201275467d8b..18ae48083f4a 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -187,16 +187,16 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, /* blkg holds a reference to blkcg */ if (!css_tryget(&blkcg->css)) { - blkg = ERR_PTR(-EINVAL); - goto out_free; + ret = -EINVAL; + goto err_free_blkg; } /* allocate */ if (!new_blkg) { new_blkg = blkg_alloc(blkcg, q, GFP_ATOMIC); if (unlikely(!new_blkg)) { - blkg = ERR_PTR(-ENOMEM); - goto out_put; + ret = -ENOMEM; + goto err_put_css; } } blkg = new_blkg; @@ -213,12 +213,11 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, if (!ret) return blkg; - blkg = ERR_PTR(ret); -out_put: +err_put_css: css_put(&blkcg->css); -out_free: +err_free_blkg: blkg_free(new_blkg); - return blkg; + return ERR_PTR(ret); } /** From 3c547865902e9fc30dc15941f326fd8039c6628d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:10 -0800 Subject: [PATCH 0717/4607] blkcg: make blkcg_gq's hierarchical Currently a child blkg (blkcg_gq) can be created even if its parent doesn't exist. ie. Given a blkg, it's not guaranteed that its ancestors will exist. This makes it difficult to implement proper hierarchy support for blkcg policies. Always create blkgs recursively and make a child blkg hold a reference to its parent. blkg->parent is added so that finding the parent is easy. blkcg_parent() is also added in the process. This change can be visible to userland. e.g. while issuing IO in a nested cgroup didn't affect the ancestors at all, now it will initialize all ancestor blkgs and zero stats for the request_queue will always appear on them. While this is userland visible, this shouldn't cause any functional difference. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 42 +++++++++++++++++++++++++++++++++++++----- block/blk-cgroup.h | 18 ++++++++++++++++++ 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 18ae48083f4a..942f344fdfa7 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -201,7 +201,16 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, } blkg = new_blkg; - /* insert */ + /* link parent and insert */ + if (blkcg_parent(blkcg)) { + blkg->parent = __blkg_lookup(blkcg_parent(blkcg), q, false); + if (WARN_ON_ONCE(!blkg->parent)) { + blkg = ERR_PTR(-EINVAL); + goto err_put_css; + } + blkg_get(blkg->parent); + } + spin_lock(&blkcg->lock); ret = radix_tree_insert(&blkcg->blkg_tree, q->id, blkg); if (likely(!ret)) { @@ -213,6 +222,10 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, if (!ret) return blkg; + /* @blkg failed fully initialized, use the usual release path */ + blkg_put(blkg); + return ERR_PTR(ret); + err_put_css: css_put(&blkcg->css); err_free_blkg: @@ -226,8 +239,9 @@ err_free_blkg: * @q: request_queue of interest * * Lookup blkg for the @blkcg - @q pair. If it doesn't exist, try to - * create one. This function should be called under RCU read lock and - * @q->queue_lock. + * create one. blkg creation is performed recursively from blkcg_root such + * that all non-root blkg's have access to the parent blkg. This function + * should be called under RCU read lock and @q->queue_lock. * * Returns pointer to the looked up or created blkg on success, ERR_PTR() * value on error. If @q is dead, returns ERR_PTR(-EINVAL). If @q is not @@ -252,7 +266,23 @@ struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg, if (blkg) return blkg; - return blkg_create(blkcg, q, NULL); + /* + * Create blkgs walking down from blkcg_root to @blkcg, so that all + * non-root blkgs have access to their parents. + */ + while (true) { + struct blkcg *pos = blkcg; + struct blkcg *parent = blkcg_parent(blkcg); + + while (parent && !__blkg_lookup(parent, q, false)) { + pos = parent; + parent = blkcg_parent(parent); + } + + blkg = blkg_create(pos, q, NULL); + if (pos == blkcg || IS_ERR(blkg)) + return blkg; + } } EXPORT_SYMBOL_GPL(blkg_lookup_create); @@ -321,8 +351,10 @@ static void blkg_rcu_free(struct rcu_head *rcu_head) void __blkg_release(struct blkcg_gq *blkg) { - /* release the extra blkcg reference this blkg has been holding */ + /* release the blkcg and parent blkg refs this blkg has been holding */ css_put(&blkg->blkcg->css); + if (blkg->parent) + blkg_put(blkg->parent); /* * A group is freed in rcu manner. But having an rcu lock does not diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 24597309e23d..b26ed58899fe 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -94,8 +94,13 @@ struct blkcg_gq { struct list_head q_node; struct hlist_node blkcg_node; struct blkcg *blkcg; + + /* all non-root blkcg_gq's are guaranteed to have access to parent */ + struct blkcg_gq *parent; + /* request allocation list for this blkcg-q pair */ struct request_list rl; + /* reference count */ int refcnt; @@ -180,6 +185,19 @@ static inline struct blkcg *bio_blkcg(struct bio *bio) return task_blkcg(current); } +/** + * blkcg_parent - get the parent of a blkcg + * @blkcg: blkcg of interest + * + * Return the parent blkcg of @blkcg. Can be called anytime. + */ +static inline struct blkcg *blkcg_parent(struct blkcg *blkcg) +{ + struct cgroup *pcg = blkcg->css.cgroup->parent; + + return pcg ? cgroup_to_blkcg(pcg) : NULL; +} + /** * blkg_to_pdata - get policy private data * @blkg: blkg of interest From e71357e118bdd4057e3bc020b9d80fecdd08f588 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:10 -0800 Subject: [PATCH 0718/4607] cfq-iosched: add leaf_weight cfq blkcg is about to grow proper hierarchy handling, where a child blkg's weight would nest inside the parent's. This makes tasks in a blkg to compete against both tasks in the sibling blkgs and the tasks of child blkgs. We're gonna use the existing weight as the group weight which decides the blkg's weight against its siblings. This patch introduces a new weight - leaf_weight - which decides the weight of a blkg against the child blkgs. It's named leaf_weight because another way to look at it is that each internal blkg nodes have a hidden child leaf node which contains all its tasks and leaf_weight is the weight of the leaf node and handled the same as the weight of the child blkgs. This patch only adds leaf_weight fields and exposes it to userland. The new weight isn't actually used anywhere yet. Note that cfq-iosched currently offcially supports only single level hierarchy and root blkgs compete with the first level blkgs - ie. root weight is basically being used as leaf_weight. For root blkgs, the two weights are kept in sync for backward compatibility. v2: cfqd->root_group->leaf_weight initialization was missing from cfq_init_queue() causing divide by zero when !CONFIG_CFQ_GROUP_SCHED. Fix it. Reported by Fengguang. Signed-off-by: Tejun Heo Cc: Fengguang Wu --- block/blk-cgroup.c | 4 +- block/blk-cgroup.h | 1 + block/cfq-iosched.c | 134 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 130 insertions(+), 9 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 942f344fdfa7..10e1df9da46e 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -26,7 +26,8 @@ static DEFINE_MUTEX(blkcg_pol_mutex); -struct blkcg blkcg_root = { .cfq_weight = 2 * CFQ_WEIGHT_DEFAULT }; +struct blkcg blkcg_root = { .cfq_weight = 2 * CFQ_WEIGHT_DEFAULT, + .cfq_leaf_weight = 2 * CFQ_WEIGHT_DEFAULT, }; EXPORT_SYMBOL_GPL(blkcg_root); static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS]; @@ -710,6 +711,7 @@ static struct cgroup_subsys_state *blkcg_css_alloc(struct cgroup *cgroup) return ERR_PTR(-ENOMEM); blkcg->cfq_weight = CFQ_WEIGHT_DEFAULT; + blkcg->cfq_leaf_weight = CFQ_WEIGHT_DEFAULT; blkcg->id = atomic64_inc_return(&id_seq); /* root is 0, start from 1 */ done: spin_lock_init(&blkcg->lock); diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index b26ed58899fe..24462258200e 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -54,6 +54,7 @@ struct blkcg { /* TODO: per-policy storage in blkcg */ unsigned int cfq_weight; /* belongs to cfq */ + unsigned int cfq_leaf_weight; }; struct blkg_stat { diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index bc076f45ca46..175218d66d67 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -223,10 +223,21 @@ struct cfq_group { /* group service_tree key */ u64 vdisktime; + + /* + * There are two weights - (internal) weight is the weight of this + * cfqg against the sibling cfqgs. leaf_weight is the wight of + * this cfqg against the child cfqgs. For the root cfqg, both + * weights are kept in sync for backward compatibility. + */ unsigned int weight; unsigned int new_weight; unsigned int dev_weight; + unsigned int leaf_weight; + unsigned int new_leaf_weight; + unsigned int dev_leaf_weight; + /* number of cfqq currently on this group */ int nr_cfqq; @@ -1182,10 +1193,16 @@ static void cfq_update_group_weight(struct cfq_group *cfqg) { BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); + if (cfqg->new_weight) { cfqg->weight = cfqg->new_weight; cfqg->new_weight = 0; } + + if (cfqg->new_leaf_weight) { + cfqg->leaf_weight = cfqg->new_leaf_weight; + cfqg->new_leaf_weight = 0; + } } static void @@ -1348,6 +1365,7 @@ static void cfq_pd_init(struct blkcg_gq *blkg) cfq_init_cfqg_base(cfqg); cfqg->weight = blkg->blkcg->cfq_weight; + cfqg->leaf_weight = blkg->blkcg->cfq_leaf_weight; } /* @@ -1404,6 +1422,26 @@ static int cfqg_print_weight_device(struct cgroup *cgrp, struct cftype *cft, return 0; } +static u64 cfqg_prfill_leaf_weight_device(struct seq_file *sf, + struct blkg_policy_data *pd, int off) +{ + struct cfq_group *cfqg = pd_to_cfqg(pd); + + if (!cfqg->dev_leaf_weight) + return 0; + return __blkg_prfill_u64(sf, pd, cfqg->dev_leaf_weight); +} + +static int cfqg_print_leaf_weight_device(struct cgroup *cgrp, + struct cftype *cft, + struct seq_file *sf) +{ + blkcg_print_blkgs(sf, cgroup_to_blkcg(cgrp), + cfqg_prfill_leaf_weight_device, &blkcg_policy_cfq, 0, + false); + return 0; +} + static int cfq_print_weight(struct cgroup *cgrp, struct cftype *cft, struct seq_file *sf) { @@ -1411,8 +1449,16 @@ static int cfq_print_weight(struct cgroup *cgrp, struct cftype *cft, return 0; } -static int cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft, - const char *buf) +static int cfq_print_leaf_weight(struct cgroup *cgrp, struct cftype *cft, + struct seq_file *sf) +{ + seq_printf(sf, "%u\n", + cgroup_to_blkcg(cgrp)->cfq_leaf_weight); + return 0; +} + +static int __cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft, + const char *buf, bool is_leaf_weight) { struct blkcg *blkcg = cgroup_to_blkcg(cgrp); struct blkg_conf_ctx ctx; @@ -1426,8 +1472,13 @@ static int cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft, ret = -EINVAL; cfqg = blkg_to_cfqg(ctx.blkg); if (!ctx.v || (ctx.v >= CFQ_WEIGHT_MIN && ctx.v <= CFQ_WEIGHT_MAX)) { - cfqg->dev_weight = ctx.v; - cfqg->new_weight = cfqg->dev_weight ?: blkcg->cfq_weight; + if (!is_leaf_weight) { + cfqg->dev_weight = ctx.v; + cfqg->new_weight = ctx.v ?: blkcg->cfq_weight; + } else { + cfqg->dev_leaf_weight = ctx.v; + cfqg->new_leaf_weight = ctx.v ?: blkcg->cfq_leaf_weight; + } ret = 0; } @@ -1435,7 +1486,20 @@ static int cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft, return ret; } -static int cfq_set_weight(struct cgroup *cgrp, struct cftype *cft, u64 val) +static int cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft, + const char *buf) +{ + return __cfqg_set_weight_device(cgrp, cft, buf, false); +} + +static int cfqg_set_leaf_weight_device(struct cgroup *cgrp, struct cftype *cft, + const char *buf) +{ + return __cfqg_set_weight_device(cgrp, cft, buf, true); +} + +static int __cfq_set_weight(struct cgroup *cgrp, struct cftype *cft, u64 val, + bool is_leaf_weight) { struct blkcg *blkcg = cgroup_to_blkcg(cgrp); struct blkcg_gq *blkg; @@ -1445,19 +1509,41 @@ static int cfq_set_weight(struct cgroup *cgrp, struct cftype *cft, u64 val) return -EINVAL; spin_lock_irq(&blkcg->lock); - blkcg->cfq_weight = (unsigned int)val; + + if (!is_leaf_weight) + blkcg->cfq_weight = val; + else + blkcg->cfq_leaf_weight = val; hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) { struct cfq_group *cfqg = blkg_to_cfqg(blkg); - if (cfqg && !cfqg->dev_weight) - cfqg->new_weight = blkcg->cfq_weight; + if (!cfqg) + continue; + + if (!is_leaf_weight) { + if (!cfqg->dev_weight) + cfqg->new_weight = blkcg->cfq_weight; + } else { + if (!cfqg->dev_leaf_weight) + cfqg->new_leaf_weight = blkcg->cfq_leaf_weight; + } } spin_unlock_irq(&blkcg->lock); return 0; } +static int cfq_set_weight(struct cgroup *cgrp, struct cftype *cft, u64 val) +{ + return __cfq_set_weight(cgrp, cft, val, false); +} + +static int cfq_set_leaf_weight(struct cgroup *cgrp, struct cftype *cft, u64 val) +{ + return __cfq_set_weight(cgrp, cft, val, true); +} + static int cfqg_print_stat(struct cgroup *cgrp, struct cftype *cft, struct seq_file *sf) { @@ -1518,6 +1604,37 @@ static struct cftype cfq_blkcg_files[] = { .read_seq_string = cfq_print_weight, .write_u64 = cfq_set_weight, }, + + /* on root, leaf_weight is mapped to weight */ + { + .name = "leaf_weight_device", + .flags = CFTYPE_ONLY_ON_ROOT, + .read_seq_string = cfqg_print_weight_device, + .write_string = cfqg_set_weight_device, + .max_write_len = 256, + }, + { + .name = "leaf_weight", + .flags = CFTYPE_ONLY_ON_ROOT, + .read_seq_string = cfq_print_weight, + .write_u64 = cfq_set_weight, + }, + + /* no such mapping necessary for !roots */ + { + .name = "leaf_weight_device", + .flags = CFTYPE_NOT_ON_ROOT, + .read_seq_string = cfqg_print_leaf_weight_device, + .write_string = cfqg_set_leaf_weight_device, + .max_write_len = 256, + }, + { + .name = "leaf_weight", + .flags = CFTYPE_NOT_ON_ROOT, + .read_seq_string = cfq_print_leaf_weight, + .write_u64 = cfq_set_leaf_weight, + }, + { .name = "time", .private = offsetof(struct cfq_group, stats.time), @@ -3992,6 +4109,7 @@ static int cfq_init_queue(struct request_queue *q) cfq_init_cfqg_base(cfqd->root_group); #endif cfqd->root_group->weight = 2 * CFQ_WEIGHT_DEFAULT; + cfqd->root_group->leaf_weight = 2 * CFQ_WEIGHT_DEFAULT; /* * Not strictly needed (since RB_ROOT just clears the node and we From 7918ffb5b83e3373206ada84873c674fbddf61cc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:11 -0800 Subject: [PATCH 0719/4607] cfq-iosched: implement cfq_group->nr_active and ->children_weight To prepare for blkcg hierarchy support, add cfqg->nr_active and ->children_weight. cfqg->nr_active counts the number of active cfqgs at the cfqg's level and ->children_weight is sum of weights of those cfqgs. The level covers itself (cfqg->leaf_weight) and immediate children. The two values are updated when a cfqg enters and leaves the group service tree. Unless the hierarchy is very deep, the added overhead should be negligible. Currently, the parent is determined using cfqg_flat_parent() which makes the root cfqg the parent of all other cfqgs. This is to make the transition to hierarchy-aware scheduling gradual. Scheduling logic will be converted to use cfqg->children_weight without actually changing the behavior. When everything is ready, blkcg_weight_parent() will be replaced with proper parent function. This patch doesn't introduce any behavior chagne. v2: s/cfqg->level_weight/cfqg->children_weight/ as per Vivek. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/cfq-iosched.c | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 175218d66d67..7701c3fe49b9 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -224,6 +224,18 @@ struct cfq_group { /* group service_tree key */ u64 vdisktime; + /* + * The number of active cfqgs and sum of their weights under this + * cfqg. This covers this cfqg's leaf_weight and all children's + * weights, but does not cover weights of further descendants. + * + * If a cfqg is on the service tree, it's active. An active cfqg + * also activates its parent and contributes to the children_weight + * of the parent. + */ + int nr_active; + unsigned int children_weight; + /* * There are two weights - (internal) weight is the weight of this * cfqg against the sibling cfqgs. leaf_weight is the wight of @@ -583,6 +595,22 @@ static inline struct cfq_group *blkg_to_cfqg(struct blkcg_gq *blkg) return pd_to_cfqg(blkg_to_pd(blkg, &blkcg_policy_cfq)); } +/* + * Determine the parent cfqg for weight calculation. Currently, cfqg + * scheduling is flat and the root is the parent of everyone else. + */ +static inline struct cfq_group *cfqg_flat_parent(struct cfq_group *cfqg) +{ + struct blkcg_gq *blkg = cfqg_to_blkg(cfqg); + struct cfq_group *root; + + while (blkg->parent) + blkg = blkg->parent; + root = blkg_to_cfqg(blkg); + + return root != cfqg ? root : NULL; +} + static inline void cfqg_get(struct cfq_group *cfqg) { return blkg_get(cfqg_to_blkg(cfqg)); @@ -683,6 +711,7 @@ static void cfq_pd_reset_stats(struct blkcg_gq *blkg) #else /* CONFIG_CFQ_GROUP_IOSCHED */ +static inline struct cfq_group *cfqg_flat_parent(struct cfq_group *cfqg) { return NULL; } static inline void cfqg_get(struct cfq_group *cfqg) { } static inline void cfqg_put(struct cfq_group *cfqg) { } @@ -1208,11 +1237,33 @@ cfq_update_group_weight(struct cfq_group *cfqg) static void cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg) { + struct cfq_group *pos = cfqg; + bool propagate; + + /* add to the service tree */ BUG_ON(!RB_EMPTY_NODE(&cfqg->rb_node)); cfq_update_group_weight(cfqg); __cfq_group_service_tree_add(st, cfqg); st->total_weight += cfqg->weight; + + /* + * Activate @cfqg and propagate activation upwards until we meet an + * already activated node or reach root. + */ + propagate = !pos->nr_active++; + pos->children_weight += pos->leaf_weight; + + while (propagate) { + struct cfq_group *parent = cfqg_flat_parent(pos); + + if (!parent) + break; + + propagate = !parent->nr_active++; + parent->children_weight += pos->weight; + pos = parent; + } } static void @@ -1243,6 +1294,31 @@ cfq_group_notify_queue_add(struct cfq_data *cfqd, struct cfq_group *cfqg) static void cfq_group_service_tree_del(struct cfq_rb_root *st, struct cfq_group *cfqg) { + struct cfq_group *pos = cfqg; + bool propagate; + + /* + * Undo activation from cfq_group_service_tree_add(). Deactivate + * @cfqg and propagate deactivation upwards. + */ + propagate = !--pos->nr_active; + pos->children_weight -= pos->leaf_weight; + + while (propagate) { + struct cfq_group *parent = cfqg_flat_parent(pos); + + /* @pos has 0 nr_active at this point */ + WARN_ON_ONCE(pos->children_weight); + + if (!parent) + break; + + propagate = !--parent->nr_active; + parent->children_weight -= pos->weight; + pos = parent; + } + + /* remove from the service tree */ st->total_weight -= cfqg->weight; if (!RB_EMPTY_NODE(&cfqg->rb_node)) cfq_rb_erase(&cfqg->rb_node, st); From 1d3650f713e7f6392b02fde450c5bae40291e65b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:11 -0800 Subject: [PATCH 0720/4607] cfq-iosched: implement hierarchy-ready cfq_group charge scaling Currently, cfqg charges are scaled directly according to cfqg->weight. Regardless of the number of active cfqgs or the amount of active weights, a given weight value always scales charge the same way. This works fine as long as all cfqgs are treated equally regardless of their positions in the hierarchy, which is what cfq currently implements. It can't work in hierarchical settings because the interpretation of a given weight value depends on where the weight is located in the hierarchy. This patch reimplements cfqg charge scaling so that it can be used to support hierarchy properly. The scheme is fairly simple and light-weight. * When a cfqg is added to the service tree, v(disktime)weight is calculated. It walks up the tree to root calculating the fraction it has in the hierarchy. At each level, the fraction can be calculated as cfqg->weight / parent->level_weight By compounding these, the global fraction of vdisktime the cfqg has claim to - vfraction - can be determined. * When the cfqg needs to be charged, the charge is scaled inversely proportionally to the vfraction. The new scaling scheme uses the same CFQ_SERVICE_SHIFT for fixed point representation as before; however, the smallest scaling factor is now 1 (ie. 1 << CFQ_SERVICE_SHIFT). This is different from before where 1 was for CFQ_WEIGHT_DEFAULT and higher weight would result in smaller scaling factor. While this shifts the global scale of vdisktime a bit, it doesn't change the relative relationships among cfqgs and the scheduling result isn't different. cfq_group_notify_queue_add uses fixed CFQ_IDLE_DELAY when appending new cfqg to the service tree. The specific value of CFQ_IDLE_DELAY didn't have any relevance to vdisktime before and is unlikely to cause any visible behavior difference now especially as the scale shift isn't that large. As the new scheme now makes proper distinction between cfqg->weight and ->leaf_weight, reverse the weight aliasing for root cfqgs. For root, both weights are now mapped to ->leaf_weight instead of the other way around. Because we're still using cfqg_flat_parent(), this patch shouldn't change the scheduling behavior in any noticeable way. v2: Beefed up comments on vfraction as requested by Vivek. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/cfq-iosched.c | 129 ++++++++++++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 41 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 7701c3fe49b9..b24acf66d5b5 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -236,6 +236,18 @@ struct cfq_group { int nr_active; unsigned int children_weight; + /* + * vfraction is the fraction of vdisktime that the tasks in this + * cfqg are entitled to. This is determined by compounding the + * ratios walking up from this cfqg to the root. + * + * It is in fixed point w/ CFQ_SERVICE_SHIFT and the sum of all + * vfractions on a service tree is approximately 1. The sum may + * deviate a bit due to rounding errors and fluctuations caused by + * cfqgs entering and leaving the service tree. + */ + unsigned int vfraction; + /* * There are two weights - (internal) weight is the weight of this * cfqg against the sibling cfqgs. leaf_weight is the wight of @@ -891,13 +903,27 @@ cfq_prio_to_slice(struct cfq_data *cfqd, struct cfq_queue *cfqq) return cfq_prio_slice(cfqd, cfq_cfqq_sync(cfqq), cfqq->ioprio); } -static inline u64 cfq_scale_slice(unsigned long delta, struct cfq_group *cfqg) +/** + * cfqg_scale_charge - scale disk time charge according to cfqg weight + * @charge: disk time being charged + * @vfraction: vfraction of the cfqg, fixed point w/ CFQ_SERVICE_SHIFT + * + * Scale @charge according to @vfraction, which is in range (0, 1]. The + * scaling is inversely proportional. + * + * scaled = charge / vfraction + * + * The result is also in fixed point w/ CFQ_SERVICE_SHIFT. + */ +static inline u64 cfqg_scale_charge(unsigned long charge, + unsigned int vfraction) { - u64 d = delta << CFQ_SERVICE_SHIFT; + u64 c = charge << CFQ_SERVICE_SHIFT; /* make it fixed point */ - d = d * CFQ_WEIGHT_DEFAULT; - do_div(d, cfqg->weight); - return d; + /* charge / vfraction */ + c <<= CFQ_SERVICE_SHIFT; + do_div(c, vfraction); + return c; } static inline u64 max_vdisktime(u64 min_vdisktime, u64 vdisktime) @@ -1237,7 +1263,9 @@ cfq_update_group_weight(struct cfq_group *cfqg) static void cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg) { + unsigned int vfr = 1 << CFQ_SERVICE_SHIFT; /* start with 1 */ struct cfq_group *pos = cfqg; + struct cfq_group *parent; bool propagate; /* add to the service tree */ @@ -1248,22 +1276,34 @@ cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg) st->total_weight += cfqg->weight; /* - * Activate @cfqg and propagate activation upwards until we meet an - * already activated node or reach root. + * Activate @cfqg and calculate the portion of vfraction @cfqg is + * entitled to. vfraction is calculated by walking the tree + * towards the root calculating the fraction it has at each level. + * The compounded ratio is how much vfraction @cfqg owns. + * + * Start with the proportion tasks in this cfqg has against active + * children cfqgs - its leaf_weight against children_weight. */ propagate = !pos->nr_active++; pos->children_weight += pos->leaf_weight; + vfr = vfr * pos->leaf_weight / pos->children_weight; - while (propagate) { - struct cfq_group *parent = cfqg_flat_parent(pos); - - if (!parent) - break; - - propagate = !parent->nr_active++; - parent->children_weight += pos->weight; + /* + * Compound ->weight walking up the tree. Both activation and + * vfraction calculation are done in the same loop. Propagation + * stops once an already activated node is met. vfraction + * calculation should always continue to the root. + */ + while ((parent = cfqg_flat_parent(pos))) { + if (propagate) { + propagate = !parent->nr_active++; + parent->children_weight += pos->weight; + } + vfr = vfr * pos->weight / parent->children_weight; pos = parent; } + + cfqg->vfraction = max_t(unsigned, vfr, 1); } static void @@ -1309,6 +1349,7 @@ cfq_group_service_tree_del(struct cfq_rb_root *st, struct cfq_group *cfqg) /* @pos has 0 nr_active at this point */ WARN_ON_ONCE(pos->children_weight); + pos->vfraction = 0; if (!parent) break; @@ -1381,6 +1422,7 @@ static void cfq_group_served(struct cfq_data *cfqd, struct cfq_group *cfqg, unsigned int used_sl, charge, unaccounted_sl = 0; int nr_sync = cfqg->nr_cfqq - cfqg_busy_async_queues(cfqd, cfqg) - cfqg->service_tree_idle.count; + unsigned int vfr; BUG_ON(nr_sync < 0); used_sl = charge = cfq_cfqq_slice_usage(cfqq, &unaccounted_sl); @@ -1390,10 +1432,15 @@ static void cfq_group_served(struct cfq_data *cfqd, struct cfq_group *cfqg, else if (!cfq_cfqq_sync(cfqq) && !nr_sync) charge = cfqq->allocated_slice; - /* Can't update vdisktime while group is on service tree */ + /* + * Can't update vdisktime while on service tree and cfqg->vfraction + * is valid only while on it. Cache vfr, leave the service tree, + * update vdisktime and go back on. The re-addition to the tree + * will also update the weights as necessary. + */ + vfr = cfqg->vfraction; cfq_group_service_tree_del(st, cfqg); - cfqg->vdisktime += cfq_scale_slice(charge, cfqg); - /* If a new weight was requested, update now, off tree */ + cfqg->vdisktime += cfqg_scale_charge(charge, vfr); cfq_group_service_tree_add(st, cfqg); /* This group is being expired. Save the context */ @@ -1669,44 +1716,44 @@ static int cfqg_print_avg_queue_size(struct cgroup *cgrp, struct cftype *cft, #endif /* CONFIG_DEBUG_BLK_CGROUP */ static struct cftype cfq_blkcg_files[] = { + /* on root, weight is mapped to leaf_weight */ { .name = "weight_device", + .flags = CFTYPE_ONLY_ON_ROOT, + .read_seq_string = cfqg_print_leaf_weight_device, + .write_string = cfqg_set_leaf_weight_device, + .max_write_len = 256, + }, + { + .name = "weight", + .flags = CFTYPE_ONLY_ON_ROOT, + .read_seq_string = cfq_print_leaf_weight, + .write_u64 = cfq_set_leaf_weight, + }, + + /* no such mapping necessary for !roots */ + { + .name = "weight_device", + .flags = CFTYPE_NOT_ON_ROOT, .read_seq_string = cfqg_print_weight_device, .write_string = cfqg_set_weight_device, .max_write_len = 256, }, { .name = "weight", - .read_seq_string = cfq_print_weight, - .write_u64 = cfq_set_weight, - }, - - /* on root, leaf_weight is mapped to weight */ - { - .name = "leaf_weight_device", - .flags = CFTYPE_ONLY_ON_ROOT, - .read_seq_string = cfqg_print_weight_device, - .write_string = cfqg_set_weight_device, - .max_write_len = 256, - }, - { - .name = "leaf_weight", - .flags = CFTYPE_ONLY_ON_ROOT, - .read_seq_string = cfq_print_weight, - .write_u64 = cfq_set_weight, - }, - - /* no such mapping necessary for !roots */ - { - .name = "leaf_weight_device", .flags = CFTYPE_NOT_ON_ROOT, + .read_seq_string = cfq_print_weight, + .write_u64 = cfq_set_weight, + }, + + { + .name = "leaf_weight_device", .read_seq_string = cfqg_print_leaf_weight_device, .write_string = cfqg_set_leaf_weight_device, .max_write_len = 256, }, { .name = "leaf_weight", - .flags = CFTYPE_NOT_ON_ROOT, .read_seq_string = cfq_print_leaf_weight, .write_u64 = cfq_set_leaf_weight, }, From 41cad6ab2cb9ccb3b11546ad56b8b285e47c6279 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:11 -0800 Subject: [PATCH 0721/4607] cfq-iosched: convert cfq_group_slice() to use cfqg->vfraction cfq_group_slice() calculates slice by taking a fraction of cfq_target_latency according to the ratio of cfqg->weight against service_tree->total_weight. This currently works only because all cfqgs are treated to be at the same level. To prepare for proper hierarchy support, convert cfq_group_slice() to base the calculation on cfqg->vfraction. As cfqg->vfraction is always a fraction of 1 and represents the fraction allocated to the cfqg with hierarchy considered, the slice can be simply calculated by multiplying cfqg->vfraction to cfq_target_latency (with fixed point shift factored in). As vfraction calculation currently treats all non-root cfqgs as children of the root cfqg, this patch doesn't introduce noticeable behavior difference. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/cfq-iosched.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index b24acf66d5b5..ee342826fd98 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -85,7 +85,6 @@ struct cfq_rb_root { struct rb_root rb; struct rb_node *left; unsigned count; - unsigned total_weight; u64 min_vdisktime; struct cfq_ttime ttime; }; @@ -979,9 +978,7 @@ static inline unsigned cfq_group_get_avg_queues(struct cfq_data *cfqd, static inline unsigned cfq_group_slice(struct cfq_data *cfqd, struct cfq_group *cfqg) { - struct cfq_rb_root *st = &cfqd->grp_service_tree; - - return cfqd->cfq_target_latency * cfqg->weight / st->total_weight; + return cfqd->cfq_target_latency * cfqg->vfraction >> CFQ_SERVICE_SHIFT; } static inline unsigned @@ -1273,7 +1270,6 @@ cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg) cfq_update_group_weight(cfqg); __cfq_group_service_tree_add(st, cfqg); - st->total_weight += cfqg->weight; /* * Activate @cfqg and calculate the portion of vfraction @cfqg is @@ -1360,7 +1356,6 @@ cfq_group_service_tree_del(struct cfq_rb_root *st, struct cfq_group *cfqg) } /* remove from the service tree */ - st->total_weight -= cfqg->weight; if (!RB_EMPTY_NODE(&cfqg->rb_node)) cfq_rb_erase(&cfqg->rb_node, st); } From d02f7aa8dce8166dbbc515ce393912aa45e6b8a6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:11 -0800 Subject: [PATCH 0722/4607] cfq-iosched: enable full blkcg hierarchy support With the previous two patches, all cfqg scheduling decisions are based on vfraction and ready for hierarchy support. The only thing which keeps the behavior flat is cfqg_flat_parent() which makes vfraction calculation consider all non-root cfqgs children of the root cfqg. Replace it with cfqg_parent() which returns the real parent. This enables full blkcg hierarchy support for cfq-iosched. For example, consider the following hierarchy. root / \ A:500 B:250 / \ AA:500 AB:1000 For simplicity, let's say all the leaf nodes have active tasks and are on service tree. For each leaf node, vfraction would be AA: (500 / 1500) * (500 / 750) =~ 0.2222 AB: (1000 / 1500) * (500 / 750) =~ 0.4444 B: (250 / 750) =~ 0.3333 and vdisktime will be distributed accordingly. For more detail, please refer to Documentation/block/cfq-iosched.txt. v2: cfq-iosched.txt updated to describe group scheduling as suggested by Vivek. v3: blkio-controller.txt updated. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- Documentation/block/cfq-iosched.txt | 58 ++++++++++++++++++++++ Documentation/cgroups/blkio-controller.txt | 35 +++++++++---- block/cfq-iosched.c | 21 +++----- 3 files changed, 88 insertions(+), 26 deletions(-) diff --git a/Documentation/block/cfq-iosched.txt b/Documentation/block/cfq-iosched.txt index d89b4fe724d7..a5eb7d19a65d 100644 --- a/Documentation/block/cfq-iosched.txt +++ b/Documentation/block/cfq-iosched.txt @@ -102,6 +102,64 @@ processing of request. Therefore, increasing the value can imporve the performace although this can cause the latency of some I/O to increase due to more number of requests. +CFQ Group scheduling +==================== + +CFQ supports blkio cgroup and has "blkio." prefixed files in each +blkio cgroup directory. It is weight-based and there are four knobs +for configuration - weight[_device] and leaf_weight[_device]. +Internal cgroup nodes (the ones with children) can also have tasks in +them, so the former two configure how much proportion the cgroup as a +whole is entitled to at its parent's level while the latter two +configure how much proportion the tasks in the cgroup have compared to +its direct children. + +Another way to think about it is assuming that each internal node has +an implicit leaf child node which hosts all the tasks whose weight is +configured by leaf_weight[_device]. Let's assume a blkio hierarchy +composed of five cgroups - root, A, B, AA and AB - with the following +weights where the names represent the hierarchy. + + weight leaf_weight + root : 125 125 + A : 500 750 + B : 250 500 + AA : 500 500 + AB : 1000 500 + +root never has a parent making its weight is meaningless. For backward +compatibility, weight is always kept in sync with leaf_weight. B, AA +and AB have no child and thus its tasks have no children cgroup to +compete with. They always get 100% of what the cgroup won at the +parent level. Considering only the weights which matter, the hierarchy +looks like the following. + + root + / | \ + A B leaf + 500 250 125 + / | \ + AA AB leaf + 500 1000 750 + +If all cgroups have active IOs and competing with each other, disk +time will be distributed like the following. + +Distribution below root. The total active weight at this level is +A:500 + B:250 + C:125 = 875. + + root-leaf : 125 / 875 =~ 14% + A : 500 / 875 =~ 57% + B(-leaf) : 250 / 875 =~ 28% + +A has children and further distributes its 57% among the children and +the implicit leaf node. The total active weight at this level is +AA:500 + AB:1000 + A-leaf:750 = 2250. + + A-leaf : ( 750 / 2250) * A =~ 19% + AA(-leaf) : ( 500 / 2250) * A =~ 12% + AB(-leaf) : (1000 / 2250) * A =~ 25% + CFQ IOPS Mode for group scheduling =================================== Basic CFQ design is to provide priority based time slices. Higher priority diff --git a/Documentation/cgroups/blkio-controller.txt b/Documentation/cgroups/blkio-controller.txt index b4b1fb3a83f0..1b70843c574e 100644 --- a/Documentation/cgroups/blkio-controller.txt +++ b/Documentation/cgroups/blkio-controller.txt @@ -94,13 +94,11 @@ Throttling/Upper Limit policy Hierarchical Cgroups ==================== -- Currently none of the IO control policy supports hierarchical groups. But - cgroup interface does allow creation of hierarchical cgroups and internally - IO policies treat them as flat hierarchy. +- Currently only CFQ supports hierarchical groups. For throttling, + cgroup interface does allow creation of hierarchical cgroups and + internally it treats them as flat hierarchy. - So this patch will allow creation of cgroup hierarchcy but at the backend - everything will be treated as flat. So if somebody created a hierarchy like - as follows. + If somebody created a hierarchy like as follows. root / \ @@ -108,16 +106,20 @@ Hierarchical Cgroups | test3 - CFQ and throttling will practically treat all groups at same level. + CFQ will handle the hierarchy correctly but and throttling will + practically treat all groups at same level. For details on CFQ + hierarchy support, refer to Documentation/block/cfq-iosched.txt. + Throttling will treat the hierarchy as if it looks like the + following. pivot / / \ \ root test1 test2 test3 - Down the line we can implement hierarchical accounting/control support - and also introduce a new cgroup file "use_hierarchy" which will control - whether cgroup hierarchy is viewed as flat or hierarchical by the policy.. - This is how memory controller also has implemented the things. + Nesting cgroups, while allowed, isn't officially supported and blkio + genereates warning when cgroups nest. Once throttling implements + hierarchy support, hierarchy will be supported and the warning will + be removed. Various user visible config options =================================== @@ -172,6 +174,12 @@ Proportional weight policy files dev weight 8:16 300 +- blkio.leaf_weight[_device] + - Equivalents of blkio.weight[_device] for the purpose of + deciding how much weight tasks in the given cgroup has while + competing with the cgroup's child cgroups. For details, + please refer to Documentation/block/cfq-iosched.txt. + - blkio.time - disk time allocated to cgroup per device in milliseconds. First two fields specify the major and minor number of the device and @@ -279,6 +287,11 @@ Proportional weight policy files and minor number of the device and third field specifies the number of times a group was dequeued from a particular device. +- blkio.*_recursive + - Recursive version of various stats. These files show the + same information as their non-recursive counterparts but + include stats from all the descendant cgroups. + Throttling/Upper limit policy files ----------------------------------- - blkio.throttle.read_bps_device diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index ee342826fd98..e8f31069b379 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -606,20 +606,11 @@ static inline struct cfq_group *blkg_to_cfqg(struct blkcg_gq *blkg) return pd_to_cfqg(blkg_to_pd(blkg, &blkcg_policy_cfq)); } -/* - * Determine the parent cfqg for weight calculation. Currently, cfqg - * scheduling is flat and the root is the parent of everyone else. - */ -static inline struct cfq_group *cfqg_flat_parent(struct cfq_group *cfqg) +static inline struct cfq_group *cfqg_parent(struct cfq_group *cfqg) { - struct blkcg_gq *blkg = cfqg_to_blkg(cfqg); - struct cfq_group *root; + struct blkcg_gq *pblkg = cfqg_to_blkg(cfqg)->parent; - while (blkg->parent) - blkg = blkg->parent; - root = blkg_to_cfqg(blkg); - - return root != cfqg ? root : NULL; + return pblkg ? blkg_to_cfqg(pblkg) : NULL; } static inline void cfqg_get(struct cfq_group *cfqg) @@ -722,7 +713,7 @@ static void cfq_pd_reset_stats(struct blkcg_gq *blkg) #else /* CONFIG_CFQ_GROUP_IOSCHED */ -static inline struct cfq_group *cfqg_flat_parent(struct cfq_group *cfqg) { return NULL; } +static inline struct cfq_group *cfqg_parent(struct cfq_group *cfqg) { return NULL; } static inline void cfqg_get(struct cfq_group *cfqg) { } static inline void cfqg_put(struct cfq_group *cfqg) { } @@ -1290,7 +1281,7 @@ cfq_group_service_tree_add(struct cfq_rb_root *st, struct cfq_group *cfqg) * stops once an already activated node is met. vfraction * calculation should always continue to the root. */ - while ((parent = cfqg_flat_parent(pos))) { + while ((parent = cfqg_parent(pos))) { if (propagate) { propagate = !parent->nr_active++; parent->children_weight += pos->weight; @@ -1341,7 +1332,7 @@ cfq_group_service_tree_del(struct cfq_rb_root *st, struct cfq_group *cfqg) pos->children_weight -= pos->leaf_weight; while (propagate) { - struct cfq_group *parent = cfqg_flat_parent(pos); + struct cfq_group *parent = cfqg_parent(pos); /* @pos has 0 nr_active at this point */ WARN_ON_ONCE(pos->children_weight); From b276a876a014c5fa58a16f247c0933f6c42112e3 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:12 -0800 Subject: [PATCH 0723/4607] blkcg: add blkg_policy_data->plid Add pd->plid so that the policy a pd belongs to can be identified easily. This will be used to implement hierarchical blkg_[rw]stats. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 2 ++ block/blk-cgroup.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 10e1df9da46e..3a8de321d1f6 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -113,6 +113,7 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q, blkg->pd[i] = pd; pd->blkg = blkg; + pd->plid = i; /* invoke per-policy init */ if (pol->pd_init_fn) @@ -908,6 +909,7 @@ int blkcg_activate_policy(struct request_queue *q, blkg->pd[pol->plid] = pd; pd->blkg = blkg; + pd->plid = pol->plid; pol->pd_init_fn(blkg); spin_unlock(&blkg->blkcg->lock); diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 24462258200e..40f5b9768aac 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -81,8 +81,9 @@ struct blkg_rwstat { * beginning and pd_size can't be smaller than pd. */ struct blkg_policy_data { - /* the blkg this per-policy data belongs to */ + /* the blkg and policy id this per-policy data belongs to */ struct blkcg_gq *blkg; + int plid; /* used during policy activation */ struct list_head alloc_node; From f427d909648aa592c9588d0f66b5b457752a0cd1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:12 -0800 Subject: [PATCH 0724/4607] blkcg: implement blkcg_policy->on/offline_pd_fn() and blkcg_gq->online Add two blkcg_policy methods, ->online_pd_fn() and ->offline_pd_fn(), which are invoked as the policy_data gets activated and deactivated while holding both blkcg and q locks. Also, add blkcg_gq->online bool, which is set and cleared as the blkcg_gq gets activated and deactivated. This flag also is toggled while holding both blkcg and q locks. These will be used to implement hierarchical stats. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 21 ++++++++++++++++++++- block/blk-cgroup.h | 7 +++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 3a8de321d1f6..4d625d28e070 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -182,7 +182,7 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, struct blkcg_gq *new_blkg) { struct blkcg_gq *blkg; - int ret; + int i, ret; WARN_ON_ONCE(!rcu_read_lock_held()); lockdep_assert_held(q->queue_lock); @@ -218,7 +218,15 @@ static struct blkcg_gq *blkg_create(struct blkcg *blkcg, if (likely(!ret)) { hlist_add_head_rcu(&blkg->blkcg_node, &blkcg->blkg_list); list_add(&blkg->q_node, &q->blkg_list); + + for (i = 0; i < BLKCG_MAX_POLS; i++) { + struct blkcg_policy *pol = blkcg_policy[i]; + + if (blkg->pd[i] && pol->pd_online_fn) + pol->pd_online_fn(blkg); + } } + blkg->online = true; spin_unlock(&blkcg->lock); if (!ret) @@ -291,6 +299,7 @@ EXPORT_SYMBOL_GPL(blkg_lookup_create); static void blkg_destroy(struct blkcg_gq *blkg) { struct blkcg *blkcg = blkg->blkcg; + int i; lockdep_assert_held(blkg->q->queue_lock); lockdep_assert_held(&blkcg->lock); @@ -299,6 +308,14 @@ static void blkg_destroy(struct blkcg_gq *blkg) WARN_ON_ONCE(list_empty(&blkg->q_node)); WARN_ON_ONCE(hlist_unhashed(&blkg->blkcg_node)); + for (i = 0; i < BLKCG_MAX_POLS; i++) { + struct blkcg_policy *pol = blkcg_policy[i]; + + if (blkg->pd[i] && pol->pd_offline_fn) + pol->pd_offline_fn(blkg); + } + blkg->online = false; + radix_tree_delete(&blkcg->blkg_tree, blkg->q->id); list_del_init(&blkg->q_node); hlist_del_init_rcu(&blkg->blkcg_node); @@ -956,6 +973,8 @@ void blkcg_deactivate_policy(struct request_queue *q, /* grab blkcg lock too while removing @pd from @blkg */ spin_lock(&blkg->blkcg->lock); + if (pol->pd_offline_fn) + pol->pd_offline_fn(blkg); if (pol->pd_exit_fn) pol->pd_exit_fn(blkg); diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 40f5b9768aac..678e89ed8ecb 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -106,12 +106,17 @@ struct blkcg_gq { /* reference count */ int refcnt; + /* is this blkg online? protected by both blkcg and q locks */ + bool online; + struct blkg_policy_data *pd[BLKCG_MAX_POLS]; struct rcu_head rcu_head; }; typedef void (blkcg_pol_init_pd_fn)(struct blkcg_gq *blkg); +typedef void (blkcg_pol_online_pd_fn)(struct blkcg_gq *blkg); +typedef void (blkcg_pol_offline_pd_fn)(struct blkcg_gq *blkg); typedef void (blkcg_pol_exit_pd_fn)(struct blkcg_gq *blkg); typedef void (blkcg_pol_reset_pd_stats_fn)(struct blkcg_gq *blkg); @@ -124,6 +129,8 @@ struct blkcg_policy { /* operations */ blkcg_pol_init_pd_fn *pd_init_fn; + blkcg_pol_online_pd_fn *pd_online_fn; + blkcg_pol_offline_pd_fn *pd_offline_fn; blkcg_pol_exit_pd_fn *pd_exit_fn; blkcg_pol_reset_pd_stats_fn *pd_reset_stats_fn; }; From 4d5e80a76074786a49879ff482a83e72ad634606 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:12 -0800 Subject: [PATCH 0725/4607] blkcg: s/blkg_rwstat_sum()/blkg_rwstat_total()/ Rename blkg_rwstat_sum() to blkg_rwstat_total(). sum will be used for summing up stats from multiple blkgs. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.h | 4 ++-- block/cfq-iosched.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 678e89ed8ecb..586c0ac3309a 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -461,14 +461,14 @@ static inline struct blkg_rwstat blkg_rwstat_read(struct blkg_rwstat *rwstat) } /** - * blkg_rwstat_sum - read the total count of a blkg_rwstat + * blkg_rwstat_total - read the total count of a blkg_rwstat * @rwstat: blkg_rwstat to read * * Return the total count of @rwstat regardless of the IO direction. This * function can be called without synchronization and takes care of u64 * atomicity. */ -static inline uint64_t blkg_rwstat_sum(struct blkg_rwstat *rwstat) +static inline uint64_t blkg_rwstat_total(struct blkg_rwstat *rwstat) { struct blkg_rwstat tmp = blkg_rwstat_read(rwstat); diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index e8f31069b379..d43145cc0088 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -536,7 +536,7 @@ static void cfqg_stats_set_start_empty_time(struct cfq_group *cfqg) { struct cfqg_stats *stats = &cfqg->stats; - if (blkg_rwstat_sum(&stats->queued)) + if (blkg_rwstat_total(&stats->queued)) return; /* @@ -580,7 +580,7 @@ static void cfqg_stats_update_avg_queue_size(struct cfq_group *cfqg) struct cfqg_stats *stats = &cfqg->stats; blkg_stat_add(&stats->avg_queue_size_sum, - blkg_rwstat_sum(&stats->queued)); + blkg_rwstat_total(&stats->queued)); blkg_stat_add(&stats->avg_queue_size_samples, 1); cfqg_stats_update_group_wait_time(stats); } From b50da39f51139f81b3115d0f9d8632507f802755 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:12 -0800 Subject: [PATCH 0726/4607] blkcg: export __blkg_prfill_rwstat() Hierarchical stats for cfq-iosched will need __blkg_prfill_rwstat(). Export it. Signed-off-by: Tejun Heo Reported-by: Fengguang Wu --- block/blk-cgroup.c | 1 + 1 file changed, 1 insertion(+) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 4d625d28e070..3aec4cdc8968 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -552,6 +552,7 @@ u64 __blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd, seq_printf(sf, "%s Total %llu\n", dname, (unsigned long long)v); return v; } +EXPORT_SYMBOL_GPL(__blkg_prfill_rwstat); /** * blkg_prfill_stat - prfill callback for blkg_stat From 16b3de6652c7aef151f38726faf90f0dbc9e9c71 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:12 -0800 Subject: [PATCH 0727/4607] blkcg: implement blkg_[rw]stat_recursive_sum() and blkg_[rw]stat_merge() Implement blkg_[rw]stat_recursive_sum() and blkg_[rw]stat_merge(). The former two collect the [rw]stats designated by the target policy data and offset from the pd's subtree. The latter two add one [rw]stat to another. Note that the recursive sum functions require the queue lock to be held on entry to make blkg online test reliable. This is necessary to properly handle stats of a dying blkg. These will be used to implement hierarchical stats. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 107 +++++++++++++++++++++++++++++++++++++++++++++ block/blk-cgroup.h | 35 +++++++++++++++ 2 files changed, 142 insertions(+) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 3aec4cdc8968..f9797b244eb3 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -32,6 +32,26 @@ EXPORT_SYMBOL_GPL(blkcg_root); static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS]; +static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, + struct request_queue *q, bool update_hint); + +/** + * blkg_for_each_descendant_pre - pre-order walk of a blkg's descendants + * @d_blkg: loop cursor pointing to the current descendant + * @pos_cgrp: used for iteration + * @p_blkg: target blkg to walk descendants of + * + * Walk @c_blkg through the descendants of @p_blkg. Must be used with RCU + * read locked. If called under either blkcg or queue lock, the iteration + * is guaranteed to include all and only online blkgs. The caller may + * update @pos_cgrp by calling cgroup_rightmost_descendant() to skip + * subtree. + */ +#define blkg_for_each_descendant_pre(d_blkg, pos_cgrp, p_blkg) \ + cgroup_for_each_descendant_pre((pos_cgrp), (p_blkg)->blkcg->css.cgroup) \ + if (((d_blkg) = __blkg_lookup(cgroup_to_blkcg(pos_cgrp), \ + (p_blkg)->q, false))) + static bool blkcg_policy_enabled(struct request_queue *q, const struct blkcg_policy *pol) { @@ -127,6 +147,17 @@ err_free: return NULL; } +/** + * __blkg_lookup - internal version of blkg_lookup() + * @blkcg: blkcg of interest + * @q: request_queue of interest + * @update_hint: whether to update lookup hint with the result or not + * + * This is internal version and shouldn't be used by policy + * implementations. Looks up blkgs for the @blkcg - @q pair regardless of + * @q's bypass state. If @update_hint is %true, the caller should be + * holding @q->queue_lock and lookup hint is updated on success. + */ static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q, bool update_hint) { @@ -585,6 +616,82 @@ u64 blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd, } EXPORT_SYMBOL_GPL(blkg_prfill_rwstat); +/** + * blkg_stat_recursive_sum - collect hierarchical blkg_stat + * @pd: policy private data of interest + * @off: offset to the blkg_stat in @pd + * + * Collect the blkg_stat specified by @off from @pd and all its online + * descendants and return the sum. The caller must be holding the queue + * lock for online tests. + */ +u64 blkg_stat_recursive_sum(struct blkg_policy_data *pd, int off) +{ + struct blkcg_policy *pol = blkcg_policy[pd->plid]; + struct blkcg_gq *pos_blkg; + struct cgroup *pos_cgrp; + u64 sum; + + lockdep_assert_held(pd->blkg->q->queue_lock); + + sum = blkg_stat_read((void *)pd + off); + + rcu_read_lock(); + blkg_for_each_descendant_pre(pos_blkg, pos_cgrp, pd_to_blkg(pd)) { + struct blkg_policy_data *pos_pd = blkg_to_pd(pos_blkg, pol); + struct blkg_stat *stat = (void *)pos_pd + off; + + if (pos_blkg->online) + sum += blkg_stat_read(stat); + } + rcu_read_unlock(); + + return sum; +} +EXPORT_SYMBOL_GPL(blkg_stat_recursive_sum); + +/** + * blkg_rwstat_recursive_sum - collect hierarchical blkg_rwstat + * @pd: policy private data of interest + * @off: offset to the blkg_stat in @pd + * + * Collect the blkg_rwstat specified by @off from @pd and all its online + * descendants and return the sum. The caller must be holding the queue + * lock for online tests. + */ +struct blkg_rwstat blkg_rwstat_recursive_sum(struct blkg_policy_data *pd, + int off) +{ + struct blkcg_policy *pol = blkcg_policy[pd->plid]; + struct blkcg_gq *pos_blkg; + struct cgroup *pos_cgrp; + struct blkg_rwstat sum; + int i; + + lockdep_assert_held(pd->blkg->q->queue_lock); + + sum = blkg_rwstat_read((void *)pd + off); + + rcu_read_lock(); + blkg_for_each_descendant_pre(pos_blkg, pos_cgrp, pd_to_blkg(pd)) { + struct blkg_policy_data *pos_pd = blkg_to_pd(pos_blkg, pol); + struct blkg_rwstat *rwstat = (void *)pos_pd + off; + struct blkg_rwstat tmp; + + if (!pos_blkg->online) + continue; + + tmp = blkg_rwstat_read(rwstat); + + for (i = 0; i < BLKG_RWSTAT_NR; i++) + sum.cnt[i] += tmp.cnt[i]; + } + rcu_read_unlock(); + + return sum; +} +EXPORT_SYMBOL_GPL(blkg_rwstat_recursive_sum); + /** * blkg_conf_prep - parse and prepare for per-blkg config update * @blkcg: target block cgroup diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 586c0ac3309a..f2b292925ccd 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -164,6 +164,10 @@ u64 blkg_prfill_stat(struct seq_file *sf, struct blkg_policy_data *pd, int off); u64 blkg_prfill_rwstat(struct seq_file *sf, struct blkg_policy_data *pd, int off); +u64 blkg_stat_recursive_sum(struct blkg_policy_data *pd, int off); +struct blkg_rwstat blkg_rwstat_recursive_sum(struct blkg_policy_data *pd, + int off); + struct blkg_conf_ctx { struct gendisk *disk; struct blkcg_gq *blkg; @@ -413,6 +417,18 @@ static inline void blkg_stat_reset(struct blkg_stat *stat) stat->cnt = 0; } +/** + * blkg_stat_merge - merge a blkg_stat into another + * @to: the destination blkg_stat + * @from: the source + * + * Add @from's count to @to. + */ +static inline void blkg_stat_merge(struct blkg_stat *to, struct blkg_stat *from) +{ + blkg_stat_add(to, blkg_stat_read(from)); +} + /** * blkg_rwstat_add - add a value to a blkg_rwstat * @rwstat: target blkg_rwstat @@ -484,6 +500,25 @@ static inline void blkg_rwstat_reset(struct blkg_rwstat *rwstat) memset(rwstat->cnt, 0, sizeof(rwstat->cnt)); } +/** + * blkg_rwstat_merge - merge a blkg_rwstat into another + * @to: the destination blkg_rwstat + * @from: the source + * + * Add @from's counts to @to. + */ +static inline void blkg_rwstat_merge(struct blkg_rwstat *to, + struct blkg_rwstat *from) +{ + struct blkg_rwstat v = blkg_rwstat_read(from); + int i; + + u64_stats_update_begin(&to->syncp); + for (i = 0; i < BLKG_RWSTAT_NR; i++) + to->cnt[i] += v.cnt[i]; + u64_stats_update_end(&to->syncp); +} + #else /* CONFIG_BLK_CGROUP */ struct cgroup; From 548bc8e1b38e48653a90f48f636f8d253504f8a2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:13 -0800 Subject: [PATCH 0728/4607] block: RCU free request_queue RCU free request_queue so that blkcg_gq->q can be dereferenced under RCU lock. This will be used to implement hierarchical stats. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-sysfs.c | 9 ++++++++- include/linux/blkdev.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 788147797a79..6206a934eb8c 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -497,6 +497,13 @@ queue_attr_store(struct kobject *kobj, struct attribute *attr, return res; } +static void blk_free_queue_rcu(struct rcu_head *rcu_head) +{ + struct request_queue *q = container_of(rcu_head, struct request_queue, + rcu_head); + kmem_cache_free(blk_requestq_cachep, q); +} + /** * blk_release_queue: - release a &struct request_queue when it is no longer needed * @kobj: the kobj belonging to the request queue to be released @@ -538,7 +545,7 @@ static void blk_release_queue(struct kobject *kobj) bdi_destroy(&q->backing_dev_info); ida_simple_remove(&blk_queue_ida, q->id); - kmem_cache_free(blk_requestq_cachep, q); + call_rcu(&q->rcu_head, blk_free_queue_rcu); } static const struct sysfs_ops queue_sysfs_ops = { diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index f94bc83011ed..406343c43cda 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -437,6 +438,7 @@ struct request_queue { /* Throttle data */ struct throtl_data *td; #endif + struct rcu_head rcu_head; }; #define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */ From 810ecfa765f8be20c8d9c468885b3403a2232d2f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:13 -0800 Subject: [PATCH 0729/4607] blkcg: make blkcg_print_blkgs() grab q locks instead of blkcg lock Instead of holding blkcg->lock while walking ->blkg_list and executing prfill(), RCU walk ->blkg_list and hold the blkg's queue lock while executing prfill(). This makes prfill() implementations easier as stats are mostly protected by queue lock. This will be used to implement hierarchical stats. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/blk-cgroup.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index f9797b244eb3..87ea95d1f533 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -504,8 +504,9 @@ static const char *blkg_dev_name(struct blkcg_gq *blkg) * * This function invokes @prfill on each blkg of @blkcg if pd for the * policy specified by @pol exists. @prfill is invoked with @sf, the - * policy data and @data. If @show_total is %true, the sum of the return - * values from @prfill is printed with "Total" label at the end. + * policy data and @data and the matching queue lock held. If @show_total + * is %true, the sum of the return values from @prfill is printed with + * "Total" label at the end. * * This is to be used to construct print functions for * cftype->read_seq_string method. @@ -520,11 +521,14 @@ void blkcg_print_blkgs(struct seq_file *sf, struct blkcg *blkcg, struct hlist_node *n; u64 total = 0; - spin_lock_irq(&blkcg->lock); - hlist_for_each_entry(blkg, n, &blkcg->blkg_list, blkcg_node) + rcu_read_lock(); + hlist_for_each_entry_rcu(blkg, n, &blkcg->blkg_list, blkcg_node) { + spin_lock_irq(blkg->q->queue_lock); if (blkcg_policy_enabled(blkg->q, pol)) total += prfill(sf, blkg->pd[pol->plid], data); - spin_unlock_irq(&blkcg->lock); + spin_unlock_irq(blkg->q->queue_lock); + } + rcu_read_unlock(); if (show_total) seq_printf(sf, "Total %llu\n", (unsigned long long)total); From 689665af4489f779bc82e7869509c9ac11b5a903 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:13 -0800 Subject: [PATCH 0730/4607] cfq-iosched: separate out cfqg_stats_reset() from cfq_pd_reset_stats() Separate out cfqg_stats_reset() which takes struct cfqg_stats * from cfq_pd_reset_stats() and move the latter to where other pd methods are defined. cfqg_stats_reset() will be used to implement hierarchical stats. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/cfq-iosched.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index d43145cc0088..f8b34bbbd372 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -688,11 +688,9 @@ static inline void cfqg_stats_update_completion(struct cfq_group *cfqg, io_start_time - start_time); } -static void cfq_pd_reset_stats(struct blkcg_gq *blkg) +/* @stats = 0 */ +static void cfqg_stats_reset(struct cfqg_stats *stats) { - struct cfq_group *cfqg = blkg_to_cfqg(blkg); - struct cfqg_stats *stats = &cfqg->stats; - /* queued stats shouldn't be cleared */ blkg_rwstat_reset(&stats->service_bytes); blkg_rwstat_reset(&stats->serviced); @@ -1477,6 +1475,13 @@ static void cfq_pd_init(struct blkcg_gq *blkg) cfqg->leaf_weight = blkg->blkcg->cfq_leaf_weight; } +static void cfq_pd_reset_stats(struct blkcg_gq *blkg) +{ + struct cfq_group *cfqg = blkg_to_cfqg(blkg); + + cfqg_stats_reset(&cfqg->stats); +} + /* * Search for the cfq group current task belongs to. request_queue lock must * be held. From 0b39920b5f9f3ad37dd259bfa2e9cbca33475b28 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:13 -0800 Subject: [PATCH 0731/4607] cfq-iosched: collect stats from dead cfqgs To support hierarchical stats, it's necessary to remember stats from dead children. Add cfqg->dead_stats and make a dying cfqg transfer its stats to the parent's dead-stats. The transfer happens form ->pd_offline_fn() and it is possible that there are some residual IOs completing afterwards. Currently, we lose these stats. Given that cgroup removal isn't a very high frequency operation and the amount of residual IOs on offline are likely to be nil or small, this shouldn't be a big deal and the complexity needed to handle residual IOs - another callback and rather elaborate synchronization to reach and lock the matching q - doesn't seem justified. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/cfq-iosched.c | 57 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index f8b34bbbd372..4d75b7944574 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -289,7 +289,8 @@ struct cfq_group { /* number of requests that are on the dispatch list or inside driver */ int dispatched; struct cfq_ttime ttime; - struct cfqg_stats stats; + struct cfqg_stats stats; /* stats for this cfqg */ + struct cfqg_stats dead_stats; /* stats pushed from dead children */ }; struct cfq_io_cq { @@ -709,6 +710,47 @@ static void cfqg_stats_reset(struct cfqg_stats *stats) #endif } +/* @to += @from */ +static void cfqg_stats_merge(struct cfqg_stats *to, struct cfqg_stats *from) +{ + /* queued stats shouldn't be cleared */ + blkg_rwstat_merge(&to->service_bytes, &from->service_bytes); + blkg_rwstat_merge(&to->serviced, &from->serviced); + blkg_rwstat_merge(&to->merged, &from->merged); + blkg_rwstat_merge(&to->service_time, &from->service_time); + blkg_rwstat_merge(&to->wait_time, &from->wait_time); + blkg_stat_merge(&from->time, &from->time); +#ifdef CONFIG_DEBUG_BLK_CGROUP + blkg_stat_merge(&to->unaccounted_time, &from->unaccounted_time); + blkg_stat_merge(&to->avg_queue_size_sum, &from->avg_queue_size_sum); + blkg_stat_merge(&to->avg_queue_size_samples, &from->avg_queue_size_samples); + blkg_stat_merge(&to->dequeue, &from->dequeue); + blkg_stat_merge(&to->group_wait_time, &from->group_wait_time); + blkg_stat_merge(&to->idle_time, &from->idle_time); + blkg_stat_merge(&to->empty_time, &from->empty_time); +#endif +} + +/* + * Transfer @cfqg's stats to its parent's dead_stats so that the ancestors' + * recursive stats can still account for the amount used by this cfqg after + * it's gone. + */ +static void cfqg_stats_xfer_dead(struct cfq_group *cfqg) +{ + struct cfq_group *parent = cfqg_parent(cfqg); + + lockdep_assert_held(cfqg_to_blkg(cfqg)->q->queue_lock); + + if (unlikely(!parent)) + return; + + cfqg_stats_merge(&parent->dead_stats, &cfqg->stats); + cfqg_stats_merge(&parent->dead_stats, &cfqg->dead_stats); + cfqg_stats_reset(&cfqg->stats); + cfqg_stats_reset(&cfqg->dead_stats); +} + #else /* CONFIG_CFQ_GROUP_IOSCHED */ static inline struct cfq_group *cfqg_parent(struct cfq_group *cfqg) { return NULL; } @@ -1475,11 +1517,23 @@ static void cfq_pd_init(struct blkcg_gq *blkg) cfqg->leaf_weight = blkg->blkcg->cfq_leaf_weight; } +static void cfq_pd_offline(struct blkcg_gq *blkg) +{ + /* + * @blkg is going offline and will be ignored by + * blkg_[rw]stat_recursive_sum(). Transfer stats to the parent so + * that they don't get lost. If IOs complete after this point, the + * stats for them will be lost. Oh well... + */ + cfqg_stats_xfer_dead(blkg_to_cfqg(blkg)); +} + static void cfq_pd_reset_stats(struct blkcg_gq *blkg) { struct cfq_group *cfqg = blkg_to_cfqg(blkg); cfqg_stats_reset(&cfqg->stats); + cfqg_stats_reset(&cfqg->dead_stats); } /* @@ -4408,6 +4462,7 @@ static struct blkcg_policy blkcg_policy_cfq = { .cftypes = cfq_blkcg_files, .pd_init_fn = cfq_pd_init, + .pd_offline_fn = cfq_pd_offline, .pd_reset_stats_fn = cfq_pd_reset_stats, }; #endif From 43114018cb0b253fd03c4ff4d42bcdc43389ac1c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 9 Jan 2013 08:05:13 -0800 Subject: [PATCH 0732/4607] cfq-iosched: add hierarchical cfq_group statistics Unfortunately, at this point, there's no way to make the existing statistics hierarchical without creating nasty surprises for the existing users. Just create recursive counterpart of the existing stats. Signed-off-by: Tejun Heo Acked-by: Vivek Goyal --- block/cfq-iosched.c | 105 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 4d75b7944574..b66365b6ba77 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1528,6 +1528,32 @@ static void cfq_pd_offline(struct blkcg_gq *blkg) cfqg_stats_xfer_dead(blkg_to_cfqg(blkg)); } +/* offset delta from cfqg->stats to cfqg->dead_stats */ +static const int dead_stats_off_delta = offsetof(struct cfq_group, dead_stats) - + offsetof(struct cfq_group, stats); + +/* to be used by recursive prfill, sums live and dead stats recursively */ +static u64 cfqg_stat_pd_recursive_sum(struct blkg_policy_data *pd, int off) +{ + u64 sum = 0; + + sum += blkg_stat_recursive_sum(pd, off); + sum += blkg_stat_recursive_sum(pd, off + dead_stats_off_delta); + return sum; +} + +/* to be used by recursive prfill, sums live and dead rwstats recursively */ +static struct blkg_rwstat cfqg_rwstat_pd_recursive_sum(struct blkg_policy_data *pd, + int off) +{ + struct blkg_rwstat a, b; + + a = blkg_rwstat_recursive_sum(pd, off); + b = blkg_rwstat_recursive_sum(pd, off + dead_stats_off_delta); + blkg_rwstat_merge(&a, &b); + return a; +} + static void cfq_pd_reset_stats(struct blkcg_gq *blkg) { struct cfq_group *cfqg = blkg_to_cfqg(blkg); @@ -1732,6 +1758,42 @@ static int cfqg_print_rwstat(struct cgroup *cgrp, struct cftype *cft, return 0; } +static u64 cfqg_prfill_stat_recursive(struct seq_file *sf, + struct blkg_policy_data *pd, int off) +{ + u64 sum = cfqg_stat_pd_recursive_sum(pd, off); + + return __blkg_prfill_u64(sf, pd, sum); +} + +static u64 cfqg_prfill_rwstat_recursive(struct seq_file *sf, + struct blkg_policy_data *pd, int off) +{ + struct blkg_rwstat sum = cfqg_rwstat_pd_recursive_sum(pd, off); + + return __blkg_prfill_rwstat(sf, pd, &sum); +} + +static int cfqg_print_stat_recursive(struct cgroup *cgrp, struct cftype *cft, + struct seq_file *sf) +{ + struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + + blkcg_print_blkgs(sf, blkcg, cfqg_prfill_stat_recursive, + &blkcg_policy_cfq, cft->private, false); + return 0; +} + +static int cfqg_print_rwstat_recursive(struct cgroup *cgrp, struct cftype *cft, + struct seq_file *sf) +{ + struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + + blkcg_print_blkgs(sf, blkcg, cfqg_prfill_rwstat_recursive, + &blkcg_policy_cfq, cft->private, true); + return 0; +} + #ifdef CONFIG_DEBUG_BLK_CGROUP static u64 cfqg_prfill_avg_queue_size(struct seq_file *sf, struct blkg_policy_data *pd, int off) @@ -1803,6 +1865,7 @@ static struct cftype cfq_blkcg_files[] = { .write_u64 = cfq_set_leaf_weight, }, + /* statistics, covers only the tasks in the cfqg */ { .name = "time", .private = offsetof(struct cfq_group, stats.time), @@ -1843,6 +1906,48 @@ static struct cftype cfq_blkcg_files[] = { .private = offsetof(struct cfq_group, stats.queued), .read_seq_string = cfqg_print_rwstat, }, + + /* the same statictics which cover the cfqg and its descendants */ + { + .name = "time_recursive", + .private = offsetof(struct cfq_group, stats.time), + .read_seq_string = cfqg_print_stat_recursive, + }, + { + .name = "sectors_recursive", + .private = offsetof(struct cfq_group, stats.sectors), + .read_seq_string = cfqg_print_stat_recursive, + }, + { + .name = "io_service_bytes_recursive", + .private = offsetof(struct cfq_group, stats.service_bytes), + .read_seq_string = cfqg_print_rwstat_recursive, + }, + { + .name = "io_serviced_recursive", + .private = offsetof(struct cfq_group, stats.serviced), + .read_seq_string = cfqg_print_rwstat_recursive, + }, + { + .name = "io_service_time_recursive", + .private = offsetof(struct cfq_group, stats.service_time), + .read_seq_string = cfqg_print_rwstat_recursive, + }, + { + .name = "io_wait_time_recursive", + .private = offsetof(struct cfq_group, stats.wait_time), + .read_seq_string = cfqg_print_rwstat_recursive, + }, + { + .name = "io_merged_recursive", + .private = offsetof(struct cfq_group, stats.merged), + .read_seq_string = cfqg_print_rwstat_recursive, + }, + { + .name = "io_queued_recursive", + .private = offsetof(struct cfq_group, stats.queued), + .read_seq_string = cfqg_print_rwstat_recursive, + }, #ifdef CONFIG_DEBUG_BLK_CGROUP { .name = "avg_queue_size", From b6fca72536fb94098acb23c00b4e65d3bc4bb252 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Wed, 9 Jan 2013 20:06:28 +0530 Subject: [PATCH 0733/4607] sysctl: Enable IA64 "ignore-unaligned-usertrap" to be used cross-arch IA64 defines /proc/sys/kernel/ignore-unaligned-usertrap to control verbose warnings on unaligned access emulation. Although the exact mechanics of what to do with sysctl (ignore/shout) are arch specific, this change enables the sysctl to be usable cross-arch. Signed-off-by: Vineet Gupta Cc: Fenghua Yu Cc: "Eric W. Biederman" Cc: Serge Hallyn Signed-off-by: Tony Luck --- arch/ia64/Kconfig | 1 + init/Kconfig | 7 +++++++ kernel/sysctl.c | 9 +++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index 3279646120e3..5fb10644d9ed 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -40,6 +40,7 @@ config IA64 select ARCH_THREAD_INFO_ALLOCATOR select ARCH_CLOCKSOURCE_DATA select GENERIC_TIME_VSYSCALL_OLD + select SYSCTL_ARCH_UNALIGN_NO_WARN select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA default y diff --git a/init/Kconfig b/init/Kconfig index 7d30240e5bfe..523bee15fb44 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1232,6 +1232,13 @@ config SYSCTL_EXCEPTION_TRACE help Enable support for /proc/sys/debug/exception-trace. +config SYSCTL_ARCH_UNALIGN_NO_WARN + bool + help + Enable support for /proc/sys/kernel/ignore-unaligned-usertrap + Allows arch to define/use @no_unaligned_warning to possibly warn + about unaligned access emulation going on under the hood. + config KALLSYMS bool "Load all symbols for debugging/ksymoops" if EXPERT default y diff --git a/kernel/sysctl.c b/kernel/sysctl.c index c88878db491e..840fd5eb2309 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -161,10 +161,13 @@ extern int unaligned_enabled; #endif #ifdef CONFIG_IA64 -extern int no_unaligned_warning; extern int unaligned_dump_stack; #endif +#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_NO_WARN +extern int no_unaligned_warning; +#endif + #ifdef CONFIG_PROC_SYSCTL static int proc_do_cad_pid(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); @@ -911,7 +914,7 @@ static struct ctl_table kern_table[] = { .proc_handler = proc_doulongvec_minmax, }, #endif -#ifdef CONFIG_IA64 +#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_NO_WARN { .procname = "ignore-unaligned-usertrap", .data = &no_unaligned_warning, @@ -919,6 +922,8 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, +#endif +#ifdef CONFIG_IA64 { .procname = "unaligned-dump-stack", .data = &unaligned_dump_stack, From e28bbd44dad134046ef9463cbb8c1cf81f53de5e Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Fri, 4 Jan 2013 16:18:48 +0200 Subject: [PATCH 0734/4607] KVM: x86 emulator: framework for streamlining arithmetic opcodes We emulate arithmetic opcodes by executing a "similar" (same operation, different operands) on the cpu. This ensures accurate emulation, esp. wrt. eflags. However, the prologue and epilogue around the opcode is fairly long, consisting of a switch (for the operand size) and code to load and save the operands. This is repeated for every opcode. This patch introduces an alternative way to emulate arithmetic opcodes. Instead of the above, we have four (three on i386) functions consisting of just the opcode and a ret; one for each operand size. For example: .align 8 em_notb: not %al ret .align 8 em_notw: not %ax ret .align 8 em_notl: not %eax ret .align 8 em_notq: not %rax ret The prologue and epilogue are shared across all opcodes. Note the functions use a special calling convention; notably eflags is an input/output parameter and is not clobbered. Rather than dispatching the four functions through a jump table, the functions are declared as a constant size (8) so their address can be calculated. Acked-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 53c5ad6851d1..dd71567d7c71 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -149,6 +149,7 @@ #define Aligned ((u64)1 << 41) /* Explicitly aligned (e.g. MOVDQA) */ #define Unaligned ((u64)1 << 42) /* Explicitly unaligned (e.g. MOVDQU) */ #define Avx ((u64)1 << 43) /* Advanced Vector Extensions */ +#define Fastop ((u64)1 << 44) /* Use opcode::u.fastop */ #define X2(x...) x, x #define X3(x...) X2(x), x @@ -159,6 +160,27 @@ #define X8(x...) X4(x), X4(x) #define X16(x...) X8(x), X8(x) +#define NR_FASTOP (ilog2(sizeof(ulong)) + 1) +#define FASTOP_SIZE 8 + +/* + * fastop functions have a special calling convention: + * + * dst: [rdx]:rax (in/out) + * src: rbx (in/out) + * src2: rcx (in) + * flags: rflags (in/out) + * + * Moreover, they are all exactly FASTOP_SIZE bytes long, so functions for + * different operand sizes can be reached by calculation, rather than a jump + * table (which would be bigger than the code). + * + * fastop functions are declared as taking a never-defined fastop parameter, + * so they can't be called from C directly. + */ + +struct fastop; + struct opcode { u64 flags : 56; u64 intercept : 8; @@ -168,6 +190,7 @@ struct opcode { const struct group_dual *gdual; const struct gprefix *gprefix; const struct escape *esc; + void (*fastop)(struct fastop *fake); } u; int (*check_perm)(struct x86_emulate_ctxt *ctxt); }; @@ -3646,6 +3669,7 @@ static int check_perm_out(struct x86_emulate_ctxt *ctxt) #define GD(_f, _g) { .flags = ((_f) | GroupDual | ModRM), .u.gdual = (_g) } #define E(_f, _e) { .flags = ((_f) | Escape | ModRM), .u.esc = (_e) } #define I(_f, _e) { .flags = (_f), .u.execute = (_e) } +#define F(_f, _e) { .flags = (_f) | Fastop, .u.fastop = (_e) } #define II(_f, _e, _i) \ { .flags = (_f), .u.execute = (_e), .intercept = x86_intercept_##_i } #define IIP(_f, _e, _i, _p) \ @@ -4502,6 +4526,16 @@ static void fetch_possible_mmx_operand(struct x86_emulate_ctxt *ctxt, read_mmx_reg(ctxt, &op->mm_val, op->addr.mm); } +static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)) +{ + ulong flags = (ctxt->eflags & EFLAGS_MASK) | X86_EFLAGS_IF; + fop += __ffs(ctxt->dst.bytes) * FASTOP_SIZE; + asm("push %[flags]; popf; call *%[fastop]; pushf; pop %[flags]\n" + : "+a"(ctxt->dst.val), "+b"(ctxt->src.val), [flags]"+D"(flags) + : "c"(ctxt->src2.val), [fastop]"S"(fop)); + ctxt->eflags = (ctxt->eflags & ~EFLAGS_MASK) | (flags & EFLAGS_MASK); + return X86EMUL_CONTINUE; +} int x86_emulate_insn(struct x86_emulate_ctxt *ctxt) { @@ -4631,6 +4665,13 @@ special_insn: } if (ctxt->execute) { + if (ctxt->d & Fastop) { + void (*fop)(struct fastop *) = (void *)ctxt->execute; + rc = fastop(ctxt, fop); + if (rc != X86EMUL_CONTINUE) + goto done; + goto writeback; + } rc = ctxt->execute(ctxt); if (rc != X86EMUL_CONTINUE) goto done; From b7d491e7f065b5b957a27a65c9e7cd3ef96b2736 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Fri, 4 Jan 2013 16:18:49 +0200 Subject: [PATCH 0735/4607] KVM: x86 emulator: Support for declaring single operand fastops Acked-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index dd71567d7c71..42c53c8071be 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -24,6 +24,7 @@ #include "kvm_cache_regs.h" #include #include +#include #include "x86.h" #include "tss.h" @@ -439,6 +440,30 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt) } \ } while (0) +#define FOP_ALIGN ".align " __stringify(FASTOP_SIZE) " \n\t" +#define FOP_RET "ret \n\t" + +#define FOP_START(op) \ + extern void em_##op(struct fastop *fake); \ + asm(".pushsection .text, \"ax\" \n\t" \ + ".global em_" #op " \n\t" \ + FOP_ALIGN \ + "em_" #op ": \n\t" + +#define FOP_END \ + ".popsection") + +#define FOP1E(op, dst) \ + FOP_ALIGN #op " %" #dst " \n\t" FOP_RET + +#define FASTOP1(op) \ + FOP_START(op) \ + FOP1E(op##b, al) \ + FOP1E(op##w, ax) \ + FOP1E(op##l, eax) \ + ON64(FOP1E(op##q, rax)) \ + FOP_END + #define __emulate_1op_rax_rdx(ctxt, _op, _suffix, _ex) \ do { \ unsigned long _tmp; \ From b6744dc3fb55fda7cfcb53beecfa056f02415e6d Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Fri, 4 Jan 2013 16:18:50 +0200 Subject: [PATCH 0736/4607] KVM: x86 emulator: introduce NoWrite flag Instead of disabling writeback via OP_NONE, just specify NoWrite. Acked-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 42c53c8071be..fe113fbb3737 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -151,6 +151,7 @@ #define Unaligned ((u64)1 << 42) /* Explicitly unaligned (e.g. MOVDQU) */ #define Avx ((u64)1 << 43) /* Advanced Vector Extensions */ #define Fastop ((u64)1 << 44) /* Use opcode::u.fastop */ +#define NoWrite ((u64)1 << 45) /* No writeback */ #define X2(x...) x, x #define X3(x...) X2(x), x @@ -1633,6 +1634,9 @@ static int writeback(struct x86_emulate_ctxt *ctxt) { int rc; + if (ctxt->d & NoWrite) + return X86EMUL_CONTINUE; + switch (ctxt->dst.type) { case OP_REG: write_register_operand(&ctxt->dst); From 75f728456f368140e6b34b6a79f3408774076325 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Fri, 4 Jan 2013 16:18:51 +0200 Subject: [PATCH 0737/4607] KVM: x86 emulator: mark CMP, CMPS, SCAS, TEST as NoWrite Acked-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index fe113fbb3737..2af0c4475605 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -3069,16 +3069,12 @@ static int em_xor(struct x86_emulate_ctxt *ctxt) static int em_cmp(struct x86_emulate_ctxt *ctxt) { emulate_2op_SrcV(ctxt, "cmp"); - /* Disable writeback. */ - ctxt->dst.type = OP_NONE; return X86EMUL_CONTINUE; } static int em_test(struct x86_emulate_ctxt *ctxt) { emulate_2op_SrcV(ctxt, "test"); - /* Disable writeback. */ - ctxt->dst.type = OP_NONE; return X86EMUL_CONTINUE; } @@ -3747,7 +3743,7 @@ static const struct opcode group1[] = { I(Lock | PageTable, em_and), I(Lock, em_sub), I(Lock, em_xor), - I(0, em_cmp), + I(NoWrite, em_cmp), }; static const struct opcode group1A[] = { @@ -3755,8 +3751,8 @@ static const struct opcode group1A[] = { }; static const struct opcode group3[] = { - I(DstMem | SrcImm, em_test), - I(DstMem | SrcImm, em_test), + I(DstMem | SrcImm | NoWrite, em_test), + I(DstMem | SrcImm | NoWrite, em_test), I(DstMem | SrcNone | Lock, em_not), I(DstMem | SrcNone | Lock, em_neg), I(SrcMem, em_mul_ex), @@ -3920,7 +3916,7 @@ static const struct opcode opcode_table[256] = { /* 0x30 - 0x37 */ I6ALU(Lock, em_xor), N, N, /* 0x38 - 0x3F */ - I6ALU(0, em_cmp), N, N, + I6ALU(NoWrite, em_cmp), N, N, /* 0x40 - 0x4F */ X16(D(DstReg)), /* 0x50 - 0x57 */ @@ -3946,7 +3942,7 @@ static const struct opcode opcode_table[256] = { G(DstMem | SrcImm, group1), G(ByteOp | DstMem | SrcImm | No64, group1), G(DstMem | SrcImmByte, group1), - I2bv(DstMem | SrcReg | ModRM, em_test), + I2bv(DstMem | SrcReg | ModRM | NoWrite, em_test), I2bv(DstMem | SrcReg | ModRM | Lock | PageTable, em_xchg), /* 0x88 - 0x8F */ I2bv(DstMem | SrcReg | ModRM | Mov | PageTable, em_mov), @@ -3966,12 +3962,12 @@ static const struct opcode opcode_table[256] = { I2bv(DstAcc | SrcMem | Mov | MemAbs, em_mov), I2bv(DstMem | SrcAcc | Mov | MemAbs | PageTable, em_mov), I2bv(SrcSI | DstDI | Mov | String, em_mov), - I2bv(SrcSI | DstDI | String, em_cmp), + I2bv(SrcSI | DstDI | String | NoWrite, em_cmp), /* 0xA8 - 0xAF */ - I2bv(DstAcc | SrcImm, em_test), + I2bv(DstAcc | SrcImm | NoWrite, em_test), I2bv(SrcAcc | DstDI | Mov | String, em_mov), I2bv(SrcSI | DstAcc | Mov | String, em_mov), - I2bv(SrcAcc | DstDI | String, em_cmp), + I2bv(SrcAcc | DstDI | String | NoWrite, em_cmp), /* 0xB0 - 0xB7 */ X8(I(ByteOp | DstReg | SrcImm | Mov, em_mov)), /* 0xB8 - 0xBF */ From 45a1467d7edff741d97a8be28342440ee65aa03c Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Fri, 4 Jan 2013 16:18:52 +0200 Subject: [PATCH 0738/4607] KVM: x86 emulator: convert NOT, NEG to fastop Acked-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 2af0c4475605..09dbdc5a99e1 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -2050,17 +2050,8 @@ static int em_grp2(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } -static int em_not(struct x86_emulate_ctxt *ctxt) -{ - ctxt->dst.val = ~ctxt->dst.val; - return X86EMUL_CONTINUE; -} - -static int em_neg(struct x86_emulate_ctxt *ctxt) -{ - emulate_1op(ctxt, "neg"); - return X86EMUL_CONTINUE; -} +FASTOP1(not); +FASTOP1(neg); static int em_mul_ex(struct x86_emulate_ctxt *ctxt) { @@ -3753,8 +3744,8 @@ static const struct opcode group1A[] = { static const struct opcode group3[] = { I(DstMem | SrcImm | NoWrite, em_test), I(DstMem | SrcImm | NoWrite, em_test), - I(DstMem | SrcNone | Lock, em_not), - I(DstMem | SrcNone | Lock, em_neg), + F(DstMem | SrcNone | Lock, em_not), + F(DstMem | SrcNone | Lock, em_neg), I(SrcMem, em_mul_ex), I(SrcMem, em_imul_ex), I(SrcMem, em_div_ex), From f7857f35dbf8e7ca36ebff3f43888fd3fb0f0e70 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Fri, 4 Jan 2013 16:18:53 +0200 Subject: [PATCH 0739/4607] KVM: x86 emulator: add macros for defining 2-operand fastop emulation Acked-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 09dbdc5a99e1..3b5d4dd6750a 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -465,6 +465,17 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt) ON64(FOP1E(op##q, rax)) \ FOP_END +#define FOP2E(op, dst, src) \ + FOP_ALIGN #op " %" #src ", %" #dst " \n\t" FOP_RET + +#define FASTOP2(op) \ + FOP_START(op) \ + FOP2E(op##b, al, bl) \ + FOP2E(op##w, ax, bx) \ + FOP2E(op##l, eax, ebx) \ + ON64(FOP2E(op##q, rax, rbx)) \ + FOP_END + #define __emulate_1op_rax_rdx(ctxt, _op, _suffix, _ex) \ do { \ unsigned long _tmp; \ @@ -3696,6 +3707,7 @@ static int check_perm_out(struct x86_emulate_ctxt *ctxt) #define D2bv(_f) D((_f) | ByteOp), D(_f) #define D2bvIP(_f, _i, _p) DIP((_f) | ByteOp, _i, _p), DIP(_f, _i, _p) #define I2bv(_f, _e) I((_f) | ByteOp, _e), I(_f, _e) +#define F2bv(_f, _e) F((_f) | ByteOp, _e), F(_f, _e) #define I2bvIP(_f, _e, _i, _p) \ IIP((_f) | ByteOp, _e, _i, _p), IIP(_f, _e, _i, _p) From fb864fbc72fd4e2175fb64072fe9134d3a3ab89a Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Fri, 4 Jan 2013 16:18:54 +0200 Subject: [PATCH 0740/4607] KVM: x86 emulator: convert basic ALU ops to fastop Opcodes: TEST CMP ADD ADC SUB SBB XOR OR AND Acked-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 112 +++++++++++++---------------------------- 1 file changed, 34 insertions(+), 78 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 3b5d4dd6750a..619a33d0ee0a 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -3026,59 +3026,15 @@ static int em_ret_near_imm(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } -static int em_add(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV(ctxt, "add"); - return X86EMUL_CONTINUE; -} - -static int em_or(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV(ctxt, "or"); - return X86EMUL_CONTINUE; -} - -static int em_adc(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV(ctxt, "adc"); - return X86EMUL_CONTINUE; -} - -static int em_sbb(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV(ctxt, "sbb"); - return X86EMUL_CONTINUE; -} - -static int em_and(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV(ctxt, "and"); - return X86EMUL_CONTINUE; -} - -static int em_sub(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV(ctxt, "sub"); - return X86EMUL_CONTINUE; -} - -static int em_xor(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV(ctxt, "xor"); - return X86EMUL_CONTINUE; -} - -static int em_cmp(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV(ctxt, "cmp"); - return X86EMUL_CONTINUE; -} - -static int em_test(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV(ctxt, "test"); - return X86EMUL_CONTINUE; -} +FASTOP2(add); +FASTOP2(or); +FASTOP2(adc); +FASTOP2(sbb); +FASTOP2(and); +FASTOP2(sub); +FASTOP2(xor); +FASTOP2(cmp); +FASTOP2(test); static int em_xchg(struct x86_emulate_ctxt *ctxt) { @@ -3711,9 +3667,9 @@ static int check_perm_out(struct x86_emulate_ctxt *ctxt) #define I2bvIP(_f, _e, _i, _p) \ IIP((_f) | ByteOp, _e, _i, _p), IIP(_f, _e, _i, _p) -#define I6ALU(_f, _e) I2bv((_f) | DstMem | SrcReg | ModRM, _e), \ - I2bv(((_f) | DstReg | SrcMem | ModRM) & ~Lock, _e), \ - I2bv(((_f) & ~Lock) | DstAcc | SrcImm, _e) +#define F6ALU(_f, _e) F2bv((_f) | DstMem | SrcReg | ModRM, _e), \ + F2bv(((_f) | DstReg | SrcMem | ModRM) & ~Lock, _e), \ + F2bv(((_f) & ~Lock) | DstAcc | SrcImm, _e) static const struct opcode group7_rm1[] = { DI(SrcNone | Priv, monitor), @@ -3739,14 +3695,14 @@ static const struct opcode group7_rm7[] = { }; static const struct opcode group1[] = { - I(Lock, em_add), - I(Lock | PageTable, em_or), - I(Lock, em_adc), - I(Lock, em_sbb), - I(Lock | PageTable, em_and), - I(Lock, em_sub), - I(Lock, em_xor), - I(NoWrite, em_cmp), + F(Lock, em_add), + F(Lock | PageTable, em_or), + F(Lock, em_adc), + F(Lock, em_sbb), + F(Lock | PageTable, em_and), + F(Lock, em_sub), + F(Lock, em_xor), + F(NoWrite, em_cmp), }; static const struct opcode group1A[] = { @@ -3754,8 +3710,8 @@ static const struct opcode group1A[] = { }; static const struct opcode group3[] = { - I(DstMem | SrcImm | NoWrite, em_test), - I(DstMem | SrcImm | NoWrite, em_test), + F(DstMem | SrcImm | NoWrite, em_test), + F(DstMem | SrcImm | NoWrite, em_test), F(DstMem | SrcNone | Lock, em_not), F(DstMem | SrcNone | Lock, em_neg), I(SrcMem, em_mul_ex), @@ -3897,29 +3853,29 @@ static const struct escape escape_dd = { { static const struct opcode opcode_table[256] = { /* 0x00 - 0x07 */ - I6ALU(Lock, em_add), + F6ALU(Lock, em_add), I(ImplicitOps | Stack | No64 | Src2ES, em_push_sreg), I(ImplicitOps | Stack | No64 | Src2ES, em_pop_sreg), /* 0x08 - 0x0F */ - I6ALU(Lock | PageTable, em_or), + F6ALU(Lock | PageTable, em_or), I(ImplicitOps | Stack | No64 | Src2CS, em_push_sreg), N, /* 0x10 - 0x17 */ - I6ALU(Lock, em_adc), + F6ALU(Lock, em_adc), I(ImplicitOps | Stack | No64 | Src2SS, em_push_sreg), I(ImplicitOps | Stack | No64 | Src2SS, em_pop_sreg), /* 0x18 - 0x1F */ - I6ALU(Lock, em_sbb), + F6ALU(Lock, em_sbb), I(ImplicitOps | Stack | No64 | Src2DS, em_push_sreg), I(ImplicitOps | Stack | No64 | Src2DS, em_pop_sreg), /* 0x20 - 0x27 */ - I6ALU(Lock | PageTable, em_and), N, N, + F6ALU(Lock | PageTable, em_and), N, N, /* 0x28 - 0x2F */ - I6ALU(Lock, em_sub), N, I(ByteOp | DstAcc | No64, em_das), + F6ALU(Lock, em_sub), N, I(ByteOp | DstAcc | No64, em_das), /* 0x30 - 0x37 */ - I6ALU(Lock, em_xor), N, N, + F6ALU(Lock, em_xor), N, N, /* 0x38 - 0x3F */ - I6ALU(NoWrite, em_cmp), N, N, + F6ALU(NoWrite, em_cmp), N, N, /* 0x40 - 0x4F */ X16(D(DstReg)), /* 0x50 - 0x57 */ @@ -3945,7 +3901,7 @@ static const struct opcode opcode_table[256] = { G(DstMem | SrcImm, group1), G(ByteOp | DstMem | SrcImm | No64, group1), G(DstMem | SrcImmByte, group1), - I2bv(DstMem | SrcReg | ModRM | NoWrite, em_test), + F2bv(DstMem | SrcReg | ModRM | NoWrite, em_test), I2bv(DstMem | SrcReg | ModRM | Lock | PageTable, em_xchg), /* 0x88 - 0x8F */ I2bv(DstMem | SrcReg | ModRM | Mov | PageTable, em_mov), @@ -3965,12 +3921,12 @@ static const struct opcode opcode_table[256] = { I2bv(DstAcc | SrcMem | Mov | MemAbs, em_mov), I2bv(DstMem | SrcAcc | Mov | MemAbs | PageTable, em_mov), I2bv(SrcSI | DstDI | Mov | String, em_mov), - I2bv(SrcSI | DstDI | String | NoWrite, em_cmp), + F2bv(SrcSI | DstDI | String | NoWrite, em_cmp), /* 0xA8 - 0xAF */ - I2bv(DstAcc | SrcImm | NoWrite, em_test), + F2bv(DstAcc | SrcImm | NoWrite, em_test), I2bv(SrcAcc | DstDI | Mov | String, em_mov), I2bv(SrcSI | DstAcc | Mov | String, em_mov), - I2bv(SrcAcc | DstDI | String | NoWrite, em_cmp), + F2bv(SrcAcc | DstDI | String | NoWrite, em_cmp), /* 0xB0 - 0xB7 */ X8(I(ByteOp | DstReg | SrcImm | Mov, em_mov)), /* 0xB8 - 0xBF */ From 73fa21ea4fc662a2e8e85f84c4ca3fcb55fa4da2 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 7 Jan 2013 15:51:51 +0100 Subject: [PATCH 0741/4607] KVM: s390: Dynamic allocation of virtio-ccw I/O data. Dynamically allocate any data structures like ccw used when doing channel I/O. Otherwise, we'd need to add extra serialization for the different callbacks using the same data structures. Reported-by: Christian Borntraeger Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- drivers/s390/kvm/virtio_ccw.c | 284 +++++++++++++++++++++------------- 1 file changed, 176 insertions(+), 108 deletions(-) diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c index 1a5aff31d752..70419a75d0e0 100644 --- a/drivers/s390/kvm/virtio_ccw.c +++ b/drivers/s390/kvm/virtio_ccw.c @@ -46,11 +46,9 @@ struct vq_config_block { struct virtio_ccw_device { struct virtio_device vdev; - __u8 status; + __u8 *status; __u8 config[VIRTIO_CCW_CONFIG_SIZE]; struct ccw_device *cdev; - struct ccw1 *ccw; - __u32 area; __u32 curr_io; int err; wait_queue_head_t wait_q; @@ -127,14 +125,15 @@ static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) return ret; } -static int ccw_io_helper(struct virtio_ccw_device *vcdev, __u32 intparm) +static int ccw_io_helper(struct virtio_ccw_device *vcdev, + struct ccw1 *ccw, __u32 intparm) { int ret; unsigned long flags; int flag = intparm & VIRTIO_CCW_INTPARM_MASK; spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); - ret = ccw_device_start(vcdev->cdev, vcdev->ccw, intparm, 0, 0); + ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0); if (!ret) vcdev->curr_io |= flag; spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); @@ -167,18 +166,19 @@ static void virtio_ccw_kvm_notify(struct virtqueue *vq) do_kvm_notify(schid, virtqueue_get_queue_index(vq)); } -static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, int index) +static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, + struct ccw1 *ccw, int index) { vcdev->config_block->index = index; - vcdev->ccw->cmd_code = CCW_CMD_READ_VQ_CONF; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(struct vq_config_block); - vcdev->ccw->cda = (__u32)(unsigned long)(vcdev->config_block); - ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_VQ_CONF); + ccw->cmd_code = CCW_CMD_READ_VQ_CONF; + ccw->flags = 0; + ccw->count = sizeof(struct vq_config_block); + ccw->cda = (__u32)(unsigned long)(vcdev->config_block); + ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF); return vcdev->config_block->num; } -static void virtio_ccw_del_vq(struct virtqueue *vq) +static void virtio_ccw_del_vq(struct virtqueue *vq, struct ccw1 *ccw) { struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev); struct virtio_ccw_vq_info *info = vq->priv; @@ -197,11 +197,12 @@ static void virtio_ccw_del_vq(struct virtqueue *vq) info->info_block->align = 0; info->info_block->index = index; info->info_block->num = 0; - vcdev->ccw->cmd_code = CCW_CMD_SET_VQ; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(*info->info_block); - vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block); - ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | index); + ccw->cmd_code = CCW_CMD_SET_VQ; + ccw->flags = 0; + ccw->count = sizeof(*info->info_block); + ccw->cda = (__u32)(unsigned long)(info->info_block); + ret = ccw_io_helper(vcdev, ccw, + VIRTIO_CCW_DOING_SET_VQ | index); /* * -ENODEV isn't considered an error: The device is gone anyway. * This may happen on device detach. @@ -220,14 +221,23 @@ static void virtio_ccw_del_vq(struct virtqueue *vq) static void virtio_ccw_del_vqs(struct virtio_device *vdev) { struct virtqueue *vq, *n; + struct ccw1 *ccw; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return; + list_for_each_entry_safe(vq, n, &vdev->vqs, list) - virtio_ccw_del_vq(vq); + virtio_ccw_del_vq(vq, ccw); + + kfree(ccw); } static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, int i, vq_callback_t *callback, - const char *name) + const char *name, + struct ccw1 *ccw) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); int err; @@ -250,7 +260,7 @@ static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, err = -ENOMEM; goto out_err; } - info->num = virtio_ccw_read_vq_conf(vcdev, i); + info->num = virtio_ccw_read_vq_conf(vcdev, ccw, i); size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); if (info->queue == NULL) { @@ -277,11 +287,11 @@ static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, info->info_block->align = KVM_VIRTIO_CCW_RING_ALIGN; info->info_block->index = i; info->info_block->num = info->num; - vcdev->ccw->cmd_code = CCW_CMD_SET_VQ; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(*info->info_block); - vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block); - err = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | i); + ccw->cmd_code = CCW_CMD_SET_VQ; + ccw->flags = 0; + ccw->count = sizeof(*info->info_block); + ccw->cda = (__u32)(unsigned long)(info->info_block); + err = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_VQ | i); if (err) { dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n"); free_pages_exact(info->queue, size); @@ -312,9 +322,15 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, struct virtio_ccw_device *vcdev = to_vc_device(vdev); unsigned long *indicatorp = NULL; int ret, i; + struct ccw1 *ccw; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return -ENOMEM; for (i = 0; i < nvqs; ++i) { - vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i]); + vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i], + ccw); if (IS_ERR(vqs[i])) { ret = PTR_ERR(vqs[i]); vqs[i] = NULL; @@ -329,28 +345,30 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, *indicatorp = (unsigned long) &vcdev->indicators; /* Register queue indicators with host. */ vcdev->indicators = 0; - vcdev->ccw->cmd_code = CCW_CMD_SET_IND; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(vcdev->indicators); - vcdev->ccw->cda = (__u32)(unsigned long) indicatorp; - ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_IND); + ccw->cmd_code = CCW_CMD_SET_IND; + ccw->flags = 0; + ccw->count = sizeof(vcdev->indicators); + ccw->cda = (__u32)(unsigned long) indicatorp; + ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND); if (ret) goto out; /* Register indicators2 with host for config changes */ *indicatorp = (unsigned long) &vcdev->indicators2; vcdev->indicators2 = 0; - vcdev->ccw->cmd_code = CCW_CMD_SET_CONF_IND; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(vcdev->indicators2); - vcdev->ccw->cda = (__u32)(unsigned long) indicatorp; - ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_CONF_IND); + ccw->cmd_code = CCW_CMD_SET_CONF_IND; + ccw->flags = 0; + ccw->count = sizeof(vcdev->indicators2); + ccw->cda = (__u32)(unsigned long) indicatorp; + ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_CONF_IND); if (ret) goto out; kfree(indicatorp); + kfree(ccw); return 0; out: kfree(indicatorp); + kfree(ccw); virtio_ccw_del_vqs(vdev); return ret; } @@ -358,64 +376,95 @@ out: static void virtio_ccw_reset(struct virtio_device *vdev) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); + struct ccw1 *ccw; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return; /* Zero status bits. */ - vcdev->status = 0; + *vcdev->status = 0; /* Send a reset ccw on device. */ - vcdev->ccw->cmd_code = CCW_CMD_VDEV_RESET; - vcdev->ccw->flags = 0; - vcdev->ccw->count = 0; - vcdev->ccw->cda = 0; - ccw_io_helper(vcdev, VIRTIO_CCW_DOING_RESET); + ccw->cmd_code = CCW_CMD_VDEV_RESET; + ccw->flags = 0; + ccw->count = 0; + ccw->cda = 0; + ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_RESET); + kfree(ccw); } static u32 virtio_ccw_get_features(struct virtio_device *vdev) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); - struct virtio_feature_desc features; - int ret; + struct virtio_feature_desc *features; + int ret, rc; + struct ccw1 *ccw; - /* Read the feature bits from the host. */ - /* TODO: Features > 32 bits */ - features.index = 0; - vcdev->ccw->cmd_code = CCW_CMD_READ_FEAT; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(features); - vcdev->ccw->cda = vcdev->area; - ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_FEAT); - if (ret) + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) return 0; - memcpy(&features, (void *)(unsigned long)vcdev->area, - sizeof(features)); - return le32_to_cpu(features.features); + features = kzalloc(sizeof(*features), GFP_DMA | GFP_KERNEL); + if (!features) { + rc = 0; + goto out_free; + } + /* Read the feature bits from the host. */ + /* TODO: Features > 32 bits */ + features->index = 0; + ccw->cmd_code = CCW_CMD_READ_FEAT; + ccw->flags = 0; + ccw->count = sizeof(*features); + ccw->cda = (__u32)(unsigned long)features; + ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_FEAT); + if (ret) { + rc = 0; + goto out_free; + } + + rc = le32_to_cpu(features->features); + +out_free: + kfree(features); + kfree(ccw); + return rc; } static void virtio_ccw_finalize_features(struct virtio_device *vdev) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); - struct virtio_feature_desc features; + struct virtio_feature_desc *features; int i; + struct ccw1 *ccw; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return; + + features = kzalloc(sizeof(*features), GFP_DMA | GFP_KERNEL); + if (!features) + goto out_free; /* Give virtio_ring a chance to accept features. */ vring_transport_features(vdev); - for (i = 0; i < sizeof(*vdev->features) / sizeof(features.features); + for (i = 0; i < sizeof(*vdev->features) / sizeof(features->features); i++) { int highbits = i % 2 ? 32 : 0; - features.index = i; - features.features = cpu_to_le32(vdev->features[i / 2] - >> highbits); - memcpy((void *)(unsigned long)vcdev->area, &features, - sizeof(features)); + features->index = i; + features->features = cpu_to_le32(vdev->features[i / 2] + >> highbits); /* Write the feature bits to the host. */ - vcdev->ccw->cmd_code = CCW_CMD_WRITE_FEAT; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(features); - vcdev->ccw->cda = vcdev->area; - ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_FEAT); + ccw->cmd_code = CCW_CMD_WRITE_FEAT; + ccw->flags = 0; + ccw->count = sizeof(*features); + ccw->cda = (__u32)(unsigned long)features; + ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_FEAT); } +out_free: + kfree(features); + kfree(ccw); } static void virtio_ccw_get_config(struct virtio_device *vdev, @@ -423,19 +472,32 @@ static void virtio_ccw_get_config(struct virtio_device *vdev, { struct virtio_ccw_device *vcdev = to_vc_device(vdev); int ret; + struct ccw1 *ccw; + void *config_area; - /* Read the config area from the host. */ - vcdev->ccw->cmd_code = CCW_CMD_READ_CONF; - vcdev->ccw->flags = 0; - vcdev->ccw->count = offset + len; - vcdev->ccw->cda = vcdev->area; - ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_CONFIG); - if (ret) + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) return; - memcpy(vcdev->config, (void *)(unsigned long)vcdev->area, - sizeof(vcdev->config)); + config_area = kzalloc(VIRTIO_CCW_CONFIG_SIZE, GFP_DMA | GFP_KERNEL); + if (!config_area) + goto out_free; + + /* Read the config area from the host. */ + ccw->cmd_code = CCW_CMD_READ_CONF; + ccw->flags = 0; + ccw->count = offset + len; + ccw->cda = (__u32)(unsigned long)config_area; + ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_CONFIG); + if (ret) + goto out_free; + + memcpy(vcdev->config, config_area, sizeof(vcdev->config)); memcpy(buf, &vcdev->config[offset], len); + +out_free: + kfree(config_area); + kfree(ccw); } static void virtio_ccw_set_config(struct virtio_device *vdev, @@ -443,37 +505,55 @@ static void virtio_ccw_set_config(struct virtio_device *vdev, unsigned len) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); + struct ccw1 *ccw; + void *config_area; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return; + + config_area = kzalloc(VIRTIO_CCW_CONFIG_SIZE, GFP_DMA | GFP_KERNEL); + if (!config_area) + goto out_free; memcpy(&vcdev->config[offset], buf, len); /* Write the config area to the host. */ - memcpy((void *)(unsigned long)vcdev->area, vcdev->config, - sizeof(vcdev->config)); - vcdev->ccw->cmd_code = CCW_CMD_WRITE_CONF; - vcdev->ccw->flags = 0; - vcdev->ccw->count = offset + len; - vcdev->ccw->cda = vcdev->area; - ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_CONFIG); + memcpy(config_area, vcdev->config, sizeof(vcdev->config)); + ccw->cmd_code = CCW_CMD_WRITE_CONF; + ccw->flags = 0; + ccw->count = offset + len; + ccw->cda = (__u32)(unsigned long)config_area; + ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_CONFIG); + +out_free: + kfree(config_area); + kfree(ccw); } static u8 virtio_ccw_get_status(struct virtio_device *vdev) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); - return vcdev->status; + return *vcdev->status; } static void virtio_ccw_set_status(struct virtio_device *vdev, u8 status) { struct virtio_ccw_device *vcdev = to_vc_device(vdev); + struct ccw1 *ccw; + + ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); + if (!ccw) + return; /* Write the status to the host. */ - vcdev->status = status; - memcpy((void *)(unsigned long)vcdev->area, &status, sizeof(status)); - vcdev->ccw->cmd_code = CCW_CMD_WRITE_STATUS; - vcdev->ccw->flags = 0; - vcdev->ccw->count = sizeof(status); - vcdev->ccw->cda = vcdev->area; - ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_STATUS); + *vcdev->status = status; + ccw->cmd_code = CCW_CMD_WRITE_STATUS; + ccw->flags = 0; + ccw->count = sizeof(status); + ccw->cda = (__u32)(unsigned long)vcdev->status; + ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_STATUS); + kfree(ccw); } static struct virtio_config_ops virtio_ccw_config_ops = { @@ -499,9 +579,8 @@ static void virtio_ccw_release_dev(struct device *_d) dev); struct virtio_ccw_device *vcdev = to_vc_device(dev); - kfree((void *)(unsigned long)vcdev->area); + kfree(vcdev->status); kfree(vcdev->config_block); - kfree(vcdev->ccw); kfree(vcdev); } @@ -657,9 +736,6 @@ static int virtio_ccw_offline(struct ccw_device *cdev) } -/* Area needs to be big enough to fit status, features or configuration. */ -#define VIRTIO_AREA_SIZE VIRTIO_CCW_CONFIG_SIZE /* biggest possible use */ - static int virtio_ccw_online(struct ccw_device *cdev) { int ret; @@ -671,21 +747,14 @@ static int virtio_ccw_online(struct ccw_device *cdev) ret = -ENOMEM; goto out_free; } - vcdev->area = (__u32)(unsigned long)kzalloc(VIRTIO_AREA_SIZE, - GFP_DMA | GFP_KERNEL); - if (!vcdev->area) { - dev_warn(&cdev->dev, "Cound not get memory for virtio\n"); - ret = -ENOMEM; - goto out_free; - } vcdev->config_block = kzalloc(sizeof(*vcdev->config_block), GFP_DMA | GFP_KERNEL); if (!vcdev->config_block) { ret = -ENOMEM; goto out_free; } - vcdev->ccw = kzalloc(sizeof(*vcdev->ccw), GFP_DMA | GFP_KERNEL); - if (!vcdev->ccw) { + vcdev->status = kzalloc(sizeof(*vcdev->status), GFP_DMA | GFP_KERNEL); + if (!vcdev->status) { ret = -ENOMEM; goto out_free; } @@ -714,9 +783,8 @@ out_put: return ret; out_free: if (vcdev) { - kfree((void *)(unsigned long)vcdev->area); + kfree(vcdev->status); kfree(vcdev->config_block); - kfree(vcdev->ccw); } kfree(vcdev); return ret; From b26ba22bb4f12289f9d5eb878c490e674934a197 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Mon, 7 Jan 2013 15:51:52 +0100 Subject: [PATCH 0742/4607] KVM: s390: Gracefully handle busy conditions on ccw_device_start In rare cases a virtio command might try to issue a ccw before a former ccw was answered with a tsch. This will cause CC=2 (busy). Lets just retry in that case. Signed-off-by: Christian Borntraeger Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- drivers/s390/kvm/virtio_ccw.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c index 70419a75d0e0..2edd94af131c 100644 --- a/drivers/s390/kvm/virtio_ccw.c +++ b/drivers/s390/kvm/virtio_ccw.c @@ -132,11 +132,14 @@ static int ccw_io_helper(struct virtio_ccw_device *vcdev, unsigned long flags; int flag = intparm & VIRTIO_CCW_INTPARM_MASK; - spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); - ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0); - if (!ret) - vcdev->curr_io |= flag; - spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); + do { + spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); + ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0); + if (!ret) + vcdev->curr_io |= flag; + spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); + cpu_relax(); + } while (ret == -EBUSY); wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0); return ret ? ret : vcdev->err; } From 323a6bf1d6f4ec7907d9d8aacb4ae9590f755dda Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 13 Sep 2012 23:00:49 +0000 Subject: [PATCH 0743/4607] powerpc: Add a powerpc implementation of SHA-1 This patch adds a crypto driver which provides a powerpc accelerated implementation of SHA-1, accelerated in that it is written in asm. Original patch by Paul, minor fixups for upstream by moi. Lightly tested on 64-bit with the test program here: http://michael.ellerman.id.au/files/junkcode/sha1test.c Seems to work, and is "not slower" than the generic version. Needs testing on 32-bit. Signed-off-by: Paul Mackerras Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Makefile | 1 + arch/powerpc/crypto/Makefile | 9 ++ arch/powerpc/crypto/sha1-powerpc-asm.S | 179 +++++++++++++++++++++++++ arch/powerpc/crypto/sha1.c | 157 ++++++++++++++++++++++ crypto/Kconfig | 7 + 5 files changed, 353 insertions(+) create mode 100644 arch/powerpc/crypto/Makefile create mode 100644 arch/powerpc/crypto/sha1-powerpc-asm.S create mode 100644 arch/powerpc/crypto/sha1.c diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index b639852116fa..ba45cad088c9 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -143,6 +143,7 @@ core-y += arch/powerpc/kernel/ \ arch/powerpc/sysdev/ \ arch/powerpc/platforms/ \ arch/powerpc/math-emu/ \ + arch/powerpc/crypto/ \ arch/powerpc/net/ core-$(CONFIG_XMON) += arch/powerpc/xmon/ core-$(CONFIG_KVM) += arch/powerpc/kvm/ diff --git a/arch/powerpc/crypto/Makefile b/arch/powerpc/crypto/Makefile new file mode 100644 index 000000000000..2926fb9c570a --- /dev/null +++ b/arch/powerpc/crypto/Makefile @@ -0,0 +1,9 @@ +# +# powerpc/crypto/Makefile +# +# Arch-specific CryptoAPI modules. +# + +obj-$(CONFIG_CRYPTO_SHA1_PPC) += sha1-powerpc.o + +sha1-powerpc-y := sha1-powerpc-asm.o sha1.o diff --git a/arch/powerpc/crypto/sha1-powerpc-asm.S b/arch/powerpc/crypto/sha1-powerpc-asm.S new file mode 100644 index 000000000000..a5f8264d2d3c --- /dev/null +++ b/arch/powerpc/crypto/sha1-powerpc-asm.S @@ -0,0 +1,179 @@ +/* + * SHA-1 implementation for PowerPC. + * + * Copyright (C) 2005 Paul Mackerras + */ + +#include +#include + +/* + * We roll the registers for T, A, B, C, D, E around on each + * iteration; T on iteration t is A on iteration t+1, and so on. + * We use registers 7 - 12 for this. + */ +#define RT(t) ((((t)+5)%6)+7) +#define RA(t) ((((t)+4)%6)+7) +#define RB(t) ((((t)+3)%6)+7) +#define RC(t) ((((t)+2)%6)+7) +#define RD(t) ((((t)+1)%6)+7) +#define RE(t) ((((t)+0)%6)+7) + +/* We use registers 16 - 31 for the W values */ +#define W(t) (((t)%16)+16) + +#define LOADW(t) \ + lwz W(t),(t)*4(r4) + +#define STEPD0_LOAD(t) \ + andc r0,RD(t),RB(t); \ + and r6,RB(t),RC(t); \ + rotlwi RT(t),RA(t),5; \ + or r6,r6,r0; \ + add r0,RE(t),r15; \ + add RT(t),RT(t),r6; \ + add r14,r0,W(t); \ + lwz W((t)+4),((t)+4)*4(r4); \ + rotlwi RB(t),RB(t),30; \ + add RT(t),RT(t),r14 + +#define STEPD0_UPDATE(t) \ + and r6,RB(t),RC(t); \ + andc r0,RD(t),RB(t); \ + rotlwi RT(t),RA(t),5; \ + rotlwi RB(t),RB(t),30; \ + or r6,r6,r0; \ + add r0,RE(t),r15; \ + xor r5,W((t)+4-3),W((t)+4-8); \ + add RT(t),RT(t),r6; \ + xor W((t)+4),W((t)+4-16),W((t)+4-14); \ + add r0,r0,W(t); \ + xor W((t)+4),W((t)+4),r5; \ + add RT(t),RT(t),r0; \ + rotlwi W((t)+4),W((t)+4),1 + +#define STEPD1(t) \ + xor r6,RB(t),RC(t); \ + rotlwi RT(t),RA(t),5; \ + rotlwi RB(t),RB(t),30; \ + xor r6,r6,RD(t); \ + add r0,RE(t),r15; \ + add RT(t),RT(t),r6; \ + add r0,r0,W(t); \ + add RT(t),RT(t),r0 + +#define STEPD1_UPDATE(t) \ + xor r6,RB(t),RC(t); \ + rotlwi RT(t),RA(t),5; \ + rotlwi RB(t),RB(t),30; \ + xor r6,r6,RD(t); \ + add r0,RE(t),r15; \ + xor r5,W((t)+4-3),W((t)+4-8); \ + add RT(t),RT(t),r6; \ + xor W((t)+4),W((t)+4-16),W((t)+4-14); \ + add r0,r0,W(t); \ + xor W((t)+4),W((t)+4),r5; \ + add RT(t),RT(t),r0; \ + rotlwi W((t)+4),W((t)+4),1 + +#define STEPD2_UPDATE(t) \ + and r6,RB(t),RC(t); \ + and r0,RB(t),RD(t); \ + rotlwi RT(t),RA(t),5; \ + or r6,r6,r0; \ + rotlwi RB(t),RB(t),30; \ + and r0,RC(t),RD(t); \ + xor r5,W((t)+4-3),W((t)+4-8); \ + or r6,r6,r0; \ + xor W((t)+4),W((t)+4-16),W((t)+4-14); \ + add r0,RE(t),r15; \ + add RT(t),RT(t),r6; \ + add r0,r0,W(t); \ + xor W((t)+4),W((t)+4),r5; \ + add RT(t),RT(t),r0; \ + rotlwi W((t)+4),W((t)+4),1 + +#define STEP0LD4(t) \ + STEPD0_LOAD(t); \ + STEPD0_LOAD((t)+1); \ + STEPD0_LOAD((t)+2); \ + STEPD0_LOAD((t)+3) + +#define STEPUP4(t, fn) \ + STEP##fn##_UPDATE(t); \ + STEP##fn##_UPDATE((t)+1); \ + STEP##fn##_UPDATE((t)+2); \ + STEP##fn##_UPDATE((t)+3) + +#define STEPUP20(t, fn) \ + STEPUP4(t, fn); \ + STEPUP4((t)+4, fn); \ + STEPUP4((t)+8, fn); \ + STEPUP4((t)+12, fn); \ + STEPUP4((t)+16, fn) + +_GLOBAL(powerpc_sha_transform) + PPC_STLU r1,-STACKFRAMESIZE(r1) + SAVE_8GPRS(14, r1) + SAVE_10GPRS(22, r1) + + /* Load up A - E */ + lwz RA(0),0(r3) /* A */ + lwz RB(0),4(r3) /* B */ + lwz RC(0),8(r3) /* C */ + lwz RD(0),12(r3) /* D */ + lwz RE(0),16(r3) /* E */ + + LOADW(0) + LOADW(1) + LOADW(2) + LOADW(3) + + lis r15,0x5a82 /* K0-19 */ + ori r15,r15,0x7999 + STEP0LD4(0) + STEP0LD4(4) + STEP0LD4(8) + STEPUP4(12, D0) + STEPUP4(16, D0) + + lis r15,0x6ed9 /* K20-39 */ + ori r15,r15,0xeba1 + STEPUP20(20, D1) + + lis r15,0x8f1b /* K40-59 */ + ori r15,r15,0xbcdc + STEPUP20(40, D2) + + lis r15,0xca62 /* K60-79 */ + ori r15,r15,0xc1d6 + STEPUP4(60, D1) + STEPUP4(64, D1) + STEPUP4(68, D1) + STEPUP4(72, D1) + lwz r20,16(r3) + STEPD1(76) + lwz r19,12(r3) + STEPD1(77) + lwz r18,8(r3) + STEPD1(78) + lwz r17,4(r3) + STEPD1(79) + + lwz r16,0(r3) + add r20,RE(80),r20 + add RD(0),RD(80),r19 + add RC(0),RC(80),r18 + add RB(0),RB(80),r17 + add RA(0),RA(80),r16 + mr RE(0),r20 + stw RA(0),0(r3) + stw RB(0),4(r3) + stw RC(0),8(r3) + stw RD(0),12(r3) + stw RE(0),16(r3) + + REST_8GPRS(14, r1) + REST_10GPRS(22, r1) + addi r1,r1,STACKFRAMESIZE + blr diff --git a/arch/powerpc/crypto/sha1.c b/arch/powerpc/crypto/sha1.c new file mode 100644 index 000000000000..f9e8b9491efc --- /dev/null +++ b/arch/powerpc/crypto/sha1.c @@ -0,0 +1,157 @@ +/* + * Cryptographic API. + * + * powerpc implementation of the SHA1 Secure Hash Algorithm. + * + * Derived from cryptoapi implementation, adapted for in-place + * scatterlist interface. + * + * Derived from "crypto/sha1.c" + * Copyright (c) Alan Smithee. + * Copyright (c) Andrew McDonald + * Copyright (c) Jean-Francois Dive + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +extern void powerpc_sha_transform(u32 *state, const u8 *src, u32 *temp); + +static int sha1_init(struct shash_desc *desc) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + + *sctx = (struct sha1_state){ + .state = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 }, + }; + + return 0; +} + +static int sha1_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + unsigned int partial, done; + const u8 *src; + + partial = sctx->count & 0x3f; + sctx->count += len; + done = 0; + src = data; + + if ((partial + len) > 63) { + u32 temp[SHA_WORKSPACE_WORDS]; + + if (partial) { + done = -partial; + memcpy(sctx->buffer + partial, data, done + 64); + src = sctx->buffer; + } + + do { + powerpc_sha_transform(sctx->state, src, temp); + done += 64; + src = data + done; + } while (done + 63 < len); + + memset(temp, 0, sizeof(temp)); + partial = 0; + } + memcpy(sctx->buffer + partial, src, len - done); + + return 0; +} + + +/* Add padding and return the message digest. */ +static int sha1_final(struct shash_desc *desc, u8 *out) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + __be32 *dst = (__be32 *)out; + u32 i, index, padlen; + __be64 bits; + static const u8 padding[64] = { 0x80, }; + + bits = cpu_to_be64(sctx->count << 3); + + /* Pad out to 56 mod 64 */ + index = sctx->count & 0x3f; + padlen = (index < 56) ? (56 - index) : ((64+56) - index); + sha1_update(desc, padding, padlen); + + /* Append length */ + sha1_update(desc, (const u8 *)&bits, sizeof(bits)); + + /* Store state in digest */ + for (i = 0; i < 5; i++) + dst[i] = cpu_to_be32(sctx->state[i]); + + /* Wipe context */ + memset(sctx, 0, sizeof *sctx); + + return 0; +} + +static int sha1_export(struct shash_desc *desc, void *out) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + + memcpy(out, sctx, sizeof(*sctx)); + return 0; +} + +static int sha1_import(struct shash_desc *desc, const void *in) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + + memcpy(sctx, in, sizeof(*sctx)); + return 0; +} + +static struct shash_alg alg = { + .digestsize = SHA1_DIGEST_SIZE, + .init = sha1_init, + .update = sha1_update, + .final = sha1_final, + .export = sha1_export, + .import = sha1_import, + .descsize = sizeof(struct sha1_state), + .statesize = sizeof(struct sha1_state), + .base = { + .cra_name = "sha1", + .cra_driver_name= "sha1-powerpc", + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA1_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}; + +static int __init sha1_powerpc_mod_init(void) +{ + return crypto_register_shash(&alg); +} + +static void __exit sha1_powerpc_mod_fini(void) +{ + crypto_unregister_shash(&alg); +} + +module_init(sha1_powerpc_mod_init); +module_exit(sha1_powerpc_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm"); + +MODULE_ALIAS("sha1-powerpc"); diff --git a/crypto/Kconfig b/crypto/Kconfig index 4641d95651d3..8e6ae5ed8379 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -479,6 +479,13 @@ config CRYPTO_SHA1_ARM SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2) implemented using optimized ARM assembler. +config CRYPTO_SHA1_PPC + tristate "SHA1 digest algorithm (powerpc)" + depends on PPC + help + This is the powerpc hardware accelerated implementation of the + SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2). + config CRYPTO_SHA256 tristate "SHA224 and SHA256 digest algorithm" select CRYPTO_HASH From fb9125680d0e7c23eae7c6000acc91ea26acab9c Mon Sep 17 00:00:00 2001 From: Li Zhong Date: Wed, 17 Oct 2012 21:30:13 +0000 Subject: [PATCH 0744/4607] powerpc: Fix a lazy irq related WARING in arch_local_irq_restore() The pseries CPU hotplug code uses cede_processor without properly synchronizing the SW and HW interrupt enable state. This fixes it using the same helpers that were written for the idle code. Signed-off-by: Li Zhong ======================= Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/hotplug-cpu.c | 8 ++++++++ arch/powerpc/platforms/pseries/plpar_wrappers.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index a38956269fbf..217ca5c75b20 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -127,9 +127,16 @@ static void pseries_mach_cpu_die(void) get_lppaca()->donate_dedicated_cpu = 1; while (get_preferred_offline_state(cpu) == CPU_STATE_INACTIVE) { + while (!prep_irq_for_idle()) { + local_irq_enable(); + local_irq_disable(); + } + extended_cede_processor(cede_latency_hint); } + local_irq_disable(); + if (!get_lppaca()->shared_proc) get_lppaca()->donate_dedicated_cpu = 0; get_lppaca()->idle = 0; @@ -137,6 +144,7 @@ static void pseries_mach_cpu_die(void) if (get_preferred_offline_state(cpu) == CPU_STATE_ONLINE) { unregister_slb_shadow(hwcpu); + hard_irq_disable(); /* * Call to start_secondary_resume() will not return. * Kernel stack will be reset and start_secondary() diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h index e6cc34a67053..b93384929c4c 100644 --- a/arch/powerpc/platforms/pseries/plpar_wrappers.h +++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h @@ -2,6 +2,7 @@ #define _PSERIES_PLPAR_WRAPPERS_H #include +#include #include #include @@ -41,7 +42,14 @@ static inline long extended_cede_processor(unsigned long latency_hint) u8 old_latency_hint = get_cede_latency_hint(); set_cede_latency_hint(latency_hint); + rc = cede_processor(); +#ifdef CONFIG_TRACE_IRQFLAGS + /* Ensure that H_CEDE returns with IRQs on */ + if (WARN_ON(!(mfmsr() & MSR_EE))) + __hard_irq_enable(); +#endif + set_cede_latency_hint(old_latency_hint); return rc; From 1e18c17adf32b86474fd903071b0181de9334bd4 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Fri, 5 Oct 2012 08:07:15 +0000 Subject: [PATCH 0745/4607] powerpc: Enable the Watchdog vector for 405 The watchdog and FIT code has been #if 0'd for ever, if the CPU takes an exception to either of those vectors it will jump into the middle of the PIT or Data TLB code and surely crash. At least some (all?) 405 cores have both the WDT and FIT vectors defined, so lets have proper entry points for them. Tested that the WDT vector works on a 405F6 core. Signed-off-by: Jason Gunthorpe Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/head_40x.S | 47 +++++++++++++++++++++------------- arch/powerpc/kernel/traps.c | 2 +- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/arch/powerpc/kernel/head_40x.S b/arch/powerpc/kernel/head_40x.S index 4989661b710b..8a9b6f59822d 100644 --- a/arch/powerpc/kernel/head_40x.S +++ b/arch/powerpc/kernel/head_40x.S @@ -430,30 +430,18 @@ label: EXCEPTION(0x0F00, Trap_0F, unknown_exception, EXC_XFER_EE) /* 0x1000 - Programmable Interval Timer (PIT) Exception */ - START_EXCEPTION(0x1000, Decrementer) - NORMAL_EXCEPTION_PROLOG - lis r0,TSR_PIS@h - mtspr SPRN_TSR,r0 /* Clear the PIT exception */ - addi r3,r1,STACK_FRAME_OVERHEAD - EXC_XFER_LITE(0x1000, timer_interrupt) - -#if 0 -/* NOTE: - * FIT and WDT handlers are not implemented yet. - */ + . = 0x1000 + b Decrementer /* 0x1010 - Fixed Interval Timer (FIT) Exception */ - STND_EXCEPTION(0x1010, FITException, unknown_exception) + . = 0x1010 + b FITException /* 0x1020 - Watchdog Timer (WDT) Exception */ -#ifdef CONFIG_BOOKE_WDT - CRITICAL_EXCEPTION(0x1020, WDTException, WatchdogException) -#else - CRITICAL_EXCEPTION(0x1020, WDTException, unknown_exception) -#endif -#endif + . = 0x1020 + b WDTException /* 0x1100 - Data TLB Miss Exception * As the name implies, translation is not in the MMU, so search the @@ -738,6 +726,29 @@ label: (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ NOCOPY, crit_transfer_to_handler, ret_from_crit_exc) + /* Programmable Interval Timer (PIT) Exception. (from 0x1000) */ +Decrementer: + NORMAL_EXCEPTION_PROLOG + lis r0,TSR_PIS@h + mtspr SPRN_TSR,r0 /* Clear the PIT exception */ + addi r3,r1,STACK_FRAME_OVERHEAD + EXC_XFER_LITE(0x1000, timer_interrupt) + + /* Fixed Interval Timer (FIT) Exception. (from 0x1010) */ +FITException: + NORMAL_EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD; + EXC_XFER_EE(0x1010, unknown_exception) + + /* Watchdog Timer (WDT) Exception. (from 0x1020) */ +WDTException: + CRITICAL_EXCEPTION_PROLOG; + addi r3,r1,STACK_FRAME_OVERHEAD; + EXC_XFER_TEMPLATE(WatchdogException, 0x1020+2, + (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), + NOCOPY, crit_transfer_to_handler, + ret_from_crit_exc) + /* * The other Data TLB exceptions bail out to this point * if they can't resolve the lightweight TLB fault. diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 32518401af68..114ea241916f 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1515,7 +1515,7 @@ void unrecoverable_exception(struct pt_regs *regs) die("Unrecoverable exception", regs, SIGABRT); } -#ifdef CONFIG_BOOKE_WDT +#if defined(CONFIG_BOOKE_WDT) || defined(CONFIG_40x) /* * Default handler for a Watchdog exception, * spins until a reboot occurs From 2f1d4ea7bcd96d3796eba4087388bdb37322ee7a Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Tue, 6 Nov 2012 14:49:15 +0000 Subject: [PATCH 0746/4607] powerpc/pseries: Allow firmware features to match partial strings This allows firmware_features_table names to add a '*' at the end so that only partial strings are matched. When a '*' is added, only upto the '*' is matched when setting firmware feature bits. This is useful for the matching best energy feature. Signed-off-by: Michael Neuling cc: Vaidyanathan Srinivasan cc: Linux PPC dev Reviewed-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/firmware.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/pseries/firmware.c b/arch/powerpc/platforms/pseries/firmware.c index 7b56118f531c..55b98c3dc47a 100644 --- a/arch/powerpc/platforms/pseries/firmware.c +++ b/arch/powerpc/platforms/pseries/firmware.c @@ -33,6 +33,11 @@ typedef struct { char * name; } firmware_feature_t; +/* + * The names in this table match names in rtas/ibm,hypertas-functions. If the + * entry ends in a '*', only upto the '*' is matched. Otherwise the entire + * string must match. + */ static __initdata firmware_feature_t firmware_features_table[FIRMWARE_MAX_FEATURES] = { {FW_FEATURE_PFT, "hcall-pft"}, @@ -72,9 +77,20 @@ void __init fw_feature_init(const char *hypertas, unsigned long len) for (s = hypertas; s < hypertas + len; s += strlen(s) + 1) { for (i = 0; i < FIRMWARE_MAX_FEATURES; i++) { + const char *name = firmware_features_table[i].name; + size_t size; /* check value against table of strings */ - if (!firmware_features_table[i].name || - strcmp(firmware_features_table[i].name, s)) + if (!name) + continue; + /* + * If there is a '*' at the end of name, only check + * upto there + */ + size = strlen(name); + if (size && name[size - 1] == '*') { + if (strncmp(name, s, size - 1)) + continue; + } else if (strcmp(name, s)) continue; /* we have a match */ From 0388c79c99ccb43f711af57d2e14fcd6a5f45a06 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Tue, 6 Nov 2012 14:49:16 +0000 Subject: [PATCH 0747/4607] powerpc/pseries: Cleanup best_energy_hcall detection Currently we search for the best_energy hcall using a custom function. Move this to using the firmware_feature_table. Signed-off-by: Michael Neuling cc: Vaidyanathan Srinivasan cc: Linux PPC dev Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/firmware.h | 3 +- arch/powerpc/platforms/pseries/firmware.c | 1 + .../platforms/pseries/pseries_energy.c | 37 +------------------ 3 files changed, 5 insertions(+), 36 deletions(-) diff --git a/arch/powerpc/include/asm/firmware.h b/arch/powerpc/include/asm/firmware.h index 973cc3be011b..097dee57a7a9 100644 --- a/arch/powerpc/include/asm/firmware.h +++ b/arch/powerpc/include/asm/firmware.h @@ -50,6 +50,7 @@ #define FW_FEATURE_OPAL ASM_CONST(0x0000000010000000) #define FW_FEATURE_OPALv2 ASM_CONST(0x0000000020000000) #define FW_FEATURE_SET_MODE ASM_CONST(0x0000000040000000) +#define FW_FEATURE_BEST_ENERGY ASM_CONST(0x0000000080000000) #ifndef __ASSEMBLY__ @@ -64,7 +65,7 @@ enum { FW_FEATURE_BULK_REMOVE | FW_FEATURE_XDABR | FW_FEATURE_MULTITCE | FW_FEATURE_SPLPAR | FW_FEATURE_LPAR | FW_FEATURE_CMO | FW_FEATURE_VPHN | FW_FEATURE_XCMO | - FW_FEATURE_SET_MODE, + FW_FEATURE_SET_MODE | FW_FEATURE_BEST_ENERGY, FW_FEATURE_PSERIES_ALWAYS = 0, FW_FEATURE_POWERNV_POSSIBLE = FW_FEATURE_OPAL | FW_FEATURE_OPALv2, FW_FEATURE_POWERNV_ALWAYS = 0, diff --git a/arch/powerpc/platforms/pseries/firmware.c b/arch/powerpc/platforms/pseries/firmware.c index 55b98c3dc47a..aa3693f7fb27 100644 --- a/arch/powerpc/platforms/pseries/firmware.c +++ b/arch/powerpc/platforms/pseries/firmware.c @@ -62,6 +62,7 @@ firmware_features_table[FIRMWARE_MAX_FEATURES] = { {FW_FEATURE_SPLPAR, "hcall-splpar"}, {FW_FEATURE_VPHN, "hcall-vphn"}, {FW_FEATURE_SET_MODE, "hcall-set-mode"}, + {FW_FEATURE_BEST_ENERGY, "hcall-best-energy-1*"}, }; /* Build up the firmware features bitmask using the contents of diff --git a/arch/powerpc/platforms/pseries/pseries_energy.c b/arch/powerpc/platforms/pseries/pseries_energy.c index af281dce510a..a91e6dadda2c 100644 --- a/arch/powerpc/platforms/pseries/pseries_energy.c +++ b/arch/powerpc/platforms/pseries/pseries_energy.c @@ -21,6 +21,7 @@ #include #include #include +#include #define MODULE_VERS "1.0" @@ -32,40 +33,6 @@ static int sysfs_entries; /* Helper routines */ -/* - * Routine to detect firmware support for hcall - * return 1 if H_BEST_ENERGY is supported - * else return 0 - */ - -static int check_for_h_best_energy(void) -{ - struct device_node *rtas = NULL; - const char *hypertas, *s; - int length; - int rc = 0; - - rtas = of_find_node_by_path("/rtas"); - if (!rtas) - return 0; - - hypertas = of_get_property(rtas, "ibm,hypertas-functions", &length); - if (!hypertas) { - of_node_put(rtas); - return 0; - } - - /* hypertas will have list of strings with hcall names */ - for (s = hypertas; s < hypertas + length; s += strlen(s) + 1) { - if (!strncmp("hcall-best-energy-1", s, 19)) { - rc = 1; /* Found the string */ - break; - } - } - of_node_put(rtas); - return rc; -} - /* Helper Routines to convert between drc_index to cpu numbers */ static u32 cpu_to_drc_index(int cpu) @@ -262,7 +229,7 @@ static int __init pseries_energy_init(void) int cpu, err; struct device *cpu_dev; - if (!check_for_h_best_energy()) { + if (!firmware_has_feature(FW_FEATURE_BEST_ENERGY)) { printk(KERN_INFO "Hypercall H_BEST_ENERGY not supported\n"); return 0; } From c19d82486216b1c674208f76fed6b9eb4ee45ad3 Mon Sep 17 00:00:00 2001 From: Vinh Nguyen Huu Tuong Date: Mon, 2 Jul 2012 22:52:30 +0000 Subject: [PATCH 0748/4607] powerpc/44x: Support OCM(On Chip Memory) for APM821xx SoC and Bluestone board This patch consists of: - Add driver for OCM component - Export OCM Information at /sys/kernel/debug/ppc4xx_ocm/info Signed-off-by: Vinh Nguyen Huu Tuong Acked-by: Josh Boyer Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/boot/dts/bluestone.dts | 8 + arch/powerpc/include/asm/ppc4xx_ocm.h | 45 +++ arch/powerpc/platforms/44x/Kconfig | 8 + arch/powerpc/sysdev/Makefile | 1 + arch/powerpc/sysdev/ppc4xx_ocm.c | 415 ++++++++++++++++++++++++++ 5 files changed, 477 insertions(+) create mode 100644 arch/powerpc/include/asm/ppc4xx_ocm.h create mode 100644 arch/powerpc/sysdev/ppc4xx_ocm.c diff --git a/arch/powerpc/boot/dts/bluestone.dts b/arch/powerpc/boot/dts/bluestone.dts index 9d4917aebe6b..7daaca324c01 100644 --- a/arch/powerpc/boot/dts/bluestone.dts +++ b/arch/powerpc/boot/dts/bluestone.dts @@ -107,6 +107,14 @@ interrupt-parent = <&UIC0>; }; + OCM: ocm@400040000 { + compatible = "ibm,ocm"; + status = "ok"; + cell-index = <1>; + /* configured in U-Boot */ + reg = <4 0x00040000 0x8000>; /* 32K */ + }; + SDR0: sdr { compatible = "ibm,sdr-apm821xx"; dcr-reg = <0x00e 0x002>; diff --git a/arch/powerpc/include/asm/ppc4xx_ocm.h b/arch/powerpc/include/asm/ppc4xx_ocm.h new file mode 100644 index 000000000000..6ce904605538 --- /dev/null +++ b/arch/powerpc/include/asm/ppc4xx_ocm.h @@ -0,0 +1,45 @@ +/* + * PowerPC 4xx OCM memory allocation support + * + * (C) Copyright 2009, Applied Micro Circuits Corporation + * Victor Gallardo (vgallardo@amcc.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __ASM_POWERPC_PPC4XX_OCM_H__ +#define __ASM_POWERPC_PPC4XX_OCM_H__ + +#define PPC4XX_OCM_NON_CACHED 0 +#define PPC4XX_OCM_CACHED 1 + +#if defined(CONFIG_PPC4xx_OCM) + +void *ppc4xx_ocm_alloc(phys_addr_t *phys, int size, int align, + int flags, const char *owner); +void ppc4xx_ocm_free(const void *virt); + +#else + +#define ppc4xx_ocm_alloc(phys, size, align, flags, owner) NULL +#define ppc4xx_ocm_free(addr) ((void)0) + +#endif /* CONFIG_PPC4xx_OCM */ + +#endif /* __ASM_POWERPC_PPC4XX_OCM_H__ */ diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig index 8abf6fb8f410..0effe9f5a1ea 100644 --- a/arch/powerpc/platforms/44x/Kconfig +++ b/arch/powerpc/platforms/44x/Kconfig @@ -252,6 +252,14 @@ config PPC4xx_GPIO help Enable gpiolib support for ppc440 based boards +config PPC4xx_OCM + bool "PPC4xx On Chip Memory (OCM) support" + depends on 4xx + select PPC_LIB_RHEAP + help + Enable OCM support for PowerPC 4xx platforms with on chip memory, + OCM provides the fast place for memory access to improve performance. + # 44x specific CPU modules, selected based on the board above. config 440EP bool diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index a57600b3a4e3..f44f09f3d527 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_PPC_INDIRECT_PCI) += indirect_pci.o obj-$(CONFIG_PPC_I8259) += i8259.o obj-$(CONFIG_IPIC) += ipic.o obj-$(CONFIG_4xx) += uic.o +obj-$(CONFIG_PPC4xx_OCM) += ppc4xx_ocm.o obj-$(CONFIG_4xx_SOC) += ppc4xx_soc.o obj-$(CONFIG_XILINX_VIRTEX) += xilinx_intc.o obj-$(CONFIG_XILINX_PCI) += xilinx_pci.o diff --git a/arch/powerpc/sysdev/ppc4xx_ocm.c b/arch/powerpc/sysdev/ppc4xx_ocm.c new file mode 100644 index 000000000000..1b15f93479c3 --- /dev/null +++ b/arch/powerpc/sysdev/ppc4xx_ocm.c @@ -0,0 +1,415 @@ +/* + * PowerPC 4xx OCM memory allocation support + * + * (C) Copyright 2009, Applied Micro Circuits Corporation + * Victor Gallardo (vgallardo@amcc.com) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#define OCM_DISABLED 0 +#define OCM_ENABLED 1 + +struct ocm_block { + struct list_head list; + void __iomem *addr; + int size; + const char *owner; +}; + +/* non-cached or cached region */ +struct ocm_region { + phys_addr_t phys; + void __iomem *virt; + + int memtotal; + int memfree; + + rh_info_t *rh; + struct list_head list; +}; + +struct ocm_info { + int index; + int status; + int ready; + + phys_addr_t phys; + + int alignment; + int memtotal; + int cache_size; + + struct ocm_region nc; /* non-cached region */ + struct ocm_region c; /* cached region */ +}; + +static struct ocm_info *ocm_nodes; +static int ocm_count; + +static struct ocm_info *ocm_get_node(unsigned int index) +{ + if (index >= ocm_count) { + printk(KERN_ERR "PPC4XX OCM: invalid index"); + return NULL; + } + + return &ocm_nodes[index]; +} + +static int ocm_free_region(struct ocm_region *ocm_reg, const void *addr) +{ + struct ocm_block *blk, *tmp; + unsigned long offset; + + if (!ocm_reg->virt) + return 0; + + list_for_each_entry_safe(blk, tmp, &ocm_reg->list, list) { + if (blk->addr == addr) { + offset = addr - ocm_reg->virt; + ocm_reg->memfree += blk->size; + rh_free(ocm_reg->rh, offset); + list_del(&blk->list); + kfree(blk); + return 1; + } + } + + return 0; +} + +static void __init ocm_init_node(int count, struct device_node *node) +{ + struct ocm_info *ocm; + + const unsigned int *cell_index; + const unsigned int *cache_size; + int len; + + struct resource rsrc; + int ioflags; + + ocm = ocm_get_node(count); + + cell_index = of_get_property(node, "cell-index", &len); + if (!cell_index) { + printk(KERN_ERR "PPC4XX OCM: missing cell-index property"); + return; + } + ocm->index = *cell_index; + + if (of_device_is_available(node)) + ocm->status = OCM_ENABLED; + + cache_size = of_get_property(node, "cached-region-size", &len); + if (cache_size) + ocm->cache_size = *cache_size; + + if (of_address_to_resource(node, 0, &rsrc)) { + printk(KERN_ERR "PPC4XX OCM%d: could not get resource address\n", + ocm->index); + return; + } + + ocm->phys = rsrc.start; + ocm->memtotal = (rsrc.end - rsrc.start + 1); + + printk(KERN_INFO "PPC4XX OCM%d: %d Bytes (%s)\n", + ocm->index, ocm->memtotal, + (ocm->status == OCM_DISABLED) ? "disabled" : "enabled"); + + if (ocm->status == OCM_DISABLED) + return; + + /* request region */ + + if (!request_mem_region(ocm->phys, ocm->memtotal, "ppc4xx_ocm")) { + printk(KERN_ERR "PPC4XX OCM%d: could not request region\n", + ocm->index); + return; + } + + /* Configure non-cached and cached regions */ + + ocm->nc.phys = ocm->phys; + ocm->nc.memtotal = ocm->memtotal - ocm->cache_size; + ocm->nc.memfree = ocm->nc.memtotal; + + ocm->c.phys = ocm->phys + ocm->nc.memtotal; + ocm->c.memtotal = ocm->cache_size; + ocm->c.memfree = ocm->c.memtotal; + + if (ocm->nc.memtotal == 0) + ocm->nc.phys = 0; + + if (ocm->c.memtotal == 0) + ocm->c.phys = 0; + + printk(KERN_INFO "PPC4XX OCM%d: %d Bytes (non-cached)\n", + ocm->index, ocm->nc.memtotal); + + printk(KERN_INFO "PPC4XX OCM%d: %d Bytes (cached)\n", + ocm->index, ocm->c.memtotal); + + /* ioremap the non-cached region */ + if (ocm->nc.memtotal) { + ioflags = _PAGE_NO_CACHE | _PAGE_GUARDED | _PAGE_EXEC; + ocm->nc.virt = __ioremap(ocm->nc.phys, ocm->nc.memtotal, + ioflags); + + if (!ocm->nc.virt) { + printk(KERN_ERR + "PPC4XX OCM%d: failed to ioremap non-cached memory\n", + ocm->index); + ocm->nc.memfree = 0; + return; + } + } + + /* ioremap the cached region */ + + if (ocm->c.memtotal) { + ioflags = _PAGE_EXEC; + ocm->c.virt = __ioremap(ocm->c.phys, ocm->c.memtotal, + ioflags); + + if (!ocm->c.virt) { + printk(KERN_ERR + "PPC4XX OCM%d: failed to ioremap cached memory\n", + ocm->index); + ocm->c.memfree = 0; + return; + } + } + + /* Create Remote Heaps */ + + ocm->alignment = 4; /* default 4 byte alignment */ + + if (ocm->nc.virt) { + ocm->nc.rh = rh_create(ocm->alignment); + rh_attach_region(ocm->nc.rh, 0, ocm->nc.memtotal); + } + + if (ocm->c.virt) { + ocm->c.rh = rh_create(ocm->alignment); + rh_attach_region(ocm->c.rh, 0, ocm->c.memtotal); + } + + INIT_LIST_HEAD(&ocm->nc.list); + INIT_LIST_HEAD(&ocm->c.list); + + ocm->ready = 1; + + return; +} + +static int ocm_debugfs_show(struct seq_file *m, void *v) +{ + struct ocm_block *blk, *tmp; + unsigned int i; + + for (i = 0; i < ocm_count; i++) { + struct ocm_info *ocm = ocm_get_node(i); + + if (!ocm || !ocm->ready) + continue; + + seq_printf(m, "PPC4XX OCM : %d\n", ocm->index); + seq_printf(m, "PhysAddr : 0x%llx\n", ocm->phys); + seq_printf(m, "MemTotal : %d Bytes\n", ocm->memtotal); + seq_printf(m, "MemTotal(NC) : %d Bytes\n", ocm->nc.memtotal); + seq_printf(m, "MemTotal(C) : %d Bytes\n", ocm->c.memtotal); + + seq_printf(m, "\n"); + + seq_printf(m, "NC.PhysAddr : 0x%llx\n", ocm->nc.phys); + seq_printf(m, "NC.VirtAddr : 0x%p\n", ocm->nc.virt); + seq_printf(m, "NC.MemTotal : %d Bytes\n", ocm->nc.memtotal); + seq_printf(m, "NC.MemFree : %d Bytes\n", ocm->nc.memfree); + + list_for_each_entry_safe(blk, tmp, &ocm->nc.list, list) { + seq_printf(m, "NC.MemUsed : %d Bytes (%s)\n", + blk->size, blk->owner); + } + + seq_printf(m, "\n"); + + seq_printf(m, "C.PhysAddr : 0x%llx\n", ocm->c.phys); + seq_printf(m, "C.VirtAddr : 0x%p\n", ocm->c.virt); + seq_printf(m, "C.MemTotal : %d Bytes\n", ocm->c.memtotal); + seq_printf(m, "C.MemFree : %d Bytes\n", ocm->c.memfree); + + list_for_each_entry_safe(blk, tmp, &ocm->c.list, list) { + seq_printf(m, "C.MemUsed : %d Bytes (%s)\n", + blk->size, blk->owner); + } + + seq_printf(m, "\n"); + } + + return 0; +} + +static int ocm_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, ocm_debugfs_show, NULL); +} + +static const struct file_operations ocm_debugfs_fops = { + .open = ocm_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int ocm_debugfs_init(void) +{ + struct dentry *junk; + + junk = debugfs_create_dir("ppc4xx_ocm", 0); + if (!junk) { + printk(KERN_ALERT "debugfs ppc4xx ocm: failed to create dir\n"); + return -1; + } + + if (debugfs_create_file("info", 0644, junk, NULL, &ocm_debugfs_fops)) { + printk(KERN_ALERT "debugfs ppc4xx ocm: failed to create file\n"); + return -1; + } + + return 0; +} + +void *ppc4xx_ocm_alloc(phys_addr_t *phys, int size, int align, + int flags, const char *owner) +{ + void __iomem *addr = NULL; + unsigned long offset; + struct ocm_info *ocm; + struct ocm_region *ocm_reg; + struct ocm_block *ocm_blk; + int i; + + for (i = 0; i < ocm_count; i++) { + ocm = ocm_get_node(i); + + if (!ocm || !ocm->ready) + continue; + + if (flags == PPC4XX_OCM_NON_CACHED) + ocm_reg = &ocm->nc; + else + ocm_reg = &ocm->c; + + if (!ocm_reg->virt) + continue; + + if (align < ocm->alignment) + align = ocm->alignment; + + offset = rh_alloc_align(ocm_reg->rh, size, align, NULL); + + if (IS_ERR_VALUE(offset)) + continue; + + ocm_blk = kzalloc(sizeof(struct ocm_block *), GFP_KERNEL); + if (!ocm_blk) { + printk(KERN_ERR "PPC4XX OCM: could not allocate ocm block"); + rh_free(ocm_reg->rh, offset); + break; + } + + *phys = ocm_reg->phys + offset; + addr = ocm_reg->virt + offset; + size = ALIGN(size, align); + + ocm_blk->addr = addr; + ocm_blk->size = size; + ocm_blk->owner = owner; + list_add_tail(&ocm_blk->list, &ocm_reg->list); + + ocm_reg->memfree -= size; + + break; + } + + return addr; +} + +void ppc4xx_ocm_free(const void *addr) +{ + int i; + + if (!addr) + return; + + for (i = 0; i < ocm_count; i++) { + struct ocm_info *ocm = ocm_get_node(i); + + if (!ocm || !ocm->ready) + continue; + + if (ocm_free_region(&ocm->nc, addr) || + ocm_free_region(&ocm->c, addr)) + return; + } +} + +static int __init ppc4xx_ocm_init(void) +{ + struct device_node *np; + int count; + + count = 0; + for_each_compatible_node(np, NULL, "ibm,ocm") + count++; + + if (!count) + return 0; + + ocm_nodes = kzalloc((count * sizeof(struct ocm_info)), GFP_KERNEL); + if (!ocm_nodes) { + printk(KERN_ERR "PPC4XX OCM: failed to allocate OCM nodes!\n"); + return -ENOMEM; + } + + ocm_count = count; + count = 0; + + for_each_compatible_node(np, NULL, "ibm,ocm") { + ocm_init_node(count, np); + count++; + } + + ocm_debugfs_init(); + + return 0; +} + +arch_initcall(ppc4xx_ocm_init); From 41c7b401b9c131a53823673a788d73e340ce4d2a Mon Sep 17 00:00:00 2001 From: Gernot Vormayr Date: Wed, 14 Nov 2012 06:59:09 +0000 Subject: [PATCH 0749/4607] powerpc/dts/virtex440: Add ethernet phy to virtex440-ml507 board This adds the marvel phy which is present on the ml507 board. Without this ethtool causes kernel-oopses. Tested on ml507 board. Signed-off-by: Gernot Vormayr Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/boot/dts/virtex440-ml507.dts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/powerpc/boot/dts/virtex440-ml507.dts b/arch/powerpc/boot/dts/virtex440-ml507.dts index 52d8c1ad26a1..fc7073bc547e 100644 --- a/arch/powerpc/boot/dts/virtex440-ml507.dts +++ b/arch/powerpc/boot/dts/virtex440-ml507.dts @@ -272,6 +272,12 @@ xlnx,temac-type = <0>; xlnx,txcsum = <1>; xlnx,txfifo = <0x1000>; + phy-handle = <&phy7>; + clock-frequency = <100000000>; + phy7: phy@7 { + compatible = "marvell,88e1111"; + reg = <7>; + } ; } ; } ; IIC_EEPROM: i2c@81600000 { From 42d02b81f265b77be39262666c888d50cb488fc5 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Wed, 14 Nov 2012 18:49:44 +0000 Subject: [PATCH 0750/4607] powerpc: Define differences between doorbells on book3e and book3s There are a few key differences between doorbells on server compared with embedded that we care about on Linux, namely: - We have a new msgsndp instruction for directed privileged doorbells. msgsnd is used for directed hypervisor doorbells. - The tag we use in the instruction is the Thread Identification Register of the recipient thread (since server doorbells can only occur between threads within a single core), and is only 7 bits wide. - A new message type is introduced for server doorbells (none of the existing book3e message types are currently supported on book3s). Signed-off-by: Ian Munsie Tested-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/dbell.h | 15 +++++++++++++++ arch/powerpc/include/asm/ppc-opcode.h | 3 +++ arch/powerpc/include/asm/reg.h | 1 + arch/powerpc/kernel/dbell.c | 4 ++-- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/dbell.h b/arch/powerpc/include/asm/dbell.h index 607e4eeeb694..3b338565f992 100644 --- a/arch/powerpc/include/asm/dbell.h +++ b/arch/powerpc/include/asm/dbell.h @@ -28,8 +28,23 @@ enum ppc_dbell { PPC_G_DBELL = 2, /* guest doorbell */ PPC_G_DBELL_CRIT = 3, /* guest critical doorbell */ PPC_G_DBELL_MC = 4, /* guest mcheck doorbell */ + PPC_DBELL_SERVER = 5, /* doorbell on server */ }; +#ifdef CONFIG_PPC_BOOK3S + +#define PPC_DBELL_MSGTYPE PPC_DBELL_SERVER +#define SPRN_DOORBELL_CPUTAG SPRN_TIR +#define PPC_DBELL_TAG_MASK 0x7f + +#else /* CONFIG_PPC_BOOK3S */ + +#define PPC_DBELL_MSGTYPE PPC_DBELL +#define SPRN_DOORBELL_CPUTAG SPRN_PIR +#define PPC_DBELL_TAG_MASK 0x3fff + +#endif /* CONFIG_PPC_BOOK3S */ + extern void doorbell_cause_ipi(int cpu, unsigned long data); extern void doorbell_exception(struct pt_regs *regs); extern void doorbell_setup_this_cpu(void); diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 51fb00a20d7e..0fd1928efb93 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -100,6 +100,7 @@ #define PPC_INST_MFSPR_PVR 0x7c1f42a6 #define PPC_INST_MFSPR_PVR_MASK 0xfc1fffff #define PPC_INST_MSGSND 0x7c00019c +#define PPC_INST_MSGSNDP 0x7c00011c #define PPC_INST_NOP 0x60000000 #define PPC_INST_POPCNTB 0x7c0000f4 #define PPC_INST_POPCNTB_MASK 0xfc0007fe @@ -227,6 +228,8 @@ ___PPC_RB(b) | __PPC_EH(eh)) #define PPC_MSGSND(b) stringify_in_c(.long PPC_INST_MSGSND | \ ___PPC_RB(b)) +#define PPC_MSGSNDP(b) stringify_in_c(.long PPC_INST_MSGSNDP | \ + ___PPC_RB(b)) #define PPC_POPCNTB(a, s) stringify_in_c(.long PPC_INST_POPCNTB | \ __PPC_RA(a) | __PPC_RS(s)) #define PPC_POPCNTD(a, s) stringify_in_c(.long PPC_INST_POPCNTD | \ diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 3d5c9dc8917a..af88486b0c23 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -483,6 +483,7 @@ #ifndef SPRN_PIR #define SPRN_PIR 0x3FF /* Processor Identification Register */ #endif +#define SPRN_TIR 0x1BE /* Thread Identification Register */ #define SPRN_PTEHI 0x3D5 /* 981 7450 PTE HI word (S/W TLB load) */ #define SPRN_PTELO 0x3D6 /* 982 7450 PTE LO word (S/W TLB load) */ #define SPRN_PURR 0x135 /* Processor Utilization of Resources Reg */ diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c index a892680668d8..9ebbc24bb23c 100644 --- a/arch/powerpc/kernel/dbell.c +++ b/arch/powerpc/kernel/dbell.c @@ -21,7 +21,7 @@ #ifdef CONFIG_SMP void doorbell_setup_this_cpu(void) { - unsigned long tag = mfspr(SPRN_PIR) & 0x3fff; + unsigned long tag = mfspr(SPRN_DOORBELL_CPUTAG) & PPC_DBELL_TAG_MASK; smp_muxed_ipi_set_data(smp_processor_id(), tag); } @@ -30,7 +30,7 @@ void doorbell_cause_ipi(int cpu, unsigned long data) { /* Order previous accesses vs. msgsnd, which is treated as a store */ mb(); - ppc_msgsnd(PPC_DBELL, 0, data); + ppc_msgsnd(PPC_DBELL_MSGTYPE, 0, data); } void doorbell_exception(struct pt_regs *regs) From 655bb3f4e89225354b668dcef53fa39da02fb1d0 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Wed, 14 Nov 2012 18:49:45 +0000 Subject: [PATCH 0751/4607] powerpc: Add book3s hypervisor doorbell exception vectors Directed Hypervisor Doorbell Interrupts come in at 0xe80 (or 0xc000000000004e80 if relocation on exceptions is enabled), so add exception vectors at these locations. If doorbell support is not compiled in we handle it as an unknown_exception. Signed-off-by: Ian Munsie Tested-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/exception-64s.h | 2 ++ arch/powerpc/kernel/exceptions-64s.S | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index ad708dda3ba3..9d5367e7e19b 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -305,6 +305,8 @@ label##_relon_hv: \ #define SOFTEN_VALUE_0x502 PACA_IRQ_EE #define SOFTEN_VALUE_0x900 PACA_IRQ_DEC #define SOFTEN_VALUE_0x982 PACA_IRQ_DEC +#define SOFTEN_VALUE_0xe80 PACA_IRQ_DBELL +#define SOFTEN_VALUE_0xe82 PACA_IRQ_DBELL #define __SOFTEN_TEST(h, vec) \ lbz r10,PACASOFTIRQEN(r13); \ diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 4665e82fa377..d08a3cdb7cbe 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -293,6 +293,8 @@ hv_exception_trampoline: b hmi_exception_hv . = 0xe60 b hmi_exception_hv + . = 0xe80 + b h_doorbell_hv /* We need to deal with the Altivec unavailable exception * here which is at 0xf20, thus in the middle of the @@ -514,6 +516,8 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_206) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe42) STD_EXCEPTION_HV(., 0xe62, hmi_exception) /* need to flush cache ? */ KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe62) + MASKABLE_EXCEPTION_HV(., 0xe82, h_doorbell) + KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe82) /* moved from 0xf00 */ STD_EXCEPTION_PSERIES(., 0xf00, performance_monitor) @@ -657,6 +661,11 @@ machine_check_common: STD_EXCEPTION_COMMON(0xe00, trap_0e, .unknown_exception) STD_EXCEPTION_COMMON(0xe40, emulation_assist, .program_check_exception) STD_EXCEPTION_COMMON(0xe60, hmi_exception, .unknown_exception) +#ifdef CONFIG_PPC_DOORBELL + STD_EXCEPTION_COMMON_ASYNC(0xe80, h_doorbell, .doorbell_exception) +#else + STD_EXCEPTION_COMMON_ASYNC(0xe80, h_doorbell, .unknown_exception) +#endif STD_EXCEPTION_COMMON_ASYNC(0xf00, performance_monitor, .performance_monitor_exception) STD_EXCEPTION_COMMON(0x1300, instruction_breakpoint, .instruction_breakpoint_exception) STD_EXCEPTION_COMMON(0x1502, denorm, .unknown_exception) @@ -773,9 +782,8 @@ system_call_relon_pSeries: . = 0x4e60 b hmi_exception_relon_hv - /* For when we support the doorbell interrupt: - STD_RELON_EXCEPTION_HYPERVISOR(0x4e80, 0xe80, doorbell_hyper) - */ + . = 0x4e80 + b h_doorbell_relon_hv performance_monitor_relon_pSeries_1: . = 0x4f00 @@ -1355,6 +1363,8 @@ _GLOBAL(do_stab_bolted) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe40) STD_RELON_EXCEPTION_HV(., 0xe60, hmi_exception) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe60) + MASKABLE_RELON_EXCEPTION_HV(., 0xe80, h_doorbell) + KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe80) STD_RELON_EXCEPTION_PSERIES(., 0xf00, performance_monitor) STD_RELON_EXCEPTION_PSERIES(., 0xf20, altivec_unavailable) From 1dbdafec5d63a1de6c83c89a3e953575d60fd393 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Wed, 14 Nov 2012 18:49:46 +0000 Subject: [PATCH 0752/4607] powerpc: Add book3s privileged doorbell exception vectors Directed Privileged Doorbell Interrupts come in at 0xa00 (or 0xc000000000004a00 if relocation on exception is enabled), so add exception vectors at these locations. If doorbell support is not compiled in we handle it as an unknown_exception. Signed-off-by: Ian Munsie Tested-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/exception-64s.h | 1 + arch/powerpc/kernel/exceptions-64s.S | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 9d5367e7e19b..b1edd801d314 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -305,6 +305,7 @@ label##_relon_hv: \ #define SOFTEN_VALUE_0x502 PACA_IRQ_EE #define SOFTEN_VALUE_0x900 PACA_IRQ_DEC #define SOFTEN_VALUE_0x982 PACA_IRQ_DEC +#define SOFTEN_VALUE_0xa00 PACA_IRQ_DBELL #define SOFTEN_VALUE_0xe80 PACA_IRQ_DBELL #define SOFTEN_VALUE_0xe82 PACA_IRQ_DBELL diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index d08a3cdb7cbe..176bf99e01c6 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -252,7 +252,7 @@ hardware_interrupt_hv: MASKABLE_EXCEPTION_PSERIES(0x900, 0x900, decrementer) STD_EXCEPTION_HV(0x980, 0x982, hdecrementer) - STD_EXCEPTION_PSERIES(0xa00, 0xa00, trap_0a) + MASKABLE_EXCEPTION_PSERIES(0xa00, 0xa00, doorbell_super) KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xa00) STD_EXCEPTION_PSERIES(0xb00, 0xb00, trap_0b) @@ -655,7 +655,11 @@ machine_check_common: STD_EXCEPTION_COMMON_ASYNC(0x500, hardware_interrupt, do_IRQ) STD_EXCEPTION_COMMON_ASYNC(0x900, decrementer, .timer_interrupt) STD_EXCEPTION_COMMON(0x980, hdecrementer, .hdec_interrupt) - STD_EXCEPTION_COMMON(0xa00, trap_0a, .unknown_exception) +#ifdef CONFIG_PPC_DOORBELL + STD_EXCEPTION_COMMON_ASYNC(0xa00, doorbell_super, .doorbell_exception) +#else + STD_EXCEPTION_COMMON_ASYNC(0xa00, doorbell_super, .unknown_exception) +#endif STD_EXCEPTION_COMMON(0xb00, trap_0b, .unknown_exception) STD_EXCEPTION_COMMON(0xd00, single_step, .single_step_exception) STD_EXCEPTION_COMMON(0xe00, trap_0e, .unknown_exception) @@ -755,6 +759,7 @@ hardware_interrupt_relon_hv: STD_RELON_EXCEPTION_PSERIES(0x4800, 0x800, fp_unavailable) MASKABLE_RELON_EXCEPTION_PSERIES(0x4900, 0x900, decrementer) STD_RELON_EXCEPTION_HV(0x4980, 0x982, hdecrementer) + MASKABLE_RELON_EXCEPTION_PSERIES(0x4a00, 0xa00, doorbell_super) STD_RELON_EXCEPTION_PSERIES(0x4b00, 0xb00, trap_0b) . = 0x4c00 From 919ca8681f48ce81848c76f0ed66edb1bb99209b Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Wed, 14 Nov 2012 18:49:47 +0000 Subject: [PATCH 0753/4607] powerpc: Select either privileged or hypervisor doorbell when sending On book3s we have two msgsnd instructions with differing privilege levels. This patch selects the appropriate instruction to use whenever we send a doorbell interrupt. Signed-off-by: Ian Munsie Tested-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/dbell.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/dbell.h b/arch/powerpc/include/asm/dbell.h index 3b338565f992..5fa6b20eba10 100644 --- a/arch/powerpc/include/asm/dbell.h +++ b/arch/powerpc/include/asm/dbell.h @@ -37,12 +37,25 @@ enum ppc_dbell { #define SPRN_DOORBELL_CPUTAG SPRN_TIR #define PPC_DBELL_TAG_MASK 0x7f +static inline void _ppc_msgsnd(u32 msg) +{ + if (cpu_has_feature(CPU_FTR_HVMODE)) + __asm__ __volatile__ (PPC_MSGSND(%0) : : "r" (msg)); + else + __asm__ __volatile__ (PPC_MSGSNDP(%0) : : "r" (msg)); +} + #else /* CONFIG_PPC_BOOK3S */ #define PPC_DBELL_MSGTYPE PPC_DBELL #define SPRN_DOORBELL_CPUTAG SPRN_PIR #define PPC_DBELL_TAG_MASK 0x3fff +static inline void _ppc_msgsnd(u32 msg) +{ + __asm__ __volatile__ (PPC_MSGSND(%0) : : "r" (msg)); +} + #endif /* CONFIG_PPC_BOOK3S */ extern void doorbell_cause_ipi(int cpu, unsigned long data); @@ -54,7 +67,7 @@ static inline void ppc_msgsnd(enum ppc_dbell type, u32 flags, u32 tag) u32 msg = PPC_DBELL_TYPE(type) | (flags & PPC_DBELL_MSG_BRDCAST) | (tag & 0x07ffffff); - __asm__ __volatile__ (PPC_MSGSND(%0) : : "r" (msg)); + _ppc_msgsnd(msg); } #endif /* _ASM_POWERPC_DBELL_H */ From fe9e1d54e3ea2e5134d7aaa233441f9229326ef6 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Wed, 14 Nov 2012 18:49:48 +0000 Subject: [PATCH 0754/4607] powerpc: Add code to handle soft-disabled doorbells on server This patch adds the logic to properly handle doorbells that come in when interrupts have been soft disabled and to replay them when interrupts are re-enabled: - masked_##_H##interrupt is modified to leave interrupts enabled when a doorbell has come in since doorbells are edge sensitive and as such won't be automatically re-raised. - __check_irq_replay now tests if a doorbell happened on book3s, and returns either 0xe80 or 0xa00 depending on whether we are the hypervisor or not. - restore_check_irq_replay now tests for the two possible server doorbell vector numbers to replay. - __replay_interrupt also adds tests for the two server doorbell vector numbers, and is modified to use a compare instruction rather than an andi. on the single bit difference between 0x500 and 0x900. The last two use a CPU feature section to avoid needlessly testing against the hypervisor vector if it is not the hypervisor, and vice versa. Signed-off-by: Ian Munsie Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/entry_64.S | 13 ++++++++-- arch/powerpc/kernel/exceptions-64s.S | 37 +++++++++++++++++++--------- arch/powerpc/kernel/irq.c | 11 +++++++-- 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index b310a0573625..73c1f083ba21 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -836,13 +836,22 @@ restore_check_irq_replay: addi r3,r1,STACK_FRAME_OVERHEAD; bl .timer_interrupt b .ret_from_except +#ifdef CONFIG_PPC_DOORBELL +1: #ifdef CONFIG_PPC_BOOK3E -1: cmpwi cr0,r3,0x280 + cmpwi cr0,r3,0x280 +#else + BEGIN_FTR_SECTION + cmpwi cr0,r3,0xe80 + FTR_SECTION_ELSE + cmpwi cr0,r3,0xa00 + ALT_FTR_SECTION_END_IFSET(CPU_FTR_HVMODE) +#endif /* CONFIG_PPC_BOOK3E */ bne 1f addi r3,r1,STACK_FRAME_OVERHEAD; bl .doorbell_exception b .ret_from_except -#endif /* CONFIG_PPC_BOOK3E */ +#endif /* CONFIG_PPC_DOORBELL */ 1: b .ret_from_except /* What else to do here ? */ unrecov_restore: diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 176bf99e01c6..32fc04f78890 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -528,10 +528,12 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_206) KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf40) /* - * An interrupt came in while soft-disabled. We set paca->irq_happened, - * then, if it was a decrementer interrupt, we bump the dec to max and - * and return, else we hard disable and return. This is called with - * r10 containing the value to OR to the paca field. + * An interrupt came in while soft-disabled. We set paca->irq_happened, then: + * - If it was a decrementer interrupt, we bump the dec to max and and return. + * - If it was a doorbell we return immediately since doorbells are edge + * triggered and won't automatically refire. + * - else we hard disable and return. + * This is called with r10 containing the value to OR to the paca field. */ #define MASKED_INTERRUPT(_H) \ masked_##_H##interrupt: \ @@ -539,13 +541,15 @@ masked_##_H##interrupt: \ lbz r11,PACAIRQHAPPENED(r13); \ or r11,r11,r10; \ stb r11,PACAIRQHAPPENED(r13); \ - andi. r10,r10,PACA_IRQ_DEC; \ - beq 1f; \ + cmpwi r10,PACA_IRQ_DEC; \ + bne 1f; \ lis r10,0x7fff; \ ori r10,r10,0xffff; \ mtspr SPRN_DEC,r10; \ b 2f; \ -1: mfspr r10,SPRN_##_H##SRR1; \ +1: cmpwi r10,PACA_IRQ_DBELL; \ + beq 2f; \ + mfspr r10,SPRN_##_H##SRR1; \ rldicl r10,r10,48,1; /* clear MSR_EE */ \ rotldi r10,r10,16; \ mtspr SPRN_##_H##SRR1,r10; \ @@ -562,8 +566,8 @@ masked_##_H##interrupt: \ /* * Called from arch_local_irq_enable when an interrupt needs - * to be resent. r3 contains 0x500 or 0x900 to indicate which - * kind of interrupt. MSR:EE is already off. We generate a + * to be resent. r3 contains 0x500, 0x900, 0xa00 or 0xe80 to indicate + * which kind of interrupt. MSR:EE is already off. We generate a * stackframe like if a real interrupt had happened. * * Note: While MSR:EE is off, we need to make sure that _MSR @@ -579,9 +583,18 @@ _GLOBAL(__replay_interrupt) mflr r11 mfcr r9 ori r12,r12,MSR_EE - andi. r3,r3,0x0800 - bne decrementer_common - b hardware_interrupt_common + cmpwi r3,0x900 + beq decrementer_common + cmpwi r3,0x500 + beq hardware_interrupt_common +BEGIN_FTR_SECTION + cmpwi r3,0xe80 + beq h_doorbell_common +FTR_SECTION_ELSE + cmpwi r3,0xa00 + beq doorbell_super_common +ALT_FTR_SECTION_END_IFSET(CPU_FTR_HVMODE) + blr #ifdef CONFIG_PPC_PSERIES /* diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index 71413f41278f..4f97fe345526 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -122,8 +122,8 @@ static inline notrace int decrementer_check_overflow(void) } /* This is called whenever we are re-enabling interrupts - * and returns either 0 (nothing to do) or 500/900 if there's - * either an EE or a DEC to generate. + * and returns either 0 (nothing to do) or 500/900/280/a00/e80 if + * there's an EE, DEC or DBELL to generate. * * This is called in two contexts: From arch_local_irq_restore() * before soft-enabling interrupts, and from the exception exit @@ -182,6 +182,13 @@ notrace unsigned int __check_irq_replay(void) local_paca->irq_happened &= ~PACA_IRQ_DBELL; if (happened & PACA_IRQ_DBELL) return 0x280; +#else + local_paca->irq_happened &= ~PACA_IRQ_DBELL; + if (happened & PACA_IRQ_DBELL) { + if (cpu_has_feature(CPU_FTR_HVMODE)) + return 0xe80; + return 0xa00; + } #endif /* CONFIG_PPC_BOOK3E */ /* There should be nothing left ! */ From 440bc685532fd17c16ec2a3aae2c7877d30b7942 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Wed, 14 Nov 2012 18:49:49 +0000 Subject: [PATCH 0755/4607] powerpc: Update Kconfig + Makefile to prepare for server doorbells Move the rule to build doorbell support out of the Makefile and into a new Kconfig boolean that platforms can select. We will add doorbell support to pseries as well in the next patch. Signed-off-by: Ian Munsie Tested-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/Makefile | 4 ++-- arch/powerpc/platforms/Kconfig.cputype | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 8f619342f14c..44fbbea7697a 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -75,8 +75,8 @@ endif obj64-$(CONFIG_HIBERNATION) += swsusp_asm64.o obj-$(CONFIG_MODULES) += module.o module_$(CONFIG_WORD_SIZE).o obj-$(CONFIG_44x) += cpu_setup_44x.o -obj-$(CONFIG_PPC_FSL_BOOK3E) += cpu_setup_fsl_booke.o dbell.o -obj-$(CONFIG_PPC_BOOK3E_64) += dbell.o +obj-$(CONFIG_PPC_FSL_BOOK3E) += cpu_setup_fsl_booke.o +obj-$(CONFIG_PPC_DOORBELL) += dbell.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o extra-y := head_$(CONFIG_WORD_SIZE).o diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 72afd2888cad..cea2f09c4241 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -76,6 +76,7 @@ config PPC_BOOK3E_64 bool "Embedded processors" select PPC_FPU # Make it a choice ? select PPC_SMP_MUXED_IPI + select PPC_DOORBELL endchoice @@ -208,6 +209,7 @@ config PPC_FSL_BOOK3E select FSL_EMB_PERFMON select PPC_SMP_MUXED_IPI select SYS_SUPPORTS_HUGETLBFS if PHYS_64BIT || PPC64 + select PPC_DOORBELL default y if FSL_BOOKE config PTE_64BIT @@ -382,4 +384,8 @@ config NOT_COHERENT_CACHE config CHECK_CACHE_COHERENCY bool +config PPC_DOORBELL + bool + default n + endmenu From e5e84f0a66e0dedba92f881c2dec787dbd8619d5 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Wed, 14 Nov 2012 18:49:50 +0000 Subject: [PATCH 0756/4607] powerpc: Hook up doorbells on server This patch actually hooks up doorbell interrupts on POWER8: - Select the PPC_DOORBELL Kconfig option from PPC_PSERIES - Add the doorbell CPU feature bit to POWER8 - We define a new pSeries_cause_ipi_mux() function that issues a doorbell interrupt if the recipient is another thread within the same core as the sender. If the recipient is in a different core it falls back to using XICS to deliver the IPI as before. - During pSeries_smp_probe() at boot, we check if doorbell interrupts are supported. If they are we set the cause_ipi function pointer to the above mentioned function, otherwise we leave it as whichever XICS cause_ipi function was determined by xics_smp_probe(). Signed-off-by: Ian Munsie Tested-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/cputable.h | 3 ++- arch/powerpc/platforms/pseries/Kconfig | 1 + arch/powerpc/platforms/pseries/smp.c | 33 ++++++++++++++++++++++++-- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 76f81bd64f1d..fc4d2c5b6522 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -408,7 +408,8 @@ extern const char *powerpc_base_platform; CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \ CPU_FTR_DSCR | CPU_FTR_SAO | \ CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ - CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY) + CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ + CPU_FTR_DBELL) #define CPU_FTRS_CELL (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 837cf49357ed..9a0941bc4d31 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -17,6 +17,7 @@ config PPC_PSERIES select PPC_NATIVE select PPC_PCI_CHOICE if EXPERT select ZLIB_DEFLATE + select PPC_DOORBELL default y config PPC_SPLPAR diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index 80cd0be71e06..12bc8c3663ad 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "plpar_wrappers.h" #include "pseries.h" @@ -54,6 +55,11 @@ */ static cpumask_var_t of_spin_mask; +/* + * If we multiplex IPI mechanisms, store the appropriate XICS IPI mechanism here + */ +static void (*xics_cause_ipi)(int cpu, unsigned long data); + /* Query where a cpu is now. Return codes #defined in plpar_wrappers.h */ int smp_query_cpu_stopped(unsigned int pcpu) { @@ -137,6 +143,8 @@ static void smp_xics_setup_cpu(int cpu) { if (cpu != boot_cpuid) xics_setup_cpu(); + if (cpu_has_feature(CPU_FTR_DBELL)) + doorbell_setup_this_cpu(); if (firmware_has_feature(FW_FEATURE_SPLPAR)) vpa_init(cpu); @@ -195,6 +203,27 @@ static int smp_pSeries_cpu_bootable(unsigned int nr) return 1; } +/* Only used on systems that support multiple IPI mechanisms */ +static void pSeries_cause_ipi_mux(int cpu, unsigned long data) +{ + if (cpumask_test_cpu(cpu, cpu_sibling_mask(smp_processor_id()))) + doorbell_cause_ipi(cpu, data); + else + xics_cause_ipi(cpu, data); +} + +static __init int pSeries_smp_probe(void) +{ + int ret = xics_smp_probe(); + + if (cpu_has_feature(CPU_FTR_DBELL)) { + xics_cause_ipi = smp_ops->cause_ipi; + smp_ops->cause_ipi = pSeries_cause_ipi_mux; + } + + return ret; +} + static struct smp_ops_t pSeries_mpic_smp_ops = { .message_pass = smp_mpic_message_pass, .probe = smp_mpic_probe, @@ -204,8 +233,8 @@ static struct smp_ops_t pSeries_mpic_smp_ops = { static struct smp_ops_t pSeries_xics_smp_ops = { .message_pass = NULL, /* Use smp_muxed_ipi_message_pass */ - .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ - .probe = xics_smp_probe, + .cause_ipi = NULL, /* Filled at runtime by pSeries_smp_probe() */ + .probe = pSeries_smp_probe, .kick_cpu = smp_pSeries_kick_cpu, .setup_cpu = smp_xics_setup_cpu, .cpu_bootable = smp_pSeries_cpu_bootable, From 6f79cb8134c5cd9f3346087906829013dce8d460 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Mon, 19 Nov 2012 01:11:46 +0000 Subject: [PATCH 0757/4607] powerpc/perf: Change PMU flag representation from decimal to hex Change the representation of the PMU flags from decimal to hex since they are bitfields which are easier to read in hex. Signed-off-by: Anshuman Khandual Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/perf_event_server.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h index 9710be3a2d17..d3e97487c72c 100644 --- a/arch/powerpc/include/asm/perf_event_server.h +++ b/arch/powerpc/include/asm/perf_event_server.h @@ -45,11 +45,11 @@ struct power_pmu { /* * Values for power_pmu.flags */ -#define PPMU_LIMITED_PMC5_6 1 /* PMC5/6 have limited function */ -#define PPMU_ALT_SIPR 2 /* uses alternate posn for SIPR/HV */ -#define PPMU_NO_SIPR 4 /* no SIPR/HV in MMCRA at all */ -#define PPMU_NO_CONT_SAMPLING 8 /* no continuous sampling */ -#define PPMU_SIAR_VALID 16 /* Processor has SIAR Valid bit */ +#define PPMU_LIMITED_PMC5_6 0x00000001 /* PMC5/6 have limited function */ +#define PPMU_ALT_SIPR 0x00000002 /* uses alternate posn for SIPR/HV */ +#define PPMU_NO_SIPR 0x00000004 /* no SIPR/HV in MMCRA at all */ +#define PPMU_NO_CONT_SAMPLING 0x00000008 /* no continuous sampling */ +#define PPMU_SIAR_VALID 0x00000010 /* Processor has SIAR Valid bit */ /* * Values for flags to get_alternatives() From 724c6abf29bcef1aae688f023b7d6dcb7eab0e68 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Mon, 19 Nov 2012 18:31:52 +0000 Subject: [PATCH 0758/4607] powerpc/pseries/pci: Use NULL instead of 0 for pointers The third argument for of_get_property() is a pointer, hence pass NULL instead of 0. Signed-off-by: Tushar Behera Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/pci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/pci.c b/arch/powerpc/platforms/pseries/pci.c index 56b864d777ee..0b580f413a9a 100644 --- a/arch/powerpc/platforms/pseries/pci.c +++ b/arch/powerpc/platforms/pseries/pci.c @@ -40,7 +40,8 @@ void pcibios_name_device(struct pci_dev *dev) */ dn = pci_device_to_OF_node(dev); if (dn) { - const char *loc_code = of_get_property(dn, "ibm,loc-code", 0); + const char *loc_code = of_get_property(dn, "ibm,loc-code", + NULL); if (loc_code) { int loc_len = strlen(loc_code); if (loc_len < sizeof(dev->dev.name)) { From e95e09ce6790b456325bc8b5e95ccad647bff033 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Sun, 25 Nov 2012 20:51:28 +0000 Subject: [PATCH 0759/4607] powerpc/pasemi: Enable PRINTK_TIME in defconfig Enable PRINTK_TIME in pasemi_defconfig. Also regenerate it, it seems that a lot of options have moved around since last time savedefconfig was ran on it. Signed-off-by: Olof Johansson Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/configs/pasemi_defconfig | 31 +++++++-------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/arch/powerpc/configs/pasemi_defconfig b/arch/powerpc/configs/pasemi_defconfig index 840a2c2d0430..bd8a6f71944f 100644 --- a/arch/powerpc/configs/pasemi_defconfig +++ b/arch/powerpc/configs/pasemi_defconfig @@ -1,28 +1,27 @@ CONFIG_PPC64=y CONFIG_ALTIVEC=y -# CONFIG_VIRT_CPU_ACCOUNTING is not set CONFIG_SMP=y CONFIG_NR_CPUS=2 CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_PROFILING=y CONFIG_OPROFILE=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +CONFIG_MAC_PARTITION=y # CONFIG_PPC_PSERIES is not set # CONFIG_PPC_PMAC is not set CONFIG_PPC_PASEMI=y CONFIG_PPC_PASEMI_IOMMU=y CONFIG_CPU_FREQ=y -CONFIG_CPU_FREQ_DEBUG=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y CONFIG_HZ_1000=y CONFIG_PPC_64K_PAGES=y # CONFIG_SECCOMP is not set @@ -47,7 +46,6 @@ CONFIG_INET_ESP=y # CONFIG_IPV6 is not set CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_MTD=y -CONFIG_MTD_CONCAT=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_MTD_SLRAM=y @@ -58,7 +56,6 @@ CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=16384 -CONFIG_MISC_DEVICES=y CONFIG_EEPROM_LEGACY=y CONFIG_IDE=y CONFIG_BLK_DEV_IDECD=y @@ -91,21 +88,19 @@ CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=y CONFIG_NETDEVICES=y CONFIG_DUMMY=y -CONFIG_MARVELL_PHY=y -CONFIG_NET_ETHERNET=y CONFIG_MII=y -CONFIG_NET_PCI=y -CONFIG_E1000=y CONFIG_TIGON3=y +CONFIG_E1000=y CONFIG_PASEMI_MAC=y +CONFIG_MARVELL_PHY=y CONFIG_INPUT_JOYDEV=y CONFIG_INPUT_EVDEV=y # CONFIG_KEYBOARD_ATKBD is not set # CONFIG_MOUSE_PS2 is not set # CONFIG_SERIO is not set +CONFIG_LEGACY_PTY_COUNT=4 CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_LEGACY_PTY_COUNT=4 CONFIG_HW_RANDOM=y CONFIG_RAW_DRIVER=y CONFIG_I2C_CHARDEV=y @@ -146,14 +141,11 @@ CONFIG_HID_TOPSEED=y CONFIG_HID_THRUSTMASTER=y CONFIG_HID_ZEROPLUS=y CONFIG_USB=y -CONFIG_USB_DEVICEFS=y -# CONFIG_USB_DEVICE_CLASS is not set CONFIG_USB_EHCI_HCD=y CONFIG_USB_OHCI_HCD=y CONFIG_USB_UHCI_HCD=y CONFIG_USB_SL811_HCD=y CONFIG_USB_STORAGE=y -CONFIG_USB_LIBUSUAL=y CONFIG_EDAC=y CONFIG_EDAC_MM_EDAC=y CONFIG_EDAC_PASEMI=y @@ -164,8 +156,6 @@ CONFIG_EXT2_FS_XATTR=y CONFIG_EXT2_FS_POSIX_ACL=y CONFIG_EXT3_FS=y # CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set -CONFIG_INOTIFY=y -CONFIG_AUTOFS_FS=y CONFIG_AUTOFS4_FS=y CONFIG_ISO9660_FS=y CONFIG_UDF_FS=y @@ -177,27 +167,22 @@ CONFIG_HUGETLBFS=y CONFIG_CONFIGFS_FS=y CONFIG_JFFS2_FS=y CONFIG_NFS_FS=y -CONFIG_NFS_V3=y CONFIG_ROOT_NFS=y CONFIG_NFSD=y CONFIG_NFSD_V4=y -CONFIG_PARTITION_ADVANCED=y -CONFIG_MAC_PARTITION=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y CONFIG_CRC_CCITT=y +CONFIG_PRINTK_TIME=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_FS=y CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y # CONFIG_SCHED_DEBUG is not set -# CONFIG_RCU_CPU_STALL_DETECTOR is not set -CONFIG_SYSCTL_SYSCALL_CHECK=y CONFIG_XMON=y CONFIG_XMON_DEFAULT=y CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_SHA256=y CONFIG_CRYPTO_SHA512=y -CONFIG_CRYPTO_AES=y CONFIG_CRYPTO_BLOWFISH=y # CONFIG_CRYPTO_ANSI_CPRNG is not set From 5ac47f7a6efbd4fa9141c249e8af3f74e7944eb7 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 26 Nov 2012 17:39:03 +0000 Subject: [PATCH 0760/4607] powerpc: Relocate prom_init.c on 64bit The ppc64 kernel can get loaded at any address which means our very early init code in prom_init.c must be relocatable. We do this with a pretty nasty RELOC() macro that we wrap accesses of variables with. It is very fragile and sometimes we forget to add a RELOC() to an uncommon path or sometimes a compiler change breaks it. 32bit has a much more elegant solution where we build prom_init.c with -mrelocatable and then process the relocations manually. Unfortunately we can't do the equivalent on 64bit and we would have to build the entire kernel relocatable (-pie), resulting in a large increase in kernel footprint (megabytes of relocation data). The relocation data will be marked __initdata but it still creates more pressure on our already tight memory layout at boot. Alan Modra pointed out that the 64bit ABI is relocatable even if we don't build with -pie, we just need to relocate the TOC. This patch implements that idea and relocates the TOC entries of prom_init.c. An added bonus is there are very few relocations to process which helps keep boot times on simulators down. gcc does not put 64bit integer constants into the TOC but to be safe we may want a build time script which passes through the prom_init.c TOC entries to make sure everything looks reasonable. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Makefile | 1 + arch/powerpc/include/asm/sections.h | 3 ++ arch/powerpc/kernel/Makefile | 2 +- arch/powerpc/kernel/prom_init.c | 70 +++++++++++++++++++++----- arch/powerpc/kernel/prom_init_check.sh | 2 +- arch/powerpc/kernel/vmlinux.lds.S | 5 ++ 6 files changed, 68 insertions(+), 15 deletions(-) diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index ba45cad088c9..78c2b024371a 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -136,6 +136,7 @@ head-$(CONFIG_FSL_BOOKE) := arch/powerpc/kernel/head_fsl_booke.o head-$(CONFIG_PPC64) += arch/powerpc/kernel/entry_64.o head-$(CONFIG_PPC_FPU) += arch/powerpc/kernel/fpu.o head-$(CONFIG_ALTIVEC) += arch/powerpc/kernel/vector.o +head-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += arch/powerpc/kernel/prom_init.o core-y += arch/powerpc/kernel/ \ arch/powerpc/mm/ \ diff --git a/arch/powerpc/include/asm/sections.h b/arch/powerpc/include/asm/sections.h index a0f358d4a00c..4ee06fe15de4 100644 --- a/arch/powerpc/include/asm/sections.h +++ b/arch/powerpc/include/asm/sections.h @@ -10,6 +10,9 @@ extern char __end_interrupts[]; +extern char __prom_init_toc_start[]; +extern char __prom_init_toc_end[]; + static inline int in_kernel_text(unsigned long addr) { if (addr >= (unsigned long)_stext && addr < (unsigned long)__init_end) diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 44fbbea7697a..2f6ef4ed5abf 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -91,7 +91,6 @@ obj-$(CONFIG_RELOCATABLE_PPC32) += reloc_32.o obj-$(CONFIG_PPC32) += entry_32.o setup_32.o obj-$(CONFIG_PPC64) += dma-iommu.o iommu.o obj-$(CONFIG_KGDB) += kgdb.o -obj-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init.o obj-$(CONFIG_MODULES) += ppc_ksyms.o obj-$(CONFIG_BOOTX_TEXT) += btext.o obj-$(CONFIG_SMP) += smp.o @@ -142,6 +141,7 @@ GCOV_PROFILE_kprobes.o := n extra-$(CONFIG_PPC_FPU) += fpu.o extra-$(CONFIG_ALTIVEC) += vector.o extra-$(CONFIG_PPC64) += entry_64.o +extra-$(CONFIG_PPC_OF_BOOT_TRAMPOLINE) += prom_init.o extra-y += systbl_chk.i $(obj)/systbl.o: systbl_chk diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 779f34049a56..c78ac5698b99 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -66,8 +66,8 @@ * is running at whatever address it has been loaded at. * On ppc32 we compile with -mrelocatable, which means that references * to extern and static variables get relocated automatically. - * On ppc64 we have to relocate the references explicitly with - * RELOC. (Note that strings count as static variables.) + * ppc64 objects are always relocatable, we just need to relocate the + * TOC. * * Because OF may have mapped I/O devices into the area starting at * KERNELBASE, particularly on CHRP machines, we can't safely call @@ -79,13 +79,12 @@ * On ppc64, 64 bit values are truncated to 32 bits (and * fortunately don't get interpreted as two arguments). */ +#define RELOC(x) (x) +#define ADDR(x) (u32)(unsigned long)(x) + #ifdef CONFIG_PPC64 -#define RELOC(x) (*PTRRELOC(&(x))) -#define ADDR(x) (u32) add_reloc_offset((unsigned long)(x)) #define OF_WORKAROUNDS 0 #else -#define RELOC(x) (x) -#define ADDR(x) (u32) (x) #define OF_WORKAROUNDS of_workarounds int of_workarounds; #endif @@ -334,9 +333,6 @@ static void __init prom_printf(const char *format, ...) struct prom_t *_prom = &RELOC(prom); va_start(args, format); -#ifdef CONFIG_PPC64 - format = PTRRELOC(format); -#endif for (p = format; *p != 0; p = q) { for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q) ; @@ -437,9 +433,6 @@ static unsigned int __init prom_claim(unsigned long virt, unsigned long size, static void __init __attribute__((noreturn)) prom_panic(const char *reason) { -#ifdef CONFIG_PPC64 - reason = PTRRELOC(reason); -#endif prom_print(reason); /* Do not call exit because it clears the screen on pmac * it also causes some sort of double-fault on early pmacs */ @@ -929,7 +922,7 @@ static void __init prom_send_capabilities(void) * (we assume this is the same for all cores) and use it to * divide NR_CPUS. */ - cores = (u32 *)PTRRELOC(&ibm_architecture_vec[IBM_ARCH_VEC_NRCORES_OFFSET]); + cores = (u32 *)&ibm_architecture_vec[IBM_ARCH_VEC_NRCORES_OFFSET]; if (*cores != NR_CPUS) { prom_printf("WARNING ! " "ibm_architecture_vec structure inconsistent: %lu!\n", @@ -2850,6 +2843,53 @@ static void __init prom_check_initrd(unsigned long r3, unsigned long r4) #endif /* CONFIG_BLK_DEV_INITRD */ } +#ifdef CONFIG_PPC64 +#ifdef CONFIG_RELOCATABLE +static void reloc_toc(void) +{ +} + +static void unreloc_toc(void) +{ +} +#else +static void __reloc_toc(void *tocstart, unsigned long offset, + unsigned long nr_entries) +{ + unsigned long i; + unsigned long *toc_entry = (unsigned long *)tocstart; + + for (i = 0; i < nr_entries; i++) { + *toc_entry = *toc_entry + offset; + toc_entry++; + } +} + +static void reloc_toc(void) +{ + unsigned long offset = reloc_offset(); + unsigned long nr_entries = + (__prom_init_toc_end - __prom_init_toc_start) / sizeof(long); + + /* Need to add offset to get at __prom_init_toc_start */ + __reloc_toc(__prom_init_toc_start + offset, offset, nr_entries); + + mb(); +} + +static void unreloc_toc(void) +{ + unsigned long offset = reloc_offset(); + unsigned long nr_entries = + (__prom_init_toc_end - __prom_init_toc_start) / sizeof(long); + + mb(); + + /* __prom_init_toc_start has been relocated, no need to add offset */ + __reloc_toc(__prom_init_toc_start, -offset, nr_entries); +} +#endif +#endif /* * We enter here early on, when the Open Firmware prom is still @@ -2867,6 +2907,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, #ifdef CONFIG_PPC32 unsigned long offset = reloc_offset(); reloc_got2(offset); +#else + reloc_toc(); #endif _prom = &RELOC(prom); @@ -3061,6 +3103,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, #ifdef CONFIG_PPC32 reloc_got2(-offset); +#else + unreloc_toc(); #endif #ifdef CONFIG_PPC_EARLY_DEBUG_OPAL diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh index 70f4286eaa7a..3765da6be4f2 100644 --- a/arch/powerpc/kernel/prom_init_check.sh +++ b/arch/powerpc/kernel/prom_init_check.sh @@ -22,7 +22,7 @@ __secondary_hold_acknowledge __secondary_hold_spinloop __start strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224 reloc_got2 kernstart_addr memstart_addr linux_banner _stext opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry -boot_command_line" +boot_command_line __prom_init_toc_start __prom_init_toc_end" NM="$1" OBJ="$2" diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S index 65d1c08cf09e..654e479802f2 100644 --- a/arch/powerpc/kernel/vmlinux.lds.S +++ b/arch/powerpc/kernel/vmlinux.lds.S @@ -218,6 +218,11 @@ SECTIONS .got : AT(ADDR(.got) - LOAD_OFFSET) { __toc_start = .; +#ifndef CONFIG_RELOCATABLE + __prom_init_toc_start = .; + arch/powerpc/kernel/prom_init.o*(.toc .got) + __prom_init_toc_end = .; +#endif *(.got) *(.toc) } From 5827d4165ac608d7c26fa68701391e80824ee5c9 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 26 Nov 2012 17:40:03 +0000 Subject: [PATCH 0761/4607] powerpc: Remove RELOC() macro Now we relocate prom_init.c on 64bit we can finally remove the nasty RELOC() macro. Finally a patch that I can claim has a net positive effect on the kernel. It doesn't happen very often. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/prom_init.c | 558 +++++++++++++++----------------- 1 file changed, 267 insertions(+), 291 deletions(-) diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index c78ac5698b99..7f7fb7fd991b 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -79,7 +79,6 @@ * On ppc64, 64 bit values are truncated to 32 bits (and * fortunately don't get interpreted as two arguments). */ -#define RELOC(x) (x) #define ADDR(x) (u32)(unsigned long)(x) #ifdef CONFIG_PPC64 @@ -94,7 +93,7 @@ int of_workarounds; #define PROM_BUG() do { \ prom_printf("kernel BUG at %s line 0x%x!\n", \ - RELOC(__FILE__), __LINE__); \ + __FILE__, __LINE__); \ __asm__ __volatile__(".long " BUG_ILLEGAL_INSTR); \ } while (0) @@ -232,7 +231,7 @@ static int __init call_prom(const char *service, int nargs, int nret, ...) for (i = 0; i < nret; i++) args.args[nargs+i] = 0; - if (enter_prom(&args, RELOC(prom_entry)) < 0) + if (enter_prom(&args, prom_entry) < 0) return PROM_ERROR; return (nret > 0) ? args.args[nargs] : 0; @@ -257,7 +256,7 @@ static int __init call_prom_ret(const char *service, int nargs, int nret, for (i = 0; i < nret; i++) args.args[nargs+i] = 0; - if (enter_prom(&args, RELOC(prom_entry)) < 0) + if (enter_prom(&args, prom_entry) < 0) return PROM_ERROR; if (rets != NULL) @@ -271,20 +270,19 @@ static int __init call_prom_ret(const char *service, int nargs, int nret, static void __init prom_print(const char *msg) { const char *p, *q; - struct prom_t *_prom = &RELOC(prom); - if (_prom->stdout == 0) + if (prom.stdout == 0) return; for (p = msg; *p != 0; p = q) { for (q = p; *q != 0 && *q != '\n'; ++q) ; if (q > p) - call_prom("write", 3, 1, _prom->stdout, p, q - p); + call_prom("write", 3, 1, prom.stdout, p, q - p); if (*q == 0) break; ++q; - call_prom("write", 3, 1, _prom->stdout, ADDR("\r\n"), 2); + call_prom("write", 3, 1, prom.stdout, ADDR("\r\n"), 2); } } @@ -293,7 +291,6 @@ static void __init prom_print_hex(unsigned long val) { int i, nibbles = sizeof(val)*2; char buf[sizeof(val)*2+1]; - struct prom_t *_prom = &RELOC(prom); for (i = nibbles-1; i >= 0; i--) { buf[i] = (val & 0xf) + '0'; @@ -302,7 +299,7 @@ static void __init prom_print_hex(unsigned long val) val >>= 4; } buf[nibbles] = '\0'; - call_prom("write", 3, 1, _prom->stdout, buf, nibbles); + call_prom("write", 3, 1, prom.stdout, buf, nibbles); } /* max number of decimal digits in an unsigned long */ @@ -311,7 +308,6 @@ static void __init prom_print_dec(unsigned long val) { int i, size; char buf[UL_DIGITS+1]; - struct prom_t *_prom = &RELOC(prom); for (i = UL_DIGITS-1; i >= 0; i--) { buf[i] = (val % 10) + '0'; @@ -321,7 +317,7 @@ static void __init prom_print_dec(unsigned long val) } /* shift stuff down */ size = UL_DIGITS - i; - call_prom("write", 3, 1, _prom->stdout, buf+i, size); + call_prom("write", 3, 1, prom.stdout, buf+i, size); } static void __init prom_printf(const char *format, ...) @@ -330,19 +326,18 @@ static void __init prom_printf(const char *format, ...) va_list args; unsigned long v; long vs; - struct prom_t *_prom = &RELOC(prom); va_start(args, format); for (p = format; *p != 0; p = q) { for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q) ; if (q > p) - call_prom("write", 3, 1, _prom->stdout, p, q - p); + call_prom("write", 3, 1, prom.stdout, p, q - p); if (*q == 0) break; if (*q == '\n') { ++q; - call_prom("write", 3, 1, _prom->stdout, + call_prom("write", 3, 1, prom.stdout, ADDR("\r\n"), 2); continue; } @@ -364,7 +359,7 @@ static void __init prom_printf(const char *format, ...) ++q; vs = va_arg(args, int); if (vs < 0) { - prom_print(RELOC("-")); + prom_print("-"); vs = -vs; } prom_print_dec(vs); @@ -385,7 +380,7 @@ static void __init prom_printf(const char *format, ...) ++q; vs = va_arg(args, long); if (vs < 0) { - prom_print(RELOC("-")); + prom_print("-"); vs = -vs; } prom_print_dec(vs); @@ -399,7 +394,6 @@ static void __init prom_printf(const char *format, ...) static unsigned int __init prom_claim(unsigned long virt, unsigned long size, unsigned long align) { - struct prom_t *_prom = &RELOC(prom); if (align == 0 && (OF_WORKAROUNDS & OF_WA_CLAIM)) { /* @@ -410,21 +404,21 @@ static unsigned int __init prom_claim(unsigned long virt, unsigned long size, prom_arg_t result; ret = call_prom_ret("call-method", 5, 2, &result, - ADDR("claim"), _prom->memory, + ADDR("claim"), prom.memory, align, size, virt); if (ret != 0 || result == -1) return -1; ret = call_prom_ret("call-method", 5, 2, &result, - ADDR("claim"), _prom->mmumap, + ADDR("claim"), prom.mmumap, align, size, virt); if (ret != 0) { call_prom("call-method", 4, 1, ADDR("release"), - _prom->memory, size, virt); + prom.memory, size, virt); return -1; } /* the 0x12 is M (coherence) + PP == read/write */ call_prom("call-method", 6, 1, - ADDR("map"), _prom->mmumap, 0x12, size, virt, virt); + ADDR("map"), prom.mmumap, 0x12, size, virt, virt); return virt; } return call_prom("claim", 3, 1, (prom_arg_t)virt, (prom_arg_t)size, @@ -436,7 +430,7 @@ static void __init __attribute__((noreturn)) prom_panic(const char *reason) prom_print(reason); /* Do not call exit because it clears the screen on pmac * it also causes some sort of double-fault on early pmacs */ - if (RELOC(of_platform) == PLATFORM_POWERMAC) + if (of_platform == PLATFORM_POWERMAC) asm("trap\n"); /* ToDo: should put up an SRC here on pSeries */ @@ -518,13 +512,13 @@ static int __init prom_setprop(phandle node, const char *nodename, add_string(&p, tohex((u32)(unsigned long) value)); add_string(&p, tohex(valuelen)); add_string(&p, tohex(ADDR(pname))); - add_string(&p, tohex(strlen(RELOC(pname)))); + add_string(&p, tohex(strlen(pname))); add_string(&p, "property"); *p = 0; return call_prom("interpret", 1, 1, (u32)(unsigned long) cmd); } -/* We can't use the standard versions because of RELOC headaches. */ +/* We can't use the standard versions because of relocation headaches. */ #define isxdigit(c) (('0' <= (c) && (c) <= '9') \ || ('a' <= (c) && (c) <= 'f') \ || ('A' <= (c) && (c) <= 'F')) @@ -591,43 +585,42 @@ unsigned long prom_memparse(const char *ptr, const char **retptr) */ static void __init early_cmdline_parse(void) { - struct prom_t *_prom = &RELOC(prom); const char *opt; char *p; int l = 0; - RELOC(prom_cmd_line[0]) = 0; - p = RELOC(prom_cmd_line); - if ((long)_prom->chosen > 0) - l = prom_getprop(_prom->chosen, "bootargs", p, COMMAND_LINE_SIZE-1); + prom_cmd_line[0] = 0; + p = prom_cmd_line; + if ((long)prom.chosen > 0) + l = prom_getprop(prom.chosen, "bootargs", p, COMMAND_LINE_SIZE-1); #ifdef CONFIG_CMDLINE if (l <= 0 || p[0] == '\0') /* dbl check */ - strlcpy(RELOC(prom_cmd_line), - RELOC(CONFIG_CMDLINE), sizeof(prom_cmd_line)); + strlcpy(prom_cmd_line, + CONFIG_CMDLINE, sizeof(prom_cmd_line)); #endif /* CONFIG_CMDLINE */ - prom_printf("command line: %s\n", RELOC(prom_cmd_line)); + prom_printf("command line: %s\n", prom_cmd_line); #ifdef CONFIG_PPC64 - opt = strstr(RELOC(prom_cmd_line), RELOC("iommu=")); + opt = strstr(prom_cmd_line, "iommu="); if (opt) { prom_printf("iommu opt is: %s\n", opt); opt += 6; while (*opt && *opt == ' ') opt++; - if (!strncmp(opt, RELOC("off"), 3)) - RELOC(prom_iommu_off) = 1; - else if (!strncmp(opt, RELOC("force"), 5)) - RELOC(prom_iommu_force_on) = 1; + if (!strncmp(opt, "off", 3)) + prom_iommu_off = 1; + else if (!strncmp(opt, "force", 5)) + prom_iommu_force_on = 1; } #endif - opt = strstr(RELOC(prom_cmd_line), RELOC("mem=")); + opt = strstr(prom_cmd_line, "mem="); if (opt) { opt += 4; - RELOC(prom_memory_limit) = prom_memparse(opt, (const char **)&opt); + prom_memory_limit = prom_memparse(opt, (const char **)&opt); #ifdef CONFIG_PPC64 /* Align to 16 MB == size of ppc64 large page */ - RELOC(prom_memory_limit) = ALIGN(RELOC(prom_memory_limit), 0x1000000); + prom_memory_limit = ALIGN(prom_memory_limit, 0x1000000); #endif } } @@ -880,7 +873,7 @@ static int __init prom_count_smt_threads(void) type[0] = 0; prom_getprop(node, "device_type", type, sizeof(type)); - if (strcmp(type, RELOC("cpu"))) + if (strcmp(type, "cpu")) continue; /* * There is an entry for each smt thread, each entry being @@ -998,21 +991,21 @@ static void __init prom_send_capabilities(void) */ static unsigned long __init alloc_up(unsigned long size, unsigned long align) { - unsigned long base = RELOC(alloc_bottom); + unsigned long base = alloc_bottom; unsigned long addr = 0; if (align) base = _ALIGN_UP(base, align); prom_debug("alloc_up(%x, %x)\n", size, align); - if (RELOC(ram_top) == 0) + if (ram_top == 0) prom_panic("alloc_up() called with mem not initialized\n"); if (align) - base = _ALIGN_UP(RELOC(alloc_bottom), align); + base = _ALIGN_UP(alloc_bottom, align); else - base = RELOC(alloc_bottom); + base = alloc_bottom; - for(; (base + size) <= RELOC(alloc_top); + for(; (base + size) <= alloc_top; base = _ALIGN_UP(base + 0x100000, align)) { prom_debug(" trying: 0x%x\n\r", base); addr = (unsigned long)prom_claim(base, size, 0); @@ -1024,14 +1017,14 @@ static unsigned long __init alloc_up(unsigned long size, unsigned long align) } if (addr == 0) return 0; - RELOC(alloc_bottom) = addr + size; + alloc_bottom = addr + size; prom_debug(" -> %x\n", addr); - prom_debug(" alloc_bottom : %x\n", RELOC(alloc_bottom)); - prom_debug(" alloc_top : %x\n", RELOC(alloc_top)); - prom_debug(" alloc_top_hi : %x\n", RELOC(alloc_top_high)); - prom_debug(" rmo_top : %x\n", RELOC(rmo_top)); - prom_debug(" ram_top : %x\n", RELOC(ram_top)); + prom_debug(" alloc_bottom : %x\n", alloc_bottom); + prom_debug(" alloc_top : %x\n", alloc_top); + prom_debug(" alloc_top_hi : %x\n", alloc_top_high); + prom_debug(" rmo_top : %x\n", rmo_top); + prom_debug(" ram_top : %x\n", ram_top); return addr; } @@ -1047,32 +1040,32 @@ static unsigned long __init alloc_down(unsigned long size, unsigned long align, unsigned long base, addr = 0; prom_debug("alloc_down(%x, %x, %s)\n", size, align, - highmem ? RELOC("(high)") : RELOC("(low)")); - if (RELOC(ram_top) == 0) + highmem ? "(high)" : "(low)"); + if (ram_top == 0) prom_panic("alloc_down() called with mem not initialized\n"); if (highmem) { /* Carve out storage for the TCE table. */ - addr = _ALIGN_DOWN(RELOC(alloc_top_high) - size, align); - if (addr <= RELOC(alloc_bottom)) + addr = _ALIGN_DOWN(alloc_top_high - size, align); + if (addr <= alloc_bottom) return 0; /* Will we bump into the RMO ? If yes, check out that we * didn't overlap existing allocations there, if we did, * we are dead, we must be the first in town ! */ - if (addr < RELOC(rmo_top)) { + if (addr < rmo_top) { /* Good, we are first */ - if (RELOC(alloc_top) == RELOC(rmo_top)) - RELOC(alloc_top) = RELOC(rmo_top) = addr; + if (alloc_top == rmo_top) + alloc_top = rmo_top = addr; else return 0; } - RELOC(alloc_top_high) = addr; + alloc_top_high = addr; goto bail; } - base = _ALIGN_DOWN(RELOC(alloc_top) - size, align); - for (; base > RELOC(alloc_bottom); + base = _ALIGN_DOWN(alloc_top - size, align); + for (; base > alloc_bottom; base = _ALIGN_DOWN(base - 0x100000, align)) { prom_debug(" trying: 0x%x\n\r", base); addr = (unsigned long)prom_claim(base, size, 0); @@ -1082,15 +1075,15 @@ static unsigned long __init alloc_down(unsigned long size, unsigned long align, } if (addr == 0) return 0; - RELOC(alloc_top) = addr; + alloc_top = addr; bail: prom_debug(" -> %x\n", addr); - prom_debug(" alloc_bottom : %x\n", RELOC(alloc_bottom)); - prom_debug(" alloc_top : %x\n", RELOC(alloc_top)); - prom_debug(" alloc_top_hi : %x\n", RELOC(alloc_top_high)); - prom_debug(" rmo_top : %x\n", RELOC(rmo_top)); - prom_debug(" ram_top : %x\n", RELOC(ram_top)); + prom_debug(" alloc_bottom : %x\n", alloc_bottom); + prom_debug(" alloc_top : %x\n", alloc_top); + prom_debug(" alloc_top_hi : %x\n", alloc_top_high); + prom_debug(" rmo_top : %x\n", rmo_top); + prom_debug(" ram_top : %x\n", ram_top); return addr; } @@ -1130,7 +1123,7 @@ static unsigned long __init prom_next_cell(int s, cell_t **cellp) static void __init reserve_mem(u64 base, u64 size) { u64 top = base + size; - unsigned long cnt = RELOC(mem_reserve_cnt); + unsigned long cnt = mem_reserve_cnt; if (size == 0) return; @@ -1145,9 +1138,9 @@ static void __init reserve_mem(u64 base, u64 size) if (cnt >= (MEM_RESERVE_MAP_SIZE - 1)) prom_panic("Memory reserve map exhausted !\n"); - RELOC(mem_reserve_map)[cnt].base = base; - RELOC(mem_reserve_map)[cnt].size = size; - RELOC(mem_reserve_cnt) = cnt + 1; + mem_reserve_map[cnt].base = base; + mem_reserve_map[cnt].size = size; + mem_reserve_cnt = cnt + 1; } /* @@ -1160,7 +1153,6 @@ static void __init prom_init_mem(void) char *path, type[64]; unsigned int plen; cell_t *p, *endp; - struct prom_t *_prom = &RELOC(prom); u32 rac, rsc; /* @@ -1169,14 +1161,14 @@ static void __init prom_init_mem(void) * 2) top of memory */ rac = 2; - prom_getprop(_prom->root, "#address-cells", &rac, sizeof(rac)); + prom_getprop(prom.root, "#address-cells", &rac, sizeof(rac)); rsc = 1; - prom_getprop(_prom->root, "#size-cells", &rsc, sizeof(rsc)); + prom_getprop(prom.root, "#size-cells", &rsc, sizeof(rsc)); prom_debug("root_addr_cells: %x\n", (unsigned long) rac); prom_debug("root_size_cells: %x\n", (unsigned long) rsc); prom_debug("scanning memory:\n"); - path = RELOC(prom_scratch); + path = prom_scratch; for (node = 0; prom_next_node(&node); ) { type[0] = 0; @@ -1189,15 +1181,15 @@ static void __init prom_init_mem(void) */ prom_getprop(node, "name", type, sizeof(type)); } - if (strcmp(type, RELOC("memory"))) + if (strcmp(type, "memory")) continue; - plen = prom_getprop(node, "reg", RELOC(regbuf), sizeof(regbuf)); + plen = prom_getprop(node, "reg", regbuf, sizeof(regbuf)); if (plen > sizeof(regbuf)) { prom_printf("memory node too large for buffer !\n"); plen = sizeof(regbuf); } - p = RELOC(regbuf); + p = regbuf; endp = p + (plen / sizeof(cell_t)); #ifdef DEBUG_PROM @@ -1215,14 +1207,14 @@ static void __init prom_init_mem(void) if (size == 0) continue; prom_debug(" %x %x\n", base, size); - if (base == 0 && (RELOC(of_platform) & PLATFORM_LPAR)) - RELOC(rmo_top) = size; - if ((base + size) > RELOC(ram_top)) - RELOC(ram_top) = base + size; + if (base == 0 && (of_platform & PLATFORM_LPAR)) + rmo_top = size; + if ((base + size) > ram_top) + ram_top = base + size; } } - RELOC(alloc_bottom) = PAGE_ALIGN((unsigned long)&RELOC(_end) + 0x4000); + alloc_bottom = PAGE_ALIGN((unsigned long)&_end + 0x4000); /* * If prom_memory_limit is set we reduce the upper limits *except* for @@ -1230,20 +1222,20 @@ static void __init prom_init_mem(void) * TCE's up there. */ - RELOC(alloc_top_high) = RELOC(ram_top); + alloc_top_high = ram_top; - if (RELOC(prom_memory_limit)) { - if (RELOC(prom_memory_limit) <= RELOC(alloc_bottom)) { + if (prom_memory_limit) { + if (prom_memory_limit <= alloc_bottom) { prom_printf("Ignoring mem=%x <= alloc_bottom.\n", - RELOC(prom_memory_limit)); - RELOC(prom_memory_limit) = 0; - } else if (RELOC(prom_memory_limit) >= RELOC(ram_top)) { + prom_memory_limit); + prom_memory_limit = 0; + } else if (prom_memory_limit >= ram_top) { prom_printf("Ignoring mem=%x >= ram_top.\n", - RELOC(prom_memory_limit)); - RELOC(prom_memory_limit) = 0; + prom_memory_limit); + prom_memory_limit = 0; } else { - RELOC(ram_top) = RELOC(prom_memory_limit); - RELOC(rmo_top) = min(RELOC(rmo_top), RELOC(prom_memory_limit)); + ram_top = prom_memory_limit; + rmo_top = min(rmo_top, prom_memory_limit); } } @@ -1255,36 +1247,35 @@ static void __init prom_init_mem(void) * Since 768MB is plenty of room, and we need to cap to something * reasonable on 32-bit, cap at 768MB on all machines. */ - if (!RELOC(rmo_top)) - RELOC(rmo_top) = RELOC(ram_top); - RELOC(rmo_top) = min(0x30000000ul, RELOC(rmo_top)); - RELOC(alloc_top) = RELOC(rmo_top); - RELOC(alloc_top_high) = RELOC(ram_top); + if (!rmo_top) + rmo_top = ram_top; + rmo_top = min(0x30000000ul, rmo_top); + alloc_top = rmo_top; + alloc_top_high = ram_top; /* * Check if we have an initrd after the kernel but still inside * the RMO. If we do move our bottom point to after it. */ - if (RELOC(prom_initrd_start) && - RELOC(prom_initrd_start) < RELOC(rmo_top) && - RELOC(prom_initrd_end) > RELOC(alloc_bottom)) - RELOC(alloc_bottom) = PAGE_ALIGN(RELOC(prom_initrd_end)); + if (prom_initrd_start && + prom_initrd_start < rmo_top && + prom_initrd_end > alloc_bottom) + alloc_bottom = PAGE_ALIGN(prom_initrd_end); prom_printf("memory layout at init:\n"); - prom_printf(" memory_limit : %x (16 MB aligned)\n", RELOC(prom_memory_limit)); - prom_printf(" alloc_bottom : %x\n", RELOC(alloc_bottom)); - prom_printf(" alloc_top : %x\n", RELOC(alloc_top)); - prom_printf(" alloc_top_hi : %x\n", RELOC(alloc_top_high)); - prom_printf(" rmo_top : %x\n", RELOC(rmo_top)); - prom_printf(" ram_top : %x\n", RELOC(ram_top)); + prom_printf(" memory_limit : %x (16 MB aligned)\n", prom_memory_limit); + prom_printf(" alloc_bottom : %x\n", alloc_bottom); + prom_printf(" alloc_top : %x\n", alloc_top); + prom_printf(" alloc_top_hi : %x\n", alloc_top_high); + prom_printf(" rmo_top : %x\n", rmo_top); + prom_printf(" ram_top : %x\n", ram_top); } static void __init prom_close_stdin(void) { - struct prom_t *_prom = &RELOC(prom); ihandle val; - if (prom_getprop(_prom->chosen, "stdin", &val, sizeof(val)) > 0) + if (prom_getprop(prom.chosen, "stdin", &val, sizeof(val)) > 0) call_prom("close", 1, 0, val); } @@ -1325,19 +1316,19 @@ static void __init prom_query_opal(void) } prom_printf("Querying for OPAL presence... "); - rc = opal_query_takeover(&RELOC(prom_opal_size), - &RELOC(prom_opal_align)); + rc = opal_query_takeover(&prom_opal_size, + &prom_opal_align); prom_debug("(rc = %ld) ", rc); if (rc != 0) { prom_printf("not there.\n"); return; } - RELOC(of_platform) = PLATFORM_OPAL; + of_platform = PLATFORM_OPAL; prom_printf(" there !\n"); - prom_debug(" opal_size = 0x%lx\n", RELOC(prom_opal_size)); - prom_debug(" opal_align = 0x%lx\n", RELOC(prom_opal_align)); - if (RELOC(prom_opal_align) < 0x10000) - RELOC(prom_opal_align) = 0x10000; + prom_debug(" opal_size = 0x%lx\n", prom_opal_size); + prom_debug(" opal_align = 0x%lx\n", prom_opal_align); + if (prom_opal_align < 0x10000) + prom_opal_align = 0x10000; } static int prom_rtas_call(int token, int nargs, int nret, int *outputs, ...) @@ -1358,8 +1349,8 @@ static int prom_rtas_call(int token, int nargs, int nret, int *outputs, ...) for (i = 0; i < nret; ++i) rtas_args.rets[i] = 0; - opal_enter_rtas(&rtas_args, RELOC(prom_rtas_data), - RELOC(prom_rtas_entry)); + opal_enter_rtas(&rtas_args, prom_rtas_data, + prom_rtas_entry); if (nret > 1 && outputs != NULL) for (i = 0; i < nret-1; ++i) @@ -1374,9 +1365,8 @@ static void __init prom_opal_hold_cpus(void) phandle node; char type[64]; u32 servers[8]; - struct prom_t *_prom = &RELOC(prom); - void *entry = (unsigned long *)&RELOC(opal_secondary_entry); - struct opal_secondary_data *data = &RELOC(opal_secondary_data); + void *entry = (unsigned long *)&opal_secondary_entry; + struct opal_secondary_data *data = &opal_secondary_data; prom_debug("prom_opal_hold_cpus: start...\n"); prom_debug(" - entry = 0x%x\n", entry); @@ -1389,12 +1379,12 @@ static void __init prom_opal_hold_cpus(void) for (node = 0; prom_next_node(&node); ) { type[0] = 0; prom_getprop(node, "device_type", type, sizeof(type)); - if (strcmp(type, RELOC("cpu")) != 0) + if (strcmp(type, "cpu") != 0) continue; /* Skip non-configured cpus. */ if (prom_getprop(node, "status", type, sizeof(type)) > 0) - if (strcmp(type, RELOC("okay")) != 0) + if (strcmp(type, "okay") != 0) continue; cnt = prom_getprop(node, "ibm,ppc-interrupt-server#s", servers, @@ -1405,7 +1395,7 @@ static void __init prom_opal_hold_cpus(void) for (i = 0; i < cnt; i++) { cpu = servers[i]; prom_debug("CPU %d ... ", cpu); - if (cpu == _prom->cpu) { + if (cpu == prom.cpu) { prom_debug("booted !\n"); continue; } @@ -1416,7 +1406,7 @@ static void __init prom_opal_hold_cpus(void) * spinloop. */ data->ack = -1; - rc = prom_rtas_call(RELOC(prom_rtas_start_cpu), 3, 1, + rc = prom_rtas_call(prom_rtas_start_cpu, 3, 1, NULL, cpu, entry, data); prom_debug("rtas rc=%d ...", rc); @@ -1436,21 +1426,21 @@ static void __init prom_opal_hold_cpus(void) static void __init prom_opal_takeover(void) { - struct opal_secondary_data *data = &RELOC(opal_secondary_data); + struct opal_secondary_data *data = &opal_secondary_data; struct opal_takeover_args *args = &data->args; - u64 align = RELOC(prom_opal_align); + u64 align = prom_opal_align; u64 top_addr, opal_addr; - args->k_image = (u64)RELOC(_stext); + args->k_image = (u64)_stext; args->k_size = _end - _stext; args->k_entry = 0; args->k_entry2 = 0x60; top_addr = _ALIGN_UP(args->k_size, align); - if (RELOC(prom_initrd_start) != 0) { - args->rd_image = RELOC(prom_initrd_start); - args->rd_size = RELOC(prom_initrd_end) - args->rd_image; + if (prom_initrd_start != 0) { + args->rd_image = prom_initrd_start; + args->rd_size = prom_initrd_end - args->rd_image; args->rd_loc = top_addr; top_addr = _ALIGN_UP(args->rd_loc + args->rd_size, align); } @@ -1462,13 +1452,13 @@ static void __init prom_opal_takeover(void) * has plenty of memory, and we ask for the HAL for now to * be just below the 1G point, or above the initrd */ - opal_addr = _ALIGN_DOWN(0x40000000 - RELOC(prom_opal_size), align); + opal_addr = _ALIGN_DOWN(0x40000000 - prom_opal_size, align); if (opal_addr < top_addr) opal_addr = top_addr; args->hal_addr = opal_addr; /* Copy the command line to the kernel image */ - strlcpy(RELOC(boot_command_line), RELOC(prom_cmd_line), + strlcpy(boot_command_line, prom_cmd_line, COMMAND_LINE_SIZE); prom_debug(" k_image = 0x%lx\n", args->k_image); @@ -1550,8 +1540,8 @@ static void __init prom_instantiate_opal(void) &entry, sizeof(entry)); #ifdef CONFIG_PPC_EARLY_DEBUG_OPAL - RELOC(prom_opal_base) = base; - RELOC(prom_opal_entry) = entry; + prom_opal_base = base; + prom_opal_entry = entry; #endif prom_debug("prom_instantiate_opal: end...\n"); } @@ -1609,9 +1599,9 @@ static void __init prom_instantiate_rtas(void) #ifdef CONFIG_PPC_POWERNV /* PowerVN takeover hack */ - RELOC(prom_rtas_data) = base; - RELOC(prom_rtas_entry) = entry; - prom_getprop(rtas_node, "start-cpu", &RELOC(prom_rtas_start_cpu), 4); + prom_rtas_data = base; + prom_rtas_entry = entry; + prom_getprop(rtas_node, "start-cpu", &prom_rtas_start_cpu, 4); #endif prom_debug("rtas base = 0x%x\n", base); prom_debug("rtas entry = 0x%x\n", entry); @@ -1686,20 +1676,20 @@ static void __init prom_initialize_tce_table(void) phandle node; ihandle phb_node; char compatible[64], type[64], model[64]; - char *path = RELOC(prom_scratch); + char *path = prom_scratch; u64 base, align; u32 minalign, minsize; u64 tce_entry, *tce_entryp; u64 local_alloc_top, local_alloc_bottom; u64 i; - if (RELOC(prom_iommu_off)) + if (prom_iommu_off) return; prom_debug("starting prom_initialize_tce_table\n"); /* Cache current top of allocs so we reserve a single block */ - local_alloc_top = RELOC(alloc_top_high); + local_alloc_top = alloc_top_high; local_alloc_bottom = local_alloc_top; /* Search all nodes looking for PHBs. */ @@ -1712,19 +1702,19 @@ static void __init prom_initialize_tce_table(void) prom_getprop(node, "device_type", type, sizeof(type)); prom_getprop(node, "model", model, sizeof(model)); - if ((type[0] == 0) || (strstr(type, RELOC("pci")) == NULL)) + if ((type[0] == 0) || (strstr(type, "pci") == NULL)) continue; /* Keep the old logic intact to avoid regression. */ if (compatible[0] != 0) { - if ((strstr(compatible, RELOC("python")) == NULL) && - (strstr(compatible, RELOC("Speedwagon")) == NULL) && - (strstr(compatible, RELOC("Winnipeg")) == NULL)) + if ((strstr(compatible, "python") == NULL) && + (strstr(compatible, "Speedwagon") == NULL) && + (strstr(compatible, "Winnipeg") == NULL)) continue; } else if (model[0] != 0) { - if ((strstr(model, RELOC("ython")) == NULL) && - (strstr(model, RELOC("peedwagon")) == NULL) && - (strstr(model, RELOC("innipeg")) == NULL)) + if ((strstr(model, "ython") == NULL) && + (strstr(model, "peedwagon") == NULL) && + (strstr(model, "innipeg") == NULL)) continue; } @@ -1803,8 +1793,8 @@ static void __init prom_initialize_tce_table(void) /* These are only really needed if there is a memory limit in * effect, but we don't know so export them always. */ - RELOC(prom_tce_alloc_start) = local_alloc_bottom; - RELOC(prom_tce_alloc_end) = local_alloc_top; + prom_tce_alloc_start = local_alloc_bottom; + prom_tce_alloc_end = local_alloc_top; /* Flag the first invalid entry */ prom_debug("ending prom_initialize_tce_table\n"); @@ -1841,7 +1831,6 @@ static void __init prom_hold_cpus(void) unsigned int reg; phandle node; char type[64]; - struct prom_t *_prom = &RELOC(prom); unsigned long *spinloop = (void *) LOW_ADDR(__secondary_hold_spinloop); unsigned long *acknowledge @@ -1867,12 +1856,12 @@ static void __init prom_hold_cpus(void) for (node = 0; prom_next_node(&node); ) { type[0] = 0; prom_getprop(node, "device_type", type, sizeof(type)); - if (strcmp(type, RELOC("cpu")) != 0) + if (strcmp(type, "cpu") != 0) continue; /* Skip non-configured cpus. */ if (prom_getprop(node, "status", type, sizeof(type)) > 0) - if (strcmp(type, RELOC("okay")) != 0) + if (strcmp(type, "okay") != 0) continue; reg = -1; @@ -1886,7 +1875,7 @@ static void __init prom_hold_cpus(void) */ *acknowledge = (unsigned long)-1; - if (reg != _prom->cpu) { + if (reg != prom.cpu) { /* Primary Thread of non-boot cpu or any thread */ prom_printf("starting cpu hw idx %lu... ", reg); call_prom("start-cpu", 3, 0, node, @@ -1913,22 +1902,20 @@ static void __init prom_hold_cpus(void) static void __init prom_init_client_services(unsigned long pp) { - struct prom_t *_prom = &RELOC(prom); - /* Get a handle to the prom entry point before anything else */ - RELOC(prom_entry) = pp; + prom_entry = pp; /* get a handle for the stdout device */ - _prom->chosen = call_prom("finddevice", 1, 1, ADDR("/chosen")); - if (!PHANDLE_VALID(_prom->chosen)) + prom.chosen = call_prom("finddevice", 1, 1, ADDR("/chosen")); + if (!PHANDLE_VALID(prom.chosen)) prom_panic("cannot find chosen"); /* msg won't be printed :( */ /* get device tree root */ - _prom->root = call_prom("finddevice", 1, 1, ADDR("/")); - if (!PHANDLE_VALID(_prom->root)) + prom.root = call_prom("finddevice", 1, 1, ADDR("/")); + if (!PHANDLE_VALID(prom.root)) prom_panic("cannot find device tree root"); /* msg won't be printed :( */ - _prom->mmumap = 0; + prom.mmumap = 0; } #ifdef CONFIG_PPC32 @@ -1939,7 +1926,6 @@ static void __init prom_init_client_services(unsigned long pp) */ static void __init prom_find_mmu(void) { - struct prom_t *_prom = &RELOC(prom); phandle oprom; char version[64]; @@ -1957,10 +1943,10 @@ static void __init prom_find_mmu(void) call_prom("interpret", 1, 1, "dev /memory 0 to allow-reclaim"); } else return; - _prom->memory = call_prom("open", 1, 1, ADDR("/memory")); - prom_getprop(_prom->chosen, "mmu", &_prom->mmumap, - sizeof(_prom->mmumap)); - if (!IHANDLE_VALID(_prom->memory) || !IHANDLE_VALID(_prom->mmumap)) + prom.memory = call_prom("open", 1, 1, ADDR("/memory")); + prom_getprop(prom.chosen, "mmu", &prom.mmumap, + sizeof(prom.mmumap)); + if (!IHANDLE_VALID(prom.memory) || !IHANDLE_VALID(prom.mmumap)) of_workarounds &= ~OF_WA_CLAIM; /* hmmm */ } #else @@ -1969,36 +1955,34 @@ static void __init prom_find_mmu(void) static void __init prom_init_stdout(void) { - struct prom_t *_prom = &RELOC(prom); - char *path = RELOC(of_stdout_device); + char *path = of_stdout_device; char type[16]; u32 val; - if (prom_getprop(_prom->chosen, "stdout", &val, sizeof(val)) <= 0) + if (prom_getprop(prom.chosen, "stdout", &val, sizeof(val)) <= 0) prom_panic("cannot find stdout"); - _prom->stdout = val; + prom.stdout = val; /* Get the full OF pathname of the stdout device */ memset(path, 0, 256); - call_prom("instance-to-path", 3, 1, _prom->stdout, path, 255); - val = call_prom("instance-to-package", 1, 1, _prom->stdout); - prom_setprop(_prom->chosen, "/chosen", "linux,stdout-package", + call_prom("instance-to-path", 3, 1, prom.stdout, path, 255); + val = call_prom("instance-to-package", 1, 1, prom.stdout); + prom_setprop(prom.chosen, "/chosen", "linux,stdout-package", &val, sizeof(val)); - prom_printf("OF stdout device is: %s\n", RELOC(of_stdout_device)); - prom_setprop(_prom->chosen, "/chosen", "linux,stdout-path", + prom_printf("OF stdout device is: %s\n", of_stdout_device); + prom_setprop(prom.chosen, "/chosen", "linux,stdout-path", path, strlen(path) + 1); /* If it's a display, note it */ memset(type, 0, sizeof(type)); prom_getprop(val, "device_type", type, sizeof(type)); - if (strcmp(type, RELOC("display")) == 0) + if (strcmp(type, "display") == 0) prom_setprop(val, path, "linux,boot-display", NULL, 0); } static int __init prom_find_machine_type(void) { - struct prom_t *_prom = &RELOC(prom); char compat[256]; int len, i = 0; #ifdef CONFIG_PPC64 @@ -2007,7 +1991,7 @@ static int __init prom_find_machine_type(void) #endif /* Look for a PowerMac or a Cell */ - len = prom_getprop(_prom->root, "compatible", + len = prom_getprop(prom.root, "compatible", compat, sizeof(compat)-1); if (len > 0) { compat[len] = 0; @@ -2016,16 +2000,16 @@ static int __init prom_find_machine_type(void) int sl = strlen(p); if (sl == 0) break; - if (strstr(p, RELOC("Power Macintosh")) || - strstr(p, RELOC("MacRISC"))) + if (strstr(p, "Power Macintosh") || + strstr(p, "MacRISC")) return PLATFORM_POWERMAC; #ifdef CONFIG_PPC64 /* We must make sure we don't detect the IBM Cell * blades as pSeries due to some firmware issues, * so we do it here. */ - if (strstr(p, RELOC("IBM,CBEA")) || - strstr(p, RELOC("IBM,CPBW-1.0"))) + if (strstr(p, "IBM,CBEA") || + strstr(p, "IBM,CPBW-1.0")) return PLATFORM_GENERIC; #endif /* CONFIG_PPC64 */ i += sl + 1; @@ -2042,11 +2026,11 @@ static int __init prom_find_machine_type(void) * non-IBM designs ! * - it has /rtas */ - len = prom_getprop(_prom->root, "device_type", + len = prom_getprop(prom.root, "device_type", compat, sizeof(compat)-1); if (len <= 0) return PLATFORM_GENERIC; - if (strcmp(compat, RELOC("chrp"))) + if (strcmp(compat, "chrp")) return PLATFORM_GENERIC; /* Default to pSeries. We need to know if we are running LPAR */ @@ -2108,11 +2092,11 @@ static void __init prom_check_displays(void) for (node = 0; prom_next_node(&node); ) { memset(type, 0, sizeof(type)); prom_getprop(node, "device_type", type, sizeof(type)); - if (strcmp(type, RELOC("display")) != 0) + if (strcmp(type, "display") != 0) continue; /* It seems OF doesn't null-terminate the path :-( */ - path = RELOC(prom_scratch); + path = prom_scratch; memset(path, 0, PROM_SCRATCH_SIZE); /* @@ -2136,15 +2120,15 @@ static void __init prom_check_displays(void) /* Setup a usable color table when the appropriate * method is available. Should update this to set-colors */ - clut = RELOC(default_colors); + clut = default_colors; for (i = 0; i < 16; i++, clut += 3) if (prom_set_color(ih, i, clut[0], clut[1], clut[2]) != 0) break; #ifdef CONFIG_LOGO_LINUX_CLUT224 - clut = PTRRELOC(RELOC(logo_linux_clut224.clut)); - for (i = 0; i < RELOC(logo_linux_clut224.clutsize); i++, clut += 3) + clut = PTRRELOC(logo_linux_clut224.clut); + for (i = 0; i < logo_linux_clut224.clutsize; i++, clut += 3) if (prom_set_color(ih, i + 32, clut[0], clut[1], clut[2]) != 0) break; @@ -2164,8 +2148,8 @@ static void __init *make_room(unsigned long *mem_start, unsigned long *mem_end, unsigned long room, chunk; prom_debug("Chunk exhausted, claiming more at %x...\n", - RELOC(alloc_bottom)); - room = RELOC(alloc_top) - RELOC(alloc_bottom); + alloc_bottom); + room = alloc_top - alloc_bottom; if (room > DEVTREE_CHUNK_SIZE) room = DEVTREE_CHUNK_SIZE; if (room < PAGE_SIZE) @@ -2191,9 +2175,9 @@ static unsigned long __init dt_find_string(char *str) { char *s, *os; - s = os = (char *)RELOC(dt_string_start); + s = os = (char *)dt_string_start; s += 4; - while (s < (char *)RELOC(dt_string_end)) { + while (s < (char *)dt_string_end) { if (strcmp(s, str) == 0) return s - os; s += strlen(s) + 1; @@ -2215,10 +2199,10 @@ static void __init scan_dt_build_strings(phandle node, unsigned long soff; phandle child; - sstart = (char *)RELOC(dt_string_start); + sstart = (char *)dt_string_start; /* get and store all property names */ - prev_name = RELOC(""); + prev_name = ""; for (;;) { /* 64 is max len of name including nul. */ namep = make_room(mem_start, mem_end, MAX_PROPERTY_NAME, 1); @@ -2229,9 +2213,9 @@ static void __init scan_dt_build_strings(phandle node, } /* skip "name" */ - if (strcmp(namep, RELOC("name")) == 0) { + if (strcmp(namep, "name") == 0) { *mem_start = (unsigned long)namep; - prev_name = RELOC("name"); + prev_name = "name"; continue; } /* get/create string entry */ @@ -2242,7 +2226,7 @@ static void __init scan_dt_build_strings(phandle node, } else { /* Trim off some if we can */ *mem_start = (unsigned long)namep + strlen(namep) + 1; - RELOC(dt_string_end) = *mem_start; + dt_string_end = *mem_start; } prev_name = namep; } @@ -2297,35 +2281,35 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start, } /* get it again for debugging */ - path = RELOC(prom_scratch); + path = prom_scratch; memset(path, 0, PROM_SCRATCH_SIZE); call_prom("package-to-path", 3, 1, node, path, PROM_SCRATCH_SIZE-1); /* get and store all properties */ - prev_name = RELOC(""); - sstart = (char *)RELOC(dt_string_start); + prev_name = ""; + sstart = (char *)dt_string_start; for (;;) { if (call_prom("nextprop", 3, 1, node, prev_name, - RELOC(pname)) != 1) + pname) != 1) break; /* skip "name" */ - if (strcmp(RELOC(pname), RELOC("name")) == 0) { - prev_name = RELOC("name"); + if (strcmp(pname, "name") == 0) { + prev_name = "name"; continue; } /* find string offset */ - soff = dt_find_string(RELOC(pname)); + soff = dt_find_string(pname); if (soff == 0) { prom_printf("WARNING: Can't find string index for" - " <%s>, node %s\n", RELOC(pname), path); + " <%s>, node %s\n", pname, path); break; } prev_name = sstart + soff; /* get length */ - l = call_prom("getproplen", 2, 1, node, RELOC(pname)); + l = call_prom("getproplen", 2, 1, node, pname); /* sanity checks */ if (l == PROM_ERROR) @@ -2338,10 +2322,10 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start, /* push property content */ valp = make_room(mem_start, mem_end, l, 4); - call_prom("getprop", 4, 1, node, RELOC(pname), valp, l); + call_prom("getprop", 4, 1, node, pname, valp, l); *mem_start = _ALIGN(*mem_start, 4); - if (!strcmp(RELOC(pname), RELOC("phandle"))) + if (!strcmp(pname, "phandle")) has_phandle = 1; } @@ -2349,7 +2333,7 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start, * existed (can happen with OPAL) */ if (!has_phandle) { - soff = dt_find_string(RELOC("linux,phandle")); + soff = dt_find_string("linux,phandle"); if (soff == 0) prom_printf("WARNING: Can't find string index for" " node %s\n", path); @@ -2377,7 +2361,6 @@ static void __init flatten_device_tree(void) phandle root; unsigned long mem_start, mem_end, room; struct boot_param_header *hdr; - struct prom_t *_prom = &RELOC(prom); char *namep; u64 *rsvmap; @@ -2385,10 +2368,10 @@ static void __init flatten_device_tree(void) * Check how much room we have between alloc top & bottom (+/- a * few pages), crop to 1MB, as this is our "chunk" size */ - room = RELOC(alloc_top) - RELOC(alloc_bottom) - 0x4000; + room = alloc_top - alloc_bottom - 0x4000; if (room > DEVTREE_CHUNK_SIZE) room = DEVTREE_CHUNK_SIZE; - prom_debug("starting device tree allocs at %x\n", RELOC(alloc_bottom)); + prom_debug("starting device tree allocs at %x\n", alloc_bottom); /* Now try to claim that */ mem_start = (unsigned long)alloc_up(room, PAGE_SIZE); @@ -2405,66 +2388,66 @@ static void __init flatten_device_tree(void) mem_start = _ALIGN(mem_start, 4); hdr = make_room(&mem_start, &mem_end, sizeof(struct boot_param_header), 4); - RELOC(dt_header_start) = (unsigned long)hdr; + dt_header_start = (unsigned long)hdr; rsvmap = make_room(&mem_start, &mem_end, sizeof(mem_reserve_map), 8); /* Start of strings */ mem_start = PAGE_ALIGN(mem_start); - RELOC(dt_string_start) = mem_start; + dt_string_start = mem_start; mem_start += 4; /* hole */ /* Add "linux,phandle" in there, we'll need it */ namep = make_room(&mem_start, &mem_end, 16, 1); - strcpy(namep, RELOC("linux,phandle")); + strcpy(namep, "linux,phandle"); mem_start = (unsigned long)namep + strlen(namep) + 1; /* Build string array */ prom_printf("Building dt strings...\n"); scan_dt_build_strings(root, &mem_start, &mem_end); - RELOC(dt_string_end) = mem_start; + dt_string_end = mem_start; /* Build structure */ mem_start = PAGE_ALIGN(mem_start); - RELOC(dt_struct_start) = mem_start; + dt_struct_start = mem_start; prom_printf("Building dt structure...\n"); scan_dt_build_struct(root, &mem_start, &mem_end); dt_push_token(OF_DT_END, &mem_start, &mem_end); - RELOC(dt_struct_end) = PAGE_ALIGN(mem_start); + dt_struct_end = PAGE_ALIGN(mem_start); /* Finish header */ - hdr->boot_cpuid_phys = _prom->cpu; + hdr->boot_cpuid_phys = prom.cpu; hdr->magic = OF_DT_HEADER; - hdr->totalsize = RELOC(dt_struct_end) - RELOC(dt_header_start); - hdr->off_dt_struct = RELOC(dt_struct_start) - RELOC(dt_header_start); - hdr->off_dt_strings = RELOC(dt_string_start) - RELOC(dt_header_start); - hdr->dt_strings_size = RELOC(dt_string_end) - RELOC(dt_string_start); - hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - RELOC(dt_header_start); + hdr->totalsize = dt_struct_end - dt_header_start; + hdr->off_dt_struct = dt_struct_start - dt_header_start; + hdr->off_dt_strings = dt_string_start - dt_header_start; + hdr->dt_strings_size = dt_string_end - dt_string_start; + hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - dt_header_start; hdr->version = OF_DT_VERSION; /* Version 16 is not backward compatible */ hdr->last_comp_version = 0x10; /* Copy the reserve map in */ - memcpy(rsvmap, RELOC(mem_reserve_map), sizeof(mem_reserve_map)); + memcpy(rsvmap, mem_reserve_map, sizeof(mem_reserve_map)); #ifdef DEBUG_PROM { int i; prom_printf("reserved memory map:\n"); - for (i = 0; i < RELOC(mem_reserve_cnt); i++) + for (i = 0; i < mem_reserve_cnt; i++) prom_printf(" %x - %x\n", - RELOC(mem_reserve_map)[i].base, - RELOC(mem_reserve_map)[i].size); + mem_reserve_map[i].base, + mem_reserve_map[i].size); } #endif /* Bump mem_reserve_cnt to cause further reservations to fail * since it's too late. */ - RELOC(mem_reserve_cnt) = MEM_RESERVE_MAP_SIZE; + mem_reserve_cnt = MEM_RESERVE_MAP_SIZE; prom_printf("Device tree strings 0x%x -> 0x%x\n", - RELOC(dt_string_start), RELOC(dt_string_end)); + dt_string_start, dt_string_end); prom_printf("Device tree struct 0x%x -> 0x%x\n", - RELOC(dt_struct_start), RELOC(dt_struct_end)); + dt_struct_start, dt_struct_end); } @@ -2519,7 +2502,6 @@ static void __init fixup_device_tree_maple_memory_controller(void) phandle mc; u32 mc_reg[4]; char *name = "/hostbridge@f8000000"; - struct prom_t *_prom = &RELOC(prom); u32 ac, sc; mc = call_prom("finddevice", 1, 1, ADDR(name)); @@ -2529,8 +2511,8 @@ static void __init fixup_device_tree_maple_memory_controller(void) if (prom_getproplen(mc, "reg") != 8) return; - prom_getprop(_prom->root, "#address-cells", &ac, sizeof(ac)); - prom_getprop(_prom->root, "#size-cells", &sc, sizeof(sc)); + prom_getprop(prom.root, "#address-cells", &ac, sizeof(ac)); + prom_getprop(prom.root, "#size-cells", &sc, sizeof(sc)); if ((ac != 2) || (sc != 2)) return; @@ -2799,46 +2781,43 @@ static void __init fixup_device_tree(void) static void __init prom_find_boot_cpu(void) { - struct prom_t *_prom = &RELOC(prom); u32 getprop_rval; ihandle prom_cpu; phandle cpu_pkg; - _prom->cpu = 0; - if (prom_getprop(_prom->chosen, "cpu", &prom_cpu, sizeof(prom_cpu)) <= 0) + prom.cpu = 0; + if (prom_getprop(prom.chosen, "cpu", &prom_cpu, sizeof(prom_cpu)) <= 0) return; cpu_pkg = call_prom("instance-to-package", 1, 1, prom_cpu); prom_getprop(cpu_pkg, "reg", &getprop_rval, sizeof(getprop_rval)); - _prom->cpu = getprop_rval; + prom.cpu = getprop_rval; - prom_debug("Booting CPU hw index = %lu\n", _prom->cpu); + prom_debug("Booting CPU hw index = %lu\n", prom.cpu); } static void __init prom_check_initrd(unsigned long r3, unsigned long r4) { #ifdef CONFIG_BLK_DEV_INITRD - struct prom_t *_prom = &RELOC(prom); - if (r3 && r4 && r4 != 0xdeadbeef) { unsigned long val; - RELOC(prom_initrd_start) = is_kernel_addr(r3) ? __pa(r3) : r3; - RELOC(prom_initrd_end) = RELOC(prom_initrd_start) + r4; + prom_initrd_start = is_kernel_addr(r3) ? __pa(r3) : r3; + prom_initrd_end = prom_initrd_start + r4; - val = RELOC(prom_initrd_start); - prom_setprop(_prom->chosen, "/chosen", "linux,initrd-start", + val = prom_initrd_start; + prom_setprop(prom.chosen, "/chosen", "linux,initrd-start", &val, sizeof(val)); - val = RELOC(prom_initrd_end); - prom_setprop(_prom->chosen, "/chosen", "linux,initrd-end", + val = prom_initrd_end; + prom_setprop(prom.chosen, "/chosen", "linux,initrd-end", &val, sizeof(val)); - reserve_mem(RELOC(prom_initrd_start), - RELOC(prom_initrd_end) - RELOC(prom_initrd_start)); + reserve_mem(prom_initrd_start, + prom_initrd_end - prom_initrd_start); - prom_debug("initrd_start=0x%x\n", RELOC(prom_initrd_start)); - prom_debug("initrd_end=0x%x\n", RELOC(prom_initrd_end)); + prom_debug("initrd_start=0x%x\n", prom_initrd_start); + prom_debug("initrd_end=0x%x\n", prom_initrd_end); } #endif /* CONFIG_BLK_DEV_INITRD */ } @@ -2901,7 +2880,6 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, unsigned long r6, unsigned long r7, unsigned long kbase) { - struct prom_t *_prom; unsigned long hdr; #ifdef CONFIG_PPC32 @@ -2911,12 +2889,10 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, reloc_toc(); #endif - _prom = &RELOC(prom); - /* * First zero the BSS */ - memset(&RELOC(__bss_start), 0, __bss_stop - __bss_start); + memset(&__bss_start, 0, __bss_stop - __bss_start); /* * Init interface to Open Firmware, get some node references, @@ -2935,14 +2911,14 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, */ prom_init_stdout(); - prom_printf("Preparing to boot %s", RELOC(linux_banner)); + prom_printf("Preparing to boot %s", linux_banner); /* * Get default machine type. At this point, we do not differentiate * between pSeries SMP and pSeries LPAR */ - RELOC(of_platform) = prom_find_machine_type(); - prom_printf("Detected machine type: %x\n", RELOC(of_platform)); + of_platform = prom_find_machine_type(); + prom_printf("Detected machine type: %x\n", of_platform); #ifndef CONFIG_NONSTATIC_KERNEL /* Bail if this is a kdump kernel. */ @@ -2959,15 +2935,15 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, /* * On pSeries, inform the firmware about our capabilities */ - if (RELOC(of_platform) == PLATFORM_PSERIES || - RELOC(of_platform) == PLATFORM_PSERIES_LPAR) + if (of_platform == PLATFORM_PSERIES || + of_platform == PLATFORM_PSERIES_LPAR) prom_send_capabilities(); #endif /* * Copy the CPU hold code */ - if (RELOC(of_platform) != PLATFORM_POWERMAC) + if (of_platform != PLATFORM_POWERMAC) copy_and_flush(0, kbase, 0x100, 0); /* @@ -2996,7 +2972,7 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * that uses the allocator, we need to make sure we get the top of memory * available for us here... */ - if (RELOC(of_platform) == PLATFORM_PSERIES) + if (of_platform == PLATFORM_PSERIES) prom_initialize_tce_table(); #endif @@ -3004,19 +2980,19 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * On non-powermacs, try to instantiate RTAS. PowerMacs don't * have a usable RTAS implementation. */ - if (RELOC(of_platform) != PLATFORM_POWERMAC && - RELOC(of_platform) != PLATFORM_OPAL) + if (of_platform != PLATFORM_POWERMAC && + of_platform != PLATFORM_OPAL) prom_instantiate_rtas(); #ifdef CONFIG_PPC_POWERNV /* Detect HAL and try instanciating it & doing takeover */ - if (RELOC(of_platform) == PLATFORM_PSERIES_LPAR) { + if (of_platform == PLATFORM_PSERIES_LPAR) { prom_query_opal(); - if (RELOC(of_platform) == PLATFORM_OPAL) { + if (of_platform == PLATFORM_OPAL) { prom_opal_hold_cpus(); prom_opal_takeover(); } - } else if (RELOC(of_platform) == PLATFORM_OPAL) + } else if (of_platform == PLATFORM_OPAL) prom_instantiate_opal(); #endif @@ -3030,32 +3006,32 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * * PowerMacs use a different mechanism to spin CPUs */ - if (RELOC(of_platform) != PLATFORM_POWERMAC && - RELOC(of_platform) != PLATFORM_OPAL) + if (of_platform != PLATFORM_POWERMAC && + of_platform != PLATFORM_OPAL) prom_hold_cpus(); /* * Fill in some infos for use by the kernel later on */ - if (RELOC(prom_memory_limit)) - prom_setprop(_prom->chosen, "/chosen", "linux,memory-limit", - &RELOC(prom_memory_limit), + if (prom_memory_limit) + prom_setprop(prom.chosen, "/chosen", "linux,memory-limit", + &prom_memory_limit, sizeof(prom_memory_limit)); #ifdef CONFIG_PPC64 - if (RELOC(prom_iommu_off)) - prom_setprop(_prom->chosen, "/chosen", "linux,iommu-off", + if (prom_iommu_off) + prom_setprop(prom.chosen, "/chosen", "linux,iommu-off", NULL, 0); - if (RELOC(prom_iommu_force_on)) - prom_setprop(_prom->chosen, "/chosen", "linux,iommu-force-on", + if (prom_iommu_force_on) + prom_setprop(prom.chosen, "/chosen", "linux,iommu-force-on", NULL, 0); - if (RELOC(prom_tce_alloc_start)) { - prom_setprop(_prom->chosen, "/chosen", "linux,tce-alloc-start", - &RELOC(prom_tce_alloc_start), + if (prom_tce_alloc_start) { + prom_setprop(prom.chosen, "/chosen", "linux,tce-alloc-start", + &prom_tce_alloc_start, sizeof(prom_tce_alloc_start)); - prom_setprop(_prom->chosen, "/chosen", "linux,tce-alloc-end", - &RELOC(prom_tce_alloc_end), + prom_setprop(prom.chosen, "/chosen", "linux,tce-alloc-end", + &prom_tce_alloc_end, sizeof(prom_tce_alloc_end)); } #endif @@ -3077,8 +3053,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * closed stdin already (in particular the powerbook 101). It * appears that the OPAL version of OFW doesn't like it either. */ - if (RELOC(of_platform) != PLATFORM_POWERMAC && - RELOC(of_platform) != PLATFORM_OPAL) + if (of_platform != PLATFORM_POWERMAC && + of_platform != PLATFORM_OPAL) prom_close_stdin(); /* @@ -3093,10 +3069,10 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * tree and NULL as r5, thus triggering the new entry point which * is common to us and kexec */ - hdr = RELOC(dt_header_start); + hdr = dt_header_start; /* Don't print anything after quiesce under OPAL, it crashes OFW */ - if (RELOC(of_platform) != PLATFORM_OPAL) { + if (of_platform != PLATFORM_OPAL) { prom_printf("returning from prom_init\n"); prom_debug("->dt_header_start=0x%x\n", hdr); } @@ -3110,7 +3086,7 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, #ifdef CONFIG_PPC_EARLY_DEBUG_OPAL /* OPAL early debug gets the OPAL base & entry in r8 and r9 */ __start(hdr, kbase, 0, 0, 0, - RELOC(prom_opal_base), RELOC(prom_opal_entry)); + prom_opal_base, prom_opal_entry); #else __start(hdr, kbase, 0, 0, 0, 0, 0); #endif From 1fbe9cf2598dae3bd464d860bd89c67b1ff8682b Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 26 Nov 2012 17:41:08 +0000 Subject: [PATCH 0762/4607] powerpc: Build kernel with -mcmodel=medium Finally remove the two level TOC and build with -mcmodel=medium. Unfortunately we can't build modules with -mcmodel=medium due to the tricks the kernel module loader plays with percpu data: # -mcmodel=medium breaks modules because it uses 32bit offsets from # the TOC pointer to create pointers where possible. Pointers into the # percpu data area are created by this method. # # The kernel module loader relocates the percpu data section from the # original location (starting with 0xd...) to somewhere in the base # kernel percpu data space (starting with 0xc...). We need a full # 64bit relocation for this to work, hence -mcmodel=large. On older kernels we fall back to the two level TOC (-mminimal-toc) Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Makefile | 19 +++++++++++++++- arch/powerpc/kernel/Makefile | 2 +- arch/powerpc/kernel/head_64.S | 15 +++++++++++-- arch/powerpc/kernel/module_64.c | 30 +++++++++++++++++++++++++ arch/powerpc/lib/Makefile | 2 +- arch/powerpc/mm/Makefile | 2 +- arch/powerpc/oprofile/Makefile | 2 +- arch/powerpc/platforms/pseries/Makefile | 2 +- arch/powerpc/platforms/wsp/Makefile | 2 +- arch/powerpc/sysdev/Makefile | 2 +- arch/powerpc/xmon/Makefile | 2 +- 11 files changed, 69 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index 78c2b024371a..31d8965e0847 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -67,7 +67,24 @@ LDFLAGS_vmlinux-y := -Bstatic LDFLAGS_vmlinux-$(CONFIG_RELOCATABLE) := -pie LDFLAGS_vmlinux := $(LDFLAGS_vmlinux-y) -CFLAGS-$(CONFIG_PPC64) := -mminimal-toc -mtraceback=no -mcall-aixdesc +ifeq ($(CONFIG_PPC64),y) +ifeq ($(call cc-option-yn,-mcmodel=medium),y) + # -mcmodel=medium breaks modules because it uses 32bit offsets from + # the TOC pointer to create pointers where possible. Pointers into the + # percpu data area are created by this method. + # + # The kernel module loader relocates the percpu data section from the + # original location (starting with 0xd...) to somewhere in the base + # kernel percpu data space (starting with 0xc...). We need a full + # 64bit relocation for this to work, hence -mcmodel=large. + KBUILD_CFLAGS_MODULE += -mcmodel=large +else + export NO_MINIMAL_TOC := -mno-minimal-toc +endif +endif + +CFLAGS-$(CONFIG_PPC64) := -mtraceback=no -mcall-aixdesc +CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcmodel=medium,-mminimal-toc) CFLAGS-$(CONFIG_PPC32) := -ffixed-r2 -mmultiple CFLAGS-$(CONFIG_GENERIC_CPU) += $(call cc-option,-mtune=power7,-mtune=power4) diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 2f6ef4ed5abf..b4f0e360e414 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -7,7 +7,7 @@ CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror ifeq ($(CONFIG_PPC64),y) -CFLAGS_prom_init.o += -mno-minimal-toc +CFLAGS_prom_init.o += $(NO_MINIMAL_TOC) endif ifeq ($(CONFIG_PPC32),y) CFLAGS_prom_init.o += -fPIC diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 116f0868695b..1697a25ebe91 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -169,6 +169,7 @@ _GLOBAL(generic_secondary_thread_init) /* get a valid TOC pointer, wherever we're mapped at */ bl .relative_toc + tovirt(r2,r2) #ifdef CONFIG_PPC_BOOK3E /* Book3E initialization */ @@ -195,6 +196,7 @@ _GLOBAL(generic_secondary_smp_init) /* get a valid TOC pointer, wherever we're mapped at */ bl .relative_toc + tovirt(r2,r2) #ifdef CONFIG_PPC_BOOK3E /* Book3E initialization */ @@ -531,6 +533,7 @@ _GLOBAL(pmac_secondary_start) /* get TOC pointer (real address) */ bl .relative_toc + tovirt(r2,r2) /* Copy some CPU settings from CPU 0 */ bl .__restore_cpu_ppc970 @@ -665,6 +668,13 @@ _GLOBAL(enable_64b_mode) * This puts the TOC pointer into r2, offset by 0x8000 (as expected * by the toolchain). It computes the correct value for wherever we * are running at the moment, using position-independent code. + * + * Note: The compiler constructs pointers using offsets from the + * TOC in -mcmodel=medium mode. After we relocate to 0 but before + * the MMU is on we need our TOC to be a virtual address otherwise + * these pointers will be real addresses which may get stored and + * accessed later with the MMU on. We use tovirt() at the call + * sites to handle this. */ _GLOBAL(relative_toc) mflr r0 @@ -681,8 +691,9 @@ p_toc: .llong __toc_start + 0x8000 - 0b * This is where the main kernel code starts. */ _INIT_STATIC(start_here_multiplatform) - /* set up the TOC (real address) */ - bl .relative_toc + /* set up the TOC */ + bl .relative_toc + tovirt(r2,r2) /* Clear out the BSS. It may have been done in prom_init, * already but that's irrelevant since prom_init will soon diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c index 9f44a775a106..6ee59a0eb268 100644 --- a/arch/powerpc/kernel/module_64.c +++ b/arch/powerpc/kernel/module_64.c @@ -386,6 +386,14 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, | (value & 0xffff); break; + case R_PPC64_TOC16_LO: + /* Subtract TOC pointer */ + value -= my_r2(sechdrs, me); + *((uint16_t *) location) + = (*((uint16_t *) location) & ~0xffff) + | (value & 0xffff); + break; + case R_PPC64_TOC16_DS: /* Subtract TOC pointer */ value -= my_r2(sechdrs, me); @@ -399,6 +407,28 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, | (value & 0xfffc); break; + case R_PPC64_TOC16_LO_DS: + /* Subtract TOC pointer */ + value -= my_r2(sechdrs, me); + if ((value & 3) != 0) { + printk("%s: bad TOC16_LO_DS relocation (%lu)\n", + me->name, value); + return -ENOEXEC; + } + *((uint16_t *) location) + = (*((uint16_t *) location) & ~0xfffc) + | (value & 0xfffc); + break; + + case R_PPC64_TOC16_HA: + /* Subtract TOC pointer */ + value -= my_r2(sechdrs, me); + value = ((value + 0x8000) >> 16); + *((uint16_t *) location) + = (*((uint16_t *) location) & ~0xffff) + | (value & 0xffff); + break; + case R_PPC_REL24: /* FIXME: Handle weak symbols here --RR */ if (sym->st_shndx == SHN_UNDEF) { diff --git a/arch/powerpc/lib/Makefile b/arch/powerpc/lib/Makefile index 746e0c895cd7..fa48a4cc19a3 100644 --- a/arch/powerpc/lib/Makefile +++ b/arch/powerpc/lib/Makefile @@ -4,7 +4,7 @@ subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror -ccflags-$(CONFIG_PPC64) := -mno-minimal-toc +ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) CFLAGS_REMOVE_code-patching.o = -pg CFLAGS_REMOVE_feature-fixups.o = -pg diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile index 3787b61f7d20..cf16b5733eaa 100644 --- a/arch/powerpc/mm/Makefile +++ b/arch/powerpc/mm/Makefile @@ -4,7 +4,7 @@ subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror -ccflags-$(CONFIG_PPC64) := -mno-minimal-toc +ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) obj-y := fault.o mem.o pgtable.o gup.o \ init_$(CONFIG_WORD_SIZE).o \ diff --git a/arch/powerpc/oprofile/Makefile b/arch/powerpc/oprofile/Makefile index 73456c4cec28..751ec7bd5018 100644 --- a/arch/powerpc/oprofile/Makefile +++ b/arch/powerpc/oprofile/Makefile @@ -1,6 +1,6 @@ subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror -ccflags-$(CONFIG_PPC64) := -mno-minimal-toc +ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) obj-$(CONFIG_OPROFILE) += oprofile.o diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 890622b87c8f..53866e537a92 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile @@ -1,4 +1,4 @@ -ccflags-$(CONFIG_PPC64) := -mno-minimal-toc +ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) ccflags-$(CONFIG_PPC_PSERIES_DEBUG) += -DDEBUG obj-y := lpar.o hvCall.o nvram.o reconfig.o \ diff --git a/arch/powerpc/platforms/wsp/Makefile b/arch/powerpc/platforms/wsp/Makefile index 56817ac98fc9..162fc60125a2 100644 --- a/arch/powerpc/platforms/wsp/Makefile +++ b/arch/powerpc/platforms/wsp/Makefile @@ -1,4 +1,4 @@ -ccflags-y += -mno-minimal-toc +ccflags-y += $(NO_MINIMAL_TOC) obj-y += setup.o ics.o wsp.o obj-$(CONFIG_PPC_PSR2) += psr2.o diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index f44f09f3d527..eca3d19304c7 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -1,6 +1,6 @@ subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror -ccflags-$(CONFIG_PPC64) := -mno-minimal-toc +ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) diff --git a/arch/powerpc/xmon/Makefile b/arch/powerpc/xmon/Makefile index b49fdbd15808..1278788d96e3 100644 --- a/arch/powerpc/xmon/Makefile +++ b/arch/powerpc/xmon/Makefile @@ -4,7 +4,7 @@ subdir-ccflags-$(CONFIG_PPC_WERROR) := -Werror GCOV_PROFILE := n -ccflags-$(CONFIG_PPC64) := -mno-minimal-toc +ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) obj-y += xmon.o nonstdio.o From e253ebab935693cc2eafbc93e94b68b47dc779db Mon Sep 17 00:00:00 2001 From: Li Zhong Date: Sun, 2 Dec 2012 20:19:22 +0000 Subject: [PATCH 0763/4607] powerpc: Fix MAX_STACK_TRACE_ENTRIES too low warning for ppc32 This patch fixes MAX_STACK_TRACE_ENTRIES too low warning for ppc32, which is similar to commit 12660b17. Reported-by: Christian Kujau Signed-off-by: Li Zhong Tested-by: Christian Kujau Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/entry_32.S | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index d22e73e4618b..e514de57a125 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -439,6 +439,8 @@ ret_from_fork: ret_from_kernel_thread: REST_NVGPRS(r1) bl schedule_tail + li r3,0 + stw r3,0(r1) mtlr r14 mr r3,r15 PPC440EP_ERR42 From c7c360eedb8c1eea302a224a8c83d19a4e4cf608 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sun, 2 Dec 2012 03:00:09 +0000 Subject: [PATCH 0764/4607] powerpc/windfarm: Use for_each_node_by_type() macro Use for_each_node_by_type() macro instead of open coding it. Signed-off-by: Wei Yongjun Signed-off-by: Benjamin Herrenschmidt --- drivers/macintosh/windfarm_pm112.c | 2 +- drivers/macintosh/windfarm_pm72.c | 2 +- drivers/macintosh/windfarm_rm31.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/macintosh/windfarm_pm112.c b/drivers/macintosh/windfarm_pm112.c index 35ef6e2582b8..3024685e4cca 100644 --- a/drivers/macintosh/windfarm_pm112.c +++ b/drivers/macintosh/windfarm_pm112.c @@ -681,7 +681,7 @@ static int __init wf_pm112_init(void) /* Count the number of CPU cores */ nr_cores = 0; - for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) + for_each_node_by_type(cpu, "cpu") ++nr_cores; printk(KERN_INFO "windfarm: initializing for dual-core desktop G5\n"); diff --git a/drivers/macintosh/windfarm_pm72.c b/drivers/macintosh/windfarm_pm72.c index 6e5585357cd3..2f506b9d5a52 100644 --- a/drivers/macintosh/windfarm_pm72.c +++ b/drivers/macintosh/windfarm_pm72.c @@ -804,7 +804,7 @@ static int __init wf_pm72_init(void) /* Count the number of CPU cores */ nr_chips = 0; - for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) + for_each_node_by_type(cpu, "cpu") ++nr_chips; if (nr_chips > NR_CHIPS) nr_chips = NR_CHIPS; diff --git a/drivers/macintosh/windfarm_rm31.c b/drivers/macintosh/windfarm_rm31.c index 844003fb4ef0..0b9a79b2f48a 100644 --- a/drivers/macintosh/windfarm_rm31.c +++ b/drivers/macintosh/windfarm_rm31.c @@ -696,7 +696,7 @@ static int __init wf_rm31_init(void) /* Count the number of CPU cores */ nr_chips = 0; - for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) + for_each_node_by_type(cpu, "cpu") ++nr_chips; if (nr_chips > NR_CHIPS) nr_chips = NR_CHIPS; From 96f013fe1b0904ee9059aa5105f7aa4959cd3914 Mon Sep 17 00:00:00 2001 From: Jimi Xenidis Date: Mon, 3 Dec 2012 17:05:47 +0000 Subject: [PATCH 0765/4607] powerpc/kexec: Add kexec "hold" support for Book3e processors Motivation: IBM Blue Gene/Q comes with some very strange firmware that I'm trying to get out of using in the kernel. So instead I spin all the threads in the boot wrapper (using the firmware) and have them enter the kexec stub, pre-translated at the virtual "linear" address, never touching firmware again. This works strategy works wonderfully, but I need the following patch in the kexec stub. I believe it should not effect Book3S and Book3E does not appear to be here yet so I'd love to get any criticisms up front. This patch adds two items: 1) Book3e requires that GPR4 survive the "hold" process, so we make sure that happens. 2) Book3e has no real mode, and the hold code exploits this. Since these processors ares always translated, we arrange for the kexeced threads to enter the hold code using the normal kernel linear mapping. Signed-off-by: Jimi Xenidis Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/head_64.S | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 1697a25ebe91..0886ae6dd5be 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -122,6 +122,8 @@ __secondary_hold: #endif /* Grab our physical cpu number */ mr r24,r3 + /* stash r4 for book3e */ + mr r25,r4 /* Tell the master cpu we're here */ /* Relocation is off & we are located at an address less */ @@ -129,16 +131,31 @@ __secondary_hold: std r24,__secondary_hold_acknowledge-_stext(0) sync + li r26,0 +#ifdef CONFIG_PPC_BOOK3E + tovirt(r26,r26) +#endif /* All secondary cpus wait here until told to start. */ -100: ld r4,__secondary_hold_spinloop-_stext(0) +100: ld r4,__secondary_hold_spinloop-_stext(r26) cmpdi 0,r4,0 beq 100b #if defined(CONFIG_SMP) || defined(CONFIG_KEXEC) +#ifdef CONFIG_PPC_BOOK3E + tovirt(r4,r4) +#endif ld r4,0(r4) /* deref function descriptor */ mtctr r4 mr r3,r24 + /* + * it may be the case that other platforms have r4 right to + * begin with, this gives us some safety in case it is not + */ +#ifdef CONFIG_PPC_BOOK3E + mr r4,r25 +#else li r4,0 +#endif /* Make sure that patched code is visible */ isync bctr From a413f474a0ff29404bf1af5c024215476ed6ca01 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Mon, 3 Dec 2012 18:36:13 +0000 Subject: [PATCH 0766/4607] powerpc: Disable relocation on exceptions whenever PR KVM is active For PR KVM we allow userspace to map 0xc000000000000000. Because transitioning from userspace to the guest kernel may use the relocated exception vectors we have to disable relocation on exceptions whenever PR KVM is active as we cannot trust that address. This issue does not apply to HV KVM, since changing from a guest to the hypervisor will never use the relocated exception vectors. Currently the hypervisor interface only allows us to toggle relocation on exceptions on a partition wide scope, so we need to globally disable relocation on exceptions when the first PR KVM instance is started and only re-enable them when all PR KVM instances have been destroyed. It's a bit heavy handed, but until the hypervisor gives us a lightweight way to toggle relocation on exceptions on a single thread it's only real option. Signed-off-by: Ian Munsie Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/hvcall.h | 9 +++++++++ arch/powerpc/kvm/book3s_pr.c | 18 ++++++++++++++++++ arch/powerpc/platforms/pseries/setup.c | 8 +++++--- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h index 0975e5c0bb19..4bc2c3dad6ad 100644 --- a/arch/powerpc/include/asm/hvcall.h +++ b/arch/powerpc/include/asm/hvcall.h @@ -395,6 +395,15 @@ static inline unsigned long cmo_get_page_size(void) { return CMO_PageSize; } + +extern long pSeries_enable_reloc_on_exc(void); +extern long pSeries_disable_reloc_on_exc(void); + +#else + +#define pSeries_enable_reloc_on_exc() do {} while (0) +#define pSeries_disable_reloc_on_exc() do {} while (0) + #endif /* CONFIG_PPC_PSERIES */ #endif /* __ASSEMBLY__ */ diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 28d38adeca73..67e4708388a0 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1284,12 +1285,21 @@ void kvmppc_core_flush_memslot(struct kvm *kvm, struct kvm_memory_slot *memslot) { } +static unsigned int kvm_global_user_count = 0; +static DEFINE_SPINLOCK(kvm_global_user_count_lock); + int kvmppc_core_init_vm(struct kvm *kvm) { #ifdef CONFIG_PPC64 INIT_LIST_HEAD(&kvm->arch.spapr_tce_tables); #endif + if (firmware_has_feature(FW_FEATURE_SET_MODE)) { + spin_lock(&kvm_global_user_count_lock); + if (++kvm_global_user_count == 1) + pSeries_disable_reloc_on_exc(); + spin_unlock(&kvm_global_user_count_lock); + } return 0; } @@ -1298,6 +1308,14 @@ void kvmppc_core_destroy_vm(struct kvm *kvm) #ifdef CONFIG_PPC64 WARN_ON(!list_empty(&kvm->arch.spapr_tce_tables)); #endif + + if (firmware_has_feature(FW_FEATURE_SET_MODE)) { + spin_lock(&kvm_global_user_count_lock); + BUG_ON(kvm_global_user_count == 0); + if (--kvm_global_user_count == 0) + pSeries_enable_reloc_on_exc(); + spin_unlock(&kvm_global_user_count_lock); + } } static int kvmppc_book3s_init(void) diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index ca55882465d6..1890730354bb 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -375,7 +375,7 @@ static void pSeries_idle(void) * to ever be a problem in practice we can move this into a kernel thread to * finish off the process later in boot. */ -static int __init pSeries_enable_reloc_on_exc(void) +long pSeries_enable_reloc_on_exc(void) { long rc; unsigned int delay, total_delay = 0; @@ -397,9 +397,9 @@ static int __init pSeries_enable_reloc_on_exc(void) mdelay(delay); } } +EXPORT_SYMBOL(pSeries_enable_reloc_on_exc); -#ifdef CONFIG_KEXEC -static long pSeries_disable_reloc_on_exc(void) +long pSeries_disable_reloc_on_exc(void) { long rc; @@ -410,7 +410,9 @@ static long pSeries_disable_reloc_on_exc(void) mdelay(get_longbusy_msecs(rc)); } } +EXPORT_SYMBOL(pSeries_disable_reloc_on_exc); +#ifdef CONFIG_KEXEC static void pSeries_machine_kexec(struct kimage *image) { long rc; From 7943b310001fd7858154bc3af2a78e0dd6bfea03 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 3 Dec 2012 19:16:18 +0000 Subject: [PATCH 0767/4607] tty/hvsi: Use for_each_compatible_node() macro Use for_each_compatible_node() macro instead of open coding it. Signed-off-by: Wei Yongjun Acked-by: Grant Likely Signed-off-by: Benjamin Herrenschmidt --- drivers/tty/hvc/hvsi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 68357a6e4de9..60ecdd5ca375 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -1187,9 +1187,7 @@ static int __init hvsi_console_init(void) hvsi_wait = poll_for_state; /* no irqs yet; must poll */ /* search device tree for vty nodes */ - for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); - vty != NULL; - vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { + for_each_compatible_node(vty, "serial", "hvterm-protocol") { struct hvsi_struct *hp; const uint32_t *vtermno, *irq; From 176ebf8736ee87e3bac4b4a8cbd95a5ca18dbec7 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 3 Dec 2012 19:17:01 +0000 Subject: [PATCH 0768/4607] powerpc/celleb: Use for_each_compatible_node() macro Use for_each_compatible_node() macro instead of open coding it. Signed-off-by: Wei Yongjun Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/cell/celleb_scc_sio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/cell/celleb_scc_sio.c b/arch/powerpc/platforms/cell/celleb_scc_sio.c index 3a16c5b3c464..9c339ec646f5 100644 --- a/arch/powerpc/platforms/cell/celleb_scc_sio.c +++ b/arch/powerpc/platforms/cell/celleb_scc_sio.c @@ -42,14 +42,13 @@ static struct { static int __init txx9_serial_init(void) { extern int early_serial_txx9_setup(struct uart_port *port); - struct device_node *node = NULL; + struct device_node *node; int i; struct uart_port req; struct of_irq irq; struct resource res; - while ((node = of_find_compatible_node(node, - "serial", "toshiba,sio-scc")) != NULL) { + for_each_compatible_node(node, "serial", "toshiba,sio-scc") { for (i = 0; i < ARRAY_SIZE(txx9_scc_tab); i++) { if (!(txx9_serial_bitmap & (1< Date: Mon, 3 Dec 2012 19:20:31 +0000 Subject: [PATCH 0769/4607] powerpc/82xx: Use for_each_compatible_node() macro Use for_each_compatible_node() macro instead of open coding it. Signed-off-by: Wei Yongjun Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/82xx/pq2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/82xx/pq2.c b/arch/powerpc/platforms/82xx/pq2.c index fb94d10e5a4d..fc8b2d6a7d8d 100644 --- a/arch/powerpc/platforms/82xx/pq2.c +++ b/arch/powerpc/platforms/82xx/pq2.c @@ -71,11 +71,11 @@ err: void __init pq2_init_pci(void) { - struct device_node *np = NULL; + struct device_node *np; ppc_md.pci_exclude_device = pq2_pci_exclude_device; - while ((np = of_find_compatible_node(np, NULL, "fsl,pq2-pci"))) + for_each_compatible_node(np, NULL, "fsl,pq2-pci") pq2_pci_add_bridge(np); } #endif From fe3955cb297290b0d5dedf5d2694a7cef455010e Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 19 Dec 2012 15:14:17 +0000 Subject: [PATCH 0770/4607] powerpc: Enable ARCH_USE_BUILTIN_BSWAP By using the compiler intrinsics instead of hand-crafted opaque inline assembler for byte-swapping, we let the compiler see what's actually happening and it gets to use lwbrx/stwbrx instructions instead of a normal load/store coupled with a sequence of rlwimi instructions to move bits around. Compiled-tested only. It gave a code size reduction of almost 4% for ext2, and more like 2.5% for ext3/ext4. Signed-off-by: David Woodhouse Acked-by: H. Peter Anvin Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 17903f1f356b..684fa6456797 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -144,6 +144,7 @@ config PPC select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA select CLONE_BACKWARDS + select ARCH_USE_BUILTIN_BSWAP config EARLY_PRINTK bool From 15fab56ecae2cba7e9f6663979bb160c11a9c506 Mon Sep 17 00:00:00 2001 From: Chris Freehill Date: Wed, 5 Dec 2012 12:32:46 +0000 Subject: [PATCH 0771/4607] powerpc/perf: Add stalled-cycles events Support for stalled-cycles-frontend and stalled-cycles-backend is added for e500-based processors. The following mappings are used: stalled-cycles-frontend or idle-cycles-frontend: Com:18 Cycles decode stalled stalled-cycles-backend or idle-cycles-backend Com:19 cycles issue stalled Signed-off-by: Chris Freehill Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/perf/e500-pmu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/perf/e500-pmu.c b/arch/powerpc/perf/e500-pmu.c index cb2e2949c8d1..fb664929f5da 100644 --- a/arch/powerpc/perf/e500-pmu.c +++ b/arch/powerpc/perf/e500-pmu.c @@ -24,6 +24,8 @@ static int e500_generic_events[] = { [PERF_COUNT_HW_CACHE_MISSES] = 41, /* Data L1 cache reloads */ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 12, [PERF_COUNT_HW_BRANCH_MISSES] = 15, + [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 18, + [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 19, }; #define C(x) PERF_COUNT_HW_CACHE_##x From 5d75b26443aff37a7bd356f9dbd6d6e11ec122aa Mon Sep 17 00:00:00 2001 From: Haren Myneni Date: Thu, 6 Dec 2012 21:46:37 +0000 Subject: [PATCH 0772/4607] powerpc: Move branch instruction from ACCOUNT_CPU_USER_ENTRY to caller [PATCH 1/6] powerpc: Move branch instruction from ACCOUNT_CPU_USER_ENTRY to caller The first instruction in ACCOUNT_CPU_USER_ENTRY is 'beq' which checks for exceptions coming from kernel mode. PPR value will be saved immediately after ACCOUNT_CPU_USER_ENTRY and is also for user level exceptions. So moved this branch instruction in the caller code. Signed-off-by: Haren Myneni Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/exception-64s.h | 3 ++- arch/powerpc/include/asm/ppc_asm.h | 2 -- arch/powerpc/kernel/entry_64.S | 3 ++- arch/powerpc/kernel/exceptions-64e.S | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index b1edd801d314..49b7faf7bbf2 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -224,8 +224,9 @@ do_kvm_##n: \ std r10,0(r1); /* make stack chain pointer */ \ std r0,GPR0(r1); /* save r0 in stackframe */ \ std r10,GPR1(r1); /* save r1 in stackframe */ \ + beq 4f; /* if from kernel mode */ \ ACCOUNT_CPU_USER_ENTRY(r9, r10); \ - std r2,GPR2(r1); /* save r2 in stackframe */ \ +4: std r2,GPR2(r1); /* save r2 in stackframe */ \ SAVE_4GPRS(3, r1); /* save r3 - r6 in stackframe */ \ SAVE_2GPRS(7, r1); /* save r7, r8 in stackframe */ \ ld r9,area+EX_R9(r13); /* move r9, r10 to stackframe */ \ diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index ea2a86e8ff95..376e36d707f9 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -30,7 +30,6 @@ #define ACCOUNT_STOLEN_TIME #else #define ACCOUNT_CPU_USER_ENTRY(ra, rb) \ - beq 2f; /* if from kernel mode */ \ MFTB(ra); /* get timebase */ \ ld rb,PACA_STARTTIME_USER(r13); \ std ra,PACA_STARTTIME(r13); \ @@ -38,7 +37,6 @@ ld ra,PACA_USER_TIME(r13); \ add ra,ra,rb; /* add on to user time */ \ std ra,PACA_USER_TIME(r13); \ -2: #define ACCOUNT_CPU_USER_EXIT(ra, rb) \ MFTB(ra); /* get timebase */ \ diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 73c1f083ba21..7a70d0a6df3e 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -62,8 +62,9 @@ system_call_common: std r12,_MSR(r1) std r0,GPR0(r1) std r10,GPR1(r1) + beq 2f /* if from kernel mode */ ACCOUNT_CPU_USER_ENTRY(r10, r11) - std r2,GPR2(r1) +2: std r2,GPR2(r1) std r3,GPR3(r1) mfcr r2 std r4,GPR4(r1) diff --git a/arch/powerpc/kernel/exceptions-64e.S b/arch/powerpc/kernel/exceptions-64e.S index 4684e33a26c3..ae54553eacd9 100644 --- a/arch/powerpc/kernel/exceptions-64e.S +++ b/arch/powerpc/kernel/exceptions-64e.S @@ -159,8 +159,9 @@ exc_##n##_common: \ std r9,GPR9(r1); /* save r9 in stackframe */ \ std r10,_NIP(r1); /* save SRR0 to stackframe */ \ std r11,_MSR(r1); /* save SRR1 to stackframe */ \ + beq 2f; /* if from kernel mode */ \ ACCOUNT_CPU_USER_ENTRY(r10,r11);/* accounting (uses cr0+eq) */ \ - ld r3,excf+EX_R10(r13); /* get back r10 */ \ +2: ld r3,excf+EX_R10(r13); /* get back r10 */ \ ld r4,excf+EX_R11(r13); /* get back r11 */ \ mfspr r5,SPRN_SPRG_GEN_SCRATCH;/* get back r13 */ \ std r12,GPR12(r1); /* save r12 in stackframe */ \ From d261386852178ce0bdf836f7c92267f21c8bb3f9 Mon Sep 17 00:00:00 2001 From: Haren Myneni Date: Thu, 6 Dec 2012 21:47:42 +0000 Subject: [PATCH 0773/4607] powerpc: Enable PPR save/restore [PATCH 2/6] powerpc: Enable PPR save/restore SMT thread status register (PPR) is used to set thread priority. This patch enables PPR save/restore feature (CPU_FTR_HAS_PPR) on POWER7 and POWER8 systems. Signed-off-by: Haren Myneni Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/cputable.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index fc4d2c5b6522..8dbf8bbbc624 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -171,6 +171,7 @@ extern const char *powerpc_base_platform; #define CPU_FTR_POPCNTD LONG_ASM_CONST(0x0800000000000000) #define CPU_FTR_ICSWX LONG_ASM_CONST(0x1000000000000000) #define CPU_FTR_VMX_COPY LONG_ASM_CONST(0x2000000000000000) +#define CPU_FTR_HAS_PPR LONG_ASM_CONST(0x4000000000000000) #ifndef __ASSEMBLY__ @@ -400,7 +401,8 @@ extern const char *powerpc_base_platform; CPU_FTR_PURR | CPU_FTR_SPURR | CPU_FTR_REAL_LE | \ CPU_FTR_DSCR | CPU_FTR_SAO | CPU_FTR_ASYM_SMT | \ CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ - CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY) + CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | \ + CPU_FTR_VMX_COPY | CPU_FTR_HAS_PPR) #define CPU_FTRS_POWER8 (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | CPU_FTR_ARCH_206 |\ CPU_FTR_MMCRA | CPU_FTR_SMT | \ @@ -409,7 +411,7 @@ extern const char *powerpc_base_platform; CPU_FTR_DSCR | CPU_FTR_SAO | \ CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ - CPU_FTR_DBELL) + CPU_FTR_DBELL | CPU_FTR_HAS_PPR) #define CPU_FTRS_CELL (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ From a09688cd23d477ebd9c8f57881bfe907240cb0a1 Mon Sep 17 00:00:00 2001 From: Haren Myneni Date: Thu, 6 Dec 2012 21:48:26 +0000 Subject: [PATCH 0774/4607] powerpc: Increase exceptions arrays in paca struct to save PPR [PATCH 3/6] powerpc: Increase exceptions arrays in paca struct to save PPR Using paca to save user defined PPR value in the first level exception vector. Signed-off-by: Haren Myneni Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/exception-64s.h | 1 + arch/powerpc/include/asm/paca.h | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 49b7faf7bbf2..ed3cdcb50198 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -47,6 +47,7 @@ #define EX_R3 64 #define EX_LR 72 #define EX_CFAR 80 +#define EX_PPR 88 /* SMT thread status register (priority) */ #ifdef CONFIG_RELOCATABLE #define EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) \ diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h index e9e7a6999bb8..c47d687ab01b 100644 --- a/arch/powerpc/include/asm/paca.h +++ b/arch/powerpc/include/asm/paca.h @@ -93,9 +93,9 @@ struct paca_struct { * Now, starting in cacheline 2, the exception save areas */ /* used for most interrupts/exceptions */ - u64 exgen[11] __attribute__((aligned(0x80))); - u64 exmc[11]; /* used for machine checks */ - u64 exslb[11]; /* used for SLB/segment table misses + u64 exgen[12] __attribute__((aligned(0x80))); + u64 exmc[12]; /* used for machine checks */ + u64 exslb[12]; /* used for SLB/segment table misses * on the linear mapping */ /* SLB related definitions */ u16 vmalloc_sllp; From 92779245599bb3d7fb48066b11c4bfd6aa477198 Mon Sep 17 00:00:00 2001 From: Haren Myneni Date: Thu, 6 Dec 2012 21:49:56 +0000 Subject: [PATCH 0775/4607] powerpc: Define ppr in thread_struct [PATCH 4/6] powerpc: Define ppr in thread_struct ppr in thread_struct is used to save PPR and restore it before process exits from kernel. This patch sets the default priority to 3 when tasks are created such that users can use 4 for higher priority tasks. Signed-off-by: Haren Myneni Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/processor.h | 12 ++++++++++++ arch/powerpc/kernel/asm-offsets.c | 1 + arch/powerpc/kernel/process.c | 2 ++ 3 files changed, 15 insertions(+) diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 87502046c0dc..37f87f069cbf 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -18,6 +18,16 @@ #define TS_FPRWIDTH 1 #endif +#ifdef CONFIG_PPC64 +/* Default SMT priority is set to 3. Use 11- 13bits to save priority. */ +#define PPR_PRIORITY 3 +#ifdef __ASSEMBLY__ +#define INIT_PPR (PPR_PRIORITY << 50) +#else +#define INIT_PPR ((u64)PPR_PRIORITY << 50) +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_PPC64 */ + #ifndef __ASSEMBLY__ #include #include @@ -245,6 +255,7 @@ struct thread_struct { #ifdef CONFIG_PPC64 unsigned long dscr; int dscr_inherit; + unsigned long ppr; /* used to save/restore SMT priority */ #endif }; @@ -278,6 +289,7 @@ struct thread_struct { .fpr = {{0}}, \ .fpscr = { .val = 0, }, \ .fpexc_mode = 0, \ + .ppr = INIT_PPR, \ } #endif diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 4e23ba2f3ca7..e39ca556e87f 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -77,6 +77,7 @@ int main(void) DEFINE(NMI_MASK, NMI_MASK); DEFINE(THREAD_DSCR, offsetof(struct thread_struct, dscr)); DEFINE(THREAD_DSCR_INHERIT, offsetof(struct thread_struct, dscr_inherit)); + DEFINE(TASKTHREADPPR, offsetof(struct task_struct, thread.ppr)); #else DEFINE(THREAD_INFO, offsetof(struct task_struct, stack)); #endif /* CONFIG_PPC64 */ diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 81430674e71c..3065d17f3606 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -813,6 +813,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, p->thread.dscr_inherit = current->thread.dscr_inherit; p->thread.dscr = current->thread.dscr; } + if (cpu_has_feature(CPU_FTR_HAS_PPR)) + p->thread.ppr = INIT_PPR; #endif /* * The PPC64 ABI makes use of a TOC to contain function From 13e7a8e846c2ea38a552b986ea49332f965bbb7a Mon Sep 17 00:00:00 2001 From: Haren Myneni Date: Thu, 6 Dec 2012 21:50:32 +0000 Subject: [PATCH 0776/4607] powerpc: Macros for saving/restore PPR [PATCH 5/6] powerpc: Macros for saving/restore PPR Several macros are defined for saving and restore user defined PPR value. Signed-off-by: Haren Myneni Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/exception-64s.h | 37 ++++++++++++++++++++++++ arch/powerpc/include/asm/ppc_asm.h | 25 ++++++++++++++++ arch/powerpc/include/asm/reg.h | 1 + 3 files changed, 63 insertions(+) diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index ed3cdcb50198..391e01387248 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -108,6 +108,43 @@ #define RESTORE_LR(reg, area) #endif +/* + * PPR save/restore macros used in exceptions_64s.S + * Used for P7 or later processors + */ +#define SAVE_PPR(area, ra, rb) \ +BEGIN_FTR_SECTION_NESTED(940) \ + ld ra,PACACURRENT(r13); \ + ld rb,area+EX_PPR(r13); /* Read PPR from paca */ \ + std rb,TASKTHREADPPR(ra); \ +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,940) + +#define RESTORE_PPR_PACA(area, ra) \ +BEGIN_FTR_SECTION_NESTED(941) \ + ld ra,area+EX_PPR(r13); \ + mtspr SPRN_PPR,ra; \ +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,941) + +/* + * Increase the priority on systems where PPR save/restore is not + * implemented/ supported. + */ +#define HMT_MEDIUM_PPR_DISCARD \ +BEGIN_FTR_SECTION_NESTED(942) \ + HMT_MEDIUM; \ +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,0,942) /*non P7*/ + +/* + * Save PPR in paca whenever some register is available to use. + * Then increase the priority. + */ +#define HMT_MEDIUM_PPR_SAVE(area, ra) \ +BEGIN_FTR_SECTION_NESTED(943) \ + mfspr ra,SPRN_PPR; \ + std ra,area+EX_PPR(r13); \ + HMT_MEDIUM; \ +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,943) + #define __EXCEPTION_PROLOG_1(area, extra, vec) \ GET_PACA(r13); \ std r9,area+EX_R9(r13); /* save r9 - r12 */ \ diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index 376e36d707f9..c2d0e58aba31 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -389,6 +389,31 @@ END_FTR_SECTION_IFCLR(CPU_FTR_601) FTR_SECTION_ELSE_NESTED(848); \ mtocrf (FXM), RS; \ ALT_FTR_SECTION_END_NESTED_IFCLR(CPU_FTR_NOEXECUTE, 848) + +/* + * PPR restore macros used in entry_64.S + * Used for P7 or later processors + */ +#define HMT_MEDIUM_LOW_HAS_PPR \ +BEGIN_FTR_SECTION_NESTED(944) \ + HMT_MEDIUM_LOW; \ +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,944) + +#define SET_DEFAULT_THREAD_PPR(ra, rb) \ +BEGIN_FTR_SECTION_NESTED(945) \ + lis ra,INIT_PPR@highest; /* default ppr=3 */ \ + ld rb,PACACURRENT(r13); \ + sldi ra,ra,32; /* 11- 13 bits are used for ppr */ \ + std ra,TASKTHREADPPR(rb); \ +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,945) + +#define RESTORE_PPR(ra, rb) \ +BEGIN_FTR_SECTION_NESTED(946) \ + ld ra,PACACURRENT(r13); \ + ld rb,TASKTHREADPPR(ra); \ + mtspr SPRN_PPR,rb; /* Restore PPR */ \ +END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,946) + #endif /* diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index af88486b0c23..1bdba54839b4 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -289,6 +289,7 @@ #define SPRN_DBAT6U 0x23C /* Data BAT 6 Upper Register */ #define SPRN_DBAT7L 0x23F /* Data BAT 7 Lower Register */ #define SPRN_DBAT7U 0x23E /* Data BAT 7 Upper Register */ +#define SPRN_PPR 0x380 /* SMT Thread status Register */ #define SPRN_DEC 0x016 /* Decrement Register */ #define SPRN_DER 0x095 /* Debug Enable Regsiter */ From 44e9309f1f357794b7ae93d5f3e3e6f11d2b8a7f Mon Sep 17 00:00:00 2001 From: Haren Myneni Date: Thu, 6 Dec 2012 21:51:04 +0000 Subject: [PATCH 0777/4607] powerpc: Implement PPR save/restore [PATCH 6/6] powerpc: Implement PPR save/restore When the task enters in to kernel space, the user defined priority (PPR) will be saved in to PACA at the beginning of first level exception vector and then copy from PACA to thread_info in second level vector. PPR will be restored from thread_info before exits the kernel space. P7/P8 temporarily raises the thread priority to higher level during exception until the program executes HMT_* calls. But it will not modify PPR register. So we save PPR value whenever some register is available to use and then calls HMT_MEDIUM to increase the priority. This feature supports on P7 or later processors. We save/ restore PPR for all exception vectors except system call entry. GLIBC will be saving / restore for system calls. So the default PPR value (3) will be set for the system call exit when the task returned to the user space. Signed-off-by: Haren Myneni Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/exception-64s.h | 18 ++++++++++-------- arch/powerpc/kernel/entry_64.S | 3 +++ arch/powerpc/kernel/exceptions-64s.S | 23 +++++++++++++---------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 391e01387248..370298a0bca3 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -147,8 +147,9 @@ END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,943) #define __EXCEPTION_PROLOG_1(area, extra, vec) \ GET_PACA(r13); \ - std r9,area+EX_R9(r13); /* save r9 - r12 */ \ - std r10,area+EX_R10(r13); \ + std r9,area+EX_R9(r13); /* save r9 */ \ + HMT_MEDIUM_PPR_SAVE(area, r9); \ + std r10,area+EX_R10(r13); /* save r10 - r12 */ \ BEGIN_FTR_SECTION_NESTED(66); \ mfspr r10,SPRN_CFAR; \ std r10,area+EX_CFAR(r13); \ @@ -264,6 +265,7 @@ do_kvm_##n: \ std r10,GPR1(r1); /* save r1 in stackframe */ \ beq 4f; /* if from kernel mode */ \ ACCOUNT_CPU_USER_ENTRY(r9, r10); \ + SAVE_PPR(area, r9, r10); \ 4: std r2,GPR2(r1); /* save r2 in stackframe */ \ SAVE_4GPRS(3, r1); /* save r3 - r6 in stackframe */ \ SAVE_2GPRS(7, r1); /* save r7, r8 in stackframe */ \ @@ -305,7 +307,7 @@ do_kvm_##n: \ . = loc; \ .globl label##_pSeries; \ label##_pSeries: \ - HMT_MEDIUM; \ + HMT_MEDIUM_PPR_DISCARD; \ SET_SCRATCH0(r13); /* save r13 */ \ EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ EXC_STD, KVMTEST_PR, vec) @@ -314,7 +316,7 @@ label##_pSeries: \ . = loc; \ .globl label##_hv; \ label##_hv: \ - HMT_MEDIUM; \ + HMT_MEDIUM_PPR_DISCARD; \ SET_SCRATCH0(r13); /* save r13 */ \ EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ EXC_HV, KVMTEST, vec) @@ -323,7 +325,7 @@ label##_hv: \ . = loc; \ .globl label##_relon_pSeries; \ label##_relon_pSeries: \ - HMT_MEDIUM; \ + HMT_MEDIUM_PPR_DISCARD; \ /* No guest interrupts come through here */ \ SET_SCRATCH0(r13); /* save r13 */ \ EXCEPTION_RELON_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ @@ -333,7 +335,7 @@ label##_relon_pSeries: \ . = loc; \ .globl label##_relon_hv; \ label##_relon_hv: \ - HMT_MEDIUM; \ + HMT_MEDIUM_PPR_DISCARD; \ /* No guest interrupts come through here */ \ SET_SCRATCH0(r13); /* save r13 */ \ EXCEPTION_RELON_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ @@ -371,7 +373,7 @@ label##_relon_hv: \ #define SOFTEN_NOTEST_HV(vec) _SOFTEN_TEST(EXC_HV, vec) #define __MASKABLE_EXCEPTION_PSERIES(vec, label, h, extra) \ - HMT_MEDIUM; \ + HMT_MEDIUM_PPR_DISCARD; \ SET_SCRATCH0(r13); /* save r13 */ \ __EXCEPTION_PROLOG_1(PACA_EXGEN, extra, vec); \ EXCEPTION_PROLOG_PSERIES_1(label##_common, h); @@ -393,7 +395,7 @@ label##_hv: \ EXC_HV, SOFTEN_TEST_HV) #define __MASKABLE_RELON_EXCEPTION_PSERIES(vec, label, h, extra) \ - HMT_MEDIUM; \ + HMT_MEDIUM_PPR_DISCARD; \ SET_SCRATCH0(r13); /* save r13 */ \ __EXCEPTION_PROLOG_1(PACA_EXGEN, extra, vec); \ EXCEPTION_RELON_PROLOG_PSERIES_1(label##_common, h); diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 7a70d0a6df3e..6faf51e165a5 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -227,6 +227,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_STCX_CHECKS_ADDRESS) beq- 1f ACCOUNT_CPU_USER_EXIT(r11, r12) + HMT_MEDIUM_LOW_HAS_PPR ld r13,GPR13(r1) /* only restore r13 if returning to usermode */ 1: ld r2,GPR2(r1) ld r1,GPR1(r1) @@ -303,6 +304,7 @@ syscall_exit_work: subi r12,r12,TI_FLAGS 4: /* Anything else left to do? */ + SET_DEFAULT_THREAD_PPR(r3, r9) /* Set thread.ppr = 3 */ andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SINGLESTEP) beq .ret_from_except_lite @@ -758,6 +760,7 @@ fast_exception_return: andi. r0,r3,MSR_PR beq 1f ACCOUNT_CPU_USER_EXIT(r2, r4) + RESTORE_PPR(r2, r4) REST_GPR(13, r1) 1: mtspr SPRN_SRR1,r3 diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 32fc04f78890..3425aba8da51 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -104,7 +104,7 @@ __start_interrupts: .globl system_reset_pSeries; system_reset_pSeries: - HMT_MEDIUM; + HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) #ifdef CONFIG_PPC_P7_NAP BEGIN_FTR_SECTION @@ -158,7 +158,7 @@ machine_check_pSeries_1: . = 0x300 .globl data_access_pSeries data_access_pSeries: - HMT_MEDIUM + HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) BEGIN_FTR_SECTION b data_access_check_stab @@ -170,7 +170,7 @@ END_MMU_FTR_SECTION_IFCLR(MMU_FTR_SLB) . = 0x380 .globl data_access_slb_pSeries data_access_slb_pSeries: - HMT_MEDIUM + HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) EXCEPTION_PROLOG_1(PACA_EXSLB, KVMTEST, 0x380) std r3,PACA_EXSLB+EX_R3(r13) @@ -201,7 +201,7 @@ data_access_slb_pSeries: . = 0x480 .globl instruction_access_slb_pSeries instruction_access_slb_pSeries: - HMT_MEDIUM + HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) EXCEPTION_PROLOG_1(PACA_EXSLB, KVMTEST_PR, 0x480) std r3,PACA_EXSLB+EX_R3(r13) @@ -324,10 +324,11 @@ vsx_unavailable_pSeries_1: . = 0x1500 .global denorm_exception_hv denorm_exception_hv: - HMT_MEDIUM + HMT_MEDIUM_PPR_DISCARD mtspr SPRN_SPRG_HSCRATCH0,r13 mfspr r13,SPRN_SPRG_HPACA std r9,PACA_EXGEN+EX_R9(r13) + HMT_MEDIUM_PPR_SAVE(PACA_EXGEN, r9) std r10,PACA_EXGEN+EX_R10(r13) std r11,PACA_EXGEN+EX_R11(r13) std r12,PACA_EXGEN+EX_R12(r13) @@ -369,7 +370,7 @@ denorm_exception_hv: machine_check_pSeries: .globl machine_check_fwnmi machine_check_fwnmi: - HMT_MEDIUM + HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) /* save r13 */ EXCEPTION_PROLOG_PSERIES(PACA_EXMC, machine_check_common, EXC_STD, KVMTEST, 0x200) @@ -498,6 +499,7 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_206) mtspr SPRN_HSRR0,r11 mtcrf 0x80,r9 ld r9,PACA_EXGEN+EX_R9(r13) + RESTORE_PPR_PACA(PACA_EXGEN, r10) ld r10,PACA_EXGEN+EX_R10(r13) ld r11,PACA_EXGEN+EX_R11(r13) ld r12,PACA_EXGEN+EX_R12(r13) @@ -603,7 +605,7 @@ ALT_FTR_SECTION_END_IFSET(CPU_FTR_HVMODE) .globl system_reset_fwnmi .align 7 system_reset_fwnmi: - HMT_MEDIUM + HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) /* save r13 */ EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, system_reset_common, EXC_STD, NOTEST, 0x100) @@ -716,7 +718,7 @@ machine_check_common: . = 0x4380 .globl data_access_slb_relon_pSeries data_access_slb_relon_pSeries: - HMT_MEDIUM + HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) EXCEPTION_PROLOG_1(PACA_EXSLB, NOTEST, 0x380) std r3,PACA_EXSLB+EX_R3(r13) @@ -741,7 +743,7 @@ data_access_slb_relon_pSeries: . = 0x4480 .globl instruction_access_slb_relon_pSeries instruction_access_slb_relon_pSeries: - HMT_MEDIUM + HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) EXCEPTION_PROLOG_1(PACA_EXSLB, NOTEST, 0x480) std r3,PACA_EXSLB+EX_R3(r13) @@ -1062,6 +1064,7 @@ _GLOBAL(slb_miss_realmode) mtcrf 0x01,r9 /* slb_allocate uses cr0 and cr7 */ .machine pop + RESTORE_PPR_PACA(PACA_EXSLB, r9) ld r9,PACA_EXSLB+EX_R9(r13) ld r10,PACA_EXSLB+EX_R10(r13) ld r11,PACA_EXSLB+EX_R11(r13) @@ -1411,7 +1414,7 @@ initial_stab: #ifdef CONFIG_PPC_POWERNV _GLOBAL(opal_mc_secondary_handler) - HMT_MEDIUM + HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) GET_PACA(r13) clrldi r3,r3,2 From 03743716316eecddd32ae3314fc6b4dd15e6eec9 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 12 Dec 2012 08:19:00 +0000 Subject: [PATCH 0778/4607] powerpc: Convert print_symbol to %pSR Use the new vsprintf extension to avoid any possible message interleaving. Convert the #ifdef DEBUG block to a single pr_debug. Signed-off-by: Joe Perches Acked-by: Arnd Bergmann Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/cell/spu_callbacks.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/platforms/cell/spu_callbacks.c b/arch/powerpc/platforms/cell/spu_callbacks.c index 75d613313f10..b0ec78e8ad68 100644 --- a/arch/powerpc/platforms/cell/spu_callbacks.c +++ b/arch/powerpc/platforms/cell/spu_callbacks.c @@ -60,13 +60,12 @@ long spu_sys_callback(struct spu_syscall_block *s) syscall = spu_syscall_table[s->nr_ret]; -#ifdef DEBUG - print_symbol(KERN_DEBUG "SPU-syscall %s:", (unsigned long)syscall); - printk("syscall%ld(%lx, %lx, %lx, %lx, %lx, %lx)\n", - s->nr_ret, - s->parm[0], s->parm[1], s->parm[2], - s->parm[3], s->parm[4], s->parm[5]); -#endif + pr_debug("SPU-syscall " + "%pSR:syscall%lld(%llx, %llx, %llx, %llx, %llx, %llx)\n", + syscall, + s->nr_ret, + s->parm[0], s->parm[1], s->parm[2], + s->parm[3], s->parm[4], s->parm[5]); return syscall(s->parm[0], s->parm[1], s->parm[2], s->parm[3], s->parm[4], s->parm[5]); From e6fde82224fdcffad5d2575783167cc108240a57 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 12 Dec 2012 14:32:40 +0000 Subject: [PATCH 0779/4607] powerpc: Run savedefconfig over pseries, ppc64 and ppc64e defconfig No changes, just update the configs with savedefconfig. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/configs/ppc64_defconfig | 129 ++++++++----------------- arch/powerpc/configs/ppc64e_defconfig | 79 +++++---------- arch/powerpc/configs/pseries_defconfig | 106 +++++++------------- 3 files changed, 100 insertions(+), 214 deletions(-) diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 6d03530b7506..724f35486f17 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -5,6 +5,9 @@ CONFIG_SMP=y CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y +CONFIG_IRQ_DOMAIN_DEBUG=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y CONFIG_TASKSTATS=y CONFIG_TASK_DELAY_ACCT=y CONFIG_IKCONFIG=y @@ -21,6 +24,7 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_PARTITION_ADVANCED=y CONFIG_PPC_SPLPAR=y CONFIG_SCANLOG=m CONFIG_PPC_SMLPAR=y @@ -42,11 +46,8 @@ CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_PMAC64=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y CONFIG_HZ_100=y CONFIG_BINFMT_MISC=m -CONFIG_HOTPLUG_CPU=y CONFIG_KEXEC=y CONFIG_IRQ_ALL_CPUS=y CONFIG_MEMORY_HOTREMOVE=y @@ -73,7 +74,6 @@ CONFIG_INET_ESP=m CONFIG_INET_IPCOMP=m # CONFIG_IPV6 is not set CONFIG_NETFILTER=y -CONFIG_NETFILTER_NETLINK_QUEUE=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_EVENTS=y CONFIG_NF_CT_PROTO_SCTP=m @@ -130,19 +130,12 @@ CONFIG_NETFILTER_XT_MATCH_U32=m CONFIG_NF_CONNTRACK_IPV4=m CONFIG_IP_NF_QUEUE=m CONFIG_IP_NF_IPTABLES=m -CONFIG_IP_NF_MATCH_ADDRTYPE=m CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m CONFIG_IP_NF_MATCH_TTL=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IP_NF_TARGET_LOG=m CONFIG_IP_NF_TARGET_ULOG=m -CONFIG_NF_NAT=m -CONFIG_IP_NF_TARGET_MASQUERADE=m -CONFIG_IP_NF_TARGET_NETMAP=m -CONFIG_IP_NF_TARGET_REDIRECT=m -CONFIG_NF_NAT_SNMP_BASIC=m CONFIG_IP_NF_MANGLE=m CONFIG_IP_NF_TARGET_CLUSTERIP=m CONFIG_IP_NF_TARGET_ECN=m @@ -151,6 +144,7 @@ CONFIG_IP_NF_RAW=m CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_BPF_JIT=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_FD=y @@ -173,7 +167,6 @@ CONFIG_CHR_DEV_SG=y CONFIG_SCSI_MULTI_LUN=y CONFIG_SCSI_CONSTANTS=y CONFIG_SCSI_FC_ATTRS=y -CONFIG_SCSI_SAS_ATTRS=m CONFIG_SCSI_CXGB3_ISCSI=m CONFIG_SCSI_CXGB4_ISCSI=m CONFIG_SCSI_BNX2_ISCSI=m @@ -205,13 +198,6 @@ CONFIG_DM_SNAPSHOT=m CONFIG_DM_MIRROR=m CONFIG_DM_ZERO=m CONFIG_DM_MULTIPATH=m -CONFIG_IEEE1394=y -CONFIG_IEEE1394_OHCI1394=y -CONFIG_IEEE1394_SBP2=m -CONFIG_IEEE1394_ETH1394=m -CONFIG_IEEE1394_RAWIO=y -CONFIG_IEEE1394_VIDEO1394=m -CONFIG_IEEE1394_DV1394=m CONFIG_ADB_PMU=y CONFIG_PMAC_SMU=y CONFIG_THERM_PM72=y @@ -220,50 +206,43 @@ CONFIG_WINDFARM_PM81=y CONFIG_WINDFARM_PM91=y CONFIG_WINDFARM_PM112=y CONFIG_WINDFARM_PM121=y -CONFIG_NETDEVICES=y -CONFIG_DUMMY=m CONFIG_BONDING=m -CONFIG_TUN=m -CONFIG_MARVELL_PHY=y -CONFIG_BROADCOM_PHY=m -CONFIG_NET_ETHERNET=y -CONFIG_SUNGEM=y -CONFIG_NET_VENDOR_3COM=y -CONFIG_VORTEX=y -CONFIG_IBMVETH=m -CONFIG_NET_PCI=y -CONFIG_PCNET32=y -CONFIG_E100=y -CONFIG_ACENIC=m -CONFIG_ACENIC_OMIT_TIGON_I=y -CONFIG_E1000=y -CONFIG_E1000E=y -CONFIG_TIGON3=y -CONFIG_BNX2=m -CONFIG_SPIDER_NET=m -CONFIG_GELIC_NET=m -CONFIG_GELIC_WIRELESS=y -CONFIG_CHELSIO_T1=m -CONFIG_CHELSIO_T3=m -CONFIG_CHELSIO_T4=m -CONFIG_EHEA=m -CONFIG_IXGBE=m -CONFIG_IXGB=m -CONFIG_S2IO=m -CONFIG_MYRI10GE=m -CONFIG_NETXEN_NIC=m -CONFIG_PASEMI_MAC=y -CONFIG_MLX4_EN=m -CONFIG_QLGE=m -CONFIG_BE2NET=m -CONFIG_PPP=m -CONFIG_PPP_ASYNC=m -CONFIG_PPP_SYNC_TTY=m -CONFIG_PPP_DEFLATE=m -CONFIG_PPP_BSDCOMP=m -CONFIG_PPPOE=m +CONFIG_DUMMY=m CONFIG_NETCONSOLE=y CONFIG_NETPOLL_TRAP=y +CONFIG_TUN=m +CONFIG_VORTEX=y +CONFIG_ACENIC=m +CONFIG_ACENIC_OMIT_TIGON_I=y +CONFIG_PCNET32=y +CONFIG_TIGON3=y +CONFIG_CHELSIO_T1=m +CONFIG_BE2NET=m +CONFIG_S2IO=m +CONFIG_IBMVETH=m +CONFIG_EHEA=m +CONFIG_E100=y +CONFIG_E1000=y +CONFIG_E1000E=y +CONFIG_IXGB=m +CONFIG_IXGBE=m +CONFIG_MLX4_EN=m +CONFIG_MYRI10GE=m +CONFIG_PASEMI_MAC=y +CONFIG_QLGE=m +CONFIG_NETXEN_NIC=m +CONFIG_SUNGEM=y +CONFIG_GELIC_NET=m +CONFIG_GELIC_WIRELESS=y +CONFIG_SPIDER_NET=m +CONFIG_MARVELL_PHY=y +CONFIG_BROADCOM_PHY=m +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPPOE=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m # CONFIG_INPUT_MOUSEDEV_PSAUX is not set CONFIG_INPUT_EVDEV=m CONFIG_INPUT_MISC=y @@ -279,13 +258,10 @@ CONFIG_HVC_RTAS=y CONFIG_HVC_BEAT=y CONFIG_HVCS=m CONFIG_IBM_BSR=m -CONFIG_HW_RANDOM=m -CONFIG_HW_RANDOM_PSERIES=m CONFIG_RAW_DRIVER=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_AMD8111=y CONFIG_I2C_PASEMI=y -# CONFIG_HWMON is not set CONFIG_VIDEO_OUTPUT_CONTROL=m CONFIG_FB=y CONFIG_FIRMWARE_EDID=y @@ -300,7 +276,6 @@ CONFIG_FB_RADEON=y CONFIG_FB_IBM_GXT4500=y CONFIG_FB_PS3=m CONFIG_LCD_CLASS_DEVICE=y -CONFIG_DISPLAY_SUPPORT=y # CONFIG_VGA_CONSOLE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y @@ -317,18 +292,16 @@ CONFIG_SND_AOA_FABRIC_LAYOUT=m CONFIG_SND_AOA_ONYX=m CONFIG_SND_AOA_TAS=m CONFIG_SND_AOA_TOONIE=m -CONFIG_USB_HIDDEV=y CONFIG_HID_GYRATION=y CONFIG_HID_PANTHERLORD=y CONFIG_HID_PETALYNX=y CONFIG_HID_SAMSUNG=y CONFIG_HID_SONY=y CONFIG_HID_SUNPLUS=y +CONFIG_USB_HIDDEV=y CONFIG_USB=y -CONFIG_USB_DEVICEFS=y CONFIG_USB_MON=m CONFIG_USB_EHCI_HCD=y -CONFIG_USB_EHCI_TT_NEWSCHED=y # CONFIG_USB_EHCI_HCD_PPC_OF is not set CONFIG_USB_OHCI_HCD=y CONFIG_USB_STORAGE=m @@ -370,11 +343,9 @@ CONFIG_JFS_POSIX_ACL=y CONFIG_JFS_SECURITY=y CONFIG_XFS_FS=m CONFIG_XFS_POSIX_ACL=y -CONFIG_OCFS2_FS=m CONFIG_BTRFS_FS=m CONFIG_BTRFS_FS_POSIX_ACL=y CONFIG_NILFS2_FS=m -CONFIG_INOTIFY=y CONFIG_AUTOFS4_FS=m CONFIG_FUSE_FS=m CONFIG_ISO9660_FS=y @@ -389,22 +360,18 @@ CONFIG_HFSPLUS_FS=m CONFIG_CRAMFS=m CONFIG_SQUASHFS=m CONFIG_SQUASHFS_XATTR=y -CONFIG_SQUASHFS_ZLIB=y CONFIG_SQUASHFS_LZO=y CONFIG_SQUASHFS_XZ=y CONFIG_NFS_FS=y -CONFIG_NFS_V3=y CONFIG_NFS_V3_ACL=y CONFIG_NFS_V4=y CONFIG_ROOT_NFS=y CONFIG_NFSD=m CONFIG_NFSD_V3_ACL=y CONFIG_NFSD_V4=y -CONFIG_RPCSEC_GSS_SPKM3=m CONFIG_CIFS=m CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y -CONFIG_PARTITION_ADVANCED=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_CODEPAGE_737=m CONFIG_NLS_CODEPAGE_775=m @@ -446,37 +413,25 @@ CONFIG_CRC_T10DIF=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y CONFIG_LOCKUP_DETECTOR=y -CONFIG_DETECT_HUNG_TASK=y CONFIG_DEBUG_MUTEXES=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set +CONFIG_DEBUG_STACK_USAGE=y CONFIG_LATENCYTOP=y -CONFIG_SYSCTL_SYSCALL_CHECK=y CONFIG_SCHED_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_DEBUG_STACKOVERFLOW=y -CONFIG_DEBUG_STACK_USAGE=y CONFIG_CODE_PATCHING_SELFTEST=y CONFIG_FTR_FIXUP_SELFTEST=y CONFIG_MSI_BITMAP_SELFTEST=y CONFIG_XMON=y -CONFIG_IRQ_DOMAIN_DEBUG=y CONFIG_BOOTX_TEXT=y CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_TEST=m -CONFIG_CRYPTO_CCM=m -CONFIG_CRYPTO_GCM=m -CONFIG_CRYPTO_ECB=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m -CONFIG_CRYPTO_SHA256=m -CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_TGR192=m CONFIG_CRYPTO_WP512=m -CONFIG_CRYPTO_AES=m CONFIG_CRYPTO_ANUBIS=m -CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_KHAZAD=m @@ -486,11 +441,9 @@ CONFIG_CRYPTO_TEA=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_LZO=m # CONFIG_CRYPTO_ANSI_CPRNG is not set -CONFIG_CRYPTO_HW=y CONFIG_CRYPTO_DEV_NX=y CONFIG_CRYPTO_DEV_NX_ENCRYPT=m CONFIG_VIRTUALIZATION=y CONFIG_KVM_BOOK3S_64=m CONFIG_KVM_BOOK3S_64_HV=y CONFIG_VHOST_NET=m -CONFIG_BPF_JIT=y diff --git a/arch/powerpc/configs/ppc64e_defconfig b/arch/powerpc/configs/ppc64e_defconfig index f55c27609fc6..f591d71353e4 100644 --- a/arch/powerpc/configs/ppc64e_defconfig +++ b/arch/powerpc/configs/ppc64e_defconfig @@ -4,6 +4,8 @@ CONFIG_SMP=y CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y CONFIG_TASKSTATS=y CONFIG_TASK_DELAY_ACCT=y CONFIG_IKCONFIG=y @@ -18,12 +20,12 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_MAC_PARTITION=y CONFIG_P5020_DS=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y CONFIG_BINFMT_MISC=m CONFIG_IRQ_ALL_CPUS=y CONFIG_SPARSEMEM_MANUAL=y @@ -46,7 +48,6 @@ CONFIG_INET_ESP=m CONFIG_INET_IPCOMP=m # CONFIG_IPV6 is not set CONFIG_NETFILTER=y -CONFIG_NETFILTER_NETLINK_QUEUE=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_EVENTS=y CONFIG_NF_CT_PROTO_SCTP=m @@ -103,19 +104,12 @@ CONFIG_NETFILTER_XT_MATCH_U32=m CONFIG_NF_CONNTRACK_IPV4=m CONFIG_IP_NF_QUEUE=m CONFIG_IP_NF_IPTABLES=m -CONFIG_IP_NF_MATCH_ADDRTYPE=m CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m CONFIG_IP_NF_MATCH_TTL=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IP_NF_TARGET_LOG=m CONFIG_IP_NF_TARGET_ULOG=m -CONFIG_NF_NAT=m -CONFIG_IP_NF_TARGET_MASQUERADE=m -CONFIG_IP_NF_TARGET_NETMAP=m -CONFIG_IP_NF_TARGET_REDIRECT=m -CONFIG_NF_NAT_SNMP_BASIC=m CONFIG_IP_NF_MANGLE=m CONFIG_IP_NF_TARGET_CLUSTERIP=m CONFIG_IP_NF_TARGET_ECN=m @@ -167,41 +161,31 @@ CONFIG_DM_SNAPSHOT=m CONFIG_DM_MIRROR=m CONFIG_DM_ZERO=m CONFIG_DM_MULTIPATH=m -CONFIG_IEEE1394=y -CONFIG_IEEE1394_OHCI1394=y -CONFIG_IEEE1394_SBP2=m -CONFIG_IEEE1394_ETH1394=m -CONFIG_IEEE1394_RAWIO=y -CONFIG_IEEE1394_VIDEO1394=m -CONFIG_IEEE1394_DV1394=m CONFIG_MACINTOSH_DRIVERS=y CONFIG_WINDFARM=y CONFIG_NETDEVICES=y -CONFIG_DUMMY=m CONFIG_BONDING=m -CONFIG_TUN=m -CONFIG_MARVELL_PHY=y -CONFIG_BROADCOM_PHY=m -CONFIG_NET_ETHERNET=y -CONFIG_SUNGEM=y -CONFIG_NET_VENDOR_3COM=y -CONFIG_VORTEX=y -CONFIG_NET_PCI=y -CONFIG_PCNET32=y -CONFIG_E100=y -CONFIG_ACENIC=y -CONFIG_ACENIC_OMIT_TIGON_I=y -CONFIG_E1000=y -CONFIG_TIGON3=y -CONFIG_IXGB=m -CONFIG_PPP=m -CONFIG_PPP_ASYNC=m -CONFIG_PPP_SYNC_TTY=m -CONFIG_PPP_DEFLATE=m -CONFIG_PPP_BSDCOMP=m -CONFIG_PPPOE=m +CONFIG_DUMMY=m CONFIG_NETCONSOLE=y CONFIG_NETPOLL_TRAP=y +CONFIG_TUN=m +CONFIG_VORTEX=y +CONFIG_ACENIC=y +CONFIG_ACENIC_OMIT_TIGON_I=y +CONFIG_PCNET32=y +CONFIG_TIGON3=y +CONFIG_E100=y +CONFIG_E1000=y +CONFIG_IXGB=m +CONFIG_SUNGEM=y +CONFIG_MARVELL_PHY=y +CONFIG_BROADCOM_PHY=m +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPPOE=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m # CONFIG_INPUT_MOUSEDEV_PSAUX is not set CONFIG_INPUT_EVDEV=m CONFIG_INPUT_MISC=y @@ -213,7 +197,6 @@ CONFIG_SERIAL_8250_CONSOLE=y CONFIG_RAW_DRIVER=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_AMD8111=y -# CONFIG_HWMON is not set CONFIG_VIDEO_OUTPUT_CONTROL=m CONFIG_FB=y CONFIG_FIRMWARE_EDID=y @@ -227,7 +210,6 @@ CONFIG_FB_MATROX_MAVEN=m CONFIG_FB_RADEON=y CONFIG_FB_IBM_GXT4500=y CONFIG_LCD_CLASS_DEVICE=y -CONFIG_DISPLAY_SUPPORT=y # CONFIG_VGA_CONSOLE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y @@ -238,7 +220,6 @@ CONFIG_SND_SEQ_DUMMY=m CONFIG_SND_MIXER_OSS=m CONFIG_SND_PCM_OSS=m CONFIG_SND_SEQUENCER_OSS=y -CONFIG_USB_HIDDEV=y CONFIG_HID_DRAGONRISE=y CONFIG_HID_GYRATION=y CONFIG_HID_TWINHAN=y @@ -253,8 +234,8 @@ CONFIG_HID_SMARTJOYPLUS=y CONFIG_HID_TOPSEED=y CONFIG_HID_THRUSTMASTER=y CONFIG_HID_ZEROPLUS=y +CONFIG_USB_HIDDEV=y CONFIG_USB=y -CONFIG_USB_DEVICEFS=y CONFIG_USB_EHCI_HCD=y # CONFIG_USB_EHCI_HCD_PPC_OF is not set CONFIG_USB_OHCI_HCD=y @@ -300,19 +281,15 @@ CONFIG_HFS_FS=m CONFIG_HFSPLUS_FS=m CONFIG_CRAMFS=y CONFIG_NFS_FS=y -CONFIG_NFS_V3=y CONFIG_NFS_V3_ACL=y CONFIG_NFS_V4=y CONFIG_ROOT_NFS=y CONFIG_NFSD=m CONFIG_NFSD_V3_ACL=y CONFIG_NFSD_V4=y -CONFIG_RPCSEC_GSS_SPKM3=m CONFIG_CIFS=m CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y -CONFIG_PARTITION_ADVANCED=y -CONFIG_MAC_PARTITION=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_CODEPAGE_737=m CONFIG_NLS_CODEPAGE_775=m @@ -355,14 +332,12 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y CONFIG_DETECT_HUNG_TASK=y CONFIG_DEBUG_MUTEXES=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set +CONFIG_DEBUG_STACK_USAGE=y CONFIG_LATENCYTOP=y -CONFIG_SYSCTL_SYSCALL_CHECK=y CONFIG_IRQSOFF_TRACER=y CONFIG_SCHED_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_DEBUG_STACKOVERFLOW=y -CONFIG_DEBUG_STACK_USAGE=y CONFIG_CODE_PATCHING_SELFTEST=y CONFIG_FTR_FIXUP_SELFTEST=y CONFIG_MSI_BITMAP_SELFTEST=y @@ -371,16 +346,12 @@ CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_TEST=m CONFIG_CRYPTO_CCM=m CONFIG_CRYPTO_GCM=m -CONFIG_CRYPTO_ECB=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m -CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_TGR192=m CONFIG_CRYPTO_WP512=m -CONFIG_CRYPTO_AES=m CONFIG_CRYPTO_ANUBIS=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAST6=m diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index 5b8e1e508270..1362a5f7b28d 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -6,12 +6,15 @@ CONFIG_NR_CPUS=2048 CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_IRQ_DOMAIN_DEBUG=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y CONFIG_TASKSTATS=y CONFIG_TASK_DELAY_ACCT=y CONFIG_TASK_XACCT=y CONFIG_TASK_IO_ACCOUNTING=y -CONFIG_AUDIT=y -CONFIG_AUDITSYSCALL=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_CGROUPS=y @@ -36,11 +39,8 @@ CONFIG_DTL=y # CONFIG_PPC_PMAC is not set CONFIG_RTAS_FLASH=m CONFIG_IBMEBUS=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y CONFIG_HZ_100=y CONFIG_BINFMT_MISC=m -CONFIG_HOTPLUG_CPU=y CONFIG_KEXEC=y CONFIG_IRQ_ALL_CPUS=y CONFIG_MEMORY_HOTPLUG=y @@ -65,7 +65,6 @@ CONFIG_INET_ESP=m CONFIG_INET_IPCOMP=m # CONFIG_IPV6 is not set CONFIG_NETFILTER=y -CONFIG_NETFILTER_NETLINK_QUEUE=m CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_EVENTS=y CONFIG_NF_CT_PROTO_UDPLITE=m @@ -112,19 +111,12 @@ CONFIG_NETFILTER_XT_MATCH_U32=m CONFIG_NF_CONNTRACK_IPV4=m CONFIG_IP_NF_QUEUE=m CONFIG_IP_NF_IPTABLES=m -CONFIG_IP_NF_MATCH_ADDRTYPE=m CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_ECN=m CONFIG_IP_NF_MATCH_TTL=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IP_NF_TARGET_LOG=m CONFIG_IP_NF_TARGET_ULOG=m -CONFIG_NF_NAT=m -CONFIG_IP_NF_TARGET_MASQUERADE=m -CONFIG_IP_NF_TARGET_NETMAP=m -CONFIG_IP_NF_TARGET_REDIRECT=m -CONFIG_NF_NAT_SNMP_BASIC=m CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_PROC_DEVICETREE=y CONFIG_PARPORT=m @@ -146,7 +138,6 @@ CONFIG_CHR_DEV_SG=y CONFIG_SCSI_MULTI_LUN=y CONFIG_SCSI_CONSTANTS=y CONFIG_SCSI_FC_ATTRS=y -CONFIG_SCSI_SAS_ATTRS=m CONFIG_SCSI_CXGB3_ISCSI=m CONFIG_SCSI_CXGB4_ISCSI=m CONFIG_SCSI_BNX2_ISCSI=m @@ -177,43 +168,36 @@ CONFIG_DM_SNAPSHOT=m CONFIG_DM_MIRROR=m CONFIG_DM_ZERO=m CONFIG_DM_MULTIPATH=m -CONFIG_NETDEVICES=y -CONFIG_DUMMY=m CONFIG_BONDING=m -CONFIG_TUN=m -CONFIG_NET_ETHERNET=y -CONFIG_NET_VENDOR_3COM=y -CONFIG_VORTEX=y -CONFIG_IBMVETH=y -CONFIG_NET_PCI=y -CONFIG_PCNET32=y -CONFIG_E100=y -CONFIG_ACENIC=m -CONFIG_ACENIC_OMIT_TIGON_I=y -CONFIG_E1000=y -CONFIG_E1000E=y -CONFIG_TIGON3=y -CONFIG_BNX2=m -CONFIG_CHELSIO_T1=m -CONFIG_CHELSIO_T3=m -CONFIG_CHELSIO_T4=m -CONFIG_EHEA=y -CONFIG_IXGBE=m -CONFIG_IXGB=m -CONFIG_S2IO=m -CONFIG_MYRI10GE=m -CONFIG_NETXEN_NIC=m -CONFIG_MLX4_EN=m -CONFIG_QLGE=m -CONFIG_BE2NET=m -CONFIG_PPP=m -CONFIG_PPP_ASYNC=m -CONFIG_PPP_SYNC_TTY=m -CONFIG_PPP_DEFLATE=m -CONFIG_PPP_BSDCOMP=m -CONFIG_PPPOE=m +CONFIG_DUMMY=m CONFIG_NETCONSOLE=y CONFIG_NETPOLL_TRAP=y +CONFIG_TUN=m +CONFIG_VORTEX=y +CONFIG_ACENIC=m +CONFIG_ACENIC_OMIT_TIGON_I=y +CONFIG_PCNET32=y +CONFIG_TIGON3=y +CONFIG_CHELSIO_T1=m +CONFIG_BE2NET=m +CONFIG_S2IO=m +CONFIG_IBMVETH=y +CONFIG_EHEA=y +CONFIG_E100=y +CONFIG_E1000=y +CONFIG_E1000E=y +CONFIG_IXGB=m +CONFIG_IXGBE=m +CONFIG_MLX4_EN=m +CONFIG_MYRI10GE=m +CONFIG_QLGE=m +CONFIG_NETXEN_NIC=m +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPPOE=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m # CONFIG_INPUT_MOUSEDEV_PSAUX is not set CONFIG_INPUT_EVDEV=m CONFIG_INPUT_MISC=y @@ -227,12 +211,9 @@ CONFIG_HVC_CONSOLE=y CONFIG_HVC_RTAS=y CONFIG_HVCS=m CONFIG_IBM_BSR=m -CONFIG_HW_RANDOM=m -CONFIG_HW_RANDOM_PSERIES=m CONFIG_GEN_RTC=y CONFIG_RAW_DRIVER=y CONFIG_MAX_RAW_DEVS=1024 -# CONFIG_HWMON is not set CONFIG_FB=y CONFIG_FIRMWARE_EDID=y CONFIG_FB_OF=y @@ -243,19 +224,17 @@ CONFIG_FB_MATROX_G=y CONFIG_FB_RADEON=y CONFIG_FB_IBM_GXT4500=y CONFIG_LCD_PLATFORM=m -CONFIG_DISPLAY_SUPPORT=y # CONFIG_VGA_CONSOLE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y -CONFIG_USB_HIDDEV=y CONFIG_HID_GYRATION=y CONFIG_HID_PANTHERLORD=y CONFIG_HID_PETALYNX=y CONFIG_HID_SAMSUNG=y CONFIG_HID_SONY=y CONFIG_HID_SUNPLUS=y +CONFIG_USB_HIDDEV=y CONFIG_USB=y -CONFIG_USB_DEVICEFS=y CONFIG_USB_MON=m CONFIG_USB_EHCI_HCD=y # CONFIG_USB_EHCI_HCD_PPC_OF is not set @@ -293,7 +272,6 @@ CONFIG_JFS_POSIX_ACL=y CONFIG_JFS_SECURITY=y CONFIG_XFS_FS=m CONFIG_XFS_POSIX_ACL=y -CONFIG_OCFS2_FS=m CONFIG_BTRFS_FS=m CONFIG_BTRFS_FS_POSIX_ACL=y CONFIG_NILFS2_FS=m @@ -309,17 +287,14 @@ CONFIG_HUGETLBFS=y CONFIG_CRAMFS=m CONFIG_SQUASHFS=m CONFIG_SQUASHFS_XATTR=y -CONFIG_SQUASHFS_ZLIB=y CONFIG_SQUASHFS_LZO=y CONFIG_SQUASHFS_XZ=y CONFIG_NFS_FS=y -CONFIG_NFS_V3=y CONFIG_NFS_V3_ACL=y CONFIG_NFS_V4=y CONFIG_NFSD=m CONFIG_NFSD_V3_ACL=y CONFIG_NFSD_V4=y -CONFIG_RPCSEC_GSS_SPKM3=m CONFIG_CIFS=m CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y @@ -330,36 +305,24 @@ CONFIG_CRC_T10DIF=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y CONFIG_LOCKUP_DETECTOR=y -CONFIG_DETECT_HUNG_TASK=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set +CONFIG_DEBUG_STACK_USAGE=y CONFIG_LATENCYTOP=y -CONFIG_SYSCTL_SYSCALL_CHECK=y CONFIG_SCHED_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_DEBUG_STACKOVERFLOW=y -CONFIG_DEBUG_STACK_USAGE=y CONFIG_CODE_PATCHING_SELFTEST=y CONFIG_FTR_FIXUP_SELFTEST=y CONFIG_MSI_BITMAP_SELFTEST=y CONFIG_XMON=y CONFIG_XMON_DEFAULT=y -CONFIG_IRQ_DOMAIN_DEBUG=y CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_TEST=m -CONFIG_CRYPTO_CCM=m -CONFIG_CRYPTO_GCM=m -CONFIG_CRYPTO_ECB=m CONFIG_CRYPTO_PCBC=m CONFIG_CRYPTO_HMAC=y -CONFIG_CRYPTO_MD4=m CONFIG_CRYPTO_MICHAEL_MIC=m -CONFIG_CRYPTO_SHA256=m -CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_TGR192=m CONFIG_CRYPTO_WP512=m -CONFIG_CRYPTO_AES=m CONFIG_CRYPTO_ANUBIS=m -CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_KHAZAD=m @@ -369,7 +332,6 @@ CONFIG_CRYPTO_TEA=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_LZO=m # CONFIG_CRYPTO_ANSI_CPRNG is not set -CONFIG_CRYPTO_HW=y CONFIG_CRYPTO_DEV_NX=y CONFIG_CRYPTO_DEV_NX_ENCRYPT=m CONFIG_VIRTUALIZATION=y From f648ef2c1f7a798a96c0f44c3f4619d4503b5f4e Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 12 Dec 2012 14:33:55 +0000 Subject: [PATCH 0780/4607] powerpc: Cleanup NLS config options on pseries, ppc64 and ppc64e defconfig Set CONFIG_NLS_DEFAULT to utf8. The distros do this (eg ppc64 FC17 and RHEL6) as well as the x86 defconfigs. Userspace these days is most likely to expect utf8 anyway. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/configs/ppc64_defconfig | 38 ++------------------------ arch/powerpc/configs/ppc64e_defconfig | 38 ++------------------------ arch/powerpc/configs/pseries_defconfig | 2 ++ 3 files changed, 8 insertions(+), 70 deletions(-) diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 724f35486f17..12ef78b384ce 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -372,43 +372,11 @@ CONFIG_NFSD_V4=y CONFIG_CIFS=m CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y +CONFIG_NLS_DEFAULT="utf8" CONFIG_NLS_CODEPAGE_437=y -CONFIG_NLS_CODEPAGE_737=m -CONFIG_NLS_CODEPAGE_775=m -CONFIG_NLS_CODEPAGE_850=m -CONFIG_NLS_CODEPAGE_852=m -CONFIG_NLS_CODEPAGE_855=m -CONFIG_NLS_CODEPAGE_857=m -CONFIG_NLS_CODEPAGE_860=m -CONFIG_NLS_CODEPAGE_861=m -CONFIG_NLS_CODEPAGE_862=m -CONFIG_NLS_CODEPAGE_863=m -CONFIG_NLS_CODEPAGE_864=m -CONFIG_NLS_CODEPAGE_865=m -CONFIG_NLS_CODEPAGE_866=m -CONFIG_NLS_CODEPAGE_869=m -CONFIG_NLS_CODEPAGE_936=m -CONFIG_NLS_CODEPAGE_950=m -CONFIG_NLS_CODEPAGE_932=m -CONFIG_NLS_CODEPAGE_949=m -CONFIG_NLS_CODEPAGE_874=m -CONFIG_NLS_ISO8859_8=m -CONFIG_NLS_CODEPAGE_1250=m -CONFIG_NLS_CODEPAGE_1251=m -CONFIG_NLS_ASCII=m +CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y -CONFIG_NLS_ISO8859_2=m -CONFIG_NLS_ISO8859_3=m -CONFIG_NLS_ISO8859_4=m -CONFIG_NLS_ISO8859_5=m -CONFIG_NLS_ISO8859_6=m -CONFIG_NLS_ISO8859_7=m -CONFIG_NLS_ISO8859_9=m -CONFIG_NLS_ISO8859_13=m -CONFIG_NLS_ISO8859_14=m -CONFIG_NLS_ISO8859_15=m -CONFIG_NLS_KOI8_R=m -CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=y CONFIG_CRC_T10DIF=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y diff --git a/arch/powerpc/configs/ppc64e_defconfig b/arch/powerpc/configs/ppc64e_defconfig index f591d71353e4..667f86dfd4a0 100644 --- a/arch/powerpc/configs/ppc64e_defconfig +++ b/arch/powerpc/configs/ppc64e_defconfig @@ -290,43 +290,11 @@ CONFIG_NFSD_V4=y CONFIG_CIFS=m CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y +CONFIG_NLS_DEFAULT="utf8" CONFIG_NLS_CODEPAGE_437=y -CONFIG_NLS_CODEPAGE_737=m -CONFIG_NLS_CODEPAGE_775=m -CONFIG_NLS_CODEPAGE_850=m -CONFIG_NLS_CODEPAGE_852=m -CONFIG_NLS_CODEPAGE_855=m -CONFIG_NLS_CODEPAGE_857=m -CONFIG_NLS_CODEPAGE_860=m -CONFIG_NLS_CODEPAGE_861=m -CONFIG_NLS_CODEPAGE_862=m -CONFIG_NLS_CODEPAGE_863=m -CONFIG_NLS_CODEPAGE_864=m -CONFIG_NLS_CODEPAGE_865=m -CONFIG_NLS_CODEPAGE_866=m -CONFIG_NLS_CODEPAGE_869=m -CONFIG_NLS_CODEPAGE_936=m -CONFIG_NLS_CODEPAGE_950=m -CONFIG_NLS_CODEPAGE_932=m -CONFIG_NLS_CODEPAGE_949=m -CONFIG_NLS_CODEPAGE_874=m -CONFIG_NLS_ISO8859_8=m -CONFIG_NLS_CODEPAGE_1250=m -CONFIG_NLS_CODEPAGE_1251=m -CONFIG_NLS_ASCII=m +CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y -CONFIG_NLS_ISO8859_2=m -CONFIG_NLS_ISO8859_3=m -CONFIG_NLS_ISO8859_4=m -CONFIG_NLS_ISO8859_5=m -CONFIG_NLS_ISO8859_6=m -CONFIG_NLS_ISO8859_7=m -CONFIG_NLS_ISO8859_9=m -CONFIG_NLS_ISO8859_13=m -CONFIG_NLS_ISO8859_14=m -CONFIG_NLS_ISO8859_15=m -CONFIG_NLS_KOI8_R=m -CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=y CONFIG_CRC_T10DIF=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index 1362a5f7b28d..d028905197e2 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -298,9 +298,11 @@ CONFIG_NFSD_V4=y CONFIG_CIFS=m CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y +CONFIG_NLS_DEFAULT="utf8" CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y CONFIG_CRC_T10DIF=y CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y From 0b9fd94a1cc814a07f1ce7f439b7ab718228b7c7 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 12 Dec 2012 14:34:53 +0000 Subject: [PATCH 0781/4607] powerpc: Enable devtmpfs, EFI partition support and tmpfs ACLs on pseries, ppc64 and ppc64e defconfig We need devtmpfs enabled to boot on recent versions of Fedora. EFI partitions will be useful for large block devices. tmpfs ACL support is used by some distros for managing access to devices. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/configs/ppc64_defconfig | 4 ++++ arch/powerpc/configs/ppc64e_defconfig | 4 ++++ arch/powerpc/configs/pseries_defconfig | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 12ef78b384ce..6cbdeb4290e2 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -25,6 +25,7 @@ CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y CONFIG_PPC_SPLPAR=y CONFIG_SCANLOG=m CONFIG_PPC_SMLPAR=y @@ -146,6 +147,8 @@ CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m CONFIG_BPF_JIT=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_FD=y CONFIG_BLK_DEV_LOOP=y @@ -354,6 +357,7 @@ CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_PROC_KCORE=y CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y CONFIG_HUGETLBFS=y CONFIG_HFS_FS=m CONFIG_HFSPLUS_FS=m diff --git a/arch/powerpc/configs/ppc64e_defconfig b/arch/powerpc/configs/ppc64e_defconfig index 667f86dfd4a0..4b20f76172e2 100644 --- a/arch/powerpc/configs/ppc64e_defconfig +++ b/arch/powerpc/configs/ppc64e_defconfig @@ -22,6 +22,7 @@ CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y CONFIG_PARTITION_ADVANCED=y CONFIG_MAC_PARTITION=y +CONFIG_EFI_PARTITION=y CONFIG_P5020_DS=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y @@ -119,6 +120,8 @@ CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y CONFIG_PROC_DEVICETREE=y CONFIG_BLK_DEV_FD=y CONFIG_BLK_DEV_LOOP=y @@ -277,6 +280,7 @@ CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_PROC_KCORE=y CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y CONFIG_HFS_FS=m CONFIG_HFSPLUS_FS=m CONFIG_CRAMFS=y diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index d028905197e2..04fa7f2e295f 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -32,6 +32,8 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y CONFIG_PPC_SPLPAR=y CONFIG_SCANLOG=m CONFIG_PPC_SMLPAR=y @@ -118,6 +120,8 @@ CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m CONFIG_IP_NF_TARGET_ULOG=m CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y CONFIG_PROC_DEVICETREE=y CONFIG_PARPORT=m CONFIG_PARPORT_PC=m @@ -283,6 +287,7 @@ CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y CONFIG_PROC_KCORE=y CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y CONFIG_HUGETLBFS=y CONFIG_CRAMFS=m CONFIG_SQUASHFS=m From 98679fb0927cee2084caddc5277326f18642a58b Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 12 Dec 2012 14:43:12 +0000 Subject: [PATCH 0782/4607] powerpc: Avoid load of static chain register when calling nested functions through a pointer on 64bit The ppc64 ABI has a static chain register (r11) which is only used when calling nested functions through a pointer. Considering that we take a dim view of nested functions in the kernel, we have a lot of unnecessary overhead here. gcc 4.7 has an option to disable loading of r11 so lets use it. If hell freezes over and hipsters manage to litter the kernel with nested functions, gcc will give us an error message and won't simply compile bad code: You cannot take the address of a nested function if you use the -mno-pointers-to-nested-functions option. Furthermore our kernel module trampolines don't setup the static chain register so adding this option and forcing gcc to error out makes even more sense. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index 31d8965e0847..967fd23ace78 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -85,6 +85,7 @@ endif CFLAGS-$(CONFIG_PPC64) := -mtraceback=no -mcall-aixdesc CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mcmodel=medium,-mminimal-toc) +CFLAGS-$(CONFIG_PPC64) += $(call cc-option,-mno-pointers-to-nested-functions) CFLAGS-$(CONFIG_PPC32) := -ffixed-r2 -mmultiple CFLAGS-$(CONFIG_GENERIC_CPU) += $(call cc-option,-mtune=power7,-mtune=power4) From cde4d494af5fe69527bdec3de1a2816830977d44 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 20 Dec 2012 14:06:39 +0000 Subject: [PATCH 0783/4607] powerpc: Remove extra zeros from 32 bit CPU features definitions These are 32 bit, so no need to have a bunch of wasted 0s. The 0s saved here can be put to better use elsewhere, like at the end of my pay check. Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/cputable.h | 62 ++++++++++++++--------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 8dbf8bbbc624..1b0dbc881921 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -106,37 +106,37 @@ extern const char *powerpc_base_platform; /* CPU kernel features */ /* Retain the 32b definitions all use bottom half of word */ -#define CPU_FTR_COHERENT_ICACHE ASM_CONST(0x0000000000000001) -#define CPU_FTR_L2CR ASM_CONST(0x0000000000000002) -#define CPU_FTR_SPEC7450 ASM_CONST(0x0000000000000004) -#define CPU_FTR_ALTIVEC ASM_CONST(0x0000000000000008) -#define CPU_FTR_TAU ASM_CONST(0x0000000000000010) -#define CPU_FTR_CAN_DOZE ASM_CONST(0x0000000000000020) -#define CPU_FTR_USE_TB ASM_CONST(0x0000000000000040) -#define CPU_FTR_L2CSR ASM_CONST(0x0000000000000080) -#define CPU_FTR_601 ASM_CONST(0x0000000000000100) -#define CPU_FTR_DBELL ASM_CONST(0x0000000000000200) -#define CPU_FTR_CAN_NAP ASM_CONST(0x0000000000000400) -#define CPU_FTR_L3CR ASM_CONST(0x0000000000000800) -#define CPU_FTR_L3_DISABLE_NAP ASM_CONST(0x0000000000001000) -#define CPU_FTR_NAP_DISABLE_L2_PR ASM_CONST(0x0000000000002000) -#define CPU_FTR_DUAL_PLL_750FX ASM_CONST(0x0000000000004000) -#define CPU_FTR_NO_DPM ASM_CONST(0x0000000000008000) -#define CPU_FTR_476_DD2 ASM_CONST(0x0000000000010000) -#define CPU_FTR_NEED_COHERENT ASM_CONST(0x0000000000020000) -#define CPU_FTR_NO_BTIC ASM_CONST(0x0000000000040000) -#define CPU_FTR_DEBUG_LVL_EXC ASM_CONST(0x0000000000080000) -#define CPU_FTR_NODSISRALIGN ASM_CONST(0x0000000000100000) -#define CPU_FTR_PPC_LE ASM_CONST(0x0000000000200000) -#define CPU_FTR_REAL_LE ASM_CONST(0x0000000000400000) -#define CPU_FTR_FPU_UNAVAILABLE ASM_CONST(0x0000000000800000) -#define CPU_FTR_UNIFIED_ID_CACHE ASM_CONST(0x0000000001000000) -#define CPU_FTR_SPE ASM_CONST(0x0000000002000000) -#define CPU_FTR_NEED_PAIRED_STWCX ASM_CONST(0x0000000004000000) -#define CPU_FTR_LWSYNC ASM_CONST(0x0000000008000000) -#define CPU_FTR_NOEXECUTE ASM_CONST(0x0000000010000000) -#define CPU_FTR_INDEXED_DCR ASM_CONST(0x0000000020000000) -#define CPU_FTR_EMB_HV ASM_CONST(0x0000000040000000) +#define CPU_FTR_COHERENT_ICACHE ASM_CONST(0x00000001) +#define CPU_FTR_L2CR ASM_CONST(0x00000002) +#define CPU_FTR_SPEC7450 ASM_CONST(0x00000004) +#define CPU_FTR_ALTIVEC ASM_CONST(0x00000008) +#define CPU_FTR_TAU ASM_CONST(0x00000010) +#define CPU_FTR_CAN_DOZE ASM_CONST(0x00000020) +#define CPU_FTR_USE_TB ASM_CONST(0x00000040) +#define CPU_FTR_L2CSR ASM_CONST(0x00000080) +#define CPU_FTR_601 ASM_CONST(0x00000100) +#define CPU_FTR_DBELL ASM_CONST(0x00000200) +#define CPU_FTR_CAN_NAP ASM_CONST(0x00000400) +#define CPU_FTR_L3CR ASM_CONST(0x00000800) +#define CPU_FTR_L3_DISABLE_NAP ASM_CONST(0x00001000) +#define CPU_FTR_NAP_DISABLE_L2_PR ASM_CONST(0x00002000) +#define CPU_FTR_DUAL_PLL_750FX ASM_CONST(0x00004000) +#define CPU_FTR_NO_DPM ASM_CONST(0x00008000) +#define CPU_FTR_476_DD2 ASM_CONST(0x00010000) +#define CPU_FTR_NEED_COHERENT ASM_CONST(0x00020000) +#define CPU_FTR_NO_BTIC ASM_CONST(0x00040000) +#define CPU_FTR_DEBUG_LVL_EXC ASM_CONST(0x00080000) +#define CPU_FTR_NODSISRALIGN ASM_CONST(0x00100000) +#define CPU_FTR_PPC_LE ASM_CONST(0x00200000) +#define CPU_FTR_REAL_LE ASM_CONST(0x00400000) +#define CPU_FTR_FPU_UNAVAILABLE ASM_CONST(0x00800000) +#define CPU_FTR_UNIFIED_ID_CACHE ASM_CONST(0x01000000) +#define CPU_FTR_SPE ASM_CONST(0x02000000) +#define CPU_FTR_NEED_PAIRED_STWCX ASM_CONST(0x04000000) +#define CPU_FTR_LWSYNC ASM_CONST(0x08000000) +#define CPU_FTR_NOEXECUTE ASM_CONST(0x10000000) +#define CPU_FTR_INDEXED_DCR ASM_CONST(0x20000000) +#define CPU_FTR_EMB_HV ASM_CONST(0x40000000) /* * Add the 64-bit processor unique features in the top half of the word; From 1580b3b873d37d3ce70b195ae5522f95fec533c9 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 20 Dec 2012 14:06:40 +0000 Subject: [PATCH 0784/4607] powerpc: Repack 64bit CPU features to remove holes This frees up 7 bits for crazy new CPU features. Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/cputable.h | 50 +++++++++++++++-------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 1b0dbc881921..72edbce22c69 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -148,30 +148,32 @@ extern const char *powerpc_base_platform; #define LONG_ASM_CONST(x) 0 #endif -#define CPU_FTR_HVMODE LONG_ASM_CONST(0x0000000200000000) -#define CPU_FTR_ARCH_201 LONG_ASM_CONST(0x0000000400000000) -#define CPU_FTR_ARCH_206 LONG_ASM_CONST(0x0000000800000000) -#define CPU_FTR_CFAR LONG_ASM_CONST(0x0000001000000000) -#define CPU_FTR_IABR LONG_ASM_CONST(0x0000002000000000) -#define CPU_FTR_MMCRA LONG_ASM_CONST(0x0000004000000000) -#define CPU_FTR_CTRL LONG_ASM_CONST(0x0000008000000000) -#define CPU_FTR_SMT LONG_ASM_CONST(0x0000010000000000) -#define CPU_FTR_PAUSE_ZERO LONG_ASM_CONST(0x0000200000000000) -#define CPU_FTR_PURR LONG_ASM_CONST(0x0000400000000000) -#define CPU_FTR_CELL_TB_BUG LONG_ASM_CONST(0x0000800000000000) -#define CPU_FTR_SPURR LONG_ASM_CONST(0x0001000000000000) -#define CPU_FTR_DSCR LONG_ASM_CONST(0x0002000000000000) -#define CPU_FTR_VSX LONG_ASM_CONST(0x0010000000000000) -#define CPU_FTR_SAO LONG_ASM_CONST(0x0020000000000000) -#define CPU_FTR_CP_USE_DCBTZ LONG_ASM_CONST(0x0040000000000000) -#define CPU_FTR_UNALIGNED_LD_STD LONG_ASM_CONST(0x0080000000000000) -#define CPU_FTR_ASYM_SMT LONG_ASM_CONST(0x0100000000000000) -#define CPU_FTR_STCX_CHECKS_ADDRESS LONG_ASM_CONST(0x0200000000000000) -#define CPU_FTR_POPCNTB LONG_ASM_CONST(0x0400000000000000) -#define CPU_FTR_POPCNTD LONG_ASM_CONST(0x0800000000000000) -#define CPU_FTR_ICSWX LONG_ASM_CONST(0x1000000000000000) -#define CPU_FTR_VMX_COPY LONG_ASM_CONST(0x2000000000000000) -#define CPU_FTR_HAS_PPR LONG_ASM_CONST(0x4000000000000000) +#define CPU_FTR_HVMODE LONG_ASM_CONST(0x0000000100000000) +#define CPU_FTR_ARCH_201 LONG_ASM_CONST(0x0000000200000000) +#define CPU_FTR_ARCH_206 LONG_ASM_CONST(0x0000000400000000) +#define CPU_FTR_CFAR LONG_ASM_CONST(0x0000000800000000) +#define CPU_FTR_IABR LONG_ASM_CONST(0x0000001000000000) +#define CPU_FTR_MMCRA LONG_ASM_CONST(0x0000002000000000) +#define CPU_FTR_CTRL LONG_ASM_CONST(0x0000004000000000) +#define CPU_FTR_SMT LONG_ASM_CONST(0x0000008000000000) +#define CPU_FTR_PAUSE_ZERO LONG_ASM_CONST(0x0000010000000000) +#define CPU_FTR_PURR LONG_ASM_CONST(0x0000020000000000) +#define CPU_FTR_CELL_TB_BUG LONG_ASM_CONST(0x0000040000000000) +#define CPU_FTR_SPURR LONG_ASM_CONST(0x0000080000000000) +#define CPU_FTR_DSCR LONG_ASM_CONST(0x0000100000000000) +#define CPU_FTR_VSX LONG_ASM_CONST(0x0000200000000000) +#define CPU_FTR_SAO LONG_ASM_CONST(0x0000400000000000) +#define CPU_FTR_CP_USE_DCBTZ LONG_ASM_CONST(0x0000800000000000) +#define CPU_FTR_UNALIGNED_LD_STD LONG_ASM_CONST(0x0001000000000000) +#define CPU_FTR_ASYM_SMT LONG_ASM_CONST(0x0002000000000000) +#define CPU_FTR_STCX_CHECKS_ADDRESS LONG_ASM_CONST(0x0004000000000000) +#define CPU_FTR_POPCNTB LONG_ASM_CONST(0x0008000000000000) +#define CPU_FTR_POPCNTD LONG_ASM_CONST(0x0010000000000000) +#define CPU_FTR_ICSWX LONG_ASM_CONST(0x0020000000000000) +#define CPU_FTR_VMX_COPY LONG_ASM_CONST(0x0040000000000000) +#define CPU_FTR_TM LONG_ASM_CONST(0x0080000000000000) +#define CPU_FTR_BCTAR LONG_ASM_CONST(0x0100000000000000) +#define CPU_FTR_HAS_PPR LONG_ASM_CONST(0x0200000000000000) #ifndef __ASSEMBLY__ From 376a8646a2d50b56b5aa80eea720258b95db3cad Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Thu, 20 Dec 2012 14:06:41 +0000 Subject: [PATCH 0785/4607] powerpc: Add helper functions set the DAWR and CIABR using set_mode These are just wrappers around the new set_mode HCALL. Signed-off-by: Ian Munsie Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/plpar_wrappers.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h index b93384929c4c..f368668d97b3 100644 --- a/arch/powerpc/platforms/pseries/plpar_wrappers.h +++ b/arch/powerpc/platforms/pseries/plpar_wrappers.h @@ -312,4 +312,14 @@ static inline long disable_reloc_on_exceptions(void) { return plpar_set_mode(0, 3, 0, 0); } +static inline long plapr_set_ciabr(unsigned long ciabr) +{ + return plpar_set_mode(0, 1, ciabr, 0); +} + +static inline long plapr_set_watchpoint0(unsigned long dawr0, unsigned long dawrx0) +{ + return plpar_set_mode(0, 2, dawr0, dawrx0); +} + #endif /* _PSERIES_PLPAR_WRAPPERS_H */ From 79879c17d4af34e1d87fc3d9e1cc7803dcae153b Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 20 Dec 2012 14:06:42 +0000 Subject: [PATCH 0786/4607] powerpc: Add DAWR CPU feature bit definition .. and add it to POWER8 cpu features. Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/cputable.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 72edbce22c69..06f7fb93c547 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -174,6 +174,7 @@ extern const char *powerpc_base_platform; #define CPU_FTR_TM LONG_ASM_CONST(0x0080000000000000) #define CPU_FTR_BCTAR LONG_ASM_CONST(0x0100000000000000) #define CPU_FTR_HAS_PPR LONG_ASM_CONST(0x0200000000000000) +#define CPU_FTR_DAWR LONG_ASM_CONST(0x0400000000000000) #ifndef __ASSEMBLY__ @@ -413,7 +414,7 @@ extern const char *powerpc_base_platform; CPU_FTR_DSCR | CPU_FTR_SAO | \ CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ - CPU_FTR_DBELL | CPU_FTR_HAS_PPR) + CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_DAWR) #define CPU_FTRS_CELL (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ From a8190a59e7440a7e3f7c0889d72a13e157988b3c Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 20 Dec 2012 14:06:43 +0000 Subject: [PATCH 0787/4607] powerpc: Add DAWR/X SPR number definitions Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/reg.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 1bdba54839b4..d111beb1acdb 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -200,6 +200,11 @@ #define CTRL_CT1 0x40000000 /* thread 1 */ #define CTRL_TE 0x00c00000 /* thread enable */ #define CTRL_RUNLATCH 0x1 +#define SPRN_DAWR 0xB4 +#define SPRN_DAWRX 0xBC +#define DAWRX_USER (1UL << 0) +#define DAWRX_KERNEL (1UL << 1) +#define DAWRX_HYP (1UL << 2) #define SPRN_DABR 0x3F5 /* Data Address Breakpoint Register */ #define DABR_TRANSLATION (1UL << 2) #define DABR_DATA_WRITE (1UL << 1) From 9422de3e953d0e60eb95f5430a9dd803eec1c6d7 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 20 Dec 2012 14:06:44 +0000 Subject: [PATCH 0788/4607] powerpc: Hardware breakpoints rewrite to handle non DABR breakpoint registers This is a rewrite so that we don't assume we are using the DABR throughout the code. We now use the arch_hw_breakpoint to store the breakpoint in a generic manner in the thread_struct, rather than storing the raw DABR value. The ptrace GET/SET_DEBUGREG interface currently passes the raw DABR in from userspace. We keep this functionality, so that future changes (like the POWER8 DAWR), will still fake the DABR to userspace. Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/debug.h | 15 +++-- arch/powerpc/include/asm/hw_breakpoint.h | 33 ++++++++--- arch/powerpc/include/asm/processor.h | 4 +- arch/powerpc/include/asm/reg.h | 3 - arch/powerpc/kernel/exceptions-64s.S | 2 +- arch/powerpc/kernel/hw_breakpoint.c | 72 ++++++++++------------- arch/powerpc/kernel/kgdb.c | 10 ++-- arch/powerpc/kernel/process.c | 75 +++++++++++++++++------- arch/powerpc/kernel/ptrace.c | 60 ++++++++++--------- arch/powerpc/kernel/ptrace32.c | 8 ++- arch/powerpc/kernel/signal.c | 5 +- arch/powerpc/kernel/traps.c | 4 +- arch/powerpc/mm/fault.c | 4 +- arch/powerpc/xmon/xmon.c | 21 ++++--- 14 files changed, 187 insertions(+), 129 deletions(-) diff --git a/arch/powerpc/include/asm/debug.h b/arch/powerpc/include/asm/debug.h index 32de2577bb6d..8d85ffb03e61 100644 --- a/arch/powerpc/include/asm/debug.h +++ b/arch/powerpc/include/asm/debug.h @@ -4,6 +4,8 @@ #ifndef _ASM_POWERPC_DEBUG_H #define _ASM_POWERPC_DEBUG_H +#include + struct pt_regs; extern struct dentry *powerpc_debugfs_root; @@ -15,7 +17,7 @@ extern int (*__debugger_ipi)(struct pt_regs *regs); extern int (*__debugger_bpt)(struct pt_regs *regs); extern int (*__debugger_sstep)(struct pt_regs *regs); extern int (*__debugger_iabr_match)(struct pt_regs *regs); -extern int (*__debugger_dabr_match)(struct pt_regs *regs); +extern int (*__debugger_break_match)(struct pt_regs *regs); extern int (*__debugger_fault_handler)(struct pt_regs *regs); #define DEBUGGER_BOILERPLATE(__NAME) \ @@ -31,7 +33,7 @@ DEBUGGER_BOILERPLATE(debugger_ipi) DEBUGGER_BOILERPLATE(debugger_bpt) DEBUGGER_BOILERPLATE(debugger_sstep) DEBUGGER_BOILERPLATE(debugger_iabr_match) -DEBUGGER_BOILERPLATE(debugger_dabr_match) +DEBUGGER_BOILERPLATE(debugger_break_match) DEBUGGER_BOILERPLATE(debugger_fault_handler) #else @@ -40,17 +42,18 @@ static inline int debugger_ipi(struct pt_regs *regs) { return 0; } static inline int debugger_bpt(struct pt_regs *regs) { return 0; } static inline int debugger_sstep(struct pt_regs *regs) { return 0; } static inline int debugger_iabr_match(struct pt_regs *regs) { return 0; } -static inline int debugger_dabr_match(struct pt_regs *regs) { return 0; } +static inline int debugger_break_match(struct pt_regs *regs) { return 0; } static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; } #endif -extern int set_dabr(unsigned long dabr, unsigned long dabrx); +int set_break(struct arch_hw_breakpoint *brk); #ifdef CONFIG_PPC_ADV_DEBUG_REGS extern void do_send_trap(struct pt_regs *regs, unsigned long address, unsigned long error_code, int signal_code, int brkpt); #else -extern void do_dabr(struct pt_regs *regs, unsigned long address, - unsigned long error_code); + +extern void do_break(struct pt_regs *regs, unsigned long address, + unsigned long error_code); #endif #endif /* _ASM_POWERPC_DEBUG_H */ diff --git a/arch/powerpc/include/asm/hw_breakpoint.h b/arch/powerpc/include/asm/hw_breakpoint.h index 423424599dad..2c91faf981db 100644 --- a/arch/powerpc/include/asm/hw_breakpoint.h +++ b/arch/powerpc/include/asm/hw_breakpoint.h @@ -24,16 +24,30 @@ #define _PPC_BOOK3S_64_HW_BREAKPOINT_H #ifdef __KERNEL__ -#ifdef CONFIG_HAVE_HW_BREAKPOINT - struct arch_hw_breakpoint { unsigned long address; - unsigned long dabrx; - int type; - u8 len; /* length of the target data symbol */ - bool extraneous_interrupt; + u16 type; + u16 len; /* length of the target data symbol */ }; +/* Note: Don't change the the first 6 bits below as they are in the same order + * as the dabr and dabrx. + */ +#define HW_BRK_TYPE_READ 0x01 +#define HW_BRK_TYPE_WRITE 0x02 +#define HW_BRK_TYPE_TRANSLATE 0x04 +#define HW_BRK_TYPE_USER 0x08 +#define HW_BRK_TYPE_KERNEL 0x10 +#define HW_BRK_TYPE_HYP 0x20 +#define HW_BRK_TYPE_EXTRANEOUS_IRQ 0x80 + +/* bits that overlap with the bottom 3 bits of the dabr */ +#define HW_BRK_TYPE_RDWR (HW_BRK_TYPE_READ | HW_BRK_TYPE_WRITE) +#define HW_BRK_TYPE_DABR (HW_BRK_TYPE_RDWR | HW_BRK_TYPE_TRANSLATE) +#define HW_BRK_TYPE_PRIV_ALL (HW_BRK_TYPE_USER | HW_BRK_TYPE_KERNEL | \ + HW_BRK_TYPE_HYP) + +#ifdef CONFIG_HAVE_HW_BREAKPOINT #include #include #include @@ -62,7 +76,12 @@ extern void ptrace_triggered(struct perf_event *bp, struct perf_sample_data *data, struct pt_regs *regs); static inline void hw_breakpoint_disable(void) { - set_dabr(0, 0); + struct arch_hw_breakpoint brk; + + brk.address = 0; + brk.type = 0; + brk.len = 0; + set_break(&brk); } extern void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs); diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 37f87f069cbf..7938658c168d 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -33,6 +33,7 @@ #include #include #include +#include /* We do _not_ want to define new machine types at all, those must die * in favor of using the device-tree @@ -225,8 +226,7 @@ struct thread_struct { struct perf_event *last_hit_ubp; #endif /* CONFIG_HAVE_HW_BREAKPOINT */ #endif - unsigned long dabr; /* Data address breakpoint register */ - unsigned long dabrx; /* ... extension */ + struct arch_hw_breakpoint hw_brk; /* info on the hardware breakpoint */ unsigned long trap_nr; /* last trap # on this thread */ #ifdef CONFIG_ALTIVEC /* Complete AltiVec register set */ diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index d111beb1acdb..1f59fbb7b054 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -206,9 +206,6 @@ #define DAWRX_KERNEL (1UL << 1) #define DAWRX_HYP (1UL << 2) #define SPRN_DABR 0x3F5 /* Data Address Breakpoint Register */ -#define DABR_TRANSLATION (1UL << 2) -#define DABR_DATA_WRITE (1UL << 1) -#define DABR_DATA_READ (1UL << 0) #define SPRN_DABR2 0x13D /* e300 */ #define SPRN_DABRX 0x3F7 /* Data Address Breakpoint Register Extension */ #define DABRX_USER (1UL << 0) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 3425aba8da51..a28a65fd0f07 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -1251,7 +1251,7 @@ handle_dabr_fault: ld r4,_DAR(r1) ld r5,_DSISR(r1) addi r3,r1,STACK_FRAME_OVERHEAD - bl .do_dabr + bl .do_break 12: b .ret_from_except_lite diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c index a89cae481b04..c7483d09fdd0 100644 --- a/arch/powerpc/kernel/hw_breakpoint.c +++ b/arch/powerpc/kernel/hw_breakpoint.c @@ -73,7 +73,7 @@ int arch_install_hw_breakpoint(struct perf_event *bp) * If so, DABR will be populated in single_step_dabr_instruction(). */ if (current->thread.last_hit_ubp != bp) - set_dabr(info->address | info->type | DABR_TRANSLATION, info->dabrx); + set_break(info); return 0; } @@ -97,7 +97,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) } *slot = NULL; - set_dabr(0, 0); + hw_breakpoint_disable(); } /* @@ -127,19 +127,13 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp) int arch_bp_generic_fields(int type, int *gen_bp_type) { - switch (type) { - case DABR_DATA_READ: - *gen_bp_type = HW_BREAKPOINT_R; - break; - case DABR_DATA_WRITE: - *gen_bp_type = HW_BREAKPOINT_W; - break; - case (DABR_DATA_WRITE | DABR_DATA_READ): - *gen_bp_type = (HW_BREAKPOINT_W | HW_BREAKPOINT_R); - break; - default: + *gen_bp_type = 0; + if (type & HW_BRK_TYPE_READ) + *gen_bp_type |= HW_BREAKPOINT_R; + if (type & HW_BRK_TYPE_WRITE) + *gen_bp_type |= HW_BREAKPOINT_W; + if (*gen_bp_type == 0) return -EINVAL; - } return 0; } @@ -154,29 +148,22 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) if (!bp) return ret; - switch (bp->attr.bp_type) { - case HW_BREAKPOINT_R: - info->type = DABR_DATA_READ; - break; - case HW_BREAKPOINT_W: - info->type = DABR_DATA_WRITE; - break; - case HW_BREAKPOINT_R | HW_BREAKPOINT_W: - info->type = (DABR_DATA_READ | DABR_DATA_WRITE); - break; - default: + info->type = HW_BRK_TYPE_TRANSLATE; + if (bp->attr.bp_type & HW_BREAKPOINT_R) + info->type |= HW_BRK_TYPE_READ; + if (bp->attr.bp_type & HW_BREAKPOINT_W) + info->type |= HW_BRK_TYPE_WRITE; + if (info->type == HW_BRK_TYPE_TRANSLATE) + /* must set alteast read or write */ return ret; - } - + if (!(bp->attr.exclude_user)) + info->type |= HW_BRK_TYPE_USER; + if (!(bp->attr.exclude_kernel)) + info->type |= HW_BRK_TYPE_KERNEL; + if (!(bp->attr.exclude_hv)) + info->type |= HW_BRK_TYPE_HYP; info->address = bp->attr.bp_addr; info->len = bp->attr.bp_len; - info->dabrx = DABRX_ALL; - if (bp->attr.exclude_user) - info->dabrx &= ~DABRX_USER; - if (bp->attr.exclude_kernel) - info->dabrx &= ~DABRX_KERNEL; - if (bp->attr.exclude_hv) - info->dabrx &= ~DABRX_HYP; /* * Since breakpoint length can be a maximum of HW_BREAKPOINT_LEN(8) @@ -204,7 +191,7 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs) info = counter_arch_bp(tsk->thread.last_hit_ubp); regs->msr &= ~MSR_SE; - set_dabr(info->address | info->type | DABR_TRANSLATION, info->dabrx); + set_break(info); tsk->thread.last_hit_ubp = NULL; } @@ -222,7 +209,7 @@ int __kprobes hw_breakpoint_handler(struct die_args *args) unsigned long dar = regs->dar; /* Disable breakpoints during exception handling */ - set_dabr(0, 0); + hw_breakpoint_disable(); /* * The counter may be concurrently released but that can only @@ -255,8 +242,9 @@ int __kprobes hw_breakpoint_handler(struct die_args *args) * we still need to single-step the instruction, but we don't * generate an event. */ - info->extraneous_interrupt = !((bp->attr.bp_addr <= dar) && - (dar - bp->attr.bp_addr < bp->attr.bp_len)); + if (!((bp->attr.bp_addr <= dar) && + (dar - bp->attr.bp_addr < bp->attr.bp_len))) + info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ; /* Do not emulate user-space instructions, instead single-step them */ if (user_mode(regs)) { @@ -285,10 +273,10 @@ int __kprobes hw_breakpoint_handler(struct die_args *args) * As a policy, the callback is invoked in a 'trigger-after-execute' * fashion */ - if (!info->extraneous_interrupt) + if (!(info->type & HW_BRK_TYPE_EXTRANEOUS_IRQ)) perf_bp_event(bp, regs); - set_dabr(info->address | info->type | DABR_TRANSLATION, info->dabrx); + set_break(info); out: rcu_read_unlock(); return rc; @@ -317,10 +305,10 @@ int __kprobes single_step_dabr_instruction(struct die_args *args) * We shall invoke the user-defined callback function in the single * stepping handler to confirm to 'trigger-after-execute' semantics */ - if (!info->extraneous_interrupt) + if (!(info->type & HW_BRK_TYPE_EXTRANEOUS_IRQ)) perf_bp_event(bp, regs); - set_dabr(info->address | info->type | DABR_TRANSLATION, info->dabrx); + set_break(info); current->thread.last_hit_ubp = NULL; /* diff --git a/arch/powerpc/kernel/kgdb.c b/arch/powerpc/kernel/kgdb.c index c470a40b29f5..a05f0e4a9d38 100644 --- a/arch/powerpc/kernel/kgdb.c +++ b/arch/powerpc/kernel/kgdb.c @@ -198,7 +198,7 @@ static int kgdb_iabr_match(struct pt_regs *regs) return 1; } -static int kgdb_dabr_match(struct pt_regs *regs) +static int kgdb_break_match(struct pt_regs *regs) { if (user_mode(regs)) return 0; @@ -458,7 +458,7 @@ static void *old__debugger; static void *old__debugger_bpt; static void *old__debugger_sstep; static void *old__debugger_iabr_match; -static void *old__debugger_dabr_match; +static void *old__debugger_break_match; static void *old__debugger_fault_handler; int kgdb_arch_init(void) @@ -468,7 +468,7 @@ int kgdb_arch_init(void) old__debugger_bpt = __debugger_bpt; old__debugger_sstep = __debugger_sstep; old__debugger_iabr_match = __debugger_iabr_match; - old__debugger_dabr_match = __debugger_dabr_match; + old__debugger_break_match = __debugger_break_match; old__debugger_fault_handler = __debugger_fault_handler; __debugger_ipi = kgdb_call_nmi_hook; @@ -476,7 +476,7 @@ int kgdb_arch_init(void) __debugger_bpt = kgdb_handle_breakpoint; __debugger_sstep = kgdb_singlestep; __debugger_iabr_match = kgdb_iabr_match; - __debugger_dabr_match = kgdb_dabr_match; + __debugger_break_match = kgdb_break_match; __debugger_fault_handler = kgdb_not_implemented; return 0; @@ -489,6 +489,6 @@ void kgdb_arch_exit(void) __debugger_bpt = old__debugger_bpt; __debugger_sstep = old__debugger_sstep; __debugger_iabr_match = old__debugger_iabr_match; - __debugger_dabr_match = old__debugger_dabr_match; + __debugger_breakx_match = old__debugger_break_match; __debugger_fault_handler = old__debugger_fault_handler; } diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 3065d17f3606..c16c1c2abeea 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -271,7 +271,7 @@ void do_send_trap(struct pt_regs *regs, unsigned long address, force_sig_info(SIGTRAP, &info, current); } #else /* !CONFIG_PPC_ADV_DEBUG_REGS */ -void do_dabr(struct pt_regs *regs, unsigned long address, +void do_break (struct pt_regs *regs, unsigned long address, unsigned long error_code) { siginfo_t info; @@ -281,11 +281,11 @@ void do_dabr(struct pt_regs *regs, unsigned long address, 11, SIGSEGV) == NOTIFY_STOP) return; - if (debugger_dabr_match(regs)) + if (debugger_break_match(regs)) return; - /* Clear the DABR */ - set_dabr(0, 0); + /* Clear the breakpoint */ + hw_breakpoint_disable(); /* Deliver the signal to userspace */ info.si_signo = SIGTRAP; @@ -296,7 +296,7 @@ void do_dabr(struct pt_regs *regs, unsigned long address, } #endif /* CONFIG_PPC_ADV_DEBUG_REGS */ -static DEFINE_PER_CPU(unsigned long, current_dabr); +static DEFINE_PER_CPU(struct arch_hw_breakpoint, current_brk); #ifdef CONFIG_PPC_ADV_DEBUG_REGS /* @@ -364,39 +364,72 @@ static void switch_booke_debug_regs(struct thread_struct *new_thread) #ifndef CONFIG_HAVE_HW_BREAKPOINT static void set_debug_reg_defaults(struct thread_struct *thread) { - if (thread->dabr) { - thread->dabr = 0; - thread->dabrx = 0; - set_dabr(0, 0); - } + thread->hw_brk.address = 0; + thread->hw_brk.type = 0; + set_break(&thread->hw_brk); } #endif /* !CONFIG_HAVE_HW_BREAKPOINT */ #endif /* CONFIG_PPC_ADV_DEBUG_REGS */ -int set_dabr(unsigned long dabr, unsigned long dabrx) -{ - __get_cpu_var(current_dabr) = dabr; - - if (ppc_md.set_dabr) - return ppc_md.set_dabr(dabr, dabrx); - - /* XXX should we have a CPU_FTR_HAS_DABR ? */ #ifdef CONFIG_PPC_ADV_DEBUG_REGS +static inline int __set_dabr(unsigned long dabr, unsigned long dabrx) +{ mtspr(SPRN_DAC1, dabr); #ifdef CONFIG_PPC_47x isync(); #endif + return 0; +} #elif defined(CONFIG_PPC_BOOK3S) +static inline int __set_dabr(unsigned long dabr, unsigned long dabrx) +{ mtspr(SPRN_DABR, dabr); mtspr(SPRN_DABRX, dabrx); -#endif return 0; } +#else +static inline int __set_dabr(unsigned long dabr, unsigned long dabrx) +{ + return -EINVAL; +} +#endif + +static inline int set_dabr(struct arch_hw_breakpoint *brk) +{ + unsigned long dabr, dabrx; + + dabr = brk->address | (brk->type & HW_BRK_TYPE_DABR); + dabrx = ((brk->type >> 3) & 0x7); + + if (ppc_md.set_dabr) + return ppc_md.set_dabr(dabr, dabrx); + + return __set_dabr(dabr, dabrx); +} + +int set_break(struct arch_hw_breakpoint *brk) +{ + __get_cpu_var(current_brk) = *brk; + + return set_dabr(brk); +} #ifdef CONFIG_PPC64 DEFINE_PER_CPU(struct cpu_usage, cpu_usage_array); #endif +static inline bool hw_brk_match(struct arch_hw_breakpoint *a, + struct arch_hw_breakpoint *b) +{ + if (a->address != b->address) + return false; + if (a->type != b->type) + return false; + if (a->len != b->len) + return false; + return true; +} + struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *new) { @@ -481,8 +514,8 @@ struct task_struct *__switch_to(struct task_struct *prev, * schedule DABR */ #ifndef CONFIG_HAVE_HW_BREAKPOINT - if (unlikely(__get_cpu_var(current_dabr) != new->thread.dabr)) - set_dabr(new->thread.dabr, new->thread.dabrx); + if (unlikely(hw_brk_match(&__get_cpu_var(current_brk), &new->thread.hw_brk))) + set_break(&new->thread.hw_brk); #endif /* CONFIG_HAVE_HW_BREAKPOINT */ #endif diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index c4970004d44d..d4afcccf1238 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -905,6 +905,9 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, struct perf_event *bp; struct perf_event_attr attr; #endif /* CONFIG_HAVE_HW_BREAKPOINT */ +#ifndef CONFIG_PPC_ADV_DEBUG_REGS + struct arch_hw_breakpoint hw_brk; +#endif /* For ppc64 we support one DABR and no IABR's at the moment (ppc64). * For embedded processors we support one DAC and no IAC's at the @@ -931,14 +934,17 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, */ /* Ensure breakpoint translation bit is set */ - if (data && !(data & DABR_TRANSLATION)) + if (data && !(data & HW_BRK_TYPE_TRANSLATE)) return -EIO; + hw_brk.address = data & (~HW_BRK_TYPE_DABR); + hw_brk.type = (data & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL; + hw_brk.len = 8; #ifdef CONFIG_HAVE_HW_BREAKPOINT if (ptrace_get_breakpoints(task) < 0) return -ESRCH; bp = thread->ptrace_bps[0]; - if ((!data) || !(data & (DABR_DATA_WRITE | DABR_DATA_READ))) { + if ((!data) || !(hw_brk.type & HW_BRK_TYPE_RDWR)) { if (bp) { unregister_hw_breakpoint(bp); thread->ptrace_bps[0] = NULL; @@ -948,10 +954,8 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, } if (bp) { attr = bp->attr; - attr.bp_addr = data & ~HW_BREAKPOINT_ALIGN; - arch_bp_generic_fields(data & - (DABR_DATA_WRITE | DABR_DATA_READ), - &attr.bp_type); + attr.bp_addr = hw_brk.address; + arch_bp_generic_fields(hw_brk.type, &attr.bp_type); /* Enable breakpoint */ attr.disabled = false; @@ -963,16 +967,15 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, } thread->ptrace_bps[0] = bp; ptrace_put_breakpoints(task); - thread->dabr = data; - thread->dabrx = DABRX_ALL; + thread->hw_brk = hw_brk; return 0; } /* Create a new breakpoint request if one doesn't exist already */ hw_breakpoint_init(&attr); - attr.bp_addr = data & ~HW_BREAKPOINT_ALIGN; - arch_bp_generic_fields(data & (DABR_DATA_WRITE | DABR_DATA_READ), - &attr.bp_type); + attr.bp_addr = hw_brk.address; + arch_bp_generic_fields(hw_brk.type, + &attr.bp_type); thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr, ptrace_triggered, NULL, task); @@ -985,10 +988,7 @@ int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, ptrace_put_breakpoints(task); #endif /* CONFIG_HAVE_HW_BREAKPOINT */ - - /* Move contents to the DABR register */ - task->thread.dabr = data; - task->thread.dabrx = DABRX_ALL; + task->thread.hw_brk = hw_brk; #else /* CONFIG_PPC_ADV_DEBUG_REGS */ /* As described above, it was assumed 3 bits were passed with the data * address, but we will assume only the mode bits will be passed @@ -1349,7 +1349,7 @@ static long ppc_set_hwdebug(struct task_struct *child, struct perf_event_attr attr; #endif /* CONFIG_HAVE_HW_BREAKPOINT */ #ifndef CONFIG_PPC_ADV_DEBUG_REGS - unsigned long dabr; + struct arch_hw_breakpoint brk; #endif if (bp_info->version != 1) @@ -1397,12 +1397,12 @@ static long ppc_set_hwdebug(struct task_struct *child, if ((unsigned long)bp_info->addr >= TASK_SIZE) return -EIO; - dabr = (unsigned long)bp_info->addr & ~7UL; - dabr |= DABR_TRANSLATION; + brk.address = bp_info->addr & ~7UL; + brk.type = HW_BRK_TYPE_TRANSLATE; if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ) - dabr |= DABR_DATA_READ; + brk.type |= HW_BRK_TYPE_READ; if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE) - dabr |= DABR_DATA_WRITE; + brk.type |= HW_BRK_TYPE_WRITE; #ifdef CONFIG_HAVE_HW_BREAKPOINT if (ptrace_get_breakpoints(child) < 0) return -ESRCH; @@ -1427,8 +1427,7 @@ static long ppc_set_hwdebug(struct task_struct *child, hw_breakpoint_init(&attr); attr.bp_addr = (unsigned long)bp_info->addr & ~HW_BREAKPOINT_ALIGN; attr.bp_len = len; - arch_bp_generic_fields(dabr & (DABR_DATA_WRITE | DABR_DATA_READ), - &attr.bp_type); + arch_bp_generic_fields(brk.type, &attr.bp_type); thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr, ptrace_triggered, NULL, child); @@ -1445,11 +1444,10 @@ static long ppc_set_hwdebug(struct task_struct *child, if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) return -EINVAL; - if (child->thread.dabr) + if (child->thread.hw_brk.address) return -ENOSPC; - child->thread.dabr = dabr; - child->thread.dabrx = DABRX_ALL; + child->thread.hw_brk = brk; return 1; #endif /* !CONFIG_PPC_ADV_DEBUG_DVCS */ @@ -1495,10 +1493,11 @@ static long ppc_del_hwdebug(struct task_struct *child, long data) ptrace_put_breakpoints(child); return ret; #else /* CONFIG_HAVE_HW_BREAKPOINT */ - if (child->thread.dabr == 0) + if (child->thread.hw_brk.address == 0) return -ENOENT; - child->thread.dabr = 0; + child->thread.hw_brk.address = 0; + child->thread.hw_brk.type = 0; #endif /* CONFIG_HAVE_HW_BREAKPOINT */ return 0; @@ -1642,6 +1641,9 @@ long arch_ptrace(struct task_struct *child, long request, } case PTRACE_GET_DEBUGREG: { +#ifndef CONFIG_PPC_ADV_DEBUG_REGS + unsigned long dabr_fake; +#endif ret = -EINVAL; /* We only support one DABR and no IABRS at the moment */ if (addr > 0) @@ -1649,7 +1651,9 @@ long arch_ptrace(struct task_struct *child, long request, #ifdef CONFIG_PPC_ADV_DEBUG_REGS ret = put_user(child->thread.dac1, datalp); #else - ret = put_user(child->thread.dabr, datalp); + dabr_fake = ((child->thread.hw_brk.address & (~HW_BRK_TYPE_DABR)) | + (child->thread.hw_brk.type & HW_BRK_TYPE_DABR)); + ret = put_user(dabr_fake, datalp); #endif break; } diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index 8c21658719d9..c0244e766834 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c @@ -252,6 +252,9 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, } case PTRACE_GET_DEBUGREG: { +#ifndef CONFIG_PPC_ADV_DEBUG_REGS + unsigned long dabr_fake; +#endif ret = -EINVAL; /* We only support one DABR and no IABRS at the moment */ if (addr > 0) @@ -259,7 +262,10 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, #ifdef CONFIG_PPC_ADV_DEBUG_REGS ret = put_user(child->thread.dac1, (u32 __user *)data); #else - ret = put_user(child->thread.dabr, (u32 __user *)data); + dabr_fake = ( + (child->thread.hw_brk.address & (~HW_BRK_TYPE_DABR)) | + (child->thread.hw_brk.type & HW_BRK_TYPE_DABR)); + ret = put_user(dabr_fake, (u32 __user *)data); #endif break; } diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index 3b997118df50..1f26956d3913 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -130,8 +130,9 @@ static int do_signal(struct pt_regs *regs) * user space. The DABR will have been cleared if it * triggered inside the kernel. */ - if (current->thread.dabr) - set_dabr(current->thread.dabr, current->thread.dabrx); + if (current->thread.hw_brk.address && + current->thread.hw_brk.type) + set_break(¤t->thread.hw_brk); #endif /* Re-enable the breakpoints for the signal stack */ thread_change_pc(current, regs); diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 114ea241916f..a008cf5c0fce 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -66,7 +66,7 @@ int (*__debugger_ipi)(struct pt_regs *regs) __read_mostly; int (*__debugger_bpt)(struct pt_regs *regs) __read_mostly; int (*__debugger_sstep)(struct pt_regs *regs) __read_mostly; int (*__debugger_iabr_match)(struct pt_regs *regs) __read_mostly; -int (*__debugger_dabr_match)(struct pt_regs *regs) __read_mostly; +int (*__debugger_break_match)(struct pt_regs *regs) __read_mostly; int (*__debugger_fault_handler)(struct pt_regs *regs) __read_mostly; EXPORT_SYMBOL(__debugger); @@ -74,7 +74,7 @@ EXPORT_SYMBOL(__debugger_ipi); EXPORT_SYMBOL(__debugger_bpt); EXPORT_SYMBOL(__debugger_sstep); EXPORT_SYMBOL(__debugger_iabr_match); -EXPORT_SYMBOL(__debugger_dabr_match); +EXPORT_SYMBOL(__debugger_break_match); EXPORT_SYMBOL(__debugger_fault_handler); #endif diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 3a8489a354e9..229951ffc351 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -249,8 +249,8 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE) || \ defined(CONFIG_PPC_BOOK3S_64)) if (error_code & DSISR_DABRMATCH) { - /* DABR match */ - do_dabr(regs, address, error_code); + /* breakpoint match */ + do_break(regs, address, error_code); return 0; } #endif diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 1f8d2f10a432..529c1ed7f59f 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifdef CONFIG_PPC64 #include @@ -607,7 +608,7 @@ static int xmon_sstep(struct pt_regs *regs) return 1; } -static int xmon_dabr_match(struct pt_regs *regs) +static int xmon_break_match(struct pt_regs *regs) { if ((regs->msr & (MSR_IR|MSR_PR|MSR_64BIT)) != (MSR_IR|MSR_64BIT)) return 0; @@ -740,8 +741,14 @@ static void insert_bpts(void) static void insert_cpu_bpts(void) { - if (dabr.enabled) - set_dabr(dabr.address | (dabr.enabled & 7), DABRX_ALL); + struct arch_hw_breakpoint brk; + + if (dabr.enabled) { + brk.address = dabr.address; + brk.type = (dabr.enabled & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL; + brk.len = 8; + set_break(&brk); + } if (iabr && cpu_has_feature(CPU_FTR_IABR)) mtspr(SPRN_IABR, iabr->address | (iabr->enabled & (BP_IABR|BP_IABR_TE))); @@ -769,7 +776,7 @@ static void remove_bpts(void) static void remove_cpu_bpts(void) { - set_dabr(0, 0); + hw_breakpoint_disable(); if (cpu_has_feature(CPU_FTR_IABR)) mtspr(SPRN_IABR, 0); } @@ -1138,7 +1145,7 @@ bpt_cmds(void) printf(badaddr); break; } - dabr.address &= ~7; + dabr.address &= ~HW_BRK_TYPE_DABR; dabr.enabled = mode | BP_DABR; } break; @@ -2917,7 +2924,7 @@ static void xmon_init(int enable) __debugger_bpt = xmon_bpt; __debugger_sstep = xmon_sstep; __debugger_iabr_match = xmon_iabr_match; - __debugger_dabr_match = xmon_dabr_match; + __debugger_break_match = xmon_break_match; __debugger_fault_handler = xmon_fault_handler; } else { __debugger = NULL; @@ -2925,7 +2932,7 @@ static void xmon_init(int enable) __debugger_bpt = NULL; __debugger_sstep = NULL; __debugger_iabr_match = NULL; - __debugger_dabr_match = NULL; + __debugger_break_match = NULL; __debugger_fault_handler = NULL; } } From bf99de36e48678c61adb697496e0364c610bbbfc Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 20 Dec 2012 14:06:45 +0000 Subject: [PATCH 0789/4607] powerpc: Add the DAWR support to the set_break() This adds DAWR supoprt to the set_break(). It does both bare metal and PAPR versions of setting the DAWR. There is still some work we can do to make full use of the watchpoint but that will come later. Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/machdep.h | 4 ++++ arch/powerpc/kernel/process.c | 23 +++++++++++++++++++++++ arch/powerpc/platforms/pseries/setup.c | 12 ++++++++++++ 3 files changed, 39 insertions(+) diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 19d9d96eb8d3..3d6b4100dac1 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -180,6 +180,10 @@ struct machdep_calls { int (*set_dabr)(unsigned long dabr, unsigned long dabrx); + /* Set DAWR for this platform, leave empty for default implemenation */ + int (*set_dawr)(unsigned long dawr, + unsigned long dawrx); + #ifdef CONFIG_PPC32 /* XXX for now */ /* A general init function, called by ppc_init in init/main.c. May be NULL. */ diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index c16c1c2abeea..8d56452e1dbd 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -407,10 +407,33 @@ static inline int set_dabr(struct arch_hw_breakpoint *brk) return __set_dabr(dabr, dabrx); } +static inline int set_dawr(struct arch_hw_breakpoint *brk) +{ + unsigned long dawr, dawrx; + + dawr = brk->address; + + dawrx = (brk->type & (HW_BRK_TYPE_READ | HW_BRK_TYPE_WRITE)) \ + << (63 - 58); //* read/write bits */ + dawrx |= ((brk->type & (HW_BRK_TYPE_TRANSLATE)) >> 2) \ + << (63 - 59); //* translate */ + dawrx |= (brk->type & (HW_BRK_TYPE_PRIV_ALL)) \ + >> 3; //* PRIM bits */ + + if (ppc_md.set_dawr) + return ppc_md.set_dawr(dawr, dawrx); + mtspr(SPRN_DAWR, dawr); + mtspr(SPRN_DAWRX, dawrx); + return 0; +} + int set_break(struct arch_hw_breakpoint *brk) { __get_cpu_var(current_brk) = *brk; + if (cpu_has_feature(CPU_FTR_DAWR)) + return set_dawr(brk); + return set_dabr(brk); } diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 1890730354bb..b1f60d162bb6 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -65,6 +65,7 @@ #include #include #include +#include #include "plpar_wrappers.h" #include "pseries.h" @@ -500,6 +501,14 @@ static int pseries_set_xdabr(unsigned long dabr, unsigned long dabrx) return plpar_hcall_norets(H_SET_XDABR, dabr, dabrx); } +static int pseries_set_dawr(unsigned long dawr, unsigned long dawrx) +{ + /* PAPR says we can't set HYP */ + dawrx &= ~DAWRX_HYP; + + return plapr_set_watchpoint0(dawr, dawrx); +} + #define CMO_CHARACTERISTICS_TOKEN 44 #define CMO_MAXLENGTH 1026 @@ -606,6 +615,9 @@ static void __init pSeries_init_early(void) else if (firmware_has_feature(FW_FEATURE_DABR)) ppc_md.set_dabr = pseries_set_dabr; + if (firmware_has_feature(FW_FEATURE_SET_MODE)) + ppc_md.set_dawr = pseries_set_dawr; + pSeries_cmo_feature_init(); iommu_init_early_pSeries(); From d69f1d7fa156ae2883bb3ba099046319cf00ccf6 Mon Sep 17 00:00:00 2001 From: Thomas Waldecker Date: Thu, 27 Dec 2012 04:07:27 +0000 Subject: [PATCH 0790/4607] Documentation/powerpc: Fix path to the powerpc directory ppc -> powerpc Signed-off-by: Thomas Waldecker Signed-off-by: Benjamin Herrenschmidt --- Documentation/powerpc/cpu_features.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/powerpc/cpu_features.txt b/Documentation/powerpc/cpu_features.txt index ffa4183fdb8b..ae09df8722c8 100644 --- a/Documentation/powerpc/cpu_features.txt +++ b/Documentation/powerpc/cpu_features.txt @@ -11,10 +11,10 @@ split instruction and data caches, and if the CPU supports the DOZE and NAP sleep modes. Detection of the feature set is simple. A list of processors can be found in -arch/ppc/kernel/cputable.c. The PVR register is masked and compared with each -value in the list. If a match is found, the cpu_features of cur_cpu_spec is -assigned to the feature bitmask for this processor and a __setup_cpu function -is called. +arch/powerpc/kernel/cputable.c. The PVR register is masked and compared with +each value in the list. If a match is found, the cpu_features of cur_cpu_spec +is assigned to the feature bitmask for this processor and a __setup_cpu +function is called. C code may test 'cur_cpu_spec[smp_processor_id()]->cpu_features' for a particular feature bit. This is done in quite a few places, for example @@ -51,6 +51,6 @@ should be used in the majority of cases. The END_FTR_SECTION macros are implemented by storing information about this code in the '__ftr_fixup' ELF section. When do_cpu_ftr_fixups -(arch/ppc/kernel/misc.S) is invoked, it will iterate over the records in +(arch/powerpc/kernel/misc.S) is invoked, it will iterate over the records in __ftr_fixup, and if the required feature is not present it will loop writing nop's from each BEGIN_FTR_SECTION to END_FTR_SECTION. From 7f966d394d6630313e02679de1875f67978f8bdd Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Fri, 28 Dec 2012 09:08:51 +0000 Subject: [PATCH 0791/4607] powerpc/iommu: Prevent false TCE leak message When a device DMA window includes the address 0, it's reserved in the TCE bitmap to avoid returning that address to drivers. When the device is removed, the bitmap is checked for any mappings not removed by the driver, indicating a possible DMA mapping leak. Since the reserved address is not cleared, a message is printed, warning of such a leak. Check for the reservation, and clear it before checking for any other standing mappings. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/iommu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index c862fd716fe3..31c4fdc6859c 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -717,6 +717,13 @@ void iommu_free_table(struct iommu_table *tbl, const char *node_name) return; } + /* + * In case we have reserved the first bit, we should not emit + * the warning below. + */ + if (tbl->it_offset == 0) + clear_bit(0, tbl->it_map); + /* verify that table contains no entries */ if (!bitmap_empty(tbl->it_map, tbl->it_size)) pr_warn("%s: Unexpected TCEs for %s\n", __func__, node_name); From d35d1424045df0a8ce125c7de59b4e7ad3c873fb Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Fri, 28 Dec 2012 09:13:18 +0000 Subject: [PATCH 0792/4607] powerpc/eeh/of: Checking for CONFIG_EEH is not needed The functions used are already defined as empty inline functions for the case where EEH is disabled. Signed-off-by: Thadeu Lima de Souza Cascardo Acked-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/of_platform.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index 07c12697d708..71c0469ab7fc 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -71,10 +71,8 @@ static int of_pci_phb_probe(struct platform_device *dev) eeh_dev_phb_init_dynamic(phb); /* Register devices with EEH */ -#ifdef CONFIG_EEH if (dev->dev.of_node->child) eeh_add_device_tree_early(dev->dev.of_node); -#endif /* CONFIG_EEH */ /* Scan the bus */ pcibios_scan_phb(phb); @@ -88,9 +86,7 @@ static int of_pci_phb_probe(struct platform_device *dev) pcibios_claim_one_bus(phb->bus); /* Finish EEH setup */ -#ifdef CONFIG_EEH eeh_add_device_tree_late(phb->bus); -#endif /* Add probed PCI devices to the device model */ pci_bus_add_devices(phb->bus); From 6a040ce72598159a74969a2d01ab0ba5ee6536b3 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Fri, 28 Dec 2012 09:13:19 +0000 Subject: [PATCH 0793/4607] powerpc/eeh: Fix crash when adding a device in a slot with DDW The DDW code uses a eeh_dev struct from the pci_dev. However, this is not set until eeh_add_device_late is called. Since pci_bus_add_devices is called before eeh_add_device_late, the PCI devices are added to the bus, making drivers' probe hooks to be called. These will call set_dma_mask, which will call the DDW code, which will require the eeh_dev struct from pci_dev. This would result in a crash, due to a NULL dereference. Calling eeh_add_device_late after pci_bus_add_devices would make the system BUG, because device files shouldn't be added to devices there were not added to the system. So, a new function is needed to add such files only after pci_bus_add_devices have been called. Cc: stable@vger.kernel.org Signed-off-by: Thadeu Lima de Souza Cascardo Acked-by: Gavin Shan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/eeh.h | 3 +++ arch/powerpc/kernel/of_platform.c | 3 +++ arch/powerpc/kernel/pci-common.c | 7 +++++-- arch/powerpc/platforms/pseries/eeh.c | 24 +++++++++++++++++++++++- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/eeh.h b/arch/powerpc/include/asm/eeh.h index a8fb03e22770..a80e32b46c11 100644 --- a/arch/powerpc/include/asm/eeh.h +++ b/arch/powerpc/include/asm/eeh.h @@ -201,6 +201,7 @@ int eeh_dev_check_failure(struct eeh_dev *edev); void __init eeh_addr_cache_build(void); void eeh_add_device_tree_early(struct device_node *); void eeh_add_device_tree_late(struct pci_bus *); +void eeh_add_sysfs_files(struct pci_bus *); void eeh_remove_bus_device(struct pci_dev *, int); /** @@ -240,6 +241,8 @@ static inline void eeh_add_device_tree_early(struct device_node *dn) { } static inline void eeh_add_device_tree_late(struct pci_bus *bus) { } +static inline void eeh_add_sysfs_files(struct pci_bus *bus) { } + static inline void eeh_remove_bus_device(struct pci_dev *dev, int purge_pe) { } static inline void eeh_lock(void) { } diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c index 71c0469ab7fc..a7b743076720 100644 --- a/arch/powerpc/kernel/of_platform.c +++ b/arch/powerpc/kernel/of_platform.c @@ -91,6 +91,9 @@ static int of_pci_phb_probe(struct platform_device *dev) /* Add probed PCI devices to the device model */ pci_bus_add_devices(phb->bus); + /* sysfs files should only be added after devices are added */ + eeh_add_sysfs_files(phb->bus); + return 0; } diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 7c37379ea9b1..fa12ae42d98c 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1477,11 +1477,14 @@ void pcibios_finish_adding_to_bus(struct pci_bus *bus) pcibios_allocate_bus_resources(bus); pcibios_claim_one_bus(bus); + /* Fixup EEH */ + eeh_add_device_tree_late(bus); + /* Add new devices to global lists. Register in proc, sysfs. */ pci_bus_add_devices(bus); - /* Fixup EEH */ - eeh_add_device_tree_late(bus); + /* sysfs files should only be added after devices are added */ + eeh_add_sysfs_files(bus); } EXPORT_SYMBOL_GPL(pcibios_finish_adding_to_bus); diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 9a04322b1736..6b73d6c44f51 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -788,7 +788,6 @@ static void eeh_add_device_late(struct pci_dev *dev) dev->dev.archdata.edev = edev; eeh_addr_cache_insert_dev(dev); - eeh_sysfs_add_device(dev); } /** @@ -814,6 +813,29 @@ void eeh_add_device_tree_late(struct pci_bus *bus) } EXPORT_SYMBOL_GPL(eeh_add_device_tree_late); +/** + * eeh_add_sysfs_files - Add EEH sysfs files for the indicated PCI bus + * @bus: PCI bus + * + * This routine must be used to add EEH sysfs files for PCI + * devices which are attached to the indicated PCI bus. The PCI bus + * is added after system boot through hotplug or dlpar. + */ +void eeh_add_sysfs_files(struct pci_bus *bus) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + eeh_sysfs_add_device(dev); + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + struct pci_bus *subbus = dev->subordinate; + if (subbus) + eeh_add_sysfs_files(subbus); + } + } +} +EXPORT_SYMBOL_GPL(eeh_add_sysfs_files); + /** * eeh_remove_device - Undo EEH setup for the indicated pci device * @dev: pci device to be removed From bc09c219b2e6f9436d06a1a3a10eff97faab371c Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Mon, 5 Nov 2012 15:53:54 +0000 Subject: [PATCH 0794/4607] powerpc/perf: Fix finding overflowed PMC in interrupt If a PMC is about to overflow on a counter that's on an active perf event (ie. less than 256 from the end) and a _different_ PMC overflows just at this time (a PMC that's not on an active perf event), we currently mark the event as found, but in reality it's not as it's likely the other PMC that caused the IRQ. Since we mark it as found the second catch all for overflows doesn't run, and we don't reset the overflowing PMC ever. Hence we keep hitting that same PMC IRQ over and over and don't reset the actual overflowing counter. This is a rewrite of the perf interrupt handler for book3s to get around this. We now check to see if any of the PMCs have actually overflowed (ie >= 0x80000000). If yes, record it for active counters and just reset it for inactive counters. If it's not overflowed, then we check to see if it's one of the buggy power7 counters and if it is, record it and continue. If none of the PMCs match this, then we make note that we couldn't find the PMC that caused the IRQ. Signed-off-by: Michael Neuling Reviewed-by: Sukadev Bhattiprolu cc: Paul Mackerras cc: Anton Blanchard cc: Linux PPC dev Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/perf/core-book3s.c | 87 +++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index aa2465e21f1a..53fc7b8e5d9a 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -1412,11 +1412,8 @@ unsigned long perf_instruction_pointer(struct pt_regs *regs) return regs->nip; } -static bool pmc_overflow(unsigned long val) +static bool pmc_overflow_power7(unsigned long val) { - if ((int)val < 0) - return true; - /* * Events on POWER7 can roll back if a speculative event doesn't * eventually complete. Unfortunately in some rare cases they will @@ -1428,7 +1425,15 @@ static bool pmc_overflow(unsigned long val) * PMCs because a user might set a period of less than 256 and we * don't want to mistakenly reset them. */ - if (pvr_version_is(PVR_POWER7) && ((0x80000000 - val) <= 256)) + if ((0x80000000 - val) <= 256) + return true; + + return false; +} + +static bool pmc_overflow(unsigned long val) +{ + if ((int)val < 0) return true; return false; @@ -1439,11 +1444,11 @@ static bool pmc_overflow(unsigned long val) */ static void perf_event_interrupt(struct pt_regs *regs) { - int i; + int i, j; struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); struct perf_event *event; - unsigned long val; - int found = 0; + unsigned long val[8]; + int found, active; int nmi; if (cpuhw->n_limited) @@ -1458,33 +1463,53 @@ static void perf_event_interrupt(struct pt_regs *regs) else irq_enter(); - for (i = 0; i < cpuhw->n_events; ++i) { - event = cpuhw->event[i]; - if (!event->hw.idx || is_limited_pmc(event->hw.idx)) - continue; - val = read_pmc(event->hw.idx); - if ((int)val < 0) { - /* event has overflowed */ - found = 1; - record_and_restart(event, val, regs); - } - } + /* Read all the PMCs since we'll need them a bunch of times */ + for (i = 0; i < ppmu->n_counter; ++i) + val[i] = read_pmc(i + 1); - /* - * In case we didn't find and reset the event that caused - * the interrupt, scan all events and reset any that are - * negative, to avoid getting continual interrupts. - * Any that we processed in the previous loop will not be negative. - */ - if (!found) { - for (i = 0; i < ppmu->n_counter; ++i) { - if (is_limited_pmc(i + 1)) + /* Try to find what caused the IRQ */ + found = 0; + for (i = 0; i < ppmu->n_counter; ++i) { + if (!pmc_overflow(val[i])) + continue; + if (is_limited_pmc(i + 1)) + continue; /* these won't generate IRQs */ + /* + * We've found one that's overflowed. For active + * counters we need to log this. For inactive + * counters, we need to reset it anyway + */ + found = 1; + active = 0; + for (j = 0; j < cpuhw->n_events; ++j) { + event = cpuhw->event[j]; + if (event->hw.idx == (i + 1)) { + active = 1; + record_and_restart(event, val[i], regs); + break; + } + } + if (!active) + /* reset non active counters that have overflowed */ + write_pmc(i + 1, 0); + } + if (!found && pvr_version_is(PVR_POWER7)) { + /* check active counters for special buggy p7 overflow */ + for (i = 0; i < cpuhw->n_events; ++i) { + event = cpuhw->event[i]; + if (!event->hw.idx || is_limited_pmc(event->hw.idx)) continue; - val = read_pmc(i + 1); - if (pmc_overflow(val)) - write_pmc(i + 1, 0); + if (pmc_overflow_power7(val[event->hw.idx - 1])) { + /* event has overflowed in a buggy way*/ + found = 1; + record_and_restart(event, + val[event->hw.idx - 1], + regs); + } } } + if ((!found) && printk_ratelimit()) + printk(KERN_WARNING "Can't find PMC that caused IRQ\n"); /* * Reset MMCR0 to its normal value. This will set PMXE and From e13e895f8430d8c8b7a51478a4abd481bf9b64fb Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Mon, 5 Nov 2012 15:08:38 +0000 Subject: [PATCH 0795/4607] powerpc/perf: Fix for PMCs not making progress On POWER7 when we have really small counts left before overflow, we can take a PMU IRQ, but the PMC gets wound back to just before the overflow. If the kernel is setting the PMC to a value just before the overflow, we can get interrupted again without the PMC making any progress (ie another buggy overflow). In this case, we can end up making no forward progress, with the PMC interrupt returning us to the same count over and over. The below detects when we are making no forward progress (ie. delta = 0) and then increases the amount left before the overflow. This stops us from locking up. Signed-off-by: Michael Neuling Reviewed-by: Sukadev Bhattiprolu cc: Paul Mackerras cc: Anton Blanchard cc: Linux PPC dev Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/perf/core-book3s.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 53fc7b8e5d9a..89bd59365c07 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -1349,6 +1349,8 @@ static void record_and_restart(struct perf_event *event, unsigned long val, */ val = 0; left = local64_read(&event->hw.period_left) - delta; + if (delta == 0) + left++; if (period) { if (left <= 0) { left += period; From 61383407677aef05928541a00678591abea2d84c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 10 Jan 2013 17:44:19 +1100 Subject: [PATCH 0796/4607] powerpc: Make room in exception vector area The FWNMI region is fixed at 0x7000 and the vector are now overflowing that with some configurations. Fix that by moving some hash management code out of that region as it doesn't need to be that close to the call sites (isn't accessed using conditional branches). Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/exceptions-64s.S | 110 +++++++++++++-------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index a28a65fd0f07..7a1c87cbb890 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -1180,6 +1180,61 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) .globl __end_handlers __end_handlers: + /* Equivalents to the above handlers for relocation-on interrupt vectors */ + STD_RELON_EXCEPTION_HV(., 0xe00, h_data_storage) + KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe00) + STD_RELON_EXCEPTION_HV(., 0xe20, h_instr_storage) + KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe20) + STD_RELON_EXCEPTION_HV(., 0xe40, emulation_assist) + KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe40) + STD_RELON_EXCEPTION_HV(., 0xe60, hmi_exception) + KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe60) + MASKABLE_RELON_EXCEPTION_HV(., 0xe80, h_doorbell) + KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe80) + + STD_RELON_EXCEPTION_PSERIES(., 0xf00, performance_monitor) + STD_RELON_EXCEPTION_PSERIES(., 0xf20, altivec_unavailable) + STD_RELON_EXCEPTION_PSERIES(., 0xf40, vsx_unavailable) + +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) +/* + * Data area reserved for FWNMI option. + * This address (0x7000) is fixed by the RPA. + */ + .= 0x7000 + .globl fwnmi_data_area +fwnmi_data_area: + + /* pseries and powernv need to keep the whole page from + * 0x7000 to 0x8000 free for use by the firmware + */ + . = 0x8000 +#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */ + +/* Space for CPU0's segment table */ + .balign 4096 + .globl initial_stab +initial_stab: + .space 4096 + +#ifdef CONFIG_PPC_POWERNV +_GLOBAL(opal_mc_secondary_handler) + HMT_MEDIUM_PPR_DISCARD + SET_SCRATCH0(r13) + GET_PACA(r13) + clrldi r3,r3,2 + tovirt(r3,r3) + std r3,PACA_OPAL_MC_EVT(r13) + ld r13,OPAL_MC_SRR0(r3) + mtspr SPRN_SRR0,r13 + ld r13,OPAL_MC_SRR1(r3) + mtspr SPRN_SRR1,r13 + ld r3,OPAL_MC_GPR3(r3) + GET_SCRATCH0(r13) + b machine_check_pSeries +#endif /* CONFIG_PPC_POWERNV */ + + /* * Hash table stuff */ @@ -1373,58 +1428,3 @@ _GLOBAL(do_stab_bolted) ld r13,PACA_EXSLB+EX_R13(r13) rfid b . /* prevent speculative execution */ - - - /* Equivalents to the above handlers for relocation-on interrupt vectors */ - STD_RELON_EXCEPTION_HV(., 0xe00, h_data_storage) - KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe00) - STD_RELON_EXCEPTION_HV(., 0xe20, h_instr_storage) - KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe20) - STD_RELON_EXCEPTION_HV(., 0xe40, emulation_assist) - KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe40) - STD_RELON_EXCEPTION_HV(., 0xe60, hmi_exception) - KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe60) - MASKABLE_RELON_EXCEPTION_HV(., 0xe80, h_doorbell) - KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe80) - - STD_RELON_EXCEPTION_PSERIES(., 0xf00, performance_monitor) - STD_RELON_EXCEPTION_PSERIES(., 0xf20, altivec_unavailable) - STD_RELON_EXCEPTION_PSERIES(., 0xf40, vsx_unavailable) - -#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) -/* - * Data area reserved for FWNMI option. - * This address (0x7000) is fixed by the RPA. - */ - .= 0x7000 - .globl fwnmi_data_area -fwnmi_data_area: - - /* pseries and powernv need to keep the whole page from - * 0x7000 to 0x8000 free for use by the firmware - */ - . = 0x8000 -#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */ - -/* Space for CPU0's segment table */ - .balign 4096 - .globl initial_stab -initial_stab: - .space 4096 - -#ifdef CONFIG_PPC_POWERNV -_GLOBAL(opal_mc_secondary_handler) - HMT_MEDIUM_PPR_DISCARD - SET_SCRATCH0(r13) - GET_PACA(r13) - clrldi r3,r3,2 - tovirt(r3,r3) - std r3,PACA_OPAL_MC_EVT(r13) - ld r13,OPAL_MC_SRR0(r3) - mtspr SPRN_SRR0,r13 - ld r13,OPAL_MC_SRR1(r3) - mtspr SPRN_SRR1,r13 - ld r3,OPAL_MC_GPR3(r3) - GET_SCRATCH0(r13) - b machine_check_pSeries -#endif /* CONFIG_PPC_POWERNV */ From ebe5a0596f2ee4182dc42b39583be71e81aa0443 Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Mon, 31 Dec 2012 09:29:34 +1300 Subject: [PATCH 0797/4607] gpio: vt8500: Export dedicated GPIO before multifunction pins. The vendor does not provide numbering for gpio pins. Vendor source exports dedicated gpio pins first, followed by multifunction pins. As this is what end users expect, this patch changes vt8500 and wm8505 to do the same. Signed-off-by: Tony Prisk Signed-off-by: Linus Walleij --- drivers/gpio/gpio-vt8500.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c index b53320a16fc8..9a7c434d2947 100644 --- a/drivers/gpio/gpio-vt8500.c +++ b/drivers/gpio/gpio-vt8500.c @@ -73,19 +73,20 @@ struct vt8500_gpio_data { static struct vt8500_gpio_data vt8500_data = { .num_banks = 7, .banks = { + VT8500_BANK(NO_REG, 0x3C, 0x5C, 0x7C, 9), VT8500_BANK(0x00, 0x20, 0x40, 0x60, 26), VT8500_BANK(0x04, 0x24, 0x44, 0x64, 28), VT8500_BANK(0x08, 0x28, 0x48, 0x68, 31), VT8500_BANK(0x0C, 0x2C, 0x4C, 0x6C, 19), VT8500_BANK(0x10, 0x30, 0x50, 0x70, 19), VT8500_BANK(0x14, 0x34, 0x54, 0x74, 23), - VT8500_BANK(NO_REG, 0x3C, 0x5C, 0x7C, 9), }, }; static struct vt8500_gpio_data wm8505_data = { .num_banks = 10, .banks = { + VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22), VT8500_BANK(0x40, 0x68, 0x90, 0xB8, 8), VT8500_BANK(0x44, 0x6C, 0x94, 0xBC, 32), VT8500_BANK(0x48, 0x70, 0x98, 0xC0, 6), @@ -95,7 +96,6 @@ static struct vt8500_gpio_data wm8505_data = { VT8500_BANK(0x58, 0x80, 0xA8, 0xD0, 5), VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12), VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16), - VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22), VT8500_BANK(0x500, 0x504, 0x508, 0x50C, 6), }, }; From 68e2ffed358c92341c126e606b500098fc72f5f7 Mon Sep 17 00:00:00 2001 From: Mihai Caraman Date: Tue, 11 Dec 2012 03:38:23 +0000 Subject: [PATCH 0798/4607] KVM: PPC: Fix SREGS documentation reference Reflect the uapi folder change in SREGS API documentation. Signed-off-by: Mihai Caraman Reviewed-by: Amos Kong Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index f2d6391178b9..4fc2bfcb16d5 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -345,7 +345,7 @@ struct kvm_sregs { __u64 interrupt_bitmap[(KVM_NR_INTERRUPTS + 63) / 64]; }; -/* ppc -- see arch/powerpc/include/asm/kvm.h */ +/* ppc -- see arch/powerpc/include/uapi/asm/kvm.h */ interrupt_bitmap is a bitmap of pending external interrupts. At most one bit may be set. This interrupt has been acknowledged by the APIC From 5a33169ed29060df71627103e6968078b42de945 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 14 Dec 2012 23:46:03 +0100 Subject: [PATCH 0799/4607] KVM: PPC: Only WARN on invalid emulation When we hit an emulation result that we didn't expect, that is an error, but it's nothing that warrants a BUG(), because it can be guest triggered. So instead, let's only WARN() the user that this happened. Signed-off-by: Alexander Graf --- arch/powerpc/kvm/powerpc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index be83fca2e8fd..e2225e5b8a4c 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -237,7 +237,8 @@ int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu) r = RESUME_HOST; break; default: - BUG(); + WARN_ON(1); + r = RESUME_GUEST; } return r; From 50c7bb80b5bd5a9962905306dd2292eeb9857d46 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 14 Dec 2012 23:42:05 +0100 Subject: [PATCH 0800/4607] KVM: PPC: Book3S: PR: Enable alternative instruction for SC 1 When running on top of pHyp, the hypercall instruction "sc 1" goes straight into pHyp without trapping in supervisor mode. So if we want to support PAPR guest in this configuration we need to add a second way of accessing PAPR hypercalls, preferably with the exact same semantics except for the instruction. So let's overlay an officially reserved instruction and emulate PAPR hypercalls whenever we hit that one. Signed-off-by: Alexander Graf --- arch/powerpc/include/asm/kvm_ppc.h | 1 + arch/powerpc/kvm/book3s_emulate.c | 28 ++++++++++++++++++++++++++++ arch/powerpc/kvm/book3s_pr.c | 5 +++++ 3 files changed, 34 insertions(+) diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 572aa7530619..5f5f69abd281 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -44,6 +44,7 @@ enum emulation_result { EMULATE_DO_DCR, /* kvm_run filled with DCR request */ EMULATE_FAIL, /* can't emulate this instruction */ EMULATE_AGAIN, /* something went wrong. go again */ + EMULATE_DO_PAPR, /* kvm_run filled with PAPR request */ }; extern int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu); diff --git a/arch/powerpc/kvm/book3s_emulate.c b/arch/powerpc/kvm/book3s_emulate.c index d31a716f7f2b..c88161bed8df 100644 --- a/arch/powerpc/kvm/book3s_emulate.c +++ b/arch/powerpc/kvm/book3s_emulate.c @@ -34,6 +34,8 @@ #define OP_31_XOP_MTSRIN 242 #define OP_31_XOP_TLBIEL 274 #define OP_31_XOP_TLBIE 306 +/* Opcode is officially reserved, reuse it as sc 1 when sc 1 doesn't trap */ +#define OP_31_XOP_FAKE_SC1 308 #define OP_31_XOP_SLBMTE 402 #define OP_31_XOP_SLBIE 434 #define OP_31_XOP_SLBIA 498 @@ -170,6 +172,32 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, vcpu->arch.mmu.tlbie(vcpu, addr, large); break; } +#ifdef CONFIG_KVM_BOOK3S_64_PR + case OP_31_XOP_FAKE_SC1: + { + /* SC 1 papr hypercalls */ + ulong cmd = kvmppc_get_gpr(vcpu, 3); + int i; + + if ((vcpu->arch.shared->msr & MSR_PR) || + !vcpu->arch.papr_enabled) { + emulated = EMULATE_FAIL; + break; + } + + if (kvmppc_h_pr(vcpu, cmd) == EMULATE_DONE) + break; + + run->papr_hcall.nr = cmd; + for (i = 0; i < 9; ++i) { + ulong gpr = kvmppc_get_gpr(vcpu, 4 + i); + run->papr_hcall.args[i] = gpr; + } + + emulated = EMULATE_DO_PAPR; + break; + } +#endif case OP_31_XOP_EIOIO: break; case OP_31_XOP_SLBMTE: diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 28d38adeca73..73ed11c41bac 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -760,6 +760,11 @@ program_interrupt: run->exit_reason = KVM_EXIT_MMIO; r = RESUME_HOST_NV; break; + case EMULATE_DO_PAPR: + run->exit_reason = KVM_EXIT_PAPR_HCALL; + vcpu->arch.hcall_needed = 1; + r = RESUME_HOST_NV; + break; default: BUG(); } From f2be655004ddc36f2c5fc5e541d481dcd782ab83 Mon Sep 17 00:00:00 2001 From: Mihai Caraman Date: Thu, 20 Dec 2012 04:52:39 +0000 Subject: [PATCH 0801/4607] KVM: PPC: Fix mfspr/mtspr MMUCFG emulation On mfspr/mtspr emulation path Book3E's MMUCFG SPR with value 1015 clashes with G4's MSSSR0 SPR. Move MSSSR0 emulation from generic part to Books3S. MSSSR0 also clashes with Book3S's DABRX SPR. DABRX was not explicitly handled so Book3S execution flow will behave as before. Signed-off-by: Mihai Caraman Signed-off-by: Alexander Graf --- arch/powerpc/kvm/book3s_emulate.c | 2 ++ arch/powerpc/kvm/emulate.c | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/kvm/book3s_emulate.c b/arch/powerpc/kvm/book3s_emulate.c index c88161bed8df..836c56975e21 100644 --- a/arch/powerpc/kvm/book3s_emulate.c +++ b/arch/powerpc/kvm/book3s_emulate.c @@ -455,6 +455,7 @@ int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val) case SPRN_PMC3_GEKKO: case SPRN_PMC4_GEKKO: case SPRN_WPAR_GEKKO: + case SPRN_MSSSR0: break; unprivileged: default: @@ -551,6 +552,7 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val) case SPRN_PMC3_GEKKO: case SPRN_PMC4_GEKKO: case SPRN_WPAR_GEKKO: + case SPRN_MSSSR0: *spr_val = 0; break; default: diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c index b0855e5d8905..71abcf4e2bda 100644 --- a/arch/powerpc/kvm/emulate.c +++ b/arch/powerpc/kvm/emulate.c @@ -149,8 +149,6 @@ static int kvmppc_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) case SPRN_TBWL: break; case SPRN_TBWU: break; - case SPRN_MSSSR0: break; - case SPRN_DEC: vcpu->arch.dec = spr_val; kvmppc_emulate_dec(vcpu); @@ -201,9 +199,6 @@ static int kvmppc_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt) case SPRN_PIR: spr_val = vcpu->vcpu_id; break; - case SPRN_MSSSR0: - spr_val = 0; - break; /* Note: mftb and TBRL/TBWL are user-accessible, so * the guest can always access the real TB anyways. From b8c649a99d582a6d8afd8457ba6145c624b8a76f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 20 Dec 2012 04:52:39 +0000 Subject: [PATCH 0802/4607] KVM: PPC: BookE: Allow irq deliveries to inject requests When injecting an interrupt into guest context, we usually don't need to check for requests anymore. At least not until today. With the introduction of EPR, we will have to create a request when the guest has successfully accepted an external interrupt though. So we need to prepare the interrupt delivery to abort guest entry gracefully. Otherwise we'd delay the EPR request. Signed-off-by: Alexander Graf --- arch/powerpc/kvm/booke.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 69f114015780..964f4475f55c 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -581,6 +581,11 @@ int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) kvmppc_core_check_exceptions(vcpu); + if (vcpu->requests) { + /* Exception delivery raised request; start over */ + return 1; + } + if (vcpu->arch.shared->msr & MSR_WE) { local_irq_enable(); kvm_vcpu_block(vcpu); From 37ecb257f68ce4fb7c7048a1123bbcbbe36d9575 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 4 Jan 2013 18:02:14 +0100 Subject: [PATCH 0803/4607] KVM: PPC: BookE: Emulate mfspr on EPR The EPR register is potentially valid for PR KVM as well, so we need to emulate accesses to it. It's only defined for reading, so only handle the mfspr case. Signed-off-by: Alexander Graf --- arch/powerpc/kvm/booke_emulate.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/powerpc/kvm/booke_emulate.c b/arch/powerpc/kvm/booke_emulate.c index 4685b8cf2249..27a4b2877c10 100644 --- a/arch/powerpc/kvm/booke_emulate.c +++ b/arch/powerpc/kvm/booke_emulate.c @@ -269,6 +269,9 @@ int kvmppc_booke_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val) case SPRN_ESR: *spr_val = vcpu->arch.shared->esr; break; + case SPRN_EPR: + *spr_val = vcpu->arch.epr; + break; case SPRN_CSRR0: *spr_val = vcpu->arch.csrr0; break; From 1c810636556c8d53a37406b34a64d9b9b0161aa6 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 4 Jan 2013 18:12:48 +0100 Subject: [PATCH 0804/4607] KVM: PPC: BookE: Implement EPR exit The External Proxy Facility in FSL BookE chips allows the interrupt controller to automatically acknowledge an interrupt as soon as a core gets its pending external interrupt delivered. Today, user space implements the interrupt controller, so we need to check on it during such a cycle. This patch implements logic for user space to enable EPR exiting, disable EPR exiting and EPR exiting itself, so that user space can acknowledge an interrupt when an external interrupt has successfully been delivered into the guest vcpu. Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 40 +++++++++++++++++++++++++++-- arch/powerpc/include/asm/kvm_host.h | 2 ++ arch/powerpc/include/asm/kvm_ppc.h | 9 +++++++ arch/powerpc/kvm/booke.c | 14 +++++++++- arch/powerpc/kvm/powerpc.c | 10 ++++++++ include/linux/kvm_host.h | 1 + include/uapi/linux/kvm.h | 6 +++++ 7 files changed, 79 insertions(+), 3 deletions(-) diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 4fc2bfcb16d5..a98ed09269d7 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -2246,8 +2246,8 @@ executed a memory-mapped I/O instruction which could not be satisfied by kvm. The 'data' member contains the written data if 'is_write' is true, and should be filled by application code otherwise. -NOTE: For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_DCR - and KVM_EXIT_PAPR the corresponding +NOTE: For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_DCR, + KVM_EXIT_PAPR and KVM_EXIT_EPR the corresponding operations are complete (and guest state is consistent) only after userspace has re-entered the kernel with KVM_RUN. The kernel side will first finish incomplete operations and then check for pending signals. Userspace @@ -2366,6 +2366,25 @@ interrupt for the target subchannel has been dequeued and subchannel_id, subchannel_nr, io_int_parm and io_int_word contain the parameters for that interrupt. ipb is needed for instruction parameter decoding. + /* KVM_EXIT_EPR */ + struct { + __u32 epr; + } epr; + +On FSL BookE PowerPC chips, the interrupt controller has a fast patch +interrupt acknowledge path to the core. When the core successfully +delivers an interrupt, it automatically populates the EPR register with +the interrupt vector number and acknowledges the interrupt inside +the interrupt controller. + +In case the interrupt controller lives in user space, we need to do +the interrupt acknowledge cycle through it to fetch the next to be +delivered interrupt vector using this exit. + +It gets triggered whenever both KVM_CAP_PPC_EPR are enabled and an +external interrupt has just been delivered into the guest. User space +should put the acknowledged interrupt vector into the 'epr' field. + /* Fix the size of the union. */ char padding[256]; }; @@ -2501,3 +2520,20 @@ handled in-kernel, while the other I/O instructions are passed to userspace. When this capability is enabled, KVM_EXIT_S390_TSCH will occur on TEST SUBCHANNEL intercepts. + +6.5 KVM_CAP_PPC_EPR + +Architectures: ppc +Parameters: args[0] defines whether the proxy facility is active +Returns: 0 on success; -1 on error + +This capability enables or disables the delivery of interrupts through the +external proxy facility. + +When enabled (args[0] != 0), every time the guest gets an external interrupt +delivered, it automatically exits into user space with a KVM_EXIT_EPR exit +to receive the topmost interrupt vector. + +When disabled (args[0] == 0), behavior is as if this facility is unsupported. + +When this capability is enabled, KVM_EXIT_EPR can occur. diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index ab49c6cf891c..8a72d59467eb 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -520,6 +520,8 @@ struct kvm_vcpu_arch { u8 sane; u8 cpu_type; u8 hcall_needed; + u8 epr_enabled; + u8 epr_needed; u32 cpr0_cfgaddr; /* holds the last set cpr0_cfgaddr */ diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 5f5f69abd281..493630e209c8 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -264,6 +264,15 @@ static inline void kvm_linear_init(void) {} #endif +static inline void kvmppc_set_epr(struct kvm_vcpu *vcpu, u32 epr) +{ +#ifdef CONFIG_KVM_BOOKE_HV + mtspr(SPRN_GEPR, epr); +#elif defined(CONFIG_BOOKE) + vcpu->arch.epr = epr; +#endif +} + int kvm_vcpu_ioctl_config_tlb(struct kvm_vcpu *vcpu, struct kvm_config_tlb *cfg); int kvm_vcpu_ioctl_dirty_tlb(struct kvm_vcpu *vcpu, diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 964f4475f55c..940ec806187e 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -306,7 +306,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, { int allowed = 0; ulong msr_mask = 0; - bool update_esr = false, update_dear = false; + bool update_esr = false, update_dear = false, update_epr = false; ulong crit_raw = vcpu->arch.shared->critical; ulong crit_r1 = kvmppc_get_gpr(vcpu, 1); bool crit; @@ -330,6 +330,9 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, keep_irq = true; } + if ((priority == BOOKE_IRQPRIO_EXTERNAL) && vcpu->arch.epr_enabled) + update_epr = true; + switch (priority) { case BOOKE_IRQPRIO_DTLB_MISS: case BOOKE_IRQPRIO_DATA_STORAGE: @@ -408,6 +411,8 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, set_guest_esr(vcpu, vcpu->arch.queued_esr); if (update_dear == true) set_guest_dear(vcpu, vcpu->arch.queued_dear); + if (update_epr == true) + kvm_make_request(KVM_REQ_EPR_EXIT, vcpu); new_msr &= msr_mask; #if defined(CONFIG_64BIT) @@ -615,6 +620,13 @@ int kvmppc_core_check_requests(struct kvm_vcpu *vcpu) r = 0; } + if (kvm_check_request(KVM_REQ_EPR_EXIT, vcpu)) { + vcpu->run->epr.epr = 0; + vcpu->arch.epr_needed = true; + vcpu->run->exit_reason = KVM_EXIT_EPR; + r = 0; + } + return r; } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index e2225e5b8a4c..934413cd3a1b 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -306,6 +306,7 @@ int kvm_dev_ioctl_check_extension(long ext) #ifdef CONFIG_BOOKE case KVM_CAP_PPC_BOOKE_SREGS: case KVM_CAP_PPC_BOOKE_WATCHDOG: + case KVM_CAP_PPC_EPR: #else case KVM_CAP_PPC_SEGSTATE: case KVM_CAP_PPC_HIOR: @@ -721,6 +722,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) for (i = 0; i < 9; ++i) kvmppc_set_gpr(vcpu, 4 + i, run->papr_hcall.args[i]); vcpu->arch.hcall_needed = 0; +#ifdef CONFIG_BOOKE + } else if (vcpu->arch.epr_needed) { + kvmppc_set_epr(vcpu, run->epr.epr); + vcpu->arch.epr_needed = 0; +#endif } r = kvmppc_vcpu_run(run, vcpu); @@ -762,6 +768,10 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, r = 0; vcpu->arch.papr_enabled = true; break; + case KVM_CAP_PPC_EPR: + r = 0; + vcpu->arch.epr_enabled = cap->args[0]; + break; #ifdef CONFIG_BOOKE case KVM_CAP_PPC_BOOKE_WATCHDOG: r = 0; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index cbe0d683e2e5..4dd7d7531e69 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -122,6 +122,7 @@ static inline bool is_error_page(struct page *page) #define KVM_REQ_WATCHDOG 18 #define KVM_REQ_MASTERCLOCK_UPDATE 19 #define KVM_REQ_MCLOCK_INPROGRESS 20 +#define KVM_REQ_EPR_EXIT 21 #define KVM_USERSPACE_IRQ_SOURCE_ID 0 #define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1 diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 8bb0bf83afc5..9a2db5767ed5 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -169,6 +169,7 @@ struct kvm_pit_config { #define KVM_EXIT_S390_UCONTROL 20 #define KVM_EXIT_WATCHDOG 21 #define KVM_EXIT_S390_TSCH 22 +#define KVM_EXIT_EPR 23 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -295,6 +296,10 @@ struct kvm_run { __u32 ipb; __u8 dequeued; } s390_tsch; + /* KVM_EXIT_EPR */ + struct { + __u32 epr; + } epr; /* Fix the size of the union. */ char padding[256]; }; @@ -656,6 +661,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_PPC_BOOKE_WATCHDOG 83 #define KVM_CAP_PPC_HTAB_FD 84 #define KVM_CAP_S390_CSS_SUPPORT 85 +#define KVM_CAP_PPC_EPR 86 #ifdef KVM_CAP_IRQ_ROUTING From 324b3e63167bce69e6622c2be182595790bf7e38 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Fri, 4 Jan 2013 18:28:51 +0100 Subject: [PATCH 0805/4607] KVM: PPC: BookE: Add EPR ONE_REG sync We need to be able to read and write the contents of the EPR register from user space. This patch implements that logic through the ONE_REG API and declares its (never implemented) SREGS counterpart as deprecated. Signed-off-by: Alexander Graf --- Documentation/virtual/kvm/api.txt | 1 + arch/powerpc/include/uapi/asm/kvm.h | 6 +++++- arch/powerpc/kvm/booke.c | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index a98ed09269d7..09905cbcbb0b 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1774,6 +1774,7 @@ registers, find a list below: PPC | KVM_REG_PPC_VPA_SLB | 128 PPC | KVM_REG_PPC_VPA_DTL | 128 PPC | KVM_REG_PPC_EPCR | 32 + PPC | KVM_REG_PPC_EPR | 32 4.69 KVM_GET_ONE_REG diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h index 2fba8a66fb10..16064d00adb9 100644 --- a/arch/powerpc/include/uapi/asm/kvm.h +++ b/arch/powerpc/include/uapi/asm/kvm.h @@ -114,7 +114,10 @@ struct kvm_regs { /* Embedded Floating Point (SPE) -- IVOR32-34 if KVM_SREGS_E_IVOR */ #define KVM_SREGS_E_SPE (1 << 9) -/* External Proxy (EXP) -- EPR */ +/* + * DEPRECATED! USE ONE_REG FOR THIS ONE! + * External Proxy (EXP) -- EPR + */ #define KVM_SREGS_EXP (1 << 10) /* External PID (E.PD) -- EPSC/EPLC */ @@ -412,5 +415,6 @@ struct kvm_get_htab_header { #define KVM_REG_PPC_VPA_DTL (KVM_REG_PPC | KVM_REG_SIZE_U128 | 0x84) #define KVM_REG_PPC_EPCR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x85) +#define KVM_REG_PPC_EPR (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0x86) #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 940ec806187e..8779cd4c52d9 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -300,6 +300,15 @@ static void set_guest_esr(struct kvm_vcpu *vcpu, u32 esr) #endif } +static unsigned long get_guest_epr(struct kvm_vcpu *vcpu) +{ +#ifdef CONFIG_KVM_BOOKE_HV + return mfspr(SPRN_GEPR); +#else + return vcpu->arch.epr; +#endif +} + /* Deliver the interrupt of the corresponding priority, if possible. */ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, unsigned int priority) @@ -1405,6 +1414,11 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) &vcpu->arch.dbg_reg.dac[dac], sizeof(u64)); break; } + case KVM_REG_PPC_EPR: { + u32 epr = get_guest_epr(vcpu); + r = put_user(epr, (u32 __user *)(long)reg->addr); + break; + } #if defined(CONFIG_64BIT) case KVM_REG_PPC_EPCR: r = put_user(vcpu->arch.epcr, (u32 __user *)(long)reg->addr); @@ -1437,6 +1451,13 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg) (u64 __user *)(long)reg->addr, sizeof(u64)); break; } + case KVM_REG_PPC_EPR: { + u32 new_epr; + r = get_user(new_epr, (u32 __user *)(long)reg->addr); + if (!r) + kvmppc_set_epr(vcpu, new_epr); + break; + } #if defined(CONFIG_64BIT) case KVM_REG_PPC_EPCR: { u32 new_epcr; From 097e3635dc35d1cfc14057f8006f1b1f0eaf1987 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Mon, 7 Jan 2013 18:51:52 +1100 Subject: [PATCH 0806/4607] iommu: moving initialization earlier The iommu_init() initializes IOMMU internal structures and data required for the IOMMU API as iommu_group_alloc(). It is registered as a subsys_initcall now. One of the IOMMU users is going to be a PCI subsystem on POWER. It discovers new IOMMU tables during the PCI scan so the logical place to call iommu_group_alloc() is the moment when a new group is discovered. However PCI scan is done from subsys_initcall hook as IOMMU does so PCI hook can be (and is) called before the IOMMU one. The patch moves IOMMU subsystem initialization one step earlier to make sure that IOMMU is initialized before PCI scan begins. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index ddbdacad7768..1065a1a19478 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -861,7 +861,7 @@ static int __init iommu_init(void) return 0; } -subsys_initcall(iommu_init); +arch_initcall(iommu_init); int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data) From c22885050e651c2f5d2a1706cdc2eb38698db968 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Tue, 8 Jan 2013 14:36:04 +0800 Subject: [PATCH 0807/4607] KVM: MMU: fix Dirty bit missed if CR0.WP = 0 If the write-fault access is from supervisor and CR0.WP is not set on the vcpu, kvm will fix it by adjusting pte access - it sets the W bit on pte and clears U bit. This is the chance that kvm can change pte access from readonly to writable Unfortunately, the pte access is the access of 'direct' shadow page table, means direct sp.role.access = pte_access, then we will create a writable spte entry on the readonly shadow page table. It will cause Dirty bit is not tracked when two guest ptes point to the same large page. Note, it does not have other impact except Dirty bit since cr0.wp is encoded into sp.role It can be fixed by adjusting pte access before establishing shadow page table. Also, after that, no mmu specified code exists in the common function and drop two parameters in set_spte Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/mmu.c | 47 +++++++++++--------------------------- arch/x86/kvm/paging_tmpl.h | 30 ++++++++++++++++++++---- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 01d7c2ad05f5..2a3c8909b144 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -2342,8 +2342,7 @@ static int mmu_need_write_protect(struct kvm_vcpu *vcpu, gfn_t gfn, } static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, - unsigned pte_access, int user_fault, - int write_fault, int level, + unsigned pte_access, int level, gfn_t gfn, pfn_t pfn, bool speculative, bool can_unsync, bool host_writable) { @@ -2378,9 +2377,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, spte |= (u64)pfn << PAGE_SHIFT; - if ((pte_access & ACC_WRITE_MASK) - || (!vcpu->arch.mmu.direct_map && write_fault - && !is_write_protection(vcpu) && !user_fault)) { + if (pte_access & ACC_WRITE_MASK) { /* * There are two cases: @@ -2399,19 +2396,6 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, spte |= PT_WRITABLE_MASK | SPTE_MMU_WRITEABLE; - if (!vcpu->arch.mmu.direct_map - && !(pte_access & ACC_WRITE_MASK)) { - spte &= ~PT_USER_MASK; - /* - * If we converted a user page to a kernel page, - * so that the kernel can write to it when cr0.wp=0, - * then we should prevent the kernel from executing it - * if SMEP is enabled. - */ - if (kvm_read_cr4_bits(vcpu, X86_CR4_SMEP)) - spte |= PT64_NX_MASK; - } - /* * Optimization: for pte sync, if spte was writable the hash * lookup is unnecessary (and expensive). Write protection @@ -2442,18 +2426,15 @@ done: static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned pt_access, unsigned pte_access, - int user_fault, int write_fault, - int *emulate, int level, gfn_t gfn, - pfn_t pfn, bool speculative, - bool host_writable) + int write_fault, int *emulate, int level, gfn_t gfn, + pfn_t pfn, bool speculative, bool host_writable) { int was_rmapped = 0; int rmap_count; - pgprintk("%s: spte %llx access %x write_fault %d" - " user_fault %d gfn %llx\n", + pgprintk("%s: spte %llx access %x write_fault %d gfn %llx\n", __func__, *sptep, pt_access, - write_fault, user_fault, gfn); + write_fault, gfn); if (is_rmap_spte(*sptep)) { /* @@ -2477,9 +2458,8 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, was_rmapped = 1; } - if (set_spte(vcpu, sptep, pte_access, user_fault, write_fault, - level, gfn, pfn, speculative, true, - host_writable)) { + if (set_spte(vcpu, sptep, pte_access, level, gfn, pfn, speculative, + true, host_writable)) { if (write_fault) *emulate = 1; kvm_mmu_flush_tlb(vcpu); @@ -2571,10 +2551,9 @@ static int direct_pte_prefetch_many(struct kvm_vcpu *vcpu, return -1; for (i = 0; i < ret; i++, gfn++, start++) - mmu_set_spte(vcpu, start, ACC_ALL, - access, 0, 0, NULL, - sp->role.level, gfn, - page_to_pfn(pages[i]), true, true); + mmu_set_spte(vcpu, start, ACC_ALL, access, 0, NULL, + sp->role.level, gfn, page_to_pfn(pages[i]), + true, true); return 0; } @@ -2636,8 +2615,8 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write, unsigned pte_access = ACC_ALL; mmu_set_spte(vcpu, iterator.sptep, ACC_ALL, pte_access, - 0, write, &emulate, - level, gfn, pfn, prefault, map_writable); + write, &emulate, level, gfn, pfn, + prefault, map_writable); direct_pte_prefetch(vcpu, iterator.sptep); ++vcpu->stat.pf_fixed; break; diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index 2ad76b981119..0d2e7113cb52 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -326,7 +326,7 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, * we call mmu_set_spte() with host_writable = true because * pte_prefetch_gfn_to_pfn always gets a writable pfn. */ - mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, 0, + mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, NULL, PT_PAGE_TABLE_LEVEL, gfn, pfn, true, true); return true; @@ -401,7 +401,7 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw, */ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, struct guest_walker *gw, - int user_fault, int write_fault, int hlevel, + int write_fault, int hlevel, pfn_t pfn, bool map_writable, bool prefault) { struct kvm_mmu_page *sp = NULL; @@ -474,7 +474,7 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, clear_sp_write_flooding_count(it.sptep); mmu_set_spte(vcpu, it.sptep, access, gw->pte_access, - user_fault, write_fault, &emulate, it.level, + write_fault, &emulate, it.level, gw->gfn, pfn, prefault, map_writable); FNAME(pte_prefetch)(vcpu, gw, it.sptep); @@ -560,6 +560,26 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, walker.gfn, pfn, walker.pte_access, &r)) return r; + /* + * Do not change pte_access if the pfn is a mmio page, otherwise + * we will cache the incorrect access into mmio spte. + */ + if (write_fault && !(walker.pte_access & ACC_WRITE_MASK) && + !is_write_protection(vcpu) && !user_fault && + !is_noslot_pfn(pfn)) { + walker.pte_access |= ACC_WRITE_MASK; + walker.pte_access &= ~ACC_USER_MASK; + + /* + * If we converted a user page to a kernel page, + * so that the kernel can write to it when cr0.wp=0, + * then we should prevent the kernel from executing it + * if SMEP is enabled. + */ + if (kvm_read_cr4_bits(vcpu, X86_CR4_SMEP)) + walker.pte_access &= ~ACC_EXEC_MASK; + } + spin_lock(&vcpu->kvm->mmu_lock); if (mmu_notifier_retry(vcpu->kvm, mmu_seq)) goto out_unlock; @@ -568,7 +588,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, kvm_mmu_free_some_pages(vcpu); if (!force_pt_level) transparent_hugepage_adjust(vcpu, &walker.gfn, &pfn, &level); - r = FNAME(fetch)(vcpu, addr, &walker, user_fault, write_fault, + r = FNAME(fetch)(vcpu, addr, &walker, write_fault, level, pfn, map_writable, prefault); ++vcpu->stat.pf_fixed; kvm_mmu_audit(vcpu, AUDIT_POST_PAGE_FAULT); @@ -743,7 +763,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) host_writable = sp->spt[i] & SPTE_HOST_WRITEABLE; - set_spte(vcpu, &sp->spt[i], pte_access, 0, 0, + set_spte(vcpu, &sp->spt[i], pte_access, PT_PAGE_TABLE_LEVEL, gfn, spte_to_pfn(sp->spt[i]), true, false, host_writable); From 7751babd3c6d365316e7a405f516bdd0bc7cec60 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Tue, 8 Jan 2013 14:36:51 +0800 Subject: [PATCH 0808/4607] KVM: MMU: fix infinite fault access retry We have two issues in current code: - if target gfn is used as its page table, guest will refault then kvm will use small page size to map it. We need two #PF to fix its shadow page table - sometimes, say a exception is triggered during vm-exit caused by #PF (see handle_exception() in vmx.c), we remove all the shadow pages shadowed by the target gfn before go into page fault path, it will cause infinite loop: delete shadow pages shadowed by the gfn -> try to use large page size to map the gfn -> retry the access ->... To fix these, we can adjust page size early if the target gfn is used as page table Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/mmu.c | 13 ++++--------- arch/x86/kvm/paging_tmpl.h | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 2a3c8909b144..54fc61e4b061 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -2380,15 +2380,10 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep, if (pte_access & ACC_WRITE_MASK) { /* - * There are two cases: - * - the one is other vcpu creates new sp in the window - * between mapping_level() and acquiring mmu-lock. - * - the another case is the new sp is created by itself - * (page-fault path) when guest uses the target gfn as - * its page table. - * Both of these cases can be fixed by allowing guest to - * retry the access, it will refault, then we can establish - * the mapping by using small page. + * Other vcpu creates new sp in the window between + * mapping_level() and acquiring mmu-lock. We can + * allow guest to retry the access, the mapping can + * be fixed if guest refault. */ if (level > PT_PAGE_TABLE_LEVEL && has_wrprotected_page(vcpu->kvm, gfn, level)) diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index 0d2e7113cb52..3d1a35237dbf 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -487,6 +487,38 @@ out_gpte_changed: return 0; } + /* + * To see whether the mapped gfn can write its page table in the current + * mapping. + * + * It is the helper function of FNAME(page_fault). When guest uses large page + * size to map the writable gfn which is used as current page table, we should + * force kvm to use small page size to map it because new shadow page will be + * created when kvm establishes shadow page table that stop kvm using large + * page size. Do it early can avoid unnecessary #PF and emulation. + * + * Note: the PDPT page table is not checked for PAE-32 bit guest. It is ok + * since the PDPT is always shadowed, that means, we can not use large page + * size to map the gfn which is used as PDPT. + */ +static bool +FNAME(is_self_change_mapping)(struct kvm_vcpu *vcpu, + struct guest_walker *walker, int user_fault) +{ + int level; + gfn_t mask = ~(KVM_PAGES_PER_HPAGE(walker->level) - 1); + + if (!(walker->pte_access & ACC_WRITE_MASK || + (!is_write_protection(vcpu) && !user_fault))) + return false; + + for (level = walker->level; level <= walker->max_level; level++) + if (!((walker->gfn ^ walker->table_gfn[level - 1]) & mask)) + return true; + + return false; +} + /* * Page fault handler. There are several causes for a page fault: * - there is no shadow pte for the guest pte @@ -541,7 +573,8 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, } if (walker.level >= PT_DIRECTORY_LEVEL) - force_pt_level = mapping_level_dirty_bitmap(vcpu, walker.gfn); + force_pt_level = mapping_level_dirty_bitmap(vcpu, walker.gfn) + || FNAME(is_self_change_mapping)(vcpu, &walker, user_fault); else force_pt_level = 1; if (!force_pt_level) { From f79ed82da494bc2ea677c6fc28b5439eacf4f5cc Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 8 Jan 2013 13:00:01 +0100 Subject: [PATCH 0809/4607] KVM: trace: Fix exit decoding. trace_kvm_userspace_exit has been missing the KVM_EXIT_WATCHDOG exit. CC: Bharat Bhushan Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- include/trace/events/kvm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h index a23f47c884cf..19911dddaeb7 100644 --- a/include/trace/events/kvm.h +++ b/include/trace/events/kvm.h @@ -14,7 +14,7 @@ ERSN(SHUTDOWN), ERSN(FAIL_ENTRY), ERSN(INTR), ERSN(SET_TPR), \ ERSN(TPR_ACCESS), ERSN(S390_SIEIC), ERSN(S390_RESET), ERSN(DCR),\ ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI), ERSN(PAPR_HCALL), \ - ERSN(S390_UCONTROL), ERSN(S390_TSCH) + ERSN(S390_UCONTROL), ERSN(WATCHDOG), ERSN(S390_TSCH) TRACE_EVENT(kvm_userspace_exit, TP_PROTO(__u32 reason, int errno), From 16c906e51c6f08a15763a85b5686e1ded35e77ab Mon Sep 17 00:00:00 2001 From: Asai Thambi S P Date: Thu, 20 Dec 2012 07:46:25 -0800 Subject: [PATCH 0810/4607] mtip32xx: Add workqueue and NUMA support This patch contains * parallel command completion using workers * bind the workers to the chosen numa node * bind isr to the chosen numa node * allocating memory in the chosen numa node Signed-off-by: Asai Thambi S P Signed-off-by: Sam Bradshaw Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/mtip32xx.c | 335 +++++++++++++++++++++++------- drivers/block/mtip32xx/mtip32xx.h | 31 ++- 2 files changed, 283 insertions(+), 83 deletions(-) diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 9694dd99bbbc..99c2cf461be3 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -88,6 +88,8 @@ static int instance; static int mtip_major; static struct dentry *dfs_parent; +static u32 cpu_use[NR_CPUS]; + static DEFINE_SPINLOCK(rssd_index_lock); static DEFINE_IDA(rssd_index_ida); @@ -296,16 +298,17 @@ static int hba_reset_nosleep(struct driver_data *dd) */ static inline void mtip_issue_ncq_command(struct mtip_port *port, int tag) { + int group = tag >> 5; + atomic_set(&port->commands[tag].active, 1); - spin_lock(&port->cmd_issue_lock); - + /* guard SACT and CI registers */ + spin_lock(&port->cmd_issue_lock[group]); writel((1 << MTIP_TAG_BIT(tag)), port->s_active[MTIP_TAG_INDEX(tag)]); writel((1 << MTIP_TAG_BIT(tag)), port->cmd_issue[MTIP_TAG_INDEX(tag)]); - - spin_unlock(&port->cmd_issue_lock); + spin_unlock(&port->cmd_issue_lock[group]); /* Set the command's timeout value.*/ port->commands[tag].comp_time = jiffies + msecs_to_jiffies( @@ -963,56 +966,56 @@ handle_tfe_exit: /* * Handle a set device bits interrupt */ -static inline void mtip_process_sdbf(struct driver_data *dd) +static inline void mtip_workq_sdbfx(struct mtip_port *port, int group, + u32 completed) { - struct mtip_port *port = dd->port; - int group, tag, bit; - u32 completed; + struct driver_data *dd = port->dd; + int tag, bit; struct mtip_cmd *command; - /* walk all bits in all slot groups */ - for (group = 0; group < dd->slot_groups; group++) { - completed = readl(port->completed[group]); - if (!completed) - continue; + if (!completed) { + WARN_ON_ONCE(!completed); + return; + } + /* clear completed status register in the hardware.*/ + writel(completed, port->completed[group]); - /* clear completed status register in the hardware.*/ - writel(completed, port->completed[group]); + /* Process completed commands. */ + for (bit = 0; (bit < 32) && completed; bit++) { + if (completed & 0x01) { + tag = (group << 5) | bit; - /* Process completed commands. */ - for (bit = 0; - (bit < 32) && completed; - bit++, completed >>= 1) { - if (completed & 0x01) { - tag = (group << 5) | bit; + /* skip internal command slot. */ + if (unlikely(tag == MTIP_TAG_INTERNAL)) + continue; - /* skip internal command slot. */ - if (unlikely(tag == MTIP_TAG_INTERNAL)) - continue; + command = &port->commands[tag]; + /* make internal callback */ + if (likely(command->comp_func)) { + command->comp_func( + port, + tag, + command->comp_data, + 0); + } else { + dev_warn(&dd->pdev->dev, + "Null completion " + "for tag %d", + tag); - command = &port->commands[tag]; - /* make internal callback */ - if (likely(command->comp_func)) { - command->comp_func( - port, - tag, - command->comp_data, - 0); - } else { - dev_warn(&dd->pdev->dev, - "Null completion " - "for tag %d", - tag); - - if (mtip_check_surprise_removal( - dd->pdev)) { - mtip_command_cleanup(dd); - return; - } + if (mtip_check_surprise_removal( + dd->pdev)) { + mtip_command_cleanup(dd); + return; } } } + completed >>= 1; } + + /* If last, re-enable interrupts */ + if (atomic_dec_return(&dd->irq_workers_active) == 0) + writel(0xffffffff, dd->mmio + HOST_IRQ_STAT); } /* @@ -1071,6 +1074,8 @@ static inline irqreturn_t mtip_handle_irq(struct driver_data *data) struct mtip_port *port = dd->port; u32 hba_stat, port_stat; int rv = IRQ_NONE; + int do_irq_enable = 1, i, workers; + struct mtip_work *twork; hba_stat = readl(dd->mmio + HOST_IRQ_STAT); if (hba_stat) { @@ -1081,8 +1086,42 @@ static inline irqreturn_t mtip_handle_irq(struct driver_data *data) writel(port_stat, port->mmio + PORT_IRQ_STAT); /* Demux port status */ - if (likely(port_stat & PORT_IRQ_SDB_FIS)) - mtip_process_sdbf(dd); + if (likely(port_stat & PORT_IRQ_SDB_FIS)) { + do_irq_enable = 0; + WARN_ON_ONCE(atomic_read(&dd->irq_workers_active) != 0); + + /* Start at 1: group zero is always local? */ + for (i = 0, workers = 0; i < MTIP_MAX_SLOT_GROUPS; + i++) { + twork = &dd->work[i]; + twork->completed = readl(port->completed[i]); + if (twork->completed) + workers++; + } + + atomic_set(&dd->irq_workers_active, workers); + if (workers) { + for (i = 1; i < MTIP_MAX_SLOT_GROUPS; i++) { + twork = &dd->work[i]; + if (twork->completed) + queue_work_on( + twork->cpu_binding, + dd->isr_workq, + &twork->work); + } + + if (likely(dd->work[0].completed)) + mtip_workq_sdbfx(port, 0, + dd->work[0].completed); + + } else { + /* + * Chip quirk: SDB interrupt but nothing + * to complete + */ + do_irq_enable = 1; + } + } if (unlikely(port_stat & PORT_IRQ_ERR)) { if (unlikely(mtip_check_surprise_removal(dd->pdev))) { @@ -1102,20 +1141,12 @@ static inline irqreturn_t mtip_handle_irq(struct driver_data *data) } /* acknowledge interrupt */ - writel(hba_stat, dd->mmio + HOST_IRQ_STAT); + if (unlikely(do_irq_enable)) + writel(hba_stat, dd->mmio + HOST_IRQ_STAT); return rv; } -/* - * Wrapper for mtip_handle_irq - * (ignores return code) - */ -static void mtip_tasklet(unsigned long data) -{ - mtip_handle_irq((struct driver_data *) data); -} - /* * HBA interrupt subroutine. * @@ -1129,8 +1160,8 @@ static void mtip_tasklet(unsigned long data) static irqreturn_t mtip_irq_handler(int irq, void *instance) { struct driver_data *dd = instance; - tasklet_schedule(&dd->tasklet); - return IRQ_HANDLED; + + return mtip_handle_irq(dd); } static void mtip_issue_non_ncq_command(struct mtip_port *port, int tag) @@ -3004,20 +3035,24 @@ static int mtip_hw_init(struct driver_data *dd) hba_setup(dd); - tasklet_init(&dd->tasklet, mtip_tasklet, (unsigned long)dd); - - dd->port = kzalloc(sizeof(struct mtip_port), GFP_KERNEL); + dd->port = kzalloc_node(sizeof(struct mtip_port), GFP_KERNEL, + dd->numa_node); if (!dd->port) { dev_err(&dd->pdev->dev, "Memory allocation: port structure\n"); return -ENOMEM; } + /* Continue workqueue setup */ + for (i = 0; i < MTIP_MAX_SLOT_GROUPS; i++) + dd->work[i].port = dd->port; + /* Counting semaphore to track command slot usage */ sema_init(&dd->port->cmd_slot, num_command_slots - 1); /* Spinlock to prevent concurrent issue */ - spin_lock_init(&dd->port->cmd_issue_lock); + for (i = 0; i < MTIP_MAX_SLOT_GROUPS; i++) + spin_lock_init(&dd->port->cmd_issue_lock[i]); /* Set the port mmio base address. */ dd->port->mmio = dd->mmio + PORT_OFFSET; @@ -3164,6 +3199,7 @@ static int mtip_hw_init(struct driver_data *dd) "Unable to allocate IRQ %d\n", dd->pdev->irq); goto out2; } + irq_set_affinity_hint(dd->pdev->irq, get_cpu_mask(dd->isr_binding)); /* Enable interrupts on the HBA. */ writel(readl(dd->mmio + HOST_CTL) | HOST_IRQ_EN, @@ -3240,7 +3276,8 @@ out3: writel(readl(dd->mmio + HOST_CTL) & ~HOST_IRQ_EN, dd->mmio + HOST_CTL); - /*Release the IRQ. */ + /* Release the IRQ. */ + irq_set_affinity_hint(dd->pdev->irq, NULL); devm_free_irq(&dd->pdev->dev, dd->pdev->irq, dd); out2: @@ -3290,11 +3327,9 @@ static int mtip_hw_exit(struct driver_data *dd) del_timer_sync(&dd->port->cmd_timer); /* Release the IRQ. */ + irq_set_affinity_hint(dd->pdev->irq, NULL); devm_free_irq(&dd->pdev->dev, dd->pdev->irq, dd); - /* Stop the bottom half tasklet. */ - tasklet_kill(&dd->tasklet); - /* Free the command/command header memory. */ dmam_free_coherent(&dd->pdev->dev, HW_PORT_PRIV_DMA_SZ + (ATA_SECT_SIZE * 4), @@ -3710,7 +3745,7 @@ static int mtip_block_initialize(struct driver_data *dd) goto protocol_init_error; } - dd->disk = alloc_disk(MTIP_MAX_MINORS); + dd->disk = alloc_disk_node(MTIP_MAX_MINORS, dd->numa_node); if (dd->disk == NULL) { dev_err(&dd->pdev->dev, "Unable to allocate gendisk structure\n"); @@ -3754,7 +3789,7 @@ static int mtip_block_initialize(struct driver_data *dd) skip_create_disk: /* Allocate the request queue. */ - dd->queue = blk_alloc_queue(GFP_KERNEL); + dd->queue = blk_alloc_queue_node(GFP_KERNEL, dd->numa_node); if (dd->queue == NULL) { dev_err(&dd->pdev->dev, "Unable to allocate request queue\n"); @@ -3812,9 +3847,8 @@ skip_create_disk: start_service_thread: sprintf(thd_name, "mtip_svc_thd_%02d", index); - - dd->mtip_svc_handler = kthread_run(mtip_service_thread, - dd, thd_name); + dd->mtip_svc_handler = kthread_create_on_node(mtip_service_thread, + dd, dd->numa_node, thd_name); if (IS_ERR(dd->mtip_svc_handler)) { dev_err(&dd->pdev->dev, "service thread failed to start\n"); @@ -3822,7 +3856,7 @@ start_service_thread: rv = -EFAULT; goto kthread_run_error; } - + wake_up_process(dd->mtip_svc_handler); if (wait_for_rebuild == MTIP_FTL_REBUILD_MAGIC) rv = wait_for_rebuild; @@ -3951,6 +3985,56 @@ static int mtip_block_resume(struct driver_data *dd) return 0; } +static void drop_cpu(int cpu) +{ + cpu_use[cpu]--; +} + +static int get_least_used_cpu_on_node(int node) +{ + int cpu, least_used_cpu, least_cnt; + const struct cpumask *node_mask; + + node_mask = cpumask_of_node(node); + least_used_cpu = cpumask_first(node_mask); + least_cnt = cpu_use[least_used_cpu]; + cpu = least_used_cpu; + + for_each_cpu(cpu, node_mask) { + if (cpu_use[cpu] < least_cnt) { + least_used_cpu = cpu; + least_cnt = cpu_use[cpu]; + } + } + cpu_use[least_used_cpu]++; + return least_used_cpu; +} + +/* Helper for selecting a node in round robin mode */ +static inline int mtip_get_next_rr_node(void) +{ + static int next_node = -1; + + if (next_node == -1) { + next_node = first_online_node; + return next_node; + } + + next_node = next_online_node(next_node); + if (next_node == MAX_NUMNODES) + next_node = first_online_node; + return next_node; +} + +DEFINE_HANDLER(0); +DEFINE_HANDLER(1); +DEFINE_HANDLER(2); +DEFINE_HANDLER(3); +DEFINE_HANDLER(4); +DEFINE_HANDLER(5); +DEFINE_HANDLER(6); +DEFINE_HANDLER(7); + /* * Called for each supported PCI device detected. * @@ -3965,9 +4049,25 @@ static int mtip_pci_probe(struct pci_dev *pdev, { int rv = 0; struct driver_data *dd = NULL; + char cpu_list[256]; + const struct cpumask *node_mask; + int cpu, i = 0, j = 0; + int my_node = NUMA_NO_NODE; /* Allocate memory for this devices private data. */ - dd = kzalloc(sizeof(struct driver_data), GFP_KERNEL); + my_node = pcibus_to_node(pdev->bus); + if (my_node != NUMA_NO_NODE) { + if (!node_online(my_node)) + my_node = mtip_get_next_rr_node(); + } else { + dev_info(&pdev->dev, "Kernel not reporting proximity, choosing a node\n"); + my_node = mtip_get_next_rr_node(); + } + dev_info(&pdev->dev, "NUMA node %d (closest: %d,%d, probe on %d:%d)\n", + my_node, pcibus_to_node(pdev->bus), dev_to_node(&pdev->dev), + cpu_to_node(smp_processor_id()), smp_processor_id()); + + dd = kzalloc_node(sizeof(struct driver_data), GFP_KERNEL, my_node); if (dd == NULL) { dev_err(&pdev->dev, "Unable to allocate memory for driver data\n"); @@ -4004,19 +4104,82 @@ static int mtip_pci_probe(struct pci_dev *pdev, } } - pci_set_master(pdev); + /* Copy the info we may need later into the private data structure. */ + dd->major = mtip_major; + dd->instance = instance; + dd->pdev = pdev; + dd->numa_node = my_node; + memset(dd->workq_name, 0, 32); + snprintf(dd->workq_name, 31, "mtipq%d", dd->instance); + + dd->isr_workq = create_workqueue(dd->workq_name); + if (!dd->isr_workq) { + dev_warn(&pdev->dev, "Can't create wq %d\n", dd->instance); + goto block_initialize_err; + } + + memset(cpu_list, 0, sizeof(cpu_list)); + + node_mask = cpumask_of_node(dd->numa_node); + if (!cpumask_empty(node_mask)) { + for_each_cpu(cpu, node_mask) + { + snprintf(&cpu_list[j], 256 - j, "%d ", cpu); + j = strlen(cpu_list); + } + + dev_info(&pdev->dev, "Node %d on package %d has %d cpu(s): %s\n", + dd->numa_node, + topology_physical_package_id(cpumask_first(node_mask)), + nr_cpus_node(dd->numa_node), + cpu_list); + } else + dev_dbg(&pdev->dev, "mtip32xx: node_mask empty\n"); + + dd->isr_binding = get_least_used_cpu_on_node(dd->numa_node); + dev_info(&pdev->dev, "Initial IRQ binding node:cpu %d:%d\n", + cpu_to_node(dd->isr_binding), dd->isr_binding); + + /* first worker context always runs in ISR */ + dd->work[0].cpu_binding = dd->isr_binding; + dd->work[1].cpu_binding = get_least_used_cpu_on_node(dd->numa_node); + dd->work[2].cpu_binding = get_least_used_cpu_on_node(dd->numa_node); + dd->work[3].cpu_binding = dd->work[0].cpu_binding; + dd->work[4].cpu_binding = dd->work[1].cpu_binding; + dd->work[5].cpu_binding = dd->work[2].cpu_binding; + dd->work[6].cpu_binding = dd->work[2].cpu_binding; + dd->work[7].cpu_binding = dd->work[1].cpu_binding; + + /* Log the bindings */ + for_each_present_cpu(cpu) { + memset(cpu_list, 0, sizeof(cpu_list)); + for (i = 0, j = 0; i < MTIP_MAX_SLOT_GROUPS; i++) { + if (dd->work[i].cpu_binding == cpu) { + snprintf(&cpu_list[j], 256 - j, "%d ", i); + j = strlen(cpu_list); + } + } + if (j) + dev_info(&pdev->dev, "CPU %d: WQs %s\n", cpu, cpu_list); + } + + INIT_WORK(&dd->work[0].work, mtip_workq_sdbf0); + INIT_WORK(&dd->work[1].work, mtip_workq_sdbf1); + INIT_WORK(&dd->work[2].work, mtip_workq_sdbf2); + INIT_WORK(&dd->work[3].work, mtip_workq_sdbf3); + INIT_WORK(&dd->work[4].work, mtip_workq_sdbf4); + INIT_WORK(&dd->work[5].work, mtip_workq_sdbf5); + INIT_WORK(&dd->work[6].work, mtip_workq_sdbf6); + INIT_WORK(&dd->work[7].work, mtip_workq_sdbf7); + + pci_set_master(pdev); if (pci_enable_msi(pdev)) { dev_warn(&pdev->dev, "Unable to enable MSI interrupt.\n"); goto block_initialize_err; } - /* Copy the info we may need later into the private data structure. */ - dd->major = mtip_major; - dd->instance = instance; - dd->pdev = pdev; - /* Initialize the block layer. */ rv = mtip_block_initialize(dd); if (rv < 0) { @@ -4036,7 +4199,13 @@ static int mtip_pci_probe(struct pci_dev *pdev, block_initialize_err: pci_disable_msi(pdev); - + if (dd->isr_workq) { + flush_workqueue(dd->isr_workq); + destroy_workqueue(dd->isr_workq); + drop_cpu(dd->work[0].cpu_binding); + drop_cpu(dd->work[1].cpu_binding); + drop_cpu(dd->work[2].cpu_binding); + } setmask_err: pcim_iounmap_regions(pdev, 1 << MTIP_ABAR); @@ -4077,6 +4246,14 @@ static void mtip_pci_remove(struct pci_dev *pdev) /* Clean up the block layer. */ mtip_block_remove(dd); + if (dd->isr_workq) { + flush_workqueue(dd->isr_workq); + destroy_workqueue(dd->isr_workq); + drop_cpu(dd->work[0].cpu_binding); + drop_cpu(dd->work[1].cpu_binding); + drop_cpu(dd->work[2].cpu_binding); + } + pci_disable_msi(pdev); kfree(dd); diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h index b1742640556a..d782b1aa913e 100644 --- a/drivers/block/mtip32xx/mtip32xx.h +++ b/drivers/block/mtip32xx/mtip32xx.h @@ -164,6 +164,20 @@ struct smart_attr { u8 res[3]; } __packed; +struct mtip_work { + struct work_struct work; + void *port; + int cpu_binding; + u32 completed; +} ____cacheline_aligned_in_smp; + +#define DEFINE_HANDLER(group) \ + void mtip_workq_sdbf##group(struct work_struct *work) \ + { \ + struct mtip_work *w = (struct mtip_work *) work; \ + mtip_workq_sdbfx(w->port, group, w->completed); \ + } + /* Register Frame Information Structure (FIS), host to device. */ struct host_to_dev_fis { /* @@ -424,7 +438,7 @@ struct mtip_port { */ struct semaphore cmd_slot; /* Spinlock for working around command-issue bug. */ - spinlock_t cmd_issue_lock; + spinlock_t cmd_issue_lock[MTIP_MAX_SLOT_GROUPS]; }; /* @@ -447,9 +461,6 @@ struct driver_data { struct mtip_port *port; /* Pointer to the port data structure. */ - /* Tasklet used to process the bottom half of the ISR. */ - struct tasklet_struct tasklet; - unsigned product_type; /* magic value declaring the product type */ unsigned slot_groups; /* number of slot groups the product supports */ @@ -461,6 +472,18 @@ struct driver_data { struct task_struct *mtip_svc_handler; /* task_struct of svc thd */ struct dentry *dfs_node; + + int numa_node; /* NUMA support */ + + char workq_name[32]; + + struct workqueue_struct *isr_workq; + + struct mtip_work work[MTIP_MAX_SLOT_GROUPS]; + + atomic_t irq_workers_active; + + int isr_binding; }; #endif From 152834694dcb90d3088bc77fa436755c66553929 Mon Sep 17 00:00:00 2001 From: Asai Thambi S P Date: Fri, 11 Jan 2013 14:41:34 +0100 Subject: [PATCH 0811/4607] mtip32xx: add trim support TRIM support added through vendor unique command. Signed-off-by: Sam Bradshaw < sbradshaw@micron.com> Signed-off-by: Asai Thambi S P Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/mtip32xx.c | 96 +++++++++++++++++++++++++++++++ drivers/block/mtip32xx/mtip32xx.h | 17 ++++++ 2 files changed, 113 insertions(+) diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 99c2cf461be3..b7579b76b4a1 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -1519,6 +1519,12 @@ static int mtip_get_identify(struct mtip_port *port, void __user *user_buffer) } #endif + /* Demux ID.DRAT & ID.RZAT to determine trim support */ + if (port->identify[69] & (1 << 14) && port->identify[69] & (1 << 5)) + port->dd->trim_supp = true; + else + port->dd->trim_supp = false; + /* Set the identify buffer as valid. */ port->identify_valid = 1; @@ -1705,6 +1711,81 @@ static int mtip_get_smart_attr(struct mtip_port *port, unsigned int id, return rv; } +/* + * Trim unused sectors + * + * @dd pointer to driver_data structure + * @lba starting lba + * @len # of 512b sectors to trim + * + * return value + * -ENOMEM Out of dma memory + * -EINVAL Invalid parameters passed in, trim not supported + * -EIO Error submitting trim request to hw + */ +int mtip_send_trim(struct driver_data *dd, unsigned int lba, unsigned int len) +{ + int i, rv = 0; + u64 tlba, tlen, sect_left; + struct mtip_trim_entry *buf; + dma_addr_t dma_addr; + struct host_to_dev_fis fis; + + if (!len || dd->trim_supp == false) + return -EINVAL; + + /* Trim request too big */ + WARN_ON(len > (MTIP_MAX_TRIM_ENTRY_LEN * MTIP_MAX_TRIM_ENTRIES)); + + /* Trim request not aligned on 4k boundary */ + WARN_ON(len % 8 != 0); + + /* Warn if vu_trim structure is too big */ + WARN_ON(sizeof(struct mtip_trim) > ATA_SECT_SIZE); + + /* Allocate a DMA buffer for the trim structure */ + buf = dmam_alloc_coherent(&dd->pdev->dev, ATA_SECT_SIZE, &dma_addr, + GFP_KERNEL); + if (!buf) + return -ENOMEM; + memset(buf, 0, ATA_SECT_SIZE); + + for (i = 0, sect_left = len, tlba = lba; + i < MTIP_MAX_TRIM_ENTRIES && sect_left; + i++) { + tlen = (sect_left >= MTIP_MAX_TRIM_ENTRY_LEN ? + MTIP_MAX_TRIM_ENTRY_LEN : + sect_left); + buf[i].lba = __force_bit2int cpu_to_le32(tlba); + buf[i].range = __force_bit2int cpu_to_le16(tlen); + tlba += tlen; + sect_left -= tlen; + } + WARN_ON(sect_left != 0); + + /* Build the fis */ + memset(&fis, 0, sizeof(struct host_to_dev_fis)); + fis.type = 0x27; + fis.opts = 1 << 7; + fis.command = 0xfb; + fis.features = 0x60; + fis.sect_count = 1; + fis.device = ATA_DEVICE_OBS; + + if (mtip_exec_internal_command(dd->port, + &fis, + 5, + dma_addr, + ATA_SECT_SIZE, + 0, + GFP_KERNEL, + MTIP_TRIM_TIMEOUT_MS) < 0) + rv = -EIO; + + dmam_free_coherent(&dd->pdev->dev, ATA_SECT_SIZE, buf, dma_addr); + return rv; +} + /* * Get the drive capacity. * @@ -3675,6 +3756,12 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio) } } + if (unlikely(bio->bi_rw & REQ_DISCARD)) { + bio_endio(bio, mtip_send_trim(dd, bio->bi_sector, + bio_sectors(bio))); + return; + } + if (unlikely(!bio_has_data(bio))) { blk_queue_flush(queue, 0); bio_endio(bio, 0); @@ -3817,6 +3904,15 @@ skip_create_disk: */ blk_queue_flush(dd->queue, 0); + /* Signal trim support */ + if (dd->trim_supp == true) { + set_bit(QUEUE_FLAG_DISCARD, &dd->queue->queue_flags); + dd->queue->limits.discard_granularity = 4096; + blk_queue_max_discard_sectors(dd->queue, + MTIP_MAX_TRIM_ENTRY_LEN * MTIP_MAX_TRIM_ENTRIES); + dd->queue->limits.discard_zeroes_data = 0; + } + /* Set the capacity of the device in 512 byte sectors. */ if (!(mtip_hw_get_capacity(dd, &capacity))) { dev_warn(&dd->pdev->dev, diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h index d782b1aa913e..3bffff5f670c 100644 --- a/drivers/block/mtip32xx/mtip32xx.h +++ b/drivers/block/mtip32xx/mtip32xx.h @@ -178,6 +178,21 @@ struct mtip_work { mtip_workq_sdbfx(w->port, group, w->completed); \ } +#define MTIP_TRIM_TIMEOUT_MS 240000 +#define MTIP_MAX_TRIM_ENTRIES 8 +#define MTIP_MAX_TRIM_ENTRY_LEN 0xfff8 + +struct mtip_trim_entry { + u32 lba; /* starting lba of region */ + u16 rsvd; /* unused */ + u16 range; /* # of 512b blocks to trim */ +} __packed; + +struct mtip_trim { + /* Array of regions to trim */ + struct mtip_trim_entry entry[MTIP_MAX_TRIM_ENTRIES]; +} __packed; + /* Register Frame Information Structure (FIS), host to device. */ struct host_to_dev_fis { /* @@ -473,6 +488,8 @@ struct driver_data { struct dentry *dfs_node; + bool trim_supp; /* flag indicating trim support */ + int numa_node; /* NUMA support */ char workq_name[32]; From 3d6a87430e764cf4132710538139c10970929189 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 11 Jan 2013 09:52:44 +0300 Subject: [PATCH 0812/4607] dac960: return success instead of -ENOTTY There is a missing break statement here. This used to return directly but we re-worked it in 2008 to add locking as part of the BKL push down. Signed-off-by: Dan Carpenter Signed-off-by: Jens Axboe --- drivers/block/DAC960.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index 9a13e889837e..0d3ffc51a7bd 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -7054,6 +7054,7 @@ static long DAC960_gam_ioctl(struct file *file, unsigned int Request, else ErrorCode = 0; } + break; default: ErrorCode = -ENOTTY; } From 242d98f077ac0ab80920219769eb095503b93f61 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 17 Dec 2012 10:01:27 -0500 Subject: [PATCH 0813/4607] block,elevator: use new hashtable implementation Switch elevator to use the new hashtable implementation. This reduces the amount of generic unrelated code in the elevator. This also removes the dymanic allocation of the hash table. The size of the table is constant so there's no point in paying the price of an extra dereference when accessing it. This patch depends on d9b482c ("hashtable: introduce a small and naive hashtable") which was merged in v3.6. Signed-off-by: Sasha Levin Signed-off-by: Jens Axboe --- block/blk.h | 2 +- block/elevator.c | 23 ++++------------------- include/linux/elevator.h | 5 ++++- 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/block/blk.h b/block/blk.h index 47fdfdd41520..e837b8f619b7 100644 --- a/block/blk.h +++ b/block/blk.h @@ -61,7 +61,7 @@ static inline void blk_clear_rq_complete(struct request *rq) /* * Internal elevator interface */ -#define ELV_ON_HASH(rq) (!hlist_unhashed(&(rq)->hash)) +#define ELV_ON_HASH(rq) hash_hashed(&(rq)->hash) void blk_insert_flush(struct request *rq); void blk_abort_flushes(struct request_queue *q); diff --git a/block/elevator.c b/block/elevator.c index 9edba1b8323e..11683bb10b7b 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -46,11 +46,6 @@ static LIST_HEAD(elv_list); /* * Merge hash stuff. */ -static const int elv_hash_shift = 6; -#define ELV_HASH_BLOCK(sec) ((sec) >> 3) -#define ELV_HASH_FN(sec) \ - (hash_long(ELV_HASH_BLOCK((sec)), elv_hash_shift)) -#define ELV_HASH_ENTRIES (1 << elv_hash_shift) #define rq_hash_key(rq) (blk_rq_pos(rq) + blk_rq_sectors(rq)) /* @@ -142,7 +137,6 @@ static struct elevator_queue *elevator_alloc(struct request_queue *q, struct elevator_type *e) { struct elevator_queue *eq; - int i; eq = kmalloc_node(sizeof(*eq), GFP_KERNEL | __GFP_ZERO, q->node); if (unlikely(!eq)) @@ -151,14 +145,7 @@ static struct elevator_queue *elevator_alloc(struct request_queue *q, eq->type = e; kobject_init(&eq->kobj, &elv_ktype); mutex_init(&eq->sysfs_lock); - - eq->hash = kmalloc_node(sizeof(struct hlist_head) * ELV_HASH_ENTRIES, - GFP_KERNEL, q->node); - if (!eq->hash) - goto err; - - for (i = 0; i < ELV_HASH_ENTRIES; i++) - INIT_HLIST_HEAD(&eq->hash[i]); + hash_init(eq->hash); return eq; err: @@ -173,7 +160,6 @@ static void elevator_release(struct kobject *kobj) e = container_of(kobj, struct elevator_queue, kobj); elevator_put(e->type); - kfree(e->hash); kfree(e); } @@ -240,7 +226,7 @@ EXPORT_SYMBOL(elevator_exit); static inline void __elv_rqhash_del(struct request *rq) { - hlist_del_init(&rq->hash); + hash_del(&rq->hash); } static void elv_rqhash_del(struct request_queue *q, struct request *rq) @@ -254,7 +240,7 @@ static void elv_rqhash_add(struct request_queue *q, struct request *rq) struct elevator_queue *e = q->elevator; BUG_ON(ELV_ON_HASH(rq)); - hlist_add_head(&rq->hash, &e->hash[ELV_HASH_FN(rq_hash_key(rq))]); + hash_add(e->hash, &rq->hash, rq_hash_key(rq)); } static void elv_rqhash_reposition(struct request_queue *q, struct request *rq) @@ -266,11 +252,10 @@ static void elv_rqhash_reposition(struct request_queue *q, struct request *rq) static struct request *elv_rqhash_find(struct request_queue *q, sector_t offset) { struct elevator_queue *e = q->elevator; - struct hlist_head *hash_list = &e->hash[ELV_HASH_FN(offset)]; struct hlist_node *entry, *next; struct request *rq; - hlist_for_each_entry_safe(rq, entry, next, hash_list, hash) { + hash_for_each_possible_safe(e->hash, rq, entry, next, hash, offset) { BUG_ON(!ELV_ON_HASH(rq)); if (unlikely(!rq_mergeable(rq))) { diff --git a/include/linux/elevator.h b/include/linux/elevator.h index c03af7687bb4..7c5a7c9789ee 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -2,6 +2,7 @@ #define _LINUX_ELEVATOR_H #include +#include #ifdef CONFIG_BLOCK @@ -96,6 +97,8 @@ struct elevator_type struct list_head list; }; +#define ELV_HASH_BITS 6 + /* * each queue has an elevator_queue associated with it */ @@ -105,8 +108,8 @@ struct elevator_queue void *elevator_data; struct kobject kobj; struct mutex sysfs_lock; - struct hlist_head *hash; unsigned int registered:1; + DECLARE_HASHTABLE(hash, ELV_HASH_BITS); }; /* From 422765c2638924da10ff363b5eed77924911bdc7 Mon Sep 17 00:00:00 2001 From: Jianpeng Ma Date: Fri, 11 Jan 2013 14:46:09 +0100 Subject: [PATCH 0814/4607] block: Remove should_sort judgement when flush blk_plug In commit 975927b942c932,it add blk_rq_pos to sort rq when flushing. Although this commit was used for the situation which blk_plug handled multi devices on the same time like md device. I think there must be some situations like this but only single device. So remove the should_sort judgement. Because the parameter should_sort is only for this purpose,it can delete should_sort from blk_plug. CC: Shaohua Li Signed-off-by: Jianpeng Ma Signed-off-by: Jens Axboe --- block/blk-core.c | 13 +------------ include/linux/blkdev.h | 1 - 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index c973249d68cd..aca5d82ff13c 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1548,13 +1548,6 @@ get_rq: if (list_empty(&plug->list)) trace_block_plug(q); else { - if (!plug->should_sort) { - struct request *__rq; - - __rq = list_entry_rq(plug->list.prev); - if (__rq->q != q) - plug->should_sort = 1; - } if (request_count >= BLK_MAX_REQUEST_COUNT) { blk_flush_plug_list(plug, false); trace_block_plug(q); @@ -2888,7 +2881,6 @@ void blk_start_plug(struct blk_plug *plug) plug->magic = PLUG_MAGIC; INIT_LIST_HEAD(&plug->list); INIT_LIST_HEAD(&plug->cb_list); - plug->should_sort = 0; /* * If this is a nested plug, don't actually assign it. It will be @@ -2990,10 +2982,7 @@ void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule) list_splice_init(&plug->list, &list); - if (plug->should_sort) { - list_sort(NULL, &list, plug_rq_cmp); - plug->should_sort = 0; - } + list_sort(NULL, &list, plug_rq_cmp); q = NULL; depth = 0; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index f94bc83011ed..dbe74279f3d6 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -974,7 +974,6 @@ struct blk_plug { unsigned long magic; /* detect uninitialized use-cases */ struct list_head list; /* requests */ struct list_head cb_list; /* md requires an unplug callback */ - unsigned int should_sort; /* list to be sorted before flushing? */ }; #define BLK_MAX_REQUEST_COUNT 16 From 978ae2247fac3b330ecd1095823001d2c08c7efd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 11 Jan 2013 13:01:37 -0200 Subject: [PATCH 0815/4607] [media] extract_xc3028.pl: fix permissions This is an executable file. Change permissions to reflect it. Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/extract_xc3028.pl | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Documentation/video4linux/extract_xc3028.pl diff --git a/Documentation/video4linux/extract_xc3028.pl b/Documentation/video4linux/extract_xc3028.pl old mode 100644 new mode 100755 From 3151d14aa6e983aa36d51a80d0477859f9ba12af Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 10 Jan 2013 21:35:34 -0300 Subject: [PATCH 0816/4607] [media] media: remove __dev* annotations Hi Mauro, After merging the v4l-dvb tree, today's linux-next build (x86_64 allmodconfig) failed like this: drivers/media/platform/sh_veu.c:1146:22: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'sh_veu_probe' drivers/media/platform/sh_veu.c:1228:22: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'sh_veu_remove' drivers/media/platform/sh_veu.c:1244:2: error: implicit declaration of function '__devexit_p' [-Werror=implicit-function-declaration] drivers/media/platform/sh_veu.c:1244:25: error: 'sh_veu_remove' undeclared here (not in a function) drivers/media/platform/sh_veu.c: In function 'sh_veu_init': drivers/media/platform/sh_veu.c:1253:45: error: 'sh_veu_probe' undeclared (first use in this function) drivers/media/platform/sh_veu.c:1253:45: note: each undeclared identifier is reported only once for each function it appears in drivers/media/platform/sh_veu.c: At top level: drivers/media/platform/sh_veu.c:1095:20: warning: 'sh_veu_bh' defined but not used [-Wunused-function] drivers/media/platform/sh_veu.c:1109:20: warning: 'sh_veu_isr' defined but not used [-Wunused-function] drivers/media/platform/sh_veu.c: In function 'sh_veu_init': drivers/media/platform/sh_veu.c:1254:1: warning: control reaches end of non-void function [-Wreturn-type] Caused by commit 05efa71bdc0e ("[media] media: add a VEU MEM2MEM format conversion and scaling driver") interacting with commit 54b956b90360 ("Remove __dev* markings from init.h") from the driver-core.current tree. I have applied the following merge fix patch which could be applied directly to the v4l-dvb tree (please): CONFIG_HOTPLUG is always true now and the __dev* macros have meen removed. Cc: Guennadi Liakhovetski Signed-off-by: Stephen Rothwell Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_veu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index a0186768479d..cb54c69d5748 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -1143,7 +1143,7 @@ static irqreturn_t sh_veu_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int __devinit sh_veu_probe(struct platform_device *pdev) +static int sh_veu_probe(struct platform_device *pdev) { struct sh_veu_dev *veu; struct resource *reg_res; @@ -1225,7 +1225,7 @@ einitctx: return ret; } -static int __devexit sh_veu_remove(struct platform_device *pdev) +static int sh_veu_remove(struct platform_device *pdev) { struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); struct sh_veu_dev *veu = container_of(v4l2_dev, @@ -1241,7 +1241,7 @@ static int __devexit sh_veu_remove(struct platform_device *pdev) } static struct platform_driver __refdata sh_veu_pdrv = { - .remove = __devexit_p(sh_veu_remove), + .remove = sh_veu_remove, .driver = { .name = "sh_veu", .owner = THIS_MODULE, From 25e9d28d927d2e1731df53f60cde53d75bcb7c36 Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Wed, 26 Dec 2012 10:54:02 +0900 Subject: [PATCH 0817/4607] ARM: EXYNOS: remove system mmu initialization from exynos tree This removes System MMU initialization from arch/arm/mach-exynos/ to move them to DT and the exynos-iommu driver except gating clock definitions. Signed-off-by: KyongHo Cho Acked-by: Kukjin Kim Signed-off-by: Joerg Roedel --- arch/arm/mach-exynos/Kconfig | 5 - arch/arm/mach-exynos/Makefile | 1 - arch/arm/mach-exynos/clock-exynos4.c | 41 ++- arch/arm/mach-exynos/clock-exynos4210.c | 9 +- arch/arm/mach-exynos/clock-exynos4212.c | 23 +- arch/arm/mach-exynos/clock-exynos5.c | 62 ++--- arch/arm/mach-exynos/dev-sysmmu.c | 274 --------------------- arch/arm/mach-exynos/include/mach/sysmmu.h | 66 ----- arch/arm/mach-exynos/mach-exynos4-dt.c | 34 +++ arch/arm/mach-exynos/mach-exynos5-dt.c | 30 +++ 10 files changed, 137 insertions(+), 408 deletions(-) delete mode 100644 arch/arm/mach-exynos/dev-sysmmu.c delete mode 100644 arch/arm/mach-exynos/include/mach/sysmmu.h diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index e103c290bc9e..3967c62ea271 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -105,11 +105,6 @@ config EXYNOS4_SETUP_FIMD0 help Common setup code for FIMD0. -config EXYNOS_DEV_SYSMMU - bool - help - Common setup code for SYSTEM MMU in EXYNOS platforms - config EXYNOS4_DEV_USB_OHCI bool help diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index b189881657ec..435757e57bb4 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -52,7 +52,6 @@ obj-$(CONFIG_ARCH_EXYNOS4) += dev-audio.o obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o obj-$(CONFIG_EXYNOS_DEV_DMA) += dma.o obj-$(CONFIG_EXYNOS4_DEV_USB_OHCI) += dev-ohci.o -obj-$(CONFIG_EXYNOS_DEV_SYSMMU) += dev-sysmmu.o obj-$(CONFIG_ARCH_EXYNOS) += setup-i2c0.o obj-$(CONFIG_EXYNOS4_SETUP_FIMC) += setup-fimc.o diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c index bbcb3dea0d40..8a8468d83c8c 100644 --- a/arch/arm/mach-exynos/clock-exynos4.c +++ b/arch/arm/mach-exynos/clock-exynos4.c @@ -24,7 +24,6 @@ #include #include -#include #include "common.h" #include "clock-exynos4.h" @@ -709,53 +708,53 @@ static struct clk exynos4_init_clocks_off[] = { .enable = exynos4_clk_ip_peril_ctrl, .ctrlbit = (1 << 14), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(mfc_l, 0), + .name = "sysmmu", + .devname = "exynos-sysmmu.0", .enable = exynos4_clk_ip_mfc_ctrl, .ctrlbit = (1 << 1), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(mfc_r, 1), + .name = "sysmmu", + .devname = "exynos-sysmmu.1", .enable = exynos4_clk_ip_mfc_ctrl, .ctrlbit = (1 << 2), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(tv, 2), + .name = "sysmmu", + .devname = "exynos-sysmmu.2", .enable = exynos4_clk_ip_tv_ctrl, .ctrlbit = (1 << 4), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(jpeg, 3), + .name = "sysmmu", + .devname = "exynos-sysmmu.3", .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit = (1 << 11), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(rot, 4), + .name = "sysmmu", + .devname = "exynos-sysmmu.4", .enable = exynos4_clk_ip_image_ctrl, .ctrlbit = (1 << 4), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(fimc0, 5), + .name = "sysmmu", + .devname = "exynos-sysmmu.5", .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit = (1 << 7), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(fimc1, 6), + .name = "sysmmu", + .devname = "exynos-sysmmu.6", .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit = (1 << 8), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(fimc2, 7), + .name = "sysmmu", + .devname = "exynos-sysmmu.7", .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit = (1 << 9), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(fimc3, 8), + .name = "sysmmu", + .devname = "exynos-sysmmu.8", .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit = (1 << 10), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(fimd0, 10), + .name = "sysmmu", + .devname = "exynos-sysmmu.10", .enable = exynos4_clk_ip_lcd0_ctrl, .ctrlbit = (1 << 4), } diff --git a/arch/arm/mach-exynos/clock-exynos4210.c b/arch/arm/mach-exynos/clock-exynos4210.c index fed4c26e9dad..19af9f783c56 100644 --- a/arch/arm/mach-exynos/clock-exynos4210.c +++ b/arch/arm/mach-exynos/clock-exynos4210.c @@ -26,7 +26,6 @@ #include #include #include -#include #include "common.h" #include "clock-exynos4.h" @@ -129,13 +128,13 @@ static struct clk init_clocks_off[] = { .enable = exynos4_clk_ip_lcd1_ctrl, .ctrlbit = (1 << 0), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(2d, 14), + .name = "sysmmu", + .devname = "exynos-sysmmu.9", .enable = exynos4_clk_ip_image_ctrl, .ctrlbit = (1 << 3), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(fimd1, 11), + .name = "sysmmu", + .devname = "exynos-sysmmu.11", .enable = exynos4_clk_ip_lcd1_ctrl, .ctrlbit = (1 << 4), }, { diff --git a/arch/arm/mach-exynos/clock-exynos4212.c b/arch/arm/mach-exynos/clock-exynos4212.c index 8fba0b5fb8ab..529476f8ec71 100644 --- a/arch/arm/mach-exynos/clock-exynos4212.c +++ b/arch/arm/mach-exynos/clock-exynos4212.c @@ -26,7 +26,6 @@ #include #include #include -#include #include "common.h" #include "clock-exynos4.h" @@ -111,20 +110,30 @@ static struct clksrc_clk clksrcs[] = { static struct clk init_clocks_off[] = { { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(2d, 14), + .name = "sysmmu", + .devname = "exynos-sysmmu.9", .enable = exynos4_clk_ip_dmc_ctrl, .ctrlbit = (1 << 24), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(isp, 9), + .name = "sysmmu", + .devname = "exynos-sysmmu.12", .enable = exynos4212_clk_ip_isp0_ctrl, .ctrlbit = (7 << 8), }, { - .name = SYSMMU_CLOCK_NAME2, - .devname = SYSMMU_CLOCK_DEVNAME(isp, 9), + .name = "sysmmu", + .devname = "exynos-sysmmu.13", .enable = exynos4212_clk_ip_isp1_ctrl, .ctrlbit = (1 << 4), + }, { + .name = "sysmmu", + .devname = "exynos-sysmmu.14", + .enable = exynos4212_clk_ip_isp0_ctrl, + .ctrlbit = (1 << 11), + }, { + .name = "sysmmu", + .devname = "exynos-sysmmu.15", + .enable = exynos4212_clk_ip_isp0_ctrl, + .ctrlbit = (1 << 12), }, { .name = "flite", .devname = "exynos-fimc-lite.0", diff --git a/arch/arm/mach-exynos/clock-exynos5.c b/arch/arm/mach-exynos/clock-exynos5.c index e9d7b80bae49..b0ea31fc9fb8 100644 --- a/arch/arm/mach-exynos/clock-exynos5.c +++ b/arch/arm/mach-exynos/clock-exynos5.c @@ -24,7 +24,6 @@ #include #include -#include #include "common.h" @@ -859,73 +858,78 @@ static struct clk exynos5_init_clocks_off[] = { .enable = exynos5_clk_ip_gscl_ctrl, .ctrlbit = (1 << 3), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(mfc_l, 0), + .name = "sysmmu", + .devname = "exynos-sysmmu.1", .enable = &exynos5_clk_ip_mfc_ctrl, .ctrlbit = (1 << 1), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(mfc_r, 1), + .name = "sysmmu", + .devname = "exynos-sysmmu.0", .enable = &exynos5_clk_ip_mfc_ctrl, .ctrlbit = (1 << 2), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(tv, 2), + .name = "sysmmu", + .devname = "exynos-sysmmu.2", .enable = &exynos5_clk_ip_disp1_ctrl, .ctrlbit = (1 << 9) }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(jpeg, 3), + .name = "sysmmu", + .devname = "exynos-sysmmu.3", .enable = &exynos5_clk_ip_gen_ctrl, .ctrlbit = (1 << 7), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(rot, 4), + .name = "sysmmu", + .devname = "exynos-sysmmu.4", .enable = &exynos5_clk_ip_gen_ctrl, .ctrlbit = (1 << 6) }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(gsc0, 5), + .name = "sysmmu", + .devname = "exynos-sysmmu.5", .enable = &exynos5_clk_ip_gscl_ctrl, .ctrlbit = (1 << 7), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(gsc1, 6), + .name = "sysmmu", + .devname = "exynos-sysmmu.6", .enable = &exynos5_clk_ip_gscl_ctrl, .ctrlbit = (1 << 8), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(gsc2, 7), + .name = "sysmmu", + .devname = "exynos-sysmmu.7", .enable = &exynos5_clk_ip_gscl_ctrl, .ctrlbit = (1 << 9), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(gsc3, 8), + .name = "sysmmu", + .devname = "exynos-sysmmu.8", .enable = &exynos5_clk_ip_gscl_ctrl, .ctrlbit = (1 << 10), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(isp, 9), + .name = "sysmmu", + .devname = "exynos-sysmmu.9", .enable = &exynos5_clk_ip_isp0_ctrl, .ctrlbit = (0x3F << 8), }, { - .name = SYSMMU_CLOCK_NAME2, - .devname = SYSMMU_CLOCK_DEVNAME(isp, 9), + .name = "sysmmu", + .devname = "exynos-sysmmu.10", .enable = &exynos5_clk_ip_isp1_ctrl, .ctrlbit = (0xF << 4), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(camif0, 12), + .name = "sysmmu", + .devname = "exynos-sysmmu.11", + .enable = &exynos5_clk_ip_disp1_ctrl, + .ctrlbit = (1 << 8) + }, { + .name = "sysmmu", + .devname = "exynos-sysmmu.12", .enable = &exynos5_clk_ip_gscl_ctrl, .ctrlbit = (1 << 11), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(camif1, 13), + .name = "sysmmu", + .devname = "exynos-sysmmu.13", .enable = &exynos5_clk_ip_gscl_ctrl, .ctrlbit = (1 << 12), }, { - .name = SYSMMU_CLOCK_NAME, - .devname = SYSMMU_CLOCK_DEVNAME(2d, 14), + .name = "sysmmu", + .devname = "exynos-sysmmu.14", .enable = &exynos5_clk_ip_acp_ctrl, .ctrlbit = (1 << 7) } diff --git a/arch/arm/mach-exynos/dev-sysmmu.c b/arch/arm/mach-exynos/dev-sysmmu.c deleted file mode 100644 index c5b1ea301df0..000000000000 --- a/arch/arm/mach-exynos/dev-sysmmu.c +++ /dev/null @@ -1,274 +0,0 @@ -/* linux/arch/arm/mach-exynos/dev-sysmmu.c - * - * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * EXYNOS - System MMU support - * - * 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 -#include - -#include - -#include -#include -#include - -static u64 exynos_sysmmu_dma_mask = DMA_BIT_MASK(32); - -#define SYSMMU_PLATFORM_DEVICE(ipname, devid) \ -static struct sysmmu_platform_data platdata_##ipname = { \ - .dbgname = #ipname, \ -}; \ -struct platform_device SYSMMU_PLATDEV(ipname) = \ -{ \ - .name = SYSMMU_DEVNAME_BASE, \ - .id = devid, \ - .dev = { \ - .dma_mask = &exynos_sysmmu_dma_mask, \ - .coherent_dma_mask = DMA_BIT_MASK(32), \ - .platform_data = &platdata_##ipname, \ - }, \ -} - -SYSMMU_PLATFORM_DEVICE(mfc_l, 0); -SYSMMU_PLATFORM_DEVICE(mfc_r, 1); -SYSMMU_PLATFORM_DEVICE(tv, 2); -SYSMMU_PLATFORM_DEVICE(jpeg, 3); -SYSMMU_PLATFORM_DEVICE(rot, 4); -SYSMMU_PLATFORM_DEVICE(fimc0, 5); /* fimc* and gsc* exist exclusively */ -SYSMMU_PLATFORM_DEVICE(fimc1, 6); -SYSMMU_PLATFORM_DEVICE(fimc2, 7); -SYSMMU_PLATFORM_DEVICE(fimc3, 8); -SYSMMU_PLATFORM_DEVICE(gsc0, 5); -SYSMMU_PLATFORM_DEVICE(gsc1, 6); -SYSMMU_PLATFORM_DEVICE(gsc2, 7); -SYSMMU_PLATFORM_DEVICE(gsc3, 8); -SYSMMU_PLATFORM_DEVICE(isp, 9); -SYSMMU_PLATFORM_DEVICE(fimd0, 10); -SYSMMU_PLATFORM_DEVICE(fimd1, 11); -SYSMMU_PLATFORM_DEVICE(camif0, 12); -SYSMMU_PLATFORM_DEVICE(camif1, 13); -SYSMMU_PLATFORM_DEVICE(2d, 14); - -#define SYSMMU_RESOURCE_NAME(core, ipname) sysmmures_##core##_##ipname - -#define SYSMMU_RESOURCE(core, ipname) \ - static struct resource SYSMMU_RESOURCE_NAME(core, ipname)[] __initdata = - -#define DEFINE_SYSMMU_RESOURCE(core, mem, irq) \ - DEFINE_RES_MEM_NAMED(core##_PA_SYSMMU_##mem, SZ_4K, #mem), \ - DEFINE_RES_IRQ_NAMED(core##_IRQ_SYSMMU_##irq##_0, #mem) - -#define SYSMMU_RESOURCE_DEFINE(core, ipname, mem, irq) \ - SYSMMU_RESOURCE(core, ipname) { \ - DEFINE_SYSMMU_RESOURCE(core, mem, irq) \ - } - -struct sysmmu_resource_map { - struct platform_device *pdev; - struct resource *res; - u32 rnum; - struct device *pdd; - char *clocknames; -}; - -#define SYSMMU_RESOURCE_MAPPING(core, ipname, resname) { \ - .pdev = &SYSMMU_PLATDEV(ipname), \ - .res = SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \ - .rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\ - .clocknames = SYSMMU_CLOCK_NAME, \ -} - -#define SYSMMU_RESOURCE_MAPPING_MC(core, ipname, resname, pdata) { \ - .pdev = &SYSMMU_PLATDEV(ipname), \ - .res = SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \ - .rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\ - .clocknames = SYSMMU_CLOCK_NAME "," SYSMMU_CLOCK_NAME2, \ -} - -#ifdef CONFIG_EXYNOS_DEV_PD -#define SYSMMU_RESOURCE_MAPPING_PD(core, ipname, resname, pd) { \ - .pdev = &SYSMMU_PLATDEV(ipname), \ - .res = &SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \ - .rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\ - .clocknames = SYSMMU_CLOCK_NAME, \ - .pdd = &exynos##core##_device_pd[pd].dev, \ -} - -#define SYSMMU_RESOURCE_MAPPING_MCPD(core, ipname, resname, pd, pdata) {\ - .pdev = &SYSMMU_PLATDEV(ipname), \ - .res = &SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \ - .rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\ - .clocknames = SYSMMU_CLOCK_NAME "," SYSMMU_CLOCK_NAME2, \ - .pdd = &exynos##core##_device_pd[pd].dev, \ -} -#else -#define SYSMMU_RESOURCE_MAPPING_PD(core, ipname, resname, pd) \ - SYSMMU_RESOURCE_MAPPING(core, ipname, resname) -#define SYSMMU_RESOURCE_MAPPING_MCPD(core, ipname, resname, pd, pdata) \ - SYSMMU_RESOURCE_MAPPING_MC(core, ipname, resname, pdata) - -#endif /* CONFIG_EXYNOS_DEV_PD */ - -#ifdef CONFIG_ARCH_EXYNOS4 -SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc0, FIMC0, FIMC0); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc1, FIMC1, FIMC1); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc2, FIMC2, FIMC2); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc3, FIMC3, FIMC3); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, jpeg, JPEG, JPEG); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, 2d, G2D, 2D); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, tv, TV, TV_M0); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, 2d_acp, 2D_ACP, 2D); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, rot, ROTATOR, ROTATOR); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimd0, FIMD0, LCD0_M0); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimd1, FIMD1, LCD1_M1); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, flite0, FIMC_LITE0, FIMC_LITE0); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, flite1, FIMC_LITE1, FIMC_LITE1); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, mfc_r, MFC_R, MFC_M0); -SYSMMU_RESOURCE_DEFINE(EXYNOS4, mfc_l, MFC_L, MFC_M1); -SYSMMU_RESOURCE(EXYNOS4, isp) { - DEFINE_SYSMMU_RESOURCE(EXYNOS4, FIMC_ISP, FIMC_ISP), - DEFINE_SYSMMU_RESOURCE(EXYNOS4, FIMC_DRC, FIMC_DRC), - DEFINE_SYSMMU_RESOURCE(EXYNOS4, FIMC_FD, FIMC_FD), - DEFINE_SYSMMU_RESOURCE(EXYNOS4, ISPCPU, FIMC_CX), -}; - -static struct sysmmu_resource_map sysmmu_resmap4[] __initdata = { - SYSMMU_RESOURCE_MAPPING_PD(4, fimc0, fimc0, PD_CAM), - SYSMMU_RESOURCE_MAPPING_PD(4, fimc1, fimc1, PD_CAM), - SYSMMU_RESOURCE_MAPPING_PD(4, fimc2, fimc2, PD_CAM), - SYSMMU_RESOURCE_MAPPING_PD(4, fimc3, fimc3, PD_CAM), - SYSMMU_RESOURCE_MAPPING_PD(4, tv, tv, PD_TV), - SYSMMU_RESOURCE_MAPPING_PD(4, mfc_r, mfc_r, PD_MFC), - SYSMMU_RESOURCE_MAPPING_PD(4, mfc_l, mfc_l, PD_MFC), - SYSMMU_RESOURCE_MAPPING_PD(4, rot, rot, PD_LCD0), - SYSMMU_RESOURCE_MAPPING_PD(4, jpeg, jpeg, PD_CAM), - SYSMMU_RESOURCE_MAPPING_PD(4, fimd0, fimd0, PD_LCD0), -}; - -static struct sysmmu_resource_map sysmmu_resmap4210[] __initdata = { - SYSMMU_RESOURCE_MAPPING_PD(4, 2d, 2d, PD_LCD0), - SYSMMU_RESOURCE_MAPPING_PD(4, fimd1, fimd1, PD_LCD1), -}; - -static struct sysmmu_resource_map sysmmu_resmap4212[] __initdata = { - SYSMMU_RESOURCE_MAPPING(4, 2d, 2d_acp), - SYSMMU_RESOURCE_MAPPING_PD(4, camif0, flite0, PD_ISP), - SYSMMU_RESOURCE_MAPPING_PD(4, camif1, flite1, PD_ISP), - SYSMMU_RESOURCE_MAPPING_PD(4, isp, isp, PD_ISP), -}; -#endif /* CONFIG_ARCH_EXYNOS4 */ - -#ifdef CONFIG_ARCH_EXYNOS5 -SYSMMU_RESOURCE_DEFINE(EXYNOS5, jpeg, JPEG, JPEG); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, fimd1, FIMD1, FIMD1); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, 2d, 2D, 2D); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, rot, ROTATOR, ROTATOR); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, tv, TV, TV); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, flite0, LITE0, LITE0); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, flite1, LITE1, LITE1); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc0, GSC0, GSC0); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc1, GSC1, GSC1); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc2, GSC2, GSC2); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc3, GSC3, GSC3); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, mfc_r, MFC_R, MFC_R); -SYSMMU_RESOURCE_DEFINE(EXYNOS5, mfc_l, MFC_L, MFC_L); -SYSMMU_RESOURCE(EXYNOS5, isp) { - DEFINE_SYSMMU_RESOURCE(EXYNOS5, ISP, ISP), - DEFINE_SYSMMU_RESOURCE(EXYNOS5, DRC, DRC), - DEFINE_SYSMMU_RESOURCE(EXYNOS5, FD, FD), - DEFINE_SYSMMU_RESOURCE(EXYNOS5, ISPCPU, MCUISP), - DEFINE_SYSMMU_RESOURCE(EXYNOS5, SCALERC, SCALERCISP), - DEFINE_SYSMMU_RESOURCE(EXYNOS5, SCALERP, SCALERPISP), - DEFINE_SYSMMU_RESOURCE(EXYNOS5, ODC, ODC), - DEFINE_SYSMMU_RESOURCE(EXYNOS5, DIS0, DIS0), - DEFINE_SYSMMU_RESOURCE(EXYNOS5, DIS1, DIS1), - DEFINE_SYSMMU_RESOURCE(EXYNOS5, 3DNR, 3DNR), -}; - -static struct sysmmu_resource_map sysmmu_resmap5[] __initdata = { - SYSMMU_RESOURCE_MAPPING(5, jpeg, jpeg), - SYSMMU_RESOURCE_MAPPING(5, fimd1, fimd1), - SYSMMU_RESOURCE_MAPPING(5, 2d, 2d), - SYSMMU_RESOURCE_MAPPING(5, rot, rot), - SYSMMU_RESOURCE_MAPPING_PD(5, tv, tv, PD_DISP1), - SYSMMU_RESOURCE_MAPPING_PD(5, camif0, flite0, PD_GSCL), - SYSMMU_RESOURCE_MAPPING_PD(5, camif1, flite1, PD_GSCL), - SYSMMU_RESOURCE_MAPPING_PD(5, gsc0, gsc0, PD_GSCL), - SYSMMU_RESOURCE_MAPPING_PD(5, gsc1, gsc1, PD_GSCL), - SYSMMU_RESOURCE_MAPPING_PD(5, gsc2, gsc2, PD_GSCL), - SYSMMU_RESOURCE_MAPPING_PD(5, gsc3, gsc3, PD_GSCL), - SYSMMU_RESOURCE_MAPPING_PD(5, mfc_r, mfc_r, PD_MFC), - SYSMMU_RESOURCE_MAPPING_PD(5, mfc_l, mfc_l, PD_MFC), - SYSMMU_RESOURCE_MAPPING_MCPD(5, isp, isp, PD_ISP, mc_platdata), -}; -#endif /* CONFIG_ARCH_EXYNOS5 */ - -static int __init init_sysmmu_platform_device(void) -{ - int i, j; - struct sysmmu_resource_map *resmap[2] = {NULL, NULL}; - int nmap[2] = {0, 0}; - -#ifdef CONFIG_ARCH_EXYNOS5 - if (soc_is_exynos5250()) { - resmap[0] = sysmmu_resmap5; - nmap[0] = ARRAY_SIZE(sysmmu_resmap5); - nmap[1] = 0; - } -#endif - -#ifdef CONFIG_ARCH_EXYNOS4 - if (resmap[0] == NULL) { - resmap[0] = sysmmu_resmap4; - nmap[0] = ARRAY_SIZE(sysmmu_resmap4); - } - - if (soc_is_exynos4210()) { - resmap[1] = sysmmu_resmap4210; - nmap[1] = ARRAY_SIZE(sysmmu_resmap4210); - } - - if (soc_is_exynos4412() || soc_is_exynos4212()) { - resmap[1] = sysmmu_resmap4212; - nmap[1] = ARRAY_SIZE(sysmmu_resmap4212); - } -#endif - - for (j = 0; j < 2; j++) { - for (i = 0; i < nmap[j]; i++) { - struct sysmmu_resource_map *map; - struct sysmmu_platform_data *platdata; - - map = &resmap[j][i]; - - map->pdev->dev.parent = map->pdd; - - platdata = map->pdev->dev.platform_data; - platdata->clockname = map->clocknames; - - if (platform_device_add_resources(map->pdev, map->res, - map->rnum)) { - pr_err("%s: Failed to add device resources for " - "%s.%d\n", __func__, - map->pdev->name, map->pdev->id); - continue; - } - - if (platform_device_register(map->pdev)) { - pr_err("%s: Failed to register %s.%d\n", - __func__, map->pdev->name, - map->pdev->id); - } - } - } - - return 0; -} -arch_initcall(init_sysmmu_platform_device); diff --git a/arch/arm/mach-exynos/include/mach/sysmmu.h b/arch/arm/mach-exynos/include/mach/sysmmu.h deleted file mode 100644 index 88a4543b0001..000000000000 --- a/arch/arm/mach-exynos/include/mach/sysmmu.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * EXYNOS - System MMU support - * - * 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 _ARM_MACH_EXYNOS_SYSMMU_H_ -#define _ARM_MACH_EXYNOS_SYSMMU_H_ - -struct sysmmu_platform_data { - char *dbgname; - /* comma(,) separated list of clock names for clock gating */ - char *clockname; -}; - -#define SYSMMU_DEVNAME_BASE "exynos-sysmmu" - -#define SYSMMU_CLOCK_NAME "sysmmu" -#define SYSMMU_CLOCK_NAME2 "sysmmu_mc" - -#ifdef CONFIG_EXYNOS_DEV_SYSMMU -#include -struct platform_device; - -#define SYSMMU_PLATDEV(ipname) exynos_device_sysmmu_##ipname - -extern struct platform_device SYSMMU_PLATDEV(mfc_l); -extern struct platform_device SYSMMU_PLATDEV(mfc_r); -extern struct platform_device SYSMMU_PLATDEV(tv); -extern struct platform_device SYSMMU_PLATDEV(jpeg); -extern struct platform_device SYSMMU_PLATDEV(rot); -extern struct platform_device SYSMMU_PLATDEV(fimc0); -extern struct platform_device SYSMMU_PLATDEV(fimc1); -extern struct platform_device SYSMMU_PLATDEV(fimc2); -extern struct platform_device SYSMMU_PLATDEV(fimc3); -extern struct platform_device SYSMMU_PLATDEV(gsc0); -extern struct platform_device SYSMMU_PLATDEV(gsc1); -extern struct platform_device SYSMMU_PLATDEV(gsc2); -extern struct platform_device SYSMMU_PLATDEV(gsc3); -extern struct platform_device SYSMMU_PLATDEV(isp); -extern struct platform_device SYSMMU_PLATDEV(fimd0); -extern struct platform_device SYSMMU_PLATDEV(fimd1); -extern struct platform_device SYSMMU_PLATDEV(camif0); -extern struct platform_device SYSMMU_PLATDEV(camif1); -extern struct platform_device SYSMMU_PLATDEV(2d); - -#ifdef CONFIG_IOMMU_API -static inline void platform_set_sysmmu( - struct device *sysmmu, struct device *dev) -{ - dev->archdata.iommu = sysmmu; -} -#endif - -#else /* !CONFIG_EXYNOS_DEV_SYSMMU */ -#define platform_set_sysmmu(sysmmu, dev) do { } while (0) -#endif - -#define SYSMMU_CLOCK_DEVNAME(ipname, id) (SYSMMU_DEVNAME_BASE "." #id) - -#endif /* _ARM_MACH_EXYNOS_SYSMMU_H_ */ diff --git a/arch/arm/mach-exynos/mach-exynos4-dt.c b/arch/arm/mach-exynos/mach-exynos4-dt.c index 92757ff817ae..ac09af860b38 100644 --- a/arch/arm/mach-exynos/mach-exynos4-dt.c +++ b/arch/arm/mach-exynos/mach-exynos4-dt.c @@ -80,6 +80,40 @@ static const struct of_dev_auxdata exynos4_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("arm,pl330", EXYNOS4_PA_MDMA1, "dma-pl330.2", NULL), OF_DEV_AUXDATA("samsung,exynos4210-tmu", EXYNOS4_PA_TMU, "exynos-tmu", NULL), + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x13620000, + "exynos-sysmmu.0", NULL), /* MFC_L */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x13630000, + "exynos-sysmmu.1", NULL), /* MFC_R */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x13E20000, + "exynos-sysmmu.2", NULL), /* TV */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x11A60000, + "exynos-sysmmu.3", NULL), /* JPEG */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x12A30000, + "exynos-sysmmu.4", NULL), /* ROTATOR */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x11A20000, + "exynos-sysmmu.5", NULL), /* FIMC0 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x11A30000, + "exynos-sysmmu.6", NULL), /* FIMC1 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x11A40000, + "exynos-sysmmu.7", NULL), /* FIMC2 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x11A50000, + "exynos-sysmmu.8", NULL), /* FIMC3 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x12A20000, + "exynos-sysmmu.9", NULL), /* G2D(4210) */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x10A40000, + "exynos-sysmmu.9", NULL), /* G2D(4x12) */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x11E20000, + "exynos-sysmmu.10", NULL), /* FIMD0 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x12220000, + "exynos-sysmmu.11", NULL), /* FIMD1(4210) */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x12260000, + "exynos-sysmmu.12", NULL), /* IS0(4x12) */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x122B0000, + "exynos-sysmmu.13", NULL), /* IS1(4x12) */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x123B0000, + "exynos-sysmmu.14", NULL), /* FIMC-LITE0(4x12) */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x123C0000, + "exynos-sysmmu.15", NULL), /* FIMC-LITE1(4x12) */ {}, }; diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index e99d3d8f2bcf..26710758c385 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -104,6 +104,36 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("samsung,mfc-v6", 0x11000000, "s5p-mfc-v6", NULL), OF_DEV_AUXDATA("samsung,exynos5250-tmu", 0x10060000, "exynos-tmu", NULL), + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x11210000, + "exynos-sysmmu.0", "mfc"), /* MFC_L */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x11200000, + "exynos-sysmmu.1", "mfc"), /* MFC_R */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x14650000, + "exynos-sysmmu.2", NULL), /* TV */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x11F20000, + "exynos-sysmmu.3", "jpeg"), /* JPEG */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x11D40000, + "exynos-sysmmu.4", NULL), /* ROTATOR */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x13E80000, + "exynos-sysmmu.5", "gscl"), /* GSCL0 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x13E90000, + "exynos-sysmmu.6", "gscl"), /* GSCL1 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x13EA0000, + "exynos-sysmmu.7", "gscl"), /* GSCL2 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x13EB0000, + "exynos-sysmmu.8", "gscl"), /* GSCL3 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x13260000, + "exynos-sysmmu.9", NULL), /* FIMC-IS0 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x132C0000, + "exynos-sysmmu.10", NULL), /* FIMC-IS1 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x14640000, + "exynos-sysmmu.11", NULL), /* FIMD1 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x13C40000, + "exynos-sysmmu.12", NULL), /* FIMC-LITE0 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x13C50000, + "exynos-sysmmu.13", NULL), /* FIMC-LITE1 */ + OF_DEV_AUXDATA("samsung,exynos-sysmmu", 0x10A60000, + "exynos-sysmmu.14", NULL), /* G2D */ {}, }; From 10ee27a06cc8eb57f83342a8eabcb75deb872d52 Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Thu, 10 Jan 2013 13:47:57 +0800 Subject: [PATCH 0818/4607] vfs: re-implement writeback_inodes_sb(_nr)_if_idle() and rename them writeback_inodes_sb(_nr)_if_idle() is re-implemented by replacing down_read() with down_read_trylock() because - If ->s_umount is write locked, then the sb is not idle. That is writeback_inodes_sb(_nr)_if_idle() needn't wait for the lock. - writeback_inodes_sb(_nr)_if_idle() grabs s_umount lock when it want to start writeback, it may bring us deadlock problem when doing umount. In order to fix the problem, ext4 and btrfs implemented their own writeback functions instead of writeback_inodes_sb(_nr)_if_idle(), but it introduced the redundant code, it is better to implement a new writeback_inodes_sb(_nr)_if_idle(). The name of these two functions is cumbersome, so rename them to try_to_writeback_inodes_sb(_nr). This idea came from Christoph Hellwig. Some code is from the patch of Kamal Mostafa. Reviewed-by: Jan Kara Signed-off-by: Miao Xie Signed-off-by: Fengguang Wu --- fs/btrfs/extent-tree.c | 20 ++----------- fs/ext4/inode.c | 8 ++---- fs/fs-writeback.c | 60 ++++++++++++++++++--------------------- include/linux/writeback.h | 6 ++-- 4 files changed, 36 insertions(+), 58 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 521e9d4424f6..f31abb14e06f 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3689,20 +3689,6 @@ static int can_overcommit(struct btrfs_root *root, return 0; } -static int writeback_inodes_sb_nr_if_idle_safe(struct super_block *sb, - unsigned long nr_pages, - enum wb_reason reason) -{ - if (!writeback_in_progress(sb->s_bdi) && - down_read_trylock(&sb->s_umount)) { - writeback_inodes_sb_nr(sb, nr_pages, reason); - up_read(&sb->s_umount); - return 1; - } - - return 0; -} - /* * shrink metadata reservation for delalloc */ @@ -3735,9 +3721,9 @@ static void shrink_delalloc(struct btrfs_root *root, u64 to_reclaim, u64 orig, while (delalloc_bytes && loops < 3) { max_reclaim = min(delalloc_bytes, to_reclaim); nr_pages = max_reclaim >> PAGE_CACHE_SHIFT; - writeback_inodes_sb_nr_if_idle_safe(root->fs_info->sb, - nr_pages, - WB_REASON_FS_FREE_SPACE); + try_to_writeback_inodes_sb_nr(root->fs_info->sb, + nr_pages, + WB_REASON_FS_FREE_SPACE); /* * We need to wait for the async pages to actually start before diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index cbfe13bf5b2a..5f6eef71ff21 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2512,12 +2512,8 @@ static int ext4_nonda_switch(struct super_block *sb) /* * Start pushing delalloc when 1/2 of free blocks are dirty. */ - if (dirty_blocks && (free_blocks < 2 * dirty_blocks) && - !writeback_in_progress(sb->s_bdi) && - down_read_trylock(&sb->s_umount)) { - writeback_inodes_sb(sb, WB_REASON_FS_FREE_SPACE); - up_read(&sb->s_umount); - } + if (dirty_blocks && (free_blocks < 2 * dirty_blocks)) + try_to_writeback_inodes_sb(sb, WB_REASON_FS_FREE_SPACE); if (2 * free_blocks < 3 * dirty_blocks || free_blocks < (dirty_blocks + EXT4_FREECLUSTERS_WATERMARK)) { diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 310972b72a66..ad3cc46a743a 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1332,47 +1332,43 @@ void writeback_inodes_sb(struct super_block *sb, enum wb_reason reason) EXPORT_SYMBOL(writeback_inodes_sb); /** - * writeback_inodes_sb_if_idle - start writeback if none underway - * @sb: the superblock - * @reason: reason why some writeback work was initiated - * - * Invoke writeback_inodes_sb if no writeback is currently underway. - * Returns 1 if writeback was started, 0 if not. - */ -int writeback_inodes_sb_if_idle(struct super_block *sb, enum wb_reason reason) -{ - if (!writeback_in_progress(sb->s_bdi)) { - down_read(&sb->s_umount); - writeback_inodes_sb(sb, reason); - up_read(&sb->s_umount); - return 1; - } else - return 0; -} -EXPORT_SYMBOL(writeback_inodes_sb_if_idle); - -/** - * writeback_inodes_sb_nr_if_idle - start writeback if none underway + * try_to_writeback_inodes_sb_nr - try to start writeback if none underway * @sb: the superblock * @nr: the number of pages to write - * @reason: reason why some writeback work was initiated + * @reason: the reason of writeback * - * Invoke writeback_inodes_sb if no writeback is currently underway. + * Invoke writeback_inodes_sb_nr if no writeback is currently underway. * Returns 1 if writeback was started, 0 if not. */ -int writeback_inodes_sb_nr_if_idle(struct super_block *sb, - unsigned long nr, - enum wb_reason reason) +int try_to_writeback_inodes_sb_nr(struct super_block *sb, + unsigned long nr, + enum wb_reason reason) { - if (!writeback_in_progress(sb->s_bdi)) { - down_read(&sb->s_umount); - writeback_inodes_sb_nr(sb, nr, reason); - up_read(&sb->s_umount); + if (writeback_in_progress(sb->s_bdi)) return 1; - } else + + if (!down_read_trylock(&sb->s_umount)) return 0; + + writeback_inodes_sb_nr(sb, nr, reason); + up_read(&sb->s_umount); + return 1; } -EXPORT_SYMBOL(writeback_inodes_sb_nr_if_idle); +EXPORT_SYMBOL(try_to_writeback_inodes_sb_nr); + +/** + * try_to_writeback_inodes_sb - try to start writeback if none underway + * @sb: the superblock + * @reason: reason why some writeback work was initiated + * + * Implement by try_to_writeback_inodes_sb_nr() + * Returns 1 if writeback was started, 0 if not. + */ +int try_to_writeback_inodes_sb(struct super_block *sb, enum wb_reason reason) +{ + return try_to_writeback_inodes_sb_nr(sb, get_nr_dirty_pages(), reason); +} +EXPORT_SYMBOL(try_to_writeback_inodes_sb); /** * sync_inodes_sb - sync sb inode pages diff --git a/include/linux/writeback.h b/include/linux/writeback.h index b82a83aba311..9a9367c0c076 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -87,9 +87,9 @@ int inode_wait(void *); void writeback_inodes_sb(struct super_block *, enum wb_reason reason); void writeback_inodes_sb_nr(struct super_block *, unsigned long nr, enum wb_reason reason); -int writeback_inodes_sb_if_idle(struct super_block *, enum wb_reason reason); -int writeback_inodes_sb_nr_if_idle(struct super_block *, unsigned long nr, - enum wb_reason reason); +int try_to_writeback_inodes_sb(struct super_block *, enum wb_reason reason); +int try_to_writeback_inodes_sb_nr(struct super_block *, unsigned long nr, + enum wb_reason reason); void sync_inodes_sb(struct super_block *); long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages, enum wb_reason reason); From 25bac122b8d874d625982aead1974be819eca841 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Sat, 12 Jan 2013 15:31:40 +0800 Subject: [PATCH 0819/4607] drivers/block/mtip32xx/mtip32xx.c:4029:1: sparse: symbol 'mtip_workq_sdbf0' was not declared. Should it be static? Hi Asai, FYI, there are new sparse warnings show up in tree: git://git.kernel.dk/linux-block.git for-3.9/drivers head: 3d6a87430e764cf4132710538139c10970929189 commit: 16c906e51c6f08a15763a85b5686e1ded35e77ab [1/3] mtip32xx: Add workqueue and NUMA support drivers/block/mtip32xx/mtip32xx.c:3267:17: sparse: cast to restricted __le32 >> drivers/block/mtip32xx/mtip32xx.c:4029:1: sparse: symbol 'mtip_workq_sdbf0' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4030:1: sparse: symbol 'mtip_workq_sdbf1' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4031:1: sparse: symbol 'mtip_workq_sdbf2' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4032:1: sparse: symbol 'mtip_workq_sdbf3' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4033:1: sparse: symbol 'mtip_workq_sdbf4' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4034:1: sparse: symbol 'mtip_workq_sdbf5' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4035:1: sparse: symbol 'mtip_workq_sdbf6' was not declared. Should it be static? >> drivers/block/mtip32xx/mtip32xx.c:4036:1: sparse: symbol 'mtip_workq_sdbf7' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c: In function 'mtip_hw_read_flags': drivers/block/mtip32xx/mtip32xx.c:2723:1: warning: the frame size of 1036 bytes is larger than 1024 bytes [-Wframe-larger-than=] drivers/block/mtip32xx/mtip32xx.c: In function 'mtip_hw_read_registers': drivers/block/mtip32xx/mtip32xx.c:2700:1: warning: the frame size of 1044 bytes is larger than 1024 bytes [-Wframe-larger-than=] Please consider folding the below diff :-) Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/mtip32xx.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index b7579b76b4a1..f07dbcba7ba6 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -4122,14 +4122,14 @@ static inline int mtip_get_next_rr_node(void) return next_node; } -DEFINE_HANDLER(0); -DEFINE_HANDLER(1); -DEFINE_HANDLER(2); -DEFINE_HANDLER(3); -DEFINE_HANDLER(4); -DEFINE_HANDLER(5); -DEFINE_HANDLER(6); -DEFINE_HANDLER(7); +static DEFINE_HANDLER(0); +static DEFINE_HANDLER(1); +static DEFINE_HANDLER(2); +static DEFINE_HANDLER(3); +static DEFINE_HANDLER(4); +static DEFINE_HANDLER(5); +static DEFINE_HANDLER(6); +static DEFINE_HANDLER(7); /* * Called for each supported PCI device detected. From 478c030eecbec927d62561c5f48a4515ea0fa21a Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Sat, 12 Jan 2013 16:13:01 +0800 Subject: [PATCH 0820/4607] drivers/block/mtip32xx/mtip32xx.c:1726:5: sparse: symbol 'mtip_send_trim' was not declared. Should it be static? Hi Asai, FYI, there are new sparse warnings show up in tree: git://git.kernel.dk/linux-block.git for-3.9/drivers head: 3d6a87430e764cf4132710538139c10970929189 commit: 152834694dcb90d3088bc77fa436755c66553929 [2/3] mtip32xx: add trim support >> drivers/block/mtip32xx/mtip32xx.c:1726:5: sparse: symbol 'mtip_send_trim' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:3348:17: sparse: cast to restricted __le32 drivers/block/mtip32xx/mtip32xx.c:4125:1: sparse: symbol 'mtip_workq_sdbf0' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4126:1: sparse: symbol 'mtip_workq_sdbf1' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4127:1: sparse: symbol 'mtip_workq_sdbf2' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4128:1: sparse: symbol 'mtip_workq_sdbf3' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4129:1: sparse: symbol 'mtip_workq_sdbf4' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4130:1: sparse: symbol 'mtip_workq_sdbf5' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4131:1: sparse: symbol 'mtip_workq_sdbf6' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c:4132:1: sparse: symbol 'mtip_workq_sdbf7' was not declared. Should it be static? drivers/block/mtip32xx/mtip32xx.c: In function 'mtip_hw_read_flags': drivers/block/mtip32xx/mtip32xx.c:2804:1: warning: the frame size of 1036 bytes is larger than 1024 bytes [-Wframe-larger-than=] drivers/block/mtip32xx/mtip32xx.c: In function 'mtip_hw_read_registers': drivers/block/mtip32xx/mtip32xx.c:2781:1: warning: the frame size of 1044 bytes is larger than 1024 bytes [-Wframe-larger-than=] Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/mtip32xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index f07dbcba7ba6..9790666f90f2 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -1723,7 +1723,7 @@ static int mtip_get_smart_attr(struct mtip_port *port, unsigned int id, * -EINVAL Invalid parameters passed in, trim not supported * -EIO Error submitting trim request to hw */ -int mtip_send_trim(struct driver_data *dd, unsigned int lba, unsigned int len) +static int mtip_send_trim(struct driver_data *dd, unsigned int lba, unsigned int len) { int i, rv = 0; u64 tlba, tlen, sect_left; From 61cc13a51bcff737ce02d2047834171c0365b00d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:52:56 +0200 Subject: [PATCH 0821/4607] dmaengine: introduce is_slave_direction function This function helps to distinguish the slave type of transfer by checking the direction parameter. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Reviewed-by: Mika Westerberg Reviewed-by: Linus Walleij Cc: Nicolas Ferre Cc: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 4ca9cf73ad3f..bfcdecb5d87a 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -621,6 +621,11 @@ static inline int dmaengine_slave_config(struct dma_chan *chan, (unsigned long)config); } +static inline bool is_slave_direction(enum dma_transfer_direction direction) +{ + return (direction == DMA_MEM_TO_DEV) || (direction == DMA_DEV_TO_MEM); +} + static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single( struct dma_chan *chan, dma_addr_t buf, size_t len, enum dma_transfer_direction dir, unsigned long flags) From 0e7264cc79a2d5c0ffa32c08d8f1cf84b2ec4fef Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:52:57 +0200 Subject: [PATCH 0822/4607] dma: at_hdmac: check direction properly for cyclic transfers dma_transfer_direction is a normal enum. It means we can't usually use the values as bit fields. Let's adjust this check and move it above the usage of the direction parameter, due to the nature of the following usage of it. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Acked-by: Nicolas Ferre Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 13a02f4425b0..6e13f262139a 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -778,7 +778,7 @@ err: */ static int atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr, - size_t period_len, enum dma_transfer_direction direction) + size_t period_len) { if (period_len > (ATC_BTSIZE_MAX << reg_width)) goto err_out; @@ -786,8 +786,6 @@ atc_dma_cyclic_check_values(unsigned int reg_width, dma_addr_t buf_addr, goto err_out; if (unlikely(buf_addr & ((1 << reg_width) - 1))) goto err_out; - if (unlikely(!(direction & (DMA_DEV_TO_MEM | DMA_MEM_TO_DEV)))) - goto err_out; return 0; @@ -886,14 +884,16 @@ atc_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, return NULL; } + if (unlikely(!is_slave_direction(direction))) + goto err_out; + if (sconfig->direction == DMA_MEM_TO_DEV) reg_width = convert_buswidth(sconfig->dst_addr_width); else reg_width = convert_buswidth(sconfig->src_addr_width); /* Check for too big/unaligned periods and unaligned DMA buffer */ - if (atc_dma_cyclic_check_values(reg_width, buf_addr, - period_len, direction)) + if (atc_dma_cyclic_check_values(reg_width, buf_addr, period_len)) goto err_out; /* build cyclic linked list */ From f44b92f4dd2f6caf326b149e0b9636a1d4e50184 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:52:58 +0200 Subject: [PATCH 0823/4607] dma: dw_dmac: check direction properly in dw_dma_cyclic_prep dma_transfer_direction is a normal enum. It means we can't usually use the values as bit fields. Let's adjust this check and move it above the usage of the direction parameter, due to the nature of the following usage of it. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 6c9e20a7ff51..ca996bc5ccaf 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1355,6 +1355,9 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, retval = ERR_PTR(-EINVAL); + if (unlikely(!is_slave_direction(direction))) + goto out_err; + if (direction == DMA_MEM_TO_DEV) reg_width = __ffs(sconfig->dst_addr_width); else @@ -1369,8 +1372,6 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, goto out_err; if (unlikely(buf_addr & ((1 << reg_width) - 1))) goto out_err; - if (unlikely(!(direction & (DMA_MEM_TO_DEV | DMA_DEV_TO_MEM)))) - goto out_err; retval = ERR_PTR(-ENOMEM); From 0efcdb20f4a83967c99da3d3bef9018f86532fae Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:52:59 +0200 Subject: [PATCH 0824/4607] dma: ep93xx_dma: reuse is_slave_direction helper The is_slave_direction helps to check if the transfer type is slave. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Acked-by: Mika Westerberg Signed-off-by: Vinod Koul --- drivers/dma/ep93xx_dma.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index bcfde400904f..f2bf8c0c4675 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -903,8 +903,7 @@ static int ep93xx_dma_alloc_chan_resources(struct dma_chan *chan) switch (data->port) { case EP93XX_DMA_SSP: case EP93XX_DMA_IDE: - if (data->direction != DMA_MEM_TO_DEV && - data->direction != DMA_DEV_TO_MEM) + if (!is_slave_direction(data->direction)) return -EINVAL; break; default: From 5127c4f8a314b798459985d93f7829cf9cf9bbc3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:00 +0200 Subject: [PATCH 0825/4607] dma: ipu_idmac: reuse is_slave_direction helper The is_slave_direction helps to check if the transfer type is slave. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Cc: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- drivers/dma/ipu/ipu_idmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 65855373cee6..8c61d17a86bf 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1347,7 +1347,7 @@ static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan chan->chan_id != IDMAC_IC_7) return NULL; - if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) { + if (!is_slave_direction(direction)) { dev_err(chan->device->dev, "Invalid DMA direction %d!\n", direction); return NULL; } From a725dcc0342b4d9ffc6ae4aedc2973d902aabeb1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:01 +0200 Subject: [PATCH 0826/4607] dma: ste_dma40: reuse is_slave_direction helper The is_slave_direction helps to check if the transfer type is slave. Signed-off-by: Andy Shevchenko Reviewed-by: Viresh Kumar Reviewed-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 23c5573e62dd..e5e60bb68d9d 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2337,7 +2337,7 @@ static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, unsigned long dma_flags, void *context) { - if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) + if (!is_slave_direction(direction)) return NULL; return d40_prep_sg(chan, sgl, sgl, sg_len, direction, dma_flags); From 01126856ff4f7d4cc5899c208fd4d3c7d0a2b83a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:02 +0200 Subject: [PATCH 0827/4607] dw_dmac: call .probe after we have a device in place If we don't yet have the platform device for the driver when it is being loaded we fail to probe the driver. So instead of calling probe() directly we call platform_driver_register(). It will call the probe() immediately if we have the device but also makes the driver to work on platforms where the platform device is created later. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index ca996bc5ccaf..e554027849b7 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1858,6 +1858,7 @@ MODULE_DEVICE_TABLE(of, dw_dma_id_table); #endif static struct platform_driver dw_driver = { + .probe = dw_probe, .remove = dw_remove, .shutdown = dw_shutdown, .driver = { @@ -1869,7 +1870,7 @@ static struct platform_driver dw_driver = { static int __init dw_init(void) { - return platform_driver_probe(&dw_driver, dw_probe); + return platform_driver_register(&dw_driver); } subsys_initcall(dw_init); From 0fdb567fc72da906e230ce7e2aae2feba260a6be Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:03 +0200 Subject: [PATCH 0828/4607] dw_dmac: store direction in the custom channel structure Currently the direction value comes from the generic slave configuration structure and explicitly as a preparation function parameter. The first one is kinda obsoleted. Thus, we have to store the value passed to the preparation function somewhere in our structures to be able to use it later. The best candidate to provide the storage is a custom channel structure. Until now we still keep and check the direction field of the slave config structure as well. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 12 ++++++++++-- drivers/dma/dw_dmac_regs.h | 14 ++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index e554027849b7..a2c5a60bc2e2 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -178,9 +178,9 @@ static void dwc_initialize(struct dw_dma_chan *dwc) cfghi = dws->cfg_hi; cfglo |= dws->cfg_lo & ~DWC_CFGL_CH_PRIOR_MASK; } else { - if (dwc->dma_sconfig.direction == DMA_MEM_TO_DEV) + if (dwc->direction == DMA_MEM_TO_DEV) cfghi = DWC_CFGH_DST_PER(dwc->dma_sconfig.slave_id); - else if (dwc->dma_sconfig.direction == DMA_DEV_TO_MEM) + else if (dwc->direction == DMA_DEV_TO_MEM) cfghi = DWC_CFGH_SRC_PER(dwc->dma_sconfig.slave_id); } @@ -721,6 +721,8 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, return NULL; } + dwc->direction = DMA_MEM_TO_MEM; + data_width = min_t(unsigned int, dwc->dw->data_width[dwc_get_sms(dws)], dwc->dw->data_width[dwc_get_dms(dws)]); @@ -805,6 +807,8 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, if (unlikely(!dws || !sg_len)) return NULL; + dwc->direction = direction; + prev = first = NULL; switch (direction) { @@ -981,6 +985,7 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) return -EINVAL; memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); + dwc->direction = sconfig->direction; convert_burst(&dwc->dma_sconfig.src_maxburst); convert_burst(&dwc->dma_sconfig.dst_maxburst); @@ -1358,6 +1363,8 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, if (unlikely(!is_slave_direction(direction))) goto out_err; + dwc->direction = direction; + if (direction == DMA_MEM_TO_DEV) reg_width = __ffs(sconfig->dst_addr_width); else @@ -1732,6 +1739,7 @@ static int dw_probe(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); dwc->dw = dw; + dwc->direction = DMA_TRANS_NONE; /* hardware configuration */ if (autocfg) { diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 8881e9b277a3..f9532c29b808 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -9,6 +9,7 @@ * published by the Free Software Foundation. */ +#include #include #define DW_DMA_MAX_NR_CHANNELS 8 @@ -184,12 +185,13 @@ enum dw_dmac_flags { }; struct dw_dma_chan { - struct dma_chan chan; - void __iomem *ch_regs; - u8 mask; - u8 priority; - bool paused; - bool initialized; + struct dma_chan chan; + void __iomem *ch_regs; + u8 mask; + u8 priority; + enum dma_transfer_direction direction; + bool paused; + bool initialized; /* software emulation of the LLP transfers */ struct list_head *tx_list; From 495aea4b571d1b7f77883f87754247b115627f68 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 11:11:41 +0200 Subject: [PATCH 0829/4607] dw_dmac: make usage of dw_dma_slave optional The driver requires a custom slave configuration to be present to be able to make the slave transfers. Nevertheless, in some cases we need only the request line as an additional information to the generic slave configuration. The request line is provided by slave_id parameter of the dma_slave_config structure. That's why the custom slave configuration could be optional for such cases. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index a2c5a60bc2e2..e5378b5741be 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -50,11 +50,12 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) struct dw_dma_slave *__slave = (_chan->private); \ struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ + bool _is_slave = is_slave_direction(_dwc->direction); \ int _dms = dwc_get_dms(__slave); \ int _sms = dwc_get_sms(__slave); \ - u8 _smsize = __slave ? _sconfig->src_maxburst : \ + u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ DW_DMA_MSIZE_16; \ - u8 _dmsize = __slave ? _sconfig->dst_maxburst : \ + u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ DW_DMA_MSIZE_16; \ \ (DWC_CTLL_DST_MSIZE(_dmsize) \ @@ -328,7 +329,7 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, list_splice_init(&desc->tx_list, &dwc->free_list); list_move(&desc->desc_node, &dwc->free_list); - if (!dwc->chan.private) { + if (!is_slave_direction(dwc->direction)) { struct device *parent = chan2parent(&dwc->chan); if (!(txd->flags & DMA_COMPL_SKIP_DEST_UNMAP)) { if (txd->flags & DMA_COMPL_DEST_UNMAP_SINGLE) @@ -804,7 +805,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, dev_vdbg(chan2dev(chan), "%s\n", __func__); - if (unlikely(!dws || !sg_len)) + if (unlikely(!is_slave_direction(direction) || !sg_len)) return NULL; dwc->direction = direction; @@ -980,8 +981,8 @@ set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - /* Check if it is chan is configured for slave transfers */ - if (!chan->private) + /* Check if chan will be configured for slave transfers */ + if (!is_slave_direction(sconfig->direction)) return -EINVAL; memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig)); From 23d5f4ec9de43dbc73a42f1483d9339b907c3dff Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 10 Jan 2013 10:53:05 +0200 Subject: [PATCH 0830/4607] dw_dmac: backlink to dw_dma in dw_dma_chan is superfluous The same information could be extracted from the struct dma_chan. The patch introduces helper function dwc_get_data_width() as well. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 27 ++++++++++++++++++++------- drivers/dma/dw_dmac_regs.h | 3 --- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index e5378b5741be..154952abc2e9 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -73,6 +73,22 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) */ #define NR_DESCS_PER_CHANNEL 64 +#define SRC_MASTER 0 +#define DST_MASTER 1 + +static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) +{ + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_dma_slave *dws = chan->private; + + if (master == SRC_MASTER) + return dw->data_width[dwc_get_sms(dws)]; + else if (master == DST_MASTER) + return dw->data_width[dwc_get_dms(dws)]; + + return 0; +} + /*----------------------------------------------------------------------*/ /* @@ -701,7 +717,6 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, size_t len, unsigned long flags) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_slave *dws = chan->private; struct dw_desc *desc; struct dw_desc *first; struct dw_desc *prev; @@ -724,8 +739,8 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, dwc->direction = DMA_MEM_TO_MEM; - data_width = min_t(unsigned int, dwc->dw->data_width[dwc_get_sms(dws)], - dwc->dw->data_width[dwc_get_dms(dws)]); + data_width = min_t(unsigned int, dwc_get_data_width(chan, SRC_MASTER), + dwc_get_data_width(chan, DST_MASTER)); src_width = dst_width = min_t(unsigned int, data_width, dwc_fast_fls(src | dest | len)); @@ -790,7 +805,6 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned long flags, void *context) { struct dw_dma_chan *dwc = to_dw_dma_chan(chan); - struct dw_dma_slave *dws = chan->private; struct dma_slave_config *sconfig = &dwc->dma_sconfig; struct dw_desc *prev; struct dw_desc *first; @@ -824,7 +838,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) : DWC_CTLL_FC(DW_DMA_FC_D_M2P); - data_width = dwc->dw->data_width[dwc_get_sms(dws)]; + data_width = dwc_get_data_width(chan, SRC_MASTER); for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; @@ -887,7 +901,7 @@ slave_sg_todev_fill_desc: ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) : DWC_CTLL_FC(DW_DMA_FC_D_P2M); - data_width = dwc->dw->data_width[dwc_get_dms(dws)]; + data_width = dwc_get_data_width(chan, DST_MASTER); for_each_sg(sgl, sg, sg_len, i) { struct dw_desc *desc; @@ -1739,7 +1753,6 @@ static int dw_probe(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); - dwc->dw = dw; dwc->direction = DMA_TRANS_NONE; /* hardware configuration */ diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index f9532c29b808..577f2dd35882 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -214,9 +214,6 @@ struct dw_dma_chan { /* configuration passed via DMA_SLAVE_CONFIG */ struct dma_slave_config dma_sconfig; - - /* backlink to dw_dma */ - struct dw_dma *dw; }; static inline struct dw_dma_chan_regs __iomem * From a5dbff111cacecd2e79843a51cc86d21d3648af5 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Thu, 10 Jan 2013 10:53:06 +0200 Subject: [PATCH 0831/4607] dma: dw_dmac: clear suspend bit during termination The DMA transfer could not be established if previously it was paused and terminated. In that case the channel's suspend bit remains set that prevents to transfer anything until channel is resumed. The patch adds the dwc_chan_resume() call instead of a plain flag assignment. That clears the DWC_CFGL_CH_SUSP bit as well during termination. Signed-off-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 154952abc2e9..28d5f01c350c 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1059,7 +1059,7 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, dwc_chan_disable(dw, dwc); - dwc->paused = false; + dwc_chan_resume(dwc); /* active_list entries will end up before queued entries */ list_splice_init(&dwc->queue, &list); From 860d21e2c585f7ee8a4ecc06f474fdc33c9474f4 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 12 Jan 2013 16:19:36 -0500 Subject: [PATCH 0832/4607] ext4: return ENOMEM if sb_getblk() fails The only reason for sb_getblk() failing is if it can't allocate the buffer_head. So ENOMEM is more appropriate than EIO. In addition, make sure that the file system is marked as being inconsistent if sb_getblk() fails. Signed-off-by: "Theodore Ts'o" Cc: stable@vger.kernel.org --- fs/ext4/extents.c | 25 ++++++++++++++----------- fs/ext4/indirect.c | 9 ++++++--- fs/ext4/inline.c | 2 +- fs/ext4/inode.c | 9 +++------ fs/ext4/mmp.c | 2 ++ fs/ext4/resize.c | 8 ++++---- fs/ext4/xattr.c | 3 ++- 7 files changed, 32 insertions(+), 26 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 5ae1674ec12f..d42a8c49ad69 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -725,6 +725,7 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block, struct ext4_extent_header *eh; struct buffer_head *bh; short int depth, i, ppos = 0, alloc = 0; + int ret; eh = ext_inode_hdr(inode); depth = ext_depth(inode); @@ -752,12 +753,15 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block, path[ppos].p_ext = NULL; bh = sb_getblk(inode->i_sb, path[ppos].p_block); - if (unlikely(!bh)) + if (unlikely(!bh)) { + ret = -ENOMEM; goto err; + } if (!bh_uptodate_or_lock(bh)) { trace_ext4_ext_load_extent(inode, block, path[ppos].p_block); - if (bh_submit_read(bh) < 0) { + ret = bh_submit_read(bh); + if (ret < 0) { put_bh(bh); goto err; } @@ -768,13 +772,15 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block, put_bh(bh); EXT4_ERROR_INODE(inode, "ppos %d > depth %d", ppos, depth); + ret = -EIO; goto err; } path[ppos].p_bh = bh; path[ppos].p_hdr = eh; i--; - if (ext4_ext_check_block(inode, eh, i, bh)) + ret = ext4_ext_check_block(inode, eh, i, bh); + if (ret < 0) goto err; } @@ -796,7 +802,7 @@ err: ext4_ext_drop_refs(path); if (alloc) kfree(path); - return ERR_PTR(-EIO); + return ERR_PTR(ret); } /* @@ -951,7 +957,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, } bh = sb_getblk(inode->i_sb, newblock); if (!bh) { - err = -EIO; + err = -ENOMEM; goto cleanup; } lock_buffer(bh); @@ -1024,7 +1030,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, newblock = ablocks[--a]; bh = sb_getblk(inode->i_sb, newblock); if (!bh) { - err = -EIO; + err = -ENOMEM; goto cleanup; } lock_buffer(bh); @@ -1136,11 +1142,8 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode, return err; bh = sb_getblk(inode->i_sb, newblock); - if (!bh) { - err = -EIO; - ext4_std_error(inode->i_sb, err); - return err; - } + if (!bh) + return -ENOMEM; lock_buffer(bh); err = ext4_journal_get_create_access(handle, bh); diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 20862f96e8ae..8d83d1e508e4 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -146,6 +146,7 @@ static Indirect *ext4_get_branch(struct inode *inode, int depth, struct super_block *sb = inode->i_sb; Indirect *p = chain; struct buffer_head *bh; + int ret = -EIO; *err = 0; /* i_data is not going away, no lock needed */ @@ -154,8 +155,10 @@ static Indirect *ext4_get_branch(struct inode *inode, int depth, goto no_block; while (--depth) { bh = sb_getblk(sb, le32_to_cpu(p->key)); - if (unlikely(!bh)) + if (unlikely(!bh)) { + ret = -ENOMEM; goto failure; + } if (!bh_uptodate_or_lock(bh)) { if (bh_submit_read(bh) < 0) { @@ -177,7 +180,7 @@ static Indirect *ext4_get_branch(struct inode *inode, int depth, return NULL; failure: - *err = -EIO; + *err = ret; no_block: return p; } @@ -471,7 +474,7 @@ static int ext4_alloc_branch(handle_t *handle, struct inode *inode, */ bh = sb_getblk(inode->i_sb, new_blocks[n-1]); if (unlikely(!bh)) { - err = -EIO; + err = -ENOMEM; goto failed; } diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 387c47c6cda9..93a3408fc89b 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -1188,7 +1188,7 @@ static int ext4_convert_inline_data_nolock(handle_t *handle, data_bh = sb_getblk(inode->i_sb, map.m_pblk); if (!data_bh) { - error = -EIO; + error = -ENOMEM; goto out_restore; } diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index cbfe13bf5b2a..9ccc140b82d2 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -714,7 +714,7 @@ struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode, bh = sb_getblk(inode->i_sb, map.m_pblk); if (!bh) { - *errp = -EIO; + *errp = -ENOMEM; return NULL; } if (map.m_flags & EXT4_MAP_NEW) { @@ -3660,11 +3660,8 @@ static int __ext4_get_inode_loc(struct inode *inode, iloc->offset = (inode_offset % inodes_per_block) * EXT4_INODE_SIZE(sb); bh = sb_getblk(sb, block); - if (!bh) { - EXT4_ERROR_INODE_BLOCK(inode, block, - "unable to read itable block"); - return -EIO; - } + if (!bh) + return -ENOMEM; if (!buffer_uptodate(bh)) { lock_buffer(bh); diff --git a/fs/ext4/mmp.c b/fs/ext4/mmp.c index fe7c63f4717e..44734f1ca554 100644 --- a/fs/ext4/mmp.c +++ b/fs/ext4/mmp.c @@ -80,6 +80,8 @@ static int read_mmp_block(struct super_block *sb, struct buffer_head **bh, * is not blocked in the elevator. */ if (!*bh) *bh = sb_getblk(sb, mmp_block); + if (!*bh) + return -ENOMEM; if (*bh) { get_bh(*bh); lock_buffer(*bh); diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index d99387b89edd..02824dc2ff3b 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -334,7 +334,7 @@ static struct buffer_head *bclean(handle_t *handle, struct super_block *sb, bh = sb_getblk(sb, blk); if (!bh) - return ERR_PTR(-EIO); + return ERR_PTR(-ENOMEM); if ((err = ext4_journal_get_write_access(handle, bh))) { brelse(bh); bh = ERR_PTR(err); @@ -411,7 +411,7 @@ static int set_flexbg_block_bitmap(struct super_block *sb, handle_t *handle, bh = sb_getblk(sb, flex_gd->groups[group].block_bitmap); if (!bh) - return -EIO; + return -ENOMEM; err = ext4_journal_get_write_access(handle, bh); if (err) @@ -501,7 +501,7 @@ static int setup_new_flex_group_blocks(struct super_block *sb, gdb = sb_getblk(sb, block); if (!gdb) { - err = -EIO; + err = -ENOMEM; goto out; } @@ -1065,7 +1065,7 @@ static void update_backups(struct super_block *sb, int blk_off, char *data, bh = sb_getblk(sb, backup_block); if (!bh) { - err = -EIO; + err = -ENOMEM; break; } ext4_debug("update metadata backup %llu(+%llu)\n", diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 3a91ebc2b66f..07d684a4e523 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -887,16 +887,17 @@ inserted: new_bh = sb_getblk(sb, block); if (!new_bh) { + error = -ENOMEM; getblk_failed: ext4_free_blocks(handle, inode, NULL, block, 1, EXT4_FREE_BLOCKS_METADATA); - error = -EIO; goto cleanup; } lock_buffer(new_bh); error = ext4_journal_get_create_access(handle, new_bh); if (error) { unlock_buffer(new_bh); + error = -EIO; goto getblk_failed; } memcpy(new_bh->b_data, s->base, new_bh->b_size); From aebf02430d25b6bd2b8542126fdcdb90e75a24b8 Mon Sep 17 00:00:00 2001 From: Wang Shilong Date: Sat, 12 Jan 2013 16:28:47 -0500 Subject: [PATCH 0833/4607] ext4: use unlikely to improve the efficiency of the kernel Because the function 'sb_getblk' seldomly fails to return NULL value,it will be better to use 'unlikely' to optimize it. Signed-off-by: Wang Shilong Signed-off-by: "Theodore Ts'o" --- fs/ext4/extents.c | 6 +++--- fs/ext4/inode.c | 6 +++--- fs/ext4/mmp.c | 2 +- fs/ext4/resize.c | 10 +++++----- fs/ext4/xattr.c | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index d42a8c49ad69..391e53a52e58 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -956,7 +956,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, goto cleanup; } bh = sb_getblk(inode->i_sb, newblock); - if (!bh) { + if (unlikely(!bh)) { err = -ENOMEM; goto cleanup; } @@ -1029,7 +1029,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, oldblock = newblock; newblock = ablocks[--a]; bh = sb_getblk(inode->i_sb, newblock); - if (!bh) { + if (unlikely(!bh)) { err = -ENOMEM; goto cleanup; } @@ -1142,7 +1142,7 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode, return err; bh = sb_getblk(inode->i_sb, newblock); - if (!bh) + if (unlikely(!bh)) return -ENOMEM; lock_buffer(bh); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 9ccc140b82d2..93a7e8453a68 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -713,7 +713,7 @@ struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode, return NULL; bh = sb_getblk(inode->i_sb, map.m_pblk); - if (!bh) { + if (unlikely(!bh)) { *errp = -ENOMEM; return NULL; } @@ -3660,7 +3660,7 @@ static int __ext4_get_inode_loc(struct inode *inode, iloc->offset = (inode_offset % inodes_per_block) * EXT4_INODE_SIZE(sb); bh = sb_getblk(sb, block); - if (!bh) + if (unlikely(!bh)) return -ENOMEM; if (!buffer_uptodate(bh)) { lock_buffer(bh); @@ -3693,7 +3693,7 @@ static int __ext4_get_inode_loc(struct inode *inode, /* Is the inode bitmap in cache? */ bitmap_bh = sb_getblk(sb, ext4_inode_bitmap(sb, gdp)); - if (!bitmap_bh) + if (unlikely(!bitmap_bh)) goto make_io; /* diff --git a/fs/ext4/mmp.c b/fs/ext4/mmp.c index 44734f1ca554..f9b551561d2c 100644 --- a/fs/ext4/mmp.c +++ b/fs/ext4/mmp.c @@ -93,7 +93,7 @@ static int read_mmp_block(struct super_block *sb, struct buffer_head **bh, *bh = NULL; } } - if (!*bh) { + if (unlikely(!*bh)) { ext4_warning(sb, "Error while reading MMP block %llu", mmp_block); return -EIO; diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 02824dc2ff3b..05f8d4502d42 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -333,7 +333,7 @@ static struct buffer_head *bclean(handle_t *handle, struct super_block *sb, int err; bh = sb_getblk(sb, blk); - if (!bh) + if (unlikely(!bh)) return ERR_PTR(-ENOMEM); if ((err = ext4_journal_get_write_access(handle, bh))) { brelse(bh); @@ -410,7 +410,7 @@ static int set_flexbg_block_bitmap(struct super_block *sb, handle_t *handle, return err; bh = sb_getblk(sb, flex_gd->groups[group].block_bitmap); - if (!bh) + if (unlikely(!bh)) return -ENOMEM; err = ext4_journal_get_write_access(handle, bh); @@ -500,7 +500,7 @@ static int setup_new_flex_group_blocks(struct super_block *sb, goto out; gdb = sb_getblk(sb, block); - if (!gdb) { + if (unlikely(!gdb)) { err = -ENOMEM; goto out; } @@ -1064,7 +1064,7 @@ static void update_backups(struct super_block *sb, int blk_off, char *data, ext4_bg_has_super(sb, group)); bh = sb_getblk(sb, backup_block); - if (!bh) { + if (unlikely(!bh)) { err = -ENOMEM; break; } @@ -1168,7 +1168,7 @@ static int ext4_add_new_descs(handle_t *handle, struct super_block *sb, static struct buffer_head *ext4_get_bitmap(struct super_block *sb, __u64 block) { struct buffer_head *bh = sb_getblk(sb, block); - if (!bh) + if (unlikely(!bh)) return NULL; if (!bh_uptodate_or_lock(bh)) { if (bh_submit_read(bh) < 0) { diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 07d684a4e523..c68990c392c7 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -886,7 +886,7 @@ inserted: (unsigned long long)block); new_bh = sb_getblk(sb, block); - if (!new_bh) { + if (unlikely(!new_bh)) { error = -ENOMEM; getblk_failed: ext4_free_blocks(handle, inode, NULL, block, 1, From 15b49132fc972c63894592f218ea5a9a61b1a18f Mon Sep 17 00:00:00 2001 From: Eryu Guan Date: Sat, 12 Jan 2013 16:33:25 -0500 Subject: [PATCH 0834/4607] ext4: check bh in ext4_read_block_bitmap() Validate the bh pointer before using it, since ext4_read_block_bitmap_nowait() might return NULL. I've seen this in fsfuzz testing. EXT4-fs error (device loop0): ext4_read_block_bitmap_nowait:385: comm touch: Cannot get buffer for block bitmap - block_group = 0, block_bitmap = 3925999616 BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] ext4_wait_block_bitmap+0x25/0xe0 ... Call Trace: [] ext4_read_block_bitmap+0x35/0x60 [] ext4_free_blocks+0x236/0xb80 [] ? __getblk+0x36/0x70 [] ? __find_get_block+0x8f/0x210 [] ? kmem_cache_free+0x33/0x140 [] ext4_xattr_release_block+0x1b5/0x1d0 [] ext4_xattr_delete_inode+0xbe/0x100 [] ext4_free_inode+0x7c/0x4d0 [] ? ext4_mark_inode_dirty+0x88/0x230 [] ext4_evict_inode+0x32c/0x490 [] evict+0xa7/0x1c0 [] iput_final+0xe3/0x170 [] iput+0x3e/0x50 [] ext4_add_nondir+0x4d/0x90 [] ext4_create+0xeb/0x170 [] vfs_create+0xac/0xd0 [] lookup_open+0x185/0x1c0 [] ? selinux_inode_permission+0xa9/0x170 [] do_last+0x2d4/0x7a0 [] path_openat+0xb3/0x480 [] ? handle_mm_fault+0x251/0x3b0 [] do_filp_open+0x49/0xa0 [] ? __alloc_fd+0xdd/0x150 [] do_sys_open+0x108/0x1f0 [] sys_open+0x21/0x30 [] system_call_fastpath+0x16/0x1b Also fix comment for ext4_read_block_bitmap_nowait() Signed-off-by: Eryu Guan Signed-off-by: "Theodore Ts'o" Cc: stable@vger.kernel.org --- fs/ext4/balloc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index cf1821784a16..33938c120c85 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -358,7 +358,7 @@ void ext4_validate_block_bitmap(struct super_block *sb, } /** - * ext4_read_block_bitmap() + * ext4_read_block_bitmap_nowait() * @sb: super block * @block_group: given block group * @@ -457,6 +457,8 @@ ext4_read_block_bitmap(struct super_block *sb, ext4_group_t block_group) struct buffer_head *bh; bh = ext4_read_block_bitmap_nowait(sb, block_group); + if (!bh) + return NULL; if (ext4_wait_block_bitmap(sb, block_group, bh)) { put_bh(bh); return NULL; From 3a95b9fbba893ebfa9b83de105707539e0228e0c Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Sat, 24 Nov 2012 00:22:56 +0000 Subject: [PATCH 0835/4607] pl080.h: moved from arm/include/asm/hardware to include/linux/amba/ The header is used by drivers/dma/amba-pl08x.c, which can be compiled under x86, where PL080 exists under a PCI-to-AMBA bridge. This patche moves it where it can be accessed by other architectures, and fixes all users. Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- arch/arm/mach-s3c64xx/dma.c | 2 +- arch/arm/mach-spear3xx/spear3xx.c | 1 - arch/arm/mach-spear6xx/spear6xx.c | 2 +- drivers/dma/amba-pl08x.c | 2 +- {arch/arm/include/asm/hardware => include/linux/amba}/pl080.h | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) rename {arch/arm/include/asm/hardware => include/linux/amba}/pl080.h (99%) diff --git a/arch/arm/mach-s3c64xx/dma.c b/arch/arm/mach-s3c64xx/dma.c index f2a7a1725596..a77f5214bbe8 100644 --- a/arch/arm/mach-s3c64xx/dma.c +++ b/arch/arm/mach-s3c64xx/dma.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -30,7 +31,6 @@ #include -#include /* dma channel state information */ diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c index 38fe95db31a7..3d9b1b5e8ed9 100644 --- a/arch/arm/mach-spear3xx/spear3xx.c +++ b/arch/arm/mach-spear3xx/spear3xx.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/arm/mach-spear6xx/spear6xx.c b/arch/arm/mach-spear6xx/spear6xx.c index 5a5a52db252b..8ce65a23b06e 100644 --- a/arch/arm/mach-spear6xx/spear6xx.c +++ b/arch/arm/mach-spear6xx/spear6xx.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 6eb6a5c210bb..8bad254a498d 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -83,7 +83,7 @@ #include #include #include -#include +#include #include "dmaengine.h" #include "virt-dma.h" diff --git a/arch/arm/include/asm/hardware/pl080.h b/include/linux/amba/pl080.h similarity index 99% rename from arch/arm/include/asm/hardware/pl080.h rename to include/linux/amba/pl080.h index 4eea2107214b..3e7b62fbefbd 100644 --- a/arch/arm/include/asm/hardware/pl080.h +++ b/include/linux/amba/pl080.h @@ -1,4 +1,4 @@ -/* arch/arm/include/asm/hardware/pl080.h +/* include/linux/amba/pl080.h * * Copyright 2008 Openmoko, Inc. * Copyright 2008 Simtec Electronics From 7f5118629f74b82bd4ba5e47415d1b4dcb940241 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sun, 13 Jan 2013 08:41:45 -0500 Subject: [PATCH 0836/4607] ext4: trigger the lazy inode table initialization after resize After we have finished extending the file system, we need to trigger a the lazy inode table thread to zero out the inode tables. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 2 ++ fs/ext4/ioctl.c | 9 +++++++++ fs/ext4/resize.c | 8 +++++--- fs/ext4/super.c | 21 +++++++++++---------- 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 8462eb3c33aa..80246237f6d5 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2227,6 +2227,8 @@ extern int ext4_group_desc_csum_verify(struct super_block *sb, __u32 group, struct ext4_group_desc *gdp); extern void ext4_group_desc_csum_set(struct super_block *sb, __u32 group, struct ext4_group_desc *gdp); +extern int ext4_register_li_request(struct super_block *sb, + ext4_group_t first_not_zeroed); static inline int ext4_has_group_desc_csum(struct super_block *sb) { diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 5747f52f7c72..4784ac244fc6 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -313,6 +313,9 @@ mext_out: if (err == 0) err = err2; mnt_drop_write_file(filp); + if (!err && ext4_has_group_desc_csum(sb) && + test_opt(sb, INIT_INODE_TABLE)) + err = ext4_register_li_request(sb, input.group); group_add_out: ext4_resize_end(sb); return err; @@ -358,6 +361,7 @@ group_add_out: ext4_fsblk_t n_blocks_count; struct super_block *sb = inode->i_sb; int err = 0, err2 = 0; + ext4_group_t o_group = EXT4_SB(sb)->s_groups_count; if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { @@ -388,6 +392,11 @@ group_add_out: if (err == 0) err = err2; mnt_drop_write_file(filp); + if (!err && (o_group > EXT4_SB(sb)->s_groups_count) && + ext4_has_group_desc_csum(sb) && + test_opt(sb, INIT_INODE_TABLE)) + err = ext4_register_li_request(sb, o_group); + resizefs_out: ext4_resize_end(sb); return err; diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 05f8d4502d42..8eefb636beb8 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -1506,10 +1506,12 @@ static int ext4_setup_next_flex_gd(struct super_block *sb, group_data[i].blocks_count = blocks_per_group; overhead = ext4_group_overhead_blocks(sb, group + i); group_data[i].free_blocks_count = blocks_per_group - overhead; - if (ext4_has_group_desc_csum(sb)) + if (ext4_has_group_desc_csum(sb)) { flex_gd->bg_flags[i] = EXT4_BG_BLOCK_UNINIT | EXT4_BG_INODE_UNINIT; - else + if (!test_opt(sb, INIT_INODE_TABLE)) + flex_gd->bg_flags[i] |= EXT4_BG_INODE_ZEROED; + } else flex_gd->bg_flags[i] = EXT4_BG_INODE_ZEROED; } @@ -1594,7 +1596,7 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) err = ext4_alloc_flex_bg_array(sb, input->group + 1); if (err) - return err; + goto out; err = ext4_mb_alloc_groupinfo(sb, input->group + 1); if (err) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 3d4fb81bacd5..c014edd12648 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2776,7 +2776,7 @@ static int ext4_run_li_request(struct ext4_li_request *elr) break; } - if (group == ngroups) + if (group >= ngroups) ret = 1; if (!ret) { @@ -3016,33 +3016,34 @@ static struct ext4_li_request *ext4_li_request_new(struct super_block *sb, return elr; } -static int ext4_register_li_request(struct super_block *sb, - ext4_group_t first_not_zeroed) +int ext4_register_li_request(struct super_block *sb, + ext4_group_t first_not_zeroed) { struct ext4_sb_info *sbi = EXT4_SB(sb); - struct ext4_li_request *elr; + struct ext4_li_request *elr = NULL; ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count; int ret = 0; + mutex_lock(&ext4_li_mtx); if (sbi->s_li_request != NULL) { /* * Reset timeout so it can be computed again, because * s_li_wait_mult might have changed. */ sbi->s_li_request->lr_timeout = 0; - return 0; + goto out; } if (first_not_zeroed == ngroups || (sb->s_flags & MS_RDONLY) || !test_opt(sb, INIT_INODE_TABLE)) - return 0; + goto out; elr = ext4_li_request_new(sb, first_not_zeroed); - if (!elr) - return -ENOMEM; - - mutex_lock(&ext4_li_mtx); + if (!elr) { + ret = -ENOMEM; + goto out; + } if (NULL == ext4_li_info) { ret = ext4_li_info_new(); From 9f14b4201239a9779d52bdd6137583d452b38ec3 Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Sat, 29 Dec 2012 00:08:22 +0100 Subject: [PATCH 0837/4607] scripts/tags.sh: Fix regex syntax for etags Signed-off-by: Andreas Schwab Tested-by: Jesper Juhl Signed-off-by: Michal Marek --- scripts/tags.sh | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/scripts/tags.sh b/scripts/tags.sh index 08f06c00745e..4c53b7d883d3 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -201,34 +201,34 @@ exuberant() emacs() { all_target_sources | xargs $1 -a \ - --regex='/^(ENTRY|_GLOBAL)(\([^)]*\)).*/\2/' \ + --regex='/^\(ENTRY\|_GLOBAL\)(\([^)]*\)).*/\2/' \ --regex='/^SYSCALL_DEFINE[0-9]?(\([^,)]*\).*/sys_\1/' \ --regex='/^TRACE_EVENT(\([^,)]*\).*/trace_\1/' \ --regex='/^DEFINE_EVENT([^,)]*, *\([^,)]*\).*/trace_\1/' \ - --regex='/PAGEFLAG\(([^,)]*).*/Page\1/' \ - --regex='/PAGEFLAG\(([^,)]*).*/SetPage\1/' \ - --regex='/PAGEFLAG\(([^,)]*).*/ClearPage\1/' \ - --regex='/TESTSETFLAG\(([^,)]*).*/TestSetPage\1/' \ - --regex='/TESTPAGEFLAG\(([^,)]*).*/Page\1/' \ - --regex='/SETPAGEFLAG\(([^,)]*).*/SetPage\1/' \ - --regex='/__SETPAGEFLAG\(([^,)]*).*/__SetPage\1/' \ - --regex='/TESTCLEARFLAG\(([^,)]*).*/TestClearPage\1/' \ - --regex='/__TESTCLEARFLAG\(([^,)]*).*/TestClearPage\1/' \ - --regex='/CLEARPAGEFLAG\(([^,)]*).*/ClearPage\1/' \ - --regex='/__CLEARPAGEFLAG\(([^,)]*).*/__ClearPage\1/' \ - --regex='/__PAGEFLAG\(([^,)]*).*/__SetPage\1/' \ - --regex='/__PAGEFLAG\(([^,)]*).*/__ClearPage\1/' \ - --regex='/PAGEFLAG_FALSE\(([^,)]*).*/Page\1/' \ - --regex='/TESTSCFLAG\(([^,)]*).*/TestSetPage\1/' \ - --regex='/TESTSCFLAG\(([^,)]*).*/TestClearPage\1/' \ - --regex='/SETPAGEFLAG_NOOP\(([^,)]*).*/SetPage\1/' \ - --regex='/CLEARPAGEFLAG_NOOP\(([^,)]*).*/ClearPage\1/' \ - --regex='/__CLEARPAGEFLAG_NOOP\(([^,)]*).*/__ClearPage\1/' \ - --regex='/TESTCLEARFLAG_FALSE\(([^,)]*).*/TestClearPage\1/' \ - --regex='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/' \ - --regex='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' \ - --regex='/PCI_OP_READ\(([a-z]*[a-z]).*[1-4]\)/pci_bus_read_config_\1/' \ - --regex='/PCI_OP_WRITE\(([a-z]*[a-z]).*[1-4]\)/pci_bus_write_config_\1/' + --regex='/PAGEFLAG(\([^,)]*\).*/Page\1/' \ + --regex='/PAGEFLAG(\([^,)]*\).*/SetPage\1/' \ + --regex='/PAGEFLAG(\([^,)]*\).*/ClearPage\1/' \ + --regex='/TESTSETFLAG(\([^,)]*\).*/TestSetPage\1/' \ + --regex='/TESTPAGEFLAG(\([^,)]*\).*/Page\1/' \ + --regex='/SETPAGEFLAG(\([^,)]*\).*/SetPage\1/' \ + --regex='/__SETPAGEFLAG(\([^,)]*\).*/__SetPage\1/' \ + --regex='/TESTCLEARFLAG(\([^,)]*\).*/TestClearPage\1/' \ + --regex='/__TESTCLEARFLAG(\([^,)]*\).*/TestClearPage\1/' \ + --regex='/CLEARPAGEFLAG(\([^,)]*\).*/ClearPage\1/' \ + --regex='/__CLEARPAGEFLAG(\([^,)]*\).*/__ClearPage\1/' \ + --regex='/__PAGEFLAG(\([^,)]*\).*/__SetPage\1/' \ + --regex='/__PAGEFLAG(\([^,)]*\).*/__ClearPage\1/' \ + --regex='/PAGEFLAG_FALSE(\([^,)]*\).*/Page\1/' \ + --regex='/TESTSCFLAG(\([^,)]*\).*/TestSetPage\1/' \ + --regex='/TESTSCFLAG(\([^,)]*\).*/TestClearPage\1/' \ + --regex='/SETPAGEFLAG_NOOP(\([^,)]*\).*/SetPage\1/' \ + --regex='/CLEARPAGEFLAG_NOOP(\([^,)]*\).*/ClearPage\1/' \ + --regex='/__CLEARPAGEFLAG_NOOP(\([^,)]*\).*/__ClearPage\1/' \ + --regex='/TESTCLEARFLAG_FALSE(\([^,)]*\).*/TestClearPage\1/' \ + --regex='/__TESTCLEARFLAG_FALSE(\([^,)]*\).*/__TestClearPage\1/' \ + --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/' \ + --regex='/PCI_OP_READ(\([a-z]*[a-z]\).*[1-4])/pci_bus_read_config_\1/' \ + --regex='/PCI_OP_WRITE(\([a-z]*[a-z]\).*[1-4])/pci_bus_write_config_\1/' all_kconfigs | xargs $1 -a \ --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/\3/' From 6c0cc950ae670403a362bdcbf3cde0df33744928 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 9 Jan 2013 22:33:37 +0100 Subject: [PATCH 0838/4607] ACPI / PCI: Set root bridge ACPI handle in advance The ACPI handles of PCI root bridges need to be known to acpi_bind_one(), so that it can create the appropriate "firmware_node" and "physical_node" files for them, but currently the way it gets to know those handles is not exactly straightforward (to put it lightly). This is how it works, roughly: 1. acpi_bus_scan() finds the handle of a PCI root bridge, creates a struct acpi_device object for it and passes that object to acpi_pci_root_add(). 2. acpi_pci_root_add() creates a struct acpi_pci_root object, populates its "device" field with its argument's address (device->handle is the ACPI handle found in step 1). 3. The struct acpi_pci_root object created in step 2 is passed to pci_acpi_scan_root() and used to get resources that are passed to pci_create_root_bus(). 4. pci_create_root_bus() creates a struct pci_host_bridge object and passes its "dev" member to device_register(). 5. platform_notify(), which for systems with ACPI is set to acpi_platform_notify(), is called. So far, so good. Now it starts to be "interesting". 6. acpi_find_bridge_device() is used to find the ACPI handle of the given device (which is the PCI root bridge) and executes acpi_pci_find_root_bridge(), among other things, for the given device object. 7. acpi_pci_find_root_bridge() uses the name (sic!) of the given device object to extract the segment and bus numbers of the PCI root bridge and passes them to acpi_get_pci_rootbridge_handle(). 8. acpi_get_pci_rootbridge_handle() browses the list of ACPI PCI root bridges and finds the one that matches the given segment and bus numbers. Its handle is then used to initialize the ACPI handle of the PCI root bridge's device object by acpi_bind_one(). However, this is *exactly* the ACPI handle we started with in step 1. Needless to say, this is quite embarassing, but it may be avoided thanks to commit f3fd0c8 (ACPI: Allow ACPI handles of devices to be initialized in advance), which makes it possible to initialize the ACPI handle of a device before passing it to device_register(). Accordingly, add a new __weak routine, pcibios_root_bridge_prepare(), defaulting to an empty implementation that can be replaced by the interested architecutres (x86 and ia64 at the moment) with functions that will set the root bridge's ACPI handle before its dev member is passed to device_register(). Make both x86 and ia64 provide such implementations of pcibios_root_bridge_prepare() and remove acpi_pci_find_root_bridge() and acpi_get_pci_rootbridge_handle() that aren't necessary any more. Included is a fix for breakage on systems with non-ACPI PCI host bridges from Bjorn Helgaas. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- arch/ia64/pci/pci.c | 8 ++++++++ arch/x86/include/asm/pci.h | 3 +++ arch/x86/pci/acpi.c | 9 +++++++++ drivers/acpi/pci_root.c | 18 ------------------ drivers/pci/pci-acpi.c | 19 ------------------- drivers/pci/probe.c | 16 ++++++++++++++++ include/acpi/acpi_bus.h | 1 - include/linux/pci.h | 2 ++ 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 5faa66c5c2a8..00e59c7ad3c0 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -396,6 +396,14 @@ out1: return NULL; } +int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + struct pci_controller *controller = bridge->bus->sysdata; + + ACPI_HANDLE_SET(&bridge->dev, controller->acpi_handle); + return 0; +} + static int __devinit is_valid_resource(struct pci_dev *dev, int idx) { unsigned int i, type_mask = IORESOURCE_IO | IORESOURCE_MEM; diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index dba7805176bf..9f437e97e9e8 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -14,6 +14,9 @@ struct pci_sysdata { int domain; /* PCI domain */ int node; /* NUMA node */ +#ifdef CONFIG_ACPI + void *acpi; /* ACPI-specific data */ +#endif #ifdef CONFIG_X86_64 void *iommu; /* IOMMU private data */ #endif diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 0c01261fe5a8..3d49094ed3e8 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -522,6 +522,7 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) sd = &info->sd; sd->domain = domain; sd->node = node; + sd->acpi = device->handle; /* * Maybe the desired pci bus has been already scanned. In such case * it is unnecessary to scan the pci bus with the given domain,busnum. @@ -593,6 +594,14 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) return bus; } +int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + struct pci_sysdata *sd = bridge->bus->sysdata; + + ACPI_HANDLE_SET(&bridge->dev, sd->acpi); + return 0; +} + int __init pci_acpi_init(void) { struct pci_dev *dev = NULL; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 471b2dcb1c67..bf5108ad4d63 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -107,24 +107,6 @@ void acpi_pci_unregister_driver(struct acpi_pci_driver *driver) } EXPORT_SYMBOL(acpi_pci_unregister_driver); -acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus) -{ - struct acpi_pci_root *root; - acpi_handle handle = NULL; - - mutex_lock(&acpi_pci_root_lock); - list_for_each_entry(root, &acpi_pci_roots, node) - if ((root->segment == (u16) seg) && - (root->secondary.start == (u16) bus)) { - handle = root->device->handle; - break; - } - mutex_unlock(&acpi_pci_root_lock); - return handle; -} - -EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle); - /** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge * @handle - the ACPI CA node in question. diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 42736e213f25..1c2587c40299 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -302,24 +302,6 @@ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle) return 0; } -static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle) -{ - int num; - unsigned int seg, bus; - - /* - * The string should be the same as root bridge's name - * Please look at 'pci_scan_bus_parented' - */ - num = sscanf(dev_name(dev), "pci%04x:%02x", &seg, &bus); - if (num != 2) - return -ENODEV; - *handle = acpi_get_pci_rootbridge_handle(seg, bus); - if (!*handle) - return -ENODEV; - return 0; -} - static void pci_acpi_setup(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -378,7 +360,6 @@ static void pci_acpi_cleanup(struct device *dev) static struct acpi_bus_type acpi_pci_bus = { .bus = &pci_bus_type, .find_device = acpi_pci_find_device, - .find_bridge = acpi_pci_find_root_bridge, .setup = pci_acpi_setup, .cleanup = pci_acpi_cleanup, }; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2dcd22d9c816..bbe4be7fc685 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1632,6 +1632,18 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus) return max; } +/** + * pcibios_root_bridge_prepare - Platform-specific host bridge setup. + * @bridge: Host bridge to set up. + * + * Default empty implementation. Replace with an architecture-specific setup + * routine, if necessary. + */ +int __weak pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + return 0; +} + struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { @@ -1665,6 +1677,10 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, bridge->dev.parent = parent; bridge->dev.release = pci_release_bus_bridge_dev; dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); + error = pcibios_root_bridge_prepare(bridge); + if (error) + goto bridge_dev_reg_err; + error = device_register(&bridge->dev); if (error) goto bridge_dev_reg_err; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a9e1421cd007..796ccc3247df 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -402,7 +402,6 @@ struct acpi_pci_root { /* helper */ acpi_handle acpi_get_child(acpi_handle, u64); int acpi_is_root_bridge(acpi_handle); -acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int); struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); #define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)ACPI_HANDLE(dev)) diff --git a/include/linux/pci.h b/include/linux/pci.h index 907b455ab603..6860f4dec997 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -378,6 +378,8 @@ void pci_set_host_bridge_release(struct pci_host_bridge *bridge, void (*release_fn)(struct pci_host_bridge *), void *release_data); +int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge); + /* * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond * to P2P or CardBus bridge windows) go in a table. Additional ones (for From 0074b49b3fa0886047413dbca0508594b1d80c61 Mon Sep 17 00:00:00 2001 From: "Philip, Avinash" Date: Thu, 10 Jan 2013 18:35:26 +0530 Subject: [PATCH 0839/4607] pwm: pwm-tiehrpwm: Update the clock handling of pwm-tiehrpwm driver The clock framework has changed and it's now better to invoke clock_prepare_enable() and clk_disable_unprepare() rather than the legacy clk_enable() and clk_disable() calls. This patch converts the pwm-tiehrpwm driver to the new framework. Signed-off-by: Philip Avinash Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiehrpwm.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 72a6dd40c9ec..4fcafbfba60e 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -318,6 +318,7 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); unsigned short aqcsfrc_val, aqcsfrc_mask; + int ret; /* Leave clock enabled on enabling PWM */ pm_runtime_get_sync(chip->dev); @@ -341,7 +342,12 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) configure_polarity(pc, pwm->hwpwm); /* Enable TBCLK before enabling PWM device */ - clk_enable(pc->tbclk); + ret = clk_prepare_enable(pc->tbclk); + if (ret) { + pr_err("Failed to enable TBCLK for %s\n", + dev_name(pc->chip.dev)); + return ret; + } /* Enable time counter for free_run */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); @@ -372,7 +378,7 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); /* Disabling TBCLK on PWM disable */ - clk_disable(pc->tbclk); + clk_disable_unprepare(pc->tbclk); /* Stop Time base counter */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT); From c972f3b125d8818748429b94cd2e59f473943a33 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Tue, 8 Jan 2013 19:43:28 +0900 Subject: [PATCH 0840/4607] KVM: Write protect the updated slot only when dirty logging is enabled Calling kvm_mmu_slot_remove_write_access() for a deleted slot does nothing but search for non-existent mmu pages which have mappings to that deleted memory; this is safe but a waste of time. Since we want to make the function rmap based in a later patch, in a manner which makes it unsafe to be called for a deleted slot, we makes the caller see if the slot is non-zero and being dirty logged. Reviewed-by: Marcelo Tosatti Signed-off-by: Takuya Yoshikawa Signed-off-by: Gleb Natapov --- arch/x86/kvm/x86.c | 8 +++++++- virt/kvm/kvm_main.c | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 1c9c834b72f0..add5e4801968 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6897,7 +6897,13 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, spin_lock(&kvm->mmu_lock); if (nr_mmu_pages) kvm_mmu_change_mmu_pages(kvm, nr_mmu_pages); - kvm_mmu_slot_remove_write_access(kvm, mem->slot); + /* + * Write protect all pages for dirty logging. + * Existing largepage mappings are destroyed here and new ones will + * not be created until the end of the logging. + */ + if (npages && (mem->flags & KVM_MEM_LOG_DIRTY_PAGES)) + kvm_mmu_slot_remove_write_access(kvm, mem->slot); spin_unlock(&kvm->mmu_lock); /* * If memory slot is created, or moved, we need to clear all diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index e45c20ca422a..f689a6d7f9d4 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -817,7 +817,6 @@ int __kvm_set_memory_region(struct kvm *kvm, if ((new.flags & KVM_MEM_LOG_DIRTY_PAGES) && !new.dirty_bitmap) { if (kvm_create_dirty_bitmap(&new) < 0) goto out_free; - /* destroy any largepage mappings for dirty tracking */ } if (!npages || base_gfn != old.base_gfn) { From 245c3912eae642a4b7a3ce0adfcde5cc7672d5fe Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Tue, 8 Jan 2013 19:44:09 +0900 Subject: [PATCH 0841/4607] KVM: MMU: Remove unused parameter level from __rmap_write_protect() No longer need to care about the mapping level in this function. Reviewed-by: Marcelo Tosatti Signed-off-by: Takuya Yoshikawa Signed-off-by: Gleb Natapov --- arch/x86/kvm/mmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 54fc61e4b061..2a1cde5a3938 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -1142,7 +1142,7 @@ spte_write_protect(struct kvm *kvm, u64 *sptep, bool *flush, bool pt_protect) } static bool __rmap_write_protect(struct kvm *kvm, unsigned long *rmapp, - int level, bool pt_protect) + bool pt_protect) { u64 *sptep; struct rmap_iterator iter; @@ -1180,7 +1180,7 @@ void kvm_mmu_write_protect_pt_masked(struct kvm *kvm, while (mask) { rmapp = __gfn_to_rmap(slot->base_gfn + gfn_offset + __ffs(mask), PT_PAGE_TABLE_LEVEL, slot); - __rmap_write_protect(kvm, rmapp, PT_PAGE_TABLE_LEVEL, false); + __rmap_write_protect(kvm, rmapp, false); /* clear the first set bit */ mask &= mask - 1; @@ -1199,7 +1199,7 @@ static bool rmap_write_protect(struct kvm *kvm, u64 gfn) for (i = PT_PAGE_TABLE_LEVEL; i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) { rmapp = __gfn_to_rmap(gfn, i, slot); - write_protected |= __rmap_write_protect(kvm, rmapp, i, true); + write_protected |= __rmap_write_protect(kvm, rmapp, true); } return write_protected; From b99db1d35295cb26b61a1c665f542504110b0ac3 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Tue, 8 Jan 2013 19:44:48 +0900 Subject: [PATCH 0842/4607] KVM: MMU: Make kvm_mmu_slot_remove_write_access() rmap based This makes it possible to release mmu_lock and reschedule conditionally in a later patch. Although this may increase the time needed to protect the whole slot when we start dirty logging, the kernel should not allow the userspace to trigger something that will hold a spinlock for such a long time as tens of milliseconds: actually there is no limit since it is roughly proportional to the number of guest pages. Another point to note is that this patch removes the only user of slot_bitmap which will cause some problems when we increase the number of slots further. Reviewed-by: Marcelo Tosatti Signed-off-by: Takuya Yoshikawa Signed-off-by: Gleb Natapov --- arch/x86/kvm/mmu.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 2a1cde5a3938..aeb7666eb81e 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -4172,25 +4172,27 @@ int kvm_mmu_setup(struct kvm_vcpu *vcpu) void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot) { - struct kvm_mmu_page *sp; - bool flush = false; + struct kvm_memory_slot *memslot; + gfn_t last_gfn; + int i; - list_for_each_entry(sp, &kvm->arch.active_mmu_pages, link) { - int i; - u64 *pt; + memslot = id_to_memslot(kvm->memslots, slot); + last_gfn = memslot->base_gfn + memslot->npages - 1; - if (!test_bit(slot, sp->slot_bitmap)) - continue; + for (i = PT_PAGE_TABLE_LEVEL; + i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) { + unsigned long *rmapp; + unsigned long last_index, index; - pt = sp->spt; - for (i = 0; i < PT64_ENT_PER_PAGE; ++i) { - if (!is_shadow_present_pte(pt[i]) || - !is_last_spte(pt[i], sp->role.level)) - continue; + rmapp = memslot->arch.rmap[i - PT_PAGE_TABLE_LEVEL]; + last_index = gfn_to_index(last_gfn, memslot->base_gfn, i); - spte_write_protect(kvm, &pt[i], &flush, false); + for (index = 0; index <= last_index; ++index, ++rmapp) { + if (*rmapp) + __rmap_write_protect(kvm, rmapp, false); } } + kvm_flush_remote_tlbs(kvm); } From e12091ce7bdd3c82fa392a868d1bdccecee655d5 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Tue, 8 Jan 2013 19:45:28 +0900 Subject: [PATCH 0843/4607] KVM: Remove unused slot_bitmap from kvm_mmu_page Not needed any more. Reviewed-by: Marcelo Tosatti Signed-off-by: Takuya Yoshikawa Signed-off-by: Gleb Natapov --- Documentation/virtual/kvm/mmu.txt | 7 ------- arch/x86/include/asm/kvm_host.h | 5 ----- arch/x86/kvm/mmu.c | 10 ---------- 3 files changed, 22 deletions(-) diff --git a/Documentation/virtual/kvm/mmu.txt b/Documentation/virtual/kvm/mmu.txt index fa5f1dbc6b23..43fcb761ed16 100644 --- a/Documentation/virtual/kvm/mmu.txt +++ b/Documentation/virtual/kvm/mmu.txt @@ -187,13 +187,6 @@ Shadow pages contain the following information: perform a reverse map from a pte to a gfn. When role.direct is set, any element of this array can be calculated from the gfn field when used, in this case, the array of gfns is not allocated. See role.direct and gfn. - slot_bitmap: - A bitmap containing one bit per memory slot. If the page contains a pte - mapping a page from memory slot n, then bit n of slot_bitmap will be set - (if a page is aliased among several slots, then it is not guaranteed that - all slots will be marked). - Used during dirty logging to avoid scanning a shadow page if none if its - pages need tracking. root_count: A counter keeping track of how many hardware registers (guest cr3 or pdptrs) are now pointing at the page. While this counter is nonzero, the diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index c431b33271f3..f75e1feb6ec5 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -219,11 +219,6 @@ struct kvm_mmu_page { u64 *spt; /* hold the gfn of each spte inside spt */ gfn_t *gfns; - /* - * One bit set per slot which has memory - * in this shadow page. - */ - DECLARE_BITMAP(slot_bitmap, KVM_MEM_SLOTS_NUM); bool unsync; int root_count; /* Currently serving as active root */ unsigned int unsync_children; diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index aeb7666eb81e..9c1b2d6158bf 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -1522,7 +1522,6 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, sp->gfns = mmu_memory_cache_alloc(&vcpu->arch.mmu_page_cache); set_page_private(virt_to_page(sp->spt), (unsigned long)sp); list_add(&sp->link, &vcpu->kvm->arch.active_mmu_pages); - bitmap_zero(sp->slot_bitmap, KVM_MEM_SLOTS_NUM); sp->parent_ptes = 0; mmu_page_add_parent_pte(vcpu, sp, parent_pte); kvm_mod_used_mmu_pages(vcpu->kvm, +1); @@ -2183,14 +2182,6 @@ int kvm_mmu_unprotect_page(struct kvm *kvm, gfn_t gfn) } EXPORT_SYMBOL_GPL(kvm_mmu_unprotect_page); -static void page_header_update_slot(struct kvm *kvm, void *pte, gfn_t gfn) -{ - int slot = memslot_id(kvm, gfn); - struct kvm_mmu_page *sp = page_header(__pa(pte)); - - __set_bit(slot, sp->slot_bitmap); -} - /* * The function is based on mtrr_type_lookup() in * arch/x86/kernel/cpu/mtrr/generic.c @@ -2472,7 +2463,6 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, ++vcpu->kvm->stat.lpages; if (is_shadow_present_pte(*sptep)) { - page_header_update_slot(vcpu->kvm, sptep, gfn); if (!was_rmapped) { rmap_count = rmap_add(vcpu, sptep, gfn); if (rmap_count > RMAP_RECYCLE_THRESHOLD) From b34cb590fb099f7929dd78d9464b70319ee12a98 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Tue, 8 Jan 2013 19:46:07 +0900 Subject: [PATCH 0844/4607] KVM: Make kvm_mmu_change_mmu_pages() take mmu_lock by itself No reason to make callers take mmu_lock since we do not need to protect kvm_mmu_change_mmu_pages() and kvm_mmu_slot_remove_write_access() together by mmu_lock in kvm_arch_commit_memory_region(): the former calls kvm_mmu_commit_zap_page() and flushes TLBs by itself. Note: we do not need to protect kvm->arch.n_requested_mmu_pages by mmu_lock as can be seen from the fact that it is read locklessly. Reviewed-by: Marcelo Tosatti Signed-off-by: Takuya Yoshikawa Signed-off-by: Gleb Natapov --- arch/x86/kvm/mmu.c | 4 ++++ arch/x86/kvm/x86.c | 9 ++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 9c1b2d6158bf..f5572804f594 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -2143,6 +2143,8 @@ void kvm_mmu_change_mmu_pages(struct kvm *kvm, unsigned int goal_nr_mmu_pages) * change the value */ + spin_lock(&kvm->mmu_lock); + if (kvm->arch.n_used_mmu_pages > goal_nr_mmu_pages) { while (kvm->arch.n_used_mmu_pages > goal_nr_mmu_pages && !list_empty(&kvm->arch.active_mmu_pages)) { @@ -2157,6 +2159,8 @@ void kvm_mmu_change_mmu_pages(struct kvm *kvm, unsigned int goal_nr_mmu_pages) } kvm->arch.n_max_mmu_pages = goal_nr_mmu_pages; + + spin_unlock(&kvm->mmu_lock); } int kvm_mmu_unprotect_page(struct kvm *kvm, gfn_t gfn) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index add5e4801968..080bbdcbf2ee 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3270,12 +3270,10 @@ static int kvm_vm_ioctl_set_nr_mmu_pages(struct kvm *kvm, return -EINVAL; mutex_lock(&kvm->slots_lock); - spin_lock(&kvm->mmu_lock); kvm_mmu_change_mmu_pages(kvm, kvm_nr_mmu_pages); kvm->arch.n_requested_mmu_pages = kvm_nr_mmu_pages; - spin_unlock(&kvm->mmu_lock); mutex_unlock(&kvm->slots_lock); return 0; } @@ -6894,7 +6892,6 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, if (!kvm->arch.n_requested_mmu_pages) nr_mmu_pages = kvm_mmu_calculate_mmu_pages(kvm); - spin_lock(&kvm->mmu_lock); if (nr_mmu_pages) kvm_mmu_change_mmu_pages(kvm, nr_mmu_pages); /* @@ -6902,9 +6899,11 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, * Existing largepage mappings are destroyed here and new ones will * not be created until the end of the logging. */ - if (npages && (mem->flags & KVM_MEM_LOG_DIRTY_PAGES)) + if (npages && (mem->flags & KVM_MEM_LOG_DIRTY_PAGES)) { + spin_lock(&kvm->mmu_lock); kvm_mmu_slot_remove_write_access(kvm, mem->slot); - spin_unlock(&kvm->mmu_lock); + spin_unlock(&kvm->mmu_lock); + } /* * If memory slot is created, or moved, we need to clear all * mmio sptes. From 9d1beefb71146bbf5f820ab17c450808b0d0b2df Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Tue, 8 Jan 2013 19:46:48 +0900 Subject: [PATCH 0845/4607] KVM: Make kvm_mmu_slot_remove_write_access() take mmu_lock by itself Better to place mmu_lock handling and TLB flushing code together since this is a self-contained function. Reviewed-by: Marcelo Tosatti Signed-off-by: Takuya Yoshikawa Signed-off-by: Gleb Natapov --- arch/x86/kvm/mmu.c | 3 +++ arch/x86/kvm/x86.c | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index f5572804f594..e5dcae31cebc 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -4173,6 +4173,8 @@ void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot) memslot = id_to_memslot(kvm->memslots, slot); last_gfn = memslot->base_gfn + memslot->npages - 1; + spin_lock(&kvm->mmu_lock); + for (i = PT_PAGE_TABLE_LEVEL; i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) { unsigned long *rmapp; @@ -4188,6 +4190,7 @@ void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot) } kvm_flush_remote_tlbs(kvm); + spin_unlock(&kvm->mmu_lock); } void kvm_mmu_zap_all(struct kvm *kvm) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 080bbdcbf2ee..54832280cdef 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6899,11 +6899,8 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, * Existing largepage mappings are destroyed here and new ones will * not be created until the end of the logging. */ - if (npages && (mem->flags & KVM_MEM_LOG_DIRTY_PAGES)) { - spin_lock(&kvm->mmu_lock); + if (npages && (mem->flags & KVM_MEM_LOG_DIRTY_PAGES)) kvm_mmu_slot_remove_write_access(kvm, mem->slot); - spin_unlock(&kvm->mmu_lock); - } /* * If memory slot is created, or moved, we need to clear all * mmio sptes. From 6b81b05e449e15abb60eaa4f62cdc7954f4d74f0 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Tue, 8 Jan 2013 19:47:33 +0900 Subject: [PATCH 0846/4607] KVM: MMU: Conditionally reschedule when kvm_mmu_slot_remove_write_access() takes a long time If the userspace starts dirty logging for a large slot, say 64GB of memory, kvm_mmu_slot_remove_write_access() needs to hold mmu_lock for a long time such as tens of milliseconds. This patch controls the lock hold time by asking the scheduler if we need to reschedule for others. One penalty for this is that we need to flush TLBs before releasing mmu_lock. But since holding mmu_lock for a long time does affect not only the guest, vCPU threads in other words, but also the host as a whole, we should pay for that. In practice, the cost will not be so high because we can protect a fair amount of memory before being rescheduled: on my test environment, cond_resched_lock() was called only once for protecting 12GB of memory even without THP. We can also revisit Avi's "unlocked TLB flush" work later for completely suppressing extra TLB flushes if needed. Reviewed-by: Marcelo Tosatti Signed-off-by: Takuya Yoshikawa Signed-off-by: Gleb Natapov --- arch/x86/kvm/mmu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index e5dcae31cebc..9f628f7a40b2 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -4186,6 +4186,11 @@ void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot) for (index = 0; index <= last_index; ++index, ++rmapp) { if (*rmapp) __rmap_write_protect(kvm, rmapp, false); + + if (need_resched() || spin_needbreak(&kvm->mmu_lock)) { + kvm_flush_remote_tlbs(kvm); + cond_resched_lock(&kvm->mmu_lock); + } } } From 0fd602235dd702d16722857da748d15c26b81ed1 Mon Sep 17 00:00:00 2001 From: Narayanan Date: Tue, 13 Sep 2011 17:00:22 +0530 Subject: [PATCH 0847/4607] dmaengine: ste_dma40: reset priority bit for logical channels This patch sets the SSCFG/SDCFG bit[7] PRI only for physical channel requests with high priority. For logical channels, this bit will be zero. Signed-off-by: Narayanan G Reviewed-by: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40_ll.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index 851ad56e8409..d64b72ae2dc8 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -102,17 +102,18 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg, src |= cfg->src_info.data_width << D40_SREG_CFG_ESIZE_POS; dst |= cfg->dst_info.data_width << D40_SREG_CFG_ESIZE_POS; + /* Set the priority bit to high for the physical channel */ + if (cfg->high_priority) { + src |= 1 << D40_SREG_CFG_PRI_POS; + dst |= 1 << D40_SREG_CFG_PRI_POS; + } + } else { /* Logical channel */ dst |= 1 << D40_SREG_CFG_LOG_GIM_POS; src |= 1 << D40_SREG_CFG_LOG_GIM_POS; } - if (cfg->high_priority) { - src |= 1 << D40_SREG_CFG_PRI_POS; - dst |= 1 << D40_SREG_CFG_PRI_POS; - } - if (cfg->src_info.big_endian) src |= 1 << D40_SREG_CFG_LBE_POS; if (cfg->dst_info.big_endian) From 8a5d2039ab9050a8a2e649eaf3ca4e372a7709f1 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Wed, 28 Sep 2011 09:32:20 +0200 Subject: [PATCH 0848/4607] dmaengine: ste_dma40: use writel_relaxed for lcxa lcpa and lcla are written often and the cache_sync() overhead in writel is costly, especially for wlan where every single network packet (in RX mode) corresponds to a separate DMA transfer. Signed-off-by: Per Forlin Reviewed-by: Narayanan Gopalakrishnan Reviewed-by: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40_ll.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index d64b72ae2dc8..1cfe7ab50c6b 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -332,10 +332,10 @@ void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa, { d40_log_lli_link(lli_dst, lli_src, next, flags); - writel(lli_src->lcsp02, &lcpa[0].lcsp0); - writel(lli_src->lcsp13, &lcpa[0].lcsp1); - writel(lli_dst->lcsp02, &lcpa[0].lcsp2); - writel(lli_dst->lcsp13, &lcpa[0].lcsp3); + writel_relaxed(lli_src->lcsp02, &lcpa[0].lcsp0); + writel_relaxed(lli_src->lcsp13, &lcpa[0].lcsp1); + writel_relaxed(lli_dst->lcsp02, &lcpa[0].lcsp2); + writel_relaxed(lli_dst->lcsp13, &lcpa[0].lcsp3); } void d40_log_lli_lcla_write(struct d40_log_lli *lcla, @@ -345,10 +345,10 @@ void d40_log_lli_lcla_write(struct d40_log_lli *lcla, { d40_log_lli_link(lli_dst, lli_src, next, flags); - writel(lli_src->lcsp02, &lcla[0].lcsp02); - writel(lli_src->lcsp13, &lcla[0].lcsp13); - writel(lli_dst->lcsp02, &lcla[1].lcsp02); - writel(lli_dst->lcsp13, &lcla[1].lcsp13); + writel_relaxed(lli_src->lcsp02, &lcla[0].lcsp02); + writel_relaxed(lli_src->lcsp13, &lcla[0].lcsp13); + writel_relaxed(lli_dst->lcsp02, &lcla[1].lcsp02); + writel_relaxed(lli_dst->lcsp13, &lcla[1].lcsp13); } static void d40_log_fill_lli(struct d40_log_lli *lli, From b96710e5b22609aa6e4ba5c3936ea7f026a7c427 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Tue, 18 Oct 2011 18:39:47 +0200 Subject: [PATCH 0849/4607] dmaengine: ste_dma40: set dma max seg size Maximum DMA seg size is (0xffff x data_width). If max seg size is not set it deafults to 64k. This results in failure if transferring 64k in byte mode. Large seg sizes may be supported by splitting large transfer. Signed-off-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 23c5573e62dd..f5724d95ed48 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -344,6 +344,7 @@ struct d40_base { int irq; int num_phy_chans; int num_log_chans; + struct device_dma_parameters dma_parms; struct dma_device dma_both; struct dma_device dma_slave; struct dma_device dma_memcpy; @@ -3362,6 +3363,13 @@ static int __init d40_probe(struct platform_device *pdev) if (err) goto failure; + base->dev->dma_parms = &base->dma_parms; + err = dma_set_max_seg_size(base->dev, STEDMA40_MAX_SEG_SIZE); + if (err) { + d40_err(&pdev->dev, "Failed to set dma max seg size\n"); + goto failure; + } + d40_hw_init(base); dev_info(base->dev, "initialized\n"); From 92bb6cdb5302a4b0b3c6b6cfc0854aaed882c4bc Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Thu, 13 Oct 2011 12:11:36 +0200 Subject: [PATCH 0850/4607] dmaengine: ste_dma40: limit burst size to 16 The client is not aware of the maximum burst size in the dma driver. If the size exceeds 16 set max to 16. Signed-off-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index f5724d95ed48..2d0c63dcd84c 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2578,6 +2578,14 @@ static int d40_set_runtime_config(struct dma_chan *chan, return -EINVAL; } + if (src_maxburst > 16) { + src_maxburst = 16; + dst_maxburst = src_maxburst * src_addr_width / dst_addr_width; + } else if (dst_maxburst > 16) { + dst_maxburst = 16; + src_maxburst = dst_maxburst * dst_addr_width / src_addr_width; + } + ret = dma40_config_to_halfchannel(d40c, &cfg->src_info, src_addr_width, src_maxburst); From 42365cf0fa19473dde5fe226b0e7e9ab8ea18af8 Mon Sep 17 00:00:00 2001 From: Narayanan G Date: Fri, 20 Jan 2012 13:56:14 +0530 Subject: [PATCH 0851/4607] dmaengine: ste_dma40: don't check for pm_runtime_suspended() The check for runtime suspend is not needed during a regular suspend, as the framework takes care of this. This fixes the issue of DMA driver not letting the system to go to deepsleep in the first attempt. Signed-off-by: Narayanan G Reviewed-by: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 2d0c63dcd84c..760576b85641 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2782,8 +2782,6 @@ static int dma40_pm_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct d40_base *base = platform_get_drvdata(pdev); int ret = 0; - if (!pm_runtime_suspended(dev)) - return -EBUSY; if (base->lcpa_regulator) ret = regulator_disable(base->lcpa_regulator); From ccc3d6976433aa67131117fccd2b5143d82a6f48 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Thu, 17 May 2012 13:47:38 +0530 Subject: [PATCH 0852/4607] dmaengine: ste_dma40: don't allow high priority dest event lines Hardware bug: when a logical channel is triggerred by a high priority destination event line, an extra packet transaction is generated in case of important data write response latency on previous logical channel A and if the source transfer of current logical channel B is already completed and if no other channel with a higher priority than B is waiting for execution. Software workaround: do not set the high priority level for the destination event lines that trigger logical channels. Signed-off-by: Rabin Vincent Reviewed-by: Shreshtha Kumar Sahu Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 760576b85641..9f8964a0a287 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -2180,11 +2180,24 @@ static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src) { bool realtime = d40c->dma_cfg.realtime; bool highprio = d40c->dma_cfg.high_priority; - u32 prioreg = highprio ? D40_DREG_PSEG1 : D40_DREG_PCEG1; u32 rtreg = realtime ? D40_DREG_RSEG1 : D40_DREG_RCEG1; u32 event = D40_TYPE_TO_EVENT(dev_type); u32 group = D40_TYPE_TO_GROUP(dev_type); u32 bit = 1 << event; + u32 prioreg; + + /* + * Due to a hardware bug, in some cases a logical channel triggered by + * a high priority destination event line can generate extra packet + * transactions. + * + * The workaround is to not set the high priority level for the + * destination event lines that trigger logical channels. + */ + if (!src && chan_is_logical(d40c)) + highprio = false; + + prioreg = highprio ? D40_DREG_PSEG1 : D40_DREG_PCEG1; /* Destination event lines are stored in the upper halfword */ if (!src) From f000df8c5a0e2002acc5989aad99a97d32a24718 Mon Sep 17 00:00:00 2001 From: Gerald Baeza Date: Thu, 8 Nov 2012 14:39:07 +0100 Subject: [PATCH 0853/4607] dmaengine: ste_dma40: support fixed physical channel allocation This patch makes existing use_fixed_channel field (of stedma40_chan_cfg structure) applicable to physical channels. Signed-off-by: Gerald Baeza Tested-by: Yannick Fertre Reviewed-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 9f8964a0a287..5feab7db9449 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1711,10 +1711,12 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user) int i; int j; int log_num; + int num_phy_chans; bool is_src; bool is_log = d40c->dma_cfg.mode == STEDMA40_MODE_LOGICAL; phys = d40c->base->phy_res; + num_phy_chans = d40c->base->num_phy_chans; if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) { dev_type = d40c->dma_cfg.src_dev_type; @@ -1735,12 +1737,19 @@ static int d40_allocate_channel(struct d40_chan *d40c, bool *first_phy_user) if (!is_log) { if (d40c->dma_cfg.dir == STEDMA40_MEM_TO_MEM) { /* Find physical half channel */ - for (i = 0; i < d40c->base->num_phy_chans; i++) { - + if (d40c->dma_cfg.use_fixed_channel) { + i = d40c->dma_cfg.phy_channel; if (d40_alloc_mask_set(&phys[i], is_src, 0, is_log, first_phy_user)) goto found_phy; + } else { + for (i = 0; i < num_phy_chans; i++) { + if (d40_alloc_mask_set(&phys[i], is_src, + 0, is_log, + first_phy_user)) + goto found_phy; + } } } else for (j = 0; j < d40c->base->num_phy_chans; j += 8) { From 47db92f4a63499b1605b4c66f9347ba5479e7b19 Mon Sep 17 00:00:00 2001 From: Gerald Baeza Date: Fri, 21 Sep 2012 21:21:37 +0200 Subject: [PATCH 0854/4607] dmaengine: ste_dma40: physical channels number correction DMAC_ICFG[0:2]=SCHNB only allows to count 'multiple of 4' physical channels so it was ok with platforms having 8 channels but cannot be used for next versions (with 10 or 14 channels). This patch allows to provide the number of physical channels for a DMA device via platform_data, or still rely on SCHNB if platform_data announces 0 channel. Signed-off-by: Gerald Baeza Reviewed-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 17 +++++++++++------ include/linux/platform_data/dma-ste-dma40.h | 4 ++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 5feab7db9449..ca18117def0a 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -3004,14 +3004,21 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) * ? has revision 1 * DB8500v1 has revision 2 * DB8500v2 has revision 3 + * AP9540v1 has revision 4 + * DB8540v1 has revision 4 */ rev = AMBA_REV_BITS(pid); - /* The number of physical channels on this HW */ - num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4; + plat_data = pdev->dev.platform_data; - dev_info(&pdev->dev, "hardware revision: %d @ 0x%x\n", - rev, res->start); + /* The number of physical channels on this HW */ + if (plat_data->num_of_phy_chans) + num_phy_chans = plat_data->num_of_phy_chans; + else + num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4; + + dev_info(&pdev->dev, "hardware revision: %d @ 0x%x with %d physical channels\n", + rev, res->start, num_phy_chans); if (rev < 2) { d40_err(&pdev->dev, "hardware revision: %d is not supported", @@ -3019,8 +3026,6 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) goto failure; } - plat_data = pdev->dev.platform_data; - /* Count the number of logical channels in use */ for (i = 0; i < plat_data->dev_len; i++) if (plat_data->dev_rx[i] != 0) diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index 9ff93b065686..833cb959f3df 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -147,6 +147,9 @@ struct stedma40_chan_cfg { * @memcpy_conf_log: default configuration of logical channel memcpy * @disabled_channels: A vector, ending with -1, that marks physical channels * that are for different reasons not available for the driver. + * @num_of_phy_chans: The number of physical channels implemented in HW. + * 0 means reading the number of channels from DMA HW but this is only valid + * for 'multiple of 4' channels, like 8. */ struct stedma40_platform_data { u32 dev_len; @@ -158,6 +161,7 @@ struct stedma40_platform_data { struct stedma40_chan_cfg *memcpy_conf_log; int disabled_channels[STEDMA40_MAX_PHYS]; bool use_esram_lcla; + int num_of_phy_chans; }; #ifdef CONFIG_STE_DMA40 From 3cb645dc85a050c8a6b5c2cbdcbe4b8f39dba1b8 Mon Sep 17 00:00:00 2001 From: Tong Liu Date: Wed, 26 Sep 2012 10:07:30 +0000 Subject: [PATCH 0855/4607] dmaengine: ste_dma40: support more than 128 event lines U8540 DMA controller is different from u9540 we need define new registers and use them to support handling more than 128 event lines. Signed-off-by: Tong Liu Reviewed-by: Per Forlin Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 306 +++++++++++++++++++++++++++---------- drivers/dma/ste_dma40_ll.h | 130 +++++++++++++++- 2 files changed, 355 insertions(+), 81 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index ca18117def0a..67e565bffe16 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -53,6 +53,8 @@ #define D40_ALLOC_PHY (1 << 30) #define D40_ALLOC_LOG_FREE 0 +#define MAX(a, b) (((a) < (b)) ? (b) : (a)) + /** * enum 40_command - The different commands and/or statuses. * @@ -100,8 +102,19 @@ static u32 d40_backup_regs[] = { #define BACKUP_REGS_SZ ARRAY_SIZE(d40_backup_regs) -/* TODO: Check if all these registers have to be saved/restored on dma40 v3 */ -static u32 d40_backup_regs_v3[] = { +/* + * since 9540 and 8540 has the same HW revision + * use v4a for 9540 or ealier + * use v4b for 8540 or later + * HW revision: + * DB8500ed has revision 0 + * DB8500v1 has revision 2 + * DB8500v2 has revision 3 + * AP9540v1 has revision 4 + * DB8540v1 has revision 4 + * TODO: Check if all these registers have to be saved/restored on dma40 v4a + */ +static u32 d40_backup_regs_v4a[] = { D40_DREG_PSEG1, D40_DREG_PSEG2, D40_DREG_PSEG3, @@ -120,7 +133,32 @@ static u32 d40_backup_regs_v3[] = { D40_DREG_RCEG4, }; -#define BACKUP_REGS_SZ_V3 ARRAY_SIZE(d40_backup_regs_v3) +#define BACKUP_REGS_SZ_V4A ARRAY_SIZE(d40_backup_regs_v4a) + +static u32 d40_backup_regs_v4b[] = { + D40_DREG_CPSEG1, + D40_DREG_CPSEG2, + D40_DREG_CPSEG3, + D40_DREG_CPSEG4, + D40_DREG_CPSEG5, + D40_DREG_CPCEG1, + D40_DREG_CPCEG2, + D40_DREG_CPCEG3, + D40_DREG_CPCEG4, + D40_DREG_CPCEG5, + D40_DREG_CRSEG1, + D40_DREG_CRSEG2, + D40_DREG_CRSEG3, + D40_DREG_CRSEG4, + D40_DREG_CRSEG5, + D40_DREG_CRCEG1, + D40_DREG_CRCEG2, + D40_DREG_CRCEG3, + D40_DREG_CRCEG4, + D40_DREG_CRCEG5, +}; + +#define BACKUP_REGS_SZ_V4B ARRAY_SIZE(d40_backup_regs_v4b) static u32 d40_backup_regs_chan[] = { D40_CHAN_REG_SSCFG, @@ -133,6 +171,102 @@ static u32 d40_backup_regs_chan[] = { D40_CHAN_REG_SDLNK, }; +/** + * struct d40_interrupt_lookup - lookup table for interrupt handler + * + * @src: Interrupt mask register. + * @clr: Interrupt clear register. + * @is_error: true if this is an error interrupt. + * @offset: start delta in the lookup_log_chans in d40_base. If equals to + * D40_PHY_CHAN, the lookup_phy_chans shall be used instead. + */ +struct d40_interrupt_lookup { + u32 src; + u32 clr; + bool is_error; + int offset; +}; + + +static struct d40_interrupt_lookup il_v4a[] = { + {D40_DREG_LCTIS0, D40_DREG_LCICR0, false, 0}, + {D40_DREG_LCTIS1, D40_DREG_LCICR1, false, 32}, + {D40_DREG_LCTIS2, D40_DREG_LCICR2, false, 64}, + {D40_DREG_LCTIS3, D40_DREG_LCICR3, false, 96}, + {D40_DREG_LCEIS0, D40_DREG_LCICR0, true, 0}, + {D40_DREG_LCEIS1, D40_DREG_LCICR1, true, 32}, + {D40_DREG_LCEIS2, D40_DREG_LCICR2, true, 64}, + {D40_DREG_LCEIS3, D40_DREG_LCICR3, true, 96}, + {D40_DREG_PCTIS, D40_DREG_PCICR, false, D40_PHY_CHAN}, + {D40_DREG_PCEIS, D40_DREG_PCICR, true, D40_PHY_CHAN}, +}; + +static struct d40_interrupt_lookup il_v4b[] = { + {D40_DREG_CLCTIS1, D40_DREG_CLCICR1, false, 0}, + {D40_DREG_CLCTIS2, D40_DREG_CLCICR2, false, 32}, + {D40_DREG_CLCTIS3, D40_DREG_CLCICR3, false, 64}, + {D40_DREG_CLCTIS4, D40_DREG_CLCICR4, false, 96}, + {D40_DREG_CLCTIS5, D40_DREG_CLCICR5, false, 128}, + {D40_DREG_CLCEIS1, D40_DREG_CLCICR1, true, 0}, + {D40_DREG_CLCEIS2, D40_DREG_CLCICR2, true, 32}, + {D40_DREG_CLCEIS3, D40_DREG_CLCICR3, true, 64}, + {D40_DREG_CLCEIS4, D40_DREG_CLCICR4, true, 96}, + {D40_DREG_CLCEIS5, D40_DREG_CLCICR5, true, 128}, + {D40_DREG_CPCTIS, D40_DREG_CPCICR, false, D40_PHY_CHAN}, + {D40_DREG_CPCEIS, D40_DREG_CPCICR, true, D40_PHY_CHAN}, +}; + +/** + * struct d40_reg_val - simple lookup struct + * + * @reg: The register. + * @val: The value that belongs to the register in reg. + */ +struct d40_reg_val { + unsigned int reg; + unsigned int val; +}; + +static __initdata struct d40_reg_val dma_init_reg_v4a[] = { + /* Clock every part of the DMA block from start */ + { .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL}, + + /* Interrupts on all logical channels */ + { .reg = D40_DREG_LCMIS0, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCMIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCMIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCMIS3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR0, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCICR3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS0, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_LCTIS3, .val = 0xFFFFFFFF} +}; +static __initdata struct d40_reg_val dma_init_reg_v4b[] = { + /* Clock every part of the DMA block from start */ + { .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL}, + + /* Interrupts on all logical channels */ + { .reg = D40_DREG_CLCMIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS4, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCMIS5, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR4, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCICR5, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS1, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS2, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS3, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS4, .val = 0xFFFFFFFF}, + { .reg = D40_DREG_CLCTIS5, .val = 0xFFFFFFFF} +}; + /** * struct d40_lli_pool - Structure for keeping LLIs in memory * @@ -288,6 +422,38 @@ struct d40_chan { enum dma_transfer_direction runtime_direction; }; +/** + * struct d40_gen_dmac - generic values to represent u8500/u8540 DMA + * controller + * + * @backup: the pointer to the registers address array for backup + * @backup_size: the size of the registers address array for backup + * @realtime_en: the realtime enable register + * @realtime_clear: the realtime clear register + * @high_prio_en: the high priority enable register + * @high_prio_clear: the high priority clear register + * @interrupt_en: the interrupt enable register + * @interrupt_clear: the interrupt clear register + * @il: the pointer to struct d40_interrupt_lookup + * @il_size: the size of d40_interrupt_lookup array + * @init_reg: the pointer to the struct d40_reg_val + * @init_reg_size: the size of d40_reg_val array + */ +struct d40_gen_dmac { + u32 *backup; + u32 backup_size; + u32 realtime_en; + u32 realtime_clear; + u32 high_prio_en; + u32 high_prio_clear; + u32 interrupt_en; + u32 interrupt_clear; + struct d40_interrupt_lookup *il; + u32 il_size; + struct d40_reg_val *init_reg; + u32 init_reg_size; +}; + /** * struct d40_base - The big global struct, one for each probe'd instance. * @@ -326,11 +492,13 @@ struct d40_chan { * @desc_slab: cache for descriptors. * @reg_val_backup: Here the values of some hardware registers are stored * before the DMA is powered off. They are restored when the power is back on. - * @reg_val_backup_v3: Backup of registers that only exits on dma40 v3 and - * later. + * @reg_val_backup_v4: Backup of registers that only exits on dma40 v3 and + * later * @reg_val_backup_chan: Backup data for standard channel parameter registers. * @gcc_pwr_off_mask: Mask to maintain the channels that can be turned off. * @initialized: true if the dma has been initialized + * @gen_dmac: the struct for generic registers values to represent u8500/8540 + * DMA controller */ struct d40_base { spinlock_t interrupt_lock; @@ -362,37 +530,11 @@ struct d40_base { resource_size_t lcpa_size; struct kmem_cache *desc_slab; u32 reg_val_backup[BACKUP_REGS_SZ]; - u32 reg_val_backup_v3[BACKUP_REGS_SZ_V3]; + u32 reg_val_backup_v4[MAX(BACKUP_REGS_SZ_V4A, BACKUP_REGS_SZ_V4B)]; u32 *reg_val_backup_chan; u16 gcc_pwr_off_mask; bool initialized; -}; - -/** - * struct d40_interrupt_lookup - lookup table for interrupt handler - * - * @src: Interrupt mask register. - * @clr: Interrupt clear register. - * @is_error: true if this is an error interrupt. - * @offset: start delta in the lookup_log_chans in d40_base. If equals to - * D40_PHY_CHAN, the lookup_phy_chans shall be used instead. - */ -struct d40_interrupt_lookup { - u32 src; - u32 clr; - bool is_error; - int offset; -}; - -/** - * struct d40_reg_val - simple lookup struct - * - * @reg: The register. - * @val: The value that belongs to the register in reg. - */ -struct d40_reg_val { - unsigned int reg; - unsigned int val; + struct d40_gen_dmac gen_dmac; }; static struct device *chan2dev(struct d40_chan *d40c) @@ -875,11 +1017,11 @@ static void d40_save_restore_registers(struct d40_base *base, bool save) save); /* Save/Restore registers only existing on dma40 v3 and later */ - if (base->rev >= 3) - dma40_backup(base->virtbase, base->reg_val_backup_v3, - d40_backup_regs_v3, - ARRAY_SIZE(d40_backup_regs_v3), - save); + if (base->gen_dmac.backup) + dma40_backup(base->virtbase, base->reg_val_backup_v4, + base->gen_dmac.backup, + base->gen_dmac.backup_size, + save); } #else static void d40_save_restore_registers(struct d40_base *base, bool save) @@ -1470,41 +1612,30 @@ err: static irqreturn_t d40_handle_interrupt(int irq, void *data) { - static const struct d40_interrupt_lookup il[] = { - {D40_DREG_LCTIS0, D40_DREG_LCICR0, false, 0}, - {D40_DREG_LCTIS1, D40_DREG_LCICR1, false, 32}, - {D40_DREG_LCTIS2, D40_DREG_LCICR2, false, 64}, - {D40_DREG_LCTIS3, D40_DREG_LCICR3, false, 96}, - {D40_DREG_LCEIS0, D40_DREG_LCICR0, true, 0}, - {D40_DREG_LCEIS1, D40_DREG_LCICR1, true, 32}, - {D40_DREG_LCEIS2, D40_DREG_LCICR2, true, 64}, - {D40_DREG_LCEIS3, D40_DREG_LCICR3, true, 96}, - {D40_DREG_PCTIS, D40_DREG_PCICR, false, D40_PHY_CHAN}, - {D40_DREG_PCEIS, D40_DREG_PCICR, true, D40_PHY_CHAN}, - }; - int i; - u32 regs[ARRAY_SIZE(il)]; u32 idx; u32 row; long chan = -1; struct d40_chan *d40c; unsigned long flags; struct d40_base *base = data; + u32 regs[base->gen_dmac.il_size]; + struct d40_interrupt_lookup *il = base->gen_dmac.il; + u32 il_size = base->gen_dmac.il_size; spin_lock_irqsave(&base->interrupt_lock, flags); /* Read interrupt status of both logical and physical channels */ - for (i = 0; i < ARRAY_SIZE(il); i++) + for (i = 0; i < il_size; i++) regs[i] = readl(base->virtbase + il[i].src); for (;;) { chan = find_next_bit((unsigned long *)regs, - BITS_PER_LONG * ARRAY_SIZE(il), chan + 1); + BITS_PER_LONG * il_size, chan + 1); /* No more set bits found? */ - if (chan == BITS_PER_LONG * ARRAY_SIZE(il)) + if (chan == BITS_PER_LONG * il_size) break; row = chan / BITS_PER_LONG; @@ -2189,12 +2320,14 @@ static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src) { bool realtime = d40c->dma_cfg.realtime; bool highprio = d40c->dma_cfg.high_priority; - u32 rtreg = realtime ? D40_DREG_RSEG1 : D40_DREG_RCEG1; + u32 rtreg; u32 event = D40_TYPE_TO_EVENT(dev_type); u32 group = D40_TYPE_TO_GROUP(dev_type); u32 bit = 1 << event; u32 prioreg; + struct d40_gen_dmac *dmac = &d40c->base->gen_dmac; + rtreg = realtime ? dmac->realtime_en : dmac->realtime_clear; /* * Due to a hardware bug, in some cases a logical channel triggered by * a high priority destination event line can generate extra packet @@ -2206,7 +2339,7 @@ static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src) if (!src && chan_is_logical(d40c)) highprio = false; - prioreg = highprio ? D40_DREG_PSEG1 : D40_DREG_PCEG1; + prioreg = highprio ? dmac->high_prio_en : dmac->high_prio_clear; /* Destination event lines are stored in the upper halfword */ if (!src) @@ -3056,6 +3189,36 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) base->phy_chans = ((void *)base) + ALIGN(sizeof(struct d40_base), 4); base->log_chans = &base->phy_chans[num_phy_chans]; + if (base->plat_data->num_of_phy_chans == 14) { + base->gen_dmac.backup = d40_backup_regs_v4b; + base->gen_dmac.backup_size = BACKUP_REGS_SZ_V4B; + base->gen_dmac.interrupt_en = D40_DREG_CPCMIS; + base->gen_dmac.interrupt_clear = D40_DREG_CPCICR; + base->gen_dmac.realtime_en = D40_DREG_CRSEG1; + base->gen_dmac.realtime_clear = D40_DREG_CRCEG1; + base->gen_dmac.high_prio_en = D40_DREG_CPSEG1; + base->gen_dmac.high_prio_clear = D40_DREG_CPCEG1; + base->gen_dmac.il = il_v4b; + base->gen_dmac.il_size = ARRAY_SIZE(il_v4b); + base->gen_dmac.init_reg = dma_init_reg_v4b; + base->gen_dmac.init_reg_size = ARRAY_SIZE(dma_init_reg_v4b); + } else { + if (base->rev >= 3) { + base->gen_dmac.backup = d40_backup_regs_v4a; + base->gen_dmac.backup_size = BACKUP_REGS_SZ_V4A; + } + base->gen_dmac.interrupt_en = D40_DREG_PCMIS; + base->gen_dmac.interrupt_clear = D40_DREG_PCICR; + base->gen_dmac.realtime_en = D40_DREG_RSEG1; + base->gen_dmac.realtime_clear = D40_DREG_RCEG1; + base->gen_dmac.high_prio_en = D40_DREG_PSEG1; + base->gen_dmac.high_prio_clear = D40_DREG_PCEG1; + base->gen_dmac.il = il_v4a; + base->gen_dmac.il_size = ARRAY_SIZE(il_v4a); + base->gen_dmac.init_reg = dma_init_reg_v4a; + base->gen_dmac.init_reg_size = ARRAY_SIZE(dma_init_reg_v4a); + } + base->phy_res = kzalloc(num_phy_chans * sizeof(struct d40_phy_res), GFP_KERNEL); if (!base->phy_res) @@ -3127,31 +3290,15 @@ failure: static void __init d40_hw_init(struct d40_base *base) { - static struct d40_reg_val dma_init_reg[] = { - /* Clock every part of the DMA block from start */ - { .reg = D40_DREG_GCC, .val = D40_DREG_GCC_ENABLE_ALL}, - - /* Interrupts on all logical channels */ - { .reg = D40_DREG_LCMIS0, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCMIS1, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCMIS2, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCMIS3, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR0, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR1, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR2, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCICR3, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS0, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS1, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS2, .val = 0xFFFFFFFF}, - { .reg = D40_DREG_LCTIS3, .val = 0xFFFFFFFF} - }; int i; u32 prmseo[2] = {0, 0}; u32 activeo[2] = {0xFFFFFFFF, 0xFFFFFFFF}; u32 pcmis = 0; u32 pcicr = 0; + struct d40_reg_val *dma_init_reg = base->gen_dmac.init_reg; + u32 reg_size = base->gen_dmac.init_reg_size; - for (i = 0; i < ARRAY_SIZE(dma_init_reg); i++) + for (i = 0; i < reg_size; i++) writel(dma_init_reg[i].val, base->virtbase + dma_init_reg[i].reg); @@ -3184,11 +3331,14 @@ static void __init d40_hw_init(struct d40_base *base) writel(activeo[0], base->virtbase + D40_DREG_ACTIVO); /* Write which interrupt to enable */ - writel(pcmis, base->virtbase + D40_DREG_PCMIS); + writel(pcmis, base->virtbase + base->gen_dmac.interrupt_en); /* Write which interrupt to clear */ - writel(pcicr, base->virtbase + D40_DREG_PCICR); + writel(pcicr, base->virtbase + base->gen_dmac.interrupt_clear); + /* These are __initdata and cannot be accessed after init */ + base->gen_dmac.init_reg = NULL; + base->gen_dmac.init_reg_size = 0; } static int __init d40_lcla_allocate(struct d40_base *base) diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h index 6d47373f3f58..fdde8ef77542 100644 --- a/drivers/dma/ste_dma40_ll.h +++ b/drivers/dma/ste_dma40_ll.h @@ -125,7 +125,7 @@ #define D40_DREG_GCC 0x000 #define D40_DREG_GCC_ENA 0x1 /* This assumes that there are only 4 event groups */ -#define D40_DREG_GCC_ENABLE_ALL 0xff01 +#define D40_DREG_GCC_ENABLE_ALL 0x3ff01 #define D40_DREG_GCC_EVTGRP_POS 8 #define D40_DREG_GCC_SRC 0 #define D40_DREG_GCC_DST 1 @@ -148,14 +148,31 @@ #define D40_DREG_LCPA 0x020 #define D40_DREG_LCLA 0x024 + +#define D40_DREG_SSEG1 0x030 +#define D40_DREG_SSEG2 0x034 +#define D40_DREG_SSEG3 0x038 +#define D40_DREG_SSEG4 0x03C + +#define D40_DREG_SCEG1 0x040 +#define D40_DREG_SCEG2 0x044 +#define D40_DREG_SCEG3 0x048 +#define D40_DREG_SCEG4 0x04C + #define D40_DREG_ACTIVE 0x050 #define D40_DREG_ACTIVO 0x054 -#define D40_DREG_FSEB1 0x058 -#define D40_DREG_FSEB2 0x05C +#define D40_DREG_CIDMOD 0x058 +#define D40_DREG_TCIDV 0x05C #define D40_DREG_PCMIS 0x060 #define D40_DREG_PCICR 0x064 #define D40_DREG_PCTIS 0x068 #define D40_DREG_PCEIS 0x06C + +#define D40_DREG_SPCMIS 0x070 +#define D40_DREG_SPCICR 0x074 +#define D40_DREG_SPCTIS 0x078 +#define D40_DREG_SPCEIS 0x07C + #define D40_DREG_LCMIS0 0x080 #define D40_DREG_LCMIS1 0x084 #define D40_DREG_LCMIS2 0x088 @@ -172,6 +189,33 @@ #define D40_DREG_LCEIS1 0x0B4 #define D40_DREG_LCEIS2 0x0B8 #define D40_DREG_LCEIS3 0x0BC + +#define D40_DREG_SLCMIS1 0x0C0 +#define D40_DREG_SLCMIS2 0x0C4 +#define D40_DREG_SLCMIS3 0x0C8 +#define D40_DREG_SLCMIS4 0x0CC + +#define D40_DREG_SLCICR1 0x0D0 +#define D40_DREG_SLCICR2 0x0D4 +#define D40_DREG_SLCICR3 0x0D8 +#define D40_DREG_SLCICR4 0x0DC + +#define D40_DREG_SLCTIS1 0x0E0 +#define D40_DREG_SLCTIS2 0x0E4 +#define D40_DREG_SLCTIS3 0x0E8 +#define D40_DREG_SLCTIS4 0x0EC + +#define D40_DREG_SLCEIS1 0x0F0 +#define D40_DREG_SLCEIS2 0x0F4 +#define D40_DREG_SLCEIS3 0x0F8 +#define D40_DREG_SLCEIS4 0x0FC + +#define D40_DREG_FSESS1 0x100 +#define D40_DREG_FSESS2 0x104 + +#define D40_DREG_FSEBS1 0x108 +#define D40_DREG_FSEBS2 0x10C + #define D40_DREG_PSEG1 0x110 #define D40_DREG_PSEG2 0x114 #define D40_DREG_PSEG3 0x118 @@ -188,6 +232,86 @@ #define D40_DREG_RCEG2 0x144 #define D40_DREG_RCEG3 0x148 #define D40_DREG_RCEG4 0x14C + +#define D40_DREG_PREFOT 0x15C +#define D40_DREG_EXTCFG 0x160 + +#define D40_DREG_CPSEG1 0x200 +#define D40_DREG_CPSEG2 0x204 +#define D40_DREG_CPSEG3 0x208 +#define D40_DREG_CPSEG4 0x20C +#define D40_DREG_CPSEG5 0x210 + +#define D40_DREG_CPCEG1 0x220 +#define D40_DREG_CPCEG2 0x224 +#define D40_DREG_CPCEG3 0x228 +#define D40_DREG_CPCEG4 0x22C +#define D40_DREG_CPCEG5 0x230 + +#define D40_DREG_CRSEG1 0x240 +#define D40_DREG_CRSEG2 0x244 +#define D40_DREG_CRSEG3 0x248 +#define D40_DREG_CRSEG4 0x24C +#define D40_DREG_CRSEG5 0x250 + +#define D40_DREG_CRCEG1 0x260 +#define D40_DREG_CRCEG2 0x264 +#define D40_DREG_CRCEG3 0x268 +#define D40_DREG_CRCEG4 0x26C +#define D40_DREG_CRCEG5 0x270 + +#define D40_DREG_CFSESS1 0x280 +#define D40_DREG_CFSESS2 0x284 +#define D40_DREG_CFSESS3 0x288 + +#define D40_DREG_CFSEBS1 0x290 +#define D40_DREG_CFSEBS2 0x294 +#define D40_DREG_CFSEBS3 0x298 + +#define D40_DREG_CLCMIS1 0x300 +#define D40_DREG_CLCMIS2 0x304 +#define D40_DREG_CLCMIS3 0x308 +#define D40_DREG_CLCMIS4 0x30C +#define D40_DREG_CLCMIS5 0x310 + +#define D40_DREG_CLCICR1 0x320 +#define D40_DREG_CLCICR2 0x324 +#define D40_DREG_CLCICR3 0x328 +#define D40_DREG_CLCICR4 0x32C +#define D40_DREG_CLCICR5 0x330 + +#define D40_DREG_CLCTIS1 0x340 +#define D40_DREG_CLCTIS2 0x344 +#define D40_DREG_CLCTIS3 0x348 +#define D40_DREG_CLCTIS4 0x34C +#define D40_DREG_CLCTIS5 0x350 + +#define D40_DREG_CLCEIS1 0x360 +#define D40_DREG_CLCEIS2 0x364 +#define D40_DREG_CLCEIS3 0x368 +#define D40_DREG_CLCEIS4 0x36C +#define D40_DREG_CLCEIS5 0x370 + +#define D40_DREG_CPCMIS 0x380 +#define D40_DREG_CPCICR 0x384 +#define D40_DREG_CPCTIS 0x388 +#define D40_DREG_CPCEIS 0x38C + +#define D40_DREG_SCCIDA1 0xE80 +#define D40_DREG_SCCIDA2 0xE90 +#define D40_DREG_SCCIDA3 0xEA0 +#define D40_DREG_SCCIDA4 0xEB0 +#define D40_DREG_SCCIDA5 0xEC0 + +#define D40_DREG_SCCIDB1 0xE84 +#define D40_DREG_SCCIDB2 0xE94 +#define D40_DREG_SCCIDB3 0xEA4 +#define D40_DREG_SCCIDB4 0xEB4 +#define D40_DREG_SCCIDB5 0xEC4 + +#define D40_DREG_PRSCCIDA 0xF80 +#define D40_DREG_PRSCCIDB 0xF84 + #define D40_DREG_STFU 0xFC8 #define D40_DREG_ICFG 0xFCC #define D40_DREG_PERIPHID0 0xFE0 From 4226dd86b10ac44f8e98599f6a73e3a1b929f8eb Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 13 Dec 2012 13:46:16 +0100 Subject: [PATCH 0856/4607] dmaengine: ste_dma40: add a done queue for completed descriptors This is to keep the active queue for only those transfers which are actually active in the hardware. Descriptors will be moved to the done queue after they are completed in the hardware (interrupt handler) but before all the cleanup work has been completed (tasklet). Mostly based on a previous patch by Rabin Vincent. Cc: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 67e565bffe16..ff21dcbd8208 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -382,6 +382,7 @@ struct d40_base; * @client: Cliented owned descriptor list. * @pending_queue: Submitted jobs, to be issued by issue_pending() * @active: Active descriptor. + * @done: Completed jobs * @queue: Queued jobs. * @prepare_queue: Prepared jobs. * @dma_cfg: The client configuration of this dma channel. @@ -407,6 +408,7 @@ struct d40_chan { struct list_head client; struct list_head pending_queue; struct list_head active; + struct list_head done; struct list_head queue; struct list_head prepare_queue; struct stedma40_chan_cfg dma_cfg; @@ -754,6 +756,11 @@ static void d40_phy_lli_load(struct d40_chan *chan, struct d40_desc *desc) writel(lli_dst->reg_lnk, base + D40_CHAN_REG_SDLNK); } +static void d40_desc_done(struct d40_chan *d40c, struct d40_desc *desc) +{ + list_add_tail(&desc->node, &d40c->done); +} + static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc) { struct d40_lcla_pool *pool = &chan->base->lcla_pool; @@ -914,6 +921,14 @@ static struct d40_desc *d40_first_queued(struct d40_chan *d40c) return d; } +static struct d40_desc *d40_first_done(struct d40_chan *d40c) +{ + if (list_empty(&d40c->done)) + return NULL; + + return list_first_entry(&d40c->done, struct d40_desc, node); +} + static int d40_psize_2_burst_size(bool is_log, int psize) { if (is_log) { @@ -1104,6 +1119,12 @@ static void d40_term_all(struct d40_chan *d40c) struct d40_desc *d40d; struct d40_desc *_d; + /* Release completed descriptors */ + while ((d40d = d40_first_done(d40c))) { + d40_desc_remove(d40d); + d40_desc_free(d40c, d40d); + } + /* Release active descriptors */ while ((d40d = d40_first_active_get(d40c))) { d40_desc_remove(d40d); @@ -1541,6 +1562,9 @@ static void dma_tc_handle(struct d40_chan *d40c) pm_runtime_put_autosuspend(d40c->base->dev); } + d40_desc_remove(d40d); + d40_desc_done(d40c, d40d); + d40c->pending_tx++; tasklet_schedule(&d40c->tasklet); @@ -1556,10 +1580,14 @@ static void dma_tasklet(unsigned long data) spin_lock_irqsave(&d40c->lock, flags); - /* Get first active entry from list */ - d40d = d40_first_active_get(d40c); - if (d40d == NULL) - goto err; + /* Get first entry from the done list */ + d40d = d40_first_done(d40c); + if (d40d == NULL) { + /* Check if we have reached here for cyclic job */ + d40d = d40_first_active_get(d40c); + if (d40d == NULL || !d40d->cyclic) + goto err; + } if (!d40d->cyclic) dma_cookie_complete(&d40d->txd); @@ -2823,6 +2851,7 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma, d40c->log_num = D40_PHY_CHAN; + INIT_LIST_HEAD(&d40c->done); INIT_LIST_HEAD(&d40c->active); INIT_LIST_HEAD(&d40c->queue); INIT_LIST_HEAD(&d40c->pending_queue); From 762eb33fdebed34d98943d85ee1425663d7cceaa Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 13 Dec 2012 11:38:39 +0100 Subject: [PATCH 0857/4607] dmaengine: ste_dma40: add missing kernel-doc entry Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- include/linux/platform_data/dma-ste-dma40.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index 833cb959f3df..b99024ba94ec 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -147,6 +147,7 @@ struct stedma40_chan_cfg { * @memcpy_conf_log: default configuration of logical channel memcpy * @disabled_channels: A vector, ending with -1, that marks physical channels * that are for different reasons not available for the driver. + * @use_esram_lcla: flag for mapping the lcla into esram region * @num_of_phy_chans: The number of physical channels implemented in HW. * 0 means reading the number of channels from DMA HW but this is only valid * for 'multiple of 4' channels, like 8. From f26e03ad2b50be50c98f8ecb1fd9dbdf94db91ab Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 13 Dec 2012 17:12:37 +0100 Subject: [PATCH 0858/4607] dmaengine: ste_dma40: minor cosmetic fixes This patch contains various non functional cosmetic fixes. Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 33 ++++++++++++--------------------- drivers/dma/ste_dma40_ll.c | 2 +- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index ff21dcbd8208..623779e580c3 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1609,13 +1609,11 @@ static void dma_tasklet(unsigned long data) if (async_tx_test_ack(&d40d->txd)) { d40_desc_remove(d40d); d40_desc_free(d40c, d40d); - } else { - if (!d40d->is_in_client_list) { - d40_desc_remove(d40d); - d40_lcla_free_all(d40c, d40d); - list_add_tail(&d40d->node, &d40c->client); - d40d->is_in_client_list = true; - } + } else if (!d40d->is_in_client_list) { + d40_desc_remove(d40d); + d40_lcla_free_all(d40c, d40d); + list_add_tail(&d40d->node, &d40c->client); + d40d->is_in_client_list = true; } } @@ -2123,7 +2121,6 @@ _exit: } - static u32 stedma40_residue(struct dma_chan *chan) { struct d40_chan *d40c = @@ -2199,7 +2196,6 @@ d40_prep_sg_phy(struct d40_chan *chan, struct d40_desc *desc, return ret < 0 ? ret : 0; } - static struct d40_desc * d40_prep_desc(struct d40_chan *chan, struct scatterlist *sg, unsigned int sg_len, unsigned long dma_flags) @@ -2225,7 +2221,6 @@ d40_prep_desc(struct d40_chan *chan, struct scatterlist *sg, goto err; } - desc->lli_current = 0; desc->txd.flags = dma_flags; desc->txd.tx_submit = d40_tx_submit; @@ -2274,7 +2269,6 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, return NULL; } - spin_lock_irqsave(&chan->lock, flags); desc = d40_prep_desc(chan, sg_src, sg_len, dma_flags); @@ -2432,11 +2426,11 @@ static int d40_alloc_chan_resources(struct dma_chan *chan) if (d40c->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM) d40c->lcpa = d40c->base->lcpa_base + - d40c->dma_cfg.src_dev_type * D40_LCPA_CHAN_SIZE; + d40c->dma_cfg.src_dev_type * D40_LCPA_CHAN_SIZE; else d40c->lcpa = d40c->base->lcpa_base + - d40c->dma_cfg.dst_dev_type * - D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA; + d40c->dma_cfg.dst_dev_type * + D40_LCPA_CHAN_SIZE + D40_LCPA_CHAN_DST_DELTA; } dev_dbg(chan2dev(d40c), "allocated %s channel (phy %d%s)\n", @@ -2471,7 +2465,6 @@ static void d40_free_chan_resources(struct dma_chan *chan) return; } - spin_lock_irqsave(&d40c->lock, flags); err = d40_free_dma(d40c); @@ -2514,12 +2507,10 @@ d40_prep_memcpy_sg(struct dma_chan *chan, return d40_prep_sg(chan, src_sg, dst_sg, src_nents, DMA_NONE, dma_flags); } -static struct dma_async_tx_descriptor *d40_prep_slave_sg(struct dma_chan *chan, - struct scatterlist *sgl, - unsigned int sg_len, - enum dma_transfer_direction direction, - unsigned long dma_flags, - void *context) +static struct dma_async_tx_descriptor * +d40_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long dma_flags, void *context) { if (direction != DMA_DEV_TO_MEM && direction != DMA_MEM_TO_DEV) return NULL; diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index 1cfe7ab50c6b..7180e0d41722 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -251,7 +251,7 @@ d40_phy_buf_to_lli(struct d40_phy_lli *lli, dma_addr_t addr, u32 size, return lli; - err: +err: return NULL; } From 7ce529efbcf6fdcb1854e4634adf7f6a18216e81 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 18 Dec 2012 16:59:09 +0100 Subject: [PATCH 0859/4607] dmaengine: ste_dma40: minor code readability fixes Use internal variables to the cycles to improve code readability, no functional changes. Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 623779e580c3..e317debdbe5a 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -639,19 +639,18 @@ static int d40_lcla_alloc_one(struct d40_chan *d40c, unsigned long flags; int i; int ret = -EINVAL; - int p; spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags); - p = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP; - /* * Allocate both src and dst at the same time, therefore the half * start on 1 since 0 can't be used since zero is used as end marker. */ for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) { - if (!d40c->base->lcla_pool.alloc_map[p + i]) { - d40c->base->lcla_pool.alloc_map[p + i] = d40d; + int idx = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP + i; + + if (!d40c->base->lcla_pool.alloc_map[idx]) { + d40c->base->lcla_pool.alloc_map[idx] = d40d; d40d->lcla_alloc++; ret = i; break; @@ -676,10 +675,10 @@ static int d40_lcla_free_all(struct d40_chan *d40c, spin_lock_irqsave(&d40c->base->lcla_pool.lock, flags); for (i = 1 ; i < D40_LCLA_LINK_PER_EVENT_GRP / 2; i++) { - if (d40c->base->lcla_pool.alloc_map[d40c->phy_chan->num * - D40_LCLA_LINK_PER_EVENT_GRP + i] == d40d) { - d40c->base->lcla_pool.alloc_map[d40c->phy_chan->num * - D40_LCLA_LINK_PER_EVENT_GRP + i] = NULL; + int idx = d40c->phy_chan->num * D40_LCLA_LINK_PER_EVENT_GRP + i; + + if (d40c->base->lcla_pool.alloc_map[idx] == d40d) { + d40c->base->lcla_pool.alloc_map[idx] = NULL; d40d->lcla_alloc--; if (d40d->lcla_alloc == 0) { ret = 0; From 7407048bec896268b50e3c43c1d012a4764dc210 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 18 Dec 2012 12:25:14 +0100 Subject: [PATCH 0860/4607] dmaengine: ste_dma40: add software lli support This patch add support to manage LLI by SW for select phy channels. There is a HW issue in certain controllers due to which on certain occassions HW LLI cannot be used on some physical channels. To avoid the HW issue on a specific phy channel, the phy channel number can be added to the list of soft_lli_channels and there after all the transfers on that channel will use software LLI, for peripheral to memory transfers. SoftLLI introduces relink overhead, that could impact performace for certain use cases. This is based on a previous patch of Narayanan Gopalakrishnan. Cc: Shreshtha Kumar Sahu Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 20 +++++++++++++++++++- include/linux/platform_data/dma-ste-dma40.h | 8 ++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index e317debdbe5a..2ecefb7113b9 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -355,6 +355,7 @@ struct d40_lcla_pool { * @allocated_dst: Same as for src but is dst. * allocated_dst and allocated_src uses the D40_ALLOC* defines as well as * event line number. + * @use_soft_lli: To mark if the linked lists of channel are managed by SW. */ struct d40_phy_res { spinlock_t lock; @@ -362,6 +363,7 @@ struct d40_phy_res { int num; u32 allocated_src; u32 allocated_dst; + bool use_soft_lli; }; struct d40_base; @@ -783,7 +785,16 @@ static void d40_log_lli_to_lcxa(struct d40_chan *chan, struct d40_desc *desc) * can't link back to the one in LCPA space */ if (linkback || (lli_len - lli_current > 1)) { - curr_lcla = d40_lcla_alloc_one(chan, desc); + /* + * If the channel is expected to use only soft_lli don't + * allocate a lcla. This is to avoid a HW issue that exists + * in some controller during a peripheral to memory transfer + * that uses linked lists. + */ + if (!(chan->phy_chan->use_soft_lli && + chan->dma_cfg.dir == STEDMA40_PERIPH_TO_MEM)) + curr_lcla = d40_lcla_alloc_one(chan, desc); + first_lcla = curr_lcla; } @@ -3063,6 +3074,13 @@ static int __init d40_phy_res_init(struct d40_base *base) num_phy_chans_avail--; } + /* Mark soft_lli channels */ + for (i = 0; i < base->plat_data->num_of_soft_lli_chans; i++) { + int chan = base->plat_data->soft_lli_chans[i]; + + base->phy_res[chan].use_soft_lli = true; + } + dev_info(base->dev, "%d of %d physical DMA channels available\n", num_phy_chans_avail, base->num_phy_chans); diff --git a/include/linux/platform_data/dma-ste-dma40.h b/include/linux/platform_data/dma-ste-dma40.h index b99024ba94ec..4b781014b0a0 100644 --- a/include/linux/platform_data/dma-ste-dma40.h +++ b/include/linux/platform_data/dma-ste-dma40.h @@ -147,6 +147,12 @@ struct stedma40_chan_cfg { * @memcpy_conf_log: default configuration of logical channel memcpy * @disabled_channels: A vector, ending with -1, that marks physical channels * that are for different reasons not available for the driver. + * @soft_lli_chans: A vector, that marks physical channels will use LLI by SW + * which avoids HW bug that exists in some versions of the controller. + * SoftLLI introduces relink overhead that could impact performace for + * certain use cases. + * @num_of_soft_lli_chans: The number of channels that needs to be configured + * to use SoftLLI. * @use_esram_lcla: flag for mapping the lcla into esram region * @num_of_phy_chans: The number of physical channels implemented in HW. * 0 means reading the number of channels from DMA HW but this is only valid @@ -161,6 +167,8 @@ struct stedma40_platform_data { struct stedma40_chan_cfg *memcpy_conf_phy; struct stedma40_chan_cfg *memcpy_conf_log; int disabled_channels[STEDMA40_MAX_PHYS]; + int *soft_lli_chans; + int num_of_soft_lli_chans; bool use_esram_lcla; int num_of_phy_chans; }; From 53d6d68f3c1792bce0144d8499435468e425a995 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 19 Dec 2012 14:41:56 +0100 Subject: [PATCH 0861/4607] dmaengine: set_dma40: ignore spurious interrupts Some DMA channels may be used by other cores in the SoC. This patch modifies the dma interrupt handler to ignore interrupts from unknown channels. Cc: Rabin Vincent Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 2ecefb7113b9..a97bbd3a4a47 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1677,13 +1677,22 @@ static irqreturn_t d40_handle_interrupt(int irq, void *data) row = chan / BITS_PER_LONG; idx = chan & (BITS_PER_LONG - 1); - /* ACK interrupt */ - writel(1 << idx, base->virtbase + il[row].clr); - if (il[row].offset == D40_PHY_CHAN) d40c = base->lookup_phy_chans[idx]; else d40c = base->lookup_log_chans[il[row].offset + idx]; + + if (!d40c) { + /* + * No error because this can happen if something else + * in the system is using the channel. + */ + continue; + } + + /* ACK interrupt */ + writel(1 << idx, base->virtbase + il[row].clr); + spin_lock(&d40c->lock); if (!il[row].is_error) From da2ac56a1bc9c6c56244aa9ca990d5c5c7574b5f Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Mon, 7 Jan 2013 10:58:35 +0100 Subject: [PATCH 0862/4607] dmaengine: set_dma40: balance clock in probe fail code Clock code was changed to use clk_prepare_enable in: b707c65 dma/ste_dma40: Fixup clock usage during probe but clk_disable on probe fail path was not updated. This patch fix this by using clk_disable_unprepare in place of clk_disable. Acked-by: Ulf Hansson Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Fabio Baltieri --- drivers/dma/ste_dma40.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index a97bbd3a4a47..d77d41d7c463 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -3634,7 +3634,7 @@ failure: release_mem_region(base->phy_start, base->phy_size); if (base->clk) { - clk_disable(base->clk); + clk_disable_unprepare(base->clk); clk_put(base->clk); } From 3a366e614d0837d9fc23f78cdb1a1186ebc3387f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 11 Jan 2013 13:06:33 -0800 Subject: [PATCH 0863/4607] block: add missing block_bio_complete() tracepoint bio completion didn't kick block_bio_complete TP. Only dm was explicitly triggering the TP on IO completion. This makes block_bio_complete TP useless for tracers which want to know about bios, and all other bio based drivers skip generating blktrace completion events. This patch makes all bio completions via bio_endio() generate block_bio_complete TP. * Explicit trace_block_bio_complete() invocation removed from dm and the trace point is unexported. * @rq dropped from trace_block_bio_complete(). bios may fly around w/o queue associated. Verifying and accessing the assocaited queue belongs to TP probes. * blktrace now gets both request and bio completions. Make it ignore bio completions if request completion path is happening. This makes all bio based drivers generate blktrace completion events properly and makes the block_bio_complete TP actually useful. v2: With this change, block_bio_complete TP could be invoked on sg commands which have bio's with %NULL bi_bdev. Update TP assignment code to check whether bio->bi_bdev is %NULL before dereferencing. Signed-off-by: Tejun Heo Original-patch-by: Namhyung Kim Cc: Tejun Heo Cc: Steven Rostedt Cc: Alasdair Kergon Cc: dm-devel@redhat.com Cc: Neil Brown Signed-off-by: Jens Axboe --- block/blk-core.c | 1 - drivers/md/dm.c | 1 - drivers/md/raid5.c | 11 +---------- fs/bio.c | 2 ++ include/linux/blktrace_api.h | 1 + include/trace/events/block.h | 8 ++++---- kernel/trace/blktrace.c | 26 +++++++++++++++++++++++--- 7 files changed, 31 insertions(+), 19 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index aca5d82ff13c..4f5aec708be6 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -39,7 +39,6 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(block_bio_remap); EXPORT_TRACEPOINT_SYMBOL_GPL(block_rq_remap); -EXPORT_TRACEPOINT_SYMBOL_GPL(block_bio_complete); EXPORT_TRACEPOINT_SYMBOL_GPL(block_unplug); DEFINE_IDA(blk_queue_ida); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index c72e4d5a9617..650ec2866e34 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -627,7 +627,6 @@ static void dec_pending(struct dm_io *io, int error) queue_io(md, bio); } else { /* done with normal IO or empty flush */ - trace_block_bio_complete(md->queue, bio, io_error); bio_endio(bio, io_error); } } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 19d77a026639..9ab506df42da 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -184,8 +184,6 @@ static void return_io(struct bio *return_bi) return_bi = bi->bi_next; bi->bi_next = NULL; bi->bi_size = 0; - trace_block_bio_complete(bdev_get_queue(bi->bi_bdev), - bi, 0); bio_endio(bi, 0); bi = return_bi; } @@ -3917,8 +3915,6 @@ static void raid5_align_endio(struct bio *bi, int error) rdev_dec_pending(rdev, conf->mddev); if (!error && uptodate) { - trace_block_bio_complete(bdev_get_queue(raid_bi->bi_bdev), - raid_bi, 0); bio_endio(raid_bi, 0); if (atomic_dec_and_test(&conf->active_aligned_reads)) wake_up(&conf->wait_for_stripe); @@ -4377,8 +4373,6 @@ static void make_request(struct mddev *mddev, struct bio * bi) if ( rw == WRITE ) md_write_end(mddev); - trace_block_bio_complete(bdev_get_queue(bi->bi_bdev), - bi, 0); bio_endio(bi, 0); } } @@ -4755,11 +4749,8 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio) handled++; } remaining = raid5_dec_bi_active_stripes(raid_bio); - if (remaining == 0) { - trace_block_bio_complete(bdev_get_queue(raid_bio->bi_bdev), - raid_bio, 0); + if (remaining == 0) bio_endio(raid_bio, 0); - } if (atomic_dec_and_test(&conf->active_aligned_reads)) wake_up(&conf->wait_for_stripe); return handled; diff --git a/fs/bio.c b/fs/bio.c index b96fc6ce4855..bb5768f59b32 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -1428,6 +1428,8 @@ void bio_endio(struct bio *bio, int error) else if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) error = -EIO; + trace_block_bio_complete(bio, error); + if (bio->bi_end_io) bio->bi_end_io(bio, error); } diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index 7c2e030e72f1..0ea61e07a91c 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -12,6 +12,7 @@ struct blk_trace { int trace_state; + bool rq_based; struct rchan *rchan; unsigned long __percpu *sequence; unsigned char __percpu *msg_data; diff --git a/include/trace/events/block.h b/include/trace/events/block.h index 05c5e61f0a7c..8a168db9a645 100644 --- a/include/trace/events/block.h +++ b/include/trace/events/block.h @@ -206,7 +206,6 @@ TRACE_EVENT(block_bio_bounce, /** * block_bio_complete - completed all work on the block operation - * @q: queue holding the block operation * @bio: block operation completed * @error: io error value * @@ -215,9 +214,9 @@ TRACE_EVENT(block_bio_bounce, */ TRACE_EVENT(block_bio_complete, - TP_PROTO(struct request_queue *q, struct bio *bio, int error), + TP_PROTO(struct bio *bio, int error), - TP_ARGS(q, bio, error), + TP_ARGS(bio, error), TP_STRUCT__entry( __field( dev_t, dev ) @@ -228,7 +227,8 @@ TRACE_EVENT(block_bio_complete, ), TP_fast_assign( - __entry->dev = bio->bi_bdev->bd_dev; + __entry->dev = bio->bi_bdev ? + bio->bi_bdev->bd_dev : 0; __entry->sector = bio->bi_sector; __entry->nr_sector = bio->bi_size >> 9; __entry->error = error; diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c index c0bd0308741c..190d98fbed27 100644 --- a/kernel/trace/blktrace.c +++ b/kernel/trace/blktrace.c @@ -739,6 +739,12 @@ static void blk_add_trace_rq_complete(void *ignore, struct request_queue *q, struct request *rq) { + struct blk_trace *bt = q->blk_trace; + + /* if control ever passes through here, it's a request based driver */ + if (unlikely(bt && !bt->rq_based)) + bt->rq_based = true; + blk_add_trace_rq(q, rq, BLK_TA_COMPLETE); } @@ -774,10 +780,24 @@ static void blk_add_trace_bio_bounce(void *ignore, blk_add_trace_bio(q, bio, BLK_TA_BOUNCE, 0); } -static void blk_add_trace_bio_complete(void *ignore, - struct request_queue *q, struct bio *bio, - int error) +static void blk_add_trace_bio_complete(void *ignore, struct bio *bio, int error) { + struct request_queue *q; + struct blk_trace *bt; + + if (!bio->bi_bdev) + return; + + q = bdev_get_queue(bio->bi_bdev); + bt = q->blk_trace; + + /* + * Request based drivers will generate both rq and bio completions. + * Ignore bio ones. + */ + if (likely(!bt) || bt->rq_based) + return; + blk_add_trace_bio(q, bio, BLK_TA_COMPLETE, error); } From 8c1cf6bb02fda79b0a4b9bd121f6be6d4ce7a15a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 11 Jan 2013 13:06:34 -0800 Subject: [PATCH 0864/4607] block: add @req to bio_{front|back}_merge tracepoints bio_{front|back}_merge tracepoints report a bio merging into an existing request but didn't specify which request the bio is being merged into. Add @req to it. This makes it impossible to share the event template with block_bio_queue - split it out. @req isn't used or exported to userland at this point and there is no userland visible behavior change. Later changes will make use of the extra parameter. Signed-off-by: Tejun Heo Signed-off-by: Jens Axboe --- block/blk-core.c | 4 ++-- include/trace/events/block.h | 45 +++++++++++++++++++++++++++--------- kernel/trace/blktrace.c | 2 ++ 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index 4f5aec708be6..66d31687cf6b 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1347,7 +1347,7 @@ static bool bio_attempt_back_merge(struct request_queue *q, struct request *req, if (!ll_back_merge_fn(q, req, bio)) return false; - trace_block_bio_backmerge(q, bio); + trace_block_bio_backmerge(q, req, bio); if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff) blk_rq_set_mixed_merge(req); @@ -1369,7 +1369,7 @@ static bool bio_attempt_front_merge(struct request_queue *q, if (!ll_front_merge_fn(q, req, bio)) return false; - trace_block_bio_frontmerge(q, bio); + trace_block_bio_frontmerge(q, req, bio); if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff) blk_rq_set_mixed_merge(req); diff --git a/include/trace/events/block.h b/include/trace/events/block.h index 8a168db9a645..b408f518a819 100644 --- a/include/trace/events/block.h +++ b/include/trace/events/block.h @@ -241,11 +241,11 @@ TRACE_EVENT(block_bio_complete, __entry->nr_sector, __entry->error) ); -DECLARE_EVENT_CLASS(block_bio, +DECLARE_EVENT_CLASS(block_bio_merge, - TP_PROTO(struct request_queue *q, struct bio *bio), + TP_PROTO(struct request_queue *q, struct request *rq, struct bio *bio), - TP_ARGS(q, bio), + TP_ARGS(q, rq, bio), TP_STRUCT__entry( __field( dev_t, dev ) @@ -272,31 +272,33 @@ DECLARE_EVENT_CLASS(block_bio, /** * block_bio_backmerge - merging block operation to the end of an existing operation * @q: queue holding operation + * @rq: request bio is being merged into * @bio: new block operation to merge * * Merging block request @bio to the end of an existing block request * in queue @q. */ -DEFINE_EVENT(block_bio, block_bio_backmerge, +DEFINE_EVENT(block_bio_merge, block_bio_backmerge, - TP_PROTO(struct request_queue *q, struct bio *bio), + TP_PROTO(struct request_queue *q, struct request *rq, struct bio *bio), - TP_ARGS(q, bio) + TP_ARGS(q, rq, bio) ); /** * block_bio_frontmerge - merging block operation to the beginning of an existing operation * @q: queue holding operation + * @rq: request bio is being merged into * @bio: new block operation to merge * * Merging block IO operation @bio to the beginning of an existing block * operation in queue @q. */ -DEFINE_EVENT(block_bio, block_bio_frontmerge, +DEFINE_EVENT(block_bio_merge, block_bio_frontmerge, - TP_PROTO(struct request_queue *q, struct bio *bio), + TP_PROTO(struct request_queue *q, struct request *rq, struct bio *bio), - TP_ARGS(q, bio) + TP_ARGS(q, rq, bio) ); /** @@ -306,11 +308,32 @@ DEFINE_EVENT(block_bio, block_bio_frontmerge, * * About to place the block IO operation @bio into queue @q. */ -DEFINE_EVENT(block_bio, block_bio_queue, +TRACE_EVENT(block_bio_queue, TP_PROTO(struct request_queue *q, struct bio *bio), - TP_ARGS(q, bio) + TP_ARGS(q, bio), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( sector_t, sector ) + __field( unsigned int, nr_sector ) + __array( char, rwbs, RWBS_LEN ) + __array( char, comm, TASK_COMM_LEN ) + ), + + TP_fast_assign( + __entry->dev = bio->bi_bdev->bd_dev; + __entry->sector = bio->bi_sector; + __entry->nr_sector = bio->bi_size >> 9; + blk_fill_rwbs(__entry->rwbs, bio->bi_rw, bio->bi_size); + memcpy(__entry->comm, current->comm, TASK_COMM_LEN); + ), + + TP_printk("%d,%d %s %llu + %u [%s]", + MAJOR(__entry->dev), MINOR(__entry->dev), __entry->rwbs, + (unsigned long long)__entry->sector, + __entry->nr_sector, __entry->comm) ); DECLARE_EVENT_CLASS(block_get_rq, diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c index 190d98fbed27..fb593f6a687e 100644 --- a/kernel/trace/blktrace.c +++ b/kernel/trace/blktrace.c @@ -803,6 +803,7 @@ static void blk_add_trace_bio_complete(void *ignore, struct bio *bio, int error) static void blk_add_trace_bio_backmerge(void *ignore, struct request_queue *q, + struct request *rq, struct bio *bio) { blk_add_trace_bio(q, bio, BLK_TA_BACKMERGE, 0); @@ -810,6 +811,7 @@ static void blk_add_trace_bio_backmerge(void *ignore, static void blk_add_trace_bio_frontmerge(void *ignore, struct request_queue *q, + struct request *rq, struct bio *bio) { blk_add_trace_bio(q, bio, BLK_TA_FRONTMERGE, 0); From f0059afd3e6e7aa1a0ffc23468b74c43d47660b8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 11 Jan 2013 13:06:35 -0800 Subject: [PATCH 0865/4607] buffer: make touch_buffer() an exported function We want to add a trace point to touch_buffer() but macros and inline functions defined in header files can't have tracing points. Move touch_buffer() to fs/buffer.c and make it a proper function. The new exported function is also declared inline. As most uses of touch_buffer() are inside buffer.c with nilfs2 as the only other user, the effect of this change should be negligible. Signed-off-by: Tejun Heo Cc: Steven Rostedt Signed-off-by: Jens Axboe --- fs/buffer.c | 6 ++++++ include/linux/buffer_head.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/buffer.c b/fs/buffer.c index c017a2dfb909..a8c2dfb68dcd 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -53,6 +53,12 @@ void init_buffer(struct buffer_head *bh, bh_end_io_t *handler, void *private) } EXPORT_SYMBOL(init_buffer); +inline void touch_buffer(struct buffer_head *bh) +{ + mark_page_accessed(bh->b_page); +} +EXPORT_SYMBOL(touch_buffer); + static int sleep_on_buffer(void *word) { io_schedule(); diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 458f497738a4..5afc4f94d110 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -126,7 +126,6 @@ BUFFER_FNS(Write_EIO, write_io_error) BUFFER_FNS(Unwritten, unwritten) #define bh_offset(bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK) -#define touch_buffer(bh) mark_page_accessed(bh->b_page) /* If we *know* page->private refers to buffer_heads */ #define page_buffers(page) \ @@ -142,6 +141,7 @@ BUFFER_FNS(Unwritten, unwritten) void mark_buffer_dirty(struct buffer_head *bh); void init_buffer(struct buffer_head *, bh_end_io_t *, void *); +void touch_buffer(struct buffer_head *bh); void set_bh_page(struct buffer_head *bh, struct page *page, unsigned long offset); int try_to_free_buffers(struct page *); From 5305cb830834549b9203ad4d009ad5483c5e293f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 11 Jan 2013 13:06:36 -0800 Subject: [PATCH 0866/4607] block: add block_{touch|dirty}_buffer tracepoint The former is triggered from touch_buffer() and the latter mark_buffer_dirty(). This is part of tracepoint additions to improve visiblity into dirtying / writeback operations for io tracer and userland. v2: Transformed writeback_dirty_buffer to block_dirty_buffer and made it share TP definition with block_touch_buffer. Signed-off-by: Tejun Heo Cc: Fengguang Wu Signed-off-by: Jens Axboe --- fs/buffer.c | 4 +++ include/trace/events/block.h | 51 ++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/fs/buffer.c b/fs/buffer.c index a8c2dfb68dcd..87ff335fbbe3 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -41,6 +41,7 @@ #include #include #include +#include static int fsync_buffers_list(spinlock_t *lock, struct list_head *list); @@ -55,6 +56,7 @@ EXPORT_SYMBOL(init_buffer); inline void touch_buffer(struct buffer_head *bh) { + trace_block_touch_buffer(bh); mark_page_accessed(bh->b_page); } EXPORT_SYMBOL(touch_buffer); @@ -1119,6 +1121,8 @@ void mark_buffer_dirty(struct buffer_head *bh) { WARN_ON_ONCE(!buffer_uptodate(bh)); + trace_block_dirty_buffer(bh); + /* * Very *carefully* optimize the it-is-already-dirty case. * diff --git a/include/trace/events/block.h b/include/trace/events/block.h index b408f518a819..9961726523d0 100644 --- a/include/trace/events/block.h +++ b/include/trace/events/block.h @@ -6,10 +6,61 @@ #include #include +#include #include #define RWBS_LEN 8 +DECLARE_EVENT_CLASS(block_buffer, + + TP_PROTO(struct buffer_head *bh), + + TP_ARGS(bh), + + TP_STRUCT__entry ( + __field( dev_t, dev ) + __field( sector_t, sector ) + __field( size_t, size ) + ), + + TP_fast_assign( + __entry->dev = bh->b_bdev->bd_dev; + __entry->sector = bh->b_blocknr; + __entry->size = bh->b_size; + ), + + TP_printk("%d,%d sector=%llu size=%zu", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long long)__entry->sector, __entry->size + ) +); + +/** + * block_touch_buffer - mark a buffer accessed + * @bh: buffer_head being touched + * + * Called from touch_buffer(). + */ +DEFINE_EVENT(block_buffer, block_touch_buffer, + + TP_PROTO(struct buffer_head *bh), + + TP_ARGS(bh) +); + +/** + * block_dirty_buffer - mark a buffer dirty + * @bh: buffer_head being dirtied + * + * Called from mark_buffer_dirty(). + */ +DEFINE_EVENT(block_buffer, block_dirty_buffer, + + TP_PROTO(struct buffer_head *bh), + + TP_ARGS(bh) +); + DECLARE_EVENT_CLASS(block_rq_with_error, TP_PROTO(struct request_queue *q, struct request *rq), From 9fb0a7da0c528d9bd49b597aa63b1fe2216c7203 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 11 Jan 2013 13:06:37 -0800 Subject: [PATCH 0867/4607] writeback: add more tracepoints Add tracepoints for page dirtying, writeback_single_inode start, inode dirtying and writeback. For the latter two inode events, a pair of events are defined to denote start and end of the operations (the starting one has _start suffix and the one w/o suffix happens after the operation is complete). These inode ops are FS specific and can be non-trivial and having enclosing tracepoints is useful for external tracers. This is part of tracepoint additions to improve visiblity into dirtying / writeback operations for io tracer and userland. v2: writeback_dirty_inode[_start] TPs may be called for files on pseudo FSes w/ unregistered bdi. Check whether bdi->dev is %NULL before dereferencing. v3: buffer dirtying moved to a block TP. Signed-off-by: Tejun Heo Reviewed-by: Jan Kara Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 16 ++++- include/trace/events/writeback.h | 116 +++++++++++++++++++++++++++++++ mm/page-writeback.c | 2 + 3 files changed, 132 insertions(+), 2 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 310972b72a66..359494ea1bde 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -318,8 +318,14 @@ static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work) static int write_inode(struct inode *inode, struct writeback_control *wbc) { - if (inode->i_sb->s_op->write_inode && !is_bad_inode(inode)) - return inode->i_sb->s_op->write_inode(inode, wbc); + int ret; + + if (inode->i_sb->s_op->write_inode && !is_bad_inode(inode)) { + trace_writeback_write_inode_start(inode, wbc); + ret = inode->i_sb->s_op->write_inode(inode, wbc); + trace_writeback_write_inode(inode, wbc); + return ret; + } return 0; } @@ -450,6 +456,8 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) WARN_ON(!(inode->i_state & I_SYNC)); + trace_writeback_single_inode_start(inode, wbc, nr_to_write); + ret = do_writepages(mapping, wbc); /* @@ -1150,8 +1158,12 @@ void __mark_inode_dirty(struct inode *inode, int flags) * dirty the inode itself */ if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) { + trace_writeback_dirty_inode_start(inode, flags); + if (sb->s_op->dirty_inode) sb->s_op->dirty_inode(inode, flags); + + trace_writeback_dirty_inode(inode, flags); } /* diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index b453d92c2253..6a16fd2e70ed 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -32,6 +32,115 @@ struct wb_writeback_work; +TRACE_EVENT(writeback_dirty_page, + + TP_PROTO(struct page *page, struct address_space *mapping), + + TP_ARGS(page, mapping), + + TP_STRUCT__entry ( + __array(char, name, 32) + __field(unsigned long, ino) + __field(pgoff_t, index) + ), + + TP_fast_assign( + strncpy(__entry->name, + mapping ? dev_name(mapping->backing_dev_info->dev) : "(unknown)", 32); + __entry->ino = mapping ? mapping->host->i_ino : 0; + __entry->index = page->index; + ), + + TP_printk("bdi %s: ino=%lu index=%lu", + __entry->name, + __entry->ino, + __entry->index + ) +); + +DECLARE_EVENT_CLASS(writeback_dirty_inode_template, + + TP_PROTO(struct inode *inode, int flags), + + TP_ARGS(inode, flags), + + TP_STRUCT__entry ( + __array(char, name, 32) + __field(unsigned long, ino) + __field(unsigned long, flags) + ), + + TP_fast_assign( + struct backing_dev_info *bdi = inode->i_mapping->backing_dev_info; + + /* may be called for files on pseudo FSes w/ unregistered bdi */ + strncpy(__entry->name, + bdi->dev ? dev_name(bdi->dev) : "(unknown)", 32); + __entry->ino = inode->i_ino; + __entry->flags = flags; + ), + + TP_printk("bdi %s: ino=%lu flags=%s", + __entry->name, + __entry->ino, + show_inode_state(__entry->flags) + ) +); + +DEFINE_EVENT(writeback_dirty_inode_template, writeback_dirty_inode_start, + + TP_PROTO(struct inode *inode, int flags), + + TP_ARGS(inode, flags) +); + +DEFINE_EVENT(writeback_dirty_inode_template, writeback_dirty_inode, + + TP_PROTO(struct inode *inode, int flags), + + TP_ARGS(inode, flags) +); + +DECLARE_EVENT_CLASS(writeback_write_inode_template, + + TP_PROTO(struct inode *inode, struct writeback_control *wbc), + + TP_ARGS(inode, wbc), + + TP_STRUCT__entry ( + __array(char, name, 32) + __field(unsigned long, ino) + __field(int, sync_mode) + ), + + TP_fast_assign( + strncpy(__entry->name, + dev_name(inode->i_mapping->backing_dev_info->dev), 32); + __entry->ino = inode->i_ino; + __entry->sync_mode = wbc->sync_mode; + ), + + TP_printk("bdi %s: ino=%lu sync_mode=%d", + __entry->name, + __entry->ino, + __entry->sync_mode + ) +); + +DEFINE_EVENT(writeback_write_inode_template, writeback_write_inode_start, + + TP_PROTO(struct inode *inode, struct writeback_control *wbc), + + TP_ARGS(inode, wbc) +); + +DEFINE_EVENT(writeback_write_inode_template, writeback_write_inode, + + TP_PROTO(struct inode *inode, struct writeback_control *wbc), + + TP_ARGS(inode, wbc) +); + DECLARE_EVENT_CLASS(writeback_work_class, TP_PROTO(struct backing_dev_info *bdi, struct wb_writeback_work *work), TP_ARGS(bdi, work), @@ -479,6 +588,13 @@ DECLARE_EVENT_CLASS(writeback_single_inode_template, ) ); +DEFINE_EVENT(writeback_single_inode_template, writeback_single_inode_start, + TP_PROTO(struct inode *inode, + struct writeback_control *wbc, + unsigned long nr_to_write), + TP_ARGS(inode, wbc, nr_to_write) +); + DEFINE_EVENT(writeback_single_inode_template, writeback_single_inode, TP_PROTO(struct inode *inode, struct writeback_control *wbc, diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 0713bfbf0954..3734cefd4de4 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1982,6 +1982,8 @@ int __set_page_dirty_no_writeback(struct page *page) */ void account_page_dirtied(struct page *page, struct address_space *mapping) { + trace_writeback_dirty_page(page, mapping); + if (mapping_cap_account_dirty(mapping)) { __inc_zone_page_state(page, NR_FILE_DIRTY); __inc_zone_page_state(page, NR_DIRTIED); From 25effc3647635b8775aefcfd884a359b9fa31e9d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 10 Jan 2013 11:05:06 +0900 Subject: [PATCH 0868/4607] pata_samsung_cf: Use devm_clk_get() Use devm_clk_get() rather than clk_get() to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Jeff Garzik --- drivers/ata/pata_samsung_cf.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c index 63ffb002ec67..70b0e01372b3 100644 --- a/drivers/ata/pata_samsung_cf.c +++ b/drivers/ata/pata_samsung_cf.c @@ -512,7 +512,7 @@ static int __init pata_s3c_probe(struct platform_device *pdev) return -ENOMEM; } - info->clk = clk_get(&pdev->dev, "cfcon"); + info->clk = devm_clk_get(&pdev->dev, "cfcon"); if (IS_ERR(info->clk)) { dev_err(dev, "failed to get access to cf controller clock\n"); ret = PTR_ERR(info->clk); @@ -589,7 +589,6 @@ static int __init pata_s3c_probe(struct platform_device *pdev) stop_clk: clk_disable(info->clk); - clk_put(info->clk); return ret; } @@ -601,7 +600,6 @@ static int __exit pata_s3c_remove(struct platform_device *pdev) ata_host_detach(host); clk_disable(info->clk); - clk_put(info->clk); return 0; } From 1757d902b029a29dfcef63609964385cf8865b5a Mon Sep 17 00:00:00 2001 From: David Milburn Date: Mon, 14 Jan 2013 09:59:30 -0600 Subject: [PATCH 0869/4607] libata: export host controller number thru /sys As low-level drivers register their host controller(s), keep track of the number of controllers and export thru /sys in a format so that udev can better match up port numbers with a specific controller. # pwd /sys/devices/pci0000:00 # find . -name 'ata*' -print (2nd controller with port multiplier attached) ./0000:00:1e.0/0000:05:01.0/ata2.7 ./0000:00:1e.0/0000:05:01.0/ata2.7/link7/dev7.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.0/dev7.0.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.0/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.1/dev7.1.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.1/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.2/dev7.2.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.2/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.3/dev7.3.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.3/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.4/dev7.4.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.4/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.5/dev7.5.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.5/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.6/dev7.6.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.6/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.7/dev7.7.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.7/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.8/dev7.8.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.8/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.9/dev7.9.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.9/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/ata_port ./0000:00:1e.0/0000:05:01.0/ata2.7/ata_port/ata2.7 ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.10/dev7.10.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.10/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.11/dev7.11.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.11/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.12/dev7.12.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.12/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.13/dev7.13.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.13/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.14/dev7.14.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.7/link7.14/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.8 ./0000:00:1e.0/0000:05:01.0/ata2.8/link8/dev8.0/ata_device ./0000:00:1e.0/0000:05:01.0/ata2.8/link8/ata_link ./0000:00:1e.0/0000:05:01.0/ata2.8/ata_port ./0000:00:1e.0/0000:05:01.0/ata2.8/ata_port/ata2.8 (1st controller) ./0000:00:1f.2/ata1.1 ./0000:00:1f.2/ata1.1/link1/dev1.0/ata_device ./0000:00:1f.2/ata1.1/link1/ata_link ./0000:00:1f.2/ata1.1/ata_port ./0000:00:1f.2/ata1.1/ata_port/ata1.1 ./0000:00:1f.2/ata1.2 ./0000:00:1f.2/ata1.2/link2/dev2.0/ata_device ./0000:00:1f.2/ata1.2/link2/ata_link ./0000:00:1f.2/ata1.2/ata_port ./0000:00:1f.2/ata1.2/ata_port/ata1.2 ./0000:00:1f.2/ata1.3 ./0000:00:1f.2/ata1.3/link3/dev3.0/ata_device ./0000:00:1f.2/ata1.3/link3/ata_link ./0000:00:1f.2/ata1.3/ata_port ./0000:00:1f.2/ata1.3/ata_port/ata1.3 ./0000:00:1f.2/ata1.4 ./0000:00:1f.2/ata1.4/link4/dev4.0/ata_device ./0000:00:1f.2/ata1.4/link4/ata_link ./0000:00:1f.2/ata1.4/ata_port ./0000:00:1f.2/ata1.4/ata_port/ata1.4 ./0000:00:1f.2/ata1.5 ./0000:00:1f.2/ata1.5/link5/dev5.0/ata_device ./0000:00:1f.2/ata1.5/link5/ata_link ./0000:00:1f.2/ata1.5/ata_port ./0000:00:1f.2/ata1.5/ata_port/ata1.5 ./0000:00:1f.2/ata1.6 ./0000:00:1f.2/ata1.6/link6/dev6.0/ata_device ./0000:00:1f.2/ata1.6/link6/ata_link ./0000:00:1f.2/ata1.6/ata_port ./0000:00:1f.2/ata1.6/ata_port/ata1.6 Signed-off-by: David Milburn Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 4 ++++ drivers/ata/libata-transport.c | 2 +- include/linux/libata.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 46cd3f4c6aaa..275941b576a8 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -99,6 +99,7 @@ static void ata_dev_xfermask(struct ata_device *dev); static unsigned long ata_dev_blacklisted(const struct ata_device *dev); atomic_t ata_print_id = ATOMIC_INIT(0); +atomic_t host_print_id = ATOMIC_INIT(0); struct ata_force_param { const char *name; @@ -6097,6 +6098,9 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) for (i = host->n_ports; host->ports[i]; i++) kfree(host->ports[i]); + /* track host controller */ + host->host_id = atomic_inc_return(&host_print_id); + /* give ports names and add SCSI hosts */ for (i = 0; i < host->n_ports; i++) host->ports[i]->print_id = atomic_inc_return(&ata_print_id); diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index c04d393d20c1..61dca7a20a31 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -284,7 +284,7 @@ int ata_tport_add(struct device *parent, dev->parent = get_device(parent); dev->release = ata_tport_release; - dev_set_name(dev, "ata%d", ap->print_id); + dev_set_name(dev, "ata%d.%d", ap->host->host_id, ap->print_id); transport_setup_device(dev); error = device_add(dev); if (error) { diff --git a/include/linux/libata.h b/include/linux/libata.h index 649e5f86b5f0..7ae207eb29a0 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -546,6 +546,7 @@ struct ata_host { void *private_data; struct ata_port_operations *ops; unsigned long flags; + unsigned int host_id; /* user visible host ID */ struct mutex eh_mutex; struct task_struct *eh_owner; From 3d251a5b9e2f09edcf25bbffe1fa308d0f648bf1 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Thu, 3 Jan 2013 21:19:09 +0900 Subject: [PATCH 0870/4607] UBIFS: rename random32() to prandom_u32() Use more preferable function name which implies using a pseudo-random number generator. Signed-off-by: Akinobu Mita Signed-off-by: Artem Bityutskiy --- fs/ubifs/debug.c | 8 ++++---- fs/ubifs/lpt_commit.c | 14 +++++++------- fs/ubifs/tnc_commit.c | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c index 12817ffc7345..7f60e900edff 100644 --- a/fs/ubifs/debug.c +++ b/fs/ubifs/debug.c @@ -2459,7 +2459,7 @@ error_dump: static inline int chance(unsigned int n, unsigned int out_of) { - return !!((random32() % out_of) + 1 <= n); + return !!((prandom_u32() % out_of) + 1 <= n); } @@ -2477,13 +2477,13 @@ static int power_cut_emulated(struct ubifs_info *c, int lnum, int write) if (chance(1, 2)) { d->pc_delay = 1; /* Fail withing 1 minute */ - delay = random32() % 60000; + delay = prandom_u32() % 60000; d->pc_timeout = jiffies; d->pc_timeout += msecs_to_jiffies(delay); ubifs_warn("failing after %lums", delay); } else { d->pc_delay = 2; - delay = random32() % 10000; + delay = prandom_u32() % 10000; /* Fail within 10000 operations */ d->pc_cnt_max = delay; ubifs_warn("failing after %lu calls", delay); @@ -2563,7 +2563,7 @@ static int corrupt_data(const struct ubifs_info *c, const void *buf, unsigned int from, to, ffs = chance(1, 2); unsigned char *p = (void *)buf; - from = random32() % (len + 1); + from = prandom_u32() % (len + 1); /* Corruption may only span one max. write unit */ to = min(len, ALIGN(from, c->max_write_size)); diff --git a/fs/ubifs/lpt_commit.c b/fs/ubifs/lpt_commit.c index 9daaeef675dd..4b826abb1528 100644 --- a/fs/ubifs/lpt_commit.c +++ b/fs/ubifs/lpt_commit.c @@ -2007,28 +2007,28 @@ static int dbg_populate_lsave(struct ubifs_info *c) if (!dbg_is_chk_gen(c)) return 0; - if (random32() & 3) + if (prandom_u32() & 3) return 0; for (i = 0; i < c->lsave_cnt; i++) c->lsave[i] = c->main_first; list_for_each_entry(lprops, &c->empty_list, list) - c->lsave[random32() % c->lsave_cnt] = lprops->lnum; + c->lsave[prandom_u32() % c->lsave_cnt] = lprops->lnum; list_for_each_entry(lprops, &c->freeable_list, list) - c->lsave[random32() % c->lsave_cnt] = lprops->lnum; + c->lsave[prandom_u32() % c->lsave_cnt] = lprops->lnum; list_for_each_entry(lprops, &c->frdi_idx_list, list) - c->lsave[random32() % c->lsave_cnt] = lprops->lnum; + c->lsave[prandom_u32() % c->lsave_cnt] = lprops->lnum; heap = &c->lpt_heap[LPROPS_DIRTY_IDX - 1]; for (i = 0; i < heap->cnt; i++) - c->lsave[random32() % c->lsave_cnt] = heap->arr[i]->lnum; + c->lsave[prandom_u32() % c->lsave_cnt] = heap->arr[i]->lnum; heap = &c->lpt_heap[LPROPS_DIRTY - 1]; for (i = 0; i < heap->cnt; i++) - c->lsave[random32() % c->lsave_cnt] = heap->arr[i]->lnum; + c->lsave[prandom_u32() % c->lsave_cnt] = heap->arr[i]->lnum; heap = &c->lpt_heap[LPROPS_FREE - 1]; for (i = 0; i < heap->cnt; i++) - c->lsave[random32() % c->lsave_cnt] = heap->arr[i]->lnum; + c->lsave[prandom_u32() % c->lsave_cnt] = heap->arr[i]->lnum; return 1; } diff --git a/fs/ubifs/tnc_commit.c b/fs/ubifs/tnc_commit.c index 523bbad69c0c..52a6559275c4 100644 --- a/fs/ubifs/tnc_commit.c +++ b/fs/ubifs/tnc_commit.c @@ -683,7 +683,7 @@ static int alloc_idx_lebs(struct ubifs_info *c, int cnt) c->ilebs[c->ileb_cnt++] = lnum; dbg_cmt("LEB %d", lnum); } - if (dbg_is_chk_index(c) && !(random32() & 7)) + if (dbg_is_chk_index(c) && !(prandom_u32() & 7)) return -ENOSPC; return 0; } From 42f6e3da974dc8ad81775110c8d06835acdf375e Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:53:18 +0100 Subject: [PATCH 0871/4607] drm/vmwgfx: always use ttm_bo_is_reserved Signed-off-by: Maarten Lankhorst Reviewed-by: Thomas Hellstrom --- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index e01a17b407b2..16556170fb32 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -959,13 +959,13 @@ void vmw_resource_unreserve(struct vmw_resource *res, if (new_backup && new_backup != res->backup) { if (res->backup) { - BUG_ON(atomic_read(&res->backup->base.reserved) == 0); + BUG_ON(!ttm_bo_is_reserved(&res->backup->base)); list_del_init(&res->mob_head); vmw_dmabuf_unreference(&res->backup); } res->backup = vmw_dmabuf_reference(new_backup); - BUG_ON(atomic_read(&new_backup->base.reserved) == 0); + BUG_ON(!ttm_bo_is_reserved(&new_backup->base)); list_add_tail(&res->mob_head, &new_backup->res_list); } if (new_backup) From 979ee290ff0a543352243145dc3654af5a856ab8 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:54:22 +0100 Subject: [PATCH 0872/4607] drm/nouveau: increase reservation sequence every retry This is temporary until the fence framework can be used. With the lru/reservation atomicity removal it is possible to see your old sequence number and the buffer being reserved, leading to erroneously reporting -EDEADLK. Workaround it by bumping the sequence number every retry. Signed-off-by: Maarten Lankhorst --- drivers/gpu/drm/nouveau/nouveau_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 8bf695c52f95..9fcfcb2a020c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -321,8 +321,8 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, int trycnt = 0; int ret, i; - sequence = atomic_add_return(1, &drm->ttm.validate_sequence); retry: + sequence = atomic_add_return(1, &drm->ttm.validate_sequence); if (++trycnt > 100000) { NV_ERROR(drm, "%s failed and gave up.\n", __func__); return -EINVAL; From 63d0a4195560362e2e00a3ad38fc331d34e1da9b Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:56:37 +0100 Subject: [PATCH 0873/4607] drm/ttm: remove lru_lock around ttm_bo_reserve There should no longer be assumptions that reserve will always succeed with the lru lock held, so we can safely break the whole atomic reserve/lru thing. As a bonus this fixes most lockdep annotations for reservations. Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/ttm/ttm_bo.c | 54 +++++++++++++++++--------- drivers/gpu/drm/ttm/ttm_execbuf_util.c | 2 +- include/drm/ttm/ttm_bo_driver.h | 19 ++------- 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 33d20be87db5..e8e4814b1295 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -213,14 +213,13 @@ int ttm_bo_del_from_lru(struct ttm_buffer_object *bo) return put_count; } -int ttm_bo_reserve_locked(struct ttm_buffer_object *bo, +int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence) { - struct ttm_bo_global *glob = bo->glob; int ret; - while (unlikely(atomic_read(&bo->reserved) != 0)) { + while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) { /** * Deadlock avoidance for multi-bo reserving. */ @@ -241,26 +240,36 @@ int ttm_bo_reserve_locked(struct ttm_buffer_object *bo, if (no_wait) return -EBUSY; - spin_unlock(&glob->lru_lock); ret = ttm_bo_wait_unreserved(bo, interruptible); - spin_lock(&glob->lru_lock); if (unlikely(ret)) return ret; } - atomic_set(&bo->reserved, 1); if (use_sequence) { + bool wake_up = false; /** * Wake up waiters that may need to recheck for deadlock, * if we decreased the sequence number. */ if (unlikely((bo->val_seq - sequence < (1 << 31)) || !bo->seq_valid)) - wake_up_all(&bo->event_queue); + wake_up = true; + /* + * In the worst case with memory ordering these values can be + * seen in the wrong order. However since we call wake_up_all + * in that case, this will hopefully not pose a problem, + * and the worst case would only cause someone to accidentally + * hit -EAGAIN in ttm_bo_reserve when they see old value of + * val_seq. However this would only happen if seq_valid was + * written before val_seq was, and just means some slightly + * increased cpu usage + */ bo->val_seq = sequence; bo->seq_valid = true; + if (wake_up) + wake_up_all(&bo->event_queue); } else { bo->seq_valid = false; } @@ -289,14 +298,14 @@ int ttm_bo_reserve(struct ttm_buffer_object *bo, int put_count = 0; int ret; - spin_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, interruptible, no_wait, use_sequence, - sequence); - if (likely(ret == 0)) + ret = ttm_bo_reserve_nolru(bo, interruptible, no_wait, use_sequence, + sequence); + if (likely(ret == 0)) { + spin_lock(&glob->lru_lock); put_count = ttm_bo_del_from_lru(bo); - spin_unlock(&glob->lru_lock); - - ttm_bo_list_ref_sub(bo, put_count, true); + spin_unlock(&glob->lru_lock); + ttm_bo_list_ref_sub(bo, put_count, true); + } return ret; } @@ -510,7 +519,7 @@ static void ttm_bo_cleanup_refs_or_queue(struct ttm_buffer_object *bo) int ret; spin_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); spin_lock(&bdev->fence_lock); (void) ttm_bo_wait(bo, false, false, true); @@ -603,7 +612,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, return ret; spin_lock(&glob->lru_lock); - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); /* * We raced, and lost, someone else holds the reservation now, @@ -667,7 +676,14 @@ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all) kref_get(&nentry->list_kref); } - ret = ttm_bo_reserve_locked(entry, false, !remove_all, false, 0); + ret = ttm_bo_reserve_nolru(entry, false, true, false, 0); + if (remove_all && ret) { + spin_unlock(&glob->lru_lock); + ret = ttm_bo_reserve_nolru(entry, false, false, + false, 0); + spin_lock(&glob->lru_lock); + } + if (!ret) ret = ttm_bo_cleanup_refs_and_unlock(entry, false, !remove_all); @@ -815,7 +831,7 @@ static int ttm_mem_evict_first(struct ttm_bo_device *bdev, spin_lock(&glob->lru_lock); list_for_each_entry(bo, &man->lru, lru) { - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); if (!ret) break; } @@ -1796,7 +1812,7 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink) spin_lock(&glob->lru_lock); list_for_each_entry(bo, &glob->swap_lru, swap) { - ret = ttm_bo_reserve_locked(bo, false, true, false, 0); + ret = ttm_bo_reserve_nolru(bo, false, true, false, 0); if (!ret) break; } diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index cd9e4523dc56..bd37b5cb8553 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -153,7 +153,7 @@ retry: struct ttm_buffer_object *bo = entry->bo; retry_this_bo: - ret = ttm_bo_reserve_locked(bo, true, true, true, val_seq); + ret = ttm_bo_reserve_nolru(bo, true, true, true, val_seq); switch (ret) { case 0: break; diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index e3a43a47d78c..6fff43222e20 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -790,16 +790,7 @@ extern void ttm_mem_io_unlock(struct ttm_mem_type_manager *man); * to make room for a buffer already reserved. (Buffers are reserved before * they are evicted). The following algorithm prevents such deadlocks from * occurring: - * 1) Buffers are reserved with the lru spinlock held. Upon successful - * reservation they are removed from the lru list. This stops a reserved buffer - * from being evicted. However the lru spinlock is released between the time - * a buffer is selected for eviction and the time it is reserved. - * Therefore a check is made when a buffer is reserved for eviction, that it - * is still the first buffer in the lru list, before it is removed from the - * list. @check_lru == 1 forces this check. If it fails, the function returns - * -EINVAL, and the caller should then choose a new buffer to evict and repeat - * the procedure. - * 2) Processes attempting to reserve multiple buffers other than for eviction, + * Processes attempting to reserve multiple buffers other than for eviction, * (typically execbuf), should first obtain a unique 32-bit * validation sequence number, * and call this function with @use_sequence == 1 and @sequence == the unique @@ -832,7 +823,7 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo, /** - * ttm_bo_reserve_locked: + * ttm_bo_reserve_nolru: * * @bo: A pointer to a struct ttm_buffer_object. * @interruptible: Sleep interruptible if waiting. @@ -840,9 +831,7 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo, * @use_sequence: If @bo is already reserved, Only sleep waiting for * it to become unreserved if @sequence < (@bo)->sequence. * - * Must be called with struct ttm_bo_global::lru_lock held, - * and will not remove reserved buffers from the lru lists. - * The function may release the LRU spinlock if it needs to sleep. + * Will not remove reserved buffers from the lru lists. * Otherwise identical to ttm_bo_reserve. * * Returns: @@ -855,7 +844,7 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo, * -EDEADLK: Bo already reserved using @sequence. This error code will only * be returned if @use_sequence is set to true. */ -extern int ttm_bo_reserve_locked(struct ttm_buffer_object *bo, +extern int ttm_bo_reserve_nolru(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence); From 7a1863084c9d90ce4b67d645bf9b0f1612e68f62 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:56:48 +0100 Subject: [PATCH 0874/4607] drm/ttm: cleanup ttm_eu_reserve_buffers handling With the lru lock no longer required for protecting reservations we can just do a ttm_bo_reserve_nolru on -EBUSY, and handle all errors in a single path. Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/ttm/ttm_execbuf_util.c | 53 ++++++++++---------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index bd37b5cb8553..c7d323657798 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -82,22 +82,6 @@ static void ttm_eu_list_ref_sub(struct list_head *list) } } -static int ttm_eu_wait_unreserved_locked(struct list_head *list, - struct ttm_buffer_object *bo) -{ - struct ttm_bo_global *glob = bo->glob; - int ret; - - ttm_eu_del_from_lru_locked(list); - spin_unlock(&glob->lru_lock); - ret = ttm_bo_wait_unreserved(bo, true); - spin_lock(&glob->lru_lock); - if (unlikely(ret != 0)) - ttm_eu_backoff_reservation_locked(list); - return ret; -} - - void ttm_eu_backoff_reservation(struct list_head *list) { struct ttm_validate_buffer *entry; @@ -152,19 +136,23 @@ retry: list_for_each_entry(entry, list, head) { struct ttm_buffer_object *bo = entry->bo; -retry_this_bo: ret = ttm_bo_reserve_nolru(bo, true, true, true, val_seq); switch (ret) { case 0: break; case -EBUSY: - ret = ttm_eu_wait_unreserved_locked(list, bo); - if (unlikely(ret != 0)) { - spin_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return ret; - } - goto retry_this_bo; + ttm_eu_del_from_lru_locked(list); + spin_unlock(&glob->lru_lock); + ret = ttm_bo_reserve_nolru(bo, true, false, + true, val_seq); + spin_lock(&glob->lru_lock); + if (!ret) + break; + + if (unlikely(ret != -EAGAIN)) + goto err; + + /* fallthrough */ case -EAGAIN: ttm_eu_backoff_reservation_locked(list); spin_unlock(&glob->lru_lock); @@ -174,18 +162,13 @@ retry_this_bo: return ret; goto retry; default: - ttm_eu_backoff_reservation_locked(list); - spin_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return ret; + goto err; } entry->reserved = true; if (unlikely(atomic_read(&bo->cpu_writers) > 0)) { - ttm_eu_backoff_reservation_locked(list); - spin_unlock(&glob->lru_lock); - ttm_eu_list_ref_sub(list); - return -EBUSY; + ret = -EBUSY; + goto err; } } @@ -194,6 +177,12 @@ retry_this_bo: ttm_eu_list_ref_sub(list); return 0; + +err: + ttm_eu_backoff_reservation_locked(list); + spin_unlock(&glob->lru_lock); + ttm_eu_list_ref_sub(list); + return ret; } EXPORT_SYMBOL(ttm_eu_reserve_buffers); From 5e45d7dfd74100d622f9cdc70bfd1f9fae1671de Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:57:05 +0100 Subject: [PATCH 0875/4607] drm/ttm: add ttm_bo_reserve_slowpath Instead of dropping everything, waiting for the bo to be unreserved and trying over, a better strategy would be to do a blocking wait. This can be mapped a lot better to a mutex_lock-like call. Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/ttm/ttm_bo.c | 47 +++++++++++++++++++++++++++++++++ include/drm/ttm/ttm_bo_driver.h | 30 +++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index e8e4814b1295..4dd6f9e77a7d 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -310,6 +310,53 @@ int ttm_bo_reserve(struct ttm_buffer_object *bo, return ret; } +int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo, + bool interruptible, uint32_t sequence) +{ + bool wake_up = false; + int ret; + + while (unlikely(atomic_xchg(&bo->reserved, 1) != 0)) { + WARN_ON(bo->seq_valid && sequence == bo->val_seq); + + ret = ttm_bo_wait_unreserved(bo, interruptible); + + if (unlikely(ret)) + return ret; + } + + if ((bo->val_seq - sequence < (1 << 31)) || !bo->seq_valid) + wake_up = true; + + /** + * Wake up waiters that may need to recheck for deadlock, + * if we decreased the sequence number. + */ + bo->val_seq = sequence; + bo->seq_valid = true; + if (wake_up) + wake_up_all(&bo->event_queue); + + return 0; +} + +int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, + bool interruptible, uint32_t sequence) +{ + struct ttm_bo_global *glob = bo->glob; + int put_count, ret; + + ret = ttm_bo_reserve_slowpath_nolru(bo, interruptible, sequence); + if (likely(!ret)) { + spin_lock(&glob->lru_lock); + put_count = ttm_bo_del_from_lru(bo); + spin_unlock(&glob->lru_lock); + ttm_bo_list_ref_sub(bo, put_count, true); + } + return ret; +} +EXPORT_SYMBOL(ttm_bo_reserve_slowpath); + void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo) { ttm_bo_add_to_lru(bo); diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 6fff43222e20..5af71af6bf88 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -821,6 +821,36 @@ extern int ttm_bo_reserve(struct ttm_buffer_object *bo, bool interruptible, bool no_wait, bool use_sequence, uint32_t sequence); +/** + * ttm_bo_reserve_slowpath_nolru: + * @bo: A pointer to a struct ttm_buffer_object. + * @interruptible: Sleep interruptible if waiting. + * @sequence: Set (@bo)->sequence to this value after lock + * + * This is called after ttm_bo_reserve returns -EAGAIN and we backed off + * from all our other reservations. Because there are no other reservations + * held by us, this function cannot deadlock any more. + * + * Will not remove reserved buffers from the lru lists. + * Otherwise identical to ttm_bo_reserve_slowpath. + */ +extern int ttm_bo_reserve_slowpath_nolru(struct ttm_buffer_object *bo, + bool interruptible, + uint32_t sequence); + + +/** + * ttm_bo_reserve_slowpath: + * @bo: A pointer to a struct ttm_buffer_object. + * @interruptible: Sleep interruptible if waiting. + * @sequence: Set (@bo)->sequence to this value after lock + * + * This is called after ttm_bo_reserve returns -EAGAIN and we backed off + * from all our other reservations. Because there are no other reservations + * held by us, this function cannot deadlock any more. + */ +extern int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, + bool interruptible, uint32_t sequence); /** * ttm_bo_reserve_nolru: From f2d476a110bc24fde008698ae9018c99e803e25c Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:57:10 +0100 Subject: [PATCH 0876/4607] drm/ttm: use ttm_bo_reserve_slowpath_nolru in ttm_eu_reserve_buffers, v2 This requires re-use of the seqno, which increases fairness slightly. Instead of spinning with a new seqno every time we keep the current one, but still drop all other reservations we hold. Only when we succeed, we try to get back our other reservations again. This should increase fairness slightly as well. Changes since v1: - Increase val_seq before calling ttm_bo_reserve_slowpath_nolru and retrying to take all entries to prevent a race. Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/ttm/ttm_execbuf_util.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index c7d323657798..7b90def15674 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -129,13 +129,17 @@ int ttm_eu_reserve_buffers(struct list_head *list) entry = list_first_entry(list, struct ttm_validate_buffer, head); glob = entry->bo->glob; -retry: spin_lock(&glob->lru_lock); val_seq = entry->bo->bdev->val_seq++; +retry: list_for_each_entry(entry, list, head) { struct ttm_buffer_object *bo = entry->bo; + /* already slowpath reserved? */ + if (entry->reserved) + continue; + ret = ttm_bo_reserve_nolru(bo, true, true, true, val_seq); switch (ret) { case 0: @@ -155,11 +159,26 @@ retry: /* fallthrough */ case -EAGAIN: ttm_eu_backoff_reservation_locked(list); + + /* + * temporarily increase sequence number every retry, + * to prevent us from seeing our old reservation + * sequence when someone else reserved the buffer, + * but hasn't updated the seq_valid/seqno members yet. + */ + val_seq = entry->bo->bdev->val_seq++; + spin_unlock(&glob->lru_lock); ttm_eu_list_ref_sub(list); - ret = ttm_bo_wait_unreserved(bo, true); + ret = ttm_bo_reserve_slowpath_nolru(bo, true, val_seq); if (unlikely(ret != 0)) return ret; + spin_lock(&glob->lru_lock); + entry->reserved = true; + if (unlikely(atomic_read(&bo->cpu_writers) > 0)) { + ret = -EBUSY; + goto err; + } goto retry; default: goto err; From c354c893dd57aac11f5d96ada7c47a20fe090a6e Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:57:20 +0100 Subject: [PATCH 0877/4607] drm/nouveau: use ttm_bo_reserve_slowpath in validate_init, v2 Similar rationale to the identical commit in drm/ttm. Instead of only waiting for unreservation, we make sure we actually own the reservation, then retry to get the rest. Changes since v1: - Increase the seqno before calling ttm_bo_reserve_slowpath Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/nouveau/nouveau_gem.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 9fcfcb2a020c..7fa6882c2942 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -320,9 +320,10 @@ validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, uint32_t sequence; int trycnt = 0; int ret, i; + struct nouveau_bo *res_bo = NULL; -retry: sequence = atomic_add_return(1, &drm->ttm.validate_sequence); +retry: if (++trycnt > 100000) { NV_ERROR(drm, "%s failed and gave up.\n", __func__); return -EINVAL; @@ -340,6 +341,11 @@ retry: return -ENOENT; } nvbo = gem->driver_private; + if (nvbo == res_bo) { + res_bo = NULL; + drm_gem_object_unreference_unlocked(gem); + continue; + } if (nvbo->reserved_by && nvbo->reserved_by == file_priv) { NV_ERROR(drm, "multiple instances of buffer %d on " @@ -352,15 +358,19 @@ retry: ret = ttm_bo_reserve(&nvbo->bo, true, false, true, sequence); if (ret) { validate_fini(op, NULL); - if (unlikely(ret == -EAGAIN)) - ret = ttm_bo_wait_unreserved(&nvbo->bo, true); - drm_gem_object_unreference_unlocked(gem); + if (unlikely(ret == -EAGAIN)) { + sequence = atomic_add_return(1, &drm->ttm.validate_sequence); + ret = ttm_bo_reserve_slowpath(&nvbo->bo, true, + sequence); + if (!ret) + res_bo = nvbo; + } if (unlikely(ret)) { + drm_gem_object_unreference_unlocked(gem); if (ret != -ERESTARTSYS) NV_ERROR(drm, "fail reserve\n"); return ret; } - goto retry; } b->user_priv = (uint64_t)(unsigned long)nvbo; @@ -382,6 +392,8 @@ retry: validate_fini(op, NULL); return -EINVAL; } + if (nvbo == res_bo) + goto retry; } return 0; From cc4c0c4de3c775be22072ec3251f2e581b63d9a0 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 15 Jan 2013 14:57:28 +0100 Subject: [PATCH 0878/4607] drm/ttm: unexport ttm_bo_wait_unreserved All legitimate users of this function outside ttm_bo.c are gone, now it's only an implementation detail. Signed-off-by: Maarten Lankhorst Reviewed-by: Jerome Glisse --- drivers/gpu/drm/ttm/ttm_bo.c | 4 ++-- include/drm/ttm/ttm_bo_driver.h | 12 ------------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 4dd6f9e77a7d..4df47f72214a 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -158,7 +158,8 @@ static void ttm_bo_release_list(struct kref *list_kref) ttm_mem_global_free(bdev->glob->mem_glob, acc_size); } -int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible) +static int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, + bool interruptible) { if (interruptible) { return wait_event_interruptible(bo->event_queue, @@ -168,7 +169,6 @@ int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible) return 0; } } -EXPORT_SYMBOL(ttm_bo_wait_unreserved); void ttm_bo_add_to_lru(struct ttm_buffer_object *bo) { diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 5af71af6bf88..0fbd046e7c93 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -898,18 +898,6 @@ extern void ttm_bo_unreserve(struct ttm_buffer_object *bo); */ extern void ttm_bo_unreserve_locked(struct ttm_buffer_object *bo); -/** - * ttm_bo_wait_unreserved - * - * @bo: A pointer to a struct ttm_buffer_object. - * - * Wait for a struct ttm_buffer_object to become unreserved. - * This is typically used in the execbuf code to relax cpu-usage when - * a potential deadlock condition backoff. - */ -extern int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, - bool interruptible); - /* * ttm_bo_util.c */ From 1715a826a5b72d4fb882504d0babcea9aec8a0db Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 10 Jan 2013 20:29:09 +0000 Subject: [PATCH 0879/4607] powerpc: Add DSCR support to ptrace The DSCR (aka Data Stream Control Register) is supported on some server PowerPC chips and allow some control over the prefetch of data streams. The kernel already supports DSCR value per thread but there is also a need in a ability to change it from an external process for the specific pid. The patch adds new register index PT_DSCR (index=44) which can be set/get by: ptrace(PTRACE_POKEUSER, traced_process, PT_DSCR << 3, dscr); dscr = ptrace(PTRACE_PEEKUSER, traced_process, PT_DSCR << 3, NULL); The patch does not increase PT_REGS_COUNT as the pt_regs struct has not been changed. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/uapi/asm/ptrace.h | 1 + arch/powerpc/kernel/ptrace.c | 29 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/arch/powerpc/include/uapi/asm/ptrace.h b/arch/powerpc/include/uapi/asm/ptrace.h index ee67a2bc91bb..5a4863c76b5f 100644 --- a/arch/powerpc/include/uapi/asm/ptrace.h +++ b/arch/powerpc/include/uapi/asm/ptrace.h @@ -108,6 +108,7 @@ struct pt_regs { #define PT_DAR 41 #define PT_DSISR 42 #define PT_RESULT 43 +#define PT_DSCR 44 #define PT_REGS_COUNT 44 #define PT_FPR0 48 /* each FP reg occupies 2 slots in this space */ diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index d4afcccf1238..245c1b6a0858 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -179,6 +179,30 @@ static int set_user_msr(struct task_struct *task, unsigned long msr) return 0; } +#ifdef CONFIG_PPC64 +static unsigned long get_user_dscr(struct task_struct *task) +{ + return task->thread.dscr; +} + +static int set_user_dscr(struct task_struct *task, unsigned long dscr) +{ + task->thread.dscr = dscr; + task->thread.dscr_inherit = 1; + return 0; +} +#else +static unsigned long get_user_dscr(struct task_struct *task) +{ + return -EIO; +} + +static int set_user_dscr(struct task_struct *task, unsigned long dscr) +{ + return -EIO; +} +#endif + /* * We prevent mucking around with the reserved area of trap * which are used internally by the kernel. @@ -200,6 +224,9 @@ unsigned long ptrace_get_reg(struct task_struct *task, int regno) if (regno == PT_MSR) return get_user_msr(task); + if (regno == PT_DSCR) + return get_user_dscr(task); + if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) return ((unsigned long *)task->thread.regs)[regno]; @@ -218,6 +245,8 @@ int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data) return set_user_msr(task, data); if (regno == PT_TRAP) return set_user_trap(task, data); + if (regno == PT_DSCR) + return set_user_dscr(task, data); if (regno <= PT_MAX_PUT_REG) { ((unsigned long *)task->thread.regs)[regno] = data; From fa5924640341f714af7757194d5da7ecd66abe2b Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 10 Jan 2013 16:11:11 +0000 Subject: [PATCH 0880/4607] powerpc: Fix typo in breakpoint kgdb code. Currently we are getting: arch/powerpc/kernel/kgdb.c: In function 'kgdb_arch_exit': arch/powerpc/kernel/kgdb.c:492:2: error: '__debugger_breakx_match' undeclared (first use in this function) arch/powerpc/kernel/kgdb.c:492:2: note: each undeclared identifier is reported only once for each function it appears in Fix the typo. Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/kgdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/kgdb.c b/arch/powerpc/kernel/kgdb.c index a05f0e4a9d38..8747447045b6 100644 --- a/arch/powerpc/kernel/kgdb.c +++ b/arch/powerpc/kernel/kgdb.c @@ -489,6 +489,6 @@ void kgdb_arch_exit(void) __debugger_bpt = old__debugger_bpt; __debugger_sstep = old__debugger_sstep; __debugger_iabr_match = old__debugger_iabr_match; - __debugger_breakx_match = old__debugger_break_match; + __debugger_break_match = old__debugger_break_match; __debugger_fault_handler = old__debugger_fault_handler; } From b9818c3312da66f4b83a4a2e8650628be1237cb5 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 10 Jan 2013 14:25:34 +0000 Subject: [PATCH 0881/4607] powerpc: Rename set_break to avoid naming conflict With allmodconfig we are getting: drivers/tty/synclink_gt.c:160:12: error: conflicting types for 'set_break' arch/powerpc/include/asm/debug.h:49:5: note: previous declaration of 'set_break' was here drivers/tty/synclinkmp.c:526:12: error: conflicting types for 'set_break' arch/powerpc/include/asm/debug.h:49:5: note: previous declaration of 'set_break' was here This renames set_break to set_breakpoint to avoid this naming conflict Signed-off-by: Michael Neuling Reported-by: Fengguang Wu Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/debug.h | 2 +- arch/powerpc/include/asm/hw_breakpoint.h | 2 +- arch/powerpc/kernel/hw_breakpoint.c | 8 ++++---- arch/powerpc/kernel/process.c | 6 +++--- arch/powerpc/kernel/signal.c | 2 +- arch/powerpc/xmon/xmon.c | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/include/asm/debug.h b/arch/powerpc/include/asm/debug.h index 8d85ffb03e61..d2516308ed1e 100644 --- a/arch/powerpc/include/asm/debug.h +++ b/arch/powerpc/include/asm/debug.h @@ -46,7 +46,7 @@ static inline int debugger_break_match(struct pt_regs *regs) { return 0; } static inline int debugger_fault_handler(struct pt_regs *regs) { return 0; } #endif -int set_break(struct arch_hw_breakpoint *brk); +int set_breakpoint(struct arch_hw_breakpoint *brk); #ifdef CONFIG_PPC_ADV_DEBUG_REGS extern void do_send_trap(struct pt_regs *regs, unsigned long address, unsigned long error_code, int signal_code, int brkpt); diff --git a/arch/powerpc/include/asm/hw_breakpoint.h b/arch/powerpc/include/asm/hw_breakpoint.h index 2c91faf981db..96437e5014e1 100644 --- a/arch/powerpc/include/asm/hw_breakpoint.h +++ b/arch/powerpc/include/asm/hw_breakpoint.h @@ -81,7 +81,7 @@ static inline void hw_breakpoint_disable(void) brk.address = 0; brk.type = 0; brk.len = 0; - set_break(&brk); + set_breakpoint(&brk); } extern void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs); diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c index c7483d09fdd0..2a3e8dd547ec 100644 --- a/arch/powerpc/kernel/hw_breakpoint.c +++ b/arch/powerpc/kernel/hw_breakpoint.c @@ -73,7 +73,7 @@ int arch_install_hw_breakpoint(struct perf_event *bp) * If so, DABR will be populated in single_step_dabr_instruction(). */ if (current->thread.last_hit_ubp != bp) - set_break(info); + set_breakpoint(info); return 0; } @@ -191,7 +191,7 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs) info = counter_arch_bp(tsk->thread.last_hit_ubp); regs->msr &= ~MSR_SE; - set_break(info); + set_breakpoint(info); tsk->thread.last_hit_ubp = NULL; } @@ -276,7 +276,7 @@ int __kprobes hw_breakpoint_handler(struct die_args *args) if (!(info->type & HW_BRK_TYPE_EXTRANEOUS_IRQ)) perf_bp_event(bp, regs); - set_break(info); + set_breakpoint(info); out: rcu_read_unlock(); return rc; @@ -308,7 +308,7 @@ int __kprobes single_step_dabr_instruction(struct die_args *args) if (!(info->type & HW_BRK_TYPE_EXTRANEOUS_IRQ)) perf_bp_event(bp, regs); - set_break(info); + set_breakpoint(info); current->thread.last_hit_ubp = NULL; /* diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 8d56452e1dbd..99550d3615c4 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -366,7 +366,7 @@ static void set_debug_reg_defaults(struct thread_struct *thread) { thread->hw_brk.address = 0; thread->hw_brk.type = 0; - set_break(&thread->hw_brk); + set_breakpoint(&thread->hw_brk); } #endif /* !CONFIG_HAVE_HW_BREAKPOINT */ #endif /* CONFIG_PPC_ADV_DEBUG_REGS */ @@ -427,7 +427,7 @@ static inline int set_dawr(struct arch_hw_breakpoint *brk) return 0; } -int set_break(struct arch_hw_breakpoint *brk) +int set_breakpoint(struct arch_hw_breakpoint *brk) { __get_cpu_var(current_brk) = *brk; @@ -538,7 +538,7 @@ struct task_struct *__switch_to(struct task_struct *prev, */ #ifndef CONFIG_HAVE_HW_BREAKPOINT if (unlikely(hw_brk_match(&__get_cpu_var(current_brk), &new->thread.hw_brk))) - set_break(&new->thread.hw_brk); + set_breakpoint(&new->thread.hw_brk); #endif /* CONFIG_HAVE_HW_BREAKPOINT */ #endif diff --git a/arch/powerpc/kernel/signal.c b/arch/powerpc/kernel/signal.c index 1f26956d3913..3003d890e9ef 100644 --- a/arch/powerpc/kernel/signal.c +++ b/arch/powerpc/kernel/signal.c @@ -132,7 +132,7 @@ static int do_signal(struct pt_regs *regs) */ if (current->thread.hw_brk.address && current->thread.hw_brk.type) - set_break(¤t->thread.hw_brk); + set_breakpoint(¤t->thread.hw_brk); #endif /* Re-enable the breakpoints for the signal stack */ thread_change_pc(current, regs); diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 529c1ed7f59f..13f85defabed 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -747,7 +747,7 @@ static void insert_cpu_bpts(void) brk.address = dabr.address; brk.type = (dabr.enabled & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL; brk.len = 8; - set_break(&brk); + set_breakpoint(&brk); } if (iabr && cpu_has_feature(CPU_FTR_IABR)) mtspr(SPRN_IABR, iabr->address From 81c6fdb65300bd9475788d4d4912de67cfb70345 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 10 Dec 2012 17:15:38 +0100 Subject: [PATCH 0882/4607] powerpc/mpc5121: add common .dtsi and use it in mpc5121ads.dts Provide common mpc5121.dtsi file for mpc5121 SoC and modify mpc5121ads.dts to use mpc5121.dtsi. Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/mpc5121.dtsi | 410 +++++++++++++++++++++++++++ arch/powerpc/boot/dts/mpc5121ads.dts | 319 +++------------------ 2 files changed, 449 insertions(+), 280 deletions(-) create mode 100644 arch/powerpc/boot/dts/mpc5121.dtsi diff --git a/arch/powerpc/boot/dts/mpc5121.dtsi b/arch/powerpc/boot/dts/mpc5121.dtsi new file mode 100644 index 000000000000..723e292b6b4e --- /dev/null +++ b/arch/powerpc/boot/dts/mpc5121.dtsi @@ -0,0 +1,410 @@ +/* + * base MPC5121 Device Tree Source + * + * Copyright 2007-2008 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/dts-v1/; + +/ { + model = "mpc5121"; + compatible = "fsl,mpc5121"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&ipic>; + + aliases { + ethernet0 = ð0; + pci = &pci; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,5121@0 { + device_type = "cpu"; + reg = <0>; + d-cache-line-size = <0x20>; /* 32 bytes */ + i-cache-line-size = <0x20>; /* 32 bytes */ + d-cache-size = <0x8000>; /* L1, 32K */ + i-cache-size = <0x8000>; /* L1, 32K */ + timebase-frequency = <49500000>;/* 49.5 MHz (csb/4) */ + bus-frequency = <198000000>; /* 198 MHz csb bus */ + clock-frequency = <396000000>; /* 396 MHz ppc core */ + }; + }; + + memory { + device_type = "memory"; + reg = <0x00000000 0x10000000>; /* 256MB at 0 */ + }; + + mbx@20000000 { + compatible = "fsl,mpc5121-mbx"; + reg = <0x20000000 0x4000>; + interrupts = <66 0x8>; + }; + + sram@30000000 { + compatible = "fsl,mpc5121-sram"; + reg = <0x30000000 0x20000>; /* 128K at 0x30000000 */ + }; + + nfc@40000000 { + compatible = "fsl,mpc5121-nfc"; + reg = <0x40000000 0x100000>; /* 1M at 0x40000000 */ + interrupts = <6 8>; + #address-cells = <1>; + #size-cells = <1>; + }; + + localbus@80000020 { + compatible = "fsl,mpc5121-localbus"; + #address-cells = <2>; + #size-cells = <1>; + reg = <0x80000020 0x40>; + interrupts = <7 0x8>; + ranges = <0x0 0x0 0xfc000000 0x04000000>; + }; + + soc@80000000 { + compatible = "fsl,mpc5121-immr"; + #address-cells = <1>; + #size-cells = <1>; + #interrupt-cells = <2>; + ranges = <0x0 0x80000000 0x400000>; + reg = <0x80000000 0x400000>; + bus-frequency = <66000000>; /* 66 MHz ips bus */ + + + /* + * IPIC + * interrupts cell = + * sense values match linux IORESOURCE_IRQ_* defines: + * sense == 8: Level, low assertion + * sense == 2: Edge, high-to-low change + */ + ipic: interrupt-controller@c00 { + compatible = "fsl,mpc5121-ipic", "fsl,ipic"; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + reg = <0xc00 0x100>; + }; + + /* Watchdog timer */ + wdt@900 { + compatible = "fsl,mpc5121-wdt"; + reg = <0x900 0x100>; + }; + + /* Real time clock */ + rtc@a00 { + compatible = "fsl,mpc5121-rtc"; + reg = <0xa00 0x100>; + interrupts = <79 0x8 80 0x8>; + }; + + /* Reset module */ + reset@e00 { + compatible = "fsl,mpc5121-reset"; + reg = <0xe00 0x100>; + }; + + /* Clock control */ + clock@f00 { + compatible = "fsl,mpc5121-clock"; + reg = <0xf00 0x100>; + }; + + /* Power Management Controller */ + pmc@1000{ + compatible = "fsl,mpc5121-pmc"; + reg = <0x1000 0x100>; + interrupts = <83 0x8>; + }; + + gpio@1100 { + compatible = "fsl,mpc5121-gpio"; + reg = <0x1100 0x100>; + interrupts = <78 0x8>; + }; + + can@1300 { + compatible = "fsl,mpc5121-mscan"; + reg = <0x1300 0x80>; + interrupts = <12 0x8>; + }; + + can@1380 { + compatible = "fsl,mpc5121-mscan"; + reg = <0x1380 0x80>; + interrupts = <13 0x8>; + }; + + sdhc@1500 { + compatible = "fsl,mpc5121-sdhc"; + reg = <0x1500 0x100>; + interrupts = <8 0x8>; + }; + + i2c@1700 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mpc5121-i2c", "fsl-i2c"; + reg = <0x1700 0x20>; + interrupts = <9 0x8>; + }; + + i2c@1720 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mpc5121-i2c", "fsl-i2c"; + reg = <0x1720 0x20>; + interrupts = <10 0x8>; + }; + + i2c@1740 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mpc5121-i2c", "fsl-i2c"; + reg = <0x1740 0x20>; + interrupts = <11 0x8>; + }; + + i2ccontrol@1760 { + compatible = "fsl,mpc5121-i2c-ctrl"; + reg = <0x1760 0x8>; + }; + + axe@2000 { + compatible = "fsl,mpc5121-axe"; + reg = <0x2000 0x100>; + interrupts = <42 0x8>; + }; + + display@2100 { + compatible = "fsl,mpc5121-diu"; + reg = <0x2100 0x100>; + interrupts = <64 0x8>; + }; + + can@2300 { + compatible = "fsl,mpc5121-mscan"; + reg = <0x2300 0x80>; + interrupts = <90 0x8>; + }; + + can@2380 { + compatible = "fsl,mpc5121-mscan"; + reg = <0x2380 0x80>; + interrupts = <91 0x8>; + }; + + viu@2400 { + compatible = "fsl,mpc5121-viu"; + reg = <0x2400 0x400>; + interrupts = <67 0x8>; + }; + + mdio@2800 { + compatible = "fsl,mpc5121-fec-mdio"; + reg = <0x2800 0x800>; + #address-cells = <1>; + #size-cells = <0>; + }; + + eth0: ethernet@2800 { + device_type = "network"; + compatible = "fsl,mpc5121-fec"; + reg = <0x2800 0x800>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <4 0x8>; + }; + + /* USB1 using external ULPI PHY */ + usb@3000 { + compatible = "fsl,mpc5121-usb2-dr"; + reg = <0x3000 0x600>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <43 0x8>; + dr_mode = "otg"; + phy_type = "ulpi"; + }; + + /* USB0 using internal UTMI PHY */ + usb@4000 { + compatible = "fsl,mpc5121-usb2-dr"; + reg = <0x4000 0x600>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <44 0x8>; + dr_mode = "otg"; + phy_type = "utmi_wide"; + }; + + /* IO control */ + ioctl@a000 { + compatible = "fsl,mpc5121-ioctl"; + reg = <0xA000 0x1000>; + }; + + /* LocalPlus controller */ + lpc@10000 { + compatible = "fsl,mpc5121-lpc"; + reg = <0x10000 0x200>; + }; + + pata@10200 { + compatible = "fsl,mpc5121-pata"; + reg = <0x10200 0x100>; + interrupts = <5 0x8>; + }; + + /* 512x PSCs are not 52xx PSC compatible */ + + /* PSC0 */ + psc@11000 { + compatible = "fsl,mpc5121-psc"; + reg = <0x11000 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + /* PSC1 */ + psc@11100 { + compatible = "fsl,mpc5121-psc"; + reg = <0x11100 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + /* PSC2 */ + psc@11200 { + compatible = "fsl,mpc5121-psc"; + reg = <0x11200 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + /* PSC3 */ + psc@11300 { + compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; + reg = <0x11300 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + /* PSC4 */ + psc@11400 { + compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; + reg = <0x11400 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + /* PSC5 */ + psc@11500 { + compatible = "fsl,mpc5121-psc"; + reg = <0x11500 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + /* PSC6 */ + psc@11600 { + compatible = "fsl,mpc5121-psc"; + reg = <0x11600 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + /* PSC7 */ + psc@11700 { + compatible = "fsl,mpc5121-psc"; + reg = <0x11700 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + /* PSC8 */ + psc@11800 { + compatible = "fsl,mpc5121-psc"; + reg = <0x11800 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + /* PSC9 */ + psc@11900 { + compatible = "fsl,mpc5121-psc"; + reg = <0x11900 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + /* PSC10 */ + psc@11a00 { + compatible = "fsl,mpc5121-psc"; + reg = <0x11a00 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + /* PSC11 */ + psc@11b00 { + compatible = "fsl,mpc5121-psc"; + reg = <0x11b00 0x100>; + interrupts = <40 0x8>; + fsl,rx-fifo-size = <16>; + fsl,tx-fifo-size = <16>; + }; + + pscfifo@11f00 { + compatible = "fsl,mpc5121-psc-fifo"; + reg = <0x11f00 0x100>; + interrupts = <40 0x8>; + }; + + dma@14000 { + compatible = "fsl,mpc5121-dma"; + reg = <0x14000 0x1800>; + interrupts = <65 0x8>; + }; + }; + + pci: pci@80008500 { + compatible = "fsl,mpc5121-pci"; + device_type = "pci"; + interrupts = <1 0x8>; + clock-frequency = <0>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + + reg = <0x80008500 0x100 /* internal registers */ + 0x80008300 0x8>; /* config space access registers */ + bus-range = <0x0 0x0>; + ranges = <0x42000000 0x0 0xa0000000 0xa0000000 0x0 0x10000000 + 0x02000000 0x0 0xb0000000 0xb0000000 0x0 0x10000000 + 0x01000000 0x0 0x00000000 0x84000000 0x0 0x01000000>; + }; +}; diff --git a/arch/powerpc/boot/dts/mpc5121ads.dts b/arch/powerpc/boot/dts/mpc5121ads.dts index c9ef6bbe26cf..f269b1382ef7 100644 --- a/arch/powerpc/boot/dts/mpc5121ads.dts +++ b/arch/powerpc/boot/dts/mpc5121ads.dts @@ -1,7 +1,7 @@ /* * MPC5121E ADS Device Tree Source * - * Copyright 2007,2008 Freescale Semiconductor Inc. + * Copyright 2007-2008 Freescale Semiconductor Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -9,74 +9,26 @@ * option) any later version. */ -/dts-v1/; +/include/ "mpc5121.dtsi" / { model = "mpc5121ads"; compatible = "fsl,mpc5121ads"; - #address-cells = <1>; - #size-cells = <1>; - - aliases { - pci = &pci; - }; - - cpus { - #address-cells = <1>; - #size-cells = <0>; - - PowerPC,5121@0 { - device_type = "cpu"; - reg = <0>; - d-cache-line-size = <0x20>; // 32 bytes - i-cache-line-size = <0x20>; // 32 bytes - d-cache-size = <0x8000>; // L1, 32K - i-cache-size = <0x8000>; // L1, 32K - timebase-frequency = <49500000>;// 49.5 MHz (csb/4) - bus-frequency = <198000000>; // 198 MHz csb bus - clock-frequency = <396000000>; // 396 MHz ppc core - }; - }; - - memory { - device_type = "memory"; - reg = <0x00000000 0x10000000>; // 256MB at 0 - }; - - mbx@20000000 { - compatible = "fsl,mpc5121-mbx"; - reg = <0x20000000 0x4000>; - interrupts = <66 0x8>; - interrupt-parent = < &ipic >; - }; - - sram@30000000 { - compatible = "fsl,mpc5121-sram"; - reg = <0x30000000 0x20000>; // 128K at 0x30000000 - }; nfc@40000000 { - compatible = "fsl,mpc5121-nfc"; - reg = <0x40000000 0x100000>; // 1M at 0x40000000 - interrupts = <6 8>; - interrupt-parent = < &ipic >; - #address-cells = <1>; - #size-cells = <1>; - // ADS has two Hynix 512MB Nand flash chips in a single - // stacked package. + /* + * ADS has two Hynix 512MB Nand flash chips in a single + * stacked package. + */ chips = <2>; + nand@0 { label = "nand"; - reg = <0x00000000 0x40000000>; // 512MB + 512MB + reg = <0x00000000 0x40000000>; /* 512MB + 512MB */ }; }; localbus@80000020 { - compatible = "fsl,mpc5121-localbus"; - #address-cells = <2>; - #size-cells = <1>; - reg = <0x80000020 0x40>; - ranges = <0x0 0x0 0xfc000000 0x04000000 0x2 0x0 0x82000000 0x00008000>; @@ -87,6 +39,7 @@ #size-cells = <1>; bank-width = <4>; device-width = <2>; + protected@0 { label = "protected"; reg = <0x00000000 0x00040000>; // first sector is protected @@ -121,91 +74,18 @@ interrupt-controller; #interrupt-cells = <2>; reg = <0x2 0xa 0x5>; - interrupt-parent = < &ipic >; - // irq routing - // all irqs but touch screen are routed to irq0 (ipic 48) - // touch screen is statically routed to irq1 (ipic 17) - // so don't use it here + /* irq routing: + * all irqs but touch screen are routed to irq0 (ipic 48) + * touch screen is statically routed to irq1 (ipic 17) + * so don't use it here + */ interrupts = <48 0x8>; }; }; soc@80000000 { - compatible = "fsl,mpc5121-immr"; - #address-cells = <1>; - #size-cells = <1>; - #interrupt-cells = <2>; - ranges = <0x0 0x80000000 0x400000>; - reg = <0x80000000 0x400000>; - bus-frequency = <66000000>; // 66 MHz ips bus - - - // IPIC - // interrupts cell = - // sense values match linux IORESOURCE_IRQ_* defines: - // sense == 8: Level, low assertion - // sense == 2: Edge, high-to-low change - // - ipic: interrupt-controller@c00 { - compatible = "fsl,mpc5121-ipic", "fsl,ipic"; - interrupt-controller; - #address-cells = <0>; - #interrupt-cells = <2>; - reg = <0xc00 0x100>; - }; - - rtc@a00 { // Real time clock - compatible = "fsl,mpc5121-rtc"; - reg = <0xa00 0x100>; - interrupts = <79 0x8 80 0x8>; - interrupt-parent = < &ipic >; - }; - - reset@e00 { // Reset module - compatible = "fsl,mpc5121-reset"; - reg = <0xe00 0x100>; - }; - - clock@f00 { // Clock control - compatible = "fsl,mpc5121-clock"; - reg = <0xf00 0x100>; - }; - - pmc@1000{ //Power Management Controller - compatible = "fsl,mpc5121-pmc"; - reg = <0x1000 0x100>; - interrupts = <83 0x2>; - interrupt-parent = < &ipic >; - }; - - gpio@1100 { - compatible = "fsl,mpc5121-gpio"; - reg = <0x1100 0x100>; - interrupts = <78 0x8>; - interrupt-parent = < &ipic >; - }; - - can@1300 { - compatible = "fsl,mpc5121-mscan"; - interrupts = <12 0x8>; - interrupt-parent = < &ipic >; - reg = <0x1300 0x80>; - }; - - can@1380 { - compatible = "fsl,mpc5121-mscan"; - interrupts = <13 0x8>; - interrupt-parent = < &ipic >; - reg = <0x1380 0x80>; - }; i2c@1700 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mpc5121-i2c", "fsl-i2c"; - reg = <0x1700 0x20>; - interrupts = <9 0x8>; - interrupt-parent = < &ipic >; fsl,preserve-clocking; hwmon@4a { @@ -224,196 +104,75 @@ }; }; - i2c@1720 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mpc5121-i2c", "fsl-i2c"; - reg = <0x1720 0x20>; - interrupts = <10 0x8>; - interrupt-parent = < &ipic >; + eth0: ethernet@2800 { + phy-handle = <&phy0>; }; - i2c@1740 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mpc5121-i2c", "fsl-i2c"; - reg = <0x1740 0x20>; - interrupts = <11 0x8>; - interrupt-parent = < &ipic >; + can@2300 { + status = "disabled"; }; - i2ccontrol@1760 { - compatible = "fsl,mpc5121-i2c-ctrl"; - reg = <0x1760 0x8>; + can@2380 { + status = "disabled"; }; - axe@2000 { - compatible = "fsl,mpc5121-axe"; - reg = <0x2000 0x100>; - interrupts = <42 0x8>; - interrupt-parent = < &ipic >; - }; - - display@2100 { - compatible = "fsl,mpc5121-diu"; - reg = <0x2100 0x100>; - interrupts = <64 0x8>; - interrupt-parent = < &ipic >; + viu@2400 { + status = "disabled"; }; mdio@2800 { - compatible = "fsl,mpc5121-fec-mdio"; - reg = <0x2800 0x800>; - #address-cells = <1>; - #size-cells = <0>; - phy: ethernet-phy@0 { + phy0: ethernet-phy@0 { reg = <1>; - device_type = "ethernet-phy"; }; }; - ethernet@2800 { - device_type = "network"; - compatible = "fsl,mpc5121-fec"; - reg = <0x2800 0x800>; - local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <4 0x8>; - interrupt-parent = < &ipic >; - phy-handle = < &phy >; - fsl,align-tx-packets = <4>; + /* mpc5121ads only uses USB0 */ + usb@3000 { + status = "disabled"; }; - // 5121e has two dr usb modules - // mpc5121_ads only uses USB0 - - // USB1 using external ULPI PHY - //usb@3000 { - // compatible = "fsl,mpc5121-usb2-dr"; - // reg = <0x3000 0x1000>; - // #address-cells = <1>; - // #size-cells = <0>; - // interrupt-parent = < &ipic >; - // interrupts = <43 0x8>; - // dr_mode = "otg"; - // phy_type = "ulpi"; - //}; - - // USB0 using internal UTMI PHY + /* USB0 using internal UTMI PHY */ usb@4000 { - compatible = "fsl,mpc5121-usb2-dr"; - reg = <0x4000 0x1000>; - #address-cells = <1>; - #size-cells = <0>; - interrupt-parent = < &ipic >; - interrupts = <44 0x8>; - dr_mode = "otg"; - phy_type = "utmi_wide"; + dr_mode = "host"; fsl,invert-drvvbus; fsl,invert-pwr-fault; }; - // IO control - ioctl@a000 { - compatible = "fsl,mpc5121-ioctl"; - reg = <0xA000 0x1000>; - }; - - pata@10200 { - compatible = "fsl,mpc5121-pata"; - reg = <0x10200 0x100>; - interrupts = <5 0x8>; - interrupt-parent = < &ipic >; - }; - - // 512x PSCs are not 52xx PSC compatible - // PSC3 serial port A aka ttyPSC0 - serial@11300 { - device_type = "serial"; + /* PSC3 serial port A aka ttyPSC0 */ + psc@11300 { compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; - // Logical port assignment needed until driver - // learns to use aliases - port-number = <0>; - cell-index = <3>; - reg = <0x11300 0x100>; - interrupts = <40 0x8>; - interrupt-parent = < &ipic >; - rx-fifo-size = <16>; - tx-fifo-size = <16>; }; - // PSC4 serial port B aka ttyPSC1 - serial@11400 { - device_type = "serial"; + /* PSC4 serial port B aka ttyPSC1 */ + psc@11400 { compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; - // Logical port assignment needed until driver - // learns to use aliases - port-number = <1>; - cell-index = <4>; - reg = <0x11400 0x100>; - interrupts = <40 0x8>; - interrupt-parent = < &ipic >; - rx-fifo-size = <16>; - tx-fifo-size = <16>; }; - // PSC5 in ac97 mode - ac97@11500 { + /* PSC5 in ac97 mode */ + ac97: psc@11500 { compatible = "fsl,mpc5121-psc-ac97", "fsl,mpc5121-psc"; - cell-index = <5>; - reg = <0x11500 0x100>; - interrupts = <40 0x8>; - interrupt-parent = < &ipic >; fsl,mode = "ac97-slave"; - rx-fifo-size = <384>; - tx-fifo-size = <384>; + fsl,rx-fifo-size = <384>; + fsl,tx-fifo-size = <384>; }; - - pscfifo@11f00 { - compatible = "fsl,mpc5121-psc-fifo"; - reg = <0x11f00 0x100>; - interrupts = <40 0x8>; - interrupt-parent = < &ipic >; - }; - - dma@14000 { - compatible = "fsl,mpc5121-dma"; - reg = <0x14000 0x1800>; - interrupts = <65 0x8>; - interrupt-parent = < &ipic >; - }; - }; pci: pci@80008500 { interrupt-map-mask = <0xf800 0x0 0x0 0x7>; interrupt-map = < - // IDSEL 0x15 - Slot 1 PCI + /* IDSEL 0x15 - Slot 1 PCI */ 0xa800 0x0 0x0 0x1 &cpld_pic 0x0 0x8 0xa800 0x0 0x0 0x2 &cpld_pic 0x1 0x8 0xa800 0x0 0x0 0x3 &cpld_pic 0x2 0x8 0xa800 0x0 0x0 0x4 &cpld_pic 0x3 0x8 - // IDSEL 0x16 - Slot 2 MiniPCI + /* IDSEL 0x16 - Slot 2 MiniPCI */ 0xb000 0x0 0x0 0x1 &cpld_pic 0x4 0x8 0xb000 0x0 0x0 0x2 &cpld_pic 0x5 0x8 - // IDSEL 0x17 - Slot 3 MiniPCI + /* IDSEL 0x17 - Slot 3 MiniPCI */ 0xb800 0x0 0x0 0x1 &cpld_pic 0x6 0x8 0xb800 0x0 0x0 0x2 &cpld_pic 0x7 0x8 >; - interrupt-parent = < &ipic >; - interrupts = <1 0x8>; - bus-range = <0 0>; - ranges = <0x42000000 0x0 0xa0000000 0xa0000000 0x0 0x10000000 - 0x02000000 0x0 0xb0000000 0xb0000000 0x0 0x10000000 - 0x01000000 0x0 0x00000000 0x84000000 0x0 0x01000000>; - clock-frequency = <0>; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - reg = <0x80008500 0x100 /* internal registers */ - 0x80008300 0x8>; /* config space access registers */ - compatible = "fsl,mpc5121-pci"; - device_type = "pci"; }; }; From 73e31235ca7874701718b562ac19161516e46c13 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 10 Dec 2012 18:17:43 +0100 Subject: [PATCH 0883/4607] powerpc/mpc5121: pdm360ng.dts: use common mpc5121.dtsi Change dts file for pdm360ng board to use common mpc5121 SoC dtsi file. Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/pdm360ng.dts | 279 ++++------------------------- 1 file changed, 34 insertions(+), 245 deletions(-) diff --git a/arch/powerpc/boot/dts/pdm360ng.dts b/arch/powerpc/boot/dts/pdm360ng.dts index 94dfa5c9a7f9..0b069477838a 100644 --- a/arch/powerpc/boot/dts/pdm360ng.dts +++ b/arch/powerpc/boot/dts/pdm360ng.dts @@ -13,7 +13,7 @@ * option) any later version. */ -/dts-v1/; +/include/ "mpc5121.dtsi" / { model = "pdm360ng"; @@ -22,38 +22,12 @@ #size-cells = <1>; interrupt-parent = <&ipic>; - aliases { - ethernet0 = ð0; - }; - - cpus { - #address-cells = <1>; - #size-cells = <0>; - - PowerPC,5121@0 { - device_type = "cpu"; - reg = <0>; - d-cache-line-size = <0x20>; // 32 bytes - i-cache-line-size = <0x20>; // 32 bytes - d-cache-size = <0x8000>; // L1, 32K - i-cache-size = <0x8000>; // L1, 32K - timebase-frequency = <49500000>;// 49.5 MHz (csb/4) - bus-frequency = <198000000>; // 198 MHz csb bus - clock-frequency = <396000000>; // 396 MHz ppc core - }; - }; - memory { device_type = "memory"; reg = <0x00000000 0x20000000>; // 512MB at 0 }; nfc@40000000 { - compatible = "fsl,mpc5121-nfc"; - reg = <0x40000000 0x100000>; - interrupts = <0x6 0x8>; - #address-cells = <0x1>; - #size-cells = <0x1>; bank-width = <0x1>; chips = <0x1>; @@ -63,17 +37,7 @@ }; }; - sram@50000000 { - compatible = "fsl,mpc5121-sram"; - reg = <0x50000000 0x20000>; // 128K at 0x50000000 - }; - localbus@80000020 { - compatible = "fsl,mpc5121-localbus"; - #address-cells = <2>; - #size-cells = <1>; - reg = <0x80000020 0x40>; - ranges = <0x0 0x0 0xf0000000 0x10000000 /* Flash */ 0x2 0x0 0x50040000 0x00020000>; /* CS2: MRAM */ @@ -129,74 +93,8 @@ }; soc@80000000 { - compatible = "fsl,mpc5121-immr"; - #address-cells = <1>; - #size-cells = <1>; - #interrupt-cells = <2>; - ranges = <0x0 0x80000000 0x400000>; - reg = <0x80000000 0x400000>; - bus-frequency = <66000000>; // 66 MHz ips bus - - // IPIC - // interrupts cell = - // sense values match linux IORESOURCE_IRQ_* defines: - // sense == 8: Level, low assertion - // sense == 2: Edge, high-to-low change - // - ipic: interrupt-controller@c00 { - compatible = "fsl,mpc5121-ipic", "fsl,ipic"; - interrupt-controller; - #address-cells = <0>; - #interrupt-cells = <2>; - reg = <0xc00 0x100>; - }; - - rtc@a00 { // Real time clock - compatible = "fsl,mpc5121-rtc"; - reg = <0xa00 0x100>; - interrupts = <79 0x8 80 0x8>; - }; - - reset@e00 { // Reset module - compatible = "fsl,mpc5121-reset"; - reg = <0xe00 0x100>; - }; - - clock@f00 { // Clock control - compatible = "fsl,mpc5121-clock"; - reg = <0xf00 0x100>; - }; - - pmc@1000{ //Power Management Controller - compatible = "fsl,mpc5121-pmc"; - reg = <0x1000 0x100>; - interrupts = <83 0x2>; - }; - - gpio@1100 { - compatible = "fsl,mpc5121-gpio"; - reg = <0x1100 0x100>; - interrupts = <78 0x8>; - }; - - can@1300 { - compatible = "fsl,mpc5121-mscan"; - interrupts = <12 0x8>; - reg = <0x1300 0x80>; - }; - - can@1380 { - compatible = "fsl,mpc5121-mscan"; - interrupts = <13 0x8>; - reg = <0x1380 0x80>; - }; i2c@1700 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mpc5121-i2c"; - reg = <0x1700 0x20>; - interrupts = <0x9 0x8>; fsl,preserve-clocking; eeprom@50 { @@ -210,201 +108,92 @@ }; }; + i2c@1720 { + status = "disabled"; + }; + i2c@1740 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mpc5121-i2c"; - reg = <0x1740 0x20>; - interrupts = <0xb 0x8>; fsl,preserve-clocking; }; - i2ccontrol@1760 { - compatible = "fsl,mpc5121-i2c-ctrl"; - reg = <0x1760 0x8>; - }; - - axe@2000 { - compatible = "fsl,mpc5121-axe"; - reg = <0x2000 0x100>; - interrupts = <42 0x8>; - }; - - display@2100 { - compatible = "fsl,mpc5121-diu"; - reg = <0x2100 0x100>; - interrupts = <64 0x8>; - }; - - can@2300 { - compatible = "fsl,mpc5121-mscan"; - interrupts = <90 0x8>; - reg = <0x2300 0x80>; - }; - - can@2380 { - compatible = "fsl,mpc5121-mscan"; - interrupts = <91 0x8>; - reg = <0x2380 0x80>; - }; - - viu@2400 { - compatible = "fsl,mpc5121-viu"; - reg = <0x2400 0x400>; - interrupts = <67 0x8>; + ethernet@2800 { + phy-handle = <&phy0>; }; mdio@2800 { - compatible = "fsl,mpc5121-fec-mdio"; - reg = <0x2800 0x200>; - #address-cells = <1>; - #size-cells = <0>; - phy: ethernet-phy@0 { + phy0: ethernet-phy@1f { compatible = "smsc,lan8700"; reg = <0x1f>; }; }; - eth0: ethernet@2800 { - compatible = "fsl,mpc5121-fec"; - reg = <0x2800 0x200>; - local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <4 0x8>; - phy-handle = < &phy >; - }; - - // USB1 using external ULPI PHY + /* USB1 using external ULPI PHY */ usb@3000 { - compatible = "fsl,mpc5121-usb2-dr"; - reg = <0x3000 0x600>; - #address-cells = <1>; - #size-cells = <0>; - interrupts = <43 0x8>; dr_mode = "host"; - phy_type = "ulpi"; }; - // USB0 using internal UTMI PHY + /* USB0 using internal UTMI PHY */ usb@4000 { - compatible = "fsl,mpc5121-usb2-dr"; - reg = <0x4000 0x600>; - #address-cells = <1>; - #size-cells = <0>; - interrupts = <44 0x8>; - dr_mode = "otg"; - phy_type = "utmi_wide"; fsl,invert-pwr-fault; }; - // IO control - ioctl@a000 { - compatible = "fsl,mpc5121-ioctl"; - reg = <0xA000 0x1000>; - }; - - // 512x PSCs are not 52xx PSCs compatible - serial@11000 { + psc@11000 { compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; - cell-index = <0>; - reg = <0x11000 0x100>; - interrupts = <40 0x8>; - fsl,rx-fifo-size = <16>; - fsl,tx-fifo-size = <16>; }; - serial@11100 { + psc@11100 { compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; - cell-index = <1>; - reg = <0x11100 0x100>; - interrupts = <40 0x8>; - fsl,rx-fifo-size = <16>; - fsl,tx-fifo-size = <16>; }; - serial@11200 { + psc@11200 { compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; - cell-index = <2>; - reg = <0x11200 0x100>; - interrupts = <40 0x8>; - fsl,rx-fifo-size = <16>; - fsl,tx-fifo-size = <16>; }; - serial@11300 { + psc@11300 { compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; - cell-index = <3>; - reg = <0x11300 0x100>; - interrupts = <40 0x8>; - fsl,rx-fifo-size = <16>; - fsl,tx-fifo-size = <16>; }; - serial@11400 { + psc@11400 { compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; - cell-index = <4>; - reg = <0x11400 0x100>; - interrupts = <40 0x8>; - fsl,rx-fifo-size = <16>; - fsl,tx-fifo-size = <16>; }; - serial@11600 { + psc@11500 { + status = "disabled"; + }; + + psc@11600 { compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; - cell-index = <6>; - reg = <0x11600 0x100>; - interrupts = <40 0x8>; - fsl,rx-fifo-size = <16>; - fsl,tx-fifo-size = <16>; }; - serial@11800 { + psc@11700 { + status = "disabled"; + }; + + psc@11800 { compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; - cell-index = <8>; - reg = <0x11800 0x100>; - interrupts = <40 0x8>; - fsl,rx-fifo-size = <16>; - fsl,tx-fifo-size = <16>; }; - serial@11B00 { - compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; - cell-index = <11>; - reg = <0x11B00 0x100>; - interrupts = <40 0x8>; - fsl,rx-fifo-size = <16>; - fsl,tx-fifo-size = <16>; - }; - - pscfifo@11f00 { - compatible = "fsl,mpc5121-psc-fifo"; - reg = <0x11f00 0x100>; - interrupts = <40 0x8>; - }; - - spi@11900 { + psc@11900 { compatible = "fsl,mpc5121-psc-spi", "fsl,mpc5121-psc"; - cell-index = <9>; #address-cells = <1>; #size-cells = <0>; - reg = <0x11900 0x100>; - interrupts = <40 0x8>; - fsl,rx-fifo-size = <16>; - fsl,tx-fifo-size = <16>; - // 7845 touch screen controller + /* ADS7845 touch screen controller */ ts@0 { compatible = "ti,ads7846"; reg = <0x0>; spi-max-frequency = <3000000>; - // pen irq is GPIO25 + /* pen irq is GPIO25 */ interrupts = <78 0x8>; }; }; - dma@14000 { - compatible = "fsl,mpc5121-dma"; - reg = <0x14000 0x1800>; - interrupts = <65 0x8>; + psc@11a00 { + status = "disabled"; + }; + + psc@11b00 { + compatible = "fsl,mpc5121-psc-uart", "fsl,mpc5121-psc"; }; }; }; From fa6d459d64a60db41c476c16063670ca4297e1d1 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Fri, 21 Dec 2012 12:53:38 +0100 Subject: [PATCH 0884/4607] mpc5121: remove obsolete cell-index property from PSC clock code Don't use cell-index from device tree, obtain the PSC number from PSCx register offset. Signed-off-by: Anatolij Gustschin --- arch/powerpc/platforms/512x/clock.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/platforms/512x/clock.c b/arch/powerpc/platforms/512x/clock.c index 9f771e05457c..d0095c8ee579 100644 --- a/arch/powerpc/platforms/512x/clock.c +++ b/arch/powerpc/platforms/512x/clock.c @@ -680,13 +680,12 @@ static void psc_calc_rate(struct clk *clk, int pscnum, struct device_node *np) static void psc_clks_init(void) { struct device_node *np; - const u32 *cell_index; struct platform_device *ofdev; + u32 reg; for_each_compatible_node(np, NULL, "fsl,mpc5121-psc") { - cell_index = of_get_property(np, "cell-index", NULL); - if (cell_index) { - int pscnum = *cell_index; + if (!of_property_read_u32(np, "reg", ®)) { + int pscnum = (reg & 0xf00) >> 8; struct clk *clk = psc_dev_clk(pscnum); clk->flags = CLK_HAS_RATE | CLK_HAS_CTRL; From f4ef34537ad303e2230ec543cccc7847f3c47ae8 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Fri, 21 Dec 2012 13:08:07 +0100 Subject: [PATCH 0885/4607] mpc5121: don't check PSC ac97 using node name The .dtsi now names all PSC nodes as "psc", so this ac97 check won't work. Check for ac97 PSC using compatible property. Signed-off-by: Anatolij Gustschin --- arch/powerpc/platforms/512x/clock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/512x/clock.c b/arch/powerpc/platforms/512x/clock.c index d0095c8ee579..7937361c5804 100644 --- a/arch/powerpc/platforms/512x/clock.c +++ b/arch/powerpc/platforms/512x/clock.c @@ -695,7 +695,7 @@ static void psc_clks_init(void) * AC97 is special rate clock does * not go through normal path */ - if (strcmp("ac97", np->name) == 0) + if (of_device_is_compatible(np, "fsl,mpc5121-psc-ac97")) clk->rate = ac97_clk.rate; else psc_calc_rate(clk, pscnum, np); From 6364fd0cb1e4c7f72b974613e0cf5744ae4d2cb2 Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Wed, 19 Dec 2012 09:50:58 +0800 Subject: [PATCH 0886/4607] menuconfig: Add Save/Load buttons If menuconfig have Save/Load button like alternative .config editors, xconfig, nconfig, etc.We will have a obvious benefit when use menuconfig just like when we use others, we can Save/Load our .config quickly and conveniently. This patch add the Save/Load button for menuconfig. [remove trailing space while at it for below line: "*) Formerly when I used Page Down and Page Up, the cursor would be set" ] Changes: V1-V2: 1:use PATH_MAX instead of hard code suggested by Yann E. MORIN 2:drop the spurious empty-line removal suggested by Yann E. MORIN V2-V3: 1:ajust buttons position well centered reported by Yann E. MORIN Signed-off-by: Wang YanQing Reviewed-by: "Yann E. MORIN" Tested-by: "Yann E. MORIN" Signed-off-by: "Yann E. MORIN" --- scripts/kconfig/lxdialog/menubox.c | 22 ++++++++++++---------- scripts/kconfig/mconf.c | 30 +++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/scripts/kconfig/lxdialog/menubox.c b/scripts/kconfig/lxdialog/menubox.c index 1d604738fa13..48d382e7e374 100644 --- a/scripts/kconfig/lxdialog/menubox.c +++ b/scripts/kconfig/lxdialog/menubox.c @@ -26,7 +26,7 @@ * * *) A bugfix for the Page-Down problem * - * *) Formerly when I used Page Down and Page Up, the cursor would be set + * *) Formerly when I used Page Down and Page Up, the cursor would be set * to the first position in the menu box. Now lxdialog is a bit * smarter and works more like other menu systems (just have a look at * it). @@ -154,12 +154,14 @@ static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x, */ static void print_buttons(WINDOW * win, int height, int width, int selected) { - int x = width / 2 - 16; + int x = width / 2 - 28; int y = height - 2; print_button(win, gettext("Select"), y, x, selected == 0); print_button(win, gettext(" Exit "), y, x + 12, selected == 1); print_button(win, gettext(" Help "), y, x + 24, selected == 2); + print_button(win, gettext(" Save "), y, x + 36, selected == 3); + print_button(win, gettext(" Load "), y, x + 48, selected == 4); wmove(win, y, x + 1 + 12 * selected); wrefresh(win); @@ -372,7 +374,7 @@ do_resize: case TAB: case KEY_RIGHT: button = ((key == KEY_LEFT ? --button : ++button) < 0) - ? 2 : (button > 2 ? 0 : button); + ? 4 : (button > 4 ? 0 : button); print_buttons(dialog, height, width, button); wrefresh(menu); @@ -399,17 +401,17 @@ do_resize: return 2; case 's': case 'y': - return 3; - case 'n': - return 4; - case 'm': return 5; - case ' ': + case 'n': return 6; - case '/': + case 'm': return 7; - case 'z': + case ' ': return 8; + case '/': + return 9; + case 'z': + return 10; case '\n': return button; } diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index 5f29618d1845..88e806857dc3 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -280,6 +280,7 @@ static struct menu *current_menu; static int child_count; static int single_menu_mode; static int show_all_options; +static int save_and_exit; static void conf(struct menu *menu, struct menu *active_menu); static void conf_choice(struct menu *menu); @@ -657,6 +658,12 @@ static void conf(struct menu *menu, struct menu *active_menu) show_helptext(_("README"), _(mconf_readme)); break; case 3: + conf_save(); + break; + case 4: + conf_load(); + break; + case 5: if (item_is_tag('t')) { if (sym_set_tristate_value(sym, yes)) break; @@ -664,24 +671,24 @@ static void conf(struct menu *menu, struct menu *active_menu) show_textbox(NULL, setmod_text, 6, 74); } break; - case 4: + case 6: if (item_is_tag('t')) sym_set_tristate_value(sym, no); break; - case 5: + case 7: if (item_is_tag('t')) sym_set_tristate_value(sym, mod); break; - case 6: + case 8: if (item_is_tag('t')) sym_toggle_tristate_value(sym); else if (item_is_tag('m')) conf(submenu, NULL); break; - case 7: + case 9: search_conf(); break; - case 8: + case 10: show_all_options = !show_all_options; break; } @@ -708,6 +715,17 @@ static void show_helptext(const char *title, const char *text) show_textbox(title, text, 0, 0); } +static void conf_message_callback(const char *fmt, va_list ap) +{ + char buf[PATH_MAX+1]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + if (save_and_exit) + printf("%s", buf); + else + show_textbox(NULL, buf, 6, 60); +} + static void show_help(struct menu *menu) { struct gstr help = str_new(); @@ -876,6 +894,7 @@ static int handle_exit(void) { int res; + save_and_exit = 1; dialog_clear(); if (conf_get_changed()) res = dialog_yesno(NULL, @@ -947,6 +966,7 @@ int main(int ac, char **av) } set_config_filename(conf_get_configname()); + conf_set_message_callback(conf_message_callback); do { conf(&rootmenu, NULL); res = handle_exit(); From 1bdbac478a858d2aa73a6784c7c2e09de0f6d06b Mon Sep 17 00:00:00 2001 From: Wang YanQing Date: Mon, 17 Dec 2012 22:56:14 +0800 Subject: [PATCH 0887/4607] menuconfig: Get rid of the top-level entries for "Load an Alternate/Save an Alternate" Now we have Load/Save buttons to do the Load/Save in the convenient place, so we can drop the top-level entries. Signed-off-by: Wang YanQing Reviewed-by: "Yann E. MORIN" Tested-by: "Yann E. MORIN" Signed-off-by: "Yann E. MORIN" --- scripts/kconfig/mconf.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index 88e806857dc3..680135effd9b 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -599,14 +599,6 @@ static void conf(struct menu *menu, struct menu *active_menu) build_conf(menu); if (!child_count) break; - if (menu == &rootmenu) { - item_make("--- "); - item_set_tag(':'); - item_make(_(" Load an Alternate Configuration File")); - item_set_tag('L'); - item_make(_(" Save an Alternate Configuration File")); - item_set_tag('S'); - } dialog_clear(); res = dialog_menu(prompt ? _(prompt) : _("Main Menu"), _(menu_instructions), @@ -643,12 +635,6 @@ static void conf(struct menu *menu, struct menu *active_menu) case 's': conf_string(submenu); break; - case 'L': - conf_load(); - break; - case 'S': - conf_save(); - break; } break; case 2: From 8ab3e6a08a98f7ff18c6814065eb30ba2e000233 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 2 Jan 2013 15:29:39 +0000 Subject: [PATCH 0888/4607] thermal: Use thermal zone device id in netlink messages This patch changes the function thermal_generate_netlink_event to receive a thermal zone device instead of a originator id. This way, the messages will always be bound to a thermal zone. Signed-off-by: Eduardo Valentin Reviewed-by: Durgadoss R Signed-off-by: Zhang Rui --- Documentation/thermal/sysfs-api.txt | 5 +++-- drivers/thermal/thermal_sys.c | 8 ++++++-- include/linux/thermal.h | 6 ++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt index 88c02334e356..526d4b90d6c1 100644 --- a/Documentation/thermal/sysfs-api.txt +++ b/Documentation/thermal/sysfs-api.txt @@ -329,8 +329,9 @@ The framework includes a simple notification mechanism, in the form of a netlink event. Netlink socket initialization is done during the _init_ of the framework. Drivers which intend to use the notification mechanism just need to call thermal_generate_netlink_event() with two arguments viz -(originator, event). Typically the originator will be an integer assigned -to a thermal_zone_device when it registers itself with the framework. The +(originator, event). The originator is a pointer to struct thermal_zone_device +from where the event has been originated. An integer which represents the +thermal zone device will be used in the message to identify the zone. The event will be one of:{THERMAL_AUX0, THERMAL_AUX1, THERMAL_CRITICAL, THERMAL_DEV_FAULT}. Notification can be sent when the current temperature crosses any of the configured thresholds. diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 8c8ce806180f..d85f51f433be 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1711,7 +1711,8 @@ static struct genl_multicast_group thermal_event_mcgrp = { .name = THERMAL_GENL_MCAST_GROUP_NAME, }; -int thermal_generate_netlink_event(u32 orig, enum events event) +int thermal_generate_netlink_event(struct thermal_zone_device *tz, + enum events event) { struct sk_buff *skb; struct nlattr *attr; @@ -1721,6 +1722,9 @@ int thermal_generate_netlink_event(u32 orig, enum events event) int result; static unsigned int thermal_event_seqnum; + if (!tz) + return -EINVAL; + /* allocate memory */ size = nla_total_size(sizeof(struct thermal_genl_event)) + nla_total_size(0); @@ -1755,7 +1759,7 @@ int thermal_generate_netlink_event(u32 orig, enum events event) memset(thermal_event, 0, sizeof(struct thermal_genl_event)); - thermal_event->orig = orig; + thermal_event->orig = tz->id; thermal_event->event = event; /* send multicast genetlink message */ diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 883bcda7e1e4..9b78f8c6f773 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -246,9 +246,11 @@ int thermal_register_governor(struct thermal_governor *); void thermal_unregister_governor(struct thermal_governor *); #ifdef CONFIG_NET -extern int thermal_generate_netlink_event(u32 orig, enum events event); +extern int thermal_generate_netlink_event(struct thermal_zone_device *tz, + enum events event); #else -static inline int thermal_generate_netlink_event(u32 orig, enum events event) +static int thermal_generate_netlink_event(struct thermal_zone_device *tz, + enum events event) { return 0; } From ba38bb8c72fc259a8e3f2f1feb5761ae6f5858f0 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 2 Jan 2013 15:29:40 +0000 Subject: [PATCH 0889/4607] thermal: remove unnecessary include No need for spinlocks in this file, then removing its header. Signed-off-by: Eduardo Valentin Reviewed-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index d85f51f433be..70ce100d34af 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include From 923e0b1e8dbe0939d9fc41c226dfc5d53884d8c6 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 2 Jan 2013 15:29:41 +0000 Subject: [PATCH 0890/4607] thermal: cleanup: use dev_* helper functions Change the logging messages to used dev_* helper functions. Signed-off-by: Eduardo Valentin Reviewed-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 70ce100d34af..fba27c36d707 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -354,8 +354,9 @@ static void handle_critical_trips(struct thermal_zone_device *tz, tz->ops->notify(tz, trip, trip_type); if (trip_type == THERMAL_TRIP_CRITICAL) { - pr_emerg("Critical temperature reached(%d C),shutting down\n", - tz->temperature / 1000); + dev_emerg(&tz->device, + "critical temperature reached(%d C),shutting down\n", + tz->temperature / 1000); orderly_poweroff(true); } } @@ -386,7 +387,8 @@ static void update_temperature(struct thermal_zone_device *tz) ret = tz->ops->get_temp(tz, &temp); if (ret) { - pr_warn("failed to read out thermal zone %d\n", tz->id); + dev_warn(&tz->device, "failed to read out thermal zone %d\n", + tz->id); goto exit; } @@ -1770,7 +1772,7 @@ int thermal_generate_netlink_event(struct thermal_zone_device *tz, result = genlmsg_multicast(skb, 0, thermal_event_mcgrp.id, GFP_ATOMIC); if (result) - pr_info("failed to send netlink event:%d\n", result); + dev_err(&tz->device, "Failed to send netlink event:%d", result); return result; } From aaddea812cb0a2dc38b55ba557b68999bc2f6203 Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Wed, 16 Jan 2013 20:21:26 -0500 Subject: [PATCH 0891/4607] ext4: add tracepoint in punching hole This patch adds a tracepoint in ext4_punch_hole. CC: Lukas Czerner Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 2 ++ include/trace/events/ext4.h | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 93a7e8453a68..5abf89c100b9 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3567,6 +3567,8 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) return -EOPNOTSUPP; } + trace_ext4_punch_hole(inode, offset, length); + return ext4_ext_punch_hole(file, offset, length); } diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 7e8c36bc7082..6080ea1380b8 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -1324,6 +1324,31 @@ TRACE_EVENT(ext4_fallocate_exit, __entry->ret) ); +TRACE_EVENT(ext4_punch_hole, + TP_PROTO(struct inode *inode, loff_t offset, loff_t len), + + TP_ARGS(inode, offset, len), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( ino_t, ino ) + __field( loff_t, offset ) + __field( loff_t, len ) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->offset = offset; + __entry->len = len; + ), + + TP_printk("dev %d,%d ino %lu offset %lld len %lld", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + __entry->offset, __entry->len) +); + TRACE_EVENT(ext4_unlink_enter, TP_PROTO(struct inode *parent, struct dentry *dentry), From a046b816a4587c7898772af7cc7e6798cc8b56dd Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Tue, 15 Jan 2013 11:17:29 +0100 Subject: [PATCH 0892/4607] KVM: s390: kvm/sigp.c: fix memory leakage the variable inti should be freed in the branch CPUSTAT_STOPPED. Signed-off-by: Cong Ding Signed-off-by: Cornelia Huck Signed-off-by: Gleb Natapov --- arch/s390/kvm/sigp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c index 461e84179db1..1c48ab2845e0 100644 --- a/arch/s390/kvm/sigp.c +++ b/arch/s390/kvm/sigp.c @@ -137,8 +137,10 @@ static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action) inti->type = KVM_S390_SIGP_STOP; spin_lock_bh(&li->lock); - if ((atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) + if ((atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) { + kfree(inti); goto out; + } list_add_tail(&inti->list, &li->list); atomic_set(&li->active, 1); atomic_set_mask(CPUSTAT_STOP_INT, li->cpuflags); From 6b2aa51d698492e8dc0a0ce6ce5b3193ccaec269 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 2 Jan 2013 15:29:42 +0000 Subject: [PATCH 0893/4607] thermal: check for invalid trip setup when registering thermal device This patch adds an extra check in the data structure while registering a thermal device. The check is to avoid registering zones with a number of trips greater than zero, but with no .get_trip_temp nor .get_trip_type callbacks. Receiving such data structure may end in wrong data access. Signed-off-by: Eduardo Valentin Reviewed-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index fba27c36d707..0a1bf6b032ea 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1530,6 +1530,9 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, if (!ops || !ops->get_temp) return ERR_PTR(-EINVAL); + if (trips > 0 && !ops->get_trip_type) + return ERR_PTR(-EINVAL); + tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL); if (!tz) return ERR_PTR(-ENOMEM); From 72c7901ef00925c6d0cc7ab69183a684908303bc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 6 Dec 2012 10:52:05 +0000 Subject: [PATCH 0894/4607] gpio: twl4030: Introduce private structure to store variables needed runtime Move most of the global variables inside a private structure and allocate it dynamically. Signed-off-by: Peter Ujfalusi Signed-off-by: Linus Walleij --- drivers/gpio/gpio-twl4030.c | 84 ++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 9572aa137e6f..4643f9cd0cae 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -49,11 +49,6 @@ * There are also two LED pins used sometimes as output-only GPIOs. */ - -static struct gpio_chip twl_gpiochip; -static int twl4030_gpio_base; -static int twl4030_gpio_irq_base; - /* genirq interfaces are not available to modules */ #ifdef MODULE #define is_module() true @@ -72,11 +67,20 @@ static int twl4030_gpio_irq_base; /* Data structures */ static DEFINE_MUTEX(gpio_lock); -/* store usage of each GPIO. - each bit represents one GPIO */ -static unsigned int gpio_usage_count; +struct gpio_twl4030_priv { + struct gpio_chip gpio_chip; + int irq_base; + + unsigned int usage_count; +}; /*----------------------------------------------------------------------*/ +static inline struct gpio_twl4030_priv *to_gpio_twl4030(struct gpio_chip *chip) +{ + return container_of(chip, struct gpio_twl4030_priv, gpio_chip); +} + /* * To configure TWL4030 GPIO module registers */ @@ -193,10 +197,6 @@ static int twl4030_get_gpio_datain(int gpio) u8 base = 0; int ret = 0; - if (unlikely((gpio >= TWL4030_GPIO_MAX) - || !(gpio_usage_count & BIT(gpio)))) - return -EPERM; - base = REG_GPIODATAIN1 + d_bnk; ret = gpio_twl4030_read(base); if (ret > 0) @@ -209,6 +209,7 @@ static int twl4030_get_gpio_datain(int gpio) static int twl_request(struct gpio_chip *chip, unsigned offset) { + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); int status = 0; mutex_lock(&gpio_lock); @@ -252,7 +253,7 @@ static int twl_request(struct gpio_chip *chip, unsigned offset) } /* on first use, turn GPIO module "on" */ - if (!gpio_usage_count) { + if (!priv->usage_count) { struct twl4030_gpio_platform_data *pdata; u8 value = MASK_GPIO_CTRL_GPIO_ON; @@ -266,16 +267,18 @@ static int twl_request(struct gpio_chip *chip, unsigned offset) status = gpio_twl4030_write(REG_GPIO_CTRL, value); } - if (!status) - gpio_usage_count |= (0x1 << offset); - done: + if (!status) + priv->usage_count |= BIT(offset); + mutex_unlock(&gpio_lock); return status; } static void twl_free(struct gpio_chip *chip, unsigned offset) { + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + if (offset >= TWL4030_GPIO_MAX) { twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1); return; @@ -283,10 +286,10 @@ static void twl_free(struct gpio_chip *chip, unsigned offset) mutex_lock(&gpio_lock); - gpio_usage_count &= ~BIT(offset); + priv->usage_count &= ~BIT(offset); /* on last use, switch off GPIO module */ - if (!gpio_usage_count) + if (!priv->usage_count) gpio_twl4030_write(REG_GPIO_CTRL, 0x0); mutex_unlock(&gpio_lock); @@ -301,14 +304,19 @@ static int twl_direction_in(struct gpio_chip *chip, unsigned offset) static int twl_get(struct gpio_chip *chip, unsigned offset) { + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); int status = 0; + if (!(priv->usage_count & BIT(offset))) + return -EPERM; + if (offset < TWL4030_GPIO_MAX) status = twl4030_get_gpio_datain(offset); else if (offset == TWL4030_GPIO_MAX) status = cached_leden & LEDEN_LEDAON; else status = cached_leden & LEDEN_LEDBON; + return (status < 0) ? 0 : status; } @@ -333,12 +341,14 @@ static void twl_set(struct gpio_chip *chip, unsigned offset, int value) static int twl_to_irq(struct gpio_chip *chip, unsigned offset) { - return (twl4030_gpio_irq_base && (offset < TWL4030_GPIO_MAX)) - ? (twl4030_gpio_irq_base + offset) + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + + return (priv->irq_base && (offset < TWL4030_GPIO_MAX)) + ? (priv->irq_base + offset) : -EINVAL; } -static struct gpio_chip twl_gpiochip = { +static struct gpio_chip template_chip = { .label = "twl4030", .owner = THIS_MODULE, .request = twl_request, @@ -424,8 +434,14 @@ static int gpio_twl4030_probe(struct platform_device *pdev) { struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; struct device_node *node = pdev->dev.of_node; + struct gpio_twl4030_priv *priv; int ret, irq_base; + priv = devm_kzalloc(&pdev->dev, sizeof(struct gpio_twl4030_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + /* maybe setup IRQs */ if (is_module()) { dev_err(&pdev->dev, "can't dispatch IRQs from modules\n"); @@ -445,12 +461,13 @@ static int gpio_twl4030_probe(struct platform_device *pdev) if (ret < 0) return ret; - twl4030_gpio_irq_base = irq_base; + priv->irq_base = irq_base; no_irqs: - twl_gpiochip.base = -1; - twl_gpiochip.ngpio = TWL4030_GPIO_MAX; - twl_gpiochip.dev = &pdev->dev; + priv->gpio_chip = template_chip; + priv->gpio_chip.base = -1; + priv->gpio_chip.ngpio = TWL4030_GPIO_MAX; + priv->gpio_chip.dev = &pdev->dev; if (node) pdata = of_gpio_twl4030(&pdev->dev); @@ -481,23 +498,23 @@ no_irqs: * is (still) clear if use_leds is set. */ if (pdata->use_leds) - twl_gpiochip.ngpio += 2; + priv->gpio_chip.ngpio += 2; - ret = gpiochip_add(&twl_gpiochip); + ret = gpiochip_add(&priv->gpio_chip); if (ret < 0) { dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret); - twl_gpiochip.ngpio = 0; + priv->gpio_chip.ngpio = 0; gpio_twl4030_remove(pdev); goto out; } - twl4030_gpio_base = twl_gpiochip.base; + platform_set_drvdata(pdev, priv); if (pdata && pdata->setup) { int status; - status = pdata->setup(&pdev->dev, - twl4030_gpio_base, TWL4030_GPIO_MAX); + status = pdata->setup(&pdev->dev, priv->gpio_chip.base, + TWL4030_GPIO_MAX); if (status) dev_dbg(&pdev->dev, "setup --> %d\n", status); } @@ -510,18 +527,19 @@ out: static int gpio_twl4030_remove(struct platform_device *pdev) { struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; + struct gpio_twl4030_priv *priv = platform_get_drvdata(pdev); int status; if (pdata && pdata->teardown) { - status = pdata->teardown(&pdev->dev, - twl4030_gpio_base, TWL4030_GPIO_MAX); + status = pdata->teardown(&pdev->dev, priv->gpio_chip.base, + TWL4030_GPIO_MAX); if (status) { dev_dbg(&pdev->dev, "teardown --> %d\n", status); return status; } } - status = gpiochip_remove(&twl_gpiochip); + status = gpiochip_remove(&priv->gpio_chip); if (status < 0) return status; From c111feabe2e200b15300d97107ffc1280bf8de2a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 20 Dec 2012 10:44:11 +0100 Subject: [PATCH 0895/4607] gpio: twl4030: Cache the direction and output states in private data Use more coherent locking in the driver. Use bitfield to store the GPIO direction and if the pin is configured as output store the status also in a bitfiled. In this way we can just look at these bitfields when we need information about the pin status and only reach out to the chip when it is needed. Signed-off-by: Peter Ujfalusi Acked-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/gpio/gpio-twl4030.c | 108 +++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 39 deletions(-) diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 4643f9cd0cae..4d330e36da1d 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -37,7 +37,6 @@ #include - /* * The GPIO "subchip" supports 18 GPIOs which can be configured as * inputs or outputs, with pullups or pulldowns on each pin. Each @@ -64,14 +63,15 @@ /* Mask for GPIO registers when aggregated into a 32-bit integer */ #define GPIO_32_MASK 0x0003ffff -/* Data structures */ -static DEFINE_MUTEX(gpio_lock); - struct gpio_twl4030_priv { struct gpio_chip gpio_chip; + struct mutex mutex; int irq_base; + /* Bitfields for state caching */ unsigned int usage_count; + unsigned int direction; + unsigned int out_state; }; /*----------------------------------------------------------------------*/ @@ -130,7 +130,7 @@ static inline int gpio_twl4030_read(u8 address) /*----------------------------------------------------------------------*/ -static u8 cached_leden; /* protected by gpio_lock */ +static u8 cached_leden; /* The LED lines are open drain outputs ... a FET pulls to GND, so an * external pullup is needed. We could also expose the integrated PWM @@ -144,14 +144,12 @@ static void twl4030_led_set_value(int led, int value) if (led) mask <<= 1; - mutex_lock(&gpio_lock); if (value) cached_leden &= ~mask; else cached_leden |= mask; status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden, TWL4030_LED_LEDEN_REG); - mutex_unlock(&gpio_lock); } static int twl4030_set_gpio_direction(int gpio, int is_input) @@ -162,7 +160,6 @@ static int twl4030_set_gpio_direction(int gpio, int is_input) u8 base = REG_GPIODATADIR1 + d_bnk; int ret = 0; - mutex_lock(&gpio_lock); ret = gpio_twl4030_read(base); if (ret >= 0) { if (is_input) @@ -172,7 +169,6 @@ static int twl4030_set_gpio_direction(int gpio, int is_input) ret = gpio_twl4030_write(base, reg); } - mutex_unlock(&gpio_lock); return ret; } @@ -212,7 +208,7 @@ static int twl_request(struct gpio_chip *chip, unsigned offset) struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); int status = 0; - mutex_lock(&gpio_lock); + mutex_lock(&priv->mutex); /* Support the two LED outputs as output-only GPIOs. */ if (offset >= TWL4030_GPIO_MAX) { @@ -271,7 +267,7 @@ done: if (!status) priv->usage_count |= BIT(offset); - mutex_unlock(&gpio_lock); + mutex_unlock(&priv->mutex); return status; } @@ -279,64 +275,96 @@ static void twl_free(struct gpio_chip *chip, unsigned offset) { struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + mutex_lock(&priv->mutex); if (offset >= TWL4030_GPIO_MAX) { twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1); - return; + goto out; } - mutex_lock(&gpio_lock); - priv->usage_count &= ~BIT(offset); /* on last use, switch off GPIO module */ if (!priv->usage_count) gpio_twl4030_write(REG_GPIO_CTRL, 0x0); - mutex_unlock(&gpio_lock); +out: + mutex_unlock(&priv->mutex); } static int twl_direction_in(struct gpio_chip *chip, unsigned offset) { - return (offset < TWL4030_GPIO_MAX) - ? twl4030_set_gpio_direction(offset, 1) - : -EINVAL; + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + int ret; + + mutex_lock(&priv->mutex); + if (offset < TWL4030_GPIO_MAX) + ret = twl4030_set_gpio_direction(offset, 1); + else + ret = -EINVAL; + + if (!ret) + priv->direction &= ~BIT(offset); + + mutex_unlock(&priv->mutex); + + return ret; } static int twl_get(struct gpio_chip *chip, unsigned offset) { struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + int ret; int status = 0; - if (!(priv->usage_count & BIT(offset))) - return -EPERM; - - if (offset < TWL4030_GPIO_MAX) - status = twl4030_get_gpio_datain(offset); - else if (offset == TWL4030_GPIO_MAX) - status = cached_leden & LEDEN_LEDAON; - else - status = cached_leden & LEDEN_LEDBON; - - return (status < 0) ? 0 : status; -} - -static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) -{ - if (offset < TWL4030_GPIO_MAX) { - twl4030_set_gpio_dataout(offset, value); - return twl4030_set_gpio_direction(offset, 0); - } else { - twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); - return 0; + mutex_lock(&priv->mutex); + if (!(priv->usage_count & BIT(offset))) { + ret = -EPERM; + goto out; } + + if (priv->direction & BIT(offset)) + status = priv->out_state & BIT(offset); + else + status = twl4030_get_gpio_datain(offset); + + ret = (status <= 0) ? 0 : 1; +out: + mutex_unlock(&priv->mutex); + return ret; } static void twl_set(struct gpio_chip *chip, unsigned offset, int value) { + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + + mutex_lock(&priv->mutex); if (offset < TWL4030_GPIO_MAX) twl4030_set_gpio_dataout(offset, value); else twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value); + + if (value) + priv->out_state |= BIT(offset); + else + priv->out_state &= ~BIT(offset); + + mutex_unlock(&priv->mutex); +} + +static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value) +{ + struct gpio_twl4030_priv *priv = to_gpio_twl4030(chip); + + mutex_lock(&priv->mutex); + if (offset < TWL4030_GPIO_MAX) + twl4030_set_gpio_dataout(offset, value); + + priv->direction |= BIT(offset); + mutex_unlock(&priv->mutex); + + twl_set(chip, offset, value); + + return 0; } static int twl_to_irq(struct gpio_chip *chip, unsigned offset) @@ -469,6 +497,8 @@ no_irqs: priv->gpio_chip.ngpio = TWL4030_GPIO_MAX; priv->gpio_chip.dev = &pdev->dev; + mutex_init(&priv->mutex); + if (node) pdata = of_gpio_twl4030(&pdev->dev); From 0ea75e1d26dd4ccbbfe4d1a5931903b15b82ba2a Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Fri, 11 Jan 2013 18:26:10 +0900 Subject: [PATCH 0896/4607] KVM: set_memory_region: Don't jump to out_free unnecessarily This makes the separation between the sanity checks and the rest of the code a bit clearer. Reviewed-by: Marcelo Tosatti Signed-off-by: Takuya Yoshikawa Signed-off-by: Gleb Natapov --- virt/kvm/kvm_main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index f689a6d7f9d4..28eb160fd487 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -778,9 +778,9 @@ int __kvm_set_memory_region(struct kvm *kvm, */ r = -EINVAL; if (npages && old.npages && npages != old.npages) - goto out_free; + goto out; if (!npages && !old.npages) - goto out_free; + goto out; /* Check for overlaps */ r = -EEXIST; @@ -789,7 +789,7 @@ int __kvm_set_memory_region(struct kvm *kvm, continue; if (!((base_gfn + npages <= slot->base_gfn) || (base_gfn >= slot->base_gfn + slot->npages))) - goto out_free; + goto out; } /* Free page dirty bitmap if unneeded */ @@ -890,7 +890,6 @@ out_free: kvm_free_physmem_slot(&new, &old); out: return r; - } EXPORT_SYMBOL_GPL(__kvm_set_memory_region); From 0a706beefb1de71bf8551f9860dc529ef14d6753 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Fri, 11 Jan 2013 18:26:55 +0900 Subject: [PATCH 0897/4607] KVM: set_memory_region: Don't check for overlaps unless we create or move a slot Don't need the check for deleting an existing slot or just modifiying the flags. Reviewed-by: Marcelo Tosatti Signed-off-by: Takuya Yoshikawa Signed-off-by: Gleb Natapov --- virt/kvm/kvm_main.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 28eb160fd487..fca64879f967 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -782,14 +782,16 @@ int __kvm_set_memory_region(struct kvm *kvm, if (!npages && !old.npages) goto out; - /* Check for overlaps */ - r = -EEXIST; - kvm_for_each_memslot(slot, kvm->memslots) { - if (slot->id >= KVM_USER_MEM_SLOTS || slot == memslot) - continue; - if (!((base_gfn + npages <= slot->base_gfn) || - (base_gfn >= slot->base_gfn + slot->npages))) - goto out; + if ((npages && !old.npages) || (base_gfn != old.base_gfn)) { + /* Check for overlaps */ + r = -EEXIST; + kvm_for_each_memslot(slot, kvm->memslots) { + if (slot->id >= KVM_USER_MEM_SLOTS || slot == memslot) + continue; + if (!((base_gfn + npages <= slot->base_gfn) || + (base_gfn >= slot->base_gfn + slot->npages))) + goto out; + } } /* Free page dirty bitmap if unneeded */ From a843fac2536d6d81335d7011dd7ec4e438161dd7 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Fri, 11 Jan 2013 18:27:43 +0900 Subject: [PATCH 0898/4607] KVM: set_memory_region: Remove unnecessary variable memslot One such variable, slot, is enough for holding a pointer temporarily. We also remove another local variable named slot, which is limited in a block, since it is confusing to have the same name in this function. Reviewed-by: Marcelo Tosatti Signed-off-by: Takuya Yoshikawa Signed-off-by: Gleb Natapov --- virt/kvm/kvm_main.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index fca64879f967..5e709ebb7c40 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -728,7 +728,7 @@ int __kvm_set_memory_region(struct kvm *kvm, int r; gfn_t base_gfn; unsigned long npages; - struct kvm_memory_slot *memslot, *slot; + struct kvm_memory_slot *slot; struct kvm_memory_slot old, new; struct kvm_memslots *slots = NULL, *old_memslots; @@ -754,7 +754,7 @@ int __kvm_set_memory_region(struct kvm *kvm, if (mem->guest_phys_addr + mem->memory_size < mem->guest_phys_addr) goto out; - memslot = id_to_memslot(kvm->memslots, mem->slot); + slot = id_to_memslot(kvm->memslots, mem->slot); base_gfn = mem->guest_phys_addr >> PAGE_SHIFT; npages = mem->memory_size >> PAGE_SHIFT; @@ -765,7 +765,7 @@ int __kvm_set_memory_region(struct kvm *kvm, if (!npages) mem->flags &= ~KVM_MEM_LOG_DIRTY_PAGES; - new = old = *memslot; + new = old = *slot; new.id = mem->slot; new.base_gfn = base_gfn; @@ -786,7 +786,8 @@ int __kvm_set_memory_region(struct kvm *kvm, /* Check for overlaps */ r = -EEXIST; kvm_for_each_memslot(slot, kvm->memslots) { - if (slot->id >= KVM_USER_MEM_SLOTS || slot == memslot) + if ((slot->id >= KVM_USER_MEM_SLOTS) || + (slot->id == mem->slot)) continue; if (!((base_gfn + npages <= slot->base_gfn) || (base_gfn >= slot->base_gfn + slot->npages))) @@ -822,8 +823,6 @@ int __kvm_set_memory_region(struct kvm *kvm, } if (!npages || base_gfn != old.base_gfn) { - struct kvm_memory_slot *slot; - r = -ENOMEM; slots = kmemdup(kvm->memslots, sizeof(struct kvm_memslots), GFP_KERNEL); From 4dbd27711cd92bcc364426937a0d5e80f10b1cfb Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Fri, 4 Jan 2013 11:12:43 +0000 Subject: [PATCH 0899/4607] tick: export nohz tick idle symbols for module use Allow drivers such as intel_powerclamp to use these apis for turning on/off ticks during idle. Signed-off-by: Jacob Pan Signed-off-by: Zhang Rui --- kernel/time/tick-sched.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index d58e552d9fd1..a7677579bcc3 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -553,6 +553,7 @@ void tick_nohz_idle_enter(void) local_irq_enable(); } +EXPORT_SYMBOL_GPL(tick_nohz_idle_enter); /** * tick_nohz_irq_exit - update next tick event from interrupt exit @@ -681,6 +682,7 @@ void tick_nohz_idle_exit(void) local_irq_enable(); } +EXPORT_SYMBOL_GPL(tick_nohz_idle_exit); static int tick_nohz_reprogram(struct tick_sched *ts, ktime_t now) { From 29c6fb7be156ae3c0e202c3903087ab6e57d3ad3 Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Fri, 4 Jan 2013 11:12:44 +0000 Subject: [PATCH 0900/4607] x86/nmi: export local_touch_nmi() symbol for modules Signed-off-by: Jacob Pan Signed-off-by: Zhang Rui --- arch/x86/kernel/nmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index f84f5c57de35..60308053fdb2 100644 --- a/arch/x86/kernel/nmi.c +++ b/arch/x86/kernel/nmi.c @@ -509,3 +509,4 @@ void local_touch_nmi(void) { __this_cpu_write(last_nmi_rip, 0); } +EXPORT_SYMBOL_GPL(local_touch_nmi); From 0e2feb17dec9d97f6b44d5af872633f7ffba9ffb Mon Sep 17 00:00:00 2001 From: Philip Avinash Date: Thu, 17 Jan 2013 14:50:02 +0530 Subject: [PATCH 0901/4607] pwm: pwm-tiehrpwm: Low power sleep support In low power modes of AM33XX platforms, peripherals power is cut off. This patch supports low power sleep transition support for EHRPWM driver. Signed-off-by: Philip Avinash Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiehrpwm.c | 83 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 4fcafbfba60e..33e15c4fba02 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -113,6 +113,17 @@ #define NUM_PWM_CHANNEL 2 /* EHRPWM channels */ +struct ehrpwm_context { + u16 tbctl; + u16 tbprd; + u16 cmpa; + u16 cmpb; + u16 aqctla; + u16 aqctlb; + u16 aqsfrc; + u16 aqcsfrc; +}; + struct ehrpwm_pwm_chip { struct pwm_chip chip; unsigned int clk_rate; @@ -120,6 +131,7 @@ struct ehrpwm_pwm_chip { unsigned long period_cycles[NUM_PWM_CHANNEL]; enum pwm_polarity polarity[NUM_PWM_CHANNEL]; struct clk *tbclk; + struct ehrpwm_context ctx; }; static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) @@ -127,6 +139,11 @@ static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) return container_of(chip, struct ehrpwm_pwm_chip, chip); } +static u16 ehrpwm_read(void *base, int offset) +{ + return readw(base + offset); +} + static void ehrpwm_write(void *base, int offset, unsigned int val) { writew(val & 0xFFFF, base + offset); @@ -516,11 +533,77 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev) return pwmchip_remove(&pc->chip); } +void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) +{ + pm_runtime_get_sync(pc->chip.dev); + pc->ctx.tbctl = ehrpwm_read(pc->mmio_base, TBCTL); + pc->ctx.tbprd = ehrpwm_read(pc->mmio_base, TBPRD); + pc->ctx.cmpa = ehrpwm_read(pc->mmio_base, CMPA); + pc->ctx.cmpb = ehrpwm_read(pc->mmio_base, CMPB); + pc->ctx.aqctla = ehrpwm_read(pc->mmio_base, AQCTLA); + pc->ctx.aqctlb = ehrpwm_read(pc->mmio_base, AQCTLB); + pc->ctx.aqsfrc = ehrpwm_read(pc->mmio_base, AQSFRC); + pc->ctx.aqcsfrc = ehrpwm_read(pc->mmio_base, AQCSFRC); + pm_runtime_put_sync(pc->chip.dev); +} + +void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc) +{ + ehrpwm_write(pc->mmio_base, TBPRD, pc->ctx.tbprd); + ehrpwm_write(pc->mmio_base, CMPA, pc->ctx.cmpa); + ehrpwm_write(pc->mmio_base, CMPB, pc->ctx.cmpb); + ehrpwm_write(pc->mmio_base, AQCTLA, pc->ctx.aqctla); + ehrpwm_write(pc->mmio_base, AQCTLB, pc->ctx.aqctlb); + ehrpwm_write(pc->mmio_base, AQSFRC, pc->ctx.aqsfrc); + ehrpwm_write(pc->mmio_base, AQCSFRC, pc->ctx.aqcsfrc); + ehrpwm_write(pc->mmio_base, TBCTL, pc->ctx.tbctl); +} + +static int ehrpwm_pwm_suspend(struct device *dev) +{ + struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); + int i; + + ehrpwm_pwm_save_context(pc); + for (i = 0; i < pc->chip.npwm; i++) { + struct pwm_device *pwm = &pc->chip.pwms[i]; + + if (!test_bit(PWMF_ENABLED, &pwm->flags)) + continue; + + /* Disable explicitly if PWM is running */ + pm_runtime_put_sync(dev); + } + return 0; +} + +static int ehrpwm_pwm_resume(struct device *dev) +{ + struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); + int i; + + for (i = 0; i < pc->chip.npwm; i++) { + struct pwm_device *pwm = &pc->chip.pwms[i]; + + if (!test_bit(PWMF_ENABLED, &pwm->flags)) + continue; + + /* Enable explicitly if PWM was running */ + pm_runtime_get_sync(dev); + } + ehrpwm_pwm_restore_context(pc); + return 0; +} + +static SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend, + ehrpwm_pwm_resume); + static struct platform_driver ehrpwm_pwm_driver = { .driver = { .name = "ehrpwm", .owner = THIS_MODULE, .of_match_table = ehrpwm_of_match, + .pm = &ehrpwm_pwm_pm_ops, }, .probe = ehrpwm_pwm_probe, .remove = ehrpwm_pwm_remove, From 0d75c203effefa4f62ca4d30bf52bd9ec246717f Mon Sep 17 00:00:00 2001 From: Philip Avinash Date: Thu, 17 Jan 2013 14:50:03 +0530 Subject: [PATCH 0902/4607] pwm: pwm-tiecap: Low power sleep support In low power modes of AM33XX platforms, peripherals power is cut off. This patch supports low power sleep transition support for ECAP driver. Signed-off-by: Philip Avinash Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiecap.c | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 5cf016dd9822..05b676db88e6 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -41,10 +41,17 @@ #define ECCTL2_SYNC_SEL_DISA (BIT(7) | BIT(6)) #define ECCTL2_TSCTR_FREERUN BIT(4) +struct ecap_context { + u32 cap3; + u32 cap4; + u16 ecctl2; +}; + struct ecap_pwm_chip { struct pwm_chip chip; unsigned int clk_rate; void __iomem *mmio_base; + struct ecap_context ctx; }; static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip) @@ -288,11 +295,57 @@ static int ecap_pwm_remove(struct platform_device *pdev) return pwmchip_remove(&pc->chip); } +void ecap_pwm_save_context(struct ecap_pwm_chip *pc) +{ + pm_runtime_get_sync(pc->chip.dev); + pc->ctx.ecctl2 = readw(pc->mmio_base + ECCTL2); + pc->ctx.cap4 = readl(pc->mmio_base + CAP4); + pc->ctx.cap3 = readl(pc->mmio_base + CAP3); + pm_runtime_put_sync(pc->chip.dev); +} + +void ecap_pwm_restore_context(struct ecap_pwm_chip *pc) +{ + writel(pc->ctx.cap3, pc->mmio_base + CAP3); + writel(pc->ctx.cap4, pc->mmio_base + CAP4); + writew(pc->ctx.ecctl2, pc->mmio_base + ECCTL2); +} + +static int ecap_pwm_suspend(struct device *dev) +{ + struct ecap_pwm_chip *pc = dev_get_drvdata(dev); + struct pwm_device *pwm = pc->chip.pwms; + + ecap_pwm_save_context(pc); + + /* Disable explicitly if PWM is running */ + if (test_bit(PWMF_ENABLED, &pwm->flags)) + pm_runtime_put_sync(dev); + + return 0; +} + +static int ecap_pwm_resume(struct device *dev) +{ + struct ecap_pwm_chip *pc = dev_get_drvdata(dev); + struct pwm_device *pwm = pc->chip.pwms; + + /* Enable explicitly if PWM was running */ + if (test_bit(PWMF_ENABLED, &pwm->flags)) + pm_runtime_get_sync(dev); + + ecap_pwm_restore_context(pc); + return 0; +} + +static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume); + static struct platform_driver ecap_pwm_driver = { .driver = { .name = "ecap", .owner = THIS_MODULE, .of_match_table = ecap_of_match, + .pm = &ecap_pwm_pm_ops, }, .probe = ecap_pwm_probe, .remove = ecap_pwm_remove, From 79aec9844de339531f05b019644ccaf5dd777144 Mon Sep 17 00:00:00 2001 From: Sam Lang Date: Wed, 19 Dec 2012 09:44:23 -1000 Subject: [PATCH 0903/4607] ceph: Check for err on mds request in atomic_open The error returned by ceph_mdsc_do_request includes errors sending the request, errors on timeout, or any errors coming from the mds. If ceph_mdsc_do_request returns an error, the reply struct will most likely be bogus. We need to bail out and propogate the error instead of overwriting it. Signed-off-by: Sam Lang Reviewed-by: Sage Weil --- fs/ceph/file.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ceph/file.c b/fs/ceph/file.c index d415096800a6..2c71cbd78332 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -243,6 +243,9 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, err = ceph_mdsc_do_request(mdsc, (flags & (O_CREAT|O_TRUNC)) ? dir : NULL, req); + if (err) + goto out_err; + err = ceph_handle_snapdir(req, dentry, err); if (err == 0 && (flags & O_CREAT) && !req->r_reply_info.head->is_dentry) err = ceph_handle_notrace_create(dir, dentry); From 6e8575faa8fa680d59404a4d58d12190667be815 Mon Sep 17 00:00:00 2001 From: Sam Lang Date: Fri, 28 Dec 2012 09:56:46 -0800 Subject: [PATCH 0904/4607] ceph: Check for created flag in response from mds The mds now sends back a created inode if the create request performed the create. If the file already existed, no inode is returned in the reply. This allows ceph to set the created flag in atomic_open so that permissions are properly checked in the case that the file wasn't created by the create call to the mds. To ensure compability with previous kernels, a feature for sending back the inode in the create reply was added, so that the mds will only send back the inode if the client indicates it supports the feature. Signed-off-by: Sam Lang Reviewed-by: Sage Weil --- fs/ceph/file.c | 3 +++ fs/ceph/mds_client.c | 33 ++++++++++++++++++++++++++++-- fs/ceph/mds_client.h | 6 ++++++ include/linux/ceph/ceph_features.h | 5 ++++- 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 2c71cbd78332..22b5b71b5401 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -266,6 +266,9 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, err = finish_no_open(file, dn); } else { dout("atomic_open finish_open on dn %p\n", dn); + if (req->r_op == CEPH_MDS_OP_CREATE && req->r_reply_info.has_create_ino) { + *opened |= FILE_CREATED; + } err = finish_open(file, dentry, ceph_open, opened); } diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 9165eb8309eb..d95842036c8b 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -232,6 +232,30 @@ bad: return -EIO; } +/* + * parse create results + */ +static int parse_reply_info_create(void **p, void *end, + struct ceph_mds_reply_info_parsed *info, + int features) +{ + if (features & CEPH_FEATURE_REPLY_CREATE_INODE) { + if (*p == end) { + info->has_create_ino = false; + } else { + info->has_create_ino = true; + info->ino = ceph_decode_64(p); + } + } + + if (unlikely(*p != end)) + goto bad; + return 0; + +bad: + return -EIO; +} + /* * parse extra results */ @@ -241,8 +265,12 @@ static int parse_reply_info_extra(void **p, void *end, { if (info->head->op == CEPH_MDS_OP_GETFILELOCK) return parse_reply_info_filelock(p, end, info, features); - else + else if (info->head->op == CEPH_MDS_OP_READDIR) return parse_reply_info_dir(p, end, info, features); + else if (info->head->op == CEPH_MDS_OP_CREATE) + return parse_reply_info_create(p, end, info, features); + else + return -EIO; } /* @@ -2170,7 +2198,8 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) mutex_lock(&req->r_fill_mutex); err = ceph_fill_trace(mdsc->fsc->sb, req, req->r_session); if (err == 0) { - if (result == 0 && req->r_op != CEPH_MDS_OP_GETFILELOCK && + if (result == 0 && (req->r_op == CEPH_MDS_OP_READDIR || + req->r_op == CEPH_MDS_OP_LSSNAP) && rinfo->dir_nr) ceph_readdir_prepopulate(req, req->r_session); ceph_unreserve_caps(mdsc, &req->r_caps_reservation); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index dd26846dd71d..567f7c60354e 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -74,6 +74,12 @@ struct ceph_mds_reply_info_parsed { struct ceph_mds_reply_info_in *dir_in; u8 dir_complete, dir_end; }; + + /* for create results */ + struct { + bool has_create_ino; + u64 ino; + }; }; /* encoded blob describing snapshot contexts for certain diff --git a/include/linux/ceph/ceph_features.h b/include/linux/ceph/ceph_features.h index dad579b0c0e6..6b7c6acbb3bf 100644 --- a/include/linux/ceph/ceph_features.h +++ b/include/linux/ceph/ceph_features.h @@ -14,13 +14,16 @@ #define CEPH_FEATURE_DIRLAYOUTHASH (1<<7) /* bits 8-17 defined by user-space; not supported yet here */ #define CEPH_FEATURE_CRUSH_TUNABLES (1<<18) +/* bits 19-25 defined by user-space; not supported yet here */ +#define CEPH_FEATURE_REPLY_CREATE_INODE (1<<27) /* * Features supported. */ #define CEPH_FEATURES_SUPPORTED_DEFAULT \ (CEPH_FEATURE_NOSRCADDR | \ - CEPH_FEATURE_CRUSH_TUNABLES) + CEPH_FEATURE_CRUSH_TUNABLES | \ + CEPH_FEATURE_REPLY_CREATE_INODE) #define CEPH_FEATURES_REQUIRED_DEFAULT \ (CEPH_FEATURE_NOSRCADDR) From a41bad1a9b9f9982eb9b451165724c5f81096683 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 30 Nov 2012 13:49:51 +0800 Subject: [PATCH 0905/4607] ceph: re-calculate truncate_size for strip object Otherwise osd may truncate the object to larger size. Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- net/ceph/osd_client.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index eb9a44478764..267f183b801a 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -76,8 +76,16 @@ int ceph_calc_raw_layout(struct ceph_osd_client *osdc, orig_len - *plen, off, *plen); if (op_has_extent(op->op)) { + u32 osize = le32_to_cpu(layout->fl_object_size); op->extent.offset = objoff; op->extent.length = objlen; + if (op->extent.truncate_size <= off - objoff) { + op->extent.truncate_size = 0; + } else { + op->extent.truncate_size -= off - objoff; + if (op->extent.truncate_size > osize) + op->extent.truncate_size = osize; + } } req->r_num_pages = calc_pages_for(off, *plen); req->r_page_alignment = off & ~PAGE_MASK; From 8a92a119b292012a9bd920b908c3e9f1c512291d Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 4 Jan 2013 14:28:07 +0800 Subject: [PATCH 0906/4607] ceph: move dirty inode to migrating list when clearing auth caps Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/caps.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index a1d9bb30c1bf..a9fe2d5784c9 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -611,8 +611,16 @@ retry: if (flags & CEPH_CAP_FLAG_AUTH) ci->i_auth_cap = cap; - else if (ci->i_auth_cap == cap) + else if (ci->i_auth_cap == cap) { ci->i_auth_cap = NULL; + spin_lock(&mdsc->cap_dirty_lock); + if (!list_empty(&ci->i_dirty_item)) { + dout(" moving %p to cap_dirty_migrating\n", inode); + list_move(&ci->i_dirty_item, + &mdsc->cap_dirty_migrating); + } + spin_unlock(&mdsc->cap_dirty_lock); + } dout("add_cap inode %p (%llx.%llx) cap %p %s now %s seq %d mds%d\n", inode, ceph_vinop(inode), cap, ceph_cap_string(issued), From 395c312b9c535d57db122cbb5b7292223561d0b8 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 4 Jan 2013 14:37:57 +0800 Subject: [PATCH 0907/4607] ceph: allow revoking duplicated caps issued by non-auth MDS Allow revoking duplicated caps issued by non-auth MDS if these caps are also issued by auth MDS. Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/caps.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index a9fe2d5784c9..76b19239c426 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1468,7 +1468,7 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags, struct ceph_mds_client *mdsc = fsc->mdsc; struct inode *inode = &ci->vfs_inode; struct ceph_cap *cap; - int file_wanted, used; + int file_wanted, used, cap_used; int took_snap_rwsem = 0; /* true if mdsc->snap_rwsem held */ int issued, implemented, want, retain, revoking, flushing = 0; int mds = -1; /* keep track of how far we've gone through i_caps list @@ -1571,9 +1571,14 @@ retry_locked: /* NOTE: no side-effects allowed, until we take s_mutex */ + cap_used = used; + if (ci->i_auth_cap && cap != ci->i_auth_cap) + cap_used &= ~ci->i_auth_cap->issued; + revoking = cap->implemented & ~cap->issued; - dout(" mds%d cap %p issued %s implemented %s revoking %s\n", + dout(" mds%d cap %p used %s issued %s implemented %s revoking %s\n", cap->mds, cap, ceph_cap_string(cap->issued), + ceph_cap_string(cap_used), ceph_cap_string(cap->implemented), ceph_cap_string(revoking)); @@ -1601,7 +1606,7 @@ retry_locked: } /* completed revocation? going down and there are no caps? */ - if (revoking && (revoking & used) == 0) { + if (revoking && (revoking & cap_used) == 0) { dout("completed revocation of %s\n", ceph_cap_string(cap->implemented & ~cap->issued)); goto ack; @@ -1678,8 +1683,8 @@ ack: sent++; /* __send_cap drops i_ceph_lock */ - delayed += __send_cap(mdsc, cap, CEPH_CAP_OP_UPDATE, used, want, - retain, flushing, NULL); + delayed += __send_cap(mdsc, cap, CEPH_CAP_OP_UPDATE, cap_used, + want, retain, flushing, NULL); goto retry; /* retake i_ceph_lock and restart our cap scan. */ } From 66f58691c5c820283dd7e4d6fe8649033ed43ceb Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 4 Jan 2013 14:45:18 +0800 Subject: [PATCH 0908/4607] ceph: allocate cap_release message when receiving cap import When client wants to release an imported cap, it's possible there is no reserved cap_release message in corresponding mds session. so __queue_cap_release causes kernel panic. Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/caps.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 76b19239c426..40b5bbe63a39 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2833,6 +2833,9 @@ void ceph_handle_caps(struct ceph_mds_session *session, dout(" mds%d seq %lld cap seq %u\n", session->s_mds, session->s_seq, (unsigned)seq); + if (op == CEPH_CAP_OP_IMPORT) + ceph_add_cap_releases(mdsc, session); + /* lookup ino */ inode = ceph_find_inode(sb, vino); ci = ceph_inode(inode); From 390306c38dd43908f7f7730229999790a773d1d5 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Fri, 4 Jan 2013 15:30:10 +0800 Subject: [PATCH 0909/4607] ceph: check mds_wanted for imported cap The MDS may have incorrect wanted caps after importing caps. So the client should check the value mds has and send cap update if necessary. Signed-off-by: Yan, Zheng Reviewed-by: Sage Weil --- fs/ceph/caps.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 40b5bbe63a39..1e1e02055a2b 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2429,7 +2429,9 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, dout("mds wanted %s -> %s\n", ceph_cap_string(le32_to_cpu(grant->wanted)), ceph_cap_string(wanted)); - grant->wanted = cpu_to_le32(wanted); + /* imported cap may not have correct mds_wanted */ + if (le32_to_cpu(grant->op) == CEPH_CAP_OP_IMPORT) + check_caps = 1; } cap->seq = seq; From 1604f488ac2dcce33c8218e75a000e8c5fb57e61 Mon Sep 17 00:00:00 2001 From: Jim Schutt Date: Fri, 30 Nov 2012 09:15:25 -0700 Subject: [PATCH 0910/4607] libceph: for chooseleaf rules, retry CRUSH map descent from root if leaf is failed Add libceph support for a new CRUSH tunable recently added to Ceph servers. Consider the CRUSH rule step chooseleaf firstn 0 type This rule means that replicas will be chosen in a manner such that each chosen leaf's branch will contain a unique instance of . When an object is re-replicated after a leaf failure, if the CRUSH map uses a chooseleaf rule the remapped replica ends up under the bucket that held the failed leaf. This causes uneven data distribution across the storage cluster, to the point that when all the leaves but one fail under a particular bucket, that remaining leaf holds all the data from its failed peers. This behavior also limits the number of peers that can participate in the re-replication of the data held by the failed leaf, which increases the time required to re-replicate after a failure. For a chooseleaf CRUSH rule, the tree descent has two steps: call them the inner and outer descents. If the tree descent down to is the outer descent, and the descent from down to a leaf is the inner descent, the issue is that a down leaf is detected on the inner descent, so only the inner descent is retried. In order to disperse re-replicated data as widely as possible across a storage cluster after a failure, we want to retry the outer descent. So, fix up crush_choose() to allow the inner descent to return immediately on choosing a failed leaf. Wire this up as a new CRUSH tunable. Note that after this change, for a chooseleaf rule, if the primary OSD in a placement group has failed, choosing a replacement may result in one of the other OSDs in the PG colliding with the new primary. This requires that OSD's data for that PG to need moving as well. This seems unavoidable but should be relatively rare. This corresponds to ceph.git commit 88f218181a9e6d2292e2697fc93797d0f6d6e5dc. Signed-off-by: Jim Schutt Reviewed-by: Sage Weil --- include/linux/ceph/ceph_features.h | 7 +++++-- include/linux/crush/crush.h | 2 ++ net/ceph/crush/mapper.c | 13 ++++++++++--- net/ceph/osdmap.c | 6 ++++++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/include/linux/ceph/ceph_features.h b/include/linux/ceph/ceph_features.h index 6b7c6acbb3bf..2160aab482f6 100644 --- a/include/linux/ceph/ceph_features.h +++ b/include/linux/ceph/ceph_features.h @@ -14,7 +14,9 @@ #define CEPH_FEATURE_DIRLAYOUTHASH (1<<7) /* bits 8-17 defined by user-space; not supported yet here */ #define CEPH_FEATURE_CRUSH_TUNABLES (1<<18) -/* bits 19-25 defined by user-space; not supported yet here */ +/* bits 19-24 defined by user-space; not supported yet here */ +#define CEPH_FEATURE_CRUSH_TUNABLES2 (1<<25) +/* bit 26 defined by user-space; not supported yet here */ #define CEPH_FEATURE_REPLY_CREATE_INODE (1<<27) /* @@ -22,7 +24,8 @@ */ #define CEPH_FEATURES_SUPPORTED_DEFAULT \ (CEPH_FEATURE_NOSRCADDR | \ - CEPH_FEATURE_CRUSH_TUNABLES | \ + CEPH_FEATURE_CRUSH_TUNABLES | \ + CEPH_FEATURE_CRUSH_TUNABLES2 | \ CEPH_FEATURE_REPLY_CREATE_INODE) #define CEPH_FEATURES_REQUIRED_DEFAULT \ diff --git a/include/linux/crush/crush.h b/include/linux/crush/crush.h index 25baa287cff7..6a1101f24cfb 100644 --- a/include/linux/crush/crush.h +++ b/include/linux/crush/crush.h @@ -162,6 +162,8 @@ struct crush_map { __u32 choose_local_fallback_tries; /* choose attempts before giving up */ __u32 choose_total_tries; + /* attempt chooseleaf inner descent once; on failure retry outer descent */ + __u32 chooseleaf_descend_once; }; diff --git a/net/ceph/crush/mapper.c b/net/ceph/crush/mapper.c index 35fce755ce10..96c8a58937db 100644 --- a/net/ceph/crush/mapper.c +++ b/net/ceph/crush/mapper.c @@ -287,6 +287,7 @@ static int is_out(const struct crush_map *map, const __u32 *weight, int item, in * @outpos: our position in that vector * @firstn: true if choosing "first n" items, false if choosing "indep" * @recurse_to_leaf: true if we want one device under each item of given type + * @descend_once: true if we should only try one descent before giving up * @out2: second output vector for leaf items (if @recurse_to_leaf) */ static int crush_choose(const struct crush_map *map, @@ -295,7 +296,7 @@ static int crush_choose(const struct crush_map *map, int x, int numrep, int type, int *out, int outpos, int firstn, int recurse_to_leaf, - int *out2) + int descend_once, int *out2) { int rep; unsigned int ftotal, flocal; @@ -399,6 +400,7 @@ static int crush_choose(const struct crush_map *map, x, outpos+1, 0, out2, outpos, firstn, 0, + map->chooseleaf_descend_once, NULL) <= outpos) /* didn't get leaf */ reject = 1; @@ -422,7 +424,10 @@ reject: ftotal++; flocal++; - if (collide && flocal <= map->choose_local_tries) + if (reject && descend_once) + /* let outer call try again */ + skip_rep = 1; + else if (collide && flocal <= map->choose_local_tries) /* retry locally a few times */ retry_bucket = 1; else if (map->choose_local_fallback_tries > 0 && @@ -485,6 +490,7 @@ int crush_do_rule(const struct crush_map *map, int i, j; int numrep; int firstn; + const int descend_once = 0; if ((__u32)ruleno >= map->max_rules) { dprintk(" bad ruleno %d\n", ruleno); @@ -544,7 +550,8 @@ int crush_do_rule(const struct crush_map *map, curstep->arg2, o+osize, j, firstn, - recurse_to_leaf, c+osize); + recurse_to_leaf, + descend_once, c+osize); } if (recurse_to_leaf) diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index de73214b5d26..ca05871635bc 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -170,6 +170,7 @@ static struct crush_map *crush_decode(void *pbyval, void *end) c->choose_local_tries = 2; c->choose_local_fallback_tries = 5; c->choose_total_tries = 19; + c->chooseleaf_descend_once = 0; ceph_decode_need(p, end, 4*sizeof(u32), bad); magic = ceph_decode_32(p); @@ -336,6 +337,11 @@ static struct crush_map *crush_decode(void *pbyval, void *end) dout("crush decode tunable choose_total_tries = %d", c->choose_total_tries); + ceph_decode_need(p, end, sizeof(u32), done); + c->chooseleaf_descend_once = ceph_decode_32(p); + dout("crush decode tunable chooseleaf_descend_once = %d", + c->chooseleaf_descend_once); + done: dout("crush_decode success\n"); return c; From 7d7c1f6136bac00174842f845babe7fb3483724e Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 15 Jan 2013 18:49:09 -0800 Subject: [PATCH 0911/4607] crush: avoid recursion if we have already collided This saves us some cycles, but does not affect the placement result at all. This corresponds to ceph.git commit 4abb53d4f. Signed-off-by: Sage Weil --- net/ceph/crush/mapper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ceph/crush/mapper.c b/net/ceph/crush/mapper.c index 96c8a58937db..cbd06a91941c 100644 --- a/net/ceph/crush/mapper.c +++ b/net/ceph/crush/mapper.c @@ -392,7 +392,7 @@ static int crush_choose(const struct crush_map *map, } reject = 0; - if (recurse_to_leaf) { + if (!collide && recurse_to_leaf) { if (item < 0) { if (crush_choose(map, map->buckets[-1-item], From fa5199648e273a5e3e80aca41c1eb53700438dc1 Mon Sep 17 00:00:00 2001 From: Simon Que Date: Thu, 17 Jan 2013 11:18:20 -0800 Subject: [PATCH 0912/4607] eCryptfs: initialize payload_len in keystore.c This is meant to remove a compiler warning. It should not make any functional change. payload_len should be initialized when it is passed to write_tag_64_packet() as a pointer. If that call fails, this function should return early, and payload_len won't be used. Signed-off-by: Simon Que Signed-off-by: Tyler Hicks --- fs/ecryptfs/keystore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 2333203a120b..6154cde3a052 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -1150,7 +1150,7 @@ decrypt_pki_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok, struct ecryptfs_message *msg = NULL; char *auth_tok_sig; char *payload; - size_t payload_len; + size_t payload_len = 0; int rc; rc = ecryptfs_get_auth_tok_sig(&auth_tok_sig, auth_tok); From bbcb03e3ce8c76a4ecf0ea7d365f9f0e913fc329 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Thu, 17 Jan 2013 11:36:10 -0800 Subject: [PATCH 0913/4607] eCryptfs: Fix -Wunused-but-set-variable warnings These two variables are no longer used and can be removed. Fixes warnings when building with W=1. Signed-off-by: Tyler Hicks --- fs/ecryptfs/dentry.c | 2 -- fs/ecryptfs/file.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/fs/ecryptfs/dentry.c b/fs/ecryptfs/dentry.c index 1b5d9af937df..bf12ba5dd223 100644 --- a/fs/ecryptfs/dentry.c +++ b/fs/ecryptfs/dentry.c @@ -45,14 +45,12 @@ static int ecryptfs_d_revalidate(struct dentry *dentry, unsigned int flags) { struct dentry *lower_dentry; - struct vfsmount *lower_mnt; int rc = 1; if (flags & LOOKUP_RCU) return -ECHILD; lower_dentry = ecryptfs_dentry_to_lower(dentry); - lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) goto out; rc = lower_dentry->d_op->d_revalidate(lower_dentry, flags); diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index d45ba4568128..bfa52d2ef460 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -199,7 +199,6 @@ static int ecryptfs_open(struct inode *inode, struct file *file) struct dentry *ecryptfs_dentry = file->f_path.dentry; /* Private value of ecryptfs_dentry allocated in * ecryptfs_lookup() */ - struct dentry *lower_dentry; struct ecryptfs_file_info *file_info; mount_crypt_stat = &ecryptfs_superblock_to_private( @@ -222,7 +221,6 @@ static int ecryptfs_open(struct inode *inode, struct file *file) rc = -ENOMEM; goto out; } - lower_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry); crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat; mutex_lock(&crypt_stat->cs_mutex); if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)) { From c3acb18196cf3d7d3db6a5121c1bc674c3fba31f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 7 Dec 2012 09:57:58 -0600 Subject: [PATCH 0914/4607] libceph: reformat __reset_osd() Reformat __reset_osd() into three distinct blocks of code handling the three return cases. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- net/ceph/osd_client.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 267f183b801a..eade41bb7102 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -747,31 +747,35 @@ static void remove_old_osds(struct ceph_osd_client *osdc) */ static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) { - struct ceph_osd_request *req; - int ret = 0; + struct ceph_entity_addr *peer_addr; dout("__reset_osd %p osd%d\n", osd, osd->o_osd); if (list_empty(&osd->o_requests) && list_empty(&osd->o_linger_requests)) { __remove_osd(osdc, osd); - ret = -ENODEV; - } else if (memcmp(&osdc->osdmap->osd_addr[osd->o_osd], - &osd->o_con.peer_addr, - sizeof(osd->o_con.peer_addr)) == 0 && - !ceph_con_opened(&osd->o_con)) { + + return -ENODEV; + } + + peer_addr = &osdc->osdmap->osd_addr[osd->o_osd]; + if (!memcmp(peer_addr, &osd->o_con.peer_addr, sizeof (*peer_addr)) && + !ceph_con_opened(&osd->o_con)) { + struct ceph_osd_request *req; + dout(" osd addr hasn't changed and connection never opened," " letting msgr retry"); /* touch each r_stamp for handle_timeout()'s benfit */ list_for_each_entry(req, &osd->o_requests, r_osd_item) req->r_stamp = jiffies; - ret = -EAGAIN; - } else { - ceph_con_close(&osd->o_con); - ceph_con_open(&osd->o_con, CEPH_ENTITY_TYPE_OSD, osd->o_osd, - &osdc->osdmap->osd_addr[osd->o_osd]); - osd->o_incarnation++; + + return -EAGAIN; } - return ret; + + ceph_con_close(&osd->o_con); + ceph_con_open(&osd->o_con, CEPH_ENTITY_TYPE_OSD, osd->o_osd, peer_addr); + osd->o_incarnation++; + + return 0; } static void __insert_osd(struct ceph_osd_client *osdc, struct ceph_osd *new) From c66c6e0c0b04ce4a152fe02c562dd0752665d580 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 08:39:26 -0500 Subject: [PATCH 0915/4607] rbd: document rbd_spec structure I promised Josh I would document whether there were any restrictions needed for accessing fields of an rbd_spec structure. This adds a big block of comments that documents the structure and how it is used--including the fact that we don't attempt to synchronize access to it. Signed-off-by: Alex Elder Reviewed-by: David Zafman Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 89576a0b3f2e..128978c6a4e0 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -119,7 +119,26 @@ struct rbd_image_header { * An rbd image specification. * * The tuple (pool_id, image_id, snap_id) is sufficient to uniquely - * identify an image. + * identify an image. Each rbd_dev structure includes a pointer to + * an rbd_spec structure that encapsulates this identity. + * + * Each of the id's in an rbd_spec has an associated name. For a + * user-mapped image, the names are supplied and the id's associated + * with them are looked up. For a layered image, a parent image is + * defined by the tuple, and the names are looked up. + * + * An rbd_dev structure contains a parent_spec pointer which is + * non-null if the image it represents is a child in a layered + * image. This pointer will refer to the rbd_spec structure used + * by the parent rbd_dev for its own identity (i.e., the structure + * is shared between the parent and child). + * + * Since these structures are populated once, during the discovery + * phase of image construction, they are effectively immutable so + * we make no effort to synchronize access to them. + * + * Note that code herein does not assume the image name is known (it + * could be a null pointer). */ struct rbd_spec { u64 pool_id; From 69e7a02f633ba7de0aefa87f3f0e3b31e953b09a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 08:39:26 -0500 Subject: [PATCH 0916/4607] rbd: kill rbd_spec->image_name_len There may have been a benefit to hanging on to the length of an image name before, but there is really none now. The only time it's used is when probing for rbd images, so we can just compute the length then. Signed-off-by: Alex Elder Reviewed-by: David Zafman Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 128978c6a4e0..a002984891d7 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -147,7 +147,6 @@ struct rbd_spec { char *image_id; size_t image_id_len; char *image_name; - size_t image_name_len; u64 snap_id; char *snap_name; @@ -2563,15 +2562,15 @@ static char *rbd_dev_image_name(struct rbd_device *rbd_dev) rbd_assert(!rbd_dev->spec->image_name); - image_id_size = sizeof (__le32) + rbd_dev->spec->image_id_len; + len = strlen(rbd_dev->spec->image_id); + image_id_size = sizeof (__le32) + len; image_id = kmalloc(image_id_size, GFP_KERNEL); if (!image_id) return NULL; p = image_id; end = (char *) image_id + image_id_size; - ceph_encode_string(&p, end, rbd_dev->spec->image_id, - (u32) rbd_dev->spec->image_id_len); + ceph_encode_string(&p, end, rbd_dev->spec->image_id, (u32) len); size = sizeof (__le32) + RBD_IMAGE_NAME_LEN_MAX; reply_buf = kmalloc(size, GFP_KERNEL); @@ -2631,14 +2630,12 @@ static int rbd_dev_probe_update_spec(struct rbd_device *rbd_dev) /* Fetch the image name; tolerate failure here */ name = rbd_dev_image_name(rbd_dev); - if (name) { - rbd_dev->spec->image_name_len = strlen(name); + if (name) rbd_dev->spec->image_name = (char *) name; - } else { + else pr_warning(RBD_DRV_NAME "%d " "unable to get image name for image id %s\n", rbd_dev->major, rbd_dev->spec->image_id); - } /* Look up the snapshot name. */ @@ -3252,7 +3249,7 @@ static int rbd_add_parse_args(const char *buf, if (!*spec->pool_name) goto out_err; /* Missing pool name */ - spec->image_name = dup_token(&buf, &spec->image_name_len); + spec->image_name = dup_token(&buf, NULL); if (!spec->image_name) goto out_mem; if (!*spec->image_name) @@ -3342,7 +3339,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) * First, see if the format 2 image id file exists, and if * so, get the image's persistent id from it. */ - size = sizeof (RBD_ID_PREFIX) + rbd_dev->spec->image_name_len; + size = sizeof (RBD_ID_PREFIX) + strlen(rbd_dev->spec->image_name); object_name = kmalloc(size, GFP_NOIO); if (!object_name) return -ENOMEM; @@ -3400,7 +3397,7 @@ static int rbd_dev_v1_probe(struct rbd_device *rbd_dev) /* Record the header object name for this rbd image. */ - size = rbd_dev->spec->image_name_len + sizeof (RBD_SUFFIX); + size = strlen(rbd_dev->spec->image_name) + sizeof (RBD_SUFFIX); rbd_dev->header_name = kmalloc(size, GFP_KERNEL); if (!rbd_dev->header_name) { ret = -ENOMEM; From 979ed480a2722ad8d9f87054635158f652a1241e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 08:39:26 -0500 Subject: [PATCH 0917/4607] rbd: kill rbd_spec->image_id_len There is no real benefit to keeping the length of an image id, so get rid of it. Signed-off-by: Alex Elder Reviewed-by: David Zafman Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index a002984891d7..e01dbb12ad03 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -145,7 +145,6 @@ struct rbd_spec { char *pool_name; char *image_id; - size_t image_id_len; char *image_name; u64 snap_id; @@ -2492,7 +2491,6 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) void *end; char *image_id; u64 overlap; - size_t len = 0; int ret; parent_spec = rbd_spec_alloc(); @@ -2526,13 +2524,12 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) if (parent_spec->pool_id == CEPH_NOPOOL) goto out; /* No parent? No problem. */ - image_id = ceph_extract_encoded_string(&p, end, &len, GFP_KERNEL); + image_id = ceph_extract_encoded_string(&p, end, NULL, GFP_KERNEL); if (IS_ERR(image_id)) { ret = PTR_ERR(image_id); goto out_err; } parent_spec->image_id = image_id; - parent_spec->image_id_len = len; ceph_decode_64_safe(&p, end, parent_spec->snap_id, out_err); ceph_decode_64_safe(&p, end, overlap, out_err); @@ -3368,8 +3365,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) p = response; rbd_dev->spec->image_id = ceph_extract_encoded_string(&p, p + RBD_IMAGE_ID_LEN_MAX, - &rbd_dev->spec->image_id_len, - GFP_NOIO); + NULL, GFP_NOIO); if (IS_ERR(rbd_dev->spec->image_id)) { ret = PTR_ERR(rbd_dev->spec->image_id); rbd_dev->spec->image_id = NULL; @@ -3393,7 +3389,6 @@ static int rbd_dev_v1_probe(struct rbd_device *rbd_dev) rbd_dev->spec->image_id = kstrdup("", GFP_KERNEL); if (!rbd_dev->spec->image_id) return -ENOMEM; - rbd_dev->spec->image_id_len = 0; /* Record the header object name for this rbd image. */ @@ -3443,7 +3438,7 @@ static int rbd_dev_v2_probe(struct rbd_device *rbd_dev) * Image id was filled in by the caller. Record the header * object name for this rbd image. */ - size = sizeof (RBD_HEADER_PREFIX) + rbd_dev->spec->image_id_len; + size = sizeof (RBD_HEADER_PREFIX) + strlen(rbd_dev->spec->image_id); rbd_dev->header_name = kmalloc(size, GFP_KERNEL); if (!rbd_dev->header_name) return -ENOMEM; From 4caf35f9ecdca1feef1d2e5e223b78e52ffbea87 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 08:39:27 -0500 Subject: [PATCH 0918/4607] rbd: use kmemdup() This replaces two kmalloc()/memcpy() combinations with a single call to kmemdup(). Signed-off-by: Alex Elder Reviewed-by: David Zafman Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index e01dbb12ad03..d97611e2b4ee 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -3151,11 +3151,9 @@ static inline char *dup_token(const char **buf, size_t *lenp) size_t len; len = next_token(buf); - dup = kmalloc(len + 1, GFP_KERNEL); + dup = kmemdup(*buf, len + 1, GFP_KERNEL); if (!dup) return NULL; - - memcpy(dup, *buf, len); *(dup + len) = '\0'; *buf += len; @@ -3264,10 +3262,9 @@ static int rbd_add_parse_args(const char *buf, ret = -ENAMETOOLONG; goto out_err; } - spec->snap_name = kmalloc(len + 1, GFP_KERNEL); + spec->snap_name = kmemdup(buf, len + 1, GFP_KERNEL); if (!spec->snap_name) goto out_mem; - memcpy(spec->snap_name, buf, len); *(spec->snap_name + len) = '\0'; /* Initialize all rbd options to the defaults */ From dd5f049dbdf973d9bceebef1fd73647a5ede6732 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 08:39:27 -0500 Subject: [PATCH 0919/4607] ceph: define ceph_encode_8_safe() It's kind of a silly macro, but ceph_encode_8_safe() is the only one missing from an otherwise pretty complete set. It's not used, but neither are a couple of the others in this set. While in there, insert some whitespace to tidy up the alignment of the line-terminating backslashes in some of the macro definitions. Signed-off-by: Alex Elder Reviewed-by: Dan Mick --- include/linux/ceph/decode.h | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/include/linux/ceph/decode.h b/include/linux/ceph/decode.h index 4bbf2db45f46..cd679f2d348b 100644 --- a/include/linux/ceph/decode.h +++ b/include/linux/ceph/decode.h @@ -52,10 +52,10 @@ static inline int ceph_has_room(void **p, void *end, size_t n) return end >= *p && n <= end - *p; } -#define ceph_decode_need(p, end, n, bad) \ - do { \ - if (!likely(ceph_has_room(p, end, n))) \ - goto bad; \ +#define ceph_decode_need(p, end, n, bad) \ + do { \ + if (!likely(ceph_has_room(p, end, n))) \ + goto bad; \ } while (0) #define ceph_decode_64_safe(p, end, v, bad) \ @@ -99,8 +99,8 @@ static inline int ceph_has_room(void **p, void *end, size_t n) * * There are two possible failures: * - converting the string would require accessing memory at or - * beyond the "end" pointer provided (-E - * - memory could not be allocated for the result + * beyond the "end" pointer provided (-ERANGE) + * - memory could not be allocated for the result (-ENOMEM) */ static inline char *ceph_extract_encoded_string(void **p, void *end, size_t *lenp, gfp_t gfp) @@ -217,10 +217,10 @@ static inline void ceph_encode_string(void **p, void *end, *p += len; } -#define ceph_encode_need(p, end, n, bad) \ - do { \ - if (!likely(ceph_has_room(p, end, n))) \ - goto bad; \ +#define ceph_encode_need(p, end, n, bad) \ + do { \ + if (!likely(ceph_has_room(p, end, n))) \ + goto bad; \ } while (0) #define ceph_encode_64_safe(p, end, v, bad) \ @@ -231,12 +231,17 @@ static inline void ceph_encode_string(void **p, void *end, #define ceph_encode_32_safe(p, end, v, bad) \ do { \ ceph_encode_need(p, end, sizeof(u32), bad); \ - ceph_encode_32(p, v); \ + ceph_encode_32(p, v); \ } while (0) #define ceph_encode_16_safe(p, end, v, bad) \ do { \ ceph_encode_need(p, end, sizeof(u16), bad); \ - ceph_encode_16(p, v); \ + ceph_encode_16(p, v); \ + } while (0) +#define ceph_encode_8_safe(p, end, v, bad) \ + do { \ + ceph_encode_need(p, end, sizeof(u8), bad); \ + ceph_encode_8(p, v); \ } while (0) #define ceph_encode_copy_safe(p, end, pv, n, bad) \ From 06ecc6cbf7a60cd5abd9fd2bda8ae69b395c2be3 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 10:17:15 -0500 Subject: [PATCH 0920/4607] rbd: define and use rbd_warn() Define a new function rbd_warn() that produces a boilerplate warning message, identifying in the resulting message the affected rbd device in the best way available. Use it in a few places that now use pr_warning(). Signed-off-by: Alex Elder Reviewed-by: Dan Mick Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d97611e2b4ee..635b81d0ebdb 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -294,6 +294,33 @@ static struct device rbd_root_dev = { .release = rbd_root_dev_release, }; +static __printf(2, 3) +void rbd_warn(struct rbd_device *rbd_dev, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + if (!rbd_dev) + printk(KERN_WARNING "%s: %pV\n", RBD_DRV_NAME, &vaf); + else if (rbd_dev->disk) + printk(KERN_WARNING "%s: %s: %pV\n", + RBD_DRV_NAME, rbd_dev->disk->disk_name, &vaf); + else if (rbd_dev->spec && rbd_dev->spec->image_name) + printk(KERN_WARNING "%s: image %s: %pV\n", + RBD_DRV_NAME, rbd_dev->spec->image_name, &vaf); + else if (rbd_dev->spec && rbd_dev->spec->image_id) + printk(KERN_WARNING "%s: id %s: %pV\n", + RBD_DRV_NAME, rbd_dev->spec->image_id, &vaf); + else /* punt */ + printk(KERN_WARNING "%s: rbd_dev %p: %pV\n", + RBD_DRV_NAME, rbd_dev, &vaf); + va_end(args); +} + #ifdef RBD_DEBUG #define rbd_assert(expr) \ if (unlikely(!(expr))) { \ @@ -1403,8 +1430,8 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) (unsigned int) opcode); rc = rbd_dev_refresh(rbd_dev, &hver); if (rc) - pr_warning(RBD_DRV_NAME "%d got notification but failed to " - " update snaps: %d\n", rbd_dev->major, rc); + rbd_warn(rbd_dev, "got notification but failed to " + " update snaps: %d\n", rc); rbd_req_sync_notify_ack(rbd_dev, hver, notify_id); } @@ -1767,15 +1794,13 @@ rbd_dev_v1_header_read(struct rbd_device *rbd_dev, u64 *version) goto out_err; if (WARN_ON((size_t) ret < size)) { ret = -ENXIO; - pr_warning("short header read for image %s" - " (want %zd got %d)\n", - rbd_dev->spec->image_name, size, ret); + rbd_warn(rbd_dev, "short header read (want %zd got %d)", + size, ret); goto out_err; } if (!rbd_dev_ondisk_valid(ondisk)) { ret = -ENXIO; - pr_warning("invalid header for image %s\n", - rbd_dev->spec->image_name); + rbd_warn(rbd_dev, "invalid header"); goto out_err; } @@ -2630,9 +2655,7 @@ static int rbd_dev_probe_update_spec(struct rbd_device *rbd_dev) if (name) rbd_dev->spec->image_name = (char *) name; else - pr_warning(RBD_DRV_NAME "%d " - "unable to get image name for image id %s\n", - rbd_dev->major, rbd_dev->spec->image_id); + rbd_warn(rbd_dev, "unable to get image name"); /* Look up the snapshot name. */ From 4fb5d671399e83d3875593db2f56d5b57fcb104f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 10:17:15 -0500 Subject: [PATCH 0921/4607] rbd: add warning messages for missing arguments Tell the user (via dmesg) what was wrong with the arguments provided via /sys/bus/rbd/add. Signed-off-by: Alex Elder Reviewed-by: Dan Mick --- drivers/block/rbd.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 635b81d0ebdb..31da8c538480 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -3244,8 +3244,10 @@ static int rbd_add_parse_args(const char *buf, /* The first four tokens are required */ len = next_token(&buf); - if (!len) - return -EINVAL; /* Missing monitor address(es) */ + if (!len) { + rbd_warn(NULL, "no monitor address(es) provided"); + return -EINVAL; + } mon_addrs = buf; mon_addrs_size = len + 1; buf += len; @@ -3254,8 +3256,10 @@ static int rbd_add_parse_args(const char *buf, options = dup_token(&buf, NULL); if (!options) return -ENOMEM; - if (!*options) - goto out_err; /* Missing options */ + if (!*options) { + rbd_warn(NULL, "no options provided"); + goto out_err; + } spec = rbd_spec_alloc(); if (!spec) @@ -3264,14 +3268,18 @@ static int rbd_add_parse_args(const char *buf, spec->pool_name = dup_token(&buf, NULL); if (!spec->pool_name) goto out_mem; - if (!*spec->pool_name) - goto out_err; /* Missing pool name */ + if (!*spec->pool_name) { + rbd_warn(NULL, "no pool name provided"); + goto out_err; + } spec->image_name = dup_token(&buf, NULL); if (!spec->image_name) goto out_mem; - if (!*spec->image_name) - goto out_err; /* Missing image name */ + if (!*spec->image_name) { + rbd_warn(NULL, "no image name provided"); + goto out_err; + } /* * Snapshot name is optional; default is to use "-" From f5400b7a0e78a53edce8960a079aa022640849a1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 10:17:15 -0500 Subject: [PATCH 0922/4607] rbd: add a warning in bio_chain_clone_range() Add a warning in bio_chain_clone_range() to help a user determine what exactly might have led to a failure. There is only one; please say something if you disagree with the following reasoning. There are three places this can return abnormally: - Initially, if there is nothing to clone. It turns out that right now this cannot happen anyway. The test is in place because the code below it doesn't work if those conditions don't hold. As such they could be assertions but since I can return a null to indicate an error I just do that instead. I have not added a warning here because it won't happen. - While processing bio's, if none remain but there are supposed to be more bytes to clone. Here I have added a warning. - If bio_clone_range() returns a null pointer. That function will have already produced a warning (at least the first time, via WARN_ON_ONCE()) to distinguish the cause of the error. The only exception is memory exhaustion, and I'd rather not pepper the code with warnings in all those spots. So no warning is added in that place. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 31da8c538480..ce6c0cbb3d7a 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -993,8 +993,10 @@ static struct bio *bio_chain_clone_range(struct bio **bio_src, unsigned int bi_size; struct bio *bio; - if (!bi) + if (!bi) { + rbd_warn(NULL, "bio_chain exhausted with %u left", len); goto out_err; /* EINVAL; ran out of bio's */ + } bi_size = min_t(unsigned int, bi->bi_size - off, len); bio = bio_clone_range(bi, off, bi_size, gfpmask); if (!bio) From 935dc89f3e29e2ef1d7c89778cdb9f37ff36e94b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 1 Nov 2012 10:17:15 -0500 Subject: [PATCH 0923/4607] rbd: add warnings to rbd_dev_probe_update_spec() Josh suggested adding warnings to this function to help users diagnose problems. Other than memory allocatino errors, there are two places where errors can be returned. Both represent problems that should have been caught earlier, and as such might well have been handled with BUG_ON() calls. But if either ever did manage to happen, it will be reported. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index ce6c0cbb3d7a..530a1217a0fa 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -2644,8 +2644,11 @@ static int rbd_dev_probe_update_spec(struct rbd_device *rbd_dev) osdc = &rbd_dev->rbd_client->client->osdc; name = ceph_pg_pool_name_by_id(osdc->osdmap, rbd_dev->spec->pool_id); - if (!name) - return -EIO; /* pool id too large (>= 2^31) */ + if (!name) { + rbd_warn(rbd_dev, "there is no pool with id %llu", + rbd_dev->spec->pool_id); /* Really a BUG() */ + return -EIO; + } rbd_dev->spec->pool_name = kstrdup(name, GFP_KERNEL); if (!rbd_dev->spec->pool_name) @@ -2663,6 +2666,8 @@ static int rbd_dev_probe_update_spec(struct rbd_device *rbd_dev) name = rbd_snap_name(rbd_dev, rbd_dev->spec->snap_id); if (!name) { + rbd_warn(rbd_dev, "no snapshot with id %llu", + rbd_dev->spec->snap_id); /* Really a BUG() */ ret = -EIO; goto out_err; } From 111d61a25ec11c1837ac0fd81bf4b845d3327fb7 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Thu, 17 Jan 2013 12:19:35 -0800 Subject: [PATCH 0924/4607] eCryptfs: Fix -Wmissing-prototypes warnings Mark two inode operation fuctions as static. Fixes warnings when building with W=1. Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index cc7709e7c508..ddd961ba2cf9 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -999,8 +999,8 @@ out: return rc; } -int ecryptfs_getattr_link(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) +static int ecryptfs_getattr_link(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) { struct ecryptfs_mount_crypt_stat *mount_crypt_stat; int rc = 0; @@ -1021,8 +1021,8 @@ int ecryptfs_getattr_link(struct vfsmount *mnt, struct dentry *dentry, return rc; } -int ecryptfs_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) +static int ecryptfs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) { struct kstat lower_stat; int rc; From 725afc97c91cd5f71a015143da5095d20cd668b9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: [PATCH 0925/4607] rbd: standardize rbd_request variable names There are two names used for items of rbd_request structure type: "req" and "req_data". The former name is also used to represent items of pointers to struct ceph_osd_request. Change all variables that have these names so they are instead called "rbd_req" consistently. Signed-off-by: Alex Elder Reviewed-by: Dan Mick Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 50 +++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 530a1217a0fa..0091fa466f83 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1088,10 +1088,12 @@ static void rbd_coll_end_req_index(struct request *rq, spin_unlock_irq(q->queue_lock); } -static void rbd_coll_end_req(struct rbd_request *req, +static void rbd_coll_end_req(struct rbd_request *rbd_req, int ret, u64 len) { - rbd_coll_end_req_index(req->rq, req->coll, req->coll_index, ret, len); + rbd_coll_end_req_index(rbd_req->rq, + rbd_req->coll, rbd_req->coll_index, + ret, len); } /* @@ -1119,12 +1121,12 @@ static int rbd_do_request(struct request *rq, int ret; u64 bno; struct timespec mtime = CURRENT_TIME; - struct rbd_request *req_data; + struct rbd_request *rbd_req; struct ceph_osd_request_head *reqhead; struct ceph_osd_client *osdc; - req_data = kzalloc(sizeof(*req_data), GFP_NOIO); - if (!req_data) { + rbd_req = kzalloc(sizeof(*rbd_req), GFP_NOIO); + if (!rbd_req) { if (coll) rbd_coll_end_req_index(rq, coll, coll_index, -ENOMEM, len); @@ -1132,8 +1134,8 @@ static int rbd_do_request(struct request *rq, } if (coll) { - req_data->coll = coll; - req_data->coll_index = coll_index; + rbd_req->coll = coll; + rbd_req->coll_index = coll_index; } dout("rbd_do_request object_name=%s ofs=%llu len=%llu coll=%p[%d]\n", @@ -1150,12 +1152,12 @@ static int rbd_do_request(struct request *rq, req->r_callback = rbd_cb; - req_data->rq = rq; - req_data->bio = bio; - req_data->pages = pages; - req_data->len = len; + rbd_req->rq = rq; + rbd_req->bio = bio; + rbd_req->pages = pages; + rbd_req->len = len; - req->r_priv = req_data; + req->r_priv = rbd_req; reqhead = req->r_request->front.iov_base; reqhead->snapid = cpu_to_le64(CEPH_NOSNAP); @@ -1200,11 +1202,11 @@ static int rbd_do_request(struct request *rq, return ret; done_err: - bio_chain_put(req_data->bio); + bio_chain_put(rbd_req->bio); ceph_osdc_put_request(req); done_pages: - rbd_coll_end_req(req_data, ret, len); - kfree(req_data); + rbd_coll_end_req(rbd_req, ret, len); + kfree(rbd_req); return ret; } @@ -1213,7 +1215,7 @@ done_pages: */ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) { - struct rbd_request *req_data = req->r_priv; + struct rbd_request *rbd_req = req->r_priv; struct ceph_osd_reply_head *replyhead; struct ceph_osd_op *op; __s32 rc; @@ -1232,20 +1234,20 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) (unsigned long long) bytes, read_op, (int) rc); if (rc == -ENOENT && read_op) { - zero_bio_chain(req_data->bio, 0); + zero_bio_chain(rbd_req->bio, 0); rc = 0; - } else if (rc == 0 && read_op && bytes < req_data->len) { - zero_bio_chain(req_data->bio, bytes); - bytes = req_data->len; + } else if (rc == 0 && read_op && bytes < rbd_req->len) { + zero_bio_chain(rbd_req->bio, bytes); + bytes = rbd_req->len; } - rbd_coll_end_req(req_data, rc, bytes); + rbd_coll_end_req(rbd_req, rc, bytes); - if (req_data->bio) - bio_chain_put(req_data->bio); + if (rbd_req->bio) + bio_chain_put(rbd_req->bio); ceph_osdc_put_request(req); - kfree(req_data); + kfree(rbd_req); } static void rbd_simple_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) From 5f29ddd4f0954ad6c84e28b934773f128840f064 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: [PATCH 0926/4607] rbd: standardize ceph_osd_request variable names There are spots where a ceph_osds_request pointer variable is given the name "req". Since we're dealing with (at least) three types of requests (block layer, rbd, and osd), I find this slightly distracting. Change such instances to use "osd_req" consistently to make the abstraction represented a little more obvious. Signed-off-by: Alex Elder Reviewed-by: Dan Mick Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 60 +++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 0091fa466f83..85131de90012 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1111,12 +1111,12 @@ static int rbd_do_request(struct request *rq, struct ceph_osd_req_op *ops, struct rbd_req_coll *coll, int coll_index, - void (*rbd_cb)(struct ceph_osd_request *req, - struct ceph_msg *msg), + void (*rbd_cb)(struct ceph_osd_request *, + struct ceph_msg *), struct ceph_osd_request **linger_req, u64 *ver) { - struct ceph_osd_request *req; + struct ceph_osd_request *osd_req; struct ceph_file_layout *layout; int ret; u64 bno; @@ -1143,67 +1143,68 @@ static int rbd_do_request(struct request *rq, (unsigned long long) len, coll, coll_index); osdc = &rbd_dev->rbd_client->client->osdc; - req = ceph_osdc_alloc_request(osdc, flags, snapc, ops, + osd_req = ceph_osdc_alloc_request(osdc, flags, snapc, ops, false, GFP_NOIO, pages, bio); - if (!req) { + if (!osd_req) { ret = -ENOMEM; goto done_pages; } - req->r_callback = rbd_cb; + osd_req->r_callback = rbd_cb; rbd_req->rq = rq; rbd_req->bio = bio; rbd_req->pages = pages; rbd_req->len = len; - req->r_priv = rbd_req; + osd_req->r_priv = rbd_req; - reqhead = req->r_request->front.iov_base; + reqhead = osd_req->r_request->front.iov_base; reqhead->snapid = cpu_to_le64(CEPH_NOSNAP); - strncpy(req->r_oid, object_name, sizeof(req->r_oid)); - req->r_oid_len = strlen(req->r_oid); + strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); + osd_req->r_oid_len = strlen(osd_req->r_oid); - layout = &req->r_file_layout; + layout = &osd_req->r_file_layout; memset(layout, 0, sizeof(*layout)); layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); layout->fl_stripe_count = cpu_to_le32(1); layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); layout->fl_pg_pool = cpu_to_le32((int) rbd_dev->spec->pool_id); ret = ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno, - req, ops); + osd_req, ops); rbd_assert(ret == 0); - ceph_osdc_build_request(req, ofs, &len, + ceph_osdc_build_request(osd_req, ofs, &len, ops, snapc, &mtime, - req->r_oid, req->r_oid_len); + osd_req->r_oid, osd_req->r_oid_len); if (linger_req) { - ceph_osdc_set_request_linger(osdc, req); - *linger_req = req; + ceph_osdc_set_request_linger(osdc, osd_req); + *linger_req = osd_req; } - ret = ceph_osdc_start_request(osdc, req, false); + ret = ceph_osdc_start_request(osdc, osd_req, false); if (ret < 0) goto done_err; if (!rbd_cb) { - ret = ceph_osdc_wait_request(osdc, req); + u64 version; + + ret = ceph_osdc_wait_request(osdc, osd_req); + version = le64_to_cpu(osd_req->r_reassert_version.version); if (ver) - *ver = le64_to_cpu(req->r_reassert_version.version); - dout("reassert_ver=%llu\n", - (unsigned long long) - le64_to_cpu(req->r_reassert_version.version)); - ceph_osdc_put_request(req); + *ver = version; + dout("reassert_ver=%llu\n", (unsigned long long) version); + ceph_osdc_put_request(osd_req); } return ret; done_err: bio_chain_put(rbd_req->bio); - ceph_osdc_put_request(req); + ceph_osdc_put_request(osd_req); done_pages: rbd_coll_end_req(rbd_req, ret, len); kfree(rbd_req); @@ -1213,9 +1214,9 @@ done_pages: /* * Ceph osd op callback */ -static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) +static void rbd_req_cb(struct ceph_osd_request *osd_req, struct ceph_msg *msg) { - struct rbd_request *rbd_req = req->r_priv; + struct rbd_request *rbd_req = osd_req->r_priv; struct ceph_osd_reply_head *replyhead; struct ceph_osd_op *op; __s32 rc; @@ -1246,13 +1247,14 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) if (rbd_req->bio) bio_chain_put(rbd_req->bio); - ceph_osdc_put_request(req); + ceph_osdc_put_request(osd_req); kfree(rbd_req); } -static void rbd_simple_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) +static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, + struct ceph_msg *msg) { - ceph_osdc_put_request(req); + ceph_osdc_put_request(osd_req); } /* From 8986cb37b1cf1f54b35f062f0a12dc68dd89f311 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: [PATCH 0927/4607] rbd: be picky about osd request status type The result field in a ceph osd reply header is a signed 32-bit type, but rbd code often casually uses int to represent it. The following changes the types of variables that handle this result value to be "s32" instead of "int" to be completely explicit about it. Only at the point we pass that result to __blk_end_request() does the type get converted to the plain old int defined for that interface. There is almost certainly no binary impact of this change, but I prefer to show the exact size and signedness of the value since we know it. Signed-off-by: Alex Elder Reviewed-by: Dan Mick --- drivers/block/rbd.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 85131de90012..8d93b6a649d8 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -171,7 +171,7 @@ struct rbd_client { */ struct rbd_req_status { int done; - int rc; + s32 rc; u64 bytes; }; @@ -1053,13 +1053,13 @@ static void rbd_destroy_ops(struct ceph_osd_req_op *ops) static void rbd_coll_end_req_index(struct request *rq, struct rbd_req_coll *coll, int index, - int ret, u64 len) + s32 ret, u64 len) { struct request_queue *q; int min, max, i; dout("rbd_coll_end_req_index %p index %d ret %d len %llu\n", - coll, index, ret, (unsigned long long) len); + coll, index, (int)ret, (unsigned long long)len); if (!rq) return; @@ -1080,7 +1080,7 @@ static void rbd_coll_end_req_index(struct request *rq, max++; for (i = min; istatus[i].rc, + __blk_end_request(rq, (int)coll->status[i].rc, coll->status[i].bytes); coll->num_done++; kref_put(&coll->kref, rbd_coll_release); @@ -1089,7 +1089,7 @@ static void rbd_coll_end_req_index(struct request *rq, } static void rbd_coll_end_req(struct rbd_request *rbd_req, - int ret, u64 len) + s32 ret, u64 len) { rbd_coll_end_req_index(rbd_req->rq, rbd_req->coll, rbd_req->coll_index, @@ -1129,7 +1129,7 @@ static int rbd_do_request(struct request *rq, if (!rbd_req) { if (coll) rbd_coll_end_req_index(rq, coll, coll_index, - -ENOMEM, len); + (s32)-ENOMEM, len); return -ENOMEM; } @@ -1206,7 +1206,7 @@ done_err: bio_chain_put(rbd_req->bio); ceph_osdc_put_request(osd_req); done_pages: - rbd_coll_end_req(rbd_req, ret, len); + rbd_coll_end_req(rbd_req, (s32)ret, len); kfree(rbd_req); return ret; } @@ -1219,7 +1219,7 @@ static void rbd_req_cb(struct ceph_osd_request *osd_req, struct ceph_msg *msg) struct rbd_request *rbd_req = osd_req->r_priv; struct ceph_osd_reply_head *replyhead; struct ceph_osd_op *op; - __s32 rc; + s32 rc; u64 bytes; int read_op; @@ -1227,14 +1227,14 @@ static void rbd_req_cb(struct ceph_osd_request *osd_req, struct ceph_msg *msg) replyhead = msg->front.iov_base; WARN_ON(le32_to_cpu(replyhead->num_ops) == 0); op = (void *)(replyhead + 1); - rc = le32_to_cpu(replyhead->result); + rc = (s32)le32_to_cpu(replyhead->result); bytes = le64_to_cpu(op->extent.length); read_op = (le16_to_cpu(op->op) == CEPH_OSD_OP_READ); dout("rbd_req_cb bytes=%llu readop=%d rc=%d\n", (unsigned long long) bytes, read_op, (int) rc); - if (rc == -ENOENT && read_op) { + if (rc == (s32)-ENOENT && read_op) { zero_bio_chain(rbd_req->bio, 0); rc = 0; } else if (rc == 0 && read_op && bytes < rbd_req->len) { @@ -1679,7 +1679,8 @@ static void rbd_rq_fn(struct request_queue *q) bio_chain, coll, cur_seg); else rbd_coll_end_req_index(rq, coll, cur_seg, - -ENOMEM, chain_size); + (s32)-ENOMEM, + chain_size); size -= chain_size; ofs += chain_size; From 8295cda7ceceb7b25f9a12cd21bbfb6206e28a6d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: [PATCH 0928/4607] rbd: encapsulate handling for a single request In rbd_rq_fn(), requests are fetched from the block layer and each request is processed, looping through the request's list of bio's until they've all been consumed. Separate the handling for a single request into its own function to make it a bit easier to see what's going on. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 119 +++++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 8d93b6a649d8..738d1e4c0ab5 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1583,6 +1583,64 @@ static struct rbd_req_coll *rbd_alloc_coll(int num_reqs) return coll; } +static int rbd_dev_do_request(struct request *rq, + struct rbd_device *rbd_dev, + struct ceph_snap_context *snapc, + u64 ofs, unsigned int size, + struct bio *bio_chain) +{ + int num_segs; + struct rbd_req_coll *coll; + unsigned int bio_offset; + int cur_seg = 0; + + dout("%s 0x%x bytes at 0x%llx\n", + rq_data_dir(rq) == WRITE ? "write" : "read", + size, (unsigned long long) blk_rq_pos(rq) * SECTOR_SIZE); + + num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size); + if (num_segs <= 0) + return num_segs; + + coll = rbd_alloc_coll(num_segs); + if (!coll) + return -ENOMEM; + + bio_offset = 0; + do { + u64 limit = rbd_segment_length(rbd_dev, ofs, size); + unsigned int clone_size; + struct bio *bio_clone; + + BUG_ON(limit > (u64)UINT_MAX); + clone_size = (unsigned int)limit; + dout("bio_chain->bi_vcnt=%hu\n", bio_chain->bi_vcnt); + + kref_get(&coll->kref); + + /* Pass a cloned bio chain via an osd request */ + + bio_clone = bio_chain_clone_range(&bio_chain, + &bio_offset, clone_size, + GFP_ATOMIC); + if (bio_clone) + (void)rbd_do_op(rq, rbd_dev, snapc, + ofs, clone_size, + bio_clone, coll, cur_seg); + else + rbd_coll_end_req_index(rq, coll, cur_seg, + (s32)-ENOMEM, + clone_size); + size -= clone_size; + ofs += clone_size; + + cur_seg++; + } while (size > 0); + kref_put(&coll->kref, rbd_coll_release); + + return 0; +} + /* * block device queue callback */ @@ -1596,10 +1654,8 @@ static void rbd_rq_fn(struct request_queue *q) bool do_write; unsigned int size; u64 ofs; - int num_segs, cur_seg = 0; - struct rbd_req_coll *coll; struct ceph_snap_context *snapc; - unsigned int bio_offset; + int result; dout("fetched request\n"); @@ -1637,60 +1693,11 @@ static void rbd_rq_fn(struct request_queue *q) ofs = blk_rq_pos(rq) * SECTOR_SIZE; bio = rq->bio; - dout("%s 0x%x bytes at 0x%llx\n", - do_write ? "write" : "read", - size, (unsigned long long) blk_rq_pos(rq) * SECTOR_SIZE); - - num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size); - if (num_segs <= 0) { - spin_lock_irq(q->queue_lock); - __blk_end_request_all(rq, num_segs); - ceph_put_snap_context(snapc); - continue; - } - coll = rbd_alloc_coll(num_segs); - if (!coll) { - spin_lock_irq(q->queue_lock); - __blk_end_request_all(rq, -ENOMEM); - ceph_put_snap_context(snapc); - continue; - } - - bio_offset = 0; - do { - u64 limit = rbd_segment_length(rbd_dev, ofs, size); - unsigned int chain_size; - struct bio *bio_chain; - - BUG_ON(limit > (u64) UINT_MAX); - chain_size = (unsigned int) limit; - dout("rq->bio->bi_vcnt=%hu\n", rq->bio->bi_vcnt); - - kref_get(&coll->kref); - - /* Pass a cloned bio chain via an osd request */ - - bio_chain = bio_chain_clone_range(&bio, - &bio_offset, chain_size, - GFP_ATOMIC); - if (bio_chain) - (void) rbd_do_op(rq, rbd_dev, snapc, - ofs, chain_size, - bio_chain, coll, cur_seg); - else - rbd_coll_end_req_index(rq, coll, cur_seg, - (s32)-ENOMEM, - chain_size); - size -= chain_size; - ofs += chain_size; - - cur_seg++; - } while (size > 0); - kref_put(&coll->kref, rbd_coll_release); - - spin_lock_irq(q->queue_lock); - + result = rbd_dev_do_request(rq, rbd_dev, snapc, ofs, size, bio); ceph_put_snap_context(snapc); + spin_lock_irq(q->queue_lock); + if (!size || result < 0) + __blk_end_request_all(rq, result); } } From 2c10d571169abab9181e0c91b259e0c4e392bc5b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 20 Dec 2012 21:24:07 +0100 Subject: [PATCH 0929/4607] drm/i915: wake up all pageflip waiters Otherwise it seems like we can get stuck with concurrent waiters. Right now this /shouldn't/ be a problem, since all pending pageflip waiters are serialized by the one mode_config.mutex, so there's at most on waiter. But better paranoid than sorry, since this is tricky code. v2: WARN_ON(waitqueue_active) before waiting, as suggested by Chris Wilson. Acked-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8c36a11a9a57..40b6b5e9d6ef 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2220,6 +2220,8 @@ intel_finish_fb(struct drm_framebuffer *old_fb) bool was_interruptible = dev_priv->mm.interruptible; int ret; + WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); + wait_event(dev_priv->pending_flip_queue, atomic_read(&dev_priv->mm.wedged) || atomic_read(&obj->pending_flip) == 0); @@ -2887,6 +2889,8 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) if (crtc->fb == NULL) return; + WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); + wait_event(dev_priv->pending_flip_queue, !intel_crtc_has_pending_flip(crtc)); @@ -6824,7 +6828,7 @@ static void do_intel_finish_page_flip(struct drm_device *dev, obj = work->old_fb_obj; - wake_up(&dev_priv->pending_flip_queue); + wake_up_all(&dev_priv->pending_flip_queue); queue_work(dev_priv->wq, &work->work); From af5163acd8319dbc901bb59a8d503d8bb774d88b Mon Sep 17 00:00:00 2001 From: Egbert Eich Date: Thu, 10 Jan 2013 10:02:39 -0500 Subject: [PATCH 0930/4607] drm/i915: Remove pch_rq_mask from struct drm_i915_private. This variable is only used locally in the irq postinstall functions for ivybridge and ironlake. Signed-off-by: Egbert Eich Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/i915_irq.c | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b1b1b7350ca4..910cc0b545a6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -697,7 +697,6 @@ typedef struct drm_i915_private { u32 pipestat[2]; u32 irq_mask; u32 gt_irq_mask; - u32 pch_irq_mask; u32 hotplug_supported_mask; struct work_struct hotplug_work; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6689a61b02a3..202813128441 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1892,6 +1892,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev) DE_AUX_CHANNEL_A; u32 render_irqs; u32 hotplug_mask; + u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -1935,10 +1936,10 @@ static int ironlake_irq_postinstall(struct drm_device *dev) SDE_AUX_MASK); } - dev_priv->pch_irq_mask = ~hotplug_mask; + pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, dev_priv->pch_irq_mask); + I915_WRITE(SDEIMR, pch_irq_mask); I915_WRITE(SDEIER, hotplug_mask); POSTING_READ(SDEIER); @@ -1966,6 +1967,7 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_AUX_CHANNEL_A_IVB; u32 render_irqs; u32 hotplug_mask; + u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -1995,10 +1997,10 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) SDE_PORTD_HOTPLUG_CPT | SDE_GMBUS_CPT | SDE_AUX_MASK_CPT); - dev_priv->pch_irq_mask = ~hotplug_mask; + pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, dev_priv->pch_irq_mask); + I915_WRITE(SDEIMR, pch_irq_mask); I915_WRITE(SDEIER, hotplug_mask); POSTING_READ(SDEIER); From d865110cc2345d67752cd7e1350b391c34feb2aa Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 7 Jan 2013 21:47:33 +0200 Subject: [PATCH 0931/4607] drm/i915: merge get_gtt_alignment/get_unfenced_gtt_alignment() The two functions are rather similar, so merge them. Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 5 ++- drivers/gpu/drm/i915/i915_gem.c | 44 ++++---------------------- drivers/gpu/drm/i915/i915_gem_tiling.c | 6 ++-- 3 files changed, 12 insertions(+), 43 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 910cc0b545a6..0b09361d37ae 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1565,9 +1565,8 @@ void i915_gem_free_all_phys_object(struct drm_device *dev); void i915_gem_release(struct drm_device *dev, struct drm_file *file); uint32_t -i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, - uint32_t size, - int tiling_mode); +i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size, + int tiling_mode, bool fenced); int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, enum i915_cache_level cache_level); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e6cc020ea32c..2166b61053c6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1463,16 +1463,15 @@ i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) * Return the required GTT alignment for an object, taking into account * potential fence register mapping. */ -static uint32_t -i915_gem_get_gtt_alignment(struct drm_device *dev, - uint32_t size, - int tiling_mode) +uint32_t +i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size, + int tiling_mode, bool fenced) { /* * Minimum alignment is 4k (GTT page size), but might be greater * if a fence register is needed for the object. */ - if (INTEL_INFO(dev)->gen >= 4 || + if (INTEL_INFO(dev)->gen >= 4 || (!fenced && IS_G33(dev)) || tiling_mode == I915_TILING_NONE) return 4096; @@ -1483,35 +1482,6 @@ i915_gem_get_gtt_alignment(struct drm_device *dev, return i915_gem_get_gtt_size(dev, size, tiling_mode); } -/** - * i915_gem_get_unfenced_gtt_alignment - return required GTT alignment for an - * unfenced object - * @dev: the device - * @size: size of the object - * @tiling_mode: tiling mode of the object - * - * Return the required GTT alignment for an object, only taking into account - * unfenced tiled surface requirements. - */ -uint32_t -i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, - uint32_t size, - int tiling_mode) -{ - /* - * Minimum alignment is 4k (GTT page size) for sane hw. - */ - if (INTEL_INFO(dev)->gen >= 4 || IS_G33(dev) || - tiling_mode == I915_TILING_NONE) - return 4096; - - /* Previous hardware however needs to be aligned to a power-of-two - * tile height. The simplest method for determining this is to reuse - * the power-of-tile object size. - */ - return i915_gem_get_gtt_size(dev, size, tiling_mode); -} - static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; @@ -2934,11 +2904,11 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, obj->tiling_mode); fence_alignment = i915_gem_get_gtt_alignment(dev, obj->base.size, - obj->tiling_mode); + obj->tiling_mode, true); unfenced_alignment = - i915_gem_get_unfenced_gtt_alignment(dev, + i915_gem_get_gtt_alignment(dev, obj->base.size, - obj->tiling_mode); + obj->tiling_mode, false); if (alignment == 0) alignment = map_and_fenceable ? fence_alignment : diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 65f1d4f3f775..cb71ded7122e 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -374,9 +374,9 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, /* Rebind if we need a change of alignment */ if (!obj->map_and_fenceable) { u32 unfenced_alignment = - i915_gem_get_unfenced_gtt_alignment(dev, - obj->base.size, - args->tiling_mode); + i915_gem_get_gtt_alignment(dev, obj->base.size, + args->tiling_mode, + false); if (obj->gtt_offset & (unfenced_alignment - 1)) ret = i915_gem_object_unbind(obj); } From 56c844e539f1f6f5768c5f73f119e6f4aed9d320 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 7 Jan 2013 21:47:34 +0200 Subject: [PATCH 0932/4607] drm/i915: merge {i965, sandybridge}_write_fence_reg() The two functions are rather similar, so merge them. Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 44 +++++++++++---------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 2166b61053c6..718574eb66de 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2507,52 +2507,38 @@ int i915_gpu_idle(struct drm_device *dev) return 0; } -static void sandybridge_write_fence_reg(struct drm_device *dev, int reg, - struct drm_i915_gem_object *obj) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - uint64_t val; - - if (obj) { - u32 size = obj->gtt_space->size; - - val = (uint64_t)((obj->gtt_offset + size - 4096) & - 0xfffff000) << 32; - val |= obj->gtt_offset & 0xfffff000; - val |= (uint64_t)((obj->stride / 128) - 1) << - SANDYBRIDGE_FENCE_PITCH_SHIFT; - - if (obj->tiling_mode == I915_TILING_Y) - val |= 1 << I965_FENCE_TILING_Y_SHIFT; - val |= I965_FENCE_REG_VALID; - } else - val = 0; - - I915_WRITE64(FENCE_REG_SANDYBRIDGE_0 + reg * 8, val); - POSTING_READ(FENCE_REG_SANDYBRIDGE_0 + reg * 8); -} - static void i965_write_fence_reg(struct drm_device *dev, int reg, struct drm_i915_gem_object *obj) { drm_i915_private_t *dev_priv = dev->dev_private; + int fence_reg; + int fence_pitch_shift; uint64_t val; + if (INTEL_INFO(dev)->gen >= 6) { + fence_reg = FENCE_REG_SANDYBRIDGE_0; + fence_pitch_shift = SANDYBRIDGE_FENCE_PITCH_SHIFT; + } else { + fence_reg = FENCE_REG_965_0; + fence_pitch_shift = I965_FENCE_PITCH_SHIFT; + } + if (obj) { u32 size = obj->gtt_space->size; val = (uint64_t)((obj->gtt_offset + size - 4096) & 0xfffff000) << 32; val |= obj->gtt_offset & 0xfffff000; - val |= ((obj->stride / 128) - 1) << I965_FENCE_PITCH_SHIFT; + val |= (uint64_t)((obj->stride / 128) - 1) << fence_pitch_shift; if (obj->tiling_mode == I915_TILING_Y) val |= 1 << I965_FENCE_TILING_Y_SHIFT; val |= I965_FENCE_REG_VALID; } else val = 0; - I915_WRITE64(FENCE_REG_965_0 + reg * 8, val); - POSTING_READ(FENCE_REG_965_0 + reg * 8); + fence_reg += reg * 8; + I915_WRITE64(fence_reg, val); + POSTING_READ(fence_reg); } static void i915_write_fence_reg(struct drm_device *dev, int reg, @@ -2636,7 +2622,7 @@ static void i915_gem_write_fence(struct drm_device *dev, int reg, { switch (INTEL_INFO(dev)->gen) { case 7: - case 6: sandybridge_write_fence_reg(dev, reg, obj); break; + case 6: case 5: case 4: i965_write_fence_reg(dev, reg, obj); break; case 3: i915_write_fence_reg(dev, reg, obj); break; From 0fa877965194fa79fe87944844185d90cfc35352 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 7 Jan 2013 21:47:35 +0200 Subject: [PATCH 0933/4607] drm/i915: use gtt_get_size() instead of open coding it Signed-off-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/i915_gem_tiling.c | 13 +------------ 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0b09361d37ae..35ecabc711ed 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1564,6 +1564,8 @@ void i915_gem_detach_phys_object(struct drm_device *dev, void i915_gem_free_all_phys_object(struct drm_device *dev); void i915_gem_release(struct drm_device *dev, struct drm_file *file); +uint32_t +i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode); uint32_t i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size, int tiling_mode, bool fenced); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 718574eb66de..313bdbaba3cd 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1435,7 +1435,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj) obj->fault_mappable = false; } -static uint32_t +uint32_t i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) { uint32_t gtt_size; diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index cb71ded7122e..e76f0d8470a1 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -272,18 +272,7 @@ i915_gem_object_fence_ok(struct drm_i915_gem_object *obj, int tiling_mode) return false; } - /* - * Previous chips need to be aligned to the size of the smallest - * fence register that can contain the object. - */ - if (INTEL_INFO(obj->base.dev)->gen == 3) - size = 1024*1024; - else - size = 512*1024; - - while (size < obj->base.size) - size <<= 1; - + size = i915_gem_get_gtt_size(obj->base.dev, obj->base.size, tiling_mode); if (obj->gtt_space->size != size) return false; From dd624afd533bdc87b8c10835515a0c8b2b9868b1 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 15 Jan 2013 12:39:35 +0000 Subject: [PATCH 0934/4607] drm/i915: Add a debug interface to forcibly evict and shrink our object caches As a means to investigate some bad system behaviour related to the purging of the active, inactive and unbound lists, it is useful to be able to manually control when those lists should be cleared. v2: use _safe list iterators as we kick objects from the list as we walk. Signed-off-by: Chris Wilson [danvet: Add a small comment explaining why we don't need to check and wait for gpu resets, acked by Chris on irc.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 104 ++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 2 +- 3 files changed, 106 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f7d88e99ebf0..f94418bd1ed2 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1776,6 +1776,102 @@ static const struct file_operations i915_ring_stop_fops = { .llseek = default_llseek, }; +#define DROP_UNBOUND 0x1 +#define DROP_BOUND 0x2 +#define DROP_RETIRE 0x4 +#define DROP_ACTIVE 0x8 +#define DROP_ALL (DROP_UNBOUND | \ + DROP_BOUND | \ + DROP_RETIRE | \ + DROP_ACTIVE) +static ssize_t +i915_drop_caches_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + char buf[20]; + int len; + + len = snprintf(buf, sizeof(buf), "0x%08x\n", DROP_ALL); + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_drop_caches_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj, *next; + char buf[20]; + int val = 0, ret; + + if (cnt > 0) { + if (cnt > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + val = simple_strtoul(buf, NULL, 0); + } + + DRM_DEBUG_DRIVER("Dropping caches: 0x%08x\n", val); + + /* No need to check and wait for gpu resets, only libdrm auto-restarts + * on ioctls on -EAGAIN. */ + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + if (val & DROP_ACTIVE) { + ret = i915_gpu_idle(dev); + if (ret) + goto unlock; + } + + if (val & (DROP_RETIRE | DROP_ACTIVE)) + i915_gem_retire_requests(dev); + + if (val & DROP_BOUND) { + list_for_each_entry_safe(obj, next, &dev_priv->mm.inactive_list, mm_list) + if (obj->pin_count == 0) { + ret = i915_gem_object_unbind(obj); + if (ret) + goto unlock; + } + } + + if (val & DROP_UNBOUND) { + list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list) + if (obj->pages_pin_count == 0) { + ret = i915_gem_object_put_pages(obj); + if (ret) + goto unlock; + } + } + +unlock: + mutex_unlock(&dev->struct_mutex); + + return ret ?: cnt; +} + +static const struct file_operations i915_drop_caches_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = i915_drop_caches_read, + .write = i915_drop_caches_write, + .llseek = default_llseek, +}; + static ssize_t i915_max_freq_read(struct file *filp, char __user *ubuf, @@ -2172,6 +2268,12 @@ int i915_debugfs_init(struct drm_minor *minor) if (ret) return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, + "i915_gem_drop_caches", + &i915_drop_caches_fops); + if (ret) + return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, "i915_error_state", &i915_error_state_fops); @@ -2203,6 +2305,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor) 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops, 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_drop_caches_fops, + 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_ring_stop_fops, 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_error_state_fops, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 35ecabc711ed..8f5b2816fc03 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1445,6 +1445,7 @@ int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj, bool nonblocking); void i915_gem_object_unpin(struct drm_i915_gem_object *obj); int __must_check i915_gem_object_unbind(struct drm_i915_gem_object *obj); +int i915_gem_object_put_pages(struct drm_i915_gem_object *obj); void i915_gem_release_mmap(struct drm_i915_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 313bdbaba3cd..9d33fb3f8d42 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1662,7 +1662,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) kfree(obj->pages); } -static int +int i915_gem_object_put_pages(struct drm_i915_gem_object *obj) { const struct drm_i915_gem_object_ops *ops = obj->ops; From 43e28f092b2fa4ebc46bdc210134a80610815785 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jan 2013 10:53:09 +0000 Subject: [PATCH 0935/4607] drm/i915: Bail if we attempt to allocate pages for a purged object Move the existing checking inside bind_to_gtt() to the more appropriate layer in order to prevent recreation of the pages after they have been explicitly truncated. Signed-off-by: Chris Wilson Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 9d33fb3f8d42..a2bb18914329 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1828,6 +1828,11 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj) if (obj->pages) return 0; + if (obj->madv != I915_MADV_WILLNEED) { + DRM_ERROR("Attempting to obtain a purgeable object\n"); + return -EINVAL; + } + BUG_ON(obj->pages_pin_count); ret = ops->get_pages(obj); @@ -2440,7 +2445,7 @@ int i915_gem_object_unbind(struct drm_i915_gem_object *obj) { drm_i915_private_t *dev_priv = obj->base.dev->dev_private; - int ret = 0; + int ret; if (obj->gtt_space == NULL) return 0; @@ -2880,11 +2885,6 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, bool mappable, fenceable; int ret; - if (obj->madv != I915_MADV_WILLNEED) { - DRM_ERROR("Attempting to bind a purgeable object\n"); - return -EINVAL; - } - fence_size = i915_gem_get_gtt_size(dev, obj->base.size, obj->tiling_mode); From 419fa72a1973c9ba2fd2c2505dc889f54b857459 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jan 2013 10:53:13 +0000 Subject: [PATCH 0936/4607] drm/i915: Mark a temporary allocation for copy-from-user as such The difference is that the kernel will then know that this memory will be reclaimable in the near future. Signed-off-by: Chris Wilson Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 163bb52bd3b3..986eb98f0d25 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1113,7 +1113,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data, } exec2_list = kmalloc(sizeof(*exec2_list)*args->buffer_count, - GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); + GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); if (exec2_list == NULL) exec2_list = drm_malloc_ab(sizeof(*exec2_list), args->buffer_count); From 3b96eff447b4ca34ca8ccd42e2651be2955f34b4 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jan 2013 10:53:14 +0000 Subject: [PATCH 0937/4607] drm/i915: Take the handle idr spinlock once for looking up the exec objects Signed-off-by: Chris Wilson Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 86 ++++++++++++---------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 986eb98f0d25..da103c179e3f 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -69,6 +69,46 @@ eb_add_object(struct eb_objects *eb, struct drm_i915_gem_object *obj) &eb->buckets[obj->exec_handle & eb->and]); } +static int +eb_lookup_objects(struct eb_objects *eb, + struct drm_i915_gem_exec_object2 *exec, + int count, + struct drm_file *file, + struct list_head *objects) +{ + int i; + + spin_lock(&file->table_lock); + for (i = 0; i < count; i++) { + struct drm_i915_gem_object *obj; + + obj = to_intel_bo(idr_find(&file->object_idr, exec[i].handle)); + if (obj == NULL) { + spin_unlock(&file->table_lock); + DRM_DEBUG("Invalid object handle %d at index %d\n", + exec[i].handle, i); + return -ENOENT; + } + + if (!list_empty(&obj->exec_list)) { + spin_unlock(&file->table_lock); + DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n", + obj, exec[i].handle, i); + return -EINVAL; + } + + drm_gem_object_reference(&obj->base); + list_add_tail(&obj->exec_list, objects); + + obj->exec_handle = exec[i].handle; + obj->exec_entry = &exec[i]; + eb_add_object(eb, obj); + } + spin_unlock(&file->table_lock); + + return 0; +} + static struct drm_i915_gem_object * eb_get_object(struct eb_objects *eb, unsigned long handle) { @@ -550,21 +590,9 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, /* reacquire the objects */ eb_reset(eb); - for (i = 0; i < count; i++) { - obj = to_intel_bo(drm_gem_object_lookup(dev, file, - exec[i].handle)); - if (&obj->base == NULL) { - DRM_DEBUG("Invalid object handle %d at index %d\n", - exec[i].handle, i); - ret = -ENOENT; - goto err; - } - - list_add_tail(&obj->exec_list, objects); - obj->exec_handle = exec[i].handle; - obj->exec_entry = &exec[i]; - eb_add_object(eb, obj); - } + ret = eb_lookup_objects(eb, exec, count, file, objects); + if (ret) + goto err; ret = i915_gem_execbuffer_reserve(ring, file, objects); if (ret) @@ -872,31 +900,9 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, /* Look up object handles */ INIT_LIST_HEAD(&objects); - for (i = 0; i < args->buffer_count; i++) { - struct drm_i915_gem_object *obj; - - obj = to_intel_bo(drm_gem_object_lookup(dev, file, - exec[i].handle)); - if (&obj->base == NULL) { - DRM_DEBUG("Invalid object handle %d at index %d\n", - exec[i].handle, i); - /* prevent error path from reading uninitialized data */ - ret = -ENOENT; - goto err; - } - - if (!list_empty(&obj->exec_list)) { - DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n", - obj, exec[i].handle, i); - ret = -EINVAL; - goto err; - } - - list_add_tail(&obj->exec_list, &objects); - obj->exec_handle = exec[i].handle; - obj->exec_entry = &exec[i]; - eb_add_object(eb, obj); - } + ret = eb_lookup_objects(eb, exec, args->buffer_count, file, &objects); + if (ret) + goto err; /* take note of the batch buffer before we might reorder the lists */ batch_obj = list_entry(objects.prev, From bcffc3faa692d6b2ef734e4f0c8f097175284db6 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jan 2013 10:53:15 +0000 Subject: [PATCH 0938/4607] drm/i915: Move the execbuffer objects list from the stack into the tracker Instead of passing around the eb-objects hashtable and a separate object list, we can include the object list into the eb-objects structure for convenience. Signed-off-by: Chris Wilson Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 58 ++++++++++------------ 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index da103c179e3f..386677f8fd38 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -34,6 +34,7 @@ #include struct eb_objects { + struct list_head objects; int and; struct hlist_head buckets[0]; }; @@ -53,6 +54,7 @@ eb_create(int size) return eb; eb->and = count - 1; + INIT_LIST_HEAD(&eb->objects); return eb; } @@ -73,8 +75,7 @@ static int eb_lookup_objects(struct eb_objects *eb, struct drm_i915_gem_exec_object2 *exec, int count, - struct drm_file *file, - struct list_head *objects) + struct drm_file *file) { int i; @@ -98,7 +99,7 @@ eb_lookup_objects(struct eb_objects *eb, } drm_gem_object_reference(&obj->base); - list_add_tail(&obj->exec_list, objects); + list_add_tail(&obj->exec_list, &eb->objects); obj->exec_handle = exec[i].handle; obj->exec_entry = &exec[i]; @@ -129,6 +130,15 @@ eb_get_object(struct eb_objects *eb, unsigned long handle) static void eb_destroy(struct eb_objects *eb) { + while (!list_empty(&eb->objects)) { + struct drm_i915_gem_object *obj; + + obj = list_first_entry(&eb->objects, + struct drm_i915_gem_object, + exec_list); + list_del_init(&obj->exec_list); + drm_gem_object_unreference(&obj->base); + } kfree(eb); } @@ -328,8 +338,7 @@ i915_gem_execbuffer_relocate_object_slow(struct drm_i915_gem_object *obj, static int i915_gem_execbuffer_relocate(struct drm_device *dev, - struct eb_objects *eb, - struct list_head *objects) + struct eb_objects *eb) { struct drm_i915_gem_object *obj; int ret = 0; @@ -342,7 +351,7 @@ i915_gem_execbuffer_relocate(struct drm_device *dev, * lockdep complains vehemently. */ pagefault_disable(); - list_for_each_entry(obj, objects, exec_list) { + list_for_each_entry(obj, &eb->objects, exec_list) { ret = i915_gem_execbuffer_relocate_object(obj, eb); if (ret) break; @@ -531,7 +540,6 @@ static int i915_gem_execbuffer_relocate_slow(struct drm_device *dev, struct drm_file *file, struct intel_ring_buffer *ring, - struct list_head *objects, struct eb_objects *eb, struct drm_i915_gem_exec_object2 *exec, int count) @@ -542,8 +550,8 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, int i, total, ret; /* We may process another execbuffer during the unlock... */ - while (!list_empty(objects)) { - obj = list_first_entry(objects, + while (!list_empty(&eb->objects)) { + obj = list_first_entry(&eb->objects, struct drm_i915_gem_object, exec_list); list_del_init(&obj->exec_list); @@ -590,15 +598,15 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, /* reacquire the objects */ eb_reset(eb); - ret = eb_lookup_objects(eb, exec, count, file, objects); + ret = eb_lookup_objects(eb, exec, count, file); if (ret) goto err; - ret = i915_gem_execbuffer_reserve(ring, file, objects); + ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects); if (ret) goto err; - list_for_each_entry(obj, objects, exec_list) { + list_for_each_entry(obj, &eb->objects, exec_list) { int offset = obj->exec_entry - exec; ret = i915_gem_execbuffer_relocate_object_slow(obj, eb, reloc + reloc_offset[offset]); @@ -756,7 +764,6 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct drm_i915_gem_exec_object2 *exec) { drm_i915_private_t *dev_priv = dev->dev_private; - struct list_head objects; struct eb_objects *eb; struct drm_i915_gem_object *batch_obj; struct drm_clip_rect *cliprects = NULL; @@ -899,28 +906,26 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } /* Look up object handles */ - INIT_LIST_HEAD(&objects); - ret = eb_lookup_objects(eb, exec, args->buffer_count, file, &objects); + ret = eb_lookup_objects(eb, exec, args->buffer_count, file); if (ret) goto err; /* take note of the batch buffer before we might reorder the lists */ - batch_obj = list_entry(objects.prev, + batch_obj = list_entry(eb->objects.prev, struct drm_i915_gem_object, exec_list); /* Move the objects en-masse into the GTT, evicting if necessary. */ - ret = i915_gem_execbuffer_reserve(ring, file, &objects); + ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects); if (ret) goto err; /* The objects are in their final locations, apply the relocations. */ - ret = i915_gem_execbuffer_relocate(dev, eb, &objects); + ret = i915_gem_execbuffer_relocate(dev, eb); if (ret) { if (ret == -EFAULT) { ret = i915_gem_execbuffer_relocate_slow(dev, file, ring, - &objects, eb, - exec, + eb, exec, args->buffer_count); BUG_ON(!mutex_is_locked(&dev->struct_mutex)); } @@ -943,7 +948,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, if (flags & I915_DISPATCH_SECURE && !batch_obj->has_global_gtt_mapping) i915_gem_gtt_bind_object(batch_obj, batch_obj->cache_level); - ret = i915_gem_execbuffer_move_to_gpu(ring, &objects); + ret = i915_gem_execbuffer_move_to_gpu(ring, &eb->objects); if (ret) goto err; @@ -997,20 +1002,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, trace_i915_gem_ring_dispatch(ring, intel_ring_get_seqno(ring), flags); - i915_gem_execbuffer_move_to_active(&objects, ring); + i915_gem_execbuffer_move_to_active(&eb->objects, ring); i915_gem_execbuffer_retire_commands(dev, file, ring); err: eb_destroy(eb); - while (!list_empty(&objects)) { - struct drm_i915_gem_object *obj; - - obj = list_first_entry(&objects, - struct drm_i915_gem_object, - exec_list); - list_del_init(&obj->exec_list); - drm_gem_object_unreference(&obj->base); - } mutex_unlock(&dev->struct_mutex); From ed5982e6ce5f106abcbf071f80730db344a6da42 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 17 Jan 2013 22:23:36 +0100 Subject: [PATCH 0939/4607] drm/i915: Allow userspace to hint that the relocations were known Userspace is able to hint to the kernel that its command stream and auxiliary state buffers already hold the correct presumed addresses and so the relocation process may be skipped if the kernel does not need to move any buffers in preparation for the execbuffer. Thus for the common case where the allotment of buffers is static between batches, we can avoid the overhead of individually checking the relocation entries. Note that this requires userspace to supply the domain tracking and requests for workarounds itself that would otherwise be computed based upon the relocation entries. Using copywinwin10 as an example that is dependent upon emitting a lot of relocations (2 per operation), we see improvements of: c2d/gm45: 618000.0/sec to 632000.0/sec. i3-330m: 748000.0/sec to 830000.0/sec. (measured relative to a baseline with neither optimisations applied). Signed-off-by: Chris Wilson Reviewed-by: Imre Deak [danvet: Fixup merge conflict in userspace header due to different baseline trees.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 + drivers/gpu/drm/i915/i915_gem_execbuffer.c | 68 ++++++++++++++-------- include/uapi/drm/i915_drm.h | 14 +++++ 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 6d8a1dc74934..a6e047d533ec 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -992,6 +992,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_PINNED_BATCHES: value = 1; break; + case I915_PARAM_HAS_EXEC_NO_RELOC: + value = 1; + break; default: DRM_DEBUG_DRIVER("Unknown parameter %d\n", param->param); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 386677f8fd38..34f6cdffa9f8 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -373,7 +373,8 @@ need_reloc_mappable(struct drm_i915_gem_object *obj) static int i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj, - struct intel_ring_buffer *ring) + struct intel_ring_buffer *ring, + bool *need_reloc) { struct drm_i915_private *dev_priv = obj->base.dev->dev_private; struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; @@ -414,7 +415,20 @@ i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj, obj->has_aliasing_ppgtt_mapping = 1; } - entry->offset = obj->gtt_offset; + if (entry->offset != obj->gtt_offset) { + entry->offset = obj->gtt_offset; + *need_reloc = true; + } + + if (entry->flags & EXEC_OBJECT_WRITE) { + obj->base.pending_read_domains = I915_GEM_DOMAIN_RENDER; + obj->base.pending_write_domain = I915_GEM_DOMAIN_RENDER; + } + + if (entry->flags & EXEC_OBJECT_NEEDS_GTT && + !obj->has_global_gtt_mapping) + i915_gem_gtt_bind_object(obj, obj->cache_level); + return 0; } @@ -440,7 +454,8 @@ i915_gem_execbuffer_unreserve_object(struct drm_i915_gem_object *obj) static int i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, struct drm_file *file, - struct list_head *objects) + struct list_head *objects, + bool *need_relocs) { struct drm_i915_gem_object *obj; struct list_head ordered_objects; @@ -468,7 +483,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, else list_move_tail(&obj->exec_list, &ordered_objects); - obj->base.pending_read_domains = 0; + obj->base.pending_read_domains = I915_GEM_GPU_DOMAINS & ~I915_GEM_DOMAIN_COMMAND; obj->base.pending_write_domain = 0; obj->pending_fenced_gpu_access = false; } @@ -508,7 +523,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, (need_mappable && !obj->map_and_fenceable)) ret = i915_gem_object_unbind(obj); else - ret = i915_gem_execbuffer_reserve_object(obj, ring); + ret = i915_gem_execbuffer_reserve_object(obj, ring, need_relocs); if (ret) goto err; } @@ -518,7 +533,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, if (obj->gtt_space) continue; - ret = i915_gem_execbuffer_reserve_object(obj, ring); + ret = i915_gem_execbuffer_reserve_object(obj, ring, need_relocs); if (ret) goto err; } @@ -538,16 +553,18 @@ err: /* Decrement pin count for bound objects */ static int i915_gem_execbuffer_relocate_slow(struct drm_device *dev, + struct drm_i915_gem_execbuffer2 *args, struct drm_file *file, struct intel_ring_buffer *ring, struct eb_objects *eb, - struct drm_i915_gem_exec_object2 *exec, - int count) + struct drm_i915_gem_exec_object2 *exec) { struct drm_i915_gem_relocation_entry *reloc; struct drm_i915_gem_object *obj; + bool need_relocs; int *reloc_offset; int i, total, ret; + int count = args->buffer_count; /* We may process another execbuffer during the unlock... */ while (!list_empty(&eb->objects)) { @@ -602,7 +619,8 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, if (ret) goto err; - ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects); + need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0; + ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects, &need_relocs); if (ret) goto err; @@ -660,6 +678,9 @@ i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, static bool i915_gem_check_execbuffer(struct drm_i915_gem_execbuffer2 *exec) { + if (exec->flags & __I915_EXEC_UNKNOWN_FLAGS) + return false; + return ((exec->batch_start_offset | exec->batch_len) & 0x7) == 0; } @@ -673,6 +694,9 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec, char __user *ptr = (char __user *)(uintptr_t)exec[i].relocs_ptr; int length; /* limited by fault_in_pages_readable() */ + if (exec[i].flags & __EXEC_OBJECT_UNKNOWN_FLAGS) + return -EINVAL; + /* First check for malicious input causing overflow */ if (exec[i].relocation_count > INT_MAX / sizeof(struct drm_i915_gem_relocation_entry)) @@ -680,9 +704,6 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec, length = exec[i].relocation_count * sizeof(struct drm_i915_gem_relocation_entry); - if (!access_ok(VERIFY_READ, ptr, length)) - return -EFAULT; - /* we may also need to update the presumed offsets */ if (!access_ok(VERIFY_WRITE, ptr, length)) return -EFAULT; @@ -704,8 +725,10 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects, u32 old_read = obj->base.read_domains; u32 old_write = obj->base.write_domain; - obj->base.read_domains = obj->base.pending_read_domains; obj->base.write_domain = obj->base.pending_write_domain; + if (obj->base.write_domain == 0) + obj->base.pending_read_domains |= obj->base.read_domains; + obj->base.read_domains = obj->base.pending_read_domains; obj->fenced_gpu_access = obj->pending_fenced_gpu_access; i915_gem_object_move_to_active(obj, ring); @@ -770,14 +793,12 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, struct intel_ring_buffer *ring; u32 ctx_id = i915_execbuffer2_get_context_id(*args); u32 exec_start, exec_len; - u32 mask; - u32 flags; + u32 mask, flags; int ret, mode, i; + bool need_relocs; - if (!i915_gem_check_execbuffer(args)) { - DRM_DEBUG("execbuf with invalid offset/length\n"); + if (!i915_gem_check_execbuffer(args)) return -EINVAL; - } ret = validate_exec_list(exec, args->buffer_count); if (ret) @@ -916,17 +937,18 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, exec_list); /* Move the objects en-masse into the GTT, evicting if necessary. */ - ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects); + need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0; + ret = i915_gem_execbuffer_reserve(ring, file, &eb->objects, &need_relocs); if (ret) goto err; /* The objects are in their final locations, apply the relocations. */ - ret = i915_gem_execbuffer_relocate(dev, eb); + if (need_relocs) + ret = i915_gem_execbuffer_relocate(dev, eb); if (ret) { if (ret == -EFAULT) { - ret = i915_gem_execbuffer_relocate_slow(dev, file, ring, - eb, exec, - args->buffer_count); + ret = i915_gem_execbuffer_relocate_slow(dev, args, file, ring, + eb, exec); BUG_ON(!mutex_is_locked(&dev->struct_mutex)); } if (ret) diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index c4d2e9c74002..2430b6ad6a85 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -308,6 +308,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_RSVD_FOR_FUTURE_USE 22 #define I915_PARAM_HAS_SECURE_BATCHES 23 #define I915_PARAM_HAS_PINNED_BATCHES 24 +#define I915_PARAM_HAS_EXEC_NO_RELOC 25 typedef struct drm_i915_getparam { int param; @@ -628,7 +629,11 @@ struct drm_i915_gem_exec_object2 { __u64 offset; #define EXEC_OBJECT_NEEDS_FENCE (1<<0) +#define EXEC_OBJECT_NEEDS_GTT (1<<1) +#define EXEC_OBJECT_WRITE (1<<2) +#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_WRITE<<1) __u64 flags; + __u64 rsvd1; __u64 rsvd2; }; @@ -687,6 +692,15 @@ struct drm_i915_gem_execbuffer2 { */ #define I915_EXEC_IS_PINNED (1<<10) +/** Provide a hint to the kernel that the command stream and auxilliary + * state buffers already holds the correct presumed addresses and so the + * relocation process may be skipped if no buffers need to be moved in + * preparation for the execbuffer. + */ +#define I915_EXEC_NO_RELOC (1<<11) + +#define __I915_EXEC_UNKNOWN_FLAGS -(I915_EXEC_NO_RELOC<<1) + #define I915_EXEC_CONTEXT_ID_MASK (0xffffffff) #define i915_execbuffer2_set_context_id(eb2, context) \ (eb2).rsvd1 = context & I915_EXEC_CONTEXT_ID_MASK From eef90ccb8a4d50b219a95cc53878ebb007315b32 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 8 Jan 2013 10:53:17 +0000 Subject: [PATCH 0940/4607] drm/i915: Use the reloc.handle as an index into the execbuffer array Using copywinwin10 as an example that is dependent upon emitting a lot of relocations (2 per operation), we see improvements of: c2d/gm45: 618000.0/sec to 623000.0/sec. i3-330m: 748000.0/sec to 789000.0/sec. (measured relative to a baseline with neither optimisations applied). Signed-off-by: Chris Wilson Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 + drivers/gpu/drm/i915/i915_gem_execbuffer.c | 98 +++++++++++++--------- include/uapi/drm/i915_drm.h | 8 +- 3 files changed, 70 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index a6e047d533ec..442118293883 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -995,6 +995,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_EXEC_NO_RELOC: value = 1; break; + case I915_PARAM_HAS_EXEC_HANDLE_LUT: + value = 1; + break; default: DRM_DEBUG_DRIVER("Unknown parameter %d\n", param->param); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 34f6cdffa9f8..f5a11ecf5494 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -36,24 +36,40 @@ struct eb_objects { struct list_head objects; int and; - struct hlist_head buckets[0]; + union { + struct drm_i915_gem_object *lut[0]; + struct hlist_head buckets[0]; + }; }; static struct eb_objects * -eb_create(int size) +eb_create(struct drm_i915_gem_execbuffer2 *args) { - struct eb_objects *eb; - int count = PAGE_SIZE / sizeof(struct hlist_head) / 2; - BUILD_BUG_ON(!is_power_of_2(PAGE_SIZE / sizeof(struct hlist_head))); - while (count > size) - count >>= 1; - eb = kzalloc(count*sizeof(struct hlist_head) + - sizeof(struct eb_objects), - GFP_KERNEL); - if (eb == NULL) - return eb; + struct eb_objects *eb = NULL; + + if (args->flags & I915_EXEC_HANDLE_LUT) { + int size = args->buffer_count; + size *= sizeof(struct drm_i915_gem_object *); + size += sizeof(struct eb_objects); + eb = kmalloc(size, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); + } + + if (eb == NULL) { + int size = args->buffer_count; + int count = PAGE_SIZE / sizeof(struct hlist_head) / 2; + BUILD_BUG_ON(!is_power_of_2(PAGE_SIZE / sizeof(struct hlist_head))); + while (count > 2*size) + count >>= 1; + eb = kzalloc(count*sizeof(struct hlist_head) + + sizeof(struct eb_objects), + GFP_TEMPORARY); + if (eb == NULL) + return eb; + + eb->and = count - 1; + } else + eb->and = -args->buffer_count; - eb->and = count - 1; INIT_LIST_HEAD(&eb->objects); return eb; } @@ -61,26 +77,20 @@ eb_create(int size) static void eb_reset(struct eb_objects *eb) { - memset(eb->buckets, 0, (eb->and+1)*sizeof(struct hlist_head)); -} - -static void -eb_add_object(struct eb_objects *eb, struct drm_i915_gem_object *obj) -{ - hlist_add_head(&obj->exec_node, - &eb->buckets[obj->exec_handle & eb->and]); + if (eb->and >= 0) + memset(eb->buckets, 0, (eb->and+1)*sizeof(struct hlist_head)); } static int eb_lookup_objects(struct eb_objects *eb, struct drm_i915_gem_exec_object2 *exec, - int count, + const struct drm_i915_gem_execbuffer2 *args, struct drm_file *file) { int i; spin_lock(&file->table_lock); - for (i = 0; i < count; i++) { + for (i = 0; i < args->buffer_count; i++) { struct drm_i915_gem_object *obj; obj = to_intel_bo(idr_find(&file->object_idr, exec[i].handle)); @@ -101,9 +111,15 @@ eb_lookup_objects(struct eb_objects *eb, drm_gem_object_reference(&obj->base); list_add_tail(&obj->exec_list, &eb->objects); - obj->exec_handle = exec[i].handle; obj->exec_entry = &exec[i]; - eb_add_object(eb, obj); + if (eb->and < 0) { + eb->lut[i] = obj; + } else { + uint32_t handle = args->flags & I915_EXEC_HANDLE_LUT ? i : exec[i].handle; + obj->exec_handle = handle; + hlist_add_head(&obj->exec_node, + &eb->buckets[handle & eb->and]); + } } spin_unlock(&file->table_lock); @@ -113,18 +129,24 @@ eb_lookup_objects(struct eb_objects *eb, static struct drm_i915_gem_object * eb_get_object(struct eb_objects *eb, unsigned long handle) { - struct hlist_head *head; - struct hlist_node *node; - struct drm_i915_gem_object *obj; + if (eb->and < 0) { + if (handle >= -eb->and) + return NULL; + return eb->lut[handle]; + } else { + struct hlist_head *head; + struct hlist_node *node; - head = &eb->buckets[handle & eb->and]; - hlist_for_each(node, head) { - obj = hlist_entry(node, struct drm_i915_gem_object, exec_node); - if (obj->exec_handle == handle) - return obj; + head = &eb->buckets[handle & eb->and]; + hlist_for_each(node, head) { + struct drm_i915_gem_object *obj; + + obj = hlist_entry(node, struct drm_i915_gem_object, exec_node); + if (obj->exec_handle == handle) + return obj; + } + return NULL; } - - return NULL; } static void @@ -615,7 +637,7 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev, /* reacquire the objects */ eb_reset(eb); - ret = eb_lookup_objects(eb, exec, count, file); + ret = eb_lookup_objects(eb, exec, args, file); if (ret) goto err; @@ -919,7 +941,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, goto pre_mutex_err; } - eb = eb_create(args->buffer_count); + eb = eb_create(args); if (eb == NULL) { mutex_unlock(&dev->struct_mutex); ret = -ENOMEM; @@ -927,7 +949,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, } /* Look up object handles */ - ret = eb_lookup_objects(eb, exec, args->buffer_count, file); + ret = eb_lookup_objects(eb, exec, args, file); if (ret) goto err; diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 2430b6ad6a85..07d59419fe6b 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -309,6 +309,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_SECURE_BATCHES 23 #define I915_PARAM_HAS_PINNED_BATCHES 24 #define I915_PARAM_HAS_EXEC_NO_RELOC 25 +#define I915_PARAM_HAS_EXEC_HANDLE_LUT 26 typedef struct drm_i915_getparam { int param; @@ -699,7 +700,12 @@ struct drm_i915_gem_execbuffer2 { */ #define I915_EXEC_NO_RELOC (1<<11) -#define __I915_EXEC_UNKNOWN_FLAGS -(I915_EXEC_NO_RELOC<<1) +/** Use the reloc.handle as an index into the exec object array rather + * than as the per-file handle. + */ +#define I915_EXEC_HANDLE_LUT (1<<12) + +#define __I915_EXEC_UNKNOWN_FLAGS -(I915_EXEC_HANDLE_LUT<<1) #define I915_EXEC_CONTEXT_ID_MASK (0xffffffff) #define i915_execbuffer2_set_context_id(eb2, context) \ From c70af1e4b60a1853feeaae2bf6e2aa98de68acd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 16 Jan 2013 19:59:03 +0200 Subject: [PATCH 0941/4607] drm/i915: Fix SPRITE0_FLIP_DONE_INT_EN_VLV and SPRITE0_FLIPDONE_INT_STATUS_VLV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix up some copypaste errors in the PIPESTAT register for VLV. SPRITE0_FLIP_DONE_INT_EN_VLV is bit 22, not bit 26. SPRITE0_FLIPDONE_INT_STATUS_VLV is bit 14, not bit 15. Signed-off-by: Ville Syrjälä Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 3b039f4268e3..4c33bd2bd4e6 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2672,7 +2672,7 @@ #define PIPE_VSYNC_INTERRUPT_ENABLE (1UL<<25) #define PIPE_DISPLAY_LINE_COMPARE_ENABLE (1UL<<24) #define PIPE_DPST_EVENT_ENABLE (1UL<<23) -#define SPRITE0_FLIP_DONE_INT_EN_VLV (1UL<<26) +#define SPRITE0_FLIP_DONE_INT_EN_VLV (1UL<<22) #define PIPE_LEGACY_BLC_EVENT_ENABLE (1UL<<22) #define PIPE_ODD_FIELD_INTERRUPT_ENABLE (1UL<<21) #define PIPE_EVEN_FIELD_INTERRUPT_ENABLE (1UL<<20) @@ -2682,7 +2682,7 @@ #define PIPEA_HBLANK_INT_EN_VLV (1UL<<16) #define PIPE_OVERLAY_UPDATED_ENABLE (1UL<<16) #define SPRITE1_FLIPDONE_INT_STATUS_VLV (1UL<<15) -#define SPRITE0_FLIPDONE_INT_STATUS_VLV (1UL<<15) +#define SPRITE0_FLIPDONE_INT_STATUS_VLV (1UL<<14) #define PIPE_CRC_ERROR_INTERRUPT_STATUS (1UL<<13) #define PIPE_CRC_DONE_INTERRUPT_STATUS (1UL<<12) #define PIPE_GMBUS_INTERRUPT_STATUS (1UL<<11) From c1fc6521efbed7d3aca4e37ac61b23300f161cd1 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:12 -0800 Subject: [PATCH 0942/4607] drm/i915: Kill gtt_end It's duplicated in the more useful gtt_total. Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/i915_gem_gtt.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 8f5b2816fc03..d58189e60844 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -801,7 +801,6 @@ typedef struct drm_i915_private { /** Usable portion of the GTT for GEM */ unsigned long gtt_start; unsigned long gtt_mappable_end; - unsigned long gtt_end; unsigned long stolen_base; /* limited to low memory (32-bit) */ /** "Graphics Stolen Memory" holds the global PTEs */ diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index a4af0f79e972..3ffcf528f166 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -394,7 +394,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) /* First fill our portion of the GTT with scratch pages */ i915_ggtt_clear_range(dev, dev_priv->mm.gtt_start / PAGE_SIZE, - (dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE); + dev_priv->mm.gtt_total / PAGE_SIZE); list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { i915_gem_clflush_object(obj); @@ -556,7 +556,6 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, dev_priv->mm.gtt_start = start; dev_priv->mm.gtt_mappable_end = mappable_end; - dev_priv->mm.gtt_end = end; dev_priv->mm.gtt_total = end - start; dev_priv->mm.mappable_gtt_total = min(end, mappable_end) - start; From 35451cb6fbd3655ede4694b11761a587d400d647 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:13 -0800 Subject: [PATCH 0943/4607] drm/i915: Mappable_end can't ever be > end Both DRI1 and DRI2 can never specify a mappable size which goes past the GTT size. Don't pretend otherwise. Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 3ffcf528f166..e66c70008767 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -536,6 +536,8 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, struct drm_i915_gem_object *obj; unsigned long hole_start, hole_end; + BUG_ON(mappable_end > end); + /* Subtract the guard page ... */ drm_mm_init(&dev_priv->mm.gtt_space, start, end - start - PAGE_SIZE); if (!HAS_LLC(dev)) @@ -557,7 +559,7 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, dev_priv->mm.gtt_start = start; dev_priv->mm.gtt_mappable_end = mappable_end; dev_priv->mm.gtt_total = end - start; - dev_priv->mm.mappable_gtt_total = min(end, mappable_end) - start; + dev_priv->mm.mappable_gtt_total = mappable_end - start; /* Clear any non-preallocated blocks */ drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space, From 00fc2c3c53d7bfc9a29e5f4bdf2677f0c399f3bc Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:14 -0800 Subject: [PATCH 0944/4607] drm/i915: Remove gtt_mappable_total With the assertion from the previous patch in place, it should be safe to get rid gtt_mappable_total. Keeps things saner to not have to track the same info in two places. In order to keep the diff as simple as possible and keep with the existing gtt_setup semantics we opt to keep gtt_mappable_end. It's not as consistent with the 'total' used in the previous patch, but that can be fixed later. Reviewed-by: Rodrigo Vivi [Ben modified commit message] Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 3 ++- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/i915_gem_gtt.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index f94418bd1ed2..35d326d70fab 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -259,7 +259,8 @@ static int i915_gem_object_info(struct seq_file *m, void* data) count, size); seq_printf(m, "%zu [%zu] gtt total\n", - dev_priv->mm.gtt_total, dev_priv->mm.mappable_gtt_total); + dev_priv->mm.gtt_total, + dev_priv->mm.gtt_mappable_end - dev_priv->mm.gtt_start); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d58189e60844..6e2c10b65c8d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -886,7 +886,6 @@ typedef struct drm_i915_private { /* accounting, useful for userland debugging */ size_t gtt_total; - size_t mappable_gtt_total; size_t object_memory; u32 object_count; } mm; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index e66c70008767..8c42ddd467b6 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -559,7 +559,6 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, dev_priv->mm.gtt_start = start; dev_priv->mm.gtt_mappable_end = mappable_end; dev_priv->mm.gtt_total = end - start; - dev_priv->mm.mappable_gtt_total = mappable_end - start; /* Clear any non-preallocated blocks */ drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space, From cd323ac0eb433b14cbb270bfc5a82f308f2662de Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: [PATCH 0945/4607] rbd: end request on error in rbd_do_request() caller Only one of the three callers of rbd_do_request() provide a collection structure to aggregate status. If an error occurs in rbd_do_request(), have the caller take care of calling rbd_coll_end_req() if necessary in that one spot. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 738d1e4c0ab5..21468d0b2792 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1126,12 +1126,8 @@ static int rbd_do_request(struct request *rq, struct ceph_osd_client *osdc; rbd_req = kzalloc(sizeof(*rbd_req), GFP_NOIO); - if (!rbd_req) { - if (coll) - rbd_coll_end_req_index(rq, coll, coll_index, - (s32)-ENOMEM, len); + if (!rbd_req) return -ENOMEM; - } if (coll) { rbd_req->coll = coll; @@ -1206,7 +1202,6 @@ done_err: bio_chain_put(rbd_req->bio); ceph_osdc_put_request(osd_req); done_pages: - rbd_coll_end_req(rbd_req, (s32)ret, len); kfree(rbd_req); return ret; } @@ -1359,7 +1354,9 @@ static int rbd_do_op(struct request *rq, ops, coll, coll_index, rbd_req_cb, 0, NULL); - + if (ret < 0) + rbd_coll_end_req_index(rq, coll, coll_index, + (s32)ret, seg_len); rbd_destroy_ops(ops); done: kfree(seg_name); From 5d4545aef561ad47f91bcf75814af20c104b5a9e Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:15 -0800 Subject: [PATCH 0946/4607] drm/i915: Create a gtt structure The purpose of the gtt structure is to help isolate our gtt specific properties from the rest of the code (in doing so it help us finish the isolation from the AGP connection). The following members are pulled out (and renamed): gtt_start gtt_total gtt_mappable_end gtt_mappable gtt_base_addr gsm The gtt structure will serve as a nice place to put gen specific gtt routines in upcoming patches. As far as what else I feel belongs in this structure: it is meant to encapsulate the GTT's physical properties. This is why I've not added fields which track various drm_mm properties, or things like gtt_mtrr (which is itself a pretty transient field). Reviewed-by: Rodrigo Vivi [Ben modified commit messages] Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 4 +-- drivers/gpu/drm/i915/i915_dma.c | 20 +++++++-------- drivers/gpu/drm/i915/i915_drv.h | 29 ++++++++++++++++------ drivers/gpu/drm/i915/i915_gem.c | 14 +++++------ drivers/gpu/drm/i915/i915_gem_evict.c | 2 +- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 2 +- drivers/gpu/drm/i915/i915_gem_gtt.c | 24 +++++++++--------- drivers/gpu/drm/i915/i915_gem_tiling.c | 2 +- drivers/gpu/drm/i915/i915_irq.c | 4 +-- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_fb.c | 2 +- drivers/gpu/drm/i915/intel_overlay.c | 4 +-- 12 files changed, 61 insertions(+), 48 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 35d326d70fab..773b23ecc83b 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -259,8 +259,8 @@ static int i915_gem_object_info(struct seq_file *m, void* data) count, size); seq_printf(m, "%zu [%zu] gtt total\n", - dev_priv->mm.gtt_total, - dev_priv->mm.gtt_mappable_end - dev_priv->mm.gtt_start); + dev_priv->gtt.total, + dev_priv->gtt.mappable_end - dev_priv->gtt.start); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 442118293883..bb622135798a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1076,7 +1076,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data, ring->status_page.gfx_addr = hws->addr & (0x1ffff<<12); dev_priv->dri1.gfx_hws_cpu_addr = - ioremap_wc(dev_priv->mm.gtt_base_addr + hws->addr, 4096); + ioremap_wc(dev_priv->gtt.mappable_base + hws->addr, 4096); if (dev_priv->dri1.gfx_hws_cpu_addr == NULL) { i915_dma_cleanup(dev); ring->status_page.gfx_addr = 0; @@ -1543,17 +1543,17 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) } aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - dev_priv->mm.gtt_base_addr = dev_priv->mm.gtt->gma_bus_addr; + dev_priv->gtt.mappable_base = dev_priv->mm.gtt->gma_bus_addr; - dev_priv->mm.gtt_mapping = - io_mapping_create_wc(dev_priv->mm.gtt_base_addr, + dev_priv->gtt.mappable = + io_mapping_create_wc(dev_priv->gtt.mappable_base, aperture_size); - if (dev_priv->mm.gtt_mapping == NULL) { + if (dev_priv->gtt.mappable == NULL) { ret = -EIO; goto out_rmmap; } - i915_mtrr_setup(dev_priv, dev_priv->mm.gtt_base_addr, + i915_mtrr_setup(dev_priv, dev_priv->gtt.mappable_base, aperture_size); /* The i915 workqueue is primarily used for batched retirement of @@ -1658,11 +1658,11 @@ out_gem_unload: out_mtrrfree: if (dev_priv->mm.gtt_mtrr >= 0) { mtrr_del(dev_priv->mm.gtt_mtrr, - dev_priv->mm.gtt_base_addr, + dev_priv->gtt.mappable_base, aperture_size); dev_priv->mm.gtt_mtrr = -1; } - io_mapping_free(dev_priv->mm.gtt_mapping); + io_mapping_free(dev_priv->gtt.mappable); out_rmmap: pci_iounmap(dev->pdev, dev_priv->regs); put_gmch: @@ -1696,10 +1696,10 @@ int i915_driver_unload(struct drm_device *dev) /* Cancel the retire work handler, which should be idle now. */ cancel_delayed_work_sync(&dev_priv->mm.retire_work); - io_mapping_free(dev_priv->mm.gtt_mapping); + io_mapping_free(dev_priv->gtt.mappable); if (dev_priv->mm.gtt_mtrr >= 0) { mtrr_del(dev_priv->mm.gtt_mtrr, - dev_priv->mm.gtt_base_addr, + dev_priv->gtt.mappable_base, dev_priv->mm.gtt->gtt_mappable_entries * PAGE_SIZE); dev_priv->mm.gtt_mtrr = -1; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6e2c10b65c8d..f3f2e5e1393f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -364,6 +364,25 @@ struct intel_device_info { u8 has_llc:1; }; +/* The Graphics Translation Table is the way in which GEN hardware translates a + * Graphics Virtual Address into a Physical Address. In addition to the normal + * collateral associated with any va->pa translations GEN hardware also has a + * portion of the GTT which can be mapped by the CPU and remain both coherent + * and correct (in cases like swizzling). That region is referred to as GMADR in + * the spec. + */ +struct i915_gtt { + unsigned long start; /* Start offset of used GTT */ + size_t total; /* Total size GTT can map */ + + unsigned long mappable_end; /* End offset that we can CPU map */ + struct io_mapping *mappable; /* Mapping to our CPU mappable region */ + phys_addr_t mappable_base; /* PA of our GMADR */ + + /** "Graphics Stolen Memory" holds the global PTEs */ + void __iomem *gsm; +}; + #define I915_PPGTT_PD_ENTRIES 512 #define I915_PPGTT_PT_ENTRIES 1024 struct i915_hw_ppgtt { @@ -781,6 +800,8 @@ typedef struct drm_i915_private { /* Register state */ bool modeset_on_lid; + struct i915_gtt gtt; + struct { /** Bridge to intel-gtt-ko */ struct intel_gtt *gtt; @@ -799,15 +820,8 @@ typedef struct drm_i915_private { struct list_head unbound_list; /** Usable portion of the GTT for GEM */ - unsigned long gtt_start; - unsigned long gtt_mappable_end; unsigned long stolen_base; /* limited to low memory (32-bit) */ - /** "Graphics Stolen Memory" holds the global PTEs */ - void __iomem *gsm; - - struct io_mapping *gtt_mapping; - phys_addr_t gtt_base_addr; int gtt_mtrr; /** PPGTT used for aliasing the PPGTT with the GTT */ @@ -885,7 +899,6 @@ typedef struct drm_i915_private { struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; /* accounting, useful for userland debugging */ - size_t gtt_total; size_t object_memory; u32 object_count; } mm; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a2bb18914329..51fdf16181a7 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -186,7 +186,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, pinned += obj->gtt_space->size; mutex_unlock(&dev->struct_mutex); - args->aper_size = dev_priv->mm.gtt_total; + args->aper_size = dev_priv->gtt.total; args->aper_available_size = args->aper_size - pinned; return 0; @@ -637,7 +637,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev, * source page isn't available. Return the error and we'll * retry in the slow path. */ - if (fast_user_write(dev_priv->mm.gtt_mapping, page_base, + if (fast_user_write(dev_priv->gtt.mappable, page_base, page_offset, user_data, page_length)) { ret = -EFAULT; goto out_unpin; @@ -1362,7 +1362,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) obj->fault_mappable = true; - pfn = ((dev_priv->mm.gtt_base_addr + obj->gtt_offset) >> PAGE_SHIFT) + + pfn = ((dev_priv->gtt.mappable_base + obj->gtt_offset) >> PAGE_SHIFT) + page_offset; /* Finally, remap it using the new GTT offset */ @@ -1544,7 +1544,7 @@ i915_gem_mmap_gtt(struct drm_file *file, goto unlock; } - if (obj->base.size > dev_priv->mm.gtt_mappable_end) { + if (obj->base.size > dev_priv->gtt.mappable_end) { ret = -E2BIG; goto out; } @@ -2910,7 +2910,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, * before evicting everything in a vain attempt to find space. */ if (obj->base.size > - (map_and_fenceable ? dev_priv->mm.gtt_mappable_end : dev_priv->mm.gtt_total)) { + (map_and_fenceable ? dev_priv->gtt.mappable_end : dev_priv->gtt.total)) { DRM_ERROR("Attempting to bind an object larger than the aperture\n"); return -E2BIG; } @@ -2931,7 +2931,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, if (map_and_fenceable) ret = drm_mm_insert_node_in_range_generic(&dev_priv->mm.gtt_space, node, size, alignment, obj->cache_level, - 0, dev_priv->mm.gtt_mappable_end); + 0, dev_priv->gtt.mappable_end); else ret = drm_mm_insert_node_generic(&dev_priv->mm.gtt_space, node, size, alignment, obj->cache_level); @@ -2971,7 +2971,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, (node->start & (fence_alignment - 1)) == 0; mappable = - obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end; + obj->gtt_offset + obj->base.size <= dev_priv->gtt.mappable_end; obj->map_and_fenceable = mappable && fenceable; diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 776a3225184c..c86d5d9356fd 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -80,7 +80,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, if (mappable) drm_mm_init_scan_with_range(&dev_priv->mm.gtt_space, min_size, alignment, cache_level, - 0, dev_priv->mm.gtt_mappable_end); + 0, dev_priv->gtt.mappable_end); else drm_mm_init_scan(&dev_priv->mm.gtt_space, min_size, alignment, cache_level); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index f5a11ecf5494..27269103b621 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -281,7 +281,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, /* Map the page containing the relocation we're going to perform. */ reloc->offset += obj->gtt_offset; - reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + reloc_page = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, reloc->offset & PAGE_MASK); reloc_entry = (uint32_t __iomem *) (reloc_page + (reloc->offset & ~PAGE_MASK)); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 8c42ddd467b6..61bfb12e1016 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -290,7 +290,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) return; - pd_addr = (gtt_pte_t __iomem*)dev_priv->mm.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); + pd_addr = (gtt_pte_t __iomem*)dev_priv->gtt.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; @@ -367,7 +367,7 @@ static void i915_ggtt_clear_range(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; gtt_pte_t scratch_pte; - gtt_pte_t __iomem *gtt_base = (gtt_pte_t __iomem *) dev_priv->mm.gsm + first_entry; + gtt_pte_t __iomem *gtt_base = (gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry; const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; int i; @@ -393,8 +393,8 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) struct drm_i915_gem_object *obj; /* First fill our portion of the GTT with scratch pages */ - i915_ggtt_clear_range(dev, dev_priv->mm.gtt_start / PAGE_SIZE, - dev_priv->mm.gtt_total / PAGE_SIZE); + i915_ggtt_clear_range(dev, dev_priv->gtt.start / PAGE_SIZE, + dev_priv->gtt.total / PAGE_SIZE); list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { i915_gem_clflush_object(obj); @@ -433,7 +433,7 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, const int first_entry = obj->gtt_space->start >> PAGE_SHIFT; const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; gtt_pte_t __iomem *gtt_entries = - (gtt_pte_t __iomem *)dev_priv->mm.gsm + first_entry; + (gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry; int unused, i = 0; unsigned int len, m = 0; dma_addr_t addr; @@ -556,9 +556,9 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, obj->has_global_gtt_mapping = 1; } - dev_priv->mm.gtt_start = start; - dev_priv->mm.gtt_mappable_end = mappable_end; - dev_priv->mm.gtt_total = end - start; + dev_priv->gtt.start = start; + dev_priv->gtt.mappable_end = mappable_end; + dev_priv->gtt.total = end - start; /* Clear any non-preallocated blocks */ drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space, @@ -752,9 +752,9 @@ int i915_gem_gtt_init(struct drm_device *dev) goto err_out; } - dev_priv->mm.gsm = ioremap_wc(gtt_bus_addr, - dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); - if (!dev_priv->mm.gsm) { + dev_priv->gtt.gsm = ioremap_wc(gtt_bus_addr, + dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); + if (!dev_priv->gtt.gsm) { DRM_ERROR("Failed to map the gtt page table\n"); teardown_scratch_page(dev); ret = -ENOMEM; @@ -778,7 +778,7 @@ err_out: void i915_gem_gtt_fini(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - iounmap(dev_priv->mm.gsm); + iounmap(dev_priv->gtt.gsm); teardown_scratch_page(dev); if (INTEL_INFO(dev)->gen < 6) intel_gmch_remove(); diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index e76f0d8470a1..abcba2f5a788 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -357,7 +357,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, obj->map_and_fenceable = obj->gtt_space == NULL || - (obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end && + (obj->gtt_offset + obj->base.size <= dev_priv->gtt.mappable_end && i915_gem_object_fence_ok(obj, args->tiling_mode)); /* Rebind if we need a change of alignment */ diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 202813128441..cc49a6ddc052 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -939,7 +939,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, goto unwind; local_irq_save(flags); - if (reloc_offset < dev_priv->mm.gtt_mappable_end && + if (reloc_offset < dev_priv->gtt.mappable_end && src->has_global_gtt_mapping) { void __iomem *s; @@ -948,7 +948,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, * captures what the GPU read. */ - s = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + s = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, reloc_offset); memcpy_fromio(d, s, PAGE_SIZE); io_mapping_unmap_atomic(s); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 40b6b5e9d6ef..e4c5067a54d3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8687,7 +8687,7 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; } - dev->mode_config.fb_base = dev_priv->mm.gtt_base_addr; + dev->mode_config.fb_base = dev_priv->gtt.mappable_base; DRM_DEBUG_KMS("%d display pipe%s available.\n", dev_priv->num_pipe, dev_priv->num_pipe > 1 ? "s" : ""); diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 71d55801c0d9..ce02af8ca96e 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -142,7 +142,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev, info->fix.smem_len = size; info->screen_base = - ioremap_wc(dev_priv->mm.gtt_base_addr + obj->gtt_offset, + ioremap_wc(dev_priv->gtt.mappable_base + obj->gtt_offset, size); if (!info->screen_base) { ret = -ENOSPC; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index fabe0acf808d..ba978bf93a2e 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -195,7 +195,7 @@ intel_overlay_map_regs(struct intel_overlay *overlay) if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_obj->handle->vaddr; else - regs = io_mapping_map_wc(dev_priv->mm.gtt_mapping, + regs = io_mapping_map_wc(dev_priv->gtt.mappable, overlay->reg_bo->gtt_offset); return regs; @@ -1434,7 +1434,7 @@ intel_overlay_map_regs_atomic(struct intel_overlay *overlay) regs = (struct overlay_registers __iomem *) overlay->reg_bo->phys_obj->handle->vaddr; else - regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + regs = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, overlay->reg_bo->gtt_offset); return regs; From dabb7a91ae6442d642a52e0de22d781a8ad2af12 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:16 -0800 Subject: [PATCH 0947/4607] drm/i915: Remove use on gma_bus_addr on gen6+ We have enough info to not use the intel_gtt bridge stuff. v2: Move setup of mappable_base above the legacy init stuff because we still need that on older platforms. (Daniel) v3: Remove the dev_priv hunk which was rebased in by accident Reviewed-by: Rodrigo Vivi (v2) Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 +-- drivers/gpu/drm/i915/i915_gem_gtt.c | 3 ++- drivers/gpu/drm/i915/intel_ringbuffer.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index bb622135798a..468d2a0fc378 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1426,7 +1426,7 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) if (!ap) return; - ap->ranges[0].base = dev_priv->mm.gtt->gma_bus_addr; + ap->ranges[0].base = dev_priv->gtt.mappable_base; ap->ranges[0].size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; primary = @@ -1543,7 +1543,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) } aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - dev_priv->gtt.mappable_base = dev_priv->mm.gtt->gma_bus_addr; dev_priv->gtt.mappable = io_mapping_create_wc(dev_priv->gtt.mappable_base, diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 61bfb12e1016..0b8930577953 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -691,6 +691,8 @@ int i915_gem_gtt_init(struct drm_device *dev) u16 snb_gmch_ctl; int ret; + dev_priv->gtt.mappable_base = pci_resource_start(dev->pdev, 2); + /* On modern platforms we need not worry ourself with the legacy * hostbridge query stuff. Skip it entirely */ @@ -723,7 +725,6 @@ int i915_gem_gtt_init(struct drm_device *dev) /* For GEN6+ the PTEs for the ggtt live at 2MB + BAR0 */ gtt_bus_addr = pci_resource_start(dev->pdev, 0) + (2<<20); - dev_priv->mm.gtt->gma_bus_addr = pci_resource_start(dev->pdev, 2); /* i9xx_setup */ pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 59e02691baf3..ce1d07487402 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1203,7 +1203,7 @@ static int intel_init_ring_buffer(struct drm_device *dev, goto err_unpin; ring->virtual_start = - ioremap_wc(dev_priv->mm.gtt->gma_bus_addr + obj->gtt_offset, + ioremap_wc(dev_priv->gtt.mappable_base + obj->gtt_offset, ring->size); if (ring->virtual_start == NULL) { DRM_ERROR("Failed to map ringbuffer.\n"); From b395e8b5b8f06399e3fe3ee016c9cf41ff665efc Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 8 Nov 2012 08:01:39 -0600 Subject: [PATCH 0948/4607] rbd: a little more cleanup of rbd_rq_fn() Now that a big hunk in the middle of rbd_rq_fn() has been moved into its own routine we can simplify it a little more. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 56 ++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 21468d0b2792..9d49e5b888d8 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1644,53 +1644,51 @@ static int rbd_dev_do_request(struct request *rq, static void rbd_rq_fn(struct request_queue *q) { struct rbd_device *rbd_dev = q->queuedata; + bool read_only = rbd_dev->mapping.read_only; struct request *rq; while ((rq = blk_fetch_request(q))) { - struct bio *bio; - bool do_write; - unsigned int size; - u64 ofs; - struct ceph_snap_context *snapc; + struct ceph_snap_context *snapc = NULL; + unsigned int size = 0; int result; dout("fetched request\n"); - /* filter out block requests we don't understand */ + /* Filter out block requests we don't understand */ + if ((rq->cmd_type != REQ_TYPE_FS)) { __blk_end_request_all(rq, 0); continue; } - - /* deduce our operation (read, write) */ - do_write = (rq_data_dir(rq) == WRITE); - if (do_write && rbd_dev->mapping.read_only) { - __blk_end_request_all(rq, -EROFS); - continue; - } - spin_unlock_irq(q->queue_lock); + /* Stop writes to a read-only device */ + + result = -EROFS; + if (read_only && rq_data_dir(rq) == WRITE) + goto out_end_request; + + /* Grab a reference to the snapshot context */ + down_read(&rbd_dev->header_rwsem); - - if (!rbd_dev->exists) { - rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); - up_read(&rbd_dev->header_rwsem); - dout("request for non-existent snapshot"); - spin_lock_irq(q->queue_lock); - __blk_end_request_all(rq, -ENXIO); - continue; + if (rbd_dev->exists) { + snapc = ceph_get_snap_context(rbd_dev->header.snapc); + rbd_assert(snapc != NULL); } - - snapc = ceph_get_snap_context(rbd_dev->header.snapc); - up_read(&rbd_dev->header_rwsem); - size = blk_rq_bytes(rq); - ofs = blk_rq_pos(rq) * SECTOR_SIZE; - bio = rq->bio; + if (!snapc) { + rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); + dout("request for non-existent snapshot"); + result = -ENXIO; + goto out_end_request; + } - result = rbd_dev_do_request(rq, rbd_dev, snapc, ofs, size, bio); + size = blk_rq_bytes(rq); + result = rbd_dev_do_request(rq, rbd_dev, snapc, + blk_rq_pos(rq) * SECTOR_SIZE, + size, rq->bio); +out_end_request: ceph_put_snap_context(snapc); spin_lock_irq(q->queue_lock); if (!size || result < 0) From d78b650a595e23e5a115d332e3c37e019baf7703 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:15 -0600 Subject: [PATCH 0949/4607] rbd: make exists flag atomic The rbd_device->exists field can be updated asynchronously, changing from set to clear if a mapped snapshot disappears from the base image's snapshot context. Currently, value of the "exists" flag is only read and modified under protection of the header semaphore, but that will change with the next patch. Making it atomic ensures this won't be a problem because the a the non-existence of device will be immediately known. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 9d49e5b888d8..2846536d446e 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -229,7 +229,7 @@ struct rbd_device { spinlock_t lock; /* queue lock */ struct rbd_image_header header; - bool exists; + atomic_t exists; struct rbd_spec *spec; char *header_name; @@ -751,7 +751,7 @@ static int rbd_dev_set_mapping(struct rbd_device *rbd_dev) goto done; rbd_dev->mapping.read_only = true; } - rbd_dev->exists = true; + atomic_set(&rbd_dev->exists, 1); done: return ret; } @@ -1671,7 +1671,7 @@ static void rbd_rq_fn(struct request_queue *q) /* Grab a reference to the snapshot context */ down_read(&rbd_dev->header_rwsem); - if (rbd_dev->exists) { + if (atomic_read(&rbd_dev->exists)) { snapc = ceph_get_snap_context(rbd_dev->header.snapc); rbd_assert(snapc != NULL); } @@ -2294,6 +2294,7 @@ struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, return NULL; spin_lock_init(&rbd_dev->lock); + atomic_set(&rbd_dev->exists, 0); INIT_LIST_HEAD(&rbd_dev->node); INIT_LIST_HEAD(&rbd_dev->snaps); init_rwsem(&rbd_dev->header_rwsem); @@ -2918,7 +2919,7 @@ static int rbd_dev_snaps_update(struct rbd_device *rbd_dev) /* Existing snapshot not in the new snap context */ if (rbd_dev->spec->snap_id == snap->id) - rbd_dev->exists = false; + atomic_set(&rbd_dev->exists, 0); rbd_remove_snap_dev(snap); dout("%ssnap id %llu has been removed\n", rbd_dev->spec->snap_id == snap->id ? From a7b4c65f4f15aa657b09d13da8f45ba0b72ec0df Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:15 -0600 Subject: [PATCH 0950/4607] rbd: only get snap context for write requests Right now we get the snapshot context for an rbd image (under protection of the header semaphore) for every request processed. There's no need to get the snap context if we're doing a read, so avoid doing so in that case. Note that we no longer need to hold the header semaphore to check the rbd_dev's existence flag. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 2846536d446e..0fbe9add0446 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1331,7 +1331,7 @@ static int rbd_do_op(struct request *rq, } else { opcode = CEPH_OSD_OP_READ; flags = CEPH_OSD_FLAG_READ; - snapc = NULL; + rbd_assert(!snapc); snapid = rbd_dev->spec->snap_id; payload_len = 0; } @@ -1662,22 +1662,25 @@ static void rbd_rq_fn(struct request_queue *q) } spin_unlock_irq(q->queue_lock); - /* Stop writes to a read-only device */ + /* Write requests need a reference to the snapshot context */ - result = -EROFS; - if (read_only && rq_data_dir(rq) == WRITE) - goto out_end_request; + if (rq_data_dir(rq) == WRITE) { + result = -EROFS; + if (read_only) /* Can't write to a read-only device */ + goto out_end_request; - /* Grab a reference to the snapshot context */ - - down_read(&rbd_dev->header_rwsem); - if (atomic_read(&rbd_dev->exists)) { + /* + * Note that each osd request will take its + * own reference to the snapshot context + * supplied. The reference we take here + * just guarantees the one we provide stays + * valid. + */ + down_read(&rbd_dev->header_rwsem); snapc = ceph_get_snap_context(rbd_dev->header.snapc); + up_read(&rbd_dev->header_rwsem); rbd_assert(snapc != NULL); - } - up_read(&rbd_dev->header_rwsem); - - if (!snapc) { + } else if (!atomic_read(&rbd_dev->exists)) { rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); dout("request for non-existent snapshot"); result = -ENXIO; @@ -1689,7 +1692,8 @@ static void rbd_rq_fn(struct request_queue *q) blk_rq_pos(rq) * SECTOR_SIZE, size, rq->bio); out_end_request: - ceph_put_snap_context(snapc); + if (snapc) + ceph_put_snap_context(snapc); spin_lock_irq(q->queue_lock); if (!size || result < 0) __blk_end_request_all(rq, result); From 0ec8ce87f3bb5e4a561190f5320934e003405b6f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:15 -0600 Subject: [PATCH 0951/4607] rbd: separate layout init Pull a block of code that initializes the layout structure in an osd request into its own function so it can be reused. Signed-off-by: Alex Elder Reviewed-by: Dan Mick Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 0fbe9add0446..d4e93a28fb6a 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -54,6 +54,7 @@ /* It might be useful to have this defined elsewhere too */ +#define U32_MAX ((u32) (~0U)) #define U64_MAX ((u64) (~0ULL)) #define RBD_DRV_NAME "rbd" @@ -1096,6 +1097,16 @@ static void rbd_coll_end_req(struct rbd_request *rbd_req, ret, len); } +static void rbd_layout_init(struct ceph_file_layout *layout, u64 pool_id) +{ + memset(layout, 0, sizeof (*layout)); + layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); + layout->fl_stripe_count = cpu_to_le32(1); + layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); + rbd_assert(pool_id <= (u64) U32_MAX); + layout->fl_pg_pool = cpu_to_le32((u32) pool_id); +} + /* * Send ceph osd request */ @@ -1117,7 +1128,6 @@ static int rbd_do_request(struct request *rq, u64 *ver) { struct ceph_osd_request *osd_req; - struct ceph_file_layout *layout; int ret; u64 bno; struct timespec mtime = CURRENT_TIME; @@ -1161,14 +1171,9 @@ static int rbd_do_request(struct request *rq, strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); osd_req->r_oid_len = strlen(osd_req->r_oid); - layout = &osd_req->r_file_layout; - memset(layout, 0, sizeof(*layout)); - layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); - layout->fl_stripe_count = cpu_to_le32(1); - layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); - layout->fl_pg_pool = cpu_to_le32((int) rbd_dev->spec->pool_id); - ret = ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno, - osd_req, ops); + rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); + ret = ceph_calc_raw_layout(osdc, &osd_req->r_file_layout, + snapid, ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); ceph_osdc_build_request(osd_req, ofs, &len, From af77f26caa35a95af09d1dac5c513b3901de7e37 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:15 -0600 Subject: [PATCH 0952/4607] rbd: drop oid parameters from ceph_osdc_build_request() The last two parameters to ceph_osd_build_request() describe the object id, but the values passed always come from the osd request structure whose address is also provided. Get rid of those last two parameters. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 6 +----- include/linux/ceph/osd_client.h | 4 +--- net/ceph/osd_client.c | 13 +++++-------- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d4e93a28fb6a..9a701effa0ef 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1176,11 +1176,7 @@ static int rbd_do_request(struct request *rq, snapid, ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); - ceph_osdc_build_request(osd_req, ofs, &len, - ops, - snapc, - &mtime, - osd_req->r_oid, osd_req->r_oid_len); + ceph_osdc_build_request(osd_req, ofs, &len, ops, snapc, &mtime); if (linger_req) { ceph_osdc_set_request_linger(osdc, osd_req); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index d9b880e977e6..f2e5d2cdca06 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -227,9 +227,7 @@ extern void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 *plen, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, - struct timespec *mtime, - const char *oid, - int oid_len); + struct timespec *mtime); extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, struct ceph_file_layout *layout, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index eade41bb7102..7d38327a8e89 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -376,9 +376,7 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 *plen, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, - struct timespec *mtime, - const char *oid, - int oid_len) + struct timespec *mtime) { struct ceph_msg *msg = req->r_request; struct ceph_osd_request_head *head; @@ -405,9 +403,9 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, /* fill in oid */ - head->object_len = cpu_to_le32(oid_len); - memcpy(p, oid, oid_len); - p += oid_len; + head->object_len = cpu_to_le32(req->r_oid_len); + memcpy(p, req->r_oid, req->r_oid_len); + p += req->r_oid_len; src_op = src_ops; while (src_op->op) { @@ -506,8 +504,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, ceph_osdc_build_request(req, off, plen, ops, snapc, - mtime, - req->r_oid, req->r_oid_len); + mtime); return req; } From 4775618d9255c0c135580bbee8bee6815f8194cf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:15 -0600 Subject: [PATCH 0953/4607] rbd: drop snapid parameter from rbd_req_sync_read() There is only one caller of rbd_req_sync_read(), and it passes CEPH_NOSNAP as the snapshot id argument. Delete that parameter and just use CEPH_NOSNAP within the function. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 9a701effa0ef..07e064482cd8 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1368,7 +1368,6 @@ done: * Request sync osd read */ static int rbd_req_sync_read(struct rbd_device *rbd_dev, - u64 snapid, const char *object_name, u64 ofs, u64 len, char *buf, @@ -1382,7 +1381,7 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, NULL, - snapid, + CEPH_NOSNAP, CEPH_OSD_FLAG_READ, ops, object_name, ofs, len, buf, NULL, ver); rbd_destroy_ops(ops); @@ -1799,8 +1798,7 @@ rbd_dev_v1_header_read(struct rbd_device *rbd_dev, u64 *version) if (!ondisk) return ERR_PTR(-ENOMEM); - ret = rbd_req_sync_read(rbd_dev, CEPH_NOSNAP, - rbd_dev->header_name, + ret = rbd_req_sync_read(rbd_dev, rbd_dev->header_name, 0, size, (char *) ondisk, version); From 07b2391fbbcefdecbc2f16321f8e454802e0b926 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:16 -0600 Subject: [PATCH 0954/4607] rbd: drop flags parameter from rbd_req_sync_exec() All callers of rbd_req_sync_exec() pass CEPH_OSD_FLAG_READ as their flags argument. Delete that parameter and use CEPH_OSD_FLAG_READ within the function. If we find a need to support write operations we can add it back again. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 07e064482cd8..5d48bcd1709b 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1524,7 +1524,6 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, size_t outbound_size, char *inbound, size_t inbound_size, - int flags, u64 *ver) { struct ceph_osd_req_op *ops; @@ -1555,8 +1554,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, ops[0].cls.indata_len = outbound_size; ret = rbd_req_sync_op(rbd_dev, NULL, - CEPH_NOSNAP, - flags, ops, + CEPH_NOSNAP, CEPH_OSD_FLAG_READ, ops, object_name, 0, inbound_size, inbound, NULL, ver); @@ -2418,8 +2416,7 @@ static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id, ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, "rbd", "get_size", (char *) &snapid, sizeof (snapid), - (char *) &size_buf, sizeof (size_buf), - CEPH_OSD_FLAG_READ, NULL); + (char *) &size_buf, sizeof (size_buf), NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) return ret; @@ -2454,8 +2451,7 @@ static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev) ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, "rbd", "get_object_prefix", NULL, 0, - reply_buf, RBD_OBJ_PREFIX_LEN_MAX, - CEPH_OSD_FLAG_READ, NULL); + reply_buf, RBD_OBJ_PREFIX_LEN_MAX, NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -2494,7 +2490,7 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id, "rbd", "get_features", (char *) &snapid, sizeof (snapid), (char *) &features_buf, sizeof (features_buf), - CEPH_OSD_FLAG_READ, NULL); + NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) return ret; @@ -2549,8 +2545,7 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, "rbd", "get_parent", (char *) &snapid, sizeof (snapid), - (char *) reply_buf, size, - CEPH_OSD_FLAG_READ, NULL); + (char *) reply_buf, size, NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out_err; @@ -2615,8 +2610,7 @@ static char *rbd_dev_image_name(struct rbd_device *rbd_dev) ret = rbd_req_sync_exec(rbd_dev, RBD_DIRECTORY, "rbd", "dir_get_name", image_id, image_id_size, - (char *) reply_buf, size, - CEPH_OSD_FLAG_READ, NULL); + (char *) reply_buf, size, NULL); if (ret < 0) goto out; p = reply_buf; @@ -2722,8 +2716,7 @@ static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev, u64 *ver) ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, "rbd", "get_snapcontext", NULL, 0, - reply_buf, size, - CEPH_OSD_FLAG_READ, ver); + reply_buf, size, ver); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -2792,8 +2785,7 @@ static char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev, u32 which) ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, "rbd", "get_snapshot_name", (char *) &snap_id, sizeof (snap_id), - reply_buf, size, - CEPH_OSD_FLAG_READ, NULL); + reply_buf, size, NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -3401,8 +3393,7 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) ret = rbd_req_sync_exec(rbd_dev, object_name, "rbd", "get_id", NULL, 0, - response, RBD_IMAGE_ID_LEN_MAX, - CEPH_OSD_FLAG_READ, NULL); + response, RBD_IMAGE_ID_LEN_MAX, NULL); dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out; From 25704ac9de30ac3e73c123e7b2734f7ca744c8d8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 08:43:16 -0600 Subject: [PATCH 0955/4607] rbd: kill rbd_req_sync_op() snapc and snapid parameters The snapc and snapid parameters to rbd_req_sync_op() always take the values NULL and CEPH_NOSNAP, respectively. So just get rid of them and use those values where needed. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 5d48bcd1709b..85c5852378d2 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1257,8 +1257,6 @@ static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, * Do a synchronous ceph osd operation */ static int rbd_req_sync_op(struct rbd_device *rbd_dev, - struct ceph_snap_context *snapc, - u64 snapid, int flags, struct ceph_osd_req_op *ops, const char *object_name, @@ -1278,7 +1276,7 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, if (IS_ERR(pages)) return PTR_ERR(pages); - ret = rbd_do_request(NULL, rbd_dev, snapc, snapid, + ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, object_name, ofs, inbound_size, NULL, pages, num_pages, flags, @@ -1380,9 +1378,7 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, if (!ops) return -ENOMEM; - ret = rbd_req_sync_op(rbd_dev, NULL, - CEPH_NOSNAP, - CEPH_OSD_FLAG_READ, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, ops, object_name, ofs, len, buf, NULL, ver); rbd_destroy_ops(ops); @@ -1461,8 +1457,7 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); ops[0].watch.flag = 1; - ret = rbd_req_sync_op(rbd_dev, NULL, - CEPH_NOSNAP, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, rbd_dev->header_name, @@ -1499,8 +1494,7 @@ static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); ops[0].watch.flag = 0; - ret = rbd_req_sync_op(rbd_dev, NULL, - CEPH_NOSNAP, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, ops, rbd_dev->header_name, @@ -1553,8 +1547,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, ops[0].cls.indata = outbound; ops[0].cls.indata_len = outbound_size; - ret = rbd_req_sync_op(rbd_dev, NULL, - CEPH_NOSNAP, CEPH_OSD_FLAG_READ, ops, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, ops, object_name, 0, inbound_size, inbound, NULL, ver); From 7c3d22cf16f1bbcb37a73e88338c042bb49ff112 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 9 Nov 2012 12:50:10 -0600 Subject: [PATCH 0956/4607] rbd: don't bother setting snapid in rbd_do_request() For some reason, the snapid field of the osd request header is explicitly set to CEPH_NOSNAP in rbd_do_request(). Just a few lines later--with no code that would access this field in between--a call is made to ceph_calc_raw_layout() passing the snapid provided to rbd_do_request(), which encodes the snapid value it is provided into that field instead. In other words, there is no need to fill in CEPH_NOSNAP, and doing so suggests it might be necessary. Don't do that any more. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 85c5852378d2..54bd9fc3ef7c 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1132,7 +1132,6 @@ static int rbd_do_request(struct request *rq, u64 bno; struct timespec mtime = CURRENT_TIME; struct rbd_request *rbd_req; - struct ceph_osd_request_head *reqhead; struct ceph_osd_client *osdc; rbd_req = kzalloc(sizeof(*rbd_req), GFP_NOIO); @@ -1165,9 +1164,6 @@ static int rbd_do_request(struct request *rq, osd_req->r_priv = rbd_req; - reqhead = osd_req->r_request->front.iov_base; - reqhead->snapid = cpu_to_le64(CEPH_NOSNAP); - strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); osd_req->r_oid_len = strlen(osd_req->r_oid); From c885837f7d4f8c4f5cb2a744cc6929bc078e9dc0 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: [PATCH 0957/4607] libceph: always allow trail in osd request An osd request structure contains an optional trail portion, which if present will contain data to be passed in the payload portion of the message containing the request. The trail field is a ceph_pagelist pointer, and if null it indicates there is no trail. A ceph_pagelist structure contains a length field, and it can legitimately hold value 0. Make use of this to change the interpretation of the "trail" of an osd request so that every osd request has trailing data, it just might have length 0. This means we change the r_trail field in a ceph_osd_request structure from a pointer to a structure that is always initialized. Note that in ceph_osdc_start_request(), the trail pointer (or now address of that structure) is assigned to a ceph message's trail field. Here's why that's still OK (looking at net/ceph/messenger.c): - What would have resulted in a null pointer previously will now refer to a 0-length page list. That message trail pointer is used in two functions, write_partial_msg_pages() and out_msg_pos_next(). - In write_partial_msg_pages(), a null page list pointer is handled the same as a message with 0-length trail, and both result in a "in_trail" variable set to false. The trail pointer is only used if in_trail is true. - The only other place the message trail pointer is used is out_msg_pos_next(). That function is only called by write_partial_msg_pages() and only touches the trail pointer if the in_trail value it is passed is true. Therefore a null ceph_msg->trail pointer is equivalent to a non-null pointer referring to a 0-length page list structure. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- include/linux/ceph/osd_client.h | 4 +-- net/ceph/osd_client.c | 43 +++++++++------------------------ 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index f2e5d2cdca06..61562c792855 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -10,6 +10,7 @@ #include #include #include +#include /* * Maximum object name size @@ -22,7 +23,6 @@ struct ceph_snap_context; struct ceph_osd_request; struct ceph_osd_client; struct ceph_authorizer; -struct ceph_pagelist; /* * completion callback for async writepages @@ -95,7 +95,7 @@ struct ceph_osd_request { struct bio *r_bio; /* instead of pages */ #endif - struct ceph_pagelist *r_trail; /* trailing part of the data */ + struct ceph_pagelist r_trail; /* trailing part of the data */ }; struct ceph_osd_event { diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 7d38327a8e89..2be50d82ccbc 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -171,10 +171,7 @@ void ceph_osdc_release_request(struct kref *kref) bio_put(req->r_bio); #endif ceph_put_snap_context(req->r_snapc); - if (req->r_trail) { - ceph_pagelist_release(req->r_trail); - kfree(req->r_trail); - } + ceph_pagelist_release(&req->r_trail); if (req->r_mempool) mempool_free(req, req->r_osdc->req_mempool); else @@ -208,8 +205,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, { struct ceph_osd_request *req; struct ceph_msg *msg; - int needs_trail; - int num_op = get_num_ops(ops, &needs_trail); + int num_op = get_num_ops(ops, NULL); size_t msg_size = sizeof(struct ceph_osd_request_head); msg_size += num_op*sizeof(struct ceph_osd_op); @@ -252,15 +248,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, } req->r_reply = msg; - /* allocate space for the trailing data */ - if (needs_trail) { - req->r_trail = kmalloc(sizeof(struct ceph_pagelist), gfp_flags); - if (!req->r_trail) { - ceph_osdc_put_request(req); - return NULL; - } - ceph_pagelist_init(req->r_trail); - } + ceph_pagelist_init(&req->r_trail); /* create request message; allow space for oid */ msg_size += MAX_OBJ_NAME_SIZE; @@ -312,29 +300,25 @@ static void osd_req_encode_op(struct ceph_osd_request *req, case CEPH_OSD_OP_GETXATTR: case CEPH_OSD_OP_SETXATTR: case CEPH_OSD_OP_CMPXATTR: - BUG_ON(!req->r_trail); - dst->xattr.name_len = cpu_to_le32(src->xattr.name_len); dst->xattr.value_len = cpu_to_le32(src->xattr.value_len); dst->xattr.cmp_op = src->xattr.cmp_op; dst->xattr.cmp_mode = src->xattr.cmp_mode; - ceph_pagelist_append(req->r_trail, src->xattr.name, + ceph_pagelist_append(&req->r_trail, src->xattr.name, src->xattr.name_len); - ceph_pagelist_append(req->r_trail, src->xattr.val, + ceph_pagelist_append(&req->r_trail, src->xattr.val, src->xattr.value_len); break; case CEPH_OSD_OP_CALL: - BUG_ON(!req->r_trail); - dst->cls.class_len = src->cls.class_len; dst->cls.method_len = src->cls.method_len; dst->cls.indata_len = cpu_to_le32(src->cls.indata_len); - ceph_pagelist_append(req->r_trail, src->cls.class_name, + ceph_pagelist_append(&req->r_trail, src->cls.class_name, src->cls.class_len); - ceph_pagelist_append(req->r_trail, src->cls.method_name, + ceph_pagelist_append(&req->r_trail, src->cls.method_name, src->cls.method_len); - ceph_pagelist_append(req->r_trail, src->cls.indata, + ceph_pagelist_append(&req->r_trail, src->cls.indata, src->cls.indata_len); break; case CEPH_OSD_OP_ROLLBACK: @@ -347,11 +331,9 @@ static void osd_req_encode_op(struct ceph_osd_request *req, __le32 prot_ver = cpu_to_le32(src->watch.prot_ver); __le32 timeout = cpu_to_le32(src->watch.timeout); - BUG_ON(!req->r_trail); - - ceph_pagelist_append(req->r_trail, + ceph_pagelist_append(&req->r_trail, &prot_ver, sizeof(prot_ver)); - ceph_pagelist_append(req->r_trail, + ceph_pagelist_append(&req->r_trail, &timeout, sizeof(timeout)); } case CEPH_OSD_OP_NOTIFY_ACK: @@ -414,8 +396,7 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, op++; } - if (req->r_trail) - data_len += req->r_trail->length; + data_len += req->r_trail.length; if (snapc) { head->snap_seq = cpu_to_le64(snapc->seq); @@ -1715,7 +1696,7 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc, #ifdef CONFIG_BLOCK req->r_request->bio = req->r_bio; #endif - req->r_request->trail = req->r_trail; + req->r_request->trail = &req->r_trail; register_request(osdc, req); From 5b9d1b1cd46aa6c8abf891f25c15aee31538da7e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: [PATCH 0958/4607] libceph: kill op_needs_trail() Since every osd message is now prepared to include trailing data, there's no need to check ahead of time whether any operations will make use of the trail portion of the message. We can drop the second argument to get_num_ops(), and as a result we can also get rid of op_needs_trail() which is no longer used. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- net/ceph/osd_client.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 2be50d82ccbc..37d43d5b828c 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -32,20 +32,6 @@ static void __unregister_linger_request(struct ceph_osd_client *osdc, static void __send_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req); -static int op_needs_trail(int op) -{ - switch (op) { - case CEPH_OSD_OP_GETXATTR: - case CEPH_OSD_OP_SETXATTR: - case CEPH_OSD_OP_CMPXATTR: - case CEPH_OSD_OP_CALL: - case CEPH_OSD_OP_NOTIFY: - return 1; - default: - return 0; - } -} - static int op_has_extent(int op) { return (op == CEPH_OSD_OP_READ || @@ -179,17 +165,12 @@ void ceph_osdc_release_request(struct kref *kref) } EXPORT_SYMBOL(ceph_osdc_release_request); -static int get_num_ops(struct ceph_osd_req_op *ops, int *needs_trail) +static int get_num_ops(struct ceph_osd_req_op *ops) { int i = 0; - if (needs_trail) - *needs_trail = 0; - while (ops[i].op) { - if (needs_trail && op_needs_trail(ops[i].op)) - *needs_trail = 1; + while (ops[i].op) i++; - } return i; } @@ -205,7 +186,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, { struct ceph_osd_request *req; struct ceph_msg *msg; - int num_op = get_num_ops(ops, NULL); + int num_op = get_num_ops(ops); size_t msg_size = sizeof(struct ceph_osd_request_head); msg_size += num_op*sizeof(struct ceph_osd_op); @@ -365,7 +346,7 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, struct ceph_osd_req_op *src_op; struct ceph_osd_op *op; void *p; - int num_op = get_num_ops(src_ops, NULL); + int num_op = get_num_ops(src_ops); size_t msg_size = sizeof(*head) + num_op*sizeof(*op); int flags = req->r_flags; u64 data_len = 0; From 0120be3c60d46d6d55f4bf7a3d654cc705eb0c54 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 09:38:19 -0600 Subject: [PATCH 0959/4607] libceph: pass length to ceph_osdc_build_request() The len argument to ceph_osdc_build_request() is set up to be passed by address, but that function never updates its value so there's no need to do this. Tighten up the interface by passing the length directly. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 2 +- include/linux/ceph/osd_client.h | 2 +- net/ceph/osd_client.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 54bd9fc3ef7c..c1b135b6cb97 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1172,7 +1172,7 @@ static int rbd_do_request(struct request *rq, snapid, ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); - ceph_osdc_build_request(osd_req, ofs, &len, ops, snapc, &mtime); + ceph_osdc_build_request(osd_req, ofs, len, ops, snapc, &mtime); if (linger_req) { ceph_osdc_set_request_linger(osdc, osd_req); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 61562c792855..4bfb4582439a 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -224,7 +224,7 @@ extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client * struct bio *bio); extern void ceph_osdc_build_request(struct ceph_osd_request *req, - u64 off, u64 *plen, + u64 off, u64 len, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, struct timespec *mtime); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 37d43d5b828c..e29a3ed92958 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -336,7 +336,7 @@ static void osd_req_encode_op(struct ceph_osd_request *req, * */ void ceph_osdc_build_request(struct ceph_osd_request *req, - u64 off, u64 *plen, + u64 off, u64 len, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, struct timespec *mtime) @@ -390,7 +390,7 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, if (flags & CEPH_OSD_FLAG_WRITE) { req->r_request->hdr.data_off = cpu_to_le16(off); - req->r_request->hdr.data_len = cpu_to_le32(*plen + data_len); + req->r_request->hdr.data_len = cpu_to_le32(len + data_len); } else if (data_len) { req->r_request->hdr.data_off = 0; req->r_request->hdr.data_len = cpu_to_le32(data_len); @@ -464,7 +464,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, req->r_num_pages = calc_pages_for(page_align, *plen); req->r_page_alignment = page_align; - ceph_osdc_build_request(req, off, plen, ops, + ceph_osdc_build_request(req, off, *plen, ops, snapc, mtime); From e8afad656cbcd06d02a7bacd4b318fa0e2907de0 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 09:38:19 -0600 Subject: [PATCH 0960/4607] libceph: pass length to ceph_calc_file_object_mapping() ceph_calc_file_object_mapping() takes (among other things) a "file" offset and length, and based on the layout, determines the object number ("bno") backing the affected portion of the file's data and the offset into that object where the desired range begins. It also computes the size that should be used for the request--either the amount requested or something less if that would exceed the end of the object. This patch changes the input length parameter in this function so it is used only for input. That is, the argument will be passed by value rather than by address, so the value provided won't get updated by the function. The value would only get updated if the length would surpass the current object, and in that case the value it got updated to would be exactly that returned in *oxlen. Only one of the two callers is affected by this change. Update ceph_calc_raw_layout() so it records any updated value. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- fs/ceph/ioctl.c | 2 +- include/linux/ceph/osdmap.h | 2 +- net/ceph/osd_client.c | 6 ++++-- net/ceph/osdmap.c | 9 ++++----- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index 36549a46e311..3b22150d3e19 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -194,7 +194,7 @@ static long ceph_ioctl_get_dataloc(struct file *file, void __user *arg) return -EFAULT; down_read(&osdc->map_sem); - r = ceph_calc_file_object_mapping(&ci->i_layout, dl.file_offset, &len, + r = ceph_calc_file_object_mapping(&ci->i_layout, dl.file_offset, len, &dl.object_no, &dl.object_offset, &olen); if (r < 0) diff --git a/include/linux/ceph/osdmap.h b/include/linux/ceph/osdmap.h index 5ea57ba69320..1f653e2ff5cc 100644 --- a/include/linux/ceph/osdmap.h +++ b/include/linux/ceph/osdmap.h @@ -110,7 +110,7 @@ extern void ceph_osdmap_destroy(struct ceph_osdmap *map); /* calculate mapping of a file extent to an object */ extern int ceph_calc_file_object_mapping(struct ceph_file_layout *layout, - u64 off, u64 *plen, + u64 off, u64 len, u64 *bno, u64 *oxoff, u64 *oxlen); /* calculate mapping of object to a placement group */ diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index e29a3ed92958..47e5f5b1f94c 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -53,13 +53,15 @@ int ceph_calc_raw_layout(struct ceph_osd_client *osdc, reqhead->snapid = cpu_to_le64(snapid); /* object extent? */ - r = ceph_calc_file_object_mapping(layout, off, plen, bno, + r = ceph_calc_file_object_mapping(layout, off, orig_len, bno, &objoff, &objlen); if (r < 0) return r; - if (*plen < orig_len) + if (objlen < orig_len) { + *plen = objlen; dout(" skipping last %llu, final file extent %llu~%llu\n", orig_len - *plen, off, *plen); + } if (op_has_extent(op->op)) { u32 osize = le32_to_cpu(layout->fl_object_size); diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index ca05871635bc..369f03ba9ee5 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -1016,7 +1016,7 @@ bad: * pass a stride back to the caller. */ int ceph_calc_file_object_mapping(struct ceph_file_layout *layout, - u64 off, u64 *plen, + u64 off, u64 len, u64 *ono, u64 *oxoff, u64 *oxlen) { @@ -1027,7 +1027,7 @@ int ceph_calc_file_object_mapping(struct ceph_file_layout *layout, u32 su_per_object; u64 t, su_offset; - dout("mapping %llu~%llu osize %u fl_su %u\n", off, *plen, + dout("mapping %llu~%llu osize %u fl_su %u\n", off, len, osize, su); if (su == 0 || sc == 0) goto invalid; @@ -1060,11 +1060,10 @@ int ceph_calc_file_object_mapping(struct ceph_file_layout *layout, /* * Calculate the length of the extent being written to the selected - * object. This is the minimum of the full length requested (plen) or + * object. This is the minimum of the full length requested (len) or * the remainder of the current stripe being written to. */ - *oxlen = min_t(u64, *plen, su - su_offset); - *plen = *oxlen; + *oxlen = min_t(u64, len, su - su_offset); dout(" obj extent %llu~%llu\n", *oxoff, *oxlen); return 0; From 4d6b250bf18d44571d69a0f4afec4b6a1969729f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: [PATCH 0961/4607] libceph: drop snapid in ceph_calc_raw_layout() A snapshot id must be provided to ceph_calc_raw_layout() even though it is not needed at all for calculating the layout. Where the snapshot id *is* needed is when building the request message for an osd operation. Drop the snapid parameter from ceph_calc_raw_layout() and pass that value instead in ceph_osdc_build_request(). Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 4 ++-- include/linux/ceph/osd_client.h | 2 +- net/ceph/osd_client.c | 14 ++++---------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index c1b135b6cb97..fa371868e9b0 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1169,10 +1169,10 @@ static int rbd_do_request(struct request *rq, rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); ret = ceph_calc_raw_layout(osdc, &osd_req->r_file_layout, - snapid, ofs, &len, &bno, osd_req, ops); + ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); - ceph_osdc_build_request(osd_req, ofs, len, ops, snapc, &mtime); + ceph_osdc_build_request(osd_req, ofs, len, ops, snapc, snapid, &mtime); if (linger_req) { ceph_osdc_set_request_linger(osdc, osd_req); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 4bfb4582439a..0e82a0a967ef 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -209,7 +209,6 @@ extern void ceph_osdc_handle_map(struct ceph_osd_client *osdc, extern int ceph_calc_raw_layout(struct ceph_osd_client *osdc, struct ceph_file_layout *layout, - u64 snapid, u64 off, u64 *plen, u64 *bno, struct ceph_osd_request *req, struct ceph_osd_req_op *op); @@ -227,6 +226,7 @@ extern void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 len, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, + u64 snap_id, struct timespec *mtime); extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 47e5f5b1f94c..b5a4b2875e8a 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -40,18 +40,14 @@ static int op_has_extent(int op) int ceph_calc_raw_layout(struct ceph_osd_client *osdc, struct ceph_file_layout *layout, - u64 snapid, u64 off, u64 *plen, u64 *bno, struct ceph_osd_request *req, struct ceph_osd_req_op *op) { - struct ceph_osd_request_head *reqhead = req->r_request->front.iov_base; u64 orig_len = *plen; u64 objoff, objlen; /* extent in object */ int r; - reqhead->snapid = cpu_to_le64(snapid); - /* object extent? */ r = ceph_calc_file_object_mapping(layout, off, orig_len, bno, &objoff, &objlen); @@ -121,8 +117,7 @@ static int calc_layout(struct ceph_osd_client *osdc, u64 bno; int r; - r = ceph_calc_raw_layout(osdc, layout, vino.snap, off, - plen, &bno, req, op); + r = ceph_calc_raw_layout(osdc, layout, off, plen, &bno, req, op); if (r < 0) return r; @@ -340,7 +335,7 @@ static void osd_req_encode_op(struct ceph_osd_request *req, void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 len, struct ceph_osd_req_op *src_ops, - struct ceph_snap_context *snapc, + struct ceph_snap_context *snapc, u64 snap_id, struct timespec *mtime) { struct ceph_msg *msg = req->r_request; @@ -355,6 +350,7 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, int i; head = msg->front.iov_base; + head->snapid = cpu_to_le64(snap_id); op = (void *)(head + 1); p = (void *)(op + num_op); @@ -466,9 +462,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, req->r_num_pages = calc_pages_for(page_align, *plen); req->r_page_alignment = page_align; - ceph_osdc_build_request(req, off, *plen, ops, - snapc, - mtime); + ceph_osdc_build_request(req, off, *plen, ops, snapc, vino.snap, mtime); return req; } From e75b45cf36565fd8ba206a9d80f670a86e61ba2f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:14 -0600 Subject: [PATCH 0962/4607] libceph: drop osdc from ceph_calc_raw_layout() The osdc parameter to ceph_calc_raw_layout() is not used, so get rid of it. Consequently, the corresponding parameter in calc_layout() becomes unused, so get rid of that as well. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 2 +- include/linux/ceph/osd_client.h | 3 +-- net/ceph/osd_client.c | 10 ++++------ 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index fa371868e9b0..ac8fd3856509 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1168,7 +1168,7 @@ static int rbd_do_request(struct request *rq, osd_req->r_oid_len = strlen(osd_req->r_oid); rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); - ret = ceph_calc_raw_layout(osdc, &osd_req->r_file_layout, + ret = ceph_calc_raw_layout(&osd_req->r_file_layout, ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 0e82a0a967ef..fe3a6e8db1f9 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -207,8 +207,7 @@ extern void ceph_osdc_handle_reply(struct ceph_osd_client *osdc, extern void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg); -extern int ceph_calc_raw_layout(struct ceph_osd_client *osdc, - struct ceph_file_layout *layout, +extern int ceph_calc_raw_layout(struct ceph_file_layout *layout, u64 off, u64 *plen, u64 *bno, struct ceph_osd_request *req, struct ceph_osd_req_op *op); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index b5a4b2875e8a..cd9c28387de3 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -38,8 +38,7 @@ static int op_has_extent(int op) op == CEPH_OSD_OP_WRITE); } -int ceph_calc_raw_layout(struct ceph_osd_client *osdc, - struct ceph_file_layout *layout, +int ceph_calc_raw_layout(struct ceph_file_layout *layout, u64 off, u64 *plen, u64 *bno, struct ceph_osd_request *req, struct ceph_osd_req_op *op) @@ -107,8 +106,7 @@ EXPORT_SYMBOL(ceph_calc_raw_layout); * * fill osd op in request message. */ -static int calc_layout(struct ceph_osd_client *osdc, - struct ceph_vino vino, +static int calc_layout(struct ceph_vino vino, struct ceph_file_layout *layout, u64 off, u64 *plen, struct ceph_osd_request *req, @@ -117,7 +115,7 @@ static int calc_layout(struct ceph_osd_client *osdc, u64 bno; int r; - r = ceph_calc_raw_layout(osdc, layout, off, plen, &bno, req, op); + r = ceph_calc_raw_layout(layout, off, plen, &bno, req, op); if (r < 0) return r; @@ -452,7 +450,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, return ERR_PTR(-ENOMEM); /* calculate max write size */ - r = calc_layout(osdc, vino, layout, off, plen, req, ops); + r = calc_layout(vino, layout, off, plen, req, ops); if (r < 0) return ERR_PTR(r); req->r_file_layout = *layout; /* keep a copy */ From d178a9e74006e80f568d87e29f2a68f14fc7cbb1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: [PATCH 0963/4607] libceph: don't set flags in ceph_osdc_alloc_request() The only thing ceph_osdc_alloc_request() really does with the flags value it is passed is assign it to the newly-created osd request structure. Do that in the caller instead. Both callers subsequently call ceph_osdc_build_request(), so have that function (instead of ceph_osdc_alloc_request()) issue a warning if a request comes through with neither the read nor write flags set. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 3 ++- include/linux/ceph/osd_client.h | 1 - net/ceph/osd_client.c | 11 ++++------- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index ac8fd3856509..bdbaa4cfd9d3 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1148,13 +1148,14 @@ static int rbd_do_request(struct request *rq, (unsigned long long) len, coll, coll_index); osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, flags, snapc, ops, + osd_req = ceph_osdc_alloc_request(osdc, snapc, ops, false, GFP_NOIO, pages, bio); if (!osd_req) { ret = -ENOMEM; goto done_pages; } + osd_req->r_flags = flags; osd_req->r_callback = rbd_cb; rbd_req->rq = rq; diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index fe3a6e8db1f9..6ddda5bbd1a6 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -213,7 +213,6 @@ extern int ceph_calc_raw_layout(struct ceph_file_layout *layout, struct ceph_osd_req_op *op); extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, - int flags, struct ceph_snap_context *snapc, struct ceph_osd_req_op *ops, bool use_mempool, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index cd9c28387de3..77ce1edaa07d 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -171,7 +171,6 @@ static int get_num_ops(struct ceph_osd_req_op *ops) } struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, - int flags, struct ceph_snap_context *snapc, struct ceph_osd_req_op *ops, bool use_mempool, @@ -208,10 +207,6 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, INIT_LIST_HEAD(&req->r_req_lru_item); INIT_LIST_HEAD(&req->r_osd_item); - req->r_flags = flags; - - WARN_ON((flags & (CEPH_OSD_FLAG_READ|CEPH_OSD_FLAG_WRITE)) == 0); - /* create reply message */ if (use_mempool) msg = ceph_msgpool_get(&osdc->msgpool_op_reply, 0); @@ -347,6 +342,8 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, u64 data_len = 0; int i; + WARN_ON((flags & (CEPH_OSD_FLAG_READ|CEPH_OSD_FLAG_WRITE)) == 0); + head = msg->front.iov_base; head->snapid = cpu_to_le64(snap_id); op = (void *)(head + 1); @@ -442,12 +439,12 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, } else ops[1].op = 0; - req = ceph_osdc_alloc_request(osdc, flags, - snapc, ops, + req = ceph_osdc_alloc_request(osdc, snapc, ops, use_mempool, GFP_NOFS, NULL, NULL); if (!req) return ERR_PTR(-ENOMEM); + req->r_flags = flags; /* calculate max write size */ r = calc_layout(vino, layout, off, plen, req, ops); From 54a5400721da7fa5a16cea151aade5bdfee74111 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: [PATCH 0964/4607] libceph: don't set pages or bio in ceph_osdc_alloc_request() Only one of the two callers of ceph_osdc_alloc_request() provides page or bio data for its payload. And essentially all that function was doing with those arguments was assigning them to fields in the osd request structure. Simplify ceph_osdc_alloc_request() by having the caller take care of making those assignments Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 8 ++++++-- include/linux/ceph/osd_client.h | 4 +--- net/ceph/osd_client.c | 15 ++------------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index bdbaa4cfd9d3..d1445df6398a 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1148,14 +1148,18 @@ static int rbd_do_request(struct request *rq, (unsigned long long) len, coll, coll_index); osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, snapc, ops, - false, GFP_NOIO, pages, bio); + osd_req = ceph_osdc_alloc_request(osdc, snapc, ops, false, GFP_NOIO); if (!osd_req) { ret = -ENOMEM; goto done_pages; } osd_req->r_flags = flags; + osd_req->r_pages = pages; + if (bio) { + osd_req->r_bio = bio; + bio_get(osd_req->r_bio); + } osd_req->r_callback = rbd_cb; rbd_req->rq = rq; diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 6ddda5bbd1a6..75f56d372d44 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -216,9 +216,7 @@ extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client * struct ceph_snap_context *snapc, struct ceph_osd_req_op *ops, bool use_mempool, - gfp_t gfp_flags, - struct page **pages, - struct bio *bio); + gfp_t gfp_flags); extern void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 len, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 77ce1edaa07d..bdc3bb12bfd5 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -174,9 +174,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, struct ceph_snap_context *snapc, struct ceph_osd_req_op *ops, bool use_mempool, - gfp_t gfp_flags, - struct page **pages, - struct bio *bio) + gfp_t gfp_flags) { struct ceph_osd_request *req; struct ceph_msg *msg; @@ -237,13 +235,6 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, memset(msg->front.iov_base, 0, msg->front.iov_len); req->r_request = msg; - req->r_pages = pages; -#ifdef CONFIG_BLOCK - if (bio) { - req->r_bio = bio; - bio_get(req->r_bio); - } -#endif return req; } @@ -439,9 +430,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, } else ops[1].op = 0; - req = ceph_osdc_alloc_request(osdc, snapc, ops, - use_mempool, - GFP_NOFS, NULL, NULL); + req = ceph_osdc_alloc_request(osdc, snapc, ops, use_mempool, GFP_NOFS); if (!req) return ERR_PTR(-ENOMEM); req->r_flags = flags; From d07c09589f533db9ab500ac38151bc9f3a4d0648 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: [PATCH 0965/4607] rbd: pass num_op with ops array Add a num_op parameter to rbd_do_request() and rbd_req_sync_op() to indicate the number of entries in the array. The callers of these functions always know how many entries are in the array, so just pass that information down. This is in anticipation of eliminating the extra zero-filled entry in these ops arrays. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d1445df6398a..b6872d3cb04c 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1119,6 +1119,7 @@ static int rbd_do_request(struct request *rq, struct page **pages, int num_pages, int flags, + unsigned int num_op, struct ceph_osd_req_op *ops, struct rbd_req_coll *coll, int coll_index, @@ -1259,6 +1260,7 @@ static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, */ static int rbd_req_sync_op(struct rbd_device *rbd_dev, int flags, + unsigned int num_op, struct ceph_osd_req_op *ops, const char *object_name, u64 ofs, u64 inbound_size, @@ -1281,7 +1283,7 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, object_name, ofs, inbound_size, NULL, pages, num_pages, flags, - ops, + num_op, ops, NULL, 0, NULL, linger_req, ver); @@ -1351,7 +1353,7 @@ static int rbd_do_op(struct request *rq, bio, NULL, 0, flags, - ops, + 1, ops, coll, coll_index, rbd_req_cb, 0, NULL); if (ret < 0) @@ -1380,7 +1382,7 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, - ops, object_name, ofs, len, buf, NULL, ver); + 1, ops, object_name, ofs, len, buf, NULL, ver); rbd_destroy_ops(ops); return ret; @@ -1408,7 +1410,7 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, rbd_dev->header_name, 0, 0, NULL, NULL, 0, CEPH_OSD_FLAG_READ, - ops, + 1, ops, NULL, 0, rbd_simple_req_cb, 0, NULL); @@ -1460,7 +1462,7 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - ops, + 1, ops, rbd_dev->header_name, 0, 0, NULL, &rbd_dev->watch_request, NULL); @@ -1497,7 +1499,7 @@ static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - ops, + 1, ops, rbd_dev->header_name, 0, 0, NULL, NULL, NULL); @@ -1548,7 +1550,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, ops[0].cls.indata = outbound; ops[0].cls.indata_len = outbound_size; - ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, ops, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, 1, ops, object_name, 0, inbound_size, inbound, NULL, ver); From ae7ca4a35b1f5df86e2c32b2cfc01a8d528c7b8c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: [PATCH 0966/4607] libceph: pass num_op with ops Both ceph_osdc_alloc_request() and ceph_osdc_build_request() are provided an array of ceph osd request operations. Rather than just passing the number of operations in the array, the caller is required append an additional zeroed operation structure to signal the end of the array. All callers know the number of operations at the time these functions are called, so drop the silly zero entry and supply that number directly. As a result, get_num_ops() is no longer needed. This also means that ceph_osdc_alloc_request() never uses its ops argument, so that can be dropped. Also rbd_create_rw_ops() no longer needs to add one to reserve room for the additional op. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 9 ++++--- include/linux/ceph/osd_client.h | 3 ++- net/ceph/osd_client.c | 43 ++++++++++++--------------------- 3 files changed, 22 insertions(+), 33 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index b6872d3cb04c..88de8ccb29bd 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1026,12 +1026,12 @@ out_err: /* * helpers for osd request op vectors. */ -static struct ceph_osd_req_op *rbd_create_rw_ops(int num_ops, +static struct ceph_osd_req_op *rbd_create_rw_ops(int num_op, int opcode, u32 payload_len) { struct ceph_osd_req_op *ops; - ops = kzalloc(sizeof (*ops) * (num_ops + 1), GFP_NOIO); + ops = kzalloc(num_op * sizeof (*ops), GFP_NOIO); if (!ops) return NULL; @@ -1149,7 +1149,7 @@ static int rbd_do_request(struct request *rq, (unsigned long long) len, coll, coll_index); osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, snapc, ops, false, GFP_NOIO); + osd_req = ceph_osdc_alloc_request(osdc, snapc, num_op, false, GFP_NOIO); if (!osd_req) { ret = -ENOMEM; goto done_pages; @@ -1178,7 +1178,8 @@ static int rbd_do_request(struct request *rq, ofs, &len, &bno, osd_req, ops); rbd_assert(ret == 0); - ceph_osdc_build_request(osd_req, ofs, len, ops, snapc, snapid, &mtime); + ceph_osdc_build_request(osd_req, ofs, len, num_op, ops, + snapc, snapid, &mtime); if (linger_req) { ceph_osdc_set_request_linger(osdc, osd_req); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 75f56d372d44..2b04d054e09d 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -214,12 +214,13 @@ extern int ceph_calc_raw_layout(struct ceph_file_layout *layout, extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, struct ceph_snap_context *snapc, - struct ceph_osd_req_op *ops, + unsigned int num_op, bool use_mempool, gfp_t gfp_flags); extern void ceph_osdc_build_request(struct ceph_osd_request *req, u64 off, u64 len, + unsigned int num_op, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, u64 snap_id, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index bdc3bb12bfd5..500ae8b49321 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -160,25 +160,14 @@ void ceph_osdc_release_request(struct kref *kref) } EXPORT_SYMBOL(ceph_osdc_release_request); -static int get_num_ops(struct ceph_osd_req_op *ops) -{ - int i = 0; - - while (ops[i].op) - i++; - - return i; -} - struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, struct ceph_snap_context *snapc, - struct ceph_osd_req_op *ops, + unsigned int num_op, bool use_mempool, gfp_t gfp_flags) { struct ceph_osd_request *req; struct ceph_msg *msg; - int num_op = get_num_ops(ops); size_t msg_size = sizeof(struct ceph_osd_request_head); msg_size += num_op*sizeof(struct ceph_osd_op); @@ -317,7 +306,7 @@ static void osd_req_encode_op(struct ceph_osd_request *req, * */ void ceph_osdc_build_request(struct ceph_osd_request *req, - u64 off, u64 len, + u64 off, u64 len, unsigned int num_op, struct ceph_osd_req_op *src_ops, struct ceph_snap_context *snapc, u64 snap_id, struct timespec *mtime) @@ -327,7 +316,6 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, struct ceph_osd_req_op *src_op; struct ceph_osd_op *op; void *p; - int num_op = get_num_ops(src_ops); size_t msg_size = sizeof(*head) + num_op*sizeof(*op); int flags = req->r_flags; u64 data_len = 0; @@ -346,20 +334,17 @@ void ceph_osdc_build_request(struct ceph_osd_request *req, head->flags = cpu_to_le32(flags); if (flags & CEPH_OSD_FLAG_WRITE) ceph_encode_timespec(&head->mtime, mtime); + BUG_ON(num_op > (unsigned int) ((u16) -1)); head->num_ops = cpu_to_le16(num_op); - /* fill in oid */ head->object_len = cpu_to_le32(req->r_oid_len); memcpy(p, req->r_oid, req->r_oid_len); p += req->r_oid_len; src_op = src_ops; - while (src_op->op) { - osd_req_encode_op(req, op, src_op); - src_op++; - op++; - } + while (num_op--) + osd_req_encode_op(req, op++, src_op++); data_len += req->r_trail.length; @@ -414,23 +399,24 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, bool use_mempool, int num_reply, int page_align) { - struct ceph_osd_req_op ops[3]; + struct ceph_osd_req_op ops[2]; struct ceph_osd_request *req; + unsigned int num_op = 1; int r; + memset(&ops, 0, sizeof ops); + ops[0].op = opcode; ops[0].extent.truncate_seq = truncate_seq; ops[0].extent.truncate_size = truncate_size; - ops[0].payload_len = 0; if (do_sync) { ops[1].op = CEPH_OSD_OP_STARTSYNC; - ops[1].payload_len = 0; - ops[2].op = 0; - } else - ops[1].op = 0; + num_op++; + } - req = ceph_osdc_alloc_request(osdc, snapc, ops, use_mempool, GFP_NOFS); + req = ceph_osdc_alloc_request(osdc, snapc, num_op, use_mempool, + GFP_NOFS); if (!req) return ERR_PTR(-ENOMEM); req->r_flags = flags; @@ -446,7 +432,8 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, req->r_num_pages = calc_pages_for(page_align, *plen); req->r_page_alignment = page_align; - ceph_osdc_build_request(req, off, *plen, ops, snapc, vino.snap, mtime); + ceph_osdc_build_request(req, off, *plen, num_op, ops, + snapc, vino.snap, mtime); return req; } From 139b4318ad93ae4370d88882ff89b42dcbfaaab1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: [PATCH 0967/4607] rbd: there is really only one op Throughout the rbd code there are spots where it appears we can handle an osd request containing more than one osd request op. But that is only the way it appears. In fact, currently only one operation at a time can be supported, and supporting more than one will require much more than fleshing out the support that's there now. This patch changes names to make it perfectly clear that anywhere we're dealing with a block of ops, we're in fact dealing with exactly one of them. We'll be able to simplify some things as a result. When multiple op support is implemented, we can update things again accordingly. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 118 +++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 62 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 88de8ccb29bd..cc8924d8f263 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1023,32 +1023,26 @@ out_err: return NULL; } -/* - * helpers for osd request op vectors. - */ -static struct ceph_osd_req_op *rbd_create_rw_ops(int num_op, - int opcode, u32 payload_len) +static struct ceph_osd_req_op *rbd_create_rw_op(int opcode, u32 payload_len) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; - ops = kzalloc(num_op * sizeof (*ops), GFP_NOIO); - if (!ops) + op = kzalloc(sizeof (*op), GFP_NOIO); + if (!op) return NULL; - - ops[0].op = opcode; - /* * op extent offset and length will be set later on * in calc_raw_layout() */ - ops[0].payload_len = payload_len; + op->op = opcode; + op->payload_len = payload_len; - return ops; + return op; } -static void rbd_destroy_ops(struct ceph_osd_req_op *ops) +static void rbd_destroy_op(struct ceph_osd_req_op *op) { - kfree(ops); + kfree(op); } static void rbd_coll_end_req_index(struct request *rq, @@ -1314,7 +1308,7 @@ static int rbd_do_op(struct request *rq, u64 seg_ofs; u64 seg_len; int ret; - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; u32 payload_len; int opcode; int flags; @@ -1340,8 +1334,8 @@ static int rbd_do_op(struct request *rq, } ret = -ENOMEM; - ops = rbd_create_rw_ops(1, opcode, payload_len); - if (!ops) + op = rbd_create_rw_op(opcode, payload_len); + if (!op) goto done; /* we've taken care of segment sizes earlier when we @@ -1354,13 +1348,13 @@ static int rbd_do_op(struct request *rq, bio, NULL, 0, flags, - 1, ops, + 1, op, coll, coll_index, rbd_req_cb, 0, NULL); if (ret < 0) rbd_coll_end_req_index(rq, coll, coll_index, (s32)ret, seg_len); - rbd_destroy_ops(ops); + rbd_destroy_op(op); done: kfree(seg_name); return ret; @@ -1375,16 +1369,16 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, char *buf, u64 *ver) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; int ret; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_READ, 0); - if (!ops) + op = rbd_create_rw_op(CEPH_OSD_OP_READ, 0); + if (!op) return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, - 1, ops, object_name, ofs, len, buf, NULL, ver); - rbd_destroy_ops(ops); + 1, op, object_name, ofs, len, buf, NULL, ver); + rbd_destroy_op(op); return ret; } @@ -1396,26 +1390,26 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, u64 ver, u64 notify_id) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; int ret; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_NOTIFY_ACK, 0); - if (!ops) + op = rbd_create_rw_op(CEPH_OSD_OP_NOTIFY_ACK, 0); + if (!op) return -ENOMEM; - ops[0].watch.ver = cpu_to_le64(ver); - ops[0].watch.cookie = notify_id; - ops[0].watch.flag = 0; + op->watch.ver = cpu_to_le64(ver); + op->watch.cookie = notify_id; + op->watch.flag = 0; ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, rbd_dev->header_name, 0, 0, NULL, NULL, 0, CEPH_OSD_FLAG_READ, - 1, ops, + 1, op, NULL, 0, rbd_simple_req_cb, 0, NULL); - rbd_destroy_ops(ops); + rbd_destroy_op(op); return ret; } @@ -1444,12 +1438,12 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) */ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; int ret; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0); - if (!ops) + op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0); + if (!op) return -ENOMEM; ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, @@ -1457,13 +1451,13 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) if (ret < 0) goto fail; - ops[0].watch.ver = cpu_to_le64(rbd_dev->header.obj_version); - ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); - ops[0].watch.flag = 1; + op->watch.ver = cpu_to_le64(rbd_dev->header.obj_version); + op->watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); + op->watch.flag = 1; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - 1, ops, + 1, op, rbd_dev->header_name, 0, 0, NULL, &rbd_dev->watch_request, NULL); @@ -1471,14 +1465,14 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) if (ret < 0) goto fail_event; - rbd_destroy_ops(ops); + rbd_destroy_op(op); return 0; fail_event: ceph_osdc_cancel_event(rbd_dev->watch_event); rbd_dev->watch_event = NULL; fail: - rbd_destroy_ops(ops); + rbd_destroy_op(op); return ret; } @@ -1487,25 +1481,25 @@ fail: */ static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; int ret; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_WATCH, 0); - if (!ops) + op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0); + if (!op) return -ENOMEM; - ops[0].watch.ver = 0; - ops[0].watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); - ops[0].watch.flag = 0; + op->watch.ver = 0; + op->watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); + op->watch.flag = 0; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - 1, ops, + 1, op, rbd_dev->header_name, 0, 0, NULL, NULL, NULL); - rbd_destroy_ops(ops); + rbd_destroy_op(op); ceph_osdc_cancel_event(rbd_dev->watch_event); rbd_dev->watch_event = NULL; return ret; @@ -1524,7 +1518,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, size_t inbound_size, u64 *ver) { - struct ceph_osd_req_op *ops; + struct ceph_osd_req_op *op; int class_name_len = strlen(class_name); int method_name_len = strlen(method_name); int payload_size; @@ -1539,23 +1533,23 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, * operation. */ payload_size = class_name_len + method_name_len + outbound_size; - ops = rbd_create_rw_ops(1, CEPH_OSD_OP_CALL, payload_size); - if (!ops) + op = rbd_create_rw_op(CEPH_OSD_OP_CALL, payload_size); + if (!op) return -ENOMEM; - ops[0].cls.class_name = class_name; - ops[0].cls.class_len = (__u8) class_name_len; - ops[0].cls.method_name = method_name; - ops[0].cls.method_len = (__u8) method_name_len; - ops[0].cls.argc = 0; - ops[0].cls.indata = outbound; - ops[0].cls.indata_len = outbound_size; + op->cls.class_name = class_name; + op->cls.class_len = (__u8) class_name_len; + op->cls.method_name = method_name; + op->cls.method_len = (__u8) method_name_len; + op->cls.argc = 0; + op->cls.indata = outbound; + op->cls.indata_len = outbound_size; - ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, 1, ops, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, 1, op, object_name, 0, inbound_size, inbound, NULL, ver); - rbd_destroy_ops(ops); + rbd_destroy_op(op); dout("cls_exec returned %d\n", ret); return ret; From 30573d680355ca0de4db2113b9080cd078ac726f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: [PATCH 0968/4607] rbd: assume single op in a request We now know that every of rbd_req_sync_op() passes an array of exactly one operation, as evidenced by all callers passing 1 as its num_op argument. So get rid of that argument, assuming a single op. Similarly, we now know that all callers of rbd_do_request() pass 1 as the num_op value, so that parameter can be eliminated as well. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index cc8924d8f263..7ce178fd6fe5 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1113,8 +1113,7 @@ static int rbd_do_request(struct request *rq, struct page **pages, int num_pages, int flags, - unsigned int num_op, - struct ceph_osd_req_op *ops, + struct ceph_osd_req_op *op, struct rbd_req_coll *coll, int coll_index, void (*rbd_cb)(struct ceph_osd_request *, @@ -1143,7 +1142,7 @@ static int rbd_do_request(struct request *rq, (unsigned long long) len, coll, coll_index); osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, snapc, num_op, false, GFP_NOIO); + osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_NOIO); if (!osd_req) { ret = -ENOMEM; goto done_pages; @@ -1169,10 +1168,10 @@ static int rbd_do_request(struct request *rq, rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); ret = ceph_calc_raw_layout(&osd_req->r_file_layout, - ofs, &len, &bno, osd_req, ops); + ofs, &len, &bno, osd_req, op); rbd_assert(ret == 0); - ceph_osdc_build_request(osd_req, ofs, len, num_op, ops, + ceph_osdc_build_request(osd_req, ofs, len, 1, op, snapc, snapid, &mtime); if (linger_req) { @@ -1255,8 +1254,7 @@ static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, */ static int rbd_req_sync_op(struct rbd_device *rbd_dev, int flags, - unsigned int num_op, - struct ceph_osd_req_op *ops, + struct ceph_osd_req_op *op, const char *object_name, u64 ofs, u64 inbound_size, char *inbound, @@ -1267,7 +1265,7 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, struct page **pages; int num_pages; - rbd_assert(ops != NULL); + rbd_assert(op != NULL); num_pages = calc_pages_for(ofs, inbound_size); pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL); @@ -1278,7 +1276,7 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, object_name, ofs, inbound_size, NULL, pages, num_pages, flags, - num_op, ops, + op, NULL, 0, NULL, linger_req, ver); @@ -1348,7 +1346,7 @@ static int rbd_do_op(struct request *rq, bio, NULL, 0, flags, - 1, op, + op, coll, coll_index, rbd_req_cb, 0, NULL); if (ret < 0) @@ -1377,7 +1375,7 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, - 1, op, object_name, ofs, len, buf, NULL, ver); + op, object_name, ofs, len, buf, NULL, ver); rbd_destroy_op(op); return ret; @@ -1405,7 +1403,7 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, rbd_dev->header_name, 0, 0, NULL, NULL, 0, CEPH_OSD_FLAG_READ, - 1, op, + op, NULL, 0, rbd_simple_req_cb, 0, NULL); @@ -1457,7 +1455,7 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev) ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - 1, op, + op, rbd_dev->header_name, 0, 0, NULL, &rbd_dev->watch_request, NULL); @@ -1494,7 +1492,7 @@ static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - 1, op, + op, rbd_dev->header_name, 0, 0, NULL, NULL, NULL); @@ -1545,7 +1543,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, op->cls.indata = outbound; op->cls.indata_len = outbound_size; - ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, 1, op, + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, op, object_name, 0, inbound_size, inbound, NULL, ver); From 2b5fc648af5eec2f4fe984cb6b926214e02c5cf4 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 09:38:20 -0600 Subject: [PATCH 0969/4607] rbd: kill ceph_osd_req_op->flags The flags field of struct ceph_osd_req_op is never used, so just get rid of it. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- include/linux/ceph/osd_client.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 2b04d054e09d..69287ccfe68a 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -157,7 +157,6 @@ struct ceph_osd_client { struct ceph_osd_req_op { u16 op; /* CEPH_OSD_OP_* */ - u32 flags; /* CEPH_OSD_FLAG_* */ union { struct { u64 offset, length; From 0829661863fb5c8031c1c5c119693ea157517783 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 12:25:18 -0600 Subject: [PATCH 0970/4607] rbd: pull in ceph_calc_raw_layout() This is the first in a series of patches aimed at eliminating the use of ceph_calc_raw_layout() by rbd. It simply pulls in a copy of that function and renames it rbd_calc_raw_layout(). Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 7ce178fd6fe5..eee0d3649dcf 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1101,6 +1101,40 @@ static void rbd_layout_init(struct ceph_file_layout *layout, u64 pool_id) layout->fl_pg_pool = cpu_to_le32((u32) pool_id); } +int rbd_calc_raw_layout(struct ceph_file_layout *layout, + u64 off, u64 *plen, u64 *bno, + struct ceph_osd_request *req, + struct ceph_osd_req_op *op) +{ + u64 orig_len = *plen; + u64 objoff, objlen; /* extent in object */ + int r; + + /* object extent? */ + r = ceph_calc_file_object_mapping(layout, off, orig_len, bno, + &objoff, &objlen); + if (r < 0) + return r; + if (objlen < orig_len) { + *plen = objlen; + dout(" skipping last %llu, final file extent %llu~%llu\n", + orig_len - *plen, off, *plen); + } + + if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { + op->extent.offset = objoff; + op->extent.length = objlen; + } + req->r_num_pages = calc_pages_for(off, *plen); + req->r_page_alignment = off & ~PAGE_MASK; + if (op->op == CEPH_OSD_OP_WRITE) + op->payload_len = *plen; + + dout("calc_layout bno=%llx %llu~%llu (%d pages)\n", + *bno, objoff, objlen, req->r_num_pages); + return 0; +} + /* * Send ceph osd request */ @@ -1167,7 +1201,7 @@ static int rbd_do_request(struct request *rq, osd_req->r_oid_len = strlen(osd_req->r_oid); rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); - ret = ceph_calc_raw_layout(&osd_req->r_file_layout, + ret = rbd_calc_raw_layout(&osd_req->r_file_layout, ofs, &len, &bno, osd_req, op); rbd_assert(ret == 0); From e01e79273b251dbb35ff2522a688229b09481923 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 12:25:18 -0600 Subject: [PATCH 0971/4607] rbd: open code rbd_calc_raw_layout() This patch gets rid of rbd_calc_raw_layout() by simply open coding it in its one caller. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 55 +++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index eee0d3649dcf..f5f4e4a8fc87 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1032,7 +1032,7 @@ static struct ceph_osd_req_op *rbd_create_rw_op(int opcode, u32 payload_len) return NULL; /* * op extent offset and length will be set later on - * in calc_raw_layout() + * after ceph_calc_file_object_mapping(). */ op->op = opcode; op->payload_len = payload_len; @@ -1101,40 +1101,6 @@ static void rbd_layout_init(struct ceph_file_layout *layout, u64 pool_id) layout->fl_pg_pool = cpu_to_le32((u32) pool_id); } -int rbd_calc_raw_layout(struct ceph_file_layout *layout, - u64 off, u64 *plen, u64 *bno, - struct ceph_osd_request *req, - struct ceph_osd_req_op *op) -{ - u64 orig_len = *plen; - u64 objoff, objlen; /* extent in object */ - int r; - - /* object extent? */ - r = ceph_calc_file_object_mapping(layout, off, orig_len, bno, - &objoff, &objlen); - if (r < 0) - return r; - if (objlen < orig_len) { - *plen = objlen; - dout(" skipping last %llu, final file extent %llu~%llu\n", - orig_len - *plen, off, *plen); - } - - if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { - op->extent.offset = objoff; - op->extent.length = objlen; - } - req->r_num_pages = calc_pages_for(off, *plen); - req->r_page_alignment = off & ~PAGE_MASK; - if (op->op == CEPH_OSD_OP_WRITE) - op->payload_len = *plen; - - dout("calc_layout bno=%llx %llu~%llu (%d pages)\n", - *bno, objoff, objlen, req->r_num_pages); - return 0; -} - /* * Send ceph osd request */ @@ -1158,6 +1124,8 @@ static int rbd_do_request(struct request *rq, struct ceph_osd_request *osd_req; int ret; u64 bno; + u64 obj_off = 0; + u64 obj_len = 0; struct timespec mtime = CURRENT_TIME; struct rbd_request *rbd_req; struct ceph_osd_client *osdc; @@ -1201,9 +1169,22 @@ static int rbd_do_request(struct request *rq, osd_req->r_oid_len = strlen(osd_req->r_oid); rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); - ret = rbd_calc_raw_layout(&osd_req->r_file_layout, - ofs, &len, &bno, osd_req, op); + ret = ceph_calc_file_object_mapping(&osd_req->r_file_layout, ofs, len, + &bno, &obj_off, &obj_len); rbd_assert(ret == 0); + if (obj_len < len) { + dout(" skipping last %llu, final file extent %llu~%llu\n", + len - obj_len, ofs, obj_len); + len = obj_len; + } + if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { + op->extent.offset = obj_off; + op->extent.length = obj_len; + if (op->op == CEPH_OSD_OP_WRITE) + op->payload_len = obj_len; + } + osd_req->r_num_pages = calc_pages_for(ofs, len); + osd_req->r_page_alignment = ofs & ~PAGE_MASK; ceph_osdc_build_request(osd_req, ofs, len, 1, op, snapc, snapid, &mtime); From 47dba7ba2623b088cbbe1ac0aaa1a034f3249b6d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 12:25:19 -0600 Subject: [PATCH 0972/4607] rbd: don't bother calculating file mapping When rbd_do_request() has a request to process it initializes a ceph file layout structure and uses it to compute offsets and limits for the range of the request using ceph_calc_file_object_mapping(). The layout used is fixed, and is based on RBD_MAX_OBJ_ORDER (30). It sets the layout's object size and stripe unit to be 1 GB (2^30), and sets the stripe count to be 1. The job of ceph_calc_file_object_mapping() is to determine which of a sequence of objects will contain data covered by range, and within that object, at what offset the range starts. It also truncates the length of the range at the end of the selected object if necessary. This is needed for ceph fs, but for rbd it really serves no purpose. It does its own blocking of images into objects, echo of which is (1 << obj_order) in size, and as a result it ignores the "bno" value returned by ceph_calc_file_object_mapping(). In addition, by the point a request has reached this function, it is already destined for a single rbd object, and its length will not exceed that object's extent. Because of this, and because the mapping will result in blocking up the range using an integer multiple of the image's object order, ceph_calc_file_object_mapping() will never change the offset or length values defined by the request. In other words, this call is a big no-op for rbd data requests. There is one exception. We read the header object using this function, and in that case we will not have already limited the request size. However, the header is a single object (not a file or rbd image), and should not be broken into pieces anyway. So in fact we should *not* be calling ceph_calc_file_object_mapping() when operating on the header object. So... Don't call ceph_calc_file_object_mapping() in rbd_do_request(), because useless for image data and incorrect to do sofor the image header. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index f5f4e4a8fc87..1c0192c3cf47 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1123,9 +1123,6 @@ static int rbd_do_request(struct request *rq, { struct ceph_osd_request *osd_req; int ret; - u64 bno; - u64 obj_off = 0; - u64 obj_len = 0; struct timespec mtime = CURRENT_TIME; struct rbd_request *rbd_req; struct ceph_osd_client *osdc; @@ -1169,19 +1166,12 @@ static int rbd_do_request(struct request *rq, osd_req->r_oid_len = strlen(osd_req->r_oid); rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); - ret = ceph_calc_file_object_mapping(&osd_req->r_file_layout, ofs, len, - &bno, &obj_off, &obj_len); - rbd_assert(ret == 0); - if (obj_len < len) { - dout(" skipping last %llu, final file extent %llu~%llu\n", - len - obj_len, ofs, obj_len); - len = obj_len; - } + if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { - op->extent.offset = obj_off; - op->extent.length = obj_len; + op->extent.offset = ofs; + op->extent.length = len; if (op->op == CEPH_OSD_OP_WRITE) - op->payload_len = obj_len; + op->payload_len = len; } osd_req->r_num_pages = calc_pages_for(ofs, len); osd_req->r_page_alignment = ofs & ~PAGE_MASK; From 0903e875caa93e1fb231dd66c69b118dbdad25cb Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 14 Nov 2012 12:25:19 -0600 Subject: [PATCH 0973/4607] rbd: use a common layout for each device Each osd message includes a layout structure, and for rbd it is always the same (at least for osd's in a given pool). Initialize a layout structure when an rbd_dev gets created and just copy that into osd requests for the rbd image. Replace an assertion that was done when initializing the layout structures with code that catches and handles anything that would trigger the assertion as soon as it is identified. This precludes that (bad) condition from ever occurring. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 1c0192c3cf47..d2a6e9589fab 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -235,6 +235,8 @@ struct rbd_device { char *header_name; + struct ceph_file_layout layout; + struct ceph_osd_event *watch_event; struct ceph_osd_request *watch_request; @@ -1091,16 +1093,6 @@ static void rbd_coll_end_req(struct rbd_request *rbd_req, ret, len); } -static void rbd_layout_init(struct ceph_file_layout *layout, u64 pool_id) -{ - memset(layout, 0, sizeof (*layout)); - layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); - layout->fl_stripe_count = cpu_to_le32(1); - layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); - rbd_assert(pool_id <= (u64) U32_MAX); - layout->fl_pg_pool = cpu_to_le32((u32) pool_id); -} - /* * Send ceph osd request */ @@ -1165,7 +1157,7 @@ static int rbd_do_request(struct request *rq, strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); osd_req->r_oid_len = strlen(osd_req->r_oid); - rbd_layout_init(&osd_req->r_file_layout, rbd_dev->spec->pool_id); + osd_req->r_file_layout = rbd_dev->layout; /* struct */ if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { op->extent.offset = ofs; @@ -2297,6 +2289,13 @@ struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, rbd_dev->spec = spec; rbd_dev->rbd_client = rbdc; + /* Initialize the layout used for all rbd requests */ + + rbd_dev->layout.fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); + rbd_dev->layout.fl_stripe_count = cpu_to_le32(1); + rbd_dev->layout.fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); + rbd_dev->layout.fl_pg_pool = cpu_to_le32((u32) spec->pool_id); + return rbd_dev; } @@ -2551,6 +2550,12 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) if (parent_spec->pool_id == CEPH_NOPOOL) goto out; /* No parent? No problem. */ + /* The ceph file layout needs to fit pool id in 32 bits */ + + ret = -EIO; + if (WARN_ON(parent_spec->pool_id > (u64) U32_MAX)) + goto out; + image_id = ceph_extract_encoded_string(&p, end, NULL, GFP_KERNEL); if (IS_ERR(image_id)) { ret = PTR_ERR(image_id); @@ -3680,6 +3685,13 @@ static ssize_t rbd_add(struct bus_type *bus, goto err_out_client; spec->pool_id = (u64) rc; + /* The ceph file layout needs to fit pool id in 32 bits */ + + if (WARN_ON(spec->pool_id > (u64) U32_MAX)) { + rc = -EIO; + goto err_out_client; + } + rbd_dev = rbd_dev_create(rbdc, spec); if (!rbd_dev) goto err_out_client; From 907703d050df92979b3848ee42f88d5c9c6c13fe Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 13 Nov 2012 21:11:15 -0600 Subject: [PATCH 0974/4607] rbd: combine rbd sync watch/unwatch functions The rbd_req_sync_watch() and rbd_req_sync_unwatch() functions are nearly identical. Combine them into a single function with a flag indicating whether a watch is to be initiated or torn down. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 81 +++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 54 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d2a6e9589fab..9d21fcd7e188 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1429,74 +1429,48 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) } /* - * Request sync osd watch + * Request sync osd watch/unwatch. The value of "start" determines + * whether a watch request is being initiated or torn down. */ -static int rbd_req_sync_watch(struct rbd_device *rbd_dev) +static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) { struct ceph_osd_req_op *op; - struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; + struct ceph_osd_request **linger_req = NULL; + __le64 version = 0; int ret; op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0); if (!op) return -ENOMEM; - ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, - (void *)rbd_dev, &rbd_dev->watch_event); - if (ret < 0) - goto fail; + if (start) { + struct ceph_osd_client *osdc; - op->watch.ver = cpu_to_le64(rbd_dev->header.obj_version); + osdc = &rbd_dev->rbd_client->client->osdc; + ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, rbd_dev, + &rbd_dev->watch_event); + if (ret < 0) + goto done; + version = cpu_to_le64(rbd_dev->header.obj_version); + linger_req = &rbd_dev->watch_request; + } + + op->watch.ver = version; op->watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); - op->watch.flag = 1; + op->watch.flag = (u8) start ? 1 : 0; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - op, - rbd_dev->header_name, - 0, 0, NULL, - &rbd_dev->watch_request, NULL); - - if (ret < 0) - goto fail_event; + op, rbd_dev->header_name, + 0, 0, NULL, linger_req, NULL); + if (!start || ret < 0) { + ceph_osdc_cancel_event(rbd_dev->watch_event); + rbd_dev->watch_event = NULL; + } +done: rbd_destroy_op(op); - return 0; -fail_event: - ceph_osdc_cancel_event(rbd_dev->watch_event); - rbd_dev->watch_event = NULL; -fail: - rbd_destroy_op(op); - return ret; -} - -/* - * Request sync osd unwatch - */ -static int rbd_req_sync_unwatch(struct rbd_device *rbd_dev) -{ - struct ceph_osd_req_op *op; - int ret; - - op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0); - if (!op) - return -ENOMEM; - - op->watch.ver = 0; - op->watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); - op->watch.flag = 0; - - ret = rbd_req_sync_op(rbd_dev, - CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - op, - rbd_dev->header_name, - 0, 0, NULL, NULL, NULL); - - - rbd_destroy_op(op); - ceph_osdc_cancel_event(rbd_dev->watch_event); - rbd_dev->watch_event = NULL; return ret; } @@ -3033,7 +3007,7 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev) int ret, rc; do { - ret = rbd_req_sync_watch(rbd_dev); + ret = rbd_req_sync_watch(rbd_dev, 1); if (ret == -ERANGE) { rc = rbd_dev_refresh(rbd_dev, NULL); if (rc < 0) @@ -3752,8 +3726,7 @@ static void rbd_dev_release(struct device *dev) rbd_dev->watch_request); } if (rbd_dev->watch_event) - rbd_req_sync_unwatch(rbd_dev); - + rbd_req_sync_watch(rbd_dev, 0); /* clean up and free blkdev */ rbd_free_disk(rbd_dev); From 2e53c6c379b65372df21f4d6019f6eb63af81384 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 30 Nov 2012 09:59:47 -0600 Subject: [PATCH 0975/4607] rbd: don't leak rbd_req on synchronous requests When rbd_do_request() is called it allocates and populates an rbd_req structure to hold information about the osd request to be sent. This is done for the benefit of the callback function (in particular, rbd_req_cb()), which uses this in processing when the request completes. Synchronous requests provide no callback function, in which case rbd_do_request() waits for the request to complete before returning. This case is not handling the needed free of the rbd_req structure like it should, so it is getting leaked. Note however that the synchronous case has no need for the rbd_req structure at all. So rather than simply freeing this structure for synchronous requests, just don't allocate it to begin with. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 52 ++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 9d21fcd7e188..28b62367ff93 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1113,20 +1113,11 @@ static int rbd_do_request(struct request *rq, struct ceph_osd_request **linger_req, u64 *ver) { - struct ceph_osd_request *osd_req; - int ret; - struct timespec mtime = CURRENT_TIME; - struct rbd_request *rbd_req; struct ceph_osd_client *osdc; - - rbd_req = kzalloc(sizeof(*rbd_req), GFP_NOIO); - if (!rbd_req) - return -ENOMEM; - - if (coll) { - rbd_req->coll = coll; - rbd_req->coll_index = coll_index; - } + struct ceph_osd_request *osd_req; + struct rbd_request *rbd_req = NULL; + struct timespec mtime = CURRENT_TIME; + int ret; dout("rbd_do_request object_name=%s ofs=%llu len=%llu coll=%p[%d]\n", object_name, (unsigned long long) ofs, @@ -1134,10 +1125,8 @@ static int rbd_do_request(struct request *rq, osdc = &rbd_dev->rbd_client->client->osdc; osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_NOIO); - if (!osd_req) { - ret = -ENOMEM; - goto done_pages; - } + if (!osd_req) + return -ENOMEM; osd_req->r_flags = flags; osd_req->r_pages = pages; @@ -1145,13 +1134,22 @@ static int rbd_do_request(struct request *rq, osd_req->r_bio = bio; bio_get(osd_req->r_bio); } + + if (rbd_cb) { + ret = -ENOMEM; + rbd_req = kmalloc(sizeof(*rbd_req), GFP_NOIO); + if (!rbd_req) + goto done_osd_req; + + rbd_req->rq = rq; + rbd_req->bio = bio; + rbd_req->pages = pages; + rbd_req->len = len; + rbd_req->coll = coll; + rbd_req->coll_index = coll ? coll_index : 0; + } + osd_req->r_callback = rbd_cb; - - rbd_req->rq = rq; - rbd_req->bio = bio; - rbd_req->pages = pages; - rbd_req->len = len; - osd_req->r_priv = rbd_req; strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); @@ -1193,10 +1191,12 @@ static int rbd_do_request(struct request *rq, return ret; done_err: - bio_chain_put(rbd_req->bio); - ceph_osdc_put_request(osd_req); -done_pages: + if (bio) + bio_chain_put(osd_req->r_bio); kfree(rbd_req); +done_osd_req: + ceph_osdc_put_request(osd_req); + return ret; } From 1821665749a3d7e26ad470c63fc2c46990dc6041 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 30 Nov 2012 09:59:47 -0600 Subject: [PATCH 0976/4607] rbd: don't leak rbd_req for rbd_req_sync_notify_ack() When rbd_req_sync_notify_ack() calls rbd_do_request() it supplies rbd_simple_req_cb() as its callback function. Because the callback is supplied, an rbd_req structure gets allocated and populated so it can be used by the callback. However rbd_simple_req_cb() is not freeing (or even using) the rbd_req structure, so it's getting leaked. Since rbd_simple_req_cb() has no need for the rbd_req structure, just avoid allocating one for this case. Of the three calls to rbd_do_request(), only the one from rbd_do_op() needs the rbd_req structure, and that call can be distinguished from the other two because it supplies a non-null rbd_collection pointer. So fix this leak by only allocating the rbd_req structure if a non-null "coll" value is provided to rbd_do_request(). Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 28b62367ff93..619d680960b1 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1135,7 +1135,7 @@ static int rbd_do_request(struct request *rq, bio_get(osd_req->r_bio); } - if (rbd_cb) { + if (coll) { ret = -ENOMEM; rbd_req = kmalloc(sizeof(*rbd_req), GFP_NOIO); if (!rbd_req) @@ -1146,7 +1146,7 @@ static int rbd_do_request(struct request *rq, rbd_req->pages = pages; rbd_req->len = len; rbd_req->coll = coll; - rbd_req->coll_index = coll ? coll_index : 0; + rbd_req->coll_index = coll_index; } osd_req->r_callback = rbd_cb; From c561191813e232aa52022532751855ff5c9fa319 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 19 Nov 2012 22:55:21 -0600 Subject: [PATCH 0977/4607] rbd: don't assign extent info in rbd_do_request() In rbd_do_request() there's a sort of last-minute assignment of the extent offset and length and payload length for read and write operations. Move those assignments into the caller (in those spots that might initiate read or write operations) Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 619d680960b1..c6917b11800b 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1156,13 +1156,6 @@ static int rbd_do_request(struct request *rq, osd_req->r_oid_len = strlen(osd_req->r_oid); osd_req->r_file_layout = rbd_dev->layout; /* struct */ - - if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { - op->extent.offset = ofs; - op->extent.length = len; - if (op->op == CEPH_OSD_OP_WRITE) - op->payload_len = len; - } osd_req->r_num_pages = calc_pages_for(ofs, len); osd_req->r_page_alignment = ofs & ~PAGE_MASK; @@ -1269,6 +1262,13 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, if (IS_ERR(pages)) return PTR_ERR(pages); + if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { + op->extent.offset = ofs; + op->extent.length = inbound_size; + if (op->op == CEPH_OSD_OP_WRITE) + op->payload_len = inbound_size; + } + ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, object_name, ofs, inbound_size, NULL, pages, num_pages, @@ -1332,6 +1332,9 @@ static int rbd_do_op(struct request *rq, op = rbd_create_rw_op(opcode, payload_len); if (!op) goto done; + op->extent.offset = seg_ofs; + op->extent.length = seg_len; + op->payload_len = payload_len; /* we've taken care of segment sizes earlier when we cloned the bios. We should never have a segment From 8d23bf29095e5fab84535035e7a27c4920812c44 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 19 Nov 2012 22:55:21 -0600 Subject: [PATCH 0978/4607] rbd: don't assign extent info in rbd_req_sync_op() Move the assignment of the extent offset and length and payload length out of rbd_req_sync_op() and into its caller in the one spot where a read (and note--no write) operation might be initiated. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 72 +++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index c6917b11800b..235cda083137 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1025,19 +1025,15 @@ out_err: return NULL; } -static struct ceph_osd_req_op *rbd_create_rw_op(int opcode, u32 payload_len) +static struct ceph_osd_req_op *rbd_create_rw_op(int opcode, u64 ofs, u64 len) { struct ceph_osd_req_op *op; op = kzalloc(sizeof (*op), GFP_NOIO); if (!op) return NULL; - /* - * op extent offset and length will be set later on - * after ceph_calc_file_object_mapping(). - */ + op->op = opcode; - op->payload_len = payload_len; return op; } @@ -1047,6 +1043,42 @@ static void rbd_destroy_op(struct ceph_osd_req_op *op) kfree(op); } +struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) +{ + struct ceph_osd_req_op *op; + va_list args; + + op = kzalloc(sizeof (*op), GFP_NOIO); + if (!op) + return NULL; + op->op = opcode; + va_start(args, opcode); + switch (opcode) { + case CEPH_OSD_OP_READ: + case CEPH_OSD_OP_WRITE: + /* rbd_osd_req_op_create(READ, offset, length) */ + /* rbd_osd_req_op_create(WRITE, offset, length) */ + op->extent.offset = va_arg(args, u64); + op->extent.length = va_arg(args, u64); + if (opcode == CEPH_OSD_OP_WRITE) + op->payload_len = op->extent.length; + break; + default: + rbd_warn(NULL, "unsupported opcode %hu\n", opcode); + kfree(op); + op = NULL; + break; + } + va_end(args); + + return op; +} + +static void rbd_osd_req_op_destroy(struct ceph_osd_req_op *op) +{ + kfree(op); +} + static void rbd_coll_end_req_index(struct request *rq, struct rbd_req_coll *coll, int index, @@ -1262,13 +1294,6 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, if (IS_ERR(pages)) return PTR_ERR(pages); - if (op->op == CEPH_OSD_OP_READ || op->op == CEPH_OSD_OP_WRITE) { - op->extent.offset = ofs; - op->extent.length = inbound_size; - if (op->op == CEPH_OSD_OP_WRITE) - op->payload_len = inbound_size; - } - ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, object_name, ofs, inbound_size, NULL, pages, num_pages, @@ -1304,7 +1329,6 @@ static int rbd_do_op(struct request *rq, u64 seg_len; int ret; struct ceph_osd_req_op *op; - u32 payload_len; int opcode; int flags; u64 snapid; @@ -1319,22 +1343,17 @@ static int rbd_do_op(struct request *rq, opcode = CEPH_OSD_OP_WRITE; flags = CEPH_OSD_FLAG_WRITE|CEPH_OSD_FLAG_ONDISK; snapid = CEPH_NOSNAP; - payload_len = seg_len; } else { opcode = CEPH_OSD_OP_READ; flags = CEPH_OSD_FLAG_READ; rbd_assert(!snapc); snapid = rbd_dev->spec->snap_id; - payload_len = 0; } ret = -ENOMEM; - op = rbd_create_rw_op(opcode, payload_len); + op = rbd_osd_req_op_create(opcode, seg_ofs, seg_len); if (!op) goto done; - op->extent.offset = seg_ofs; - op->extent.length = seg_len; - op->payload_len = payload_len; /* we've taken care of segment sizes earlier when we cloned the bios. We should never have a segment @@ -1352,7 +1371,7 @@ static int rbd_do_op(struct request *rq, if (ret < 0) rbd_coll_end_req_index(rq, coll, coll_index, (s32)ret, seg_len); - rbd_destroy_op(op); + rbd_osd_req_op_destroy(op); done: kfree(seg_name); return ret; @@ -1370,13 +1389,13 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, struct ceph_osd_req_op *op; int ret; - op = rbd_create_rw_op(CEPH_OSD_OP_READ, 0); + op = rbd_osd_req_op_create(CEPH_OSD_OP_READ, ofs, len); if (!op) return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, op, object_name, ofs, len, buf, NULL, ver); - rbd_destroy_op(op); + rbd_osd_req_op_destroy(op); return ret; } @@ -1391,7 +1410,7 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, struct ceph_osd_req_op *op; int ret; - op = rbd_create_rw_op(CEPH_OSD_OP_NOTIFY_ACK, 0); + op = rbd_create_rw_op(CEPH_OSD_OP_NOTIFY_ACK, 0, 0); if (!op) return -ENOMEM; @@ -1442,7 +1461,7 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) __le64 version = 0; int ret; - op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0); + op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0, 0); if (!op) return -ENOMEM; @@ -1505,9 +1524,10 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, * operation. */ payload_size = class_name_len + method_name_len + outbound_size; - op = rbd_create_rw_op(CEPH_OSD_OP_CALL, payload_size); + op = rbd_create_rw_op(CEPH_OSD_OP_CALL, 0, 0); if (!op) return -ENOMEM; + op->payload_len = payload_size; op->cls.class_name = class_name; op->cls.class_len = (__u8) class_name_len; From 2647ba38100765298fc67ce1ec5d32e80d9fe046 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 19 Nov 2012 22:55:21 -0600 Subject: [PATCH 0979/4607] rbd: move call osd op setup into rbd_osd_req_op_create() Move the initialization of the CEPH_OSD_OP_CALL operation into rbd_osd_req_op_create(). Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 48 ++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 235cda083137..760be82548d9 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -52,10 +52,12 @@ #define SECTOR_SHIFT 9 #define SECTOR_SIZE (1ULL << SECTOR_SHIFT) -/* It might be useful to have this defined elsewhere too */ +/* It might be useful to have these defined elsewhere */ -#define U32_MAX ((u32) (~0U)) -#define U64_MAX ((u64) (~0ULL)) +#define U8_MAX ((u8) (~0U)) +#define U16_MAX ((u16) (~0U)) +#define U32_MAX ((u32) (~0U)) +#define U64_MAX ((u64) (~0ULL)) #define RBD_DRV_NAME "rbd" #define RBD_DRV_NAME_LONG "rbd (rados block device)" @@ -1047,6 +1049,7 @@ struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) { struct ceph_osd_req_op *op; va_list args; + size_t size; op = kzalloc(sizeof (*op), GFP_NOIO); if (!op) @@ -1063,6 +1066,27 @@ struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) if (opcode == CEPH_OSD_OP_WRITE) op->payload_len = op->extent.length; break; + case CEPH_OSD_OP_CALL: + /* rbd_osd_req_op_create(CALL, class, method, data, datalen) */ + op->cls.class_name = va_arg(args, char *); + size = strlen(op->cls.class_name); + rbd_assert(size <= (size_t) U8_MAX); + op->cls.class_len = size; + op->payload_len = size; + + op->cls.method_name = va_arg(args, char *); + size = strlen(op->cls.method_name); + rbd_assert(size <= (size_t) U8_MAX); + op->cls.method_len = size; + op->payload_len += size; + + op->cls.argc = 0; + op->cls.indata = va_arg(args, void *); + size = va_arg(args, size_t); + rbd_assert(size <= (size_t) U32_MAX); + op->cls.indata_len = (u32) size; + op->payload_len += size; + break; default: rbd_warn(NULL, "unsupported opcode %hu\n", opcode); kfree(op); @@ -1510,9 +1534,6 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, u64 *ver) { struct ceph_osd_req_op *op; - int class_name_len = strlen(class_name); - int method_name_len = strlen(method_name); - int payload_size; int ret; /* @@ -1523,25 +1544,16 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, * the perspective of the server side) in the OSD request * operation. */ - payload_size = class_name_len + method_name_len + outbound_size; - op = rbd_create_rw_op(CEPH_OSD_OP_CALL, 0, 0); + op = rbd_osd_req_op_create(CEPH_OSD_OP_CALL, class_name, + method_name, outbound, outbound_size); if (!op) return -ENOMEM; - op->payload_len = payload_size; - - op->cls.class_name = class_name; - op->cls.class_len = (__u8) class_name_len; - op->cls.method_name = method_name; - op->cls.method_len = (__u8) method_name_len; - op->cls.argc = 0; - op->cls.indata = outbound; - op->cls.indata_len = outbound_size; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, op, object_name, 0, inbound_size, inbound, NULL, ver); - rbd_destroy_op(op); + rbd_osd_req_op_destroy(op); dout("cls_exec returned %d\n", ret); return ret; From 5efea49a98d1a3b3a7301d3a17f826ad4c31b290 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 19 Nov 2012 22:55:21 -0600 Subject: [PATCH 0980/4607] rbd: move remaining osd op setup into rbd_osd_req_op_create() The two remaining osd ops used by rbd are CEPH_OSD_OP_WATCH and CEPH_OSD_OP_NOTIFY_ACK. Move the setup of those operations into rbd_osd_req_op_create(), and get rid of rbd_create_rw_op() and rbd_destroy_op(). Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 68 ++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 760be82548d9..3802a7857280 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1027,24 +1027,6 @@ out_err: return NULL; } -static struct ceph_osd_req_op *rbd_create_rw_op(int opcode, u64 ofs, u64 len) -{ - struct ceph_osd_req_op *op; - - op = kzalloc(sizeof (*op), GFP_NOIO); - if (!op) - return NULL; - - op->op = opcode; - - return op; -} - -static void rbd_destroy_op(struct ceph_osd_req_op *op) -{ - kfree(op); -} - struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) { struct ceph_osd_req_op *op; @@ -1087,6 +1069,16 @@ struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) op->cls.indata_len = (u32) size; op->payload_len += size; break; + case CEPH_OSD_OP_NOTIFY_ACK: + case CEPH_OSD_OP_WATCH: + /* rbd_osd_req_op_create(NOTIFY_ACK, cookie, version) */ + /* rbd_osd_req_op_create(WATCH, cookie, version, flag) */ + op->watch.cookie = va_arg(args, u64); + op->watch.ver = va_arg(args, u64); + op->watch.ver = cpu_to_le64(op->watch.ver); + if (opcode == CEPH_OSD_OP_WATCH && va_arg(args, int)) + op->watch.flag = (u8) 1; + break; default: rbd_warn(NULL, "unsupported opcode %hu\n", opcode); kfree(op); @@ -1434,14 +1426,10 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, struct ceph_osd_req_op *op; int ret; - op = rbd_create_rw_op(CEPH_OSD_OP_NOTIFY_ACK, 0, 0); + op = rbd_osd_req_op_create(CEPH_OSD_OP_NOTIFY_ACK, notify_id, ver); if (!op) return -ENOMEM; - op->watch.ver = cpu_to_le64(ver); - op->watch.cookie = notify_id; - op->watch.flag = 0; - ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, rbd_dev->header_name, 0, 0, NULL, NULL, 0, @@ -1450,7 +1438,8 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, NULL, 0, rbd_simple_req_cb, 0, NULL); - rbd_destroy_op(op); + rbd_osd_req_op_destroy(op); + return ret; } @@ -1480,14 +1469,9 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) */ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) { - struct ceph_osd_req_op *op; struct ceph_osd_request **linger_req = NULL; - __le64 version = 0; - int ret; - - op = rbd_create_rw_op(CEPH_OSD_OP_WATCH, 0, 0); - if (!op) - return -ENOMEM; + struct ceph_osd_req_op *op; + int ret = 0; if (start) { struct ceph_osd_client *osdc; @@ -1496,26 +1480,28 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, rbd_dev, &rbd_dev->watch_event); if (ret < 0) - goto done; - version = cpu_to_le64(rbd_dev->header.obj_version); + return ret; linger_req = &rbd_dev->watch_request; + } else { + rbd_assert(rbd_dev->watch_request != NULL); } - op->watch.ver = version; - op->watch.cookie = cpu_to_le64(rbd_dev->watch_event->cookie); - op->watch.flag = (u8) start ? 1 : 0; - - ret = rbd_req_sync_op(rbd_dev, + op = rbd_osd_req_op_create(CEPH_OSD_OP_WATCH, + rbd_dev->watch_event->cookie, + rbd_dev->header.obj_version, start); + if (op) + ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, op, rbd_dev->header_name, 0, 0, NULL, linger_req, NULL); - if (!start || ret < 0) { + /* Cancel the event if we're tearing down, or on error */ + + if (!start || !op || ret < 0) { ceph_osdc_cancel_event(rbd_dev->watch_event); rbd_dev->watch_event = NULL; } -done: - rbd_destroy_op(op); + rbd_osd_req_op_destroy(op); return ret; } From 8b84de7940b69fd7326946ba244621aa5fc412e0 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 20 Nov 2012 14:17:17 -0600 Subject: [PATCH 0981/4607] rbd: assign watch request more directly Both rbd_req_sync_op() and rbd_do_request() have a "linger" parameter, which is the address of a pointer that should refer to the osd request structure used to issue a request to an osd. Only one case ever supplies a non-null "linger" argument: an CEPH_OSD_OP_WATCH start. And in that one case it is assigned &rbd_dev->watch_request. Within rbd_do_request() (where the assignment ultimately gets made) we know the rbd_dev and therefore its watch_request field. We also know whether the op being sent is CEPH_OSD_OP_WATCH start. Stop opaquely passing down the "linger" pointer, and instead just assign the value directly inside rbd_do_request() when it's needed. This makes it unnecessary for rbd_req_sync_watch() to make arrangements to hold a value that's not available until a bit later. This more clearly separates setting up a watch request from submitting it. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 3802a7857280..a3a11c3c1cac 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1158,7 +1158,6 @@ static int rbd_do_request(struct request *rq, int coll_index, void (*rbd_cb)(struct ceph_osd_request *, struct ceph_msg *), - struct ceph_osd_request **linger_req, u64 *ver) { struct ceph_osd_client *osdc; @@ -1210,9 +1209,9 @@ static int rbd_do_request(struct request *rq, ceph_osdc_build_request(osd_req, ofs, len, 1, op, snapc, snapid, &mtime); - if (linger_req) { + if (op->op == CEPH_OSD_OP_WATCH && op->watch.flag) { ceph_osdc_set_request_linger(osdc, osd_req); - *linger_req = osd_req; + rbd_dev->watch_request = osd_req; } ret = ceph_osdc_start_request(osdc, osd_req, false); @@ -1296,7 +1295,6 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, const char *object_name, u64 ofs, u64 inbound_size, char *inbound, - struct ceph_osd_request **linger_req, u64 *ver) { int ret; @@ -1317,7 +1315,7 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, op, NULL, 0, NULL, - linger_req, ver); + ver); if (ret < 0) goto done; @@ -1383,7 +1381,7 @@ static int rbd_do_op(struct request *rq, flags, op, coll, coll_index, - rbd_req_cb, 0, NULL); + rbd_req_cb, NULL); if (ret < 0) rbd_coll_end_req_index(rq, coll, coll_index, (s32)ret, seg_len); @@ -1410,7 +1408,7 @@ static int rbd_req_sync_read(struct rbd_device *rbd_dev, return -ENOMEM; ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, - op, object_name, ofs, len, buf, NULL, ver); + op, object_name, ofs, len, buf, ver); rbd_osd_req_op_destroy(op); return ret; @@ -1436,7 +1434,7 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, CEPH_OSD_FLAG_READ, op, NULL, 0, - rbd_simple_req_cb, 0, NULL); + rbd_simple_req_cb, NULL); rbd_osd_req_op_destroy(op); @@ -1469,7 +1467,6 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) */ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) { - struct ceph_osd_request **linger_req = NULL; struct ceph_osd_req_op *op; int ret = 0; @@ -1481,7 +1478,6 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) &rbd_dev->watch_event); if (ret < 0) return ret; - linger_req = &rbd_dev->watch_request; } else { rbd_assert(rbd_dev->watch_request != NULL); } @@ -1493,7 +1489,7 @@ static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, op, rbd_dev->header_name, - 0, 0, NULL, linger_req, NULL); + 0, 0, NULL, NULL); /* Cancel the event if we're tearing down, or on error */ @@ -1537,7 +1533,7 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, op, object_name, 0, inbound_size, inbound, - NULL, ver); + ver); rbd_osd_req_op_destroy(op); From e0b49868d3629708eda593b6739cb78f33ab238a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 9 Jan 2013 14:44:18 -0600 Subject: [PATCH 0982/4607] rbd: fix type of snap_id in rbd_dev_v2_snap_info() The type of the snap_id local variable is defined with the wrong byte order. Fix that. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index a3a11c3c1cac..007b726ea0eb 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -2802,7 +2802,7 @@ out: static char *rbd_dev_v2_snap_info(struct rbd_device *rbd_dev, u32 which, u64 *snap_size, u64 *snap_features) { - __le64 snap_id; + u64 snap_id; u8 order; int ret; From 7139bc1579901b53db7e898789e916ee2fb52d78 Mon Sep 17 00:00:00 2001 From: John David Anglin Date: Mon, 14 Jan 2013 19:45:00 -0500 Subject: [PATCH 0983/4607] [PARISC] Purge existing TLB entries in set_pte_at and ptep_set_wrprotect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch goes a long way toward fixing the minifail bug, and it  significantly improves the stability of SMP machines such as the rp3440.  When write  protecting a page for COW, we need to purge the existing translation.  Otherwise, the COW break doesn't occur as expected because the TLB may still have a stale entry which allows writes. [jejb: fix up checkpatch errors] Signed-off-by: John David Anglin Cc: stable@vger.kernel.org Signed-off-by: James Bottomley --- arch/parisc/include/asm/pgtable.h | 13 ++++++++++--- arch/parisc/kernel/cache.c | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/arch/parisc/include/asm/pgtable.h b/arch/parisc/include/asm/pgtable.h index ee99f2339356..7df49fad29f9 100644 --- a/arch/parisc/include/asm/pgtable.h +++ b/arch/parisc/include/asm/pgtable.h @@ -12,11 +12,10 @@ #include #include +#include #include #include -struct vm_area_struct; - /* * kern_addr_valid(ADDR) tests if ADDR is pointing to valid kernel * memory. For the return value to be meaningful, ADDR must be >= @@ -40,7 +39,14 @@ struct vm_area_struct; do{ \ *(pteptr) = (pteval); \ } while(0) -#define set_pte_at(mm,addr,ptep,pteval) set_pte(ptep,pteval) + +extern void purge_tlb_entries(struct mm_struct *, unsigned long); + +#define set_pte_at(mm, addr, ptep, pteval) \ + do { \ + set_pte(ptep, pteval); \ + purge_tlb_entries(mm, addr); \ + } while (0) #endif /* !__ASSEMBLY__ */ @@ -466,6 +472,7 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, old = pte_val(*ptep); new = pte_val(pte_wrprotect(__pte (old))); } while (cmpxchg((unsigned long *) ptep, old, new) != old); + purge_tlb_entries(mm, addr); #else pte_t old_pte = *ptep; set_pte_at(mm, addr, ptep, pte_wrprotect(old_pte)); diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index 48e16dc20102..b89a85ab945e 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -419,6 +419,24 @@ void kunmap_parisc(void *addr) EXPORT_SYMBOL(kunmap_parisc); #endif +void purge_tlb_entries(struct mm_struct *mm, unsigned long addr) +{ + unsigned long flags; + + /* Note: purge_tlb_entries can be called at startup with + no context. */ + + /* Disable preemption while we play with %sr1. */ + preempt_disable(); + mtsp(mm->context, 1); + purge_tlb_start(flags); + pdtlb(addr); + pitlb(addr); + purge_tlb_end(flags); + preempt_enable(); +} +EXPORT_SYMBOL(purge_tlb_entries); + void __flush_tlb_range(unsigned long sid, unsigned long start, unsigned long end) { From 17bebdcd5c7c56cde82b8ccb02c5cea69e05f6d3 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 7 Jan 2013 11:00:16 +0100 Subject: [PATCH 0984/4607] crypto: bfin_crc - reposition free_irq to avoid access to invalid data The data referenced by an interrupt handler should not be freed before the interrupt is ended. The handler is bfin_crypto_crc_handler. It may refer to crc->regs, which is released by the iounmap. Furthermore, the second argument to all calls to free_irq is incorrect. It should be the same as the last argument of request_irq, which is crc, rather than crc->dev. The semantic match that finds the first problem is as follows: (http://coccinelle.lip6.fr/) // @fn exists@ expression list es; expression a,b; identifier f; @@ if (...) { ... when any free_irq(a,b); ... when any f(es); ... when any return ...; } @@ expression list fn.es; expression fn.a,fn.b; identifier fn.f; @@ *f(es); ... when any *free_irq(a,b); // Signed-off-by: Julia Lawall Signed-off-by: Herbert Xu --- drivers/crypto/bfin_crc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/crypto/bfin_crc.c b/drivers/crypto/bfin_crc.c index 5398580b4313..06fa1e4f49d3 100644 --- a/drivers/crypto/bfin_crc.c +++ b/drivers/crypto/bfin_crc.c @@ -694,7 +694,7 @@ out_error_dma: dma_free_coherent(&pdev->dev, PAGE_SIZE, crc->sg_cpu, crc->sg_dma); free_dma(crc->dma_ch); out_error_irq: - free_irq(crc->irq, crc->dev); + free_irq(crc->irq, crc); out_error_unmap: iounmap((void *)crc->regs); out_error_free_mem: @@ -720,10 +720,10 @@ static int __devexit bfin_crypto_crc_remove(struct platform_device *pdev) crypto_unregister_ahash(&algs); tasklet_kill(&crc->done_task); - iounmap((void *)crc->regs); free_dma(crc->dma_ch); if (crc->irq > 0) - free_irq(crc->irq, crc->dev); + free_irq(crc->irq, crc); + iounmap((void *)crc->regs); kfree(crc); return 0; From 05f369a89a8ee44e79553462a1322c083dfbb760 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:38 -0700 Subject: [PATCH 0985/4607] crypto: omap-aes - Remmove unnecessary pr_info noise Remove the unnecessary pr_info() calls from omap_aes_probe() and omap_aes_mod_init(). CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index e66e8ee5a9af..481da719b16e 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -880,8 +880,6 @@ static int omap_aes_probe(struct platform_device *pdev) goto err_algs; } - pr_info("probe() done\n"); - return 0; err_algs: for (j = 0; j < i; j++) @@ -938,8 +936,6 @@ static struct platform_driver omap_aes_driver = { static int __init omap_aes_mod_init(void) { - pr_info("loading %s driver\n", "omap-aes"); - return platform_driver_register(&omap_aes_driver); } From 7219368b05bd05bd3366bfb22fc38d2dc41085e5 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:39 -0700 Subject: [PATCH 0986/4607] crypto: omap-aes - Don't reset controller for every operation The AES controller only needs to be reset once and that will be done by the hwmod infrastructure, if possible. Therefore, remove the reset code from the omap-aes driver. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 481da719b16e..33cd78305461 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -160,19 +160,6 @@ static void omap_aes_write_n(struct omap_aes_dev *dd, u32 offset, omap_aes_write(dd, offset, *value); } -static int omap_aes_wait(struct omap_aes_dev *dd, u32 offset, u32 bit) -{ - unsigned long timeout = jiffies + DEFAULT_TIMEOUT; - - while (!(omap_aes_read(dd, offset) & bit)) { - if (time_is_before_jiffies(timeout)) { - dev_err(dd->dev, "omap-aes timeout\n"); - return -ETIMEDOUT; - } - } - return 0; -} - static int omap_aes_hw_init(struct omap_aes_dev *dd) { /* @@ -183,20 +170,6 @@ static int omap_aes_hw_init(struct omap_aes_dev *dd) clk_enable(dd->iclk); if (!(dd->flags & FLAGS_INIT)) { - /* is it necessary to reset before every operation? */ - omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_SOFTRESET, - AES_REG_MASK_SOFTRESET); - /* - * prevent OCP bus error (SRESP) in case an access to the module - * is performed while the module is coming out of soft reset - */ - __asm__ __volatile__("nop"); - __asm__ __volatile__("nop"); - - if (omap_aes_wait(dd, AES_REG_SYSSTATUS, - AES_REG_SYSSTATUS_RESETDONE)) - return -ETIMEDOUT; - dd->flags |= FLAGS_INIT; dd->err = 0; } From 5946c4a5e7707d255faf430969d344ad98430b69 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:40 -0700 Subject: [PATCH 0987/4607] crypto: omap-aes - Convert to use pm_runtime API Convert the omap-aes crypto driver to use the pm_runtime API instead of the clk API. CC: Kevin Hilman CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 33cd78305461..c2298521388c 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -19,10 +19,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -96,7 +96,6 @@ struct omap_aes_dev { struct list_head list; unsigned long phys_base; void __iomem *io_base; - struct clk *iclk; struct omap_aes_ctx *ctx; struct device *dev; unsigned long flags; @@ -167,7 +166,7 @@ static int omap_aes_hw_init(struct omap_aes_dev *dd) * It may be long delays between requests. * Device might go to off mode to save power. */ - clk_enable(dd->iclk); + pm_runtime_get_sync(dd->dev); if (!(dd->flags & FLAGS_INIT)) { dd->flags |= FLAGS_INIT; @@ -518,7 +517,7 @@ static void omap_aes_finish_req(struct omap_aes_dev *dd, int err) pr_debug("err: %d\n", err); - clk_disable(dd->iclk); + pm_runtime_put_sync(dd->dev); dd->flags &= ~FLAGS_BUSY; req->base.complete(&req->base, err); @@ -813,26 +812,21 @@ static int omap_aes_probe(struct platform_device *pdev) else dd->dma_in = res->start; - /* Initializing the clock */ - dd->iclk = clk_get(dev, "ick"); - if (IS_ERR(dd->iclk)) { - dev_err(dev, "clock intialization failed.\n"); - err = PTR_ERR(dd->iclk); - goto err_res; - } - dd->io_base = ioremap(dd->phys_base, SZ_4K); if (!dd->io_base) { dev_err(dev, "can't ioremap\n"); err = -ENOMEM; - goto err_io; + goto err_res; } - clk_enable(dd->iclk); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + reg = omap_aes_read(dd, AES_REG_REV); dev_info(dev, "OMAP AES hw accel rev: %u.%u\n", (reg & AES_REG_REV_MAJOR) >> 4, reg & AES_REG_REV_MINOR); - clk_disable(dd->iclk); + + pm_runtime_put_sync(dev); tasklet_init(&dd->done_task, omap_aes_done_task, (unsigned long)dd); tasklet_init(&dd->queue_task, omap_aes_queue_task, (unsigned long)dd); @@ -862,8 +856,7 @@ err_dma: tasklet_kill(&dd->done_task); tasklet_kill(&dd->queue_task); iounmap(dd->io_base); -err_io: - clk_put(dd->iclk); + pm_runtime_disable(dev); err_res: kfree(dd); dd = NULL; @@ -891,7 +884,7 @@ static int omap_aes_remove(struct platform_device *pdev) tasklet_kill(&dd->queue_task); omap_aes_dma_cleanup(dd); iounmap(dd->io_base); - clk_put(dd->iclk); + pm_runtime_disable(dd->dev); kfree(dd); dd = NULL; From 0635fb3a3c6a6d1a70996428016dca6d3d8f0961 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:41 -0700 Subject: [PATCH 0988/4607] crypto: omap-aes - Add suspend/resume support Add suspend/resume support to the OMAP AES driver. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index c2298521388c..3262139eb9bd 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -891,12 +891,31 @@ static int omap_aes_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int omap_aes_suspend(struct device *dev) +{ + pm_runtime_put_sync(dev); + return 0; +} + +static int omap_aes_resume(struct device *dev) +{ + pm_runtime_get_sync(dev); + return 0; +} +#endif + +static const struct dev_pm_ops omap_aes_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap_aes_suspend, omap_aes_resume) +}; + static struct platform_driver omap_aes_driver = { .probe = omap_aes_probe, .remove = omap_aes_remove, .driver = { .name = "omap-aes", .owner = THIS_MODULE, + .pm = &omap_aes_pm_ops, }, }; From ebedbf79026bebe6667322c2407bf05023600929 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:42 -0700 Subject: [PATCH 0989/4607] crypto: omap-aes - Add code to use dmaengine API Add code to use the new dmaengine API alongside the existing DMA code that uses the private OMAP DMA API. The API to use is chosen by defining or undefining 'OMAP_AES_DMA_PRIVATE'. CC: Russell King CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 184 +++++++++++++++++++++++++++++++++++++- 1 file changed, 183 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 3262139eb9bd..14ec9e209ef8 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -12,6 +12,8 @@ * */ +#define OMAP_AES_DMA_PRIVATE + #define pr_fmt(fmt) "%s: " fmt, __func__ #include @@ -22,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -29,7 +33,8 @@ #include #include -#include +#define DST_MAXBURST 4 +#define DMA_MIN (DST_MAXBURST * sizeof(u32)) /* OMAP TRM gives bitfields as start:end, where start is the higher bit number. For example 7:0 */ @@ -110,19 +115,33 @@ struct omap_aes_dev { struct ablkcipher_request *req; size_t total; struct scatterlist *in_sg; +#ifndef OMAP_AES_DMA_PRIVATE + struct scatterlist in_sgl; +#endif size_t in_offset; struct scatterlist *out_sg; +#ifndef OMAP_AES_DMA_PRIVATE + struct scatterlist out_sgl; +#endif size_t out_offset; size_t buflen; void *buf_in; size_t dma_size; int dma_in; +#ifdef OMAP_AES_DMA_PRIVATE int dma_lch_in; +#else + struct dma_chan *dma_lch_in; +#endif dma_addr_t dma_addr_in; void *buf_out; int dma_out; +#ifdef OMAP_AES_DMA_PRIVATE int dma_lch_out; +#else + struct dma_chan *dma_lch_out; +#endif dma_addr_t dma_addr_out; }; @@ -187,10 +206,17 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) return err; val = 0; +#ifdef OMAP_AES_DMA_PRIVATE if (dd->dma_lch_out >= 0) val |= AES_REG_MASK_DMA_OUT_EN; if (dd->dma_lch_in >= 0) val |= AES_REG_MASK_DMA_IN_EN; +#else + if (dd->dma_lch_out != NULL) + val |= AES_REG_MASK_DMA_OUT_EN; + if (dd->dma_lch_in != NULL) + val |= AES_REG_MASK_DMA_IN_EN; +#endif mask = AES_REG_MASK_DMA_IN_EN | AES_REG_MASK_DMA_OUT_EN; @@ -218,6 +244,7 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) omap_aes_write_mask(dd, AES_REG_CTRL, val, mask); +#ifdef OMAP_AES_DMA_PRIVATE /* IN */ omap_set_dma_dest_params(dd->dma_lch_in, 0, OMAP_DMA_AMODE_CONSTANT, dd->phys_base + AES_REG_DATA, 0, 4); @@ -231,6 +258,7 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) omap_set_dma_src_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4); omap_set_dma_dest_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4); +#endif return 0; } @@ -256,6 +284,7 @@ static struct omap_aes_dev *omap_aes_find_dev(struct omap_aes_ctx *ctx) return dd; } +#ifdef OMAP_AES_DMA_PRIVATE static void omap_aes_dma_callback(int lch, u16 ch_status, void *data) { struct omap_aes_dev *dd = data; @@ -271,13 +300,30 @@ static void omap_aes_dma_callback(int lch, u16 ch_status, void *data) /* dma_lch_out - completed */ tasklet_schedule(&dd->done_task); } +#else +static void omap_aes_dma_out_callback(void *data) +{ + struct omap_aes_dev *dd = data; + + /* dma_lch_out - completed */ + tasklet_schedule(&dd->done_task); +} +#endif static int omap_aes_dma_init(struct omap_aes_dev *dd) { int err = -ENOMEM; +#ifndef OMAP_AES_DMA_PRIVATE + dma_cap_mask_t mask; +#endif +#ifdef OMAP_AES_DMA_PRIVATE dd->dma_lch_out = -1; dd->dma_lch_in = -1; +#else + dd->dma_lch_out = NULL; + dd->dma_lch_in = NULL; +#endif dd->buf_in = (void *)__get_free_pages(GFP_KERNEL, OMAP_AES_CACHE_SIZE); dd->buf_out = (void *)__get_free_pages(GFP_KERNEL, OMAP_AES_CACHE_SIZE); @@ -306,6 +352,7 @@ static int omap_aes_dma_init(struct omap_aes_dev *dd) goto err_map_out; } +#ifdef OMAP_AES_DMA_PRIVATE err = omap_request_dma(dd->dma_in, "omap-aes-rx", omap_aes_dma_callback, dd, &dd->dma_lch_in); if (err) { @@ -318,11 +365,33 @@ static int omap_aes_dma_init(struct omap_aes_dev *dd) dev_err(dd->dev, "Unable to request DMA channel\n"); goto err_dma_out; } +#else + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dd->dma_lch_in = dma_request_channel(mask, omap_dma_filter_fn, + &dd->dma_in); + if (!dd->dma_lch_in) { + dev_err(dd->dev, "Unable to request in DMA channel\n"); + goto err_dma_in; + } + + dd->dma_lch_out = dma_request_channel(mask, omap_dma_filter_fn, + &dd->dma_out); + if (!dd->dma_lch_out) { + dev_err(dd->dev, "Unable to request out DMA channel\n"); + goto err_dma_out; + } +#endif return 0; err_dma_out: +#ifdef OMAP_AES_DMA_PRIVATE omap_free_dma(dd->dma_lch_in); +#else + dma_release_channel(dd->dma_lch_in); +#endif err_dma_in: dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen, DMA_FROM_DEVICE); @@ -339,8 +408,13 @@ err_alloc: static void omap_aes_dma_cleanup(struct omap_aes_dev *dd) { +#ifdef OMAP_AES_DMA_PRIVATE omap_free_dma(dd->dma_lch_out); omap_free_dma(dd->dma_lch_in); +#else + dma_release_channel(dd->dma_lch_out); + dma_release_channel(dd->dma_lch_in); +#endif dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen, DMA_FROM_DEVICE); dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen, DMA_TO_DEVICE); @@ -398,12 +472,24 @@ static int sg_copy(struct scatterlist **sg, size_t *offset, void *buf, return off; } +#ifdef OMAP_AES_DMA_PRIVATE static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in, dma_addr_t dma_addr_out, int length) +#else +static int omap_aes_crypt_dma(struct crypto_tfm *tfm, + struct scatterlist *in_sg, struct scatterlist *out_sg) +#endif { struct omap_aes_ctx *ctx = crypto_tfm_ctx(tfm); struct omap_aes_dev *dd = ctx->dd; +#ifdef OMAP_AES_DMA_PRIVATE int len32; +#else + struct dma_async_tx_descriptor *tx_in, *tx_out; + struct dma_slave_config cfg; + dma_addr_t dma_addr_in = sg_dma_address(in_sg); + int ret, length = sg_dma_len(in_sg); +#endif pr_debug("len: %d\n", length); @@ -413,6 +499,7 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in, dma_sync_single_for_device(dd->dev, dma_addr_in, length, DMA_TO_DEVICE); +#ifdef OMAP_AES_DMA_PRIVATE len32 = DIV_ROUND_UP(length, sizeof(u32)); /* IN */ @@ -433,6 +520,60 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in, omap_start_dma(dd->dma_lch_in); omap_start_dma(dd->dma_lch_out); +#else + memset(&cfg, 0, sizeof(cfg)); + + cfg.src_addr = dd->phys_base + AES_REG_DATA; + cfg.dst_addr = dd->phys_base + AES_REG_DATA; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.src_maxburst = DST_MAXBURST; + cfg.dst_maxburst = DST_MAXBURST; + + /* IN */ + ret = dmaengine_slave_config(dd->dma_lch_in, &cfg); + if (ret) { + dev_err(dd->dev, "can't configure IN dmaengine slave: %d\n", + ret); + return ret; + } + + tx_in = dmaengine_prep_slave_sg(dd->dma_lch_in, in_sg, 1, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_in) { + dev_err(dd->dev, "IN prep_slave_sg() failed\n"); + return -EINVAL; + } + + /* No callback necessary */ + tx_in->callback_param = dd; + + /* OUT */ + ret = dmaengine_slave_config(dd->dma_lch_out, &cfg); + if (ret) { + dev_err(dd->dev, "can't configure OUT dmaengine slave: %d\n", + ret); + return ret; + } + + tx_out = dmaengine_prep_slave_sg(dd->dma_lch_out, out_sg, 1, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx_out) { + dev_err(dd->dev, "OUT prep_slave_sg() failed\n"); + return -EINVAL; + } + + tx_out->callback = omap_aes_dma_out_callback; + tx_out->callback_param = dd; + + dmaengine_submit(tx_in); + dmaengine_submit(tx_out); + + dma_async_issue_pending(dd->dma_lch_in); + dma_async_issue_pending(dd->dma_lch_out); +#endif /* start DMA or disable idle mode */ omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_START, @@ -448,6 +589,10 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) int err, fast = 0, in, out; size_t count; dma_addr_t addr_in, addr_out; +#ifndef OMAP_AES_DMA_PRIVATE + struct scatterlist *in_sg, *out_sg; + int len32; +#endif pr_debug("total: %d\n", dd->total); @@ -486,6 +631,11 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) addr_in = sg_dma_address(dd->in_sg); addr_out = sg_dma_address(dd->out_sg); +#ifndef OMAP_AES_DMA_PRIVATE + in_sg = dd->in_sg; + out_sg = dd->out_sg; +#endif + dd->flags |= FLAGS_FAST; } else { @@ -493,6 +643,29 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) count = sg_copy(&dd->in_sg, &dd->in_offset, dd->buf_in, dd->buflen, dd->total, 0); +#ifndef OMAP_AES_DMA_PRIVATE + len32 = DIV_ROUND_UP(count, DMA_MIN) * DMA_MIN; + + /* + * The data going into the AES module has been copied + * to a local buffer and the data coming out will go + * into a local buffer so set up local SG entries for + * both. + */ + sg_init_table(&dd->in_sgl, 1); + dd->in_sgl.offset = dd->in_offset; + sg_dma_len(&dd->in_sgl) = len32; + sg_dma_address(&dd->in_sgl) = dd->dma_addr_in; + + sg_init_table(&dd->out_sgl, 1); + dd->out_sgl.offset = dd->out_offset; + sg_dma_len(&dd->out_sgl) = len32; + sg_dma_address(&dd->out_sgl) = dd->dma_addr_out; + + in_sg = &dd->in_sgl; + out_sg = &dd->out_sgl; +#endif + addr_in = dd->dma_addr_in; addr_out = dd->dma_addr_out; @@ -502,7 +675,11 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) dd->total -= count; +#ifdef OMAP_AES_DMA_PRIVATE err = omap_aes_crypt_dma(tfm, addr_in, addr_out, count); +#else + err = omap_aes_crypt_dma(tfm, in_sg, out_sg); +#endif if (err) { dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE); dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_TO_DEVICE); @@ -532,8 +709,13 @@ static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd) omap_aes_write_mask(dd, AES_REG_MASK, 0, AES_REG_MASK_START); +#ifdef OMAP_AES_DMA_PRIVATE omap_stop_dma(dd->dma_lch_in); omap_stop_dma(dd->dma_lch_out); +#else + dmaengine_terminate_all(dd->dma_lch_in); + dmaengine_terminate_all(dd->dma_lch_out); +#endif if (dd->flags & FLAGS_FAST) { dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_FROM_DEVICE); From 44f04c1d6f34fee940daed71151ca35b3f1f1e64 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:43 -0700 Subject: [PATCH 0990/4607] crypto: omap-aes - Remove usage of private DMA API Remove usage of the private OMAP DMA API. The dmaengine API will be used instead. CC: Russell King CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 133 -------------------------------------- 1 file changed, 133 deletions(-) diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 14ec9e209ef8..faf522ff82d7 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -12,8 +12,6 @@ * */ -#define OMAP_AES_DMA_PRIVATE - #define pr_fmt(fmt) "%s: " fmt, __func__ #include @@ -115,33 +113,21 @@ struct omap_aes_dev { struct ablkcipher_request *req; size_t total; struct scatterlist *in_sg; -#ifndef OMAP_AES_DMA_PRIVATE struct scatterlist in_sgl; -#endif size_t in_offset; struct scatterlist *out_sg; -#ifndef OMAP_AES_DMA_PRIVATE struct scatterlist out_sgl; -#endif size_t out_offset; size_t buflen; void *buf_in; size_t dma_size; int dma_in; -#ifdef OMAP_AES_DMA_PRIVATE - int dma_lch_in; -#else struct dma_chan *dma_lch_in; -#endif dma_addr_t dma_addr_in; void *buf_out; int dma_out; -#ifdef OMAP_AES_DMA_PRIVATE - int dma_lch_out; -#else struct dma_chan *dma_lch_out; -#endif dma_addr_t dma_addr_out; }; @@ -206,17 +192,10 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) return err; val = 0; -#ifdef OMAP_AES_DMA_PRIVATE - if (dd->dma_lch_out >= 0) - val |= AES_REG_MASK_DMA_OUT_EN; - if (dd->dma_lch_in >= 0) - val |= AES_REG_MASK_DMA_IN_EN; -#else if (dd->dma_lch_out != NULL) val |= AES_REG_MASK_DMA_OUT_EN; if (dd->dma_lch_in != NULL) val |= AES_REG_MASK_DMA_IN_EN; -#endif mask = AES_REG_MASK_DMA_IN_EN | AES_REG_MASK_DMA_OUT_EN; @@ -244,22 +223,6 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) omap_aes_write_mask(dd, AES_REG_CTRL, val, mask); -#ifdef OMAP_AES_DMA_PRIVATE - /* IN */ - omap_set_dma_dest_params(dd->dma_lch_in, 0, OMAP_DMA_AMODE_CONSTANT, - dd->phys_base + AES_REG_DATA, 0, 4); - - omap_set_dma_dest_burst_mode(dd->dma_lch_in, OMAP_DMA_DATA_BURST_4); - omap_set_dma_src_burst_mode(dd->dma_lch_in, OMAP_DMA_DATA_BURST_4); - - /* OUT */ - omap_set_dma_src_params(dd->dma_lch_out, 0, OMAP_DMA_AMODE_CONSTANT, - dd->phys_base + AES_REG_DATA, 0, 4); - - omap_set_dma_src_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4); - omap_set_dma_dest_burst_mode(dd->dma_lch_out, OMAP_DMA_DATA_BURST_4); -#endif - return 0; } @@ -284,23 +247,6 @@ static struct omap_aes_dev *omap_aes_find_dev(struct omap_aes_ctx *ctx) return dd; } -#ifdef OMAP_AES_DMA_PRIVATE -static void omap_aes_dma_callback(int lch, u16 ch_status, void *data) -{ - struct omap_aes_dev *dd = data; - - if (ch_status != OMAP_DMA_BLOCK_IRQ) { - pr_err("omap-aes DMA error status: 0x%hx\n", ch_status); - dd->err = -EIO; - dd->flags &= ~FLAGS_INIT; /* request to re-initialize */ - } else if (lch == dd->dma_lch_in) { - return; - } - - /* dma_lch_out - completed */ - tasklet_schedule(&dd->done_task); -} -#else static void omap_aes_dma_out_callback(void *data) { struct omap_aes_dev *dd = data; @@ -308,22 +254,14 @@ static void omap_aes_dma_out_callback(void *data) /* dma_lch_out - completed */ tasklet_schedule(&dd->done_task); } -#endif static int omap_aes_dma_init(struct omap_aes_dev *dd) { int err = -ENOMEM; -#ifndef OMAP_AES_DMA_PRIVATE dma_cap_mask_t mask; -#endif -#ifdef OMAP_AES_DMA_PRIVATE - dd->dma_lch_out = -1; - dd->dma_lch_in = -1; -#else dd->dma_lch_out = NULL; dd->dma_lch_in = NULL; -#endif dd->buf_in = (void *)__get_free_pages(GFP_KERNEL, OMAP_AES_CACHE_SIZE); dd->buf_out = (void *)__get_free_pages(GFP_KERNEL, OMAP_AES_CACHE_SIZE); @@ -352,20 +290,6 @@ static int omap_aes_dma_init(struct omap_aes_dev *dd) goto err_map_out; } -#ifdef OMAP_AES_DMA_PRIVATE - err = omap_request_dma(dd->dma_in, "omap-aes-rx", - omap_aes_dma_callback, dd, &dd->dma_lch_in); - if (err) { - dev_err(dd->dev, "Unable to request DMA channel\n"); - goto err_dma_in; - } - err = omap_request_dma(dd->dma_out, "omap-aes-tx", - omap_aes_dma_callback, dd, &dd->dma_lch_out); - if (err) { - dev_err(dd->dev, "Unable to request DMA channel\n"); - goto err_dma_out; - } -#else dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -382,16 +306,11 @@ static int omap_aes_dma_init(struct omap_aes_dev *dd) dev_err(dd->dev, "Unable to request out DMA channel\n"); goto err_dma_out; } -#endif return 0; err_dma_out: -#ifdef OMAP_AES_DMA_PRIVATE - omap_free_dma(dd->dma_lch_in); -#else dma_release_channel(dd->dma_lch_in); -#endif err_dma_in: dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen, DMA_FROM_DEVICE); @@ -408,13 +327,8 @@ err_alloc: static void omap_aes_dma_cleanup(struct omap_aes_dev *dd) { -#ifdef OMAP_AES_DMA_PRIVATE - omap_free_dma(dd->dma_lch_out); - omap_free_dma(dd->dma_lch_in); -#else dma_release_channel(dd->dma_lch_out); dma_release_channel(dd->dma_lch_in); -#endif dma_unmap_single(dd->dev, dd->dma_addr_out, dd->buflen, DMA_FROM_DEVICE); dma_unmap_single(dd->dev, dd->dma_addr_in, dd->buflen, DMA_TO_DEVICE); @@ -472,24 +386,15 @@ static int sg_copy(struct scatterlist **sg, size_t *offset, void *buf, return off; } -#ifdef OMAP_AES_DMA_PRIVATE -static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_addr_t dma_addr_in, - dma_addr_t dma_addr_out, int length) -#else static int omap_aes_crypt_dma(struct crypto_tfm *tfm, struct scatterlist *in_sg, struct scatterlist *out_sg) -#endif { struct omap_aes_ctx *ctx = crypto_tfm_ctx(tfm); struct omap_aes_dev *dd = ctx->dd; -#ifdef OMAP_AES_DMA_PRIVATE - int len32; -#else struct dma_async_tx_descriptor *tx_in, *tx_out; struct dma_slave_config cfg; dma_addr_t dma_addr_in = sg_dma_address(in_sg); int ret, length = sg_dma_len(in_sg); -#endif pr_debug("len: %d\n", length); @@ -499,28 +404,6 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_sync_single_for_device(dd->dev, dma_addr_in, length, DMA_TO_DEVICE); -#ifdef OMAP_AES_DMA_PRIVATE - len32 = DIV_ROUND_UP(length, sizeof(u32)); - - /* IN */ - omap_set_dma_transfer_params(dd->dma_lch_in, OMAP_DMA_DATA_TYPE_S32, - len32, 1, OMAP_DMA_SYNC_PACKET, dd->dma_in, - OMAP_DMA_DST_SYNC); - - omap_set_dma_src_params(dd->dma_lch_in, 0, OMAP_DMA_AMODE_POST_INC, - dma_addr_in, 0, 0); - - /* OUT */ - omap_set_dma_transfer_params(dd->dma_lch_out, OMAP_DMA_DATA_TYPE_S32, - len32, 1, OMAP_DMA_SYNC_PACKET, - dd->dma_out, OMAP_DMA_SRC_SYNC); - - omap_set_dma_dest_params(dd->dma_lch_out, 0, OMAP_DMA_AMODE_POST_INC, - dma_addr_out, 0, 0); - - omap_start_dma(dd->dma_lch_in); - omap_start_dma(dd->dma_lch_out); -#else memset(&cfg, 0, sizeof(cfg)); cfg.src_addr = dd->phys_base + AES_REG_DATA; @@ -573,7 +456,6 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_async_issue_pending(dd->dma_lch_in); dma_async_issue_pending(dd->dma_lch_out); -#endif /* start DMA or disable idle mode */ omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_START, @@ -589,10 +471,8 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) int err, fast = 0, in, out; size_t count; dma_addr_t addr_in, addr_out; -#ifndef OMAP_AES_DMA_PRIVATE struct scatterlist *in_sg, *out_sg; int len32; -#endif pr_debug("total: %d\n", dd->total); @@ -631,10 +511,8 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) addr_in = sg_dma_address(dd->in_sg); addr_out = sg_dma_address(dd->out_sg); -#ifndef OMAP_AES_DMA_PRIVATE in_sg = dd->in_sg; out_sg = dd->out_sg; -#endif dd->flags |= FLAGS_FAST; @@ -643,7 +521,6 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) count = sg_copy(&dd->in_sg, &dd->in_offset, dd->buf_in, dd->buflen, dd->total, 0); -#ifndef OMAP_AES_DMA_PRIVATE len32 = DIV_ROUND_UP(count, DMA_MIN) * DMA_MIN; /* @@ -664,7 +541,6 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) in_sg = &dd->in_sgl; out_sg = &dd->out_sgl; -#endif addr_in = dd->dma_addr_in; addr_out = dd->dma_addr_out; @@ -675,11 +551,7 @@ static int omap_aes_crypt_dma_start(struct omap_aes_dev *dd) dd->total -= count; -#ifdef OMAP_AES_DMA_PRIVATE - err = omap_aes_crypt_dma(tfm, addr_in, addr_out, count); -#else err = omap_aes_crypt_dma(tfm, in_sg, out_sg); -#endif if (err) { dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE); dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_TO_DEVICE); @@ -709,13 +581,8 @@ static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd) omap_aes_write_mask(dd, AES_REG_MASK, 0, AES_REG_MASK_START); -#ifdef OMAP_AES_DMA_PRIVATE - omap_stop_dma(dd->dma_lch_in); - omap_stop_dma(dd->dma_lch_out); -#else dmaengine_terminate_all(dd->dma_lch_in); dmaengine_terminate_all(dd->dma_lch_out); -#endif if (dd->flags & FLAGS_FAST) { dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_FROM_DEVICE); From bc69d124d8141dff942c8c7fbaae76f9c0f4c796 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:44 -0700 Subject: [PATCH 0991/4607] crypto: omap-aes - Add Device Tree Support Add Device Tree suport to the omap-aes crypto driver. Currently, only support for OMAP2 and OMAP3 is being added but support for OMAP4 will be added in a subsequent patch. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 121 ++++++++++++++++++++++++++++++-------- 1 file changed, 96 insertions(+), 25 deletions(-) diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index faf522ff82d7..dfebd4025654 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -819,11 +822,97 @@ static struct crypto_alg algs[] = { } }; +#ifdef CONFIG_OF +static const struct of_device_id omap_aes_of_match[] = { + { + .compatible = "ti,omap2-aes", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_aes_of_match); + +static int omap_aes_get_res_of(struct omap_aes_dev *dd, + struct device *dev, struct resource *res) +{ + struct device_node *node = dev->of_node; + const struct of_device_id *match; + int err = 0; + + match = of_match_device(of_match_ptr(omap_aes_of_match), dev); + if (!match) { + dev_err(dev, "no compatible OF match\n"); + err = -EINVAL; + goto err; + } + + err = of_address_to_resource(node, 0, res); + if (err < 0) { + dev_err(dev, "can't translate OF node address\n"); + err = -EINVAL; + goto err; + } + + dd->dma_out = -1; /* Dummy value that's unused */ + dd->dma_in = -1; /* Dummy value that's unused */ + +err: + return err; +} +#else +static const struct of_device_id omap_aes_of_match[] = { + {}, +}; + +static int omap_aes_get_res_of(struct omap_aes_dev *dd, + struct device *dev, struct resource *res) +{ + return -EINVAL; +} +#endif + +static int omap_aes_get_res_pdev(struct omap_aes_dev *dd, + struct platform_device *pdev, struct resource *res) +{ + struct device *dev = &pdev->dev; + struct resource *r; + int err = 0; + + /* Get the base address */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(dev, "no MEM resource info\n"); + err = -ENODEV; + goto err; + } + memcpy(res, r, sizeof(*res)); + + /* Get the DMA out channel */ + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!r) { + dev_err(dev, "no DMA out resource info\n"); + err = -ENODEV; + goto err; + } + dd->dma_out = r->start; + + /* Get the DMA in channel */ + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!r) { + dev_err(dev, "no DMA in resource info\n"); + err = -ENODEV; + goto err; + } + dd->dma_in = r->start; + +err: + return err; +} + static int omap_aes_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct omap_aes_dev *dd; - struct resource *res; + struct resource res; int err = -ENOMEM, i, j; u32 reg; @@ -838,35 +927,18 @@ static int omap_aes_probe(struct platform_device *pdev) spin_lock_init(&dd->lock); crypto_init_queue(&dd->queue, OMAP_AES_QUEUE_LENGTH); - /* Get the base address */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "invalid resource type\n"); - err = -ENODEV; + err = (dev->of_node) ? omap_aes_get_res_of(dd, dev, &res) : + omap_aes_get_res_pdev(dd, pdev, &res); + if (err) goto err_res; - } - dd->phys_base = res->start; - /* Get the DMA */ - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) - dev_info(dev, "no DMA info\n"); - else - dd->dma_out = res->start; - - /* Get the DMA */ - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) - dev_info(dev, "no DMA info\n"); - else - dd->dma_in = res->start; - - dd->io_base = ioremap(dd->phys_base, SZ_4K); + dd->io_base = devm_request_and_ioremap(dev, &res); if (!dd->io_base) { dev_err(dev, "can't ioremap\n"); err = -ENOMEM; goto err_res; } + dd->phys_base = res.start; pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -904,7 +976,6 @@ err_algs: err_dma: tasklet_kill(&dd->done_task); tasklet_kill(&dd->queue_task); - iounmap(dd->io_base); pm_runtime_disable(dev); err_res: kfree(dd); @@ -932,7 +1003,6 @@ static int omap_aes_remove(struct platform_device *pdev) tasklet_kill(&dd->done_task); tasklet_kill(&dd->queue_task); omap_aes_dma_cleanup(dd); - iounmap(dd->io_base); pm_runtime_disable(dd->dev); kfree(dd); dd = NULL; @@ -965,6 +1035,7 @@ static struct platform_driver omap_aes_driver = { .name = "omap-aes", .owner = THIS_MODULE, .pm = &omap_aes_pm_ops, + .of_match_table = omap_aes_of_match, }, }; From b4b87a934c30fb91cbdd18ae028acdc361e1cf0f Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:45 -0700 Subject: [PATCH 0992/4607] crypto: omap-aes - Convert to dma_request_slave_channel_compat() Use the dma_request_slave_channel_compat() call instead of the dma_request_channel() call to request a DMA channel. This allows the omap-aes driver use different DMA engines. CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index dfebd4025654..d34aa5df3dc1 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -296,15 +296,19 @@ static int omap_aes_dma_init(struct omap_aes_dev *dd) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - dd->dma_lch_in = dma_request_channel(mask, omap_dma_filter_fn, - &dd->dma_in); + dd->dma_lch_in = dma_request_slave_channel_compat(mask, + omap_dma_filter_fn, + &dd->dma_in, + dd->dev, "rx"); if (!dd->dma_lch_in) { dev_err(dd->dev, "Unable to request in DMA channel\n"); goto err_dma_in; } - dd->dma_lch_out = dma_request_channel(mask, omap_dma_filter_fn, - &dd->dma_out); + dd->dma_lch_out = dma_request_slave_channel_compat(mask, + omap_dma_filter_fn, + &dd->dma_out, + dd->dev, "tx"); if (!dd->dma_lch_out) { dev_err(dd->dev, "Unable to request out DMA channel\n"); goto err_dma_out; From 0d35583a13ad29af06375678daa2e11772ec9267 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:46 -0700 Subject: [PATCH 0993/4607] crypto: omap-aes - Add OMAP4/AM33XX AES Support Add support for the OMAP4 version of the AES module that is present on OMAP4 and AM33xx SoCs. The modules have several differences including register offsets and how DMA is triggered. To handle these differences, a platform_data structure is defined and contains routine pointers, register offsets, and bit offsets within registers. OMAP2/OMAP3-specific routines are suffixed with '_omap2' and OMAP4/AM33xx routines are suffixed with '_omap4'. Note: The code being integrated is from the TI AM33xx SDK and was written by Greg Turner and Herman Schuurman (current email unknown) while at TI. CC: Greg Turner CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 158 ++++++++++++++++++++++++++++++-------- 1 file changed, 125 insertions(+), 33 deletions(-) diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index d34aa5df3dc1..bd1ad97404ab 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -5,6 +5,7 @@ * * Copyright (c) 2010 Nokia Corporation * Author: Dmitry Kasatkin + * Copyright (c) 2011 Texas Instruments Incorporated * * 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 @@ -42,10 +43,11 @@ #define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) #define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) -#define AES_REG_KEY(x) (0x1C - ((x ^ 0x01) * 0x04)) -#define AES_REG_IV(x) (0x20 + ((x) * 0x04)) +#define AES_REG_KEY(dd, x) ((dd)->pdata->key_ofs - \ + ((x ^ 0x01) * 0x04)) +#define AES_REG_IV(dd, x) ((dd)->pdata->iv_ofs + ((x) * 0x04)) -#define AES_REG_CTRL 0x30 +#define AES_REG_CTRL(dd) ((dd)->pdata->ctrl_ofs) #define AES_REG_CTRL_CTR_WIDTH (1 << 7) #define AES_REG_CTRL_CTR (1 << 6) #define AES_REG_CTRL_CBC (1 << 5) @@ -54,14 +56,11 @@ #define AES_REG_CTRL_INPUT_READY (1 << 1) #define AES_REG_CTRL_OUTPUT_READY (1 << 0) -#define AES_REG_DATA 0x34 -#define AES_REG_DATA_N(x) (0x34 + ((x) * 0x04)) +#define AES_REG_DATA_N(dd, x) ((dd)->pdata->data_ofs + ((x) * 0x04)) -#define AES_REG_REV 0x44 -#define AES_REG_REV_MAJOR 0xF0 -#define AES_REG_REV_MINOR 0x0F +#define AES_REG_REV(dd) ((dd)->pdata->rev_ofs) -#define AES_REG_MASK 0x48 +#define AES_REG_MASK(dd) ((dd)->pdata->mask_ofs) #define AES_REG_MASK_SIDLE (1 << 6) #define AES_REG_MASK_START (1 << 5) #define AES_REG_MASK_DMA_OUT_EN (1 << 3) @@ -69,8 +68,7 @@ #define AES_REG_MASK_SOFTRESET (1 << 1) #define AES_REG_AUTOIDLE (1 << 0) -#define AES_REG_SYSSTATUS 0x4C -#define AES_REG_SYSSTATUS_RESETDONE (1 << 0) +#define AES_REG_LENGTH_N(x) (0x54 + ((x) * 0x04)) #define DEFAULT_TIMEOUT (5*HZ) @@ -98,6 +96,26 @@ struct omap_aes_reqctx { #define OMAP_AES_QUEUE_LENGTH 1 #define OMAP_AES_CACHE_SIZE 0 +struct omap_aes_pdata { + void (*trigger)(struct omap_aes_dev *dd, int length); + + u32 key_ofs; + u32 iv_ofs; + u32 ctrl_ofs; + u32 data_ofs; + u32 rev_ofs; + u32 mask_ofs; + + u32 dma_enable_in; + u32 dma_enable_out; + u32 dma_start; + + u32 major_mask; + u32 major_shift; + u32 minor_mask; + u32 minor_shift; +}; + struct omap_aes_dev { struct list_head list; unsigned long phys_base; @@ -132,6 +150,8 @@ struct omap_aes_dev { int dma_out; struct dma_chan *dma_lch_out; dma_addr_t dma_addr_out; + + const struct omap_aes_pdata *pdata; }; /* keep registered devices data here */ @@ -194,26 +214,16 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) if (err) return err; - val = 0; - if (dd->dma_lch_out != NULL) - val |= AES_REG_MASK_DMA_OUT_EN; - if (dd->dma_lch_in != NULL) - val |= AES_REG_MASK_DMA_IN_EN; - - mask = AES_REG_MASK_DMA_IN_EN | AES_REG_MASK_DMA_OUT_EN; - - omap_aes_write_mask(dd, AES_REG_MASK, val, mask); - key32 = dd->ctx->keylen / sizeof(u32); /* it seems a key should always be set even if it has not changed */ for (i = 0; i < key32; i++) { - omap_aes_write(dd, AES_REG_KEY(i), + omap_aes_write(dd, AES_REG_KEY(dd, i), __le32_to_cpu(dd->ctx->key[i])); } if ((dd->flags & FLAGS_CBC) && dd->req->info) - omap_aes_write_n(dd, AES_REG_IV(0), dd->req->info, 4); + omap_aes_write_n(dd, AES_REG_IV(dd, 0), dd->req->info, 4); val = FLD_VAL(((dd->ctx->keylen >> 3) - 1), 4, 3); if (dd->flags & FLAGS_CBC) @@ -224,11 +234,47 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) mask = AES_REG_CTRL_CBC | AES_REG_CTRL_DIRECTION | AES_REG_CTRL_KEY_SIZE; - omap_aes_write_mask(dd, AES_REG_CTRL, val, mask); + omap_aes_write_mask(dd, AES_REG_CTRL(dd), val, mask); return 0; } +static void omap_aes_dma_trigger_omap2(struct omap_aes_dev *dd, int length) +{ + u32 mask, val; + + val = dd->pdata->dma_start; + + if (dd->dma_lch_out != NULL) + val |= dd->pdata->dma_enable_out; + if (dd->dma_lch_in != NULL) + val |= dd->pdata->dma_enable_in; + + mask = dd->pdata->dma_enable_out | dd->pdata->dma_enable_in | + dd->pdata->dma_start; + + omap_aes_write_mask(dd, AES_REG_MASK(dd), val, mask); + +} + +static void omap_aes_dma_trigger_omap4(struct omap_aes_dev *dd, int length) +{ + omap_aes_write(dd, AES_REG_LENGTH_N(0), length); + omap_aes_write(dd, AES_REG_LENGTH_N(1), 0); + + omap_aes_dma_trigger_omap2(dd, length); +} + +static void omap_aes_dma_stop(struct omap_aes_dev *dd) +{ + u32 mask; + + mask = dd->pdata->dma_enable_out | dd->pdata->dma_enable_in | + dd->pdata->dma_start; + + omap_aes_write_mask(dd, AES_REG_MASK(dd), 0, mask); +} + static struct omap_aes_dev *omap_aes_find_dev(struct omap_aes_ctx *ctx) { struct omap_aes_dev *dd = NULL, *tmp; @@ -413,8 +459,8 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, memset(&cfg, 0, sizeof(cfg)); - cfg.src_addr = dd->phys_base + AES_REG_DATA; - cfg.dst_addr = dd->phys_base + AES_REG_DATA; + cfg.src_addr = dd->phys_base + AES_REG_DATA_N(dd, 0); + cfg.dst_addr = dd->phys_base + AES_REG_DATA_N(dd, 0); cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.src_maxburst = DST_MAXBURST; @@ -464,9 +510,8 @@ static int omap_aes_crypt_dma(struct crypto_tfm *tfm, dma_async_issue_pending(dd->dma_lch_in); dma_async_issue_pending(dd->dma_lch_out); - /* start DMA or disable idle mode */ - omap_aes_write_mask(dd, AES_REG_MASK, AES_REG_MASK_START, - AES_REG_MASK_START); + /* start DMA */ + dd->pdata->trigger(dd, length); return 0; } @@ -586,7 +631,7 @@ static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd) pr_debug("total: %d\n", dd->total); - omap_aes_write_mask(dd, AES_REG_MASK, 0, AES_REG_MASK_START); + omap_aes_dma_stop(dd); dmaengine_terminate_all(dd->dma_lch_in); dmaengine_terminate_all(dd->dma_lch_out); @@ -826,10 +871,48 @@ static struct crypto_alg algs[] = { } }; +static const struct omap_aes_pdata omap_aes_pdata_omap2 = { + .trigger = omap_aes_dma_trigger_omap2, + .key_ofs = 0x1c, + .iv_ofs = 0x20, + .ctrl_ofs = 0x30, + .data_ofs = 0x34, + .rev_ofs = 0x44, + .mask_ofs = 0x48, + .dma_enable_in = BIT(2), + .dma_enable_out = BIT(3), + .dma_start = BIT(5), + .major_mask = 0xf0, + .major_shift = 4, + .minor_mask = 0x0f, + .minor_shift = 0, +}; + #ifdef CONFIG_OF +static const struct omap_aes_pdata omap_aes_pdata_omap4 = { + .trigger = omap_aes_dma_trigger_omap4, + .key_ofs = 0x3c, + .iv_ofs = 0x40, + .ctrl_ofs = 0x50, + .data_ofs = 0x60, + .rev_ofs = 0x80, + .mask_ofs = 0x84, + .dma_enable_in = BIT(5), + .dma_enable_out = BIT(6), + .major_mask = 0x0700, + .major_shift = 8, + .minor_mask = 0x003f, + .minor_shift = 0, +}; + static const struct of_device_id omap_aes_of_match[] = { { .compatible = "ti,omap2-aes", + .data = &omap_aes_pdata_omap2, + }, + { + .compatible = "ti,omap4-aes", + .data = &omap_aes_pdata_omap4, }, {}, }; @@ -859,6 +942,8 @@ static int omap_aes_get_res_of(struct omap_aes_dev *dd, dd->dma_out = -1; /* Dummy value that's unused */ dd->dma_in = -1; /* Dummy value that's unused */ + dd->pdata = match->data; + err: return err; } @@ -908,6 +993,9 @@ static int omap_aes_get_res_pdev(struct omap_aes_dev *dd, } dd->dma_in = r->start; + /* Only OMAP2/3 can be non-DT */ + dd->pdata = &omap_aes_pdata_omap2; + err: return err; } @@ -947,12 +1035,16 @@ static int omap_aes_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_sync(dev); - reg = omap_aes_read(dd, AES_REG_REV); - dev_info(dev, "OMAP AES hw accel rev: %u.%u\n", - (reg & AES_REG_REV_MAJOR) >> 4, reg & AES_REG_REV_MINOR); + omap_aes_dma_stop(dd); + + reg = omap_aes_read(dd, AES_REG_REV(dd)); pm_runtime_put_sync(dev); + dev_info(dev, "OMAP AES hw accel rev: %u.%u\n", + (reg & dd->pdata->major_mask) >> dd->pdata->major_shift, + (reg & dd->pdata->minor_mask) >> dd->pdata->minor_shift); + tasklet_init(&dd->done_task, omap_aes_done_task, (unsigned long)dd); tasklet_init(&dd->queue_task, omap_aes_queue_task, (unsigned long)dd); From f9fb69e73c774a6490a13128381af9ba468d3a6e Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 8 Jan 2013 11:57:47 -0700 Subject: [PATCH 0994/4607] crypto: omap-aes - Add CTR algorithm Support The OMAP3 and OMAP4/AM33xx versions of the AES crypto module support the CTR algorithm in addition to ECB and CBC that the OMAP2 version of the module supports. So, OMAP2 and OMAP3 share a common register set but OMAP3 supports CTR while OMAP2 doesn't. OMAP4/AM33XX uses a different register set from OMAP2/OMAP3 and also supports CTR. To add this support, use the platform_data introduced in an ealier commit to hold the list of algorithms supported by the current module. The probe routine will use that list to register the correct algorithms. Note: The code being integrated is from the TI AM33xx SDK and was written by Greg Turner and Herman Schuurman (current email unknown) while at TI. CC: Greg Turner CC: Dmitry Kasatkin Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-aes.c | 143 ++++++++++++++++++++++++++++++++++---- 1 file changed, 128 insertions(+), 15 deletions(-) diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index bd1ad97404ab..6aa425fe0ed5 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -48,7 +48,11 @@ #define AES_REG_IV(dd, x) ((dd)->pdata->iv_ofs + ((x) * 0x04)) #define AES_REG_CTRL(dd) ((dd)->pdata->ctrl_ofs) -#define AES_REG_CTRL_CTR_WIDTH (1 << 7) +#define AES_REG_CTRL_CTR_WIDTH_MASK (3 << 7) +#define AES_REG_CTRL_CTR_WIDTH_32 (0 << 7) +#define AES_REG_CTRL_CTR_WIDTH_64 (1 << 7) +#define AES_REG_CTRL_CTR_WIDTH_96 (2 << 7) +#define AES_REG_CTRL_CTR_WIDTH_128 (3 << 7) #define AES_REG_CTRL_CTR (1 << 6) #define AES_REG_CTRL_CBC (1 << 5) #define AES_REG_CTRL_KEY_SIZE (3 << 3) @@ -76,6 +80,7 @@ #define FLAGS_ENCRYPT BIT(0) #define FLAGS_CBC BIT(1) #define FLAGS_GIV BIT(2) +#define FLAGS_CTR BIT(3) #define FLAGS_INIT BIT(4) #define FLAGS_FAST BIT(5) @@ -96,7 +101,16 @@ struct omap_aes_reqctx { #define OMAP_AES_QUEUE_LENGTH 1 #define OMAP_AES_CACHE_SIZE 0 +struct omap_aes_algs_info { + struct crypto_alg *algs_list; + unsigned int size; + unsigned int registered; +}; + struct omap_aes_pdata { + struct omap_aes_algs_info *algs_info; + unsigned int algs_info_size; + void (*trigger)(struct omap_aes_dev *dd, int length); u32 key_ofs; @@ -208,7 +222,7 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) { unsigned int key32; int i, err; - u32 val, mask; + u32 val, mask = 0; err = omap_aes_hw_init(dd); if (err) @@ -222,16 +236,20 @@ static int omap_aes_write_ctrl(struct omap_aes_dev *dd) __le32_to_cpu(dd->ctx->key[i])); } - if ((dd->flags & FLAGS_CBC) && dd->req->info) + if ((dd->flags & (FLAGS_CBC | FLAGS_CTR)) && dd->req->info) omap_aes_write_n(dd, AES_REG_IV(dd, 0), dd->req->info, 4); val = FLD_VAL(((dd->ctx->keylen >> 3) - 1), 4, 3); if (dd->flags & FLAGS_CBC) val |= AES_REG_CTRL_CBC; + if (dd->flags & FLAGS_CTR) { + val |= AES_REG_CTRL_CTR | AES_REG_CTRL_CTR_WIDTH_32; + mask = AES_REG_CTRL_CTR | AES_REG_CTRL_CTR_WIDTH_MASK; + } if (dd->flags & FLAGS_ENCRYPT) val |= AES_REG_CTRL_DIRECTION; - mask = AES_REG_CTRL_CBC | AES_REG_CTRL_DIRECTION | + mask |= AES_REG_CTRL_CBC | AES_REG_CTRL_DIRECTION | AES_REG_CTRL_KEY_SIZE; omap_aes_write_mask(dd, AES_REG_CTRL(dd), val, mask); @@ -807,6 +825,16 @@ static int omap_aes_cbc_decrypt(struct ablkcipher_request *req) return omap_aes_crypt(req, FLAGS_CBC); } +static int omap_aes_ctr_encrypt(struct ablkcipher_request *req) +{ + return omap_aes_crypt(req, FLAGS_ENCRYPT | FLAGS_CTR); +} + +static int omap_aes_ctr_decrypt(struct ablkcipher_request *req) +{ + return omap_aes_crypt(req, FLAGS_CTR); +} + static int omap_aes_cra_init(struct crypto_tfm *tfm) { pr_debug("enter\n"); @@ -823,7 +851,7 @@ static void omap_aes_cra_exit(struct crypto_tfm *tfm) /* ********************** ALGS ************************************ */ -static struct crypto_alg algs[] = { +static struct crypto_alg algs_ecb_cbc[] = { { .cra_name = "ecb(aes)", .cra_driver_name = "ecb-aes-omap", @@ -871,7 +899,43 @@ static struct crypto_alg algs[] = { } }; +static struct crypto_alg algs_ctr[] = { +{ + .cra_name = "ctr(aes)", + .cra_driver_name = "ctr-aes-omap", + .cra_priority = 100, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | + CRYPTO_ALG_KERN_DRIVER_ONLY | + CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct omap_aes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = omap_aes_cra_init, + .cra_exit = omap_aes_cra_exit, + .cra_u.ablkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .geniv = "eseqiv", + .ivsize = AES_BLOCK_SIZE, + .setkey = omap_aes_setkey, + .encrypt = omap_aes_ctr_encrypt, + .decrypt = omap_aes_ctr_decrypt, + } +} , +}; + +static struct omap_aes_algs_info omap_aes_algs_info_ecb_cbc[] = { + { + .algs_list = algs_ecb_cbc, + .size = ARRAY_SIZE(algs_ecb_cbc), + }, +}; + static const struct omap_aes_pdata omap_aes_pdata_omap2 = { + .algs_info = omap_aes_algs_info_ecb_cbc, + .algs_info_size = ARRAY_SIZE(omap_aes_algs_info_ecb_cbc), .trigger = omap_aes_dma_trigger_omap2, .key_ofs = 0x1c, .iv_ofs = 0x20, @@ -889,7 +953,39 @@ static const struct omap_aes_pdata omap_aes_pdata_omap2 = { }; #ifdef CONFIG_OF +static struct omap_aes_algs_info omap_aes_algs_info_ecb_cbc_ctr[] = { + { + .algs_list = algs_ecb_cbc, + .size = ARRAY_SIZE(algs_ecb_cbc), + }, + { + .algs_list = algs_ctr, + .size = ARRAY_SIZE(algs_ctr), + }, +}; + +static const struct omap_aes_pdata omap_aes_pdata_omap3 = { + .algs_info = omap_aes_algs_info_ecb_cbc_ctr, + .algs_info_size = ARRAY_SIZE(omap_aes_algs_info_ecb_cbc_ctr), + .trigger = omap_aes_dma_trigger_omap2, + .key_ofs = 0x1c, + .iv_ofs = 0x20, + .ctrl_ofs = 0x30, + .data_ofs = 0x34, + .rev_ofs = 0x44, + .mask_ofs = 0x48, + .dma_enable_in = BIT(2), + .dma_enable_out = BIT(3), + .dma_start = BIT(5), + .major_mask = 0xf0, + .major_shift = 4, + .minor_mask = 0x0f, + .minor_shift = 0, +}; + static const struct omap_aes_pdata omap_aes_pdata_omap4 = { + .algs_info = omap_aes_algs_info_ecb_cbc_ctr, + .algs_info_size = ARRAY_SIZE(omap_aes_algs_info_ecb_cbc_ctr), .trigger = omap_aes_dma_trigger_omap4, .key_ofs = 0x3c, .iv_ofs = 0x40, @@ -910,6 +1006,10 @@ static const struct of_device_id omap_aes_of_match[] = { .compatible = "ti,omap2-aes", .data = &omap_aes_pdata_omap2, }, + { + .compatible = "ti,omap3-aes", + .data = &omap_aes_pdata_omap3, + }, { .compatible = "ti,omap4-aes", .data = &omap_aes_pdata_omap4, @@ -1004,6 +1104,7 @@ static int omap_aes_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct omap_aes_dev *dd; + struct crypto_alg *algp; struct resource res; int err = -ENOMEM, i, j; u32 reg; @@ -1057,17 +1158,27 @@ static int omap_aes_probe(struct platform_device *pdev) list_add_tail(&dd->list, &dev_list); spin_unlock(&list_lock); - for (i = 0; i < ARRAY_SIZE(algs); i++) { - pr_debug("i: %d\n", i); - err = crypto_register_alg(&algs[i]); - if (err) - goto err_algs; + for (i = 0; i < dd->pdata->algs_info_size; i++) { + for (j = 0; j < dd->pdata->algs_info[i].size; j++) { + algp = &dd->pdata->algs_info[i].algs_list[j]; + + pr_debug("reg alg: %s\n", algp->cra_name); + INIT_LIST_HEAD(&algp->cra_list); + + err = crypto_register_alg(algp); + if (err) + goto err_algs; + + dd->pdata->algs_info[i].registered++; + } } return 0; err_algs: - for (j = 0; j < i; j++) - crypto_unregister_alg(&algs[j]); + for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) + for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) + crypto_unregister_alg( + &dd->pdata->algs_info[i].algs_list[j]); omap_aes_dma_cleanup(dd); err_dma: tasklet_kill(&dd->done_task); @@ -1084,7 +1195,7 @@ err_data: static int omap_aes_remove(struct platform_device *pdev) { struct omap_aes_dev *dd = platform_get_drvdata(pdev); - int i; + int i, j; if (!dd) return -ENODEV; @@ -1093,8 +1204,10 @@ static int omap_aes_remove(struct platform_device *pdev) list_del(&dd->list); spin_unlock(&list_lock); - for (i = 0; i < ARRAY_SIZE(algs); i++) - crypto_unregister_alg(&algs[i]); + for (i = dd->pdata->algs_info_size - 1; i >= 0; i--) + for (j = dd->pdata->algs_info[i].registered - 1; j >= 0; j--) + crypto_unregister_alg( + &dd->pdata->algs_info[i].algs_list[j]); tasklet_kill(&dd->done_task); tasklet_kill(&dd->queue_task); From 5c22ba6619796da82ea0aa18c72caf4fe003a329 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 10 Jan 2013 11:05:30 +0900 Subject: [PATCH 0995/4607] crypto: s5p-sss - Use devm_clk_get() Use devm_clk_get() rather than clk_get() to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Herbert Xu --- drivers/crypto/s5p-sss.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c index 49ad8cbade69..4b314326f48a 100644 --- a/drivers/crypto/s5p-sss.c +++ b/drivers/crypto/s5p-sss.c @@ -580,7 +580,7 @@ static int s5p_aes_probe(struct platform_device *pdev) resource_size(res), pdev->name)) return -EBUSY; - pdata->clk = clk_get(dev, "secss"); + pdata->clk = devm_clk_get(dev, "secss"); if (IS_ERR(pdata->clk)) { dev_err(dev, "failed to find secss clock source\n"); return -ENOENT; @@ -645,7 +645,6 @@ static int s5p_aes_probe(struct platform_device *pdev) err_irq: clk_disable(pdata->clk); - clk_put(pdata->clk); s5p_dev = NULL; platform_set_drvdata(pdev, NULL); @@ -667,7 +666,6 @@ static int s5p_aes_remove(struct platform_device *pdev) tasklet_kill(&pdata->tasklet); clk_disable(pdata->clk); - clk_put(pdata->clk); s5p_dev = NULL; platform_set_drvdata(pdev, NULL); From 78c37d191dd6899d8c219fee597a17d6e3c5d288 Mon Sep 17 00:00:00 2001 From: Alexander Boyko Date: Thu, 10 Jan 2013 18:54:59 +0400 Subject: [PATCH 0996/4607] crypto: crc32 - add crc32 pclmulqdq implementation and wrappers for table implementation This patch adds crc32 algorithms to shash crypto api. One is wrapper to gerneric crc32_le function. Second is crc32 pclmulqdq implementation. It use hardware provided PCLMULQDQ instruction to accelerate the CRC32 disposal. This instruction present from Intel Westmere and AMD Bulldozer CPUs. For intel core i5 I got 450MB/s for table implementation and 2100MB/s for pclmulqdq implementation. Signed-off-by: Alexander Boyko Signed-off-by: Herbert Xu --- arch/x86/crypto/Makefile | 2 + arch/x86/crypto/crc32-pclmul_asm.S | 247 ++++++++++++++++++++++++++++ arch/x86/crypto/crc32-pclmul_glue.c | 201 ++++++++++++++++++++++ crypto/Kconfig | 21 +++ crypto/Makefile | 1 + crypto/crc32.c | 158 ++++++++++++++++++ 6 files changed, 630 insertions(+) create mode 100644 arch/x86/crypto/crc32-pclmul_asm.S create mode 100644 arch/x86/crypto/crc32-pclmul_glue.c create mode 100644 crypto/crc32.c diff --git a/arch/x86/crypto/Makefile b/arch/x86/crypto/Makefile index e0ca7c9ac383..63947a8f9f0f 100644 --- a/arch/x86/crypto/Makefile +++ b/arch/x86/crypto/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL) += ghash-clmulni-intel.o obj-$(CONFIG_CRYPTO_CRC32C_INTEL) += crc32c-intel.o obj-$(CONFIG_CRYPTO_SHA1_SSSE3) += sha1-ssse3.o +obj-$(CONFIG_CRYPTO_CRC32_PCLMUL) += crc32-pclmul.o aes-i586-y := aes-i586-asm_32.o aes_glue.o twofish-i586-y := twofish-i586-asm_32.o twofish_glue.o @@ -52,3 +53,4 @@ ghash-clmulni-intel-y := ghash-clmulni-intel_asm.o ghash-clmulni-intel_glue.o sha1-ssse3-y := sha1_ssse3_asm.o sha1_ssse3_glue.o crc32c-intel-y := crc32c-intel_glue.o crc32c-intel-$(CONFIG_CRYPTO_CRC32C_X86_64) += crc32c-pcl-intel-asm_64.o +crc32-pclmul-y := crc32-pclmul_asm.o crc32-pclmul_glue.o diff --git a/arch/x86/crypto/crc32-pclmul_asm.S b/arch/x86/crypto/crc32-pclmul_asm.S new file mode 100644 index 000000000000..65ea6a624907 --- /dev/null +++ b/arch/x86/crypto/crc32-pclmul_asm.S @@ -0,0 +1,247 @@ +/* GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * 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 version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see http://www.gnu.org/licenses + * + * Please visit http://www.xyratex.com/contact if you need additional + * information or have any questions. + * + * GPL HEADER END + */ + +/* + * Copyright 2012 Xyratex Technology Limited + * + * Using hardware provided PCLMULQDQ instruction to accelerate the CRC32 + * calculation. + * CRC32 polynomial:0x04c11db7(BE)/0xEDB88320(LE) + * PCLMULQDQ is a new instruction in Intel SSE4.2, the reference can be found + * at: + * http://www.intel.com/products/processor/manuals/ + * Intel(R) 64 and IA-32 Architectures Software Developer's Manual + * Volume 2B: Instruction Set Reference, N-Z + * + * Authors: Gregory Prestas + * Alexander Boyko + */ + +#include +#include + + +.align 16 +/* + * [x4*128+32 mod P(x) << 32)]' << 1 = 0x154442bd4 + * #define CONSTANT_R1 0x154442bd4LL + * + * [(x4*128-32 mod P(x) << 32)]' << 1 = 0x1c6e41596 + * #define CONSTANT_R2 0x1c6e41596LL + */ +.Lconstant_R2R1: + .octa 0x00000001c6e415960000000154442bd4 +/* + * [(x128+32 mod P(x) << 32)]' << 1 = 0x1751997d0 + * #define CONSTANT_R3 0x1751997d0LL + * + * [(x128-32 mod P(x) << 32)]' << 1 = 0x0ccaa009e + * #define CONSTANT_R4 0x0ccaa009eLL + */ +.Lconstant_R4R3: + .octa 0x00000000ccaa009e00000001751997d0 +/* + * [(x64 mod P(x) << 32)]' << 1 = 0x163cd6124 + * #define CONSTANT_R5 0x163cd6124LL + */ +.Lconstant_R5: + .octa 0x00000000000000000000000163cd6124 +.Lconstant_mask32: + .octa 0x000000000000000000000000FFFFFFFF +/* + * #define CRCPOLY_TRUE_LE_FULL 0x1DB710641LL + * + * Barrett Reduction constant (u64`) = u` = (x**64 / P(x))` = 0x1F7011641LL + * #define CONSTANT_RU 0x1F7011641LL + */ +.Lconstant_RUpoly: + .octa 0x00000001F701164100000001DB710641 + +#define CONSTANT %xmm0 + +#ifdef __x86_64__ +#define BUF %rdi +#define LEN %rsi +#define CRC %edx +#else +#warning Using 32bit code support +#define BUF %eax +#define LEN %edx +#define CRC %ecx +#endif + + + +.text +/** + * Calculate crc32 + * BUF - buffer (16 bytes aligned) + * LEN - sizeof buffer (16 bytes aligned), LEN should be grater than 63 + * CRC - initial crc32 + * return %eax crc32 + * uint crc32_pclmul_le_16(unsigned char const *buffer, + * size_t len, uint crc32) + */ +.globl crc32_pclmul_le_16 +.align 4, 0x90 +crc32_pclmul_le_16:/* buffer and buffer size are 16 bytes aligned */ + movdqa (BUF), %xmm1 + movdqa 0x10(BUF), %xmm2 + movdqa 0x20(BUF), %xmm3 + movdqa 0x30(BUF), %xmm4 + movd CRC, CONSTANT + pxor CONSTANT, %xmm1 + sub $0x40, LEN + add $0x40, BUF +#ifndef __x86_64__ + /* This is for position independent code(-fPIC) support for 32bit */ + call delta +delta: + pop %ecx +#endif + cmp $0x40, LEN + jb less_64 + +#ifdef __x86_64__ + movdqa .Lconstant_R2R1(%rip), CONSTANT +#else + movdqa .Lconstant_R2R1 - delta(%ecx), CONSTANT +#endif + +loop_64:/* 64 bytes Full cache line folding */ + prefetchnta 0x40(BUF) + movdqa %xmm1, %xmm5 + movdqa %xmm2, %xmm6 + movdqa %xmm3, %xmm7 +#ifdef __x86_64__ + movdqa %xmm4, %xmm8 +#endif + PCLMULQDQ 00, CONSTANT, %xmm1 + PCLMULQDQ 00, CONSTANT, %xmm2 + PCLMULQDQ 00, CONSTANT, %xmm3 +#ifdef __x86_64__ + PCLMULQDQ 00, CONSTANT, %xmm4 +#endif + PCLMULQDQ 0x11, CONSTANT, %xmm5 + PCLMULQDQ 0x11, CONSTANT, %xmm6 + PCLMULQDQ 0x11, CONSTANT, %xmm7 +#ifdef __x86_64__ + PCLMULQDQ 0x11, CONSTANT, %xmm8 +#endif + pxor %xmm5, %xmm1 + pxor %xmm6, %xmm2 + pxor %xmm7, %xmm3 +#ifdef __x86_64__ + pxor %xmm8, %xmm4 +#else + /* xmm8 unsupported for x32 */ + movdqa %xmm4, %xmm5 + PCLMULQDQ 00, CONSTANT, %xmm4 + PCLMULQDQ 0x11, CONSTANT, %xmm5 + pxor %xmm5, %xmm4 +#endif + + pxor (BUF), %xmm1 + pxor 0x10(BUF), %xmm2 + pxor 0x20(BUF), %xmm3 + pxor 0x30(BUF), %xmm4 + + sub $0x40, LEN + add $0x40, BUF + cmp $0x40, LEN + jge loop_64 +less_64:/* Folding cache line into 128bit */ +#ifdef __x86_64__ + movdqa .Lconstant_R4R3(%rip), CONSTANT +#else + movdqa .Lconstant_R4R3 - delta(%ecx), CONSTANT +#endif + prefetchnta (BUF) + + movdqa %xmm1, %xmm5 + PCLMULQDQ 0x00, CONSTANT, %xmm1 + PCLMULQDQ 0x11, CONSTANT, %xmm5 + pxor %xmm5, %xmm1 + pxor %xmm2, %xmm1 + + movdqa %xmm1, %xmm5 + PCLMULQDQ 0x00, CONSTANT, %xmm1 + PCLMULQDQ 0x11, CONSTANT, %xmm5 + pxor %xmm5, %xmm1 + pxor %xmm3, %xmm1 + + movdqa %xmm1, %xmm5 + PCLMULQDQ 0x00, CONSTANT, %xmm1 + PCLMULQDQ 0x11, CONSTANT, %xmm5 + pxor %xmm5, %xmm1 + pxor %xmm4, %xmm1 + + cmp $0x10, LEN + jb fold_64 +loop_16:/* Folding rest buffer into 128bit */ + movdqa %xmm1, %xmm5 + PCLMULQDQ 0x00, CONSTANT, %xmm1 + PCLMULQDQ 0x11, CONSTANT, %xmm5 + pxor %xmm5, %xmm1 + pxor (BUF), %xmm1 + sub $0x10, LEN + add $0x10, BUF + cmp $0x10, LEN + jge loop_16 + +fold_64: + /* perform the last 64 bit fold, also adds 32 zeroes + * to the input stream */ + PCLMULQDQ 0x01, %xmm1, CONSTANT /* R4 * xmm1.low */ + psrldq $0x08, %xmm1 + pxor CONSTANT, %xmm1 + + /* final 32-bit fold */ + movdqa %xmm1, %xmm2 +#ifdef __x86_64__ + movdqa .Lconstant_R5(%rip), CONSTANT + movdqa .Lconstant_mask32(%rip), %xmm3 +#else + movdqa .Lconstant_R5 - delta(%ecx), CONSTANT + movdqa .Lconstant_mask32 - delta(%ecx), %xmm3 +#endif + psrldq $0x04, %xmm2 + pand %xmm3, %xmm1 + PCLMULQDQ 0x00, CONSTANT, %xmm1 + pxor %xmm2, %xmm1 + + /* Finish up with the bit-reversed barrett reduction 64 ==> 32 bits */ +#ifdef __x86_64__ + movdqa .Lconstant_RUpoly(%rip), CONSTANT +#else + movdqa .Lconstant_RUpoly - delta(%ecx), CONSTANT +#endif + movdqa %xmm1, %xmm2 + pand %xmm3, %xmm1 + PCLMULQDQ 0x10, CONSTANT, %xmm1 + pand %xmm3, %xmm1 + PCLMULQDQ 0x00, CONSTANT, %xmm1 + pxor %xmm2, %xmm1 + pextrd $0x01, %xmm1, %eax + + ret diff --git a/arch/x86/crypto/crc32-pclmul_glue.c b/arch/x86/crypto/crc32-pclmul_glue.c new file mode 100644 index 000000000000..9d014a74ef96 --- /dev/null +++ b/arch/x86/crypto/crc32-pclmul_glue.c @@ -0,0 +1,201 @@ +/* GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * 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 version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see http://www.gnu.org/licenses + * + * Please visit http://www.xyratex.com/contact if you need additional + * information or have any questions. + * + * GPL HEADER END + */ + +/* + * Copyright 2012 Xyratex Technology Limited + * + * Wrappers for kernel crypto shash api to pclmulqdq crc32 imlementation. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define CHKSUM_BLOCK_SIZE 1 +#define CHKSUM_DIGEST_SIZE 4 + +#define PCLMUL_MIN_LEN 64L /* minimum size of buffer + * for crc32_pclmul_le_16 */ +#define SCALE_F 16L /* size of xmm register */ +#define SCALE_F_MASK (SCALE_F - 1) + +u32 crc32_pclmul_le_16(unsigned char const *buffer, size_t len, u32 crc32); + +static u32 __attribute__((pure)) + crc32_pclmul_le(u32 crc, unsigned char const *p, size_t len) +{ + unsigned int iquotient; + unsigned int iremainder; + unsigned int prealign; + + if (len < PCLMUL_MIN_LEN + SCALE_F_MASK || !irq_fpu_usable()) + return crc32_le(crc, p, len); + + if ((long)p & SCALE_F_MASK) { + /* align p to 16 byte */ + prealign = SCALE_F - ((long)p & SCALE_F_MASK); + + crc = crc32_le(crc, p, prealign); + len -= prealign; + p = (unsigned char *)(((unsigned long)p + SCALE_F_MASK) & + ~SCALE_F_MASK); + } + iquotient = len & (~SCALE_F_MASK); + iremainder = len & SCALE_F_MASK; + + kernel_fpu_begin(); + crc = crc32_pclmul_le_16(p, iquotient, crc); + kernel_fpu_end(); + + if (iremainder) + crc = crc32_le(crc, p + iquotient, iremainder); + + return crc; +} + +static int crc32_pclmul_cra_init(struct crypto_tfm *tfm) +{ + u32 *key = crypto_tfm_ctx(tfm); + + *key = 0; + + return 0; +} + +static int crc32_pclmul_setkey(struct crypto_shash *hash, const u8 *key, + unsigned int keylen) +{ + u32 *mctx = crypto_shash_ctx(hash); + + if (keylen != sizeof(u32)) { + crypto_shash_set_flags(hash, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + *mctx = le32_to_cpup((__le32 *)key); + return 0; +} + +static int crc32_pclmul_init(struct shash_desc *desc) +{ + u32 *mctx = crypto_shash_ctx(desc->tfm); + u32 *crcp = shash_desc_ctx(desc); + + *crcp = *mctx; + + return 0; +} + +static int crc32_pclmul_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + u32 *crcp = shash_desc_ctx(desc); + + *crcp = crc32_pclmul_le(*crcp, data, len); + return 0; +} + +/* No final XOR 0xFFFFFFFF, like crc32_le */ +static int __crc32_pclmul_finup(u32 *crcp, const u8 *data, unsigned int len, + u8 *out) +{ + *(__le32 *)out = cpu_to_le32(crc32_pclmul_le(*crcp, data, len)); + return 0; +} + +static int crc32_pclmul_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + return __crc32_pclmul_finup(shash_desc_ctx(desc), data, len, out); +} + +static int crc32_pclmul_final(struct shash_desc *desc, u8 *out) +{ + u32 *crcp = shash_desc_ctx(desc); + + *(__le32 *)out = cpu_to_le32p(crcp); + return 0; +} + +static int crc32_pclmul_digest(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + return __crc32_pclmul_finup(crypto_shash_ctx(desc->tfm), data, len, + out); +} + +static struct shash_alg alg = { + .setkey = crc32_pclmul_setkey, + .init = crc32_pclmul_init, + .update = crc32_pclmul_update, + .final = crc32_pclmul_final, + .finup = crc32_pclmul_finup, + .digest = crc32_pclmul_digest, + .descsize = sizeof(u32), + .digestsize = CHKSUM_DIGEST_SIZE, + .base = { + .cra_name = "crc32", + .cra_driver_name = "crc32-pclmul", + .cra_priority = 200, + .cra_blocksize = CHKSUM_BLOCK_SIZE, + .cra_ctxsize = sizeof(u32), + .cra_module = THIS_MODULE, + .cra_init = crc32_pclmul_cra_init, + } +}; + +static const struct x86_cpu_id crc32pclmul_cpu_id[] = { + X86_FEATURE_MATCH(X86_FEATURE_PCLMULQDQ), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, crc32pclmul_cpu_id); + + +static int __init crc32_pclmul_mod_init(void) +{ + + if (!x86_match_cpu(crc32pclmul_cpu_id)) { + pr_info("PCLMULQDQ-NI instructions are not detected.\n"); + return -ENODEV; + } + return crypto_register_shash(&alg); +} + +static void __exit crc32_pclmul_mod_fini(void) +{ + crypto_unregister_shash(&alg); +} + +module_init(crc32_pclmul_mod_init); +module_exit(crc32_pclmul_mod_fini); + +MODULE_AUTHOR("Alexander Boyko "); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS("crc32"); +MODULE_ALIAS("crc32-pclmul"); diff --git a/crypto/Kconfig b/crypto/Kconfig index 4641d95651d3..e8b51e068179 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -355,6 +355,27 @@ config CRYPTO_CRC32C_SPARC64 CRC32c CRC algorithm implemented using sparc64 crypto instructions, when available. +config CRYPTO_CRC32 + tristate "CRC32 CRC algorithm" + select CRYPTO_HASH + select CRC32 + help + CRC-32-IEEE 802.3 cyclic redundancy-check algorithm. + Shash crypto api wrappers to crc32_le function. + +config CRYPTO_CRC32_PCLMUL + tristate "CRC32 PCLMULQDQ hardware acceleration" + depends on X86 + select CRYPTO_HASH + select CRC32 + help + From Intel Westmere and AMD Bulldozer processor with SSE4.2 + and PCLMULQDQ supported, the processor will support + CRC32 PCLMULQDQ implementation using hardware accelerated PCLMULQDQ + instruction. This option will create 'crc32-plcmul' module, + which will enable any routine to use the CRC-32-IEEE 802.3 checksum + and gain better performance as compared with the table implementation. + config CRYPTO_GHASH tristate "GHASH digest algorithm" select CRYPTO_GF128MUL diff --git a/crypto/Makefile b/crypto/Makefile index d59dec749804..be1a1bebbb86 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o obj-$(CONFIG_CRYPTO_ZLIB) += zlib.o obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o +obj-$(CONFIG_CRYPTO_CRC32) += crc32.o obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o authencesn.o obj-$(CONFIG_CRYPTO_LZO) += lzo.o obj-$(CONFIG_CRYPTO_842) += 842.o diff --git a/crypto/crc32.c b/crypto/crc32.c new file mode 100644 index 000000000000..9d1c41569898 --- /dev/null +++ b/crypto/crc32.c @@ -0,0 +1,158 @@ +/* GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * 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 version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see http://www.gnu.org/licenses + * + * Please visit http://www.xyratex.com/contact if you need additional + * information or have any questions. + * + * GPL HEADER END + */ + +/* + * Copyright 2012 Xyratex Technology Limited + */ + +/* + * This is crypto api shash wrappers to crc32_le. + */ + +#include +#include +#include +#include +#include +#include + +#define CHKSUM_BLOCK_SIZE 1 +#define CHKSUM_DIGEST_SIZE 4 + +static u32 __crc32_le(u32 crc, unsigned char const *p, size_t len) +{ + return crc32_le(crc, p, len); +} + +/** No default init with ~0 */ +static int crc32_cra_init(struct crypto_tfm *tfm) +{ + u32 *key = crypto_tfm_ctx(tfm); + + *key = 0; + + return 0; +} + + +/* + * Setting the seed allows arbitrary accumulators and flexible XOR policy + * If your algorithm starts with ~0, then XOR with ~0 before you set + * the seed. + */ +static int crc32_setkey(struct crypto_shash *hash, const u8 *key, + unsigned int keylen) +{ + u32 *mctx = crypto_shash_ctx(hash); + + if (keylen != sizeof(u32)) { + crypto_shash_set_flags(hash, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + *mctx = le32_to_cpup((__le32 *)key); + return 0; +} + +static int crc32_init(struct shash_desc *desc) +{ + u32 *mctx = crypto_shash_ctx(desc->tfm); + u32 *crcp = shash_desc_ctx(desc); + + *crcp = *mctx; + + return 0; +} + +static int crc32_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + u32 *crcp = shash_desc_ctx(desc); + + *crcp = __crc32_le(*crcp, data, len); + return 0; +} + +/* No final XOR 0xFFFFFFFF, like crc32_le */ +static int __crc32_finup(u32 *crcp, const u8 *data, unsigned int len, + u8 *out) +{ + *(__le32 *)out = cpu_to_le32(__crc32_le(*crcp, data, len)); + return 0; +} + +static int crc32_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + return __crc32_finup(shash_desc_ctx(desc), data, len, out); +} + +static int crc32_final(struct shash_desc *desc, u8 *out) +{ + u32 *crcp = shash_desc_ctx(desc); + + *(__le32 *)out = cpu_to_le32p(crcp); + return 0; +} + +static int crc32_digest(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + return __crc32_finup(crypto_shash_ctx(desc->tfm), data, len, + out); +} +static struct shash_alg alg = { + .setkey = crc32_setkey, + .init = crc32_init, + .update = crc32_update, + .final = crc32_final, + .finup = crc32_finup, + .digest = crc32_digest, + .descsize = sizeof(u32), + .digestsize = CHKSUM_DIGEST_SIZE, + .base = { + .cra_name = "crc32", + .cra_driver_name = "crc32-table", + .cra_priority = 100, + .cra_blocksize = CHKSUM_BLOCK_SIZE, + .cra_ctxsize = sizeof(u32), + .cra_module = THIS_MODULE, + .cra_init = crc32_cra_init, + } +}; + +static int __init crc32_mod_init(void) +{ + return crypto_register_shash(&alg); +} + +static void __exit crc32_mod_fini(void) +{ + crypto_unregister_shash(&alg); +} + +module_init(crc32_mod_init); +module_exit(crc32_mod_fini); + +MODULE_AUTHOR("Alexander Boyko "); +MODULE_DESCRIPTION("CRC32 calculations wrapper for lib/crc32"); +MODULE_LICENSE("GPL"); From c3c3b3292d202e9924fb3af0f4139848fd7e1de0 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Tue, 15 Jan 2013 13:53:02 -0700 Subject: [PATCH 0997/4607] crypto: omap-sham - Fix compile errors when CONFIG_OF not defined Fix the compile errors created by commit 2545e8d (crypto: omap-sham - Add Device Tree Support) when CONFIG_OF is not defined. This includes changing omap_sham_get_res_dev() to omap_sham_get_res_of() and creating an empty version of omap_sham_of_match[]. Signed-off-by: Mark A. Greer Signed-off-by: Herbert Xu --- drivers/crypto/omap-sham.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index edff981edfb1..dc2d354219c6 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -1607,7 +1607,11 @@ err: return err; } #else -static int omap_sham_get_res_dev(struct omap_sham_dev *dd, +static const struct of_device_id omap_sham_of_match[] = { + {}, +}; + +static int omap_sham_get_res_of(struct omap_sham_dev *dd, struct device *dev, struct resource *res) { return -EINVAL; From 66e5bd0063f4efd99c9c7e3bc23344dfd88bf98d Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:31:36 +0200 Subject: [PATCH 0998/4607] crypto: testmgr - add test vector for fcrypt fcrypt is used only as pcbc(fcrypt), but testmgr does not know this. Use the zero key, zero plaintext pcbc(fcrypt) test vector for testing plain 'fcrypt' to hide "no test for fcrypt" warnings. Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- crypto/testmgr.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/crypto/testmgr.c b/crypto/testmgr.c index edf4a0818773..efd8b20e13dc 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -2268,6 +2268,21 @@ static const struct alg_test_desc alg_test_descs[] = { } } } + }, { + .alg = "ecb(fcrypt)", + .test = alg_test_skcipher, + .suite = { + .cipher = { + .enc = { + .vecs = fcrypt_pcbc_enc_tv_template, + .count = 1 + }, + .dec = { + .vecs = fcrypt_pcbc_dec_tv_template, + .count = 1 + } + } + } }, { .alg = "ecb(khazad)", .test = alg_test_skcipher, From 3f299743839ae0a4c183035c36aa1e2807e53fe4 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:38:50 +0200 Subject: [PATCH 0999/4607] crypto: x86/aes - assembler clean-ups: use ENTRY/ENDPROC, localize jump targets Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/aes-i586-asm_32.S | 15 +++++---------- arch/x86/crypto/aes-x86_64-asm_64.S | 30 ++++++++++++++--------------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/arch/x86/crypto/aes-i586-asm_32.S b/arch/x86/crypto/aes-i586-asm_32.S index b949ec2f9af4..2849dbc59e11 100644 --- a/arch/x86/crypto/aes-i586-asm_32.S +++ b/arch/x86/crypto/aes-i586-asm_32.S @@ -36,6 +36,7 @@ .file "aes-i586-asm.S" .text +#include #include #define tlen 1024 // length of each of 4 'xor' arrays (256 32-bit words) @@ -219,14 +220,10 @@ // AES (Rijndael) Encryption Subroutine /* void aes_enc_blk(struct crypto_aes_ctx *ctx, u8 *out_blk, const u8 *in_blk) */ -.global aes_enc_blk - .extern crypto_ft_tab .extern crypto_fl_tab -.align 4 - -aes_enc_blk: +ENTRY(aes_enc_blk) push %ebp mov ctx(%esp),%ebp @@ -290,18 +287,15 @@ aes_enc_blk: mov %r0,(%ebp) pop %ebp ret +ENDPROC(aes_enc_blk) // AES (Rijndael) Decryption Subroutine /* void aes_dec_blk(struct crypto_aes_ctx *ctx, u8 *out_blk, const u8 *in_blk) */ -.global aes_dec_blk - .extern crypto_it_tab .extern crypto_il_tab -.align 4 - -aes_dec_blk: +ENTRY(aes_dec_blk) push %ebp mov ctx(%esp),%ebp @@ -365,3 +359,4 @@ aes_dec_blk: mov %r0,(%ebp) pop %ebp ret +ENDPROC(aes_dec_blk) diff --git a/arch/x86/crypto/aes-x86_64-asm_64.S b/arch/x86/crypto/aes-x86_64-asm_64.S index 5b577d5a059b..910565547163 100644 --- a/arch/x86/crypto/aes-x86_64-asm_64.S +++ b/arch/x86/crypto/aes-x86_64-asm_64.S @@ -15,6 +15,7 @@ .text +#include #include #define R1 %rax @@ -49,10 +50,8 @@ #define R11 %r11 #define prologue(FUNC,KEY,B128,B192,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11) \ - .global FUNC; \ - .type FUNC,@function; \ - .align 8; \ -FUNC: movq r1,r2; \ + ENTRY(FUNC); \ + movq r1,r2; \ movq r3,r4; \ leaq KEY+48(r8),r9; \ movq r10,r11; \ @@ -71,14 +70,15 @@ FUNC: movq r1,r2; \ je B192; \ leaq 32(r9),r9; -#define epilogue(r1,r2,r3,r4,r5,r6,r7,r8,r9) \ +#define epilogue(FUNC,r1,r2,r3,r4,r5,r6,r7,r8,r9) \ movq r1,r2; \ movq r3,r4; \ movl r5 ## E,(r9); \ movl r6 ## E,4(r9); \ movl r7 ## E,8(r9); \ movl r8 ## E,12(r9); \ - ret; + ret; \ + ENDPROC(FUNC); #define round(TAB,OFFSET,r1,r2,r3,r4,r5,r6,r7,r8,ra,rb,rc,rd) \ movzbl r2 ## H,r5 ## E; \ @@ -133,7 +133,7 @@ FUNC: movq r1,r2; \ #define entry(FUNC,KEY,B128,B192) \ prologue(FUNC,KEY,B128,B192,R2,R8,R7,R9,R1,R3,R4,R6,R10,R5,R11) -#define return epilogue(R8,R2,R9,R7,R5,R6,R3,R4,R11) +#define return(FUNC) epilogue(FUNC,R8,R2,R9,R7,R5,R6,R3,R4,R11) #define encrypt_round(TAB,OFFSET) \ round(TAB,OFFSET,R1,R2,R3,R4,R5,R6,R7,R10,R5,R6,R3,R4) \ @@ -151,12 +151,12 @@ FUNC: movq r1,r2; \ /* void aes_enc_blk(stuct crypto_tfm *tfm, u8 *out, const u8 *in) */ - entry(aes_enc_blk,0,enc128,enc192) + entry(aes_enc_blk,0,.Le128,.Le192) encrypt_round(crypto_ft_tab,-96) encrypt_round(crypto_ft_tab,-80) -enc192: encrypt_round(crypto_ft_tab,-64) +.Le192: encrypt_round(crypto_ft_tab,-64) encrypt_round(crypto_ft_tab,-48) -enc128: encrypt_round(crypto_ft_tab,-32) +.Le128: encrypt_round(crypto_ft_tab,-32) encrypt_round(crypto_ft_tab,-16) encrypt_round(crypto_ft_tab, 0) encrypt_round(crypto_ft_tab, 16) @@ -166,16 +166,16 @@ enc128: encrypt_round(crypto_ft_tab,-32) encrypt_round(crypto_ft_tab, 80) encrypt_round(crypto_ft_tab, 96) encrypt_final(crypto_fl_tab,112) - return + return(aes_enc_blk) /* void aes_dec_blk(struct crypto_tfm *tfm, u8 *out, const u8 *in) */ - entry(aes_dec_blk,240,dec128,dec192) + entry(aes_dec_blk,240,.Ld128,.Ld192) decrypt_round(crypto_it_tab,-96) decrypt_round(crypto_it_tab,-80) -dec192: decrypt_round(crypto_it_tab,-64) +.Ld192: decrypt_round(crypto_it_tab,-64) decrypt_round(crypto_it_tab,-48) -dec128: decrypt_round(crypto_it_tab,-32) +.Ld128: decrypt_round(crypto_it_tab,-32) decrypt_round(crypto_it_tab,-16) decrypt_round(crypto_it_tab, 0) decrypt_round(crypto_it_tab, 16) @@ -185,4 +185,4 @@ dec128: decrypt_round(crypto_it_tab,-32) decrypt_round(crypto_it_tab, 80) decrypt_round(crypto_it_tab, 96) decrypt_final(crypto_il_tab,112) - return + return(aes_dec_blk) From 8309b745bbaf3fe3df7fc67de51d0049b51452c5 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:38:55 +0200 Subject: [PATCH 1000/4607] crypto: aesni-intel - add ENDPROC statements for assembler functions Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/aesni-intel_asm.S | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/arch/x86/crypto/aesni-intel_asm.S b/arch/x86/crypto/aesni-intel_asm.S index 3470624d7835..04b797767b9e 100644 --- a/arch/x86/crypto/aesni-intel_asm.S +++ b/arch/x86/crypto/aesni-intel_asm.S @@ -1262,7 +1262,6 @@ TMP7 XMM1 XMM2 XMM3 XMM4 XMMDst * poly = x^128 + x^127 + x^126 + x^121 + 1 * *****************************************************************************/ - ENTRY(aesni_gcm_dec) push %r12 push %r13 @@ -1437,6 +1436,7 @@ _return_T_done_decrypt: pop %r13 pop %r12 ret +ENDPROC(aesni_gcm_dec) /***************************************************************************** @@ -1700,10 +1700,12 @@ _return_T_done_encrypt: pop %r13 pop %r12 ret +ENDPROC(aesni_gcm_enc) #endif +.align 4 _key_expansion_128: _key_expansion_256a: pshufd $0b11111111, %xmm1, %xmm1 @@ -1715,6 +1717,8 @@ _key_expansion_256a: movaps %xmm0, (TKEYP) add $0x10, TKEYP ret +ENDPROC(_key_expansion_128) +ENDPROC(_key_expansion_256a) .align 4 _key_expansion_192a: @@ -1739,6 +1743,7 @@ _key_expansion_192a: movaps %xmm1, 0x10(TKEYP) add $0x20, TKEYP ret +ENDPROC(_key_expansion_192a) .align 4 _key_expansion_192b: @@ -1758,6 +1763,7 @@ _key_expansion_192b: movaps %xmm0, (TKEYP) add $0x10, TKEYP ret +ENDPROC(_key_expansion_192b) .align 4 _key_expansion_256b: @@ -1770,6 +1776,7 @@ _key_expansion_256b: movaps %xmm2, (TKEYP) add $0x10, TKEYP ret +ENDPROC(_key_expansion_256b) /* * int aesni_set_key(struct crypto_aes_ctx *ctx, const u8 *in_key, @@ -1882,6 +1889,7 @@ ENTRY(aesni_set_key) popl KEYP #endif ret +ENDPROC(aesni_set_key) /* * void aesni_enc(struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src) @@ -1903,6 +1911,7 @@ ENTRY(aesni_enc) popl KEYP #endif ret +ENDPROC(aesni_enc) /* * _aesni_enc1: internal ABI @@ -1960,6 +1969,7 @@ _aesni_enc1: movaps 0x70(TKEYP), KEY AESENCLAST KEY STATE ret +ENDPROC(_aesni_enc1) /* * _aesni_enc4: internal ABI @@ -2068,6 +2078,7 @@ _aesni_enc4: AESENCLAST KEY STATE3 AESENCLAST KEY STATE4 ret +ENDPROC(_aesni_enc4) /* * void aesni_dec (struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src) @@ -2090,6 +2101,7 @@ ENTRY(aesni_dec) popl KEYP #endif ret +ENDPROC(aesni_dec) /* * _aesni_dec1: internal ABI @@ -2147,6 +2159,7 @@ _aesni_dec1: movaps 0x70(TKEYP), KEY AESDECLAST KEY STATE ret +ENDPROC(_aesni_dec1) /* * _aesni_dec4: internal ABI @@ -2255,6 +2268,7 @@ _aesni_dec4: AESDECLAST KEY STATE3 AESDECLAST KEY STATE4 ret +ENDPROC(_aesni_dec4) /* * void aesni_ecb_enc(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src, @@ -2312,6 +2326,7 @@ ENTRY(aesni_ecb_enc) popl LEN #endif ret +ENDPROC(aesni_ecb_enc) /* * void aesni_ecb_dec(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src, @@ -2370,6 +2385,7 @@ ENTRY(aesni_ecb_dec) popl LEN #endif ret +ENDPROC(aesni_ecb_dec) /* * void aesni_cbc_enc(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src, @@ -2411,6 +2427,7 @@ ENTRY(aesni_cbc_enc) popl IVP #endif ret +ENDPROC(aesni_cbc_enc) /* * void aesni_cbc_dec(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src, @@ -2501,6 +2518,7 @@ ENTRY(aesni_cbc_dec) popl IVP #endif ret +ENDPROC(aesni_cbc_dec) #ifdef __x86_64__ .align 16 @@ -2527,6 +2545,7 @@ _aesni_inc_init: MOVQ_R64_XMM TCTR_LOW INC MOVQ_R64_XMM CTR TCTR_LOW ret +ENDPROC(_aesni_inc_init) /* * _aesni_inc: internal ABI @@ -2555,6 +2574,7 @@ _aesni_inc: movaps CTR, IV PSHUFB_XMM BSWAP_MASK IV ret +ENDPROC(_aesni_inc) /* * void aesni_ctr_enc(struct crypto_aes_ctx *ctx, const u8 *dst, u8 *src, @@ -2615,4 +2635,5 @@ ENTRY(aesni_ctr_enc) movups IV, (IVP) .Lctr_enc_just_ret: ret +ENDPROC(aesni_ctr_enc) #endif From 5186e395fee266bede96b6906773973ed6fa2278 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:39:00 +0200 Subject: [PATCH 1001/4607] crypto: blowfish-x86_64: use ENTRY()/ENDPROC() for assembler functions and localize jump targets Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/blowfish-x86_64-asm_64.S | 39 +++++++++--------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/arch/x86/crypto/blowfish-x86_64-asm_64.S b/arch/x86/crypto/blowfish-x86_64-asm_64.S index 391d245dc086..246c67006ed0 100644 --- a/arch/x86/crypto/blowfish-x86_64-asm_64.S +++ b/arch/x86/crypto/blowfish-x86_64-asm_64.S @@ -20,6 +20,8 @@ * */ +#include + .file "blowfish-x86_64-asm.S" .text @@ -116,11 +118,7 @@ bswapq RX0; \ xorq RX0, (RIO); -.align 8 -.global __blowfish_enc_blk -.type __blowfish_enc_blk,@function; - -__blowfish_enc_blk: +ENTRY(__blowfish_enc_blk) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -148,19 +146,16 @@ __blowfish_enc_blk: movq %r10, RIO; test %cl, %cl; - jnz __enc_xor; + jnz .L__enc_xor; write_block(); ret; -__enc_xor: +.L__enc_xor: xor_block(); ret; +ENDPROC(__blowfish_enc_blk) -.align 8 -.global blowfish_dec_blk -.type blowfish_dec_blk,@function; - -blowfish_dec_blk: +ENTRY(blowfish_dec_blk) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -189,6 +184,7 @@ blowfish_dec_blk: movq %r11, %rbp; ret; +ENDPROC(blowfish_dec_blk) /********************************************************************** 4-way blowfish, four blocks parallel @@ -300,11 +296,7 @@ blowfish_dec_blk: bswapq RX3; \ xorq RX3, 24(RIO); -.align 8 -.global __blowfish_enc_blk_4way -.type __blowfish_enc_blk_4way,@function; - -__blowfish_enc_blk_4way: +ENTRY(__blowfish_enc_blk_4way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -336,7 +328,7 @@ __blowfish_enc_blk_4way: movq %r11, RIO; test %bpl, %bpl; - jnz __enc_xor4; + jnz .L__enc_xor4; write_block4(); @@ -344,18 +336,15 @@ __blowfish_enc_blk_4way: popq %rbp; ret; -__enc_xor4: +.L__enc_xor4: xor_block4(); popq %rbx; popq %rbp; ret; +ENDPROC(__blowfish_enc_blk_4way) -.align 8 -.global blowfish_dec_blk_4way -.type blowfish_dec_blk_4way,@function; - -blowfish_dec_blk_4way: +ENTRY(blowfish_dec_blk_4way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -387,4 +376,4 @@ blowfish_dec_blk_4way: popq %rbp; ret; - +ENDPROC(blowfish_dec_blk_4way) From 59990684b0d2b5ab57e37141412bc41cb6c9a2e9 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:39:05 +0200 Subject: [PATCH 1002/4607] crypto: camellia-x86_64/aes-ni: use ENTRY()/ENDPROC() for assembler functions and localize jump targets Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/camellia-aesni-avx-asm_64.S | 38 ++++++---------- arch/x86/crypto/camellia-x86_64-asm_64.S | 50 +++++++++------------ 2 files changed, 36 insertions(+), 52 deletions(-) diff --git a/arch/x86/crypto/camellia-aesni-avx-asm_64.S b/arch/x86/crypto/camellia-aesni-avx-asm_64.S index 2306d2e4816f..cfc163469c71 100644 --- a/arch/x86/crypto/camellia-aesni-avx-asm_64.S +++ b/arch/x86/crypto/camellia-aesni-avx-asm_64.S @@ -15,6 +15,8 @@ * http://koti.mbnet.fi/axh/crypto/camellia-BSD-1.2.0-aesni1.tar.xz */ +#include + #define CAMELLIA_TABLE_BYTE_LEN 272 /* struct camellia_ctx: */ @@ -190,6 +192,7 @@ roundsm16_x0_x1_x2_x3_x4_x5_x6_x7_y0_y1_y2_y3_y4_y5_y6_y7_cd: %xmm8, %xmm9, %xmm10, %xmm11, %xmm12, %xmm13, %xmm14, %xmm15, %rcx, (%r9)); ret; +ENDPROC(roundsm16_x0_x1_x2_x3_x4_x5_x6_x7_y0_y1_y2_y3_y4_y5_y6_y7_cd) .align 8 roundsm16_x4_x5_x6_x7_x0_x1_x2_x3_y4_y5_y6_y7_y0_y1_y2_y3_ab: @@ -197,6 +200,7 @@ roundsm16_x4_x5_x6_x7_x0_x1_x2_x3_y4_y5_y6_y7_y0_y1_y2_y3_ab: %xmm12, %xmm13, %xmm14, %xmm15, %xmm8, %xmm9, %xmm10, %xmm11, %rax, (%r9)); ret; +ENDPROC(roundsm16_x4_x5_x6_x7_x0_x1_x2_x3_y4_y5_y6_y7_y0_y1_y2_y3_ab) /* * IN/OUT: @@ -709,8 +713,6 @@ roundsm16_x4_x5_x6_x7_x0_x1_x2_x3_y4_y5_y6_y7_y0_y1_y2_y3_ab: .text .align 8 -.type __camellia_enc_blk16,@function; - __camellia_enc_blk16: /* input: * %rdi: ctx, CTX @@ -793,10 +795,9 @@ __camellia_enc_blk16: %xmm15, %rax, %rcx, 24); jmp .Lenc_done; +ENDPROC(__camellia_enc_blk16) .align 8 -.type __camellia_dec_blk16,@function; - __camellia_dec_blk16: /* input: * %rdi: ctx, CTX @@ -877,12 +878,9 @@ __camellia_dec_blk16: ((key_table + (24) * 8) + 4)(CTX)); jmp .Ldec_max24; +ENDPROC(__camellia_dec_blk16) -.align 8 -.global camellia_ecb_enc_16way -.type camellia_ecb_enc_16way,@function; - -camellia_ecb_enc_16way: +ENTRY(camellia_ecb_enc_16way) /* input: * %rdi: ctx, CTX * %rsi: dst (16 blocks) @@ -903,12 +901,9 @@ camellia_ecb_enc_16way: %xmm8, %rsi); ret; +ENDPROC(camellia_ecb_enc_16way) -.align 8 -.global camellia_ecb_dec_16way -.type camellia_ecb_dec_16way,@function; - -camellia_ecb_dec_16way: +ENTRY(camellia_ecb_dec_16way) /* input: * %rdi: ctx, CTX * %rsi: dst (16 blocks) @@ -934,12 +929,9 @@ camellia_ecb_dec_16way: %xmm8, %rsi); ret; +ENDPROC(camellia_ecb_dec_16way) -.align 8 -.global camellia_cbc_dec_16way -.type camellia_cbc_dec_16way,@function; - -camellia_cbc_dec_16way: +ENTRY(camellia_cbc_dec_16way) /* input: * %rdi: ctx, CTX * %rsi: dst (16 blocks) @@ -986,6 +978,7 @@ camellia_cbc_dec_16way: %xmm8, %rsi); ret; +ENDPROC(camellia_cbc_dec_16way) #define inc_le128(x, minus_one, tmp) \ vpcmpeqq minus_one, x, tmp; \ @@ -993,11 +986,7 @@ camellia_cbc_dec_16way: vpslldq $8, tmp, tmp; \ vpsubq tmp, x, x; -.align 8 -.global camellia_ctr_16way -.type camellia_ctr_16way,@function; - -camellia_ctr_16way: +ENTRY(camellia_ctr_16way) /* input: * %rdi: ctx, CTX * %rsi: dst (16 blocks) @@ -1100,3 +1089,4 @@ camellia_ctr_16way: %xmm8, %rsi); ret; +ENDPROC(camellia_ctr_16way) diff --git a/arch/x86/crypto/camellia-x86_64-asm_64.S b/arch/x86/crypto/camellia-x86_64-asm_64.S index 0b3374335fdc..310319c601ed 100644 --- a/arch/x86/crypto/camellia-x86_64-asm_64.S +++ b/arch/x86/crypto/camellia-x86_64-asm_64.S @@ -20,6 +20,8 @@ * */ +#include + .file "camellia-x86_64-asm_64.S" .text @@ -188,10 +190,7 @@ bswapq RAB0; \ movq RAB0, 4*2(RIO); -.global __camellia_enc_blk; -.type __camellia_enc_blk,@function; - -__camellia_enc_blk: +ENTRY(__camellia_enc_blk) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -214,33 +213,31 @@ __camellia_enc_blk: movl $24, RT1d; /* max */ cmpb $16, key_length(CTX); - je __enc_done; + je .L__enc_done; enc_fls(24); enc_rounds(24); movl $32, RT1d; /* max */ -__enc_done: +.L__enc_done: testb RXORbl, RXORbl; movq RDST, RIO; - jnz __enc_xor; + jnz .L__enc_xor; enc_outunpack(mov, RT1); movq RRBP, %rbp; ret; -__enc_xor: +.L__enc_xor: enc_outunpack(xor, RT1); movq RRBP, %rbp; ret; +ENDPROC(__camellia_enc_blk) -.global camellia_dec_blk; -.type camellia_dec_blk,@function; - -camellia_dec_blk: +ENTRY(camellia_dec_blk) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -258,12 +255,12 @@ camellia_dec_blk: dec_inpack(RT2); cmpb $24, RT2bl; - je __dec_rounds16; + je .L__dec_rounds16; dec_rounds(24); dec_fls(24); -__dec_rounds16: +.L__dec_rounds16: dec_rounds(16); dec_fls(16); dec_rounds(8); @@ -276,6 +273,7 @@ __dec_rounds16: movq RRBP, %rbp; ret; +ENDPROC(camellia_dec_blk) /********************************************************************** 2-way camellia @@ -426,10 +424,7 @@ __dec_rounds16: bswapq RAB1; \ movq RAB1, 12*2(RIO); -.global __camellia_enc_blk_2way; -.type __camellia_enc_blk_2way,@function; - -__camellia_enc_blk_2way: +ENTRY(__camellia_enc_blk_2way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -453,16 +448,16 @@ __camellia_enc_blk_2way: movl $24, RT2d; /* max */ cmpb $16, key_length(CTX); - je __enc2_done; + je .L__enc2_done; enc_fls2(24); enc_rounds2(24); movl $32, RT2d; /* max */ -__enc2_done: +.L__enc2_done: test RXORbl, RXORbl; movq RDST, RIO; - jnz __enc2_xor; + jnz .L__enc2_xor; enc_outunpack2(mov, RT2); @@ -470,17 +465,15 @@ __enc2_done: popq %rbx; ret; -__enc2_xor: +.L__enc2_xor: enc_outunpack2(xor, RT2); movq RRBP, %rbp; popq %rbx; ret; +ENDPROC(__camellia_enc_blk_2way) -.global camellia_dec_blk_2way; -.type camellia_dec_blk_2way,@function; - -camellia_dec_blk_2way: +ENTRY(camellia_dec_blk_2way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -499,12 +492,12 @@ camellia_dec_blk_2way: dec_inpack2(RT2); cmpb $24, RT2bl; - je __dec2_rounds16; + je .L__dec2_rounds16; dec_rounds2(24); dec_fls2(24); -__dec2_rounds16: +.L__dec2_rounds16: dec_rounds2(16); dec_fls2(16); dec_rounds2(8); @@ -518,3 +511,4 @@ __dec2_rounds16: movq RRBP, %rbp; movq RXOR, %rbx; ret; +ENDPROC(camellia_dec_blk_2way) From e17e209ea44ae69bcfdcfacd6974cf48d04e6f71 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:39:11 +0200 Subject: [PATCH 1003/4607] crypto: cast5-avx: use ENTRY()/ENDPROC() for assembler functions and localize jump targets Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/cast5-avx-x86_64-asm_64.S | 48 +++++++++-------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/arch/x86/crypto/cast5-avx-x86_64-asm_64.S b/arch/x86/crypto/cast5-avx-x86_64-asm_64.S index 15b00ac7cbd3..c35fd5d6ecd2 100644 --- a/arch/x86/crypto/cast5-avx-x86_64-asm_64.S +++ b/arch/x86/crypto/cast5-avx-x86_64-asm_64.S @@ -23,6 +23,8 @@ * */ +#include + .file "cast5-avx-x86_64-asm_64.S" .extern cast_s1 @@ -211,8 +213,6 @@ .text .align 16 -.type __cast5_enc_blk16,@function; - __cast5_enc_blk16: /* input: * %rdi: ctx, CTX @@ -263,14 +263,14 @@ __cast5_enc_blk16: movzbl rr(CTX), %eax; testl %eax, %eax; - jnz __skip_enc; + jnz .L__skip_enc; round(RL, RR, 12, 1); round(RR, RL, 13, 2); round(RL, RR, 14, 3); round(RR, RL, 15, 1); -__skip_enc: +.L__skip_enc: popq %rbx; popq %rbp; @@ -282,10 +282,9 @@ __skip_enc: outunpack_blocks(RR4, RL4, RTMP, RX, RKM); ret; +ENDPROC(__cast5_enc_blk16) .align 16 -.type __cast5_dec_blk16,@function; - __cast5_dec_blk16: /* input: * %rdi: ctx, CTX @@ -323,14 +322,14 @@ __cast5_dec_blk16: movzbl rr(CTX), %eax; testl %eax, %eax; - jnz __skip_dec; + jnz .L__skip_dec; round(RL, RR, 15, 1); round(RR, RL, 14, 3); round(RL, RR, 13, 2); round(RR, RL, 12, 1); -__dec_tail: +.L__dec_tail: round(RL, RR, 11, 3); round(RR, RL, 10, 2); round(RL, RR, 9, 1); @@ -355,15 +354,12 @@ __dec_tail: ret; -__skip_dec: +.L__skip_dec: vpsrldq $4, RKR, RKR; - jmp __dec_tail; + jmp .L__dec_tail; +ENDPROC(__cast5_dec_blk16) -.align 16 -.global cast5_ecb_enc_16way -.type cast5_ecb_enc_16way,@function; - -cast5_ecb_enc_16way: +ENTRY(cast5_ecb_enc_16way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -393,12 +389,9 @@ cast5_ecb_enc_16way: vmovdqu RL4, (7*4*4)(%r11); ret; +ENDPROC(cast5_ecb_enc_16way) -.align 16 -.global cast5_ecb_dec_16way -.type cast5_ecb_dec_16way,@function; - -cast5_ecb_dec_16way: +ENTRY(cast5_ecb_dec_16way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -428,12 +421,9 @@ cast5_ecb_dec_16way: vmovdqu RL4, (7*4*4)(%r11); ret; +ENDPROC(cast5_ecb_dec_16way) -.align 16 -.global cast5_cbc_dec_16way -.type cast5_cbc_dec_16way,@function; - -cast5_cbc_dec_16way: +ENTRY(cast5_cbc_dec_16way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -480,12 +470,9 @@ cast5_cbc_dec_16way: popq %r12; ret; +ENDPROC(cast5_cbc_dec_16way) -.align 16 -.global cast5_ctr_16way -.type cast5_ctr_16way,@function; - -cast5_ctr_16way: +ENTRY(cast5_ctr_16way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -556,3 +543,4 @@ cast5_ctr_16way: popq %r12; ret; +ENDPROC(cast5_ctr_16way) From 1985fecf019dae1db78c90ef9af435e1462e7766 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:39:16 +0200 Subject: [PATCH 1004/4607] crypto: cast6-avx: use ENTRY()/ENDPROC() for assembler functions Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/cast6-avx-x86_64-asm_64.S | 35 +++++++---------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/arch/x86/crypto/cast6-avx-x86_64-asm_64.S b/arch/x86/crypto/cast6-avx-x86_64-asm_64.S index 2569d0da841f..f93b6105a0ce 100644 --- a/arch/x86/crypto/cast6-avx-x86_64-asm_64.S +++ b/arch/x86/crypto/cast6-avx-x86_64-asm_64.S @@ -23,6 +23,7 @@ * */ +#include #include "glue_helper-asm-avx.S" .file "cast6-avx-x86_64-asm_64.S" @@ -250,8 +251,6 @@ .text .align 8 -.type __cast6_enc_blk8,@function; - __cast6_enc_blk8: /* input: * %rdi: ctx, CTX @@ -295,10 +294,9 @@ __cast6_enc_blk8: outunpack_blocks(RA2, RB2, RC2, RD2, RTMP, RX, RKRF, RKM); ret; +ENDPROC(__cast6_enc_blk8) .align 8 -.type __cast6_dec_blk8,@function; - __cast6_dec_blk8: /* input: * %rdi: ctx, CTX @@ -341,12 +339,9 @@ __cast6_dec_blk8: outunpack_blocks(RA2, RB2, RC2, RD2, RTMP, RX, RKRF, RKM); ret; +ENDPROC(__cast6_dec_blk8) -.align 8 -.global cast6_ecb_enc_8way -.type cast6_ecb_enc_8way,@function; - -cast6_ecb_enc_8way: +ENTRY(cast6_ecb_enc_8way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -362,12 +357,9 @@ cast6_ecb_enc_8way: store_8way(%r11, RA1, RB1, RC1, RD1, RA2, RB2, RC2, RD2); ret; +ENDPROC(cast6_ecb_enc_8way) -.align 8 -.global cast6_ecb_dec_8way -.type cast6_ecb_dec_8way,@function; - -cast6_ecb_dec_8way: +ENTRY(cast6_ecb_dec_8way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -383,12 +375,9 @@ cast6_ecb_dec_8way: store_8way(%r11, RA1, RB1, RC1, RD1, RA2, RB2, RC2, RD2); ret; +ENDPROC(cast6_ecb_dec_8way) -.align 8 -.global cast6_cbc_dec_8way -.type cast6_cbc_dec_8way,@function; - -cast6_cbc_dec_8way: +ENTRY(cast6_cbc_dec_8way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -409,12 +398,9 @@ cast6_cbc_dec_8way: popq %r12; ret; +ENDPROC(cast6_cbc_dec_8way) -.align 8 -.global cast6_ctr_8way -.type cast6_ctr_8way,@function; - -cast6_ctr_8way: +ENTRY(cast6_ctr_8way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -437,3 +423,4 @@ cast6_ctr_8way: popq %r12; ret; +ENDPROC(cast6_ctr_8way) From 698a5abbb0c15ac273bf02f55a1385725714353a Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:39:21 +0200 Subject: [PATCH 1005/4607] crypto: x86/crc32c - assembler clean-up: use ENTRY/ENDPROC Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/crc32c-pcl-intel-asm_64.S | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/x86/crypto/crc32c-pcl-intel-asm_64.S b/arch/x86/crypto/crc32c-pcl-intel-asm_64.S index 93c6d39237ac..cf1a7ec4cc3a 100644 --- a/arch/x86/crypto/crc32c-pcl-intel-asm_64.S +++ b/arch/x86/crypto/crc32c-pcl-intel-asm_64.S @@ -42,6 +42,8 @@ * SOFTWARE. */ +#include + ## ISCSI CRC 32 Implementation with crc32 and pclmulqdq Instruction .macro LABEL prefix n @@ -68,8 +70,7 @@ # unsigned int crc_pcl(u8 *buffer, int len, unsigned int crc_init); -.global crc_pcl -crc_pcl: +ENTRY(crc_pcl) #define bufp %rdi #define bufp_dw %edi #define bufp_w %di @@ -323,6 +324,9 @@ JMPTBL_ENTRY %i .noaltmacro i=i+1 .endr + +ENDPROC(crc_pcl) + ################################################################ ## PCLMULQDQ tables ## Table is 128 entries x 2 quad words each From b05d3f375676e57672ac5a9090cb1068fab8b85f Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:39:26 +0200 Subject: [PATCH 1006/4607] crypto: x86/ghash - assembler clean-up: use ENDPROC at end of assember functions Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/ghash-clmulni-intel_asm.S | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/crypto/ghash-clmulni-intel_asm.S b/arch/x86/crypto/ghash-clmulni-intel_asm.S index 1eb7f90cb7b9..586f41aac361 100644 --- a/arch/x86/crypto/ghash-clmulni-intel_asm.S +++ b/arch/x86/crypto/ghash-clmulni-intel_asm.S @@ -94,6 +94,7 @@ __clmul_gf128mul_ble: pxor T2, T1 pxor T1, DATA ret +ENDPROC(__clmul_gf128mul_ble) /* void clmul_ghash_mul(char *dst, const be128 *shash) */ ENTRY(clmul_ghash_mul) @@ -105,6 +106,7 @@ ENTRY(clmul_ghash_mul) PSHUFB_XMM BSWAP DATA movups DATA, (%rdi) ret +ENDPROC(clmul_ghash_mul) /* * void clmul_ghash_update(char *dst, const char *src, unsigned int srclen, @@ -131,6 +133,7 @@ ENTRY(clmul_ghash_update) movups DATA, (%rdi) .Lupdate_just_ret: ret +ENDPROC(clmul_ghash_update) /* * void clmul_ghash_setkey(be128 *shash, const u8 *key); @@ -155,3 +158,4 @@ ENTRY(clmul_ghash_setkey) pxor %xmm1, %xmm0 movups %xmm0, (%rdi) ret +ENDPROC(clmul_ghash_setkey) From 044438082cf1447e37534b24beff723835464954 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:39:31 +0200 Subject: [PATCH 1007/4607] crypto: x86/salsa20 - assembler cleanup, use ENTRY/ENDPROC for assember functions and rename ECRYPT_* to salsa20_* Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/salsa20-i586-asm_32.S | 28 ++++++++++++------------- arch/x86/crypto/salsa20-x86_64-asm_64.S | 28 ++++++++++++------------- arch/x86/crypto/salsa20_glue.c | 5 ----- 3 files changed, 27 insertions(+), 34 deletions(-) diff --git a/arch/x86/crypto/salsa20-i586-asm_32.S b/arch/x86/crypto/salsa20-i586-asm_32.S index 72eb306680b2..329452b8f794 100644 --- a/arch/x86/crypto/salsa20-i586-asm_32.S +++ b/arch/x86/crypto/salsa20-i586-asm_32.S @@ -2,11 +2,12 @@ # D. J. Bernstein # Public domain. -# enter ECRYPT_encrypt_bytes +#include + .text -.p2align 5 -.globl ECRYPT_encrypt_bytes -ECRYPT_encrypt_bytes: + +# enter salsa20_encrypt_bytes +ENTRY(salsa20_encrypt_bytes) mov %esp,%eax and $31,%eax add $256,%eax @@ -933,11 +934,10 @@ ECRYPT_encrypt_bytes: add $64,%esi # goto bytesatleast1 jmp ._bytesatleast1 -# enter ECRYPT_keysetup -.text -.p2align 5 -.globl ECRYPT_keysetup -ECRYPT_keysetup: +ENDPROC(salsa20_encrypt_bytes) + +# enter salsa20_keysetup +ENTRY(salsa20_keysetup) mov %esp,%eax and $31,%eax add $256,%eax @@ -1060,11 +1060,10 @@ ECRYPT_keysetup: # leave add %eax,%esp ret -# enter ECRYPT_ivsetup -.text -.p2align 5 -.globl ECRYPT_ivsetup -ECRYPT_ivsetup: +ENDPROC(salsa20_keysetup) + +# enter salsa20_ivsetup +ENTRY(salsa20_ivsetup) mov %esp,%eax and $31,%eax add $256,%eax @@ -1112,3 +1111,4 @@ ECRYPT_ivsetup: # leave add %eax,%esp ret +ENDPROC(salsa20_ivsetup) diff --git a/arch/x86/crypto/salsa20-x86_64-asm_64.S b/arch/x86/crypto/salsa20-x86_64-asm_64.S index 6214a9b09706..9279e0b2d60e 100644 --- a/arch/x86/crypto/salsa20-x86_64-asm_64.S +++ b/arch/x86/crypto/salsa20-x86_64-asm_64.S @@ -1,8 +1,7 @@ -# enter ECRYPT_encrypt_bytes -.text -.p2align 5 -.globl ECRYPT_encrypt_bytes -ECRYPT_encrypt_bytes: +#include + +# enter salsa20_encrypt_bytes +ENTRY(salsa20_encrypt_bytes) mov %rsp,%r11 and $31,%r11 add $256,%r11 @@ -802,11 +801,10 @@ ECRYPT_encrypt_bytes: # comment:fp stack unchanged by jump # goto bytesatleast1 jmp ._bytesatleast1 -# enter ECRYPT_keysetup -.text -.p2align 5 -.globl ECRYPT_keysetup -ECRYPT_keysetup: +ENDPROC(salsa20_encrypt_bytes) + +# enter salsa20_keysetup +ENTRY(salsa20_keysetup) mov %rsp,%r11 and $31,%r11 add $256,%r11 @@ -892,11 +890,10 @@ ECRYPT_keysetup: mov %rdi,%rax mov %rsi,%rdx ret -# enter ECRYPT_ivsetup -.text -.p2align 5 -.globl ECRYPT_ivsetup -ECRYPT_ivsetup: +ENDPROC(salsa20_keysetup) + +# enter salsa20_ivsetup +ENTRY(salsa20_ivsetup) mov %rsp,%r11 and $31,%r11 add $256,%r11 @@ -918,3 +915,4 @@ ECRYPT_ivsetup: mov %rdi,%rax mov %rsi,%rdx ret +ENDPROC(salsa20_ivsetup) diff --git a/arch/x86/crypto/salsa20_glue.c b/arch/x86/crypto/salsa20_glue.c index a3a3c0205c16..5e8e67739bb5 100644 --- a/arch/x86/crypto/salsa20_glue.c +++ b/arch/x86/crypto/salsa20_glue.c @@ -26,11 +26,6 @@ #define SALSA20_MIN_KEY_SIZE 16U #define SALSA20_MAX_KEY_SIZE 32U -// use the ECRYPT_* function names -#define salsa20_keysetup ECRYPT_keysetup -#define salsa20_ivsetup ECRYPT_ivsetup -#define salsa20_encrypt_bytes ECRYPT_encrypt_bytes - struct salsa20_ctx { u32 input[16]; From 2dcfd44dee3fd3a63e3e3d3f5cbfd2436d1f98a6 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:39:36 +0200 Subject: [PATCH 1008/4607] crypto: x86/serpent - use ENTRY/ENDPROC for assember functions and localize jump targets Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/serpent-avx-x86_64-asm_64.S | 35 ++++++-------------- arch/x86/crypto/serpent-sse2-i586-asm_32.S | 20 +++++------ arch/x86/crypto/serpent-sse2-x86_64-asm_64.S | 20 +++++------ 3 files changed, 27 insertions(+), 48 deletions(-) diff --git a/arch/x86/crypto/serpent-avx-x86_64-asm_64.S b/arch/x86/crypto/serpent-avx-x86_64-asm_64.S index 02b0e9fe997c..43c938612b74 100644 --- a/arch/x86/crypto/serpent-avx-x86_64-asm_64.S +++ b/arch/x86/crypto/serpent-avx-x86_64-asm_64.S @@ -24,6 +24,7 @@ * */ +#include #include "glue_helper-asm-avx.S" .file "serpent-avx-x86_64-asm_64.S" @@ -566,8 +567,6 @@ transpose_4x4(x0, x1, x2, x3, t0, t1, t2) .align 8 -.type __serpent_enc_blk8_avx,@function; - __serpent_enc_blk8_avx: /* input: * %rdi: ctx, CTX @@ -619,10 +618,9 @@ __serpent_enc_blk8_avx: write_blocks(RA2, RB2, RC2, RD2, RK0, RK1, RK2); ret; +ENDPROC(__serpent_enc_blk8_avx) .align 8 -.type __serpent_dec_blk8_avx,@function; - __serpent_dec_blk8_avx: /* input: * %rdi: ctx, CTX @@ -674,12 +672,9 @@ __serpent_dec_blk8_avx: write_blocks(RC2, RD2, RB2, RE2, RK0, RK1, RK2); ret; +ENDPROC(__serpent_dec_blk8_avx) -.align 8 -.global serpent_ecb_enc_8way_avx -.type serpent_ecb_enc_8way_avx,@function; - -serpent_ecb_enc_8way_avx: +ENTRY(serpent_ecb_enc_8way_avx) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -693,12 +688,9 @@ serpent_ecb_enc_8way_avx: store_8way(%rsi, RA1, RB1, RC1, RD1, RA2, RB2, RC2, RD2); ret; +ENDPROC(serpent_ecb_enc_8way_avx) -.align 8 -.global serpent_ecb_dec_8way_avx -.type serpent_ecb_dec_8way_avx,@function; - -serpent_ecb_dec_8way_avx: +ENTRY(serpent_ecb_dec_8way_avx) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -712,12 +704,9 @@ serpent_ecb_dec_8way_avx: store_8way(%rsi, RC1, RD1, RB1, RE1, RC2, RD2, RB2, RE2); ret; +ENDPROC(serpent_ecb_dec_8way_avx) -.align 8 -.global serpent_cbc_dec_8way_avx -.type serpent_cbc_dec_8way_avx,@function; - -serpent_cbc_dec_8way_avx: +ENTRY(serpent_cbc_dec_8way_avx) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -731,12 +720,9 @@ serpent_cbc_dec_8way_avx: store_cbc_8way(%rdx, %rsi, RC1, RD1, RB1, RE1, RC2, RD2, RB2, RE2); ret; +ENDPROC(serpent_cbc_dec_8way_avx) -.align 8 -.global serpent_ctr_8way_avx -.type serpent_ctr_8way_avx,@function; - -serpent_ctr_8way_avx: +ENTRY(serpent_ctr_8way_avx) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -752,3 +738,4 @@ serpent_ctr_8way_avx: store_ctr_8way(%rdx, %rsi, RA1, RB1, RC1, RD1, RA2, RB2, RC2, RD2); ret; +ENDPROC(serpent_ctr_8way_avx) diff --git a/arch/x86/crypto/serpent-sse2-i586-asm_32.S b/arch/x86/crypto/serpent-sse2-i586-asm_32.S index c00053d42f99..d348f1553a79 100644 --- a/arch/x86/crypto/serpent-sse2-i586-asm_32.S +++ b/arch/x86/crypto/serpent-sse2-i586-asm_32.S @@ -24,6 +24,8 @@ * */ +#include + .file "serpent-sse2-i586-asm_32.S" .text @@ -510,11 +512,7 @@ pxor t0, x3; \ movdqu x3, (3*4*4)(out); -.align 8 -.global __serpent_enc_blk_4way -.type __serpent_enc_blk_4way,@function; - -__serpent_enc_blk_4way: +ENTRY(__serpent_enc_blk_4way) /* input: * arg_ctx(%esp): ctx, CTX * arg_dst(%esp): dst @@ -566,22 +564,19 @@ __serpent_enc_blk_4way: movl arg_dst(%esp), %eax; cmpb $0, arg_xor(%esp); - jnz __enc_xor4; + jnz .L__enc_xor4; write_blocks(%eax, RA, RB, RC, RD, RT0, RT1, RE); ret; -__enc_xor4: +.L__enc_xor4: xor_blocks(%eax, RA, RB, RC, RD, RT0, RT1, RE); ret; +ENDPROC(__serpent_enc_blk_4way) -.align 8 -.global serpent_dec_blk_4way -.type serpent_dec_blk_4way,@function; - -serpent_dec_blk_4way: +ENTRY(serpent_dec_blk_4way) /* input: * arg_ctx(%esp): ctx, CTX * arg_dst(%esp): dst @@ -633,3 +628,4 @@ serpent_dec_blk_4way: write_blocks(%eax, RC, RD, RB, RE, RT0, RT1, RA); ret; +ENDPROC(serpent_dec_blk_4way) diff --git a/arch/x86/crypto/serpent-sse2-x86_64-asm_64.S b/arch/x86/crypto/serpent-sse2-x86_64-asm_64.S index 3ee1ff04d3e9..acc066c7c6b2 100644 --- a/arch/x86/crypto/serpent-sse2-x86_64-asm_64.S +++ b/arch/x86/crypto/serpent-sse2-x86_64-asm_64.S @@ -24,6 +24,8 @@ * */ +#include + .file "serpent-sse2-x86_64-asm_64.S" .text @@ -632,11 +634,7 @@ pxor t0, x3; \ movdqu x3, (3*4*4)(out); -.align 8 -.global __serpent_enc_blk_8way -.type __serpent_enc_blk_8way,@function; - -__serpent_enc_blk_8way: +ENTRY(__serpent_enc_blk_8way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -687,24 +685,21 @@ __serpent_enc_blk_8way: leaq (4*4*4)(%rsi), %rax; testb %cl, %cl; - jnz __enc_xor8; + jnz .L__enc_xor8; write_blocks(%rsi, RA1, RB1, RC1, RD1, RK0, RK1, RK2); write_blocks(%rax, RA2, RB2, RC2, RD2, RK0, RK1, RK2); ret; -__enc_xor8: +.L__enc_xor8: xor_blocks(%rsi, RA1, RB1, RC1, RD1, RK0, RK1, RK2); xor_blocks(%rax, RA2, RB2, RC2, RD2, RK0, RK1, RK2); ret; +ENDPROC(__serpent_enc_blk_8way) -.align 8 -.global serpent_dec_blk_8way -.type serpent_dec_blk_8way,@function; - -serpent_dec_blk_8way: +ENTRY(serpent_dec_blk_8way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -756,3 +751,4 @@ serpent_dec_blk_8way: write_blocks(%rax, RC2, RD2, RB2, RE2, RK0, RK1, RK2); ret; +ENDPROC(serpent_dec_blk_8way) From ac9d55dd42858b127bedb84f4e59789958263d37 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:39:41 +0200 Subject: [PATCH 1009/4607] crypto: x86/sha1 - assembler clean-ups: use ENTRY/ENDPROC Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/sha1_ssse3_asm.S | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/crypto/sha1_ssse3_asm.S b/arch/x86/crypto/sha1_ssse3_asm.S index 49d6987a73d9..a4109506a5e8 100644 --- a/arch/x86/crypto/sha1_ssse3_asm.S +++ b/arch/x86/crypto/sha1_ssse3_asm.S @@ -28,6 +28,8 @@ * (at your option) any later version. */ +#include + #define CTX %rdi // arg1 #define BUF %rsi // arg2 #define CNT %rdx // arg3 @@ -69,10 +71,8 @@ * param: function's name */ .macro SHA1_VECTOR_ASM name - .global \name - .type \name, @function - .align 32 -\name: + ENTRY(\name) + push %rbx push %rbp push %r12 @@ -106,7 +106,7 @@ pop %rbx ret - .size \name, .-\name + ENDPROC(\name) .endm /* From d3f5188dfea70e7ea6570bd4bc9d6d7dbd431e39 Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sat, 19 Jan 2013 13:39:46 +0200 Subject: [PATCH 1010/4607] crypto: x86/twofish - assembler clean-ups: use ENTRY/ENDPROC, localize jump labels Signed-off-by: Jussi Kivilinna Acked-by: David S. Miller Signed-off-by: Herbert Xu --- arch/x86/crypto/twofish-avx-x86_64-asm_64.S | 35 ++++++-------------- arch/x86/crypto/twofish-i586-asm_32.S | 11 +++--- arch/x86/crypto/twofish-x86_64-asm_64-3way.S | 20 +++++------ arch/x86/crypto/twofish-x86_64-asm_64.S | 11 +++--- 4 files changed, 29 insertions(+), 48 deletions(-) diff --git a/arch/x86/crypto/twofish-avx-x86_64-asm_64.S b/arch/x86/crypto/twofish-avx-x86_64-asm_64.S index ebac16bfa830..8d3e113b2c95 100644 --- a/arch/x86/crypto/twofish-avx-x86_64-asm_64.S +++ b/arch/x86/crypto/twofish-avx-x86_64-asm_64.S @@ -23,6 +23,7 @@ * */ +#include #include "glue_helper-asm-avx.S" .file "twofish-avx-x86_64-asm_64.S" @@ -243,8 +244,6 @@ vpxor x3, wkey, x3; .align 8 -.type __twofish_enc_blk8,@function; - __twofish_enc_blk8: /* input: * %rdi: ctx, CTX @@ -284,10 +283,9 @@ __twofish_enc_blk8: outunpack_blocks(RC2, RD2, RA2, RB2, RK1, RX0, RY0, RK2); ret; +ENDPROC(__twofish_enc_blk8) .align 8 -.type __twofish_dec_blk8,@function; - __twofish_dec_blk8: /* input: * %rdi: ctx, CTX @@ -325,12 +323,9 @@ __twofish_dec_blk8: outunpack_blocks(RA2, RB2, RC2, RD2, RK1, RX0, RY0, RK2); ret; +ENDPROC(__twofish_dec_blk8) -.align 8 -.global twofish_ecb_enc_8way -.type twofish_ecb_enc_8way,@function; - -twofish_ecb_enc_8way: +ENTRY(twofish_ecb_enc_8way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -346,12 +341,9 @@ twofish_ecb_enc_8way: store_8way(%r11, RC1, RD1, RA1, RB1, RC2, RD2, RA2, RB2); ret; +ENDPROC(twofish_ecb_enc_8way) -.align 8 -.global twofish_ecb_dec_8way -.type twofish_ecb_dec_8way,@function; - -twofish_ecb_dec_8way: +ENTRY(twofish_ecb_dec_8way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -367,12 +359,9 @@ twofish_ecb_dec_8way: store_8way(%r11, RA1, RB1, RC1, RD1, RA2, RB2, RC2, RD2); ret; +ENDPROC(twofish_ecb_dec_8way) -.align 8 -.global twofish_cbc_dec_8way -.type twofish_cbc_dec_8way,@function; - -twofish_cbc_dec_8way: +ENTRY(twofish_cbc_dec_8way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -393,12 +382,9 @@ twofish_cbc_dec_8way: popq %r12; ret; +ENDPROC(twofish_cbc_dec_8way) -.align 8 -.global twofish_ctr_8way -.type twofish_ctr_8way,@function; - -twofish_ctr_8way: +ENTRY(twofish_ctr_8way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -421,3 +407,4 @@ twofish_ctr_8way: popq %r12; ret; +ENDPROC(twofish_ctr_8way) diff --git a/arch/x86/crypto/twofish-i586-asm_32.S b/arch/x86/crypto/twofish-i586-asm_32.S index 658af4bb35c9..694ea4587ba7 100644 --- a/arch/x86/crypto/twofish-i586-asm_32.S +++ b/arch/x86/crypto/twofish-i586-asm_32.S @@ -20,6 +20,7 @@ .file "twofish-i586-asm.S" .text +#include #include /* return address at 0 */ @@ -219,11 +220,7 @@ xor %esi, d ## D;\ ror $1, d ## D; -.align 4 -.global twofish_enc_blk -.global twofish_dec_blk - -twofish_enc_blk: +ENTRY(twofish_enc_blk) push %ebp /* save registers according to calling convention*/ push %ebx push %esi @@ -277,8 +274,9 @@ twofish_enc_blk: pop %ebp mov $1, %eax ret +ENDPROC(twofish_enc_blk) -twofish_dec_blk: +ENTRY(twofish_dec_blk) push %ebp /* save registers according to calling convention*/ push %ebx push %esi @@ -333,3 +331,4 @@ twofish_dec_blk: pop %ebp mov $1, %eax ret +ENDPROC(twofish_dec_blk) diff --git a/arch/x86/crypto/twofish-x86_64-asm_64-3way.S b/arch/x86/crypto/twofish-x86_64-asm_64-3way.S index 5b012a2c5119..1c3b7ceb36d2 100644 --- a/arch/x86/crypto/twofish-x86_64-asm_64-3way.S +++ b/arch/x86/crypto/twofish-x86_64-asm_64-3way.S @@ -20,6 +20,8 @@ * */ +#include + .file "twofish-x86_64-asm-3way.S" .text @@ -214,11 +216,7 @@ rorq $32, RAB2; \ outunpack3(mov, RIO, 2, RAB, 2); -.align 8 -.global __twofish_enc_blk_3way -.type __twofish_enc_blk_3way,@function; - -__twofish_enc_blk_3way: +ENTRY(__twofish_enc_blk_3way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -250,7 +248,7 @@ __twofish_enc_blk_3way: popq %rbp; /* bool xor */ testb %bpl, %bpl; - jnz __enc_xor3; + jnz .L__enc_xor3; outunpack_enc3(mov); @@ -262,7 +260,7 @@ __twofish_enc_blk_3way: popq %r15; ret; -__enc_xor3: +.L__enc_xor3: outunpack_enc3(xor); popq %rbx; @@ -272,11 +270,9 @@ __enc_xor3: popq %r14; popq %r15; ret; +ENDPROC(__twofish_enc_blk_3way) -.global twofish_dec_blk_3way -.type twofish_dec_blk_3way,@function; - -twofish_dec_blk_3way: +ENTRY(twofish_dec_blk_3way) /* input: * %rdi: ctx, CTX * %rsi: dst @@ -313,4 +309,4 @@ twofish_dec_blk_3way: popq %r14; popq %r15; ret; - +ENDPROC(twofish_dec_blk_3way) diff --git a/arch/x86/crypto/twofish-x86_64-asm_64.S b/arch/x86/crypto/twofish-x86_64-asm_64.S index 7bcf3fcc3668..a039d21986a2 100644 --- a/arch/x86/crypto/twofish-x86_64-asm_64.S +++ b/arch/x86/crypto/twofish-x86_64-asm_64.S @@ -20,6 +20,7 @@ .file "twofish-x86_64-asm.S" .text +#include #include #define a_offset 0 @@ -214,11 +215,7 @@ xor %r8d, d ## D;\ ror $1, d ## D; -.align 8 -.global twofish_enc_blk -.global twofish_dec_blk - -twofish_enc_blk: +ENTRY(twofish_enc_blk) pushq R1 /* %rdi contains the ctx address */ @@ -269,8 +266,9 @@ twofish_enc_blk: popq R1 movq $1,%rax ret +ENDPROC(twofish_enc_blk) -twofish_dec_blk: +ENTRY(twofish_dec_blk) pushq R1 /* %rdi contains the ctx address */ @@ -320,3 +318,4 @@ twofish_dec_blk: popq R1 movq $1,%rax ret +ENDPROC(twofish_dec_blk) From 7983627657db5e37594af5c28cdb623855eb554f Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sun, 20 Jan 2013 18:05:02 +1100 Subject: [PATCH 1011/4607] crypto: crc32-pclmul - Kill warning on x86-32 This patch removes a gratuitous warning on x86-32: arch/x86/crypto/crc32-pclmul_asm.S:87:2: warning: #warning Using 32bit code support [-Wcpp] Signed-off-by: Herbert Xu --- arch/x86/crypto/crc32-pclmul_asm.S | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/x86/crypto/crc32-pclmul_asm.S b/arch/x86/crypto/crc32-pclmul_asm.S index 65ea6a624907..c8335014a044 100644 --- a/arch/x86/crypto/crc32-pclmul_asm.S +++ b/arch/x86/crypto/crc32-pclmul_asm.S @@ -84,7 +84,6 @@ #define LEN %rsi #define CRC %edx #else -#warning Using 32bit code support #define BUF %eax #define LEN %edx #define CRC %ecx From bac4b7c3b5c0660c08dc4949fe40e08e20364ee3 Mon Sep 17 00:00:00 2001 From: Carsten Emde Date: Thu, 19 Jul 2012 15:54:25 +0000 Subject: [PATCH 1012/4607] drm: Load EDID: Explain better how to write your own EDID firmware A description was lacking how to write an EDID firmware file that corresponds to a given X11 setting. Signed-off-by: Carsten Emde Reviewed-by: Adam Jackson Signed-off-by: Dave Airlie --- Documentation/EDID/HOWTO.txt | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/Documentation/EDID/HOWTO.txt b/Documentation/EDID/HOWTO.txt index 75a9f2a0c43d..2d0a8f09475d 100644 --- a/Documentation/EDID/HOWTO.txt +++ b/Documentation/EDID/HOWTO.txt @@ -28,11 +28,30 @@ Makefile environment are given here. To create binary EDID and C source code files from the existing data material, simply type "make". -If you want to create your own EDID file, copy the file 1024x768.S and -replace the settings with your own data. The CRC value in the last line +If you want to create your own EDID file, copy the file 1024x768.S, +replace the settings with your own data and add a new target to the +Makefile. Please note that the EDID data structure expects the timing +values in a different way as compared to the standard X11 format. + +X11: +HTimings: hdisp hsyncstart hsyncend htotal +VTimings: vdisp vsyncstart vsyncend vtotal + +EDID: +#define XPIX hdisp +#define XBLANK htotal-hdisp +#define XOFFSET hsyncstart-hdisp +#define XPULSE hsyncend-hsyncstart + +#define YPIX vdisp +#define YBLANK vtotal-vdisp +#define YOFFSET (63+(vsyncstart-vdisp)) +#define YPULSE (63+(vsyncend-vsyncstart)) + +The CRC value in the last line #define CRC 0x55 -is a bit tricky. After a first version of the binary data set is -created, it must be be checked with the "edid-decode" utility which will +also is a bit tricky. After a first version of the binary data set is +created, it must be checked with the "edid-decode" utility which will most probably complain about a wrong CRC. Fortunately, the utility also displays the correct CRC which must then be inserted into the source file. After the make procedure is repeated, the EDID data set is ready From 661f7cb55c61fa7491e0caf21e55f59e5bc49abe Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Thu, 10 Jan 2013 13:41:04 -0500 Subject: [PATCH 1013/4607] dma: edma: fix slave config dependency on direction The edma_slave_config() implementation depends on the direction field such that it will not properly configure a slave channel when called without direction set. This fixes the implementation so that the slave config is copied as is and prep_slave_sg() handles the direction dependent handling. spi-omap2-mcspi and omap_hsmmc both expose this bug as they configure the slave channel config from a common path with an unconfigured direction field. Signed-off-by: Matt Porter Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 55 +++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 232b4583ae93..82c8672f26e8 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -69,9 +69,7 @@ struct edma_chan { int ch_num; bool alloced; int slot[EDMA_MAX_SLOTS]; - dma_addr_t addr; - int addr_width; - int maxburst; + struct dma_slave_config cfg; }; struct edma_cc { @@ -178,29 +176,14 @@ static int edma_terminate_all(struct edma_chan *echan) return 0; } - static int edma_slave_config(struct edma_chan *echan, - struct dma_slave_config *config) + struct dma_slave_config *cfg) { - if ((config->src_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES) || - (config->dst_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES)) + if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || + cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) return -EINVAL; - if (config->direction == DMA_MEM_TO_DEV) { - if (config->dst_addr) - echan->addr = config->dst_addr; - if (config->dst_addr_width) - echan->addr_width = config->dst_addr_width; - if (config->dst_maxburst) - echan->maxburst = config->dst_maxburst; - } else if (config->direction == DMA_DEV_TO_MEM) { - if (config->src_addr) - echan->addr = config->src_addr; - if (config->src_addr_width) - echan->addr_width = config->src_addr_width; - if (config->src_maxburst) - echan->maxburst = config->src_maxburst; - } + memcpy(&echan->cfg, cfg, sizeof(echan->cfg)); return 0; } @@ -235,6 +218,9 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( struct edma_chan *echan = to_edma_chan(chan); struct device *dev = chan->device->dev; struct edma_desc *edesc; + dma_addr_t dev_addr; + enum dma_slave_buswidth dev_width; + u32 burst; struct scatterlist *sg; int i; int acnt, bcnt, ccnt, src, dst, cidx; @@ -243,7 +229,20 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( if (unlikely(!echan || !sgl || !sg_len)) return NULL; - if (echan->addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { + if (direction == DMA_DEV_TO_MEM) { + dev_addr = echan->cfg.src_addr; + dev_width = echan->cfg.src_addr_width; + burst = echan->cfg.src_maxburst; + } else if (direction == DMA_MEM_TO_DEV) { + dev_addr = echan->cfg.dst_addr; + dev_width = echan->cfg.dst_addr_width; + burst = echan->cfg.dst_maxburst; + } else { + dev_err(dev, "%s: bad direction?\n", __func__); + return NULL; + } + + if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { dev_err(dev, "Undefined slave buswidth\n"); return NULL; } @@ -275,14 +274,14 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( } } - acnt = echan->addr_width; + acnt = dev_width; /* * If the maxburst is equal to the fifo width, use * A-synced transfers. This allows for large contiguous * buffer transfers using only one PaRAM set. */ - if (echan->maxburst == 1) { + if (burst == 1) { edesc->absync = false; ccnt = sg_dma_len(sg) / acnt / (SZ_64K - 1); bcnt = sg_dma_len(sg) / acnt - ccnt * (SZ_64K - 1); @@ -302,7 +301,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( */ } else { edesc->absync = true; - bcnt = echan->maxburst; + bcnt = burst; ccnt = sg_dma_len(sg) / (acnt * bcnt); if (ccnt > (SZ_64K - 1)) { dev_err(dev, "Exceeded max SG segment size\n"); @@ -313,13 +312,13 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( if (direction == DMA_MEM_TO_DEV) { src = sg_dma_address(sg); - dst = echan->addr; + dst = dev_addr; src_bidx = acnt; src_cidx = cidx; dst_bidx = 0; dst_cidx = 0; } else { - src = echan->addr; + src = dev_addr; dst = sg_dma_address(sg); src_bidx = 0; src_cidx = 0; From 93d187993b783c68383a884091a600d9ad499ea6 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:17 -0800 Subject: [PATCH 1014/4607] drm/i915: Remove use of gtt_mappable_entries Mappable_end, ie. size is almost always what you want as opposed to the number of entries. Since we already have that information, we can scrap the number of entries and only calculate it when needed. If gtt_start is !0, this will have slightly different behavior. This difference can only occur in DRI1, and exists when we try to kick out the firmware fb. The new code seems like a bugfix to me. The other case where we've changed the behavior is during init we check the mappable region against our current known upper and lower limits (64MB, and 512MB). This now matches the comment, and makes things more convenient after removing gtt_mappable_entries. Also worth noting is the setting of mappable_end is taken out of setup because we do it earlier now in the DRI2 case and therefore need to add that tiny hunk to support the DRI1 IOCTL. v2: Move up mappable end to before legacy AGP init v3: Add the dev_priv inclusion here from previous rebase error in patch 5 Reviewed-by: Rodrigo Vivi (v2) Signed-off-by: Ben Widawsky [danvet: squash in fix for a printk format flag mismatch warning.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- drivers/gpu/drm/i915/i915_dma.c | 8 ++++---- drivers/gpu/drm/i915/i915_gem.c | 2 ++ drivers/gpu/drm/i915/i915_gem_gtt.c | 15 +++++++-------- drivers/gpu/drm/i915/intel_fb.c | 3 +-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 773b23ecc83b..90a6fc506dd5 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -258,7 +258,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data) seq_printf(m, "%u fault mappable objects, %zu bytes\n", count, size); - seq_printf(m, "%zu [%zu] gtt total\n", + seq_printf(m, "%zu [%lu] gtt total\n", dev_priv->gtt.total, dev_priv->gtt.mappable_end - dev_priv->gtt.start); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 468d2a0fc378..3f70178c63ca 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1427,8 +1427,8 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) return; ap->ranges[0].base = dev_priv->gtt.mappable_base; - ap->ranges[0].size = - dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + ap->ranges[0].size = dev_priv->gtt.mappable_end - dev_priv->gtt.start; + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; @@ -1542,7 +1542,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto put_gmch; } - aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + aperture_size = dev_priv->gtt.mappable_end; dev_priv->gtt.mappable = io_mapping_create_wc(dev_priv->gtt.mappable_base, @@ -1699,7 +1699,7 @@ int i915_driver_unload(struct drm_device *dev) if (dev_priv->mm.gtt_mtrr >= 0) { mtrr_del(dev_priv->mm.gtt_mtrr, dev_priv->gtt.mappable_base, - dev_priv->mm.gtt->gtt_mappable_entries * PAGE_SIZE); + dev_priv->gtt.mappable_end); dev_priv->mm.gtt_mtrr = -1; } diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 51fdf16181a7..e4132ffb7609 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -149,6 +149,7 @@ int i915_gem_init_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_init *args = data; if (drm_core_check_feature(dev, DRIVER_MODESET)) @@ -165,6 +166,7 @@ i915_gem_init_ioctl(struct drm_device *dev, void *data, mutex_lock(&dev->struct_mutex); i915_gem_setup_global_gtt(dev, args->gtt_start, args->gtt_end, args->gtt_end); + dev_priv->gtt.mappable_end = args->gtt_end; mutex_unlock(&dev->struct_mutex); return 0; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 0b8930577953..0f0db02c1e56 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -557,7 +557,6 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, } dev_priv->gtt.start = start; - dev_priv->gtt.mappable_end = mappable_end; dev_priv->gtt.total = end - start; /* Clear any non-preallocated blocks */ @@ -596,7 +595,7 @@ void i915_gem_init_global_gtt(struct drm_device *dev) int ret; gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; - mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + mappable_size = dev_priv->gtt.mappable_end; if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { /* PPGTT pdes are stolen from global gtt ptes, so shrink the @@ -692,6 +691,7 @@ int i915_gem_gtt_init(struct drm_device *dev) int ret; dev_priv->gtt.mappable_base = pci_resource_start(dev->pdev, 2); + dev_priv->gtt.mappable_end = pci_resource_len(dev->pdev, 2); /* On modern platforms we need not worry ourself with the legacy * hostbridge query stuff. Skip it entirely @@ -735,14 +735,13 @@ int i915_gem_gtt_init(struct drm_device *dev) else dev_priv->mm.gtt->stolen_size = gen7_get_stolen_size(snb_gmch_ctl); - dev_priv->mm.gtt->gtt_mappable_entries = pci_resource_len(dev->pdev, 2) >> PAGE_SHIFT; /* 64/512MB is the current min/max we actually know of, but this is just a * coarse sanity check. */ - if ((dev_priv->mm.gtt->gtt_mappable_entries >> 8) < 64 || - dev_priv->mm.gtt->gtt_mappable_entries > dev_priv->mm.gtt->gtt_total_entries) { - DRM_ERROR("Unknown GMADR entries (%d)\n", - dev_priv->mm.gtt->gtt_mappable_entries); + if ((dev_priv->gtt.mappable_end < (64<<20) || + (dev_priv->gtt.mappable_end > (512<<20)))) { + DRM_ERROR("Unknown GMADR size (%lx)\n", + dev_priv->gtt.mappable_end); ret = -ENXIO; goto err_out; } @@ -764,7 +763,7 @@ int i915_gem_gtt_init(struct drm_device *dev) /* GMADR is the PCI aperture used by SW to access tiled GFX surfaces in a linear fashion. */ DRM_INFO("Memory usable by graphics device = %dM\n", dev_priv->mm.gtt->gtt_total_entries >> 8); - DRM_DEBUG_DRIVER("GMADR size = %dM\n", dev_priv->mm.gtt->gtt_mappable_entries >> 8); + DRM_DEBUG_DRIVER("GMADR size = %ldM\n", dev_priv->gtt.mappable_end >> 20); DRM_DEBUG_DRIVER("GTT stolen size = %dM\n", dev_priv->mm.gtt->stolen_size >> 20); return 0; diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index ce02af8ca96e..ce5f54498426 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -135,8 +135,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev, goto out_unpin; } info->apertures->ranges[0].base = dev->mode_config.fb_base; - info->apertures->ranges[0].size = - dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + info->apertures->ranges[0].size = dev_priv->gtt.mappable_end; info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset; info->fix.smem_len = size; From 3685a8f38f2c54bb6058c0e66ae0562f8ab84e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 17 Jan 2013 16:31:28 +0200 Subject: [PATCH 1015/4607] drm/i915: Fix RGB color range property for PCH platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RGB color range select bit on the DP/SDVO/HDMI registers disappeared when PCH was introduced, and instead a new PIPECONF bit was added that performs the same function. Add a new INTEL_MODE_LIMITED_COLOR_RANGE private mode flag, and set it in the encoder mode_fixup if limited color range is requested. Set the the PIPECONF bit 13 based on the flag. Experimentation showed that simply toggling the bit while the pipe is active doesn't work. We need to restart the pipe, which luckily already happens. The DP/SDVO/HDMI bit 8 is marked MBZ in the docs, so avoid setting it, although it doesn't seem to do any harm in practice. TODO: - the PIPECONF bit too seems to have disappeared from HSW. Need a volunteer to test if it's just a documentation issue or if it's really gone. If the bit is gone and no easy replacement is found, then I suppose we may need to use the pipe CSC unit to perform the range compression. v2: Use mode private_flags instead of intel_encoder virtual functions v3: Moved the intel_dp color_range handling after bpc check to help later patches Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=46800 Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_display.c | 5 +++++ drivers/gpu/drm/i915/intel_dp.c | 7 ++++++- drivers/gpu/drm/i915/intel_drv.h | 5 +++++ drivers/gpu/drm/i915/intel_hdmi.c | 5 +++++ drivers/gpu/drm/i915/intel_sdvo.c | 5 ++++- 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 4c33bd2bd4e6..2521617b55da 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2650,6 +2650,7 @@ #define PIPECONF_INTERLACED_DBL_ILK (4 << 21) /* ilk/snb only */ #define PIPECONF_PFIT_PF_INTERLACED_DBL_ILK (5 << 21) /* ilk/snb only */ #define PIPECONF_CXSR_DOWNCLOCK (1<<16) +#define PIPECONF_COLOR_RANGE_SELECT (1 << 13) #define PIPECONF_BPC_MASK (0x7 << 5) #define PIPECONF_8BPC (0<<5) #define PIPECONF_10BPC (1<<5) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e4c5067a54d3..b35902e5d925 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5096,6 +5096,11 @@ static void ironlake_set_pipeconf(struct drm_crtc *crtc, else val |= PIPECONF_PROGRESSIVE; + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + val |= PIPECONF_COLOR_RANGE_SELECT; + else + val &= ~PIPECONF_COLOR_RANGE_SELECT; + I915_WRITE(PIPECONF(pipe), val); POSTING_READ(PIPECONF(pipe)); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 5f12eb2d0fb5..d9956278a56e 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -763,6 +763,10 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, return false; bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + + if (intel_dp->color_range) + adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; + mode_rate = intel_dp_link_required(adjusted_mode->clock, bpp); for (clock = 0; clock <= max_clock; clock++) { @@ -967,7 +971,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, else intel_dp->DP |= DP_PLL_FREQ_270MHZ; } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) { - intel_dp->DP |= intel_dp->color_range; + if (!HAS_PCH_SPLIT(dev)) + intel_dp->DP |= intel_dp->color_range; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) intel_dp->DP |= DP_SYNC_HS_HIGH; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 54a034c82061..4df47be84abd 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -109,6 +109,11 @@ * timings in the mode to prevent the crtc fixup from overwriting them. * Currently only lvds needs that. */ #define INTEL_MODE_CRTC_TIMINGS_SET (0x20) +/* + * Set when limited 16-235 (as opposed to full 0-255) RGB color range is + * to be used. + */ +#define INTEL_MODE_LIMITED_COLOR_RANGE (0x40) static inline void intel_mode_set_pixel_multiplier(struct drm_display_mode *mode, diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 6387f9b0df99..f194d756a58c 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -766,6 +766,11 @@ bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + + if (intel_hdmi->color_range) + adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; + return true; } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 153377bed66a..3b8491af1f23 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1064,6 +1064,9 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, multiplier = intel_sdvo_get_pixel_multiplier(adjusted_mode); intel_mode_set_pixel_multiplier(adjusted_mode, multiplier); + if (intel_sdvo->color_range) + adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; + return true; } @@ -1153,7 +1156,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, /* The real mode polarity is set by the SDVO commands, using * struct intel_sdvo_dtd. */ sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH; - if (intel_sdvo->is_hdmi) + if (!HAS_PCH_SPLIT(dev) && intel_sdvo->is_hdmi) sdvox |= intel_sdvo->color_range; if (INTEL_INFO(dev)->gen < 5) sdvox |= SDVO_BORDER_ENABLE; From 55bc60db5988c8366751d3d04dd690698a53412c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 17 Jan 2013 16:31:29 +0200 Subject: [PATCH 1016/4607] drm/i915: Add "Automatic" mode for the "Broadcast RGB" property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new "Automatic" mode to the "Broadcast RGB" range property. When selected the driver automagically selects between full range and limited range output. Based on CEA-861 [1] guidelines, limited range output is selected if the mode is a CEA mode, except 640x480. Otherwise full range output is used. Additionally DVI monitors should most likely default to full range always. As per DP1.2a [2] DisplayPort should always use full range for 18bpp, and otherwise will follow CEA-861 rules. NOTE: The default value for the property will now be "Automatic" so some people may be affected in case they're relying on the current full range default. [1] CEA-861-E - 5.1 Default Encoding Parameters [2] VESA DisplayPort Ver.1.2a - 5.1.1.1 Video Colorimetry v2: Use has_hdmi_sink to check if a HDMI monitor is present v3: Add information about relevant spec chapters Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 4 ++++ drivers/gpu/drm/i915/intel_dp.c | 32 +++++++++++++++++++++---- drivers/gpu/drm/i915/intel_drv.h | 2 ++ drivers/gpu/drm/i915/intel_hdmi.c | 29 +++++++++++++++++++---- drivers/gpu/drm/i915/intel_modes.c | 5 ++-- drivers/gpu/drm/i915/intel_sdvo.c | 38 ++++++++++++++++++++++++------ 6 files changed, 93 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f3f2e5e1393f..0a2a18b8a075 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1811,5 +1811,9 @@ __i915_write(64, q) #define POSTING_READ(reg) (void)I915_READ_NOTRACE(reg) #define POSTING_READ16(reg) (void)I915_READ16_NOTRACE(reg) +/* "Broadcast RGB" property */ +#define INTEL_BROADCAST_RGB_AUTO 0 +#define INTEL_BROADCAST_RGB_FULL 1 +#define INTEL_BROADCAST_RGB_LIMITED 2 #endif diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index d9956278a56e..1492706ed087 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -764,6 +764,18 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + if (intel_dp->color_range_auto) { + /* + * See: + * CEA-861-E - 5.1 Default Encoding Parameters + * VESA DisplayPort Ver.1.2a - 5.1.1.1 Video Colorimetry + */ + if (bpp != 18 && drm_mode_cea_vic(adjusted_mode) > 1) + intel_dp->color_range = DP_COLOR_RANGE_16_235; + else + intel_dp->color_range = 0; + } + if (intel_dp->color_range) adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; @@ -2462,10 +2474,21 @@ intel_dp_set_property(struct drm_connector *connector, } if (property == dev_priv->broadcast_rgb_property) { - if (val == !!intel_dp->color_range) - return 0; - - intel_dp->color_range = val ? DP_COLOR_RANGE_16_235 : 0; + switch (val) { + case INTEL_BROADCAST_RGB_AUTO: + intel_dp->color_range_auto = true; + break; + case INTEL_BROADCAST_RGB_FULL: + intel_dp->color_range_auto = false; + intel_dp->color_range = 0; + break; + case INTEL_BROADCAST_RGB_LIMITED: + intel_dp->color_range_auto = false; + intel_dp->color_range = DP_COLOR_RANGE_16_235; + break; + default: + return -EINVAL; + } goto done; } @@ -2606,6 +2629,7 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect intel_attach_force_audio_property(connector); intel_attach_broadcast_rgb_property(connector); + intel_dp->color_range_auto = true; if (is_edp(intel_dp)) { drm_mode_create_scaling_mode_property(connector->dev); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 4df47be84abd..1a698c6f16e7 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -343,6 +343,7 @@ struct intel_hdmi { u32 sdvox_reg; int ddc_bus; uint32_t color_range; + bool color_range_auto; bool has_hdmi_sink; bool has_audio; enum hdmi_force_audio force_audio; @@ -362,6 +363,7 @@ struct intel_dp { bool has_audio; enum hdmi_force_audio force_audio; uint32_t color_range; + bool color_range_auto; uint8_t link_bw; uint8_t lane_count; uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index f194d756a58c..db67be66639b 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -768,6 +768,15 @@ bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, { struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + if (intel_hdmi->color_range_auto) { + /* See CEA-861-E - 5.1 Default Encoding Parameters */ + if (intel_hdmi->has_hdmi_sink && + drm_mode_cea_vic(adjusted_mode) > 1) + intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235; + else + intel_hdmi->color_range = 0; + } + if (intel_hdmi->color_range) adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; @@ -912,10 +921,21 @@ intel_hdmi_set_property(struct drm_connector *connector, } if (property == dev_priv->broadcast_rgb_property) { - if (val == !!intel_hdmi->color_range) - return 0; - - intel_hdmi->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0; + switch (val) { + case INTEL_BROADCAST_RGB_AUTO: + intel_hdmi->color_range_auto = true; + break; + case INTEL_BROADCAST_RGB_FULL: + intel_hdmi->color_range_auto = false; + intel_hdmi->color_range = 0; + break; + case INTEL_BROADCAST_RGB_LIMITED: + intel_hdmi->color_range_auto = false; + intel_hdmi->color_range = SDVO_COLOR_RANGE_16_235; + break; + default: + return -EINVAL; + } goto done; } @@ -964,6 +984,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c { intel_attach_force_audio_property(connector); intel_attach_broadcast_rgb_property(connector); + intel_hdmi->color_range_auto = true; } void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c index 49249bb97485..0e860f39933d 100644 --- a/drivers/gpu/drm/i915/intel_modes.c +++ b/drivers/gpu/drm/i915/intel_modes.c @@ -100,8 +100,9 @@ intel_attach_force_audio_property(struct drm_connector *connector) } static const struct drm_prop_enum_list broadcast_rgb_names[] = { - { 0, "Full" }, - { 1, "Limited 16:235" }, + { INTEL_BROADCAST_RGB_AUTO, "Automatic" }, + { INTEL_BROADCAST_RGB_FULL, "Full" }, + { INTEL_BROADCAST_RGB_LIMITED, "Limited 16:235" }, }; void diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 3b8491af1f23..3e34a3592e32 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -103,6 +103,7 @@ struct intel_sdvo { * It is only valid when using TMDS encoding and 8 bit per color mode. */ uint32_t color_range; + bool color_range_auto; /** * This is set if we're going to treat the device as TV-out. @@ -1064,6 +1065,15 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder, multiplier = intel_sdvo_get_pixel_multiplier(adjusted_mode); intel_mode_set_pixel_multiplier(adjusted_mode, multiplier); + if (intel_sdvo->color_range_auto) { + /* See CEA-861-E - 5.1 Default Encoding Parameters */ + if (intel_sdvo->has_hdmi_monitor && + drm_mode_cea_vic(adjusted_mode) > 1) + intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235; + else + intel_sdvo->color_range = 0; + } + if (intel_sdvo->color_range) adjusted_mode->private_flags |= INTEL_MODE_LIMITED_COLOR_RANGE; @@ -1900,10 +1910,21 @@ intel_sdvo_set_property(struct drm_connector *connector, } if (property == dev_priv->broadcast_rgb_property) { - if (val == !!intel_sdvo->color_range) - return 0; - - intel_sdvo->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0; + switch (val) { + case INTEL_BROADCAST_RGB_AUTO: + intel_sdvo->color_range_auto = true; + break; + case INTEL_BROADCAST_RGB_FULL: + intel_sdvo->color_range_auto = false; + intel_sdvo->color_range = 0; + break; + case INTEL_BROADCAST_RGB_LIMITED: + intel_sdvo->color_range_auto = false; + intel_sdvo->color_range = SDVO_COLOR_RANGE_16_235; + break; + default: + return -EINVAL; + } goto done; } @@ -2200,13 +2221,16 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector, } static void -intel_sdvo_add_hdmi_properties(struct intel_sdvo_connector *connector) +intel_sdvo_add_hdmi_properties(struct intel_sdvo *intel_sdvo, + struct intel_sdvo_connector *connector) { struct drm_device *dev = connector->base.base.dev; intel_attach_force_audio_property(&connector->base.base); - if (INTEL_INFO(dev)->gen >= 4 && IS_MOBILE(dev)) + if (INTEL_INFO(dev)->gen >= 4 && IS_MOBILE(dev)) { intel_attach_broadcast_rgb_property(&connector->base.base); + intel_sdvo->color_range_auto = true; + } } static bool @@ -2254,7 +2278,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); if (intel_sdvo->is_hdmi) - intel_sdvo_add_hdmi_properties(intel_sdvo_connector); + intel_sdvo_add_hdmi_properties(intel_sdvo, intel_sdvo_connector); return true; } From b1edd6a6ecd436af33f210a5c75e0249466fd200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 17 Jan 2013 16:31:30 +0200 Subject: [PATCH 1017/4607] drm/edid: Add drm_rgb_quant_range_selectable() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drm_rgb_quant_range_selectable() will report whether the monitor claims to support for RGB quantization range selection. The information can be found in the CEA Video capability block. v2: s/quantzation/quantization/ in the comment Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Acked-by: David Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_edid.c | 33 +++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 1 + 2 files changed, 34 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 5a3770fbd770..a3a3b61059ff 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1483,9 +1483,11 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, #define VIDEO_BLOCK 0x02 #define VENDOR_BLOCK 0x03 #define SPEAKER_BLOCK 0x04 +#define VIDEO_CAPABILITY_BLOCK 0x07 #define EDID_BASIC_AUDIO (1 << 6) #define EDID_CEA_YCRCB444 (1 << 5) #define EDID_CEA_YCRCB422 (1 << 4) +#define EDID_CEA_VCDB_QS (1 << 6) /** * Search EDID for CEA extension block. @@ -1901,6 +1903,37 @@ end: } EXPORT_SYMBOL(drm_detect_monitor_audio); +/** + * drm_rgb_quant_range_selectable - is RGB quantization range selectable? + * + * Check whether the monitor reports the RGB quantization range selection + * as supported. The AVI infoframe can then be used to inform the monitor + * which quantization range (full or limited) is used. + */ +bool drm_rgb_quant_range_selectable(struct edid *edid) +{ + u8 *edid_ext; + int i, start, end; + + edid_ext = drm_find_cea_extension(edid); + if (!edid_ext) + return false; + + if (cea_db_offsets(edid_ext, &start, &end)) + return false; + + for_each_cea_db(edid_ext, i, start, end) { + if (cea_db_tag(&edid_ext[i]) == VIDEO_CAPABILITY_BLOCK && + cea_db_payload_len(&edid_ext[i]) == 2) { + DRM_DEBUG_KMS("CEA VCDB 0x%02x\n", edid_ext[i + 2]); + return edid_ext[i + 2] & EDID_CEA_VCDB_QS; + } + } + + return false; +} +EXPORT_SYMBOL(drm_rgb_quant_range_selectable); + /** * drm_add_display_info - pull display info out if present * @edid: EDID data diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 00d78b5161c0..30892dc9376e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1033,6 +1033,7 @@ extern u8 *drm_find_cea_extension(struct edid *edid); extern u8 drm_match_cea_mode(struct drm_display_mode *to_match); extern bool drm_detect_hdmi_monitor(struct edid *edid); extern bool drm_detect_monitor_audio(struct edid *edid); +extern bool drm_rgb_quant_range_selectable(struct edid *edid); extern int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, From abedc077b45eff0b5a8630af8431ad5d59213582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 17 Jan 2013 16:31:31 +0200 Subject: [PATCH 1018/4607] drm/i915: Provide the quantization range in the AVI infoframe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AVI infoframe is able to inform the display whether the source is sending full or limited range RGB data. As per CEA-861 [1] we must first check whether the display reports the quantization range as selectable, and if so we can set the approriate bits in the AVI inforframe. [1] CEA-861-E - 6.4 Format of Version 2 AVI InfoFrame v2: Give the Q bits better names, add spec chapter information Reviewed-by: Paulo Zanoni Signed-off-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 4 ++++ drivers/gpu/drm/i915/intel_hdmi.c | 11 +++++++++++ drivers/gpu/drm/i915/intel_sdvo.c | 16 ++++++++++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1a698c6f16e7..aeff0d1067ad 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -289,6 +289,9 @@ struct cxsr_latency { #define DIP_LEN_AVI 13 #define DIP_AVI_PR_1 0 #define DIP_AVI_PR_2 1 +#define DIP_AVI_RGB_QUANT_RANGE_DEFAULT (0 << 2) +#define DIP_AVI_RGB_QUANT_RANGE_LIMITED (1 << 2) +#define DIP_AVI_RGB_QUANT_RANGE_FULL (2 << 2) #define DIP_TYPE_SPD 0x83 #define DIP_VERSION_SPD 0x1 @@ -347,6 +350,7 @@ struct intel_hdmi { bool has_hdmi_sink; bool has_audio; enum hdmi_force_audio force_audio; + bool rgb_quant_range_selectable; void (*write_infoframe)(struct drm_encoder *encoder, struct dip_infoframe *frame); void (*set_infoframes)(struct drm_encoder *encoder, diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index db67be66639b..d53b73131b0e 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -331,6 +331,7 @@ static void intel_set_infoframe(struct drm_encoder *encoder, static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); struct dip_infoframe avi_if = { .type = DIP_TYPE_AVI, .ver = DIP_VERSION_AVI, @@ -340,6 +341,13 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) avi_if.body.avi.YQ_CN_PR |= DIP_AVI_PR_2; + if (intel_hdmi->rgb_quant_range_selectable) { + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_LIMITED; + else + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL; + } + avi_if.body.avi.VIC = drm_mode_cea_vic(adjusted_mode); intel_set_infoframe(encoder, &avi_if); @@ -825,6 +833,7 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) intel_hdmi->has_hdmi_sink = false; intel_hdmi->has_audio = false; + intel_hdmi->rgb_quant_range_selectable = false; edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus)); @@ -836,6 +845,8 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) intel_hdmi->has_hdmi_sink = drm_detect_hdmi_monitor(edid); intel_hdmi->has_audio = drm_detect_monitor_audio(edid); + intel_hdmi->rgb_quant_range_selectable = + drm_rgb_quant_range_selectable(edid); } kfree(edid); } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 3e34a3592e32..f01063a2323a 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -126,6 +126,7 @@ struct intel_sdvo { bool is_hdmi; bool has_hdmi_monitor; bool has_hdmi_audio; + bool rgb_quant_range_selectable; /** * This is set if we detect output of sdvo device as LVDS and @@ -947,7 +948,8 @@ static bool intel_sdvo_write_infoframe(struct intel_sdvo *intel_sdvo, &tx_rate, 1); } -static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo) +static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo, + const struct drm_display_mode *adjusted_mode) { struct dip_infoframe avi_if = { .type = DIP_TYPE_AVI, @@ -956,6 +958,13 @@ static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo) }; uint8_t sdvo_data[4 + sizeof(avi_if.body.avi)]; + if (intel_sdvo->rgb_quant_range_selectable) { + if (adjusted_mode->private_flags & INTEL_MODE_LIMITED_COLOR_RANGE) + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_LIMITED; + else + avi_if.body.avi.ITC_EC_Q_SC |= DIP_AVI_RGB_QUANT_RANGE_FULL; + } + intel_dip_infoframe_csum(&avi_if); /* sdvo spec says that the ecc is handled by the hw, and it looks like @@ -1134,7 +1143,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI); intel_sdvo_set_colorimetry(intel_sdvo, SDVO_COLORIMETRY_RGB256); - intel_sdvo_set_avi_infoframe(intel_sdvo); + intel_sdvo_set_avi_infoframe(intel_sdvo, adjusted_mode); } else intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_DVI); @@ -1526,6 +1535,8 @@ intel_sdvo_tmds_sink_detect(struct drm_connector *connector) if (intel_sdvo->is_hdmi) { intel_sdvo->has_hdmi_monitor = drm_detect_hdmi_monitor(edid); intel_sdvo->has_hdmi_audio = drm_detect_monitor_audio(edid); + intel_sdvo->rgb_quant_range_selectable = + drm_rgb_quant_range_selectable(edid); } } else status = connector_status_disconnected; @@ -1577,6 +1588,7 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) intel_sdvo->has_hdmi_monitor = false; intel_sdvo->has_hdmi_audio = false; + intel_sdvo->rgb_quant_range_selectable = false; if ((intel_sdvo_connector->output_flag & response) == 0) ret = connector_status_disconnected; From a81cc00c11ab6816fbcb7dd99a60b50e71765d25 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 18 Jan 2013 12:30:31 -0800 Subject: [PATCH 1019/4607] drm/i915: Cut out the infamous ILK w/a from AGP layer And, move it to where the rest of the logic is. There is some slight functionality changes. There was extra paranoid checks in AGP code making sure we never do idle maps on gen2 parts. That was not duplicated as the simple PCI id check should do the right thing. v2: use IS_GEN5 && IS_MOBILE check instead. For now, this is the same as IS_IRONLAKE_M but is more future proof. The workaround docs hint that more than one platform may be effected, but we've never seen such a platform in the wild. (Rodrigo, Daniel) Reviewed-by: Rodrigo Vivi (v1) Cc: Dave Airlie Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 27 --------------------------- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_gem_gtt.c | 24 +++++++++++++++++++++--- include/drm/intel-gtt.h | 2 -- 4 files changed, 23 insertions(+), 32 deletions(-) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index c8d9dcb15db0..12c31026eb56 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -840,9 +840,6 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, { int ret = -EINVAL; - if (intel_private.base.do_idle_maps) - return -ENODEV; - if (intel_private.clear_fake_agp) { int start = intel_private.base.stolen_size / PAGE_SIZE; int end = intel_private.base.gtt_mappable_entries; @@ -907,9 +904,6 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem, if (mem->page_count == 0) return 0; - if (intel_private.base.do_idle_maps) - return -ENODEV; - intel_gtt_clear_range(pg_start, mem->page_count); if (intel_private.base.needs_dmar) { @@ -1069,24 +1063,6 @@ static void i965_write_entry(dma_addr_t addr, writel(addr | pte_flags, intel_private.gtt + entry); } -/* Certain Gen5 chipsets require require idling the GPU before - * unmapping anything from the GTT when VT-d is enabled. - */ -static inline int needs_idle_maps(void) -{ -#ifdef CONFIG_INTEL_IOMMU - const unsigned short gpu_devid = intel_private.pcidev->device; - - /* Query intel_iommu to see if we need the workaround. Presumably that - * was loaded first. - */ - if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || - gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) && - intel_iommu_gfx_mapped) - return 1; -#endif - return 0; -} static int i9xx_setup(void) { @@ -1115,9 +1091,6 @@ static int i9xx_setup(void) break; } - if (needs_idle_maps()) - intel_private.base.do_idle_maps = 1; - intel_i9xx_setup_flush(); return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0a2a18b8a075..1fe802f4df13 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -381,6 +381,8 @@ struct i915_gtt { /** "Graphics Stolen Memory" holds the global PTEs */ void __iomem *gsm; + + bool do_idle_maps; }; #define I915_PPGTT_PD_ENTRIES 512 diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 0f0db02c1e56..84c521fd11a2 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -338,11 +338,27 @@ void i915_gem_init_ppgtt(struct drm_device *dev) } } +extern int intel_iommu_gfx_mapped; +/* Certain Gen5 chipsets require require idling the GPU before + * unmapping anything from the GTT when VT-d is enabled. + */ +static inline bool needs_idle_maps(struct drm_device *dev) +{ +#ifdef CONFIG_INTEL_IOMMU + /* Query intel_iommu to see if we need the workaround. Presumably that + * was loaded first. + */ + if (IS_GEN5(dev) && IS_MOBILE(dev) && intel_iommu_gfx_mapped) + return true; +#endif + return false; +} + static bool do_idling(struct drm_i915_private *dev_priv) { bool ret = dev_priv->mm.interruptible; - if (unlikely(dev_priv->mm.gtt->do_idle_maps)) { + if (unlikely(dev_priv->gtt.do_idle_maps)) { dev_priv->mm.interruptible = false; if (i915_gpu_idle(dev_priv->dev)) { DRM_ERROR("Couldn't idle GPU\n"); @@ -356,11 +372,10 @@ static bool do_idling(struct drm_i915_private *dev_priv) static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible) { - if (unlikely(dev_priv->mm.gtt->do_idle_maps)) + if (unlikely(dev_priv->gtt.do_idle_maps)) dev_priv->mm.interruptible = interruptible; } - static void i915_ggtt_clear_range(struct drm_device *dev, unsigned first_entry, unsigned num_entries) @@ -709,6 +724,9 @@ int i915_gem_gtt_init(struct drm_device *dev) intel_gmch_remove(); return -ENODEV; } + + dev_priv->gtt.do_idle_maps = needs_idle_maps(dev); + return 0; } diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 3e3a166a2690..1747aa095f41 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -13,8 +13,6 @@ struct intel_gtt { unsigned int gtt_mappable_entries; /* Whether i915 needs to use the dmar apis or not. */ unsigned int needs_dmar : 1; - /* Whether we idle the gpu before mapping/unmapping */ - unsigned int do_idle_maps : 1; /* Share the scratch page dma with ppgtts. */ dma_addr_t scratch_page_dma; struct page *scratch_page; From 9c61a32d31a55c8c6e590d83ae5645e14fde09f2 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 18 Jan 2013 12:30:32 -0800 Subject: [PATCH 1020/4607] drm/i915: Remove scratch page from shared We already had a mapping in both (minus the phys_addr in AGP). Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 9 +++++---- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_gem_gtt.c | 16 ++++++++-------- include/drm/intel-gtt.h | 3 --- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 12c31026eb56..7fcee5c2e986 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -75,6 +75,7 @@ static struct _intel_private { struct resource ifp_resource; int resource_valid; struct page *scratch_page; + phys_addr_t scratch_page_dma; int refcount; } intel_private; @@ -297,9 +298,9 @@ static int intel_gtt_setup_scratch_page(void) if (pci_dma_mapping_error(intel_private.pcidev, dma_addr)) return -EINVAL; - intel_private.base.scratch_page_dma = dma_addr; + intel_private.scratch_page_dma = dma_addr; } else - intel_private.base.scratch_page_dma = page_to_phys(page); + intel_private.scratch_page_dma = page_to_phys(page); intel_private.scratch_page = page; @@ -546,7 +547,7 @@ static unsigned int intel_gtt_mappable_entries(void) static void intel_gtt_teardown_scratch_page(void) { set_pages_wb(intel_private.scratch_page, 1); - pci_unmap_page(intel_private.pcidev, intel_private.base.scratch_page_dma, + pci_unmap_page(intel_private.pcidev, intel_private.scratch_page_dma, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); put_page(intel_private.scratch_page); __free_page(intel_private.scratch_page); @@ -891,7 +892,7 @@ void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries) unsigned int i; for (i = first_entry; i < (first_entry + num_entries); i++) { - intel_private.driver->write_entry(intel_private.base.scratch_page_dma, + intel_private.driver->write_entry(intel_private.scratch_page_dma, i, 0); } readl(intel_private.gtt+i-1); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1fe802f4df13..3189034deeb8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -383,6 +383,8 @@ struct i915_gtt { void __iomem *gsm; bool do_idle_maps; + dma_addr_t scratch_page_dma; + struct page *scratch_page; }; #define I915_PPGTT_PD_ENTRIES 512 diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 84c521fd11a2..3c1f66ebc245 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -162,7 +162,7 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev) } } - ppgtt->scratch_page_dma_addr = dev_priv->mm.gtt->scratch_page_dma; + ppgtt->scratch_page_dma_addr = dev_priv->gtt.scratch_page_dma; i915_ppgtt_clear_range(ppgtt, 0, ppgtt->num_pd_entries*I915_PPGTT_PT_ENTRIES); @@ -396,7 +396,7 @@ static void i915_ggtt_clear_range(struct drm_device *dev, first_entry, num_entries, max_entries)) num_entries = max_entries; - scratch_pte = pte_encode(dev, dev_priv->mm.gtt->scratch_page_dma, I915_CACHE_LLC); + scratch_pte = pte_encode(dev, dev_priv->gtt.scratch_page_dma, I915_CACHE_LLC); for (i = 0; i < num_entries; i++) iowrite32(scratch_pte, >t_base[i]); readl(gtt_base); @@ -659,8 +659,8 @@ static int setup_scratch_page(struct drm_device *dev) #else dma_addr = page_to_phys(page); #endif - dev_priv->mm.gtt->scratch_page = page; - dev_priv->mm.gtt->scratch_page_dma = dma_addr; + dev_priv->gtt.scratch_page = page; + dev_priv->gtt.scratch_page_dma = dma_addr; return 0; } @@ -668,11 +668,11 @@ static int setup_scratch_page(struct drm_device *dev) static void teardown_scratch_page(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - set_pages_wb(dev_priv->mm.gtt->scratch_page, 1); - pci_unmap_page(dev->pdev, dev_priv->mm.gtt->scratch_page_dma, + set_pages_wb(dev_priv->gtt.scratch_page, 1); + pci_unmap_page(dev->pdev, dev_priv->gtt.scratch_page_dma, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); - put_page(dev_priv->mm.gtt->scratch_page); - __free_page(dev_priv->mm.gtt->scratch_page); + put_page(dev_priv->gtt.scratch_page); + __free_page(dev_priv->gtt.scratch_page); } static inline unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl) diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 1747aa095f41..c787ee4c4c07 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -13,9 +13,6 @@ struct intel_gtt { unsigned int gtt_mappable_entries; /* Whether i915 needs to use the dmar apis or not. */ unsigned int needs_dmar : 1; - /* Share the scratch page dma with ppgtts. */ - dma_addr_t scratch_page_dma; - struct page *scratch_page; /* needed for ioremap in drm/i915 */ phys_addr_t gma_bus_addr; } *intel_gtt_get(void); From 8d2e630899165d413ae8a2adc36846ac0b71bada Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 18 Jan 2013 12:30:33 -0800 Subject: [PATCH 1021/4607] drm/i915: Needs_dmar, not The reasoning behind our code taking two paths depending upon whether or not we may have been configured for IOMMU isn't clear to me. It should always be safe to use the pci mapping functions as they are designed to abstract the decision we were handling in i915. Aside from simpler code, removing another member for the intel_gtt struct is a nice motivation. I ran this by Chris, and he wasn't concerned about the extra kzalloc, and memory references vs. page_to_phys calculation in the case without IOMMU. v2: Update commit message v3: Remove needs_dmar addition from Zhenyu upstream This reverts (and then other stuff) commit 20652097dadd9a7fb4d652f25466299974bc78f9 Author: Zhenyu Wang Date: Thu Dec 13 23:47:47 2012 +0800 drm/i915: Fix missed needs_dmar setting Reviewed-by: Rodrigo Vivi (v2) Cc: Zhenyu Wang Signed-off-by: Ben Widawsky [danvet: Squash in follow-up fix to remove the bogus hunk which deleted the dma_mask configuration for gen6+.] Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 10 ++++--- drivers/gpu/drm/i915/i915_gem_gtt.c | 45 ++++++++++------------------- include/drm/intel-gtt.h | 2 -- 3 files changed, 22 insertions(+), 35 deletions(-) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 7fcee5c2e986..b45241479a56 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -77,6 +77,8 @@ static struct _intel_private { struct page *scratch_page; phys_addr_t scratch_page_dma; int refcount; + /* Whether i915 needs to use the dmar apis or not. */ + unsigned int needs_dmar : 1; } intel_private; #define INTEL_GTT_GEN intel_private.driver->gen @@ -292,7 +294,7 @@ static int intel_gtt_setup_scratch_page(void) get_page(page); set_pages_uc(page, 1); - if (intel_private.base.needs_dmar) { + if (intel_private.needs_dmar) { dma_addr = pci_map_page(intel_private.pcidev, page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); if (pci_dma_mapping_error(intel_private.pcidev, dma_addr)) @@ -608,7 +610,7 @@ static int intel_gtt_init(void) intel_private.base.stolen_size = intel_gtt_stolen_size(); - intel_private.base.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2; + intel_private.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2; ret = intel_gtt_setup_scratch_page(); if (ret != 0) { @@ -866,7 +868,7 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, if (!mem->is_flushed) global_cache_flush(); - if (intel_private.base.needs_dmar) { + if (intel_private.needs_dmar) { struct sg_table st; ret = intel_gtt_map_memory(mem->pages, mem->page_count, &st); @@ -907,7 +909,7 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem, intel_gtt_clear_range(pg_start, mem->page_count); - if (intel_private.base.needs_dmar) { + if (intel_private.needs_dmar) { intel_gtt_unmap_memory(mem->sg_list, mem->num_sg); mem->sg_list = NULL; mem->num_sg = 0; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 3c1f66ebc245..a0ba4a9e53c7 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -138,28 +138,23 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev) goto err_pt_alloc; } - if (dev_priv->mm.gtt->needs_dmar) { - ppgtt->pt_dma_addr = kzalloc(sizeof(dma_addr_t) - *ppgtt->num_pd_entries, - GFP_KERNEL); - if (!ppgtt->pt_dma_addr) - goto err_pt_alloc; + ppgtt->pt_dma_addr = kzalloc(sizeof(dma_addr_t) *ppgtt->num_pd_entries, + GFP_KERNEL); + if (!ppgtt->pt_dma_addr) + goto err_pt_alloc; - for (i = 0; i < ppgtt->num_pd_entries; i++) { - dma_addr_t pt_addr; + for (i = 0; i < ppgtt->num_pd_entries; i++) { + dma_addr_t pt_addr; - pt_addr = pci_map_page(dev->pdev, ppgtt->pt_pages[i], - 0, 4096, - PCI_DMA_BIDIRECTIONAL); + pt_addr = pci_map_page(dev->pdev, ppgtt->pt_pages[i], 0, 4096, + PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(dev->pdev, - pt_addr)) { - ret = -EIO; - goto err_pd_pin; + if (pci_dma_mapping_error(dev->pdev, pt_addr)) { + ret = -EIO; + goto err_pd_pin; - } - ppgtt->pt_dma_addr[i] = pt_addr; } + ppgtt->pt_dma_addr[i] = pt_addr; } ppgtt->scratch_page_dma_addr = dev_priv->gtt.scratch_page_dma; @@ -294,11 +289,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; - if (dev_priv->mm.gtt->needs_dmar) - pt_addr = ppgtt->pt_dma_addr[i]; - else - pt_addr = page_to_phys(ppgtt->pt_pages[i]); - + pt_addr = ppgtt->pt_dma_addr[i]; pd_entry = GEN6_PDE_ADDR_ENCODE(pt_addr); pd_entry |= GEN6_PDE_VALID; @@ -730,16 +721,12 @@ int i915_gem_gtt_init(struct drm_device *dev) return 0; } - dev_priv->mm.gtt = kzalloc(sizeof(*dev_priv->mm.gtt), GFP_KERNEL); - if (!dev_priv->mm.gtt) - return -ENOMEM; - if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(40))) pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(40)); -#ifdef CONFIG_INTEL_IOMMU - dev_priv->mm.gtt->needs_dmar = 1; -#endif + dev_priv->mm.gtt = kzalloc(sizeof(*dev_priv->mm.gtt), GFP_KERNEL); + if (!dev_priv->mm.gtt) + return -ENOMEM; /* For GEN6+ the PTEs for the ggtt live at 2MB + BAR0 */ gtt_bus_addr = pci_resource_start(dev->pdev, 0) + (2<<20); diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index c787ee4c4c07..984105cddc57 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -11,8 +11,6 @@ struct intel_gtt { /* Part of the gtt that is mappable by the cpu, for those chips where * this is not the full gtt. */ unsigned int gtt_mappable_entries; - /* Whether i915 needs to use the dmar apis or not. */ - unsigned int needs_dmar : 1; /* needed for ioremap in drm/i915 */ phys_addr_t gma_bus_addr; } *intel_gtt_get(void); From e5c653777986b40e2986d2c918847fddbcba3a34 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 18 Jan 2013 12:30:34 -0800 Subject: [PATCH 1022/4607] agp/intel: Add gma_bus_addr It is no longer used in the i915 code, so isolate it from the shared struct. This was originally part of: commit 0e275518f325418d559c05327775bff894b237f7 Author: Ben Widawsky Date: Mon Jan 14 13:35:33 2013 -0800 agp/intel: decouple more of the agp-i915 sharing Reviewed-by: Rodrigo Vivi Signed-off-by: Ben Widawsky That commit had some other hunks which can't be used due to issues Daniel found in previous commits. Signed-off-by: Ben Widawsky [danvet: drop squash notice from the commit since it's imo ok to keep this one separate.] Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 5 +++-- include/drm/intel-gtt.h | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index b45241479a56..ff5f3483e8ea 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -79,6 +79,7 @@ static struct _intel_private { int refcount; /* Whether i915 needs to use the dmar apis or not. */ unsigned int needs_dmar : 1; + phys_addr_t gma_bus_addr; } intel_private; #define INTEL_GTT_GEN intel_private.driver->gen @@ -625,7 +626,7 @@ static int intel_gtt_init(void) pci_read_config_dword(intel_private.pcidev, I915_GMADDR, &gma_addr); - intel_private.base.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); + intel_private.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); return 0; } @@ -781,7 +782,7 @@ static int intel_fake_agp_configure(void) return -EIO; intel_private.clear_fake_agp = true; - agp_bridge->gart_bus_addr = intel_private.base.gma_bus_addr; + agp_bridge->gart_bus_addr = intel_private.gma_bus_addr; return 0; } diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 984105cddc57..769b6c79097e 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -11,8 +11,6 @@ struct intel_gtt { /* Part of the gtt that is mappable by the cpu, for those chips where * this is not the full gtt. */ unsigned int gtt_mappable_entries; - /* needed for ioremap in drm/i915 */ - phys_addr_t gma_bus_addr; } *intel_gtt_get(void); int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, From 4b5aed62121eddfc47fd8f2739ca6b802b97390e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 14 Nov 2012 17:14:03 +0100 Subject: [PATCH 1023/4607] drm/i915: move dev_priv->mm out of line Tha one is really big, since it contains tons of comments explaining how things work. Which is nice ;-) Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 202 ++++++++++++++++---------------- 1 file changed, 102 insertions(+), 100 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3189034deeb8..ea3226852ea8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -665,6 +665,107 @@ struct intel_l3_parity { struct work_struct error_work; }; +struct i915_gem_mm { + /** Bridge to intel-gtt-ko */ + struct intel_gtt *gtt; + /** Memory allocator for GTT stolen memory */ + struct drm_mm stolen; + /** Memory allocator for GTT */ + struct drm_mm gtt_space; + /** List of all objects in gtt_space. Used to restore gtt + * mappings on resume */ + struct list_head bound_list; + /** + * List of objects which are not bound to the GTT (thus + * are idle and not used by the GPU) but still have + * (presumably uncached) pages still attached. + */ + struct list_head unbound_list; + + /** Usable portion of the GTT for GEM */ + unsigned long stolen_base; /* limited to low memory (32-bit) */ + + int gtt_mtrr; + + /** PPGTT used for aliasing the PPGTT with the GTT */ + struct i915_hw_ppgtt *aliasing_ppgtt; + + struct shrinker inactive_shrinker; + bool shrinker_no_lock_stealing; + + /** + * List of objects currently involved in rendering. + * + * Includes buffers having the contents of their GPU caches + * flushed, not necessarily primitives. last_rendering_seqno + * represents when the rendering involved will be completed. + * + * A reference is held on the buffer while on this list. + */ + struct list_head active_list; + + /** + * LRU list of objects which are not in the ringbuffer and + * are ready to unbind, but are still in the GTT. + * + * last_rendering_seqno is 0 while an object is in this list. + * + * A reference is not held on the buffer while on this list, + * as merely being GTT-bound shouldn't prevent its being + * freed, and we'll pull it off the list in the free path. + */ + struct list_head inactive_list; + + /** LRU list of objects with fence regs on them. */ + struct list_head fence_list; + + /** + * We leave the user IRQ off as much as possible, + * but this means that requests will finish and never + * be retired once the system goes idle. Set a timer to + * fire periodically while the ring is running. When it + * fires, go retire requests. + */ + struct delayed_work retire_work; + + /** + * Are we in a non-interruptible section of code like + * modesetting? + */ + bool interruptible; + + /** + * Flag if the X Server, and thus DRM, is not currently in + * control of the device. + * + * This is set between LeaveVT and EnterVT. It needs to be + * replaced with a semaphore. It also needs to be + * transitioned away from for kernel modesetting. + */ + int suspended; + + /** + * Flag if the hardware appears to be wedged. + * + * This is set when attempts to idle the device timeout. + * It prevents command submission from occurring and makes + * every pending request fail + */ + atomic_t wedged; + + /** Bit 6 swizzling required for X tiling */ + uint32_t bit_6_swizzle_x; + /** Bit 6 swizzling required for Y tiling */ + uint32_t bit_6_swizzle_y; + + /* storage for physical objects */ + struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; + + /* accounting, useful for userland debugging */ + size_t object_memory; + u32 object_count; +}; + typedef struct drm_i915_private { struct drm_device *dev; struct kmem_cache *slab; @@ -806,106 +907,7 @@ typedef struct drm_i915_private { struct i915_gtt gtt; - struct { - /** Bridge to intel-gtt-ko */ - struct intel_gtt *gtt; - /** Memory allocator for GTT stolen memory */ - struct drm_mm stolen; - /** Memory allocator for GTT */ - struct drm_mm gtt_space; - /** List of all objects in gtt_space. Used to restore gtt - * mappings on resume */ - struct list_head bound_list; - /** - * List of objects which are not bound to the GTT (thus - * are idle and not used by the GPU) but still have - * (presumably uncached) pages still attached. - */ - struct list_head unbound_list; - - /** Usable portion of the GTT for GEM */ - unsigned long stolen_base; /* limited to low memory (32-bit) */ - - int gtt_mtrr; - - /** PPGTT used for aliasing the PPGTT with the GTT */ - struct i915_hw_ppgtt *aliasing_ppgtt; - - struct shrinker inactive_shrinker; - bool shrinker_no_lock_stealing; - - /** - * List of objects currently involved in rendering. - * - * Includes buffers having the contents of their GPU caches - * flushed, not necessarily primitives. last_rendering_seqno - * represents when the rendering involved will be completed. - * - * A reference is held on the buffer while on this list. - */ - struct list_head active_list; - - /** - * LRU list of objects which are not in the ringbuffer and - * are ready to unbind, but are still in the GTT. - * - * last_rendering_seqno is 0 while an object is in this list. - * - * A reference is not held on the buffer while on this list, - * as merely being GTT-bound shouldn't prevent its being - * freed, and we'll pull it off the list in the free path. - */ - struct list_head inactive_list; - - /** LRU list of objects with fence regs on them. */ - struct list_head fence_list; - - /** - * We leave the user IRQ off as much as possible, - * but this means that requests will finish and never - * be retired once the system goes idle. Set a timer to - * fire periodically while the ring is running. When it - * fires, go retire requests. - */ - struct delayed_work retire_work; - - /** - * Are we in a non-interruptible section of code like - * modesetting? - */ - bool interruptible; - - /** - * Flag if the X Server, and thus DRM, is not currently in - * control of the device. - * - * This is set between LeaveVT and EnterVT. It needs to be - * replaced with a semaphore. It also needs to be - * transitioned away from for kernel modesetting. - */ - int suspended; - - /** - * Flag if the hardware appears to be wedged. - * - * This is set when attempts to idle the device timeout. - * It prevents command submission from occurring and makes - * every pending request fail - */ - atomic_t wedged; - - /** Bit 6 swizzling required for X tiling */ - uint32_t bit_6_swizzle_x; - /** Bit 6 swizzling required for Y tiling */ - uint32_t bit_6_swizzle_y; - - /* storage for physical objects */ - struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; - - /* accounting, useful for userland debugging */ - size_t object_memory; - u32 object_count; - } mm; + struct i915_gem_mm mm; /* Kernel Modesetting */ From 99584db33ba4f864777e2cfef5329ed1bf13f714 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 14 Nov 2012 17:14:04 +0100 Subject: [PATCH 1024/4607] drm/i915: extract hangcheck/reset/error_state state into substruct This has been sprinkled all over the place in dev_priv. I think it'd be good to also move all the code into a separate file like i915_gem_error.c, but that's for another patch. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 10 ++--- drivers/gpu/drm/i915/i915_dma.c | 6 +-- drivers/gpu/drm/i915/i915_drv.c | 8 ++-- drivers/gpu/drm/i915/i915_drv.h | 39 +++++++++------- drivers/gpu/drm/i915/i915_gem.c | 10 ++--- drivers/gpu/drm/i915/i915_irq.c | 59 ++++++++++++++----------- drivers/gpu/drm/i915/intel_ringbuffer.c | 2 +- 7 files changed, 73 insertions(+), 61 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 90a6fc506dd5..3b1bf4e70d94 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -814,11 +814,11 @@ static int i915_error_state_open(struct inode *inode, struct file *file) error_priv->dev = dev; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error_priv->error = dev_priv->first_error; + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error_priv->error = dev_priv->gpu_error.first_error; if (error_priv->error) kref_get(&error_priv->error->ref); - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); return single_open(file, i915_error_state, error_priv); } @@ -1727,7 +1727,7 @@ i915_ring_stop_read(struct file *filp, int len; len = snprintf(buf, sizeof(buf), - "0x%08x\n", dev_priv->stop_rings); + "0x%08x\n", dev_priv->gpu_error.stop_rings); if (len > sizeof(buf)) len = sizeof(buf); @@ -1763,7 +1763,7 @@ i915_ring_stop_write(struct file *filp, if (ret) return ret; - dev_priv->stop_rings = val; + dev_priv->gpu_error.stop_rings = val; mutex_unlock(&dev->struct_mutex); return cnt; diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3f70178c63ca..11c7aa80e29b 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1605,7 +1605,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) pci_enable_msi(dev->pdev); spin_lock_init(&dev_priv->irq_lock); - spin_lock_init(&dev_priv->error_lock); + spin_lock_init(&dev_priv->gpu_error.lock); spin_lock_init(&dev_priv->rps.lock); mutex_init(&dev_priv->dpio_lock); @@ -1725,8 +1725,8 @@ int i915_driver_unload(struct drm_device *dev) } /* Free error state after interrupts are fully disabled. */ - del_timer_sync(&dev_priv->hangcheck_timer); - cancel_work_sync(&dev_priv->error_work); + del_timer_sync(&dev_priv->gpu_error.hangcheck_timer); + cancel_work_sync(&dev_priv->gpu_error.work); i915_destroy_error_state(dev); if (dev->pdev->msi_enabled) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index c8cbc32fe8db..3ff8e73f4341 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -779,9 +779,9 @@ int intel_gpu_reset(struct drm_device *dev) } /* Also reset the gpu hangman. */ - if (dev_priv->stop_rings) { + if (dev_priv->gpu_error.stop_rings) { DRM_DEBUG("Simulated gpu hang, resetting stop_rings\n"); - dev_priv->stop_rings = 0; + dev_priv->gpu_error.stop_rings = 0; if (ret == -ENODEV) { DRM_ERROR("Reset not implemented, but ignoring " "error for simulated gpu hangs\n"); @@ -820,12 +820,12 @@ int i915_reset(struct drm_device *dev) i915_gem_reset(dev); ret = -ENODEV; - if (get_seconds() - dev_priv->last_gpu_reset < 5) + if (get_seconds() - dev_priv->gpu_error.last_reset < 5) DRM_ERROR("GPU hanging too fast, declaring wedged!\n"); else ret = intel_gpu_reset(dev); - dev_priv->last_gpu_reset = get_seconds(); + dev_priv->gpu_error.last_reset = get_seconds(); if (ret) { DRM_ERROR("Failed to reset chip.\n"); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ea3226852ea8..dfe0e747c4f7 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -766,6 +766,28 @@ struct i915_gem_mm { u32 object_count; }; +struct i915_gpu_error { + /* For hangcheck timer */ +#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ +#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD) + struct timer_list hangcheck_timer; + int hangcheck_count; + uint32_t last_acthd[I915_NUM_RINGS]; + uint32_t prev_instdone[I915_NUM_INSTDONE_REG]; + + /* For reset and error_state handling. */ + spinlock_t lock; + /* Protected by the above dev->gpu_error.lock. */ + struct drm_i915_error_state *first_error; + struct work_struct work; + struct completion completion; + + unsigned long last_reset; + + /* For gpu hang simulation. */ + unsigned int stop_rings; +}; + typedef struct drm_i915_private { struct drm_device *dev; struct kmem_cache *slab; @@ -829,16 +851,6 @@ typedef struct drm_i915_private { int num_pipe; int num_pch_pll; - /* For hangcheck timer */ -#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ -#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD) - struct timer_list hangcheck_timer; - int hangcheck_count; - uint32_t last_acthd[I915_NUM_RINGS]; - uint32_t prev_instdone[I915_NUM_INSTDONE_REG]; - - unsigned int stop_rings; - unsigned long cfb_size; unsigned int cfb_fb; enum plane cfb_plane; @@ -886,11 +898,6 @@ typedef struct drm_i915_private { unsigned int fsb_freq, mem_freq, is_ddr3; - spinlock_t error_lock; - /* Protected by dev->error_lock. */ - struct drm_i915_error_state *first_error; - struct work_struct error_work; - struct completion error_completion; struct workqueue_struct *wq; /* Display functions */ @@ -949,7 +956,7 @@ typedef struct drm_i915_private { struct drm_mm_node *compressed_fb; struct drm_mm_node *compressed_llb; - unsigned long last_gpu_reset; + struct i915_gpu_error gpu_error; /* list of fbdev register on this device */ struct intel_fbdev *fbdev; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e4132ffb7609..95e022e7a26e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -90,7 +90,7 @@ static int i915_gem_wait_for_error(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct completion *x = &dev_priv->error_completion; + struct completion *x = &dev_priv->gpu_error.completion; unsigned long flags; int ret; @@ -943,7 +943,7 @@ i915_gem_check_wedge(struct drm_i915_private *dev_priv, bool interruptible) { if (atomic_read(&dev_priv->mm.wedged)) { - struct completion *x = &dev_priv->error_completion; + struct completion *x = &dev_priv->gpu_error.completion; bool recovery_complete; unsigned long flags; @@ -2045,7 +2045,7 @@ i915_add_request(struct intel_ring_buffer *ring, if (!dev_priv->mm.suspended) { if (i915_enable_hangcheck) { - mod_timer(&dev_priv->hangcheck_timer, + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } if (was_empty) { @@ -3803,7 +3803,7 @@ i915_gem_idle(struct drm_device *dev) * And not confound mm.suspended! */ dev_priv->mm.suspended = 1; - del_timer_sync(&dev_priv->hangcheck_timer); + del_timer_sync(&dev_priv->gpu_error.hangcheck_timer); i915_kernel_lost_context(dev); i915_gem_cleanup_ringbuffer(dev); @@ -4064,7 +4064,7 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list); INIT_DELAYED_WORK(&dev_priv->mm.retire_work, i915_gem_retire_work_handler); - init_completion(&dev_priv->error_completion); + init_completion(&dev_priv->gpu_error.completion); /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ if (IS_GEN3(dev)) { diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index cc49a6ddc052..c768ebdf8a27 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -356,8 +356,8 @@ static void notify_ring(struct drm_device *dev, wake_up_all(&ring->irq_queue); if (i915_enable_hangcheck) { - dev_priv->hangcheck_count = 0; - mod_timer(&dev_priv->hangcheck_timer, + dev_priv->gpu_error.hangcheck_count = 0; + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } } @@ -863,7 +863,7 @@ done: static void i915_error_work_func(struct work_struct *work) { drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, - error_work); + gpu_error.work); struct drm_device *dev = dev_priv->dev; char *error_event[] = { "ERROR=1", NULL }; char *reset_event[] = { "RESET=1", NULL }; @@ -878,7 +878,7 @@ static void i915_error_work_func(struct work_struct *work) atomic_set(&dev_priv->mm.wedged, 0); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); } - complete_all(&dev_priv->error_completion); + complete_all(&dev_priv->gpu_error.completion); } } @@ -1255,9 +1255,9 @@ static void i915_capture_error_state(struct drm_device *dev) unsigned long flags; int i, pipe; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error = dev_priv->first_error; - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error = dev_priv->gpu_error.first_error; + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) return; @@ -1341,12 +1341,12 @@ static void i915_capture_error_state(struct drm_device *dev) error->overlay = intel_overlay_capture_error_state(dev); error->display = intel_display_capture_error_state(dev); - spin_lock_irqsave(&dev_priv->error_lock, flags); - if (dev_priv->first_error == NULL) { - dev_priv->first_error = error; + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + if (dev_priv->gpu_error.first_error == NULL) { + dev_priv->gpu_error.first_error = error; error = NULL; } - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) i915_error_state_free(&error->ref); @@ -1358,10 +1358,10 @@ void i915_destroy_error_state(struct drm_device *dev) struct drm_i915_error_state *error; unsigned long flags; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error = dev_priv->first_error; - dev_priv->first_error = NULL; - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error = dev_priv->gpu_error.first_error; + dev_priv->gpu_error.first_error = NULL; + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) kref_put(&error->ref, i915_error_state_free); @@ -1482,7 +1482,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged) i915_report_and_clear_eir(dev); if (wedged) { - INIT_COMPLETION(dev_priv->error_completion); + INIT_COMPLETION(dev_priv->gpu_error.completion); atomic_set(&dev_priv->mm.wedged, 1); /* @@ -1492,7 +1492,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged) wake_up_all(&ring->irq_queue); } - queue_work(dev_priv->wq, &dev_priv->error_work); + queue_work(dev_priv->wq, &dev_priv->gpu_error.work); } static void i915_pageflip_stall_check(struct drm_device *dev, int pipe) @@ -1723,7 +1723,7 @@ static bool i915_hangcheck_hung(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - if (dev_priv->hangcheck_count++ > 1) { + if (dev_priv->gpu_error.hangcheck_count++ > 1) { bool hung = true; DRM_ERROR("Hangcheck timer elapsed... GPU hung\n"); @@ -1782,25 +1782,29 @@ void i915_hangcheck_elapsed(unsigned long data) goto repeat; } - dev_priv->hangcheck_count = 0; + dev_priv->gpu_error.hangcheck_count = 0; return; } i915_get_extra_instdone(dev, instdone); - if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 && - memcmp(dev_priv->prev_instdone, instdone, sizeof(instdone)) == 0) { + if (memcmp(dev_priv->gpu_error.last_acthd, acthd, + sizeof(acthd)) == 0 && + memcmp(dev_priv->gpu_error.prev_instdone, instdone, + sizeof(instdone)) == 0) { if (i915_hangcheck_hung(dev)) return; } else { - dev_priv->hangcheck_count = 0; + dev_priv->gpu_error.hangcheck_count = 0; - memcpy(dev_priv->last_acthd, acthd, sizeof(acthd)); - memcpy(dev_priv->prev_instdone, instdone, sizeof(instdone)); + memcpy(dev_priv->gpu_error.last_acthd, acthd, + sizeof(acthd)); + memcpy(dev_priv->gpu_error.prev_instdone, instdone, + sizeof(instdone)); } repeat: /* Reset timer case chip hangs without another request being added */ - mod_timer(&dev_priv->hangcheck_timer, + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } @@ -2769,11 +2773,12 @@ void intel_irq_init(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); - INIT_WORK(&dev_priv->error_work, i915_error_work_func); + INIT_WORK(&dev_priv->gpu_error.work, i915_error_work_func); INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work); INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work); - setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, + setup_timer(&dev_priv->gpu_error.hangcheck_timer, + i915_hangcheck_elapsed, (unsigned long) dev); pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index ce1d07487402..d6b06aa4c05c 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1491,7 +1491,7 @@ void intel_ring_advance(struct intel_ring_buffer *ring) struct drm_i915_private *dev_priv = ring->dev->dev_private; ring->tail &= ring->size - 1; - if (dev_priv->stop_rings & intel_ring_flag(ring)) + if (dev_priv->gpu_error.stop_rings & intel_ring_flag(ring)) return; ring->write_tail(ring, ring->tail); } From 33196deddacc7790defb9a7e84659e0362d4da7a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 14 Nov 2012 17:14:05 +0100 Subject: [PATCH 1025/4607] drm/i915: move wedged to the other gpu error handling stuff And to make Ben Widawsky happier, use the gpu_error instead of the entire device as the argument in some functions. Drop the outdated comment on ->wedged for now, a follow-up patch will change the semantics and add a proper comment again. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 13 +++------- drivers/gpu/drm/i915/i915_gem.c | 34 ++++++++++++------------- drivers/gpu/drm/i915/i915_irq.c | 6 ++--- drivers/gpu/drm/i915/intel_display.c | 4 +-- drivers/gpu/drm/i915/intel_ringbuffer.c | 6 +++-- 6 files changed, 30 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 3b1bf4e70d94..e1b7eaf60d24 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1672,7 +1672,7 @@ i915_wedged_read(struct file *filp, len = snprintf(buf, sizeof(buf), "wedged : %d\n", - atomic_read(&dev_priv->mm.wedged)); + atomic_read(&dev_priv->gpu_error.wedged)); if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index dfe0e747c4f7..62da6c7a4884 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -744,15 +744,6 @@ struct i915_gem_mm { */ int suspended; - /** - * Flag if the hardware appears to be wedged. - * - * This is set when attempts to idle the device timeout. - * It prevents command submission from occurring and makes - * every pending request fail - */ - atomic_t wedged; - /** Bit 6 swizzling required for X tiling */ uint32_t bit_6_swizzle_x; /** Bit 6 swizzling required for Y tiling */ @@ -784,6 +775,8 @@ struct i915_gpu_error { unsigned long last_reset; + atomic_t wedged; + /* For gpu hang simulation. */ unsigned int stop_rings; }; @@ -1548,7 +1541,7 @@ i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj) void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring); -int __must_check i915_gem_check_wedge(struct drm_i915_private *dev_priv, +int __must_check i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible); void i915_gem_reset(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 95e022e7a26e..04b2f92eb456 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -87,14 +87,13 @@ static void i915_gem_info_remove_obj(struct drm_i915_private *dev_priv, } static int -i915_gem_wait_for_error(struct drm_device *dev) +i915_gem_wait_for_error(struct i915_gpu_error *error) { - struct drm_i915_private *dev_priv = dev->dev_private; - struct completion *x = &dev_priv->gpu_error.completion; + struct completion *x = &error->completion; unsigned long flags; int ret; - if (!atomic_read(&dev_priv->mm.wedged)) + if (!atomic_read(&error->wedged)) return 0; /* @@ -110,7 +109,7 @@ i915_gem_wait_for_error(struct drm_device *dev) return ret; } - if (atomic_read(&dev_priv->mm.wedged)) { + if (atomic_read(&error->wedged)) { /* GPU is hung, bump the completion count to account for * the token we just consumed so that we never hit zero and * end up waiting upon a subsequent completion event that @@ -125,9 +124,10 @@ i915_gem_wait_for_error(struct drm_device *dev) int i915_mutex_lock_interruptible(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; int ret; - ret = i915_gem_wait_for_error(dev); + ret = i915_gem_wait_for_error(&dev_priv->gpu_error); if (ret) return ret; @@ -939,11 +939,11 @@ unlock: } int -i915_gem_check_wedge(struct drm_i915_private *dev_priv, +i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible) { - if (atomic_read(&dev_priv->mm.wedged)) { - struct completion *x = &dev_priv->gpu_error.completion; + if (atomic_read(&error->wedged)) { + struct completion *x = &error->completion; bool recovery_complete; unsigned long flags; @@ -1025,7 +1025,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, #define EXIT_COND \ (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - atomic_read(&dev_priv->mm.wedged)) + atomic_read(&dev_priv->gpu_error.wedged)) do { if (interruptible) end = wait_event_interruptible_timeout(ring->irq_queue, @@ -1035,7 +1035,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, end = wait_event_timeout(ring->irq_queue, EXIT_COND, timeout_jiffies); - ret = i915_gem_check_wedge(dev_priv, interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible); if (ret) end = ret; } while (end == 0 && wait_forever); @@ -1081,7 +1081,7 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) BUG_ON(!mutex_is_locked(&dev->struct_mutex)); BUG_ON(seqno == 0); - ret = i915_gem_check_wedge(dev_priv, interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible); if (ret) return ret; @@ -1146,7 +1146,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, if (seqno == 0) return 0; - ret = i915_gem_check_wedge(dev_priv, true); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, true); if (ret) return ret; @@ -1379,7 +1379,7 @@ out: /* If this -EIO is due to a gpu hang, give the reset code a * chance to clean up the mess. Otherwise return the proper * SIGBUS. */ - if (!atomic_read(&dev_priv->mm.wedged)) + if (!atomic_read(&dev_priv->gpu_error.wedged)) return VM_FAULT_SIGBUS; case -EAGAIN: /* Give the error handler a chance to run and move the @@ -3390,7 +3390,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) u32 seqno = 0; int ret; - if (atomic_read(&dev_priv->mm.wedged)) + if (atomic_read(&dev_priv->gpu_error.wedged)) return -EIO; spin_lock(&file_priv->mm.lock); @@ -3978,9 +3978,9 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, if (drm_core_check_feature(dev, DRIVER_MODESET)) return 0; - if (atomic_read(&dev_priv->mm.wedged)) { + if (atomic_read(&dev_priv->gpu_error.wedged)) { DRM_ERROR("Reenabling wedged hardware, good luck\n"); - atomic_set(&dev_priv->mm.wedged, 0); + atomic_set(&dev_priv->gpu_error.wedged, 0); } mutex_lock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c768ebdf8a27..f2c0016fa533 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -871,11 +871,11 @@ static void i915_error_work_func(struct work_struct *work) kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); - if (atomic_read(&dev_priv->mm.wedged)) { + if (atomic_read(&dev_priv->gpu_error.wedged)) { DRM_DEBUG_DRIVER("resetting chip\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); if (!i915_reset(dev)) { - atomic_set(&dev_priv->mm.wedged, 0); + atomic_set(&dev_priv->gpu_error.wedged, 0); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); } complete_all(&dev_priv->gpu_error.completion); @@ -1483,7 +1483,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged) if (wedged) { INIT_COMPLETION(dev_priv->gpu_error.completion); - atomic_set(&dev_priv->mm.wedged, 1); + atomic_set(&dev_priv->gpu_error.wedged, 1); /* * Wakeup waiting processes so they don't hang diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b35902e5d925..160aa5f76118 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2223,7 +2223,7 @@ intel_finish_fb(struct drm_framebuffer *old_fb) WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); wait_event(dev_priv->pending_flip_queue, - atomic_read(&dev_priv->mm.wedged) || + atomic_read(&dev_priv->gpu_error.wedged) || atomic_read(&obj->pending_flip) == 0); /* Big Hammer, we also need to ensure that any pending @@ -2871,7 +2871,7 @@ static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc) unsigned long flags; bool pending; - if (atomic_read(&dev_priv->mm.wedged)) + if (atomic_read(&dev_priv->gpu_error.wedged)) return false; spin_lock_irqsave(&dev->event_lock, flags); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index d6b06aa4c05c..9438bcd50678 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1371,7 +1371,8 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n) msleep(1); - ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, + dev_priv->mm.interruptible); if (ret) return ret; } while (!time_after(jiffies, end)); @@ -1460,7 +1461,8 @@ int intel_ring_begin(struct intel_ring_buffer *ring, drm_i915_private_t *dev_priv = ring->dev->dev_private; int ret; - ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, + dev_priv->mm.interruptible); if (ret) return ret; From 308887aad14c4ecc3fc10a3c58ec42641c5e4423 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 14 Nov 2012 17:14:06 +0100 Subject: [PATCH 1026/4607] drm/i915: fix reset handling in the throttle ioctl While auditing the code I've noticed one place (the throttle ioctl) which does not yet wait for the reset handler to complete and doesn't properly decode the wedge state into -EAGAIN/-EIO. Fix this up by calling the right helpers. This might explain the oddball "my compositor just died in a successfull gpu reset" reports. Or maybe not, since current mesa doesn't use this ioctl to throttle command submission. The throttle ioctl doesn't take the struct_mutex, so to avoid busy-looping with -EAGAIN while a reset is in process, check for errors first and wait for the handler to complete if a reset is pending by calling i915_gem_wait_for_error. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 04b2f92eb456..c96a501b8205 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3390,8 +3390,13 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) u32 seqno = 0; int ret; - if (atomic_read(&dev_priv->gpu_error.wedged)) - return -EIO; + ret = i915_gem_wait_for_error(&dev_priv->gpu_error); + if (ret) + return ret; + + ret = i915_gem_check_wedge(&dev_priv->gpu_error, false); + if (ret) + return ret; spin_lock(&file_priv->mm.lock); list_for_each_entry(request, &file_priv->mm.request_list, client_list) { From 1f83fee08d625f8d0130f9fe5ef7b17c2e022f3c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Nov 2012 17:17:22 +0100 Subject: [PATCH 1027/4607] drm/i915: clear up wedged transitions We have two important transitions of the wedged state in the current code: - 0 -> 1: This means a hang has been detected, and signals to everyone that they please get of any locks, so that the reset work item can do its job. - 1 -> 0: The reset handler has completed. Now the last transition mixes up two states: "Reset completed and successful" and "Reset failed". To distinguish these two we do some tricks with the reset completion, but I simply could not convince myself that this doesn't race under odd circumstances. Hence split this up, and add a new terminal state indicating that the hw is gone for good. Also add explicit #defines for both states, update comments. v2: Split out the reset handling bugfix for the throttle ioctl. v3: s/tmp/wedged/ sugested by Chris Wilson. Also fixup up a rebase error which prevented this patch from actually compiling. v4: To unify the wedged state with the reset counter, keep the reset-in-progress state just as a flag. The terminally-wedged state is now denoted with a big number. v5: Add a comment to the reset_counter special values explaining that WEDGED & RESET_IN_PROGRESS needs to be true for the code to be correct. v6: Fixup logic errors introduced with the wedged+reset_counter unification. Since WEDGED implies reset-in-progress (in a way we're terminally stuck in the dead-but-reset-not-completed state), we need ensure that we check for this everywhere. The specific bug was in wait_for_error, which would simply have timed out. v7: Extract an inline i915_reset_in_progress helper to make the code more readable. Also annote the reset-in-progress case with an unlikely, to help the compiler optimize the fastpath. Do the same for the terminally wedged case with i915_terminally_wedged. Reviewed-by: Damien Lespiau Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 40 +++++++++++++++++++++-- drivers/gpu/drm/i915/i915_gem.c | 49 ++++++++++------------------ drivers/gpu/drm/i915/i915_irq.c | 23 ++++++++----- drivers/gpu/drm/i915/intel_display.c | 4 +-- 5 files changed, 74 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e1b7eaf60d24..384f19368a1d 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1672,7 +1672,7 @@ i915_wedged_read(struct file *filp, len = snprintf(buf, sizeof(buf), "wedged : %d\n", - atomic_read(&dev_priv->gpu_error.wedged)); + atomic_read(&dev_priv->gpu_error.reset_counter)); if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 62da6c7a4884..c84743bb6937 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -771,11 +771,37 @@ struct i915_gpu_error { /* Protected by the above dev->gpu_error.lock. */ struct drm_i915_error_state *first_error; struct work_struct work; - struct completion completion; unsigned long last_reset; - atomic_t wedged; + /** + * State variable controlling the reset flow + * + * Upper bits are for the reset counter. + * + * Lowest bit controls the reset state machine: Set means a reset is in + * progress. This state will (presuming we don't have any bugs) decay + * into either unset (successful reset) or the special WEDGED value (hw + * terminally sour). All waiters on the reset_queue will be woken when + * that happens. + */ + atomic_t reset_counter; + + /** + * Special values/flags for reset_counter + * + * Note that the code relies on + * I915_WEDGED & I915_RESET_IN_PROGRESS_FLAG + * being true. + */ +#define I915_RESET_IN_PROGRESS_FLAG 1 +#define I915_WEDGED 0xffffffff + + /** + * Waitqueue to signal when the reset has completed. Used by clients + * that wait for dev_priv->mm.wedged to settle. + */ + wait_queue_head_t reset_queue; /* For gpu hang simulation. */ unsigned int stop_rings; @@ -1543,6 +1569,16 @@ void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring); int __must_check i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible); +static inline bool i915_reset_in_progress(struct i915_gpu_error *error) +{ + return unlikely(atomic_read(&error->reset_counter) + & I915_RESET_IN_PROGRESS_FLAG); +} + +static inline bool i915_terminally_wedged(struct i915_gpu_error *error) +{ + return atomic_read(&error->reset_counter) == I915_WEDGED; +} void i915_gem_reset(struct drm_device *dev); void i915_gem_clflush_object(struct drm_i915_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c96a501b8205..2ca901194824 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -89,36 +89,32 @@ static void i915_gem_info_remove_obj(struct drm_i915_private *dev_priv, static int i915_gem_wait_for_error(struct i915_gpu_error *error) { - struct completion *x = &error->completion; - unsigned long flags; int ret; - if (!atomic_read(&error->wedged)) +#define EXIT_COND (!i915_reset_in_progress(error)) + if (EXIT_COND) return 0; + /* GPU is already declared terminally dead, give up. */ + if (i915_terminally_wedged(error)) + return -EIO; + /* * Only wait 10 seconds for the gpu reset to complete to avoid hanging * userspace. If it takes that long something really bad is going on and * we should simply try to bail out and fail as gracefully as possible. */ - ret = wait_for_completion_interruptible_timeout(x, 10*HZ); + ret = wait_event_interruptible_timeout(error->reset_queue, + EXIT_COND, + 10*HZ); if (ret == 0) { DRM_ERROR("Timed out waiting for the gpu reset to complete\n"); return -EIO; } else if (ret < 0) { return ret; } +#undef EXIT_COND - if (atomic_read(&error->wedged)) { - /* GPU is hung, bump the completion count to account for - * the token we just consumed so that we never hit zero and - * end up waiting upon a subsequent completion event that - * will never happen. - */ - spin_lock_irqsave(&x->wait.lock, flags); - x->done++; - spin_unlock_irqrestore(&x->wait.lock, flags); - } return 0; } @@ -942,23 +938,14 @@ int i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible) { - if (atomic_read(&error->wedged)) { - struct completion *x = &error->completion; - bool recovery_complete; - unsigned long flags; - - /* Give the error handler a chance to run. */ - spin_lock_irqsave(&x->wait.lock, flags); - recovery_complete = x->done > 0; - spin_unlock_irqrestore(&x->wait.lock, flags); - + if (i915_reset_in_progress(error)) { /* Non-interruptible callers can't handle -EAGAIN, hence return * -EIO unconditionally for these. */ if (!interruptible) return -EIO; - /* Recovery complete, but still wedged means reset failure. */ - if (recovery_complete) + /* Recovery complete, but the reset failed ... */ + if (i915_terminally_wedged(error)) return -EIO; return -EAGAIN; @@ -1025,7 +1012,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, #define EXIT_COND \ (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - atomic_read(&dev_priv->gpu_error.wedged)) + i915_reset_in_progress(&dev_priv->gpu_error)) do { if (interruptible) end = wait_event_interruptible_timeout(ring->irq_queue, @@ -1379,7 +1366,7 @@ out: /* If this -EIO is due to a gpu hang, give the reset code a * chance to clean up the mess. Otherwise return the proper * SIGBUS. */ - if (!atomic_read(&dev_priv->gpu_error.wedged)) + if (i915_terminally_wedged(&dev_priv->gpu_error)) return VM_FAULT_SIGBUS; case -EAGAIN: /* Give the error handler a chance to run and move the @@ -3983,9 +3970,9 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, if (drm_core_check_feature(dev, DRIVER_MODESET)) return 0; - if (atomic_read(&dev_priv->gpu_error.wedged)) { + if (i915_reset_in_progress(&dev_priv->gpu_error)) { DRM_ERROR("Reenabling wedged hardware, good luck\n"); - atomic_set(&dev_priv->gpu_error.wedged, 0); + atomic_set(&dev_priv->gpu_error.reset_counter, 0); } mutex_lock(&dev->struct_mutex); @@ -4069,7 +4056,7 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list); INIT_DELAYED_WORK(&dev_priv->mm.retire_work, i915_gem_retire_work_handler); - init_completion(&dev_priv->gpu_error.completion); + init_waitqueue_head(&dev_priv->gpu_error.reset_queue); /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ if (IS_GEN3(dev)) { diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index f2c0016fa533..4562c5406ef8 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -862,8 +862,10 @@ done: */ static void i915_error_work_func(struct work_struct *work) { - drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, - gpu_error.work); + struct i915_gpu_error *error = container_of(work, struct i915_gpu_error, + work); + drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t, + gpu_error); struct drm_device *dev = dev_priv->dev; char *error_event[] = { "ERROR=1", NULL }; char *reset_event[] = { "RESET=1", NULL }; @@ -871,14 +873,18 @@ static void i915_error_work_func(struct work_struct *work) kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); - if (atomic_read(&dev_priv->gpu_error.wedged)) { + if (i915_reset_in_progress(error)) { DRM_DEBUG_DRIVER("resetting chip\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); + if (!i915_reset(dev)) { - atomic_set(&dev_priv->gpu_error.wedged, 0); + atomic_set(&error->reset_counter, 0); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); + } else { + atomic_set(&error->reset_counter, I915_WEDGED); } - complete_all(&dev_priv->gpu_error.completion); + + wake_up_all(&dev_priv->gpu_error.reset_queue); } } @@ -1482,11 +1488,12 @@ void i915_handle_error(struct drm_device *dev, bool wedged) i915_report_and_clear_eir(dev); if (wedged) { - INIT_COMPLETION(dev_priv->gpu_error.completion); - atomic_set(&dev_priv->gpu_error.wedged, 1); + atomic_set(&dev_priv->gpu_error.reset_counter, + I915_RESET_IN_PROGRESS_FLAG); /* - * Wakeup waiting processes so they don't hang + * Wakeup waiting processes so that the reset work item + * doesn't deadlock trying to grab various locks. */ for_each_ring(ring, dev_priv, i) wake_up_all(&ring->irq_queue); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 160aa5f76118..77254460a5cb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2223,7 +2223,7 @@ intel_finish_fb(struct drm_framebuffer *old_fb) WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); wait_event(dev_priv->pending_flip_queue, - atomic_read(&dev_priv->gpu_error.wedged) || + i915_reset_in_progress(&dev_priv->gpu_error) || atomic_read(&obj->pending_flip) == 0); /* Big Hammer, we also need to ensure that any pending @@ -2871,7 +2871,7 @@ static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc) unsigned long flags; bool pending; - if (atomic_read(&dev_priv->gpu_error.wedged)) + if (i915_reset_in_progress(&dev_priv->gpu_error)) return false; spin_lock_irqsave(&dev->event_lock, flags); From d0a57789d5ec807fc218151b2fb2de4da30fbef5 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 9 Oct 2012 19:24:37 +0100 Subject: [PATCH 1028/4607] drm/i915: Only insert the mb() before updating the fence parameter With a fence, we only need to insert a memory barrier around the actual fence alteration for CPU accesses through the GTT. Performing the barrier in flush-fence was inserting unnecessary and expensive barriers for never fenced objects. Note removing the barriers from flush-fence, which was effectively a barrier before every direct access through the GTT, revealed that we where missing a barrier before the first access through the GTT. Lack of that barrier was sufficient to cause GPU hangs. v2: Add a couple more comments to explain the new barriers Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 40 ++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 2ca901194824..ce706555d011 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2611,9 +2611,22 @@ static void i830_write_fence_reg(struct drm_device *dev, int reg, POSTING_READ(FENCE_REG_830_0 + reg * 4); } +inline static bool i915_gem_object_needs_mb(struct drm_i915_gem_object *obj) +{ + return obj && obj->base.read_domains & I915_GEM_DOMAIN_GTT; +} + static void i915_gem_write_fence(struct drm_device *dev, int reg, struct drm_i915_gem_object *obj) { + struct drm_i915_private *dev_priv = dev->dev_private; + + /* Ensure that all CPU reads are completed before installing a fence + * and all writes before removing the fence. + */ + if (i915_gem_object_needs_mb(dev_priv->fence_regs[reg].obj)) + mb(); + switch (INTEL_INFO(dev)->gen) { case 7: case 6: @@ -2623,6 +2636,12 @@ static void i915_gem_write_fence(struct drm_device *dev, int reg, case 2: i830_write_fence_reg(dev, reg, obj); break; default: BUG(); } + + /* And similarly be paranoid that no direct access to this region + * is reordered to before the fence is installed. + */ + if (i915_gem_object_needs_mb(obj)) + mb(); } static inline int fence_number(struct drm_i915_private *dev_priv, @@ -2652,7 +2671,7 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj, } static int -i915_gem_object_flush_fence(struct drm_i915_gem_object *obj) +i915_gem_object_wait_fence(struct drm_i915_gem_object *obj) { if (obj->last_fenced_seqno) { int ret = i915_wait_seqno(obj->ring, obj->last_fenced_seqno); @@ -2662,12 +2681,6 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj) obj->last_fenced_seqno = 0; } - /* Ensure that all CPU reads are completed before installing a fence - * and all writes before removing the fence. - */ - if (obj->base.read_domains & I915_GEM_DOMAIN_GTT) - mb(); - obj->fenced_gpu_access = false; return 0; } @@ -2678,7 +2691,7 @@ i915_gem_object_put_fence(struct drm_i915_gem_object *obj) struct drm_i915_private *dev_priv = obj->base.dev->dev_private; int ret; - ret = i915_gem_object_flush_fence(obj); + ret = i915_gem_object_wait_fence(obj); if (ret) return ret; @@ -2752,7 +2765,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) * will need to serialise the write to the associated fence register? */ if (obj->fence_dirty) { - ret = i915_gem_object_flush_fence(obj); + ret = i915_gem_object_wait_fence(obj); if (ret) return ret; } @@ -2773,7 +2786,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) if (reg->obj) { struct drm_i915_gem_object *old = reg->obj; - ret = i915_gem_object_flush_fence(old); + ret = i915_gem_object_wait_fence(old); if (ret) return ret; @@ -3068,6 +3081,13 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) i915_gem_object_flush_cpu_write_domain(obj); + /* Serialise direct access to this object with the barriers for + * coherent writes from the GPU, by effectively invalidating the + * GTT domain upon first access. + */ + if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) + mb(); + old_write_domain = obj->base.write_domain; old_read_domains = obj->base.read_domains; From 97c809fd9cf5e914322b53773ad0d67efe503fde Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 9 Oct 2012 19:24:38 +0100 Subject: [PATCH 1029/4607] drm/i915: Only apply the mb() when flushing the GTT domain during a finish Now that we seem to have brought order to the GTT barriers, the last one to review is the terminal barrier before we unbind the buffer from the GTT. This needs to only be performed if the buffer still resides in the GTT domain, and so we can skip some needless barriers otherwise. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ce706555d011..5bb370fdc99c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2407,15 +2407,15 @@ static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj) { u32 old_write_domain, old_read_domains; - /* Act a barrier for all accesses through the GTT */ - mb(); - /* Force a pagefault for domain tracking on next user access */ i915_gem_release_mmap(obj); if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) return; + /* Wait for any direct GTT access to complete */ + mb(); + old_read_domains = obj->base.read_domains; old_write_domain = obj->base.write_domain; From ed30933e6f3dbeaaab1de91e1bec25f42d5d32df Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Tue, 15 Jan 2013 01:19:48 +0100 Subject: [PATCH 1030/4607] dma: remove unnecessary null pointer check in mmp_pdma.c the pointer cfg is dereferenced in line 594, so it's no reason to check null again in line 620. Signed-off-by: Cong Ding Signed-off-by: Vinod Koul --- drivers/dma/mmp_pdma.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index 13bdf4a7e1ec..41ad6a62f838 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -617,10 +617,8 @@ static int mmp_pdma_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd, else if (maxburst == 32) chan->dcmd |= DCMD_BURST32; - if (cfg) { - chan->dir = cfg->direction; - chan->drcmr = cfg->slave_id; - } + chan->dir = cfg->direction; + chan->drcmr = cfg->slave_id; chan->dev_addr = addr; break; default: From 855372c013bbad8369223f7c75242bd3c94f9345 Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Tue, 15 Jan 2013 01:23:48 +0100 Subject: [PATCH 1031/4607] dma: sh/shdma-base.c: remove unnecessary null pointer check the variable chan is dereferenced in line 635, so it is no reason to check null again in line 641. Signed-off-by: Cong Ding Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma-base.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index f4cd946d259d..4acb85a10250 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -638,9 +638,6 @@ static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long flags; int ret; - if (!chan) - return -EINVAL; - switch (cmd) { case DMA_TERMINATE_ALL: spin_lock_irqsave(&schan->chan_lock, flags); From 8faf6b18a2a6bece008de1e6bb80f0c608e58483 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 23:43:11 +0100 Subject: [PATCH 1032/4607] drm: review locking rules in drm_crtc.c - config_cleanup was confused: It claimed that callers need to hold the modeset lock, but the connector|encoder_cleanup helpers grabbed that themselves (note that crtc_cleanup did _not_ grab the modeset lock). Which resulted in all drivers _not_ hodling the lock. Since this is for single-threaded cleanup code, drop the requirement from docs and also drop the lock_grabbing from all _cleanup functions. - Kill the LOCKING section in the doctype, since clearly we're not good enough to keep them up-to-date. And misleading locking documentation is worse than useless (see e.g. the comment in the vmgfx driver about the cleanup mess). And since for most functions the very first line either grabs the lock or has a WARN_ON(!locked) the documentation doesn't really add anything. - Instead put in some effort into explaining the only two special cases a bit better: config_init and config_cleanup are both called from single-threaded setup/teardown code, so don't do any locking. It's the driver's job though to enforce this. - Where lacking, add a WARN_ON(!is_locked). Not many places though, since locking around fbdev setup/teardown is through-roughly screwed up, and so will break almost every single WARN annotation I've tried to add. - Add a drm_modeset_is_locked helper - the Grate Modset Locking Rework will use the compiler to assist in the big reorg by renaming the mode lock, so start encapsulating things. Unfortunately this ended up in the "wrong" header file since it needs the definition of struct drm_device. v2: Drop most WARNS again - we hit them all over the place, mostly in the setup and teardown sequences. And trying to fix it up leads to nice deadlocks, since the locking in the setup code is really inconsistent. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 105 +++++-------------------------------- include/drm/drmP.h | 5 ++ 2 files changed, 18 insertions(+), 92 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f2d667b8bee2..b970e4147862 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -208,8 +208,6 @@ char *drm_get_connector_status_name(enum drm_connector_status status) * @ptr: object pointer, used to generate unique ID * @type: object type * - * LOCKING: - * * Create a unique identifier based on @ptr in @dev's identifier space. Used * for tracking modes, CRTCs and connectors. * @@ -247,9 +245,6 @@ again: * @dev: DRM device * @id: ID to free * - * LOCKING: - * Caller must hold DRM mode_config lock. - * * Free @id from @dev's unique identifier pool. */ static void drm_mode_object_put(struct drm_device *dev, @@ -279,9 +274,6 @@ EXPORT_SYMBOL(drm_mode_object_find); * drm_framebuffer_init - initialize a framebuffer * @dev: DRM device * - * LOCKING: - * Caller must hold mode config lock. - * * Allocates an ID for the framebuffer's parent mode object, sets its mode * functions & device file and adds it to the master fd list. * @@ -317,15 +309,12 @@ static void drm_framebuffer_free(struct kref *kref) /** * drm_framebuffer_unreference - unref a framebuffer - * - * LOCKING: - * Caller must hold mode config lock. */ void drm_framebuffer_unreference(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; DRM_DEBUG("FB ID: %d\n", fb->base.id); - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(!drm_modeset_is_locked(dev)); kref_put(&fb->refcount, drm_framebuffer_free); } EXPORT_SYMBOL(drm_framebuffer_unreference); @@ -344,15 +333,13 @@ EXPORT_SYMBOL(drm_framebuffer_reference); * drm_framebuffer_cleanup - remove a framebuffer object * @fb: framebuffer to remove * - * LOCKING: - * Caller must hold mode config lock. - * * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes * it, setting it to NULL. */ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; + /* * This could be moved to drm_framebuffer_remove(), but for * debugging is nice to keep around the list of fb's that are @@ -370,9 +357,6 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); * drm_framebuffer_remove - remove and unreference a framebuffer object * @fb: framebuffer to remove * - * LOCKING: - * Caller must hold mode config lock. - * * Scans all the CRTCs and planes in @dev's mode_config. If they're * using @fb, removes it, setting it to NULL. */ @@ -384,6 +368,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) struct drm_mode_set set; int ret; + WARN_ON(!drm_modeset_is_locked(dev)); + /* remove from any CRTC */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (crtc->fb == fb) { @@ -421,9 +407,6 @@ EXPORT_SYMBOL(drm_framebuffer_remove); * @crtc: CRTC object to init * @funcs: callbacks for the new CRTC * - * LOCKING: - * Takes mode_config lock. - * * Inits a new object created as base part of an driver crtc object. * * RETURNS: @@ -460,9 +443,6 @@ EXPORT_SYMBOL(drm_crtc_init); * drm_crtc_cleanup - Cleans up the core crtc usage. * @crtc: CRTC to cleanup * - * LOCKING: - * Caller must hold mode config lock. - * * Cleanup @crtc. Removes from drm modesetting space * does NOT free object, caller does that. */ @@ -484,9 +464,6 @@ EXPORT_SYMBOL(drm_crtc_cleanup); * @connector: connector the new mode * @mode: mode data * - * LOCKING: - * Caller must hold mode config lock. - * * Add @mode to @connector's mode list for later use. */ void drm_mode_probed_add(struct drm_connector *connector, @@ -501,9 +478,6 @@ EXPORT_SYMBOL(drm_mode_probed_add); * @connector: connector list to modify * @mode: mode to remove * - * LOCKING: - * Caller must hold mode config lock. - * * Remove @mode from @connector's mode list, then free it. */ void drm_mode_remove(struct drm_connector *connector, @@ -521,9 +495,6 @@ EXPORT_SYMBOL(drm_mode_remove); * @funcs: callbacks for this connector * @name: user visible name of the connector * - * LOCKING: - * Takes mode config lock. - * * Initialises a preallocated connector. Connectors should be * subclassed as part of driver connector objects. * @@ -577,9 +548,6 @@ EXPORT_SYMBOL(drm_connector_init); * drm_connector_cleanup - cleans up an initialised connector * @connector: connector to cleanup * - * LOCKING: - * Takes mode config lock. - * * Cleans up the connector but doesn't free the object. */ void drm_connector_cleanup(struct drm_connector *connector) @@ -596,11 +564,9 @@ void drm_connector_cleanup(struct drm_connector *connector) list_for_each_entry_safe(mode, t, &connector->user_modes, head) drm_mode_remove(connector, mode); - mutex_lock(&dev->mode_config.mutex); drm_mode_object_put(dev, &connector->base); list_del(&connector->head); dev->mode_config.num_connector--; - mutex_unlock(&dev->mode_config.mutex); } EXPORT_SYMBOL(drm_connector_cleanup); @@ -721,9 +687,6 @@ EXPORT_SYMBOL(drm_plane_cleanup); * drm_mode_create - create a new display mode * @dev: DRM device * - * LOCKING: - * Caller must hold DRM mode_config lock. - * * Create a new drm_display_mode, give it an ID, and return it. * * RETURNS: @@ -751,9 +714,6 @@ EXPORT_SYMBOL(drm_mode_create); * @dev: DRM device * @mode: mode to remove * - * LOCKING: - * Caller must hold mode config lock. - * * Free @mode's unique identifier, then free it. */ void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) @@ -978,11 +938,13 @@ EXPORT_SYMBOL(drm_mode_create_dirty_info_property); * drm_mode_config_init - initialize DRM mode_configuration structure * @dev: DRM device * - * LOCKING: - * None, should happen single threaded at init time. - * * Initialize @dev's mode_config structure, used for tracking the graphics * configuration of @dev. + * + * Since this initializes the modeset locks, no locking is possible. Which is no + * problem, since this should happen single threaded at init time. It is the + * driver's problem to ensure this guarantee. + * */ void drm_mode_config_init(struct drm_device *dev) { @@ -1057,12 +1019,13 @@ EXPORT_SYMBOL(drm_mode_group_init_legacy_group); * drm_mode_config_cleanup - free up DRM mode_config info * @dev: DRM device * - * LOCKING: - * Caller must hold mode config lock. - * * Free up all the connectors and CRTCs associated with this DRM device, then * free up the framebuffers and associated buffer objects. * + * Note that since this /should/ happen single-threaded at driver/device + * teardown time, no locking is required. It's the driver's job to ensure that + * this guarantee actually holds true. + * * FIXME: cleanup any dangling user buffer objects too */ void drm_mode_config_cleanup(struct drm_device *dev) @@ -1112,9 +1075,6 @@ EXPORT_SYMBOL(drm_mode_config_cleanup); * @out: drm_mode_modeinfo struct to return to the user * @in: drm_display_mode to use * - * LOCKING: - * None. - * * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to * the user. */ @@ -1151,9 +1111,6 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, * @out: drm_display_mode to return to the user * @in: drm_mode_modeinfo to use * - * LOCKING: - * None. - * * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to * the caller. * @@ -1193,9 +1150,6 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out, * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Construct a set of configuration description structures and return * them to the user, including CRTC, connector and framebuffer configuration. * @@ -1381,9 +1335,6 @@ out: * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Construct a CRTC configuration structure to return to the user. * * Called by the user via ioctl. @@ -1441,9 +1392,6 @@ out: * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Construct a connector configuration structure to return to the user. * * Called by the user via ioctl. @@ -1618,9 +1566,6 @@ out: * @data: ioctl data * @file_priv: DRM file info * - * LOCKING: - * Takes mode config lock. - * * Return an plane count and set of IDs. */ int drm_mode_getplane_res(struct drm_device *dev, void *data, @@ -1667,9 +1612,6 @@ out: * @data: ioctl data * @file_priv: DRM file info * - * LOCKING: - * Takes mode config lock. - * * Return plane info, including formats supported, gamma size, any * current fb, etc. */ @@ -1735,9 +1677,6 @@ out: * @data: ioctl data* * @file_prive: DRM file info * - * LOCKING: - * Takes mode config lock. - * * Set plane info, including placement, fb, scaling, and other factors. * Or pass a NULL fb to disable. */ @@ -1867,9 +1806,6 @@ out: * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Build a new CRTC configuration based on user request. * * Called by the user via ioctl. @@ -2125,9 +2061,6 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format); * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Add a new FB to the specified CRTC, given a user request. * * Called by the user via ioctl. @@ -2309,9 +2242,6 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Add a new FB to the specified CRTC, given a user request with format. * * Called by the user via ioctl. @@ -2375,9 +2305,6 @@ out: * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Remove the FB specified by the user. * * Called by the user via ioctl. @@ -2430,9 +2357,6 @@ out: * @cmd: cmd from ioctl * @arg: arg from ioctl * - * LOCKING: - * Takes mode config lock. - * * Lookup the FB given its ID and return info about it. * * Called by the user via ioctl. @@ -2549,9 +2473,6 @@ out_err1: * drm_fb_release - remove and free the FBs on this file * @filp: file * from the ioctl * - * LOCKING: - * Takes mode config lock. - * * Destroy all the FBs associated with @filp. * * Called by the user via ioctl. diff --git a/include/drm/drmP.h b/include/drm/drmP.h index fad21c927a38..3c609abe8c80 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1276,6 +1276,11 @@ static inline int drm_device_is_unplugged(struct drm_device *dev) return ret; } +static inline bool drm_modeset_is_locked(struct drm_device *dev) +{ + return mutex_is_locked(&dev->mode_config.mutex); +} + /******************************************************************/ /** \name Internal function definitions */ /*@{*/ From 065a50ed3ef75cb265e12e3e1b615db0835150bc Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 00:09:18 +0100 Subject: [PATCH 1033/4607] drm/doc: integrate drm_crtc.c kerneldoc And do a quick pass to adjust them to the last few (years?) of changes ... This time actually compile-tested ;-) Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 4 ++ drivers/gpu/drm/drm_crtc.c | 92 ++++++++++++++++------------------ 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 4ee2304f82f9..caab791b0a6c 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -1609,6 +1609,10 @@ void intel_crt_init(struct drm_device *dev) make its properties available to applications. + + KMS API Functions +!Edrivers/gpu/drm/drm_crtc.c + diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index b970e4147862..81545540b2df 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -203,10 +203,10 @@ char *drm_get_connector_status_name(enum drm_connector_status status) } /** - * drm_mode_object_get - allocate a new identifier + * drm_mode_object_get - allocate a new modeset identifier * @dev: DRM device - * @ptr: object pointer, used to generate unique ID - * @type: object type + * @obj: object pointer, used to generate unique ID + * @obj_type: object type * * Create a unique identifier based on @ptr in @dev's identifier space. Used * for tracking modes, CRTCs and connectors. @@ -241,9 +241,9 @@ again: } /** - * drm_mode_object_put - free an identifer + * drm_mode_object_put - free a modeset identifer * @dev: DRM device - * @id: ID to free + * @object: object to free * * Free @id from @dev's unique identifier pool. */ @@ -273,6 +273,8 @@ EXPORT_SYMBOL(drm_mode_object_find); /** * drm_framebuffer_init - initialize a framebuffer * @dev: DRM device + * @fb: framebuffer to be initialized + * @funcs: ... with these functions * * Allocates an ID for the framebuffer's parent mode object, sets its mode * functions & device file and adds it to the master fd list. @@ -309,6 +311,9 @@ static void drm_framebuffer_free(struct kref *kref) /** * drm_framebuffer_unreference - unref a framebuffer + * @fb: framebuffer to unref + * + * This functions decrements the fb's refcount and frees it if it drops to zero. */ void drm_framebuffer_unreference(struct drm_framebuffer *fb) { @@ -321,6 +326,7 @@ EXPORT_SYMBOL(drm_framebuffer_unreference); /** * drm_framebuffer_reference - incr the fb refcnt + * @fb: framebuffer */ void drm_framebuffer_reference(struct drm_framebuffer *fb) { @@ -493,7 +499,7 @@ EXPORT_SYMBOL(drm_mode_remove); * @dev: DRM device * @connector: the connector to init * @funcs: callbacks for this connector - * @name: user visible name of the connector + * @connector_type: user visible type of the connector * * Initialises a preallocated connector. Connectors should be * subclassed as part of driver connector objects. @@ -1145,10 +1151,9 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out, /** * drm_mode_getresources - get graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Construct a set of configuration description structures and return * them to the user, including CRTC, connector and framebuffer configuration. @@ -1330,10 +1335,9 @@ out: /** * drm_mode_getcrtc - get CRTC configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Construct a CRTC configuration structure to return to the user. * @@ -1387,10 +1391,9 @@ out: /** * drm_mode_getconnector - get connector configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Construct a connector configuration structure to return to the user. * @@ -1675,7 +1678,7 @@ out: * drm_mode_setplane - set up or tear down an plane * @dev: DRM device * @data: ioctl data* - * @file_prive: DRM file info + * @file_priv: DRM file info * * Set plane info, including placement, fb, scaling, and other factors. * Or pass a NULL fb to disable. @@ -1801,10 +1804,9 @@ out: /** * drm_mode_setcrtc - set CRTC configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Build a new CRTC configuration based on user request. * @@ -2056,10 +2058,9 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format); /** * drm_mode_addfb - add an FB to the graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Add a new FB to the specified CRTC, given a user request. * @@ -2237,10 +2238,9 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) /** * drm_mode_addfb2 - add an FB to the graphics configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Add a new FB to the specified CRTC, given a user request with format. * @@ -2300,10 +2300,9 @@ out: /** * drm_mode_rmfb - remove an FB from the configuration - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Remove the FB specified by the user. * @@ -2352,10 +2351,9 @@ out: /** * drm_mode_getfb - get FB info - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Lookup the FB given its ID and return info about it. * @@ -2471,7 +2469,7 @@ out_err1: /** * drm_fb_release - remove and free the FBs on this file - * @filp: file * from the ioctl + * @priv: drm file for the ioctl * * Destroy all the FBs associated with @filp. * @@ -2581,10 +2579,9 @@ EXPORT_SYMBOL(drm_mode_detachmode_crtc); /** * drm_fb_attachmode - Attach a user mode to an connector - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * This attaches a user specified mode to an connector. * Called by the user via ioctl. @@ -2636,10 +2633,9 @@ out: /** * drm_fb_detachmode - Detach a user specified mode from an connector - * @inode: inode from the ioctl - * @filp: file * from the ioctl - * @cmd: cmd from ioctl - * @arg: arg from ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call * * Called by the user via ioctl. * From c7d73f6a8ad71f9d9f58c86981322c6e48093a4f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 13 Dec 2012 23:38:38 +0100 Subject: [PATCH 1034/4607] drm/: reorder framebuffer init sequence With more fine-grained locking we can no longer rely on the big mode_config lock to prevent concurrent access to mode resources like framebuffers. Instead a framebuffer becomes accessible to other threads as soon as it is added to the relevant lookup structures. Hence it needs to be fully set up by the time drivers call drm_framebuffer_init. This patch here is the drivers part of that reorg. Nothing really fancy going on safe for three special cases. - exynos needs to be careful to properly unref all handles. - nouveau gets a resource leak fixed for free: one of the error cases didn't cleanup the framebuffer, which is now moot since the framebuffer is only registered once it is fully set up. - vmwgfx requires a slight reordering of operations, I'm hoping I didn't break anything (but it's refcount management only, so should be safe). v2: Split out exynos, since it's a bit more hairy than expected. v3: Drop bogus cirrus hunk noticed by Richard Wilbur. v4: Split out vmwgfx since there's a small change in return values. Reviewed-by: Rob Clark (core + omapdrm) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_main.c | 4 ++-- drivers/gpu/drm/cirrus/cirrus_main.c | 4 ++-- drivers/gpu/drm/drm_fb_cma_helper.c | 10 +++++----- drivers/gpu/drm/gma500/framebuffer.c | 4 ++-- drivers/gpu/drm/i915/intel_display.c | 5 +++-- drivers/gpu/drm/mgag200/mgag200_main.c | 8 +++++--- drivers/gpu/drm/nouveau/nouveau_display.c | 10 +++++----- drivers/gpu/drm/radeon/radeon_display.c | 2 +- drivers/gpu/drm/udl/udl_fb.c | 2 +- drivers/staging/omapdrm/omap_fb.c | 16 ++++++++-------- 10 files changed, 34 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index f668e6cc0f7a..d5ba7097e5b5 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -266,13 +266,13 @@ int ast_framebuffer_init(struct drm_device *dev, { int ret; + drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); + ast_fb->obj = obj; ret = drm_framebuffer_init(dev, &ast_fb->base, &ast_fb_funcs); if (ret) { DRM_ERROR("framebuffer init failed %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); - ast_fb->obj = obj; return 0; } diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 6a9b12e88d46..364474c66202 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -42,13 +42,13 @@ int cirrus_framebuffer_init(struct drm_device *dev, { int ret; + drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); + gfb->obj = obj; ret = drm_framebuffer_init(dev, &gfb->base, &cirrus_fb_funcs); if (ret) { DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); - gfb->obj = obj; return 0; } diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index fd9d0af4d536..e1e0cb0d531a 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -85,6 +85,11 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, if (!fb_cma) return ERR_PTR(-ENOMEM); + drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); + + for (i = 0; i < num_planes; i++) + fb_cma->obj[i] = obj[i]; + ret = drm_framebuffer_init(dev, &fb_cma->fb, &drm_fb_cma_funcs); if (ret) { dev_err(dev->dev, "Failed to initalize framebuffer: %d\n", ret); @@ -92,11 +97,6 @@ static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, return ERR_PTR(ret); } - drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); - - for (i = 0; i < num_planes; i++) - fb_cma->obj[i] = obj[i]; - return fb_cma; } diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index afded54dbb10..38e7e7597de2 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -260,13 +260,13 @@ static int psb_framebuffer_init(struct drm_device *dev, default: return -EINVAL; } + drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); + fb->gtt = gt; ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs); if (ret) { dev_err(dev->dev, "framebuffer init failed: %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); - fb->gtt = gt; return 0; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index da1ad9c80bb5..26fa6a795afe 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8666,14 +8666,15 @@ int intel_framebuffer_init(struct drm_device *dev, if (mode_cmd->offsets[0] != 0) return -EINVAL; + drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); + intel_fb->obj = obj; + ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs); if (ret) { DRM_ERROR("framebuffer init failed %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd); - intel_fb->obj = obj; return 0; } diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 70dd3c5529d4..266438af61ea 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -40,13 +40,15 @@ int mgag200_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object *obj) { - int ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs); + int ret; + + drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); + gfb->obj = obj; + ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs); if (ret) { DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); - gfb->obj = obj; return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 508b00a2ce0d..d42c9e860c16 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -78,11 +78,6 @@ nouveau_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb = &nv_fb->base; int ret; - ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs); - if (ret) { - return ret; - } - drm_helper_mode_fill_fb_struct(fb, mode_cmd); nv_fb->nvbo = nvbo; @@ -125,6 +120,11 @@ nouveau_framebuffer_init(struct drm_device *dev, } } + ret = drm_framebuffer_init(dev, fb, &nouveau_framebuffer_funcs); + if (ret) { + return ret; + } + return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 1da2386d7cf7..12ec3effb409 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -1089,12 +1089,12 @@ radeon_framebuffer_init(struct drm_device *dev, { int ret; rfb->obj = obj; + drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd); ret = drm_framebuffer_init(dev, &rfb->base, &radeon_fb_funcs); if (ret) { rfb->obj = NULL; return ret; } - drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd); return 0; } diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index d4ab3beaada0..829c4a76938c 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -435,8 +435,8 @@ udl_framebuffer_init(struct drm_device *dev, int ret; ufb->obj = obj; - ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs); drm_helper_mode_fill_fb_struct(&ufb->base, mode_cmd); + ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs); return ret; } diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c index 09028e9c1093..bf6421f26c40 100644 --- a/drivers/staging/omapdrm/omap_fb.c +++ b/drivers/staging/omapdrm/omap_fb.c @@ -424,14 +424,6 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, } fb = &omap_fb->base; - ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs); - if (ret) { - dev_err(dev->dev, "framebuffer init failed: %d\n", ret); - goto fail; - } - - DBG("create: FB ID: %d (%p)", fb->base.id, fb); - omap_fb->format = format; for (i = 0; i < n; i++) { @@ -462,6 +454,14 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, drm_helper_mode_fill_fb_struct(fb, mode_cmd); + ret = drm_framebuffer_init(dev, fb, &omap_framebuffer_funcs); + if (ret) { + dev_err(dev->dev, "framebuffer init failed: %d\n", ret); + goto fail; + } + + DBG("create: FB ID: %d (%p)", fb->base.id, fb); + return fb; fail: From 80f0b5aff8f49f63eaf08bdae243de3a8ea3f77e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 13 Dec 2012 23:39:01 +0100 Subject: [PATCH 1035/4607] drm/vmwgfx: reorder framebuffer init sequence vmwgfx has an oddity, when failing to reference the surface it'll return 0, since that's what the successfull drm_framebuffer_init will leave behind in ret. Fix this up by returning -EINVAL. Split out from all the other driver updates due to the above tiny semantic change. Shouldn't matter though since the reference grabbing seemingly can't fail. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 54743943d8b3..edc97929c9a3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -681,14 +681,10 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, goto out_err1; } - ret = drm_framebuffer_init(dev, &vfbs->base.base, - &vmw_framebuffer_surface_funcs); - if (ret) - goto out_err2; - if (!vmw_surface_reference(surface)) { DRM_ERROR("failed to reference surface %p\n", surface); - goto out_err3; + ret = -EINVAL; + goto out_err2; } /* XXX get the first 3 from the surface info */ @@ -707,10 +703,15 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, *out = &vfbs->base; + ret = drm_framebuffer_init(dev, &vfbs->base.base, + &vmw_framebuffer_surface_funcs); + if (ret) + goto out_err3; + return 0; out_err3: - drm_framebuffer_cleanup(&vfbs->base.base); + vmw_surface_unreference(&surface); out_err2: kfree(vfbs); out_err1: @@ -1053,14 +1054,10 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, goto out_err1; } - ret = drm_framebuffer_init(dev, &vfbd->base.base, - &vmw_framebuffer_dmabuf_funcs); - if (ret) - goto out_err2; - if (!vmw_dmabuf_reference(dmabuf)) { DRM_ERROR("failed to reference dmabuf %p\n", dmabuf); - goto out_err3; + ret = -EINVAL; + goto out_err2; } vfbd->base.base.bits_per_pixel = mode_cmd->bpp; @@ -1077,10 +1074,15 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, vfbd->base.user_handle = mode_cmd->handle; *out = &vfbd->base; + ret = drm_framebuffer_init(dev, &vfbd->base.base, + &vmw_framebuffer_dmabuf_funcs); + if (ret) + goto out_err3; + return 0; out_err3: - drm_framebuffer_cleanup(&vfbd->base.base); + vmw_dmabuf_unreference(&dmabuf); out_err2: kfree(vfbd); out_err1: From 7147573a5ce499dec3979e6b524691d47e1288d5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 21:55:41 +0100 Subject: [PATCH 1036/4607] drm/gma500: move fbcon restore to lastclose Doing this within the fb->destroy callback leads to a locking nightmare. And all other drm drivers that restore the fbcon do it in lastclose, too. With this adjustments all fb->destroy callbacks optionally drop references to any gem objects used as backing storage, call drm_framebuffer_cleanup and then kfree the struct. Which nicely simplifies the locking for framebuffer unreferencing and freeing, since this doesn't require that we hold the mode_config lock. A slight exception is the vmwgfx surface backed framebuffer, it also calls drm_master_put and removes the object from a device-private framebuffer list. Both seem to have solid locking in place already. Conclusion is that now it is no longer required to hold the mode_config lock while freeing a framebuffer. v2: Drop the corresponding mutex_lock WARN check from drm_framebuffer_unreference. v3: Use just the mode_config lock not modeset_lock_all, due to patch reordering. Acked-by: Alan Cox Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 2 -- drivers/gpu/drm/gma500/framebuffer.c | 24 ------------------------ drivers/gpu/drm/gma500/psb_drv.c | 10 ++++++++++ 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 81545540b2df..8d665fafc15e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -317,9 +317,7 @@ static void drm_framebuffer_free(struct kref *kref) */ void drm_framebuffer_unreference(struct drm_framebuffer *fb) { - struct drm_device *dev = fb->dev; DRM_DEBUG("FB ID: %d\n", fb->base.id); - WARN_ON(!drm_modeset_is_locked(dev)); kref_put(&fb->refcount, drm_framebuffer_free); } EXPORT_SYMBOL(drm_framebuffer_unreference); diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 38e7e7597de2..49800d2b79dd 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -668,30 +668,6 @@ static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb) { struct psb_framebuffer *psbfb = to_psb_fb(fb); struct gtt_range *r = psbfb->gtt; - struct drm_device *dev = fb->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_fbdev *fbdev = dev_priv->fbdev; - struct drm_crtc *crtc; - int reset = 0; - - /* Should never get stolen memory for a user fb */ - WARN_ON(r->stolen); - - /* Check if we are erroneously live */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - if (crtc->fb == fb) - reset = 1; - - if (reset) - /* - * Now force a sane response before we permit the DRM CRTC - * layer to do stupid things like blank the display. Instead - * we reset this framebuffer as if the user had forced a reset. - * We must do this before the cleanup so that the DRM layer - * doesn't get a chance to stick its oar in where it isn't - * wanted. - */ - drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); /* Let DRM do its clean up */ drm_framebuffer_cleanup(fb); diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index dd1fbfa7e467..dbcefe9f78fa 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -149,6 +149,16 @@ static struct drm_ioctl_desc psb_ioctls[] = { static void psb_lastclose(struct drm_device *dev) { + int ret; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_fbdev *fbdev = dev_priv->fbdev; + + mutex_lock(&dev->mode_config.mutex); + ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); + if (ret) + DRM_DEBUG("failed to restore crtc mode\n"); + mutex_unlock(&dev->mode_config.mutex); + return; } From 59ad1465423d968f06f243bc52cc3b1a320a27cf Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 14:49:44 +0100 Subject: [PATCH 1037/4607] drm/nouveau: protect evo_wait/evo_kick sections with a channel mutex With per-crtc locks modeset operations can run in parallel, and the cursor code uses the device-global evo master channel for hw frobbing. But the pageflip code can also sync with the master under some circumstances. Hence just wrap things up in a mutex to ensure that pushbuf access doesn't intermingle. The approach here is a bit overkill since the per-crtc channels used to schedule the pageflips could probably be used without this pushbuf locking, but I'm not familiar enough with the nouveau codebase to be sure of that. v2: Add missing mutex_init to avoid angering lockdep. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/nouveau/nv50_display.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 35874085a61e..d4cbea19b890 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -128,6 +128,11 @@ struct nv50_dmac { struct nv50_chan base; dma_addr_t handle; u32 *ptr; + + /* Protects against concurrent pushbuf access to this channel, lock is + * grabbed by evo_wait (if the pushbuf reservation is successful) and + * dropped again by evo_kick. */ + struct mutex lock; }; static void @@ -271,6 +276,8 @@ nv50_dmac_create(struct nouveau_object *core, u32 bclass, u8 head, u32 pushbuf = *(u32 *)data; int ret; + mutex_init(&dmac->lock); + dmac->ptr = pci_alloc_consistent(nv_device(core)->pdev, PAGE_SIZE, &dmac->handle); if (!dmac->ptr) @@ -395,11 +402,13 @@ evo_wait(void *evoc, int nr) struct nv50_dmac *dmac = evoc; u32 put = nv_ro32(dmac->base.user, 0x0000) / 4; + mutex_lock(&dmac->lock); if (put + nr >= (PAGE_SIZE / 4) - 8) { dmac->ptr[put] = 0x20000000; nv_wo32(dmac->base.user, 0x0000, 0x00000000); if (!nv_wait(dmac->base.user, 0x0004, ~0, 0x00000000)) { + mutex_unlock(&dmac->lock); NV_ERROR(dmac->base.user, "channel stalled\n"); return NULL; } @@ -415,6 +424,7 @@ evo_kick(u32 *push, void *evoc) { struct nv50_dmac *dmac = evoc; nv_wo32(dmac->base.user, 0x0000, (push - dmac->ptr) << 2); + mutex_unlock(&dmac->lock); } #define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) From 0ae6d7bc0e70dafc1739d50b2b8d9d7c61968395 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 21:52:30 +0100 Subject: [PATCH 1038/4607] drm/nouveau: try to protect nbo->pin_refcount ... by moving the bo_pin/bo_unpin manipulation of the pin_refcount under the protection of the ttm reservation lock. pin/unpin seems to get called from all over the place, so atm this is completely racy. After this patch there are only a few places in cleanup functions left which access ->pin_refcount without locking. But I'm hoping that those are safe and some other code invariant guarantees that this won't blow up. In any case, I only need to fix up pin/unpin to make ->pageflip work safely, so let's keep it at that. Add a comment to the header to explain the new locking rule. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/nouveau/nouveau_bo.c | 22 +++++++++++----------- drivers/gpu/drm/nouveau/nouveau_bo.h | 2 ++ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 69d7b1d0b9d6..64d6e3047dee 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -300,17 +300,18 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype) struct ttm_buffer_object *bo = &nvbo->bo; int ret; + ret = ttm_bo_reserve(bo, false, false, false, 0); + if (ret) + goto out; + if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) { NV_ERROR(drm, "bo %p pinned elsewhere: 0x%08x vs 0x%08x\n", bo, 1 << bo->mem.mem_type, memtype); - return -EINVAL; + ret = -EINVAL; + goto out; } if (nvbo->pin_refcnt++) - return 0; - - ret = ttm_bo_reserve(bo, false, false, false, 0); - if (ret) goto out; nouveau_bo_placement_set(nvbo, memtype, 0); @@ -328,10 +329,8 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype) break; } } - ttm_bo_unreserve(bo); out: - if (unlikely(ret)) - nvbo->pin_refcnt--; + ttm_bo_unreserve(bo); return ret; } @@ -342,13 +341,13 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo) struct ttm_buffer_object *bo = &nvbo->bo; int ret; - if (--nvbo->pin_refcnt) - return 0; - ret = ttm_bo_reserve(bo, false, false, false, 0); if (ret) return ret; + if (--nvbo->pin_refcnt) + goto out; + nouveau_bo_placement_set(nvbo, bo->mem.placement, 0); ret = nouveau_bo_validate(nvbo, false, false); @@ -365,6 +364,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo) } } +out: ttm_bo_unreserve(bo); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index 25ca37989d2c..81d00fe03b56 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -28,6 +28,8 @@ struct nouveau_bo { struct nouveau_drm_tile *tile; struct drm_gem_object *gem; + + /* protect by the ttm reservation lock */ int pin_refcnt; struct ttm_bo_kmap_obj dma_buf_vmap; From af26ef3b3978349cfbd864163a6ebb4906b733b5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 13 Dec 2012 23:07:50 +0100 Subject: [PATCH 1039/4607] drm/: Unified handling of unimplemented fb->create_handle Some drivers don't have real ->create_handle callbacks. - cirrus/ast/mga200: Returns either 0 or -EINVAL. - udl: Didn't even bother with a callback, leading to a nice userspace-triggerable OOPS. - vmwgfx: This driver bothered with an implementation to return 0 as the handle (which is the canonical no-obj gem handle). All have in common that ->create_handle doesn't really make too much sense for them - that ioctl is used only for seamless fb takeover in the radeon/nouveau/i915 ddx drivers. So allow drivers to not implement this and return a consistent -ENODEV. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_main.c | 8 -------- drivers/gpu/drm/cirrus/cirrus_main.c | 8 -------- drivers/gpu/drm/drm_crtc.c | 5 ++++- drivers/gpu/drm/mgag200/mgag200_main.c | 8 -------- drivers/gpu/drm/udl/udl_fb.c | 1 - drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 12 ------------ 6 files changed, 4 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index d5ba7097e5b5..f60fd7bd1183 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -246,16 +246,8 @@ static void ast_user_framebuffer_destroy(struct drm_framebuffer *fb) kfree(fb); } -static int ast_user_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file, - unsigned int *handle) -{ - return -EINVAL; -} - static const struct drm_framebuffer_funcs ast_fb_funcs = { .destroy = ast_user_framebuffer_destroy, - .create_handle = ast_user_framebuffer_create_handle, }; diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 364474c66202..35cbae827771 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -23,16 +23,8 @@ static void cirrus_user_framebuffer_destroy(struct drm_framebuffer *fb) kfree(fb); } -static int cirrus_user_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) -{ - return 0; -} - static const struct drm_framebuffer_funcs cirrus_fb_funcs = { .destroy = cirrus_user_framebuffer_destroy, - .create_handle = cirrus_user_framebuffer_create_handle, }; int cirrus_framebuffer_init(struct drm_device *dev, diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 8d665fafc15e..a9abf49bb3ef 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2384,7 +2384,10 @@ int drm_mode_getfb(struct drm_device *dev, r->depth = fb->depth; r->bpp = fb->bits_per_pixel; r->pitch = fb->pitches[0]; - fb->funcs->create_handle(fb, file_priv, &r->handle); + if (fb->funcs->create_handle) + ret = fb->funcs->create_handle(fb, file_priv, &r->handle); + else + ret = -ENODEV; out: mutex_unlock(&dev->mode_config.mutex); diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 266438af61ea..64297c72464f 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -23,16 +23,8 @@ static void mga_user_framebuffer_destroy(struct drm_framebuffer *fb) kfree(fb); } -static int mga_user_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) -{ - return 0; -} - static const struct drm_framebuffer_funcs mga_fb_funcs = { .destroy = mga_user_framebuffer_destroy, - .create_handle = mga_user_framebuffer_create_handle, }; int mgag200_framebuffer_init(struct drm_device *dev, diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index 829c4a76938c..f8904b4e68de 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -422,7 +422,6 @@ static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb) static const struct drm_framebuffer_funcs udlfb_funcs = { .destroy = udl_user_framebuffer_destroy, .dirty = udl_user_framebuffer_dirty, - .create_handle = NULL, }; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index edc97929c9a3..9c0876b908ae 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -373,16 +373,6 @@ void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) * Generic framebuffer code */ -int vmw_framebuffer_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, - unsigned int *handle) -{ - if (handle) - *handle = 0; - - return 0; -} - /* * Surface framebuffer code */ @@ -610,7 +600,6 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = { .destroy = vmw_framebuffer_surface_destroy, .dirty = vmw_framebuffer_surface_dirty, - .create_handle = vmw_framebuffer_create_handle, }; static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, @@ -961,7 +950,6 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = { .destroy = vmw_framebuffer_dmabuf_destroy, .dirty = vmw_framebuffer_dmabuf_dirty, - .create_handle = vmw_framebuffer_create_handle, }; /** From 2d13b6796e420ed00389b7399a5d5ac7b1fed436 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 13:47:23 +0100 Subject: [PATCH 1040/4607] drm: encapsulate crtc->set_config calls With refcounting we need to adjust framebuffer refcounts at each callsite - much easier to do if they all call the same little helper function. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 19 +++++++++++++++++-- drivers/gpu/drm/drm_fb_helper.c | 6 +++--- drivers/gpu/drm/i2c/ch7006_drv.c | 2 +- drivers/gpu/drm/nouveau/nv04_display.c | 2 +- drivers/gpu/drm/nouveau/nv17_tv.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 2 +- include/drm/drm_crtc.h | 1 + 7 files changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index a9abf49bb3ef..7ca2f28348e6 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -381,7 +381,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) memset(&set, 0, sizeof(struct drm_mode_set)); set.crtc = crtc; set.fb = NULL; - ret = crtc->funcs->set_config(&set); + ret = drm_mode_set_config_internal(&set); if (ret) DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); } @@ -1800,6 +1800,21 @@ out: return ret; } +/** + * drm_mode_set_config_internal - helper to call ->set_config + * @set: modeset config to set + * + * This is a little helper to wrap internal calls to the ->set_config driver + * interface. The only thing it adds is correct refcounting dance. + */ +int drm_mode_set_config_internal(struct drm_mode_set *set) +{ + struct drm_crtc *crtc = set->crtc; + + return crtc->funcs->set_config(set); +} +EXPORT_SYMBOL(drm_mode_set_config_internal); + /** * drm_mode_setcrtc - set CRTC configuration * @dev: drm device for the ioctl @@ -1963,7 +1978,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, set.connectors = connector_set; set.num_connectors = crtc_req->count_connectors; set.fb = fb; - ret = crtc->funcs->set_config(&set); + ret = drm_mode_set_config_internal(&set); out: kfree(connector_set); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 954d175bd7fa..82c3a9ff80a5 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -245,7 +245,7 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) int i, ret; for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; - ret = mode_set->crtc->funcs->set_config(mode_set); + ret = drm_mode_set_config_internal(mode_set); if (ret) error = true; } @@ -675,7 +675,7 @@ int drm_fb_helper_set_par(struct fb_info *info) mutex_lock(&dev->mode_config.mutex); for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; - ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); + ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set); if (ret) { mutex_unlock(&dev->mode_config.mutex); return ret; @@ -711,7 +711,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, modeset->y = var->yoffset; if (modeset->num_connectors) { - ret = crtc->funcs->set_config(modeset); + ret = drm_mode_set_config_internal(modeset); if (!ret) { info->var.xoffset = var->xoffset; info->var.yoffset = var->yoffset; diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c index b865d0728e28..51fa32392029 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -364,7 +364,7 @@ static int ch7006_encoder_set_property(struct drm_encoder *encoder, .crtc = crtc, }; - crtc->funcs->set_config(&modeset); + drm_mode_set_config_internal(&modeset); } } diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 2cd6fb8c548e..4c6e9f83fe82 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -140,7 +140,7 @@ nv04_display_destroy(struct drm_device *dev) .crtc = crtc, }; - crtc->funcs->set_config(&modeset); + drm_mode_set_config_internal(&modeset); } /* Restore state */ diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c index 2ca276ada507..977e42be2050 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv.c +++ b/drivers/gpu/drm/nouveau/nv17_tv.c @@ -768,7 +768,7 @@ static int nv17_tv_set_property(struct drm_encoder *encoder, .crtc = crtc, }; - crtc->funcs->set_config(&modeset); + drm_mode_set_config_internal(&modeset); } } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 161f8b2549aa..07dfd823cc30 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -829,7 +829,7 @@ static void vmw_lastclose(struct drm_device *dev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { set.crtc = crtc; - ret = crtc->funcs->set_config(&set); + ret = drm_mode_set_config_internal(&set); WARN_ON(ret != 0); } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 00d78b5161c0..665289a68cca 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -985,6 +985,7 @@ extern int drm_mode_getcrtc(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_getconnector(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int drm_mode_set_config_internal(struct drm_mode_set *set); extern int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_getplane(struct drm_device *dev, From 848499032504b1defdebdcf930aa70bd01a45ac1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 00:28:11 +0100 Subject: [PATCH 1041/4607] drm: add drm_modeset_lock|unlock_all This is the first step towards introducing the new modeset locking scheme. The plan is to put helper functions into place at all the right places step-by-step, so that the final patch to switch on the new locking scheme doesn't need to touch every single driver. This helper here will serve as the shotgun solutions for all places where a more fine-grained locking isn't (yet) implemented. v2: Fixup kerneldoc for unlock_all. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 147 +++++++++++++++++------------- drivers/gpu/drm/drm_crtc_helper.c | 8 +- drivers/gpu/drm/drm_fb_helper.c | 20 ++-- include/drm/drm_crtc.h | 3 + 4 files changed, 102 insertions(+), 76 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 7ca2f28348e6..a82ec05dee23 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -37,6 +37,29 @@ #include #include +/** + * drm_modeset_lock_all - take all modeset locks + * @dev: drm device + * + * This function takes all modeset locks, suitable where a more fine-grained + * scheme isn't (yet) implemented. + */ +void drm_modeset_lock_all(struct drm_device *dev) +{ + mutex_lock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_modeset_lock_all); + +/** + * drm_modeset_unlock_all - drop all modeset locks + * @dev: device + */ +void drm_modeset_unlock_all(struct drm_device *dev) +{ + mutex_unlock(&dev->mode_config.mutex); +} +EXPORT_SYMBOL(drm_modeset_unlock_all); + /* Avoid boilerplate. I'm tired of typing. */ #define DRM_ENUM_NAME_FN(fnname, list) \ char *fnname(int val) \ @@ -425,7 +448,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, crtc->funcs = funcs; crtc->invert_dimensions = false; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) @@ -437,7 +460,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, dev->mode_config.num_crtc++; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -512,7 +535,7 @@ int drm_connector_init(struct drm_device *dev, { int ret; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); if (ret) @@ -542,7 +565,7 @@ int drm_connector_init(struct drm_device *dev, dev->mode_config.dpms_property, 0); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -592,7 +615,7 @@ int drm_encoder_init(struct drm_device *dev, { int ret; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); if (ret) @@ -606,7 +629,7 @@ int drm_encoder_init(struct drm_device *dev, dev->mode_config.num_encoder++; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -615,11 +638,11 @@ EXPORT_SYMBOL(drm_encoder_init); void drm_encoder_cleanup(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); drm_mode_object_put(dev, &encoder->base); list_del(&encoder->head); dev->mode_config.num_encoder--; - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_encoder_cleanup); @@ -631,7 +654,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, { int ret; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); if (ret) @@ -665,7 +688,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -675,7 +698,7 @@ void drm_plane_cleanup(struct drm_plane *plane) { struct drm_device *dev = plane->dev; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); kfree(plane->format_types); drm_mode_object_put(dev, &plane->base); /* if not added to a list, it must be a private plane */ @@ -683,7 +706,7 @@ void drm_plane_cleanup(struct drm_plane *plane) list_del(&plane->head); dev->mode_config.num_plane--; } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_plane_cleanup); @@ -963,9 +986,9 @@ void drm_mode_config_init(struct drm_device *dev) INIT_LIST_HEAD(&dev->mode_config.plane_list); idr_init(&dev->mode_config.crtc_idr); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); drm_mode_create_standard_connector_properties(dev); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); /* Just to be sure */ dev->mode_config.num_fb = 0; @@ -1185,7 +1208,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); /* * For the non-control nodes we need to limit the list of resources @@ -1327,7 +1350,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, card_res->count_connectors, card_res->count_encoders); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1355,7 +1378,7 @@ int drm_mode_getcrtc(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_resp->crtc_id, DRM_MODE_OBJECT_CRTC); @@ -1383,7 +1406,7 @@ int drm_mode_getcrtc(struct drm_device *dev, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1426,7 +1449,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); @@ -1523,7 +1546,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->count_encoders = encoders_count; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1538,7 +1561,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, enc_resp->encoder_id, DRM_MODE_OBJECT_ENCODER); if (!obj) { @@ -1557,7 +1580,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, enc_resp->possible_clones = encoder->possible_clones; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1581,7 +1604,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); config = &dev->mode_config; /* @@ -1603,7 +1626,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, plane_resp->count_planes = config->num_plane; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1628,7 +1651,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, plane_resp->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { @@ -1668,7 +1691,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data, plane_resp->count_format_types = plane->format_count; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1696,7 +1719,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); /* * First, find the plane, crtc, and fb objects. If not available, @@ -1795,7 +1818,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1850,7 +1873,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX) return -ERANGE; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { @@ -1983,7 +2006,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, out: kfree(connector_set); drm_mode_destroy(dev, mode); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2001,7 +2024,7 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); @@ -2029,7 +2052,7 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, } } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2108,7 +2131,7 @@ int drm_mode_addfb(struct drm_device *dev, if ((config->min_height > r.height) || (r.height > config->max_height)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); /* TODO check buffer is sufficiently large */ /* TODO setup destructor callback */ @@ -2125,7 +2148,7 @@ int drm_mode_addfb(struct drm_device *dev, DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2293,7 +2316,7 @@ int drm_mode_addfb2(struct drm_device *dev, if (ret) return ret; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); if (IS_ERR(fb)) { @@ -2307,7 +2330,7 @@ int drm_mode_addfb2(struct drm_device *dev, DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2337,7 +2360,7 @@ int drm_mode_rmfb(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); /* TODO check that we really get a framebuffer back. */ if (!obj) { @@ -2358,7 +2381,7 @@ int drm_mode_rmfb(struct drm_device *dev, drm_framebuffer_remove(fb); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2386,7 +2409,7 @@ int drm_mode_getfb(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { ret = -EINVAL; @@ -2405,7 +2428,7 @@ int drm_mode_getfb(struct drm_device *dev, ret = -ENODEV; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2424,7 +2447,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { ret = -EINVAL; @@ -2478,7 +2501,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, out_err2: kfree(clips); out_err1: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2499,11 +2522,11 @@ void drm_fb_release(struct drm_file *priv) struct drm_device *dev = priv->minor->dev; struct drm_framebuffer *fb, *tfb; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { drm_framebuffer_remove(fb); } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } /** @@ -2618,7 +2641,7 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { @@ -2642,7 +2665,7 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev, drm_mode_attachmode(dev, connector, mode); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2671,7 +2694,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { @@ -2688,7 +2711,7 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev, ret = drm_mode_detachmode(dev, connector, &mode); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -2934,7 +2957,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); if (!obj) { ret = -EINVAL; @@ -3012,7 +3035,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, out_resp->count_enum_blobs = blob_count; } done: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3063,7 +3086,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); if (!obj) { ret = -EINVAL; @@ -3081,7 +3104,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, out_resp->length = blob->length; done: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3223,7 +3246,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); if (!obj) { @@ -3260,7 +3283,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, } arg->count_props = props_count; out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3277,7 +3300,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); if (!arg_obj) @@ -3315,7 +3338,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3377,7 +3400,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { ret = -EINVAL; @@ -3418,7 +3441,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3436,7 +3459,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { ret = -EINVAL; @@ -3469,7 +3492,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, goto out; } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -3489,7 +3512,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, page_flip->reserved != 0) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) goto out; @@ -3567,7 +3590,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, } out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 7b2d378b2576..400ef86a2b43 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -980,7 +980,7 @@ static void output_poll_execute(struct work_struct *work) if (!drm_kms_helper_poll) return; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { /* Ignore forced connectors. */ @@ -1010,7 +1010,7 @@ static void output_poll_execute(struct work_struct *work) changed = true; } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); if (changed) drm_kms_helper_hotplug_event(dev); @@ -1070,7 +1070,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev) if (!dev->mode_config.poll_enabled) return; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { /* Only handle HPD capable connectors. */ @@ -1088,7 +1088,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev) changed = true; } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); if (changed) drm_kms_helper_hotplug_event(dev); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 82c3a9ff80a5..be0f2d65db3a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -337,7 +337,7 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) /* * For each CRTC in this fb, turn the connectors on/off. */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -352,7 +352,7 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) dev->mode_config.dpms_property, dpms_mode); } } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } int drm_fb_helper_blank(int blank, struct fb_info *info) @@ -672,16 +672,16 @@ int drm_fb_helper_set_par(struct fb_info *info) return -EINVAL; } - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set); if (ret) { - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); if (fb_helper->delayed_hotplug) { fb_helper->delayed_hotplug = false; @@ -701,7 +701,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, int ret = 0; int i; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -718,7 +718,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, } } } - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } EXPORT_SYMBOL(drm_fb_helper_pan_display); @@ -1375,7 +1375,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) if (!fb_helper->fb) return 0; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (crtc->fb) crtcs_bound++; @@ -1385,7 +1385,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) if (bound < crtcs_bound) { fb_helper->delayed_hotplug = true; - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return 0; } DRM_DEBUG_KMS("\n"); @@ -1397,7 +1397,7 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height); drm_setup_crtcs(fb_helper); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 665289a68cca..9f0524d507f0 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -842,6 +842,9 @@ struct drm_prop_enum_list { char *name; }; +extern void drm_modeset_lock_all(struct drm_device *dev); +extern void drm_modeset_unlock_all(struct drm_device *dev); + extern int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, const struct drm_crtc_funcs *funcs); From a0e99e68c12ac6dc5d6b1da7942b5e05d5f848af Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 01:05:46 +0100 Subject: [PATCH 1042/4607] drm/i915: use drm_modeset_lock_all Two exceptions: - debugfs files only read information which is not related to crtc, so can stay on the modeset_config lock. - Same holds for the edp vdd work in intel_dp.c. Add a corresponding WARN_ON and a comment next to the intel_dp struct fields for documentation. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 2 ++ drivers/gpu/drm/i915/intel_fb.c | 4 ++-- drivers/gpu/drm/i915/intel_lvds.c | 4 ++-- drivers/gpu/drm/i915/intel_overlay.c | 14 +++++++------- drivers/gpu/drm/i915/intel_sprite.c | 8 ++++---- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1b63d55318a0..6912d29f46f3 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1057,6 +1057,8 @@ static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) { pp = ironlake_get_pp_control(dev_priv); pp &= ~EDP_FORCE_VDD; diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 7b30b5c2c4ee..a9f7280f87fb 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -280,7 +280,7 @@ void intel_fb_restore_mode(struct drm_device *dev) struct drm_mode_config *config = &dev->mode_config; struct drm_plane *plane; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper); if (ret) @@ -290,5 +290,5 @@ void intel_fb_restore_mode(struct drm_device *dev) list_for_each_entry(plane, &config->plane_list, head) plane->funcs->disable_plane(plane); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 17aee74258ad..279e9d734ece 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -531,9 +531,9 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, dev_priv->modeset_on_lid = 0; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); intel_modeset_setup_hw_state(dev, true); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return NOTIFY_OK; } diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index d7bc817f51a0..5be09d125509 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -1045,13 +1045,13 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, } if (!(put_image_rec->flags & I915_OVERLAY_ENABLE)) { - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); mutex_lock(&dev->struct_mutex); ret = intel_overlay_switch_off(overlay); mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -1075,7 +1075,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, goto out_free; } - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); mutex_lock(&dev->struct_mutex); if (new_bo->tiling_mode) { @@ -1157,7 +1157,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, goto out_unlock; mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); kfree(params); @@ -1165,7 +1165,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, out_unlock: mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); drm_gem_object_unreference_unlocked(&new_bo->base); out_free: kfree(params); @@ -1241,7 +1241,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, return -ENODEV; } - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); mutex_lock(&dev->struct_mutex); ret = -EINVAL; @@ -1307,7 +1307,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, ret = 0; out_unlock: mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index d7b060e0a231..f8293061d6bd 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -593,7 +593,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, if ((set->flags & (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) == (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) return -EINVAL; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { @@ -606,7 +606,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, ret = intel_plane->update_colorkey(plane, set); out_unlock: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } @@ -622,7 +622,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { @@ -635,7 +635,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, intel_plane->get_colorkey(plane, get); out_unlock: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; } From 0a819515fc346b4e79d6e3fc01d837a660452c74 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 01:33:17 +0100 Subject: [PATCH 1043/4607] drm/gma500: use drm_modeset_lock_all Only two places: - suspend/resume - Some really strange mode validation tool with too much funny-lucking hand-rolled conversion code. - The recently-added lastclose fbdev restore code. Better safe than sorry, so convert both places to keep the locking semantics as much as possible. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/gma500/psb_device.c | 8 ++++---- drivers/gpu/drm/gma500/psb_drv.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index b58c4701c4e8..f6f534b4197e 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -194,7 +194,7 @@ static int psb_save_display_registers(struct drm_device *dev) regs->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); /* Save crtc and output state */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (drm_helper_crtc_in_use(crtc)) crtc->funcs->save(crtc); @@ -204,7 +204,7 @@ static int psb_save_display_registers(struct drm_device *dev) if (connector->funcs->save) connector->funcs->save(connector); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return 0; } @@ -234,7 +234,7 @@ static int psb_restore_display_registers(struct drm_device *dev) /*make sure VGA plane is off. it initializes to on after reset!*/ PSB_WVDC32(0x80000000, VGACNTRL); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) if (drm_helper_crtc_in_use(crtc)) crtc->funcs->restore(crtc); @@ -243,7 +243,7 @@ static int psb_restore_display_registers(struct drm_device *dev) if (connector->funcs->restore) connector->funcs->restore(connector); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return 0; } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index dbcefe9f78fa..111e3df9c5de 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -153,11 +153,11 @@ static void psb_lastclose(struct drm_device *dev) struct drm_psb_private *dev_priv = dev->dev_private; struct psb_fbdev *fbdev = dev_priv->fbdev; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); if (ret) DRM_DEBUG("failed to restore crtc mode\n"); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return; } @@ -486,7 +486,7 @@ static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, case PSB_MODE_OPERATION_MODE_VALID: umode = &arg->mode; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR); @@ -535,7 +535,7 @@ static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, if (mode) drm_mode_destroy(dev, mode); mode_op_out: - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return ret; default: From 795f1426b484ddfc136e723a26bfd6a46c8ab085 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 01:34:59 +0100 Subject: [PATCH 1044/4607] drm/ast: use drm_modeset_lock_all Just a call to drm_helper_resume_force_mode, obviously wants full locking for that. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 2d2c2f8d6dc6..df0d0a08097a 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -94,9 +94,9 @@ static int ast_drm_thaw(struct drm_device *dev) ast_post_gpu(dev); drm_mode_config_reset(dev); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); drm_helper_resume_force_mode(dev); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); console_lock(); ast_fbdev_set_suspend(dev, 0); From b13f59804918ad584a97a639064a24d14e2fd740 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 01:36:59 +0100 Subject: [PATCH 1045/4607] drm/shmobile: use drm_modeset_lock_all Only a resume method to account for. v2: Fixup compilation. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/shmobile/shmob_drm_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index d1d5306ebf24..f6e0b5395051 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -313,9 +313,9 @@ static int shmob_drm_pm_resume(struct device *dev) { struct shmob_drm_device *sdev = dev_get_drvdata(dev); - mutex_lock(&sdev->ddev->mode_config.mutex); + drm_modeset_lock_all(sdev->ddev); shmob_drm_crtc_resume(&sdev->crtc); - mutex_unlock(&sdev->ddev->mode_config.mutex); + drm_modeset_unlock_all(sdev->ddev); drm_kms_helper_poll_enable(sdev->ddev); return 0; From bbe4b99ff2443e305598768ae8eac6bc3516b7c9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 01:48:38 +0100 Subject: [PATCH 1046/4607] drm/vmwgfx: use drm_modeset_lock_all Ok, this one here is a bit more complicated, and I can't really claim to fully understand the locking and lifetime rules of the vmwgfx driver. So just convert ever mutex_lock call, including the interruptible one. Since other places (e.g. in the execbuf ioctl) take the mode_config.mutex without bothering with interruptible handling, I've figured I should be able to get away with this in a few more places ... Signed-off-by: Daniel Vetter --- drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index d9fbbe191071..a135498a1298 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -163,11 +163,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, goto out_no_copy; } - ret = mutex_lock_interruptible(&dev->mode_config.mutex); - if (unlikely(ret != 0)) { - ret = -ERESTARTSYS; - goto out_no_mode_mutex; - } + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { @@ -200,8 +196,7 @@ out_no_surface: ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: out_no_fb: - mutex_unlock(&dev->mode_config.mutex); -out_no_mode_mutex: + drm_modeset_unlock_all(dev); out_no_copy: kfree(clips); out_clips: @@ -251,11 +246,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, goto out_no_copy; } - ret = mutex_lock_interruptible(&dev->mode_config.mutex); - if (unlikely(ret != 0)) { - ret = -ERESTARTSYS; - goto out_no_mode_mutex; - } + drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { @@ -282,8 +273,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: out_no_fb: - mutex_unlock(&dev->mode_config.mutex); -out_no_mode_mutex: + drm_modeset_unlock_all(dev); out_no_copy: kfree(clips); out_clips: From d5d2636ed7990b93c7216f6a4d323f6b0eee08af Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 15:50:41 +0100 Subject: [PATCH 1047/4607] omapdrm: use modeset_lock_all I've left the locking in the debugfs code as-is, it's essentially just used to keep the framebuffer object alive (which won't be necessary any more later on). We don't need fb refcounting either, since the new mode_config.fb_lock ensures that the framebuffers can't disappear (once mode_config.mutex doesn't guarantee this any more later on in the series). The fbcon restore needs all modeset locks. The crtc callbacks seem to only need the crtc locks, but I've quickly discussed things with Rob Clark and he's fine with just using modeset_lock_all for those, too. He'll look into converting things over later. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/staging/omapdrm/omap_crtc.c | 8 ++++---- drivers/staging/omapdrm/omap_drv.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c index 5c6ed6040eff..510942e67020 100644 --- a/drivers/staging/omapdrm/omap_crtc.c +++ b/drivers/staging/omapdrm/omap_crtc.c @@ -278,13 +278,13 @@ static void page_flip_worker(struct work_struct *work) struct drm_display_mode *mode = &crtc->mode; struct drm_gem_object *bo; - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb, 0, 0, mode->hdisplay, mode->vdisplay, crtc->x << 16, crtc->y << 16, mode->hdisplay << 16, mode->vdisplay << 16, vblank_cb, crtc); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); bo = omap_framebuffer_bo(crtc->fb, 0); drm_gem_object_unreference_unlocked(bo); @@ -417,7 +417,7 @@ static void apply_worker(struct work_struct *work) * the callbacks and list modification all serialized * with respect to modesetting ioctls from userspace. */ - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); dispc_runtime_get(); /* @@ -462,7 +462,7 @@ static void apply_worker(struct work_struct *work) out: dispc_runtime_put(); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); } int omap_crtc_apply(struct drm_crtc *crtc, diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c index ae5ecc2efbc7..dfdb4ba1e7c6 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/staging/omapdrm/omap_drv.c @@ -449,9 +449,9 @@ static void dev_lastclose(struct drm_device *dev) } } - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); if (ret) DBG("failed to restore crtc mode"); } From 29494c174dc4793ebd236aa522a2a1ed73b7180e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 02:18:25 +0100 Subject: [PATCH 1048/4607] drm: add per-crtc locks *drumroll* The basic idea is to protect per-crtc state which can change without touching the output configuration with separate mutexes, i.e. all the input side state to a crtc like framebuffers, cursor settings or plane configuration. Holding such a crtc lock gives a read-lock on all the other crtc state which can be changed by e.g. a modeset. All non-crtc state is still protected by the mode_config mutex. Callers that need to change modeset state of a crtc (e.g. dpms or set_mode) need to grab both the mode_config lock and nested within any crtc locks. Note that since there can only ever be one holder of the mode_config lock we can grab the subordinate crtc locks in any order (if we need to grab more than one of them). Lockdep can handle such nesting with the mutex_lock_nest_lock call correctly. With this functions that only touch connectors/encoders but not crtcs only need to take the mode_config lock. The biggest such case is the output probing, which means that we can now pageflip and move cursors while the output probe code is reading an edid. Most cases neatly fall into the three buckets: - Only touches connectors and similar output state and so only needs the mode_config lock. - Touches the global configuration and so needs all locks. - Only touches the crtc input side and so only needs the crtc lock. But a few cases that need special consideration: - Load detection which requires a crtc. The mode_config lock already prevents a modeset change, so we can use any unused crtc as we like to do load detection. The only thing to consider is that such temporary state changes don't leak out to userspace through ioctls that only take the crtc look (like a pageflip). Hence the load detect code needs to grab the crtc of any output pipes it touches (but only if it touches state used by the pageflip or cursor ioctls). - Atomic pageflip when moving planes. The first case is sane hw, where planes have a fixed association with crtcs - nothing needs to be done there. More insane^Wflexible hw needs to have plane->crtc mapping which is separately protect with a lock that nests within the crtc lock. If the plane is unused we can just assign it to the current crtc and continue. But if a plane is already in use by another crtc we can't just reassign it. Two solution present themselves: Either go back to a slow-path which takes all modeset locks, potentially incure quite a hefty delay. Or simply disallowing such changes in one atomic pageflip - in general the vblanks of two crtcs are not synced, so there's no sane way to atomically flip such plane changes accross more than one crtc. I'd heavily favour the later approach, going as far as mandating it as part of the ABI of such a new a nuclear pageflip. And if we _really_ want such semantics, we can always get them by introducing another pageflip mutex between the mode_config.mutex and the individual crtc locks. Pageflips crossing more than one crtc would then need to take that lock first, to lock out concurrent multi-crtc pageflips. - Optimized global modeset operations: We could just take the mode_config lock and then lazily lock all crtc which are affected by a modeset operation. This has the advantage that pageflip could continue unhampered on unaffected crtc. But if e.g. global resources like plls need to be reassigned and so affect unrelated crtcs we can still do that - nested locking works in any order. This patch just adds the locks and takes them in drm_modeset_lock_all, no real locking changes yet. v2: Need to initialize the new lock in crtc_init and lock it righ away, for otherwise the modeset_unlock_all below will try to unlock a not-locked mutex. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 12 ++++++++++++ include/drm/drm_crtc.h | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index a82ec05dee23..b7c6168fae7e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -46,7 +46,12 @@ */ void drm_modeset_lock_all(struct drm_device *dev) { + struct drm_crtc *crtc; + mutex_lock(&dev->mode_config.mutex); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); } EXPORT_SYMBOL(drm_modeset_lock_all); @@ -56,6 +61,11 @@ EXPORT_SYMBOL(drm_modeset_lock_all); */ void drm_modeset_unlock_all(struct drm_device *dev) { + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + mutex_unlock(&crtc->mutex); + mutex_unlock(&dev->mode_config.mutex); } EXPORT_SYMBOL(drm_modeset_unlock_all); @@ -449,6 +459,8 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, crtc->invert_dimensions = false; drm_modeset_lock_all(dev); + mutex_init(&crtc->mutex); + mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 9f0524d507f0..c89b1161f0be 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -390,6 +390,15 @@ struct drm_crtc { struct drm_device *dev; struct list_head head; + /** + * crtc mutex + * + * This provides a read lock for the overall crtc state (mode, dpms + * state, ...) and a write lock for everything which can be update + * without a full modeset (fb, cursor data, ...) + */ + struct mutex mutex; + struct drm_mode_object base; /* framebuffer the connector is currently bound to */ From bfb899282f500eeb9dff2600729904aad0fd39e7 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 13:48:21 +0100 Subject: [PATCH 1049/4607] drm: only take the crtc lock for ->cursor_set First convert ->cursor_set to only take the crtc lock, since that seems to be the function with the least amount of state - the core ioctl function doesn't check anything which can change at runtime, so we don't have any object lifetime issues to contend. The only thing which is important is that the driver's implementation doesn't touch any state outside of that single crtc which is not yet properly protected by other locking: - ast: access the global ast->cache_kmap. Luckily we only have on crtc on this driver, so this is fine. Add a comment. - gma500: calls gma_power_begin|and and psb_gtt_pin|unpin, both which have their own locking to protect their state. Everything else is crtc-local. - i915: touches a bit of global gem state, all protected by the One Lock to Rule Them All (dev->struct_mutex). - nouveau: Pre-nv50 is all nice, nv50+ uses the evo channels to queue up all display changes. And some of these channels are device global. But this is fine now since the previous patch introduced an evo channel mutex. - radeon: Uses some indirect register access for cursor updates, but with the previous patches to protect these indirect 2-register access patterns with a spinlock, this should be fine now, too. - vmwgfx: I have no idea how that works - update_cursor_position doesn't take any per-crtc argument and I haven't figured out any other place where this could be set in some form of a side-channel. But vmwgfx definitely has more than one crtc (or at least can register more than one), so I have no idea how this is supposed to not fail with the current code already. Hence take the easy way out and simply acquire all locks (which requires dropping the crtc lock the core acquired for us). That way it's not worse off for consistency than the old code. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_drv.h | 2 ++ drivers/gpu/drm/drm_crtc.c | 6 ++++-- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 32 +++++++++++++++++++++++------ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 5ccf984f063a..528429252f0f 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -98,6 +98,8 @@ struct ast_private { struct drm_gem_object *cursor_cache; uint64_t cursor_cache_gpu_addr; + /* Acces to this cache is protected by the crtc->mutex of the only crtc + * we have. */ struct ttm_bo_kmap_obj cache_kmap; int next_cursor; }; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index b7c6168fae7e..58fa69e5ff4c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2036,7 +2036,6 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL; - drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); @@ -2051,20 +2050,23 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, goto out; } /* Turns off the cursor if handle is 0 */ + mutex_lock(&crtc->mutex); ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, req->width, req->height); + mutex_unlock(&crtc->mutex); } if (req->flags & DRM_MODE_CURSOR_MOVE) { if (crtc->funcs->cursor_move) { + drm_modeset_lock_all(dev); ret = crtc->funcs->cursor_move(crtc, req->x, req->y); + drm_modeset_unlock_all(dev); } else { ret = -EFAULT; goto out; } } out: - drm_modeset_unlock_all(dev); return ret; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 9c0876b908ae..8d82e631c305 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -180,16 +180,29 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, struct vmw_dma_buffer *dmabuf = NULL; int ret; + /* + * FIXME: Unclear whether there's any global state touched by the + * cursor_set function, especially vmw_cursor_update_position looks + * suspicious. For now take the easy route and reacquire all locks. We + * can do this since the caller in the drm core doesn't check anything + * which is protected by any looks. + */ + mutex_unlock(&crtc->mutex); + drm_modeset_lock_all(dev_priv->dev); + /* A lot of the code assumes this */ - if (handle && (width != 64 || height != 64)) - return -EINVAL; + if (handle && (width != 64 || height != 64)) { + ret = -EINVAL; + goto out; + } if (handle) { ret = vmw_user_lookup_handle(dev_priv, tfile, handle, &surface, &dmabuf); if (ret) { DRM_ERROR("failed to find surface or dmabuf: %i\n", ret); - return -EINVAL; + ret = -EINVAL; + goto out; } } @@ -197,7 +210,8 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, if (surface && !surface->snooper.image) { DRM_ERROR("surface not suitable for cursor\n"); vmw_surface_unreference(&surface); - return -EINVAL; + ret = -EINVAL; + goto out; } /* takedown old cursor */ @@ -225,14 +239,20 @@ int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, du->hotspot_x, du->hotspot_y); } else { vmw_cursor_update_position(dev_priv, false, 0, 0); - return 0; + ret = 0; + goto out; } vmw_cursor_update_position(dev_priv, true, du->cursor_x + du->hotspot_x, du->cursor_y + du->hotspot_y); - return 0; + ret = 0; +out: + drm_modeset_unlock_all(dev_priv->dev); + mutex_lock(&crtc->mutex); + + return ret; } int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) From dac35663cef4ca7f572d430bb54b14be8f03cb10 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 15:24:10 +0100 Subject: [PATCH 1050/4607] drm: only take the crtc lock for ->cursor_move ->cursor_move uses mostly the same facilities in drivers as ->cursor_set, so pretty much nothing to fix up: - ast/gma500/i915: They all use per-crtc registers to update the cursor position. ast again touches the global cursor cache, but that's ok since there's only one crtc. - nouveau: nv50+ is again special, updates happen through the per-crtc channel (without pushbufs), so it's not protected by the new evo lock introduced earlier. But since this channel is per-crtc, we should be fine anyway. - radeon: A bit a mess: avivo asics need a workaround when both output pipes are enabled, which means it'll access the crtc list. Just reading that flag is ok though as long as radeon _always_ grabs all locks when changing the crtc configuration. Which means with the current scheme it cannot do an optimized modeset which only locks the relevant crtcs. This can be fixed though by introducing a bit of global state with separate locks and ensure in the modeset code that the cursor will be updated appropriately when enabling the 2nd pipe (on affected asics). - vmwgfx: I still don't understand what it's doing exactly, so apply the same trick for now. v2: Fixup unlocking for the error cases, spotted by Richard Wilbur. v3: Another error-case fixup. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 10 ++++------ drivers/gpu/drm/radeon/radeon_cursor.c | 8 +++++++- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 13 +++++++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 58fa69e5ff4c..4af6a3d5c9a1 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2039,34 +2039,32 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); - ret = -EINVAL; - goto out; + return -EINVAL; } crtc = obj_to_crtc(obj); + mutex_lock(&crtc->mutex); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set) { ret = -ENXIO; goto out; } /* Turns off the cursor if handle is 0 */ - mutex_lock(&crtc->mutex); ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, req->width, req->height); - mutex_unlock(&crtc->mutex); } if (req->flags & DRM_MODE_CURSOR_MOVE) { if (crtc->funcs->cursor_move) { - drm_modeset_lock_all(dev); ret = crtc->funcs->cursor_move(crtc, req->x, req->y); - drm_modeset_unlock_all(dev); } else { ret = -EFAULT; goto out; } } out: + mutex_unlock(&crtc->mutex); + return ret; } diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index ad6df625e8b8..c1680e6d76ad 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -245,8 +245,14 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc, int i = 0; struct drm_crtc *crtc_p; - /* avivo cursor image can't end on 128 pixel boundary or + /* + * avivo cursor image can't end on 128 pixel boundary or * go past the end of the frame if both crtcs are enabled + * + * NOTE: It is safe to access crtc->enabled of other crtcs + * without holding either the mode_config lock or the other + * crtc's lock as long as write access to this flag _always_ + * grabs all locks. */ list_for_each_entry(crtc_p, &crtc->dev->mode_config.crtc_list, head) { if (crtc_p->enabled) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 8d82e631c305..3e3c7ab33ca2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -264,10 +264,23 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) du->cursor_x = x + crtc->x; du->cursor_y = y + crtc->y; + /* + * FIXME: Unclear whether there's any global state touched by the + * cursor_set function, especially vmw_cursor_update_position looks + * suspicious. For now take the easy route and reacquire all locks. We + * can do this since the caller in the drm core doesn't check anything + * which is protected by any looks. + */ + mutex_unlock(&crtc->mutex); + drm_modeset_lock_all(dev_priv->dev); + vmw_cursor_update_position(dev_priv, shown, du->cursor_x + du->hotspot_x, du->cursor_y + du->hotspot_y); + drm_modeset_unlock_all(dev_priv->dev); + mutex_lock(&crtc->mutex); + return 0; } From 4b096ac10da0b63f09bd123b86fed8deb80646ce Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 10 Dec 2012 21:19:18 +0100 Subject: [PATCH 1051/4607] drm: revamp locking around fb creation/destruction Well, at least step 1. The goal here is that framebuffer objects can survive outside of the mode_config lock, with just a reference held as protection. The first step to get there is to introduce a special fb_lock which protects fb lookup, creation and destruction, to make them appear atomic. This new fb_lock can nest within the mode_config lock. But the idea is (once the reference counting part is completed) that we only quickly take that fb_lock to lookup a framebuffer and grab a reference, without any other locks involved. vmwgfx is the only driver which does framebuffer lookups itself, also wrap those calls to drm_mode_object_find with the new lock. Also protect the fb_list walking in i915 and omapdrm with the new lock. As a slight complication there's also the list of user-created fbs attached to the file private. The problem now is that at fclose() time we need to walk that list, eventually do a modeset call to remove the fb from active usage (and are required to be able to take the mode_config lock), but in the end we need to grab the new fb_lock to remove the fb from the list. The easiest solution is to add another mutex to protect this per-file list. Currently that new fbs_lock nests within the modeset locks and so appears redudant. But later patches will switch around this sequence so that taking the modeset locks in the fb destruction path is optional in the fastpath. Ultimately the goal is that addfb and rmfb do not require the mode_config lock, since otherwise they have the potential to introduce stalls in the pageflip sequence of a compositor (if the compositor e.g. switches to a fullscreen client or if it enables a plane). But that requires a few more steps and hoops to jump through. Note that framebuffer creation/destruction is now double-protected - once by the fb_lock and in parts by the idr_lock. The later would be unnecessariy if framebuffers would have their own idr allocator. But that's material for another patch (series). v2: Properly initialize the fb->filp_head list in _init, otherwise the newly added WARN to check whether the fb isn't on a fpriv list any more will fail for driver-private objects. v3: Fixup two error-case unlock bugs spotted by Richard Wilbur. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 116 +++++++++++++++++-------- drivers/gpu/drm/drm_fops.c | 1 + drivers/gpu/drm/i915/i915_debugfs.c | 5 +- drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 4 + drivers/staging/omapdrm/omap_debugfs.c | 2 + include/drm/drmP.h | 8 ++ include/drm/drm_crtc.h | 14 +++ 7 files changed, 113 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 4af6a3d5c9a1..13a3d3426961 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -262,15 +262,21 @@ again: mutex_lock(&dev->mode_config.idr_mutex); ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); + + if (!ret) { + /* + * Set up the object linking under the protection of the idr + * lock so that other users can't see inconsistent state. + */ + obj->id = new_id; + obj->type = obj_type; + } mutex_unlock(&dev->mode_config.idr_mutex); + if (ret == -EAGAIN) goto again; - else if (ret) - return ret; - obj->id = new_id; - obj->type = obj_type; - return 0; + return ret; } /** @@ -312,6 +318,12 @@ EXPORT_SYMBOL(drm_mode_object_find); * Allocates an ID for the framebuffer's parent mode object, sets its mode * functions & device file and adds it to the master fd list. * + * IMPORTANT: + * This functions publishes the fb and makes it available for concurrent access + * by other users. Which means by this point the fb _must_ be fully set up - + * since all the fb attributes are invariant over its lifetime, no further + * locking but only correct reference counting is required. + * * RETURNS: * Zero on success, error code on failure. */ @@ -320,16 +332,20 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, { int ret; + mutex_lock(&dev->mode_config.fb_lock); kref_init(&fb->refcount); + INIT_LIST_HEAD(&fb->filp_head); + fb->dev = dev; + fb->funcs = funcs; ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); if (ret) - return ret; + goto out; - fb->dev = dev; - fb->funcs = funcs; dev->mode_config.num_fb++; list_add(&fb->head, &dev->mode_config.fb_list); +out: + mutex_unlock(&dev->mode_config.fb_lock); return 0; } @@ -385,8 +401,10 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) * this.) */ drm_mode_object_put(dev, &fb->base); + mutex_lock(&dev->mode_config.fb_lock); list_del(&fb->head); dev->mode_config.num_fb--; + mutex_unlock(&dev->mode_config.fb_lock); } EXPORT_SYMBOL(drm_framebuffer_cleanup); @@ -406,6 +424,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) int ret; WARN_ON(!drm_modeset_is_locked(dev)); + WARN_ON(!list_empty(&fb->filp_head)); /* remove from any CRTC */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { @@ -432,8 +451,6 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) } } - list_del(&fb->filp_head); - drm_framebuffer_unreference(fb); } EXPORT_SYMBOL(drm_framebuffer_remove); @@ -989,6 +1006,7 @@ void drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); mutex_init(&dev->mode_config.idr_mutex); + mutex_init(&dev->mode_config.fb_lock); INIT_LIST_HEAD(&dev->mode_config.fb_list); INIT_LIST_HEAD(&dev->mode_config.crtc_list); INIT_LIST_HEAD(&dev->mode_config.connector_list); @@ -1091,6 +1109,9 @@ void drm_mode_config_cleanup(struct drm_device *dev) drm_property_destroy(dev, property); } + /* Single-threaded teardown context, so it's not requied to grab the + * fb_lock to protect against concurrent fb_list access. Contrary, it + * would actually deadlock with the drm_framebuffer_cleanup function. */ list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { drm_framebuffer_remove(fb); } @@ -1220,8 +1241,8 @@ int drm_mode_getresources(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); + mutex_lock(&file_priv->fbs_lock); /* * For the non-control nodes we need to limit the list of resources * by IDs in the group list for this node @@ -1229,6 +1250,23 @@ int drm_mode_getresources(struct drm_device *dev, void *data, list_for_each(lh, &file_priv->fbs) fb_count++; + /* handle this in 4 parts */ + /* FBs */ + if (card_res->count_fbs >= fb_count) { + copied = 0; + fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; + list_for_each_entry(fb, &file_priv->fbs, filp_head) { + if (put_user(fb->base.id, fb_id + copied)) { + mutex_unlock(&file_priv->fbs_lock); + return -EFAULT; + } + copied++; + } + } + card_res->count_fbs = fb_count; + mutex_unlock(&file_priv->fbs_lock); + + drm_modeset_lock_all(dev); mode_group = &file_priv->master->minor->mode_group; if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { @@ -1252,21 +1290,6 @@ int drm_mode_getresources(struct drm_device *dev, void *data, card_res->max_width = dev->mode_config.max_width; card_res->min_width = dev->mode_config.min_width; - /* handle this in 4 parts */ - /* FBs */ - if (card_res->count_fbs >= fb_count) { - copied = 0; - fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; - list_for_each_entry(fb, &file_priv->fbs, filp_head) { - if (put_user(fb->base.id, fb_id + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - card_res->count_fbs = fb_count; - /* CRTCs */ if (card_res->count_crtcs >= crtc_count) { copied = 0; @@ -1765,8 +1788,10 @@ int drm_mode_setplane(struct drm_device *dev, void *data, } crtc = obj_to_crtc(obj); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, plane_req->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", plane_req->fb_id); @@ -1908,8 +1933,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, } fb = crtc->fb; } else { + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, crtc_req->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { DRM_DEBUG_KMS("Unknown FB ID%d\n", crtc_req->fb_id); @@ -2151,16 +2178,17 @@ int drm_mode_addfb(struct drm_device *dev, fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); - ret = PTR_ERR(fb); - goto out; + drm_modeset_unlock_all(dev); + return PTR_ERR(fb); } + mutex_lock(&file_priv->fbs_lock); or->fb_id = fb->base.id; list_add(&fb->filp_head, &file_priv->fbs); DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); - -out: + mutex_unlock(&file_priv->fbs_lock); drm_modeset_unlock_all(dev); + return ret; } @@ -2333,16 +2361,18 @@ int drm_mode_addfb2(struct drm_device *dev, fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); - ret = PTR_ERR(fb); - goto out; + drm_modeset_unlock_all(dev); + return PTR_ERR(fb); } + mutex_lock(&file_priv->fbs_lock); r->fb_id = fb->base.id; list_add(&fb->filp_head, &file_priv->fbs); DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); + mutex_unlock(&file_priv->fbs_lock); -out: drm_modeset_unlock_all(dev); + return ret; } @@ -2373,27 +2403,34 @@ int drm_mode_rmfb(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); /* TODO check that we really get a framebuffer back. */ if (!obj) { + mutex_unlock(&dev->mode_config.fb_lock); ret = -EINVAL; goto out; } fb = obj_to_fb(obj); + mutex_unlock(&dev->mode_config.fb_lock); + mutex_lock(&file_priv->fbs_lock); list_for_each_entry(fbl, &file_priv->fbs, filp_head) if (fb == fbl) found = 1; - if (!found) { ret = -EINVAL; + mutex_unlock(&file_priv->fbs_lock); goto out; } - drm_framebuffer_remove(fb); + list_del_init(&fb->filp_head); + mutex_unlock(&file_priv->fbs_lock); + drm_framebuffer_remove(fb); out: drm_modeset_unlock_all(dev); + return ret; } @@ -2422,7 +2459,9 @@ int drm_mode_getfb(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { ret = -EINVAL; goto out; @@ -2460,7 +2499,9 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { ret = -EINVAL; goto out_err1; @@ -2535,9 +2576,12 @@ void drm_fb_release(struct drm_file *priv) struct drm_framebuffer *fb, *tfb; drm_modeset_lock_all(dev); + mutex_lock(&priv->fbs_lock); list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { + list_del_init(&fb->filp_head); drm_framebuffer_remove(fb); } + mutex_unlock(&priv->fbs_lock); drm_modeset_unlock_all(dev); } @@ -3542,7 +3586,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (crtc->funcs->page_flip == NULL) goto out; + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) goto out; fb = obj_to_fb(obj); diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 133b4132983e..13fdcd10a605 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -276,6 +276,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, INIT_LIST_HEAD(&priv->lhead); INIT_LIST_HEAD(&priv->fbs); + mutex_init(&priv->fbs_lock); INIT_LIST_HEAD(&priv->event_list); init_waitqueue_head(&priv->event_wait); priv->event_space = 4096; /* set aside 4k for event buffer */ diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e6a11ca85eaf..a40c674a57be 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1374,7 +1374,9 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) fb->base.bits_per_pixel); describe_obj(m, fb->obj); seq_printf(m, "\n"); + mutex_unlock(&dev->mode_config.mutex); + mutex_lock(&dev->mode_config.fb_lock); list_for_each_entry(fb, &dev->mode_config.fb_list, base.head) { if (&fb->base == ifbdev->helper.fb) continue; @@ -1387,8 +1389,7 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) describe_obj(m, fb->obj); seq_printf(m, "\n"); } - - mutex_unlock(&dev->mode_config.mutex); + mutex_unlock(&dev->mode_config.fb_lock); return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index a135498a1298..0d6a161b204b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -165,7 +165,9 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; @@ -248,7 +250,9 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.fb_lock); obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); + mutex_unlock(&dev->mode_config.fb_lock); if (!obj) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; diff --git a/drivers/staging/omapdrm/omap_debugfs.c b/drivers/staging/omapdrm/omap_debugfs.c index 2f122e00b51d..e95540b3e2f6 100644 --- a/drivers/staging/omapdrm/omap_debugfs.c +++ b/drivers/staging/omapdrm/omap_debugfs.c @@ -72,6 +72,7 @@ static int fb_show(struct seq_file *m, void *arg) seq_printf(m, "fbcon "); omap_framebuffer_describe(priv->fbdev->fb, m); + mutex_lock(&dev->mode_config.fb_lock); list_for_each_entry(fb, &dev->mode_config.fb_list, head) { if (fb == priv->fbdev->fb) continue; @@ -79,6 +80,7 @@ static int fb_show(struct seq_file *m, void *arg) seq_printf(m, "user "); omap_framebuffer_describe(fb, m); } + mutex_unlock(&dev->mode_config.fb_lock); mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->mode_config.mutex); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3c609abe8c80..e74731c1a912 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -446,7 +446,15 @@ struct drm_file { int is_master; /* this file private is a master for a minor */ struct drm_master *master; /* master this node is currently associated with N.B. not always minor->master */ + + /** + * fbs - List of framebuffers associated with this file. + * + * Protected by fbs_lock. Note that the fbs list holds a reference on + * the fb object to prevent it from untimely disappearing. + */ struct list_head fbs; + struct mutex fbs_lock; wait_queue_head_t event_wait; struct list_head event_list; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c89b1161f0be..c35a807d7e5c 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -254,6 +254,10 @@ struct drm_framebuffer { * userspace perspective. */ struct kref refcount; + /* + * Place on the dev->mode_config.fb_list, access protected by + * dev->mode_config.fb_lock. + */ struct list_head head; struct drm_mode_object base; const struct drm_framebuffer_funcs *funcs; @@ -780,8 +784,18 @@ struct drm_mode_config { struct mutex idr_mutex; /* for IDR management */ struct idr crtc_idr; /* use this idr for all IDs, fb, crtc, connector, modes - just makes life easier */ /* this is limited to one for now */ + + + /** + * fb_lock - mutex to protect fb state + * + * Besides the global fb list his also protects the fbs list in the + * file_priv + */ + struct mutex fb_lock; int num_fb; struct list_head fb_list; + int num_connector; struct list_head connector_list; int num_encoder; From 786b99ed13223d8ac58a937dd348aead45eb8191 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 2 Dec 2012 21:53:40 +0100 Subject: [PATCH 1052/4607] drm: create drm_framebuffer_lookup And replace all fb lookups with it. Also add a WARN to drm_mode_object_find since that is now no longer the blessed interface to look up an fb. And add kerneldoc to both functions. This only updates all callsites, but immediately drops the acquired refence again. Hence all callers still rely on the fact that a mode fb can't disappear while they're holding the struct mutex. Subsequent patches will instate proper use of refcounts, and then rework the rmfb and unref code to no longer serialize fb destruction with the mode_config lock. We don't want that since otherwise a compositor might end up stalling for a few frames in rmfb. v2: Don't use kref_get_unless_zero - Greg KH doesn't like that kind of interface. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 109 +++++++++++++++++--------- drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 24 +++--- include/drm/drm_crtc.h | 2 + 3 files changed, 86 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 13a3d3426961..f2ccda85309f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -294,11 +294,24 @@ static void drm_mode_object_put(struct drm_device *dev, mutex_unlock(&dev->mode_config.idr_mutex); } +/** + * drm_mode_object_find - look up a drm object with static lifetime + * @dev: drm device + * @id: id of the mode object + * @type: type of the mode object + * + * Note that framebuffers cannot be looked up with this functions - since those + * are reference counted, they need special treatment. + */ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) { struct drm_mode_object *obj = NULL; + /* Framebuffers are reference counted and need their own lookup + * function.*/ + WARN_ON(type == DRM_MODE_OBJECT_FB); + mutex_lock(&dev->mode_config.idr_mutex); obj = idr_find(&dev->mode_config.crtc_idr, id); if (!obj || (obj->type != type) || (obj->id != id)) @@ -358,6 +371,40 @@ static void drm_framebuffer_free(struct kref *kref) fb->funcs->destroy(fb); } +/** + * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference + * @dev: drm device + * @id: id of the fb object + * + * If successful, this grabs an additional reference to the framebuffer - + * callers need to make sure to eventually unreference the returned framebuffer + * again. + */ +struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *obj = NULL; + struct drm_framebuffer *fb; + + mutex_lock(&dev->mode_config.fb_lock); + + mutex_lock(&dev->mode_config.idr_mutex); + obj = idr_find(&dev->mode_config.crtc_idr, id); + if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) + fb = NULL; + else + fb = obj_to_fb(obj); + mutex_unlock(&dev->mode_config.idr_mutex); + + if (fb) + kref_get(&fb->refcount); + + mutex_unlock(&dev->mode_config.fb_lock); + + return fb; +} +EXPORT_SYMBOL(drm_framebuffer_lookup); + /** * drm_framebuffer_unreference - unref a framebuffer * @fb: framebuffer to unref @@ -1788,17 +1835,15 @@ int drm_mode_setplane(struct drm_device *dev, void *data, } crtc = obj_to_crtc(obj); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, plane_req->fb_id, - DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, plane_req->fb_id); + if (!fb) { DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", plane_req->fb_id); ret = -ENOENT; goto out; } - fb = obj_to_fb(obj); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); /* Check whether this plane supports the fb pixel format. */ for (i = 0; i < plane->format_count; i++) @@ -1933,17 +1978,16 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, } fb = crtc->fb; } else { - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, crtc_req->fb_id, - DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); + if (!fb) { DRM_DEBUG_KMS("Unknown FB ID%d\n", crtc_req->fb_id); ret = -EINVAL; goto out; } - fb = obj_to_fb(obj); + /* fb is protect by the mode_config lock, so drop the + * ref immediately */ + drm_framebuffer_unreference(fb); } mode = drm_mode_create(dev); @@ -2392,7 +2436,6 @@ int drm_mode_addfb2(struct drm_device *dev, int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct drm_mode_object *obj; struct drm_framebuffer *fb = NULL; struct drm_framebuffer *fbl = NULL; uint32_t *id = data; @@ -2403,16 +2446,13 @@ int drm_mode_rmfb(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); - /* TODO check that we really get a framebuffer back. */ - if (!obj) { - mutex_unlock(&dev->mode_config.fb_lock); + fb = drm_framebuffer_lookup(dev, *id); + if (!fb) { ret = -EINVAL; goto out; } - fb = obj_to_fb(obj); - mutex_unlock(&dev->mode_config.fb_lock); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); mutex_lock(&file_priv->fbs_lock); list_for_each_entry(fbl, &file_priv->fbs, filp_head) @@ -2451,7 +2491,6 @@ int drm_mode_getfb(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_fb_cmd *r = data; - struct drm_mode_object *obj; struct drm_framebuffer *fb; int ret = 0; @@ -2459,14 +2498,13 @@ int drm_mode_getfb(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, r->fb_id); + if (!fb) { ret = -EINVAL; goto out; } - fb = obj_to_fb(obj); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); r->height = fb->height; r->width = fb->width; @@ -2489,7 +2527,6 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, struct drm_clip_rect __user *clips_ptr; struct drm_clip_rect *clips = NULL; struct drm_mode_fb_dirty_cmd *r = data; - struct drm_mode_object *obj; struct drm_framebuffer *fb; unsigned flags; int num_clips; @@ -2499,14 +2536,13 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, r->fb_id); + if (!fb) { ret = -EINVAL; goto out_err1; } - fb = obj_to_fb(obj); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); num_clips = r->num_clips; clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; @@ -3586,12 +3622,11 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (crtc->funcs->page_flip == NULL) goto out; - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) + fb = drm_framebuffer_lookup(dev, page_flip->fb_id); + if (!fb) goto out; - fb = obj_to_fb(obj); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); hdisplay = crtc->mode.hdisplay; vdisplay = crtc->mode.vdisplay; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 0d6a161b204b..1b8f428accae 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -131,7 +131,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_vmw_rect __user *clips_ptr; struct drm_vmw_rect *clips = NULL; - struct drm_mode_object *obj; + struct drm_framebuffer *fb; struct vmw_framebuffer *vfb; struct vmw_resource *res; uint32_t num_clips; @@ -165,15 +165,15 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, arg->fb_id); + if (!fb) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; goto out_no_fb; } - vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); + vfb = vmw_framebuffer_to_vfb(fb); ret = ttm_read_lock(&vmaster->lock, true); if (unlikely(ret != 0)) @@ -217,7 +217,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_vmw_rect __user *clips_ptr; struct drm_vmw_rect *clips = NULL; - struct drm_mode_object *obj; + struct drm_framebuffer *fb; struct vmw_framebuffer *vfb; uint32_t num_clips; int ret; @@ -250,16 +250,16 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); - mutex_lock(&dev->mode_config.fb_lock); - obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); - mutex_unlock(&dev->mode_config.fb_lock); - if (!obj) { + fb = drm_framebuffer_lookup(dev, arg->fb_id); + if (!fb) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; goto out_no_fb; } + /* fb is protect by the mode_config lock, so drop the ref immediately */ + drm_framebuffer_unreference(fb); - vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); + vfb = vmw_framebuffer_to_vfb(fb); if (!vfb->dmabuf) { DRM_ERROR("Framebuffer not dmabuf backed.\n"); ret = -EINVAL; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c35a807d7e5c..7dc1b31059d4 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -958,6 +958,8 @@ extern void drm_framebuffer_set_object(struct drm_device *dev, extern int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs); +extern struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, + uint32_t id); extern void drm_framebuffer_unreference(struct drm_framebuffer *fb); extern void drm_framebuffer_reference(struct drm_framebuffer *fb); extern void drm_framebuffer_remove(struct drm_framebuffer *fb); From 362063619cf67c2c2fc2eb90951b2623cbb69a7c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 10 Dec 2012 20:42:17 +0100 Subject: [PATCH 1053/4607] drm: revamp framebuffer cleanup interfaces We have two classes of framebuffer - Created by the driver (atm only for fbdev), and the driver holds onto the last reference count until destruction. - Created by userspace and associated with a given fd. These framebuffers will be reaped when their assoiciated fb is closed. Now these two cases are set up differently, the framebuffers are on different lists and hence destruction needs to clean up different things. Also, for userspace framebuffers we remove them from any current usage, whereas for internal framebuffers it is assumed that the driver has done this already. Long story short, we need two different ways to cleanup such drivers. Three functions are involved in total: - drm_framebuffer_remove: Convenience function which removes the fb from all active usage and then drops the passed-in reference. - drm_framebuffer_unregister_private: Will remove driver-private framebuffers from relevant lists and drop the corresponding references. Should be called for driver-private framebuffers before dropping the last reference (or like for a lot of the drivers where the fbdev is embedded someplace else, before doing the cleanup manually). - drm_framebuffer_cleanup: Final cleanup for both classes of fbs, should be called by the driver's ->destroy callback once the last reference is gone. This patch just rolls out the new interfaces and updates all drivers (by adding calls to drm_framebuffer_unregister_private at all the right places)- no functional changes yet. Follow-on patches will move drm core code around and update the lifetime management for framebuffers, so that we are no longer required to keep framebuffers alive by locking mode_config.mutex. I've also updated the kerneldoc already. vmwgfx seems to again be a bit special, at least I haven't figured out how the fbdev support in that driver works. It smells like it's external though. v2: The i915 driver creates another private framebuffer in the load-detect code. Adjust its cleanup code, too. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_fb.c | 1 + drivers/gpu/drm/cirrus/cirrus_fbdev.c | 1 + drivers/gpu/drm/drm_crtc.c | 31 ++++++++++++++++++++--- drivers/gpu/drm/drm_fb_cma_helper.c | 5 +++- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 4 ++- drivers/gpu/drm/gma500/framebuffer.c | 1 + drivers/gpu/drm/i915/intel_display.c | 6 +++-- drivers/gpu/drm/i915/intel_fb.c | 1 + drivers/gpu/drm/mgag200/mgag200_fb.c | 1 + drivers/gpu/drm/nouveau/nouveau_fbcon.c | 1 + drivers/gpu/drm/radeon/radeon_fb.c | 2 ++ drivers/gpu/drm/udl/udl_fb.c | 1 + drivers/staging/omapdrm/omap_fbdev.c | 8 ++++-- include/drm/drm_crtc.h | 1 + 14 files changed, 55 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index d9ec77959dff..3e6584b940dc 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -290,6 +290,7 @@ static void ast_fbdev_destroy(struct drm_device *dev, drm_fb_helper_fini(&afbdev->helper); vfree(afbdev->sysram); + drm_framebuffer_unregister_private(&afb->base); drm_framebuffer_cleanup(&afb->base); } diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 6c6b4c87d309..3daea0f638c3 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -258,6 +258,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev, vfree(gfbdev->sysram); drm_fb_helper_fini(&gfbdev->helper); + drm_framebuffer_unregister_private(&gfb->base); drm_framebuffer_cleanup(&gfb->base); return 0; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f2ccda85309f..3eddfabeba96 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -68,6 +68,7 @@ void drm_modeset_unlock_all(struct drm_device *dev) mutex_unlock(&dev->mode_config.mutex); } + EXPORT_SYMBOL(drm_modeset_unlock_all); /* Avoid boilerplate. I'm tired of typing. */ @@ -429,12 +430,35 @@ void drm_framebuffer_reference(struct drm_framebuffer *fb) } EXPORT_SYMBOL(drm_framebuffer_reference); +/** + * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr + * @fb: fb to unregister + * + * Drivers need to call this when cleaning up driver-private framebuffers, e.g. + * those used for fbdev. Note that the caller must hold a reference of it's own, + * i.e. the object may not be destroyed through this call (since it'll lead to a + * locking inversion). + */ +void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) +{ +} +EXPORT_SYMBOL(drm_framebuffer_unregister_private); + /** * drm_framebuffer_cleanup - remove a framebuffer object * @fb: framebuffer to remove * - * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes - * it, setting it to NULL. + * Cleanup references to a user-created framebuffer. This function is intended + * to be used from the drivers ->destroy callback. + * + * Note that this function does not remove the fb from active usuage - if it is + * still used anywhere, hilarity can ensue since userspace could call getfb on + * the id and get back -EINVAL. Obviously no concern at driver unload time. + * + * Also, the framebuffer will not be removed from the lookup idr - for + * user-created framebuffers this will happen in in the rmfb ioctl. For + * driver-private objects (e.g. for fbdev) drivers need to explicitly call + * drm_framebuffer_unregister_private. */ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) { @@ -460,7 +484,8 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); * @fb: framebuffer to remove * * Scans all the CRTCs and planes in @dev's mode_config. If they're - * using @fb, removes it, setting it to NULL. + * using @fb, removes it, setting it to NULL. Then drops the reference to the + * passed-in framebuffer. */ void drm_framebuffer_remove(struct drm_framebuffer *fb) { diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index e1e0cb0d531a..3742bc96421e 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -266,6 +266,7 @@ static int drm_fbdev_cma_create(struct drm_fb_helper *helper, return 0; err_drm_fb_cma_destroy: + drm_framebuffer_unregister_private(fb); drm_fb_cma_destroy(fb); err_framebuffer_release: framebuffer_release(fbi); @@ -370,8 +371,10 @@ void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma) framebuffer_release(info); } - if (fbdev_cma->fb) + if (fbdev_cma->fb) { + drm_framebuffer_unregister_private(&fbdev_cma->fb->fb); drm_fb_cma_destroy(&fbdev_cma->fb->fb); + } drm_fb_helper_fini(&fbdev_cma->fb_helper); kfree(fbdev_cma); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 71f867340a88..90d335cfb8c0 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -326,8 +326,10 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev, /* release drm framebuffer and real buffer */ if (fb_helper->fb && fb_helper->fb->funcs) { fb = fb_helper->fb; - if (fb) + if (fb) { + drm_framebuffer_unregister_private(fb); drm_framebuffer_remove(fb); + } } /* release linux framebuffer */ diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 49800d2b79dd..c1ef37e2efdf 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -590,6 +590,7 @@ static int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev) framebuffer_release(info); } drm_fb_helper_fini(&fbdev->psb_fb_helper); + drm_framebuffer_unregister_private(&psbfb->base); drm_framebuffer_cleanup(&psbfb->base); if (psbfb->gtt) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 26fa6a795afe..df51203dcebd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6789,8 +6789,10 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, intel_encoder->new_crtc = NULL; intel_set_mode(crtc, NULL, 0, 0, NULL); - if (old->release_fb) - old->release_fb->funcs->destroy(old->release_fb); + if (old->release_fb) { + drm_framebuffer_unregister_private(old->release_fb); + drm_framebuffer_unreference(old->release_fb); + } return; } diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index a9f7280f87fb..c7b8316137e9 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -212,6 +212,7 @@ static void intel_fbdev_destroy(struct drm_device *dev, drm_fb_helper_fini(&ifbdev->helper); + drm_framebuffer_unregister_private(&ifb->base); drm_framebuffer_cleanup(&ifb->base); if (ifb->obj) { drm_gem_object_unreference_unlocked(&ifb->obj->base); diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 2f486481d79a..5c69b432f99a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -247,6 +247,7 @@ static int mga_fbdev_destroy(struct drm_device *dev, } drm_fb_helper_fini(&mfbdev->helper); vfree(mfbdev->sysram); + drm_framebuffer_unregister_private(&mfb->base); drm_framebuffer_cleanup(&mfb->base); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index 67a1a069de28..d4ecb4deb484 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -433,6 +433,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon) nouveau_fb->nvbo = NULL; } drm_fb_helper_fini(&fbcon->helper); + drm_framebuffer_unregister_private(&nouveau_fb->base); drm_framebuffer_cleanup(&nouveau_fb->base); return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index cc8489d8c6d1..515e5ee1f9ee 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -293,6 +293,7 @@ out_unref: } if (fb && ret) { drm_gem_object_unreference(gobj); + drm_framebuffer_unregister_private(fb); drm_framebuffer_cleanup(fb); kfree(fb); } @@ -339,6 +340,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb rfb->obj = NULL; } drm_fb_helper_fini(&rfbdev->helper); + drm_framebuffer_unregister_private(&rfb->base); drm_framebuffer_cleanup(&rfb->base); return 0; diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index f8904b4e68de..caa84f1de9c1 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -555,6 +555,7 @@ static void udl_fbdev_destroy(struct drm_device *dev, framebuffer_release(info); } drm_fb_helper_fini(&ufbdev->helper); + drm_framebuffer_unregister_private(&ufbdev->ufb.base); drm_framebuffer_cleanup(&ufbdev->ufb.base); drm_gem_object_unreference_unlocked(&ufbdev->ufb.obj->base); } diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c index 8a027bb77d97..2728e37e02be 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/staging/omapdrm/omap_fbdev.c @@ -275,8 +275,10 @@ fail: if (ret) { if (fbi) framebuffer_release(fbi); - if (fb) + if (fb) { + drm_framebuffer_unregister_private(fb); drm_framebuffer_remove(fb); + } } return ret; @@ -400,8 +402,10 @@ void omap_fbdev_free(struct drm_device *dev) fbdev = to_omap_fbdev(priv->fbdev); /* this will free the backing object */ - if (fbdev->fb) + if (fbdev->fb) { + drm_framebuffer_unregister_private(fbdev->fb); drm_framebuffer_remove(fbdev->fb); + } kfree(fbdev); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 7dc1b31059d4..66b2732f175a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -964,6 +964,7 @@ extern void drm_framebuffer_unreference(struct drm_framebuffer *fb); extern void drm_framebuffer_reference(struct drm_framebuffer *fb); extern void drm_framebuffer_remove(struct drm_framebuffer *fb); extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb); +extern void drm_framebuffer_unregister_private(struct drm_framebuffer *fb); extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc); extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); extern void drm_crtc_probe_connector_modes(struct drm_device *dev, int maxX, int maxY); From 2b677e8c08eed11e4ebe66a7c334f03e389a19a3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 10 Dec 2012 21:16:05 +0100 Subject: [PATCH 1054/4607] drm: reference framebuffers which are on the idr Since otherwise looking and reference-counting around drm_framebuffer_lookup will be an unmanageable mess. With this change, an object can either be found in the idr and will stay around once we incremented the reference counter. Or it will be gone for good and can't be looked up using its id any more. Atomicity is guaranteed by the dev->mode_config.fb_lock. The newly-introduce fpriv->fbs_lock looks a bit redundant, but the next patch will shuffle the locking order between these two locks and all the modeset locks taken in modeset_lock_all, so we'll need it. Also, since userspace could do really funky stuff and race e.g. a getresources with an rmfb, we need to make sure that the kernel doesn't fall over trying to look-up an inexistent fb, or causing confusion by having two fbs around with the same id. Simply reset the framebuffer id to 0, which marks it as reaped. Any lookups of that id will fail, so the object is really gone for good from userspace's pov. Note that we still need to protect the "remove framebuffer from all use-cases" and the final unreference with the modeset-lock, since most framebuffer use-sites don't implement proper reference counting yet. We can only lift this once _all_ users are converted. With this change, two references are held on alife, but unused framebuffers: - The reference for the idr lookup, created in this patch. - For user-created framebuffers the fpriv->fbs reference, for driver-private fbs the driver is supposed to hold it's own last reference. Note that the dev->mode_config.fb_list itself does _not_ hold a reference onto the framebuffers (this list is essentially only used for debugfs files). Hence if there's anything left there when the driver has cleaned up all it's modeset resources, this is a ref-leak. WARN about it. Now we only need to fix up all other places to properly reference count framebuffers. v2: Fix spelling fail in a comment spotted by Rob Clark. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 118 +++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 3eddfabeba96..a50f7553b31d 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -356,6 +356,9 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, if (ret) goto out; + /* Grab the idr reference. */ + drm_framebuffer_reference(fb); + dev->mode_config.num_fb++; list_add(&fb->head, &dev->mode_config.fb_list); out: @@ -372,6 +375,23 @@ static void drm_framebuffer_free(struct kref *kref) fb->funcs->destroy(fb); } +static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev, + uint32_t id) +{ + struct drm_mode_object *obj = NULL; + struct drm_framebuffer *fb; + + mutex_lock(&dev->mode_config.idr_mutex); + obj = idr_find(&dev->mode_config.crtc_idr, id); + if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) + fb = NULL; + else + fb = obj_to_fb(obj); + mutex_unlock(&dev->mode_config.idr_mutex); + + return fb; +} + /** * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference * @dev: drm device @@ -384,22 +404,12 @@ static void drm_framebuffer_free(struct kref *kref) struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, uint32_t id) { - struct drm_mode_object *obj = NULL; struct drm_framebuffer *fb; mutex_lock(&dev->mode_config.fb_lock); - - mutex_lock(&dev->mode_config.idr_mutex); - obj = idr_find(&dev->mode_config.crtc_idr, id); - if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) - fb = NULL; - else - fb = obj_to_fb(obj); - mutex_unlock(&dev->mode_config.idr_mutex); - + fb = __drm_framebuffer_lookup(dev, id); if (fb) kref_get(&fb->refcount); - mutex_unlock(&dev->mode_config.fb_lock); return fb; @@ -430,6 +440,24 @@ void drm_framebuffer_reference(struct drm_framebuffer *fb) } EXPORT_SYMBOL(drm_framebuffer_reference); +static void drm_framebuffer_free_bug(struct kref *kref) +{ + BUG(); +} + +/* dev->mode_config.fb_lock must be held! */ +static void __drm_framebuffer_unregister(struct drm_device *dev, + struct drm_framebuffer *fb) +{ + mutex_lock(&dev->mode_config.idr_mutex); + idr_remove(&dev->mode_config.crtc_idr, fb->base.id); + mutex_unlock(&dev->mode_config.idr_mutex); + + fb->base.id = 0; + + kref_put(&fb->refcount, drm_framebuffer_free_bug); +} + /** * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr * @fb: fb to unregister @@ -441,6 +469,12 @@ EXPORT_SYMBOL(drm_framebuffer_reference); */ void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) { + struct drm_device *dev = fb->dev; + + mutex_lock(&dev->mode_config.fb_lock); + /* Mark fb as reaped and drop idr ref. */ + __drm_framebuffer_unregister(dev, fb); + mutex_unlock(&dev->mode_config.fb_lock); } EXPORT_SYMBOL(drm_framebuffer_unregister_private); @@ -464,14 +498,6 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; - /* - * This could be moved to drm_framebuffer_remove(), but for - * debugging is nice to keep around the list of fb's that are - * no longer associated w/ a drm_file but are not unreferenced - * yet. (i915 and omapdrm have debugfs files which will show - * this.) - */ - drm_mode_object_put(dev, &fb->base); mutex_lock(&dev->mode_config.fb_lock); list_del(&fb->head); dev->mode_config.num_fb--; @@ -1181,9 +1207,15 @@ void drm_mode_config_cleanup(struct drm_device *dev) drm_property_destroy(dev, property); } - /* Single-threaded teardown context, so it's not requied to grab the + /* + * Single-threaded teardown context, so it's not required to grab the * fb_lock to protect against concurrent fb_list access. Contrary, it - * would actually deadlock with the drm_framebuffer_cleanup function. */ + * would actually deadlock with the drm_framebuffer_cleanup function. + * + * Also, if there are any framebuffers left, that's a driver leak now, + * so politely WARN about this. + */ + WARN_ON(!list_empty(&dev->mode_config.fb_list)); list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { drm_framebuffer_remove(fb); } @@ -2464,39 +2496,41 @@ int drm_mode_rmfb(struct drm_device *dev, struct drm_framebuffer *fb = NULL; struct drm_framebuffer *fbl = NULL; uint32_t *id = data; - int ret = 0; int found = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); - fb = drm_framebuffer_lookup(dev, *id); - if (!fb) { - ret = -EINVAL; - goto out; - } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); - mutex_lock(&file_priv->fbs_lock); + mutex_lock(&dev->mode_config.fb_lock); + fb = __drm_framebuffer_lookup(dev, *id); + if (!fb) + goto fail_lookup; + list_for_each_entry(fbl, &file_priv->fbs, filp_head) if (fb == fbl) found = 1; - if (!found) { - ret = -EINVAL; - mutex_unlock(&file_priv->fbs_lock); - goto out; - } + if (!found) + goto fail_lookup; + + /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ + __drm_framebuffer_unregister(dev, fb); list_del_init(&fb->filp_head); + mutex_unlock(&dev->mode_config.fb_lock); mutex_unlock(&file_priv->fbs_lock); + drm_modeset_lock_all(dev); drm_framebuffer_remove(fb); -out: drm_modeset_unlock_all(dev); - return ret; + return 0; + +fail_lookup: + mutex_unlock(&dev->mode_config.fb_lock); + mutex_unlock(&file_priv->fbs_lock); + + return -EINVAL; } /** @@ -2639,7 +2673,15 @@ void drm_fb_release(struct drm_file *priv) drm_modeset_lock_all(dev); mutex_lock(&priv->fbs_lock); list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { + + mutex_lock(&dev->mode_config.fb_lock); + /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ + __drm_framebuffer_unregister(dev, fb); + mutex_unlock(&dev->mode_config.fb_lock); + list_del_init(&fb->filp_head); + + /* This will also drop the fpriv->fbs reference. */ drm_framebuffer_remove(fb); } mutex_unlock(&priv->fbs_lock); From 7d331595b05d6f3c38567ad7031b75075557ce2a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 10 Dec 2012 23:44:22 +0100 Subject: [PATCH 1055/4607] drm: nest modeset locks within fpriv->fbs_lock Atm we still need to unconditionally take the modeset locks in the rmfb paths. But eventually we only want to take them if there are other users around as a slow-path. This way sane userspace avoids blocking on edid reads and other stuff in rmfb if it ensures that the fb isn't used anywhere by a crtc/plane. We can do a quick check for such other users once framebuffers are properly refcounting by locking at the refcount - if it's more than 1, there are other users left. Again, rmfb racing against other ioctls isn't a real problem, userspace is allowed to shoot its foot. This patch just prepares this by moving the modeset locks to nest within fpriv->fbs_lock. Now the distinction between the fbs_lock and the device-global fb_lock is clear, since we need to hold the fbs_lock outside of any modeset_locks in fb_release. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index a50f7553b31d..09e02a7023f9 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2282,13 +2282,13 @@ int drm_mode_addfb(struct drm_device *dev, drm_modeset_unlock_all(dev); return PTR_ERR(fb); } + drm_modeset_unlock_all(dev); mutex_lock(&file_priv->fbs_lock); or->fb_id = fb->base.id; list_add(&fb->filp_head, &file_priv->fbs); DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); mutex_unlock(&file_priv->fbs_lock); - drm_modeset_unlock_all(dev); return ret; } @@ -2465,6 +2465,7 @@ int drm_mode_addfb2(struct drm_device *dev, drm_modeset_unlock_all(dev); return PTR_ERR(fb); } + drm_modeset_unlock_all(dev); mutex_lock(&file_priv->fbs_lock); r->fb_id = fb->base.id; @@ -2472,7 +2473,6 @@ int drm_mode_addfb2(struct drm_device *dev, DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); mutex_unlock(&file_priv->fbs_lock); - drm_modeset_unlock_all(dev); return ret; } @@ -2670,7 +2670,6 @@ void drm_fb_release(struct drm_file *priv) struct drm_device *dev = priv->minor->dev; struct drm_framebuffer *fb, *tfb; - drm_modeset_lock_all(dev); mutex_lock(&priv->fbs_lock); list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { @@ -2682,10 +2681,11 @@ void drm_fb_release(struct drm_file *priv) list_del_init(&fb->filp_head); /* This will also drop the fpriv->fbs reference. */ + drm_modeset_lock_all(dev); drm_framebuffer_remove(fb); + drm_modeset_unlock_all(dev); } mutex_unlock(&priv->fbs_lock); - drm_modeset_unlock_all(dev); } /** From 468174f748603497e73dba9b5c6d1d9f71121486 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 00:09:12 +0100 Subject: [PATCH 1056/4607] drm: push modeset_lock_all into ->fb_create driver callbacks And drop it where it's not needed. Most driver just lookup the gem object, allocate an fb struct, fill in all the useful fields and then register it with drm_framebuffer_init. All of these operations are already separately locked, and since we only put the fb into the fpriv->fbs list _after_ having called ->fb_create, we can't also race with rmfb. We can otoh race with other ioctls that put the framebuffer to use, but all drivers have been reorganized already to call drm_framebuffer_init last in the fb creation sequence. So essentially, we can completely remove any modeset locks from the addfb ioctl paths. Yeah! Also, reference-counting is solid - we get a reference from fb_create which we transfer to the fpriv->fbs list. And after unlocking the fpriv->fbs_lock we don't touch the framebuffer any longer. Furthermore drm_framebuffer_init has added a 2nd reference for the idr lookup, and any access through that table will do it's own refcounting. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 09e02a7023f9..2e6103c5d632 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2271,18 +2271,12 @@ int drm_mode_addfb(struct drm_device *dev, if ((config->min_height > r.height) || (r.height > config->max_height)) return -EINVAL; - drm_modeset_lock_all(dev); - - /* TODO check buffer is sufficiently large */ - /* TODO setup destructor callback */ - fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); drm_modeset_unlock_all(dev); return PTR_ERR(fb); } - drm_modeset_unlock_all(dev); mutex_lock(&file_priv->fbs_lock); or->fb_id = fb->base.id; @@ -2457,15 +2451,12 @@ int drm_mode_addfb2(struct drm_device *dev, if (ret) return ret; - drm_modeset_lock_all(dev); - fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); if (IS_ERR(fb)) { DRM_DEBUG_KMS("could not create framebuffer\n"); drm_modeset_unlock_all(dev); return PTR_ERR(fb); } - drm_modeset_unlock_all(dev); mutex_lock(&file_priv->fbs_lock); r->fb_id = fb->base.id; From 58c0dca10614117cf4b385e3314e79e3b37fa66b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 13 Dec 2012 23:06:08 +0100 Subject: [PATCH 1057/4607] drm: don't take modeset locks in getfb ioctl We only need to push the fb unreference a bit down. While at it, properly pass the return value from ->create_handle back to userspace. Most drivers either return -ENODEV if they don't have a concept of buffer objects (ast, cirrus, ...) or just install a handle for the underlying gem object (which is ok since we hold a reference on that through the framebuffer). v2: Split out the ->create_handle rework in the individual drivers. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 2e6103c5d632..cba8c8bb789c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2542,19 +2542,14 @@ int drm_mode_getfb(struct drm_device *dev, { struct drm_mode_fb_cmd *r = data; struct drm_framebuffer *fb; - int ret = 0; + int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); fb = drm_framebuffer_lookup(dev, r->fb_id); - if (!fb) { - ret = -EINVAL; - goto out; - } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); + if (!fb) + return -EINVAL; r->height = fb->height; r->width = fb->width; @@ -2566,8 +2561,8 @@ int drm_mode_getfb(struct drm_device *dev, else ret = -ENODEV; -out: - drm_modeset_unlock_all(dev); + drm_framebuffer_unreference(fb); + return ret; } From 4ccf097f1935321f03ad36218588a9e446006b6a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 00:38:18 +0100 Subject: [PATCH 1058/4607] drm: fb refcounting for dirtyfb_ioctl We only need to ensure that the fb stays around for long enough. While at it, only grab the modeset locks when we need them (since most drivers don't implement the dirty callback, this should help jitter and stalls when using the generic modeset driver). Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index cba8c8bb789c..d4f8fa5b3c18 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2580,14 +2580,9 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); fb = drm_framebuffer_lookup(dev, r->fb_id); - if (!fb) { - ret = -EINVAL; - goto out_err1; - } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); + if (!fb) + return -EINVAL; num_clips = r->num_clips; clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; @@ -2625,17 +2620,19 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, } if (fb->funcs->dirty) { + drm_modeset_lock_all(dev); ret = fb->funcs->dirty(fb, file_priv, flags, r->color, clips, num_clips); + drm_modeset_unlock_all(dev); } else { ret = -ENOSYS; - goto out_err2; } out_err2: kfree(clips); out_err1: - drm_modeset_unlock_all(dev); + drm_framebuffer_unreference(fb); + return ret; } From 6c2a75325c800de286166c693e0cd33c3a1c5ec8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 00:59:24 +0100 Subject: [PATCH 1059/4607] drm: refcounting for sprite framebuffers Now plane->fb holds a reference onto it's framebuffer. Nothing too fancy going on here: - Extract __drm_framebuffer_unreference to be called when we know we're not dropping the last reference, e.g. useful in the fb cleanup code. - Reduce the locked sections in the set_plane ioctl to only protect plane->fb/plane->crtc and the driver callback (i.e. hw state). Everything either doesn't disappear (crtc, plane) or is refcounted (fb), and all the data we check is invariant over the respective object's lifetimes. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index d4f8fa5b3c18..64ef12079528 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -445,6 +445,12 @@ static void drm_framebuffer_free_bug(struct kref *kref) BUG(); } +static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) +{ + DRM_DEBUG("FB ID: %d\n", fb->base.id); + kref_put(&fb->refcount, drm_framebuffer_free_bug); +} + /* dev->mode_config.fb_lock must be held! */ static void __drm_framebuffer_unregister(struct drm_device *dev, struct drm_framebuffer *fb) @@ -455,7 +461,7 @@ static void __drm_framebuffer_unregister(struct drm_device *dev, fb->base.id = 0; - kref_put(&fb->refcount, drm_framebuffer_free_bug); + __drm_framebuffer_unreference(fb); } /** @@ -544,6 +550,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) if (ret) DRM_ERROR("failed to disable plane with busy fb\n"); /* disconnect the plane from the fb and crtc: */ + __drm_framebuffer_unreference(plane->fb); plane->fb = NULL; plane->crtc = NULL; } @@ -1850,7 +1857,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, struct drm_mode_object *obj; struct drm_plane *plane; struct drm_crtc *crtc; - struct drm_framebuffer *fb; + struct drm_framebuffer *fb = NULL, *old_fb = NULL; int ret = 0; unsigned int fb_width, fb_height; int i; @@ -1858,8 +1865,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); - /* * First, find the plane, crtc, and fb objects. If not available, * we don't bother to call the driver. @@ -1869,16 +1874,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data, if (!obj) { DRM_DEBUG_KMS("Unknown plane ID %d\n", plane_req->plane_id); - ret = -ENOENT; - goto out; + return -ENOENT; } plane = obj_to_plane(obj); /* No fb means shut it down */ if (!plane_req->fb_id) { + drm_modeset_lock_all(dev); + old_fb = plane->fb; plane->funcs->disable_plane(plane); plane->crtc = NULL; plane->fb = NULL; + drm_modeset_unlock_all(dev); goto out; } @@ -1899,8 +1906,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data, ret = -ENOENT; goto out; } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); /* Check whether this plane supports the fb pixel format. */ for (i = 0; i < plane->format_count; i++) @@ -1946,18 +1951,25 @@ int drm_mode_setplane(struct drm_device *dev, void *data, goto out; } + drm_modeset_lock_all(dev); ret = plane->funcs->update_plane(plane, crtc, fb, plane_req->crtc_x, plane_req->crtc_y, plane_req->crtc_w, plane_req->crtc_h, plane_req->src_x, plane_req->src_y, plane_req->src_w, plane_req->src_h); if (!ret) { + old_fb = plane->fb; + fb = NULL; plane->crtc = crtc; plane->fb = fb; } + drm_modeset_unlock_all(dev); out: - drm_modeset_unlock_all(dev); + if (fb) + drm_framebuffer_unreference(fb); + if (old_fb) + drm_framebuffer_unreference(old_fb); return ret; } From b0d1232589df5575c5971224ac4cb30e7e525884 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 01:07:12 +0100 Subject: [PATCH 1060/4607] drm: refcounting for crtc framebuffers With the prep patch to encapsulate ->set_crtc calls, this is now rather easy. Hooray for inconsistent semantics between ->set_crtc and ->page_flip, where the driver callback is supposed to update the fb pointer, and ->update_plane, where the drm core does the same. Also, since the drm core functions check crtc->fb before calling into driver callbacks, we can't really reduce the critical sections protected by the mode_config locks. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 64ef12079528..6dc75ee10079 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1984,8 +1984,21 @@ out: int drm_mode_set_config_internal(struct drm_mode_set *set) { struct drm_crtc *crtc = set->crtc; + struct drm_framebuffer *fb, *old_fb; + int ret; - return crtc->funcs->set_config(set); + old_fb = crtc->fb; + fb = set->fb; + + ret = crtc->funcs->set_config(set); + if (ret == 0) { + if (old_fb) + drm_framebuffer_unreference(old_fb); + if (fb) + drm_framebuffer_reference(fb); + } + + return ret; } EXPORT_SYMBOL(drm_mode_set_config_internal); @@ -2046,6 +2059,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; } fb = crtc->fb; + /* Make refcounting symmetric with the lookup path. */ + drm_framebuffer_reference(fb); } else { fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); if (!fb) { @@ -2054,9 +2069,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ret = -EINVAL; goto out; } - /* fb is protect by the mode_config lock, so drop the - * ref immediately */ - drm_framebuffer_unreference(fb); } mode = drm_mode_create(dev); @@ -2156,6 +2168,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ret = drm_mode_set_config_internal(&set); out: + if (fb) + drm_framebuffer_unreference(fb); + kfree(connector_set); drm_mode_destroy(dev, mode); drm_modeset_unlock_all(dev); @@ -3656,7 +3671,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, struct drm_mode_crtc_page_flip *page_flip = data; struct drm_mode_object *obj; struct drm_crtc *crtc; - struct drm_framebuffer *fb; + struct drm_framebuffer *fb = NULL, *old_fb = NULL; struct drm_pending_vblank_event *e = NULL; unsigned long flags; int hdisplay, vdisplay; @@ -3687,8 +3702,6 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, fb = drm_framebuffer_lookup(dev, page_flip->fb_id); if (!fb) goto out; - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); hdisplay = crtc->mode.hdisplay; vdisplay = crtc->mode.vdisplay; @@ -3734,6 +3747,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, (void (*) (struct drm_pending_event *)) kfree; } + old_fb = crtc->fb; ret = crtc->funcs->page_flip(crtc, fb, e); if (ret) { if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { @@ -3742,9 +3756,18 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, spin_unlock_irqrestore(&dev->event_lock, flags); kfree(e); } + /* Keep the old fb, don't unref it. */ + old_fb = NULL; + } else { + /* Unref only the old framebuffer. */ + fb = NULL; } out: + if (fb) + drm_framebuffer_unreference(fb); + if (old_fb) + drm_framebuffer_unreference(old_fb); drm_modeset_unlock_all(dev); return ret; } From 623f9783027ef0a948205f17792c9e1fcedb61c6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 16:21:38 +0100 Subject: [PATCH 1061/4607] drm/i915: dump refcount into framebuffer debugfs file Useful for checking whether the new refcounting works as advertised. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a40c674a57be..8a11085e0043 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1367,11 +1367,12 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) ifbdev = dev_priv->fbdev; fb = to_intel_framebuffer(ifbdev->helper.fb); - seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, obj ", + seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, refcount %d, obj ", fb->base.width, fb->base.height, fb->base.depth, - fb->base.bits_per_pixel); + fb->base.bits_per_pixel, + atomic_read(&fb->base.refcount.refcount)); describe_obj(m, fb->obj); seq_printf(m, "\n"); mutex_unlock(&dev->mode_config.mutex); @@ -1381,11 +1382,12 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data) if (&fb->base == ifbdev->helper.fb) continue; - seq_printf(m, "user size: %d x %d, depth %d, %d bpp, obj ", + seq_printf(m, "user size: %d x %d, depth %d, %d bpp, refcount %d, obj ", fb->base.width, fb->base.height, fb->base.depth, - fb->base.bits_per_pixel); + fb->base.bits_per_pixel, + atomic_read(&fb->base.refcount.refcount)); describe_obj(m, fb->obj); seq_printf(m, "\n"); } From 2fd5eabab02d9cdade04397eae0bfd49f452cdba Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 16:28:34 +0100 Subject: [PATCH 1062/4607] drm/vmwgfx: add proper framebuffer refcounting Afact vmwgfx already has all the right refcounting implemented on the backing storage, and we only need to ensure that the drm fb doesn't disappear untimely. So holding onto the fb reference from _lookup until vmw_kms_present has completed should be enough. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 1b8f428accae..c509d40c4897 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -171,8 +171,6 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, ret = -EINVAL; goto out_no_fb; } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); vfb = vmw_framebuffer_to_vfb(fb); ret = ttm_read_lock(&vmaster->lock, true); @@ -197,6 +195,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, out_no_surface: ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: + drm_framebuffer_unreference(fb); out_no_fb: drm_modeset_unlock_all(dev); out_no_copy: @@ -256,14 +255,12 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, ret = -EINVAL; goto out_no_fb; } - /* fb is protect by the mode_config lock, so drop the ref immediately */ - drm_framebuffer_unreference(fb); vfb = vmw_framebuffer_to_vfb(fb); if (!vfb->dmabuf) { DRM_ERROR("Framebuffer not dmabuf backed.\n"); ret = -EINVAL; - goto out_no_fb; + goto out_no_ttm_lock; } ret = ttm_read_lock(&vmaster->lock, true); @@ -276,6 +273,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: + drm_framebuffer_unreference(fb); out_no_fb: drm_modeset_unlock_all(dev); out_no_copy: From b62584e366ebcb3adffefad373a5abc4c4b677ca Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 16:51:35 +0100 Subject: [PATCH 1063/4607] drm: optimize drm_framebuffer_remove Now that all framebuffer usage is properly refcounted, we are no longer required to hold the modeset locks while dropping the last reference. Hence implemented a fastpath which avoids the potential stalls associated with grabbing mode_config.lock for the case where there's no other reference around. Explain in a big comment why it is safe. Also update kerneldocs with the new locking rules around drm_framebuffer_remove. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 72 ++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 6dc75ee10079..9492e7ec9d9d 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -517,7 +517,11 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); * * Scans all the CRTCs and planes in @dev's mode_config. If they're * using @fb, removes it, setting it to NULL. Then drops the reference to the - * passed-in framebuffer. + * passed-in framebuffer. Might take the modeset locks. + * + * Note that this function optimizes the cleanup away if the caller holds the + * last reference to the framebuffer. It is also guaranteed to not take the + * modeset locks in this case. */ void drm_framebuffer_remove(struct drm_framebuffer *fb) { @@ -527,33 +531,51 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) struct drm_mode_set set; int ret; - WARN_ON(!drm_modeset_is_locked(dev)); WARN_ON(!list_empty(&fb->filp_head)); - /* remove from any CRTC */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->fb == fb) { - /* should turn off the crtc */ - memset(&set, 0, sizeof(struct drm_mode_set)); - set.crtc = crtc; - set.fb = NULL; - ret = drm_mode_set_config_internal(&set); - if (ret) - DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); + /* + * drm ABI mandates that we remove any deleted framebuffers from active + * useage. But since most sane clients only remove framebuffers they no + * longer need, try to optimize this away. + * + * Since we're holding a reference ourselves, observing a refcount of 1 + * means that we're the last holder and can skip it. Also, the refcount + * can never increase from 1 again, so we don't need any barriers or + * locks. + * + * Note that userspace could try to race with use and instate a new + * usage _after_ we've cleared all current ones. End result will be an + * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot + * in this manner. + */ + if (atomic_read(&fb->refcount.refcount) > 1) { + drm_modeset_lock_all(dev); + /* remove from any CRTC */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb == fb) { + /* should turn off the crtc */ + memset(&set, 0, sizeof(struct drm_mode_set)); + set.crtc = crtc; + set.fb = NULL; + ret = drm_mode_set_config_internal(&set); + if (ret) + DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); + } } - } - list_for_each_entry(plane, &dev->mode_config.plane_list, head) { - if (plane->fb == fb) { - /* should turn off the crtc */ - ret = plane->funcs->disable_plane(plane); - if (ret) - DRM_ERROR("failed to disable plane with busy fb\n"); - /* disconnect the plane from the fb and crtc: */ - __drm_framebuffer_unreference(plane->fb); - plane->fb = NULL; - plane->crtc = NULL; + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { + if (plane->fb == fb) { + /* should turn off the crtc */ + ret = plane->funcs->disable_plane(plane); + if (ret) + DRM_ERROR("failed to disable plane with busy fb\n"); + /* disconnect the plane from the fb and crtc: */ + __drm_framebuffer_unreference(plane->fb); + plane->fb = NULL; + plane->crtc = NULL; + } } + drm_modeset_unlock_all(dev); } drm_framebuffer_unreference(fb); @@ -2538,9 +2560,7 @@ int drm_mode_rmfb(struct drm_device *dev, mutex_unlock(&dev->mode_config.fb_lock); mutex_unlock(&file_priv->fbs_lock); - drm_modeset_lock_all(dev); drm_framebuffer_remove(fb); - drm_modeset_unlock_all(dev); return 0; @@ -2691,9 +2711,7 @@ void drm_fb_release(struct drm_file *priv) list_del_init(&fb->filp_head); /* This will also drop the fpriv->fbs reference. */ - drm_modeset_lock_all(dev); drm_framebuffer_remove(fb); - drm_modeset_unlock_all(dev); } mutex_unlock(&priv->fbs_lock); } From b4d5e7d1dbdd6a758a4f7717beef7bd6b007bd66 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 16:59:31 +0100 Subject: [PATCH 1064/4607] drm: only grab the crtc lock for pageflips The pagelip ioctl itself is rather simply, so the hard work for this patch is auditing all the drivers: - exynos: Pageflip is protect with dev->struct_mutex and ... synchronous. But nothing fancy going on, besides a check whether the crtc is enabled, which should probably be somewhere in the drm core so that we have unified behaviour across all drivers. - i915: hw-state is protected with dev->struct_mutex, the delayed unpin work together with the other stuff the pageflip complete irq handler needs is protected by the event_lock spinlock. - nouveau: With the pin/unpin functions fixed, everything looks safe: A bit of ttm wrestling and refcounting, and a few channel accesses. The later are either already proteced sufficiently, or are now safe with the channel locking introduced to make cursor updates safe. - radeon: The irq_get/put functions look a bit race, since the atomic_inc/dec isn't protect with locks. Otoh they're all per-crtc, so we should be safe with per-crtc locking from the drm core. Then there's tons of per-crtc register access, which could potentially go through the indirect reg acces. But that's fixed to make cursor updates concurrent. Bookeeping for the drm even is also protected with the even_lock, which also protects against the pageflip irq handler since radeon hw seems to have no way to queue these up asynchronously. Otherwise just a bit of ttm-based buffer handling and fencing, which is now safe with the previous patch to hold bdev->fence_lock while grabbing the ttm fence. - shmob: Only one crtc. That's an easy one ... - vmwgfx: As usual a bit special with tons different things: - Flippable check using is_implicit and num_implicit. Changes to those seem to be nicely covered with the global modeset lock, so we should be fine. - Some dirty cliprect handling stuff, or at least that is my guess. Looks like it's fine since either it's per-crtc, invariant or (like the execbuf stuff launched) protected otherwise. - Adding the actual flip to the fence_event list. On a quick look this seems to have solid locking in place, too. ... but generally this is all way over my head. - imx: Impressive display of races between the page_flip implementation and the irq handler. Also, ipu_drm_set_base which gets eventually called from the irq handler to update the display base isn't really protected against concurrent set_config calls from process context. In any case, going for per-crtc locking won't make this worse, so nothing to do. - omap: The new async callback code merged into 3.8 seems to have solid locking in place, and there doesn't seem to be any shared state at risk. Especially since the callbacks still use modeset_lock_all and are so not converted. v2: Update omapdrm analysis to 3.8 code per the discussion with Rob Clark. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9492e7ec9d9d..fd3e9a13b04f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3699,12 +3699,12 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, page_flip->reserved != 0) return -EINVAL; - drm_modeset_lock_all(dev); obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) - goto out; + return -EINVAL; crtc = obj_to_crtc(obj); + mutex_lock(&crtc->mutex); if (crtc->fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not @@ -3786,7 +3786,8 @@ out: drm_framebuffer_unreference(fb); if (old_fb) drm_framebuffer_unreference(old_fb); - drm_modeset_unlock_all(dev); + mutex_unlock(&crtc->mutex); + return ret; } From 7b24056be6db7ce907baffdd4cf142ab774ea60c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 12 Dec 2012 00:35:33 +0100 Subject: [PATCH 1065/4607] drm: don't hold crtc mutexes for connector ->detect callbacks The coup de grace of the entire journey. No more dropped frames every 10s on my testbox! I've tried to audit all ->detect and ->get_modes callbacks, but things became a bit fuzzy after trying to piece together the umpteenth implemenation. Afaict most drivers just have bog-standard output register frobbing with a notch of i2c edid reading, nothing which could potentially race with the newly concurrent pageflip/set_cursor code. The big exception is load-detection code which requires a running pipe, but radeon/nouveau seem to to this without touching any state which can be observed from page_flip (e.g. disabled crtcs temporarily getting enabled and so a pageflip succeeding). The only special case I could find is the i915 load detect code. That uses the normal modeset interface to enable the load-detect crtc, and so userspace could try to squeeze in a pageflip on the load-detect pipe. So we need to grab the relevant crtc mutex in there, to avoid the temporary crtc enabling to sneak out and be visible to userspace. Note that the sysfs files already stopped grabbing the per-crtc locks, since I didn't want to bother with doing a interruptible modeset_lock_all. But since there's very little in-between breakage (essentially just the ability for userspace to pageflip on load-detect crtcs when it shouldn't on the i915 driver) I figured I don't need to bother. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 5 +++-- drivers/gpu/drm/drm_crtc_helper.c | 8 ++++---- drivers/gpu/drm/i915/intel_display.c | 10 ++++++++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fd3e9a13b04f..9c797f6fea75 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1617,7 +1617,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); - drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); @@ -1714,7 +1714,8 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->count_encoders = encoders_count; out: - drm_modeset_unlock_all(dev); + mutex_unlock(&dev->mode_config.mutex); + return ret; } diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 400ef86a2b43..7b2d378b2576 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -980,7 +980,7 @@ static void output_poll_execute(struct work_struct *work) if (!drm_kms_helper_poll) return; - drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.mutex); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { /* Ignore forced connectors. */ @@ -1010,7 +1010,7 @@ static void output_poll_execute(struct work_struct *work) changed = true; } - drm_modeset_unlock_all(dev); + mutex_unlock(&dev->mode_config.mutex); if (changed) drm_kms_helper_hotplug_event(dev); @@ -1070,7 +1070,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev) if (!dev->mode_config.poll_enabled) return; - drm_modeset_lock_all(dev); + mutex_lock(&dev->mode_config.mutex); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { /* Only handle HPD capable connectors. */ @@ -1088,7 +1088,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev) changed = true; } - drm_modeset_unlock_all(dev); + mutex_unlock(&dev->mode_config.mutex); if (changed) drm_kms_helper_hotplug_event(dev); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index df51203dcebd..c8f417b34d1a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6700,6 +6700,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (encoder->crtc) { crtc = encoder->crtc; + mutex_lock(&crtc->mutex); + old->dpms_mode = connector->dpms; old->load_detect_temp = false; @@ -6729,6 +6731,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, return false; } + mutex_lock(&crtc->mutex); intel_encoder->new_crtc = to_intel_crtc(crtc); to_intel_connector(connector)->new_encoder = intel_encoder; @@ -6756,6 +6759,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n"); if (IS_ERR(fb)) { DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n"); + mutex_unlock(&crtc->mutex); return false; } @@ -6763,6 +6767,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); if (old->release_fb) old->release_fb->funcs->destroy(old->release_fb); + mutex_unlock(&crtc->mutex); return false; } @@ -6777,14 +6782,13 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_encoder *intel_encoder = intel_attached_encoder(connector); struct drm_encoder *encoder = &intel_encoder->base; + struct drm_crtc *crtc = encoder->crtc; DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", connector->base.id, drm_get_connector_name(connector), encoder->base.id, drm_get_encoder_name(encoder)); if (old->load_detect_temp) { - struct drm_crtc *crtc = encoder->crtc; - to_intel_connector(connector)->new_encoder = NULL; intel_encoder->new_crtc = NULL; intel_set_mode(crtc, NULL, 0, 0, NULL); @@ -6800,6 +6804,8 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, /* Switch crtc and encoder back off if necessary */ if (old->dpms_mode != DRM_MODE_DPMS_ON) connector->funcs->dpms(connector, old->dpms_mode); + + mutex_unlock(&crtc->mutex); } /* Returns the clock of the currently programmed mode of the given pipe. */ From 5d7a951537927555fa1286a338e1b91c3b8b7445 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 4 Jan 2013 22:31:20 +0100 Subject: [PATCH 1066/4607] drm/doc: updates for new framebuffer lifetime rules Now that framebuffer are reference-counted for all use-sites, update the documentation accordingly to stress the new rules for initialization and teardown. Also add a short paragraph about the implications for drivers of the new locking rules. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 59 ++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index caab791b0a6c..6c11d77c8d02 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -978,10 +978,25 @@ int max_width, max_height; If the parameters are deemed valid, drivers then create, initialize and return an instance of struct drm_framebuffer. If desired the instance can be embedded in a larger driver-specific - structure. The new instance is initialized with a call to - drm_framebuffer_init which takes a pointer to DRM - frame buffer operations (struct - drm_framebuffer_funcs). Frame buffer operations are + structure. Drivers must fill its width, + height, pitches, + offsets, depth, + bits_per_pixel and + pixel_format fields from the values passed + through the drm_mode_fb_cmd2 argument. They + should call the drm_helper_mode_fill_fb_struct + helper function to do so. + + + + The initailization of the new framebuffer instance is finalized with a + call to drm_framebuffer_init which takes a pointer + to DRM frame buffer operations (struct + drm_framebuffer_funcs). Note that this function + publishes the framebuffer and so from this point on it can be accessed + concurrently from other threads. Hence it must be the last step in the + driver's framebuffer initialization sequence. Frame buffer operations + are int (*create_handle)(struct drm_framebuffer *fb, @@ -1022,16 +1037,16 @@ int max_width, max_height; - After initializing the drm_framebuffer - instance drivers must fill its width, - height, pitches, - offsets, depth, - bits_per_pixel and - pixel_format fields from the values passed - through the drm_mode_fb_cmd2 argument. They - should call the drm_helper_mode_fill_fb_struct - helper function to do so. - + The lifetime of a drm framebuffer is controlled with a reference count, + drivers can grab additional references with + drm_framebuffer_reference and drop them + again with drm_framebuffer_unreference. For + driver-private framebuffers for which the last reference is never + dropped (e.g. for the fbdev framebuffer when the struct + drm_framebuffer is embedded into the fbdev + helper struct) drivers can manually clean up a framebuffer at module + unload time with + drm_framebuffer_unregister_private. Output Polling @@ -1043,6 +1058,22 @@ int max_width, max_height; operation. + + Locking + + Beside some lookup structures with their own locking (which is hidden + behind the interface functions) most of the modeset state is protected + by the dev-<mode_config.lock mutex and additionally + per-crtc locks to allow cursor updates, pageflips and similar operations + to occur concurrently with background tasks like output detection. + Operations which cross domains like a full modeset always grab all + locks. Drivers there need to protect resources shared between crtcs with + additional locking. They also need to be careful to always grab the + relevant crtc locks if a modset functions touches crtc state, e.g. for + load detection (which does only grab the mode_config.lock + to allow concurrent screen updates on live crtcs). + + From 20c60c35de3285222b3476c3445c66bedf0c449c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 17 Dec 2012 12:13:23 +0100 Subject: [PATCH 1067/4607] drm/fb_helper: check whether fbcon is bound We need to make sure that the fbcon is still bound when touching the hw, since otherwise we might corrupt the modeset state of kms clients. X mostly works around that with VT switching and setting the VT into raw mode, which disables most fbcon events. Raw kms test programs though don't do that dance, and in the future we might want to aim to abolish CONFIG_VT anyway. So improve preventive measures a bit. To do so, extract the existing logic for handling hotplug events (which X can't block with the current set of tricks) and reuse it for the fbdev blanking helper. Long-term we really need to either scrap this all and only have a OOPS console, or come up with a saner model for device ownership sharing between fbdev/fbcon and kms userspace. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 39 ++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index be0f2d65db3a..0c6e25e979dd 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -305,6 +305,24 @@ void drm_fb_helper_restore(void) } EXPORT_SYMBOL(drm_fb_helper_restore); +static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) +{ + struct drm_device *dev = fb_helper->dev; + struct drm_crtc *crtc; + int bound = 0, crtcs_bound = 0; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->fb) + crtcs_bound++; + if (crtc->fb == fb_helper->fb) + bound++; + } + + if (bound < crtcs_bound) + return false; + return true; +} + #ifdef CONFIG_MAGIC_SYSRQ static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) { @@ -338,6 +356,11 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) * For each CRTC in this fb, turn the connectors on/off. */ drm_modeset_lock_all(dev); + if (!drm_fb_helper_is_bound(fb_helper)) { + drm_modeset_unlock_all(dev); + return; + } + for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -702,6 +725,11 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, int i; drm_modeset_lock_all(dev); + if (!drm_fb_helper_is_bound(fb_helper)) { + drm_modeset_unlock_all(dev); + return -EBUSY; + } + for (i = 0; i < fb_helper->crtc_count; i++) { crtc = fb_helper->crtc_info[i].mode_set.crtc; @@ -1369,21 +1397,12 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; int count = 0; u32 max_width, max_height, bpp_sel; - int bound = 0, crtcs_bound = 0; - struct drm_crtc *crtc; if (!fb_helper->fb) return 0; drm_modeset_lock_all(dev); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->fb) - crtcs_bound++; - if (crtc->fb == fb_helper->fb) - bound++; - } - - if (bound < crtcs_bound) { + if (!drm_fb_helper_is_bound(fb_helper)) { fb_helper->delayed_hotplug = true; drm_modeset_unlock_all(dev); return 0; From f8122a82d2eae8ef42de48829deed0ca9d9e1f17 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 16 Jan 2013 15:48:50 +0200 Subject: [PATCH 1068/4607] dw_dmac: allocate dma descriptors from DMA_COHERENT memory Currently descriptors are allocated from normal cacheable memory and that slows down filling the descriptors, as we need to call cache_coherency routines afterwards. It would be better to allocate memory for these descriptors from DMA_COHERENT memory. This would make code much cleaner too. Signed-off-by: Andy Shevchenko Tested-by: Mika Westerberg Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 81 ++++++++------------------------------ drivers/dma/dw_dmac_regs.h | 1 + 2 files changed, 18 insertions(+), 64 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 28d5f01c350c..76301c4ba1ad 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -91,14 +92,6 @@ static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) /*----------------------------------------------------------------------*/ -/* - * Because we're not relying on writeback from the controller (it may not - * even be configured into the core!) we don't need to use dma_pool. These - * descriptors -- and associated data -- are cacheable. We do need to make - * sure their dcache entries are written back before handing them off to - * the controller, though. - */ - static struct device *chan2dev(struct dma_chan *chan) { return &chan->dev->device; @@ -137,19 +130,6 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) return ret; } -static void dwc_sync_desc_for_cpu(struct dw_dma_chan *dwc, struct dw_desc *desc) -{ - struct dw_desc *child; - - list_for_each_entry(child, &desc->tx_list, desc_node) - dma_sync_single_for_cpu(chan2parent(&dwc->chan), - child->txd.phys, sizeof(child->lli), - DMA_TO_DEVICE); - dma_sync_single_for_cpu(chan2parent(&dwc->chan), - desc->txd.phys, sizeof(desc->lli), - DMA_TO_DEVICE); -} - /* * Move a descriptor, including any children, to the free list. * `desc' must not be on any lists. @@ -161,8 +141,6 @@ static void dwc_desc_put(struct dw_dma_chan *dwc, struct dw_desc *desc) if (desc) { struct dw_desc *child; - dwc_sync_desc_for_cpu(dwc, desc); - spin_lock_irqsave(&dwc->lock, flags); list_for_each_entry(child, &desc->tx_list, desc_node) dev_vdbg(chan2dev(&dwc->chan), @@ -335,8 +313,6 @@ dwc_descriptor_complete(struct dw_dma_chan *dwc, struct dw_desc *desc, param = txd->callback_param; } - dwc_sync_desc_for_cpu(dwc, desc); - /* async_tx_ack */ list_for_each_entry(child, &desc->tx_list, desc_node) async_tx_ack(&child->txd); @@ -770,25 +746,17 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } prev = desc; } - if (flags & DMA_PREP_INTERRUPT) /* Trigger interrupt after last block */ prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); - first->txd.flags = flags; first->len = len; @@ -876,10 +844,6 @@ slave_sg_todev_fill_desc: first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, - sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } @@ -938,10 +902,6 @@ slave_sg_fromdev_fill_desc: first = desc; } else { prev->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, - sizeof(prev->lli), - DMA_TO_DEVICE); list_add_tail(&desc->desc_node, &first->tx_list); } @@ -961,10 +921,6 @@ slave_sg_fromdev_fill_desc: prev->lli.ctllo |= DWC_CTLL_INT_EN; prev->lli.llp = 0; - dma_sync_single_for_device(chan2parent(chan), - prev->txd.phys, sizeof(prev->lli), - DMA_TO_DEVICE); - first->len = total_len; return &first->txd; @@ -1118,7 +1074,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) struct dw_desc *desc; int i; unsigned long flags; - int ret; dev_vdbg(chan2dev(chan), "%s\n", __func__); @@ -1139,21 +1094,21 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&dwc->lock, flags); i = dwc->descs_allocated; while (dwc->descs_allocated < NR_DESCS_PER_CHANNEL) { + dma_addr_t phys; + spin_unlock_irqrestore(&dwc->lock, flags); - desc = kzalloc(sizeof(struct dw_desc), GFP_KERNEL); + desc = dma_pool_alloc(dw->desc_pool, GFP_ATOMIC, &phys); if (!desc) goto err_desc_alloc; + memset(desc, 0, sizeof(struct dw_desc)); + INIT_LIST_HEAD(&desc->tx_list); dma_async_tx_descriptor_init(&desc->txd, chan); desc->txd.tx_submit = dwc_tx_submit; desc->txd.flags = DMA_CTRL_ACK; - desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli, - sizeof(desc->lli), DMA_TO_DEVICE); - ret = dma_mapping_error(chan2parent(chan), desc->txd.phys); - if (ret) - goto err_desc_alloc; + desc->txd.phys = phys; dwc_desc_put(dwc, desc); @@ -1168,8 +1123,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) return i; err_desc_alloc: - kfree(desc); - dev_info(chan2dev(chan), "only allocated %d descriptors\n", i); return i; @@ -1204,9 +1157,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) list_for_each_entry_safe(desc, _desc, &list, desc_node) { dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); - dma_unmap_single(chan2parent(chan), desc->txd.phys, - sizeof(desc->lli), DMA_TO_DEVICE); - kfree(desc); + dma_pool_free(dw->desc_pool, desc, desc->txd.phys); } dev_vdbg(chan2dev(chan), "%s: done\n", __func__); @@ -1451,20 +1402,14 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, desc->lli.ctlhi = (period_len >> reg_width); cdesc->desc[i] = desc; - if (last) { + if (last) last->lli.llp = desc->txd.phys; - dma_sync_single_for_device(chan2parent(chan), - last->txd.phys, sizeof(last->lli), - DMA_TO_DEVICE); - } last = desc; } /* lets make a cyclic list */ last->lli.llp = cdesc->desc[0]->txd.phys; - dma_sync_single_for_device(chan2parent(chan), last->txd.phys, - sizeof(last->lli), DMA_TO_DEVICE); dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " "period %zu periods %d\n", (unsigned long long)buf_addr, @@ -1722,6 +1667,14 @@ static int dw_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dw); + /* create a pool of consistent memory blocks for hardware descriptors */ + dw->desc_pool = dmam_pool_create("dw_dmac_desc_pool", &pdev->dev, + sizeof(struct dw_desc), 4, 0); + if (!dw->desc_pool) { + dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); + return -ENOMEM; + } + tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); INIT_LIST_HEAD(&dw->dma.channels); diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index 577f2dd35882..fef296de4af1 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -235,6 +235,7 @@ static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) struct dw_dma { struct dma_device dma; void __iomem *regs; + struct dma_pool *desc_pool; struct tasklet_struct tasklet; struct clk *clk; From 5be10f349bc0a2f3dd2ab6417ffe29746403984c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 17 Jan 2013 10:03:01 +0200 Subject: [PATCH 1069/4607] dw_dmac: don't exceed AHB master number in dwc_get_data_width The driver assumes that hardware has two AHB masters which might not be always true. In such cases we must not exceed number of the AHB masters present in the hardware. In the proposed scheme in this patch, we would choose the master with highest possible number whenever we exceed max AHB masters. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 76301c4ba1ad..635a4a5d31ae 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -47,13 +47,29 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) return slave ? slave->src_master : 1; } +#define SRC_MASTER 0 +#define DST_MASTER 1 + +static inline unsigned int dwc_get_master(struct dma_chan *chan, int master) +{ + struct dw_dma *dw = to_dw_dma(chan->device); + struct dw_dma_slave *dws = chan->private; + unsigned int m; + + if (master == SRC_MASTER) + m = dwc_get_sms(dws); + else + m = dwc_get_dms(dws); + + return min_t(unsigned int, dw->nr_masters - 1, m); +} + #define DWC_DEFAULT_CTLLO(_chan) ({ \ - struct dw_dma_slave *__slave = (_chan->private); \ struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \ struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \ bool _is_slave = is_slave_direction(_dwc->direction); \ - int _dms = dwc_get_dms(__slave); \ - int _sms = dwc_get_sms(__slave); \ + int _dms = dwc_get_master(_chan, DST_MASTER); \ + int _sms = dwc_get_master(_chan, SRC_MASTER); \ u8 _smsize = _is_slave ? _sconfig->src_maxburst : \ DW_DMA_MSIZE_16; \ u8 _dmsize = _is_slave ? _sconfig->dst_maxburst : \ @@ -74,20 +90,11 @@ static inline unsigned int dwc_get_sms(struct dw_dma_slave *slave) */ #define NR_DESCS_PER_CHANNEL 64 -#define SRC_MASTER 0 -#define DST_MASTER 1 - static inline unsigned int dwc_get_data_width(struct dma_chan *chan, int master) { struct dw_dma *dw = to_dw_dma(chan->device); - struct dw_dma_slave *dws = chan->private; - if (master == SRC_MASTER) - return dw->data_width[dwc_get_sms(dws)]; - else if (master == DST_MASTER) - return dw->data_width[dwc_get_dms(dws)]; - - return 0; + return dw->data_width[dwc_get_master(chan, master)]; } /*----------------------------------------------------------------------*/ From 77bcc497c60ec62dbb84abc809a6e218d53409e9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 18 Jan 2013 14:14:15 +0200 Subject: [PATCH 1070/4607] dw_dmac: move soft LLP code from tasklet to dwc_scan_descriptors The proper place for the main logic of the soft LLP mode is dwc_scan_descriptors. It prevents to get the transfer unexpectedly aborted in case the user calls dwc_tx_status. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 635a4a5d31ae..dc3b9558a25c 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -400,6 +400,20 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) if (status_xfer & dwc->mask) { /* Everything we've submitted is done */ dma_writel(dw, CLEAR.XFER, dwc->mask); + + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { + if (dwc->tx_node_active != dwc->tx_list) { + desc = to_dw_desc(dwc->tx_node_active); + + /* Submit next block */ + dwc_do_single_block(dwc, desc); + spin_unlock_irqrestore(&dwc->lock, flags); + + return; + } + /* We are done here */ + clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); + } spin_unlock_irqrestore(&dwc->lock, flags); dwc_complete_all(dw, dwc); @@ -411,6 +425,12 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) return; } + if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { + dev_vdbg(chan2dev(&dwc->chan), "%s: soft LLP mode\n", __func__); + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } + dev_vdbg(chan2dev(&dwc->chan), "%s: llp=0x%llx\n", __func__, (unsigned long long)llp); @@ -596,29 +616,8 @@ static void dw_dma_tasklet(unsigned long data) dwc_handle_cyclic(dw, dwc, status_err, status_xfer); else if (status_err & (1 << i)) dwc_handle_error(dw, dwc); - else if (status_xfer & (1 << i)) { - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); - if (test_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags)) { - if (dwc->tx_node_active != dwc->tx_list) { - struct dw_desc *desc = - to_dw_desc(dwc->tx_node_active); - - dma_writel(dw, CLEAR.XFER, dwc->mask); - - dwc_do_single_block(dwc, desc); - - spin_unlock_irqrestore(&dwc->lock, flags); - continue; - } - /* we are done here */ - clear_bit(DW_DMA_IS_SOFT_LLP, &dwc->flags); - } - spin_unlock_irqrestore(&dwc->lock, flags); - + else if (status_xfer & (1 << i)) dwc_scan_descriptors(dw, dwc); - } } /* From 64748a2c9062da0c32b59c1b368a86fc4613b1e1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Jan 2013 17:03:02 +1030 Subject: [PATCH 1071/4607] module: printk message when module signature fail taints kernel. Reported-by: Chris Samuel Signed-off-by: Rusty Russell --- kernel/module.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/module.c b/kernel/module.c index eab08274ec9b..e69a5a68766f 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3192,8 +3192,13 @@ again: #ifdef CONFIG_MODULE_SIG mod->sig_ok = info->sig_ok; - if (!mod->sig_ok) + if (!mod->sig_ok) { + printk_once(KERN_NOTICE + "%s: module verification failed: signature and/or" + " required key missing - tainting kernel\n", + mod->name); add_taint_module(mod, TAINT_FORCED_MODULE); + } #endif /* Now module is in final location, initialize linked lists, etc. */ From 373d4d099761cb1f637bed488ab3871945882273 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Jan 2013 17:17:39 +1030 Subject: [PATCH 1072/4607] taint: add explicit flag to show whether lock dep is still OK. Fix up all callers as they were before, with make one change: an unsigned module taints the kernel, but doesn't turn off lockdep. Signed-off-by: Rusty Russell --- arch/alpha/kernel/traps.c | 2 +- arch/arm/kernel/traps.c | 2 +- arch/arm64/kernel/traps.c | 2 +- arch/avr32/kernel/traps.c | 2 +- arch/hexagon/kernel/traps.c | 2 +- arch/ia64/kernel/traps.c | 2 +- arch/m68k/kernel/traps.c | 2 +- arch/mips/kernel/traps.c | 2 +- arch/parisc/kernel/traps.c | 2 +- arch/powerpc/kernel/traps.c | 2 +- arch/s390/kernel/traps.c | 2 +- arch/sh/kernel/traps.c | 2 +- arch/sparc/kernel/setup_64.c | 2 +- arch/sparc/kernel/traps_32.c | 2 +- arch/sparc/kernel/traps_64.c | 2 +- arch/unicore32/kernel/traps.c | 2 +- arch/x86/kernel/cpu/amd.c | 3 +-- arch/x86/kernel/cpu/mcheck/mce.c | 2 +- arch/x86/kernel/cpu/mcheck/p5.c | 2 +- arch/x86/kernel/cpu/mcheck/winchip.c | 2 +- arch/x86/kernel/cpu/mtrr/generic.c | 2 +- arch/x86/kernel/dumpstack.c | 2 +- arch/xtensa/kernel/traps.c | 2 +- drivers/acpi/custom_method.c | 2 +- drivers/acpi/osl.c | 2 +- drivers/base/regmap/regmap-debugfs.c | 2 +- include/linux/kernel.h | 6 ++++- kernel/module.c | 26 ++++++++++++--------- kernel/panic.c | 34 ++++++++++++---------------- kernel/sched/core.c | 2 +- kernel/sysctl.c | 2 +- lib/bug.c | 3 ++- mm/memory.c | 2 +- mm/page_alloc.c | 2 +- mm/slab.c | 2 +- mm/slub.c | 2 +- sound/soc/soc-core.c | 2 +- 37 files changed, 69 insertions(+), 67 deletions(-) diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index 272666d006df..4037461a6493 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -186,7 +186,7 @@ die_if_kernel(char * str, struct pt_regs *regs, long err, unsigned long *r9_15) #endif printk("%s(%d): %s %ld\n", current->comm, task_pid_nr(current), str, err); dik_show_regs(regs, r9_15); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); dik_show_trace((unsigned long *)(regs+1)); dik_show_code((unsigned int *)regs->pc); diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index b0179b89a04c..1c089119b2d7 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -296,7 +296,7 @@ static void oops_end(unsigned long flags, struct pt_regs *regs, int signr) bust_spinlocks(0); die_owner = -1; - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); die_nest_count--; if (!die_nest_count) /* Nest count reaches zero, release the lock. */ diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 3883f842434f..b3c5f628bdb4 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -242,7 +242,7 @@ void die(const char *str, struct pt_regs *regs, int err) crash_kexec(regs); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); raw_spin_unlock_irq(&die_lock); oops_exit(); diff --git a/arch/avr32/kernel/traps.c b/arch/avr32/kernel/traps.c index 3d760c06f024..682b2478691a 100644 --- a/arch/avr32/kernel/traps.c +++ b/arch/avr32/kernel/traps.c @@ -61,7 +61,7 @@ void die(const char *str, struct pt_regs *regs, long err) show_regs_log_lvl(regs, KERN_EMERG); show_stack_log_lvl(current, regs->sp, regs, KERN_EMERG); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die_lock); if (in_interrupt()) diff --git a/arch/hexagon/kernel/traps.c b/arch/hexagon/kernel/traps.c index a41eeb8eeaa1..be5e2dd9c9d3 100644 --- a/arch/hexagon/kernel/traps.c +++ b/arch/hexagon/kernel/traps.c @@ -225,7 +225,7 @@ int die(const char *str, struct pt_regs *regs, long err) do_show_stack(current, ®s->r30, pt_elr(regs)); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die.lock); diff --git a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c index bd42b76000d1..f7f9f9c6caf0 100644 --- a/arch/ia64/kernel/traps.c +++ b/arch/ia64/kernel/traps.c @@ -72,7 +72,7 @@ die (const char *str, struct pt_regs *regs, long err) bust_spinlocks(0); die.lock_owner = -1; - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die.lock); if (!regs) diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index cbc624af4494..f32ab22e7ed3 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -1176,7 +1176,7 @@ void die_if_kernel (char *str, struct pt_regs *fp, int nr) console_verbose(); printk("%s: %08x\n",str,nr); show_registers(fp); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); do_exit(SIGSEGV); } diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index cf7ac5483f53..9007966d56d4 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -396,7 +396,7 @@ void __noreturn die(const char *str, struct pt_regs *regs) printk("%s[#%d]:\n", str, ++die_counter); show_registers(regs); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); raw_spin_unlock_irq(&die_lock); oops_exit(); diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index 45ba99f5080b..aeb8f8f2c07a 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c @@ -282,7 +282,7 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err) show_regs(regs); dump_stack(); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); if (in_interrupt()) panic("Fatal exception in interrupt"); diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 32518401af68..c579db859388 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -138,7 +138,7 @@ static void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, { bust_spinlocks(0); die_owner = -1; - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); die_nest_count--; oops_exit(); printk("\n"); diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 70ecfc5fe8f0..13dd63fba367 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -271,7 +271,7 @@ void die(struct pt_regs *regs, const char *str) print_modules(); show_regs(regs); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die_lock); if (in_interrupt()) panic("Fatal exception in interrupt"); diff --git a/arch/sh/kernel/traps.c b/arch/sh/kernel/traps.c index 72246bc06884..dfdad72c61ca 100644 --- a/arch/sh/kernel/traps.c +++ b/arch/sh/kernel/traps.c @@ -38,7 +38,7 @@ void die(const char *str, struct pt_regs *regs, long err) notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die_lock); oops_exit(); diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c index 0eaf0059aaef..88a127b9c69e 100644 --- a/arch/sparc/kernel/setup_64.c +++ b/arch/sparc/kernel/setup_64.c @@ -115,7 +115,7 @@ static void __init process_switch(char c) break; } cheetah_pcache_forced_on = 1; - add_taint(TAINT_MACHINE_CHECK); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); cheetah_enable_pcache(); break; diff --git a/arch/sparc/kernel/traps_32.c b/arch/sparc/kernel/traps_32.c index a5785ea2a85d..662982946a89 100644 --- a/arch/sparc/kernel/traps_32.c +++ b/arch/sparc/kernel/traps_32.c @@ -58,7 +58,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) printk("%s(%d): %s [#%d]\n", current->comm, task_pid_nr(current), str, ++die_counter); show_regs(regs); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; __SAVE; diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c index e7ecf1507d90..8d38ca97aa23 100644 --- a/arch/sparc/kernel/traps_64.c +++ b/arch/sparc/kernel/traps_64.c @@ -2383,7 +2383,7 @@ void die_if_kernel(char *str, struct pt_regs *regs) notify_die(DIE_OOPS, str, regs, 0, 255, SIGSEGV); __asm__ __volatile__("flushw"); show_regs(regs); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); if (regs->tstate & TSTATE_PRIV) { struct thread_info *tp = current_thread_info(); struct reg_window *rw = (struct reg_window *) diff --git a/arch/unicore32/kernel/traps.c b/arch/unicore32/kernel/traps.c index 2054f0d4db13..0870b68d2ad9 100644 --- a/arch/unicore32/kernel/traps.c +++ b/arch/unicore32/kernel/traps.c @@ -231,7 +231,7 @@ void die(const char *str, struct pt_regs *regs, int err) ret = __die(str, err, thread, regs); bust_spinlocks(0); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die_lock); oops_exit(); diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 15239fffd6fe..5853e57523e5 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -220,8 +220,7 @@ static void __cpuinit amd_k7_smp_check(struct cpuinfo_x86 *c) */ WARN_ONCE(1, "WARNING: This combination of AMD" " processors is not suitable for SMP.\n"); - if (!test_taint(TAINT_UNSAFE_SMP)) - add_taint(TAINT_UNSAFE_SMP); + add_taint(TAINT_UNSAFE_SMP, LOCKDEP_NOW_UNRELIABLE); valid_k7: ; diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index 80dbda84f1c3..6bc15edbc8cd 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -1085,7 +1085,7 @@ void do_machine_check(struct pt_regs *regs, long error_code) /* * Set taint even when machine check was not enabled. */ - add_taint(TAINT_MACHINE_CHECK); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); severity = mce_severity(&m, cfg->tolerant, NULL); diff --git a/arch/x86/kernel/cpu/mcheck/p5.c b/arch/x86/kernel/cpu/mcheck/p5.c index 2d5454cd2c4f..1c044b1ccc59 100644 --- a/arch/x86/kernel/cpu/mcheck/p5.c +++ b/arch/x86/kernel/cpu/mcheck/p5.c @@ -33,7 +33,7 @@ static void pentium_machine_check(struct pt_regs *regs, long error_code) smp_processor_id()); } - add_taint(TAINT_MACHINE_CHECK); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); } /* Set up machine check reporting for processors with Intel style MCE: */ diff --git a/arch/x86/kernel/cpu/mcheck/winchip.c b/arch/x86/kernel/cpu/mcheck/winchip.c index 2d7998fb628c..e9a701aecaa1 100644 --- a/arch/x86/kernel/cpu/mcheck/winchip.c +++ b/arch/x86/kernel/cpu/mcheck/winchip.c @@ -15,7 +15,7 @@ static void winchip_machine_check(struct pt_regs *regs, long error_code) { printk(KERN_EMERG "CPU0: Machine Check Exception.\n"); - add_taint(TAINT_MACHINE_CHECK); + add_taint(TAINT_MACHINE_CHECK, LOCKDEP_NOW_UNRELIABLE); } /* Set up machine check reporting on the Winchip C6 series */ diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c index e9fe907cd249..fa72a39e5d46 100644 --- a/arch/x86/kernel/cpu/mtrr/generic.c +++ b/arch/x86/kernel/cpu/mtrr/generic.c @@ -542,7 +542,7 @@ static void generic_get_mtrr(unsigned int reg, unsigned long *base, if (tmp != mask_lo) { printk(KERN_WARNING "mtrr: your BIOS has configured an incorrect mask, fixing it.\n"); - add_taint(TAINT_FIRMWARE_WORKAROUND); + add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); mask_lo = tmp; } } diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index ae42418bc50f..c8797d55b245 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c @@ -232,7 +232,7 @@ void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr) bust_spinlocks(0); die_owner = -1; - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); die_nest_count--; if (!die_nest_count) /* Nest count reaches zero, release the lock. */ diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 01e0111bf787..ded955d45155 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -524,7 +524,7 @@ void die(const char * str, struct pt_regs * regs, long err) if (!user_mode(regs)) show_stack(NULL, (unsigned long*)regs->areg[1]); - add_taint(TAINT_DIE); + add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); spin_unlock_irq(&die_lock); if (in_interrupt()) diff --git a/drivers/acpi/custom_method.c b/drivers/acpi/custom_method.c index 5d42c2414ae5..cd9a68e2fea9 100644 --- a/drivers/acpi/custom_method.c +++ b/drivers/acpi/custom_method.c @@ -66,7 +66,7 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, buf = NULL; if (ACPI_FAILURE(status)) return -EINVAL; - add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); + add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE); } return count; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 3ff267861541..535e888bad78 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -661,7 +661,7 @@ static void acpi_table_taint(struct acpi_table_header *table) pr_warn(PREFIX "Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n", table->signature, table->oem_table_id); - add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); + add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE); } diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 46a213a596e2..f63c830083ec 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -267,7 +267,7 @@ static ssize_t regmap_map_write_file(struct file *file, return -EINVAL; /* Userspace has been fiddling around behind the kernel's back */ - add_taint(TAINT_USER); + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); regmap_write(map, reg, value); return buf_size; diff --git a/include/linux/kernel.h b/include/linux/kernel.h index c566927efcbd..80d36874689b 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -398,7 +398,11 @@ extern int panic_on_unrecovered_nmi; extern int panic_on_io_nmi; extern int sysctl_panic_on_stackoverflow; extern const char *print_tainted(void); -extern void add_taint(unsigned flag); +enum lockdep_ok { + LOCKDEP_STILL_OK, + LOCKDEP_NOW_UNRELIABLE +}; +extern void add_taint(unsigned flag, enum lockdep_ok); extern int test_taint(unsigned flag); extern unsigned long get_taint(void); extern int root_mountflags; diff --git a/kernel/module.c b/kernel/module.c index e69a5a68766f..cc000dd6e4a8 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -197,9 +197,10 @@ static inline int strong_try_module_get(struct module *mod) return -ENOENT; } -static inline void add_taint_module(struct module *mod, unsigned flag) +static inline void add_taint_module(struct module *mod, unsigned flag, + enum lockdep_ok lockdep_ok) { - add_taint(flag); + add_taint(flag, lockdep_ok); mod->taints |= (1U << flag); } @@ -727,7 +728,7 @@ static inline int try_force_unload(unsigned int flags) { int ret = (flags & O_TRUNC); if (ret) - add_taint(TAINT_FORCED_RMMOD); + add_taint(TAINT_FORCED_RMMOD, LOCKDEP_NOW_UNRELIABLE); return ret; } #else @@ -1138,7 +1139,7 @@ static int try_to_force_load(struct module *mod, const char *reason) if (!test_taint(TAINT_FORCED_MODULE)) printk(KERN_WARNING "%s: %s: kernel tainted.\n", mod->name, reason); - add_taint_module(mod, TAINT_FORCED_MODULE); + add_taint_module(mod, TAINT_FORCED_MODULE, LOCKDEP_NOW_UNRELIABLE); return 0; #else return -ENOEXEC; @@ -2147,7 +2148,8 @@ static void set_license(struct module *mod, const char *license) if (!test_taint(TAINT_PROPRIETARY_MODULE)) printk(KERN_WARNING "%s: module license '%s' taints " "kernel.\n", mod->name, license); - add_taint_module(mod, TAINT_PROPRIETARY_MODULE); + add_taint_module(mod, TAINT_PROPRIETARY_MODULE, + LOCKDEP_NOW_UNRELIABLE); } } @@ -2700,10 +2702,10 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags) } if (!get_modinfo(info, "intree")) - add_taint_module(mod, TAINT_OOT_MODULE); + add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK); if (get_modinfo(info, "staging")) { - add_taint_module(mod, TAINT_CRAP); + add_taint_module(mod, TAINT_CRAP, LOCKDEP_STILL_OK); printk(KERN_WARNING "%s: module is from the staging directory," " the quality is unknown, you have been warned.\n", mod->name); @@ -2869,15 +2871,17 @@ static int check_module_license_and_versions(struct module *mod) * using GPL-only symbols it needs. */ if (strcmp(mod->name, "ndiswrapper") == 0) - add_taint(TAINT_PROPRIETARY_MODULE); + add_taint(TAINT_PROPRIETARY_MODULE, LOCKDEP_NOW_UNRELIABLE); /* driverloader was caught wrongly pretending to be under GPL */ if (strcmp(mod->name, "driverloader") == 0) - add_taint_module(mod, TAINT_PROPRIETARY_MODULE); + add_taint_module(mod, TAINT_PROPRIETARY_MODULE, + LOCKDEP_NOW_UNRELIABLE); /* lve claims to be GPL but upstream won't provide source */ if (strcmp(mod->name, "lve") == 0) - add_taint_module(mod, TAINT_PROPRIETARY_MODULE); + add_taint_module(mod, TAINT_PROPRIETARY_MODULE, + LOCKDEP_NOW_UNRELIABLE); #ifdef CONFIG_MODVERSIONS if ((mod->num_syms && !mod->crcs) @@ -3197,7 +3201,7 @@ again: "%s: module verification failed: signature and/or" " required key missing - tainting kernel\n", mod->name); - add_taint_module(mod, TAINT_FORCED_MODULE); + add_taint_module(mod, TAINT_FORCED_MODULE, LOCKDEP_STILL_OK); } #endif diff --git a/kernel/panic.c b/kernel/panic.c index e1b2822fff97..7c57cc9eee2c 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -259,26 +259,19 @@ unsigned long get_taint(void) return tainted_mask; } -void add_taint(unsigned flag) +/** + * add_taint: add a taint flag if not already set. + * @flag: one of the TAINT_* constants. + * @lockdep_ok: whether lock debugging is still OK. + * + * If something bad has gone wrong, you'll want @lockdebug_ok = false, but for + * some notewortht-but-not-corrupting cases, it can be set to true. + */ +void add_taint(unsigned flag, enum lockdep_ok lockdep_ok) { - /* - * Can't trust the integrity of the kernel anymore. - * We don't call directly debug_locks_off() because the issue - * is not necessarily serious enough to set oops_in_progress to 1 - * Also we want to keep up lockdep for staging/out-of-tree - * development and post-warning case. - */ - switch (flag) { - case TAINT_CRAP: - case TAINT_OOT_MODULE: - case TAINT_WARN: - case TAINT_FIRMWARE_WORKAROUND: - break; - - default: - if (__debug_locks_off()) - printk(KERN_WARNING "Disabling lock debugging due to kernel taint\n"); - } + if (lockdep_ok == LOCKDEP_NOW_UNRELIABLE && __debug_locks_off()) + printk(KERN_WARNING + "Disabling lock debugging due to kernel taint\n"); set_bit(flag, &tainted_mask); } @@ -421,7 +414,8 @@ static void warn_slowpath_common(const char *file, int line, void *caller, print_modules(); dump_stack(); print_oops_end_marker(); - add_taint(taint); + /* Just a warning, don't kill lockdep. */ + add_taint(taint, LOCKDEP_STILL_OK); } void warn_slowpath_fmt(const char *file, int line, const char *fmt, ...) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 257002c13bb0..662f3d512183 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2785,7 +2785,7 @@ static noinline void __schedule_bug(struct task_struct *prev) if (irqs_disabled()) print_irqtrace_events(prev); dump_stack(); - add_taint(TAINT_WARN); + add_taint(TAINT_WARN, LOCKDEP_STILL_OK); } /* diff --git a/kernel/sysctl.c b/kernel/sysctl.c index c88878db491e..f97f9d75cde8 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -2006,7 +2006,7 @@ static int proc_taint(struct ctl_table *table, int write, int i; for (i = 0; i < BITS_PER_LONG && tmptaint >> i; i++) { if ((tmptaint >> i) & 1) - add_taint(i); + add_taint(i, LOCKDEP_STILL_OK); } } diff --git a/lib/bug.c b/lib/bug.c index d0cdf14c651a..168603477f02 100644 --- a/lib/bug.c +++ b/lib/bug.c @@ -166,7 +166,8 @@ enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) print_modules(); show_regs(regs); print_oops_end_marker(); - add_taint(BUG_GET_TAINT(bug)); + /* Just a warning, don't kill lockdep. */ + add_taint(BUG_GET_TAINT(bug), LOCKDEP_STILL_OK); return BUG_TRAP_TYPE_WARN; } diff --git a/mm/memory.c b/mm/memory.c index bb1369f7b9b4..bc8bec762db7 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -716,7 +716,7 @@ static void print_bad_pte(struct vm_area_struct *vma, unsigned long addr, print_symbol(KERN_ALERT "vma->vm_file->f_op->mmap: %s\n", (unsigned long)vma->vm_file->f_op->mmap); dump_stack(); - add_taint(TAINT_BAD_PAGE); + add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); } static inline bool is_cow_mapping(vm_flags_t flags) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index df2022ff0c8a..4c99cb7e276a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -320,7 +320,7 @@ static void bad_page(struct page *page) out: /* Leave bad fields for debug, except PageBuddy could make trouble */ reset_page_mapcount(page); /* remove PageBuddy */ - add_taint(TAINT_BAD_PAGE); + add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); } /* diff --git a/mm/slab.c b/mm/slab.c index e7667a3584bc..856e4a192d25 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -812,7 +812,7 @@ static void __slab_error(const char *function, struct kmem_cache *cachep, printk(KERN_ERR "slab error in %s(): cache `%s': %s\n", function, cachep->name, msg); dump_stack(); - add_taint(TAINT_BAD_PAGE); + add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); } #endif diff --git a/mm/slub.c b/mm/slub.c index ba2ca53f6c3a..7ec3041bdd0d 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -562,7 +562,7 @@ static void slab_bug(struct kmem_cache *s, char *fmt, ...) printk(KERN_ERR "----------------------------------------" "-------------------------------------\n\n"); - add_taint(TAINT_BAD_PAGE); + add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); } static void slab_fix(struct kmem_cache *s, char *fmt, ...) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2370063b5824..4c6526c0db03 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -251,7 +251,7 @@ static ssize_t codec_reg_write_file(struct file *file, return -EINVAL; /* Userspace has been fiddling around behind the kernel's back */ - add_taint(TAINT_USER); + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); snd_soc_write(codec, reg, value); return buf_size; From 93843b3764170819aeb07fc867a0499b33a875ca Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 21 Jan 2013 17:17:39 +1030 Subject: [PATCH 1073/4607] module: constify within_module_* These helper functions just check a set intersection with a range, and don't actually modify struct module. Signed-off-by: Sasha Levin Signed-off-by: Rusty Russell --- include/linux/module.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/module.h b/include/linux/module.h index 1375ee3f03aa..ead1b5719a12 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -396,13 +396,13 @@ bool is_module_address(unsigned long addr); bool is_module_percpu_address(unsigned long addr); bool is_module_text_address(unsigned long addr); -static inline int within_module_core(unsigned long addr, struct module *mod) +static inline int within_module_core(unsigned long addr, const struct module *mod) { return (unsigned long)mod->module_core <= addr && addr < (unsigned long)mod->module_core + mod->core_size; } -static inline int within_module_init(unsigned long addr, struct module *mod) +static inline int within_module_init(unsigned long addr, const struct module *mod) { return (unsigned long)mod->module_init <= addr && addr < (unsigned long)mod->module_init + mod->init_size; From f2e207f32422c7b9624d3393db015dfd118d9d22 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Mon, 21 Jan 2013 17:18:57 +1030 Subject: [PATCH 1074/4607] modpost: Ignore ARC specific non-alloc sections ARC relocatable object files contain one/more .gnu.linkonce.arcextmap.* sections (collated by kernel/vmlinux.lds into .arcextmap in final link). This section is used by debuggers to display the extension instructions and need-not be loaded by target (hence !SHF_ALLOC) The final kernel binary only needs .arcextmap entry in modpost's ignore list (section_white_list[]). However when building modules, modpost scans each object file individually, hence tripping on non-aggregated .gnu.linkonce.arcextmap.* entries as well. Thus need for the 2 entires ! Signed-off-by: Vineet Gupta Acked-by: Sam Ravnborg Signed-off-by: Rusty Russell --- scripts/mod/modpost.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index ff36c508a10e..1c6fbb1a4f8e 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -830,6 +830,8 @@ static const char *section_white_list[] = ".toc*", ".xt.prop", /* xtensa */ ".xt.lit", /* xtensa */ + ".arcextmap*", /* arc */ + ".gnu.linkonce.arcext*", /* arc : modules */ NULL }; From a3535c7e4f4495fe947f7901d25447d80e04fe52 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Jan 2013 17:18:59 +1030 Subject: [PATCH 1075/4607] module: clean up load_module a little more. 1fb9341ac34825aa40354e74d9a2c69df7d2c304 made our locking in load_module more complicated: we grab the mutex once to insert the module in the list, then again to upgrade it once it's formed. Since the locking is self-contained, it's neater to do this in separate functions. Signed-off-by: Rusty Russell --- kernel/module.c | 107 ++++++++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 39 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index cc000dd6e4a8..921bed4794e9 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3145,12 +3145,72 @@ static int may_init_module(void) return 0; } +/* + * We try to place it in the list now to make sure it's unique before + * we dedicate too many resources. In particular, temporary percpu + * memory exhaustion. + */ +static int add_unformed_module(struct module *mod) +{ + int err; + struct module *old; + + mod->state = MODULE_STATE_UNFORMED; + +again: + mutex_lock(&module_mutex); + if ((old = find_module_all(mod->name, true)) != NULL) { + if (old->state == MODULE_STATE_COMING + || old->state == MODULE_STATE_UNFORMED) { + /* Wait in case it fails to load. */ + mutex_unlock(&module_mutex); + err = wait_event_interruptible(module_wq, + finished_loading(mod->name)); + if (err) + goto out_unlocked; + goto again; + } + err = -EEXIST; + goto out; + } + list_add_rcu(&mod->list, &modules); + err = 0; + +out: + mutex_unlock(&module_mutex); +out_unlocked: + return err; +} + +static int complete_formation(struct module *mod, struct load_info *info) +{ + int err; + + mutex_lock(&module_mutex); + + /* Find duplicate symbols (must be called under lock). */ + err = verify_export_symbols(mod); + if (err < 0) + goto out; + + /* This relies on module_mutex for list integrity. */ + module_bug_finalize(info->hdr, info->sechdrs, mod); + + /* Mark state as coming so strong_try_module_get() ignores us, + * but kallsyms etc. can see us. */ + mod->state = MODULE_STATE_COMING; + +out: + mutex_unlock(&module_mutex); + return err; +} + /* Allocate and load the module: note that size of section 0 is always zero, and we rely on this for optional sections. */ static int load_module(struct load_info *info, const char __user *uargs, int flags) { - struct module *mod, *old; + struct module *mod; long err; err = module_sig_check(info); @@ -3168,31 +3228,10 @@ static int load_module(struct load_info *info, const char __user *uargs, goto free_copy; } - /* - * We try to place it in the list now to make sure it's unique - * before we dedicate too many resources. In particular, - * temporary percpu memory exhaustion. - */ - mod->state = MODULE_STATE_UNFORMED; -again: - mutex_lock(&module_mutex); - if ((old = find_module_all(mod->name, true)) != NULL) { - if (old->state == MODULE_STATE_COMING - || old->state == MODULE_STATE_UNFORMED) { - /* Wait in case it fails to load. */ - mutex_unlock(&module_mutex); - err = wait_event_interruptible(module_wq, - finished_loading(mod->name)); - if (err) - goto free_module; - goto again; - } - err = -EEXIST; - mutex_unlock(&module_mutex); + /* Reserve our place in the list. */ + err = add_unformed_module(mod); + if (err) goto free_module; - } - list_add_rcu(&mod->list, &modules); - mutex_unlock(&module_mutex); #ifdef CONFIG_MODULE_SIG mod->sig_ok = info->sig_ok; @@ -3245,21 +3284,11 @@ again: dynamic_debug_setup(info->debug, info->num_debug); - mutex_lock(&module_mutex); - /* Find duplicate symbols (must be called under lock). */ - err = verify_export_symbols(mod); - if (err < 0) + /* Finally it's fully formed, ready to start executing. */ + err = complete_formation(mod, info); + if (err) goto ddebug_cleanup; - /* This relies on module_mutex for list integrity. */ - module_bug_finalize(info->hdr, info->sechdrs, mod); - - /* Mark state as coming so strong_try_module_get() ignores us, - * but kallsyms etc. can see us. */ - mod->state = MODULE_STATE_COMING; - - mutex_unlock(&module_mutex); - /* Module is ready to execute: parsing args may do that. */ err = parse_args(mod->name, mod->args, mod->kp, mod->num_kp, -32768, 32767, &ddebug_dyndbg_module_param_cb); @@ -3283,8 +3312,8 @@ again: /* module_bug_cleanup needs module_mutex protection */ mutex_lock(&module_mutex); module_bug_cleanup(mod); - ddebug_cleanup: mutex_unlock(&module_mutex); + ddebug_cleanup: dynamic_debug_remove(info->debug); synchronize_sched(); kfree(mod->args); From 306a74920ba9ccf6b5f110f97c1cb6bb2caeff93 Mon Sep 17 00:00:00 2001 From: Guo Chao Date: Tue, 18 Dec 2012 16:56:39 +0800 Subject: [PATCH 1076/4607] ext3, ext4, ocfs2: remove unused macro NAMEI_RA_INDEX This macro, initially introduced by ext2 in v0.99.15, does not have any users from the beginning. It has been removed in later ext2 version but still remains in the code of ext3, ext4, ocfs2. Remove this macro there. Cc: Jan Kara Cc: linux-ext4@vger.kernel.org Cc: ocfs2-devel@oss.oracle.com Acked-by: Mark Fasheh Acked-by: "Theodore Ts'o" Signed-off-by: Guo Chao Signed-off-by: Jan Kara --- fs/ext3/namei.c | 1 - fs/ext4/namei.c | 1 - fs/ocfs2/dir.c | 1 - 3 files changed, 3 deletions(-) diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 890b8947c546..88f64eb1b6fa 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -36,7 +36,6 @@ #define NAMEI_RA_CHUNKS 2 #define NAMEI_RA_BLOCKS 4 #define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) -#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b)) static struct buffer_head *ext3_append(handle_t *handle, struct inode *inode, diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index f9ed946a448e..cc9a512f3a8a 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -47,7 +47,6 @@ #define NAMEI_RA_CHUNKS 2 #define NAMEI_RA_BLOCKS 4 #define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) -#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b)) static struct buffer_head *ext4_append(handle_t *handle, struct inode *inode, diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 8fe4e2892ab9..fc121350d8cb 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -67,7 +67,6 @@ #define NAMEI_RA_CHUNKS 2 #define NAMEI_RA_BLOCKS 4 #define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) -#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b)) static unsigned char ocfs2_filetype_table[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK From f56426ae4d4414c9c996567710dceecbdfc39acc Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 31 Dec 2012 12:38:36 +0100 Subject: [PATCH 1077/4607] ext3: Fix memory leak when quota options are specified multiple times When usrjquota or grpjquota mount options are specified several times, we leak memory storing the names. Free the memory correctly. Reported-by: Chen Gang Signed-off-by: Jan Kara --- fs/ext3/super.c | 53 +++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 6e50223b3299..0926fe46ae3e 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -916,21 +916,24 @@ static int set_qf_name(struct super_block *sb, int qtype, substring_t *args) "Not enough memory for storing quotafile name"); return 0; } - if (sbi->s_qf_names[qtype] && - strcmp(sbi->s_qf_names[qtype], qname)) { + if (sbi->s_qf_names[qtype]) { + int same = !strcmp(sbi->s_qf_names[qtype], qname); + + kfree(qname); + if (!same) { + ext3_msg(sb, KERN_ERR, + "%s quota file already specified", + QTYPE2NAME(qtype)); + } + return same; + } + if (strchr(qname, '/')) { ext3_msg(sb, KERN_ERR, - "%s quota file already specified", QTYPE2NAME(qtype)); + "quotafile must be on filesystem root"); kfree(qname); return 0; } sbi->s_qf_names[qtype] = qname; - if (strchr(sbi->s_qf_names[qtype], '/')) { - ext3_msg(sb, KERN_ERR, - "quotafile must be on filesystem root"); - kfree(sbi->s_qf_names[qtype]); - sbi->s_qf_names[qtype] = NULL; - return 0; - } set_opt(sbi->s_mount_opt, QUOTA); return 1; } @@ -945,11 +948,10 @@ static int clear_qf_name(struct super_block *sb, int qtype) { " when quota turned on"); return 0; } - /* - * The space will be released later when all options are confirmed - * to be correct - */ - sbi->s_qf_names[qtype] = NULL; + if (sbi->s_qf_names[qtype]) { + kfree(sbi->s_qf_names[qtype]); + sbi->s_qf_names[qtype] = NULL; + } return 1; } #endif @@ -2605,7 +2607,18 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data) #ifdef CONFIG_QUOTA old_opts.s_jquota_fmt = sbi->s_jquota_fmt; for (i = 0; i < MAXQUOTAS; i++) - old_opts.s_qf_names[i] = sbi->s_qf_names[i]; + if (sbi->s_qf_names[i]) { + old_opts.s_qf_names[i] = kstrdup(sbi->s_qf_names[i], + GFP_KERNEL); + if (!old_opts.s_qf_names[i]) { + int j; + + for (j = 0; j < i; j++) + kfree(old_opts.s_qf_names[j]); + return -ENOMEM; + } + } else + old_opts.s_qf_names[i] = NULL; #endif /* @@ -2698,9 +2711,7 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data) #ifdef CONFIG_QUOTA /* Release old quota file names */ for (i = 0; i < MAXQUOTAS; i++) - if (old_opts.s_qf_names[i] && - old_opts.s_qf_names[i] != sbi->s_qf_names[i]) - kfree(old_opts.s_qf_names[i]); + kfree(old_opts.s_qf_names[i]); #endif if (enable_quota) dquot_resume(sb, -1); @@ -2714,9 +2725,7 @@ restore_opts: #ifdef CONFIG_QUOTA sbi->s_jquota_fmt = old_opts.s_jquota_fmt; for (i = 0; i < MAXQUOTAS; i++) { - if (sbi->s_qf_names[i] && - old_opts.s_qf_names[i] != sbi->s_qf_names[i]) - kfree(sbi->s_qf_names[i]); + kfree(sbi->s_qf_names[i]); sbi->s_qf_names[i] = old_opts.s_qf_names[i]; } #endif From 8d8759eb488f9e88fa5f976c4fd7ed205661c872 Mon Sep 17 00:00:00 2001 From: Wang Shilong Date: Sat, 12 Jan 2013 01:19:32 -0800 Subject: [PATCH 1078/4607] Ext2: free memory allocated and forget buffer head when io error happens Add a necessary check when an io error happens. If io error happens,free the memory allocated and forget buffer head. Signed-off-by: Wang Shilong Signed-off-by: Jan Kara --- fs/ext2/inode.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 6363ac66fafa..c3881e56662e 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -495,6 +495,10 @@ static int ext2_alloc_branch(struct inode *inode, * parent to disk. */ bh = sb_getblk(inode->i_sb, new_blocks[n-1]); + if (unlikely(!bh)) { + err = -ENOMEM; + goto failed; + } branch[n].bh = bh; lock_buffer(bh); memset(bh->b_data, 0, blocksize); @@ -523,6 +527,14 @@ static int ext2_alloc_branch(struct inode *inode, } *blks = num; return err; + +failed: + for (i = 1; i < n; i++) + bforget(branch[i].bh); + for (i = 0; i < indirect_blks; i++) + ext2_free_blocks(inode, new_blocks[i], 1); + ext2_free_blocks(inode, new_blocks[i], num); + return err; } /** From 61f43e6880dee5983999fe40bf96c1cf43740b4c Mon Sep 17 00:00:00 2001 From: Wang Shilong Date: Sat, 12 Jan 2013 01:22:33 -0800 Subject: [PATCH 1079/4607] Ext3: add necessary check in case IO error happens As we know io error may happen when the function 'sb_getblk' is called.Add necessary check for it The patch also fix a coding style problem. Signed-off-by: Wang Shilong Signed-off-by: Jan Kara --- fs/ext3/inode.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index b176d4253544..6e4f8a529fbc 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -676,6 +676,10 @@ static int ext3_alloc_branch(handle_t *handle, struct inode *inode, * parent to disk. */ bh = sb_getblk(inode->i_sb, new_blocks[n-1]); + if (unlikely(!bh)) { + err = -ENOMEM; + goto failed; + } branch[n].bh = bh; lock_buffer(bh); BUFFER_TRACE(bh, "call get_create_access"); @@ -717,7 +721,7 @@ failed: BUFFER_TRACE(branch[i].bh, "call journal_forget"); ext3_journal_forget(handle, branch[i].bh); } - for (i = 0; i Date: Sat, 12 Jan 2013 01:34:50 -0800 Subject: [PATCH 1080/4607] Ext2: use unlikely to improve the efficiency of the kernel Because the function 'sb_getblk' seldomly fails to return NULL value. It will be better to use unlikely to optimize it. Signed-off-by: Wang shilong Signed-off-by: Jan Kara --- fs/ext2/super.c | 2 +- fs/ext2/xattr.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ext2/super.c b/fs/ext2/super.c index fa04d023177e..7f68c8114026 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -1500,7 +1500,7 @@ static ssize_t ext2_quota_write(struct super_block *sb, int type, bh = sb_bread(sb, tmp_bh.b_blocknr); else bh = sb_getblk(sb, tmp_bh.b_blocknr); - if (!bh) { + if (unlikely(!bh)) { err = -EIO; goto out; } diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index b6754dbbce3c..06209ec46152 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -662,7 +662,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, ea_idebug(inode, "creating block %d", block); new_bh = sb_getblk(sb, block); - if (!new_bh) { + if (unlikely(!new_bh)) { ext2_free_blocks(inode, block, 1); mark_inode_dirty(inode); error = -EIO; From 1b7d76e9b1106f2be062f915b05d47658dd4fc63 Mon Sep 17 00:00:00 2001 From: Wang Shilong Date: Sat, 12 Jan 2013 01:36:17 -0800 Subject: [PATCH 1081/4607] Ext3: use unlikely to improve the efficiency of the kernel Because the function 'sb_getblk' seldomly fails to return NULL value,it will be better to use unlikely to check it. Signed-off-by: Wang Shilong Signed-off-by: Jan Kara --- fs/ext3/inode.c | 6 +++--- fs/ext3/resize.c | 6 +++--- fs/ext3/xattr.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 6e4f8a529fbc..d7df06839f6a 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1082,7 +1082,7 @@ struct buffer_head *ext3_getblk(handle_t *handle, struct inode *inode, if (!err && buffer_mapped(&dummy)) { struct buffer_head *bh; bh = sb_getblk(inode->i_sb, dummy.b_blocknr); - if (!bh) { + if (unlikely(!bh)) { *errp = -EIO; goto err; } @@ -2733,7 +2733,7 @@ static int __ext3_get_inode_loc(struct inode *inode, return -EIO; bh = sb_getblk(inode->i_sb, block); - if (!bh) { + if (unlikely(!bh)) { ext3_error (inode->i_sb, "ext3_get_inode_loc", "unable to read inode block - " "inode=%lu, block="E3FSBLK, @@ -2787,7 +2787,7 @@ static int __ext3_get_inode_loc(struct inode *inode, bitmap_bh = sb_getblk(inode->i_sb, le32_to_cpu(desc->bg_inode_bitmap)); - if (!bitmap_bh) + if (unlikely(!bitmap_bh)) goto make_io; /* diff --git a/fs/ext3/resize.c b/fs/ext3/resize.c index 0f814f3450de..704e8ce7d782 100644 --- a/fs/ext3/resize.c +++ b/fs/ext3/resize.c @@ -116,7 +116,7 @@ static struct buffer_head *bclean(handle_t *handle, struct super_block *sb, int err; bh = sb_getblk(sb, blk); - if (!bh) + if (unlikely(!bh)) return ERR_PTR(-EIO); if ((err = ext3_journal_get_write_access(handle, bh))) { brelse(bh); @@ -234,7 +234,7 @@ static int setup_new_group_blocks(struct super_block *sb, goto exit_bh; gdb = sb_getblk(sb, block); - if (!gdb) { + if (unlikely(!gdb)) { err = -EIO; goto exit_bh; } @@ -722,7 +722,7 @@ static void update_backups(struct super_block *sb, break; bh = sb_getblk(sb, group * bpg + blk_off); - if (!bh) { + if (unlikely(!bh)) { err = -EIO; break; } diff --git a/fs/ext3/xattr.c b/fs/ext3/xattr.c index d22ebb7a4f55..9f57470b1727 100644 --- a/fs/ext3/xattr.c +++ b/fs/ext3/xattr.c @@ -813,7 +813,7 @@ inserted: ea_idebug(inode, "creating block %d", block); new_bh = sb_getblk(sb, block); - if (!new_bh) { + if (unlikely(!new_bh)) { getblk_failed: ext3_free_blocks(handle, inode, block, 1); error = -EIO; From ab6a773dbcbd2bba3ead8676ae21ce5adbbdc035 Mon Sep 17 00:00:00 2001 From: Wang Shilong Date: Tue, 15 Jan 2013 21:19:06 -0800 Subject: [PATCH 1082/4607] Ext2: return ENOMEM rather than EIO if sb_getblk fails As the only reason that sb_getblks fails is that allocation fails. It will be better to use ENOMEM rather than EIO. Signed-off-by: Wang Shilong Signed-off-by: Jan Kara --- fs/ext2/xattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 06209ec46152..2d7557db3ae8 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -665,7 +665,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, if (unlikely(!new_bh)) { ext2_free_blocks(inode, block, 1); mark_inode_dirty(inode); - error = -EIO; + error = -ENOMEM; goto cleanup; } lock_buffer(new_bh); From c04e88e271ab67de1409c3b4a4e80dbe13eac7b0 Mon Sep 17 00:00:00 2001 From: Wang Shilong Date: Tue, 15 Jan 2013 21:20:01 -0800 Subject: [PATCH 1083/4607] Ext3: return ENOMEM rather than EIO if sb_getblk fails It will be better to use ENOMEM rather than EIO, because the only reason that sb_getblk fails is that allocation fails. Signed-off-by: Wang Shilong Signed-off-by: Jan Kara --- fs/ext3/inode.c | 4 ++-- fs/ext3/resize.c | 6 +++--- fs/ext3/xattr.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index d7df06839f6a..d512c4bc4ad7 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1083,7 +1083,7 @@ struct buffer_head *ext3_getblk(handle_t *handle, struct inode *inode, struct buffer_head *bh; bh = sb_getblk(inode->i_sb, dummy.b_blocknr); if (unlikely(!bh)) { - *errp = -EIO; + *errp = -ENOMEM; goto err; } if (buffer_new(&dummy)) { @@ -2738,7 +2738,7 @@ static int __ext3_get_inode_loc(struct inode *inode, "unable to read inode block - " "inode=%lu, block="E3FSBLK, inode->i_ino, block); - return -EIO; + return -ENOMEM; } if (!buffer_uptodate(bh)) { lock_buffer(bh); diff --git a/fs/ext3/resize.c b/fs/ext3/resize.c index 704e8ce7d782..27105655502c 100644 --- a/fs/ext3/resize.c +++ b/fs/ext3/resize.c @@ -117,7 +117,7 @@ static struct buffer_head *bclean(handle_t *handle, struct super_block *sb, bh = sb_getblk(sb, blk); if (unlikely(!bh)) - return ERR_PTR(-EIO); + return ERR_PTR(-ENOMEM); if ((err = ext3_journal_get_write_access(handle, bh))) { brelse(bh); bh = ERR_PTR(err); @@ -235,7 +235,7 @@ static int setup_new_group_blocks(struct super_block *sb, gdb = sb_getblk(sb, block); if (unlikely(!gdb)) { - err = -EIO; + err = -ENOMEM; goto exit_bh; } if ((err = ext3_journal_get_write_access(handle, gdb))) { @@ -723,7 +723,7 @@ static void update_backups(struct super_block *sb, bh = sb_getblk(sb, group * bpg + blk_off); if (unlikely(!bh)) { - err = -EIO; + err = -ENOMEM; break; } ext3_debug("update metadata backup %#04lx\n", diff --git a/fs/ext3/xattr.c b/fs/ext3/xattr.c index 9f57470b1727..b1fc96383e08 100644 --- a/fs/ext3/xattr.c +++ b/fs/ext3/xattr.c @@ -816,7 +816,7 @@ inserted: if (unlikely(!new_bh)) { getblk_failed: ext3_free_blocks(handle, inode, block, 1); - error = -EIO; + error = -ENOMEM; goto cleanup; } lock_buffer(new_bh); From 9734c971aa6be6db61226b0046e080ca10383748 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 17 Jan 2013 22:11:38 +0100 Subject: [PATCH 1084/4607] udf: Write LVID to disk after opening / closing So far we just marked the buffer as dirty and left writing on flusher thread but especially on opening that opens possible race window where we could write other modified fs structures to disk before we mark filesystem as open. So sync LVID buffer to disk after opening and closing fs. Reported-by: Steve Nickel Signed-off-by: Jan Kara --- fs/udf/super.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/udf/super.c b/fs/udf/super.c index e9be396a558d..186adbf94b20 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1866,6 +1866,8 @@ static void udf_open_lvid(struct super_block *sb) mark_buffer_dirty(bh); sbi->s_lvid_dirty = 0; mutex_unlock(&sbi->s_alloc_mutex); + /* Make opening of filesystem visible on the media immediately */ + sync_dirty_buffer(bh); } static void udf_close_lvid(struct super_block *sb) @@ -1906,6 +1908,8 @@ static void udf_close_lvid(struct super_block *sb) mark_buffer_dirty(bh); sbi->s_lvid_dirty = 0; mutex_unlock(&sbi->s_alloc_mutex); + /* Make closing of filesystem visible on the media immediately */ + sync_dirty_buffer(bh); } u64 lvid_get_unique_id(struct super_block *sb) From f69061bedd6ea63f271fe97914364def2f33fc6b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 6 Dec 2012 09:01:42 +0100 Subject: [PATCH 1085/4607] drm/i915: create a race-free reset detection With the previous patch the state transition handling of the reset code itself is now (hopefully) race free and solid. But that still leaves out everyone else - with the various lock-free wait paths we have there's the possibility that the reset happens between the point where we read the seqno we should wait on and the actual wait. And if __wait_seqno then never sees the RESET_IN_PROGRESS state, we'll happily wait for a seqno which will in all likelyhood never signal. In practice this is not a big problem since the X server gets constantly interrupted, and can then submit more work (hopefully) to unblock everyone else: As soon as a new seqno write lands, all waiters will unblock. But running the i-g-t reset testcase ZZ_hangman can expose this race, especially on slower hw with fewer cpu cores. Now looking forward to ARB_robustness and friends that's not the best possible behaviour, hence this patch adds a reset_counter to be able to detect any reset, even if a given thread never observed the in-progress state. The important part is to correctly order things: - The write side needs to increment the counter after any seqno gets reset. Hence we need to do that at the end of the reset work, and again wake everyone up. We also need to place a barrier in between any possible seqno changes and the counter increment, since any unlock operations only guarantee that nothing leaks out, but not that at later load operation gets moved ahead. - On the read side we need to ensure that no reset can sneak in and invalidate the seqno. In all cases we can use the one-sided barrier that unlock operations guarantee (of the lock protecting the respective seqno/ring pair) to ensure correct ordering. Hence it is sufficient to place the atomic read before the mutex/spin_unlock and no additional barriers are required. The end-result of all this is that we need to wake up everyone twice in a reset operation: - First, before the reset starts, to get any lockholders of the locks, so that the reset can proceed. - Second, after the reset is completed, to allow waiters to properly and reliably detect the reset condition and bail out. I admit that this entire reset_counter thing smells a bit like overkill, but I think it's justified since it makes it really explicit what the bail-out condition is. And we need a reset counter anyway to implement ARB_robustness, and imo with finer-grained locking on the horizont this is the most resilient scheme I could think of. v2: Drop spurious change in the wait_for_error EXIT_COND - we only need to wait until we leave the reset-in-progress wedged state. v3: Don't play tricks with barriers in the throttle ioctl, the spin_unlock is barrier enough. I've also considered using a little helper to grab the current reset_counter, but then decided that hiding the atomic_read isn't a great idea, since having it explicitly show up in the code is a nice remainder to reviews to check the memory barriers. v4: Add a comment to explain why we need to fall through in __wait_seqno in the end variable assignments. v5: Review from Damien: - s/smb/smp/ in a comment - don't increment the reset counter after we've set it to WEDGED. Now we (again) properly wedge the gpu when the reset fails. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 11 ++++++++-- drivers/gpu/drm/i915/i915_gem.c | 36 ++++++++++++++++++++++++++++----- drivers/gpu/drm/i915/i915_irq.c | 30 ++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c84743bb6937..56ece50910a8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -775,9 +775,16 @@ struct i915_gpu_error { unsigned long last_reset; /** - * State variable controlling the reset flow + * State variable and reset counter controlling the reset flow * - * Upper bits are for the reset counter. + * Upper bits are for the reset counter. This counter is used by the + * wait_seqno code to race-free noticed that a reset event happened and + * that it needs to restart the entire ioctl (since most likely the + * seqno it waited for won't ever signal anytime soon). + * + * This is important for lock-free wait paths, where no contended lock + * naturally enforces the correct ordering between the bail-out of the + * waiter and the gpu reset work code. * * Lowest bit controls the reset state machine: Set means a reset is in * progress. This state will (presuming we don't have any bugs) decay diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5bb370fdc99c..5226ebcac33a 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -976,13 +976,22 @@ i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno) * __wait_seqno - wait until execution of seqno has finished * @ring: the ring expected to report seqno * @seqno: duh! + * @reset_counter: reset sequence associated with the given seqno * @interruptible: do an interruptible wait (normally yes) * @timeout: in - how long to wait (NULL forever); out - how much time remaining * + * Note: It is of utmost importance that the passed in seqno and reset_counter + * values have been read by the caller in an smp safe manner. Where read-side + * locks are involved, it is sufficient to read the reset_counter before + * unlocking the lock that protects the seqno. For lockless tricks, the + * reset_counter _must_ be read before, and an appropriate smp_rmb must be + * inserted. + * * Returns 0 if the seqno was found within the alloted time. Else returns the * errno with remaining time filled in timeout argument. */ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, + unsigned reset_counter, bool interruptible, struct timespec *timeout) { drm_i915_private_t *dev_priv = ring->dev->dev_private; @@ -1012,7 +1021,8 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, #define EXIT_COND \ (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - i915_reset_in_progress(&dev_priv->gpu_error)) + i915_reset_in_progress(&dev_priv->gpu_error) || \ + reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) do { if (interruptible) end = wait_event_interruptible_timeout(ring->irq_queue, @@ -1022,6 +1032,13 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, end = wait_event_timeout(ring->irq_queue, EXIT_COND, timeout_jiffies); + /* We need to check whether any gpu reset happened in between + * the caller grabbing the seqno and now ... */ + if (reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) + end = -EAGAIN; + + /* ... but upgrade the -EGAIN to an -EIO if the gpu is truely + * gone. */ ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible); if (ret) end = ret; @@ -1076,7 +1093,9 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) if (ret) return ret; - return __wait_seqno(ring, seqno, interruptible, NULL); + return __wait_seqno(ring, seqno, + atomic_read(&dev_priv->gpu_error.reset_counter), + interruptible, NULL); } /** @@ -1123,6 +1142,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ring_buffer *ring = obj->ring; + unsigned reset_counter; u32 seqno; int ret; @@ -1141,8 +1161,9 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, if (ret) return ret; + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); mutex_unlock(&dev->struct_mutex); - ret = __wait_seqno(ring, seqno, true, NULL); + ret = __wait_seqno(ring, seqno, reset_counter, true, NULL); mutex_lock(&dev->struct_mutex); i915_gem_retire_requests_ring(ring); @@ -2297,10 +2318,12 @@ i915_gem_object_flush_active(struct drm_i915_gem_object *obj) int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_wait *args = data; struct drm_i915_gem_object *obj; struct intel_ring_buffer *ring = NULL; struct timespec timeout_stack, *timeout = NULL; + unsigned reset_counter; u32 seqno = 0; int ret = 0; @@ -2341,9 +2364,10 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) } drm_gem_object_unreference(&obj->base); + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); mutex_unlock(&dev->struct_mutex); - ret = __wait_seqno(ring, seqno, true, timeout); + ret = __wait_seqno(ring, seqno, reset_counter, true, timeout); if (timeout) { WARN_ON(!timespec_valid(timeout)); args->timeout_ns = timespec_to_ns(timeout); @@ -3394,6 +3418,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) unsigned long recent_enough = jiffies - msecs_to_jiffies(20); struct drm_i915_gem_request *request; struct intel_ring_buffer *ring = NULL; + unsigned reset_counter; u32 seqno = 0; int ret; @@ -3413,12 +3438,13 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) ring = request->ring; seqno = request->seqno; } + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); spin_unlock(&file_priv->mm.lock); if (seqno == 0) return 0; - ret = __wait_seqno(ring, seqno, true, NULL); + ret = __wait_seqno(ring, seqno, reset_counter, true, NULL); if (ret == 0) queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, 0); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 4562c5406ef8..f833f2c155f8 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -867,9 +867,11 @@ static void i915_error_work_func(struct work_struct *work) drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t, gpu_error); struct drm_device *dev = dev_priv->dev; + struct intel_ring_buffer *ring; char *error_event[] = { "ERROR=1", NULL }; char *reset_event[] = { "RESET=1", NULL }; char *reset_done_event[] = { "ERROR=0", NULL }; + int i, ret; kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); @@ -877,13 +879,31 @@ static void i915_error_work_func(struct work_struct *work) DRM_DEBUG_DRIVER("resetting chip\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); - if (!i915_reset(dev)) { - atomic_set(&error->reset_counter, 0); - kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); + ret = i915_reset(dev); + + if (ret == 0) { + /* + * After all the gem state is reset, increment the reset + * counter and wake up everyone waiting for the reset to + * complete. + * + * Since unlock operations are a one-sided barrier only, + * we need to insert a barrier here to order any seqno + * updates before + * the counter increment. + */ + smp_mb__before_atomic_inc(); + atomic_inc(&dev_priv->gpu_error.reset_counter); + + kobject_uevent_env(&dev->primary->kdev.kobj, + KOBJ_CHANGE, reset_done_event); } else { atomic_set(&error->reset_counter, I915_WEDGED); } + for_each_ring(ring, dev_priv, i) + wake_up_all(&ring->irq_queue); + wake_up_all(&dev_priv->gpu_error.reset_queue); } } @@ -1488,8 +1508,8 @@ void i915_handle_error(struct drm_device *dev, bool wedged) i915_report_and_clear_eir(dev); if (wedged) { - atomic_set(&dev_priv->gpu_error.reset_counter, - I915_RESET_IN_PROGRESS_FLAG); + atomic_set_mask(I915_RESET_IN_PROGRESS_FLAG, + &dev_priv->gpu_error.reset_counter); /* * Wakeup waiting processes so that the reset work item From 7db0ba242b3a7ddcfc1450bc50eb3102c68f0244 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 6 Dec 2012 16:23:37 +0100 Subject: [PATCH 1086/4607] drm/i915: clarify concurrent hang detect/gpu reset consistency Damien Lespiau wondered how race the gpu reset/hang detection code is against concurrent gpu resets/hang detections or combinations thereof. Luckily the single work item is guranteed to never run concurrently, so reset handling is already single-threaded. Hence we only have to worry about concurrent hang detections, or a hang detection firing off while we're still processing an older gpu reset request. Due to the new mechanism of setting the reset in progress flag and the ordering guaranteed by the schedule_work function there's nothing to do but add a comment explaining why we're safe. The only thing I've noticed is that we still try to reset the gpu now, even when it is declared terminally wedged. Add a check for that to avoid continous warnings about failed resets, in case the hangcheck timer ever gets stuck. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index f833f2c155f8..943db1001cfd 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -875,9 +875,20 @@ static void i915_error_work_func(struct work_struct *work) kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); - if (i915_reset_in_progress(error)) { + /* + * Note that there's only one work item which does gpu resets, so we + * need not worry about concurrent gpu resets potentially incrementing + * error->reset_counter twice. We only need to take care of another + * racing irq/hangcheck declaring the gpu dead for a second time. A + * quick check for that is good enough: schedule_work ensures the + * correct ordering between hang detection and this work item, and since + * the reset in-progress bit is only ever set by code outside of this + * work we don't need to worry about any other races. + */ + if (i915_reset_in_progress(error) && !i915_terminally_wedged(error)) { DRM_DEBUG_DRIVER("resetting chip\n"); - kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); + kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, + reset_event); ret = i915_reset(dev); From afe759511808cd5bb508b598007cf0c7b0ca8e08 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:20:58 +0800 Subject: [PATCH 1087/4607] libata: identify and init ZPODD devices The ODD can be enabled for ZPODD if the following three conditions are satisfied: 1 The ODD supports device attention; 2 The platform can runtime power off the ODD through ACPI; 3 The ODD is either slot type or drawer type. For such ODDs, zpodd_init is called and a new structure is allocated for it to store ZPODD related stuffs. And the zpodd_dev_enabled function is used to test if ZPODD is currently enabled for this ODD. A new config CONFIG_SATA_ZPODD is added to selectively build ZPODD code. Signed-off-by: Aaron Lu Acked-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/Kconfig | 13 +++++ drivers/ata/Makefile | 1 + drivers/ata/libata-core.c | 4 +- drivers/ata/libata-scsi.c | 2 + drivers/ata/libata-zpodd.c | 100 +++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 14 ++++++ include/linux/libata.h | 3 ++ include/uapi/linux/cdrom.h | 34 +++++++++++++ 8 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 drivers/ata/libata-zpodd.c diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index e08d322d01d7..996d16c9c6e5 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -58,6 +58,19 @@ config ATA_ACPI You can disable this at kernel boot time by using the option libata.noacpi=1 +config SATA_ZPODD + bool "SATA Zero Power ODD Support" + depends on ATA_ACPI + default n + help + This option adds support for SATA ZPODD. It requires both + ODD and the platform support, and if enabled, will automatically + power on/off the ODD when certain condition is satisfied. This + does not impact user's experience of the ODD, only power is saved + when ODD is not in use(i.e. no disc inside). + + If unsure, say N. + config SATA_PMP bool "SATA Port Multiplier support" default y diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 9329dafba91b..85e3de463ed1 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -107,3 +107,4 @@ libata-y := libata-core.o libata-scsi.o libata-eh.o libata-transport.o libata-$(CONFIG_ATA_SFF) += libata-sff.o libata-$(CONFIG_SATA_PMP) += libata-pmp.o libata-$(CONFIG_ATA_ACPI) += libata-acpi.o +libata-$(CONFIG_SATA_ZPODD) += libata-zpodd.o diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 275941b576a8..c7ecd8492f1e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2401,8 +2401,10 @@ int ata_dev_configure(struct ata_device *dev) dma_dir_string = ", DMADIR"; } - if (ata_id_has_da(dev->id)) + if (ata_id_has_da(dev->id)) { dev->flags |= ATA_DFLAG_DA; + zpodd_init(dev); + } /* print device info to dmesg */ if (ata_msg_drv(ap) && print_info) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 7c337e754dab..1ff018525e3b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3755,6 +3755,8 @@ static void ata_scsi_remove_dev(struct ata_device *dev) mutex_lock(&ap->scsi_host->scan_mutex); spin_lock_irqsave(ap->lock, flags); + if (zpodd_dev_enabled(dev)) + zpodd_exit(dev); ata_acpi_unbind(dev); /* clearing dev->sdev is protected by host lock */ diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c new file mode 100644 index 000000000000..27eed2f09a8a --- /dev/null +++ b/drivers/ata/libata-zpodd.c @@ -0,0 +1,100 @@ +#include +#include + +#include "libata.h" + +enum odd_mech_type { + ODD_MECH_TYPE_SLOT, + ODD_MECH_TYPE_DRAWER, + ODD_MECH_TYPE_UNSUPPORTED, +}; + +struct zpodd { + enum odd_mech_type mech_type; /* init during probe, RO afterwards */ + struct ata_device *dev; +}; + +/* Per the spec, only slot type and drawer type ODD can be supported */ +static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev) +{ + char buf[16]; + unsigned int ret; + struct rm_feature_desc *desc = (void *)(buf + 8); + struct ata_taskfile tf = {}; + + char cdb[] = { GPCMD_GET_CONFIGURATION, + 2, /* only 1 feature descriptor requested */ + 0, 3, /* 3, removable medium feature */ + 0, 0, 0,/* reserved */ + 0, sizeof(buf), + 0, 0, 0, + }; + + tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.command = ATA_CMD_PACKET; + tf.protocol = ATAPI_PROT_PIO; + tf.lbam = sizeof(buf); + + ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE, + buf, sizeof(buf), 0); + if (ret) + return ODD_MECH_TYPE_UNSUPPORTED; + + if (be16_to_cpu(desc->feature_code) != 3) + return ODD_MECH_TYPE_UNSUPPORTED; + + if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1) + return ODD_MECH_TYPE_SLOT; + else if (desc->mech_type == 1 && desc->load == 0 && desc->eject == 1) + return ODD_MECH_TYPE_DRAWER; + else + return ODD_MECH_TYPE_UNSUPPORTED; +} + +static bool odd_can_poweroff(struct ata_device *ata_dev) +{ + acpi_handle handle; + acpi_status status; + struct acpi_device *acpi_dev; + + handle = ata_dev_acpi_handle(ata_dev); + if (!handle) + return false; + + status = acpi_bus_get_device(handle, &acpi_dev); + if (ACPI_FAILURE(status)) + return false; + + return acpi_device_can_poweroff(acpi_dev); +} + +void zpodd_init(struct ata_device *dev) +{ + enum odd_mech_type mech_type; + struct zpodd *zpodd; + + if (dev->zpodd) + return; + + if (!odd_can_poweroff(dev)) + return; + + mech_type = zpodd_get_mech_type(dev); + if (mech_type == ODD_MECH_TYPE_UNSUPPORTED) + return; + + zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL); + if (!zpodd) + return; + + zpodd->mech_type = mech_type; + + zpodd->dev = dev; + dev->zpodd = zpodd; +} + +void zpodd_exit(struct ata_device *dev) +{ + kfree(dev->zpodd); + dev->zpodd = NULL; +} diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 7148a58020b9..a21740b4ee11 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -230,4 +230,18 @@ static inline void ata_sff_exit(void) { } #endif /* CONFIG_ATA_SFF */ +/* libata-zpodd.c */ +#ifdef CONFIG_SATA_ZPODD +void zpodd_init(struct ata_device *dev); +void zpodd_exit(struct ata_device *dev); +static inline bool zpodd_dev_enabled(struct ata_device *dev) +{ + return dev->zpodd != NULL; +} +#else /* CONFIG_SATA_ZPODD */ +static inline void zpodd_init(struct ata_device *dev) {} +static inline void zpodd_exit(struct ata_device *dev) {} +static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; } +#endif /* CONFIG_SATA_ZPODD */ + #endif /* __LIBATA_H__ */ diff --git a/include/linux/libata.h b/include/linux/libata.h index 7ae207eb29a0..65ff67e34b77 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -620,6 +620,9 @@ struct ata_device { #ifdef CONFIG_ATA_ACPI union acpi_object *gtf_cache; unsigned int gtf_filter; +#endif +#ifdef CONFIG_SATA_ZPODD + void *zpodd; #endif struct device tdev; /* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */ diff --git a/include/uapi/linux/cdrom.h b/include/uapi/linux/cdrom.h index 898b866b300c..bd17ad5aa06d 100644 --- a/include/uapi/linux/cdrom.h +++ b/include/uapi/linux/cdrom.h @@ -908,5 +908,39 @@ struct mode_page_header { __be16 desc_length; }; +/* removable medium feature descriptor */ +struct rm_feature_desc { + __be16 feature_code; +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 reserved1:2; + __u8 feature_version:4; + __u8 persistent:1; + __u8 curr:1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + __u8 curr:1; + __u8 persistent:1; + __u8 feature_version:4; + __u8 reserved1:2; +#endif + __u8 add_len; +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 mech_type:3; + __u8 load:1; + __u8 eject:1; + __u8 pvnt_jmpr:1; + __u8 dbml:1; + __u8 lock:1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + __u8 lock:1; + __u8 dbml:1; + __u8 pvnt_jmpr:1; + __u8 eject:1; + __u8 load:1; + __u8 mech_type:3; +#endif + __u8 reserved2; + __u8 reserved3; + __u8 reserved4; +}; #endif /* _UAPI_LINUX_CDROM_H */ From f064a20dded807448669426c9bfb7d03aba5659c Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:20:59 +0800 Subject: [PATCH 1088/4607] libata: move acpi notification code to zpodd Since the ata acpi notification code introduced in commit 3bd46600a7a7e938c54df8cdbac9910668c7dfb0 is solely for ZPODD, and we now have a dedicated place for it, move these code there. And the ata_acpi_add_pm_notifier code is changed a little bit in that it is now invoked when scsi device is not bound with ACPI yet, so the way to get the acpi handle is different with the previous version. And the ata_acpi_add/remove_pm_notifier is also simplified a little bit in that it doesn't check if the acpi_device for the handle exists or not as the odd_can_poweroff function already checked that. Signed-off-by: Aaron Lu Acked-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-acpi.c | 71 -------------------------------------- drivers/ata/libata-zpodd.c | 34 ++++++++++++++++++ 2 files changed, 34 insertions(+), 71 deletions(-) diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index ef01ac07502e..446b4e761af0 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -974,57 +974,6 @@ void ata_acpi_on_disable(struct ata_device *dev) ata_acpi_clear_gtf(dev); } -static void ata_acpi_wake_dev(acpi_handle handle, u32 event, void *context) -{ - struct ata_device *ata_dev = context; - - if (event == ACPI_NOTIFY_DEVICE_WAKE && ata_dev && - pm_runtime_suspended(&ata_dev->sdev->sdev_gendev)) - scsi_autopm_get_device(ata_dev->sdev); -} - -static void ata_acpi_add_pm_notifier(struct ata_device *dev) -{ - struct acpi_device *acpi_dev; - acpi_handle handle; - acpi_status status; - - handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - status = acpi_bus_get_device(handle, &acpi_dev); - if (ACPI_FAILURE(status)) - return; - - if (dev->sdev->can_power_off) { - acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - ata_acpi_wake_dev, dev); - device_set_run_wake(&dev->sdev->sdev_gendev, true); - } -} - -static void ata_acpi_remove_pm_notifier(struct ata_device *dev) -{ - struct acpi_device *acpi_dev; - acpi_handle handle; - acpi_status status; - - handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - status = acpi_bus_get_device(handle, &acpi_dev); - if (ACPI_FAILURE(status)) - return; - - if (dev->sdev->can_power_off) { - device_set_run_wake(&dev->sdev->sdev_gendev, false); - acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - ata_acpi_wake_dev); - } -} - static void ata_acpi_register_power_resource(struct ata_device *dev) { struct scsi_device *sdev = dev->sdev; @@ -1057,13 +1006,11 @@ static void ata_acpi_unregister_power_resource(struct ata_device *dev) void ata_acpi_bind(struct ata_device *dev) { - ata_acpi_add_pm_notifier(dev); ata_acpi_register_power_resource(dev); } void ata_acpi_unbind(struct ata_device *dev) { - ata_acpi_remove_pm_notifier(dev); ata_acpi_unregister_power_resource(dev); } @@ -1105,9 +1052,6 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, acpi_handle *handle) { struct ata_device *ata_dev; - acpi_status status; - struct acpi_device *acpi_dev; - struct acpi_device_power_state *states; if (ap->flags & ATA_FLAG_ACPI_SATA) { if (!sata_pmp_attached(ap)) @@ -1124,21 +1068,6 @@ static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, if (!*handle) return -ENODEV; - status = acpi_bus_get_device(*handle, &acpi_dev); - if (ACPI_FAILURE(status)) - return 0; - - /* - * If firmware has _PS3 or _PR3 for this device, - * and this ata ODD device support device attention, - * it means this device can be powered off - */ - states = acpi_dev->power.states; - if ((states[ACPI_STATE_D3_HOT].flags.valid || - states[ACPI_STATE_D3_COLD].flags.explicit_set) && - ata_dev->flags & ATA_DFLAG_DA) - sdev->can_power_off = 1; - return 0; } diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index 27eed2f09a8a..9a0d90d09d81 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include "libata.h" @@ -12,6 +14,10 @@ enum odd_mech_type { struct zpodd { enum odd_mech_type mech_type; /* init during probe, RO afterwards */ struct ata_device *dev; + + /* The following fields are synchronized by PM core. */ + bool from_notify; /* resumed as a result of + * acpi wake notification */ }; /* Per the spec, only slot type and drawer type ODD can be supported */ @@ -68,6 +74,32 @@ static bool odd_can_poweroff(struct ata_device *ata_dev) return acpi_device_can_poweroff(acpi_dev); } +static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context) +{ + struct ata_device *ata_dev = context; + struct zpodd *zpodd = ata_dev->zpodd; + struct device *dev = &ata_dev->sdev->sdev_gendev; + + if (event == ACPI_NOTIFY_DEVICE_WAKE && ata_dev && + pm_runtime_suspended(dev)) { + zpodd->from_notify = true; + pm_runtime_resume(dev); + } +} + +static void ata_acpi_add_pm_notifier(struct ata_device *dev) +{ + acpi_handle handle = ata_dev_acpi_handle(dev); + acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + zpodd_wake_dev, dev); +} + +static void ata_acpi_remove_pm_notifier(struct ata_device *dev) +{ + acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->sdev->sdev_gendev); + acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, zpodd_wake_dev); +} + void zpodd_init(struct ata_device *dev) { enum odd_mech_type mech_type; @@ -89,12 +121,14 @@ void zpodd_init(struct ata_device *dev) zpodd->mech_type = mech_type; + ata_acpi_add_pm_notifier(dev); zpodd->dev = dev; dev->zpodd = zpodd; } void zpodd_exit(struct ata_device *dev) { + ata_acpi_remove_pm_notifier(dev); kfree(dev->zpodd); dev->zpodd = NULL; } From 3dc67440d99b2c718ef5f1eb1424a9066ffa3fb9 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:21:00 +0800 Subject: [PATCH 1089/4607] libata: check zero power ready status for ZPODD Per the Mount Fuji spec, the ODD is considered zero power ready when: - For slot type ODD, no media inside; - For tray type ODD, no media inside and tray closed. The information can be retrieved by either the returned information of command GET_EVENT_STATUS_NOTIFICATION(the command is used to poll for media event) or sense code. The information provided by the media status byte is not accurate, it is possible that after a new disc is just inserted, the status byte still returns media not present. So this information can not be used as the deciding factor, we use sense code to decide if zpready status is true. When we first sensed the ODD in the zero power ready state, the zp_sampled will be set and timestamp will be recoreded. And after ODD stayed in this state for some pre-defined period, the ODD is considered as power off ready and the zp_ready flag will be set. The zp_ready flag serves as the deciding factor other code will use to see if power off is OK for the ODD. The Mount Fuji spec suggests a delay should be used here, to avoid the case user ejects the ODD and then instantly inserts a new one again, so that we can avoid a power transition. And some ODDs may be slow to place its head to the home position after disc is ejected, so a delay here is generally a good idea. And the delay time can be changed via the module param zpodd_poweroff_delay. The zero power ready status check is performed in the ata port's runtime suspend code path, when port is not frozen yet, as we need to issue some IOs to the ODD. Signed-off-by: Aaron Lu Signed-off-by: Jeff Garzik --- drivers/ata/libata-eh.c | 14 ++++++- drivers/ata/libata-zpodd.c | 75 ++++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 5 +++ 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index bcf4437214f5..a0dddc3b4924 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1591,7 +1591,7 @@ static int ata_eh_read_log_10h(struct ata_device *dev, * RETURNS: * 0 on success, AC_ERR_* mask on failure. */ -static unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) +unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) { u8 cdb[ATAPI_CDB_LEN] = { TEST_UNIT_READY, 0, 0, 0, 0, 0 }; struct ata_taskfile tf; @@ -1624,7 +1624,7 @@ static unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) * RETURNS: * 0 on success, AC_ERR_* mask on failure */ -static unsigned int atapi_eh_request_sense(struct ata_device *dev, +unsigned int atapi_eh_request_sense(struct ata_device *dev, u8 *sense_buf, u8 dfl_sense_key) { u8 cdb[ATAPI_CDB_LEN] = @@ -4022,6 +4022,7 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) { unsigned long flags; int rc = 0; + struct ata_device *dev; /* are we suspending? */ spin_lock_irqsave(ap->lock, flags); @@ -4034,6 +4035,15 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED); + /* + * If we have a ZPODD attached, check its zero + * power ready status before the port is frozen. + */ + ata_for_each_dev(dev, &ap->link, ENABLED) { + if (zpodd_dev_enabled(dev)) + zpodd_on_suspend(dev); + } + /* tell ACPI we're suspending */ rc = ata_acpi_on_suspend(ap); if (rc) diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index 9a0d90d09d81..71dd48c94f2c 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -1,10 +1,15 @@ #include #include #include +#include #include #include "libata.h" +static int zpodd_poweroff_delay = 30; /* 30 seconds for power off delay */ +module_param(zpodd_poweroff_delay, int, 0644); +MODULE_PARM_DESC(zpodd_poweroff_delay, "Poweroff delay for ZPODD in seconds"); + enum odd_mech_type { ODD_MECH_TYPE_SLOT, ODD_MECH_TYPE_DRAWER, @@ -18,6 +23,9 @@ struct zpodd { /* The following fields are synchronized by PM core. */ bool from_notify; /* resumed as a result of * acpi wake notification */ + bool zp_ready; /* ZP ready state */ + unsigned long last_ready; /* last ZP ready timestamp */ + bool zp_sampled; /* ZP ready state sampled */ }; /* Per the spec, only slot type and drawer type ODD can be supported */ @@ -74,6 +82,73 @@ static bool odd_can_poweroff(struct ata_device *ata_dev) return acpi_device_can_poweroff(acpi_dev); } +/* Test if ODD is zero power ready by sense code */ +static bool zpready(struct ata_device *dev) +{ + u8 sense_key, *sense_buf; + unsigned int ret, asc, ascq, add_len; + struct zpodd *zpodd = dev->zpodd; + + ret = atapi_eh_tur(dev, &sense_key); + + if (!ret || sense_key != NOT_READY) + return false; + + sense_buf = dev->link->ap->sector_buf; + ret = atapi_eh_request_sense(dev, sense_buf, sense_key); + if (ret) + return false; + + /* sense valid */ + if ((sense_buf[0] & 0x7f) != 0x70) + return false; + + add_len = sense_buf[7]; + /* has asc and ascq */ + if (add_len < 6) + return false; + + asc = sense_buf[12]; + ascq = sense_buf[13]; + + if (zpodd->mech_type == ODD_MECH_TYPE_SLOT) + /* no media inside */ + return asc == 0x3a; + else + /* no media inside and door closed */ + return asc == 0x3a && ascq == 0x01; +} + +/* + * Update the zpodd->zp_ready field. This field will only be set + * if the ODD has stayed in ZP ready state for zpodd_poweroff_delay + * time, and will be used to decide if power off is allowed. If it + * is set, it will be cleared during resume from powered off state. + */ +void zpodd_on_suspend(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + unsigned long expires; + + if (!zpready(dev)) { + zpodd->zp_sampled = false; + return; + } + + if (!zpodd->zp_sampled) { + zpodd->zp_sampled = true; + zpodd->last_ready = jiffies; + return; + } + + expires = zpodd->last_ready + + msecs_to_jiffies(zpodd_poweroff_delay * 1000); + if (time_before(jiffies, expires)) + return; + + zpodd->zp_ready = true; +} + static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context) { struct ata_device *ata_dev = context; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index a21740b4ee11..b9b2bb1d5dc6 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -182,6 +182,9 @@ extern void ata_eh_finish(struct ata_port *ap); extern int ata_ering_map(struct ata_ering *ering, int (*map_fn)(struct ata_ering_entry *, void *), void *arg); +extern unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key); +extern unsigned int atapi_eh_request_sense(struct ata_device *dev, + u8 *sense_buf, u8 dfl_sense_key); /* libata-pmp.c */ #ifdef CONFIG_SATA_PMP @@ -238,10 +241,12 @@ static inline bool zpodd_dev_enabled(struct ata_device *dev) { return dev->zpodd != NULL; } +void zpodd_on_suspend(struct ata_device *dev); #else /* CONFIG_SATA_ZPODD */ static inline void zpodd_init(struct ata_device *dev) {} static inline void zpodd_exit(struct ata_device *dev) {} static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; } +static inline void zpodd_on_suspend(struct ata_device *dev) {} #endif /* CONFIG_SATA_ZPODD */ #endif /* __LIBATA_H__ */ From 213342053db58eabdaddff9c036c2b81ca63c443 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:21:01 +0800 Subject: [PATCH 1090/4607] libata: handle power transition of ODD When ata port is runtime suspended, it will check if the ODD attched to it is a zero power(ZP) capable ODD and if the ZP capable ODD is in zero power ready state. And if this is not the case, the highest acpi state will be limited to ACPI_STATE_D3_HOT to avoid powering off the ODD. And if the ODD can be powered off, runtime wake capability needs to be enabled and powered_off flag will be set to let resume code knows that the ODD was in powered off state. And on resume, before it is powered on, if it was powered off during suspend, runtime wake capability needs to be disabled. After it is recovered, the ODD is considered functional, post power on processing like eject tray if the ODD is drawer type is done, and several ZPODD related fields will also be reset. Signed-off-by: Aaron Lu Acked-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-acpi.c | 35 +++++++++++----- drivers/ata/libata-eh.c | 2 + drivers/ata/libata-zpodd.c | 83 ++++++++++++++++++++++++++++++++++++++ drivers/ata/libata.h | 8 ++++ 4 files changed, 118 insertions(+), 10 deletions(-) diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 446b4e761af0..6f72c648ea14 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -835,6 +835,22 @@ void ata_acpi_on_resume(struct ata_port *ap) } } +static int ata_acpi_choose_suspend_state(struct ata_device *dev) +{ + int d_max_in = ACPI_STATE_D3_COLD; + + /* + * For ATAPI, runtime D3 cold is only allowed + * for ZPODD in zero power ready state + */ + if (dev->class == ATA_DEV_ATAPI && + !(zpodd_dev_enabled(dev) && zpodd_zpready(dev))) + d_max_in = ACPI_STATE_D3_HOT; + + return acpi_pm_device_sleep_state(&dev->sdev->sdev_gendev, + NULL, d_max_in); +} + /** * ata_acpi_set_state - set the port power state * @ap: target ATA port @@ -861,17 +877,16 @@ void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) continue; if (state.event != PM_EVENT_ON) { - acpi_state = acpi_pm_device_sleep_state( - &dev->sdev->sdev_gendev, NULL, ACPI_STATE_D3); - if (acpi_state > 0) - acpi_bus_set_power(handle, acpi_state); - /* TBD: need to check if it's runtime pm request */ - acpi_pm_device_run_wake( - &dev->sdev->sdev_gendev, true); + acpi_state = ata_acpi_choose_suspend_state(dev); + if (acpi_state == ACPI_STATE_D0) + continue; + if (zpodd_dev_enabled(dev) && + acpi_state == ACPI_STATE_D3_COLD) + zpodd_enable_run_wake(dev); + acpi_bus_set_power(handle, acpi_state); } else { - /* Ditto */ - acpi_pm_device_run_wake( - &dev->sdev->sdev_gendev, false); + if (zpodd_dev_enabled(dev)) + zpodd_disable_run_wake(dev); acpi_bus_set_power(handle, ACPI_STATE_D0); } } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index a0dddc3b4924..50f3ef04809d 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -3857,6 +3857,8 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, rc = atapi_eh_clear_ua(dev); if (rc) goto rest_fail; + if (zpodd_dev_enabled(dev)) + zpodd_post_poweron(dev); } } diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index 71dd48c94f2c..1f5d52ae3974 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -26,8 +26,26 @@ struct zpodd { bool zp_ready; /* ZP ready state */ unsigned long last_ready; /* last ZP ready timestamp */ bool zp_sampled; /* ZP ready state sampled */ + bool powered_off; /* ODD is powered off + * during suspend */ }; +static int eject_tray(struct ata_device *dev) +{ + struct ata_taskfile tf = {}; + const char cdb[] = { GPCMD_START_STOP_UNIT, + 0, 0, 0, + 0x02, /* LoEj */ + 0, 0, 0, 0, 0, 0, 0, + }; + + tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.command = ATA_CMD_PACKET; + tf.protocol = ATAPI_PROT_NODATA; + + return ata_exec_internal(dev, &tf, cdb, DMA_NONE, NULL, 0, 0); +} + /* Per the spec, only slot type and drawer type ODD can be supported */ static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev) { @@ -149,6 +167,71 @@ void zpodd_on_suspend(struct ata_device *dev) zpodd->zp_ready = true; } +bool zpodd_zpready(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + return zpodd->zp_ready; +} + +/* + * Enable runtime wake capability through ACPI and set the powered_off flag, + * this flag will be used during resume to decide what operations are needed + * to take. + */ +void zpodd_enable_run_wake(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + + zpodd->powered_off = true; + device_set_run_wake(&dev->sdev->sdev_gendev, true); + acpi_pm_device_run_wake(&dev->sdev->sdev_gendev, true); +} + +/* Disable runtime wake capability if it is enabled */ +void zpodd_disable_run_wake(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + + if (zpodd->powered_off) { + acpi_pm_device_run_wake(&dev->sdev->sdev_gendev, false); + device_set_run_wake(&dev->sdev->sdev_gendev, false); + } +} + +/* + * Post power on processing after the ODD has been recovered. If the + * ODD wasn't powered off during suspend, it doesn't do anything. + * + * For drawer type ODD, if it is powered on due to user pressed the + * eject button, the tray needs to be ejected. This can only be done + * after the ODD has been recovered, i.e. link is initialized and + * device is able to process NON_DATA PIO command, as eject needs to + * send command for the ODD to process. + * + * The from_notify flag set in wake notification handler function + * zpodd_wake_dev represents if power on is due to user's action. + * + * For both types of ODD, several fields need to be reset. + */ +void zpodd_post_poweron(struct ata_device *dev) +{ + struct zpodd *zpodd = dev->zpodd; + + if (!zpodd->powered_off) + return; + + zpodd->powered_off = false; + + if (zpodd->from_notify) { + zpodd->from_notify = false; + if (zpodd->mech_type == ODD_MECH_TYPE_DRAWER) + eject_tray(dev); + } + + zpodd->zp_sampled = false; + zpodd->zp_ready = false; +} + static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context) { struct ata_device *ata_dev = context; diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index b9b2bb1d5dc6..c949dd311b2e 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -242,11 +242,19 @@ static inline bool zpodd_dev_enabled(struct ata_device *dev) return dev->zpodd != NULL; } void zpodd_on_suspend(struct ata_device *dev); +bool zpodd_zpready(struct ata_device *dev); +void zpodd_enable_run_wake(struct ata_device *dev); +void zpodd_disable_run_wake(struct ata_device *dev); +void zpodd_post_poweron(struct ata_device *dev); #else /* CONFIG_SATA_ZPODD */ static inline void zpodd_init(struct ata_device *dev) {} static inline void zpodd_exit(struct ata_device *dev) {} static inline bool zpodd_dev_enabled(struct ata_device *dev) { return false; } static inline void zpodd_on_suspend(struct ata_device *dev) {} +static inline bool zpodd_zpready(struct ata_device *dev) { return false; } +static inline void zpodd_enable_run_wake(struct ata_device *dev) {} +static inline void zpodd_disable_run_wake(struct ata_device *dev) {} +static inline void zpodd_post_poweron(struct ata_device *dev) {} #endif /* CONFIG_SATA_ZPODD */ #endif /* __LIBATA_H__ */ From a59b9aae230316134597775c6202cf28f6da0333 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:21:02 +0800 Subject: [PATCH 1091/4607] libata: expose pm qos flags for ata device Expose pm qos flags to user space so that user has a chance to disable ZPODD feature, if he/she has a broken platform or devices or simply does not like this feature. This flag is exposed to user space only for ZPODD devices. Due to this flag, it is possible the ODD is ZP ready but we didn't power it off. So the zp_ready flag will need to be cleared whenever we found the ODD is not in ZP ready state. Previously, once zp_ready is set, the ODD will always be powered off and the flag will be cleared in post_poweron. But this is no longer the case now. Signed-off-by: Aaron Lu Signed-off-by: Jeff Garzik --- drivers/ata/libata-acpi.c | 3 +++ drivers/ata/libata-zpodd.c | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 6f72c648ea14..97094496127e 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "libata.h" @@ -1022,6 +1023,8 @@ static void ata_acpi_unregister_power_resource(struct ata_device *dev) void ata_acpi_bind(struct ata_device *dev) { ata_acpi_register_power_resource(dev); + if (zpodd_dev_enabled(dev)) + dev_pm_qos_expose_flags(&dev->sdev->sdev_gendev, 0); } void ata_acpi_unbind(struct ata_device *dev) diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index 1f5d52ae3974..540b0b7904fb 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -150,6 +150,7 @@ void zpodd_on_suspend(struct ata_device *dev) if (!zpready(dev)) { zpodd->zp_sampled = false; + zpodd->zp_ready = false; return; } From 7e15e9be37eb834aaaca69030064ac97eaf5df2f Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Tue, 15 Jan 2013 17:21:04 +0800 Subject: [PATCH 1092/4607] libata: do not suspend port if normal ODD is attached For ODDs, the upper layer will poll for media change every few seconds, which will make it enter and leave suspend state very often. And as each suspend will also cause a hard/soft reset, the gain of runtime suspend is very little while the ODD may malfunction after constantly being reset. So the idle callback here will not proceed to suspend if a non-ZPODD capable ODD is attached to the port. Signed-off-by: Aaron Lu Acked-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-core.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c7ecd8492f1e..b7eed827daae 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5413,8 +5413,27 @@ static int ata_port_resume(struct device *dev) return rc; } +/* + * For ODDs, the upper layer will poll for media change every few seconds, + * which will make it enter and leave suspend state every few seconds. And + * as each suspend will cause a hard/soft reset, the gain of runtime suspend + * is very little and the ODD may malfunction after constantly being reset. + * So the idle callback here will not proceed to suspend if a non-ZPODD capable + * ODD is attached to the port. + */ static int ata_port_runtime_idle(struct device *dev) { + struct ata_port *ap = to_ata_port(dev); + struct ata_link *link; + struct ata_device *adev; + + ata_for_each_link(link, ap, HOST_FIRST) { + ata_for_each_dev(adev, link, ENABLED) + if (adev->class == ATA_DEV_ATAPI && + !zpodd_dev_enabled(adev)) + return -EBUSY; + } + return pm_runtime_suspend(dev); } From 22368028febf81f5c6d1e9fdd737d50543219b00 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Sun, 13 Jan 2013 23:44:12 +0800 Subject: [PATCH 1093/4607] KVM: x86: clean up reexecute_instruction Little cleanup for reexecute_instruction, also use gpa_to_gfn in retry_instruction Reviewed-by: Gleb Natapov Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/x86.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 54832280cdef..f0288c52d5e4 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4759,19 +4759,18 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t gva) if (tdp_enabled) return false; + gpa = kvm_mmu_gva_to_gpa_system(vcpu, gva, NULL); + if (gpa == UNMAPPED_GVA) + return true; /* let cpu generate fault */ + /* * if emulation was due to access to shadowed page table * and it failed try to unshadow page and re-enter the * guest to let CPU execute the instruction. */ - if (kvm_mmu_unprotect_page_virt(vcpu, gva)) + if (kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(gpa))) return true; - gpa = kvm_mmu_gva_to_gpa_system(vcpu, gva, NULL); - - if (gpa == UNMAPPED_GVA) - return true; /* let cpu generate fault */ - /* * Do not retry the unhandleable instruction if it faults on the * readonly host memory, otherwise it will goto a infinite loop: @@ -4826,7 +4825,7 @@ static bool retry_instruction(struct x86_emulate_ctxt *ctxt, if (!vcpu->arch.mmu.direct_map) gpa = kvm_mmu_gva_to_gpa_write(vcpu, cr2, NULL); - kvm_mmu_unprotect_page(vcpu->kvm, gpa >> PAGE_SHIFT); + kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(gpa)); return true; } From 95b3cf69bdf8b27a02d878e24ca353cebb4e009e Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Sun, 13 Jan 2013 23:46:52 +0800 Subject: [PATCH 1094/4607] KVM: x86: let reexecute_instruction work for tdp Currently, reexecute_instruction refused to retry all instructions if tdp is enabled. If nested npt is used, the emulation may be caused by shadow page, it can be fixed by dropping the shadow page. And the only condition that tdp can not retry the instruction is the access fault on error pfn Reviewed-by: Gleb Natapov Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/x86.c | 61 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index f0288c52d5e4..6f9cab071eca 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4751,25 +4751,25 @@ static int handle_emulation_failure(struct kvm_vcpu *vcpu) return r; } -static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t gva) +static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t cr2) { - gpa_t gpa; + gpa_t gpa = cr2; pfn_t pfn; - if (tdp_enabled) - return false; + if (!vcpu->arch.mmu.direct_map) { + /* + * Write permission should be allowed since only + * write access need to be emulated. + */ + gpa = kvm_mmu_gva_to_gpa_write(vcpu, cr2, NULL); - gpa = kvm_mmu_gva_to_gpa_system(vcpu, gva, NULL); - if (gpa == UNMAPPED_GVA) - return true; /* let cpu generate fault */ - - /* - * if emulation was due to access to shadowed page table - * and it failed try to unshadow page and re-enter the - * guest to let CPU execute the instruction. - */ - if (kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(gpa))) - return true; + /* + * If the mapping is invalid in guest, let cpu retry + * it to generate fault. + */ + if (gpa == UNMAPPED_GVA) + return true; + } /* * Do not retry the unhandleable instruction if it faults on the @@ -4778,12 +4778,37 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t gva) * instruction -> ... */ pfn = gfn_to_pfn(vcpu->kvm, gpa_to_gfn(gpa)); - if (!is_error_noslot_pfn(pfn)) { - kvm_release_pfn_clean(pfn); + + /* + * If the instruction failed on the error pfn, it can not be fixed, + * report the error to userspace. + */ + if (is_error_noslot_pfn(pfn)) + return false; + + kvm_release_pfn_clean(pfn); + + /* The instructions are well-emulated on direct mmu. */ + if (vcpu->arch.mmu.direct_map) { + unsigned int indirect_shadow_pages; + + spin_lock(&vcpu->kvm->mmu_lock); + indirect_shadow_pages = vcpu->kvm->arch.indirect_shadow_pages; + spin_unlock(&vcpu->kvm->mmu_lock); + + if (indirect_shadow_pages) + kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(gpa)); + return true; } - return false; + /* + * if emulation was due to access to shadowed page table + * and it failed try to unshadow page and re-enter the + * guest to let CPU execute the instruction. + */ + kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(gpa)); + return true; } static bool retry_instruction(struct x86_emulate_ctxt *ctxt, From 93c05d3ef25275829d421a255271595ac219a518 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Sun, 13 Jan 2013 23:49:07 +0800 Subject: [PATCH 1095/4607] KVM: x86: improve reexecute_instruction The current reexecute_instruction can not well detect the failed instruction emulation. It allows guest to retry all the instructions except it accesses on error pfn For example, some cases are nested-write-protect - if the page we want to write is used as PDE but it chains to itself. Under this case, we should stop the emulation and report the case to userspace Reviewed-by: Gleb Natapov Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- arch/x86/include/asm/kvm_host.h | 7 +++++++ arch/x86/kvm/paging_tmpl.h | 27 ++++++++++++++++++++------- arch/x86/kvm/x86.c | 22 ++++++++++++++++++---- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index f75e1feb6ec5..77d56a4ba89c 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -497,6 +497,13 @@ struct kvm_vcpu_arch { u64 msr_val; struct gfn_to_hva_cache data; } pv_eoi; + + /* + * Indicate whether the access faults on its page table in guest + * which is set when fix page fault and used to detect unhandeable + * instruction. + */ + bool write_fault_to_shadow_pgtable; }; struct kvm_lpage_info { diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index 3d1a35237dbf..ca69dcccbe31 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -497,26 +497,34 @@ out_gpte_changed: * created when kvm establishes shadow page table that stop kvm using large * page size. Do it early can avoid unnecessary #PF and emulation. * + * @write_fault_to_shadow_pgtable will return true if the fault gfn is + * currently used as its page table. + * * Note: the PDPT page table is not checked for PAE-32 bit guest. It is ok * since the PDPT is always shadowed, that means, we can not use large page * size to map the gfn which is used as PDPT. */ static bool FNAME(is_self_change_mapping)(struct kvm_vcpu *vcpu, - struct guest_walker *walker, int user_fault) + struct guest_walker *walker, int user_fault, + bool *write_fault_to_shadow_pgtable) { int level; gfn_t mask = ~(KVM_PAGES_PER_HPAGE(walker->level) - 1); + bool self_changed = false; if (!(walker->pte_access & ACC_WRITE_MASK || (!is_write_protection(vcpu) && !user_fault))) return false; - for (level = walker->level; level <= walker->max_level; level++) - if (!((walker->gfn ^ walker->table_gfn[level - 1]) & mask)) - return true; + for (level = walker->level; level <= walker->max_level; level++) { + gfn_t gfn = walker->gfn ^ walker->table_gfn[level - 1]; - return false; + self_changed |= !(gfn & mask); + *write_fault_to_shadow_pgtable |= !gfn; + } + + return self_changed; } /* @@ -544,7 +552,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, int level = PT_PAGE_TABLE_LEVEL; int force_pt_level; unsigned long mmu_seq; - bool map_writable; + bool map_writable, is_self_change_mapping; pgprintk("%s: addr %lx err %x\n", __func__, addr, error_code); @@ -572,9 +580,14 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code, return 0; } + vcpu->arch.write_fault_to_shadow_pgtable = false; + + is_self_change_mapping = FNAME(is_self_change_mapping)(vcpu, + &walker, user_fault, &vcpu->arch.write_fault_to_shadow_pgtable); + if (walker.level >= PT_DIRECTORY_LEVEL) force_pt_level = mapping_level_dirty_bitmap(vcpu, walker.gfn) - || FNAME(is_self_change_mapping)(vcpu, &walker, user_fault); + || is_self_change_mapping; else force_pt_level = 1; if (!force_pt_level) { diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 6f9cab071eca..e00dd0515a84 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4751,7 +4751,8 @@ static int handle_emulation_failure(struct kvm_vcpu *vcpu) return r; } -static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t cr2) +static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t cr2, + bool write_fault_to_shadow_pgtable) { gpa_t gpa = cr2; pfn_t pfn; @@ -4808,7 +4809,13 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gva_t cr2) * guest to let CPU execute the instruction. */ kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(gpa)); - return true; + + /* + * If the access faults on its page table, it can not + * be fixed by unprotecting shadow page and it should + * be reported to userspace. + */ + return !write_fault_to_shadow_pgtable; } static bool retry_instruction(struct x86_emulate_ctxt *ctxt, @@ -4867,7 +4874,13 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, int r; struct x86_emulate_ctxt *ctxt = &vcpu->arch.emulate_ctxt; bool writeback = true; + bool write_fault_to_spt = vcpu->arch.write_fault_to_shadow_pgtable; + /* + * Clear write_fault_to_shadow_pgtable here to ensure it is + * never reused. + */ + vcpu->arch.write_fault_to_shadow_pgtable = false; kvm_clear_exception_queue(vcpu); if (!(emulation_type & EMULTYPE_NO_DECODE)) { @@ -4886,7 +4899,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, if (r != EMULATION_OK) { if (emulation_type & EMULTYPE_TRAP_UD) return EMULATE_FAIL; - if (reexecute_instruction(vcpu, cr2)) + if (reexecute_instruction(vcpu, cr2, + write_fault_to_spt)) return EMULATE_DONE; if (emulation_type & EMULTYPE_SKIP) return EMULATE_FAIL; @@ -4916,7 +4930,7 @@ restart: return EMULATE_DONE; if (r == EMULATION_FAILED) { - if (reexecute_instruction(vcpu, cr2)) + if (reexecute_instruction(vcpu, cr2, write_fault_to_spt)) return EMULATE_DONE; return handle_emulation_failure(vcpu); From 1f8051876a194d7f7fe7834d9853f240d6b4b9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Tue, 22 Jan 2013 09:51:20 +1030 Subject: [PATCH 1096/4607] virtio_console: Let unconnected rproc device receive data. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow rproc serial ports to receive data before the port is connected. Rproc serial ports usually talk to very simple remote devices with no control queue managing open/close events. So we must let remote devices write to the virtio ring even if the device is not yet fully initialized. Signed-off-by: Sjur Brændeland Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 684b0d53764f..95cae778bd73 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1763,8 +1763,11 @@ static void in_intr(struct virtqueue *vq) * tty is spawned) and the host sends out data to console * ports. For generic serial ports, the host won't * (shouldn't) send data till the guest is connected. + * However a remote device might send data before the port is + * connected. So don't remove data from a rproc_serial device. */ - if (!port->guest_connected) + + if (!port->guest_connected && !is_rproc_serial(port->portdev->vdev)) discard_port_data(port); spin_unlock_irqrestore(&port->inbuf_lock, flags); From d59b4eaaf04db07a02f092bfcb00de7f2e2d10db Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 17 Jan 2013 22:03:22 +0800 Subject: [PATCH 1097/4607] gpio: fix warning of 'struct gpio_chip' declaration The struct gpio_chip is only defined inside #ifdef CONFIG_GPIOLIB, but it's referenced by gpiochip_add_pin_range() and gpiochip_remove_pin_ranges() which are outside #ifdef CONFIG_GPIOLIB. Thus, we see the following warning when building blackfin image, where GPIOLIB is not required. CC arch/blackfin/kernel/bfin_gpio.o CC init/version.o In file included from arch/blackfin/include/asm/gpio.h:321, from arch/blackfin/kernel/bfin_gpio.c:15: include/asm-generic/gpio.h:298: warning: 'struct gpio_chip' declared inside parameter list include/asm-generic/gpio.h:298: warning: its scope is only this definition or declaration, which is probably not what you want include/asm-generic/gpio.h:304: warning: 'struct gpio_chip' declared inside parameter list Move pinctrl trunk into #ifdef CONFIG_GPIOLIB to fix the warning, since it appears that pinctrl gpio range support depends on GPIOLIB. Signed-off-by: Shawn Guo Signed-off-by: Linus Walleij --- include/asm-generic/gpio.h | 74 +++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 20ca7663975f..23410147555f 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -212,6 +212,43 @@ extern void gpio_unexport(unsigned gpio); #endif /* CONFIG_GPIO_SYSFS */ +#ifdef CONFIG_PINCTRL + +/** + * struct gpio_pin_range - pin range controlled by a gpio chip + * @head: list for maintaining set of pin ranges, used internally + * @pctldev: pinctrl device which handles corresponding pins + * @range: actual range of pins controlled by a gpio controller + */ + +struct gpio_pin_range { + struct list_head node; + struct pinctrl_dev *pctldev; + struct pinctrl_gpio_range range; +}; + +int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int gpio_offset, unsigned int pin_offset, + unsigned int npins); +void gpiochip_remove_pin_ranges(struct gpio_chip *chip); + +#else + +static inline int +gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, + unsigned int gpio_offset, unsigned int pin_offset, + unsigned int npins) +{ + return 0; +} + +static inline void +gpiochip_remove_pin_ranges(struct gpio_chip *chip) +{ +} + +#endif /* CONFIG_PINCTRL */ + #else /* !CONFIG_GPIOLIB */ static inline bool gpio_is_valid(int number) @@ -270,41 +307,4 @@ static inline void gpio_unexport(unsigned gpio) } #endif /* CONFIG_GPIO_SYSFS */ -#ifdef CONFIG_PINCTRL - -/** - * struct gpio_pin_range - pin range controlled by a gpio chip - * @head: list for maintaining set of pin ranges, used internally - * @pctldev: pinctrl device which handles corresponding pins - * @range: actual range of pins controlled by a gpio controller - */ - -struct gpio_pin_range { - struct list_head node; - struct pinctrl_dev *pctldev; - struct pinctrl_gpio_range range; -}; - -int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int gpio_offset, unsigned int pin_offset, - unsigned int npins); -void gpiochip_remove_pin_ranges(struct gpio_chip *chip); - -#else - -static inline int -gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name, - unsigned int gpio_offset, unsigned int pin_offset, - unsigned int npins) -{ - return 0; -} - -static inline void -gpiochip_remove_pin_ranges(struct gpio_chip *chip) -{ -} - -#endif /* CONFIG_PINCTRL */ - #endif /* _ASM_GENERIC_GPIO_H */ From 6a89a314ab107a12af08c71420c19a37a30fc2d3 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 18 Jan 2013 15:57:46 +0800 Subject: [PATCH 1098/4607] gpio: devm_gpio_* support should not depend on GPIOLIB Some architectures (e.g. blackfin) provide gpio API without requiring GPIOLIB support (ARCH_WANT_OPTIONAL_GPIOLIB). devm_gpio_* functions should also work for these architectures, since they do not really depend on GPIOLIB. Add a new option GPIO_DEVRES (enabled by default) to control the build of devres.c. It also removes the empty version of devm_gpio_* functions for !GENERIC_GPIO build from linux/gpio.h, and moves the function declarations from asm-generic/gpio.h into linux/gpio.h. Signed-off-by: Shawn Guo Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 3 +++ drivers/gpio/Makefile | 3 ++- include/asm-generic/gpio.h | 6 ------ include/linux/gpio.h | 28 ++++++++-------------------- 4 files changed, 13 insertions(+), 27 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 682de754d63f..d9729322b1c7 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -30,6 +30,9 @@ config ARCH_REQUIRE_GPIOLIB Selecting this from the architecture code will cause the gpiolib code to always get built in. +config GPIO_DEVRES + def_bool y + depends on HAS_IOMEM menuconfig GPIOLIB diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c5aebd008dde..36ca605ff038 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -2,7 +2,8 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG -obj-$(CONFIG_GPIOLIB) += gpiolib.o devres.o +obj-$(CONFIG_GPIO_DEVRES) += devres.o +obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 23410147555f..45b8916805f3 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -192,12 +192,6 @@ extern int gpio_request_one(unsigned gpio, unsigned long flags, const char *labe extern int gpio_request_array(const struct gpio *array, size_t num); extern void gpio_free_array(const struct gpio *array, size_t num); -/* bindings for managed devices that want to request gpios */ -int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); -int devm_gpio_request_one(struct device *dev, unsigned gpio, - unsigned long flags, const char *label); -void devm_gpio_free(struct device *dev, unsigned int gpio); - #ifdef CONFIG_GPIO_SYSFS /* diff --git a/include/linux/gpio.h b/include/linux/gpio.h index bfe665621536..f6c7ae3e223b 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -94,24 +94,12 @@ static inline int gpio_request(unsigned gpio, const char *label) return -ENOSYS; } -static inline int devm_gpio_request(struct device *dev, unsigned gpio, - const char *label) -{ - return -ENOSYS; -} - static inline int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) { return -ENOSYS; } -static inline int devm_gpio_request_one(struct device *dev, unsigned gpio, - unsigned long flags, const char *label) -{ - return -ENOSYS; -} - static inline int gpio_request_array(const struct gpio *array, size_t num) { return -ENOSYS; @@ -125,14 +113,6 @@ static inline void gpio_free(unsigned gpio) WARN_ON(1); } -static inline void devm_gpio_free(struct device *dev, unsigned gpio) -{ - might_sleep(); - - /* GPIO can never have been requested */ - WARN_ON(1); -} - static inline void gpio_free_array(const struct gpio *array, size_t num) { might_sleep(); @@ -248,4 +228,12 @@ gpiochip_remove_pin_ranges(struct gpio_chip *chip) #endif /* ! CONFIG_GENERIC_GPIO */ +struct device; + +/* bindings for managed devices that want to request gpios */ +int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); +int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label); +void devm_gpio_free(struct device *dev, unsigned int gpio); + #endif /* __LINUX_GPIO_H */ From 5985d76cc1b62125301754c9b636a18c346bfc52 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Fri, 18 Jan 2013 15:31:13 +0800 Subject: [PATCH 1099/4607] gpio: pl061: set initcall level to module init Replace subsys initcall by module initcall level. Since pinctrl driver is already launched before gpio driver. It's unnecessary to set gpio driver in subsys init call level. Signed-off-by: Haojian Zhuang Tested-by: Dinh Nguyen Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pl061.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index c1720de18a4f..b820869ca93c 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -365,7 +365,7 @@ static int __init pl061_gpio_init(void) { return amba_driver_register(&pl061_gpio_driver); } -subsys_initcall(pl061_gpio_init); +module_init(pl061_gpio_init); MODULE_AUTHOR("Baruch Siach "); MODULE_DESCRIPTION("PL061 GPIO driver"); From 99600051b04bc4ec8bd4d16a8bf993ca54042db6 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 19 Jan 2013 11:17:14 +0900 Subject: [PATCH 1100/4607] udf: add extent cache support in case of file reading This patch implements extent caching in case of file reading. While reading a file, currently, UDF reads metadata serially which takes a lot of time depending on the number of extents present in the file. Caching last accessd extent improves metadata read time. Instead of reading file metadata from start, now we read from the cached extent. This patch considerably improves the time spent by CPU in kernel mode. For example, while reading a 10.9 GB file using dd: Time before applying patch: 11677022208 bytes (10.9GB) copied, 1529.748921 seconds, 7.3MB/s real 25m 29.85s user 0m 12.41s sys 15m 34.75s Time after applying patch: 11677022208 bytes (10.9GB) copied, 1469.338231 seconds, 7.6MB/s real 24m 29.44s user 0m 15.73s sys 3m 27.61s [JK: Fix bh refcounting issues, simplify initialization] Signed-off-by: Namjae Jeon Signed-off-by: Ashish Sangwan Signed-off-by: Bonggil Bak Signed-off-by: Jan Kara --- fs/udf/inode.c | 86 ++++++++++++++++++++++++++++++++++++++++++++---- fs/udf/super.c | 2 ++ fs/udf/udf_i.h | 16 +++++++++ fs/udf/udfdecl.h | 5 --- 4 files changed, 98 insertions(+), 11 deletions(-) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index cbae1ed0b7c1..7a12e48ad819 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -67,6 +67,74 @@ static void udf_update_extents(struct inode *, struct extent_position *); static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int); +static void __udf_clear_extent_cache(struct inode *inode) +{ + struct udf_inode_info *iinfo = UDF_I(inode); + + if (iinfo->cached_extent.lstart != -1) { + brelse(iinfo->cached_extent.epos.bh); + iinfo->cached_extent.lstart = -1; + } +} + +/* Invalidate extent cache */ +static void udf_clear_extent_cache(struct inode *inode) +{ + struct udf_inode_info *iinfo = UDF_I(inode); + + spin_lock(&iinfo->i_extent_cache_lock); + __udf_clear_extent_cache(inode); + spin_unlock(&iinfo->i_extent_cache_lock); +} + +/* Return contents of extent cache */ +static int udf_read_extent_cache(struct inode *inode, loff_t bcount, + loff_t *lbcount, struct extent_position *pos) +{ + struct udf_inode_info *iinfo = UDF_I(inode); + int ret = 0; + + spin_lock(&iinfo->i_extent_cache_lock); + if ((iinfo->cached_extent.lstart <= bcount) && + (iinfo->cached_extent.lstart != -1)) { + /* Cache hit */ + *lbcount = iinfo->cached_extent.lstart; + memcpy(pos, &iinfo->cached_extent.epos, + sizeof(struct extent_position)); + if (pos->bh) + get_bh(pos->bh); + ret = 1; + } + spin_unlock(&iinfo->i_extent_cache_lock); + return ret; +} + +/* Add extent to extent cache */ +static void udf_update_extent_cache(struct inode *inode, loff_t estart, + struct extent_position *pos, int next_epos) +{ + struct udf_inode_info *iinfo = UDF_I(inode); + + spin_lock(&iinfo->i_extent_cache_lock); + /* Invalidate previously cached extent */ + __udf_clear_extent_cache(inode); + if (pos->bh) + get_bh(pos->bh); + memcpy(&iinfo->cached_extent.epos, pos, + sizeof(struct extent_position)); + iinfo->cached_extent.lstart = estart; + if (next_epos) + switch (iinfo->i_alloc_type) { + case ICBTAG_FLAG_AD_SHORT: + iinfo->cached_extent.epos.offset -= + sizeof(struct short_ad); + break; + case ICBTAG_FLAG_AD_LONG: + iinfo->cached_extent.epos.offset -= + sizeof(struct long_ad); + } + spin_unlock(&iinfo->i_extent_cache_lock); +} void udf_evict_inode(struct inode *inode) { @@ -90,6 +158,7 @@ void udf_evict_inode(struct inode *inode) } kfree(iinfo->i_ext.i_data); iinfo->i_ext.i_data = NULL; + udf_clear_extent_cache(inode); if (want_delete) { udf_free_inode(inode); } @@ -105,6 +174,7 @@ static void udf_write_failed(struct address_space *mapping, loff_t to) truncate_pagecache(inode, to, isize); if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { down_write(&iinfo->i_data_sem); + udf_clear_extent_cache(inode); udf_truncate_extents(inode); up_write(&iinfo->i_data_sem); } @@ -372,7 +442,7 @@ static int udf_get_block(struct inode *inode, sector_t block, iinfo->i_next_alloc_goal++; } - + udf_clear_extent_cache(inode); phys = inode_getblk(inode, block, &err, &new); if (!phys) goto abort; @@ -1171,6 +1241,7 @@ set_size: } else { if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { down_write(&iinfo->i_data_sem); + udf_clear_extent_cache(inode); memset(iinfo->i_ext.i_data + iinfo->i_lenEAttr + newsize, 0x00, bsize - newsize - udf_file_entry_alloc_offset(inode)); @@ -1184,6 +1255,7 @@ set_size: if (err) return err; down_write(&iinfo->i_data_sem); + udf_clear_extent_cache(inode); truncate_setsize(inode, newsize); udf_truncate_extents(inode); up_write(&iinfo->i_data_sem); @@ -2156,11 +2228,12 @@ int8_t inode_bmap(struct inode *inode, sector_t block, struct udf_inode_info *iinfo; iinfo = UDF_I(inode); - pos->offset = 0; - pos->block = iinfo->i_location; - pos->bh = NULL; + if (!udf_read_extent_cache(inode, bcount, &lbcount, pos)) { + pos->offset = 0; + pos->block = iinfo->i_location; + pos->bh = NULL; + } *elen = 0; - do { etype = udf_next_aext(inode, pos, eloc, elen, 1); if (etype == -1) { @@ -2170,7 +2243,8 @@ int8_t inode_bmap(struct inode *inode, sector_t block, } lbcount += *elen; } while (lbcount <= bcount); - + /* update extent cache */ + udf_update_extent_cache(inode, lbcount - *elen, pos, 1); *offset = (bcount + *elen - lbcount) >> blocksize_bits; return etype; diff --git a/fs/udf/super.c b/fs/udf/super.c index 186adbf94b20..da8ce9f14387 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -134,6 +134,8 @@ static struct inode *udf_alloc_inode(struct super_block *sb) ei->i_next_alloc_goal = 0; ei->i_strat4096 = 0; init_rwsem(&ei->i_data_sem); + ei->cached_extent.lstart = -1; + spin_lock_init(&ei->i_extent_cache_lock); return &ei->vfs_inode; } diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h index bb8309dcd5c1..b5cd8ed2aa12 100644 --- a/fs/udf/udf_i.h +++ b/fs/udf/udf_i.h @@ -1,6 +1,19 @@ #ifndef _UDF_I_H #define _UDF_I_H +struct extent_position { + struct buffer_head *bh; + uint32_t offset; + struct kernel_lb_addr block; +}; + +struct udf_ext_cache { + /* Extent position */ + struct extent_position epos; + /* Start logical offset in bytes */ + loff_t lstart; +}; + /* * The i_data_sem and i_mutex serve for protection of allocation information * of a regular files and symlinks. This includes all extents belonging to @@ -35,6 +48,9 @@ struct udf_inode_info { __u8 *i_data; } i_ext; struct rw_semaphore i_data_sem; + struct udf_ext_cache cached_extent; + /* Spinlock for protecting extent cache */ + spinlock_t i_extent_cache_lock; struct inode vfs_inode; }; diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index de038da6f6bd..be7dabbbcb49 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -113,11 +113,6 @@ struct ustr { uint8_t u_len; }; -struct extent_position { - struct buffer_head *bh; - uint32_t offset; - struct kernel_lb_addr block; -}; /* super.c */ From a77cfcac79c7b171d344e2bc0f05c075bc1fcfb2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Jan 2013 09:26:09 -0300 Subject: [PATCH 1101/4607] [media] mb86a20s: improve error handling at get_frontend The read/write errors are not handled well on get_frontend. Fix it, by letting the frontend cached values to represent the DVB properties that were successfully retrieved. While here, use "c" for dtv_frontend_properties cache, instead of "p", as this is more common. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 138 ++++++++++++++----------- 1 file changed, 80 insertions(+), 58 deletions(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index fade566927c3..4ff3a0c9d977 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -1,11 +1,9 @@ /* * Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver * - * Copyright (C) 2010 Mauro Carvalho Chehab + * Copyright (C) 2010-2013 Mauro Carvalho Chehab * Copyright (C) 2009-2010 Douglas Landgraf * - * FIXME: Need to port to DVB v5.2 API - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation version 2. @@ -360,7 +358,7 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) /* * FIXME: Properly implement the set frontend properties */ - struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; #endif dprintk("\n"); @@ -507,93 +505,117 @@ static int mb86a20s_get_segment_count(struct mb86a20s_state *state, return count; } +static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + /* Fixed parameters */ + c->delivery_system = SYS_ISDBT; + c->bandwidth_hz = 6000000; + + /* Initialize values that will be later autodetected */ + c->isdbt_layer_enabled = 0; + c->transmission_mode = TRANSMISSION_MODE_AUTO; + c->guard_interval = GUARD_INTERVAL_AUTO; + c->isdbt_sb_mode = 0; + c->isdbt_sb_segment_count = 0; +} + static int mb86a20s_get_frontend(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; - struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; int i, rc; - /* Fixed parameters */ - p->delivery_system = SYS_ISDBT; - p->bandwidth_hz = 6000000; + /* Reset frontend cache to default values */ + mb86a20s_reset_frontend_cache(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); /* Check for partial reception */ rc = mb86a20s_writereg(state, 0x6d, 0x85); - if (rc >= 0) - rc = mb86a20s_readreg(state, 0x6e); - if (rc >= 0) - p->isdbt_partial_reception = (rc & 0x10) ? 1 : 0; + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x6e); + if (rc < 0) + return rc; + c->isdbt_partial_reception = (rc & 0x10) ? 1 : 0; /* Get per-layer data */ - p->isdbt_layer_enabled = 0; + for (i = 0; i < 3; i++) { rc = mb86a20s_get_segment_count(state, i); - if (rc >= 0 && rc < 14) - p->layer[i].segment_count = rc; - if (rc == 0x0f) + if (rc < 0) + goto error; + if (rc >= 0 && rc < 14) + c->layer[i].segment_count = rc; + else { + c->layer[i].segment_count = 0; continue; - p->isdbt_layer_enabled |= 1 << i; + } + c->isdbt_layer_enabled |= 1 << i; rc = mb86a20s_get_modulation(state, i); - if (rc >= 0) - p->layer[i].modulation = rc; + if (rc < 0) + goto error; + c->layer[i].modulation = rc; rc = mb86a20s_get_fec(state, i); - if (rc >= 0) - p->layer[i].fec = rc; + if (rc < 0) + goto error; + c->layer[i].fec = rc; rc = mb86a20s_get_interleaving(state, i); - if (rc >= 0) - p->layer[i].interleaving = rc; + if (rc < 0) + goto error; + c->layer[i].interleaving = rc; } - p->isdbt_sb_mode = 0; rc = mb86a20s_writereg(state, 0x6d, 0x84); - if ((rc >= 0) && ((rc & 0x60) == 0x20)) { - p->isdbt_sb_mode = 1; + if (rc < 0) + return rc; + if ((rc & 0x60) == 0x20) { + c->isdbt_sb_mode = 1; /* At least, one segment should exist */ - if (!p->isdbt_sb_segment_count) - p->isdbt_sb_segment_count = 1; - } else - p->isdbt_sb_segment_count = 0; + if (!c->isdbt_sb_segment_count) + c->isdbt_sb_segment_count = 1; + } /* Get transmission mode and guard interval */ - p->transmission_mode = TRANSMISSION_MODE_AUTO; - p->guard_interval = GUARD_INTERVAL_AUTO; rc = mb86a20s_readreg(state, 0x07); - if (rc >= 0) { - if ((rc & 0x60) == 0x20) { - switch (rc & 0x0c >> 2) { - case 0: - p->transmission_mode = TRANSMISSION_MODE_2K; - break; - case 1: - p->transmission_mode = TRANSMISSION_MODE_4K; - break; - case 2: - p->transmission_mode = TRANSMISSION_MODE_8K; - break; - } + if (rc < 0) + return rc; + if ((rc & 0x60) == 0x20) { + switch (rc & 0x0c >> 2) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_4K; + break; + case 2: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; } - if (!(rc & 0x10)) { - switch (rc & 0x3) { - case 0: - p->guard_interval = GUARD_INTERVAL_1_4; - break; - case 1: - p->guard_interval = GUARD_INTERVAL_1_8; - break; - case 2: - p->guard_interval = GUARD_INTERVAL_1_16; - break; - } + } + if (!(rc & 0x10)) { + switch (rc & 0x3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_16; + break; } } +error: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - return 0; + return rc; + } static int mb86a20s_tune(struct dvb_frontend *fe, From fd53744efea8bf845dc54bd3095be6203b1b07a1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Jan 2013 10:16:07 -0300 Subject: [PATCH 1102/4607] [media] mb86a20s: Fix i2c gate on error If an error happens, restore tuner I2C gate to the right value. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 4ff3a0c9d977..3c8587e38a05 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -262,10 +262,10 @@ static int mb86a20s_initfe(struct dvb_frontend *fe) goto err; } +err: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); -err: if (rc < 0) { state->need_init = true; printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); @@ -363,6 +363,10 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) dprintk("\n"); + /* + * Gate should already be opened, but it doesn't hurt to + * double-check + */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); dprintk("Calling tuner set parameters\n"); From ce77d120ed24d44aa020bde61f32bbdabb9ed596 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Jan 2013 14:12:10 -0300 Subject: [PATCH 1103/4607] [media] mb86a20s: make AGC work better It is recommented to change register 0x0440 value to 0, in order to fix some AGC bug. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 3c8587e38a05..40c61838c61d 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -126,7 +126,8 @@ static struct regdata mb86a20s_init[] = { { 0x50, 0xd7 }, { 0x51, 0x3f }, { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x28, 0x74 }, { 0x29, 0x40 }, { 0x28, 0x46 }, { 0x29, 0x2c }, { 0x28, 0x46 }, { 0x29, 0x0c }, - { 0x04, 0x40 }, { 0x05, 0x01 }, + + { 0x04, 0x40 }, { 0x05, 0x00 }, { 0x28, 0x00 }, { 0x29, 0x10 }, { 0x28, 0x05 }, { 0x29, 0x02 }, { 0x1c, 0x01 }, From 04585921ac0fa0f4baaf510cc7e52e3399018fb4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Jan 2013 12:31:13 -0300 Subject: [PATCH 1104/4607] [media] mb86a20s: fix interleaving and FEC retrival Get the proper bits from the TMCC table registers. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 40c61838c61d..8f4fff1f0fa5 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -413,7 +413,7 @@ static int mb86a20s_get_modulation(struct mb86a20s_state *state, rc = mb86a20s_readreg(state, 0x6e); if (rc < 0) return rc; - switch ((rc & 0x70) >> 4) { + switch ((rc >> 4) & 0x07) { case 0: return DQPSK; case 1: @@ -446,7 +446,7 @@ static int mb86a20s_get_fec(struct mb86a20s_state *state, rc = mb86a20s_readreg(state, 0x6e); if (rc < 0) return rc; - switch (rc) { + switch ((rc >> 4) & 0x07) { case 0: return FEC_1_2; case 1: @@ -481,9 +481,21 @@ static int mb86a20s_get_interleaving(struct mb86a20s_state *state, rc = mb86a20s_readreg(state, 0x6e); if (rc < 0) return rc; - if (rc > 3) - return -EINVAL; /* Not used */ - return rc; + + switch ((rc >> 4) & 0x07) { + case 1: + return GUARD_INTERVAL_1_4; + case 2: + return GUARD_INTERVAL_1_8; + case 3: + return GUARD_INTERVAL_1_16; + case 4: + return GUARD_INTERVAL_1_32; + + default: + case 0: + return GUARD_INTERVAL_AUTO; + } } static int mb86a20s_get_segment_count(struct mb86a20s_state *state, From d36e418a7b1eaeb006ee304533054e2720537db7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jan 2013 08:49:39 -0200 Subject: [PATCH 1105/4607] [media] mb86a20s: Split status read logic from DVB callback Split the logic that reads the status from the DVB callback. That helps to properly return an error code, if status read fails. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 31 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 8f4fff1f0fa5..03b74d3afd8f 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -320,16 +320,14 @@ static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct mb86a20s_state *state = fe->demodulator_priv; - u8 val; + int val; dprintk("\n"); *status = 0; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); val = mb86a20s_readreg(state, 0x0a) & 0xf; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + if (val < 0) + return val; if (val >= 2) *status |= FE_HAS_SIGNAL; @@ -635,6 +633,25 @@ error: } +static int mb86a20s_read_status_gate(struct dvb_frontend *fe, + fe_status_t *status) +{ + int ret; + + dprintk("\n"); + *status = 0; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + ret = mb86a20s_read_status(fe, status); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + return ret; +} + static int mb86a20s_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, @@ -649,7 +666,7 @@ static int mb86a20s_tune(struct dvb_frontend *fe, rc = mb86a20s_set_frontend(fe); if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) - mb86a20s_read_status(fe, status); + mb86a20s_read_status_gate(fe, status); return rc; } @@ -730,7 +747,7 @@ static struct dvb_frontend_ops mb86a20s_ops = { .init = mb86a20s_initfe, .set_frontend = mb86a20s_set_frontend, .get_frontend = mb86a20s_get_frontend, - .read_status = mb86a20s_read_status, + .read_status = mb86a20s_read_status_gate, .read_signal_strength = mb86a20s_read_signal_strength, .tune = mb86a20s_tune, }; From dd4493ef34cb4062d59d87717aaf8a1c27d450c9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jan 2013 08:53:11 -0200 Subject: [PATCH 1106/4607] [media] mb86a20s: Function reorder Reorder functions to have everything related to stats/status read close. That will make the file more organized as other stats routines will be added. No functional changes. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 215 +++++++++++++------------ 1 file changed, 110 insertions(+), 105 deletions(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 03b74d3afd8f..b348f97aa7b5 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -175,6 +175,10 @@ static struct regdata mb86a20s_reset_reception[] = { { 0x08, 0x00 }, }; +/* + * I2C read/write functions and macros + */ + static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, u8 i2c_addr, int reg, int data) { @@ -236,45 +240,36 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, mb86a20s_i2c_writeregdata(state, state->config->demod_address, \ regdata, ARRAY_SIZE(regdata)) -static int mb86a20s_initfe(struct dvb_frontend *fe) +static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct mb86a20s_state *state = fe->demodulator_priv; - int rc; - u8 regD5 = 1; + int val; dprintk("\n"); + *status = 0; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); + val = mb86a20s_readreg(state, 0x0a) & 0xf; + if (val < 0) + return val; - /* Initialize the frontend */ - rc = mb86a20s_writeregdata(state, mb86a20s_init); - if (rc < 0) - goto err; + if (val >= 2) + *status |= FE_HAS_SIGNAL; - if (!state->config->is_serial) { - regD5 &= ~1; + if (val >= 4) + *status |= FE_HAS_CARRIER; - rc = mb86a20s_writereg(state, 0x50, 0xd5); - if (rc < 0) - goto err; - rc = mb86a20s_writereg(state, 0x51, regD5); - if (rc < 0) - goto err; - } + if (val >= 5) + *status |= FE_HAS_VITERBI; -err: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + if (val >= 7) + *status |= FE_HAS_SYNC; - if (rc < 0) { - state->need_init = true; - printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); - } else { - state->need_init = false; - dprintk("Initialization succeeded.\n"); - } - return rc; + if (val >= 8) /* Maybe 9? */ + *status |= FE_HAS_LOCK; + + dprintk("val = %d, status = 0x%02x\n", val, *status); + + return 0; } static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) @@ -317,82 +312,6 @@ static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) return 0; } -static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) -{ - struct mb86a20s_state *state = fe->demodulator_priv; - int val; - - dprintk("\n"); - *status = 0; - - val = mb86a20s_readreg(state, 0x0a) & 0xf; - if (val < 0) - return val; - - if (val >= 2) - *status |= FE_HAS_SIGNAL; - - if (val >= 4) - *status |= FE_HAS_CARRIER; - - if (val >= 5) - *status |= FE_HAS_VITERBI; - - if (val >= 7) - *status |= FE_HAS_SYNC; - - if (val >= 8) /* Maybe 9? */ - *status |= FE_HAS_LOCK; - - dprintk("val = %d, status = 0x%02x\n", val, *status); - - return 0; -} - -static int mb86a20s_set_frontend(struct dvb_frontend *fe) -{ - struct mb86a20s_state *state = fe->demodulator_priv; - int rc; -#if 0 - /* - * FIXME: Properly implement the set frontend properties - */ - struct dtv_frontend_properties *c = &fe->dtv_property_cache; -#endif - - dprintk("\n"); - - /* - * Gate should already be opened, but it doesn't hurt to - * double-check - */ - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - dprintk("Calling tuner set parameters\n"); - fe->ops.tuner_ops.set_params(fe); - - /* - * Make it more reliable: if, for some reason, the initial - * device initialization doesn't happen, initialize it when - * a SBTVD parameters are adjusted. - * - * Unfortunately, due to a hard to track bug at tda829x/tda18271, - * the agc callback logic is not called during DVB attach time, - * causing mb86a20s to not be initialized with Kworld SBTVD. - * So, this hack is needed, in order to make Kworld SBTVD to work. - */ - if (state->need_init) - mb86a20s_initfe(fe); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - return rc; -} - static int mb86a20s_get_modulation(struct mb86a20s_state *state, unsigned layer) { @@ -633,6 +552,92 @@ error: } +static int mb86a20s_initfe(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc; + u8 regD5 = 1; + + dprintk("\n"); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + /* Initialize the frontend */ + rc = mb86a20s_writeregdata(state, mb86a20s_init); + if (rc < 0) + goto err; + + if (!state->config->is_serial) { + regD5 &= ~1; + + rc = mb86a20s_writereg(state, 0x50, 0xd5); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x51, regD5); + if (rc < 0) + goto err; + } + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (rc < 0) { + state->need_init = true; + printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); + } else { + state->need_init = false; + dprintk("Initialization succeeded.\n"); + } + return rc; +} + +static int mb86a20s_set_frontend(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc; +#if 0 + /* + * FIXME: Properly implement the set frontend properties + */ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; +#endif + + dprintk("\n"); + + /* + * Gate should already be opened, but it doesn't hurt to + * double-check + */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + dprintk("Calling tuner set parameters\n"); + fe->ops.tuner_ops.set_params(fe); + + /* + * Make it more reliable: if, for some reason, the initial + * device initialization doesn't happen, initialize it when + * a SBTVD parameters are adjusted. + * + * Unfortunately, due to a hard to track bug at tda829x/tda18271, + * the agc callback logic is not called during DVB attach time, + * causing mb86a20s to not be initialized with Kworld SBTVD. + * So, this hack is needed, in order to make Kworld SBTVD to work. + */ + if (state->need_init) + mb86a20s_initfe(fe); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + return rc; +} + + static int mb86a20s_read_status_gate(struct dvb_frontend *fe, fe_status_t *status) { From f66d81b54dac26d4e601d4d7faca53f3bdc98427 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jan 2013 09:13:08 -0200 Subject: [PATCH 1107/4607] [media] mb86a20s: convert it to use dev_info/dev_err/dev_dbg Instead of having its own set of macros, use the Kernel default ones for debug, error and info. While here, do some cleanup on the debug printk's. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 96 ++++++++++++++------------ 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index b348f97aa7b5..c52ae2ea0bf9 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -24,18 +24,6 @@ static int debug = 1; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); -#define rc(args...) do { \ - printk(KERN_ERR "mb86a20s: " args); \ -} while (0) - -#define dprintk(args...) \ - do { \ - if (debug) { \ - printk(KERN_DEBUG "mb86a20s: %s: ", __func__); \ - printk(args); \ - } \ - } while (0) - struct mb86a20s_state { struct i2c_adapter *i2c; const struct mb86a20s_config *config; @@ -190,8 +178,9 @@ static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, rc = i2c_transfer(state->i2c, &msg, 1); if (rc != 1) { - printk("%s: writereg error (rc == %i, reg == 0x%02x," - " data == 0x%02x)\n", __func__, rc, reg, data); + dev_err(&state->i2c->dev, + "%s: writereg error (rc == %i, reg == 0x%02x, data == 0x%02x)\n", + __func__, rc, reg, data); return rc; } @@ -225,8 +214,9 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, rc = i2c_transfer(state->i2c, msg, 2); if (rc != 2) { - rc("%s: reg=0x%x (error=%d)\n", __func__, reg, rc); - return rc; + dev_err(&state->i2c->dev, "%s: reg=0x%x (error=%d)\n", + __func__, reg, rc); + return (rc < 0) ? rc : -EIO; } return val; @@ -245,7 +235,6 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) struct mb86a20s_state *state = fe->demodulator_priv; int val; - dprintk("\n"); *status = 0; val = mb86a20s_readreg(state, 0x0a) & 0xf; @@ -267,7 +256,8 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) if (val >= 8) /* Maybe 9? */ *status |= FE_HAS_LOCK; - dprintk("val = %d, status = 0x%02x\n", val, *status); + dev_dbg(&state->i2c->dev, "%s: Status = 0x%02x (state = %d)\n", + __func__, *status, val); return 0; } @@ -278,8 +268,6 @@ static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) unsigned rf_max, rf_min, rf; u8 val; - dprintk("\n"); - if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); @@ -300,12 +288,13 @@ static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) rf_max = (rf_max + rf_min) / 2; if (rf_max - rf_min < 4) { *strength = (((rf_max + rf_min) / 2) * 65535) / 4095; + dev_dbg(&state->i2c->dev, + "%s: signal strength = %d (%d < RF=%d < %d)\n", + __func__, rf, rf_min, rf >> 4, rf_max); break; } } while (1); - dprintk("signal strength = %d\n", *strength); - if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -419,15 +408,17 @@ static int mb86a20s_get_segment_count(struct mb86a20s_state *state, unsigned layer) { int rc, count; - static unsigned char reg[] = { [0] = 0x89, /* Layer A */ [1] = 0x8d, /* Layer B */ [2] = 0x91, /* Layer C */ }; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + if (layer >= ARRAY_SIZE(reg)) return -EINVAL; + rc = mb86a20s_writereg(state, 0x6d, reg[layer]); if (rc < 0) return rc; @@ -436,13 +427,18 @@ static int mb86a20s_get_segment_count(struct mb86a20s_state *state, return rc; count = (rc >> 4) & 0x0f; + dev_dbg(&state->i2c->dev, "%s: segments: %d.\n", __func__, count); + return count; } static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe) { + struct mb86a20s_state *state = fe->demodulator_priv; struct dtv_frontend_properties *c = &fe->dtv_property_cache; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + /* Fixed parameters */ c->delivery_system = SYS_ISDBT; c->bandwidth_hz = 6000000; @@ -461,6 +457,8 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; int i, rc; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + /* Reset frontend cache to default values */ mb86a20s_reset_frontend_cache(fe); @@ -479,9 +477,12 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) /* Get per-layer data */ for (i = 0; i < 3; i++) { + dev_dbg(&state->i2c->dev, "%s: getting data for layer %c.\n", + __func__, 'A' + i); + rc = mb86a20s_get_segment_count(state, i); if (rc < 0) - goto error; + goto noperlayer_error; if (rc >= 0 && rc < 14) c->layer[i].segment_count = rc; else { @@ -491,15 +492,21 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) c->isdbt_layer_enabled |= 1 << i; rc = mb86a20s_get_modulation(state, i); if (rc < 0) - goto error; + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: modulation %d.\n", + __func__, rc); c->layer[i].modulation = rc; rc = mb86a20s_get_fec(state, i); if (rc < 0) - goto error; + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: FEC %d.\n", + __func__, rc); c->layer[i].fec = rc; rc = mb86a20s_get_interleaving(state, i); if (rc < 0) - goto error; + goto noperlayer_error; + dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n", + __func__, rc); c->layer[i].interleaving = rc; } @@ -544,7 +551,7 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) } } -error: +noperlayer_error: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -558,7 +565,7 @@ static int mb86a20s_initfe(struct dvb_frontend *fe) int rc; u8 regD5 = 1; - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); @@ -585,10 +592,11 @@ err: if (rc < 0) { state->need_init = true; - printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); + dev_info(&state->i2c->dev, + "mb86a20s: Init failed. Will try again later\n"); } else { state->need_init = false; - dprintk("Initialization succeeded.\n"); + dev_dbg(&state->i2c->dev, "Initialization succeeded.\n"); } return rc; } @@ -603,8 +611,7 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) */ struct dtv_frontend_properties *c = &fe->dtv_property_cache; #endif - - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); /* * Gate should already be opened, but it doesn't hurt to @@ -612,7 +619,6 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - dprintk("Calling tuner set parameters\n"); fe->ops.tuner_ops.set_params(fe); /* @@ -637,13 +643,11 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) return rc; } - static int mb86a20s_read_status_gate(struct dvb_frontend *fe, fe_status_t *status) { int ret; - dprintk("\n"); *status = 0; if (fe->ops.i2c_gate_ctrl) @@ -663,9 +667,10 @@ static int mb86a20s_tune(struct dvb_frontend *fe, unsigned int *delay, fe_status_t *status) { + struct mb86a20s_state *state = fe->demodulator_priv; int rc = 0; - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); if (re_tune) rc = mb86a20s_set_frontend(fe); @@ -680,7 +685,7 @@ static void mb86a20s_release(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); kfree(state); } @@ -690,15 +695,16 @@ static struct dvb_frontend_ops mb86a20s_ops; struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, struct i2c_adapter *i2c) { + struct mb86a20s_state *state; u8 rev; /* allocate memory for the internal state */ - struct mb86a20s_state *state = - kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL); + state = kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL); - dprintk("\n"); + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); if (state == NULL) { - rc("Unable to kzalloc\n"); + dev_err(&state->i2c->dev, + "%s: unable to allocate memory for state\n", __func__); goto error; } @@ -715,9 +721,11 @@ struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, rev = mb86a20s_readreg(state, 0); if (rev == 0x13) { - printk(KERN_INFO "Detected a Fujitsu mb86a20s frontend\n"); + dev_info(&state->i2c->dev, + "Detected a Fujitsu mb86a20s frontend\n"); } else { - printk(KERN_ERR "Frontend revision %d is unknown - aborting.\n", + dev_dbg(&state->i2c->dev, + "Frontend revision %d is unknown - aborting.\n", rev); goto error; } From 1ffff60320879830e469e26062c18f75236822ba Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 22 Jan 2013 12:50:34 +0200 Subject: [PATCH 1108/4607] drm/i915: add quirk to invert brightness on eMachines G725 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=59628 Reported-by: Roland Gruber Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 77254460a5cb..44f9d8f73a20 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8599,6 +8599,9 @@ static struct intel_quirk intel_quirks[] = { /* Acer Aspire 5734Z must invert backlight brightness */ { 0x2a42, 0x1025, 0x0459, quirk_invert_brightness }, + + /* Acer/eMachines G725 */ + { 0x2a42, 0x1025, 0x0210, quirk_invert_brightness }, }; static void intel_init_quirks(struct drm_device *dev) From 01e3a8feb40e54b962a20fa7eb595c5efef5e109 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 22 Jan 2013 12:50:35 +0200 Subject: [PATCH 1109/4607] drm/i915: add quirk to invert brightness on eMachines e725 Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=31522#c35 [Note: There are more than one broken setups in the bug. This fixes one.] Reported-by: Martins Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 44f9d8f73a20..8575a62d29d3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8602,6 +8602,9 @@ static struct intel_quirk intel_quirks[] = { /* Acer/eMachines G725 */ { 0x2a42, 0x1025, 0x0210, quirk_invert_brightness }, + + /* Acer/eMachines e725 */ + { 0x2a42, 0x1025, 0x0212, quirk_invert_brightness }, }; static void intel_init_quirks(struct drm_device *dev) From 5559ecadad5a73b27f863e92f4b4f369501dce6f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 22 Jan 2013 12:50:36 +0200 Subject: [PATCH 1110/4607] drm/i915: add quirk to invert brightness on Packard Bell NCL20 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=44156 Reported-by: Alan Zimmerman Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8575a62d29d3..726278668ab6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8605,6 +8605,9 @@ static struct intel_quirk intel_quirks[] = { /* Acer/eMachines e725 */ { 0x2a42, 0x1025, 0x0212, quirk_invert_brightness }, + + /* Acer/Packard Bell NCL20 */ + { 0x2a42, 0x1025, 0x034b, quirk_invert_brightness }, }; static void intel_init_quirks(struct drm_device *dev) From 99433931950f33039d9e1a52b4ed9af3f1b58e84 Mon Sep 17 00:00:00 2001 From: Mika Kuoppala Date: Tue, 22 Jan 2013 14:12:17 +0200 Subject: [PATCH 1111/4607] drm/i915: use gem_set_seqno() on hardware init When machine was rebooted or module was reloaded, gem_hw_init() set last_seqno to be identical to next_seqno. This lead to situation that waits for first ever request always passed immediately regardless if it was actually executed. Use gem_set_seqno() to be consistent how hw is initialized on init, wrap and on resume. Signed-off-by: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 6 ++++-- drivers/gpu/drm/i915/intel_ringbuffer.c | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5226ebcac33a..801f77ece72e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3941,8 +3941,6 @@ i915_gem_init_hw(struct drm_device *dev) i915_gem_init_swizzling(dev); - dev_priv->next_seqno = dev_priv->last_seqno = (u32)~0 - 0x1000; - ret = intel_init_render_ring_buffer(dev); if (ret) return ret; @@ -3959,6 +3957,10 @@ i915_gem_init_hw(struct drm_device *dev) goto cleanup_bsd_ring; } + ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000)); + if (ret) + return ret; + /* * XXX: There was some w/a described somewhere suggesting loading * contexts before PPGTT. diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 9438bcd50678..dc6ae2fa1cee 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1223,8 +1223,6 @@ static int intel_init_ring_buffer(struct drm_device *dev, if (IS_I830(ring->dev) || IS_845G(ring->dev)) ring->effective_size -= 128; - intel_ring_init_seqno(ring, dev_priv->last_seqno); - return 0; err_unmap: From 7b9f35a6dd72f89452c58bbdbaf063027bf857ec Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Tue, 22 Jan 2013 23:25:25 +0800 Subject: [PATCH 1112/4607] drm/i915: HDMI/DP - ELD info refresh support for Haswell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ELD info should be updated dynamically according to hot plug event. For haswell chip, clear/set the eld valid bit and output enable bit from callback intel_disable/eanble_ddi(). Reviewed-by: Ville Syrjälä Reviewed-by: Rodrigo Vivi Signed-off-by: Wang Xingchao Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 21 +++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 4 ++++ drivers/gpu/drm/i915/intel_drv.h | 1 + 3 files changed, 26 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 2e904a5cd6cb..33b911218023 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -677,6 +677,7 @@ static void intel_ddi_mode_set(struct drm_encoder *encoder, DRM_DEBUG_KMS("Preparing DDI mode for Haswell on port %c, pipe %c\n", port_name(port), pipe_name(pipe)); + intel_crtc->eld_vld = false; if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); @@ -1287,10 +1288,14 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder) static void intel_enable_ddi(struct intel_encoder *intel_encoder) { struct drm_encoder *encoder = &intel_encoder->base; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; enum port port = intel_ddi_get_encoder_port(intel_encoder); int type = intel_encoder->type; + uint32_t tmp; if (type == INTEL_OUTPUT_HDMI) { /* In HDMI/DVI mode, the port width, and swing/emphasis values @@ -1303,18 +1308,34 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder) ironlake_edp_backlight_on(intel_dp); } + + if (intel_crtc->eld_vld) { + tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); + tmp |= ((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); + I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); + } } static void intel_disable_ddi(struct intel_encoder *intel_encoder) { struct drm_encoder *encoder = &intel_encoder->base; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; int type = intel_encoder->type; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t tmp; if (type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); ironlake_edp_backlight_off(intel_dp); } + + tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); + tmp &= ~((AUDIO_OUTPUT_ENABLE_A | AUDIO_ELD_VALID_A) << (pipe * 4)); + I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp); } int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 726278668ab6..305be115319e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3721,10 +3721,12 @@ static void intel_crtc_disable(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_connector *connector; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); /* crtc should still be enabled when we disable it. */ WARN_ON(!crtc->enabled); + intel_crtc->eld_vld = false; dev_priv->display.crtc_disable(crtc); intel_crtc_update_sarea(crtc, false); dev_priv->display.off(crtc); @@ -5792,6 +5794,7 @@ static void haswell_write_eld(struct drm_connector *connector, struct drm_i915_private *dev_priv = connector->dev->dev_private; uint8_t *eld = connector->eld; struct drm_device *dev = crtc->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); uint32_t eldv; uint32_t i; int len; @@ -5833,6 +5836,7 @@ static void haswell_write_eld(struct drm_connector *connector, DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe)); eldv = AUDIO_ELD_VALID_A << (pipe * 4); + intel_crtc->eld_vld = true; if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n"); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index aeff0d1067ad..66619d80348f 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -211,6 +211,7 @@ struct intel_crtc { * some outputs connected to this crtc. */ bool active; + bool eld_vld; bool primary_disabled; /* is the crtc obscured by a plane? */ bool lowfreq_avail; struct intel_overlay *overlay; From 7b2e1277598e4187c9be3e61fd9b0f0423f97986 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 10 Nov 2012 12:04:15 +0100 Subject: [PATCH 1113/4607] ARM: OMAP3: clock: Back-propagate rate change from cam_mclk to dpll4_m5 The cam_mclk clock is generated through the following clocks chain: dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk As dpll4_m5 and dpll4_m5x2 do not driver any clock other than cam_mclk, back-propagate the cam_clk rate changes up to dpll4_m5. Signed-off-by: Laurent Pinchart Reviewed-by: Mike Turquette Acked-by: Sakari Ailus Tested-by: Sakari Ailus --- arch/arm/mach-omap2/cclock3xxx_data.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/cclock3xxx_data.c b/arch/arm/mach-omap2/cclock3xxx_data.c index 6ef87580c33f..4579c3c5338f 100644 --- a/arch/arm/mach-omap2/cclock3xxx_data.c +++ b/arch/arm/mach-omap2/cclock3xxx_data.c @@ -426,6 +426,7 @@ static struct clk dpll4_m5x2_ck_3630 = { .parent_names = dpll4_m5x2_ck_parent_names, .num_parents = ARRAY_SIZE(dpll4_m5x2_ck_parent_names), .ops = &dpll4_m5x2_ck_3630_ops, + .flags = CLK_SET_RATE_PARENT, }; static struct clk cam_mclk; @@ -443,7 +444,14 @@ static struct clk_hw_omap cam_mclk_hw = { .clkdm_name = "cam_clkdm", }; -DEFINE_STRUCT_CLK(cam_mclk, cam_mclk_parent_names, aes2_ick_ops); +static struct clk cam_mclk = { + .name = "cam_mclk", + .hw = &cam_mclk_hw.hw, + .parent_names = cam_mclk_parent_names, + .num_parents = ARRAY_SIZE(cam_mclk_parent_names), + .ops = &aes2_ick_ops, + .flags = CLK_SET_RATE_PARENT, +}; static const struct clksel_rate clkout2_src_core_rates[] = { { .div = 1, .val = 0, .flags = RATE_IN_3XXX }, From 6d1aa02f10497b138e01ebe6eafabd6071729334 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 10 Nov 2012 12:06:25 +0100 Subject: [PATCH 1114/4607] omap3isp: Set cam_mclk rate directly Now that the cam_mclk rate changes are back-propagated to dpll4_m5_ck we can set the cam_mclk rate directly instead of manually setting the rate of the parent clock. Signed-off-by: Laurent Pinchart Reviewed-by: Mike Turquette Acked-by: Sakari Ailus Tested-by: Sakari Ailus --- drivers/media/platform/omap3isp/isp.c | 18 ++---------------- drivers/media/platform/omap3isp/isp.h | 8 +++----- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index e4aaee91201d..e7f5da0296f0 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -1338,28 +1338,15 @@ static int isp_enable_clocks(struct isp_device *isp) { int r; unsigned long rate; - int divisor; - - /* - * cam_mclk clock chain: - * dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk - * - * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are - * set to the same value. Hence the rate set for dpll4_m5 - * has to be twice of what is set on OMAP3430 to get - * the required value for cam_mclk - */ - divisor = isp->revision == ISP_REVISION_15_0 ? 1 : 2; r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_ICK]); if (r) { dev_err(isp->dev, "failed to enable cam_ick clock\n"); goto out_clk_enable_ick; } - r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK], - CM_CAM_MCLK_HZ/divisor); + r = clk_set_rate(isp->clock[ISP_CLK_CAM_MCLK], CM_CAM_MCLK_HZ); if (r) { - dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n"); + dev_err(isp->dev, "clk_set_rate for cam_mclk failed\n"); goto out_clk_enable_mclk; } r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_MCLK]); @@ -1401,7 +1388,6 @@ static void isp_disable_clocks(struct isp_device *isp) static const char *isp_clocks[] = { "cam_ick", "cam_mclk", - "dpll4_m5_ck", "csi2_96m_fck", "l3_ick", }; diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h index 517d348ce32b..c77e1f2ae5ca 100644 --- a/drivers/media/platform/omap3isp/isp.h +++ b/drivers/media/platform/omap3isp/isp.h @@ -147,7 +147,6 @@ struct isp_platform_callback { * @ref_count: Reference count for handling multiple ISP requests. * @cam_ick: Pointer to camera interface clock structure. * @cam_mclk: Pointer to camera functional clock structure. - * @dpll4_m5_ck: Pointer to DPLL4 M5 clock structure. * @csi2_fck: Pointer to camera CSI2 complexIO clock structure. * @l3_ick: Pointer to OMAP3 L3 bus interface clock. * @irq: Currently attached ISP ISR callbacks information structure. @@ -189,10 +188,9 @@ struct isp_device { u32 xclk_divisor[2]; /* Two clocks, a and b. */ #define ISP_CLK_CAM_ICK 0 #define ISP_CLK_CAM_MCLK 1 -#define ISP_CLK_DPLL4_M5_CK 2 -#define ISP_CLK_CSI2_FCK 3 -#define ISP_CLK_L3_ICK 4 - struct clk *clock[5]; +#define ISP_CLK_CSI2_FCK 2 +#define ISP_CLK_L3_ICK 3 + struct clk *clock[4]; /* ISP modules */ struct ispstat isp_af; From c00db2463978ffab59d731773aae1a4f4e11d78c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 22 Jan 2013 15:33:27 +0100 Subject: [PATCH 1115/4607] drm/i915: fixup sbi_read/write locking commit 09153000b8ca32a539a1207edebabd0d40b6c61b Author: Daniel Vetter Date: Wed Dec 12 14:06:44 2012 +0100 drm/i915: rework locking for intel_dpio|sbi_read|write reworked the locking around sbi_read/write functions for 3.8-fixes. But commit dde86e2db54545ef981b64805097a7b4c3156d6e Author: Paulo Zanoni Date: Sat Dec 1 12:04:25 2012 -0200 drm/i915: add lpt_init_pch_refcl Added new use-cases in the -next tree which has not been updated in the merge. Fix it up. Reported-by: Ben Widawsky Reviewed-by: Ben Widawsky Tested-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 305be115319e..886124a720e9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4873,6 +4873,8 @@ static void lpt_init_pch_refclk(struct drm_device *dev) if (!has_vga) return; + mutex_lock(&dev_priv->dpio_lock); + /* XXX: Rip out SDV support once Haswell ships for real. */ if (IS_HASWELL(dev) && (dev->pci_device & 0xFF00) == 0x0C00) is_sdv = true; @@ -5015,6 +5017,8 @@ static void lpt_init_pch_refclk(struct drm_device *dev) tmp = intel_sbi_read(dev_priv, SBI_DBUFF0, SBI_ICLK); tmp |= SBI_DBUFF0_ENABLE; intel_sbi_write(dev_priv, SBI_DBUFF0, tmp, SBI_ICLK); + + mutex_unlock(&dev_priv->dpio_lock); } /* From f167e302c6a1321ae9f4d3a24a6e5bac90a5c79d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 23 Jan 2013 13:22:22 -0200 Subject: [PATCH 1116/4607] [media] mb86a20s: don't use state before initializing it As reported by Feng's kbuild test: From: kbuild test robot Subject: drivers/media/dvb-frontends/mb86a20s.c:706 mb86a20s_attach() error: potential null dereference 'state'. (kzalloc returns null) Date: Wed, 23 Jan 2013 19:30:43 +0800 commit: f66d81b54dac26d4e601d4d7faca53f3bdc98427 [media] mb86a20s: convert it to use dev_info/dev_err/dev_dbg drivers/media/dvb-frontends/mb86a20s.c:706 mb86a20s_attach() error: potential null dereference 'state'. (kzalloc returns null) drivers/media/dvb-frontends/mb86a20s.c:706 mb86a20s_attach() error: we previously assumed 'state' could be null (see line 705) As, at mb86a20s_attach(), we have an i2c pointer, use it for all printk messages there, instead of state->i2c. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index c52ae2ea0bf9..4b3ffc418294 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -698,12 +698,12 @@ struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, struct mb86a20s_state *state; u8 rev; + dev_dbg(&i2c->dev, "%s called.\n", __func__); + /* allocate memory for the internal state */ state = kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL); - - dev_dbg(&state->i2c->dev, "%s called.\n", __func__); if (state == NULL) { - dev_err(&state->i2c->dev, + dev_err(&i2c->dev, "%s: unable to allocate memory for state\n", __func__); goto error; } @@ -721,10 +721,10 @@ struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, rev = mb86a20s_readreg(state, 0); if (rev == 0x13) { - dev_info(&state->i2c->dev, + dev_info(&i2c->dev, "Detected a Fujitsu mb86a20s frontend\n"); } else { - dev_dbg(&state->i2c->dev, + dev_dbg(&i2c->dev, "Frontend revision %d is unknown - aborting.\n", rev); goto error; From 9569793a79836320c33d400c686dcb78f886bdad Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 6 Jan 2013 12:22:06 -0300 Subject: [PATCH 1117/4607] [media] dvb: Add DVBv5 statistics properties The DVBv3 statistics parameters are limited on several ways: - It doesn't provide any way to indicate the used measure, so userspace need to guess how to calculate/use it; - Only a limited set of stats are supported; - Can't be called in a way to require them to be filled all at once (atomic reads from the hardware), with may cause troubles on interpreting them on userspace; - On some OFDM delivery systems, the carriers can be independently modulated, having different properties. Currently, there's no way to report per-layer stats. To address the above issues, adding a new DVBv5-based stats API. While here, correct inner code nomenclature on a few places. Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/dvb/dvbapi.xml | 2 +- .../DocBook/media/dvb/dvbproperty.xml | 180 +++++++++++++++++- Documentation/DocBook/media/dvb/frontend.xml | 2 +- include/uapi/linux/dvb/frontend.h | 79 +++++++- include/uapi/linux/dvb/version.h | 2 +- 5 files changed, 260 insertions(+), 5 deletions(-) diff --git a/Documentation/DocBook/media/dvb/dvbapi.xml b/Documentation/DocBook/media/dvb/dvbapi.xml index 757488b24f4f..0197bcc7842d 100644 --- a/Documentation/DocBook/media/dvb/dvbapi.xml +++ b/Documentation/DocBook/media/dvb/dvbapi.xml @@ -84,7 +84,7 @@ Added ISDB-T test originally written by Patrick Boettcher LINUX DVB API -Version 5.8 +Version 5.10 &sub-intro; diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml index 957e3acaae8e..4a5eaeed0b9e 100644 --- a/Documentation/DocBook/media/dvb/dvbproperty.xml +++ b/Documentation/DocBook/media/dvb/dvbproperty.xml @@ -7,14 +7,41 @@ the capability ioctls weren't implemented yet via the new way. The typical usage for the FE_GET_PROPERTY/FE_SET_PROPERTY API is to replace the ioctl's were the struct dvb_frontend_parameters were used. +
+DTV stats type + +struct dtv_stats { + __u8 scale; /* enum fecap_scale_params type */ + union { + __u64 uvalue; /* for counters and relative scales */ + __s64 svalue; /* for 1/1000 dB measures */ + }; +} __packed; + +
+
+DTV stats type + +#define MAX_DTV_STATS 4 + +struct dtv_fe_stats { + __u8 len; + struct dtv_stats stat[MAX_DTV_STATS]; +} __packed; + +
+
DTV property type /* Reserved fields should be set to 0 */ + struct dtv_property { __u32 cmd; + __u32 reserved[3]; union { __u32 data; + struct dtv_fe_stats st; struct { __u8 data[32]; __u32 len; @@ -440,7 +467,7 @@ typedef enum fe_delivery_system { <constant>DTV-ISDBT-LAYER*</constant> parameters ISDB-T channels can be coded hierarchically. As opposed to DVB-T in ISDB-T hierarchical layers can be decoded simultaneously. For that - reason a ISDB-T demodulator has 3 viterbi and 3 reed-solomon-decoders. + reason a ISDB-T demodulator has 3 Viterbi and 3 Reed-Solomon decoders. ISDB-T has 3 hierarchical layers which each can use a part of the available segments. The total number of segments over all layers has to 13 in ISDB-T. @@ -850,6 +877,147 @@ enum fe_interleaving { use the special macro LNA_AUTO to set LNA auto
+ +
+ Frontend statistics indicators + The values are returned via dtv_property.stat. + If the property is supported, dtv_property.stat.len is bigger than zero. + For most delivery systems, dtv_property.stat.len + will be 1 if the stats is supported, and the properties will + return a single value for each parameter. + It should be noticed, however, that new OFDM delivery systems + like ISDB can use different modulation types for each group of + carriers. On such standards, up to 3 groups of statistics can be + provided, and dtv_property.stat.len is updated + to reflect the "global" metrics, plus one metric per each carrier + group (called "layer" on ISDB). + So, in order to be consistent with other delivery systems, the first + value at dtv_property.stat.dtv_stats + array refers to the global metric. The other elements of the array + represent each layer, starting from layer A(index 1), + layer B (index 2) and so on. + The number of filled elements are stored at dtv_property.stat.len. + Each element of the dtv_property.stat.dtv_stats array consists on two elements: + + svalue or uvalue, where + svalue is for signed values of the measure (dB measures) + and uvalue is for unsigned values (counters, relative scale) + scale - Scale for the value. It can be: +
+ + FE_SCALE_NOT_AVAILABLE - The parameter is supported by the frontend, but it was not possible to collect it (could be a transitory or permanent condition) + FE_SCALE_DECIBEL - parameter is a signed value, measured in 1/1000 dB + FE_SCALE_RELATIVE - parameter is a unsigned value, where 0 means 0% and 65535 means 100%. + FE_SCALE_COUNTER - parameter is a unsigned value that counts the occurrence of an event, like bit error, block error, or lapsed time. + +
+
+
+
+ <constant>DTV_STAT_SIGNAL_STRENGTH</constant> + Indicates the signal strength level at the analog part of the tuner or of the demod. + Possible scales for this metric are: + + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_DECIBEL - signal strength is in 0.0001 dBm units, power measured in miliwatts. This value is generally negative. + FE_SCALE_RELATIVE - The frontend provides a 0% to 100% measurement for power (actually, 0 to 65535). + +
+
+ <constant>DTV_STAT_CNR</constant> + Indicates the Signal to Noise ratio for the main carrier. + Possible scales for this metric are: + + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_DECIBEL - Signal/Noise ratio is in 0.0001 dB units. + FE_SCALE_RELATIVE - The frontend provides a 0% to 100% measurement for Signal/Noise (actually, 0 to 65535). + +
+
+ <constant>DTV_STAT_PRE_ERROR_BIT_COUNT</constant> + Measures the number of bit errors before the forward error correction (FEC) on the inner coding block (before Viterbi, LDPC or other inner code). + This measure is taken during the same interval as DTV_STAT_PRE_TOTAL_BIT_COUNT. + In order to get the BER (Bit Error Rate) measurement, it should be divided by + DTV_STAT_PRE_TOTAL_BIT_COUNT. + This measurement is monotonically increased, as the frontend gets more bit count measurements. + The frontend may reset it when a channel/transponder is tuned. + Possible scales for this metric are: + + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of error bits counted before the inner coding. + +
+
+ <constant>DTV_STAT_PRE_TOTAL_BIT_COUNT</constant> + Measures the amount of bits received before the inner code block, during the same period as + DTV_STAT_PRE_ERROR_BIT_COUNT measurement was taken. + It should be noticed that this measurement can be smaller than the total amount of bits on the transport stream, + as the frontend may need to manually restart the measurement, loosing some data between each measurement interval. + This measurement is monotonically increased, as the frontend gets more bit count measurements. + The frontend may reset it when a channel/transponder is tuned. + Possible scales for this metric are: + + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of bits counted while measuring + DTV_STAT_PRE_ERROR_BIT_COUNT. + +
+
+ <constant>DTV_STAT_POST_ERROR_BIT_COUNT</constant> + Measures the number of bit errors after the forward error correction (FEC) done by inner code block (after Viterbi, LDPC or other inner code). + This measure is taken during the same interval as DTV_STAT_POST_TOTAL_BIT_COUNT. + In order to get the BER (Bit Error Rate) measurement, it should be divided by + DTV_STAT_POST_TOTAL_BIT_COUNT. + This measurement is monotonically increased, as the frontend gets more bit count measurements. + The frontend may reset it when a channel/transponder is tuned. + Possible scales for this metric are: + + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of error bits counted after the inner coding. + +
+
+ <constant>DTV_STAT_POST_TOTAL_BIT_COUNT</constant> + Measures the amount of bits received after the inner coding, during the same period as + DTV_STAT_POST_ERROR_BIT_COUNT measurement was taken. + It should be noticed that this measurement can be smaller than the total amount of bits on the transport stream, + as the frontend may need to manually restart the measurement, loosing some data between each measurement interval. + This measurement is monotonically increased, as the frontend gets more bit count measurements. + The frontend may reset it when a channel/transponder is tuned. + Possible scales for this metric are: + + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of bits counted while measuring + DTV_STAT_POST_ERROR_BIT_COUNT. + +
+
+ <constant>DTV_STAT_ERROR_BLOCK_COUNT</constant> + Measures the number of block errors after the outer forward error correction coding (after Reed-Solomon or other outer code). + This measurement is monotonically increased, as the frontend gets more bit count measurements. + The frontend may reset it when a channel/transponder is tuned. + Possible scales for this metric are: + + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of error blocks counted after the outer coding. + +
+
+ <constant>DTV-STAT_TOTAL_BLOCK_COUNT</constant> + Measures the total number of blocks received during the same period as + DTV_STAT_ERROR_BLOCK_COUNT measurement was taken. + It can be used to calculate the PER indicator, by dividing + DTV_STAT_ERROR_BLOCK_COUNT + by DTV-STAT-TOTAL-BLOCK-COUNT. + Possible scales for this metric are: + + FE_SCALE_NOT_AVAILABLE - it failed to measure it, or the measurement was not complete yet. + FE_SCALE_COUNTER - Number of blocks counted while measuring + DTV_STAT_ERROR_BLOCK_COUNT. + +
+
+
Properties used on terrestrial delivery systems
@@ -871,6 +1039,7 @@ enum fe_interleaving { DTV_HIERARCHY DTV_LNA + In addition, the DTV QoS statistics are also valid.
DVB-T2 delivery system @@ -895,6 +1064,7 @@ enum fe_interleaving { DTV_STREAM_ID DTV_LNA + In addition, the DTV QoS statistics are also valid.
ISDB-T delivery system @@ -948,6 +1118,7 @@ enum fe_interleaving { DTV_ISDBT_LAYERC_SEGMENT_COUNT DTV_ISDBT_LAYERC_TIME_INTERLEAVING + In addition, the DTV QoS statistics are also valid.
ATSC delivery system @@ -961,6 +1132,7 @@ enum fe_interleaving { DTV_MODULATION DTV_BANDWIDTH_HZ + In addition, the DTV QoS statistics are also valid.
ATSC-MH delivery system @@ -988,6 +1160,7 @@ enum fe_interleaving { DTV_ATSCMH_SCCC_CODE_MODE_C DTV_ATSCMH_SCCC_CODE_MODE_D + In addition, the DTV QoS statistics are also valid.
DTMB delivery system @@ -1007,6 +1180,7 @@ enum fe_interleaving { DTV_INTERLEAVING DTV_LNA + In addition, the DTV QoS statistics are also valid.
@@ -1028,6 +1202,7 @@ enum fe_interleaving { DTV_INNER_FEC DTV_LNA + In addition, the DTV QoS statistics are also valid.
DVB-C Annex B delivery system @@ -1043,6 +1218,7 @@ enum fe_interleaving { DTV_INVERSION DTV_LNA + In addition, the DTV QoS statistics are also valid.
@@ -1062,6 +1238,7 @@ enum fe_interleaving { DTV_VOLTAGE DTV_TONE + In addition, the DTV QoS statistics are also valid. Future implementations might add those two missing parameters: DTV_DISEQC_MASTER @@ -1077,6 +1254,7 @@ enum fe_interleaving { DTV_ROLLOFF DTV_STREAM_ID + In addition, the DTV QoS statistics are also valid.
Turbo code delivery system diff --git a/Documentation/DocBook/media/dvb/frontend.xml b/Documentation/DocBook/media/dvb/frontend.xml index 426c2526a454..df39ba395df0 100644 --- a/Documentation/DocBook/media/dvb/frontend.xml +++ b/Documentation/DocBook/media/dvb/frontend.xml @@ -230,7 +230,7 @@ typedef enum fe_status { The frontend has found a DVB signal FE_HAS_VITERBI -The frontend FEC code is stable +The frontend FEC inner coding (Viterbi, LDPC or other inner code) is stable FE_HAS_SYNC Syncronization bytes was found diff --git a/include/uapi/linux/dvb/frontend.h b/include/uapi/linux/dvb/frontend.h index c12d452cb40d..c56d77c496a5 100644 --- a/include/uapi/linux/dvb/frontend.h +++ b/include/uapi/linux/dvb/frontend.h @@ -365,7 +365,17 @@ struct dvb_frontend_event { #define DTV_INTERLEAVING 60 #define DTV_LNA 61 -#define DTV_MAX_COMMAND DTV_LNA +/* Quality parameters */ +#define DTV_STAT_SIGNAL_STRENGTH 62 +#define DTV_STAT_CNR 63 +#define DTV_STAT_PRE_ERROR_BIT_COUNT 64 +#define DTV_STAT_PRE_TOTAL_BIT_COUNT 65 +#define DTV_STAT_POST_ERROR_BIT_COUNT 66 +#define DTV_STAT_POST_TOTAL_BIT_COUNT 67 +#define DTV_STAT_ERROR_BLOCK_COUNT 68 +#define DTV_STAT_TOTAL_BLOCK_COUNT 69 + +#define DTV_MAX_COMMAND DTV_STAT_TOTAL_BLOCK_COUNT typedef enum fe_pilot { PILOT_ON, @@ -452,11 +462,78 @@ struct dtv_cmds_h { __u32 reserved:30; /* Align */ }; +/** + * Scale types for the quality parameters. + * @FE_SCALE_NOT_AVAILABLE: That QoS measure is not available. That + * could indicate a temporary or a permanent + * condition. + * @FE_SCALE_DECIBEL: The scale is measured in 0.0001 dB steps, typically + * used on signal measures. + * @FE_SCALE_RELATIVE: The scale is a relative percentual measure, + * ranging from 0 (0%) to 0xffff (100%). + * @FE_SCALE_COUNTER: The scale counts the occurrence of an event, like + * bit error, block error, lapsed time. + */ +enum fecap_scale_params { + FE_SCALE_NOT_AVAILABLE = 0, + FE_SCALE_DECIBEL, + FE_SCALE_RELATIVE, + FE_SCALE_COUNTER +}; + +/** + * struct dtv_stats - Used for reading a DTV status property + * + * @value: value of the measure. Should range from 0 to 0xffff; + * @scale: Filled with enum fecap_scale_params - the scale + * in usage for that parameter + * + * For most delivery systems, this will return a single value for each + * parameter. + * It should be noticed, however, that new OFDM delivery systems like + * ISDB can use different modulation types for each group of carriers. + * On such standards, up to 8 groups of statistics can be provided, one + * for each carrier group (called "layer" on ISDB). + * In order to be consistent with other delivery systems, the first + * value refers to the entire set of carriers ("global"). + * dtv_status:scale should use the value FE_SCALE_NOT_AVAILABLE when + * the value for the entire group of carriers or from one specific layer + * is not provided by the hardware. + * st.len should be filled with the latest filled status + 1. + * + * In other words, for ISDB, those values should be filled like: + * u.st.stat.svalue[0] = global statistics; + * u.st.stat.scale[0] = FE_SCALE_DECIBELS; + * u.st.stat.value[1] = layer A statistics; + * u.st.stat.scale[1] = FE_SCALE_NOT_AVAILABLE (if not available); + * u.st.stat.svalue[2] = layer B statistics; + * u.st.stat.scale[2] = FE_SCALE_DECIBELS; + * u.st.stat.svalue[3] = layer C statistics; + * u.st.stat.scale[3] = FE_SCALE_DECIBELS; + * u.st.len = 4; + */ +struct dtv_stats { + __u8 scale; /* enum fecap_scale_params type */ + union { + __u64 uvalue; /* for counters and relative scales */ + __s64 svalue; /* for 0.0001 dB measures */ + }; +} __attribute__ ((packed)); + + +#define MAX_DTV_STATS 4 + +struct dtv_fe_stats { + __u8 len; + struct dtv_stats stat[MAX_DTV_STATS]; +} __attribute__ ((packed)); + struct dtv_property { __u32 cmd; __u32 reserved[3]; union { __u32 data; + struct dtv_fe_stats st; struct { __u8 data[32]; __u32 len; diff --git a/include/uapi/linux/dvb/version.h b/include/uapi/linux/dvb/version.h index 827cce7e33e3..e53e2ad4444f 100644 --- a/include/uapi/linux/dvb/version.h +++ b/include/uapi/linux/dvb/version.h @@ -24,6 +24,6 @@ #define _DVBVERSION_H_ #define DVB_API_VERSION 5 -#define DVB_API_VERSION_MINOR 9 +#define DVB_API_VERSION_MINOR 10 #endif /*_DVBVERSION_H_*/ From 7cd4ece58f9b94372687de820c22cb2eae4a623e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 7 Jan 2013 15:41:35 -0300 Subject: [PATCH 1118/4607] [media] dvb: the core logic to handle the DVBv5 QoS properties Add the logic to poll, reset counters and report the QoS stats to the end user. The idea is that the core will periodically poll the frontend for the stats. The frontend may return -EBUSY, if the previous collect didn't finish, or it may fill the cached data. The value returned to the end user is always the cached data. Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 35 +++++++++++++++++++++++++++ drivers/media/dvb-core/dvb_frontend.h | 10 ++++++++ 2 files changed, 45 insertions(+) diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index dd35fa972067..0c6f936ffac8 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -1053,6 +1053,16 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = { _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0), _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0), _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0), + + /* Statistics API */ + _DTV_CMD(DTV_STAT_SIGNAL_STRENGTH, 0, 0), + _DTV_CMD(DTV_STAT_CNR, 0, 0), + _DTV_CMD(DTV_STAT_PRE_ERROR_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_PRE_TOTAL_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_POST_ERROR_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_POST_TOTAL_BIT_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_ERROR_BLOCK_COUNT, 0, 0), + _DTV_CMD(DTV_STAT_TOTAL_BLOCK_COUNT, 0, 0), }; static void dtv_property_dump(struct dvb_frontend *fe, struct dtv_property *tvp) @@ -1443,6 +1453,31 @@ static int dtv_property_process_get(struct dvb_frontend *fe, tvp->u.data = c->lna; break; + /* Fill quality measures */ + case DTV_STAT_SIGNAL_STRENGTH: + tvp->u.st = c->strength; + break; + case DTV_STAT_CNR: + tvp->u.st = c->cnr; + break; + case DTV_STAT_PRE_ERROR_BIT_COUNT: + tvp->u.st = c->pre_bit_error; + break; + case DTV_STAT_PRE_TOTAL_BIT_COUNT: + tvp->u.st = c->pre_bit_count; + break; + case DTV_STAT_POST_ERROR_BIT_COUNT: + tvp->u.st = c->post_bit_error; + break; + case DTV_STAT_POST_TOTAL_BIT_COUNT: + tvp->u.st = c->post_bit_count; + break; + case DTV_STAT_ERROR_BLOCK_COUNT: + tvp->u.st = c->block_error; + break; + case DTV_STAT_TOTAL_BLOCK_COUNT: + tvp->u.st = c->block_count; + break; default: return -EINVAL; } diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h index 97112cd88a17..b34922a08156 100644 --- a/drivers/media/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb-core/dvb_frontend.h @@ -393,6 +393,16 @@ struct dtv_frontend_properties { u8 atscmh_sccc_code_mode_d; u32 lna; + + /* statistics data */ + struct dtv_fe_stats strength; + struct dtv_fe_stats cnr; + struct dtv_fe_stats pre_bit_error; + struct dtv_fe_stats pre_bit_count; + struct dtv_fe_stats post_bit_error; + struct dtv_fe_stats post_bit_count; + struct dtv_fe_stats block_error; + struct dtv_fe_stats block_count; }; struct dvb_frontend { From 09b6d21e100a8dcda7cf5a32ecd52e8008094f72 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jan 2013 12:28:31 -0300 Subject: [PATCH 1119/4607] [media] mb86a20s: calculate statistics at .read_status() Instead of providing separate callbacks to read the several FE stats properties, the better seems to use just one method that will: - Read lock status; - Read signal strength; - if locked, get TMCC data; - if locked, get DVB statistics. As the DVB frontend thread will call this read_status callback on every 3 seconds, and userspace can even call it earlier, all stats data and layers layout will be updated together if available, with is a good thing. Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 267 +++++++++++++++++++++---- 1 file changed, 226 insertions(+), 41 deletions(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 4b3ffc418294..d7668e6292a0 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -27,6 +27,7 @@ MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); struct mb86a20s_state { struct i2c_adapter *i2c; const struct mb86a20s_config *config; + u32 last_frequency; struct dvb_frontend frontend; @@ -80,17 +81,26 @@ static struct regdata mb86a20s_init[] = { { 0x04, 0x13 }, { 0x05, 0xff }, { 0x04, 0x15 }, { 0x05, 0x4e }, { 0x04, 0x16 }, { 0x05, 0x20 }, - { 0x52, 0x01 }, - { 0x50, 0xa7 }, { 0x51, 0xff }, + + /* + * On this demod, when the bit count reaches the count below, + * it collects the bit error count. The bit counters are initialized + * to 65535 here. This warrants that all of them will be quickly + * calculated when device gets locked. As TMCC is parsed, the values + * can be adjusted later in the driver's code. + */ + { 0x52, 0x01 }, /* Turn on BER before Viterbi */ + { 0x50, 0xa7 }, { 0x51, 0x00 }, { 0x50, 0xa8 }, { 0x51, 0xff }, { 0x50, 0xa9 }, { 0x51, 0xff }, - { 0x50, 0xaa }, { 0x51, 0xff }, + { 0x50, 0xaa }, { 0x51, 0x00 }, { 0x50, 0xab }, { 0x51, 0xff }, { 0x50, 0xac }, { 0x51, 0xff }, - { 0x50, 0xad }, { 0x51, 0xff }, + { 0x50, 0xad }, { 0x51, 0x00 }, { 0x50, 0xae }, { 0x51, 0xff }, { 0x50, 0xaf }, { 0x51, 0xff }, - { 0x5e, 0x07 }, + + { 0x5e, 0x00 }, /* Turn off BER after Viterbi */ { 0x50, 0xdc }, { 0x51, 0x01 }, { 0x50, 0xdd }, { 0x51, 0xf4 }, { 0x50, 0xde }, { 0x51, 0x01 }, @@ -105,8 +115,8 @@ static struct regdata mb86a20s_init[] = { { 0x50, 0xb6 }, { 0x51, 0xff }, { 0x50, 0xb7 }, { 0x51, 0xff }, { 0x50, 0x50 }, { 0x51, 0x02 }, - { 0x50, 0x51 }, { 0x51, 0x04 }, - { 0x45, 0x04 }, + { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */ + { 0x45, 0x04 }, /* CN symbol 4 */ { 0x48, 0x04 }, { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ { 0x50, 0xd6 }, { 0x51, 0x1f }, @@ -163,12 +173,23 @@ static struct regdata mb86a20s_reset_reception[] = { { 0x08, 0x00 }, }; +static struct regdata mb86a20s_vber_reset[] = { + { 0x53, 0x00 }, /* VBER Counter reset */ + { 0x53, 0x07 }, +}; + +static struct regdata mb86a20s_per_reset[] = { + { 0x50, 0xb1 }, /* PER Counter reset */ + { 0x51, 0x07 }, + { 0x51, 0x00 }, +}; + /* * I2C read/write functions and macros */ static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, - u8 i2c_addr, int reg, int data) + u8 i2c_addr, u8 reg, u8 data) { u8 buf[] = { reg, data }; struct i2c_msg msg = { @@ -230,6 +251,12 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, mb86a20s_i2c_writeregdata(state, state->config->demod_address, \ regdata, ARRAY_SIZE(regdata)) +/* + * Ancillary internal routines (likely compiled inlined) + * + * The functions below assume that gateway lock has already obtained + */ + static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct mb86a20s_state *state = fe->demodulator_priv; @@ -262,42 +289,49 @@ static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) return 0; } -static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +static int mb86a20s_read_signal_strength(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; + int rc; unsigned rf_max, rf_min, rf; - u8 val; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); /* Does a binary search to get RF strength */ rf_max = 0xfff; rf_min = 0; do { rf = (rf_max + rf_min) / 2; - mb86a20s_writereg(state, 0x04, 0x1f); - mb86a20s_writereg(state, 0x05, rf >> 8); - mb86a20s_writereg(state, 0x04, 0x20); - mb86a20s_writereg(state, 0x04, rf); + rc = mb86a20s_writereg(state, 0x04, 0x1f); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x05, rf >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x04, 0x20); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x04, rf); + if (rc < 0) + return rc; - val = mb86a20s_readreg(state, 0x02); - if (val & 0x08) + rc = mb86a20s_readreg(state, 0x02); + if (rc < 0) + return rc; + if (rc & 0x08) rf_min = (rf_max + rf_min) / 2; else rf_max = (rf_max + rf_min) / 2; if (rf_max - rf_min < 4) { - *strength = (((rf_max + rf_min) / 2) * 65535) / 4095; + rf = (rf_max + rf_min) / 2; + + /* Rescale it from 2^12 (4096) to 2^16 */ + rf <<= (16 - 12); dev_dbg(&state->i2c->dev, "%s: signal strength = %d (%d < RF=%d < %d)\n", __func__, rf, rf_min, rf >> 4, rf_max); - break; + return rf; } } while (1); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - return 0; } @@ -462,9 +496,6 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) /* Reset frontend cache to default values */ mb86a20s_reset_frontend_cache(fe); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - /* Check for partial reception */ rc = mb86a20s_writereg(state, 0x6d, 0x85); if (rc < 0) @@ -550,15 +581,119 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) break; } } + return 0; noperlayer_error: - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + + /* per-layer info is incomplete; discard all per-layer */ + c->isdbt_layer_enabled = 0; return rc; - } +static int mb86a20s_reset_counters(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc, val; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Reset the counters, if the channel changed */ + if (state->last_frequency != c->frequency) { + memset(&c->strength, 0, sizeof(c->strength)); + memset(&c->cnr, 0, sizeof(c->cnr)); + memset(&c->pre_bit_error, 0, sizeof(c->pre_bit_error)); + memset(&c->pre_bit_count, 0, sizeof(c->pre_bit_count)); + memset(&c->block_error, 0, sizeof(c->block_error)); + memset(&c->block_count, 0, sizeof(c->block_count)); + + state->last_frequency = c->frequency; + } + + /* Clear status for most stats */ + + /* BER counter reset */ + rc = mb86a20s_writeregdata(state, mb86a20s_vber_reset); + if (rc < 0) + goto err; + + /* MER, PER counter reset */ + rc = mb86a20s_writeregdata(state, mb86a20s_per_reset); + if (rc < 0) + goto err; + + /* CNR counter reset */ + rc = mb86a20s_readreg(state, 0x45); + if (rc < 0) + goto err; + val = rc; + rc = mb86a20s_writereg(state, 0x45, val | 0x10); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x45, val & 0x6f); + if (rc < 0) + goto err; + + /* MER counter reset */ + rc = mb86a20s_writereg(state, 0x50, 0x50); + if (rc < 0) + goto err; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + goto err; + val = rc; + rc = mb86a20s_writereg(state, 0x51, val | 0x01); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x51, val & 0x06); + if (rc < 0) + goto err; + +err: + return rc; +} + +static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int i; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Fill the length of each status counter */ + + /* Only global stats */ + c->strength.len = 1; + + /* Per-layer stats - 3 layers + global */ + c->cnr.len = 4; + c->pre_bit_error.len = 4; + c->pre_bit_count.len = 4; + c->block_error.len = 4; + c->block_count.len = 4; + + /* Signal is always available */ + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = 0; + + /* Put all of them at FE_SCALE_NOT_AVAILABLE */ + for (i = 0; i < 4; i++) { + c->cnr.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_error.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + c->block_count.stat[i].scale = FE_SCALE_NOT_AVAILABLE; + } +} + + +/* + * The functions below are called via DVB callbacks, so they need to + * properly use the I2C gate control + */ + static int mb86a20s_initfe(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; @@ -637,30 +772,80 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); + mb86a20s_reset_counters(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); return rc; } -static int mb86a20s_read_status_gate(struct dvb_frontend *fe, - fe_status_t *status) +static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, + fe_status_t *status) { - int ret; + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc; - *status = 0; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); - ret = mb86a20s_read_status(fe, status); + /* Get lock */ + rc = mb86a20s_read_status(fe, status); + if (!(*status & FE_HAS_LOCK)) { + mb86a20s_stats_not_ready(fe); + mb86a20s_reset_frontend_cache(fe); + } + if (rc < 0) + goto error; + + /* Get signal strength */ + rc = mb86a20s_read_signal_strength(fe); + if (rc < 0) { + mb86a20s_stats_not_ready(fe); + mb86a20s_reset_frontend_cache(fe); + goto error; + } + /* Fill signal strength */ + c->strength.stat[0].uvalue = rc; + + if (*status & FE_HAS_LOCK) { + /* Get TMCC info*/ + rc = mb86a20s_get_frontend(fe); + if (rc < 0) + goto error; + } + + mb86a20s_stats_not_ready(fe); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - - return ret; +error: + return rc; } +static int mb86a20s_read_signal_strength_from_cache(struct dvb_frontend *fe, + u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + + *strength = c->strength.stat[0].uvalue; + + return 0; +} + +static int mb86a20s_get_frontend_dummy(struct dvb_frontend *fe) +{ + /* + * get_frontend is now handled together with other stats + * retrival, when read_status() is called, as some statistics + * will depend on the layers detection. + */ + return 0; +}; + static int mb86a20s_tune(struct dvb_frontend *fe, bool re_tune, unsigned int mode_flags, @@ -676,7 +861,7 @@ static int mb86a20s_tune(struct dvb_frontend *fe, rc = mb86a20s_set_frontend(fe); if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) - mb86a20s_read_status_gate(fe, status); + mb86a20s_read_status_and_stats(fe, status); return rc; } @@ -759,9 +944,9 @@ static struct dvb_frontend_ops mb86a20s_ops = { .init = mb86a20s_initfe, .set_frontend = mb86a20s_set_frontend, - .get_frontend = mb86a20s_get_frontend, - .read_status = mb86a20s_read_status_gate, - .read_signal_strength = mb86a20s_read_signal_strength, + .get_frontend = mb86a20s_get_frontend_dummy, + .read_status = mb86a20s_read_status_and_stats, + .read_signal_strength = mb86a20s_read_signal_strength_from_cache, .tune = mb86a20s_tune, }; From 149d518ad0fd0d566d4859a4d3f280ee33de8ed7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jan 2013 12:30:07 -0300 Subject: [PATCH 1120/4607] [media] mb86a20s: add BER measurement Add the methods to read bit error/bit count measurements from mb86a20s. On ISDB-T devices, those reads are done per layer. However, as userspace applications may not be aware of that, add a global measure that will sum the bit errors and bit counts for each layer, storing them into a global value. Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 183 ++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index d7668e6292a0..354ea664b713 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -650,10 +650,95 @@ static int mb86a20s_reset_counters(struct dvb_frontend *fe) if (rc < 0) goto err; + goto ok; err: + dev_err(&state->i2c->dev, + "%s: Can't reset FE statistics (error %d).\n", + __func__, rc); +ok: return rc; } +static int mb86a20s_get_ber_before_vterbi(struct dvb_frontend *fe, + unsigned layer, + u32 *error, u32 *count) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + int rc; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + if (layer >= 3) + return -EINVAL; + + /* Check if the BER measures are already available */ + rc = mb86a20s_readreg(state, 0x54); + if (rc < 0) + return rc; + + /* Check if data is available for that layer */ + if (!(rc & (1 << layer))) { + dev_dbg(&state->i2c->dev, + "%s: BER for layer %c is not available yet.\n", + __func__, 'A' + layer); + return -EBUSY; + } + + /* Read Bit Error Count */ + rc = mb86a20s_readreg(state, 0x55 + layer * 3); + if (rc < 0) + return rc; + *error = rc << 16; + rc = mb86a20s_readreg(state, 0x56 + layer * 3); + if (rc < 0) + return rc; + *error |= rc << 8; + rc = mb86a20s_readreg(state, 0x57 + layer * 3); + if (rc < 0) + return rc; + *error |= rc; + + dev_dbg(&state->i2c->dev, + "%s: bit error before Viterbi for layer %c: %d.\n", + __func__, 'A' + layer, *error); + + /* Read Bit Count */ + rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count = rc << 16; + rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count |= rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + *count |= rc; + + dev_dbg(&state->i2c->dev, + "%s: bit count before Viterbi for layer %c: %d.\n", + __func__, 'A' + layer, *count); + + + /* Reset counter to collect new data */ + rc = mb86a20s_writereg(state, 0x53, 0x07 & ~(1 << layer)); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x53, 0x07); + + return 0; +} + static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; @@ -688,6 +773,72 @@ static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) } } +static int mb86a20s_get_stats(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int rc = 0, i; + u32 bit_error = 0, bit_count = 0; + u32 t_pre_bit_error = 0, t_pre_bit_count = 0; + int active_layers = 0, ber_layers = 0; + + /* Get per-layer stats */ + for (i = 0; i < 3; i++) { + if (c->isdbt_layer_enabled & (1 << i)) { + /* Layer is active and has rc segments */ + active_layers++; + + /* Read per-layer BER */ + /* Handle BER before vterbi */ + rc = mb86a20s_get_ber_before_vterbi(fe, i, + &bit_error, + &bit_count); + if (rc >= 0) { + c->pre_bit_error.stat[1 + i].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[1 + i].uvalue += bit_error; + c->pre_bit_count.stat[1 + i].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[1 + i].uvalue += bit_count; + } else if (rc != -EBUSY) { + /* + * If an I/O error happened, + * measures are now unavailable + */ + c->pre_bit_error.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + c->pre_bit_count.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + dev_err(&state->i2c->dev, + "%s: Can't get BER for layer %c (error %d).\n", + __func__, 'A' + i, rc); + } + + if (c->block_error.stat[1 + i].scale != FE_SCALE_NOT_AVAILABLE) + ber_layers++; + + /* Update total BER */ + t_pre_bit_error += c->pre_bit_error.stat[1 + i].uvalue; + t_pre_bit_count += c->pre_bit_count.stat[1 + i].uvalue; + } + } + + /* + * Start showing global count if at least one error count is + * available. + */ + if (ber_layers) { + /* + * At least one per-layer BER measure was read. We can now + * calculate the total BER + * + * Total Bit Error/Count is calculated as the sum of the + * bit errors on all active layers. + */ + c->pre_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_error.stat[0].uvalue = t_pre_bit_error; + c->pre_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->pre_bit_count.stat[0].uvalue = t_pre_bit_count; + } + + return rc; +} /* * The functions below are called via DVB callbacks, so they need to @@ -797,14 +948,21 @@ static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, mb86a20s_stats_not_ready(fe); mb86a20s_reset_frontend_cache(fe); } - if (rc < 0) + if (rc < 0) { + dev_err(&state->i2c->dev, + "%s: Can't read frontend lock status\n", __func__); goto error; + } /* Get signal strength */ rc = mb86a20s_read_signal_strength(fe); if (rc < 0) { + dev_err(&state->i2c->dev, + "%s: Can't reset VBER registers.\n", __func__); mb86a20s_stats_not_ready(fe); mb86a20s_reset_frontend_cache(fe); + + rc = 0; /* Status is OK */ goto error; } /* Fill signal strength */ @@ -813,15 +971,32 @@ static int mb86a20s_read_status_and_stats(struct dvb_frontend *fe, if (*status & FE_HAS_LOCK) { /* Get TMCC info*/ rc = mb86a20s_get_frontend(fe); - if (rc < 0) + if (rc < 0) { + dev_err(&state->i2c->dev, + "%s: Can't get FE TMCC data.\n", __func__); + rc = 0; /* Status is OK */ goto error; - } + } + /* Get statistics */ + rc = mb86a20s_get_stats(fe); + if (rc < 0 && rc != -EBUSY) { + dev_err(&state->i2c->dev, + "%s: Can't get FE statistics.\n", __func__); + rc = 0; + goto error; + } + rc = 0; /* Don't return EBUSY to userspace */ + } + goto ok; + +error: mb86a20s_stats_not_ready(fe); +ok: if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); -error: + return rc; } From d01a8ee37afdeb1a00458a79854a1672ada5c9f0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Jan 2013 20:34:55 -0300 Subject: [PATCH 1121/4607] [media] mb86a20s: improve bit error count for BER Do a better job on setting the bit error counters, in order to have all layer measures to happen in a little less than one second. Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 160 ++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 354ea664b713..c68e4676e5fd 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -31,6 +31,8 @@ struct mb86a20s_state { struct dvb_frontend frontend; + u32 estimated_rate[3]; + bool need_init; }; @@ -39,6 +41,8 @@ struct regdata { u8 data; }; +#define BER_SAMPLING_RATE 1 /* Seconds */ + /* * Initialization sequence: Use whatevere default values that PV SBTVD * does on its initialisation, obtained via USB snoop @@ -87,7 +91,7 @@ static struct regdata mb86a20s_init[] = { * it collects the bit error count. The bit counters are initialized * to 65535 here. This warrants that all of them will be quickly * calculated when device gets locked. As TMCC is parsed, the values - * can be adjusted later in the driver's code. + * will be adjusted later in the driver's code. */ { 0x52, 0x01 }, /* Turn on BER before Viterbi */ { 0x50, 0xa7 }, { 0x51, 0x00 }, @@ -485,6 +489,113 @@ static void mb86a20s_reset_frontend_cache(struct dvb_frontend *fe) c->isdbt_sb_segment_count = 0; } +/* + * Estimates the bit rate using the per-segment bit rate given by + * ABNT/NBR 15601 spec (table 4). + */ +static u32 isdbt_rate[3][5][4] = { + { /* DQPSK/QPSK */ + { 280850, 312060, 330420, 340430 }, /* 1/2 */ + { 374470, 416080, 440560, 453910 }, /* 2/3 */ + { 421280, 468090, 495630, 510650 }, /* 3/4 */ + { 468090, 520100, 550700, 567390 }, /* 5/6 */ + { 491500, 546110, 578230, 595760 }, /* 7/8 */ + }, { /* QAM16 */ + { 561710, 624130, 660840, 680870 }, /* 1/2 */ + { 748950, 832170, 881120, 907820 }, /* 2/3 */ + { 842570, 936190, 991260, 1021300 }, /* 3/4 */ + { 936190, 1040210, 1101400, 1134780 }, /* 5/6 */ + { 983000, 1092220, 1156470, 1191520 }, /* 7/8 */ + }, { /* QAM64 */ + { 842570, 936190, 991260, 1021300 }, /* 1/2 */ + { 1123430, 1248260, 1321680, 1361740 }, /* 2/3 */ + { 1263860, 1404290, 1486900, 1531950 }, /* 3/4 */ + { 1404290, 1560320, 1652110, 1702170 }, /* 5/6 */ + { 1474500, 1638340, 1734710, 1787280 }, /* 7/8 */ + } +}; + +static void mb86a20s_layer_bitrate(struct dvb_frontend *fe, u32 layer, + u32 modulation, u32 fec, u32 interleaving, + u32 segment) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + u32 rate; + int m, f, i; + + /* + * If modulation/fec/interleaving is not detected, the default is + * to consider the lowest bit rate, to avoid taking too long time + * to get BER. + */ + switch (modulation) { + case DQPSK: + case QPSK: + default: + m = 0; + break; + case QAM_16: + m = 1; + break; + case QAM_64: + m = 2; + break; + } + + switch (fec) { + default: + case FEC_1_2: + case FEC_AUTO: + f = 0; + break; + case FEC_2_3: + f = 1; + break; + case FEC_3_4: + f = 2; + break; + case FEC_5_6: + f = 3; + break; + case FEC_7_8: + f = 4; + break; + } + + switch (interleaving) { + default: + case GUARD_INTERVAL_1_4: + i = 0; + break; + case GUARD_INTERVAL_1_8: + i = 1; + break; + case GUARD_INTERVAL_1_16: + i = 2; + break; + case GUARD_INTERVAL_1_32: + i = 3; + break; + } + + /* Samples BER at BER_SAMPLING_RATE seconds */ + rate = isdbt_rate[m][f][i] * segment * BER_SAMPLING_RATE; + + /* Avoids sampling too quickly or to overflow the register */ + if (rate < 256) + rate = 256; + else if (rate > (1 << 24) - 1) + rate = (1 << 24) - 1; + + dev_dbg(&state->i2c->dev, + "%s: layer %c bitrate: %d kbps; counter = %d (0x%06x)\n", + __func__, 'A' + layer, segment * isdbt_rate[m][f][i]/1000, + rate, rate); + + state->estimated_rate[i] = rate; +} + + static int mb86a20s_get_frontend(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; @@ -514,10 +625,11 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) rc = mb86a20s_get_segment_count(state, i); if (rc < 0) goto noperlayer_error; - if (rc >= 0 && rc < 14) + if (rc >= 0 && rc < 14) { c->layer[i].segment_count = rc; - else { + } else { c->layer[i].segment_count = 0; + state->estimated_rate[i] = 0; continue; } c->isdbt_layer_enabled |= 1 << i; @@ -539,6 +651,10 @@ static int mb86a20s_get_frontend(struct dvb_frontend *fe) dev_dbg(&state->i2c->dev, "%s: interleaving %d.\n", __func__, rc); c->layer[i].interleaving = rc; + mb86a20s_layer_bitrate(fe, i, c->layer[i].modulation, + c->layer[i].fec, + c->layer[i].interleaving, + c->layer[i].segment_count); } rc = mb86a20s_writereg(state, 0x6d, 0x84); @@ -730,6 +846,42 @@ static int mb86a20s_get_ber_before_vterbi(struct dvb_frontend *fe, __func__, 'A' + layer, *count); + /* + * As we get TMCC data from the frontend, we can better estimate the + * BER bit counters, in order to do the BER measure during a longer + * time. Use those data, if available, to update the bit count + * measure. + */ + + if (state->estimated_rate[layer] + && state->estimated_rate[layer] != *count) { + dev_dbg(&state->i2c->dev, + "%s: updating layer %c counter to %d.\n", + __func__, 'A' + layer, state->estimated_rate[layer]); + rc = mb86a20s_writereg(state, 0x50, 0xa7 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer] >> 16); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xa8 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer] >> 8); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x50, 0xa9 + layer * 3); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, + state->estimated_rate[layer]); + if (rc < 0) + return rc; + } + + /* Reset counter to collect new data */ rc = mb86a20s_writereg(state, 0x53, 0x07 & ~(1 << layer)); if (rc < 0) @@ -922,8 +1074,10 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); mb86a20s_reset_counters(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); From 25188bd0e66f244d8111c4459f2c2f262a13d272 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 16 Jan 2013 15:12:05 -0300 Subject: [PATCH 1122/4607] [media] mb86a20s: add CNR measurement Add Signal/Noise ratio measurement. On this device, a global measure is taken by the demod. It also provides per-layer CNR measurements, based on Modulation Error measures (MER). Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/mb86a20s.c | 336 ++++++++++++++++++++++++- 1 file changed, 334 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index c68e4676e5fd..4f3e222a2bcf 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -118,10 +118,12 @@ static struct regdata mb86a20s_init[] = { { 0x50, 0xb5 }, { 0x51, 0xff }, { 0x50, 0xb6 }, { 0x51, 0xff }, { 0x50, 0xb7 }, { 0x51, 0xff }, - { 0x50, 0x50 }, { 0x51, 0x02 }, + + { 0x50, 0x50 }, { 0x51, 0x02 }, /* MER manual mode */ { 0x50, 0x51 }, { 0x51, 0x04 }, /* MER symbol 4 */ { 0x45, 0x04 }, /* CN symbol 4 */ - { 0x48, 0x04 }, + { 0x48, 0x04 }, /* CN manual mode */ + { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ { 0x50, 0xd6 }, { 0x51, 0x1f }, { 0x50, 0xd2 }, { 0x51, 0x03 }, @@ -891,6 +893,330 @@ static int mb86a20s_get_ber_before_vterbi(struct dvb_frontend *fe, return 0; } +struct linear_segments { + unsigned x, y; +}; + +/* + * All tables below return a dB/1000 measurement + */ + +static struct linear_segments cnr_to_db_table[] = { + { 19648, 0}, + { 18187, 1000}, + { 16534, 2000}, + { 14823, 3000}, + { 13161, 4000}, + { 11622, 5000}, + { 10279, 6000}, + { 9089, 7000}, + { 8042, 8000}, + { 7137, 9000}, + { 6342, 10000}, + { 5641, 11000}, + { 5030, 12000}, + { 4474, 13000}, + { 3988, 14000}, + { 3556, 15000}, + { 3180, 16000}, + { 2841, 17000}, + { 2541, 18000}, + { 2276, 19000}, + { 2038, 20000}, + { 1800, 21000}, + { 1625, 22000}, + { 1462, 23000}, + { 1324, 24000}, + { 1175, 25000}, + { 1063, 26000}, + { 980, 27000}, + { 907, 28000}, + { 840, 29000}, + { 788, 30000}, +}; + +static struct linear_segments cnr_64qam_table[] = { + { 3922688, 0}, + { 3920384, 1000}, + { 3902720, 2000}, + { 3894784, 3000}, + { 3882496, 4000}, + { 3872768, 5000}, + { 3858944, 6000}, + { 3851520, 7000}, + { 3838976, 8000}, + { 3829248, 9000}, + { 3818240, 10000}, + { 3806976, 11000}, + { 3791872, 12000}, + { 3767040, 13000}, + { 3720960, 14000}, + { 3637504, 15000}, + { 3498496, 16000}, + { 3296000, 17000}, + { 3031040, 18000}, + { 2715392, 19000}, + { 2362624, 20000}, + { 1963264, 21000}, + { 1649664, 22000}, + { 1366784, 23000}, + { 1120768, 24000}, + { 890880, 25000}, + { 723456, 26000}, + { 612096, 27000}, + { 518912, 28000}, + { 448256, 29000}, + { 388864, 30000}, +}; + +static struct linear_segments cnr_16qam_table[] = { + { 5314816, 0}, + { 5219072, 1000}, + { 5118720, 2000}, + { 4998912, 3000}, + { 4875520, 4000}, + { 4736000, 5000}, + { 4604160, 6000}, + { 4458752, 7000}, + { 4300288, 8000}, + { 4092928, 9000}, + { 3836160, 10000}, + { 3521024, 11000}, + { 3155968, 12000}, + { 2756864, 13000}, + { 2347008, 14000}, + { 1955072, 15000}, + { 1593600, 16000}, + { 1297920, 17000}, + { 1043968, 18000}, + { 839680, 19000}, + { 672256, 20000}, + { 523008, 21000}, + { 424704, 22000}, + { 345088, 23000}, + { 280064, 24000}, + { 221440, 25000}, + { 179712, 26000}, + { 151040, 27000}, + { 128512, 28000}, + { 110080, 29000}, + { 95744, 30000}, +}; + +struct linear_segments cnr_qpsk_table[] = { + { 2834176, 0}, + { 2683648, 1000}, + { 2536960, 2000}, + { 2391808, 3000}, + { 2133248, 4000}, + { 1906176, 5000}, + { 1666560, 6000}, + { 1422080, 7000}, + { 1189632, 8000}, + { 976384, 9000}, + { 790272, 10000}, + { 633344, 11000}, + { 505600, 12000}, + { 402944, 13000}, + { 320768, 14000}, + { 255488, 15000}, + { 204032, 16000}, + { 163072, 17000}, + { 130304, 18000}, + { 105216, 19000}, + { 83456, 20000}, + { 65024, 21000}, + { 52480, 22000}, + { 42752, 23000}, + { 34560, 24000}, + { 27136, 25000}, + { 22016, 26000}, + { 18432, 27000}, + { 15616, 28000}, + { 13312, 29000}, + { 11520, 30000}, +}; + +static u32 interpolate_value(u32 value, struct linear_segments *segments, + unsigned len) +{ + u64 tmp64; + u32 dx, dy; + int i, ret; + + if (value >= segments[0].x) + return segments[0].y; + if (value < segments[len-1].x) + return segments[len-1].y; + + for (i = 1; i < len - 1; i++) { + /* If value is identical, no need to interpolate */ + if (value == segments[i].x) + return segments[i].y; + if (value > segments[i].x) + break; + } + + /* Linear interpolation between the two (x,y) points */ + dy = segments[i].y - segments[i - 1].y; + dx = segments[i - 1].x - segments[i].x; + tmp64 = value - segments[i].x; + tmp64 *= dy; + do_div(tmp64, dx); + ret = segments[i].y - tmp64; + + return ret; +} + +static int mb86a20s_get_main_CNR(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 cnr_linear, cnr; + int rc, val; + + /* Check if CNR is available */ + rc = mb86a20s_readreg(state, 0x45); + if (rc < 0) + return rc; + + if (!(rc & 0x40)) { + dev_info(&state->i2c->dev, "%s: CNR is not available yet.\n", + __func__); + return -EBUSY; + } + val = rc; + + rc = mb86a20s_readreg(state, 0x46); + if (rc < 0) + return rc; + cnr_linear = rc << 8; + + rc = mb86a20s_readreg(state, 0x46); + if (rc < 0) + return rc; + cnr_linear |= rc; + + cnr = interpolate_value(cnr_linear, + cnr_to_db_table, ARRAY_SIZE(cnr_to_db_table)); + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = cnr; + + dev_dbg(&state->i2c->dev, "%s: CNR is %d.%03d dB (%d)\n", + __func__, cnr / 1000, cnr % 1000, cnr_linear); + + /* CNR counter reset */ + rc = mb86a20s_writereg(state, 0x45, val | 0x10); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x45, val & 0x6f); + + return rc; +} + +static int mb86a20s_get_per_layer_CNR(struct dvb_frontend *fe) +{ + struct mb86a20s_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u32 mer, cnr; + int rc, val, i; + struct linear_segments *segs; + unsigned segs_len; + + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + /* Check if the measures are already available */ + rc = mb86a20s_writereg(state, 0x50, 0x5b); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + + /* Check if data is available */ + if (!(rc & 0x01)) { + dev_info(&state->i2c->dev, + "%s: MER measures aren't available yet.\n", __func__); + return -EBUSY; + } + + /* Read all layers */ + for (i = 0; i < 3; i++) { + if (!(c->isdbt_layer_enabled & (1 << i))) { + c->cnr.stat[1 + i].scale = FE_SCALE_NOT_AVAILABLE; + continue; + } + + rc = mb86a20s_writereg(state, 0x50, 0x52 + i * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer = rc << 16; + rc = mb86a20s_writereg(state, 0x50, 0x53 + i * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer |= rc << 8; + rc = mb86a20s_writereg(state, 0x50, 0x54 + i * 3); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + mer |= rc; + + switch (c->layer[i].modulation) { + case DQPSK: + case QPSK: + segs = cnr_qpsk_table; + segs_len = ARRAY_SIZE(cnr_qpsk_table); + break; + case QAM_16: + segs = cnr_16qam_table; + segs_len = ARRAY_SIZE(cnr_16qam_table); + break; + default: + case QAM_64: + segs = cnr_64qam_table; + segs_len = ARRAY_SIZE(cnr_64qam_table); + break; + } + cnr = interpolate_value(mer, segs, segs_len); + + c->cnr.stat[1 + i].scale = FE_SCALE_DECIBEL; + c->cnr.stat[1 + i].svalue = cnr; + + dev_dbg(&state->i2c->dev, + "%s: CNR for layer %c is %d.%03d dB (MER = %d).\n", + __func__, 'A' + i, cnr / 1000, cnr % 1000, mer); + + } + + /* Start a new MER measurement */ + /* MER counter reset */ + rc = mb86a20s_writereg(state, 0x50, 0x50); + if (rc < 0) + return rc; + rc = mb86a20s_readreg(state, 0x51); + if (rc < 0) + return rc; + val = rc; + + rc = mb86a20s_writereg(state, 0x51, val | 0x01); + if (rc < 0) + return rc; + rc = mb86a20s_writereg(state, 0x51, val & 0x06); + if (rc < 0) + return rc; + + return 0; +} + static void mb86a20s_stats_not_ready(struct dvb_frontend *fe) { struct mb86a20s_state *state = fe->demodulator_priv; @@ -934,7 +1260,13 @@ static int mb86a20s_get_stats(struct dvb_frontend *fe) u32 t_pre_bit_error = 0, t_pre_bit_count = 0; int active_layers = 0, ber_layers = 0; + dev_dbg(&state->i2c->dev, "%s called.\n", __func__); + + mb86a20s_get_main_CNR(fe); + /* Get per-layer stats */ + mb86a20s_get_per_layer_CNR(fe); + for (i = 0; i < 3; i++) { if (c->isdbt_layer_enabled & (1 << i)) { /* Layer is active and has rc segments */ From 94a93e5f85040114d6a77c085457b3943b6da889 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 23 Jan 2013 17:06:02 -0300 Subject: [PATCH 1123/4607] [media] dvb_frontend: print a msg if a property doesn't exist If userspace calls a property that doesn't exist, it currently just returns -EINVAL. However, this is more likely a problem at the userspace application, calling it with a non-existing property. So, add a debug message to help tracking it. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb_frontend.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 0c6f936ffac8..b059abf572d6 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -1479,6 +1479,9 @@ static int dtv_property_process_get(struct dvb_frontend *fe, tvp->u.st = c->block_count; break; default: + dev_dbg(fe->dvb->device, + "%s: FE property %d doesn't exist\n", + __func__, tvp->cmd); return -EINVAL; } From 84822d0b3bc5a74a4290727dd1ab4fc7dcd6a348 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 14 Dec 2012 17:57:50 -0500 Subject: [PATCH 1124/4607] nfsd4: simplify nfsd4_encode_fattr interface slightly It seems slightly simpler to make nfsd4_encode_fattr rather than its callers responsible for advancing the write pointer on success. (Also: the count == 0 check in the verify case looks superfluous. Running out of buffer space is really the only reason fattr encoding should fail with eresource.) Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4proc.c | 7 ++++--- fs/nfsd/nfs4xdr.c | 21 ++++++++------------- fs/nfsd/xdr4.h | 2 +- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 9d1c5dba2bbb..ae73175e6e68 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -993,14 +993,15 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (!buf) return nfserr_jukebox; + p = buf; status = nfsd4_encode_fattr(&cstate->current_fh, cstate->current_fh.fh_export, - cstate->current_fh.fh_dentry, buf, - &count, verify->ve_bmval, + cstate->current_fh.fh_dentry, &p, + count, verify->ve_bmval, rqstp, 0); /* this means that nfsd4_encode_fattr() ran out of space */ - if (status == nfserr_resource && count == 0) + if (status == nfserr_resource) status = nfserr_not_same; if (status) goto out_kfree; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 0dc11586682f..fcb5bed99c33 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -2006,12 +2006,11 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat) * Note: @fhp can be NULL; in this case, we might have to compose the filehandle * ourselves. * - * @countp is the buffer size in _words_; upon successful return this becomes - * replaced with the number of words written. + * countp is the buffer size in _words_ */ __be32 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, - struct dentry *dentry, __be32 *buffer, int *countp, u32 *bmval, + struct dentry *dentry, __be32 **buffer, int count, u32 *bmval, struct svc_rqst *rqstp, int ignore_crossmnt) { u32 bmval0 = bmval[0]; @@ -2020,12 +2019,12 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, struct kstat stat; struct svc_fh tempfh; struct kstatfs statfs; - int buflen = *countp << 2; + int buflen = count << 2; __be32 *attrlenp; u32 dummy; u64 dummy64; u32 rdattr_err = 0; - __be32 *p = buffer; + __be32 *p = *buffer; __be32 status; int err; int aclsupport = 0; @@ -2431,7 +2430,7 @@ out_acl: } *attrlenp = htonl((char *)p - (char *)attrlenp - 4); - *countp = p - buffer; + *buffer = p; status = nfs_ok; out: @@ -2443,7 +2442,6 @@ out_nfserr: status = nfserrno(err); goto out; out_resource: - *countp = 0; status = nfserr_resource; goto out; out_serverfault: @@ -2462,7 +2460,7 @@ static inline int attributes_need_mount(u32 *bmval) static __be32 nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, - const char *name, int namlen, __be32 *p, int *buflen) + const char *name, int namlen, __be32 **p, int buflen) { struct svc_export *exp = cd->rd_fhp->fh_export; struct dentry *dentry; @@ -2568,10 +2566,9 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ p = xdr_encode_array(p, name, namlen); /* name length & name */ - nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, p, &buflen); + nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, &p, buflen); switch (nfserr) { case nfs_ok: - p += buflen; break; case nfserr_resource: nfserr = nfserr_toosmall; @@ -2698,10 +2695,8 @@ nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >> 2); nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry, - resp->p, &buflen, getattr->ga_bmval, + &resp->p, buflen, getattr->ga_bmval, resp->rqstp, 0); - if (!nfserr) - resp->p += buflen; return nfserr; } diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 0889bfb43dc9..546f8983ecf1 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -563,7 +563,7 @@ __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *, u32); void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *); void nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op); __be32 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, - struct dentry *dentry, __be32 *buffer, int *countp, + struct dentry *dentry, __be32 **buffer, int countp, u32 *bmval, struct svc_rqst *, int ignore_crossmnt); extern __be32 nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *, From 74b70dded311fa0e6e7529514b29bbb8e6bb1f3e Mon Sep 17 00:00:00 2001 From: Yanchuan Nian Date: Mon, 24 Dec 2012 18:11:27 +0800 Subject: [PATCH 1125/4607] nfsd: Pass correct slot number to nfsd4_put_drc_mem() In alloc_session(), numslots is the correct slot number used by the session. But the slot number passed to nfsd4_put_drc_mem() is the one from nfs client. Signed-off-by: Yanchuan Nian Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ac8ed96c4199..29924a04cf3d 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -905,7 +905,7 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan, new = __alloc_session(slotsize, numslots); if (!new) { - nfsd4_put_drc_mem(slotsize, fchan->maxreqs); + nfsd4_put_drc_mem(slotsize, numslots); return NULL; } init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize, nn); From 266533c6df7a4a4e2ebd0bfdd272f7eb7cf4b81f Mon Sep 17 00:00:00 2001 From: Yanchuan Nian Date: Mon, 24 Dec 2012 18:11:45 +0800 Subject: [PATCH 1126/4607] nfsd: Don't unlock the state while it's not locked In the procedure of CREATE_SESSION, the state is locked after alloc_conn_from_crses(). If the allocation fails, the function goes to "out_free_session", and then "out" where there is an unlock function. Signed-off-by: Yanchuan Nian Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 29924a04cf3d..cc41bf4bcab2 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1844,11 +1844,12 @@ nfsd4_create_session(struct svc_rqst *rqstp, /* cache solo and embedded create sessions under the state lock */ nfsd4_cache_create_session(cr_ses, cs_slot, status); -out: nfs4_unlock_state(); +out: dprintk("%s returns %d\n", __func__, ntohl(status)); return status; out_free_conn: + nfs4_unlock_state(); free_conn(conn); out_free_session: __free_session(new); From ec1686761753ead775e9474a6265323a4c555bc6 Mon Sep 17 00:00:00 2001 From: Yanchuan Nian Date: Fri, 4 Jan 2013 19:45:35 +0800 Subject: [PATCH 1127/4607] nfsd: Remove write permission from file content The write function doesn't be implemented in file content, and it's meaningless to write data into this file directly. Remove write permission from it. Signed-off-by: Yanchuan Nian Signed-off-by: J. Bruce Fields --- net/sunrpc/cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 9afa4393c217..9f8470353362 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -1614,7 +1614,7 @@ static int create_cache_proc_entries(struct cache_detail *cd, struct net *net) goto out_nomem; } if (cd->cache_show) { - p = proc_create_data("content", S_IFREG|S_IRUSR|S_IWUSR, + p = proc_create_data("content", S_IFREG|S_IRUSR, cd->u.procfs.proc_ent, &content_file_operations_procfs, cd); cd->u.procfs.content_ent = p; From 624ab4644819948e9dc87c114201e98f2e52490f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 8 Jan 2013 16:22:15 -0500 Subject: [PATCH 1128/4607] svcrpc: silence "unused variable" warning in !RPC_DEBUG case Signed-off-by: J. Bruce Fields --- net/sunrpc/svc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index dbf12ac5ecb7..b9ba2a8c1c19 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -1042,6 +1042,7 @@ static void svc_unregister(const struct svc_serv *serv, struct net *net) /* * dprintk the given error with the address of the client that caused it. */ +#ifdef RPC_DEBUG static __printf(2, 3) void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) { @@ -1058,6 +1059,9 @@ void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) va_end(args); } +#else +static __printf(2,3) void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) {} +#endif /* * Common routine for processing the RPC request. From 35525b79786b2ba58ef13822198ce22c497bc7a2 Mon Sep 17 00:00:00 2001 From: Andriy Skulysh Date: Mon, 7 Jan 2013 00:12:15 +0200 Subject: [PATCH 1129/4607] sunrpc: Fix lockd sleeping until timeout There is a race in enqueueing thread to a pool and waking up a thread. lockd doesn't wake up on reception of lock granted callback if svc_wake_up() is called before lockd's thread is added to a pool. Signed-off-by: Andriy Skulysh Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc.h | 1 + net/sunrpc/svc_xprt.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 676ddf53b3ee..1f0216b9a6c9 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -50,6 +50,7 @@ struct svc_pool { unsigned int sp_nrthreads; /* # of threads in pool */ struct list_head sp_all_threads; /* all server threads */ struct svc_pool_stats sp_stats; /* statistics on pool operation */ + int sp_task_pending;/* has pending task */ } ____cacheline_aligned_in_smp; /* diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index b8e47fac7315..5a9d40c5a9f3 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -499,7 +499,8 @@ void svc_wake_up(struct svc_serv *serv) rqstp->rq_xprt = NULL; */ wake_up(&rqstp->rq_wait); - } + } else + pool->sp_task_pending = 1; spin_unlock_bh(&pool->sp_lock); } } @@ -634,7 +635,13 @@ struct svc_xprt *svc_get_next_xprt(struct svc_rqst *rqstp, long timeout) * long for cache updates. */ rqstp->rq_chandle.thread_wait = 1*HZ; + pool->sp_task_pending = 0; } else { + if (pool->sp_task_pending) { + pool->sp_task_pending = 0; + spin_unlock_bh(&pool->sp_lock); + return ERR_PTR(-EAGAIN); + } /* No data pending. Go to sleep */ svc_thread_enqueue(pool, rqstp); From bca0ec6511bb96bcb6cb247fd4100a4ea1d1e4f5 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 9 Jan 2013 12:38:34 +0300 Subject: [PATCH 1130/4607] nfsd: fix unused "nn" variable warning in free_client() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_LOCKDEP is disabled, then there would be a warning like this: CC [M] fs/nfsd/nfs4state.o fs/nfsd/nfs4state.c: In function ‘free_client’: fs/nfsd/nfs4state.c:1051:19: warning: unused variable ‘nn’ [-Wunused-variable] So, let's add "maybe_unused" tag to this variable. Reported-by: Toralf Förster Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cc41bf4bcab2..4db46aa387b7 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1048,7 +1048,7 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) static inline void free_client(struct nfs4_client *clp) { - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); + struct nfsd_net __maybe_unused *nn = net_generic(clp->net, nfsd_net_id); lockdep_assert_held(&nn->client_lock); while (!list_empty(&clp->cl_sessions)) { From ff89be87c70247ffe3a72271e02eb7765cdd12c4 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 23 Jan 2013 18:25:01 -0500 Subject: [PATCH 1131/4607] nfsd4: require version 4 when enabling or disabling minorversion The current code will allow silly things like: echo "+2 +3 +4 +7.1">/proc/fs/nfsd/versions Reported-by: Fan Chaoting Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 74934284d9a7..65889ec1d28e 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -534,7 +534,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) else num = simple_strtol(vers, &minorp, 0); if (*minorp == '.') { - if (num < 4) + if (num != 4) return -EINVAL; minor = simple_strtoul(minorp+1, NULL, 0); if (minor == 0) From 0bdea06892e33afddbdc5da6df305e9fe9c41365 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 19 Jan 2013 19:51:50 +0200 Subject: [PATCH 1132/4607] KVM: x86 emulator: Convert SHLD, SHRD to fastop Reviewed-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 619a33d0ee0a..a21773f22107 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -454,6 +454,8 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt) #define FOP_END \ ".popsection") +#define FOPNOP() FOP_ALIGN FOP_RET + #define FOP1E(op, dst) \ FOP_ALIGN #op " %" #dst " \n\t" FOP_RET @@ -476,6 +478,18 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt) ON64(FOP2E(op##q, rax, rbx)) \ FOP_END +#define FOP3E(op, dst, src, src2) \ + FOP_ALIGN #op " %" #src2 ", %" #src ", %" #dst " \n\t" FOP_RET + +/* 3-operand, word-only, src2=cl */ +#define FASTOP3WCL(op) \ + FOP_START(op) \ + FOPNOP() \ + FOP3E(op##w, ax, bx, cl) \ + FOP3E(op##l, eax, ebx, cl) \ + ON64(FOP3E(op##q, rax, rbx, cl)) \ + FOP_END + #define __emulate_1op_rax_rdx(ctxt, _op, _suffix, _ex) \ do { \ unsigned long _tmp; \ @@ -3036,6 +3050,9 @@ FASTOP2(xor); FASTOP2(cmp); FASTOP2(test); +FASTOP3WCL(shld); +FASTOP3WCL(shrd); + static int em_xchg(struct x86_emulate_ctxt *ctxt) { /* Write back the register source. */ @@ -4015,14 +4032,14 @@ static const struct opcode twobyte_table[256] = { /* 0xA0 - 0xA7 */ I(Stack | Src2FS, em_push_sreg), I(Stack | Src2FS, em_pop_sreg), II(ImplicitOps, em_cpuid, cpuid), I(DstMem | SrcReg | ModRM | BitOp, em_bt), - D(DstMem | SrcReg | Src2ImmByte | ModRM), - D(DstMem | SrcReg | Src2CL | ModRM), N, N, + F(DstMem | SrcReg | Src2ImmByte | ModRM, em_shld), + F(DstMem | SrcReg | Src2CL | ModRM, em_shld), N, N, /* 0xA8 - 0xAF */ I(Stack | Src2GS, em_push_sreg), I(Stack | Src2GS, em_pop_sreg), DI(ImplicitOps, rsm), I(DstMem | SrcReg | ModRM | BitOp | Lock | PageTable, em_bts), - D(DstMem | SrcReg | Src2ImmByte | ModRM), - D(DstMem | SrcReg | Src2CL | ModRM), + F(DstMem | SrcReg | Src2ImmByte | ModRM, em_shrd), + F(DstMem | SrcReg | Src2CL | ModRM, em_shrd), D(ModRM), I(DstReg | SrcMem | ModRM, em_imul), /* 0xB0 - 0xB7 */ I2bv(DstMem | SrcReg | ModRM | Lock | PageTable, em_cmpxchg), @@ -4834,14 +4851,6 @@ twobyte_insn: case 0x90 ... 0x9f: /* setcc r/m8 */ ctxt->dst.val = test_cc(ctxt->b, ctxt->eflags); break; - case 0xa4: /* shld imm8, r, r/m */ - case 0xa5: /* shld cl, r, r/m */ - emulate_2op_cl(ctxt, "shld"); - break; - case 0xac: /* shrd imm8, r, r/m */ - case 0xad: /* shrd cl, r, r/m */ - emulate_2op_cl(ctxt, "shrd"); - break; case 0xae: /* clflush */ break; case 0xb6 ... 0xb7: /* movzx */ From 007a3b547512d69f67ceb9641796d64552bd337e Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 19 Jan 2013 19:51:51 +0200 Subject: [PATCH 1133/4607] KVM: x86 emulator: convert shift/rotate instructions to fastop SHL, SHR, ROL, ROR, RCL, RCR, SAR, SAL Reviewed-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 72 ++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index a21773f22107..a94b1d76f799 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -478,6 +478,15 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt) ON64(FOP2E(op##q, rax, rbx)) \ FOP_END +/* 2 operand, src is CL */ +#define FASTOP2CL(op) \ + FOP_START(op) \ + FOP2E(op##b, al, cl) \ + FOP2E(op##w, ax, cl) \ + FOP2E(op##l, eax, cl) \ + ON64(FOP2E(op##q, rax, cl)) \ + FOP_END + #define FOP3E(op, dst, src, src2) \ FOP_ALIGN #op " %" #src2 ", %" #src ", %" #dst " \n\t" FOP_RET @@ -2046,38 +2055,17 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } -static int em_grp2(struct x86_emulate_ctxt *ctxt) -{ - switch (ctxt->modrm_reg) { - case 0: /* rol */ - emulate_2op_SrcB(ctxt, "rol"); - break; - case 1: /* ror */ - emulate_2op_SrcB(ctxt, "ror"); - break; - case 2: /* rcl */ - emulate_2op_SrcB(ctxt, "rcl"); - break; - case 3: /* rcr */ - emulate_2op_SrcB(ctxt, "rcr"); - break; - case 4: /* sal/shl */ - case 6: /* sal/shl */ - emulate_2op_SrcB(ctxt, "sal"); - break; - case 5: /* shr */ - emulate_2op_SrcB(ctxt, "shr"); - break; - case 7: /* sar */ - emulate_2op_SrcB(ctxt, "sar"); - break; - } - return X86EMUL_CONTINUE; -} - FASTOP1(not); FASTOP1(neg); +FASTOP2CL(rol); +FASTOP2CL(ror); +FASTOP2CL(rcl); +FASTOP2CL(rcr); +FASTOP2CL(shl); +FASTOP2CL(shr); +FASTOP2CL(sar); + static int em_mul_ex(struct x86_emulate_ctxt *ctxt) { u8 ex = 0; @@ -3726,6 +3714,17 @@ static const struct opcode group1A[] = { I(DstMem | SrcNone | Mov | Stack, em_pop), N, N, N, N, N, N, N, }; +static const struct opcode group2[] = { + F(DstMem | ModRM, em_rol), + F(DstMem | ModRM, em_ror), + F(DstMem | ModRM, em_rcl), + F(DstMem | ModRM, em_rcr), + F(DstMem | ModRM, em_shl), + F(DstMem | ModRM, em_shr), + F(DstMem | ModRM, em_shl), + F(DstMem | ModRM, em_sar), +}; + static const struct opcode group3[] = { F(DstMem | SrcImm | NoWrite, em_test), F(DstMem | SrcImm | NoWrite, em_test), @@ -3949,7 +3948,7 @@ static const struct opcode opcode_table[256] = { /* 0xB8 - 0xBF */ X8(I(DstReg | SrcImm64 | Mov, em_mov)), /* 0xC0 - 0xC7 */ - D2bv(DstMem | SrcImmByte | ModRM), + G(ByteOp | Src2ImmByte, group2), G(Src2ImmByte, group2), I(ImplicitOps | Stack | SrcImmU16, em_ret_near_imm), I(ImplicitOps | Stack, em_ret), I(DstReg | SrcMemFAddr | ModRM | No64 | Src2ES, em_lseg), @@ -3961,7 +3960,8 @@ static const struct opcode opcode_table[256] = { D(ImplicitOps), DI(SrcImmByte, intn), D(ImplicitOps | No64), II(ImplicitOps, em_iret, iret), /* 0xD0 - 0xD7 */ - D2bv(DstMem | SrcOne | ModRM), D2bv(DstMem | ModRM), + G(Src2One | ByteOp, group2), G(Src2One, group2), + G(Src2CL | ByteOp, group2), G(Src2CL, group2), N, I(DstAcc | SrcImmByte | No64, em_aad), N, N, /* 0xD8 - 0xDF */ N, E(0, &escape_d9), N, E(0, &escape_db), N, E(0, &escape_dd), N, N, @@ -4713,9 +4713,6 @@ special_insn: case 8: ctxt->dst.val = (s32)ctxt->dst.val; break; } break; - case 0xc0 ... 0xc1: - rc = em_grp2(ctxt); - break; case 0xcc: /* int3 */ rc = emulate_int(ctxt, 3); break; @@ -4726,13 +4723,6 @@ special_insn: if (ctxt->eflags & EFLG_OF) rc = emulate_int(ctxt, 4); break; - case 0xd0 ... 0xd1: /* Grp2 */ - rc = em_grp2(ctxt); - break; - case 0xd2 ... 0xd3: /* Grp2 */ - ctxt->src.val = reg_read(ctxt, VCPU_REGS_RCX); - rc = em_grp2(ctxt); - break; case 0xe9: /* jmp rel */ case 0xeb: /* jmp rel short */ jmp_rel(ctxt, ctxt->src.val); From 9ae9febae9500a0a6f5ce29ee4b8d942b5332529 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 19 Jan 2013 19:51:52 +0200 Subject: [PATCH 1134/4607] KVM: x86 emulator: covert SETCC to fastop This is a bit of a special case since we don't have the usual byte/word/long/quad switch; instead we switch on the condition code embedded in the instruction. Reviewed-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 60 ++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index a94b1d76f799..e13138dd073e 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -499,6 +499,28 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt) ON64(FOP3E(op##q, rax, rbx, cl)) \ FOP_END +/* Special case for SETcc - 1 instruction per cc */ +#define FOP_SETCC(op) ".align 4; " #op " %al; ret \n\t" + +FOP_START(setcc) +FOP_SETCC(seto) +FOP_SETCC(setno) +FOP_SETCC(setc) +FOP_SETCC(setnc) +FOP_SETCC(setz) +FOP_SETCC(setnz) +FOP_SETCC(setbe) +FOP_SETCC(setnbe) +FOP_SETCC(sets) +FOP_SETCC(setns) +FOP_SETCC(setp) +FOP_SETCC(setnp) +FOP_SETCC(setl) +FOP_SETCC(setnl) +FOP_SETCC(setle) +FOP_SETCC(setnle) +FOP_END; + #define __emulate_1op_rax_rdx(ctxt, _op, _suffix, _ex) \ do { \ unsigned long _tmp; \ @@ -939,39 +961,15 @@ static int read_descriptor(struct x86_emulate_ctxt *ctxt, return rc; } -static int test_cc(unsigned int condition, unsigned int flags) +static u8 test_cc(unsigned int condition, unsigned long flags) { - int rc = 0; + u8 rc; + void (*fop)(void) = (void *)em_setcc + 4 * (condition & 0xf); - switch ((condition & 15) >> 1) { - case 0: /* o */ - rc |= (flags & EFLG_OF); - break; - case 1: /* b/c/nae */ - rc |= (flags & EFLG_CF); - break; - case 2: /* z/e */ - rc |= (flags & EFLG_ZF); - break; - case 3: /* be/na */ - rc |= (flags & (EFLG_CF|EFLG_ZF)); - break; - case 4: /* s */ - rc |= (flags & EFLG_SF); - break; - case 5: /* p/pe */ - rc |= (flags & EFLG_PF); - break; - case 7: /* le/ng */ - rc |= (flags & EFLG_ZF); - /* fall through */ - case 6: /* l/nge */ - rc |= (!(flags & EFLG_SF) != !(flags & EFLG_OF)); - break; - } - - /* Odd condition identifiers (lsb == 1) have inverted sense. */ - return (!!rc ^ (condition & 1)); + flags = (flags & EFLAGS_MASK) | X86_EFLAGS_IF; + asm("pushq %[flags]; popf; call *%[fastop]" + : "=a"(rc) : [fastop]"r"(fop), [flags]"r"(flags)); + return rc; } static void fetch_register_operand(struct operand *op) From 95413dc41398fec2518abf4e0449503b1306dcbc Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 19 Jan 2013 19:51:53 +0200 Subject: [PATCH 1135/4607] KVM: x86 emulator: convert INC/DEC to fastop Reviewed-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index e13138dd073e..edb09e9c111c 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -2055,6 +2055,8 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt) FASTOP1(not); FASTOP1(neg); +FASTOP1(inc); +FASTOP1(dec); FASTOP2CL(rol); FASTOP2CL(ror); @@ -2105,12 +2107,6 @@ static int em_grp45(struct x86_emulate_ctxt *ctxt) int rc = X86EMUL_CONTINUE; switch (ctxt->modrm_reg) { - case 0: /* inc */ - emulate_1op(ctxt, "inc"); - break; - case 1: /* dec */ - emulate_1op(ctxt, "dec"); - break; case 2: /* call near abs */ { long int old_eip; old_eip = ctxt->_eip; @@ -3735,14 +3731,14 @@ static const struct opcode group3[] = { }; static const struct opcode group4[] = { - I(ByteOp | DstMem | SrcNone | Lock, em_grp45), - I(ByteOp | DstMem | SrcNone | Lock, em_grp45), + F(ByteOp | DstMem | SrcNone | Lock, em_inc), + F(ByteOp | DstMem | SrcNone | Lock, em_dec), N, N, N, N, N, N, }; static const struct opcode group5[] = { - I(DstMem | SrcNone | Lock, em_grp45), - I(DstMem | SrcNone | Lock, em_grp45), + F(DstMem | SrcNone | Lock, em_inc), + F(DstMem | SrcNone | Lock, em_dec), I(SrcMem | Stack, em_grp45), I(SrcMemFAddr | ImplicitOps | Stack, em_call_far), I(SrcMem | Stack, em_grp45), @@ -3891,7 +3887,7 @@ static const struct opcode opcode_table[256] = { /* 0x38 - 0x3F */ F6ALU(NoWrite, em_cmp), N, N, /* 0x40 - 0x4F */ - X16(D(DstReg)), + X8(F(DstReg, em_inc)), X8(F(DstReg, em_dec)), /* 0x50 - 0x57 */ X8(I(SrcReg | Stack, em_push)), /* 0x58 - 0x5F */ @@ -4681,12 +4677,6 @@ special_insn: goto twobyte_insn; switch (ctxt->b) { - case 0x40 ... 0x47: /* inc r16/r32 */ - emulate_1op(ctxt, "inc"); - break; - case 0x48 ... 0x4f: /* dec r16/r32 */ - emulate_1op(ctxt, "dec"); - break; case 0x63: /* movsxd */ if (ctxt->mode != X86EMUL_MODE_PROT64) goto cannot_emulate; From 11c363ba8f8eb163c275920b4a27697eb43da6e9 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 19 Jan 2013 19:51:54 +0200 Subject: [PATCH 1136/4607] KVM: x86 emulator: convert BT/BTS/BTR/BTC/BSF/BSR to fastop Reviewed-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 76 +++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 50 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index edb09e9c111c..62014dca9e26 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -478,6 +478,15 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt) ON64(FOP2E(op##q, rax, rbx)) \ FOP_END +/* 2 operand, word only */ +#define FASTOP2W(op) \ + FOP_START(op) \ + FOPNOP() \ + FOP2E(op##w, ax, bx) \ + FOP2E(op##l, eax, ebx) \ + ON64(FOP2E(op##q, rax, rbx)) \ + FOP_END + /* 2 operand, src is CL */ #define FASTOP2CL(op) \ FOP_START(op) \ @@ -2066,6 +2075,13 @@ FASTOP2CL(shl); FASTOP2CL(shr); FASTOP2CL(sar); +FASTOP2W(bsf); +FASTOP2W(bsr); +FASTOP2W(bt); +FASTOP2W(bts); +FASTOP2W(btr); +FASTOP2W(btc); + static int em_mul_ex(struct x86_emulate_ctxt *ctxt) { u8 ex = 0; @@ -3377,47 +3393,6 @@ static int em_sti(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } -static int em_bt(struct x86_emulate_ctxt *ctxt) -{ - /* Disable writeback. */ - ctxt->dst.type = OP_NONE; - /* only subword offset */ - ctxt->src.val &= (ctxt->dst.bytes << 3) - 1; - - emulate_2op_SrcV_nobyte(ctxt, "bt"); - return X86EMUL_CONTINUE; -} - -static int em_bts(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV_nobyte(ctxt, "bts"); - return X86EMUL_CONTINUE; -} - -static int em_btr(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV_nobyte(ctxt, "btr"); - return X86EMUL_CONTINUE; -} - -static int em_btc(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV_nobyte(ctxt, "btc"); - return X86EMUL_CONTINUE; -} - -static int em_bsf(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV_nobyte(ctxt, "bsf"); - return X86EMUL_CONTINUE; -} - -static int em_bsr(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV_nobyte(ctxt, "bsr"); - return X86EMUL_CONTINUE; -} - static int em_cpuid(struct x86_emulate_ctxt *ctxt) { u32 eax, ebx, ecx, edx; @@ -3773,10 +3748,10 @@ static const struct group_dual group7 = { { static const struct opcode group8[] = { N, N, N, N, - I(DstMem | SrcImmByte, em_bt), - I(DstMem | SrcImmByte | Lock | PageTable, em_bts), - I(DstMem | SrcImmByte | Lock, em_btr), - I(DstMem | SrcImmByte | Lock | PageTable, em_btc), + F(DstMem | SrcImmByte | NoWrite, em_bt), + F(DstMem | SrcImmByte | Lock | PageTable, em_bts), + F(DstMem | SrcImmByte | Lock, em_btr), + F(DstMem | SrcImmByte | Lock | PageTable, em_btc), }; static const struct group_dual group9 = { { @@ -4025,28 +4000,29 @@ static const struct opcode twobyte_table[256] = { X16(D(ByteOp | DstMem | SrcNone | ModRM| Mov)), /* 0xA0 - 0xA7 */ I(Stack | Src2FS, em_push_sreg), I(Stack | Src2FS, em_pop_sreg), - II(ImplicitOps, em_cpuid, cpuid), I(DstMem | SrcReg | ModRM | BitOp, em_bt), + II(ImplicitOps, em_cpuid, cpuid), + F(DstMem | SrcReg | ModRM | BitOp | NoWrite, em_bt), F(DstMem | SrcReg | Src2ImmByte | ModRM, em_shld), F(DstMem | SrcReg | Src2CL | ModRM, em_shld), N, N, /* 0xA8 - 0xAF */ I(Stack | Src2GS, em_push_sreg), I(Stack | Src2GS, em_pop_sreg), DI(ImplicitOps, rsm), - I(DstMem | SrcReg | ModRM | BitOp | Lock | PageTable, em_bts), + F(DstMem | SrcReg | ModRM | BitOp | Lock | PageTable, em_bts), F(DstMem | SrcReg | Src2ImmByte | ModRM, em_shrd), F(DstMem | SrcReg | Src2CL | ModRM, em_shrd), D(ModRM), I(DstReg | SrcMem | ModRM, em_imul), /* 0xB0 - 0xB7 */ I2bv(DstMem | SrcReg | ModRM | Lock | PageTable, em_cmpxchg), I(DstReg | SrcMemFAddr | ModRM | Src2SS, em_lseg), - I(DstMem | SrcReg | ModRM | BitOp | Lock, em_btr), + F(DstMem | SrcReg | ModRM | BitOp | Lock, em_btr), I(DstReg | SrcMemFAddr | ModRM | Src2FS, em_lseg), I(DstReg | SrcMemFAddr | ModRM | Src2GS, em_lseg), D(DstReg | SrcMem8 | ModRM | Mov), D(DstReg | SrcMem16 | ModRM | Mov), /* 0xB8 - 0xBF */ N, N, G(BitOp, group8), - I(DstMem | SrcReg | ModRM | BitOp | Lock | PageTable, em_btc), - I(DstReg | SrcMem | ModRM, em_bsf), I(DstReg | SrcMem | ModRM, em_bsr), + F(DstMem | SrcReg | ModRM | BitOp | Lock | PageTable, em_btc), + F(DstReg | SrcMem | ModRM, em_bsf), F(DstReg | SrcMem | ModRM, em_bsr), D(DstReg | SrcMem8 | ModRM | Mov), D(DstReg | SrcMem16 | ModRM | Mov), /* 0xC0 - 0xC7 */ D2bv(DstMem | SrcReg | ModRM | Lock), From 4d7583493e1777f42cc0fda9573d312e4753aa3c Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 19 Jan 2013 19:51:55 +0200 Subject: [PATCH 1137/4607] KVM: x86 emulator: convert 2-operand IMUL to fastop Reviewed-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 62014dca9e26..45ddec8b7566 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -441,6 +441,8 @@ static void invalidate_registers(struct x86_emulate_ctxt *ctxt) } \ } while (0) +static int fastop(struct x86_emulate_ctxt *ctxt, void (*fop)(struct fastop *)); + #define FOP_ALIGN ".align " __stringify(FASTOP_SIZE) " \n\t" #define FOP_RET "ret \n\t" @@ -3051,6 +3053,8 @@ FASTOP2(test); FASTOP3WCL(shld); FASTOP3WCL(shrd); +FASTOP2W(imul); + static int em_xchg(struct x86_emulate_ctxt *ctxt) { /* Write back the register source. */ @@ -3063,16 +3067,10 @@ static int em_xchg(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } -static int em_imul(struct x86_emulate_ctxt *ctxt) -{ - emulate_2op_SrcV_nobyte(ctxt, "imul"); - return X86EMUL_CONTINUE; -} - static int em_imul_3op(struct x86_emulate_ctxt *ctxt) { ctxt->dst.val = ctxt->src2.val; - return em_imul(ctxt); + return fastop(ctxt, em_imul); } static int em_cwd(struct x86_emulate_ctxt *ctxt) @@ -4010,7 +4008,7 @@ static const struct opcode twobyte_table[256] = { F(DstMem | SrcReg | ModRM | BitOp | Lock | PageTable, em_bts), F(DstMem | SrcReg | Src2ImmByte | ModRM, em_shrd), F(DstMem | SrcReg | Src2CL | ModRM, em_shrd), - D(ModRM), I(DstReg | SrcMem | ModRM, em_imul), + D(ModRM), F(DstReg | SrcMem | ModRM, em_imul), /* 0xB0 - 0xB7 */ I2bv(DstMem | SrcReg | ModRM | Lock | PageTable, em_cmpxchg), I(DstReg | SrcMemFAddr | ModRM | Src2SS, em_lseg), From 34b77652b9e98b5796b3a69df600e1717572e51d Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 19 Jan 2013 19:51:56 +0200 Subject: [PATCH 1138/4607] KVM: x86 emulator: rearrange fastop definitions Make fastop opcodes usable in other emulations. Reviewed-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 70 +++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 45ddec8b7566..d06354d9a16a 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -972,6 +972,41 @@ static int read_descriptor(struct x86_emulate_ctxt *ctxt, return rc; } +FASTOP2(add); +FASTOP2(or); +FASTOP2(adc); +FASTOP2(sbb); +FASTOP2(and); +FASTOP2(sub); +FASTOP2(xor); +FASTOP2(cmp); +FASTOP2(test); + +FASTOP3WCL(shld); +FASTOP3WCL(shrd); + +FASTOP2W(imul); + +FASTOP1(not); +FASTOP1(neg); +FASTOP1(inc); +FASTOP1(dec); + +FASTOP2CL(rol); +FASTOP2CL(ror); +FASTOP2CL(rcl); +FASTOP2CL(rcr); +FASTOP2CL(shl); +FASTOP2CL(shr); +FASTOP2CL(sar); + +FASTOP2W(bsf); +FASTOP2W(bsr); +FASTOP2W(bt); +FASTOP2W(bts); +FASTOP2W(btr); +FASTOP2W(btc); + static u8 test_cc(unsigned int condition, unsigned long flags) { u8 rc; @@ -2064,26 +2099,6 @@ static int em_jmp_far(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } -FASTOP1(not); -FASTOP1(neg); -FASTOP1(inc); -FASTOP1(dec); - -FASTOP2CL(rol); -FASTOP2CL(ror); -FASTOP2CL(rcl); -FASTOP2CL(rcr); -FASTOP2CL(shl); -FASTOP2CL(shr); -FASTOP2CL(sar); - -FASTOP2W(bsf); -FASTOP2W(bsr); -FASTOP2W(bt); -FASTOP2W(bts); -FASTOP2W(btr); -FASTOP2W(btc); - static int em_mul_ex(struct x86_emulate_ctxt *ctxt) { u8 ex = 0; @@ -3040,21 +3055,6 @@ static int em_ret_near_imm(struct x86_emulate_ctxt *ctxt) return X86EMUL_CONTINUE; } -FASTOP2(add); -FASTOP2(or); -FASTOP2(adc); -FASTOP2(sbb); -FASTOP2(and); -FASTOP2(sub); -FASTOP2(xor); -FASTOP2(cmp); -FASTOP2(test); - -FASTOP3WCL(shld); -FASTOP3WCL(shrd); - -FASTOP2W(imul); - static int em_xchg(struct x86_emulate_ctxt *ctxt) { /* Write back the register source. */ From 158de57f905ed97ea0f993feac1c40a40f5c7a04 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Sat, 19 Jan 2013 19:51:57 +0200 Subject: [PATCH 1139/4607] KVM: x86 emulator: convert a few freestanding emulations to fastop Reviewed-by: Gleb Natapov Signed-off-by: Avi Kivity Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/emulate.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index d06354d9a16a..e99fb72cd4c5 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -2209,7 +2209,7 @@ static int em_cmpxchg(struct x86_emulate_ctxt *ctxt) /* Save real source value, then compare EAX against destination. */ ctxt->src.orig_val = ctxt->src.val; ctxt->src.val = reg_read(ctxt, VCPU_REGS_RAX); - emulate_2op_SrcV(ctxt, "cmp"); + fastop(ctxt, em_cmp); if (ctxt->eflags & EFLG_ZF) { /* Success: write back to memory. */ @@ -2977,7 +2977,7 @@ static int em_das(struct x86_emulate_ctxt *ctxt) ctxt->src.type = OP_IMM; ctxt->src.val = 0; ctxt->src.bytes = 1; - emulate_2op_SrcV(ctxt, "or"); + fastop(ctxt, em_or); ctxt->eflags &= ~(X86_EFLAGS_AF | X86_EFLAGS_CF); if (cf) ctxt->eflags |= X86_EFLAGS_CF; @@ -4816,7 +4816,7 @@ twobyte_insn: (s16) ctxt->src.val; break; case 0xc0 ... 0xc1: /* xadd */ - emulate_2op_SrcV(ctxt, "add"); + fastop(ctxt, em_add); /* Write back the register source. */ ctxt->src.val = ctxt->dst.orig_val; write_register_operand(&ctxt->src); From 1f3141e80b149e7215313dff29e9a0c47811b1d1 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 21 Jan 2013 15:36:41 +0200 Subject: [PATCH 1140/4607] KVM: VMX: remove special CPL cache access during transition to real mode. Since vmx_get_cpl() always returns 0 when VCPU is in real mode it is no longer needed. Also reset CPL cache to zero during transaction to protected mode since transaction may happen while CS.selectors & 3 != 0, but in reality CPL is 0. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index dd2a85c1c6f0..9d2ec88eeed2 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2817,6 +2817,10 @@ static void enter_pmode(struct kvm_vcpu *vcpu) fix_pmode_dataseg(vcpu, VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]); fix_pmode_dataseg(vcpu, VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]); fix_pmode_dataseg(vcpu, VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]); + + /* CPL is always 0 when CPU enters protected mode */ + __set_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail); + vmx->cpl = 0; } static gva_t rmode_tss_base(struct kvm *kvm) @@ -3229,14 +3233,6 @@ static int vmx_get_cpl(struct kvm_vcpu *vcpu) && (kvm_get_rflags(vcpu) & X86_EFLAGS_VM)) /* if virtual 8086 */ return 3; - /* - * If we enter real mode with cs.sel & 3 != 0, the normal CPL calculations - * fail; use the cache instead. - */ - if (unlikely(vmx->emulation_required && emulate_invalid_guest_state)) { - return vmx->cpl; - } - if (!test_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail)) { __set_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail); vmx->cpl = vmx_read_guest_seg_selector(vmx, VCPU_SREG_CS) & 3; From 2f143240cb822c0d23ad591b89fe10e7c1f842f5 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 21 Jan 2013 15:36:42 +0200 Subject: [PATCH 1141/4607] KVM: VMX: reset CPL only on CS register write. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 9d2ec88eeed2..edfbe94c622c 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3269,7 +3269,8 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, const struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; vmx_segment_cache_clear(vmx); - __clear_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail); + if (seg == VCPU_SREG_CS) + __clear_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail); if (vmx->rmode.vm86_active && seg != VCPU_SREG_LDTR) { vmx->rmode.segs[seg] = *var; From c5e97c80b5ddd6139bdadcbd44e263c2a3e7fae6 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 21 Jan 2013 15:36:43 +0200 Subject: [PATCH 1142/4607] KVM: VMX: if unrestricted guest is enabled vcpu state is always valid. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index edfbe94c622c..f942b201b345 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3488,6 +3488,9 @@ static bool cs_ss_rpl_check(struct kvm_vcpu *vcpu) */ static bool guest_state_valid(struct kvm_vcpu *vcpu) { + if (enable_unrestricted_guest) + return true; + /* real mode guest state checks */ if (!is_protmode(vcpu)) { if (!rmode_segment_valid(vcpu, VCPU_SREG_CS)) From 286da4156dc65c8a054580fdd96b7709132dce8d Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 21 Jan 2013 15:36:44 +0200 Subject: [PATCH 1143/4607] KVM: VMX: remove hack that disables emulation on vcpu reset/init There is no reason for it. If state is suitable for vmentry it will be detected during guest entry and no emulation will happen. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index f942b201b345..20409bd5eb4a 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -4035,9 +4035,6 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu) ret = 0; - /* HACK: Don't enable emulation on guest boot/reset */ - vmx->emulation_required = 0; - return ret; } From 218e763f458c44f30041c1b48b4371e130fd4317 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 21 Jan 2013 15:36:45 +0200 Subject: [PATCH 1144/4607] KVM: VMX: skip vmx->rmode.vm86_active check on cr0 write if unrestricted guest is enabled vmx->rmode.vm86_active is never true is unrestricted guest is enabled. Make it more explicit that neither enter_pmode() nor enter_rmode() is called in this case. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 20409bd5eb4a..319e840e2ada 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2877,9 +2877,6 @@ static void enter_rmode(struct kvm_vcpu *vcpu) unsigned long flags; struct vcpu_vmx *vmx = to_vmx(vcpu); - if (enable_unrestricted_guest) - return; - vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_TR], VCPU_SREG_TR); vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_ES], VCPU_SREG_ES); vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_DS], VCPU_SREG_DS); @@ -3086,14 +3083,15 @@ static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) if (enable_unrestricted_guest) hw_cr0 = (cr0 & ~KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST) | KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST; - else + else { hw_cr0 = (cr0 & ~KVM_GUEST_CR0_MASK) | KVM_VM_CR0_ALWAYS_ON; - if (vmx->rmode.vm86_active && (cr0 & X86_CR0_PE)) - enter_pmode(vcpu); + if (vmx->rmode.vm86_active && (cr0 & X86_CR0_PE)) + enter_pmode(vcpu); - if (!vmx->rmode.vm86_active && !(cr0 & X86_CR0_PE)) - enter_rmode(vcpu); + if (!vmx->rmode.vm86_active && !(cr0 & X86_CR0_PE)) + enter_rmode(vcpu); + } #ifdef CONFIG_X86_64 if (vcpu->arch.efer & EFER_LME) { From 25391454e73e3156202264eb3c473825afe4bc94 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 21 Jan 2013 15:36:46 +0200 Subject: [PATCH 1145/4607] KVM: VMX: don't clobber segment AR of unusable segments. Usability is returned in unusable field, so not need to clobber entire AR. Callers have to know how to deal with unusable segments already since if emulate_invalid_guest_state=true AR is not zeroed. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 319e840e2ada..77212d1e1df9 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3196,8 +3196,6 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu, var->limit = vmx_read_guest_seg_limit(vmx, seg); var->selector = vmx_read_guest_seg_selector(vmx, seg); ar = vmx_read_guest_seg_ar(vmx, seg); - if ((ar & AR_UNUSABLE_MASK) && !emulate_invalid_guest_state) - ar = 0; var->type = ar & 15; var->s = (ar >> 4) & 1; var->dpl = (ar >> 5) & 3; From 91b0aa2ca63e60ac69c5dd96bff580d3947073d4 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 21 Jan 2013 15:36:47 +0200 Subject: [PATCH 1146/4607] KVM: VMX: rename fix_pmode_dataseg to fix_pmode_seg. The function deals with code segment too. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 77212d1e1df9..9bc68c7bca7d 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2759,7 +2759,7 @@ static __exit void hardware_unsetup(void) free_kvm_area(); } -static void fix_pmode_dataseg(struct kvm_vcpu *vcpu, int seg, +static void fix_pmode_seg(struct kvm_vcpu *vcpu, int seg, struct kvm_segment *save) { if (!emulate_invalid_guest_state) { @@ -2811,12 +2811,12 @@ static void enter_pmode(struct kvm_vcpu *vcpu) update_exception_bitmap(vcpu); - fix_pmode_dataseg(vcpu, VCPU_SREG_CS, &vmx->rmode.segs[VCPU_SREG_CS]); - fix_pmode_dataseg(vcpu, VCPU_SREG_SS, &vmx->rmode.segs[VCPU_SREG_SS]); - fix_pmode_dataseg(vcpu, VCPU_SREG_ES, &vmx->rmode.segs[VCPU_SREG_ES]); - fix_pmode_dataseg(vcpu, VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]); - fix_pmode_dataseg(vcpu, VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]); - fix_pmode_dataseg(vcpu, VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]); + fix_pmode_seg(vcpu, VCPU_SREG_CS, &vmx->rmode.segs[VCPU_SREG_CS]); + fix_pmode_seg(vcpu, VCPU_SREG_SS, &vmx->rmode.segs[VCPU_SREG_SS]); + fix_pmode_seg(vcpu, VCPU_SREG_ES, &vmx->rmode.segs[VCPU_SREG_ES]); + fix_pmode_seg(vcpu, VCPU_SREG_DS, &vmx->rmode.segs[VCPU_SREG_DS]); + fix_pmode_seg(vcpu, VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]); + fix_pmode_seg(vcpu, VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]); /* CPL is always 0 when CPU enters protected mode */ __set_bit(VCPU_EXREG_CPL, (ulong *)&vcpu->arch.regs_avail); From 378a8b099fc207ddcb91b19a8c1457667e0af398 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 21 Jan 2013 15:36:48 +0200 Subject: [PATCH 1147/4607] KVM: x86: fix use of uninitialized memory as segment descriptor in emulator. If VMX reports segment as unusable, zero descriptor passed by the emulator before returning. Such descriptor will be considered not present by the emulator. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/x86.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index e00dd0515a84..b9f55299ed7e 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4489,8 +4489,10 @@ static bool emulator_get_segment(struct x86_emulate_ctxt *ctxt, u16 *selector, kvm_get_segment(emul_to_vcpu(ctxt), &var, seg); *selector = var.selector; - if (var.unusable) + if (var.unusable) { + memset(desc, 0, sizeof(*desc)); return false; + } if (var.g) var.limit >>= 12; From 141687869fb904e912568c6b94a6b1fa2114f6ed Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 21 Jan 2013 15:36:49 +0200 Subject: [PATCH 1148/4607] KVM: VMX: set vmx->emulation_required only when needed. If emulate_invalid_guest_state=false vmx->emulation_required is never actually used, but it ends up to be always set to true since handle_invalid_guest_state(), the only place it is reset back to false, is never called. This, besides been not very clean, makes vmexit and vmentry path to check emulate_invalid_guest_state needlessly. The patch fixes that by keeping emulation_required coherent with emulate_invalid_guest_state setting. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 9bc68c7bca7d..02eeba86328d 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2759,6 +2759,11 @@ static __exit void hardware_unsetup(void) free_kvm_area(); } +static bool emulation_required(struct kvm_vcpu *vcpu) +{ + return emulate_invalid_guest_state && !guest_state_valid(vcpu); +} + static void fix_pmode_seg(struct kvm_vcpu *vcpu, int seg, struct kvm_segment *save) { @@ -2794,7 +2799,6 @@ static void enter_pmode(struct kvm_vcpu *vcpu) vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_SS], VCPU_SREG_SS); vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_CS], VCPU_SREG_CS); - vmx->emulation_required = 1; vmx->rmode.vm86_active = 0; vmx_segment_cache_clear(vmx); @@ -2885,7 +2889,6 @@ static void enter_rmode(struct kvm_vcpu *vcpu) vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_SS], VCPU_SREG_SS); vmx_get_segment(vcpu, &vmx->rmode.segs[VCPU_SREG_CS], VCPU_SREG_CS); - vmx->emulation_required = 1; vmx->rmode.vm86_active = 1; /* @@ -3111,6 +3114,9 @@ static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) vmcs_writel(CR0_READ_SHADOW, cr0); vmcs_writel(GUEST_CR0, hw_cr0); vcpu->arch.cr0 = cr0; + + /* depends on vcpu->arch.cr0 to be set to a new value */ + vmx->emulation_required = emulation_required(vcpu); } static u64 construct_eptp(unsigned long root_hpa) @@ -3298,8 +3304,7 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu, vmcs_write32(sf->ar_bytes, vmx_segment_access_rights(var)); out: - if (!vmx->emulation_required) - vmx->emulation_required = !guest_state_valid(vcpu); + vmx->emulation_required |= emulation_required(vcpu); } static void vmx_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l) @@ -5027,7 +5032,7 @@ static int handle_invalid_guest_state(struct kvm_vcpu *vcpu) schedule(); } - vmx->emulation_required = !guest_state_valid(vcpu); + vmx->emulation_required = emulation_required(vcpu); out: return ret; } @@ -5970,7 +5975,7 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu) u32 vectoring_info = vmx->idt_vectoring_info; /* If guest state is invalid, start emulating */ - if (vmx->emulation_required && emulate_invalid_guest_state) + if (vmx->emulation_required) return handle_invalid_guest_state(vcpu); /* @@ -6253,7 +6258,7 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu) /* Don't enter VMX if guest state is invalid, let the exit handler start emulation until we arrive back to a valid state */ - if (vmx->emulation_required && emulate_invalid_guest_state) + if (vmx->emulation_required) return; if (test_bit(VCPU_REGS_RSP, (unsigned long *)&vcpu->arch.regs_dirty)) From ea4f3111ef0daffa1d11fded6f375227febca458 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Thu, 22 Nov 2012 13:11:32 +0100 Subject: [PATCH 1149/4607] viafb: rename display_timing to via_display_timing The struct display_timing is specific to the via subsystem. The naming leads to collisions with the new struct display_timing, which is supposed to be a shared struct between different subsystems. To clean this up, prepend the existing struct with the subsystem it is specific to. Signed-off-by: Steffen Trumtrar --- drivers/video/via/hw.c | 6 +++--- drivers/video/via/hw.h | 2 +- drivers/video/via/lcd.c | 2 +- drivers/video/via/share.h | 2 +- drivers/video/via/via_modesetting.c | 8 ++++---- drivers/video/via/via_modesetting.h | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c index 80233dae358a..22450908306c 100644 --- a/drivers/video/via/hw.c +++ b/drivers/video/via/hw.c @@ -1467,10 +1467,10 @@ void viafb_set_vclock(u32 clk, int set_iga) via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */ } -struct display_timing var_to_timing(const struct fb_var_screeninfo *var, +struct via_display_timing var_to_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres) { - struct display_timing timing; + struct via_display_timing timing; u16 dx = (var->xres - cxres) / 2, dy = (var->yres - cyres) / 2; timing.hor_addr = cxres; @@ -1491,7 +1491,7 @@ struct display_timing var_to_timing(const struct fb_var_screeninfo *var, void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres, int iga) { - struct display_timing crt_reg = var_to_timing(var, + struct via_display_timing crt_reg = var_to_timing(var, cxres ? cxres : var->xres, cyres ? cyres : var->yres); if (iga == IGA1) diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h index a8205754c736..3be073c58b03 100644 --- a/drivers/video/via/hw.h +++ b/drivers/video/via/hw.h @@ -637,7 +637,7 @@ extern int viafb_LCD_ON; extern int viafb_DVI_ON; extern int viafb_hotplug; -struct display_timing var_to_timing(const struct fb_var_screeninfo *var, +struct via_display_timing var_to_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres); void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, u16 cxres, u16 cyres, int iga); diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c index 980ee1b1dcf3..5d21ff436ec8 100644 --- a/drivers/video/via/lcd.c +++ b/drivers/video/via/lcd.c @@ -549,7 +549,7 @@ void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres, int panel_hres = plvds_setting_info->lcd_panel_hres; int panel_vres = plvds_setting_info->lcd_panel_vres; u32 clock; - struct display_timing timing; + struct via_display_timing timing; struct fb_var_screeninfo panel_var; const struct fb_videomode *mode_crt_table, *panel_crt_table; diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h index 3158dfc90bed..65c65c611e0a 100644 --- a/drivers/video/via/share.h +++ b/drivers/video/via/share.h @@ -319,7 +319,7 @@ struct crt_mode_table { int refresh_rate; int h_sync_polarity; int v_sync_polarity; - struct display_timing crtc; + struct via_display_timing crtc; }; struct io_reg { diff --git a/drivers/video/via/via_modesetting.c b/drivers/video/via/via_modesetting.c index 0e431aee17bb..0b414b09b9b4 100644 --- a/drivers/video/via/via_modesetting.c +++ b/drivers/video/via/via_modesetting.c @@ -30,9 +30,9 @@ #include "debug.h" -void via_set_primary_timing(const struct display_timing *timing) +void via_set_primary_timing(const struct via_display_timing *timing) { - struct display_timing raw; + struct via_display_timing raw; raw.hor_total = timing->hor_total / 8 - 5; raw.hor_addr = timing->hor_addr / 8 - 1; @@ -88,9 +88,9 @@ void via_set_primary_timing(const struct display_timing *timing) via_write_reg_mask(VIACR, 0x17, 0x80, 0x80); } -void via_set_secondary_timing(const struct display_timing *timing) +void via_set_secondary_timing(const struct via_display_timing *timing) { - struct display_timing raw; + struct via_display_timing raw; raw.hor_total = timing->hor_total - 1; raw.hor_addr = timing->hor_addr - 1; diff --git a/drivers/video/via/via_modesetting.h b/drivers/video/via/via_modesetting.h index 06e09fe351ae..f6a6503da3b3 100644 --- a/drivers/video/via/via_modesetting.h +++ b/drivers/video/via/via_modesetting.h @@ -33,7 +33,7 @@ #define VIA_PITCH_MAX 0x3FF8 -struct display_timing { +struct via_display_timing { u16 hor_total; u16 hor_addr; u16 hor_blank_start; @@ -49,8 +49,8 @@ struct display_timing { }; -void via_set_primary_timing(const struct display_timing *timing); -void via_set_secondary_timing(const struct display_timing *timing); +void via_set_primary_timing(const struct via_display_timing *timing); +void via_set_secondary_timing(const struct via_display_timing *timing); void via_set_primary_address(u32 addr); void via_set_secondary_address(u32 addr); void via_set_primary_pitch(u32 pitch); From 8714c0cecfc28f7ce73a520be4831f09743c4fd7 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Mon, 17 Dec 2012 14:20:17 +0100 Subject: [PATCH 1150/4607] video: add display_timing and videomode Add display_timing structure and the according helper functions. This allows the description of a display via its supported timing parameters. Also, add helper functions to convert from display timings to a generic videomode structure. The struct display_timing specifies all needed parameters to describe the signal properties of a display in one mode. This includes - ranges for signals that may have min-, max- and typical values - single integers for signals that can be on, off or are ignored - booleans for signals that are either on or off As a display may support multiple modes like this, a struct display_timings is added, that holds all given struct display_timing pointers and declares the native mode of the display. Although a display may state that a signal can be in a range, it is driven with fixed values that indicate a videomode. Therefore graphic drivers don't need all the information of struct display_timing, but would generate a videomode from the given set of supported signal timings and work with that. The video subsystems all define their own structs that describe a mode and work with that (e.g. fb_videomode or drm_display_mode). To slowly replace all those various structures and allow code reuse across those subsystems, add struct videomode as a generic description. This patch only includes the most basic fields in struct videomode. All missing fields that are needed to have a really generic video mode description can be added at a later stage. Signed-off-by: Steffen Trumtrar Reviewed-by: Thierry Reding Acked-by: Thierry Reding Tested-by: Thierry Reding Tested-by: Philipp Zabel Reviewed-by: Laurent Pinchart Acked-by: Laurent Pinchart Tested-by: Afzal Mohammed Tested-by: Rob Clark Tested-by: Leela Krishna Amudala --- drivers/video/Kconfig | 6 ++ drivers/video/Makefile | 2 + drivers/video/display_timing.c | 24 +++++++ drivers/video/videomode.c | 39 +++++++++++ include/video/display_timing.h | 124 +++++++++++++++++++++++++++++++++ include/video/videomode.h | 48 +++++++++++++ 6 files changed, 243 insertions(+) create mode 100644 drivers/video/display_timing.c create mode 100644 drivers/video/videomode.c create mode 100644 include/video/display_timing.h create mode 100644 include/video/videomode.h diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e7068c508800..09a8f0d8a3d4 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -33,6 +33,12 @@ config VIDEO_OUTPUT_CONTROL This framework adds support for low-level control of the video output switch. +config DISPLAY_TIMING + bool + +config VIDEOMODE + bool + menuconfig FB tristate "Support for frame buffer devices" ---help--- diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 768a137a1bac..e0dd8202365f 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -168,3 +168,5 @@ obj-$(CONFIG_FB_VIRTUAL) += vfb.o #video output switch sysfs driver obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o +obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o +obj-$(CONFIG_VIDEOMODE) += videomode.o diff --git a/drivers/video/display_timing.c b/drivers/video/display_timing.c new file mode 100644 index 000000000000..5e1822cef571 --- /dev/null +++ b/drivers/video/display_timing.c @@ -0,0 +1,24 @@ +/* + * generic display timing functions + * + * Copyright (c) 2012 Steffen Trumtrar , Pengutronix + * + * This file is released under the GPLv2 + */ + +#include +#include +#include
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index 8fe29427c8e4..c3851a2fb50d 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -142,10 +142,12 @@ applications. --> 3.9 2012-12-03 - sa + sa, sn Added timestamp types to v4l2_buffer, see . + Added V4L2_EVENT_CTRL_CH_RANGE control + event changes flag, see . diff --git a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml index 98a856f9ec30..89891adb928a 100644 --- a/Documentation/DocBook/media/v4l/vidioc-dqevent.xml +++ b/Documentation/DocBook/media/v4l/vidioc-dqevent.xml @@ -261,6 +261,12 @@ This control event was triggered because the control flags changed.
+ + V4L2_EVENT_CTRL_CH_RANGE + 0x0004 + This control event was triggered because the minimum, + maximum, step or the default value of the control changed. + diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 7b486ac3f4d9..3f27571b814d 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1158,8 +1158,7 @@ static int new_to_user(struct v4l2_ext_control *c, } /* Copy the new value to the current value. */ -static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, - bool update_inactive) +static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) { bool changed = false; @@ -1183,8 +1182,8 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, ctrl->cur.val = ctrl->val; break; } - if (update_inactive) { - /* Note: update_inactive can only be true for auto clusters. */ + if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { + /* Note: CH_FLAGS is only set for auto clusters. */ ctrl->flags &= ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE); if (!is_cur_manual(ctrl->cluster[0])) { @@ -1194,14 +1193,13 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, } fh = NULL; } - if (changed || update_inactive) { + if (changed || ch_flags) { /* If a control was changed that was not one of the controls modified by the application, then send the event to all. */ if (!ctrl->is_new) fh = NULL; send_event(fh, ctrl, - (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) | - (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0)); + (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) | ch_flags); if (ctrl->call_notify && changed && ctrl->handler->notify) ctrl->handler->notify(ctrl, ctrl->handler->notify_priv); } @@ -1257,6 +1255,41 @@ static int cluster_changed(struct v4l2_ctrl *master) return diff; } +/* Control range checking */ +static int check_range(enum v4l2_ctrl_type type, + s32 min, s32 max, u32 step, s32 def) +{ + switch (type) { + case V4L2_CTRL_TYPE_BOOLEAN: + if (step != 1 || max > 1 || min < 0) + return -ERANGE; + /* fall through */ + case V4L2_CTRL_TYPE_INTEGER: + if (step <= 0 || min > max || def < min || def > max) + return -ERANGE; + return 0; + case V4L2_CTRL_TYPE_BITMASK: + if (step || min || !max || (def & ~max)) + return -ERANGE; + return 0; + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + if (min > max || def < min || def > max) + return -ERANGE; + /* Note: step == menu_skip_mask for menu controls. + So here we check if the default value is masked out. */ + if (step && ((1 << def) & step)) + return -EINVAL; + return 0; + case V4L2_CTRL_TYPE_STRING: + if (min > max || min < 0 || step < 1 || def) + return -ERANGE; + return 0; + default: + return 0; + } +} + /* Validate a new control */ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) @@ -1529,30 +1562,21 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, { struct v4l2_ctrl *ctrl; unsigned sz_extra = 0; + int err; if (hdl->error) return NULL; /* Sanity checks */ if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE || - (type == V4L2_CTRL_TYPE_INTEGER && step == 0) || - (type == V4L2_CTRL_TYPE_BITMASK && max == 0) || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || - (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) || - (type == V4L2_CTRL_TYPE_STRING && max == 0)) { + (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) { handler_set_err(hdl, -ERANGE); return NULL; } - if (type != V4L2_CTRL_TYPE_BITMASK && max < min) { - handler_set_err(hdl, -ERANGE); - return NULL; - } - if ((type == V4L2_CTRL_TYPE_INTEGER || - type == V4L2_CTRL_TYPE_MENU || - type == V4L2_CTRL_TYPE_INTEGER_MENU || - type == V4L2_CTRL_TYPE_BOOLEAN) && - (def < min || def > max)) { - handler_set_err(hdl, -ERANGE); + err = check_range(type, min, max, step, def); + if (err) { + handler_set_err(hdl, err); return NULL; } if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) { @@ -2426,8 +2450,8 @@ EXPORT_SYMBOL(v4l2_ctrl_g_ctrl_int64); /* Core function that calls try/s_ctrl and ensures that the new value is copied to the current value on a set. Must be called with ctrl->handler->lock held. */ -static int try_or_set_cluster(struct v4l2_fh *fh, - struct v4l2_ctrl *master, bool set) +static int try_or_set_cluster(struct v4l2_fh *fh, struct v4l2_ctrl *master, + bool set, u32 ch_flags) { bool update_flag; int ret; @@ -2465,7 +2489,8 @@ static int try_or_set_cluster(struct v4l2_fh *fh, /* If OK, then make the new values permanent. */ update_flag = is_cur_manual(master) != is_new_manual(master); for (i = 0; i < master->ncontrols; i++) - new_to_cur(fh, master->cluster[i], update_flag && i > 0); + new_to_cur(fh, master->cluster[i], ch_flags | + ((update_flag && i > 0) ? V4L2_EVENT_CTRL_CH_FLAGS : 0)); return 0; } @@ -2592,7 +2617,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, } while (!ret && idx); if (!ret) - ret = try_or_set_cluster(fh, master, set); + ret = try_or_set_cluster(fh, master, set, 0); /* Copy the new values back to userspace. */ if (!ret) { @@ -2638,10 +2663,9 @@ EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls); /* Helper function for VIDIOC_S_CTRL compatibility */ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, - struct v4l2_ext_control *c) + struct v4l2_ext_control *c, u32 ch_flags) { struct v4l2_ctrl *master = ctrl->cluster[0]; - int ret; int i; /* String controls are not supported. The user_to_new() and @@ -2651,12 +2675,6 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, if (ctrl->type == V4L2_CTRL_TYPE_STRING) return -EINVAL; - ret = validate_new(ctrl, c); - if (ret) - return ret; - - v4l2_ctrl_lock(ctrl); - /* Reset the 'is_new' flags of the cluster */ for (i = 0; i < master->ncontrols; i++) if (master->cluster[i]) @@ -2670,10 +2688,22 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, update_from_auto_cluster(master); user_to_new(c, ctrl); - ret = try_or_set_cluster(fh, master, true); - cur_to_user(c, ctrl); + return try_or_set_cluster(fh, master, true, ch_flags); +} - v4l2_ctrl_unlock(ctrl); +/* Helper function for VIDIOC_S_CTRL compatibility */ +static int set_ctrl_lock(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, + struct v4l2_ext_control *c) +{ + int ret = validate_new(ctrl, c); + + if (!ret) { + v4l2_ctrl_lock(ctrl); + ret = set_ctrl(fh, ctrl, c, 0); + if (!ret) + cur_to_user(c, ctrl); + v4l2_ctrl_unlock(ctrl); + } return ret; } @@ -2691,7 +2721,7 @@ int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, return -EACCES; c.value = control->value; - ret = set_ctrl(fh, ctrl, &c); + ret = set_ctrl_lock(fh, ctrl, &c); control->value = c.value; return ret; } @@ -2710,7 +2740,7 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) /* It's a driver bug if this happens. */ WARN_ON(!type_is_int(ctrl)); c.value = val; - return set_ctrl(NULL, ctrl, &c); + return set_ctrl_lock(NULL, ctrl, &c); } EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); @@ -2721,7 +2751,7 @@ int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) /* It's a driver bug if this happens. */ WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64); c.value64 = val; - return set_ctrl(NULL, ctrl, &c); + return set_ctrl_lock(NULL, ctrl, &c); } EXPORT_SYMBOL(v4l2_ctrl_s_ctrl_int64); @@ -2741,6 +2771,41 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void } EXPORT_SYMBOL(v4l2_ctrl_notify); +int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, + s32 min, s32 max, u32 step, s32 def) +{ + int ret = check_range(ctrl->type, min, max, step, def); + struct v4l2_ext_control c; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_BITMASK: + if (ret) + return ret; + break; + default: + return -EINVAL; + } + v4l2_ctrl_lock(ctrl); + ctrl->minimum = min; + ctrl->maximum = max; + ctrl->step = step; + ctrl->default_value = def; + c.value = ctrl->cur.val; + if (validate_new(ctrl, &c)) + c.value = def; + if (c.value != ctrl->cur.val) + ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE); + else + send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE); + v4l2_ctrl_unlock(ctrl); + return ret; +} +EXPORT_SYMBOL(v4l2_ctrl_modify_range); + static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) { struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index c4cc04136074..91125b6f05a5 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -518,6 +518,26 @@ void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active); */ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); +/** v4l2_ctrl_modify_range() - Update the range of a control. + * @ctrl: The control to update. + * @min: The control's minimum value. + * @max: The control's maximum value. + * @step: The control's step value + * @def: The control's default value. + * + * Update the range of a control on the fly. This works for control types + * INTEGER, BOOLEAN, MENU, INTEGER MENU and BITMASK. For menu controls the + * @step value is interpreted as a menu_skip_mask. + * + * An error is returned if one of the range arguments is invalid for this + * control type. + * + * This function assumes that the control handler is not locked and will + * take the lock itself. + */ +int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, + s32 min, s32 max, u32 step, s32 def); + /** v4l2_ctrl_lock() - Helper function to lock the handler * associated with the control. * @ctrl: The control to lock. diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 94cbe26e9f00..928799c2e2d9 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1822,6 +1822,7 @@ struct v4l2_event_vsync { /* Payload for V4L2_EVENT_CTRL */ #define V4L2_EVENT_CTRL_CH_VALUE (1 << 0) #define V4L2_EVENT_CTRL_CH_FLAGS (1 << 1) +#define V4L2_EVENT_CTRL_CH_RANGE (1 << 2) struct v4l2_event_ctrl { __u32 changes; From 4f4d14b70a29c679dd53e367b0d9b007a7117ee3 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 22 Jan 2013 18:58:57 -0300 Subject: [PATCH 1905/4607] [media] V4L: Add v4l2_event_subdev_unsubscribe() helper function Add a v4l2 core helper function that can be used as the subdev .unsubscribe_event handler. This allows to eliminate some boilerplate from drivers that are only handling the control events. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-event.c | 7 +++++++ include/media/v4l2-event.h | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c index c72009218152..86dcb5483c42 100644 --- a/drivers/media/v4l2-core/v4l2-event.c +++ b/drivers/media/v4l2-core/v4l2-event.c @@ -311,3 +311,10 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, return 0; } EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe); + +int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + return v4l2_event_unsubscribe(fh, sub); +} +EXPORT_SYMBOL_GPL(v4l2_event_subdev_unsubscribe); diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h index eff85f934b24..be05d019de25 100644 --- a/include/media/v4l2-event.h +++ b/include/media/v4l2-event.h @@ -64,6 +64,7 @@ */ struct v4l2_fh; +struct v4l2_subdev; struct v4l2_subscribed_event; struct video_device; @@ -129,5 +130,6 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, int v4l2_event_unsubscribe(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub); void v4l2_event_unsubscribe_all(struct v4l2_fh *fh); - +int v4l2_event_subdev_unsubscribe(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub); #endif /* V4L2_EVENT_H */ From 22fa4279eebc3fa4b3c3dc2b190158dbbafcda9f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 22 Jan 2013 19:00:23 -0300 Subject: [PATCH 1906/4607] [media] V4L: Add v4l2_ctrl_subdev_subscribe_event() helper function Add a v4l2 core helper function that can be used as the subdev .subscribe_event handler. This allows to eliminate some boilerplate from drivers that are only handling the control events. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 9 +++++++++ include/media/v4l2-ctrls.h | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 3f27571b814d..9051ec53875c 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2885,6 +2885,15 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, } EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); +int v4l2_ctrl_subdev_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + if (!sd->ctrl_handler) + return -EINVAL; + return v4l2_ctrl_subscribe_event(fh, sub); +} +EXPORT_SYMBOL(v4l2_ctrl_subdev_subscribe_event); + unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait) { struct v4l2_fh *fh = file->private_data; diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 91125b6f05a5..1e849461fc9d 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -654,4 +654,9 @@ int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); +/* Can be used as a subscribe_event function that just subscribes control + events. */ +int v4l2_ctrl_subdev_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub); + #endif From ffa9b9f016a9c97a3cc205d0d634b10d8f72eb36 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 22 Jan 2013 19:01:02 -0300 Subject: [PATCH 1907/4607] [media] V4L: Add v4l2_ctrl_subdev_log_status() helper function This patch adds a v4l2 core helper function that can be used as the log_status handler for subdevs that only need to log state of the v4l2 controls owned by the subdev's control handler. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 7 +++++++ include/media/v4l2-ctrls.h | 3 +++ 2 files changed, 10 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 9051ec53875c..6b28b5800500 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2004,6 +2004,13 @@ void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl, } EXPORT_SYMBOL(v4l2_ctrl_handler_log_status); +int v4l2_ctrl_subdev_log_status(struct v4l2_subdev *sd) +{ + v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); + return 0; +} +EXPORT_SYMBOL(v4l2_ctrl_subdev_log_status); + /* Call s_ctrl for all controls owned by the handler */ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) { diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 1e849461fc9d..f00d42bc01a6 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -659,4 +659,7 @@ int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); int v4l2_ctrl_subdev_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, struct v4l2_event_subscription *sub); +/* Log all controls owned by subdev's control handler. */ +int v4l2_ctrl_subdev_log_status(struct v4l2_subdev *sd); + #endif From 84a15ded76ec8ec23d84974238b7864813143655 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 26 Dec 2012 15:50:03 -0300 Subject: [PATCH 1908/4607] [media] V4L: Add driver for OV9650/52 image sensors This patch adds V4L2 sub-device driver for OV9650/OV9652 image sensors. The driver exposes following V4L2 controls: - auto/manual exposure, - auto/manual white balance, - auto/manual gain, - brightness, saturation, sharpness, - horizontal/vertical flip, - color bar test pattern, - banding filter (power line frequency). Frame rate can be configured with g/s_frame_interval pad level ops. Supported resolution are only: SXGA, VGA, QVGA. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 7 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/ov9650.c | 1562 ++++++++++++++++++++++++++++++++++++ include/media/ov9650.h | 27 + 4 files changed, 1597 insertions(+) create mode 100644 drivers/media/i2c/ov9650.c create mode 100644 include/media/ov9650.h diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 1e4b2d0f23ce..d73078cdffec 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -421,6 +421,13 @@ config VIDEO_OV7670 OV7670 VGA camera. It currently only works with the M88ALP01 controller. +config VIDEO_OV9650 + tristate "OmniVision OV9650/OV9652 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a V4L2 sensor-level driver for the Omnivision + OV9650 and OV9652 camera sensors. + config VIDEO_VS6624 tristate "ST VS6624 sensor support" depends on VIDEO_V4L2 && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index b1d62dfd49b8..8b62e54d3884 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o +obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c new file mode 100644 index 000000000000..1dbb8118a285 --- /dev/null +++ b/drivers/media/i2c/ov9650.c @@ -0,0 +1,1562 @@ +/* + * Omnivision OV9650/OV9652 CMOS Image Sensor driver + * + * Copyright (C) 2013, Sylwester Nawrocki + * + * Register definitions and initial settings based on a driver written + * by Vladimir Fonov. + * Copyright (c) 2010, Vladimir Fonov + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +#define DRIVER_NAME "OV9650" + +/* + * OV9650/OV9652 register definitions + */ +#define REG_GAIN 0x00 /* Gain control, AGC[7:0] */ +#define REG_BLUE 0x01 /* AWB - Blue chanel gain */ +#define REG_RED 0x02 /* AWB - Red chanel gain */ +#define REG_VREF 0x03 /* [7:6] - AGC[9:8], [5:3]/[2:0] */ +#define VREF_GAIN_MASK 0xc0 /* - VREF end/start low 3 bits */ +#define REG_COM1 0x04 +#define COM1_CCIR656 0x40 +#define REG_B_AVE 0x05 +#define REG_GB_AVE 0x06 +#define REG_GR_AVE 0x07 +#define REG_R_AVE 0x08 +#define REG_COM2 0x09 +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ +#define REG_COM3 0x0c +#define COM3_SWAP 0x40 +#define COM3_VARIOPIXEL1 0x04 +#define REG_COM4 0x0d /* Vario Pixels */ +#define COM4_VARIOPIXEL2 0x80 +#define REG_COM5 0x0e /* System clock options */ +#define COM5_SLAVE_MODE 0x10 +#define COM5_SYSTEMCLOCK48MHZ 0x80 +#define REG_COM6 0x0f /* HREF & ADBLC options */ +#define REG_AECH 0x10 /* Exposure value, AEC[9:2] */ +#define REG_CLKRC 0x11 /* Clock control */ +#define CLK_EXT 0x40 /* Use external clock directly */ +#define CLK_SCALE 0x3f /* Mask for internal clock scale */ +#define REG_COM7 0x12 /* SCCB reset, output format */ +#define COM7_RESET 0x80 +#define COM7_FMT_MASK 0x38 +#define COM7_FMT_VGA 0x40 +#define COM7_FMT_CIF 0x20 +#define COM7_FMT_QVGA 0x10 +#define COM7_FMT_QCIF 0x08 +#define COM7_RGB 0x04 +#define COM7_YUV 0x00 +#define COM7_BAYER 0x01 +#define COM7_PBAYER 0x05 +#define REG_COM8 0x13 /* AGC/AEC options */ +#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define COM8_BFILT 0x20 /* Band filter enable */ +#define COM8_AGC 0x04 /* Auto gain enable */ +#define COM8_AWB 0x02 /* White balance enable */ +#define COM8_AEC 0x01 /* Auto exposure enable */ +#define REG_COM9 0x14 /* Gain ceiling */ +#define COM9_GAIN_CEIL_MASK 0x70 /* */ +#define REG_COM10 0x15 /* PCLK, HREF, HSYNC signals polarity */ +#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ +#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ +#define COM10_HREF_REV 0x08 /* Reverse HREF */ +#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ +#define COM10_VS_NEG 0x02 /* VSYNC negative */ +#define COM10_HS_NEG 0x01 /* HSYNC negative */ +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ +#define REG_MIDH 0x1c /* Manufacturer ID MSB */ +#define REG_MIDL 0x1d /* Manufufacturer ID LSB */ +#define REG_MVFP 0x1e /* Image mirror/flip */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ +#define REG_BOS 0x20 /* B channel Offset */ +#define REG_GBOS 0x21 /* Gb channel Offset */ +#define REG_GROS 0x22 /* Gr channel Offset */ +#define REG_ROS 0x23 /* R channel Offset */ +#define REG_AEW 0x24 /* AGC upper limit */ +#define REG_AEB 0x25 /* AGC lower limit */ +#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define REG_BBIAS 0x27 /* B channel output bias */ +#define REG_GBBIAS 0x28 /* Gb channel output bias */ +#define REG_GRCOM 0x29 /* Analog BLC & regulator */ +#define REG_EXHCH 0x2a /* Dummy pixel insert MSB */ +#define REG_EXHCL 0x2b /* Dummy pixel insert LSB */ +#define REG_RBIAS 0x2c /* R channel output bias */ +#define REG_ADVFL 0x2d /* LSB of dummy line insert */ +#define REG_ADVFH 0x2e /* MSB of dummy line insert */ +#define REG_YAVE 0x2f /* Y/G channel average value */ +#define REG_HSYST 0x30 /* HSYNC rising edge delay LSB*/ +#define REG_HSYEN 0x31 /* HSYNC falling edge delay LSB*/ +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_CHLF 0x33 /* reserved */ +#define REG_ADC 0x37 /* reserved */ +#define REG_ACOM 0x38 /* reserved */ +#define REG_OFON 0x39 /* Power down register */ +#define OFON_PWRDN 0x08 /* Power down bit */ +#define REG_TSLB 0x3a /* YUVU format */ +#define TSLB_YUYV_MASK 0x0c /* UYVY or VYUY - see com13 */ +#define REG_COM11 0x3b /* Night mode, banding filter enable */ +#define COM11_NIGHT 0x80 /* Night mode enable */ +#define COM11_NMFR 0x60 /* Two bit NM frame rate */ +#define COM11_BANDING 0x01 /* Banding filter */ +#define COM11_AEC_REF_MASK 0x18 /* AEC reference area selection */ +#define REG_COM12 0x3c /* HREF option, UV average */ +#define COM12_HREF 0x80 /* HREF always */ +#define REG_COM13 0x3d /* Gamma selection, Color matrix en. */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ +#define REG_COM14 0x3e /* Edge enhancement options */ +#define COM14_EDGE_EN 0x02 +#define COM14_EEF_X2 0x01 +#define REG_EDGE 0x3f /* Edge enhancement factor */ +#define EDGE_FACTOR_MASK 0x0f +#define REG_COM15 0x40 /* Output range, RGB 555/565 */ +#define COM15_R10F0 0x00 /* Data range 10 to F0 */ +#define COM15_R01FE 0x80 /* 01 to FE */ +#define COM15_R00FF 0xc0 /* 00 to FF */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_RGB555 0x30 /* RGB555 output */ +#define COM15_SWAPRB 0x04 /* Swap R&B */ +#define REG_COM16 0x41 /* Color matrix coeff options */ +#define REG_COM17 0x42 /* Single frame out, banding filter */ +/* n = 1...9, 0x4f..0x57 */ +#define REG_MTX(__n) (0x4f + (__n) - 1) +#define REG_MTXS 0x58 +/* Lens Correction Option 1...5, __n = 0...5 */ +#define REG_LCC(__n) (0x62 + (__n) - 1) +#define LCC5_LCC_ENABLE 0x01 /* LCC5, enable lens correction */ +#define LCC5_LCC_COLOR 0x04 +#define REG_MANU 0x67 /* Manual U value */ +#define REG_MANV 0x68 /* Manual V value */ +#define REG_HV 0x69 /* Manual banding filter MSB */ +#define REG_MBD 0x6a /* Manual banding filter value */ +#define REG_DBLV 0x6b /* reserved */ +#define REG_GSP 0x6c /* Gamma curve */ +#define GSP_LEN 15 +#define REG_GST 0x7c /* Gamma curve */ +#define GST_LEN 15 +#define REG_COM21 0x8b +#define REG_COM22 0x8c /* Edge enhancement, denoising */ +#define COM22_WHTPCOR 0x02 /* White pixel correction enable */ +#define COM22_WHTPCOROPT 0x01 /* White pixel correction option */ +#define COM22_DENOISE 0x10 /* White pixel correction option */ +#define REG_COM23 0x8d /* Color bar test, color gain */ +#define COM23_TEST_MODE 0x10 +#define REG_DBLC1 0x8f /* Digital BLC */ +#define REG_DBLC_B 0x90 /* Digital BLC B channel offset */ +#define REG_DBLC_R 0x91 /* Digital BLC R channel offset */ +#define REG_DM_LNL 0x92 /* Dummy line low 8 bits */ +#define REG_DM_LNH 0x93 /* Dummy line high 8 bits */ +#define REG_LCCFB 0x9d /* Lens Correction B channel */ +#define REG_LCCFR 0x9e /* Lens Correction R channel */ +#define REG_DBLC_GB 0x9f /* Digital BLC GB chan offset */ +#define REG_DBLC_GR 0xa0 /* Digital BLC GR chan offset */ +#define REG_AECHM 0xa1 /* Exposure value - bits AEC[15:10] */ +#define REG_BD50ST 0xa2 /* Banding filter value for 50Hz */ +#define REG_BD60ST 0xa3 /* Banding filter value for 60Hz */ +#define REG_NULL 0xff /* Array end token */ + +#define DEF_CLKRC 0x80 + +#define OV965X_ID(_msb, _lsb) ((_msb) << 8 | (_lsb)) +#define OV9650_ID 0x9650 +#define OV9652_ID 0x9652 + +struct ov965x_ctrls { + struct v4l2_ctrl_handler handler; + struct { + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + }; + struct { + struct v4l2_ctrl *auto_wb; + struct v4l2_ctrl *blue_balance; + struct v4l2_ctrl *red_balance; + }; + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + struct { + struct v4l2_ctrl *auto_gain; + struct v4l2_ctrl *gain; + }; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *light_freq; + u8 update; +}; + +struct ov965x_framesize { + u16 width; + u16 height; + u16 max_exp_lines; + const u8 *regs; +}; + +struct ov965x_interval { + struct v4l2_fract interval; + /* Maximum resolution for this interval */ + struct v4l2_frmsize_discrete size; + u8 clkrc_div; +}; + +enum gpio_id { + GPIO_PWDN, + GPIO_RST, + NUM_GPIOS, +}; + +struct ov965x { + struct v4l2_subdev sd; + struct media_pad pad; + enum v4l2_mbus_type bus_type; + int gpios[NUM_GPIOS]; + /* External master clock frequency */ + unsigned long mclk_frequency; + + /* Protects the struct fields below */ + struct mutex lock; + + struct i2c_client *client; + + /* Exposure row interval in us */ + unsigned int exp_row_interval; + + unsigned short id; + const struct ov965x_framesize *frame_size; + /* YUYV sequence (pixel format) control register */ + u8 tslb_reg; + struct v4l2_mbus_framefmt format; + + struct ov965x_ctrls ctrls; + /* Pointer to frame rate control data structure */ + const struct ov965x_interval *fiv; + + int streaming; + int power; + + u8 apply_frame_fmt; +}; + +struct i2c_rv { + u8 addr; + u8 value; +}; + +static const struct i2c_rv ov965x_init_regs[] = { + { REG_COM2, 0x10 }, /* Set soft sleep mode */ + { REG_COM5, 0x00 }, /* System clock options */ + { REG_COM2, 0x01 }, /* Output drive, soft sleep mode */ + { REG_COM10, 0x00 }, /* Slave mode, HREF vs HSYNC, signals negate */ + { REG_EDGE, 0xa6 }, /* Edge enhancement treshhold and factor */ + { REG_COM16, 0x02 }, /* Color matrix coeff double option */ + { REG_COM17, 0x08 }, /* Single frame out, banding filter */ + { 0x16, 0x06 }, + { REG_CHLF, 0xc0 }, /* Reserved */ + { 0x34, 0xbf }, + { 0xa8, 0x80 }, + { 0x96, 0x04 }, + { 0x8e, 0x00 }, + { REG_COM12, 0x77 }, /* HREF option, UV average */ + { 0x8b, 0x06 }, + { 0x35, 0x91 }, + { 0x94, 0x88 }, + { 0x95, 0x88 }, + { REG_COM15, 0xc1 }, /* Output range, RGB 555/565 */ + { REG_GRCOM, 0x2f }, /* Analog BLC & regulator */ + { REG_COM6, 0x43 }, /* HREF & ADBLC options */ + { REG_COM8, 0xe5 }, /* AGC/AEC options */ + { REG_COM13, 0x90 }, /* Gamma selection, colour matrix, UV delay */ + { REG_HV, 0x80 }, /* Manual banding filter MSB */ + { 0x5c, 0x96 }, /* Reserved up to 0xa5 */ + { 0x5d, 0x96 }, + { 0x5e, 0x10 }, + { 0x59, 0xeb }, + { 0x5a, 0x9c }, + { 0x5b, 0x55 }, + { 0x43, 0xf0 }, + { 0x44, 0x10 }, + { 0x45, 0x55 }, + { 0x46, 0x86 }, + { 0x47, 0x64 }, + { 0x48, 0x86 }, + { 0x5f, 0xe0 }, + { 0x60, 0x8c }, + { 0x61, 0x20 }, + { 0xa5, 0xd9 }, + { 0xa4, 0x74 }, /* reserved */ + { REG_COM23, 0x02 }, /* Color gain analog/_digital_ */ + { REG_COM8, 0xe7 }, /* Enable AEC, AWB, AEC */ + { REG_COM22, 0x23 }, /* Edge enhancement, denoising */ + { 0xa9, 0xb8 }, + { 0xaa, 0x92 }, + { 0xab, 0x0a }, + { REG_DBLC1, 0xdf }, /* Digital BLC */ + { REG_DBLC_B, 0x00 }, /* Digital BLC B chan offset */ + { REG_DBLC_R, 0x00 }, /* Digital BLC R chan offset */ + { REG_DBLC_GB, 0x00 }, /* Digital BLC GB chan offset */ + { REG_DBLC_GR, 0x00 }, + { REG_COM9, 0x3a }, /* Gain ceiling 16x */ + { REG_NULL, 0 } +}; + +#define NUM_FMT_REGS 14 +/* + * COM7, COM3, COM4, HSTART, HSTOP, HREF, VSTART, VSTOP, VREF, + * EXHCH, EXHCL, ADC, OCOM, OFON + */ +static const u8 frame_size_reg_addr[NUM_FMT_REGS] = { + 0x12, 0x0c, 0x0d, 0x17, 0x18, 0x32, 0x19, 0x1a, 0x03, + 0x2a, 0x2b, 0x37, 0x38, 0x39, +}; + +static const u8 ov965x_sxga_regs[NUM_FMT_REGS] = { + 0x00, 0x00, 0x00, 0x1e, 0xbe, 0xbf, 0x01, 0x81, 0x12, + 0x10, 0x34, 0x81, 0x93, 0x51, +}; + +static const u8 ov965x_vga_regs[NUM_FMT_REGS] = { + 0x40, 0x04, 0x80, 0x26, 0xc6, 0xed, 0x01, 0x3d, 0x00, + 0x10, 0x40, 0x91, 0x12, 0x43, +}; + +/* Determined empirically. */ +static const u8 ov965x_qvga_regs[NUM_FMT_REGS] = { + 0x10, 0x04, 0x80, 0x25, 0xc5, 0xbf, 0x00, 0x80, 0x12, + 0x10, 0x40, 0x91, 0x12, 0x43, +}; + +static const struct ov965x_framesize ov965x_framesizes[] = { + { + .width = SXGA_WIDTH, + .height = SXGA_HEIGHT, + .regs = ov965x_sxga_regs, + .max_exp_lines = 1048, + }, { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .regs = ov965x_vga_regs, + .max_exp_lines = 498, + }, { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .regs = ov965x_qvga_regs, + .max_exp_lines = 248, + }, +}; + +struct ov965x_pixfmt { + enum v4l2_mbus_pixelcode code; + u32 colorspace; + /* REG_TSLB value, only bits [3:2] may be set. */ + u8 tslb_reg; +}; + +static const struct ov965x_pixfmt ov965x_formats[] = { + { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 0x00}, + { V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG, 0x04}, + { V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG, 0x0c}, + { V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG, 0x08}, +}; + +/* + * This table specifies possible frame resolution and interval + * combinations. Default CLKRC[5:0] divider values are valid + * only for 24 MHz external clock frequency. + */ +static struct ov965x_interval ov965x_intervals[] = { + {{ 100, 625 }, { SXGA_WIDTH, SXGA_HEIGHT }, 0 }, /* 6.25 fps */ + {{ 10, 125 }, { VGA_WIDTH, VGA_HEIGHT }, 1 }, /* 12.5 fps */ + {{ 10, 125 }, { QVGA_WIDTH, QVGA_HEIGHT }, 3 }, /* 12.5 fps */ + {{ 1, 25 }, { VGA_WIDTH, VGA_HEIGHT }, 0 }, /* 25 fps */ + {{ 1, 25 }, { QVGA_WIDTH, QVGA_HEIGHT }, 1 }, /* 25 fps */ +}; + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct ov965x, ctrls.handler)->sd; +} + +static inline struct ov965x *to_ov965x(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov965x, sd); +} + +static int ov965x_read(struct i2c_client *client, u8 addr, u8 *val) +{ + u8 buf = addr; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &buf + }; + int ret; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1) { + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + + if (ret == 1) + *val = buf; + } + + v4l2_dbg(2, debug, client, "%s: 0x%02x @ 0x%02x. (%d)\n", + __func__, *val, addr, ret); + + return ret == 1 ? 0 : ret; +} + +static int ov965x_write(struct i2c_client *client, u8 addr, u8 val) +{ + u8 buf[2] = { addr, val }; + + int ret = i2c_master_send(client, buf, 2); + + v4l2_dbg(2, debug, client, "%s: 0x%02x @ 0x%02X (%d)\n", + __func__, val, addr, ret); + + return ret == 2 ? 0 : ret; +} + +static int ov965x_write_array(struct i2c_client *client, + const struct i2c_rv *regs) +{ + int i, ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov965x_write(client, regs[i].addr, regs[i].value); + + return ret; +} + +static int ov965x_set_default_gamma_curve(struct ov965x *ov965x) +{ + static const u8 gamma_curve[] = { + /* Values taken from OV application note. */ + 0x40, 0x30, 0x4b, 0x60, 0x70, 0x70, 0x70, 0x70, + 0x60, 0x60, 0x50, 0x48, 0x3a, 0x2e, 0x28, 0x22, + 0x04, 0x07, 0x10, 0x28, 0x36, 0x44, 0x52, 0x60, + 0x6c, 0x78, 0x8c, 0x9e, 0xbb, 0xd2, 0xe6 + }; + u8 addr = REG_GSP; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gamma_curve); i++) { + int ret = ov965x_write(ov965x->client, addr, gamma_curve[i]); + if (ret < 0) + return ret; + addr++; + } + + return 0; +}; + +static int ov965x_set_color_matrix(struct ov965x *ov965x) +{ + static const u8 mtx[] = { + /* MTX1..MTX9, MTXS */ + 0x3a, 0x3d, 0x03, 0x12, 0x26, 0x38, 0x40, 0x40, 0x40, 0x0d + }; + u8 addr = REG_MTX(1); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mtx); i++) { + int ret = ov965x_write(ov965x->client, addr, mtx[i]); + if (ret < 0) + return ret; + addr++; + } + + return 0; +} + +static void ov965x_gpio_set(int gpio, int val) +{ + if (gpio_is_valid(gpio)) + gpio_set_value(gpio, val); +} + +static void __ov965x_set_power(struct ov965x *ov965x, int on) +{ + if (on) { + ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 0); + ov965x_gpio_set(ov965x->gpios[GPIO_RST], 0); + usleep_range(25000, 26000); + } else { + ov965x_gpio_set(ov965x->gpios[GPIO_RST], 1); + ov965x_gpio_set(ov965x->gpios[GPIO_PWDN], 1); + } + + ov965x->streaming = 0; +} + +static int ov965x_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov965x *ov965x = to_ov965x(sd); + struct i2c_client *client = ov965x->client; + int ret = 0; + + v4l2_dbg(1, debug, client, "%s: on: %d\n", __func__, on); + + mutex_lock(&ov965x->lock); + if (ov965x->power == !on) { + __ov965x_set_power(ov965x, on); + if (on) { + ret = ov965x_write_array(client, + ov965x_init_regs); + ov965x->apply_frame_fmt = 1; + ov965x->ctrls.update = 1; + } + } + if (!ret) + ov965x->power += on ? 1 : -1; + + WARN_ON(ov965x->power < 0); + mutex_unlock(&ov965x->lock); + return ret; +} + +/* + * V4L2 controls + */ + +static void ov965x_update_exposure_ctrl(struct ov965x *ov965x) +{ + struct v4l2_ctrl *ctrl = ov965x->ctrls.exposure; + unsigned long fint, trow; + int min, max, def; + u8 clkrc; + + mutex_lock(&ov965x->lock); + if (WARN_ON(!ctrl || !ov965x->frame_size)) { + mutex_unlock(&ov965x->lock); + return; + } + clkrc = DEF_CLKRC + ov965x->fiv->clkrc_div; + /* Calculate internal clock frequency */ + fint = ov965x->mclk_frequency * ((clkrc >> 7) + 1) / + ((2 * ((clkrc & 0x3f) + 1))); + /* and the row interval (in us). */ + trow = (2 * 1520 * 1000000UL) / fint; + max = ov965x->frame_size->max_exp_lines * trow; + ov965x->exp_row_interval = trow; + mutex_unlock(&ov965x->lock); + + v4l2_dbg(1, debug, &ov965x->sd, "clkrc: %#x, fi: %lu, tr: %lu, %d\n", + clkrc, fint, trow, max); + + /* Update exposure time range to match current frame format. */ + min = (trow + 100) / 100; + max = (max - 100) / 100; + def = min + (max - min) / 2; + + if (v4l2_ctrl_modify_range(ctrl, min, max, 1, def)) + v4l2_err(&ov965x->sd, "Exposure ctrl range update failed\n"); +} + +static int ov965x_set_banding_filter(struct ov965x *ov965x, int value) +{ + unsigned long mbd, light_freq; + int ret; + u8 reg; + + ret = ov965x_read(ov965x->client, REG_COM8, ®); + if (!ret) { + if (value == V4L2_CID_POWER_LINE_FREQUENCY_DISABLED) + reg &= ~COM8_BFILT; + else + reg |= COM8_BFILT; + ret = ov965x_write(ov965x->client, REG_COM8, reg); + } + if (value == V4L2_CID_POWER_LINE_FREQUENCY_DISABLED) + return 0; + if (WARN_ON(ov965x->fiv == NULL)) + return -EINVAL; + /* Set minimal exposure time for 50/60 HZ lighting */ + if (value == V4L2_CID_POWER_LINE_FREQUENCY_50HZ) + light_freq = 50; + else + light_freq = 60; + mbd = (1000UL * ov965x->fiv->interval.denominator * + ov965x->frame_size->max_exp_lines) / + ov965x->fiv->interval.numerator; + mbd = ((mbd / (light_freq * 2)) + 500) / 1000UL; + + return ov965x_write(ov965x->client, REG_MBD, mbd); +} + +static int ov965x_set_white_balance(struct ov965x *ov965x, int awb) +{ + int ret; + u8 reg; + + ret = ov965x_read(ov965x->client, REG_COM8, ®); + if (!ret) { + reg = awb ? reg | REG_COM8 : reg & ~REG_COM8; + ret = ov965x_write(ov965x->client, REG_COM8, reg); + } + if (!ret && !awb) { + ret = ov965x_write(ov965x->client, REG_BLUE, + ov965x->ctrls.blue_balance->val); + if (ret < 0) + return ret; + ret = ov965x_write(ov965x->client, REG_RED, + ov965x->ctrls.red_balance->val); + } + return ret; +} + +#define NUM_BR_LEVELS 7 +#define NUM_BR_REGS 3 + +static int ov965x_set_brightness(struct ov965x *ov965x, int val) +{ + static const u8 regs[NUM_BR_LEVELS + 1][NUM_BR_REGS] = { + { REG_AEW, REG_AEB, REG_VPT }, + { 0x1c, 0x12, 0x50 }, /* -3 */ + { 0x3d, 0x30, 0x71 }, /* -2 */ + { 0x50, 0x44, 0x92 }, /* -1 */ + { 0x70, 0x64, 0xc3 }, /* 0 */ + { 0x90, 0x84, 0xd4 }, /* +1 */ + { 0xc4, 0xbf, 0xf9 }, /* +2 */ + { 0xd8, 0xd0, 0xfa }, /* +3 */ + }; + int i, ret = 0; + + val += (NUM_BR_LEVELS / 2 + 1); + if (val > NUM_BR_LEVELS) + return -EINVAL; + + for (i = 0; i < NUM_BR_REGS && !ret; i++) + ret = ov965x_write(ov965x->client, regs[0][i], + regs[val][i]); + return ret; +} + +static int ov965x_set_gain(struct ov965x *ov965x, int auto_gain) +{ + struct i2c_client *client = ov965x->client; + struct ov965x_ctrls *ctrls = &ov965x->ctrls; + int ret = 0; + u8 reg; + /* + * For manual mode we need to disable AGC first, so + * gain value in REG_VREF, REG_GAIN is not overwritten. + */ + if (ctrls->auto_gain->is_new) { + ret = ov965x_read(client, REG_COM8, ®); + if (ret < 0) + return ret; + if (ctrls->auto_gain->val) + reg |= COM8_AGC; + else + reg &= ~COM8_AGC; + ret = ov965x_write(client, REG_COM8, reg); + if (ret < 0) + return ret; + } + + if (ctrls->gain->is_new && !auto_gain) { + unsigned int gain = ctrls->gain->val; + unsigned int rgain; + int m; + /* + * Convert gain control value to the sensor's gain + * registers (VREF[7:6], GAIN[7:0]) format. + */ + for (m = 6; m >= 0; m--) + if (gain >= (1 << m) * 16) + break; + rgain = (gain - ((1 << m) * 16)) / (1 << m); + rgain |= (((1 << m) - 1) << 4); + + ret = ov965x_write(client, REG_GAIN, rgain & 0xff); + if (ret < 0) + return ret; + ret = ov965x_read(client, REG_VREF, ®); + if (ret < 0) + return ret; + reg &= ~VREF_GAIN_MASK; + reg |= (((rgain >> 8) & 0x3) << 6); + ret = ov965x_write(client, REG_VREF, reg); + if (ret < 0) + return ret; + /* Return updated control's value to userspace */ + ctrls->gain->val = (1 << m) * (16 + (rgain & 0xf)); + } + + return ret; +} + +static int ov965x_set_sharpness(struct ov965x *ov965x, unsigned int value) +{ + u8 com14, edge; + int ret; + + ret = ov965x_read(ov965x->client, REG_COM14, &com14); + if (ret < 0) + return ret; + ret = ov965x_read(ov965x->client, REG_EDGE, &edge); + if (ret < 0) + return ret; + com14 = value ? com14 | COM14_EDGE_EN : com14 & ~COM14_EDGE_EN; + value--; + if (value > 0x0f) { + com14 |= COM14_EEF_X2; + value >>= 1; + } else { + com14 &= ~COM14_EEF_X2; + } + ret = ov965x_write(ov965x->client, REG_COM14, com14); + if (ret < 0) + return ret; + + edge &= ~EDGE_FACTOR_MASK; + edge |= ((u8)value & 0x0f); + + return ov965x_write(ov965x->client, REG_EDGE, edge); +} + +static int ov965x_set_exposure(struct ov965x *ov965x, int exp) +{ + struct i2c_client *client = ov965x->client; + struct ov965x_ctrls *ctrls = &ov965x->ctrls; + bool auto_exposure = (exp == V4L2_EXPOSURE_AUTO); + int ret; + u8 reg; + + if (ctrls->auto_exp->is_new) { + ret = ov965x_read(client, REG_COM8, ®); + if (ret < 0) + return ret; + if (auto_exposure) + reg |= (COM8_AEC | COM8_AGC); + else + reg &= ~(COM8_AEC | COM8_AGC); + ret = ov965x_write(client, REG_COM8, reg); + if (ret < 0) + return ret; + } + + if (!auto_exposure && ctrls->exposure->is_new) { + unsigned int exposure = (ctrls->exposure->val * 100) + / ov965x->exp_row_interval; + /* + * Manual exposure value + * [b15:b0] - AECHM (b15:b10), AECH (b9:b2), COM1 (b1:b0) + */ + ret = ov965x_write(client, REG_COM1, exposure & 0x3); + if (!ret) + ret = ov965x_write(client, REG_AECH, + (exposure >> 2) & 0xff); + if (!ret) + ret = ov965x_write(client, REG_AECHM, + (exposure >> 10) & 0x3f); + /* Update the value to minimize rounding errors */ + ctrls->exposure->val = ((exposure * ov965x->exp_row_interval) + + 50) / 100; + if (ret < 0) + return ret; + } + + v4l2_ctrl_activate(ov965x->ctrls.brightness, !exp); + return 0; +} + +static int ov965x_set_flip(struct ov965x *ov965x) +{ + u8 mvfp = 0; + + if (ov965x->ctrls.hflip->val) + mvfp |= MVFP_MIRROR; + + if (ov965x->ctrls.vflip->val) + mvfp |= MVFP_FLIP; + + return ov965x_write(ov965x->client, REG_MVFP, mvfp); +} + +#define NUM_SAT_LEVELS 5 +#define NUM_SAT_REGS 6 + +static int ov965x_set_saturation(struct ov965x *ov965x, int val) +{ + static const u8 regs[NUM_SAT_LEVELS][NUM_SAT_REGS] = { + /* MTX(1)...MTX(6) */ + { 0x1d, 0x1f, 0x02, 0x09, 0x13, 0x1c }, /* -2 */ + { 0x2e, 0x31, 0x02, 0x0e, 0x1e, 0x2d }, /* -1 */ + { 0x3a, 0x3d, 0x03, 0x12, 0x26, 0x38 }, /* 0 */ + { 0x46, 0x49, 0x04, 0x16, 0x2e, 0x43 }, /* +1 */ + { 0x57, 0x5c, 0x05, 0x1b, 0x39, 0x54 }, /* +2 */ + }; + u8 addr = REG_MTX(1); + int i, ret = 0; + + val += (NUM_SAT_LEVELS / 2); + if (val >= NUM_SAT_LEVELS) + return -EINVAL; + + for (i = 0; i < NUM_SAT_REGS && !ret; i++) + ret = ov965x_write(ov965x->client, addr + i, regs[val][i]); + + return ret; +} + +static int ov965x_set_test_pattern(struct ov965x *ov965x, int value) +{ + int ret; + u8 reg; + + ret = ov965x_read(ov965x->client, REG_COM23, ®); + if (ret < 0) + return ret; + reg = value ? reg | COM23_TEST_MODE : reg & ~COM23_TEST_MODE; + return ov965x_write(ov965x->client, REG_COM23, reg); +} + +static int __g_volatile_ctrl(struct ov965x *ov965x, struct v4l2_ctrl *ctrl) +{ + struct i2c_client *client = ov965x->client; + unsigned int exposure, gain, m; + u8 reg0, reg1, reg2; + int ret; + + if (!ov965x->power) + return 0; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + if (!ctrl->val) + return 0; + ret = ov965x_read(client, REG_GAIN, ®0); + if (ret < 0) + return ret; + ret = ov965x_read(client, REG_VREF, ®1); + if (ret < 0) + return ret; + gain = ((reg1 >> 6) << 8) | reg0; + m = 0x01 << fls(gain >> 4); + ov965x->ctrls.gain->val = m * (16 + (gain & 0xf)); + break; + + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->val == V4L2_EXPOSURE_MANUAL) + return 0; + ret = ov965x_read(client, REG_COM1, ®0); + if (!ret) + ret = ov965x_read(client, REG_AECH, ®1); + if (!ret) + ret = ov965x_read(client, REG_AECHM, ®2); + if (ret < 0) + return ret; + exposure = ((reg2 & 0x3f) << 10) | (reg1 << 2) | + (reg0 & 0x3); + ov965x->ctrls.exposure->val = ((exposure * + ov965x->exp_row_interval) + 50) / 100; + break; + } + + return 0; +} + +static int ov965x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct ov965x *ov965x = to_ov965x(sd); + int ret; + + v4l2_dbg(1, debug, sd, "g_ctrl: %s\n", ctrl->name); + + mutex_lock(&ov965x->lock); + ret = __g_volatile_ctrl(ov965x, ctrl); + mutex_unlock(&ov965x->lock); + return ret; +} + +static int ov965x_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct ov965x *ov965x = to_ov965x(sd); + int ret = -EINVAL; + + v4l2_dbg(1, debug, sd, "s_ctrl: %s, value: %d. power: %d\n", + ctrl->name, ctrl->val, ov965x->power); + + mutex_lock(&ov965x->lock); + /* + * If the device is not powered up now postpone applying control's + * value to the hardware, until it is ready to accept commands. + */ + if (ov965x->power == 0) { + mutex_unlock(&ov965x->lock); + return 0; + } + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = ov965x_set_white_balance(ov965x, ctrl->val); + break; + + case V4L2_CID_BRIGHTNESS: + ret = ov965x_set_brightness(ov965x, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + ret = ov965x_set_exposure(ov965x, ctrl->val); + break; + + case V4L2_CID_AUTOGAIN: + ret = ov965x_set_gain(ov965x, ctrl->val); + break; + + case V4L2_CID_HFLIP: + ret = ov965x_set_flip(ov965x); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + ret = ov965x_set_banding_filter(ov965x, ctrl->val); + break; + + case V4L2_CID_SATURATION: + ret = ov965x_set_saturation(ov965x, ctrl->val); + break; + + case V4L2_CID_SHARPNESS: + ret = ov965x_set_sharpness(ov965x, ctrl->val); + break; + + case V4L2_CID_TEST_PATTERN: + ret = ov965x_set_test_pattern(ov965x, ctrl->val); + break; + } + + mutex_unlock(&ov965x->lock); + return ret; +} + +static const struct v4l2_ctrl_ops ov965x_ctrl_ops = { + .g_volatile_ctrl = ov965x_g_volatile_ctrl, + .s_ctrl = ov965x_s_ctrl, +}; + +static const char * const test_pattern_menu[] = { + "Disabled", + "Color bars", + NULL +}; + +static int ov965x_initialize_controls(struct ov965x *ov965x) +{ + const struct v4l2_ctrl_ops *ops = &ov965x_ctrl_ops; + struct ov965x_ctrls *ctrls = &ov965x->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + int ret; + + ret = v4l2_ctrl_handler_init(hdl, 16); + if (ret < 0) + return ret; + + /* Auto/manual white balance */ + ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BLUE_BALANCE, + 0, 0xff, 1, 0x80); + ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_RED_BALANCE, + 0, 0xff, 1, 0x80); + /* Auto/manual exposure */ + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); + /* Exposure time, in 100 us units. min/max is updated dynamically. */ + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_EXPOSURE_ABSOLUTE, + 2, 1500, 1, 500); + /* Auto/manual gain */ + ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTOGAIN, + 0, 1, 1, 1); + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, + 16, 64 * (16 + 15), 1, 64 * 16); + + ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, + -2, 2, 1, 0); + ctrls->brightness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, + -3, 3, 1, 0); + ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, + 0, 32, 1, 6); + + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + + ctrls->light_freq = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, ~0x7, + V4L2_CID_POWER_LINE_FREQUENCY_50HZ); + + v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(test_pattern_menu) - 1, 0, 0, + test_pattern_menu); + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; + ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; + + v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false); + v4l2_ctrl_auto_cluster(3, &ctrls->auto_gain, 0, true); + v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 1, true); + v4l2_ctrl_cluster(2, &ctrls->hflip); + + ov965x->sd.ctrl_handler = hdl; + return 0; +} + +/* + * V4L2 subdev video and pad level operations + */ +static void ov965x_get_default_format(struct v4l2_mbus_framefmt *mf) +{ + mf->width = ov965x_framesizes[0].width; + mf->height = ov965x_framesizes[0].height; + mf->colorspace = ov965x_formats[0].colorspace; + mf->code = ov965x_formats[0].code; + mf->field = V4L2_FIELD_NONE; +} + +static int ov965x_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(ov965x_formats)) + return -EINVAL; + + code->code = ov965x_formats[code->index].code; + return 0; +} + +static int ov965x_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int i = ARRAY_SIZE(ov965x_formats); + + if (fse->index > ARRAY_SIZE(ov965x_framesizes)) + return -EINVAL; + + while (--i) + if (fse->code == ov965x_formats[i].code) + break; + + fse->code = ov965x_formats[i].code; + + fse->min_width = ov965x_framesizes[fse->index].width; + fse->max_width = fse->min_width; + fse->max_height = ov965x_framesizes[fse->index].height; + fse->min_height = fse->max_height; + + return 0; +} + +static int ov965x_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov965x *ov965x = to_ov965x(sd); + + mutex_lock(&ov965x->lock); + fi->interval = ov965x->fiv->interval; + mutex_unlock(&ov965x->lock); + + return 0; +} + +static int __ov965x_set_frame_interval(struct ov965x *ov965x, + struct v4l2_subdev_frame_interval *fi) +{ + struct v4l2_mbus_framefmt *mbus_fmt = &ov965x->format; + const struct ov965x_interval *fiv = &ov965x_intervals[0]; + u64 req_int, err, min_err = ~0ULL; + unsigned int i; + + + if (fi->interval.denominator == 0) + return -EINVAL; + + req_int = (u64)(fi->interval.numerator * 10000) / + fi->interval.denominator; + + for (i = 0; i < ARRAY_SIZE(ov965x_intervals); i++) { + const struct ov965x_interval *iv = &ov965x_intervals[i]; + + if (mbus_fmt->width != iv->size.width || + mbus_fmt->height != iv->size.height) + continue; + err = abs64((u64)(iv->interval.numerator * 10000) / + iv->interval.denominator - req_int); + if (err < min_err) { + fiv = iv; + min_err = err; + } + } + ov965x->fiv = fiv; + + v4l2_dbg(1, debug, &ov965x->sd, "Changed frame interval to %u us\n", + fiv->interval.numerator * 1000000 / fiv->interval.denominator); + + return 0; +} + +static int ov965x_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct ov965x *ov965x = to_ov965x(sd); + int ret; + + v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", + fi->interval.numerator, fi->interval.denominator); + + mutex_lock(&ov965x->lock); + ret = __ov965x_set_frame_interval(ov965x, fi); + ov965x->apply_frame_fmt = 1; + mutex_unlock(&ov965x->lock); + return ret; +} + +static int ov965x_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct ov965x *ov965x = to_ov965x(sd); + struct v4l2_mbus_framefmt *mf; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, 0); + fmt->format = *mf; + return 0; + } + + mutex_lock(&ov965x->lock); + fmt->format = ov965x->format; + mutex_unlock(&ov965x->lock); + + return 0; +} + +static void __ov965x_try_frame_size(struct v4l2_mbus_framefmt *mf, + const struct ov965x_framesize **size) +{ + const struct ov965x_framesize *fsize = &ov965x_framesizes[0], + *match = NULL; + int i = ARRAY_SIZE(ov965x_framesizes); + unsigned int min_err = UINT_MAX; + + while (i--) { + int err = abs(fsize->width - mf->width) + + abs(fsize->height - mf->height); + if (err < min_err) { + min_err = err; + match = fsize; + } + fsize++; + } + if (!match) + match = &ov965x_framesizes[0]; + mf->width = match->width; + mf->height = match->height; + if (size) + *size = match; +} + +static int ov965x_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + unsigned int index = ARRAY_SIZE(ov965x_formats); + struct v4l2_mbus_framefmt *mf = &fmt->format; + struct ov965x *ov965x = to_ov965x(sd); + const struct ov965x_framesize *size = NULL; + int ret = 0; + + __ov965x_try_frame_size(mf, &size); + + while (--index) + if (ov965x_formats[index].code == mf->code) + break; + + mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->code = ov965x_formats[index].code; + mf->field = V4L2_FIELD_NONE; + + mutex_lock(&ov965x->lock); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + if (fh != NULL) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + } + } else { + if (ov965x->streaming) { + ret = -EBUSY; + } else { + ov965x->frame_size = size; + ov965x->format = fmt->format; + ov965x->tslb_reg = ov965x_formats[index].tslb_reg; + ov965x->apply_frame_fmt = 1; + } + } + + if (!ret && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + struct v4l2_subdev_frame_interval fiv = { + .interval = { 0, 1 } + }; + /* Reset to minimum possible frame interval */ + __ov965x_set_frame_interval(ov965x, &fiv); + } + mutex_unlock(&ov965x->lock); + + if (!ret) + ov965x_update_exposure_ctrl(ov965x); + + return ret; +} + +static int ov965x_set_frame_size(struct ov965x *ov965x) +{ + int i, ret = 0; + + for (i = 0; ret == 0 && i < NUM_FMT_REGS; i++) + ret = ov965x_write(ov965x->client, frame_size_reg_addr[i], + ov965x->frame_size->regs[i]); + return ret; +} + +static int __ov965x_set_params(struct ov965x *ov965x) +{ + struct i2c_client *client = ov965x->client; + struct ov965x_ctrls *ctrls = &ov965x->ctrls; + int ret = 0; + u8 reg; + + if (ov965x->apply_frame_fmt) { + reg = DEF_CLKRC + ov965x->fiv->clkrc_div; + ret = ov965x_write(client, REG_CLKRC, reg); + if (ret < 0) + return ret; + ret = ov965x_set_frame_size(ov965x); + if (ret < 0) + return ret; + ret = ov965x_read(client, REG_TSLB, ®); + if (ret < 0) + return ret; + reg &= ~TSLB_YUYV_MASK; + reg |= ov965x->tslb_reg; + ret = ov965x_write(client, REG_TSLB, reg); + if (ret < 0) + return ret; + } + ret = ov965x_set_default_gamma_curve(ov965x); + if (ret < 0) + return ret; + ret = ov965x_set_color_matrix(ov965x); + if (ret < 0) + return ret; + /* + * Select manual banding filter, the filter will + * be enabled further if required. + */ + ret = ov965x_read(client, REG_COM11, ®); + if (!ret) + reg |= COM11_BANDING; + ret = ov965x_write(client, REG_COM11, reg); + if (ret < 0) + return ret; + /* + * Banding filter (REG_MBD value) needs to match selected + * resolution and frame rate, so it's always updated here. + */ + return ov965x_set_banding_filter(ov965x, ctrls->light_freq->val); +} + +static int ov965x_s_stream(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov965x *ov965x = to_ov965x(sd); + struct ov965x_ctrls *ctrls = &ov965x->ctrls; + int ret = 0; + + v4l2_dbg(1, debug, client, "%s: on: %d\n", __func__, on); + + mutex_lock(&ov965x->lock); + if (ov965x->streaming == !on) { + if (on) + ret = __ov965x_set_params(ov965x); + + if (!ret && ctrls->update) { + /* + * ov965x_s_ctrl callback takes the mutex + * so it needs to be released here. + */ + mutex_unlock(&ov965x->lock); + ret = v4l2_ctrl_handler_setup(&ctrls->handler); + + mutex_lock(&ov965x->lock); + if (!ret) + ctrls->update = 0; + } + if (!ret) + ret = ov965x_write(client, REG_COM2, + on ? 0x01 : 0x11); + } + if (!ret) + ov965x->streaming += on ? 1 : -1; + + WARN_ON(ov965x->streaming < 0); + mutex_unlock(&ov965x->lock); + + return ret; +} + +/* + * V4L2 subdev internal operations + */ +static int ov965x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); + + ov965x_get_default_format(mf); + return 0; +} + +static const struct v4l2_subdev_pad_ops ov965x_pad_ops = { + .enum_mbus_code = ov965x_enum_mbus_code, + .enum_frame_size = ov965x_enum_frame_sizes, + .get_fmt = ov965x_get_fmt, + .set_fmt = ov965x_set_fmt, +}; + +static const struct v4l2_subdev_video_ops ov965x_video_ops = { + .s_stream = ov965x_s_stream, + .g_frame_interval = ov965x_g_frame_interval, + .s_frame_interval = ov965x_s_frame_interval, + +}; + +static const struct v4l2_subdev_internal_ops ov965x_sd_internal_ops = { + .open = ov965x_open, +}; + +static const struct v4l2_subdev_core_ops ov965x_core_ops = { + .s_power = ov965x_s_power, + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_ops ov965x_subdev_ops = { + .core = &ov965x_core_ops, + .pad = &ov965x_pad_ops, + .video = &ov965x_video_ops, +}; + +/* + * Reset and power down GPIOs configuration + */ +static int ov965x_configure_gpios(struct ov965x *ov965x, + const struct ov9650_platform_data *pdata) +{ + int ret, i; + + ov965x->gpios[GPIO_PWDN] = pdata->gpio_pwdn; + ov965x->gpios[GPIO_RST] = pdata->gpio_reset; + + for (i = 0; i < ARRAY_SIZE(ov965x->gpios); i++) { + int gpio = ov965x->gpios[i]; + + if (!gpio_is_valid(gpio)) + continue; + ret = devm_gpio_request_one(&ov965x->client->dev, gpio, + GPIOF_OUT_INIT_HIGH, "OV965X"); + if (ret < 0) + return ret; + v4l2_dbg(1, debug, &ov965x->sd, "set gpio %d to 1\n", gpio); + + gpio_set_value(gpio, 1); + gpio_export(gpio, 0); + ov965x->gpios[i] = gpio; + } + + return 0; +} + +static int ov965x_detect_sensor(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov965x *ov965x = to_ov965x(sd); + u8 pid, ver; + int ret; + + mutex_lock(&ov965x->lock); + __ov965x_set_power(ov965x, 1); + usleep_range(25000, 26000); + + /* Check sensor revision */ + ret = ov965x_read(client, REG_PID, &pid); + if (!ret) + ret = ov965x_read(client, REG_VER, &ver); + + __ov965x_set_power(ov965x, 0); + + if (!ret) { + ov965x->id = OV965X_ID(pid, ver); + if (ov965x->id == OV9650_ID || ov965x->id == OV9652_ID) { + v4l2_info(sd, "Found OV%04X sensor\n", ov965x->id); + } else { + v4l2_err(sd, "Sensor detection failed (%04X, %d)\n", + ov965x->id, ret); + ret = -ENODEV; + } + } + mutex_unlock(&ov965x->lock); + + return ret; +} + +static int ov965x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct ov9650_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + struct ov965x *ov965x; + int ret; + + if (pdata == NULL) { + dev_err(&client->dev, "platform data not specified\n"); + return -EINVAL; + } + + if (pdata->mclk_frequency == 0) { + dev_err(&client->dev, "MCLK frequency not specified\n"); + return -EINVAL; + } + + ov965x = devm_kzalloc(&client->dev, sizeof(*ov965x), GFP_KERNEL); + if (!ov965x) + return -ENOMEM; + + mutex_init(&ov965x->lock); + ov965x->client = client; + ov965x->mclk_frequency = pdata->mclk_frequency; + + sd = &ov965x->sd; + v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops); + strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); + + sd->internal_ops = &ov965x_sd_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + + ret = ov965x_configure_gpios(ov965x, pdata); + if (ret < 0) + return ret; + + ov965x->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + ret = media_entity_init(&sd->entity, 1, &ov965x->pad, 0); + if (ret < 0) + return ret; + + ret = ov965x_initialize_controls(ov965x); + if (ret < 0) + goto err_me; + + ov965x_get_default_format(&ov965x->format); + ov965x->frame_size = &ov965x_framesizes[0]; + ov965x->fiv = &ov965x_intervals[0]; + + ret = ov965x_detect_sensor(sd); + if (ret < 0) + goto err_ctrls; + + /* Update exposure time min/max to match frame format */ + ov965x_update_exposure_ctrl(ov965x); + + return 0; +err_ctrls: + v4l2_ctrl_handler_free(sd->ctrl_handler); +err_me: + media_entity_cleanup(&sd->entity); + return ret; +} + +static int ov965x_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); + + return 0; +} + +static const struct i2c_device_id ov965x_id[] = { + { "OV9650", 0 }, + { "OV9652", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, ov965x_id); + +static struct i2c_driver ov965x_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = ov965x_probe, + .remove = ov965x_remove, + .id_table = ov965x_id, +}; + +module_i2c_driver(ov965x_i2c_driver); + +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_DESCRIPTION("OV9650/OV9652 CMOS Image Sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/include/media/ov9650.h b/include/media/ov9650.h new file mode 100644 index 000000000000..d630cf9e028d --- /dev/null +++ b/include/media/ov9650.h @@ -0,0 +1,27 @@ +/* + * OV9650/OV9652 camera sensors driver + * + * Copyright (C) 2013 Sylwester Nawrocki + * + * 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 OV9650_H_ +#define OV9650_H_ + +/** + * struct ov9650_platform_data - ov9650 driver platform data + * @mclk_frequency: the sensor's master clock frequency in Hz + * @gpio_pwdn: number of a GPIO connected to OV965X PWDN pin + * @gpio_reset: number of a GPIO connected to OV965X RESET pin + * + * If any of @gpio_pwdn or @gpio_reset are unused then they should be + * set to a negative value. @mclk_frequency must always be specified. + */ +struct ov9650_platform_data { + unsigned long mclk_frequency; + int gpio_pwdn; + int gpio_reset; +}; +#endif /* OV9650_H_ */ From ba1066d2e9686a5c96c5c0dfcbda7f874fa7b88d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 28 Sep 2012 08:18:07 -0300 Subject: [PATCH 1909/4607] [media] tuner-core: map audmode to STEREO for radio devices Fixes a v4l2-compliance error: setting audmode to a value other than mono or stereo for a radio device should map to MODE_STEREO. The spec specifies that for radio devices only mono and stereo audmodes are valid. If the user specifies another audmode in v4l2_tuner, then that should be mapped to valid audmode. That didn't happen here. Note that tuner drivers might decide to limit the possible audmode even further if it only supports mono. In that case the tuner driver can set audmode to mono. However, that new value wasn't copied back to t->audmode, and that has been fixed as well in this patch. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/tuner-core.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index b5a819af2b8c..b5a8aac2e126 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -1013,6 +1013,11 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) t->standby = false; analog_ops->set_params(&t->fe, ¶ms); + /* + * The tuner driver might decide to change the audmode if it only + * supports stereo, so update t->audmode. + */ + t->audmode = params.audmode; } /* @@ -1235,8 +1240,18 @@ static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) if (set_mode(t, vt->type)) return 0; - if (t->mode == V4L2_TUNER_RADIO) + if (t->mode == V4L2_TUNER_RADIO) { t->audmode = vt->audmode; + /* + * For radio audmode can only be mono or stereo. Map any + * other values to stereo. The actual tuner driver that is + * called in set_radio_freq can decide to limit the audmode to + * mono if only mono is supported. + */ + if (t->audmode != V4L2_TUNER_MODE_MONO && + t->audmode != V4L2_TUNER_MODE_STEREO) + t->audmode = V4L2_TUNER_MODE_STEREO; + } set_freq(t, 0); return 0; From 33133ea7aca7eedf8b1b4cee514c76dce7654a8c Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Fri, 11 Jan 2013 11:29:32 -0300 Subject: [PATCH 1910/4607] [media] s5p-mfc: Fix a watchdog bug Fixed wrong condition in firmware reload function used by the watchdog. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c index 1682271c2453..2e5f30b40dea 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c @@ -130,7 +130,7 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev) release_firmware(fw_blob); return -ENOMEM; } - if (dev->fw_virt_addr) { + if (!dev->fw_virt_addr) { mfc_err("MFC firmware is not allocated\n"); release_firmware(fw_blob); return -EINVAL; From fa8880bece7321d61a7a5e7bf4b67832071ee047 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 11 Jan 2013 06:36:19 -0300 Subject: [PATCH 1911/4607] [media] s5p-fimc: Fix bytesperline value for V4L2_PIX_FMT_YUV420M format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure bytesperline for Cb, Cr planes for V4L2_PIX_FMT_YUV420M format is half of the Y plane value, rather than having same bytesperline for all planes. While at it, simplify the bytesperline parameter handling by storing it when image format is set and returning those values when getting the format, instead of recalculating bytesperline from intermediate parameters. Reported-by: Sebastian Dröge Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/s5p-fimc/fimc-capture.c | 8 +++--- drivers/media/platform/s5p-fimc/fimc-core.c | 27 +++++++++---------- drivers/media/platform/s5p-fimc/fimc-core.h | 4 ++- drivers/media/platform/s5p-fimc/fimc-m2m.c | 7 ++--- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 18a70e4a0b0f..df6fc6c7eecd 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -950,9 +950,9 @@ static int fimc_cap_g_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct fimc_dev *fimc = video_drvdata(file); - struct fimc_ctx *ctx = fimc->vid_cap.ctx; - return fimc_fill_format(&ctx->d_frame, f); + __fimc_get_format(&fimc->vid_cap.ctx->d_frame, f); + return 0; } static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, @@ -1074,8 +1074,10 @@ static int __fimc_capture_set_format(struct fimc_dev *fimc, return ret; } - for (i = 0; i < ff->fmt->memplanes; i++) + for (i = 0; i < ff->fmt->memplanes; i++) { + ff->bytesperline[i] = pix->plane_fmt[i].bytesperline; ff->payload[i] = pix->plane_fmt[i].sizeimage; + } set_frame_bounds(ff, pix->width, pix->height); /* Reset the composition rectangle if not yet configured */ diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 2e86f44edd23..92d477c91811 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -691,7 +691,7 @@ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx) v4l2_ctrl_unlock(ctrl); } -int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f) +void __fimc_get_format(struct fimc_frame *frame, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; int i; @@ -704,19 +704,9 @@ int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f) pixm->num_planes = frame->fmt->memplanes; for (i = 0; i < pixm->num_planes; ++i) { - int bpl = frame->f_width; - if (frame->fmt->colplanes == 1) /* packed formats */ - bpl = (bpl * frame->fmt->depth[0]) / 8; - pixm->plane_fmt[i].bytesperline = bpl; - - if (frame->fmt->flags & FMT_FLAGS_COMPRESSED) { - pixm->plane_fmt[i].sizeimage = frame->payload[i]; - continue; - } - pixm->plane_fmt[i].sizeimage = (frame->o_width * - frame->o_height * frame->fmt->depth[i]) / 8; + pixm->plane_fmt[i].bytesperline = frame->bytesperline[i]; + pixm->plane_fmt[i].sizeimage = frame->payload[i]; } - return 0; } void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f) @@ -765,9 +755,16 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, if (fmt->colplanes == 1 && /* Packed */ (bpl == 0 || ((bpl * 8) / fmt->depth[i]) < pix->width)) bpl = (pix->width * fmt->depth[0]) / 8; - - if (i == 0) /* Same bytesperline for each plane. */ + /* + * Currently bytesperline for each plane is same, except + * V4L2_PIX_FMT_YUV420M format. This calculation may need + * to be changed when other multi-planar formats are added + * to the fimc_formats[] array. + */ + if (i == 0) bytesperline = bpl; + else if (i == 1 && fmt->memplanes == 3) + bytesperline /= 2; plane_fmt->bytesperline = bytesperline; plane_fmt->sizeimage = max((pix->width * pix->height * diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/s5p-fimc/fimc-core.h index 424ff960f0d9..cf760c364099 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.h +++ b/drivers/media/platform/s5p-fimc/fimc-core.h @@ -265,6 +265,7 @@ struct fimc_vid_buffer { * @width: image pixel width * @height: image pixel weight * @payload: image size in bytes (w x h x bpp) + * @bytesperline: bytesperline value for each plane * @paddr: image frame buffer physical addresses * @dma_offset: DMA offset in bytes * @fmt: fimc color format pointer @@ -279,6 +280,7 @@ struct fimc_frame { u32 width; u32 height; unsigned int payload[VIDEO_MAX_PLANES]; + unsigned int bytesperline[VIDEO_MAX_PLANES]; struct fimc_addr paddr; struct fimc_dma_offset dma_offset; struct fimc_fmt *fmt; @@ -637,7 +639,7 @@ int fimc_ctrls_create(struct fimc_ctx *ctx); void fimc_ctrls_delete(struct fimc_ctx *ctx); void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active); void fimc_alpha_ctrl_update(struct fimc_ctx *ctx); -int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f); +void __fimc_get_format(struct fimc_frame *frame, struct v4l2_format *f); void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, struct v4l2_pix_format_mplane *pix); struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code, diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index 1d57f3b87aa3..1eabd7e74849 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -294,7 +294,8 @@ static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, if (IS_ERR(frame)) return PTR_ERR(frame); - return fimc_fill_format(frame, f); + __fimc_get_format(frame, f); + return 0; } static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) @@ -389,8 +390,8 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, fimc_alpha_ctrl_update(ctx); for (i = 0; i < frame->fmt->colplanes; i++) { - frame->payload[i] = - (pix->width * pix->height * frame->fmt->depth[i]) / 8; + frame->bytesperline[i] = pix->plane_fmt[i].bytesperline; + frame->payload[i] = pix->plane_fmt[i].sizeimage; } fimc_fill_frame(frame, f); From 0e5d61d87b3a1ad5591e0dfbe4c548f862e9f5a6 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Mon, 14 Jan 2013 06:09:41 -0300 Subject: [PATCH 1912/4607] [media] s5p-mfc: Use NULL instead of 0 for pointer Fixes the following warning: drivers/media/platform/s5p-mfc/s5p_mfc_opr.c:56:27: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Acked-by: Hans Verkuil Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_opr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c index b4c194331d8c..10f8ac37cecd 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c @@ -53,7 +53,7 @@ void s5p_mfc_release_priv_buf(struct device *dev, { if (b->virt) { dma_free_coherent(dev, b->size, b->virt, b->dma); - b->virt = 0; + b->virt = NULL; b->dma = 0; b->size = 0; } From 62ce272d87f09f34f70a9b3f783783af6c1a09d8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 17 Jan 2013 00:07:18 -0300 Subject: [PATCH 1913/4607] [media] s5p-g2d: Add support for G2D H/W Rev.4.1 Modified the G2D driver (which initially supported only H/W Rev.3) to support H/W Rev.4.1 present on Exynos4x12 and Exynos52x0 SOCs. - Set the SRC and DST type to 'memory' instead of using reset values. - FIMG2D v4.1 H/W uses different logic for stretching(scaling). - Use CACHECTL_REG only with FIMG2D v3. [s.nawrocki: removed empty line at end of file]] Signed-off-by: Ajay Kumar Signed-off-by: Sachin Kamat Acked-by: Kamil Debski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-g2d/g2d-hw.c | 16 +++++++++--- drivers/media/platform/s5p-g2d/g2d-regs.h | 7 +++++ drivers/media/platform/s5p-g2d/g2d.c | 31 +++++++++++++++++++++-- drivers/media/platform/s5p-g2d/g2d.h | 17 ++++++++++--- 4 files changed, 62 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/s5p-g2d/g2d-hw.c b/drivers/media/platform/s5p-g2d/g2d-hw.c index 5b86cbe408e2..e87bd93811d4 100644 --- a/drivers/media/platform/s5p-g2d/g2d-hw.c +++ b/drivers/media/platform/s5p-g2d/g2d-hw.c @@ -28,6 +28,7 @@ void g2d_set_src_size(struct g2d_dev *d, struct g2d_frame *f) { u32 n; + w(0, SRC_SELECT_REG); w(f->stride & 0xFFFF, SRC_STRIDE_REG); n = f->o_height & 0xFFF; @@ -52,6 +53,7 @@ void g2d_set_dst_size(struct g2d_dev *d, struct g2d_frame *f) { u32 n; + w(0, DST_SELECT_REG); w(f->stride & 0xFFFF, DST_STRIDE_REG); n = f->o_height & 0xFFF; @@ -82,10 +84,14 @@ void g2d_set_flip(struct g2d_dev *d, u32 r) w(r, SRC_MSK_DIRECT_REG); } -u32 g2d_cmd_stretch(u32 e) +void g2d_set_v41_stretch(struct g2d_dev *d, struct g2d_frame *src, + struct g2d_frame *dst) { - e &= 1; - return e << 4; + w(DEFAULT_SCALE_MODE, SRC_SCALE_CTRL_REG); + + /* inversed scaling factor: src is numerator */ + w((src->c_width << 16) / dst->c_width, SRC_XSCALE_REG); + w((src->c_height << 16) / dst->c_height, SRC_YSCALE_REG); } void g2d_set_cmd(struct g2d_dev *d, u32 c) @@ -96,7 +102,9 @@ void g2d_set_cmd(struct g2d_dev *d, u32 c) void g2d_start(struct g2d_dev *d) { /* Clear cache */ - w(0x7, CACHECTL_REG); + if (d->variant->hw_rev == TYPE_G2D_3X) + w(0x7, CACHECTL_REG); + /* Enable interrupt */ w(1, INTEN_REG); /* Start G2D engine */ diff --git a/drivers/media/platform/s5p-g2d/g2d-regs.h b/drivers/media/platform/s5p-g2d/g2d-regs.h index 02e1cf50da4e..9bf31ad35d47 100644 --- a/drivers/media/platform/s5p-g2d/g2d-regs.h +++ b/drivers/media/platform/s5p-g2d/g2d-regs.h @@ -35,6 +35,9 @@ #define SRC_COLOR_MODE_REG 0x030C /* Src Image Color Mode reg */ #define SRC_LEFT_TOP_REG 0x0310 /* Src Left Top Coordinate reg */ #define SRC_RIGHT_BOTTOM_REG 0x0314 /* Src Right Bottom Coordinate reg */ +#define SRC_SCALE_CTRL_REG 0x0328 /* Src Scaling type select */ +#define SRC_XSCALE_REG 0x032c /* Src X Scaling ratio */ +#define SRC_YSCALE_REG 0x0330 /* Src Y Scaling ratio */ /* Parameter Setting Registers (Dest) */ #define DST_SELECT_REG 0x0400 /* Dest Image Selection reg */ @@ -113,3 +116,7 @@ #define DEFAULT_WIDTH 100 #define DEFAULT_HEIGHT 100 +#define DEFAULT_SCALE_MODE (2 << 0) + +/* Command mode register values */ +#define CMD_V3_ENABLE_STRETCH (1 << 4) diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index dcd53357704d..7e415297e174 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -604,8 +604,13 @@ static void device_run(void *prv) g2d_set_flip(dev, ctx->flip); if (ctx->in.c_width != ctx->out.c_width || - ctx->in.c_height != ctx->out.c_height) - cmd |= g2d_cmd_stretch(1); + ctx->in.c_height != ctx->out.c_height) { + if (dev->variant->hw_rev == TYPE_G2D_3X) + cmd |= CMD_V3_ENABLE_STRETCH; + else + g2d_set_v41_stretch(dev, &ctx->in, &ctx->out); + } + g2d_set_cmd(dev, cmd); g2d_start(dev); @@ -791,6 +796,7 @@ static int g2d_probe(struct platform_device *pdev) } def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3; + dev->variant = g2d_get_drv_data(pdev); return 0; @@ -830,9 +836,30 @@ static int g2d_remove(struct platform_device *pdev) return 0; } +static struct g2d_variant g2d_drvdata_v3x = { + .hw_rev = TYPE_G2D_3X, +}; + +static struct g2d_variant g2d_drvdata_v4x = { + .hw_rev = TYPE_G2D_4X, /* Revision 4.1 for Exynos4X12 and Exynos5 */ +}; + +static struct platform_device_id g2d_driver_ids[] = { + { + .name = "s5p-g2d", + .driver_data = (unsigned long)&g2d_drvdata_v3x, + }, { + .name = "s5p-g2d-v4x", + .driver_data = (unsigned long)&g2d_drvdata_v4x, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, g2d_driver_ids); + static struct platform_driver g2d_pdrv = { .probe = g2d_probe, .remove = g2d_remove, + .id_table = g2d_driver_ids, .driver = { .name = G2D_NAME, .owner = THIS_MODULE, diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/s5p-g2d/g2d.h index 6b765b0216c5..300ca05ba404 100644 --- a/drivers/media/platform/s5p-g2d/g2d.h +++ b/drivers/media/platform/s5p-g2d/g2d.h @@ -10,10 +10,13 @@ * License, or (at your option) any later version */ +#include #include #include #define G2D_NAME "s5p-g2d" +#define TYPE_G2D_3X 3 +#define TYPE_G2D_4X 4 struct g2d_dev { struct v4l2_device v4l2_dev; @@ -27,6 +30,7 @@ struct g2d_dev { struct clk *clk; struct clk *gate; struct g2d_ctx *curr; + struct g2d_variant *variant; int irq; wait_queue_head_t irq_queue; }; @@ -53,7 +57,7 @@ struct g2d_frame { struct g2d_ctx { struct v4l2_fh fh; struct g2d_dev *dev; - struct v4l2_m2m_ctx *m2m_ctx; + struct v4l2_m2m_ctx *m2m_ctx; struct g2d_frame in; struct g2d_frame out; struct v4l2_ctrl *ctrl_hflip; @@ -70,6 +74,9 @@ struct g2d_fmt { u32 hw; }; +struct g2d_variant { + unsigned short hw_rev; +}; void g2d_reset(struct g2d_dev *d); void g2d_set_src_size(struct g2d_dev *d, struct g2d_frame *f); @@ -80,7 +87,11 @@ void g2d_start(struct g2d_dev *d); void g2d_clear_int(struct g2d_dev *d); void g2d_set_rop4(struct g2d_dev *d, u32 r); void g2d_set_flip(struct g2d_dev *d, u32 r); -u32 g2d_cmd_stretch(u32 e); +void g2d_set_v41_stretch(struct g2d_dev *d, + struct g2d_frame *src, struct g2d_frame *dst); void g2d_set_cmd(struct g2d_dev *d, u32 c); - +static inline struct g2d_variant *g2d_get_drv_data(struct platform_device *pdev) +{ + return (struct g2d_variant *)platform_get_device_id(pdev)->driver_data; +} From 23575bf4219c06057632e601524ee66cccf03703 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 13 Jan 2013 14:50:59 -0300 Subject: [PATCH 1914/4607] [media] noon010p30: Remove unneeded v4l2 control compatibility ops All host drivers using this subdev driver are already converted to use the control framework so the compatibility ops can be dropped. Signed-off-by: Sylwester Nawrocki Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/noon010pc30.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c index 440c12962bae..8554b47f993a 100644 --- a/drivers/media/i2c/noon010pc30.c +++ b/drivers/media/i2c/noon010pc30.c @@ -660,13 +660,6 @@ static const struct v4l2_ctrl_ops noon010_ctrl_ops = { static const struct v4l2_subdev_core_ops noon010_core_ops = { .s_power = noon010_s_power, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, .log_status = noon010_log_status, }; From 97466d0da3b08e087e517725ab56c7a2a2df706f Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 8 Jan 2013 02:48:24 -0300 Subject: [PATCH 1915/4607] [media] s5k6aa: Use devm_regulator_bulk_get API devm_regulator_bulk_get is device managed and saves some cleanup and exit code. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5k6aa.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index 57cd4fa0193d..bdf5e3db31d1 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -1598,7 +1598,7 @@ static int s5k6aa_probe(struct i2c_client *client, for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++) s5k6aa->supplies[i].supply = s5k6aa_supply_names[i]; - ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, + ret = devm_regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); if (ret) { dev_err(&client->dev, "Failed to get regulators\n"); @@ -1607,7 +1607,7 @@ static int s5k6aa_probe(struct i2c_client *client, ret = s5k6aa_initialize_ctrls(s5k6aa); if (ret) - goto out_err4; + goto out_err3; s5k6aa_presets_data_init(s5k6aa); @@ -1618,8 +1618,6 @@ static int s5k6aa_probe(struct i2c_client *client, return 0; -out_err4: - regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); out_err3: s5k6aa_free_gpios(s5k6aa); out_err2: @@ -1635,7 +1633,6 @@ static int s5k6aa_remove(struct i2c_client *client) v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(sd->ctrl_handler); media_entity_cleanup(&sd->entity); - regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); s5k6aa_free_gpios(s5k6aa); return 0; From e82564475eab196b2e8a11572fff8e268329530e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 22 Jan 2013 01:00:06 -0300 Subject: [PATCH 1916/4607] [media] s5p-mfc: Use WARN_ON(condition) directly Use WARN_ON(condition) directly instead of wrapping around an if condition. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 6347f09ca783..5050168ad211 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -582,8 +582,7 @@ static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx, clear_work_bit(ctx); - if (test_and_clear_bit(0, &dev->hw_lock) == 0) - WARN_ON(1); + WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); s5p_mfc_clock_off(); wake_up(&ctx->queue); From 6e83e6e25eb49dc57a69b3f8ecc1e764c9775101 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Fri, 18 Jan 2013 15:42:34 -0300 Subject: [PATCH 1917/4607] [media] s5p-mfc: Fix kernel warning on memory init Cleaned up the memory devices allocation code and added missing device_initialize() call to remove the kernel warning during memory allocations. Signed-off-by: Arun Kumar K Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc.c | 78 ++++++++++++++---------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 5050168ad211..3669e3b933ca 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1014,6 +1014,46 @@ static int match_child(struct device *dev, void *data) static void *mfc_get_drv_data(struct platform_device *pdev); +static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev) +{ + unsigned int mem_info[2]; + + dev->mem_dev_l = devm_kzalloc(&dev->plat_dev->dev, + sizeof(struct device), GFP_KERNEL); + if (!dev->mem_dev_l) { + mfc_err("Not enough memory\n"); + return -ENOMEM; + } + device_initialize(dev->mem_dev_l); + of_property_read_u32_array(dev->plat_dev->dev.of_node, + "samsung,mfc-l", mem_info, 2); + if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0], + mem_info[0], mem_info[1], + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { + mfc_err("Failed to declare coherent memory for\n" + "MFC device\n"); + return -ENOMEM; + } + + dev->mem_dev_r = devm_kzalloc(&dev->plat_dev->dev, + sizeof(struct device), GFP_KERNEL); + if (!dev->mem_dev_r) { + mfc_err("Not enough memory\n"); + return -ENOMEM; + } + device_initialize(dev->mem_dev_r); + of_property_read_u32_array(dev->plat_dev->dev.of_node, + "samsung,mfc-r", mem_info, 2); + if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0], + mem_info[0], mem_info[1], + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { + pr_err("Failed to declare coherent memory for\n" + "MFC device\n"); + return -ENOMEM; + } + return 0; +} + /* MFC probe function */ static int s5p_mfc_probe(struct platform_device *pdev) { @@ -1021,7 +1061,6 @@ static int s5p_mfc_probe(struct platform_device *pdev) struct video_device *vfd; struct resource *res; int ret; - unsigned int mem_info[2]; pr_debug("%s++\n", __func__); dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -1069,39 +1108,8 @@ static int s5p_mfc_probe(struct platform_device *pdev) } if (pdev->dev.of_node) { - dev->mem_dev_l = kzalloc(sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_l) { - mfc_err("Not enough memory\n"); - ret = -ENOMEM; + if (s5p_mfc_alloc_memdevs(dev) < 0) goto err_res; - } - of_property_read_u32_array(pdev->dev.of_node, "samsung,mfc-l", - mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_l, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - mfc_err("Failed to declare coherent memory for\n" - "MFC device\n"); - ret = -ENOMEM; - goto err_res; - } - - dev->mem_dev_r = kzalloc(sizeof(struct device), GFP_KERNEL); - if (!dev->mem_dev_r) { - mfc_err("Not enough memory\n"); - ret = -ENOMEM; - goto err_res; - } - of_property_read_u32_array(pdev->dev.of_node, "samsung,mfc-r", - mem_info, 2); - if (dma_declare_coherent_memory(dev->mem_dev_r, mem_info[0], - mem_info[0], mem_info[1], - DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) == 0) { - pr_err("Failed to declare coherent memory for\n" - "MFC device\n"); - ret = -ENOMEM; - goto err_res; - } } else { dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l", match_child); @@ -1247,6 +1255,10 @@ static int s5p_mfc_remove(struct platform_device *pdev) s5p_mfc_release_firmware(dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); + if (pdev->dev.of_node) { + put_device(dev->mem_dev_l); + put_device(dev->mem_dev_r); + } s5p_mfc_final_pm(dev); return 0; From cac47f1822fcb97018e24b05a7fb31f11a6bc28c Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Thu, 22 Nov 2012 11:39:18 -0300 Subject: [PATCH 1918/4607] [media] V4L: Add S5C73M3 camera driver Add driver for S5C73M3 image sensor. The driver exposes the sensor as two subdevs: pure sensor and output interface. Two subdev architecture supports interleaved UYVY/JPEG image format with separate frame size for both sub-formats, there is a spearate pad for each sub-format. Signed-off-by: Sylwester Nawrocki Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 7 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/s5c73m3/Makefile | 2 + drivers/media/i2c/s5c73m3/s5c73m3-core.c | 1706 +++++++++++++++++++++ drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c | 563 +++++++ drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 156 ++ drivers/media/i2c/s5c73m3/s5c73m3.h | 459 ++++++ include/media/s5c73m3.h | 55 + 8 files changed, 2949 insertions(+) create mode 100644 drivers/media/i2c/s5c73m3/Makefile create mode 100644 drivers/media/i2c/s5c73m3/s5c73m3-core.c create mode 100644 drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c create mode 100644 drivers/media/i2c/s5c73m3/s5c73m3-spi.c create mode 100644 drivers/media/i2c/s5c73m3/s5c73m3.h create mode 100644 include/media/s5c73m3.h diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index d73078cdffec..ecdf7e3fdcf4 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -523,6 +523,13 @@ config VIDEO_S5K4ECGX source "drivers/media/i2c/smiapp/Kconfig" +config VIDEO_S5C73M3 + tristate "Samsung S5C73M3 sensor support" + depends on I2C && SPI && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a V4L2 sensor-level driver for Samsung S5C73M3 + 8 Mpixel camera. + comment "Flash devices" config VIDEO_ADP1653 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 8b62e54d3884..b35e25798cd3 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o +obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o diff --git a/drivers/media/i2c/s5c73m3/Makefile b/drivers/media/i2c/s5c73m3/Makefile new file mode 100644 index 000000000000..fa4df342d1f1 --- /dev/null +++ b/drivers/media/i2c/s5c73m3/Makefile @@ -0,0 +1,2 @@ +s5c73m3-objs := s5c73m3-core.o s5c73m3-spi.o s5c73m3-ctrls.o +obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3.o diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c new file mode 100644 index 000000000000..600909ddb150 --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -0,0 +1,1706 @@ +/* + * Samsung LSI S5C73M3 8M pixel camera driver + * + * Copyright (C) 2012, Samsung Electronics, Co., Ltd. + * Sylwester Nawrocki + * Andrzej Hajda + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "s5c73m3.h" + +int s5c73m3_dbg; +module_param_named(debug, s5c73m3_dbg, int, 0644); + +int boot_from_rom = 1; +module_param(boot_from_rom, int, 0644); + +int update_fw; +module_param(update_fw, int, 0644); + +#define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K + +static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = { + "vdd-int", /* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */ + "vdda", /* Analog Core supply (1.2V), CAM_SENSOR_CORE_1.2V */ + "vdd-reg", /* Regulator input supply (2.8V), CAM_SENSOR_A2.8V */ + "vddio-host", /* Digital Host I/O power supply (1.8V...2.8V), + CAM_ISP_SENSOR_1.8V */ + "vddio-cis", /* Digital CIS I/O power (1.2V...1.8V), + CAM_ISP_MIPI_1.2V */ + "vdd-af", /* Lens, CAM_AF_2.8V */ +}; + +static const struct s5c73m3_frame_size s5c73m3_isp_resolutions[] = { + { 320, 240, COMM_CHG_MODE_YUV_320_240 }, + { 352, 288, COMM_CHG_MODE_YUV_352_288 }, + { 640, 480, COMM_CHG_MODE_YUV_640_480 }, + { 880, 720, COMM_CHG_MODE_YUV_880_720 }, + { 960, 720, COMM_CHG_MODE_YUV_960_720 }, + { 1008, 672, COMM_CHG_MODE_YUV_1008_672 }, + { 1184, 666, COMM_CHG_MODE_YUV_1184_666 }, + { 1280, 720, COMM_CHG_MODE_YUV_1280_720 }, + { 1536, 864, COMM_CHG_MODE_YUV_1536_864 }, + { 1600, 1200, COMM_CHG_MODE_YUV_1600_1200 }, + { 1632, 1224, COMM_CHG_MODE_YUV_1632_1224 }, + { 1920, 1080, COMM_CHG_MODE_YUV_1920_1080 }, + { 1920, 1440, COMM_CHG_MODE_YUV_1920_1440 }, + { 2304, 1296, COMM_CHG_MODE_YUV_2304_1296 }, + { 3264, 2448, COMM_CHG_MODE_YUV_3264_2448 }, +}; + +static const struct s5c73m3_frame_size s5c73m3_jpeg_resolutions[] = { + { 640, 480, COMM_CHG_MODE_JPEG_640_480 }, + { 800, 450, COMM_CHG_MODE_JPEG_800_450 }, + { 800, 600, COMM_CHG_MODE_JPEG_800_600 }, + { 1024, 768, COMM_CHG_MODE_JPEG_1024_768 }, + { 1280, 720, COMM_CHG_MODE_JPEG_1280_720 }, + { 1280, 960, COMM_CHG_MODE_JPEG_1280_960 }, + { 1600, 900, COMM_CHG_MODE_JPEG_1600_900 }, + { 1600, 1200, COMM_CHG_MODE_JPEG_1600_1200 }, + { 2048, 1152, COMM_CHG_MODE_JPEG_2048_1152 }, + { 2048, 1536, COMM_CHG_MODE_JPEG_2048_1536 }, + { 2560, 1440, COMM_CHG_MODE_JPEG_2560_1440 }, + { 2560, 1920, COMM_CHG_MODE_JPEG_2560_1920 }, + { 3264, 1836, COMM_CHG_MODE_JPEG_3264_1836 }, + { 3264, 2176, COMM_CHG_MODE_JPEG_3264_2176 }, + { 3264, 2448, COMM_CHG_MODE_JPEG_3264_2448 }, +}; + +static const struct s5c73m3_frame_size * const s5c73m3_resolutions[] = { + [RES_ISP] = s5c73m3_isp_resolutions, + [RES_JPEG] = s5c73m3_jpeg_resolutions +}; + +static const int s5c73m3_resolutions_len[] = { + [RES_ISP] = ARRAY_SIZE(s5c73m3_isp_resolutions), + [RES_JPEG] = ARRAY_SIZE(s5c73m3_jpeg_resolutions) +}; + +static const struct s5c73m3_interval s5c73m3_intervals[] = { + { COMM_FRAME_RATE_FIXED_7FPS, {142857, 1000000}, {3264, 2448} }, + { COMM_FRAME_RATE_FIXED_15FPS, {66667, 1000000}, {3264, 2448} }, + { COMM_FRAME_RATE_FIXED_20FPS, {50000, 1000000}, {2304, 1296} }, + { COMM_FRAME_RATE_FIXED_30FPS, {33333, 1000000}, {2304, 1296} }, +}; + +#define S5C73M3_DEFAULT_FRAME_INTERVAL 3 /* 30 fps */ + +static void s5c73m3_fill_mbus_fmt(struct v4l2_mbus_framefmt *mf, + const struct s5c73m3_frame_size *fs, + u32 code) +{ + mf->width = fs->width; + mf->height = fs->height; + mf->code = code; + mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->field = V4L2_FIELD_NONE; +} + +static int s5c73m3_i2c_write(struct i2c_client *client, u16 addr, u16 data) +{ + u8 buf[4] = { addr >> 8, addr & 0xff, data >> 8, data & 0xff }; + + int ret = i2c_master_send(client, buf, sizeof(buf)); + + v4l_dbg(4, s5c73m3_dbg, client, "%s: addr 0x%04x, data 0x%04x\n", + __func__, addr, data); + + if (ret == 4) + return 0; + + return ret < 0 ? ret : -EREMOTEIO; +} + +static int s5c73m3_i2c_read(struct i2c_client *client, u16 addr, u16 *data) +{ + int ret; + u8 rbuf[2], wbuf[2] = { addr >> 8, addr & 0xff }; + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = sizeof(wbuf), + .buf = wbuf + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(rbuf), + .buf = rbuf + } + }; + /* + * Issue repeated START after writing 2 address bytes and + * just one STOP only after reading the data bytes. + */ + ret = i2c_transfer(client->adapter, msg, 2); + if (ret == 2) { + *data = be16_to_cpup((u16 *)rbuf); + v4l2_dbg(4, s5c73m3_dbg, client, + "%s: addr: 0x%04x, data: 0x%04x\n", + __func__, addr, *data); + return 0; + } + + v4l2_err(client, "I2C read failed: addr: %04x, (%d)\n", addr, ret); + + return ret >= 0 ? -EREMOTEIO : ret; +} + +int s5c73m3_write(struct s5c73m3 *state, u32 addr, u16 data) +{ + struct i2c_client *client = state->i2c_client; + int ret; + + if ((addr ^ state->i2c_write_address) & 0xffff0000) { + ret = s5c73m3_i2c_write(client, REG_CMDWR_ADDRH, addr >> 16); + if (ret < 0) { + state->i2c_write_address = 0; + return ret; + } + } + + if ((addr ^ state->i2c_write_address) & 0xffff) { + ret = s5c73m3_i2c_write(client, REG_CMDWR_ADDRL, addr & 0xffff); + if (ret < 0) { + state->i2c_write_address = 0; + return ret; + } + } + + state->i2c_write_address = addr; + + ret = s5c73m3_i2c_write(client, REG_CMDBUF_ADDR, data); + if (ret < 0) + return ret; + + state->i2c_write_address += 2; + + return ret; +} + +int s5c73m3_read(struct s5c73m3 *state, u32 addr, u16 *data) +{ + struct i2c_client *client = state->i2c_client; + int ret; + + if ((addr ^ state->i2c_read_address) & 0xffff0000) { + ret = s5c73m3_i2c_write(client, REG_CMDRD_ADDRH, addr >> 16); + if (ret < 0) { + state->i2c_read_address = 0; + return ret; + } + } + + if ((addr ^ state->i2c_read_address) & 0xffff) { + ret = s5c73m3_i2c_write(client, REG_CMDRD_ADDRL, addr & 0xffff); + if (ret < 0) { + state->i2c_read_address = 0; + return ret; + } + } + + state->i2c_read_address = addr; + + ret = s5c73m3_i2c_read(client, REG_CMDBUF_ADDR, data); + if (ret < 0) + return ret; + + state->i2c_read_address += 2; + + return ret; +} + +static int s5c73m3_check_status(struct s5c73m3 *state, unsigned int value) +{ + unsigned long start = jiffies; + unsigned long end = start + msecs_to_jiffies(2000); + int ret = 0; + u16 status; + int count = 0; + + while (time_is_after_jiffies(end)) { + ret = s5c73m3_read(state, REG_STATUS, &status); + if (ret < 0 || status == value) + break; + usleep_range(500, 1000); + ++count; + } + + if (count > 0) + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "status check took %dms\n", + jiffies_to_msecs(jiffies - start)); + + if (ret == 0 && status != value) { + u16 i2c_status = 0; + u16 i2c_seq_status = 0; + + s5c73m3_read(state, REG_I2C_STATUS, &i2c_status); + s5c73m3_read(state, REG_I2C_SEQ_STATUS, &i2c_seq_status); + + v4l2_err(&state->sensor_sd, + "wrong status %#x, expected: %#x, i2c_status: %#x/%#x\n", + status, value, i2c_status, i2c_seq_status); + + return -ETIMEDOUT; + } + + return ret; +} + +int s5c73m3_isp_command(struct s5c73m3 *state, u16 command, u16 data) +{ + int ret; + + ret = s5c73m3_check_status(state, REG_STATUS_ISP_COMMAND_COMPLETED); + if (ret < 0) + return ret; + + ret = s5c73m3_write(state, 0x00095000, command); + if (ret < 0) + return ret; + + ret = s5c73m3_write(state, 0x00095002, data); + if (ret < 0) + return ret; + + return s5c73m3_write(state, REG_STATUS, 0x0001); +} + +int s5c73m3_isp_comm_result(struct s5c73m3 *state, u16 command, u16 *data) +{ + return s5c73m3_read(state, COMM_RESULT_OFFSET + command, data); +} + +static int s5c73m3_set_af_softlanding(struct s5c73m3 *state) +{ + unsigned long start = jiffies; + u16 af_softlanding; + int count = 0; + int ret; + const char *msg; + + ret = s5c73m3_isp_command(state, COMM_AF_SOFTLANDING, + COMM_AF_SOFTLANDING_ON); + if (ret < 0) { + v4l2_info(&state->sensor_sd, "AF soft-landing failed\n"); + return ret; + } + + for (;;) { + ret = s5c73m3_isp_comm_result(state, COMM_AF_SOFTLANDING, + &af_softlanding); + if (ret < 0) { + msg = "failed"; + break; + } + if (af_softlanding == COMM_AF_SOFTLANDING_RES_COMPLETE) { + msg = "succeeded"; + break; + } + if (++count > 100) { + ret = -ETIME; + msg = "timed out"; + break; + } + msleep(25); + } + + v4l2_info(&state->sensor_sd, "AF soft-landing %s after %dms\n", + msg, jiffies_to_msecs(jiffies - start)); + + return ret; +} + +static int s5c73m3_load_fw(struct v4l2_subdev *sd) +{ + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + struct i2c_client *client = state->i2c_client; + const struct firmware *fw; + int ret; + char fw_name[20]; + + snprintf(fw_name, sizeof(fw_name), "SlimISP_%.2s.bin", + state->fw_file_version); + ret = request_firmware(&fw, fw_name, &client->dev); + if (ret < 0) { + v4l2_err(sd, "Firmware request failed (%s)\n", fw_name); + return -EINVAL; + } + + v4l2_info(sd, "Loading firmware (%s, %d B)\n", fw_name, fw->size); + + ret = s5c73m3_spi_write(state, fw->data, fw->size, 64); + + if (ret >= 0) + state->isp_ready = 1; + else + v4l2_err(sd, "SPI write failed\n"); + + release_firmware(fw); + + return ret; +} + +static int s5c73m3_set_frame_size(struct s5c73m3 *state) +{ + const struct s5c73m3_frame_size *prev_size = + state->sensor_pix_size[RES_ISP]; + const struct s5c73m3_frame_size *cap_size = + state->sensor_pix_size[RES_JPEG]; + unsigned int chg_mode; + + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "Preview size: %dx%d, reg_val: 0x%x\n", + prev_size->width, prev_size->height, prev_size->reg_val); + + chg_mode = prev_size->reg_val | COMM_CHG_MODE_NEW; + + if (state->mbus_code == S5C73M3_JPEG_FMT) { + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "Capture size: %dx%d, reg_val: 0x%x\n", + cap_size->width, cap_size->height, cap_size->reg_val); + chg_mode |= cap_size->reg_val; + } + + return s5c73m3_isp_command(state, COMM_CHG_MODE, chg_mode); +} + +static int s5c73m3_set_frame_rate(struct s5c73m3 *state) +{ + int ret; + + if (state->ctrls.stabilization->val) + return 0; + + if (WARN_ON(state->fiv == NULL)) + return -EINVAL; + + ret = s5c73m3_isp_command(state, COMM_FRAME_RATE, state->fiv->fps_reg); + if (!ret) + state->apply_fiv = 0; + + return ret; +} + +static int __s5c73m3_s_stream(struct s5c73m3 *state, struct v4l2_subdev *sd, + int on) +{ + u16 mode; + int ret; + + if (on && state->apply_fmt) { + if (state->mbus_code == S5C73M3_JPEG_FMT) + mode = COMM_IMG_OUTPUT_INTERLEAVED; + else + mode = COMM_IMG_OUTPUT_YUV; + + ret = s5c73m3_isp_command(state, COMM_IMG_OUTPUT, mode); + if (!ret) + ret = s5c73m3_set_frame_size(state); + if (ret) + return ret; + state->apply_fmt = 0; + } + + ret = s5c73m3_isp_command(state, COMM_SENSOR_STREAMING, !!on); + if (ret) + return ret; + + state->streaming = !!on; + + if (!on) + return ret; + + if (state->apply_fiv) { + ret = s5c73m3_set_frame_rate(state); + if (ret < 0) + v4l2_err(sd, "Error setting frame rate(%d)\n", ret); + } + + return s5c73m3_check_status(state, REG_STATUS_ISP_COMMAND_COMPLETED); +} + +static int s5c73m3_oif_s_stream(struct v4l2_subdev *sd, int on) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + int ret; + + mutex_lock(&state->lock); + ret = __s5c73m3_s_stream(state, sd, on); + mutex_unlock(&state->lock); + + return ret; +} + +static int s5c73m3_system_status_wait(struct s5c73m3 *state, u32 value, + unsigned int delay, unsigned int steps) +{ + u16 reg = 0; + + while (steps-- > 0) { + int ret = s5c73m3_read(state, 0x30100010, ®); + if (ret < 0) + return ret; + if (reg == value) + return 0; + usleep_range(delay, delay + 25); + } + return -ETIMEDOUT; +} + +static int s5c73m3_read_fw_version(struct s5c73m3 *state) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + int i, ret; + u16 data[2]; + int offset; + + offset = state->isp_ready ? 0x60 : 0; + + for (i = 0; i < S5C73M3_SENSOR_FW_LEN / 2; i++) { + ret = s5c73m3_read(state, offset + i * 2, data); + if (ret < 0) + return ret; + state->sensor_fw[i * 2] = (char)(*data & 0xff); + state->sensor_fw[i * 2 + 1] = (char)(*data >> 8); + } + state->sensor_fw[S5C73M3_SENSOR_FW_LEN] = '\0'; + + + for (i = 0; i < S5C73M3_SENSOR_TYPE_LEN / 2; i++) { + ret = s5c73m3_read(state, offset + 6 + i * 2, data); + if (ret < 0) + return ret; + state->sensor_type[i * 2] = (char)(*data & 0xff); + state->sensor_type[i * 2 + 1] = (char)(*data >> 8); + } + state->sensor_type[S5C73M3_SENSOR_TYPE_LEN] = '\0'; + + ret = s5c73m3_read(state, offset + 0x14, data); + if (ret >= 0) { + ret = s5c73m3_read(state, offset + 0x16, data + 1); + if (ret >= 0) + state->fw_size = data[0] + (data[1] << 16); + } + + v4l2_info(sd, "Sensor type: %s, FW version: %s\n", + state->sensor_type, state->sensor_fw); + return ret; +} + +static int s5c73m3_fw_update_from(struct s5c73m3 *state) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + u16 status = COMM_FW_UPDATE_NOT_READY; + int ret; + int count = 0; + + v4l2_warn(sd, "Updating F-ROM firmware.\n"); + do { + if (status == COMM_FW_UPDATE_NOT_READY) { + ret = s5c73m3_isp_command(state, COMM_FW_UPDATE, 0); + if (ret < 0) + return ret; + } + + ret = s5c73m3_read(state, 0x00095906, &status); + if (ret < 0) + return ret; + switch (status) { + case COMM_FW_UPDATE_FAIL: + v4l2_warn(sd, "Updating F-ROM firmware failed.\n"); + return -EIO; + case COMM_FW_UPDATE_SUCCESS: + v4l2_warn(sd, "Updating F-ROM firmware finished.\n"); + return 0; + } + ++count; + msleep(20); + } while (count < 500); + + v4l2_warn(sd, "Updating F-ROM firmware timed-out.\n"); + return -ETIMEDOUT; +} + +static int s5c73m3_spi_boot(struct s5c73m3 *state, bool load_fw) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + int ret; + + /* Run ARM MCU */ + ret = s5c73m3_write(state, 0x30000004, 0xffff); + if (ret < 0) + return ret; + + usleep_range(400, 500); + + /* Check booting status */ + ret = s5c73m3_system_status_wait(state, 0x0c, 100, 3); + if (ret < 0) { + v4l2_err(sd, "booting failed: %d\n", ret); + return ret; + } + + /* P,M,S and Boot Mode */ + ret = s5c73m3_write(state, 0x30100014, 0x2146); + if (ret < 0) + return ret; + + ret = s5c73m3_write(state, 0x30100010, 0x210c); + if (ret < 0) + return ret; + + usleep_range(200, 250); + + /* Check SPI status */ + ret = s5c73m3_system_status_wait(state, 0x210d, 100, 300); + if (ret < 0) + v4l2_err(sd, "SPI not ready: %d\n", ret); + + /* Firmware download over SPI */ + if (load_fw) + s5c73m3_load_fw(sd); + + /* MCU reset */ + ret = s5c73m3_write(state, 0x30000004, 0xfffd); + if (ret < 0) + return ret; + + /* Remap */ + ret = s5c73m3_write(state, 0x301000a4, 0x0183); + if (ret < 0) + return ret; + + /* MCU restart */ + ret = s5c73m3_write(state, 0x30000004, 0xffff); + if (ret < 0 || !load_fw) + return ret; + + ret = s5c73m3_read_fw_version(state); + if (ret < 0) + return ret; + + if (load_fw && update_fw) { + ret = s5c73m3_fw_update_from(state); + update_fw = 0; + } + + return ret; +} + +static int s5c73m3_set_timing_register_for_vdd(struct s5c73m3 *state) +{ + static const u32 regs[][2] = { + { 0x30100018, 0x0618 }, + { 0x3010001c, 0x10c1 }, + { 0x30100020, 0x249e } + }; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(regs); i++) { + ret = s5c73m3_write(state, regs[i][0], regs[i][1]); + if (ret < 0) + return ret; + } + + return 0; +} + +static void s5c73m3_set_fw_file_version(struct s5c73m3 *state) +{ + switch (state->sensor_fw[0]) { + case 'G': + case 'O': + state->fw_file_version[0] = 'G'; + break; + case 'S': + case 'Z': + state->fw_file_version[0] = 'Z'; + break; + } + + switch (state->sensor_fw[1]) { + case 'C'...'F': + state->fw_file_version[1] = state->sensor_fw[1]; + break; + } +} + +static int s5c73m3_get_fw_version(struct s5c73m3 *state) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + int ret; + + /* Run ARM MCU */ + ret = s5c73m3_write(state, 0x30000004, 0xffff); + if (ret < 0) + return ret; + usleep_range(400, 500); + + /* Check booting status */ + ret = s5c73m3_system_status_wait(state, 0x0c, 100, 3); + if (ret < 0) { + + v4l2_err(sd, "%s: booting failed: %d\n", __func__, ret); + return ret; + } + + /* Change I/O Driver Current in order to read from F-ROM */ + ret = s5c73m3_write(state, 0x30100120, 0x0820); + ret = s5c73m3_write(state, 0x30100124, 0x0820); + + /* Offset Setting */ + ret = s5c73m3_write(state, 0x00010418, 0x0008); + + /* P,M,S and Boot Mode */ + ret = s5c73m3_write(state, 0x30100014, 0x2146); + if (ret < 0) + return ret; + ret = s5c73m3_write(state, 0x30100010, 0x230c); + if (ret < 0) + return ret; + + usleep_range(200, 250); + + /* Check SPI status */ + ret = s5c73m3_system_status_wait(state, 0x230e, 100, 300); + if (ret < 0) + v4l2_err(sd, "SPI not ready: %d\n", ret); + + /* ARM reset */ + ret = s5c73m3_write(state, 0x30000004, 0xfffd); + if (ret < 0) + return ret; + + /* Remap */ + ret = s5c73m3_write(state, 0x301000a4, 0x0183); + if (ret < 0) + return ret; + + s5c73m3_set_timing_register_for_vdd(state); + + ret = s5c73m3_read_fw_version(state); + + s5c73m3_set_fw_file_version(state); + + return ret; +} + +static int s5c73m3_rom_boot(struct s5c73m3 *state, bool load_fw) +{ + static const u32 boot_regs[][2] = { + { 0x3100010c, 0x0044 }, + { 0x31000108, 0x000d }, + { 0x31000304, 0x0001 }, + { 0x00010000, 0x5800 }, + { 0x00010002, 0x0002 }, + { 0x31000000, 0x0001 }, + { 0x30100014, 0x1b85 }, + { 0x30100010, 0x230c } + }; + struct v4l2_subdev *sd = &state->sensor_sd; + int i, ret; + + /* Run ARM MCU */ + ret = s5c73m3_write(state, 0x30000004, 0xffff); + if (ret < 0) + return ret; + usleep_range(400, 450); + + /* Check booting status */ + ret = s5c73m3_system_status_wait(state, 0x0c, 100, 4); + if (ret < 0) { + v4l2_err(sd, "Booting failed: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(boot_regs); i++) { + ret = s5c73m3_write(state, boot_regs[i][0], boot_regs[i][1]); + if (ret < 0) + return ret; + } + msleep(200); + + /* Check the binary read status */ + ret = s5c73m3_system_status_wait(state, 0x230e, 1000, 150); + if (ret < 0) { + v4l2_err(sd, "Binary read failed: %d\n", ret); + return ret; + } + + /* ARM reset */ + ret = s5c73m3_write(state, 0x30000004, 0xfffd); + if (ret < 0) + return ret; + /* Remap */ + ret = s5c73m3_write(state, 0x301000a4, 0x0183); + if (ret < 0) + return ret; + /* MCU re-start */ + ret = s5c73m3_write(state, 0x30000004, 0xffff); + if (ret < 0) + return ret; + + state->isp_ready = 1; + + return s5c73m3_read_fw_version(state); +} + +static int s5c73m3_isp_init(struct s5c73m3 *state) +{ + int ret; + + state->i2c_read_address = 0; + state->i2c_write_address = 0; + + ret = s5c73m3_i2c_write(state->i2c_client, AHB_MSB_ADDR_PTR, 0x3310); + if (ret < 0) + return ret; + + if (boot_from_rom) + return s5c73m3_rom_boot(state, true); + else + return s5c73m3_spi_boot(state, true); +} + +static const struct s5c73m3_frame_size *s5c73m3_find_frame_size( + struct v4l2_mbus_framefmt *fmt, + enum s5c73m3_resolution_types idx) +{ + const struct s5c73m3_frame_size *fs; + const struct s5c73m3_frame_size *best_fs; + int best_dist = INT_MAX; + int i; + + fs = s5c73m3_resolutions[idx]; + best_fs = NULL; + for (i = 0; i < s5c73m3_resolutions_len[idx]; ++i) { + int dist = abs(fs->width - fmt->width) + + abs(fs->height - fmt->height); + if (dist < best_dist) { + best_dist = dist; + best_fs = fs; + } + ++fs; + } + + return best_fs; +} + +static void s5c73m3_oif_try_format(struct s5c73m3 *state, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt, + const struct s5c73m3_frame_size **fs) +{ + u32 code; + + switch (fmt->pad) { + case OIF_ISP_PAD: + *fs = s5c73m3_find_frame_size(&fmt->format, RES_ISP); + code = S5C73M3_ISP_FMT; + break; + case OIF_JPEG_PAD: + *fs = s5c73m3_find_frame_size(&fmt->format, RES_JPEG); + code = S5C73M3_JPEG_FMT; + break; + case OIF_SOURCE_PAD: + default: + if (fmt->format.code == S5C73M3_JPEG_FMT) + code = S5C73M3_JPEG_FMT; + else + code = S5C73M3_ISP_FMT; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + *fs = state->oif_pix_size[RES_ISP]; + else + *fs = s5c73m3_find_frame_size( + v4l2_subdev_get_try_format(fh, + OIF_ISP_PAD), + RES_ISP); + break; + } + + s5c73m3_fill_mbus_fmt(&fmt->format, *fs, code); +} + +static void s5c73m3_try_format(struct s5c73m3 *state, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt, + const struct s5c73m3_frame_size **fs) +{ + u32 code; + + if (fmt->pad == S5C73M3_ISP_PAD) { + *fs = s5c73m3_find_frame_size(&fmt->format, RES_ISP); + code = S5C73M3_ISP_FMT; + } else { + *fs = s5c73m3_find_frame_size(&fmt->format, RES_JPEG); + code = S5C73M3_JPEG_FMT; + } + + s5c73m3_fill_mbus_fmt(&fmt->format, *fs, code); +} + +static int s5c73m3_oif_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + + if (fi->pad != OIF_SOURCE_PAD) + return -EINVAL; + + mutex_lock(&state->lock); + fi->interval = state->fiv->interval; + mutex_unlock(&state->lock); + + return 0; +} + +static int __s5c73m3_set_frame_interval(struct s5c73m3 *state, + struct v4l2_subdev_frame_interval *fi) +{ + const struct s5c73m3_frame_size *prev_size = + state->sensor_pix_size[RES_ISP]; + const struct s5c73m3_interval *fiv = &s5c73m3_intervals[0]; + unsigned int ret, min_err = UINT_MAX; + unsigned int i, fr_time; + + if (fi->interval.denominator == 0) + return -EINVAL; + + fr_time = fi->interval.numerator * 1000 / fi->interval.denominator; + + for (i = 0; i < ARRAY_SIZE(s5c73m3_intervals); i++) { + const struct s5c73m3_interval *iv = &s5c73m3_intervals[i]; + + if (prev_size->width > iv->size.width || + prev_size->height > iv->size.height) + continue; + + ret = abs(iv->interval.numerator / 1000 - fr_time); + if (ret < min_err) { + fiv = iv; + min_err = ret; + } + } + state->fiv = fiv; + + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "Changed frame interval to %u us\n", fiv->interval.numerator); + return 0; +} + +static int s5c73m3_oif_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + int ret; + + if (fi->pad != OIF_SOURCE_PAD) + return -EINVAL; + + v4l2_dbg(1, s5c73m3_dbg, sd, "Setting %d/%d frame interval\n", + fi->interval.numerator, fi->interval.denominator); + + mutex_lock(&state->lock); + + ret = __s5c73m3_set_frame_interval(state, fi); + if (!ret) { + if (state->streaming) + ret = s5c73m3_set_frame_rate(state); + else + state->apply_fiv = 1; + } + mutex_unlock(&state->lock); + return ret; +} + +static int s5c73m3_oif_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + const struct s5c73m3_interval *fi; + int ret = 0; + + if (fie->pad != OIF_SOURCE_PAD) + return -EINVAL; + if (fie->index > ARRAY_SIZE(s5c73m3_intervals)) + return -EINVAL; + + mutex_lock(&state->lock); + fi = &s5c73m3_intervals[fie->index]; + if (fie->width > fi->size.width || fie->height > fi->size.height) + ret = -EINVAL; + else + fie->interval = fi->interval; + mutex_unlock(&state->lock); + + return ret; +} + +static int s5c73m3_oif_get_pad_code(int pad, int index) +{ + if (pad == OIF_SOURCE_PAD) { + if (index > 1) + return -EINVAL; + return (index == 0) ? S5C73M3_ISP_FMT : S5C73M3_JPEG_FMT; + } + + if (index > 0) + return -EINVAL; + + return (pad == OIF_ISP_PAD) ? S5C73M3_ISP_FMT : S5C73M3_JPEG_FMT; +} + +static int s5c73m3_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + const struct s5c73m3_frame_size *fs; + u32 code; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + return 0; + } + + mutex_lock(&state->lock); + + switch (fmt->pad) { + case S5C73M3_ISP_PAD: + code = S5C73M3_ISP_FMT; + fs = state->sensor_pix_size[RES_ISP]; + break; + case S5C73M3_JPEG_PAD: + code = S5C73M3_JPEG_FMT; + fs = state->sensor_pix_size[RES_JPEG]; + break; + default: + mutex_unlock(&state->lock); + return -EINVAL; + } + s5c73m3_fill_mbus_fmt(&fmt->format, fs, code); + + mutex_unlock(&state->lock); + return 0; +} + +static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + const struct s5c73m3_frame_size *fs; + u32 code; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + return 0; + } + + mutex_lock(&state->lock); + + switch (fmt->pad) { + case OIF_ISP_PAD: + code = S5C73M3_ISP_FMT; + fs = state->oif_pix_size[RES_ISP]; + break; + case OIF_JPEG_PAD: + code = S5C73M3_JPEG_FMT; + fs = state->oif_pix_size[RES_JPEG]; + break; + case OIF_SOURCE_PAD: + code = state->mbus_code; + fs = state->oif_pix_size[RES_ISP]; + break; + default: + mutex_unlock(&state->lock); + return -EINVAL; + } + s5c73m3_fill_mbus_fmt(&fmt->format, fs, code); + + mutex_unlock(&state->lock); + return 0; +} + +static int s5c73m3_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + const struct s5c73m3_frame_size *frame_size = NULL; + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + struct v4l2_mbus_framefmt *mf; + int ret = 0; + + mutex_lock(&state->lock); + + s5c73m3_try_format(state, fh, fmt, &frame_size); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + } else { + switch (fmt->pad) { + case S5C73M3_ISP_PAD: + state->sensor_pix_size[RES_ISP] = frame_size; + break; + case S5C73M3_JPEG_PAD: + state->sensor_pix_size[RES_JPEG] = frame_size; + break; + default: + ret = -EBUSY; + } + + if (state->streaming) + ret = -EBUSY; + else + state->apply_fmt = 1; + } + + mutex_unlock(&state->lock); + + return ret; +} + +static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + const struct s5c73m3_frame_size *frame_size = NULL; + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + struct v4l2_mbus_framefmt *mf; + int ret = 0; + + mutex_lock(&state->lock); + + s5c73m3_oif_try_format(state, fh, fmt, &frame_size); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + } else { + switch (fmt->pad) { + case OIF_ISP_PAD: + state->oif_pix_size[RES_ISP] = frame_size; + break; + case OIF_JPEG_PAD: + state->oif_pix_size[RES_JPEG] = frame_size; + break; + case OIF_SOURCE_PAD: + state->mbus_code = fmt->format.code; + break; + default: + ret = -EBUSY; + } + + if (state->streaming) + ret = -EBUSY; + else + state->apply_fmt = 1; + } + + mutex_unlock(&state->lock); + + return ret; +} + +static int s5c73m3_oif_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + int i; + + if (pad != OIF_SOURCE_PAD || fd == NULL) + return -EINVAL; + + mutex_lock(&state->lock); + fd->num_entries = 2; + for (i = 0; i < fd->num_entries; i++) + fd->entry[i] = state->frame_desc.entry[i]; + mutex_unlock(&state->lock); + + return 0; +} + +static int s5c73m3_oif_set_frame_desc(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_frame_desc *fd) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + struct v4l2_mbus_frame_desc *frame_desc = &state->frame_desc; + int i; + + if (pad != OIF_SOURCE_PAD || fd == NULL) + return -EINVAL; + + fd->entry[0].length = 10 * SZ_1M; + fd->entry[1].length = max_t(u32, fd->entry[1].length, + S5C73M3_EMBEDDED_DATA_MAXLEN); + fd->num_entries = 2; + + mutex_lock(&state->lock); + for (i = 0; i < fd->num_entries; i++) + frame_desc->entry[i] = fd->entry[i]; + mutex_unlock(&state->lock); + + return 0; +} + +static int s5c73m3_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const int codes[] = { + [S5C73M3_ISP_PAD] = S5C73M3_ISP_FMT, + [S5C73M3_JPEG_PAD] = S5C73M3_JPEG_FMT}; + + if (code->index > 0 || code->pad >= S5C73M3_NUM_PADS) + return -EINVAL; + + code->code = codes[code->pad]; + + return 0; +} + +static int s5c73m3_oif_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + int ret; + + ret = s5c73m3_oif_get_pad_code(code->pad, code->index); + if (ret < 0) + return ret; + + code->code = ret; + + return 0; +} + +static int s5c73m3_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int idx; + + if (fse->pad == S5C73M3_ISP_PAD) { + if (fse->code != S5C73M3_ISP_FMT) + return -EINVAL; + idx = RES_ISP; + } else{ + if (fse->code != S5C73M3_JPEG_FMT) + return -EINVAL; + idx = RES_JPEG; + } + + if (fse->index >= s5c73m3_resolutions_len[idx]) + return -EINVAL; + + fse->min_width = s5c73m3_resolutions[idx][fse->index].width; + fse->max_width = fse->min_width; + fse->max_height = s5c73m3_resolutions[idx][fse->index].height; + fse->min_height = fse->max_height; + + return 0; +} + +static int s5c73m3_oif_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int idx; + + if (fse->pad == OIF_SOURCE_PAD) { + if (fse->index > 0) + return -EINVAL; + + switch (fse->code) { + case S5C73M3_JPEG_FMT: + case S5C73M3_ISP_FMT: { + struct v4l2_mbus_framefmt *mf = + v4l2_subdev_get_try_format(fh, OIF_ISP_PAD); + + fse->max_width = fse->min_width = mf->width; + fse->max_height = fse->min_height = mf->height; + return 0; + } + default: + return -EINVAL; + } + } + + if (fse->code != s5c73m3_oif_get_pad_code(fse->pad, 0)) + return -EINVAL; + + if (fse->pad == OIF_JPEG_PAD) + idx = RES_JPEG; + else + idx = RES_ISP; + + if (fse->index >= s5c73m3_resolutions_len[idx]) + return -EINVAL; + + fse->min_width = s5c73m3_resolutions[idx][fse->index].width; + fse->max_width = fse->min_width; + fse->max_height = s5c73m3_resolutions[idx][fse->index].height; + fse->min_height = fse->max_height; + + return 0; +} + +static int s5c73m3_oif_log_status(struct v4l2_subdev *sd) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + + v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); + + v4l2_info(sd, "power: %d, apply_fmt: %d\n", state->power, + state->apply_fmt); + + return 0; +} + +static int s5c73m3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_get_try_format(fh, S5C73M3_ISP_PAD); + s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], + S5C73M3_ISP_FMT); + + mf = v4l2_subdev_get_try_format(fh, S5C73M3_JPEG_PAD); + s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], + S5C73M3_JPEG_FMT); + + return 0; +} + +static int s5c73m3_oif_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *mf; + + mf = v4l2_subdev_get_try_format(fh, OIF_ISP_PAD); + s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], + S5C73M3_ISP_FMT); + + mf = v4l2_subdev_get_try_format(fh, OIF_JPEG_PAD); + s5c73m3_fill_mbus_fmt(mf, &s5c73m3_jpeg_resolutions[1], + S5C73M3_JPEG_FMT); + + mf = v4l2_subdev_get_try_format(fh, OIF_SOURCE_PAD); + s5c73m3_fill_mbus_fmt(mf, &s5c73m3_isp_resolutions[1], + S5C73M3_ISP_FMT); + return 0; +} + +static int s5c73m3_gpio_set_value(struct s5c73m3 *priv, int id, u32 val) +{ + if (!gpio_is_valid(priv->gpio[id].gpio)) + return 0; + gpio_set_value(priv->gpio[id].gpio, !!val); + return 1; +} + +static int s5c73m3_gpio_assert(struct s5c73m3 *priv, int id) +{ + return s5c73m3_gpio_set_value(priv, id, priv->gpio[id].level); +} + +static int s5c73m3_gpio_deassert(struct s5c73m3 *priv, int id) +{ + return s5c73m3_gpio_set_value(priv, id, !priv->gpio[id].level); +} + +static int __s5c73m3_power_on(struct s5c73m3 *state) +{ + int i, ret; + + for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) { + ret = regulator_enable(state->supplies[i].consumer); + if (ret) + goto err; + } + + s5c73m3_gpio_deassert(state, STBY); + usleep_range(100, 200); + + s5c73m3_gpio_deassert(state, RST); + usleep_range(50, 100); + + return 0; +err: + for (--i; i >= 0; i--) + regulator_disable(state->supplies[i].consumer); + return ret; +} + +static int __s5c73m3_power_off(struct s5c73m3 *state) +{ + int i, ret; + + if (s5c73m3_gpio_assert(state, RST)) + usleep_range(10, 50); + + if (s5c73m3_gpio_assert(state, STBY)) + usleep_range(100, 200); + state->streaming = 0; + state->isp_ready = 0; + + for (i = S5C73M3_MAX_SUPPLIES - 1; i >= 0; i--) { + ret = regulator_disable(state->supplies[i].consumer); + if (ret) + goto err; + } + return 0; +err: + for (++i; i < S5C73M3_MAX_SUPPLIES; i++) + regulator_enable(state->supplies[i].consumer); + + return ret; +} + +static int s5c73m3_oif_set_power(struct v4l2_subdev *sd, int on) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + int ret = 0; + + mutex_lock(&state->lock); + + if (on && !state->power) { + ret = __s5c73m3_power_on(state); + if (!ret) + ret = s5c73m3_isp_init(state); + if (!ret) { + state->apply_fiv = 1; + state->apply_fmt = 1; + } + } else if (!on == state->power) { + ret = s5c73m3_set_af_softlanding(state); + if (!ret) + ret = __s5c73m3_power_off(state); + else + v4l2_err(sd, "Soft landing lens failed\n"); + } + if (!ret) + state->power += on ? 1 : -1; + + v4l2_dbg(1, s5c73m3_dbg, sd, "%s: power: %d\n", + __func__, state->power); + + mutex_unlock(&state->lock); + return ret; +} + +static int s5c73m3_oif_registered(struct v4l2_subdev *sd) +{ + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + int ret; + + ret = v4l2_device_register_subdev(sd->v4l2_dev, &state->sensor_sd); + if (ret) { + v4l2_err(sd->v4l2_dev, "Failed to register %s\n", + state->oif_sd.name); + return ret; + } + + ret = media_entity_create_link(&state->sensor_sd.entity, + S5C73M3_ISP_PAD, &state->oif_sd.entity, OIF_ISP_PAD, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + + ret = media_entity_create_link(&state->sensor_sd.entity, + S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + + mutex_lock(&state->lock); + ret = __s5c73m3_power_on(state); + if (ret == 0) + s5c73m3_get_fw_version(state); + + __s5c73m3_power_off(state); + mutex_unlock(&state->lock); + + v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n", + __func__, ret ? "failed" : "succeded", ret); + + return ret; +} + +static const struct v4l2_subdev_internal_ops s5c73m3_internal_ops = { + .open = s5c73m3_open, +}; + +static const struct v4l2_subdev_pad_ops s5c73m3_pad_ops = { + .enum_mbus_code = s5c73m3_enum_mbus_code, + .enum_frame_size = s5c73m3_enum_frame_size, + .get_fmt = s5c73m3_get_fmt, + .set_fmt = s5c73m3_set_fmt, +}; + +static const struct v4l2_subdev_ops s5c73m3_subdev_ops = { + .pad = &s5c73m3_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops oif_internal_ops = { + .registered = s5c73m3_oif_registered, + .open = s5c73m3_oif_open, +}; + +static const struct v4l2_subdev_pad_ops s5c73m3_oif_pad_ops = { + .enum_mbus_code = s5c73m3_oif_enum_mbus_code, + .enum_frame_size = s5c73m3_oif_enum_frame_size, + .enum_frame_interval = s5c73m3_oif_enum_frame_interval, + .get_fmt = s5c73m3_oif_get_fmt, + .set_fmt = s5c73m3_oif_set_fmt, + .get_frame_desc = s5c73m3_oif_get_frame_desc, + .set_frame_desc = s5c73m3_oif_set_frame_desc, +}; + +static const struct v4l2_subdev_core_ops s5c73m3_oif_core_ops = { + .s_power = s5c73m3_oif_set_power, + .log_status = s5c73m3_oif_log_status, +}; + +static const struct v4l2_subdev_video_ops s5c73m3_oif_video_ops = { + .s_stream = s5c73m3_oif_s_stream, + .g_frame_interval = s5c73m3_oif_g_frame_interval, + .s_frame_interval = s5c73m3_oif_s_frame_interval, +}; + +static const struct v4l2_subdev_ops oif_subdev_ops = { + .core = &s5c73m3_oif_core_ops, + .pad = &s5c73m3_oif_pad_ops, + .video = &s5c73m3_oif_video_ops, +}; + +static int s5c73m3_configure_gpio(int nr, int val, const char *name) +{ + unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + int ret; + + if (!gpio_is_valid(nr)) + return 0; + ret = gpio_request_one(nr, flags, name); + if (!ret) + gpio_export(nr, 0); + return ret; +} + +static int s5c73m3_free_gpios(struct s5c73m3 *state) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(state->gpio); i++) { + if (!gpio_is_valid(state->gpio[i].gpio)) + continue; + gpio_free(state->gpio[i].gpio); + state->gpio[i].gpio = -EINVAL; + } + return 0; +} + +static int s5c73m3_configure_gpios(struct s5c73m3 *state, + const struct s5c73m3_platform_data *pdata) +{ + const struct s5c73m3_gpio *gpio = &pdata->gpio_stby; + int ret; + + state->gpio[STBY].gpio = -EINVAL; + state->gpio[RST].gpio = -EINVAL; + + ret = s5c73m3_configure_gpio(gpio->gpio, gpio->level, "S5C73M3_STBY"); + if (ret) { + s5c73m3_free_gpios(state); + return ret; + } + state->gpio[STBY] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + gpio = &pdata->gpio_reset; + ret = s5c73m3_configure_gpio(gpio->gpio, gpio->level, "S5C73M3_RST"); + if (ret) { + s5c73m3_free_gpios(state); + return ret; + } + state->gpio[RST] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + return 0; +} + +static int __devinit s5c73m3_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct s5c73m3_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + struct v4l2_subdev *oif_sd; + struct s5c73m3 *state; + int ret, i; + + if (pdata == NULL) { + dev_err(&client->dev, "Platform data not specified\n"); + return -EINVAL; + } + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + mutex_init(&state->lock); + sd = &state->sensor_sd; + oif_sd = &state->oif_sd; + + v4l2_subdev_init(sd, &s5c73m3_subdev_ops); + sd->owner = client->driver->driver.owner; + v4l2_set_subdevdata(sd, state); + strlcpy(sd->name, "S5C73M3", sizeof(sd->name)); + + sd->internal_ops = &s5c73m3_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + state->sensor_pads[S5C73M3_JPEG_PAD].flags = MEDIA_PAD_FL_SOURCE; + state->sensor_pads[S5C73M3_ISP_PAD].flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + + ret = media_entity_init(&sd->entity, S5C73M3_NUM_PADS, + state->sensor_pads, 0); + if (ret < 0) + return ret; + + v4l2_i2c_subdev_init(oif_sd, client, &oif_subdev_ops); + strcpy(oif_sd->name, "S5C73M3-OIF"); + + oif_sd->internal_ops = &oif_internal_ops; + oif_sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + state->oif_pads[OIF_ISP_PAD].flags = MEDIA_PAD_FL_SINK; + state->oif_pads[OIF_JPEG_PAD].flags = MEDIA_PAD_FL_SINK; + state->oif_pads[OIF_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE; + oif_sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; + + ret = media_entity_init(&oif_sd->entity, OIF_NUM_PADS, + state->oif_pads, 0); + if (ret < 0) + return ret; + + state->mclk_frequency = pdata->mclk_frequency; + state->bus_type = pdata->bus_type; + + ret = s5c73m3_configure_gpios(state, pdata); + if (ret) + goto out_err1; + + for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) + state->supplies[i].supply = s5c73m3_supply_names[i]; + + ret = regulator_bulk_get(dev, S5C73M3_MAX_SUPPLIES, + state->supplies); + if (ret) { + dev_err(dev, "failed to get regulators\n"); + goto out_err2; + } + + ret = s5c73m3_init_controls(state); + if (ret) + goto out_err3; + + state->sensor_pix_size[RES_ISP] = &s5c73m3_isp_resolutions[1]; + state->sensor_pix_size[RES_JPEG] = &s5c73m3_jpeg_resolutions[1]; + state->oif_pix_size[RES_ISP] = state->sensor_pix_size[RES_ISP]; + state->oif_pix_size[RES_JPEG] = state->sensor_pix_size[RES_JPEG]; + + state->mbus_code = S5C73M3_ISP_FMT; + + state->fiv = &s5c73m3_intervals[S5C73M3_DEFAULT_FRAME_INTERVAL]; + + state->fw_file_version[0] = 'G'; + state->fw_file_version[1] = 'C'; + + ret = s5c73m3_register_spi_driver(state); + if (ret < 0) + goto out_err3; + + state->i2c_client = client; + + v4l2_info(sd, "%s: completed succesfully\n", __func__); + return 0; + +out_err3: + regulator_bulk_free(S5C73M3_MAX_SUPPLIES, state->supplies); +out_err2: + s5c73m3_free_gpios(state); +out_err1: + media_entity_cleanup(&sd->entity); + return ret; +} + +static int __devexit s5c73m3_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + + v4l2_device_unregister_subdev(sd); + + v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); + + s5c73m3_unregister_spi_driver(state); + regulator_bulk_free(S5C73M3_MAX_SUPPLIES, state->supplies); + s5c73m3_free_gpios(state); + + return 0; +} + +static const struct i2c_device_id s5c73m3_id[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, s5c73m3_id); + +static struct i2c_driver s5c73m3_i2c_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = s5c73m3_probe, + .remove = __devexit_p(s5c73m3_remove), + .id_table = s5c73m3_id, +}; + +module_i2c_driver(s5c73m3_i2c_driver); + +MODULE_DESCRIPTION("Samsung S5C73M3 camera driver"); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c new file mode 100644 index 000000000000..8001cde1db1e --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3-ctrls.c @@ -0,0 +1,563 @@ +/* + * Samsung LSI S5C73M3 8M pixel camera driver + * + * Copyright (C) 2012, Samsung Electronics, Co., Ltd. + * Sylwester Nawrocki + * Andrzej Hajda + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "s5c73m3.h" + +static int s5c73m3_get_af_status(struct s5c73m3 *state, struct v4l2_ctrl *ctrl) +{ + u16 reg = REG_AF_STATUS_UNFOCUSED; + + int ret = s5c73m3_read(state, REG_AF_STATUS, ®); + + switch (reg) { + case REG_CAF_STATUS_FIND_SEARCH_DIR: + case REG_AF_STATUS_FOCUSING: + case REG_CAF_STATUS_FOCUSING: + ctrl->val = V4L2_AUTO_FOCUS_STATUS_BUSY; + break; + case REG_CAF_STATUS_FOCUSED: + case REG_AF_STATUS_FOCUSED: + ctrl->val = V4L2_AUTO_FOCUS_STATUS_REACHED; + break; + default: + v4l2_info(&state->sensor_sd, "Unknown AF status %#x\n", reg); + /* Fall through */ + case REG_CAF_STATUS_UNFOCUSED: + case REG_AF_STATUS_UNFOCUSED: + case REG_AF_STATUS_INVALID: + ctrl->val = V4L2_AUTO_FOCUS_STATUS_FAILED; + break; + } + + return ret; +} + +static int s5c73m3_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sensor_sd(ctrl); + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + int ret; + + if (state->power == 0) + return -EBUSY; + + switch (ctrl->id) { + case V4L2_CID_FOCUS_AUTO: + ret = s5c73m3_get_af_status(state, state->ctrls.af_status); + if (ret) + return ret; + break; + } + + return 0; +} + +static int s5c73m3_set_colorfx(struct s5c73m3 *state, int val) +{ + static const unsigned short colorfx[][2] = { + { V4L2_COLORFX_NONE, COMM_IMAGE_EFFECT_NONE }, + { V4L2_COLORFX_BW, COMM_IMAGE_EFFECT_MONO }, + { V4L2_COLORFX_SEPIA, COMM_IMAGE_EFFECT_SEPIA }, + { V4L2_COLORFX_NEGATIVE, COMM_IMAGE_EFFECT_NEGATIVE }, + { V4L2_COLORFX_AQUA, COMM_IMAGE_EFFECT_AQUA }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(colorfx); i++) { + if (colorfx[i][0] != val) + continue; + + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "Setting %s color effect\n", + v4l2_ctrl_get_menu(state->ctrls.colorfx->id)[i]); + + return s5c73m3_isp_command(state, COMM_IMAGE_EFFECT, + colorfx[i][1]); + } + return -EINVAL; +} + +/* Set exposure metering/exposure bias */ +static int s5c73m3_set_exposure(struct s5c73m3 *state, int auto_exp) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + struct s5c73m3_ctrls *ctrls = &state->ctrls; + int ret = 0; + + if (ctrls->exposure_metering->is_new) { + u16 metering; + + switch (ctrls->exposure_metering->val) { + case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: + metering = COMM_METERING_CENTER; + break; + case V4L2_EXPOSURE_METERING_SPOT: + metering = COMM_METERING_SPOT; + break; + default: + metering = COMM_METERING_AVERAGE; + break; + } + + ret = s5c73m3_isp_command(state, COMM_METERING, metering); + } + + if (!ret && ctrls->exposure_bias->is_new) { + u16 exp_bias = ctrls->exposure_bias->val; + ret = s5c73m3_isp_command(state, COMM_EV, exp_bias); + } + + v4l2_dbg(1, s5c73m3_dbg, sd, + "%s: exposure bias: %#x, metering: %#x (%d)\n", __func__, + ctrls->exposure_bias->val, ctrls->exposure_metering->val, ret); + + return ret; +} + +static int s5c73m3_set_white_balance(struct s5c73m3 *state, int val) +{ + static const unsigned short wb[][2] = { + { V4L2_WHITE_BALANCE_INCANDESCENT, COMM_AWB_MODE_INCANDESCENT}, + { V4L2_WHITE_BALANCE_FLUORESCENT, COMM_AWB_MODE_FLUORESCENT1}, + { V4L2_WHITE_BALANCE_FLUORESCENT_H, COMM_AWB_MODE_FLUORESCENT2}, + { V4L2_WHITE_BALANCE_CLOUDY, COMM_AWB_MODE_CLOUDY}, + { V4L2_WHITE_BALANCE_DAYLIGHT, COMM_AWB_MODE_DAYLIGHT}, + { V4L2_WHITE_BALANCE_AUTO, COMM_AWB_MODE_AUTO}, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(wb); i++) { + if (wb[i][0] != val) + continue; + + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, + "Setting white balance to: %s\n", + v4l2_ctrl_get_menu(state->ctrls.auto_wb->id)[i]); + + return s5c73m3_isp_command(state, COMM_AWB_MODE, wb[i][1]); + } + + return -EINVAL; +} + +static int s5c73m3_af_run(struct s5c73m3 *state, bool on) +{ + struct s5c73m3_ctrls *c = &state->ctrls; + + if (!on) + return s5c73m3_isp_command(state, COMM_AF_CON, + COMM_AF_CON_STOP); + + if (c->focus_auto->val) + return s5c73m3_isp_command(state, COMM_AF_MODE, + COMM_AF_MODE_PREVIEW_CAF_START); + + return s5c73m3_isp_command(state, COMM_AF_CON, COMM_AF_CON_START); +} + +static int s5c73m3_3a_lock(struct s5c73m3 *state, struct v4l2_ctrl *ctrl) +{ + bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; + bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; + bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; + int ret = 0; + + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { + ret = s5c73m3_isp_command(state, COMM_AE_CON, + ae_lock ? COMM_AE_STOP : COMM_AE_START); + if (ret) + return ret; + } + + if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) + && state->ctrls.auto_wb->val) { + ret = s5c73m3_isp_command(state, COMM_AWB_CON, + awb_lock ? COMM_AWB_STOP : COMM_AWB_START); + if (ret) + return ret; + } + + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) + ret = s5c73m3_af_run(state, ~af_lock); + + return ret; +} + +static int s5c73m3_set_auto_focus(struct s5c73m3 *state, int caf) +{ + struct s5c73m3_ctrls *c = &state->ctrls; + int ret = 1; + + if (c->af_distance->is_new) { + u16 mode = (c->af_distance->val == V4L2_AUTO_FOCUS_RANGE_MACRO) + ? COMM_AF_MODE_MACRO : COMM_AF_MODE_NORMAL; + ret = s5c73m3_isp_command(state, COMM_AF_MODE, mode); + if (ret != 0) + return ret; + } + + if (!ret || (c->focus_auto->is_new && c->focus_auto->val) || + c->af_start->is_new) + ret = s5c73m3_af_run(state, 1); + else if ((c->focus_auto->is_new && !c->focus_auto->val) || + c->af_stop->is_new) + ret = s5c73m3_af_run(state, 0); + else + ret = 0; + + return ret; +} + +static int s5c73m3_set_contrast(struct s5c73m3 *state, int val) +{ + u16 reg = (val < 0) ? -val + 2 : val; + return s5c73m3_isp_command(state, COMM_CONTRAST, reg); +} + +static int s5c73m3_set_saturation(struct s5c73m3 *state, int val) +{ + u16 reg = (val < 0) ? -val + 2 : val; + return s5c73m3_isp_command(state, COMM_SATURATION, reg); +} + +static int s5c73m3_set_sharpness(struct s5c73m3 *state, int val) +{ + u16 reg = (val < 0) ? -val + 2 : val; + return s5c73m3_isp_command(state, COMM_SHARPNESS, reg); +} + +static int s5c73m3_set_iso(struct s5c73m3 *state, int val) +{ + u32 iso; + + if (val == V4L2_ISO_SENSITIVITY_MANUAL) + iso = state->ctrls.iso->val + 1; + else + iso = 0; + + return s5c73m3_isp_command(state, COMM_ISO, iso); +} + +static int s5c73m3_set_stabilization(struct s5c73m3 *state, int val) +{ + struct v4l2_subdev *sd = &state->sensor_sd; + + v4l2_dbg(1, s5c73m3_dbg, sd, "Image stabilization: %d\n", val); + + return s5c73m3_isp_command(state, COMM_FRAME_RATE, val ? + COMM_FRAME_RATE_ANTI_SHAKE : COMM_FRAME_RATE_AUTO_SET); +} + +static int s5c73m3_set_jpeg_quality(struct s5c73m3 *state, int quality) +{ + int reg; + + if (quality <= 65) + reg = COMM_IMAGE_QUALITY_NORMAL; + else if (quality <= 75) + reg = COMM_IMAGE_QUALITY_FINE; + else + reg = COMM_IMAGE_QUALITY_SUPERFINE; + + return s5c73m3_isp_command(state, COMM_IMAGE_QUALITY, reg); +} + +static int s5c73m3_set_scene_program(struct s5c73m3 *state, int val) +{ + static const unsigned short scene_lookup[] = { + COMM_SCENE_MODE_NONE, /* V4L2_SCENE_MODE_NONE */ + COMM_SCENE_MODE_AGAINST_LIGHT,/* V4L2_SCENE_MODE_BACKLIGHT */ + COMM_SCENE_MODE_BEACH, /* V4L2_SCENE_MODE_BEACH_SNOW */ + COMM_SCENE_MODE_CANDLE, /* V4L2_SCENE_MODE_CANDLE_LIGHT */ + COMM_SCENE_MODE_DAWN, /* V4L2_SCENE_MODE_DAWN_DUSK */ + COMM_SCENE_MODE_FALL, /* V4L2_SCENE_MODE_FALL_COLORS */ + COMM_SCENE_MODE_FIRE, /* V4L2_SCENE_MODE_FIREWORKS */ + COMM_SCENE_MODE_LANDSCAPE, /* V4L2_SCENE_MODE_LANDSCAPE */ + COMM_SCENE_MODE_NIGHT, /* V4L2_SCENE_MODE_NIGHT */ + COMM_SCENE_MODE_INDOOR, /* V4L2_SCENE_MODE_PARTY_INDOOR */ + COMM_SCENE_MODE_PORTRAIT, /* V4L2_SCENE_MODE_PORTRAIT */ + COMM_SCENE_MODE_SPORTS, /* V4L2_SCENE_MODE_SPORTS */ + COMM_SCENE_MODE_SUNSET, /* V4L2_SCENE_MODE_SUNSET */ + COMM_SCENE_MODE_TEXT, /* V4L2_SCENE_MODE_TEXT */ + }; + + v4l2_dbg(1, s5c73m3_dbg, &state->sensor_sd, "Setting %s scene mode\n", + v4l2_ctrl_get_menu(state->ctrls.scene_mode->id)[val]); + + return s5c73m3_isp_command(state, COMM_SCENE_MODE, scene_lookup[val]); +} + +static int s5c73m3_set_power_line_freq(struct s5c73m3 *state, int val) +{ + unsigned int pwr_line_freq = COMM_FLICKER_NONE; + + switch (val) { + case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: + pwr_line_freq = COMM_FLICKER_NONE; + break; + case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: + pwr_line_freq = COMM_FLICKER_AUTO_50HZ; + break; + case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: + pwr_line_freq = COMM_FLICKER_AUTO_60HZ; + break; + default: + case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: + pwr_line_freq = COMM_FLICKER_NONE; + } + + return s5c73m3_isp_command(state, COMM_FLICKER_MODE, pwr_line_freq); +} + +static int s5c73m3_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sensor_sd(ctrl); + struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); + int ret = 0; + + v4l2_dbg(1, s5c73m3_dbg, sd, "set_ctrl: %s, value: %d\n", + ctrl->name, ctrl->val); + + mutex_lock(&state->lock); + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (state->power == 0) + goto unlock; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) { + ret = -EINVAL; + goto unlock; + } + + switch (ctrl->id) { + case V4L2_CID_3A_LOCK: + ret = s5c73m3_3a_lock(state, ctrl); + break; + + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ret = s5c73m3_set_white_balance(state, ctrl->val); + break; + + case V4L2_CID_CONTRAST: + ret = s5c73m3_set_contrast(state, ctrl->val); + break; + + case V4L2_CID_COLORFX: + ret = s5c73m3_set_colorfx(state, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + ret = s5c73m3_set_exposure(state, ctrl->val); + break; + + case V4L2_CID_FOCUS_AUTO: + ret = s5c73m3_set_auto_focus(state, ctrl->val); + break; + + case V4L2_CID_IMAGE_STABILIZATION: + ret = s5c73m3_set_stabilization(state, ctrl->val); + break; + + case V4L2_CID_ISO_SENSITIVITY: + ret = s5c73m3_set_iso(state, ctrl->val); + break; + + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ret = s5c73m3_set_jpeg_quality(state, ctrl->val); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + ret = s5c73m3_set_power_line_freq(state, ctrl->val); + break; + + case V4L2_CID_SATURATION: + ret = s5c73m3_set_saturation(state, ctrl->val); + break; + + case V4L2_CID_SCENE_MODE: + ret = s5c73m3_set_scene_program(state, ctrl->val); + break; + + case V4L2_CID_SHARPNESS: + ret = s5c73m3_set_sharpness(state, ctrl->val); + break; + + case V4L2_CID_WIDE_DYNAMIC_RANGE: + ret = s5c73m3_isp_command(state, COMM_WDR, !!ctrl->val); + break; + + case V4L2_CID_ZOOM_ABSOLUTE: + ret = s5c73m3_isp_command(state, COMM_ZOOM_STEP, ctrl->val); + break; + } +unlock: + mutex_unlock(&state->lock); + return ret; +} + +static const struct v4l2_ctrl_ops s5c73m3_ctrl_ops = { + .g_volatile_ctrl = s5c73m3_g_volatile_ctrl, + .s_ctrl = s5c73m3_s_ctrl, +}; + +/* Supported manual ISO values */ +static const s64 iso_qmenu[] = { + /* COMM_ISO: 0x0001...0x0004 */ + 100, 200, 400, 800, +}; + +/* Supported exposure bias values (-2.0EV...+2.0EV) */ +static const s64 ev_bias_qmenu[] = { + /* COMM_EV: 0x0000...0x0008 */ + -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 +}; + +int s5c73m3_init_controls(struct s5c73m3 *state) +{ + const struct v4l2_ctrl_ops *ops = &s5c73m3_ctrl_ops; + struct s5c73m3_ctrls *ctrls = &state->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + + int ret = v4l2_ctrl_handler_init(hdl, 22); + if (ret) + return ret; + + /* White balance */ + ctrls->auto_wb = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + 9, ~0x15e, V4L2_WHITE_BALANCE_AUTO); + + /* Exposure (only automatic exposure) */ + ctrls->auto_exposure = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, 0, ~0x01, V4L2_EXPOSURE_AUTO); + + ctrls->exposure_bias = v4l2_ctrl_new_int_menu(hdl, ops, + V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(ev_bias_qmenu) - 1, + ARRAY_SIZE(ev_bias_qmenu)/2 - 1, + ev_bias_qmenu); + + ctrls->exposure_metering = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_METERING, + 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); + + /* Auto focus */ + ctrls->focus_auto = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_FOCUS_AUTO, 0, 1, 1, 0); + + ctrls->af_start = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_AUTO_FOCUS_START, 0, 1, 1, 0); + + ctrls->af_stop = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_AUTO_FOCUS_STOP, 0, 1, 1, 0); + + ctrls->af_status = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_AUTO_FOCUS_STATUS, 0, + (V4L2_AUTO_FOCUS_STATUS_BUSY | + V4L2_AUTO_FOCUS_STATUS_REACHED | + V4L2_AUTO_FOCUS_STATUS_FAILED), + 0, V4L2_AUTO_FOCUS_STATUS_IDLE); + + ctrls->af_distance = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_AUTO_FOCUS_RANGE, + V4L2_AUTO_FOCUS_RANGE_MACRO, + ~(1 << V4L2_AUTO_FOCUS_RANGE_NORMAL | + 1 << V4L2_AUTO_FOCUS_RANGE_MACRO), + V4L2_AUTO_FOCUS_RANGE_NORMAL); + /* ISO sensitivity */ + ctrls->auto_iso = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_ISO_SENSITIVITY_AUTO, 1, 0, + V4L2_ISO_SENSITIVITY_AUTO); + + ctrls->iso = v4l2_ctrl_new_int_menu(hdl, ops, + V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, + ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); + + ctrls->contrast = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_CONTRAST, -2, 2, 1, 0); + + ctrls->saturation = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_SATURATION, -2, 2, 1, 0); + + ctrls->sharpness = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_SHARPNESS, -2, 2, 1, 0); + + ctrls->zoom = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_ZOOM_ABSOLUTE, 0, 30, 1, 0); + + ctrls->colorfx = v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, + V4L2_COLORFX_AQUA, ~0x40f, V4L2_COLORFX_NONE); + + ctrls->wdr = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); + + ctrls->stabilization = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO); + + ctrls->jpeg_quality = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); + + ctrls->scene_mode = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_SCENE_MODE, V4L2_SCENE_MODE_TEXT, ~0x3fff, + V4L2_SCENE_MODE_NONE); + + ctrls->aaa_lock = v4l2_ctrl_new_std(hdl, ops, + V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); + + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + v4l2_ctrl_auto_cluster(3, &ctrls->auto_exposure, 0, false); + ctrls->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_UPDATE; + v4l2_ctrl_auto_cluster(2, &ctrls->auto_iso, 0, false); + ctrls->af_status->flags |= V4L2_CTRL_FLAG_VOLATILE; + v4l2_ctrl_cluster(6, &ctrls->focus_auto); + + state->sensor_sd.ctrl_handler = hdl; + + return 0; +} diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c new file mode 100644 index 000000000000..889139c2ac5e --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c @@ -0,0 +1,156 @@ +/* + * Samsung LSI S5C73M3 8M pixel camera driver + * + * Copyright (C) 2012, Samsung Electronics, Co., Ltd. + * Sylwester Nawrocki + * Andrzej Hajda + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "s5c73m3.h" + +#define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI" + +enum spi_direction { + SPI_DIR_RX, + SPI_DIR_TX +}; + +static int spi_xmit(struct spi_device *spi_dev, void *addr, const int len, + enum spi_direction dir) +{ + struct spi_message msg; + int r; + struct spi_transfer xfer = { + .len = len, + }; + + if (dir == SPI_DIR_TX) + xfer.tx_buf = addr; + else + xfer.rx_buf = addr; + + if (spi_dev == NULL) { + dev_err(&spi_dev->dev, "SPI device is uninitialized\n"); + return -ENODEV; + } + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + r = spi_sync(spi_dev, &msg); + if (r < 0) + dev_err(&spi_dev->dev, "%s spi_sync failed %d\n", __func__, r); + + return r; +} + +int s5c73m3_spi_write(struct s5c73m3 *state, const void *addr, + const unsigned int len, const unsigned int tx_size) +{ + struct spi_device *spi_dev = state->spi_dev; + u32 count = len / tx_size; + u32 extra = len % tx_size; + unsigned int i, j = 0; + u8 padding[32]; + int r = 0; + + memset(padding, 0, sizeof(padding)); + + for (i = 0; i < count ; i++) { + r = spi_xmit(spi_dev, (void *)addr + j, tx_size, SPI_DIR_TX); + if (r < 0) + return r; + j += tx_size; + } + + if (extra > 0) { + r = spi_xmit(spi_dev, (void *)addr + j, extra, SPI_DIR_TX); + if (r < 0) + return r; + } + + return spi_xmit(spi_dev, padding, sizeof(padding), SPI_DIR_TX); +} + +int s5c73m3_spi_read(struct s5c73m3 *state, void *addr, + const unsigned int len, const unsigned int tx_size) +{ + struct spi_device *spi_dev = state->spi_dev; + u32 count = len / tx_size; + u32 extra = len % tx_size; + unsigned int i, j = 0; + int r = 0; + + for (i = 0; i < count ; i++) { + r = spi_xmit(spi_dev, addr + j, tx_size, SPI_DIR_RX); + if (r < 0) + return r; + j += tx_size; + } + + if (extra > 0) + return spi_xmit(spi_dev, addr + j, extra, SPI_DIR_RX); + + return 0; +} + +static int __devinit s5c73m3_spi_probe(struct spi_device *spi) +{ + int r; + struct s5c73m3 *state = container_of(spi->dev.driver, struct s5c73m3, + spidrv.driver); + spi->bits_per_word = 32; + + r = spi_setup(spi); + if (r < 0) { + dev_err(&spi->dev, "spi_setup() failed\n"); + return r; + } + + mutex_lock(&state->lock); + state->spi_dev = spi; + mutex_unlock(&state->lock); + + v4l2_info(&state->sensor_sd, "S5C73M3 SPI probed successfully\n"); + return 0; +} + +static int __devexit s5c73m3_spi_remove(struct spi_device *spi) +{ + return 0; +} + +int s5c73m3_register_spi_driver(struct s5c73m3 *state) +{ + struct spi_driver *spidrv = &state->spidrv; + + spidrv->remove = __devexit_p(s5c73m3_spi_remove); + spidrv->probe = s5c73m3_spi_probe; + spidrv->driver.name = S5C73M3_SPI_DRV_NAME; + spidrv->driver.bus = &spi_bus_type; + spidrv->driver.owner = THIS_MODULE; + + return spi_register_driver(spidrv); +} + +void s5c73m3_unregister_spi_driver(struct s5c73m3 *state) +{ + spi_unregister_driver(&state->spidrv); +} diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h new file mode 100644 index 000000000000..9d2c08652246 --- /dev/null +++ b/drivers/media/i2c/s5c73m3/s5c73m3.h @@ -0,0 +1,459 @@ +/* + * Samsung LSI S5C73M3 8M pixel camera driver + * + * Copyright (C) 2012, Samsung Electronics, Co., Ltd. + * Sylwester Nawrocki + * Andrzej Hajda + * + * 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. + * + * 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. + */ +#ifndef S5C73M3_H_ +#define S5C73M3_H_ + +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "S5C73M3" + +#define S5C73M3_ISP_FMT V4L2_MBUS_FMT_VYUY8_2X8 +#define S5C73M3_JPEG_FMT V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8 + +/* Subdevs pad index definitions */ +enum s5c73m3_pads { + S5C73M3_ISP_PAD, + S5C73M3_JPEG_PAD, + S5C73M3_NUM_PADS +}; + +enum s5c73m3_oif_pads { + OIF_ISP_PAD, + OIF_JPEG_PAD, + OIF_SOURCE_PAD, + OIF_NUM_PADS +}; + +#define S5C73M3_SENSOR_FW_LEN 6 +#define S5C73M3_SENSOR_TYPE_LEN 12 + +#define S5C73M3_REG(_addrh, _addrl) (((_addrh) << 16) | _addrl) + +#define AHB_MSB_ADDR_PTR 0xfcfc +#define REG_CMDWR_ADDRH 0x0050 +#define REG_CMDWR_ADDRL 0x0054 +#define REG_CMDRD_ADDRH 0x0058 +#define REG_CMDRD_ADDRL 0x005c +#define REG_CMDBUF_ADDR 0x0f14 + +#define REG_I2C_SEQ_STATUS S5C73M3_REG(0x0009, 0x59A6) +#define SEQ_END_PLL (1<<0x0) +#define SEQ_END_SENSOR (1<<0x1) +#define SEQ_END_GPIO (1<<0x2) +#define SEQ_END_FROM (1<<0x3) +#define SEQ_END_STABLE_AE_AWB (1<<0x4) +#define SEQ_END_READY_I2C_CMD (1<<0x5) + +#define REG_I2C_STATUS S5C73M3_REG(0x0009, 0x599E) +#define I2C_STATUS_CIS_I2C (1<<0x0) +#define I2C_STATUS_AF_INIT (1<<0x1) +#define I2C_STATUS_CAL_DATA (1<<0x2) +#define I2C_STATUS_FRAME_COUNT (1<<0x3) +#define I2C_STATUS_FROM_INIT (1<<0x4) +#define I2C_STATUS_I2C_CIS_STREAM_OFF (1<<0x5) +#define I2C_STATUS_I2C_N_CMD_OVER (1<<0x6) +#define I2C_STATUS_I2C_N_CMD_MISMATCH (1<<0x7) +#define I2C_STATUS_CHECK_BIN_CRC (1<<0x8) +#define I2C_STATUS_EXCEPTION (1<<0x9) +#define I2C_STATUS_INIF_INIT_STATE (0x8) + +#define REG_STATUS S5C73M3_REG(0x0009, 0x5080) +#define REG_STATUS_BOOT_SUB_MAIN_ENTER 0xff01 +#define REG_STATUS_BOOT_SRAM_TIMING_OK 0xff02 +#define REG_STATUS_BOOT_INTERRUPTS_EN 0xff03 +#define REG_STATUS_BOOT_R_PLL_DONE 0xff04 +#define REG_STATUS_BOOT_R_PLL_LOCKTIME_DONE 0xff05 +#define REG_STATUS_BOOT_DELAY_COUNT_DONE 0xff06 +#define REG_STATUS_BOOT_I_PLL_DONE 0xff07 +#define REG_STATUS_BOOT_I_PLL_LOCKTIME_DONE 0xff08 +#define REG_STATUS_BOOT_PLL_INIT_OK 0xff09 +#define REG_STATUS_BOOT_SENSOR_INIT_OK 0xff0a +#define REG_STATUS_BOOT_GPIO_SETTING_OK 0xff0b +#define REG_STATUS_BOOT_READ_CAL_DATA_OK 0xff0c +#define REG_STATUS_BOOT_STABLE_AE_AWB_OK 0xff0d +#define REG_STATUS_ISP_COMMAND_COMPLETED 0xffff +#define REG_STATUS_EXCEPTION_OCCURED 0xdead + +#define COMM_RESULT_OFFSET S5C73M3_REG(0x0009, 0x5000) + +#define COMM_IMG_OUTPUT 0x0902 +#define COMM_IMG_OUTPUT_HDR 0x0008 +#define COMM_IMG_OUTPUT_YUV 0x0009 +#define COMM_IMG_OUTPUT_INTERLEAVED 0x000d + +#define COMM_STILL_PRE_FLASH 0x0a00 +#define COMM_STILL_PRE_FLASH_FIRE 0x0000 +#define COMM_STILL_PRE_FLASH_NON_FIRED 0x0000 +#define COMM_STILL_PRE_FLASH_FIRED 0x0001 + +#define COMM_STILL_MAIN_FLASH 0x0a02 +#define COMM_STILL_MAIN_FLASH_CANCEL 0x0001 +#define COMM_STILL_MAIN_FLASH_FIRE 0x0002 + +#define COMM_ZOOM_STEP 0x0b00 + +#define COMM_IMAGE_EFFECT 0x0b0a +#define COMM_IMAGE_EFFECT_NONE 0x0001 +#define COMM_IMAGE_EFFECT_NEGATIVE 0x0002 +#define COMM_IMAGE_EFFECT_AQUA 0x0003 +#define COMM_IMAGE_EFFECT_SEPIA 0x0004 +#define COMM_IMAGE_EFFECT_MONO 0x0005 + +#define COMM_IMAGE_QUALITY 0x0b0c +#define COMM_IMAGE_QUALITY_SUPERFINE 0x0000 +#define COMM_IMAGE_QUALITY_FINE 0x0001 +#define COMM_IMAGE_QUALITY_NORMAL 0x0002 + +#define COMM_FLASH_MODE 0x0b0e +#define COMM_FLASH_MODE_OFF 0x0000 +#define COMM_FLASH_MODE_ON 0x0001 +#define COMM_FLASH_MODE_AUTO 0x0002 + +#define COMM_FLASH_STATUS 0x0b80 +#define COMM_FLASH_STATUS_OFF 0x0001 +#define COMM_FLASH_STATUS_ON 0x0002 +#define COMM_FLASH_STATUS_AUTO 0x0003 + +#define COMM_FLASH_TORCH 0x0b12 +#define COMM_FLASH_TORCH_OFF 0x0000 +#define COMM_FLASH_TORCH_ON 0x0001 + +#define COMM_AE_NEEDS_FLASH 0x0cba +#define COMM_AE_NEEDS_FLASH_OFF 0x0000 +#define COMM_AE_NEEDS_FLASH_ON 0x0001 + +#define COMM_CHG_MODE 0x0b10 +#define COMM_CHG_MODE_NEW 0x8000 +#define COMM_CHG_MODE_SUBSAMPLING_HALF 0x2000 +#define COMM_CHG_MODE_SUBSAMPLING_QUARTER 0x4000 + +#define COMM_CHG_MODE_YUV_320_240 0x0001 +#define COMM_CHG_MODE_YUV_640_480 0x0002 +#define COMM_CHG_MODE_YUV_880_720 0x0003 +#define COMM_CHG_MODE_YUV_960_720 0x0004 +#define COMM_CHG_MODE_YUV_1184_666 0x0005 +#define COMM_CHG_MODE_YUV_1280_720 0x0006 +#define COMM_CHG_MODE_YUV_1536_864 0x0007 +#define COMM_CHG_MODE_YUV_1600_1200 0x0008 +#define COMM_CHG_MODE_YUV_1632_1224 0x0009 +#define COMM_CHG_MODE_YUV_1920_1080 0x000a +#define COMM_CHG_MODE_YUV_1920_1440 0x000b +#define COMM_CHG_MODE_YUV_2304_1296 0x000c +#define COMM_CHG_MODE_YUV_3264_2448 0x000d +#define COMM_CHG_MODE_YUV_352_288 0x000e +#define COMM_CHG_MODE_YUV_1008_672 0x000f + +#define COMM_CHG_MODE_JPEG_640_480 0x0010 +#define COMM_CHG_MODE_JPEG_800_450 0x0020 +#define COMM_CHG_MODE_JPEG_800_600 0x0030 +#define COMM_CHG_MODE_JPEG_1280_720 0x0040 +#define COMM_CHG_MODE_JPEG_1280_960 0x0050 +#define COMM_CHG_MODE_JPEG_1600_900 0x0060 +#define COMM_CHG_MODE_JPEG_1600_1200 0x0070 +#define COMM_CHG_MODE_JPEG_2048_1152 0x0080 +#define COMM_CHG_MODE_JPEG_2048_1536 0x0090 +#define COMM_CHG_MODE_JPEG_2560_1440 0x00a0 +#define COMM_CHG_MODE_JPEG_2560_1920 0x00b0 +#define COMM_CHG_MODE_JPEG_3264_2176 0x00c0 +#define COMM_CHG_MODE_JPEG_1024_768 0x00d0 +#define COMM_CHG_MODE_JPEG_3264_1836 0x00e0 +#define COMM_CHG_MODE_JPEG_3264_2448 0x00f0 + +#define COMM_AF_CON 0x0e00 +#define COMM_AF_CON_STOP 0x0000 +#define COMM_AF_CON_SCAN 0x0001 /* Full Search */ +#define COMM_AF_CON_START 0x0002 /* Fast Search */ + +#define COMM_AF_CAL 0x0e06 +#define COMM_AF_TOUCH_AF 0x0e0a + +#define REG_AF_STATUS S5C73M3_REG(0x0009, 0x5e80) +#define REG_CAF_STATUS_FIND_SEARCH_DIR 0x0001 +#define REG_CAF_STATUS_FOCUSING 0x0002 +#define REG_CAF_STATUS_FOCUSED 0x0003 +#define REG_CAF_STATUS_UNFOCUSED 0x0004 +#define REG_AF_STATUS_INVALID 0x0010 +#define REG_AF_STATUS_FOCUSING 0x0020 +#define REG_AF_STATUS_FOCUSED 0x0030 +#define REG_AF_STATUS_UNFOCUSED 0x0040 + +#define REG_AF_TOUCH_POSITION S5C73M3_REG(0x0009, 0x5e8e) +#define COMM_AF_FACE_ZOOM 0x0e10 + +#define COMM_AF_MODE 0x0e02 +#define COMM_AF_MODE_NORMAL 0x0000 +#define COMM_AF_MODE_MACRO 0x0001 +#define COMM_AF_MODE_MOVIE_CAF_START 0x0002 +#define COMM_AF_MODE_MOVIE_CAF_STOP 0x0003 +#define COMM_AF_MODE_PREVIEW_CAF_START 0x0004 +#define COMM_AF_MODE_PREVIEW_CAF_STOP 0x0005 + +#define COMM_AF_SOFTLANDING 0x0e16 +#define COMM_AF_SOFTLANDING_ON 0x0000 +#define COMM_AF_SOFTLANDING_RES_COMPLETE 0x0001 + +#define COMM_FACE_DET 0x0e0c +#define COMM_FACE_DET_OFF 0x0000 +#define COMM_FACE_DET_ON 0x0001 + +#define COMM_FACE_DET_OSD 0x0e0e +#define COMM_FACE_DET_OSD_OFF 0x0000 +#define COMM_FACE_DET_OSD_ON 0x0001 + +#define COMM_AE_CON 0x0c00 +#define COMM_AE_STOP 0x0000 /* lock */ +#define COMM_AE_START 0x0001 /* unlock */ + +#define COMM_ISO 0x0c02 +#define COMM_ISO_AUTO 0x0000 +#define COMM_ISO_100 0x0001 +#define COMM_ISO_200 0x0002 +#define COMM_ISO_400 0x0003 +#define COMM_ISO_800 0x0004 +#define COMM_ISO_SPORTS 0x0005 +#define COMM_ISO_NIGHT 0x0006 +#define COMM_ISO_INDOOR 0x0007 + +/* 0x00000 (-2.0 EV)...0x0008 (2.0 EV), 0.5EV step */ +#define COMM_EV 0x0c04 + +#define COMM_METERING 0x0c06 +#define COMM_METERING_CENTER 0x0000 +#define COMM_METERING_SPOT 0x0001 +#define COMM_METERING_AVERAGE 0x0002 +#define COMM_METERING_SMART 0x0003 + +#define COMM_WDR 0x0c08 +#define COMM_WDR_OFF 0x0000 +#define COMM_WDR_ON 0x0001 + +#define COMM_FLICKER_MODE 0x0c12 +#define COMM_FLICKER_NONE 0x0000 +#define COMM_FLICKER_MANUAL_50HZ 0x0001 +#define COMM_FLICKER_MANUAL_60HZ 0x0002 +#define COMM_FLICKER_AUTO 0x0003 +#define COMM_FLICKER_AUTO_50HZ 0x0004 +#define COMM_FLICKER_AUTO_60HZ 0x0005 + +#define COMM_FRAME_RATE 0x0c1e +#define COMM_FRAME_RATE_AUTO_SET 0x0000 +#define COMM_FRAME_RATE_FIXED_30FPS 0x0002 +#define COMM_FRAME_RATE_FIXED_20FPS 0x0003 +#define COMM_FRAME_RATE_FIXED_15FPS 0x0004 +#define COMM_FRAME_RATE_FIXED_60FPS 0x0007 +#define COMM_FRAME_RATE_FIXED_120FPS 0x0008 +#define COMM_FRAME_RATE_FIXED_7FPS 0x0009 +#define COMM_FRAME_RATE_FIXED_10FPS 0x000a +#define COMM_FRAME_RATE_FIXED_90FPS 0x000b +#define COMM_FRAME_RATE_ANTI_SHAKE 0x0013 + +/* 0x0000...0x0004 -> sharpness: 0, 1, 2, -1, -2 */ +#define COMM_SHARPNESS 0x0c14 + +/* 0x0000...0x0004 -> saturation: 0, 1, 2, -1, -2 */ +#define COMM_SATURATION 0x0c16 + +/* 0x0000...0x0004 -> contrast: 0, 1, 2, -1, -2 */ +#define COMM_CONTRAST 0x0c18 + +#define COMM_SCENE_MODE 0x0c1a +#define COMM_SCENE_MODE_NONE 0x0000 +#define COMM_SCENE_MODE_PORTRAIT 0x0001 +#define COMM_SCENE_MODE_LANDSCAPE 0x0002 +#define COMM_SCENE_MODE_SPORTS 0x0003 +#define COMM_SCENE_MODE_INDOOR 0x0004 +#define COMM_SCENE_MODE_BEACH 0x0005 +#define COMM_SCENE_MODE_SUNSET 0x0006 +#define COMM_SCENE_MODE_DAWN 0x0007 +#define COMM_SCENE_MODE_FALL 0x0008 +#define COMM_SCENE_MODE_NIGHT 0x0009 +#define COMM_SCENE_MODE_AGAINST_LIGHT 0x000a +#define COMM_SCENE_MODE_FIRE 0x000b +#define COMM_SCENE_MODE_TEXT 0x000c +#define COMM_SCENE_MODE_CANDLE 0x000d + +#define COMM_AE_AUTO_BRACKET 0x0b14 +#define COMM_AE_AUTO_BRAKET_EV05 0x0080 +#define COMM_AE_AUTO_BRAKET_EV10 0x0100 +#define COMM_AE_AUTO_BRAKET_EV15 0x0180 +#define COMM_AE_AUTO_BRAKET_EV20 0x0200 + +#define COMM_SENSOR_STREAMING 0x090a +#define COMM_SENSOR_STREAMING_OFF 0x0000 +#define COMM_SENSOR_STREAMING_ON 0x0001 + +#define COMM_AWB_MODE 0x0d02 +#define COMM_AWB_MODE_INCANDESCENT 0x0000 +#define COMM_AWB_MODE_FLUORESCENT1 0x0001 +#define COMM_AWB_MODE_FLUORESCENT2 0x0002 +#define COMM_AWB_MODE_DAYLIGHT 0x0003 +#define COMM_AWB_MODE_CLOUDY 0x0004 +#define COMM_AWB_MODE_AUTO 0x0005 + +#define COMM_AWB_CON 0x0d00 +#define COMM_AWB_STOP 0x0000 /* lock */ +#define COMM_AWB_START 0x0001 /* unlock */ + +#define COMM_FW_UPDATE 0x0906 +#define COMM_FW_UPDATE_NOT_READY 0x0000 +#define COMM_FW_UPDATE_SUCCESS 0x0005 +#define COMM_FW_UPDATE_FAIL 0x0007 +#define COMM_FW_UPDATE_BUSY 0xffff + + +#define S5C73M3_MAX_SUPPLIES 6 + +struct s5c73m3_ctrls { + struct v4l2_ctrl_handler handler; + struct { + /* exposure/exposure bias cluster */ + struct v4l2_ctrl *auto_exposure; + struct v4l2_ctrl *exposure_bias; + struct v4l2_ctrl *exposure_metering; + }; + struct { + /* iso/auto iso cluster */ + struct v4l2_ctrl *auto_iso; + struct v4l2_ctrl *iso; + }; + struct v4l2_ctrl *auto_wb; + struct { + /* continuous auto focus/auto focus cluster */ + struct v4l2_ctrl *focus_auto; + struct v4l2_ctrl *af_start; + struct v4l2_ctrl *af_stop; + struct v4l2_ctrl *af_status; + struct v4l2_ctrl *af_distance; + }; + + struct v4l2_ctrl *aaa_lock; + struct v4l2_ctrl *colorfx; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *zoom; + struct v4l2_ctrl *wdr; + struct v4l2_ctrl *stabilization; + struct v4l2_ctrl *jpeg_quality; + struct v4l2_ctrl *scene_mode; +}; + +enum s5c73m3_gpio_id { + STBY, + RST, + GPIO_NUM, +}; + +enum s5c73m3_resolution_types { + RES_ISP, + RES_JPEG, +}; + +struct s5c73m3_interval { + u16 fps_reg; + struct v4l2_fract interval; + /* Maximum rectangle for the interval */ + struct v4l2_frmsize_discrete size; +}; + +struct s5c73m3 { + struct v4l2_subdev sensor_sd; + struct media_pad sensor_pads[S5C73M3_NUM_PADS]; + + struct v4l2_subdev oif_sd; + struct media_pad oif_pads[OIF_NUM_PADS]; + + struct spi_driver spidrv; + struct spi_device *spi_dev; + struct i2c_client *i2c_client; + u32 i2c_write_address; + u32 i2c_read_address; + + struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES]; + struct s5c73m3_gpio gpio[GPIO_NUM]; + + /* External master clock frequency */ + u32 mclk_frequency; + /* Video bus type - MIPI-CSI2/paralell */ + enum v4l2_mbus_type bus_type; + + const struct s5c73m3_frame_size *sensor_pix_size[2]; + const struct s5c73m3_frame_size *oif_pix_size[2]; + enum v4l2_mbus_pixelcode mbus_code; + + const struct s5c73m3_interval *fiv; + + struct v4l2_mbus_frame_desc frame_desc; + /* protects the struct members below */ + struct mutex lock; + + struct s5c73m3_ctrls ctrls; + + u8 streaming:1; + u8 apply_fmt:1; + u8 apply_fiv:1; + u8 isp_ready:1; + + short power; + + char sensor_fw[S5C73M3_SENSOR_FW_LEN + 2]; + char sensor_type[S5C73M3_SENSOR_TYPE_LEN + 2]; + char fw_file_version[2]; + unsigned int fw_size; +}; + +struct s5c73m3_frame_size { + u32 width; + u32 height; + u8 reg_val; +}; + +extern int s5c73m3_dbg; + +int s5c73m3_register_spi_driver(struct s5c73m3 *state); +void s5c73m3_unregister_spi_driver(struct s5c73m3 *state); +int s5c73m3_spi_write(struct s5c73m3 *state, const void *addr, + const unsigned int len, const unsigned int tx_size); +int s5c73m3_spi_read(struct s5c73m3 *state, void *addr, + const unsigned int len, const unsigned int tx_size); + +int s5c73m3_read(struct s5c73m3 *state, u32 addr, u16 *data); +int s5c73m3_write(struct s5c73m3 *state, u32 addr, u16 data); +int s5c73m3_isp_command(struct s5c73m3 *state, u16 command, u16 data); +int s5c73m3_init_controls(struct s5c73m3 *state); + +static inline struct v4l2_subdev *ctrl_to_sensor_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct s5c73m3, + ctrls.handler)->sensor_sd; +} + +static inline struct s5c73m3 *sensor_sd_to_s5c73m3(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s5c73m3, sensor_sd); +} + +static inline struct s5c73m3 *oif_sd_to_s5c73m3(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s5c73m3, oif_sd); +} +#endif /* S5C73M3_H_ */ diff --git a/include/media/s5c73m3.h b/include/media/s5c73m3.h new file mode 100644 index 000000000000..ccb9e5448762 --- /dev/null +++ b/include/media/s5c73m3.h @@ -0,0 +1,55 @@ +/* + * Samsung LSI S5C73M3 8M pixel camera driver + * + * Copyright (C) 2012, Samsung Electronics, Co., Ltd. + * Sylwester Nawrocki + * Andrzej Hajda + * + * 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. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef MEDIA_S5C73M3__ +#define MEDIA_S5C73M3__ + +#include +#include + +/** + * struct s5c73m3_gpio - data structure describing a GPIO + * @gpio: GPIO number + * @level: indicates active state of the @gpio + */ +struct s5c73m3_gpio { + int gpio; + int level; +}; + +/** + * struct s5c73m3_platform_data - s5c73m3 driver platform data + * @mclk_frequency: sensor's master clock frequency in Hz + * @gpio_reset: GPIO driving RESET pin + * @gpio_stby: GPIO driving STBY pin + * @nlanes: maximum number of MIPI-CSI lanes used + * @horiz_flip: default horizontal image flip value, non zero to enable + * @vert_flip: default vertical image flip value, non zero to enable + */ + +struct s5c73m3_platform_data { + unsigned long mclk_frequency; + + struct s5c73m3_gpio gpio_reset; + struct s5c73m3_gpio gpio_stby; + + enum v4l2_mbus_type bus_type; + u8 nlanes; + u8 horiz_flip; + u8 vert_flip; +}; + +#endif /* MEDIA_S5C73M3__ */ From 81b9f70210b62e256cf63eb8f703042ef44e4cdd Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 18 Jan 2013 12:02:31 -0300 Subject: [PATCH 1919/4607] [media] s5p-fimc: fimc-lite: Remove empty s_power subdev callback The .s_power FIMC-LITE subdev callback is now empty and unneeded. The FIMC-LITE IP power handling will be done by the FIMC-IS driver, so just remove the callback. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-lite.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index c67dd2ebcff5..fe6cd0c3bdd3 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -1280,18 +1280,6 @@ static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) return ret; } -static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on) -{ - struct fimc_lite *fimc = v4l2_get_subdevdata(sd); - - if (fimc->out_path == FIMC_IO_DMA) - return -ENOIOCTLCMD; - - /* TODO: */ - - return 0; -} - static int fimc_lite_log_status(struct v4l2_subdev *sd) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); @@ -1391,7 +1379,6 @@ static const struct v4l2_subdev_video_ops fimc_lite_subdev_video_ops = { }; static const struct v4l2_subdev_core_ops fimc_lite_core_ops = { - .s_power = fimc_lite_subdev_s_power, .log_status = fimc_lite_log_status, }; From 03878bb473bb46cf8514223d8c955420b1ef73bc Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 18 Jan 2013 12:02:32 -0300 Subject: [PATCH 1920/4607] [media] s5p-fimc: fimc-lite: Prevent deadlock at STREAMON/OFF ioctls This patch fixes regression introduced in commit 6319d6a002beb26631 '[media] fimc-lite: Add ISP FIFO output support'. In case of a configuration where video is captured at the video node exposed by the FIMC-LITE driver there is a following video pipeline: sensor -> MIPI-CSIS.n -> FIMC-LITE.n subdev -> FIMC-LITE.n video node In this situation s_stream() handler of the FIMC-LITE.n is called back from within VIDIOC_STREAMON/OFF ioctl of the FIMC-LITE.n video node, through vb2_stream_on/off(), start/stop_streaming and fimc_pipeline_call(set_stream). The fimc->lock mutex is already held then, before invoking vidioc_streamon/off. So it must not be taken again in the s_stream() callback in this case, to avoid a deadlock. This patch makes fimc->out_path atomic_t so the mutex don't need to be taken in the FIMC-LITE subdev s_stream() callback in the DMA output case. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyugmin Park Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/s5p-fimc/fimc-lite-reg.c | 2 +- drivers/media/platform/s5p-fimc/fimc-lite.c | 35 ++++++++++--------- drivers/media/platform/s5p-fimc/fimc-lite.h | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c index ad63ebf082c5..962652da3b43 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c @@ -65,7 +65,7 @@ void flite_hw_set_interrupt_mask(struct fimc_lite *dev) u32 cfg, intsrc; /* Select interrupts to be enabled for each output mode */ - if (dev->out_path == FIMC_IO_DMA) { + if (atomic_read(&dev->out_path) == FIMC_IO_DMA) { intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | FLITE_REG_CIGCTRL_IRQ_LASTEN | FLITE_REG_CIGCTRL_IRQ_STARTEN; diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index fe6cd0c3bdd3..ef3989fe63e5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -260,7 +260,7 @@ static irqreturn_t flite_irq_handler(int irq, void *priv) wake_up(&fimc->irq_queue); } - if (fimc->out_path != FIMC_IO_DMA) + if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) goto done; if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) && @@ -465,7 +465,7 @@ static int fimc_lite_open(struct file *file) mutex_lock(&me->parent->graph_mutex); mutex_lock(&fimc->lock); - if (fimc->out_path != FIMC_IO_DMA) { + if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) { ret = -EBUSY; goto done; } @@ -479,7 +479,8 @@ static int fimc_lite_open(struct file *file) if (ret < 0) goto done; - if (++fimc->ref_count == 1 && fimc->out_path == FIMC_IO_DMA) { + if (++fimc->ref_count == 1 && + atomic_read(&fimc->out_path) == FIMC_IO_DMA) { ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, &fimc->vfd.entity, true); if (ret < 0) { @@ -504,7 +505,8 @@ static int fimc_lite_close(struct file *file) mutex_lock(&fimc->lock); - if (--fimc->ref_count == 0 && fimc->out_path == FIMC_IO_DMA) { + if (--fimc->ref_count == 0 && + atomic_read(&fimc->out_path) == FIMC_IO_DMA) { clear_bit(ST_FLITE_IN_USE, &fimc->state); fimc_lite_stop_capture(fimc, false); fimc_pipeline_call(fimc, close, &fimc->pipeline); @@ -1034,18 +1036,18 @@ static int fimc_lite_link_setup(struct media_entity *entity, case FLITE_SD_PAD_SOURCE_DMA: if (!(flags & MEDIA_LNK_FL_ENABLED)) - fimc->out_path = FIMC_IO_NONE; + atomic_set(&fimc->out_path, FIMC_IO_NONE); else if (remote_ent_type == MEDIA_ENT_T_DEVNODE) - fimc->out_path = FIMC_IO_DMA; + atomic_set(&fimc->out_path, FIMC_IO_DMA); else ret = -EINVAL; break; case FLITE_SD_PAD_SOURCE_ISP: if (!(flags & MEDIA_LNK_FL_ENABLED)) - fimc->out_path = FIMC_IO_NONE; + atomic_set(&fimc->out_path, FIMC_IO_NONE); else if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) - fimc->out_path = FIMC_IO_ISP; + atomic_set(&fimc->out_path, FIMC_IO_ISP); else ret = -EINVAL; break; @@ -1054,6 +1056,7 @@ static int fimc_lite_link_setup(struct media_entity *entity, v4l2_err(sd, "Invalid pad index\n"); ret = -EINVAL; } + mb(); mutex_unlock(&fimc->lock); return ret; @@ -1123,8 +1126,10 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, mf->colorspace = V4L2_COLORSPACE_JPEG; mutex_lock(&fimc->lock); - if ((fimc->out_path == FIMC_IO_ISP && sd->entity.stream_count > 0) || - (fimc->out_path == FIMC_IO_DMA && vb2_is_busy(&fimc->vb_queue))) { + if ((atomic_read(&fimc->out_path) == FIMC_IO_ISP && + sd->entity.stream_count > 0) || + (atomic_read(&fimc->out_path) == FIMC_IO_DMA && + vb2_is_busy(&fimc->vb_queue))) { mutex_unlock(&fimc->lock); return -EBUSY; } @@ -1247,12 +1252,10 @@ static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) */ fimc->sensor = __find_remote_sensor(&sd->entity); - mutex_lock(&fimc->lock); - if (fimc->out_path != FIMC_IO_ISP) { - mutex_unlock(&fimc->lock); + if (atomic_read(&fimc->out_path) != FIMC_IO_ISP) return -ENOIOCTLCMD; - } + mutex_lock(&fimc->lock); if (on) { flite_hw_reset(fimc); ret = fimc_lite_hw_init(fimc, true); @@ -1298,7 +1301,7 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) memset(vfd, 0, sizeof(*vfd)); fimc->fmt = &fimc_lite_formats[0]; - fimc->out_path = FIMC_IO_DMA; + atomic_set(&fimc->out_path, FIMC_IO_DMA); snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture", fimc->index); @@ -1589,7 +1592,7 @@ static int fimc_lite_resume(struct device *dev) INIT_LIST_HEAD(&fimc->active_buf_q); fimc_pipeline_call(fimc, open, &fimc->pipeline, &fimc->vfd.entity, false); - fimc_lite_hw_init(fimc, fimc->out_path == FIMC_IO_ISP); + fimc_lite_hw_init(fimc, atomic_read(&fimc->out_path) == FIMC_IO_ISP); clear_bit(ST_FLITE_SUSPENDED, &fimc->state); for (i = 0; i < fimc->reqbufs_count; i++) { diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.h b/drivers/media/platform/s5p-fimc/fimc-lite.h index 4576922952f3..7085761f8c4b 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.h +++ b/drivers/media/platform/s5p-fimc/fimc-lite.h @@ -159,7 +159,7 @@ struct fimc_lite { unsigned long payload[FLITE_MAX_PLANES]; struct flite_frame inp_frame; struct flite_frame out_frame; - enum fimc_datapath out_path; + atomic_t out_path; unsigned int source_subdev_grp_id; unsigned long state; From bed3cd2753d986768a15a6c45b5ae3a56007c1b2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 8 Jan 2013 02:58:51 -0300 Subject: [PATCH 1921/4607] [media] s5p-csis: Use devm_regulator_bulk_get API devm_regulator_bulk_get is device managed and saves some cleanup and exit code. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index cde510f0eea6..7e36ad92dd94 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -743,7 +743,7 @@ static int s5pcsis_probe(struct platform_device *pdev) for (i = 0; i < CSIS_NUM_SUPPLIES; i++) state->supplies[i].supply = csis_supply_name[i]; - ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES, + ret = devm_regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES, state->supplies); if (ret) return ret; @@ -762,7 +762,7 @@ static int s5pcsis_probe(struct platform_device *pdev) 0, dev_name(&pdev->dev), state); if (ret) { dev_err(&pdev->dev, "Interrupt request failed\n"); - goto e_regput; + goto e_clkput; } v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); @@ -793,8 +793,6 @@ static int s5pcsis_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); return 0; -e_regput: - regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies); e_clkput: clk_disable(state->clock[CSIS_CLK_MUX]); s5pcsis_clk_put(state); @@ -903,7 +901,6 @@ static int s5pcsis_remove(struct platform_device *pdev) clk_disable(state->clock[CSIS_CLK_MUX]); pm_runtime_set_suspended(&pdev->dev); s5pcsis_clk_put(state); - regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies); media_entity_cleanup(&state->sd.entity); From 969e877cc1e6e91dc76f973d08ad70e2065c56ae Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 7 Dec 2012 16:40:08 -0300 Subject: [PATCH 1922/4607] [media] s5p-fimc: Add missing line breaks Add missing line breaks in the debug traces. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-core.c | 4 ++-- drivers/media/platform/s5p-fimc/fimc-lite.c | 12 ++++++------ drivers/media/platform/s5p-fimc/fimc-mdevice.c | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 92d477c91811..6d5b03a7b5e5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -257,14 +257,14 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx) ty = d_frame->height; } if (tx <= 0 || ty <= 0) { - dev_err(dev, "Invalid target size: %dx%d", tx, ty); + dev_err(dev, "Invalid target size: %dx%d\n", tx, ty); return -EINVAL; } sx = s_frame->width; sy = s_frame->height; if (sx <= 0 || sy <= 0) { - dev_err(dev, "Invalid source size: %dx%d", sx, sy); + dev_err(dev, "Invalid source size: %dx%d\n", sx, sy); return -EINVAL; } sc->real_width = sx; diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index ef3989fe63e5..e18babfa89e0 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -611,7 +611,7 @@ static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r) r->left = round_down(r->left, fimc->variant->win_hor_offs_align); r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height); - v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d", + v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d\n", r->left, r->top, r->width, r->height, frame->f_width, frame->f_height); } @@ -631,7 +631,7 @@ static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r) r->left = round_down(r->left, fimc->variant->out_hor_offs_align); r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height); - v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d", + v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d\n", r->left, r->top, r->width, r->height, frame->f_width, frame->f_height); } @@ -1011,7 +1011,7 @@ static int fimc_lite_link_setup(struct media_entity *entity, if (WARN_ON(fimc == NULL)) return 0; - v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x", + v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x\n", __func__, remote->entity->name, local->entity->name, flags, fimc->source_subdev_grp_id); @@ -1120,7 +1120,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, struct flite_frame *source = &fimc->out_frame; const struct fimc_fmt *ffmt; - v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d", + v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d\n", fmt->pad, mf->code, mf->width, mf->height); mf->colorspace = V4L2_COLORSPACE_JPEG; @@ -1196,7 +1196,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, } mutex_unlock(&fimc->lock); - v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d", + v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n", __func__, f->rect.left, f->rect.top, f->rect.width, f->rect.height, f->f_width, f->f_height); @@ -1230,7 +1230,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, } mutex_unlock(&fimc->lock); - v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d", + v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n", __func__, f->rect.left, f->rect.top, f->rect.width, f->rect.height, f->f_width, f->f_height); diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 27d346195dbd..52e1aa3c8982 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -556,7 +556,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, if (ret) break; - v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", + v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n", source->name, flags ? '=' : '-', sink->name); } return 0; @@ -640,7 +640,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) if (ret) return ret; - v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]", + v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]\n", sensor->entity.name, csis->entity.name); source = NULL; From 7b43a6f3f517109c2912d82f7f666a84420689bc Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 12 Dec 2012 08:16:05 -0300 Subject: [PATCH 1923/4607] [media] s5p-fimc: Change platform subdevs registration method The previous method of registering platform entities into the main driver using driver_find() and then iterating over devices bound to a driver was racy and is being removed here. Nothing was preventing module from unloading during a call to try_module_get(driver->owner). Instead, we look up a device first and then check for its driver while holding device lock. The platform sub-devices are looked up and registered to the top level driver. When any sub-device is not yet initialized and ready the main driver's probe() will be deferred. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/s5p-fimc/fimc-mdevice.c | 205 ++++++++++-------- 1 file changed, 109 insertions(+), 96 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 52e1aa3c8982..2b0587204868 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -1,8 +1,8 @@ /* * S5P/EXYNOS4 SoC series camera host interface media device driver * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Contact: Sylwester Nawrocki, + * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published @@ -312,138 +312,149 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) } /* - * MIPI CSIS and FIMC platform devices registration. + * MIPI-CSIS, FIMC and FIMC-LITE platform devices registration. */ -static int fimc_register_callback(struct device *dev, void *p) + +static int register_fimc_lite_entity(struct fimc_md *fmd, + struct fimc_lite *fimc_lite) { - struct fimc_dev *fimc = dev_get_drvdata(dev); struct v4l2_subdev *sd; - struct fimc_md *fmd = p; int ret; - if (fimc == NULL || fimc->id >= FIMC_MAX_DEVS) - return 0; + if (WARN_ON(fimc_lite->index >= FIMC_LITE_MAX_DEVS || + fmd->fimc_lite[fimc_lite->index])) + return -EBUSY; + + sd = &fimc_lite->subdev; + sd->grp_id = GRP_ID_FLITE; + v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (!ret) + fmd->fimc_lite[fimc_lite->index] = fimc_lite; + else + v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.LITE%d\n", + fimc_lite->index); + return ret; +} + +static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc) +{ + struct v4l2_subdev *sd; + int ret; + + if (WARN_ON(fimc->id >= FIMC_MAX_DEVS || fmd->fimc[fimc->id])) + return -EBUSY; sd = &fimc->vid_cap.subdev; sd->grp_id = GRP_ID_FIMC; v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); - if (ret) { + if (!ret) { + fmd->fimc[fimc->id] = fimc; + fimc->vid_cap.user_subdev_api = fmd->user_subdev_api; + } else { v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n", fimc->id, ret); - return ret; } - - fmd->fimc[fimc->id] = fimc; - return 0; + return ret; } -static int fimc_lite_register_callback(struct device *dev, void *p) +static int register_csis_entity(struct fimc_md *fmd, + struct platform_device *pdev, + struct v4l2_subdev *sd) { - struct fimc_lite *fimc = dev_get_drvdata(dev); - struct fimc_md *fmd = p; - int ret; - - if (fimc == NULL || fimc->index >= FIMC_LITE_MAX_DEVS) - return 0; - - fimc->subdev.grp_id = GRP_ID_FLITE; - v4l2_set_subdev_hostdata(&fimc->subdev, (void *)&fimc_pipeline_ops); - - ret = v4l2_device_register_subdev(&fmd->v4l2_dev, &fimc->subdev); - if (ret) { - v4l2_err(&fmd->v4l2_dev, - "Failed to register FIMC-LITE.%d (%d)\n", - fimc->index, ret); - return ret; - } - - fmd->fimc_lite[fimc->index] = fimc; - return 0; -} - -static int csis_register_callback(struct device *dev, void *p) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct platform_device *pdev; - struct fimc_md *fmd = p; + struct device_node *node = pdev->dev.of_node; int id, ret; - if (!sd) - return 0; - pdev = v4l2_get_subdevdata(sd); - if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES) - return 0; - v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name); + id = node ? of_alias_get_id(node, "csis") : max(0, pdev->id); + + if (WARN_ON(id >= CSIS_MAX_ENTITIES || fmd->csis[id].sd)) + return -EBUSY; + + if (WARN_ON(id >= CSIS_MAX_ENTITIES)) + return 0; - id = pdev->id < 0 ? 0 : pdev->id; sd->grp_id = GRP_ID_CSIS; - ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (!ret) fmd->csis[id].sd = sd; else v4l2_err(&fmd->v4l2_dev, - "Failed to register CSIS subdevice: %d\n", ret); + "Failed to register MIPI-CSIS.%d (%d)\n", id, ret); return ret; } -/** - * fimc_md_register_platform_entities - register FIMC and CSIS media entities - */ -static int fimc_md_register_platform_entities(struct fimc_md *fmd) +static int fimc_md_register_platform_entity(struct fimc_md *fmd, + struct platform_device *pdev, + int plat_entity) { - struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; - struct device_driver *driver; - int ret, i; + struct device *dev = &pdev->dev; + int ret = -EPROBE_DEFER; + void *drvdata; - driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type); - if (!driver) { - v4l2_warn(&fmd->v4l2_dev, - "%s driver not found, deffering probe\n", - FIMC_MODULE_NAME); - return -EPROBE_DEFER; - } + /* Lock to ensure dev->driver won't change. */ + device_lock(dev); - ret = driver_for_each_device(driver, NULL, fmd, - fimc_register_callback); - if (ret) - return ret; + if (!dev->driver || !try_module_get(dev->driver->owner)) + goto dev_unlock; - driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type); - if (driver && try_module_get(driver->owner)) { - ret = driver_for_each_device(driver, NULL, fmd, - fimc_lite_register_callback); - if (ret) - return ret; - module_put(driver->owner); - } - /* - * Check if there is any sensor on the MIPI-CSI2 bus and - * if not skip the s5p-csis module loading. - */ - if (pdata == NULL) - return 0; - for (i = 0; i < pdata->num_clients; i++) { - if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) { - ret = 1; + drvdata = dev_get_drvdata(dev); + /* Some subdev didn't probe succesfully id drvdata is NULL */ + if (drvdata) { + switch (plat_entity) { + case IDX_FIMC: + ret = register_fimc_entity(fmd, drvdata); break; + case IDX_FLITE: + ret = register_fimc_lite_entity(fmd, drvdata); + break; + case IDX_CSIS: + ret = register_csis_entity(fmd, pdev, drvdata); + break; + default: + ret = -ENODEV; } } - if (!ret) - return 0; - driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); - if (!driver || !try_module_get(driver->owner)) { - v4l2_warn(&fmd->v4l2_dev, - "%s driver not found, deffering probe\n", - CSIS_DRIVER_NAME); - return -EPROBE_DEFER; + module_put(dev->driver->owner); +dev_unlock: + device_unlock(dev); + if (ret == -EPROBE_DEFER) + dev_info(&fmd->pdev->dev, "deferring %s device registration\n", + dev_name(dev)); + else if (ret < 0) + dev_err(&fmd->pdev->dev, "%s device registration failed (%d)\n", + dev_name(dev), ret); + return ret; +} + +static int fimc_md_pdev_match(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + int plat_entity = -1; + int ret; + char *p; + + if (!get_device(dev)) + return -ENODEV; + + if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) { + plat_entity = IDX_CSIS; + } else if (!strcmp(pdev->name, FIMC_LITE_DRV_NAME)) { + plat_entity = IDX_FLITE; + } else { + p = strstr(pdev->name, "fimc"); + if (p && *(p + 4) == 0) + plat_entity = IDX_FIMC; } - return driver_for_each_device(driver, NULL, fmd, - csis_register_callback); + if (plat_entity >= 0) + ret = fimc_md_register_platform_entity(data, pdev, + plat_entity); + put_device(dev); + return 0; } static void fimc_md_unregister_entities(struct fimc_md *fmd) @@ -477,6 +488,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) fimc_md_unregister_sensor(fmd->sensor[i].subdev); fmd->sensor[i].subdev = NULL; } + v4l2_info(&fmd->v4l2_dev, "Unregistered all entities\n"); } /** @@ -939,7 +951,8 @@ static int fimc_md_probe(struct platform_device *pdev) /* Protect the media graph while we're registering entities */ mutex_lock(&fmd->media_dev.graph_mutex); - ret = fimc_md_register_platform_entities(fmd); + ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, + fimc_md_pdev_match); if (ret) goto err_unlock; From b71b56b264ae27f32784973d15bfdfbc7df6d579 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 29 Jan 2013 06:42:28 -0300 Subject: [PATCH 1924/4607] [media] s5p-fimc: Check return value of clk_enable/clk_set_rate clk_set_rate(), clk_enable() functions can fail, so check the return values to avoid surprises. While at it use ERR_PTR() value to indicate an invalid clock. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-core.c | 24 +++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 6d5b03a7b5e5..a9625414ea95 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -808,11 +808,11 @@ static void fimc_clk_put(struct fimc_dev *fimc) { int i; for (i = 0; i < MAX_FIMC_CLOCKS; i++) { - if (IS_ERR_OR_NULL(fimc->clock[i])) + if (IS_ERR(fimc->clock[i])) continue; clk_unprepare(fimc->clock[i]); clk_put(fimc->clock[i]); - fimc->clock[i] = NULL; + fimc->clock[i] = ERR_PTR(-EINVAL); } } @@ -820,14 +820,19 @@ static int fimc_clk_get(struct fimc_dev *fimc) { int i, ret; + for (i = 0; i < MAX_FIMC_CLOCKS; i++) + fimc->clock[i] = ERR_PTR(-EINVAL); + for (i = 0; i < MAX_FIMC_CLOCKS; i++) { fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]); - if (IS_ERR(fimc->clock[i])) + if (IS_ERR(fimc->clock[i])) { + ret = PTR_ERR(fimc->clock[i]); goto err; + } ret = clk_prepare(fimc->clock[i]); if (ret < 0) { clk_put(fimc->clock[i]); - fimc->clock[i] = NULL; + fimc->clock[i] = ERR_PTR(-EINVAL); goto err; } } @@ -921,8 +926,14 @@ static int fimc_probe(struct platform_device *pdev) ret = fimc_clk_get(fimc); if (ret) return ret; - clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); - clk_enable(fimc->clock[CLK_BUS]); + + ret = clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); + if (ret < 0) + return ret; + + ret = clk_enable(fimc->clock[CLK_BUS]); + if (ret < 0) + return ret; ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler, 0, dev_name(&pdev->dev), fimc); @@ -956,6 +967,7 @@ err_pm: err_sd: fimc_unregister_capture_subdev(fimc); err_clk: + clk_disable(fimc->clock[CLK_BUS]); fimc_clk_put(fimc); return ret; } From 44e2b09ca468c7c91fa4bb8058fcda38f344974a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 29 Jan 2013 06:52:29 -0300 Subject: [PATCH 1925/4607] [media] s5p-csis: Check return value of clk_enable/clk_set_rate clk_set_rate(), clk_enable() functions can fail, so check the return values to avoid surprises. While at it use ERR_PTR() value to indicate an invalid clock. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 29 ++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 7e36ad92dd94..613482fd74a9 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -352,11 +352,11 @@ static void s5pcsis_clk_put(struct csis_state *state) int i; for (i = 0; i < NUM_CSIS_CLOCKS; i++) { - if (IS_ERR_OR_NULL(state->clock[i])) + if (IS_ERR(state->clock[i])) continue; clk_unprepare(state->clock[i]); clk_put(state->clock[i]); - state->clock[i] = NULL; + state->clock[i] = ERR_PTR(-EINVAL); } } @@ -365,14 +365,19 @@ static int s5pcsis_clk_get(struct csis_state *state) struct device *dev = &state->pdev->dev; int i, ret; + for (i = 0; i < NUM_CSIS_CLOCKS; i++) + state->clock[i] = ERR_PTR(-EINVAL); + for (i = 0; i < NUM_CSIS_CLOCKS; i++) { state->clock[i] = clk_get(dev, csi_clock_name[i]); - if (IS_ERR(state->clock[i])) + if (IS_ERR(state->clock[i])) { + ret = PTR_ERR(state->clock[i]); goto err; + } ret = clk_prepare(state->clock[i]); if (ret < 0) { clk_put(state->clock[i]); - state->clock[i] = NULL; + state->clock[i] = ERR_PTR(-EINVAL); goto err; } } @@ -380,7 +385,7 @@ static int s5pcsis_clk_get(struct csis_state *state) err: s5pcsis_clk_put(state); dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]); - return -ENXIO; + return ret; } static void dump_regs(struct csis_state *state, const char *label) @@ -749,14 +754,20 @@ static int s5pcsis_probe(struct platform_device *pdev) return ret; ret = s5pcsis_clk_get(state); - if (ret) - goto e_clkput; + if (ret < 0) + return ret; - clk_enable(state->clock[CSIS_CLK_MUX]); if (pdata->clk_rate) - clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate); + ret = clk_set_rate(state->clock[CSIS_CLK_MUX], + pdata->clk_rate); else dev_WARN(&pdev->dev, "No clock frequency specified!\n"); + if (ret < 0) + goto e_clkput; + + ret = clk_enable(state->clock[CSIS_CLK_MUX]); + if (ret < 0) + goto e_clkput; ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler, 0, dev_name(&pdev->dev), state); From c6c03915b630c2b4e488be4f21ab46703e31c16b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 29 Jan 2013 02:32:30 -0300 Subject: [PATCH 1926/4607] [media] s5c73m3: Staticize some symbols Fixes the following sparse warnings: drivers/media/i2c/s5c73m3/s5c73m3-core.c:42:5: warning: symbol 'boot_from_rom' was not declared. Should it be static? drivers/media/i2c/s5c73m3/s5c73m3-core.c:45:5: warning: symbol 'update_fw' was not declared. Should it be static? drivers/media/i2c/s5c73m3/s5c73m3-core.c:298:5: warning: symbol 's5c73m3_isp_comm_result' was not declared. Should it be static? Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index 600909ddb150..b063b4ddf767 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -39,10 +39,10 @@ int s5c73m3_dbg; module_param_named(debug, s5c73m3_dbg, int, 0644); -int boot_from_rom = 1; +static int boot_from_rom = 1; module_param(boot_from_rom, int, 0644); -int update_fw; +static int update_fw; module_param(update_fw, int, 0644); #define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K @@ -295,7 +295,8 @@ int s5c73m3_isp_command(struct s5c73m3 *state, u16 command, u16 data) return s5c73m3_write(state, REG_STATUS, 0x0001); } -int s5c73m3_isp_comm_result(struct s5c73m3 *state, u16 command, u16 *data) +static int s5c73m3_isp_comm_result(struct s5c73m3 *state, u16 command, + u16 *data) { return s5c73m3_read(state, COMM_RESULT_OFFSET + command, data); } From 031f515b3d303eca80518ed9a71c79ed420b352a Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 9 Jan 2013 15:09:55 -0300 Subject: [PATCH 1927/4607] [media] s5p-fimc: Avoid null pointer dereference in fimc_capture_ctrls_create() With presence of some faults, e.g. caused by wrong platform data or the device tree structure the IDX_SENSOR entry in the array may be NULL, so make sure it is not dereferenced then. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index df6fc6c7eecd..5ec2f48f7db5 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -486,6 +486,7 @@ static struct vb2_ops fimc_capture_qops = { int fimc_capture_ctrls_create(struct fimc_dev *fimc) { struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR]; int ret; if (WARN_ON(vid_cap->ctx == NULL)) @@ -494,11 +495,13 @@ int fimc_capture_ctrls_create(struct fimc_dev *fimc) return 0; ret = fimc_ctrls_create(vid_cap->ctx); - if (ret || vid_cap->user_subdev_api || !vid_cap->ctx->ctrls.ready) + + if (ret || vid_cap->user_subdev_api || !sensor || + !vid_cap->ctx->ctrls.ready) return ret; return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler, - fimc->pipeline.subdevs[IDX_SENSOR]->ctrl_handler, NULL); + sensor->ctrl_handler, NULL); } static int fimc_capture_set_default_format(struct fimc_dev *fimc); From 81619ce1931a1d96e53b455ca2f35757f0c457a5 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 30 Jan 2013 09:54:06 -0300 Subject: [PATCH 1928/4607] [media] s5p-fimc: Set default image format at device open() Make sure a valid image format is initially set on both the CAPTURE and the OUTPUT buffer queue. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-core.c | 20 +-- drivers/media/platform/s5p-fimc/fimc-core.h | 5 +- drivers/media/platform/s5p-fimc/fimc-m2m.c | 131 +++++++++++--------- 3 files changed, 75 insertions(+), 81 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index a9625414ea95..29f7bb71c7e1 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -525,7 +525,6 @@ static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) { struct fimc_dev *fimc = ctx->fimc_dev; const struct fimc_variant *variant = fimc->variant; - unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT; int ret = 0; if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) @@ -541,8 +540,7 @@ static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) break; case V4L2_CID_ROTATE: - if (fimc_capture_pending(fimc) || - (ctx->state & flags) == flags) { + if (fimc_capture_pending(fimc)) { ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, ctx->s_frame.height, ctx->d_frame.width, ctx->d_frame.height, ctrl->val); @@ -709,22 +707,6 @@ void __fimc_get_format(struct fimc_frame *frame, struct v4l2_format *f) } } -void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; - - frame->f_width = pixm->plane_fmt[0].bytesperline; - if (frame->fmt->colplanes == 1) - frame->f_width = (frame->f_width * 8) / frame->fmt->depth[0]; - frame->f_height = pixm->height; - frame->width = pixm->width; - frame->height = pixm->height; - frame->o_width = pixm->width; - frame->o_height = pixm->height; - frame->offs_h = 0; - frame->offs_v = 0; -} - /** * fimc_adjust_mplane_format - adjust bytesperline/sizeimage for each plane * @fmt: fimc pixel format description (input) diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/s5p-fimc/fimc-core.h index cf760c364099..412d50708f75 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.h +++ b/drivers/media/platform/s5p-fimc/fimc-core.h @@ -112,9 +112,7 @@ enum fimc_color_fmt { /* The hardware context state. */ #define FIMC_PARAMS (1 << 0) -#define FIMC_SRC_FMT (1 << 3) -#define FIMC_DST_FMT (1 << 4) -#define FIMC_COMPOSE (1 << 5) +#define FIMC_COMPOSE (1 << 1) #define FIMC_CTX_M2M (1 << 16) #define FIMC_CTX_CAP (1 << 17) #define FIMC_CTX_SHUT (1 << 18) @@ -654,7 +652,6 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, struct fimc_frame *frame, struct fimc_addr *paddr); void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f); void fimc_set_yuv_order(struct fimc_ctx *ctx); -void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f); void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf); int fimc_register_m2m_device(struct fimc_dev *fimc, diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c index 1eabd7e74849..f3d535cdd87f 100644 --- a/drivers/media/platform/s5p-fimc/fimc-m2m.c +++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c @@ -1,8 +1,8 @@ /* * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver * - * Copyright (C) 2012 Samsung Electronics Co., Ltd. - * Sylwester Nawrocki, + * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published @@ -160,8 +160,7 @@ static void fimc_device_run(void *priv) fimc_hw_set_output_addr(fimc, &df->paddr, -1); fimc_activate_capture(ctx); - ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP | - FIMC_SRC_FMT | FIMC_DST_FMT); + ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP); fimc_hw_activate_input_dma(fimc, true); dma_unlock: @@ -309,8 +308,6 @@ static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) if (!IS_M2M(f->type)) return -EINVAL; - dbg("w: %d, h: %d", pix->width, pix->height); - fmt = fimc_find_format(&pix->pixelformat, NULL, get_m2m_fmt_flags(f->type), 0); if (WARN(fmt == NULL, "Pixel format lookup failed")) @@ -350,19 +347,39 @@ static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return fimc_try_fmt_mplane(ctx, f); } +static void __set_frame_format(struct fimc_frame *frame, struct fimc_fmt *fmt, + struct v4l2_pix_format_mplane *pixm) +{ + int i; + + for (i = 0; i < fmt->colplanes; i++) { + frame->bytesperline[i] = pixm->plane_fmt[i].bytesperline; + frame->payload[i] = pixm->plane_fmt[i].sizeimage; + } + + frame->f_width = pixm->width; + frame->f_height = pixm->height; + frame->o_width = pixm->width; + frame->o_height = pixm->height; + frame->width = pixm->width; + frame->height = pixm->height; + frame->offs_h = 0; + frame->offs_v = 0; + frame->fmt = fmt; +} + static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct fimc_ctx *ctx = fh_to_ctx(fh); struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_fmt *fmt; struct vb2_queue *vq; struct fimc_frame *frame; - struct v4l2_pix_format_mplane *pix; - int i, ret = 0; + int ret; ret = fimc_try_fmt_mplane(ctx, f); if (ret) @@ -380,31 +397,16 @@ static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, else frame = &ctx->d_frame; - pix = &f->fmt.pix_mp; - frame->fmt = fimc_find_format(&pix->pixelformat, NULL, - get_m2m_fmt_flags(f->type), 0); - if (!frame->fmt) + fmt = fimc_find_format(&f->fmt.pix_mp.pixelformat, NULL, + get_m2m_fmt_flags(f->type), 0); + if (!fmt) return -EINVAL; + __set_frame_format(frame, fmt, &f->fmt.pix_mp); + /* Update RGB Alpha control state and value range */ fimc_alpha_ctrl_update(ctx); - for (i = 0; i < frame->fmt->colplanes; i++) { - frame->bytesperline[i] = pix->plane_fmt[i].bytesperline; - frame->payload[i] = pix->plane_fmt[i].sizeimage; - } - - fimc_fill_frame(frame, f); - - ctx->scaler.enabled = 1; - - if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); - else - fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); - - dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); - return 0; } @@ -412,7 +414,6 @@ static int fimc_m2m_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *reqbufs) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); } @@ -420,7 +421,6 @@ static int fimc_m2m_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); } @@ -428,7 +428,6 @@ static int fimc_m2m_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); } @@ -436,7 +435,6 @@ static int fimc_m2m_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); } @@ -444,7 +442,6 @@ static int fimc_m2m_expbuf(struct file *file, void *fh, struct v4l2_exportbuffer *eb) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); } @@ -453,15 +450,6 @@ static int fimc_m2m_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { struct fimc_ctx *ctx = fh_to_ctx(fh); - - /* The source and target color format need to be set */ - if (V4L2_TYPE_IS_OUTPUT(type)) { - if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx)) - return -EINVAL; - } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) { - return -EINVAL; - } - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); } @@ -469,7 +457,6 @@ static int fimc_m2m_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) { struct fimc_ctx *ctx = fh_to_ctx(fh); - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); } @@ -577,20 +564,18 @@ static int fimc_m2m_s_crop(struct file *file, void *fh, const struct v4l2_crop * &ctx->s_frame : &ctx->d_frame; /* Check to see if scaling ratio is within supported range */ - if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { - if (cr.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - ret = fimc_check_scaler_ratio(ctx, cr.c.width, - cr.c.height, ctx->d_frame.width, - ctx->d_frame.height, ctx->rotation); - } else { - ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, - ctx->s_frame.height, cr.c.width, - cr.c.height, ctx->rotation); - } - if (ret) { - v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n"); - return -EINVAL; - } + if (cr.type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret = fimc_check_scaler_ratio(ctx, cr.c.width, + cr.c.height, ctx->d_frame.width, + ctx->d_frame.height, ctx->rotation); + } else { + ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, + ctx->s_frame.height, cr.c.width, + cr.c.height, ctx->rotation); + } + if (ret) { + v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n"); + return -EINVAL; } f->offs_h = cr.c.left; @@ -653,6 +638,29 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, return vb2_queue_init(dst_vq); } +static int fimc_m2m_set_default_format(struct fimc_ctx *ctx) +{ + struct v4l2_pix_format_mplane pixm = { + .pixelformat = V4L2_PIX_FMT_RGB32, + .width = 800, + .height = 600, + .plane_fmt[0] = { + .bytesperline = 800 * 4, + .sizeimage = 800 * 4 * 600, + }, + }; + struct fimc_fmt *fmt; + + fmt = fimc_find_format(&pixm.pixelformat, NULL, FMT_FLAGS_M2M, 0); + if (!fmt) + return -EINVAL; + + __set_frame_format(&ctx->s_frame, fmt, &pixm); + __set_frame_format(&ctx->d_frame, fmt, &pixm); + + return 0; +} + static int fimc_m2m_open(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); @@ -697,6 +705,7 @@ static int fimc_m2m_open(struct file *file) ctx->flags = 0; ctx->in_path = FIMC_IO_DMA; ctx->out_path = FIMC_IO_DMA; + ctx->scaler.enabled = 1; ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); if (IS_ERR(ctx->m2m_ctx)) { @@ -707,9 +716,15 @@ static int fimc_m2m_open(struct file *file) if (fimc->m2m.refcnt++ == 0) set_bit(ST_M2M_RUN, &fimc->state); + ret = fimc_m2m_set_default_format(ctx); + if (ret < 0) + goto error_m2m_ctx; + mutex_unlock(&fimc->lock); return 0; +error_m2m_ctx: + v4l2_m2m_ctx_release(ctx->m2m_ctx); error_c: fimc_ctrls_delete(ctx); error_fh: From 8b164105d87d1c0101dfbf8d2bacee82c70d91aa Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 30 Jan 2013 09:55:46 -0300 Subject: [PATCH 1929/4607] [media] s5p-fimc: Fix FIMC.n subdev set_selection ioctl handler The V4L2_SEL_TGT_CROP_BOUNDS, V4L2_SEL_TGT_COMPOSE_BOUNDS selection targets are not supposed to be handled in the set_selection ioctl. Remove the code that doesn't do anything sensible now and make sure ctx->state is modified with the spinlock held. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-capture.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 5ec2f48f7db5..f553cc2a8ee8 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -1688,16 +1688,6 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP); switch (sel->target) { - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - f = &ctx->d_frame; - case V4L2_SEL_TGT_CROP_BOUNDS: - r->width = f->o_width; - r->height = f->o_height; - r->left = 0; - r->top = 0; - mutex_unlock(&fimc->lock); - return 0; - case V4L2_SEL_TGT_CROP: try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); break; @@ -1716,9 +1706,9 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd, spin_lock_irqsave(&fimc->slock, flags); set_frame_crop(f, r->left, r->top, r->width, r->height); set_bit(ST_CAPT_APPLY_CFG, &fimc->state); - spin_unlock_irqrestore(&fimc->slock, flags); if (sel->target == V4L2_SEL_TGT_COMPOSE) ctx->state |= FIMC_COMPOSE; + spin_unlock_irqrestore(&fimc->slock, flags); } dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top, From 0e23cbbe478809fe8499dab9b2a26bb6154c773b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 18 Jan 2013 15:34:37 -0300 Subject: [PATCH 1930/4607] [media] s5p-fimc: Add clk_prepare/unprepare for sclk_cam clocks Add clk_prepare(), clk_unprepare() calls for the sclk_cam clocks to ensure the driver works on platforms with the common clocks API enabled. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/s5p-fimc/fimc-mdevice.c | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 2b0587204868..d940454a0297 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -708,37 +708,56 @@ static int fimc_md_create_links(struct fimc_md *fmd) /* * The peripheral sensor clock management. */ -static int fimc_md_get_clocks(struct fimc_md *fmd) -{ - char clk_name[32]; - struct clk *clock; - int i; - - for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { - snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i); - clock = clk_get(NULL, clk_name); - if (IS_ERR(clock)) { - v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s", - clk_name); - return -ENXIO; - } - fmd->camclk[i].clock = clock; - } - return 0; -} - static void fimc_md_put_clocks(struct fimc_md *fmd) { int i = FIMC_MAX_CAMCLKS; while (--i >= 0) { - if (IS_ERR_OR_NULL(fmd->camclk[i].clock)) + if (IS_ERR(fmd->camclk[i].clock)) continue; + clk_unprepare(fmd->camclk[i].clock); clk_put(fmd->camclk[i].clock); - fmd->camclk[i].clock = NULL; + fmd->camclk[i].clock = ERR_PTR(-EINVAL); } } +static int fimc_md_get_clocks(struct fimc_md *fmd) +{ + struct device *dev = NULL; + char clk_name[32]; + struct clk *clock; + int ret, i; + + for (i = 0; i < FIMC_MAX_CAMCLKS; i++) + fmd->camclk[i].clock = ERR_PTR(-EINVAL); + + if (fmd->pdev->dev.of_node) + dev = &fmd->pdev->dev; + + for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { + snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i); + clock = clk_get(dev, clk_name); + + if (IS_ERR(clock)) { + dev_err(&fmd->pdev->dev, "Failed to get clock: %s\n", + clk_name); + ret = PTR_ERR(clock); + break; + } + ret = clk_prepare(clock); + if (ret < 0) { + clk_put(clock); + fmd->camclk[i].clock = ERR_PTR(-EINVAL); + break; + } + fmd->camclk[i].clock = clock; + } + if (ret) + fimc_md_put_clocks(fmd); + + return ret; +} + static int __fimc_md_set_camclk(struct fimc_md *fmd, struct fimc_sensor_info *s_info, bool on) From 33fba5de1e2318d301b84089e6468dedec9ad381 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 31 Jan 2013 01:12:46 -0300 Subject: [PATCH 1931/4607] [media] s5c73m3: Use devm_regulator_bulk_get API devm_regulator_bulk_get saves some cleanup and exit code. Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index b063b4ddf767..c143c9ec7ba9 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1627,7 +1627,7 @@ static int __devinit s5c73m3_probe(struct i2c_client *client, for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) state->supplies[i].supply = s5c73m3_supply_names[i]; - ret = regulator_bulk_get(dev, S5C73M3_MAX_SUPPLIES, + ret = devm_regulator_bulk_get(dev, S5C73M3_MAX_SUPPLIES, state->supplies); if (ret) { dev_err(dev, "failed to get regulators\n"); @@ -1636,7 +1636,7 @@ static int __devinit s5c73m3_probe(struct i2c_client *client, ret = s5c73m3_init_controls(state); if (ret) - goto out_err3; + goto out_err2; state->sensor_pix_size[RES_ISP] = &s5c73m3_isp_resolutions[1]; state->sensor_pix_size[RES_JPEG] = &s5c73m3_jpeg_resolutions[1]; @@ -1652,15 +1652,13 @@ static int __devinit s5c73m3_probe(struct i2c_client *client, ret = s5c73m3_register_spi_driver(state); if (ret < 0) - goto out_err3; + goto out_err2; state->i2c_client = client; v4l2_info(sd, "%s: completed succesfully\n", __func__); return 0; -out_err3: - regulator_bulk_free(S5C73M3_MAX_SUPPLIES, state->supplies); out_err2: s5c73m3_free_gpios(state); out_err1: @@ -1679,7 +1677,6 @@ static int __devexit s5c73m3_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); s5c73m3_unregister_spi_driver(state); - regulator_bulk_free(S5C73M3_MAX_SUPPLIES, state->supplies); s5c73m3_free_gpios(state); return 0; From b84ef24e1e421da266fe9c0a3ee82a49db517ddf Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Thu, 31 Jan 2013 07:03:05 -0300 Subject: [PATCH 1932/4607] [media] MAINTAINERS: Add s5c73m3 driver entry Signed-off-by: Andrzej Hajda Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 975ba7c276dd..93c863833998 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6748,6 +6748,13 @@ S: Maintained F: drivers/media/platform/s3c-camif/ F: include/media/s3c_camif.h +SAMSUNG S5C73M3 CAMERA DRIVER +M: Kyungmin Park +M: Andrzej Hajda +L: linux-media@vger.kernel.org +S: Supported +F: drivers/media/i2c/s5c73m3/* + SERIAL DRIVERS M: Alan Cox L: linux-serial@vger.kernel.org From 56bc911ac3b94c731db3a6de20258827f1a61c20 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 1 Feb 2013 15:00:40 -0300 Subject: [PATCH 1933/4607] [media] s5p-fimc: Redefine platform data structure for fimc-is Newer Exynos4 SoC are equipped with a local camera ISP that controls external raw image sensor directly. Such sensors can be connected through FIMC-LITEn (and MIPI-CSISn) IPs to the ISP, which then feeds image data to the FIMCn IP. Thus there can be two busses associated with an image source (sensor). Rename struct s5p_fimc_isp_info describing external image sensor (video decoder) to struct fimc_source_info to avoid confusion. bus_type is split into fimc_bus_type and sensor_bus_type. The bus type enumeration is extended to include both FIMC Writeback input types. The bus_type enumeration and the data structure name in the board files are modified according to the above changes. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Kukjin Kim Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-exynos/mach-nuri.c | 8 +-- arch/arm/mach-exynos/mach-universal_c210.c | 8 +-- arch/arm/mach-s5pv210/mach-goni.c | 6 +-- .../media/platform/s5p-fimc/fimc-lite-reg.c | 8 +-- .../media/platform/s5p-fimc/fimc-lite-reg.h | 4 +- .../media/platform/s5p-fimc/fimc-mdevice.c | 16 +++--- .../media/platform/s5p-fimc/fimc-mdevice.h | 2 +- drivers/media/platform/s5p-fimc/fimc-reg.c | 34 +++++++------ drivers/media/platform/s5p-fimc/fimc-reg.h | 6 +-- include/media/s5p_fimc.h | 49 ++++++++++++------- 10 files changed, 79 insertions(+), 62 deletions(-) diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index 27d4ed8b116e..7c2600e09a91 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -1209,25 +1209,25 @@ static struct i2c_board_info m5mols_board_info = { .platform_data = &m5mols_platdata, }; -static struct s5p_fimc_isp_info nuri_camera_sensors[] = { +static struct fimc_source_info nuri_camera_sensors[] = { { .flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_VSYNC_ACTIVE_LOW, - .bus_type = FIMC_ITU_601, + .fimc_bus_type = FIMC_BUS_TYPE_ITU_601, .board_info = &s5k6aa_board_info, .clk_frequency = 24000000UL, .i2c_bus_num = 6, }, { .flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_VSYNC_ACTIVE_LOW, - .bus_type = FIMC_MIPI_CSI2, + .fimc_bus_type = FIMC_BUS_TYPE_MIPI_CSI2, .board_info = &m5mols_board_info, .clk_frequency = 24000000UL, }, }; static struct s5p_platform_fimc fimc_md_platdata = { - .isp_info = nuri_camera_sensors, + .source_info = nuri_camera_sensors, .num_clients = ARRAY_SIZE(nuri_camera_sensors), }; diff --git a/arch/arm/mach-exynos/mach-universal_c210.c b/arch/arm/mach-exynos/mach-universal_c210.c index 9e3340f18950..c09290a8fa2e 100644 --- a/arch/arm/mach-exynos/mach-universal_c210.c +++ b/arch/arm/mach-exynos/mach-universal_c210.c @@ -988,12 +988,12 @@ static struct i2c_board_info m5mols_board_info = { .platform_data = &m5mols_platdata, }; -static struct s5p_fimc_isp_info universal_camera_sensors[] = { +static struct fimc_source_info universal_camera_sensors[] = { { .mux_id = 0, .flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_VSYNC_ACTIVE_LOW, - .bus_type = FIMC_ITU_601, + .fimc_bus_type = FIMC_BUS_TYPE_ITU_601, .board_info = &s5k6aa_board_info, .i2c_bus_num = 0, .clk_frequency = 24000000UL, @@ -1001,7 +1001,7 @@ static struct s5p_fimc_isp_info universal_camera_sensors[] = { .mux_id = 0, .flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_VSYNC_ACTIVE_LOW, - .bus_type = FIMC_MIPI_CSI2, + .fimc_bus_type = FIMC_BUS_TYPE_MIPI_CSI2, .board_info = &m5mols_board_info, .i2c_bus_num = 0, .clk_frequency = 24000000UL, @@ -1009,7 +1009,7 @@ static struct s5p_fimc_isp_info universal_camera_sensors[] = { }; static struct s5p_platform_fimc fimc_md_platdata = { - .isp_info = universal_camera_sensors, + .source_info = universal_camera_sensors, .num_clients = ARRAY_SIZE(universal_camera_sensors), }; diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index c72b31078c99..423f6b6e8dbe 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -841,12 +841,12 @@ static struct i2c_board_info noon010pc30_board_info = { .platform_data = &noon010pc30_pldata, }; -static struct s5p_fimc_isp_info goni_camera_sensors[] = { +static struct fimc_source_info goni_camera_sensors[] = { { .mux_id = 0, .flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_VSYNC_ACTIVE_LOW, - .bus_type = FIMC_ITU_601, + .bus_type = FIMC_BUS_TYPE_ITU_601, .board_info = &noon010pc30_board_info, .i2c_bus_num = 0, .clk_frequency = 16000000UL, @@ -854,7 +854,7 @@ static struct s5p_fimc_isp_info goni_camera_sensors[] = { }; static struct s5p_platform_fimc goni_fimc_md_platdata __initdata = { - .isp_info = goni_camera_sensors, + .source_info = goni_camera_sensors, .num_clients = ARRAY_SIZE(goni_camera_sensors), }; diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c index 962652da3b43..f0af0754a7b4 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c @@ -187,12 +187,12 @@ static void flite_hw_set_camera_port(struct fimc_lite *dev, int id) /* Select serial or parallel bus, camera port (A,B) and set signals polarity */ void flite_hw_set_camera_bus(struct fimc_lite *dev, - struct s5p_fimc_isp_info *s_info) + struct fimc_source_info *si) { u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); - unsigned int flags = s_info->flags; + unsigned int flags = si->flags; - if (s_info->bus_type != FIMC_MIPI_CSI2) { + if (si->sensor_bus_type != FIMC_BUS_TYPE_MIPI_CSI2) { cfg &= ~(FLITE_REG_CIGCTRL_SELCAM_MIPI | FLITE_REG_CIGCTRL_INVPOLPCLK | FLITE_REG_CIGCTRL_INVPOLVSYNC | @@ -212,7 +212,7 @@ void flite_hw_set_camera_bus(struct fimc_lite *dev, writel(cfg, dev->regs + FLITE_REG_CIGCTRL); - flite_hw_set_camera_port(dev, s_info->mux_id); + flite_hw_set_camera_port(dev, si->mux_id); } static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.h b/drivers/media/platform/s5p-fimc/fimc-lite-reg.h index adb9e9e6f3c2..0e345844c13a 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite-reg.h +++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.h @@ -131,9 +131,9 @@ void flite_hw_set_interrupt_mask(struct fimc_lite *dev); void flite_hw_capture_start(struct fimc_lite *dev); void flite_hw_capture_stop(struct fimc_lite *dev); void flite_hw_set_camera_bus(struct fimc_lite *dev, - struct s5p_fimc_isp_info *s_info); + struct fimc_source_info *s_info); void flite_hw_set_camera_polarity(struct fimc_lite *dev, - struct s5p_fimc_isp_info *cam); + struct fimc_source_info *cam); void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f); void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f); diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index d940454a0297..f49f6f17a3f7 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -290,7 +290,7 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) for (i = 0; i < num_clients; i++) { struct v4l2_subdev *sd; - fmd->sensor[i].pdata = pdata->isp_info[i]; + fmd->sensor[i].pdata = pdata->source_info[i]; ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); if (ret) break; @@ -504,7 +504,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, struct v4l2_subdev *sensor, int pad, int link_mask) { - struct fimc_sensor_info *s_info; + struct fimc_sensor_info *s_info = NULL; struct media_entity *sink; unsigned int flags = 0; int ret, i; @@ -614,7 +614,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) { struct v4l2_subdev *csi_sensors[CSIS_MAX_ENTITIES] = { NULL }; struct v4l2_subdev *sensor, *csis; - struct s5p_fimc_isp_info *pdata; + struct fimc_source_info *pdata; struct fimc_sensor_info *s_info; struct media_entity *source, *sink; int i, pad, fimc_id = 0, ret = 0; @@ -632,8 +632,8 @@ static int fimc_md_create_links(struct fimc_md *fmd) source = NULL; pdata = &s_info->pdata; - switch (pdata->bus_type) { - case FIMC_MIPI_CSI2: + switch (pdata->sensor_bus_type) { + case FIMC_BUS_TYPE_MIPI_CSI2: if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES, "Wrong CSI channel id: %d\n", pdata->mux_id)) return -EINVAL; @@ -659,14 +659,14 @@ static int fimc_md_create_links(struct fimc_md *fmd) csi_sensors[pdata->mux_id] = sensor; break; - case FIMC_ITU_601...FIMC_ITU_656: + case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656: source = &sensor->entity; pad = 0; break; default: v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n", - pdata->bus_type); + pdata->sensor_bus_type); return -EINVAL; } if (source == NULL) @@ -762,7 +762,7 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, struct fimc_sensor_info *s_info, bool on) { - struct s5p_fimc_isp_info *pdata = &s_info->pdata; + struct fimc_source_info *pdata = &s_info->pdata; struct fimc_camclk_info *camclk; int ret = 0; diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.h b/drivers/media/platform/s5p-fimc/fimc-mdevice.h index da7d9922cf58..06b0d8276fd2 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.h @@ -53,7 +53,7 @@ struct fimc_camclk_info { * This data structure applies to image sensor and the writeback subdevs. */ struct fimc_sensor_info { - struct s5p_fimc_isp_info pdata; + struct fimc_source_info pdata; struct v4l2_subdev *subdev; struct fimc_dev *host; }; diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c index c05d0444192f..50b97c75b956 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.c +++ b/drivers/media/platform/s5p-fimc/fimc-reg.c @@ -554,7 +554,7 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev, } int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam) + struct fimc_source_info *cam) { u32 cfg = readl(fimc->regs + FIMC_REG_CIGCTRL); @@ -596,14 +596,15 @@ static const struct mbus_pixfmt_desc pix_desc[] = { }; int fimc_hw_set_camera_source(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam) + struct fimc_source_info *source) { struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame; - u32 cfg = 0; - u32 bus_width; + u32 bus_width, cfg = 0; int i; - if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) { + switch (source->fimc_bus_type) { + case FIMC_BUS_TYPE_ITU_601: + case FIMC_BUS_TYPE_ITU_656: for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) { cfg = pix_desc[i].cisrcfmt; @@ -619,15 +620,17 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, return -EINVAL; } - if (cam->bus_type == FIMC_ITU_601) { + if (source->fimc_bus_type == FIMC_BUS_TYPE_ITU_601) { if (bus_width == 8) cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; else if (bus_width == 16) cfg |= FIMC_REG_CISRCFMT_ITU601_16BIT; } /* else defaults to ITU-R BT.656 8-bit */ - } else if (cam->bus_type == FIMC_MIPI_CSI2) { + break; + case FIMC_BUS_TYPE_MIPI_CSI2: if (fimc_fmt_is_user_defined(f->fmt->color)) cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; + break; } cfg |= (f->o_width << 16) | f->o_height; @@ -655,7 +658,7 @@ void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f) } int fimc_hw_set_camera_type(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam) + struct fimc_source_info *source) { u32 cfg, tmp; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; @@ -668,11 +671,11 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB | FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG); - switch (cam->bus_type) { - case FIMC_MIPI_CSI2: + switch (source->fimc_bus_type) { + case FIMC_BUS_TYPE_MIPI_CSI2: cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI; - if (cam->mux_id == 0) + if (source->mux_id == 0) cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI_A; /* TODO: add remaining supported formats. */ @@ -695,15 +698,16 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, writel(tmp, fimc->regs + FIMC_REG_CSIIMGFMT); break; - case FIMC_ITU_601...FIMC_ITU_656: - if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */ + case FIMC_BUS_TYPE_ITU_601...FIMC_BUS_TYPE_ITU_656: + if (source->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */ cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A; break; - case FIMC_LCD_WB: + case FIMC_BUS_TYPE_LCD_WRITEBACK_A: cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; break; default: - v4l2_err(&vid_cap->vfd, "Invalid camera bus type selected\n"); + v4l2_err(&vid_cap->vfd, "Invalid FIMC bus type selected: %d\n", + source->fimc_bus_type); return -EINVAL; } writel(cfg, fimc->regs + FIMC_REG_CIGCTRL); diff --git a/drivers/media/platform/s5p-fimc/fimc-reg.h b/drivers/media/platform/s5p-fimc/fimc-reg.h index f3e0b78a3736..1a40df6d1a80 100644 --- a/drivers/media/platform/s5p-fimc/fimc-reg.h +++ b/drivers/media/platform/s5p-fimc/fimc-reg.h @@ -297,12 +297,12 @@ void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, int index); int fimc_hw_set_camera_source(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); + struct fimc_source_info *cam); void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f); int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); + struct fimc_source_info *cam); int fimc_hw_set_camera_type(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); + struct fimc_source_info *cam); void fimc_hw_clear_irq(struct fimc_dev *dev); void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on); void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on); diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h index eaea62a382f8..28f3590aa031 100644 --- a/include/media/s5p_fimc.h +++ b/include/media/s5p_fimc.h @@ -1,8 +1,8 @@ /* - * Samsung S5P SoC camera interface driver header + * Samsung S5P/Exynos4 SoC series camera interface driver header * - * Copyright (c) 2010 Samsung Electronics Co., Ltd - * Author: Sylwester Nawrocki, + * Copyright (C) 2010 - 2013 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki * * 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 @@ -14,45 +14,58 @@ #include -enum cam_bus_type { - FIMC_ITU_601 = 1, - FIMC_ITU_656, - FIMC_MIPI_CSI2, - FIMC_LCD_WB, /* FIFO link from LCD mixer */ +/* + * Enumeration of the FIMC data bus types. + */ +enum fimc_bus_type { + /* Camera parallel bus */ + FIMC_BUS_TYPE_ITU_601 = 1, + /* Camera parallel bus with embedded synchronization */ + FIMC_BUS_TYPE_ITU_656, + /* Camera MIPI-CSI2 serial bus */ + FIMC_BUS_TYPE_MIPI_CSI2, + /* FIFO link from LCD controller (WriteBack A) */ + FIMC_BUS_TYPE_LCD_WRITEBACK_A, + /* FIFO link from LCD controller (WriteBack B) */ + FIMC_BUS_TYPE_LCD_WRITEBACK_B, + /* FIFO link from FIMC-IS */ + FIMC_BUS_TYPE_ISP_WRITEBACK = FIMC_BUS_TYPE_LCD_WRITEBACK_B, }; struct i2c_board_info; /** - * struct s5p_fimc_isp_info - image sensor information required for host - * interace configuration. + * struct fimc_source_info - video source description required for the host + * interface configuration * * @board_info: pointer to I2C subdevice's board info * @clk_frequency: frequency of the clock the host interface provides to sensor - * @bus_type: determines bus type, MIPI, ITU-R BT.601 etc. + * @fimc_bus_type: FIMC camera input type + * @sensor_bus_type: image sensor bus type, MIPI, ITU-R BT.601 etc. + * @flags: the parallel sensor bus flags defining signals polarity (V4L2_MBUS_*) * @i2c_bus_num: i2c control bus id the sensor is attached to * @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU) * @clk_id: index of the SoC peripheral clock for sensors - * @flags: the parallel bus flags defining signals polarity (V4L2_MBUS_*) */ -struct s5p_fimc_isp_info { +struct fimc_source_info { struct i2c_board_info *board_info; unsigned long clk_frequency; - enum cam_bus_type bus_type; + enum fimc_bus_type fimc_bus_type; + enum fimc_bus_type sensor_bus_type; + u16 flags; u16 i2c_bus_num; u16 mux_id; - u16 flags; u8 clk_id; }; /** * struct s5p_platform_fimc - camera host interface platform data * - * @isp_info: properties of camera sensor required for host interface setup - * @num_clients: the number of attached image sensors + * @source_info: properties of an image source for the host interface setup + * @num_clients: the number of attached image sources */ struct s5p_platform_fimc { - struct s5p_fimc_isp_info *isp_info; + struct fimc_source_info *source_info; int num_clients; }; From 28718152e0a78085297ec7705f53869e41d1ae73 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 24 Jan 2013 04:42:05 -0300 Subject: [PATCH 1934/4607] [media] Move DV-class control IDs from videodev2.h to v4l2-controls.h When the control IDs were split off from videodev2.h to v4l2-controls.h these new Digital Video controls were forgotten (the two patches may have crossed one another). Move these controls to their proper place in v4l2-controls.h. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/v4l2-controls.h | 24 ++++++++++++++++++++++++ include/uapi/linux/videodev2.h | 22 ---------------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 4dc0822700fe..0bece06792d7 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -778,6 +778,7 @@ enum v4l2_jpeg_chroma_subsampling { #define V4L2_JPEG_ACTIVE_MARKER_DQT (1 << 17) #define V4L2_JPEG_ACTIVE_MARKER_DHT (1 << 18) + /* Image source controls */ #define V4L2_CID_IMAGE_SOURCE_CLASS_BASE (V4L2_CTRL_CLASS_IMAGE_SOURCE | 0x900) #define V4L2_CID_IMAGE_SOURCE_CLASS (V4L2_CTRL_CLASS_IMAGE_SOURCE | 1) @@ -796,4 +797,27 @@ enum v4l2_jpeg_chroma_subsampling { #define V4L2_CID_PIXEL_RATE (V4L2_CID_IMAGE_PROC_CLASS_BASE + 2) #define V4L2_CID_TEST_PATTERN (V4L2_CID_IMAGE_PROC_CLASS_BASE + 3) + +/* DV-class control IDs defined by V4L2 */ +#define V4L2_CID_DV_CLASS_BASE (V4L2_CTRL_CLASS_DV | 0x900) +#define V4L2_CID_DV_CLASS (V4L2_CTRL_CLASS_DV | 1) + +#define V4L2_CID_DV_TX_HOTPLUG (V4L2_CID_DV_CLASS_BASE + 1) +#define V4L2_CID_DV_TX_RXSENSE (V4L2_CID_DV_CLASS_BASE + 2) +#define V4L2_CID_DV_TX_EDID_PRESENT (V4L2_CID_DV_CLASS_BASE + 3) +#define V4L2_CID_DV_TX_MODE (V4L2_CID_DV_CLASS_BASE + 4) +enum v4l2_dv_tx_mode { + V4L2_DV_TX_MODE_DVI_D = 0, + V4L2_DV_TX_MODE_HDMI = 1, +}; +#define V4L2_CID_DV_TX_RGB_RANGE (V4L2_CID_DV_CLASS_BASE + 5) +enum v4l2_dv_rgb_range { + V4L2_DV_RGB_RANGE_AUTO = 0, + V4L2_DV_RGB_RANGE_LIMITED = 1, + V4L2_DV_RGB_RANGE_FULL = 2, +}; + +#define V4L2_CID_DV_RX_POWER_PRESENT (V4L2_CID_DV_CLASS_BASE + 100) +#define V4L2_CID_DV_RX_RGB_RANGE (V4L2_CID_DV_CLASS_BASE + 101) + #endif diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 928799c2e2d9..234d1d870914 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1354,28 +1354,6 @@ struct v4l2_querymenu { #define V4L2_CID_PRIVATE_BASE 0x08000000 -/* DV-class control IDs defined by V4L2 */ -#define V4L2_CID_DV_CLASS_BASE (V4L2_CTRL_CLASS_DV | 0x900) -#define V4L2_CID_DV_CLASS (V4L2_CTRL_CLASS_DV | 1) - -#define V4L2_CID_DV_TX_HOTPLUG (V4L2_CID_DV_CLASS_BASE + 1) -#define V4L2_CID_DV_TX_RXSENSE (V4L2_CID_DV_CLASS_BASE + 2) -#define V4L2_CID_DV_TX_EDID_PRESENT (V4L2_CID_DV_CLASS_BASE + 3) -#define V4L2_CID_DV_TX_MODE (V4L2_CID_DV_CLASS_BASE + 4) -enum v4l2_dv_tx_mode { - V4L2_DV_TX_MODE_DVI_D = 0, - V4L2_DV_TX_MODE_HDMI = 1, -}; -#define V4L2_CID_DV_TX_RGB_RANGE (V4L2_CID_DV_CLASS_BASE + 5) -enum v4l2_dv_rgb_range { - V4L2_DV_RGB_RANGE_AUTO = 0, - V4L2_DV_RGB_RANGE_LIMITED = 1, - V4L2_DV_RGB_RANGE_FULL = 2, -}; - -#define V4L2_CID_DV_RX_POWER_PRESENT (V4L2_CID_DV_CLASS_BASE + 100) -#define V4L2_CID_DV_RX_RGB_RANGE (V4L2_CID_DV_CLASS_BASE + 101) - /* * T U N I N G */ From ea01a83d5c88a3e0bb124ec4b3abf3aefcf0d719 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 7 Sep 2012 05:42:15 -0300 Subject: [PATCH 1935/4607] [media] mt9v011: convert to the control framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Hans Verkuil Tested-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9v011.c | 223 +++++++++++------------------------- 1 file changed, 67 insertions(+), 156 deletions(-) diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 6bf01ad62765..73b7688cbebd 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -13,6 +13,7 @@ #include #include #include +#include #include MODULE_DESCRIPTION("Micron mt9v011 sensor driver"); @@ -48,68 +49,9 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); #define MT9V011_VERSION 0x8232 #define MT9V011_REV_B_VERSION 0x8243 -/* supported controls */ -static struct v4l2_queryctrl mt9v011_qctrl[] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = (1 << 12) - 1 - 0x0020, - .step = 1, - .default_value = 0x0020, - .flags = 0, - }, { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 2047, - .step = 1, - .default_value = 0x01fc, - .flags = 0, - }, { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = -1 << 9, - .maximum = (1 << 9) - 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = -1 << 9, - .maximum = (1 << 9) - 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vflip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - } -}; - struct mt9v011 { struct v4l2_subdev sd; + struct v4l2_ctrl_handler ctrls; unsigned width, height; unsigned xtal; unsigned hflip:1; @@ -380,99 +322,6 @@ static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) set_res(sd); set_read_mode(sd); - return 0; -}; - -static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct mt9v011 *core = to_mt9v011(sd); - - v4l2_dbg(1, debug, sd, "g_ctrl called\n"); - - switch (ctrl->id) { - case V4L2_CID_GAIN: - ctrl->value = core->global_gain; - return 0; - case V4L2_CID_EXPOSURE: - ctrl->value = core->exposure; - return 0; - case V4L2_CID_RED_BALANCE: - ctrl->value = core->red_bal; - return 0; - case V4L2_CID_BLUE_BALANCE: - ctrl->value = core->blue_bal; - return 0; - case V4L2_CID_HFLIP: - ctrl->value = core->hflip ? 1 : 0; - return 0; - case V4L2_CID_VFLIP: - ctrl->value = core->vflip ? 1 : 0; - return 0; - } - return -EINVAL; -} - -static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - int i; - - v4l2_dbg(1, debug, sd, "queryctrl called\n"); - - for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) - if (qc->id && qc->id == mt9v011_qctrl[i].id) { - memcpy(qc, &(mt9v011_qctrl[i]), - sizeof(*qc)); - return 0; - } - - return -EINVAL; -} - - -static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct mt9v011 *core = to_mt9v011(sd); - u8 i, n; - n = ARRAY_SIZE(mt9v011_qctrl); - - for (i = 0; i < n; i++) { - if (ctrl->id != mt9v011_qctrl[i].id) - continue; - if (ctrl->value < mt9v011_qctrl[i].minimum || - ctrl->value > mt9v011_qctrl[i].maximum) - return -ERANGE; - v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", - ctrl->id, ctrl->value); - break; - } - - switch (ctrl->id) { - case V4L2_CID_GAIN: - core->global_gain = ctrl->value; - break; - case V4L2_CID_EXPOSURE: - core->exposure = ctrl->value; - break; - case V4L2_CID_RED_BALANCE: - core->red_bal = ctrl->value; - break; - case V4L2_CID_BLUE_BALANCE: - core->blue_bal = ctrl->value; - break; - case V4L2_CID_HFLIP: - core->hflip = ctrl->value; - set_read_mode(sd); - return 0; - case V4L2_CID_VFLIP: - core->vflip = ctrl->value; - set_read_mode(sd); - return 0; - default: - return -EINVAL; - } - - set_balance(sd); - return 0; } @@ -599,10 +448,46 @@ static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, version); } -static const struct v4l2_subdev_core_ops mt9v011_core_ops = { - .queryctrl = mt9v011_queryctrl, - .g_ctrl = mt9v011_g_ctrl, +static int mt9v011_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mt9v011 *core = + container_of(ctrl->handler, struct mt9v011, ctrls); + struct v4l2_subdev *sd = &core->sd; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + core->global_gain = ctrl->val; + break; + case V4L2_CID_EXPOSURE: + core->exposure = ctrl->val; + break; + case V4L2_CID_RED_BALANCE: + core->red_bal = ctrl->val; + break; + case V4L2_CID_BLUE_BALANCE: + core->blue_bal = ctrl->val; + break; + case V4L2_CID_HFLIP: + core->hflip = ctrl->val; + set_read_mode(sd); + return 0; + case V4L2_CID_VFLIP: + core->vflip = ctrl->val; + set_read_mode(sd); + return 0; + default: + return -EINVAL; + } + + set_balance(sd); + return 0; +} + +static struct v4l2_ctrl_ops mt9v011_ctrl_ops = { .s_ctrl = mt9v011_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops mt9v011_core_ops = { .reset = mt9v011_reset, .g_chip_ident = mt9v011_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -658,6 +543,30 @@ static int mt9v011_probe(struct i2c_client *c, return -EINVAL; } + v4l2_ctrl_handler_init(&core->ctrls, 5); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_GAIN, 0, (1 << 12) - 1 - 0x20, 1, 0x20); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 2047, 1, 0x01fc); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_RED_BALANCE, -(1 << 9), (1 << 9) - 1, 1, 0); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_BLUE_BALANCE, -(1 << 9), (1 << 9) - 1, 1, 0); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (core->ctrls.error) { + int ret = core->ctrls.error; + + v4l2_err(sd, "control initialization error %d\n", ret); + v4l2_ctrl_handler_free(&core->ctrls); + kfree(core); + return ret; + } + core->sd.ctrl_handler = &core->ctrls; + core->global_gain = 0x0024; core->exposure = 0x01fc; core->width = 640; @@ -681,12 +590,14 @@ static int mt9v011_probe(struct i2c_client *c, static int mt9v011_remove(struct i2c_client *c) { struct v4l2_subdev *sd = i2c_get_clientdata(c); + struct mt9v011 *core = to_mt9v011(sd); v4l2_dbg(1, debug, sd, "mt9v011.c: removing mt9v011 adapter on address 0x%x\n", c->addr << 1); v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&core->ctrls); kfree(to_mt9v011(sd)); return 0; } From a346caacf31907085e8425bdab7cf5147545439e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 2 Feb 2013 05:57:37 -0300 Subject: [PATCH 1936/4607] [media] tvaudio: fix broken volume/balance calculations The balance control did not do what it is supposed to do due to wrong calculations. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvaudio.c | 65 +++++++++++++++---------------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index 3b24d3fc1866..d5cd2eb95558 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -93,8 +93,8 @@ struct CHIPDESC { /* which register has which value */ int leftreg,rightreg,treblereg,bassreg; - /* initialize with (defaults to 65535/65535/32768/32768 */ - int leftinit,rightinit,trebleinit,bassinit; + /* initialize with (defaults to 65535/32768/32768 */ + int volinit, trebleinit, bassinit; /* functions to convert the values (v4l -> chip) */ getvalue volfunc,treblefunc,bassfunc; @@ -122,7 +122,7 @@ struct CHIPSTATE { audiocmd shadow; /* current settings */ - __u16 left, right, treble, bass, muted; + u16 volume, balance, treble, bass, muted; int prevmode; int radio; int input; @@ -1523,8 +1523,7 @@ static struct CHIPDESC chiplist[] = { .rightreg = TDA9875_MVR, .bassreg = TDA9875_MBA, .treblereg = TDA9875_MTR, - .leftinit = 58880, - .rightinit = 58880, + .volinit = 58880, }, { .name = "tda9850", @@ -1694,20 +1693,13 @@ static int tvaudio_g_ctrl(struct v4l2_subdev *sd, case V4L2_CID_AUDIO_VOLUME: if (!(desc->flags & CHIP_HAS_VOLUME)) break; - ctrl->value = max(chip->left,chip->right); + ctrl->value = chip->volume; return 0; case V4L2_CID_AUDIO_BALANCE: - { - int volume; if (!(desc->flags & CHIP_HAS_VOLUME)) break; - volume = max(chip->left,chip->right); - if (volume) - ctrl->value=(32768*min(chip->left,chip->right))/volume; - else - ctrl->value=32768; + ctrl->value = chip->balance; return 0; - } case V4L2_CID_AUDIO_BASS: if (!(desc->flags & CHIP_HAS_BASSTREBLE)) break; @@ -1744,41 +1736,38 @@ static int tvaudio_s_ctrl(struct v4l2_subdev *sd, return 0; case V4L2_CID_AUDIO_VOLUME: { - int volume,balance; + u32 volume, balance; + u32 left, right; if (!(desc->flags & CHIP_HAS_VOLUME)) break; - volume = max(chip->left,chip->right); - if (volume) - balance=(32768*min(chip->left,chip->right))/volume; - else - balance=32768; - - volume=ctrl->value; - chip->left = (min(65536 - balance,32768) * volume) / 32768; - chip->right = (min(balance,volume *(__u16)32768)) / 32768; - - chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); - chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); + volume = ctrl->value; + chip->volume = volume; + balance = chip->balance; + left = (min(65536U - balance, 32768U) * volume) / 32768U; + right = (min(balance, 32768U) * volume) / 32768U; + chip_write(chip, desc->leftreg, desc->volfunc(left)); + chip_write(chip, desc->rightreg, desc->volfunc(right)); return 0; } case V4L2_CID_AUDIO_BALANCE: { - int volume, balance; + u32 volume, balance; + u32 left, right; if (!(desc->flags & CHIP_HAS_VOLUME)) break; - volume = max(chip->left, chip->right); balance = ctrl->value; - chip->left = (min(65536 - balance, 32768) * volume) / 32768; - chip->right = (min(balance, volume * (__u16)32768)) / 32768; - - chip_write(chip, desc->leftreg, desc->volfunc(chip->left)); - chip_write(chip, desc->rightreg, desc->volfunc(chip->right)); + chip->balance = balance; + volume = chip->volume; + left = (min(65536U - balance, 32768U) * volume) / 32768U; + right = (min(balance, 32768U) * volume) / 32768U; + chip_write(chip, desc->leftreg, desc->volfunc(left)); + chip_write(chip, desc->rightreg, desc->volfunc(right)); return 0; } case V4L2_CID_AUDIO_BASS: @@ -2043,12 +2032,12 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * v4l2_info(sd, "volume callback undefined!\n"); desc->flags &= ~CHIP_HAS_VOLUME; } else { - chip->left = desc->leftinit ? desc->leftinit : 65535; - chip->right = desc->rightinit ? desc->rightinit : 65535; + chip->volume = desc->volinit ? desc->volinit : 65535; + chip->balance = 32768; chip_write(chip, desc->leftreg, - desc->volfunc(chip->left)); + desc->volfunc(chip->volume)); chip_write(chip, desc->rightreg, - desc->volfunc(chip->right)); + desc->volfunc(chip->volume)); } } if (desc->flags & CHIP_HAS_BASSTREBLE) { From 71df09bc67f08844a8af4f48966d3bb550a0fb59 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 10 Sep 2012 11:06:57 -0300 Subject: [PATCH 1937/4607] [media] tvaudio: fix two tea6420 errors The inputmask for the tea6420 wasn't set and the wrong mute register value was used. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvaudio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index d5cd2eb95558..df6aaa5fbdec 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -1617,7 +1617,8 @@ static struct CHIPDESC chiplist[] = { .inputreg = -1, .inputmap = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC }, - .inputmute = TEA6300_S_GMU, + .inputmute = TEA6420_S_GMU, + .inputmask = 0x07, }, { .name = "tda8425", From c9114031d88bb71659d7f0cc74ecf8ddea47e1b7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 2 Feb 2013 06:03:36 -0300 Subject: [PATCH 1938/4607] [media] tvaudio: convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvaudio.c | 208 ++++++++++++++---------------------- 1 file changed, 78 insertions(+), 130 deletions(-) diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index df6aaa5fbdec..e3b33b78dd21 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -91,13 +92,13 @@ struct CHIPDESC { audiocmd init; /* which register has which value */ - int leftreg,rightreg,treblereg,bassreg; + int leftreg, rightreg, treblereg, bassreg; /* initialize with (defaults to 65535/32768/32768 */ int volinit, trebleinit, bassinit; /* functions to convert the values (v4l -> chip) */ - getvalue volfunc,treblefunc,bassfunc; + getvalue volfunc, treblefunc, bassfunc; /* get/set mode */ getrxsubchans getrxsubchans; @@ -113,6 +114,12 @@ struct CHIPDESC { /* current state of the chip */ struct CHIPSTATE { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct { + /* volume/balance cluster */ + struct v4l2_ctrl *volume; + struct v4l2_ctrl *balance; + }; /* chip-specific description - should point to an entry at CHIPDESC table */ @@ -122,7 +129,7 @@ struct CHIPSTATE { audiocmd shadow; /* current settings */ - u16 volume, balance, treble, bass, muted; + u16 muted; int prevmode; int radio; int input; @@ -138,6 +145,11 @@ static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd) return container_of(sd, struct CHIPSTATE, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct CHIPSTATE, hdl)->sd; +} + /* ---------------------------------------------------------------------- */ /* i2c I/O functions */ @@ -1679,91 +1691,27 @@ static struct CHIPDESC chiplist[] = { /* ---------------------------------------------------------------------- */ -static int tvaudio_g_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) +static int tvaudio_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - if (!(desc->flags & CHIP_HAS_INPUTSEL)) - break; - ctrl->value=chip->muted; - return 0; - case V4L2_CID_AUDIO_VOLUME: - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - ctrl->value = chip->volume; - return 0; - case V4L2_CID_AUDIO_BALANCE: - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - ctrl->value = chip->balance; - return 0; - case V4L2_CID_AUDIO_BASS: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - ctrl->value = chip->bass; - return 0; - case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - ctrl->value = chip->treble; - return 0; - } - return -EINVAL; -} - -static int tvaudio_s_ctrl(struct v4l2_subdev *sd, - struct v4l2_control *ctrl) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (!(desc->flags & CHIP_HAS_INPUTSEL)) - break; - - if (ctrl->value < 0 || ctrl->value >= 2) - return -ERANGE; - chip->muted = ctrl->value; + chip->muted = ctrl->val; if (chip->muted) chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); else chip_write_masked(chip,desc->inputreg, desc->inputmap[chip->input],desc->inputmask); return 0; - case V4L2_CID_AUDIO_VOLUME: - { + case V4L2_CID_AUDIO_VOLUME: { u32 volume, balance; u32 left, right; - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - - volume = ctrl->value; - chip->volume = volume; - balance = chip->balance; - left = (min(65536U - balance, 32768U) * volume) / 32768U; - right = (min(balance, 32768U) * volume) / 32768U; - - chip_write(chip, desc->leftreg, desc->volfunc(left)); - chip_write(chip, desc->rightreg, desc->volfunc(right)); - return 0; - } - case V4L2_CID_AUDIO_BALANCE: - { - u32 volume, balance; - u32 left, right; - - if (!(desc->flags & CHIP_HAS_VOLUME)) - break; - - balance = ctrl->value; - chip->balance = balance; - volume = chip->volume; + volume = chip->volume->val; + balance = chip->balance->val; left = (min(65536U - balance, 32768U) * volume) / 32768U; right = (min(balance, 32768U) * volume) / 32768U; @@ -1772,18 +1720,10 @@ static int tvaudio_s_ctrl(struct v4l2_subdev *sd, return 0; } case V4L2_CID_AUDIO_BASS: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - chip->bass = ctrl->value; - chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); - + chip_write(chip, desc->bassreg, desc->bassfunc(ctrl->val)); return 0; case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - break; - chip->treble = ctrl->value; - chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); - + chip_write(chip, desc->treblereg, desc->treblefunc(ctrl->val)); return 0; } return -EINVAL; @@ -1802,35 +1742,6 @@ static int tvaudio_s_radio(struct v4l2_subdev *sd) return 0; } -static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - struct CHIPSTATE *chip = to_state(sd); - struct CHIPDESC *desc = chip->desc; - - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - if (desc->flags & CHIP_HAS_INPUTSEL) - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - break; - case V4L2_CID_AUDIO_VOLUME: - if (desc->flags & CHIP_HAS_VOLUME) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); - break; - case V4L2_CID_AUDIO_BALANCE: - if (desc->flags & CHIP_HAS_VOLUME) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - break; - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - break; - default: - break; - } - return -EINVAL; -} - static int tvaudio_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -1934,13 +1845,32 @@ static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ide return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0); } +static int tvaudio_log_status(struct v4l2_subdev *sd) +{ + struct CHIPSTATE *chip = to_state(sd); + struct CHIPDESC *desc = chip->desc; + + v4l2_info(sd, "Chip: %s\n", desc->name); + v4l2_ctrl_handler_log_status(&chip->hdl, sd->name); + return 0; +} + /* ----------------------------------------------------------------------- */ -static const struct v4l2_subdev_core_ops tvaudio_core_ops = { - .g_chip_ident = tvaudio_g_chip_ident, - .queryctrl = tvaudio_queryctrl, - .g_ctrl = tvaudio_g_ctrl, +static const struct v4l2_ctrl_ops tvaudio_ctrl_ops = { .s_ctrl = tvaudio_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops tvaudio_core_ops = { + .log_status = tvaudio_log_status, + .g_chip_ident = tvaudio_g_chip_ident, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .s_std = tvaudio_s_std, }; @@ -2025,6 +1955,10 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * else chip_cmd(chip, "init", &desc->init); + v4l2_ctrl_handler_init(&chip->hdl, 5); + if (desc->flags & CHIP_HAS_INPUTSEL) + v4l2_ctrl_new_std(&chip->hdl, &tvaudio_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); if (desc->flags & CHIP_HAS_VOLUME) { if (!desc->volfunc) { /* This shouldn't be happen. Warn user, but keep working @@ -2033,12 +1967,14 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * v4l2_info(sd, "volume callback undefined!\n"); desc->flags &= ~CHIP_HAS_VOLUME; } else { - chip->volume = desc->volinit ? desc->volinit : 65535; - chip->balance = 32768; - chip_write(chip, desc->leftreg, - desc->volfunc(chip->volume)); - chip_write(chip, desc->rightreg, - desc->volfunc(chip->volume)); + chip->volume = v4l2_ctrl_new_std(&chip->hdl, + &tvaudio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, + 0, 65535, 65535 / 100, + desc->volinit ? desc->volinit : 65535); + chip->balance = v4l2_ctrl_new_std(&chip->hdl, + &tvaudio_ctrl_ops, V4L2_CID_AUDIO_BALANCE, + 0, 65535, 65535 / 100, 32768); + v4l2_ctrl_cluster(2, &chip->volume); } } if (desc->flags & CHIP_HAS_BASSTREBLE) { @@ -2049,17 +1985,28 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * v4l2_info(sd, "bass/treble callbacks undefined!\n"); desc->flags &= ~CHIP_HAS_BASSTREBLE; } else { - chip->treble = desc->trebleinit ? - desc->trebleinit : 32768; - chip->bass = desc->bassinit ? - desc->bassinit : 32768; - chip_write(chip, desc->bassreg, - desc->bassfunc(chip->bass)); - chip_write(chip, desc->treblereg, - desc->treblefunc(chip->treble)); + v4l2_ctrl_new_std(&chip->hdl, + &tvaudio_ctrl_ops, V4L2_CID_AUDIO_BASS, + 0, 65535, 65535 / 100, + desc->bassinit ? desc->bassinit : 32768); + v4l2_ctrl_new_std(&chip->hdl, + &tvaudio_ctrl_ops, V4L2_CID_AUDIO_TREBLE, + 0, 65535, 65535 / 100, + desc->trebleinit ? desc->trebleinit : 32768); } } + sd->ctrl_handler = &chip->hdl; + if (chip->hdl.error) { + int err = chip->hdl.error; + + v4l2_ctrl_handler_free(&chip->hdl); + kfree(chip); + return err; + } + /* set controls to the default values */ + v4l2_ctrl_handler_setup(&chip->hdl); + chip->thread = NULL; init_timer(&chip->wt); if (desc->flags & CHIP_NEED_CHECKMODE) { @@ -2095,6 +2042,7 @@ static int tvaudio_remove(struct i2c_client *client) } v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&chip->hdl); kfree(chip); return 0; } From f122d9a83e5d1e73f34da9fb832c9b005030b9cb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 11:26:36 -0300 Subject: [PATCH 1939/4607] [media] radio-miropcm20: fix querycap Don't set version (done by the v4l2 core), fill in bus_info, set correct driver name and add device_caps support. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 11f76ed4c6fb..3a89e50ade6c 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -79,11 +79,13 @@ static const struct v4l2_file_operations pcm20_fops = { static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { + struct pcm20 *dev = video_drvdata(file); + strlcpy(v->driver, "Miro PCM20", sizeof(v->driver)); strlcpy(v->card, "Miro PCM20", sizeof(v->card)); - strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); - v->version = 0x1; - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", dev->v4l2_dev.name); + v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -229,7 +231,7 @@ static int __init pcm20_init(void) "you must load the snd-miro driver first!\n"); return -ENODEV; } - strlcpy(v4l2_dev->name, "miropcm20", sizeof(v4l2_dev->name)); + strlcpy(v4l2_dev->name, "radio-miropcm20", sizeof(v4l2_dev->name)); mutex_init(&dev->lock); res = v4l2_device_register(NULL, v4l2_dev); From 1a64b63481f6ad5f2db6ee8f3cad31e882ad8a27 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:49:53 -0300 Subject: [PATCH 1940/4607] [media] radio-miropcm20: remove input/audio ioctls Such ioctls are not valid for radio devices. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 30 --------------------------- 1 file changed, 30 deletions(-) diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 3a89e50ade6c..4b7c1640edb0 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -178,32 +178,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv, return 0; } -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, - const struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} - static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, @@ -213,10 +187,6 @@ static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, }; static int __init pcm20_init(void) From 7f51a6108382dcbf2d5e9a268e86255d72318737 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:50:28 -0300 Subject: [PATCH 1941/4607] [media] radio-miropcm20: convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 66 +++++++++++---------------- 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 4b7c1640edb0..061ebcf4876d 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -17,6 +17,7 @@ #include #include #include +#include #include static int radio_nr = -1; @@ -30,6 +31,7 @@ MODULE_PARM_DESC(mono, "Force tuner into mono mode."); struct pcm20 { struct v4l2_device v4l2_dev; struct video_device vdev; + struct v4l2_ctrl_handler ctrl_handler; unsigned long freq; int muted; struct snd_miro_aci *aci; @@ -138,61 +140,35 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +static int pcm20_s_ctrl(struct v4l2_ctrl *ctrl) { - switch (qc->id) { + struct pcm20 *dev = container_of(ctrl->handler, struct pcm20, ctrl_handler); + + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + pcm20_mute(dev, ctrl->val); + return 0; } return -EINVAL; } -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct pcm20 *dev = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->muted; - break; - default: - return -EINVAL; - } - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct pcm20 *dev = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - pcm20_mute(dev, ctrl->value); - break; - default: - return -EINVAL; - } - return 0; -} - static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, +}; + +static const struct v4l2_ctrl_ops pcm20_ctrl_ops = { + .s_ctrl = pcm20_s_ctrl, }; static int __init pcm20_init(void) { struct pcm20 *dev = &pcm20_card; struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct v4l2_ctrl_handler *hdl; int res; dev->aci = snd_aci_get_aci(); @@ -210,6 +186,16 @@ static int __init pcm20_init(void) return -EINVAL; } + hdl = &dev->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_new_std(hdl, &pcm20_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + v4l2_dev->ctrl_handler = hdl; + if (hdl->error) { + res = hdl->error; + v4l2_err(v4l2_dev, "Could not register control\n"); + goto err_hdl; + } strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); dev->vdev.v4l2_dev = v4l2_dev; dev->vdev.fops = &pcm20_fops; @@ -219,11 +205,12 @@ static int __init pcm20_init(void) video_set_drvdata(&dev->vdev, dev); if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) - goto fail; + goto err_hdl; v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n"); return 0; -fail: +err_hdl: + v4l2_ctrl_handler_free(hdl); v4l2_device_unregister(v4l2_dev); return -EINVAL; } @@ -237,6 +224,7 @@ static void __exit pcm20_cleanup(void) struct pcm20 *dev = &pcm20_card; video_unregister_device(&dev->vdev); + v4l2_ctrl_handler_free(&dev->ctrl_handler); v4l2_device_unregister(&dev->v4l2_dev); } From f7c096f73561ba9754d4b13a6dc38e964194d784 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:54:33 -0300 Subject: [PATCH 1942/4607] [media] radio-miropcm20: add prio and control event support Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 061ebcf4876d..4ac813c5af17 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include static int radio_nr = -1; @@ -75,6 +77,9 @@ static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) static const struct v4l2_file_operations pcm20_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .poll = v4l2_ctrl_poll, + .release = v4l2_fh_release, .unlocked_ioctl = video_ioctl2, }; @@ -158,6 +163,9 @@ static const struct v4l2_ioctl_ops pcm20_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static const struct v4l2_ctrl_ops pcm20_ctrl_ops = { @@ -202,6 +210,7 @@ static int __init pcm20_init(void) dev->vdev.ioctl_ops = &pcm20_ioctl_ops; dev->vdev.release = video_device_release_empty; dev->vdev.lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); video_set_drvdata(&dev->vdev, dev); if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) From f5e7cc4af36c631edf7dc73111d80af975f526a6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 05:55:50 -0300 Subject: [PATCH 1943/4607] [media] radio-miropcm20: Fix audmode/tuner/frequency handling - instead of a mute module option, use audmode as per the spec. - clamp the frequency before setting it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 52 +++++++++++++-------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index 4ac813c5af17..eb6cd86337a8 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -26,44 +26,27 @@ static int radio_nr = -1; module_param(radio_nr, int, 0); MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); -static bool mono; -module_param(mono, bool, 0); -MODULE_PARM_DESC(mono, "Force tuner into mono mode."); - struct pcm20 { struct v4l2_device v4l2_dev; struct video_device vdev; struct v4l2_ctrl_handler ctrl_handler; unsigned long freq; - int muted; + u32 audmode; struct snd_miro_aci *aci; struct mutex lock; }; static struct pcm20 pcm20_card = { - .freq = 87*16000, - .muted = 1, + .freq = 87 * 16000, + .audmode = V4L2_TUNER_MODE_STEREO, }; -static int pcm20_mute(struct pcm20 *dev, unsigned char mute) -{ - dev->muted = mute; - return snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, mute, -1); -} - -static int pcm20_stereo(struct pcm20 *dev, unsigned char stereo) -{ - return snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, !stereo, -1); -} - static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) { unsigned char freql; unsigned char freqh; struct snd_miro_aci *aci = dev->aci; - dev->freq = freq; - freq /= 160; if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0)) freq /= 10; /* I don't know exactly which version @@ -71,7 +54,6 @@ static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) freql = freq & 0xff; freqh = freq >> 8; - pcm20_stereo(dev, !mono); return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh); } @@ -99,7 +81,9 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - if (v->index) /* Only 1 tuner */ + struct pcm20 *dev = video_drvdata(file); + + if (v->index) return -EINVAL; strlcpy(v->name, "FM", sizeof(v->name)); v->type = V4L2_TUNER_RADIO; @@ -107,15 +91,23 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->rangehigh = 108*16000; v->signal = 0xffff; v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + v->audmode = dev->audmode; return 0; } static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - return v->index ? -EINVAL : 0; + struct pcm20 *dev = video_drvdata(file); + + if (v->index) + return -EINVAL; + if (v->audmode > V4L2_TUNER_MODE_STEREO) + v->audmode = V4L2_TUNER_MODE_STEREO; + snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, + v->audmode == V4L2_TUNER_MODE_MONO, -1); + return 0; } static int vidioc_g_frequency(struct file *file, void *priv, @@ -140,8 +132,8 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) return -EINVAL; - dev->freq = f->frequency; - pcm20_setfreq(dev, f->frequency); + dev->freq = clamp(f->frequency, 87 * 16000U, 108 * 16000U); + pcm20_setfreq(dev, dev->freq); return 0; } @@ -151,7 +143,7 @@ static int pcm20_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - pcm20_mute(dev, ctrl->val); + snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, ctrl->val, -1); return 0; } return -EINVAL; @@ -212,6 +204,9 @@ static int __init pcm20_init(void) dev->vdev.lock = &dev->lock; set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); video_set_drvdata(&dev->vdev, dev); + snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, + dev->audmode == V4L2_TUNER_MODE_MONO, -1); + pcm20_setfreq(dev, dev->freq); if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) goto err_hdl; @@ -233,6 +228,7 @@ static void __exit pcm20_cleanup(void) struct pcm20 *dev = &pcm20_card; video_unregister_device(&dev->vdev); + snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, 1, -1); v4l2_ctrl_handler_free(&dev->ctrl_handler); v4l2_device_unregister(&dev->v4l2_dev); } From 9bbc5820ffe1675c923ed0b82952cc64a610bd5d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 28 May 2012 11:10:01 -0300 Subject: [PATCH 1944/4607] [media] radio-miropcm20: fix signal and stereo indication Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-miropcm20.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c index eb6cd86337a8..3d0ff4404d12 100644 --- a/drivers/media/radio/radio-miropcm20.c +++ b/drivers/media/radio/radio-miropcm20.c @@ -82,6 +82,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { struct pcm20 *dev = video_drvdata(file); + int res; if (v->index) return -EINVAL; @@ -89,8 +90,13 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->type = V4L2_TUNER_RADIO; v->rangelow = 87*16000; v->rangehigh = 108*16000; - v->signal = 0xffff; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + res = snd_aci_cmd(dev->aci, ACI_READ_TUNERSTATION, -1, -1); + v->signal = (res & 0x80) ? 0 : 0xffff; + /* Note: stereo detection does not work if the audio is muted, + it will default to mono in that case. */ + res = snd_aci_cmd(dev->aci, ACI_READ_TUNERSTEREO, -1, -1); + v->rxsubchans = (res & 0x40) ? V4L2_TUNER_SUB_MONO : + V4L2_TUNER_SUB_STEREO; v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; v->audmode = dev->audmode; return 0; From 11d379395291609f95cf1e102f9f23892b83a493 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 11:59:48 -0300 Subject: [PATCH 1945/4607] [media] bw-qcam: zero priv field Fix a v4l2-compliance problem: the priv field of v4l2_pix_format must be cleared by drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/parport/bw-qcam.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c index 5b75a64b199b..497b342b0f72 100644 --- a/drivers/media/parport/bw-qcam.c +++ b/drivers/media/parport/bw-qcam.c @@ -693,6 +693,7 @@ static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f pix->sizeimage = pix->width * pix->height; /* Just a guess */ pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->priv = 0; return 0; } @@ -718,6 +719,7 @@ static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format pix->sizeimage = pix->width * pix->height; /* Just a guess */ pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->priv = 0; return 0; } From 1888e4a9742087df22ba64ea2e0e1064edeb8785 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 14:10:14 -0300 Subject: [PATCH 1946/4607] [media] bw-qcam: convert to videobuf2 I know, nobody really cares about this black-and-white webcam anymore, but it was fun to do. Tested with an actual webcam. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/parport/Kconfig | 1 + drivers/media/parport/bw-qcam.c | 157 ++++++++++++++++++++++---------- 2 files changed, 112 insertions(+), 46 deletions(-) diff --git a/drivers/media/parport/Kconfig b/drivers/media/parport/Kconfig index ece13dcff07d..948c981d9f05 100644 --- a/drivers/media/parport/Kconfig +++ b/drivers/media/parport/Kconfig @@ -9,6 +9,7 @@ if MEDIA_PARPORT_SUPPORT config VIDEO_BWQCAM tristate "Quickcam BW Video For Linux" depends on PARPORT && VIDEO_V4L2 + select VIDEOBUF2_VMALLOC help Say Y have if you the black and white version of the QuickCam camera. See the next option for the color version. diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c index 497b342b0f72..d3fe34f14c0d 100644 --- a/drivers/media/parport/bw-qcam.c +++ b/drivers/media/parport/bw-qcam.c @@ -80,6 +80,7 @@ OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include /* One from column A... */ #define QC_NOTSET 0 @@ -107,9 +108,11 @@ struct qcam { struct v4l2_device v4l2_dev; struct video_device vdev; struct v4l2_ctrl_handler hdl; + struct vb2_queue vb_vidq; struct pardevice *pdev; struct parport *pport; struct mutex lock; + struct mutex queue_lock; int width, height; int bpp; int mode; @@ -558,7 +561,7 @@ static inline int qc_readbytes(struct qcam *q, char buffer[]) * n=2^(bit depth)-1. Ask me for more details if you don't understand * this. */ -static long qc_capture(struct qcam *q, char __user *buf, unsigned long len) +static long qc_capture(struct qcam *q, u8 *buf, unsigned long len) { int i, j, k, yield; int bytes; @@ -609,7 +612,7 @@ static long qc_capture(struct qcam *q, char __user *buf, unsigned long len) if (o < len) { u8 ch = invert - buffer[k]; got++; - put_user(ch << shift, buf + o); + buf[o] = ch << shift; } } pixels_read += bytes; @@ -639,6 +642,67 @@ static long qc_capture(struct qcam *q, char __user *buf, unsigned long len) return len; } +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct qcam *dev = vb2_get_drv_priv(vq); + + if (0 == *nbuffers) + *nbuffers = 3; + *nplanes = 1; + mutex_lock(&dev->lock); + if (fmt) + sizes[0] = fmt->fmt.pix.width * fmt->fmt.pix.height; + else + sizes[0] = (dev->width / dev->transfer_scale) * + (dev->height / dev->transfer_scale); + mutex_unlock(&dev->lock); + return 0; +} + +static void buffer_queue(struct vb2_buffer *vb) +{ + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); +} + +static int buffer_finish(struct vb2_buffer *vb) +{ + struct qcam *qcam = vb2_get_drv_priv(vb->vb2_queue); + void *vbuf = vb2_plane_vaddr(vb, 0); + int size = vb->vb2_queue->plane_sizes[0]; + int len; + + mutex_lock(&qcam->lock); + parport_claim_or_block(qcam->pdev); + + qc_reset(qcam); + + /* Update the camera parameters if we need to */ + if (qcam->status & QC_PARAM_CHANGE) + qc_set(qcam); + + len = qc_capture(qcam, vbuf, size); + + parport_release(qcam->pdev); + mutex_unlock(&qcam->lock); + if (len != size) + vb->state = VB2_BUF_STATE_ERROR; + vb2_set_plane_payload(vb, 0, len); + return 0; +} + +static struct vb2_ops qcam_video_qops = { + .queue_setup = queue_setup, + .buf_queue = buffer_queue, + .buf_finish = buffer_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + /* * Video4linux interfacing */ @@ -651,7 +715,8 @@ static int qcam_querycap(struct file *file, void *priv, strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver)); strlcpy(vcap->card, "Connectix B&W Quickcam", sizeof(vcap->card)); strlcpy(vcap->bus_info, qcam->pport->name, sizeof(vcap->bus_info)); - vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -731,6 +796,8 @@ static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f if (ret) return ret; + if (vb2_is_busy(&qcam->vb_vidq)) + return -EBUSY; qcam->width = 320; qcam->height = 240; if (pix->height == 60) @@ -744,12 +811,10 @@ static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f else qcam->bpp = 4; - mutex_lock(&qcam->lock); qc_setscanmode(qcam); /* We must update the camera before we grab. We could just have changed the grab size */ qcam->status |= QC_PARAM_CHANGE; - mutex_unlock(&qcam->lock); return 0; } @@ -794,41 +859,12 @@ static int qcam_enum_framesizes(struct file *file, void *fh, return 0; } -static ssize_t qcam_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct qcam *qcam = video_drvdata(file); - int len; - parport_claim_or_block(qcam->pdev); - - mutex_lock(&qcam->lock); - - qc_reset(qcam); - - /* Update the camera parameters if we need to */ - if (qcam->status & QC_PARAM_CHANGE) - qc_set(qcam); - - len = qc_capture(qcam, buf, count); - - mutex_unlock(&qcam->lock); - - parport_release(qcam->pdev); - return len; -} - -static unsigned int qcam_poll(struct file *filp, poll_table *wait) -{ - return v4l2_ctrl_poll(filp, wait) | POLLIN | POLLRDNORM; -} - static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) { struct qcam *qcam = container_of(ctrl->handler, struct qcam, hdl); int ret = 0; - mutex_lock(&qcam->lock); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: qcam->brightness = ctrl->val; @@ -847,17 +883,17 @@ static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) qc_setscanmode(qcam); qcam->status |= QC_PARAM_CHANGE; } - mutex_unlock(&qcam->lock); return ret; } static const struct v4l2_file_operations qcam_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, - .release = v4l2_fh_release, - .poll = qcam_poll, + .release = vb2_fop_release, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, - .read = qcam_read, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, }; static const struct v4l2_ioctl_ops qcam_ioctl_ops = { @@ -870,6 +906,14 @@ static const struct v4l2_ioctl_ops qcam_ioctl_ops = { .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, @@ -886,6 +930,8 @@ static struct qcam *qcam_init(struct parport *port) { struct qcam *qcam; struct v4l2_device *v4l2_dev; + struct vb2_queue *q; + int err; qcam = kzalloc(sizeof(struct qcam), GFP_KERNEL); if (qcam == NULL) @@ -909,31 +955,45 @@ static struct qcam *qcam_init(struct parport *port) V4L2_CID_GAMMA, 0, 255, 1, 105); if (qcam->hdl.error) { v4l2_err(v4l2_dev, "couldn't register controls\n"); - v4l2_ctrl_handler_free(&qcam->hdl); - kfree(qcam); - return NULL; + goto exit; } + + mutex_init(&qcam->lock); + mutex_init(&qcam->queue_lock); + + /* initialize queue */ + q = &qcam->vb_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + q->drv_priv = qcam; + q->ops = &qcam_video_qops; + q->mem_ops = &vb2_vmalloc_memops; + err = vb2_queue_init(q); + if (err < 0) { + v4l2_err(v4l2_dev, "couldn't init vb2_queue for %s.\n", port->name); + goto exit; + } + qcam->vdev.queue = q; + qcam->vdev.queue->lock = &qcam->queue_lock; + qcam->pport = port; qcam->pdev = parport_register_device(port, v4l2_dev->name, NULL, NULL, NULL, 0, NULL); if (qcam->pdev == NULL) { v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name); - v4l2_ctrl_handler_free(&qcam->hdl); - kfree(qcam); - return NULL; + goto exit; } strlcpy(qcam->vdev.name, "Connectix QuickCam", sizeof(qcam->vdev.name)); qcam->vdev.v4l2_dev = v4l2_dev; qcam->vdev.ctrl_handler = &qcam->hdl; qcam->vdev.fops = &qcam_fops; + qcam->vdev.lock = &qcam->lock; qcam->vdev.ioctl_ops = &qcam_ioctl_ops; set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags); qcam->vdev.release = video_device_release_empty; video_set_drvdata(&qcam->vdev, qcam); - mutex_init(&qcam->lock); - qcam->port_mode = (QC_ANY | QC_NOTSET); qcam->width = 320; qcam->height = 240; @@ -947,6 +1007,11 @@ static struct qcam *qcam_init(struct parport *port) qcam->mode = -1; qcam->status = QC_PARAM_CHANGE; return qcam; + +exit: + v4l2_ctrl_handler_free(&qcam->hdl); + kfree(qcam); + return NULL; } static int qc_calibrate(struct qcam *q) From 971dfc678114d61c07bd6f8ff8380558b6e12d5d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 14:07:38 -0300 Subject: [PATCH 1947/4607] [media] bw-qcam: remove unnecessary qc_reset and qc_setscanmode calls These are already done elsewhere. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/parport/bw-qcam.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c index d3fe34f14c0d..06231b85e1a9 100644 --- a/drivers/media/parport/bw-qcam.c +++ b/drivers/media/parport/bw-qcam.c @@ -421,8 +421,6 @@ static void qc_set(struct qcam *q) int val; int val2; - qc_reset(q); - /* Set the brightness. Yes, this is repetitive, but it works. * Shorter versions seem to fail subtly. Feel free to try :-). */ /* I think the problem was in qc_command, not here -- bls */ @@ -879,10 +877,8 @@ static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) ret = -EINVAL; break; } - if (ret == 0) { - qc_setscanmode(qcam); + if (ret == 0) qcam->status |= QC_PARAM_CHANGE; - } return ret; } From cd13823f5db3e66552801c04f0e761408ef17eb0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 30 Jan 2013 13:29:02 -0300 Subject: [PATCH 1948/4607] [media] videobuf2: don't return POLLERR when only polling for events If you only poll for events, then vb2_poll will return POLLPRI | POLLERR if no streaming is in progress. That's not right, it's perfectly valid to poll just for events. Cc: Pawel Osciak Cc: Marek Szyprowski Cc: Kyungmin Park Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index d09be38dd377..db1235dcb328 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1965,6 +1965,11 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) poll_wait(file, &fh->wait, wait); } + if (!V4L2_TYPE_IS_OUTPUT(q->type) && !(req_events & (POLLIN | POLLRDNORM))) + return res; + if (V4L2_TYPE_IS_OUTPUT(q->type) && !(req_events & (POLLOUT | POLLWRNORM))) + return res; + /* * Start file I/O emulator only if streaming API has not been used yet. */ From ed986d1fee77bbbb62291a1db1c7edbb00d99515 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 29 Jan 2013 07:21:02 -0300 Subject: [PATCH 1949/4607] [media] meye: convert to the control framework Convert the meye driver to the control framework. Some private controls have been replaced with standardized controls (SHARPNESS and JPEGQUAL). The AGC control looks like it can be replaced by the AUTOGAIN control, but it isn't a boolean so I do not know how to interpret it. The FRAMERATE control looks like it can be replaced by S_PARM, but again, without knowing how to interpret it I decided to leave it alone. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/meye/meye.c | 276 +++++++++-------------------- drivers/media/pci/meye/meye.h | 2 + include/uapi/linux/meye.h | 8 +- include/uapi/linux/v4l2-controls.h | 5 + 4 files changed, 98 insertions(+), 193 deletions(-) diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c index 3b39deaa8f6b..7859c43479d7 100644 --- a/drivers/media/pci/meye/meye.c +++ b/drivers/media/pci/meye/meye.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -865,7 +867,7 @@ static int meye_open(struct file *file) meye.grab_buffer[i].state = MEYE_BUF_UNUSED; kfifo_reset(&meye.grabq); kfifo_reset(&meye.doneq); - return 0; + return v4l2_fh_open(file); } static int meye_release(struct file *file) @@ -873,7 +875,7 @@ static int meye_release(struct file *file) mchip_hic_stop(); mchip_dma_free(); clear_bit(0, &meye.in_use); - return 0; + return v4l2_fh_release(file); } static int meyeioc_g_params(struct meye_params *p) @@ -1032,8 +1034,9 @@ static int vidioc_querycap(struct file *file, void *fh, cap->version = (MEYE_DRIVER_MAJORVERSION << 8) + MEYE_DRIVER_MINORVERSION; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1063,191 +1066,50 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int i) return 0; } -static int vidioc_queryctrl(struct file *file, void *fh, - struct v4l2_queryctrl *c) -{ - switch (c->id) { - - case V4L2_CID_BRIGHTNESS: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Brightness"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_HUE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Hue"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_CONTRAST: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Contrast"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_SATURATION: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Saturation"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_AGC: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Agc"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 48; - c->flags = 0; - break; - case V4L2_CID_MEYE_SHARPNESS: - case V4L2_CID_SHARPNESS: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Sharpness"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - - /* Continue to report legacy private SHARPNESS ctrl but - * say it is disabled in preference to ctrl in the spec - */ - c->flags = (c->id == V4L2_CID_SHARPNESS) ? 0 : - V4L2_CTRL_FLAG_DISABLED; - break; - case V4L2_CID_PICTURE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Picture"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 0; - c->flags = 0; - break; - case V4L2_CID_JPEGQUAL: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "JPEG quality"); - c->minimum = 0; - c->maximum = 10; - c->step = 1; - c->default_value = 8; - c->flags = 0; - break; - case V4L2_CID_FRAMERATE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Framerate"); - c->minimum = 0; - c->maximum = 31; - c->step = 1; - c->default_value = 0; - c->flags = 0; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) +static int meye_s_ctrl(struct v4l2_ctrl *ctrl) { mutex_lock(&meye.lock); - switch (c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); - meye.brightness = c->value << 10; + SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, ctrl->val); + meye.brightness = ctrl->val << 10; break; case V4L2_CID_HUE: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAHUE, c->value); - meye.hue = c->value << 10; + SONY_PIC_COMMAND_SETCAMERAHUE, ctrl->val); + meye.hue = ctrl->val << 10; break; case V4L2_CID_CONTRAST: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); - meye.contrast = c->value << 10; + SONY_PIC_COMMAND_SETCAMERACONTRAST, ctrl->val); + meye.contrast = ctrl->val << 10; break; case V4L2_CID_SATURATION: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); - meye.colour = c->value << 10; + SONY_PIC_COMMAND_SETCAMERACOLOR, ctrl->val); + meye.colour = ctrl->val << 10; break; - case V4L2_CID_AGC: + case V4L2_CID_MEYE_AGC: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAAGC, c->value); - meye.params.agc = c->value; + SONY_PIC_COMMAND_SETCAMERAAGC, ctrl->val); + meye.params.agc = ctrl->val; break; case V4L2_CID_SHARPNESS: - case V4L2_CID_MEYE_SHARPNESS: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value); - meye.params.sharpness = c->value; + SONY_PIC_COMMAND_SETCAMERASHARPNESS, ctrl->val); + meye.params.sharpness = ctrl->val; break; - case V4L2_CID_PICTURE: + case V4L2_CID_MEYE_PICTURE: sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value); - meye.params.picture = c->value; + SONY_PIC_COMMAND_SETCAMERAPICTURE, ctrl->val); + meye.params.picture = ctrl->val; break; - case V4L2_CID_JPEGQUAL: - meye.params.quality = c->value; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + meye.params.quality = ctrl->val; break; - case V4L2_CID_FRAMERATE: - meye.params.framerate = c->value; - break; - default: - mutex_unlock(&meye.lock); - return -EINVAL; - } - mutex_unlock(&meye.lock); - - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) -{ - mutex_lock(&meye.lock); - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = meye.brightness >> 10; - break; - case V4L2_CID_HUE: - c->value = meye.hue >> 10; - break; - case V4L2_CID_CONTRAST: - c->value = meye.contrast >> 10; - break; - case V4L2_CID_SATURATION: - c->value = meye.colour >> 10; - break; - case V4L2_CID_AGC: - c->value = meye.params.agc; - break; - case V4L2_CID_SHARPNESS: - case V4L2_CID_MEYE_SHARPNESS: - c->value = meye.params.sharpness; - break; - case V4L2_CID_PICTURE: - c->value = meye.params.picture; - break; - case V4L2_CID_JPEGQUAL: - c->value = meye.params.quality; - break; - case V4L2_CID_FRAMERATE: - c->value = meye.params.framerate; + case V4L2_CID_MEYE_FRAMERATE: + meye.params.framerate = ctrl->val; break; default: mutex_unlock(&meye.lock); @@ -1577,12 +1439,12 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio, static unsigned int meye_poll(struct file *file, poll_table *wait) { - unsigned int res = 0; + unsigned int res = v4l2_ctrl_poll(file, wait); mutex_lock(&meye.lock); poll_wait(file, &meye.proc_list, wait); if (kfifo_len(&meye.doneq)) - res = POLLIN | POLLRDNORM; + res |= POLLIN | POLLRDNORM; mutex_unlock(&meye.lock); return res; } @@ -1669,9 +1531,6 @@ static const struct v4l2_ioctl_ops meye_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, @@ -1682,6 +1541,9 @@ static const struct v4l2_ioctl_ops meye_ioctl_ops = { .vidioc_dqbuf = vidioc_dqbuf, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, .vidioc_default = vidioc_default, }; @@ -1692,6 +1554,10 @@ static struct video_device meye_template = { .release = video_device_release, }; +static const struct v4l2_ctrl_ops meye_ctrl_ops = { + .s_ctrl = meye_s_ctrl, +}; + #ifdef CONFIG_PM static int meye_suspend(struct pci_dev *pdev, pm_message_t state) { @@ -1730,6 +1596,32 @@ static int meye_resume(struct pci_dev *pdev) static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) { + static const struct v4l2_ctrl_config ctrl_agc = { + .id = V4L2_CID_MEYE_AGC, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "AGC", + .max = 63, + .step = 1, + .def = 48, + .flags = V4L2_CTRL_FLAG_SLIDER, + }; + static const struct v4l2_ctrl_config ctrl_picture = { + .id = V4L2_CID_MEYE_PICTURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "Picture", + .max = 63, + .step = 1, + }; + static const struct v4l2_ctrl_config ctrl_framerate = { + .id = V4L2_CID_MEYE_FRAMERATE, + .type = V4L2_CTRL_TYPE_INTEGER, + .ops = &meye_ctrl_ops, + .name = "Framerate", + .max = 31, + .step = 1, + }; struct v4l2_device *v4l2_dev = &meye.v4l2_dev; int ret = -EBUSY; unsigned long mchip_adr; @@ -1833,24 +1725,31 @@ static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) mutex_init(&meye.lock); init_waitqueue_head(&meye.proc_list); - meye.brightness = 32 << 10; - meye.hue = 32 << 10; - meye.colour = 32 << 10; - meye.contrast = 32 << 10; - meye.params.subsample = 0; - meye.params.quality = 8; - meye.params.sharpness = 32; - meye.params.agc = 48; - meye.params.picture = 0; - meye.params.framerate = 0; - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, 32); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, 32); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, 32); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, 32); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, 32); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48); + v4l2_ctrl_handler_init(&meye.hdl, 3); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_HUE, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_CONTRAST, 0, 63, 1, 32); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_SATURATION, 0, 63, 1, 32); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_agc, NULL); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 63, 1, 32); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_picture, NULL); + v4l2_ctrl_new_std(&meye.hdl, &meye_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 0, 10, 1, 8); + v4l2_ctrl_new_custom(&meye.hdl, &ctrl_framerate, NULL); + if (meye.hdl.error) { + v4l2_err(v4l2_dev, "couldn't register controls\n"); + goto outvideoreg; + } + + v4l2_ctrl_handler_setup(&meye.hdl); + meye.vdev->ctrl_handler = &meye.hdl; + set_bit(V4L2_FL_USE_FH_PRIO, &meye.vdev->flags); if (video_register_device(meye.vdev, VFL_TYPE_GRABBER, video_nr) < 0) { @@ -1866,6 +1765,7 @@ static int meye_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) return 0; outvideoreg: + v4l2_ctrl_handler_free(&meye.hdl); free_irq(meye.mchip_irq, meye_irq); outreqirq: iounmap(meye.mchip_mmregs); diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h index 4bdeb03f1644..6fed9274cfa5 100644 --- a/drivers/media/pci/meye/meye.h +++ b/drivers/media/pci/meye/meye.h @@ -39,6 +39,7 @@ #include #include #include +#include /****************************************************************************/ /* Motion JPEG chip registers */ @@ -290,6 +291,7 @@ struct meye_grab_buffer { /* Motion Eye device structure */ struct meye { struct v4l2_device v4l2_dev; /* Main v4l2_device struct */ + struct v4l2_ctrl_handler hdl; struct pci_dev *mchip_dev; /* pci device */ u8 mchip_irq; /* irq */ u8 mchip_mode; /* actual mchip mode: HIC_MODE... */ diff --git a/include/uapi/linux/meye.h b/include/uapi/linux/meye.h index 0dd49954f746..8ff50fe9e481 100644 --- a/include/uapi/linux/meye.h +++ b/include/uapi/linux/meye.h @@ -57,10 +57,8 @@ struct meye_params { #define MEYEIOC_STILLJCAPT _IOR ('v', BASE_VIDIOC_PRIVATE+5, int) /* V4L2 private controls */ -#define V4L2_CID_AGC V4L2_CID_PRIVATE_BASE -#define V4L2_CID_MEYE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 1) -#define V4L2_CID_PICTURE (V4L2_CID_PRIVATE_BASE + 2) -#define V4L2_CID_JPEGQUAL (V4L2_CID_PRIVATE_BASE + 3) -#define V4L2_CID_FRAMERATE (V4L2_CID_PRIVATE_BASE + 4) +#define V4L2_CID_MEYE_AGC (V4L2_CID_USER_MEYE_BASE + 0) +#define V4L2_CID_MEYE_PICTURE (V4L2_CID_USER_MEYE_BASE + 1) +#define V4L2_CID_MEYE_FRAMERATE (V4L2_CID_USER_MEYE_BASE + 2) #endif diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 0bece06792d7..dcd63745e83a 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -140,6 +140,11 @@ enum v4l2_colorfx { /* last CID + 1 */ #define V4L2_CID_LASTP1 (V4L2_CID_BASE+43) +/* USER-class private control IDs */ + +/* The base for the meye driver controls. See linux/meye.h for the list + * of controls. We reserve 16 controls for this driver. */ +#define V4L2_CID_USER_MEYE_BASE (V4L2_CID_USER_BASE + 0x1000) /* MPEG-class control IDs */ From 2c2a053626cb712d6006cb10f2760a6018a65631 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Sep 2012 07:35:30 -0300 Subject: [PATCH 1950/4607] [media] tm6000: fix querycap and input/tuner compliance issues - add device_caps support - fix bus_info - fix numerous tuner-related problems due to incorrect tests and setting v4l2_tuner fields to wrong values. - remove (audio) input support from the radio: it doesn't belong there. This also fixed a nasty issue where opening the radio would set dev->input to 5 for no good reason. This was never set back to a valid TV input after closing the radio device, thus leaving it at 5 which is out of bounds of the vinput card array. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 147 +++++------------------- 1 file changed, 31 insertions(+), 116 deletions(-) diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index e3c567c27918..7a653b263678 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -948,16 +948,21 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + struct video_device *vdev = video_devdata(file); strlcpy(cap->driver, "tm6000", sizeof(cap->driver)); strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE; - + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; + cap->device_caps |= V4L2_CAP_TUNER; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + else + cap->device_caps |= V4L2_CAP_RADIO; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | + V4L2_CAP_RADIO | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; return 0; } @@ -965,7 +970,7 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - if (unlikely(f->index >= ARRAY_SIZE(format))) + if (f->index >= ARRAY_SIZE(format)) return -EINVAL; strlcpy(f->description, format[f->index].name, sizeof(f->description)); @@ -1301,14 +1306,14 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct tm6000_fh *fh = priv; struct tm6000_core *dev = fh->dev; - if (unlikely(UNSET == dev->tuner_type)) - return -EINVAL; + if (UNSET == dev->tuner_type) + return -ENOTTY; if (0 != t->index) return -EINVAL; strcpy(t->name, "Television"); t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO; t->rangehigh = 0xffffffffUL; t->rxsubchans = V4L2_TUNER_SUB_STEREO; @@ -1326,11 +1331,14 @@ static int vidioc_s_tuner(struct file *file, void *priv, struct tm6000_core *dev = fh->dev; if (UNSET == dev->tuner_type) - return -EINVAL; + return -ENOTTY; if (0 != t->index) return -EINVAL; - dev->amode = t->audmode; + if (t->audmode > V4L2_TUNER_MODE_STEREO) + dev->amode = V4L2_TUNER_MODE_STEREO; + else + dev->amode = t->audmode; dprintk(dev, 3, "audio mode: %x\n", t->audmode); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); @@ -1344,10 +1352,11 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct tm6000_fh *fh = priv; struct tm6000_core *dev = fh->dev; - if (unlikely(UNSET == dev->tuner_type)) + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (f->tuner) return -EINVAL; - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); @@ -1361,13 +1370,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, struct tm6000_fh *fh = priv; struct tm6000_core *dev = fh->dev; - if (unlikely(UNSET == dev->tuner_type)) - return -EINVAL; - if (unlikely(f->tuner != 0)) - return -EINVAL; - if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) - return -EINVAL; - if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) + if (UNSET == dev->tuner_type) + return -ENOTTY; + if (f->tuner != 0) return -EINVAL; dev->freq = f->frequency; @@ -1376,27 +1381,6 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct tm6000_fh *fh = file->private_data; - struct tm6000_core *dev = fh->dev; - - strcpy(cap->driver, "tm6000"); - strlcpy(cap->card, dev->name, sizeof(dev->name)); - sprintf(cap->bus_info, "USB%04x:%04x", - le16_to_cpu(dev->udev->descriptor.idVendor), - le16_to_cpu(dev->udev->descriptor.idProduct)); - cap->version = dev->dev_type; - cap->capabilities = V4L2_CAP_TUNER | - V4L2_CAP_AUDIO | - V4L2_CAP_RADIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - - return 0; -} - static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1409,7 +1393,9 @@ static int radio_g_tuner(struct file *file, void *priv, memset(t, 0, sizeof(*t)); strcpy(t->name, "Radio"); t->type = V4L2_TUNER_RADIO; + t->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; t->rxsubchans = V4L2_TUNER_SUB_STEREO; + t->audmode = V4L2_TUNER_MODE_STEREO; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); @@ -1424,78 +1410,14 @@ static int radio_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; + if (t->audmode > V4L2_TUNER_MODE_STEREO) + t->audmode = V4L2_TUNER_MODE_STEREO; v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); return 0; } -static int radio_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (i->index != 0) - return -EINVAL; - - if (!dev->rinput.type) - return -EINVAL; - - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} - -static int radio_g_input(struct file *filp, void *priv, unsigned int *i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (dev->input != 5) - return -EINVAL; - - *i = dev->input - 5; - - return 0; -} - -static int radio_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - memset(a, 0, sizeof(*a)); - strcpy(a->name, "Radio"); - return 0; -} - -static int radio_s_audio(struct file *file, void *priv, - const struct v4l2_audio *a) -{ - return 0; -} - -static int radio_s_input(struct file *filp, void *priv, unsigned int i) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - - if (i) - return -EINVAL; - - if (!dev->rinput.type) - return -EINVAL; - - dev->input = i + 5; - - return 0; -} - -static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) -{ - return 0; -} - static int radio_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c) { @@ -1599,7 +1521,6 @@ static int __tm6000_open(struct file *file) sizeof(struct tm6000_buffer), fh, &dev->lock); } else { dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n"); - dev->input = 5; tm6000_set_audio_rinput(dev); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); tm6000_prepare_isoc(dev); @@ -1789,16 +1710,10 @@ static const struct v4l2_file_operations radio_fops = { }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, + .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, - .vidioc_s_std = radio_s_std, .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_input = radio_g_input, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, From 9f7473592bd2c8d73657dcc1644de4ab610b0d90 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 31 Jan 2013 08:23:01 -0300 Subject: [PATCH 1951/4607] [media] tm6000: convert to the control framework Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 236 +++++++----------------- drivers/media/usb/tm6000/tm6000.h | 3 + 2 files changed, 70 insertions(+), 169 deletions(-) diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 7a653b263678..4329fbcf2de2 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -63,71 +63,6 @@ static bool keep_urb; /* keep urb buffers allocated */ int tm6000_debug; EXPORT_SYMBOL_GPL(tm6000_debug); -static const struct v4l2_queryctrl no_ctrl = { - .name = "42", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; - -/* supported controls */ -static struct v4l2_queryctrl tm6000_qctrl[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 54, - .flags = 0, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 119, - .flags = 0, - }, { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 112, - .flags = 0, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -128, - .maximum = 127, - .step = 0x1, - .default_value = 0, - .flags = 0, - }, - /* --- audio --- */ - { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, { - .id = V4L2_CID_AUDIO_VOLUME, - .name = "Volume", - .minimum = -15, - .maximum = 15, - .step = 1, - .default_value = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - } -}; - -static const unsigned int CTRLS = ARRAY_SIZE(tm6000_qctrl); -static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)]; - static struct tm6000_fmt format[] = { { .name = "4:2:2, packed, YVY2", @@ -144,16 +79,6 @@ static struct tm6000_fmt format[] = { } }; -static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) -{ - unsigned int i; - - for (i = 0; i < CTRLS; i++) - if (tm6000_qctrl[i].id == id) - return tm6000_qctrl+i; - return NULL; -} - /* ------------------------------------------------------------------ * DMA and thread functions * ------------------------------------------------------------------ @@ -1215,65 +1140,11 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) } /* --- controls ---------------------------------------------- */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) + +static int tm6000_s_ctrl(struct v4l2_ctrl *ctrl) { - int i; - - for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) - if (qc->id && qc->id == tm6000_qctrl[i].id) { - memcpy(qc, &(tm6000_qctrl[i]), - sizeof(*qc)); - return 0; - } - - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - int val; - - /* FIXME: Probably, those won't work! Maybe we need shadow regs */ - switch (ctrl->id) { - case V4L2_CID_CONTRAST: - val = tm6000_get_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0); - break; - case V4L2_CID_BRIGHTNESS: - val = tm6000_get_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0); - return 0; - case V4L2_CID_SATURATION: - val = tm6000_get_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0); - return 0; - case V4L2_CID_HUE: - val = tm6000_get_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, 0); - return 0; - case V4L2_CID_AUDIO_MUTE: - val = dev->ctl_mute; - return 0; - case V4L2_CID_AUDIO_VOLUME: - val = dev->ctl_volume; - return 0; - default: - return -EINVAL; - } - - if (val < 0) - return val; - - ctrl->value = val; - - return 0; -} -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct tm6000_fh *fh = priv; - struct tm6000_core *dev = fh->dev; - u8 val = ctrl->value; + struct tm6000_core *dev = container_of(ctrl->handler, struct tm6000_core, ctrl_handler); + u8 val = ctrl->val; switch (ctrl->id) { case V4L2_CID_CONTRAST: @@ -1288,6 +1159,21 @@ static int vidioc_s_ctrl(struct file *file, void *priv, case V4L2_CID_HUE: tm6000_set_reg(dev, TM6010_REQ07_R0B_CHROMA_HUE_PHASE_ADJ, val); return 0; + } + return -EINVAL; +} + +static const struct v4l2_ctrl_ops tm6000_ctrl_ops = { + .s_ctrl = tm6000_s_ctrl, +}; + +static int tm6000_radio_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct tm6000_core *dev = container_of(ctrl->handler, + struct tm6000_core, radio_ctrl_handler); + u8 val = ctrl->val; + + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: dev->ctl_mute = val; tm6000_tvaudio_set_mute(dev, val); @@ -1300,6 +1186,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv, return -EINVAL; } +static const struct v4l2_ctrl_ops tm6000_radio_ctrl_ops = { + .s_ctrl = tm6000_radio_s_ctrl, +}; + static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -1418,23 +1308,6 @@ static int radio_s_tuner(struct file *file, void *priv, return 0; } -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *c) -{ - const struct v4l2_queryctrl *ctrl; - - if (c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) - return -EINVAL; - if (c->id == V4L2_CID_AUDIO_MUTE) { - ctrl = ctrl_by_id(c->id); - *c = *ctrl; - } else - *c = no_ctrl; - - return 0; -} - /* ------------------------------------------------------------------ File operations for the device ------------------------------------------------------------------*/ @@ -1445,7 +1318,7 @@ static int __tm6000_open(struct file *file) struct tm6000_core *dev = video_drvdata(file); struct tm6000_fh *fh; enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - int i, rc; + int rc; int radio = 0; dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called (dev=%s)\n", @@ -1505,13 +1378,7 @@ static int __tm6000_open(struct file *file) if (rc < 0) return rc; - if (dev->mode != TM6000_MODE_ANALOG) { - /* Put all controls at a sane state */ - for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) - qctl_regs[i] = tm6000_qctrl[i].default_value; - - dev->mode = TM6000_MODE_ANALOG; - } + dev->mode = TM6000_MODE_ANALOG; if (!fh->radio) { videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops, @@ -1678,9 +1545,6 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, @@ -1713,9 +1577,6 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, .vidioc_s_tuner = radio_s_tuner, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, }; @@ -1755,15 +1616,41 @@ static struct video_device *vdev_init(struct tm6000_core *dev, int tm6000_v4l2_register(struct tm6000_core *dev) { - int ret = -1; + int ret = 0; + + v4l2_ctrl_handler_init(&dev->ctrl_handler, 6); + v4l2_ctrl_handler_init(&dev->radio_ctrl_handler, 2); + v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); + v4l2_ctrl_new_std(&dev->radio_ctrl_handler, &tm6000_radio_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, -15, 15, 1, 0); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 54); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 119); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 112); + v4l2_ctrl_new_std(&dev->ctrl_handler, &tm6000_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + v4l2_ctrl_add_handler(&dev->ctrl_handler, + &dev->radio_ctrl_handler, NULL); + + if (dev->radio_ctrl_handler.error) + ret = dev->radio_ctrl_handler.error; + if (!ret && dev->ctrl_handler.error) + ret = dev->ctrl_handler.error; + if (ret) + goto free_ctrl; dev->vfd = vdev_init(dev, &tm6000_template, "video"); if (!dev->vfd) { printk(KERN_INFO "%s: can't register video device\n", dev->name); - return -ENOMEM; + ret = -ENOMEM; + goto free_ctrl; } + dev->vfd->ctrl_handler = &dev->ctrl_handler; /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); @@ -1774,7 +1661,9 @@ int tm6000_v4l2_register(struct tm6000_core *dev) if (ret < 0) { printk(KERN_INFO "%s: can't register video device\n", dev->name); - return ret; + video_device_release(dev->vfd); + dev->vfd = NULL; + goto free_ctrl; } printk(KERN_INFO "%s: registered device %s\n", @@ -1787,15 +1676,17 @@ int tm6000_v4l2_register(struct tm6000_core *dev) printk(KERN_INFO "%s: can't register radio device\n", dev->name); ret = -ENXIO; - return ret; /* FIXME release resource */ + goto unreg_video; } + dev->radio_dev->ctrl_handler = &dev->radio_ctrl_handler; ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, radio_nr); if (ret < 0) { printk(KERN_INFO "%s: can't register radio device\n", dev->name); - return ret; /* FIXME release resource */ + video_device_release(dev->radio_dev); + goto unreg_video; } printk(KERN_INFO "%s: registered device %s\n", @@ -1804,6 +1695,13 @@ int tm6000_v4l2_register(struct tm6000_core *dev) printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); return ret; + +unreg_video: + video_unregister_device(dev->vfd); +free_ctrl: + v4l2_ctrl_handler_free(&dev->ctrl_handler); + v4l2_ctrl_handler_free(&dev->radio_ctrl_handler); + return ret; } int tm6000_v4l2_unregister(struct tm6000_core *dev) diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h index 173dcd7a7284..a9ac262fcc93 100644 --- a/drivers/media/usb/tm6000/tm6000.h +++ b/drivers/media/usb/tm6000/tm6000.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "dvb_demux.h" @@ -222,6 +223,8 @@ struct tm6000_core { struct video_device *radio_dev; struct tm6000_dmaqueue vidq; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl_handler radio_ctrl_handler; int input; struct tm6000_input vinput[3]; /* video input */ From 770056c47fbb5c4d892d1cd55ae5ec68cf44c2b4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Sep 2012 11:50:37 -0300 Subject: [PATCH 1952/4607] [media] tm6000: add support for control events and prio handling Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 31 ++++++++++++++++++------- drivers/media/usb/tm6000/tm6000.h | 2 ++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 4329fbcf2de2..25202a71d65e 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1350,6 +1351,7 @@ static int __tm6000_open(struct file *file) return -ENOMEM; } + v4l2_fh_init(&fh->fh, vdev); file->private_data = fh; fh->dev = dev; fh->radio = radio; @@ -1393,6 +1395,7 @@ static int __tm6000_open(struct file *file) tm6000_prepare_isoc(dev); tm6000_start_thread(dev); } + v4l2_fh_add(&fh->fh); return 0; } @@ -1433,29 +1436,35 @@ tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos) static unsigned int __tm6000_poll(struct file *file, struct poll_table_struct *wait) { + unsigned long req_events = poll_requested_events(wait); struct tm6000_fh *fh = file->private_data; struct tm6000_buffer *buf; + int res = 0; + if (v4l2_event_pending(&fh->fh)) + res = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(file, &fh->fh.wait, wait); if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) - return POLLERR; + return res | POLLERR; if (!!is_res_streaming(fh->dev, fh)) - return POLLERR; + return res | POLLERR; if (!is_res_read(fh->dev, fh)) { /* streaming capture */ if (list_empty(&fh->vb_vidq.stream)) - return POLLERR; + return res | POLLERR; buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream); - } else { + } else if (req_events & (POLLIN | POLLRDNORM)) { /* read() capture */ - return videobuf_poll_stream(file, &fh->vb_vidq, wait); + return res | videobuf_poll_stream(file, &fh->vb_vidq, wait); } poll_wait(file, &buf->vb.done, wait); if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) - return POLLIN | POLLRDNORM; - return 0; + return res | POLLIN | POLLRDNORM; + return res; } static unsigned int tm6000_poll(struct file *file, struct poll_table_struct *wait) @@ -1505,7 +1514,8 @@ static int tm6000_release(struct file *file) if (!fh->radio) videobuf_mmap_free(&fh->vb_vidq); } - + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); kfree(fh); mutex_unlock(&dev->lock); @@ -1555,6 +1565,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device tm6000_template = { @@ -1579,6 +1591,8 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_s_tuner = radio_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device tm6000_radio_template = { @@ -1607,6 +1621,7 @@ static struct video_device *vdev_init(struct tm6000_core *dev, vfd->release = video_device_release; vfd->debug = tm6000_debug; vfd->lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/usb/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h index a9ac262fcc93..08bd0740dd23 100644 --- a/drivers/media/usb/tm6000/tm6000.h +++ b/drivers/media/usb/tm6000/tm6000.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "dvb_demux.h" @@ -290,6 +291,7 @@ struct tm6000_ops { }; struct tm6000_fh { + struct v4l2_fh fh; struct tm6000_core *dev; unsigned int radio; From e618578dd828458f35c2cd16edfa7a1dba07ce43 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Sep 2012 11:51:02 -0300 Subject: [PATCH 1953/4607] [media] tm6000: set colorspace field Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 25202a71d65e..ac2588535f48 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -913,6 +913,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.height = fh->height; f->fmt.pix.field = fh->vb_vidq.field; f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; f->fmt.pix.sizeimage = @@ -967,6 +968,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; return 0; } From 52dec548d4244d9ec86c58e5696e1d4ee98c7b3c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Sep 2012 11:53:51 -0300 Subject: [PATCH 1954/4607] [media] tm6000: add poll op for radio device node Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index ac2588535f48..f41dbb187caf 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1583,6 +1583,7 @@ static struct video_device tm6000_template = { static const struct v4l2_file_operations radio_fops = { .owner = THIS_MODULE, .open = tm6000_open, + .poll = v4l2_ctrl_poll, .release = tm6000_release, .unlocked_ioctl = video_ioctl2, }; From ed57256f6fe8882cf6dde8d99f5fac5fd84c5a2d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 1 Feb 2013 09:08:46 -0300 Subject: [PATCH 1955/4607] [media] tm6000: fix G/TRY_FMT Two fixes: - the priv field wasn't set to 0. - only V4L2_FIELD_INTERLACED is supported. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index f41dbb187caf..eab23413a909 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -918,6 +918,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, (f->fmt.pix.width * fh->fmt->depth) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.priv = 0; return 0; } @@ -948,12 +949,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, field = f->fmt.pix.field; - if (field == V4L2_FIELD_ANY) - field = V4L2_FIELD_SEQ_TB; - else if (V4L2_FIELD_INTERLACED != field) { - dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n"); - return -EINVAL; - } + field = V4L2_FIELD_INTERLACED; tm6000_get_std_res(dev); @@ -963,6 +959,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width &= ~0x01; f->fmt.pix.field = field; + f->fmt.pix.priv = 0; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; From 0b302d88534f0811c5f49bfba7aa46c4e1e032b7 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 22 Jan 2013 01:19:50 -0300 Subject: [PATCH 1956/4607] [media] media: adv7343: accept configuration through platform data The current code was implemented with some default configurations, this default configuration works on board and doesn't work on other. This patch accepts the configuration through platform data and configures the encoder depending on the data passed. Signed-off-by: Lad, Prabhakar Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7343.c | 36 +++++++++++++++++++++---- include/media/adv7343.h | 52 +++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index 432eb5f7a0e5..9fc2b985df0e 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -43,6 +43,7 @@ MODULE_PARM_DESC(debug, "Debug level 0-1"); struct adv7343_state { struct v4l2_subdev sd; struct v4l2_ctrl_handler hdl; + const struct adv7343_platform_data *pdata; u8 reg00; u8 reg01; u8 reg02; @@ -215,12 +216,23 @@ static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) /* Enable Appropriate DAC */ val = state->reg00 & 0x03; - if (output_type == ADV7343_COMPOSITE_ID) - val |= ADV7343_COMPOSITE_POWER_VALUE; - else if (output_type == ADV7343_COMPONENT_ID) - val |= ADV7343_COMPONENT_POWER_VALUE; + /* configure default configuration */ + if (!state->pdata) + if (output_type == ADV7343_COMPOSITE_ID) + val |= ADV7343_COMPOSITE_POWER_VALUE; + else if (output_type == ADV7343_COMPONENT_ID) + val |= ADV7343_COMPONENT_POWER_VALUE; + else + val |= ADV7343_SVIDEO_POWER_VALUE; else - val |= ADV7343_SVIDEO_POWER_VALUE; + val = state->pdata->mode_config.sleep_mode << 0 | + state->pdata->mode_config.pll_control << 1 | + state->pdata->mode_config.dac_3 << 2 | + state->pdata->mode_config.dac_2 << 3 | + state->pdata->mode_config.dac_1 << 4 | + state->pdata->mode_config.dac_6 << 5 | + state->pdata->mode_config.dac_5 << 6 | + state->pdata->mode_config.dac_4 << 7; err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val); if (err < 0) @@ -238,6 +250,17 @@ static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) /* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */ val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI); + + if (state->pdata && state->pdata->sd_config.sd_dac_out1) + val = val | (state->pdata->sd_config.sd_dac_out1 << 1); + else if (state->pdata && !state->pdata->sd_config.sd_dac_out1) + val = val & ~(state->pdata->sd_config.sd_dac_out1 << 1); + + if (state->pdata && state->pdata->sd_config.sd_dac_out2) + val = val | (state->pdata->sd_config.sd_dac_out2 << 2); + else if (state->pdata && !state->pdata->sd_config.sd_dac_out2) + val = val & ~(state->pdata->sd_config.sd_dac_out2 << 2); + err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val); if (err < 0) goto setoutput_exit; @@ -402,6 +425,9 @@ static int adv7343_probe(struct i2c_client *client, if (state == NULL) return -ENOMEM; + /* Copy board specific information here */ + state->pdata = client->dev.platform_data; + state->reg00 = 0x80; state->reg01 = 0x00; state->reg02 = 0x20; diff --git a/include/media/adv7343.h b/include/media/adv7343.h index d6f8a4e1a1fc..944757be49bb 100644 --- a/include/media/adv7343.h +++ b/include/media/adv7343.h @@ -20,4 +20,56 @@ #define ADV7343_COMPONENT_ID (1) #define ADV7343_SVIDEO_ID (2) +/** + * adv7343_power_mode - power mode configuration. + * @sleep_mode: on enable the current consumption is reduced to micro ampere + * level. All DACs and the internal PLL circuit are disabled. + * Registers can be read from and written in sleep mode. + * @pll_control: PLL and oversampling control. This control allows internal + * PLL 1 circuit to be powered down and the oversampling to be + * switched off. + * @dac_1: power on/off DAC 1. + * @dac_2: power on/off DAC 2. + * @dac_3: power on/off DAC 3. + * @dac_4: power on/off DAC 4. + * @dac_5: power on/off DAC 5. + * @dac_6: power on/off DAC 6. + * + * Power mode register (Register 0x0), for more info refer REGISTER MAP ACCESS + * section of datasheet[1], table 17 page no 30. + * + * [1] http://www.analog.com/static/imported-files/data_sheets/ADV7342_7343.pdf + */ +struct adv7343_power_mode { + bool sleep_mode; + bool pll_control; + bool dac_1; + bool dac_2; + bool dac_3; + bool dac_4; + bool dac_5; + bool dac_6; +}; + +/** + * struct adv7343_sd_config - SD Only Output Configuration. + * @sd_dac_out1: Configure SD DAC Output 1. + * @sd_dac_out2: Configure SD DAC Output 2. + */ +struct adv7343_sd_config { + /* SD only Output Configuration */ + bool sd_dac_out1; + bool sd_dac_out2; +}; + +/** + * struct adv7343_platform_data - Platform data values and access functions. + * @mode_config: Configuration for power mode. + * @sd_config: SD Only Configuration. + */ +struct adv7343_platform_data { + struct adv7343_power_mode mode_config; + struct adv7343_sd_config sd_config; +}; + #endif /* End of #ifndef ADV7343_H */ From 3e85a44aacd68744765f2fc7f0645645b34e64f8 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 15 Jan 2013 05:04:41 -0300 Subject: [PATCH 1957/4607] [media] ARM: davinci: da850 evm: pass platform data for adv7343 encoder Without this patch the adv7343 encoder was being set to default configuration which caused display not to work on this board. This patch passes the necessary platform data required for adv7343 encoder to work on da850 evm. Signed-off-by: Lad, Prabhakar Acked-by: Sekhar Nori Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-davinci/board-da850-evm.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 0299915575a8..d0e3ec3d49e4 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1256,11 +1256,24 @@ static struct vpif_capture_config da850_vpif_capture_config = { }; /* VPIF display configuration */ + +static struct adv7343_platform_data adv7343_pdata = { + .mode_config = { + .dac_3 = 1, + .dac_2 = 1, + .dac_1 = 1, + }, + .sd_config = { + .sd_dac_out1 = 1, + }, +}; + static struct vpif_subdev_info da850_vpif_subdev[] = { { .name = "adv7343", .board_info = { I2C_BOARD_INFO("adv7343", 0x2a), + .platform_data = &adv7343_pdata, }, }, }; From 6f2627c29f6619ebdbc6de8934b33c23b73be8e6 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sun, 6 Jan 2013 13:19:43 -0300 Subject: [PATCH 1958/4607] [media] winbond-cir: only enable higher sample resolution if needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A sample resolution of 2us generates more than 300 interrupts per key and this resolution is not needed unless carrier reports are enabled. Revert to a resolution of 10us unless carrier reports are needed. This generates up to a fifth of the interrupts. Signed-off-by: Sean Young Acked-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/winbond-cir.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 85424856ae7f..535a18dccbd0 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -154,6 +154,8 @@ #define WBCIR_CNTR_R 0x02 /* Invert TX */ #define WBCIR_IRTX_INV 0x04 +/* Receiver oversampling */ +#define WBCIR_RX_T_OV 0x40 /* Valid banks for the SP3 UART */ enum wbcir_bank { @@ -394,7 +396,8 @@ wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device) if (data->rxstate == WBCIR_RXSTATE_ERROR) continue; - duration = ((irdata & 0x7F) + 1) * 2; + duration = ((irdata & 0x7F) + 1) * + (data->carrier_report_enabled ? 2 : 10); rawir.pulse = irdata & 0x80 ? false : true; rawir.duration = US_TO_NS(duration); @@ -550,6 +553,17 @@ wbcir_set_carrier_report(struct rc_dev *dev, int enable) wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CCTL, WBCIR_CNTR_EN, WBCIR_CNTR_EN | WBCIR_CNTR_R); + /* Set a higher sampling resolution if carrier reports are enabled */ + wbcir_select_bank(data, WBCIR_BANK_2); + data->dev->rx_resolution = US_TO_NS(enable ? 2 : 10); + outb(enable ? 0x03 : 0x0f, data->sbase + WBCIR_REG_SP3_BGDL); + outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); + + /* Enable oversampling if carrier reports are enabled */ + wbcir_select_bank(data, WBCIR_BANK_7); + wbcir_set_bits(data->sbase + WBCIR_REG_SP3_RCCFG, + enable ? WBCIR_RX_T_OV : 0, WBCIR_RX_T_OV); + data->carrier_report_enabled = enable; spin_unlock_irqrestore(&data->spinlock, flags); @@ -931,8 +945,8 @@ wbcir_init_hw(struct wbcir_data *data) /* prescaler 1.0, tx/rx fifo lvl 16 */ outb(0x30, data->sbase + WBCIR_REG_SP3_EXCR2); - /* Set baud divisor to sample every 2 ns */ - outb(0x03, data->sbase + WBCIR_REG_SP3_BGDL); + /* Set baud divisor to sample every 10 us */ + outb(0x0f, data->sbase + WBCIR_REG_SP3_BGDL); outb(0x00, data->sbase + WBCIR_REG_SP3_BGDH); /* Set CEIR mode */ @@ -941,12 +955,9 @@ wbcir_init_hw(struct wbcir_data *data) inb(data->sbase + WBCIR_REG_SP3_LSR); /* Clear LSR */ inb(data->sbase + WBCIR_REG_SP3_MSR); /* Clear MSR */ - /* - * Disable RX demod, enable run-length enc/dec, set freq span and - * enable over-sampling - */ + /* Disable RX demod, enable run-length enc/dec, set freq span */ wbcir_select_bank(data, WBCIR_BANK_7); - outb(0xd0, data->sbase + WBCIR_REG_SP3_RCCFG); + outb(0x90, data->sbase + WBCIR_REG_SP3_RCCFG); /* Disable timer */ wbcir_select_bank(data, WBCIR_BANK_4); From d0aab6564d12add07572141ceb34c60046e93ac3 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sun, 6 Jan 2013 13:19:44 -0300 Subject: [PATCH 1959/4607] [media] iguanair: ensure transmission mask is initialized Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/iguanair.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index b99b096d8a8f..b8b3e37e5c57 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -512,6 +512,7 @@ static int iguanair_probe(struct usb_interface *intf, rc->rx_resolution = RX_RESOLUTION; iguanair_set_tx_carrier(rc, 38000); + iguanair_set_tx_mask(rc, 0); ret = rc_register_device(rc); if (ret < 0) { From c6a3ea570e5d0ca20069cce5d537c845128b70f4 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Mon, 14 Jan 2013 05:51:44 -0300 Subject: [PATCH 1960/4607] [media] iguanair: intermittent initialization failure On cold boot the device does not initialize until the first packet is received, and that packet is not processed. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/iguanair.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index b8b3e37e5c57..a4ab2e6b3f82 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -58,6 +58,7 @@ struct iguanair { char phys[64]; }; +#define CMD_NOP 0x00 #define CMD_GET_VERSION 0x01 #define CMD_GET_BUFSIZE 0x11 #define CMD_GET_FEATURES 0x10 @@ -196,6 +197,10 @@ static void iguanair_irq_out(struct urb *urb) if (urb->status) dev_dbg(ir->dev, "Error: out urb status = %d\n", urb->status); + + /* if we sent an nop packet, do not expect a response */ + if (urb->status == 0 && ir->packet->header.cmd == CMD_NOP) + complete(&ir->completion); } static int iguanair_send(struct iguanair *ir, unsigned size) @@ -219,10 +224,17 @@ static int iguanair_get_features(struct iguanair *ir) { int rc; + /* + * On cold boot, the iguanair initializes on the first packet + * received but does not process that packet. Send an empty + * packet. + */ ir->packet->header.start = 0; ir->packet->header.direction = DIR_OUT; - ir->packet->header.cmd = CMD_GET_VERSION; + ir->packet->header.cmd = CMD_NOP; + iguanair_send(ir, sizeof(ir->packet->header)); + ir->packet->header.cmd = CMD_GET_VERSION; rc = iguanair_send(ir, sizeof(ir->packet->header)); if (rc) { dev_info(ir->dev, "failed to get version\n"); @@ -255,19 +267,14 @@ static int iguanair_get_features(struct iguanair *ir) ir->packet->header.cmd = CMD_GET_FEATURES; rc = iguanair_send(ir, sizeof(ir->packet->header)); - if (rc) { + if (rc) dev_info(ir->dev, "failed to get features\n"); - goto out; - } - out: return rc; } static int iguanair_receiver(struct iguanair *ir, bool enable) { - int rc; - ir->packet->header.start = 0; ir->packet->header.direction = DIR_OUT; ir->packet->header.cmd = enable ? CMD_RECEIVER_ON : CMD_RECEIVER_OFF; @@ -275,9 +282,7 @@ static int iguanair_receiver(struct iguanair *ir, bool enable) if (enable) ir_raw_event_reset(ir->rc); - rc = iguanair_send(ir, sizeof(ir->packet->header)); - - return rc; + return iguanair_send(ir, sizeof(ir->packet->header)); } /* From 7e20f6bfc47992d93b36f4ed068782f8726b75a3 Mon Sep 17 00:00:00 2001 From: Nickolai Zeldovich Date: Sat, 5 Jan 2013 15:13:05 -0300 Subject: [PATCH 1961/4607] [media] drivers/media/usb/dvb-usb/dib0700_core.c: fix left shift Fix bug introduced in 7757ddda6f4febbc52342d82440dd4f7a7d4f14f, where instead of bit-negating the bitmask, the bit position was bit-negated instead. Signed-off-by: Nickolai Zeldovich Cc: Olivier Grenie Cc: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dib0700_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index bf2a908d74cf..bd6a43785d5e 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -584,7 +584,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) if (onoff) st->channel_state |= 1 << (adap->id); else - st->channel_state |= 1 << ~(adap->id); + st->channel_state &= ~(1 << (adap->id)); } else { if (onoff) st->channel_state |= 1 << (adap->fe_adap[0].stream.props.endpoint-2); From a46edeb0f6098088f316c3543474784f8108508e Mon Sep 17 00:00:00 2001 From: Nickolai Zeldovich Date: Sun, 6 Jan 2013 21:52:03 -0300 Subject: [PATCH 1962/4607] [media] media: cx18, ivtv: eliminate unnecessary array index checks The idx values passed to cx18_i2c_register() and ivtv_i2c_register() by cx18_init_subdevs() and ivtv_load_and_init_modules() respectively are always in-range, based on how the hw_all bitmask is populated. Previously, the checks were already ineffective because arrays were being dereferenced using the index before the check. Acked-by: Andy Walls Signed-off-by: Nickolai Zeldovich Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-i2c.c | 3 --- drivers/media/pci/ivtv/ivtv-i2c.c | 2 -- 2 files changed, 5 deletions(-) diff --git a/drivers/media/pci/cx18/cx18-i2c.c b/drivers/media/pci/cx18/cx18-i2c.c index d61ac6393e7e..4af8cd6df95d 100644 --- a/drivers/media/pci/cx18/cx18-i2c.c +++ b/drivers/media/pci/cx18/cx18-i2c.c @@ -116,9 +116,6 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx) const char *type = hw_devicenames[idx]; u32 hw = 1 << idx; - if (idx >= ARRAY_SIZE(hw_addrs)) - return -1; - if (hw == CX18_HW_TUNER) { /* special tuner group handling */ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c index a1811054fde4..ceed2d87abfd 100644 --- a/drivers/media/pci/ivtv/ivtv-i2c.c +++ b/drivers/media/pci/ivtv/ivtv-i2c.c @@ -267,8 +267,6 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) const char *type = hw_devicenames[idx]; u32 hw = 1 << idx; - if (idx >= ARRAY_SIZE(hw_addrs)) - return -1; if (hw == IVTV_HW_TUNER) { /* special tuner handling */ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, 0, From 22331a5e0a493f8edfbd504bb58bd03d308ddb0c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 7 Jan 2013 16:30:24 -0300 Subject: [PATCH 1963/4607] [media] DocBook: media: struct v4l2_capability card field is a UTF-8 string The struct v4l2_capability card field stores the device name. That name can be hardcoded in drivers, or be retrieved directly from the device. The later is very common with USB devices. As several devices already report names that include non-ASCII characters, update the field description to use UTF-8. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/vidioc-querycap.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/DocBook/media/v4l/vidioc-querycap.xml b/Documentation/DocBook/media/v4l/vidioc-querycap.xml index 4c70215ae03f..d5a3c97b206a 100644 --- a/Documentation/DocBook/media/v4l/vidioc-querycap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-querycap.xml @@ -76,7 +76,7 @@ make sure the strings are properly NUL-terminated. __u8 card[32] - Name of the device, a NUL-terminated ASCII string. + Name of the device, a NUL-terminated UTF-8 string. For example: "Yoyodyne TV/FM". One driver may support different brands or models of video hardware. This information is intended for users, for example in a menu of available devices. Since multiple TV cards of From bb71b14d80bde8484ae63ee09d969c72d8615e0c Mon Sep 17 00:00:00 2001 From: Nickolai Zeldovich Date: Mon, 7 Jan 2013 22:28:05 -0300 Subject: [PATCH 1964/4607] [media] drivers/media/pci: use memmove for overlapping regions Change several memcpy() to memmove() in cases when the regions are definitely overlapping; memcpy() of overlapping regions is undefined behavior in C and can produce different results depending on the compiler, the memcpy implementation, etc. Cc: Andy Walls Signed-off-by: Nickolai Zeldovich Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/bt8xx/dst_ca.c | 4 ++-- drivers/media/pci/cx18/cx18-vbi.c | 2 +- drivers/media/pci/ivtv/ivtv-vbi.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c index 7d96fab7d246..0e788fca992c 100644 --- a/drivers/media/pci/bt8xx/dst_ca.c +++ b/drivers/media/pci/bt8xx/dst_ca.c @@ -180,11 +180,11 @@ static int ca_get_app_info(struct dst_state *state) put_command_and_length(&state->messages[0], CA_APP_INFO, length); // Copy application_type, application_manufacturer and manufacturer_code - memcpy(&state->messages[4], &state->messages[7], 5); + memmove(&state->messages[4], &state->messages[7], 5); // Set string length and copy string state->messages[9] = str_length; - memcpy(&state->messages[10], &state->messages[12], str_length); + memmove(&state->messages[10], &state->messages[12], str_length); return 0; } diff --git a/drivers/media/pci/cx18/cx18-vbi.c b/drivers/media/pci/cx18/cx18-vbi.c index 6d3121ff45a2..add99642f1e2 100644 --- a/drivers/media/pci/cx18/cx18-vbi.c +++ b/drivers/media/pci/cx18/cx18-vbi.c @@ -84,7 +84,7 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) (the max size of the VBI data is 36 * 43 + 4 bytes). So in this case we use the magic number 'ITV0'. */ memcpy(dst + sd, "ITV0", 4); - memcpy(dst + sd + 4, dst + sd + 12, line * 43); + memmove(dst + sd + 4, dst + sd + 12, line * 43); size = 4 + ((43 * line + 3) & ~3); } else { memcpy(dst + sd, "itv0", 4); diff --git a/drivers/media/pci/ivtv/ivtv-vbi.c b/drivers/media/pci/ivtv/ivtv-vbi.c index 293db806d936..3c156bc70fb4 100644 --- a/drivers/media/pci/ivtv/ivtv-vbi.c +++ b/drivers/media/pci/ivtv/ivtv-vbi.c @@ -224,7 +224,7 @@ static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp) (the max size of the VBI data is 36 * 43 + 4 bytes). So in this case we use the magic number 'ITV0'. */ memcpy(dst + sd, "ITV0", 4); - memcpy(dst + sd + 4, dst + sd + 12, line * 43); + memmove(dst + sd + 4, dst + sd + 12, line * 43); size = 4 + ((43 * line + 3) & ~3); } else { memcpy(dst + sd, "itv0", 4); @@ -532,7 +532,7 @@ void ivtv_vbi_work_handler(struct ivtv *itv) while (vi->cc_payload_idx) { cc = vi->cc_payload[0]; - memcpy(vi->cc_payload, vi->cc_payload + 1, + memmove(vi->cc_payload, vi->cc_payload + 1, sizeof(vi->cc_payload) - sizeof(vi->cc_payload[0])); vi->cc_payload_idx--; if (vi->cc_payload_idx && cc.odd[0] == 0x80 && cc.odd[1] == 0x80) From f1ec57239f9bf19b2ccc0535cb8bc17dc4aaaab2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 10 Jan 2013 05:00:57 -0300 Subject: [PATCH 1965/4607] [media] staging: go7007: print the audio input type Smatch complains that the "Audio input:" printk isn't reachable. Hiding the "return 0;" behind another statement is a style violation. It looks like audio_input is normally configured so I've enabled the print statement. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/s2250-board.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c index d60e06596375..37400bfa6ccb 100644 --- a/drivers/staging/media/go7007/s2250-board.c +++ b/drivers/staging/media/go7007/s2250-board.c @@ -534,7 +534,7 @@ static int s2250_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "Brightness: %d\n", state->brightness); v4l2_info(sd, "Contrast: %d\n", state->contrast); v4l2_info(sd, "Saturation: %d\n", state->saturation); - v4l2_info(sd, "Hue: %d\n", state->hue); return 0; + v4l2_info(sd, "Hue: %d\n", state->hue); v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" : state->audio_input == 1 ? "Mic" : state->audio_input == 2 ? "Mic Boost" : From f62436a96a0678a4e8bc54e7b987be72977af9ce Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 11 Jan 2013 02:48:41 -0300 Subject: [PATCH 1966/4607] [media] cx231xx: add a missing break statement My static checker complains about the fall through here. From the context it looks like we should add a break statement. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/cx231xx/cx231xx-video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 93dfc182ec51..06376d904c9f 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -1751,6 +1751,7 @@ static int vidioc_s_register(struct file *file, void *priv, 0x02, (u16)reg->reg, 1, value, 1, 2); + break; case 0x322: ret = cx231xx_write_i2c_master(dev, From 0b3966e40c99afeb89849b5cf7b100a3bb4271cd Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:15:08 -0300 Subject: [PATCH 1967/4607] [media] em28xx: add missing IR RC slave address to the list of known i2c devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-i2c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 9ae8f6051d8e..8532c1d4fd46 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -534,6 +534,7 @@ static struct i2c_client em28xx_client_template = { * incomplete list of known devices */ static char *i2c_devs[128] = { + [0x3e >> 1] = "remote IR sensor", [0x4a >> 1] = "saa7113h", [0x52 >> 1] = "drxk", [0x60 >> 1] = "remote IR sensor", From 59cf17d84353e175d40ff10ee9fa221d304d0836 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:39 -0300 Subject: [PATCH 1968/4607] [media] em28xx-input: remove dead code line from em28xx_get_key_em_haup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Field 'old' of struct IR_i2c is used nowhere in module ir-kbd-i2c. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 07f6030d7455..f554a5291619 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -125,8 +125,6 @@ static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (buf[1] == 0xff) return 0; - ir->old = buf[1]; - /* * Rearranges bits to the right order. * The bit order were determined experimentally by using From 62ec3f86ff86483ae27b9411179b8ded74558c19 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:40 -0300 Subject: [PATCH 1969/4607] [media] em28xx: remove i2cdprintk() messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't report any key/scan codes or errors inside the key polling functions for internal IR RC devices, just in the key handling fucntions. Do the same for external devices. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 29 +++++-------------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index f554a5291619..edcd6978f2e1 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -40,11 +40,6 @@ MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); #define MODULE_NAME "em28xx" -#define i2cdprintk(fmt, arg...) \ - if (ir_debug) { \ - printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ - } - #define dprintk(fmt, arg...) \ if (ir_debug) { \ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ @@ -86,17 +81,13 @@ static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) { - i2cdprintk("read error\n"); + if (1 != i2c_master_recv(ir->c, &b, 1)) return -EIO; - } /* it seems that 0xFE indicates that a button is still hold down, while 0xff indicates that no button is hold down. 0xfe sequences are sometimes interrupted by 0xFF */ - i2cdprintk("key %02x\n", b); - if (b == 0xff) return 0; @@ -147,9 +138,6 @@ static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ((buf[1] & 0x40) ? 0x0200 : 0) | /* 0000 0010 */ ((buf[1] & 0x80) ? 0x0100 : 0); /* 0000 0001 */ - i2cdprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x%02x)\n", - code, buf[1], buf[0]); - /* return key */ *ir_key = code; *ir_raw = code; @@ -163,12 +151,9 @@ static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, /* poll IR chip */ - if (3 != i2c_master_recv(ir->c, buf, 3)) { - i2cdprintk("read error\n"); + if (3 != i2c_master_recv(ir->c, buf, 3)) return -EIO; - } - i2cdprintk("key %02x\n", buf[2]&0x3f); if (buf[0] != 0x00) return 0; @@ -188,19 +173,15 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, { .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; subaddr = 0x10; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { - i2cdprintk("read error\n"); + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) return -EIO; - } if (keydetect == 0x00) return 0; subaddr = 0x00; msg[1].buf = &key; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { - i2cdprintk("read error\n"); - return -EIO; - } + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) + return -EIO; if (key == 0x00) return 0; From 768da3dbcf50b697e5e7a921492b7f0d2cd8a8fb Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:41 -0300 Subject: [PATCH 1970/4607] [media] em28xx: get rid of the dependency on module ir-kbd-i2c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have the key polling functions and the polling infrastructure in em28xx-input, so we can easily get rid of the dependency on module ir-kbd-i2c. For maximum safety, do not touch the key reporting mechanism for those devices. Code size could be improved further but would have minor peformance impacts. Tested with device "Terratec Cinergy 200 USB" (EM2800_BOARD_TERRATEC_CINERGY_200) [mchehab@redhat.com: Fix two checkpatch.pl warnings: ERROR: "foo * bar" should be "foo *bar" (line 465) WARNING: kfree(NULL) is safe this check is probably not required (line 725)] Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 219 +++++++++++++++--------- drivers/media/usb/em28xx/em28xx.h | 3 - 2 files changed, 134 insertions(+), 88 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index edcd6978f2e1..72cb0cfad8d0 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -62,13 +62,17 @@ struct em28xx_IR { char name[32]; char phys[32]; - /* poll external decoder */ + /* poll decoder */ int polling; struct delayed_work work; unsigned int full_code:1; unsigned int last_readcount; u64 rc_type; + /* external device (if used) */ + struct i2c_client *i2c_dev; + + int (*get_key_i2c)(struct i2c_client *, u32 *, u32 *); int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; @@ -76,12 +80,13 @@ struct em28xx_IR { I2C IR based get keycodes - should be used with ir-kbd-i2c **********************************************************/ -static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(ir->c, &b, 1)) + if (1 != i2c_master_recv(i2c_dev, &b, 1)) return -EIO; /* it seems that 0xFE indicates that a button is still hold @@ -100,14 +105,15 @@ static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char buf[2]; u16 code; int size; /* poll IR chip */ - size = i2c_master_recv(ir->c, buf, sizeof(buf)); + size = i2c_master_recv(i2c_dev, buf, sizeof(buf)); if (size != 2) return -EIO; @@ -144,14 +150,14 @@ static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char buf[3]; /* poll IR chip */ - if (3 != i2c_master_recv(ir->c, buf, 3)) + if (3 != i2c_master_recv(i2c_dev, buf, 3)) return -EIO; if (buf[0] != 0x00) @@ -163,24 +169,24 @@ static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, return 1; } -static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw) +static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, + u32 *ir_key, u32 *ir_raw) { unsigned char subaddr, keydetect, key; - struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, .buf = &subaddr, .len = 1}, + struct i2c_msg msg[] = { { .addr = i2c_dev->addr, .flags = 0, .buf = &subaddr, .len = 1}, - { .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; + { .addr = i2c_dev->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; subaddr = 0x10; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) + if (2 != i2c_transfer(i2c_dev->adapter, msg, 2)) return -EIO; if (keydetect == 0x00) return 0; subaddr = 0x00; msg[1].buf = &key; - if (2 != i2c_transfer(ir->c->adapter, msg, 2)) + if (2 != i2c_transfer(i2c_dev->adapter, msg, 2)) return -EIO; if (key == 0x00) return 0; @@ -280,6 +286,24 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, Polling code for em28xx **********************************************************/ +static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir) +{ + static u32 ir_key, ir_raw; + int rc; + + rc = ir->get_key_i2c(ir->i2c_dev, &ir_key, &ir_raw); + if (rc < 0) { + dprintk("ir->get_key_i2c() failed: %d\n", rc); + return rc; + } + + if (rc) { + dprintk("%s: keycode = 0x%04x\n", __func__, ir_key); + rc_keydown(ir->rc, ir_key, 0); + } + return 0; +} + static void em28xx_ir_handle_key(struct em28xx_IR *ir) { int result; @@ -288,7 +312,7 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) /* read the registers containing the IR status */ result = ir->get_key(ir, &poll_result); if (unlikely(result < 0)) { - dprintk("ir->get_key() failed %d\n", result); + dprintk("ir->get_key() failed: %d\n", result); return; } @@ -318,6 +342,14 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) } } +static void em28xx_i2c_ir_work(struct work_struct *work) +{ + struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); + + em28xx_i2c_ir_handle_key(ir); + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); +} + static void em28xx_ir_work(struct work_struct *work) { struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); @@ -330,7 +362,10 @@ static int em28xx_ir_start(struct rc_dev *rc) { struct em28xx_IR *ir = rc->priv; - INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); + if (ir->i2c_dev) /* external i2c device */ + INIT_DELAYED_WORK(&ir->work, em28xx_i2c_ir_work); + else /* internal device */ + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); schedule_delayed_work(&ir->work, 0); return 0; @@ -427,49 +462,33 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) } } -static void em28xx_register_i2c_ir(struct em28xx *dev) +static struct i2c_client *em28xx_probe_i2c_ir(struct em28xx *dev) { + int i = 0; + struct i2c_client *i2c_dev = NULL; /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ /* at address 0x18, so if that address is needed for another board in */ /* the future, please put it after 0x1f. */ - struct i2c_board_info info; const unsigned short addr_list[] = { 0x1f, 0x30, 0x47, I2C_CLIENT_END }; - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - - /* detect & configure */ - switch (dev->model) { - case EM2800_BOARD_TERRATEC_CINERGY_200: - case EM2820_BOARD_TERRATEC_CINERGY_250: - dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; - dev->init_data.get_key = em28xx_get_key_terratec; - dev->init_data.name = "Terratec Cinergy 200/250"; - break; - case EM2820_BOARD_PINNACLE_USB_2: - dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; - dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - dev->init_data.name = "Pinnacle USB2"; - break; - case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; - dev->init_data.get_key = em28xx_get_key_em_haup; - dev->init_data.name = "WinTV USB2"; - dev->init_data.type = RC_BIT_RC5; - break; - case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: - dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; - dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; - dev->init_data.name = "Winfast TV USBII Deluxe"; - break; + while (addr_list[i] != I2C_CLIENT_END) { + if (i2c_probe_func_quick_read(&dev->i2c_adap, addr_list[i]) == 1) { + i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); + if (i2c_dev) { + i2c_dev->addr = addr_list[i]; + i2c_dev->adapter = &dev->i2c_adap; + /* NOTE: as long as we don't register the device + * at the i2c subsystem, no other fields need to + * be set up */ + } + break; + } + i++; } - if (dev->init_data.name) - info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); + return i2c_dev; } /********************************************************** @@ -565,19 +584,21 @@ static int em28xx_ir_init(struct em28xx *dev) struct rc_dev *rc; int err = -ENOMEM; u64 rc_type; + struct i2c_client *i2c_rc_dev = NULL; if (dev->board.has_snapshot_button) em28xx_register_snapshot_button(dev); if (dev->board.has_ir_i2c) { - em28xx_register_i2c_ir(dev); -#if defined(CONFIG_MODULES) && defined(MODULE) - request_module("ir-kbd-i2c"); -#endif - return 0; + i2c_rc_dev = em28xx_probe_i2c_ir(dev); + if (!i2c_rc_dev) { + dev->board.has_ir_i2c = 0; + em28xx_warn("No i2c IR remote control device found.\n"); + return -ENODEV; + } } - if (dev->board.ir_codes == NULL) { + if (dev->board.ir_codes == NULL && !dev->board.has_ir_i2c) { /* No remote control support */ em28xx_warn("Remote control support is not available for " "this card.\n"); @@ -594,45 +615,70 @@ static int em28xx_ir_init(struct em28xx *dev) dev->ir = ir; ir->rc = rc; - /* - * em2874 supports more protocols. For now, let's just announce - * the two protocols that were already tested - */ - rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; rc->priv = ir; - rc->change_protocol = em28xx_ir_change_protocol; rc->open = em28xx_ir_start; rc->close = em28xx_ir_stop; - switch (dev->chip_id) { - case CHIP_ID_EM2860: - case CHIP_ID_EM2883: - rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; - ir->get_key = default_polling_getkey; - break; - case CHIP_ID_EM2884: - case CHIP_ID_EM2874: - case CHIP_ID_EM28174: - ir->get_key = em2874_polling_getkey; - rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | RC_BIT_RC6_0; - break; - default: - err = -ENODEV; - goto error; - } + if (dev->board.has_ir_i2c) { /* external i2c device */ + switch (dev->model) { + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: + rc->map_name = RC_MAP_EM_TERRATEC; + ir->get_key_i2c = em28xx_get_key_terratec; + break; + case EM2820_BOARD_PINNACLE_USB_2: + rc->map_name = RC_MAP_PINNACLE_GREY; + ir->get_key_i2c = em28xx_get_key_pinnacle_usb_grey; + break; + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: + rc->map_name = RC_MAP_HAUPPAUGE; + ir->get_key_i2c = em28xx_get_key_em_haup; + rc->allowed_protos = RC_BIT_RC5; + break; + case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: + rc->map_name = RC_MAP_WINFAST_USBII_DELUXE; + ir->get_key_i2c = em28xx_get_key_winfast_usbii_deluxe; + break; + default: + err = -ENODEV; + goto error; + } - /* By default, keep protocol field untouched */ - rc_type = RC_BIT_UNKNOWN; - err = em28xx_ir_change_protocol(rc, &rc_type); - if (err) - goto error; + ir->i2c_dev = i2c_rc_dev; + } else { /* internal device */ + switch (dev->chip_id) { + case CHIP_ID_EM2860: + case CHIP_ID_EM2883: + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC; + ir->get_key = default_polling_getkey; + break; + case CHIP_ID_EM2884: + case CHIP_ID_EM2874: + case CHIP_ID_EM28174: + ir->get_key = em2874_polling_getkey; + rc->allowed_protos = RC_BIT_RC5 | RC_BIT_NEC | + RC_BIT_RC6_0; + break; + default: + err = -ENODEV; + goto error; + } + + rc->change_protocol = em28xx_ir_change_protocol; + rc->map_name = dev->board.ir_codes; + + /* By default, keep protocol field untouched */ + rc_type = RC_BIT_UNKNOWN; + err = em28xx_ir_change_protocol(rc, &rc_type); + if (err) + goto error; + } /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ /* init input device */ - snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", - dev->name); + snprintf(ir->name, sizeof(ir->name), "em28xx IR (%s)", dev->name); usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); @@ -644,7 +690,6 @@ static int em28xx_ir_init(struct em28xx *dev) rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct); rc->dev.parent = &dev->udev->dev; - rc->map_name = dev->board.ir_codes; rc->driver_name = MODULE_NAME; /* all done */ @@ -655,6 +700,8 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; error: + if (ir && ir->i2c_dev) + kfree(ir->i2c_dev); dev->ir = NULL; rc_free_device(rc); kfree(ir); @@ -674,6 +721,8 @@ static int em28xx_ir_fini(struct em28xx *dev) if (ir->rc) rc_unregister_device(ir->rc); + kfree(ir->i2c_dev); + /* done */ kfree(ir); dev->ir = NULL; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 2aa4b8472a62..5f0b2c59e846 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -640,9 +640,6 @@ struct em28xx { struct delayed_work sbutton_query_work; struct em28xx_dvb *dvb; - - /* I2C keyboard data */ - struct IR_i2c_init_data init_data; }; struct em28xx_ops { From 146b7ee63866cee57620ec08d10250f7fffaf4bc Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:42 -0300 Subject: [PATCH 1971/4607] [media] em28xx: remove unused parameter ir_raw from i2c RC key polling functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 72cb0cfad8d0..d500e9c2f9e2 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -72,7 +72,7 @@ struct em28xx_IR { /* external device (if used) */ struct i2c_client *i2c_dev; - int (*get_key_i2c)(struct i2c_client *, u32 *, u32 *); + int (*get_key_i2c)(struct i2c_client *, u32 *); int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); }; @@ -80,8 +80,7 @@ struct em28xx_IR { I2C IR based get keycodes - should be used with ir-kbd-i2c **********************************************************/ -static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, - u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, u32 *ir_key) { unsigned char b; @@ -101,12 +100,10 @@ static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, return 1; *ir_key = b; - *ir_raw = b; return 1; } -static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, - u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, u32 *ir_key) { unsigned char buf[2]; u16 code; @@ -146,12 +143,11 @@ static int em28xx_get_key_em_haup(struct i2c_client *i2c_dev, /* return key */ *ir_key = code; - *ir_raw = code; return 1; } static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, - u32 *ir_key, u32 *ir_raw) + u32 *ir_key) { unsigned char buf[3]; @@ -164,13 +160,12 @@ static int em28xx_get_key_pinnacle_usb_grey(struct i2c_client *i2c_dev, return 0; *ir_key = buf[2]&0x3f; - *ir_raw = buf[2]&0x3f; return 1; } static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, - u32 *ir_key, u32 *ir_raw) + u32 *ir_key) { unsigned char subaddr, keydetect, key; @@ -192,7 +187,6 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, return 0; *ir_key = key; - *ir_raw = key; return 1; } @@ -288,10 +282,10 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir) { - static u32 ir_key, ir_raw; + static u32 ir_key; int rc; - rc = ir->get_key_i2c(ir->i2c_dev, &ir_key, &ir_raw); + rc = ir->get_key_i2c(ir->i2c_dev, &ir_key); if (rc < 0) { dprintk("ir->get_key_i2c() failed: %d\n", rc); return rc; From 450c7dd65b6c28c8f5b445c75bb55e8b84133bb9 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:43 -0300 Subject: [PATCH 1972/4607] [media] em28xx: fix a comment and a small coding style issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index d500e9c2f9e2..f3cff2bfb5ba 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -89,8 +89,7 @@ static int em28xx_get_key_terratec(struct i2c_client *i2c_dev, u32 *ir_key) return -EIO; /* it seems that 0xFE indicates that a button is still hold - down, while 0xff indicates that no button is hold - down. 0xfe sequences are sometimes interrupted by 0xFF */ + down, while 0xff indicates that no button is hold down. */ if (b == 0xff) return 0; @@ -170,8 +169,7 @@ static int em28xx_get_key_winfast_usbii_deluxe(struct i2c_client *i2c_dev, unsigned char subaddr, keydetect, key; struct i2c_msg msg[] = { { .addr = i2c_dev->addr, .flags = 0, .buf = &subaddr, .len = 1}, - - { .addr = i2c_dev->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; + { .addr = i2c_dev->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; subaddr = 0x10; if (2 != i2c_transfer(i2c_dev->adapter, msg, 2)) From 1d968cdaaec2ea994b2656c00b5a4f10d4159fe8 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:44 -0300 Subject: [PATCH 1973/4607] [media] em28xx: i2c RC devices: minor code size and memory usage optimization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set up the i2c_client locally in em28xx_i2c_ir_handle_key(). Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 42 ++++++++++--------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index f3cff2bfb5ba..63b97285ddbd 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -69,8 +69,8 @@ struct em28xx_IR { unsigned int last_readcount; u64 rc_type; - /* external device (if used) */ - struct i2c_client *i2c_dev; + /* i2c slave address of external device (if used) */ + u16 i2c_dev_addr; int (*get_key_i2c)(struct i2c_client *, u32 *); int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); @@ -282,8 +282,12 @@ static int em28xx_i2c_ir_handle_key(struct em28xx_IR *ir) { static u32 ir_key; int rc; + struct i2c_client client; - rc = ir->get_key_i2c(ir->i2c_dev, &ir_key); + client.adapter = &ir->dev->i2c_adap; + client.addr = ir->i2c_dev_addr; + + rc = ir->get_key_i2c(&client, &ir_key); if (rc < 0) { dprintk("ir->get_key_i2c() failed: %d\n", rc); return rc; @@ -354,7 +358,7 @@ static int em28xx_ir_start(struct rc_dev *rc) { struct em28xx_IR *ir = rc->priv; - if (ir->i2c_dev) /* external i2c device */ + if (ir->i2c_dev_addr) /* external i2c device */ INIT_DELAYED_WORK(&ir->work, em28xx_i2c_ir_work); else /* internal device */ INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); @@ -454,10 +458,9 @@ static int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 *rc_type) } } -static struct i2c_client *em28xx_probe_i2c_ir(struct em28xx *dev) +static int em28xx_probe_i2c_ir(struct em28xx *dev) { int i = 0; - struct i2c_client *i2c_dev = NULL; /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ /* at address 0x18, so if that address is needed for another board in */ /* the future, please put it after 0x1f. */ @@ -466,21 +469,12 @@ static struct i2c_client *em28xx_probe_i2c_ir(struct em28xx *dev) }; while (addr_list[i] != I2C_CLIENT_END) { - if (i2c_probe_func_quick_read(&dev->i2c_adap, addr_list[i]) == 1) { - i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); - if (i2c_dev) { - i2c_dev->addr = addr_list[i]; - i2c_dev->adapter = &dev->i2c_adap; - /* NOTE: as long as we don't register the device - * at the i2c subsystem, no other fields need to - * be set up */ - } - break; - } + if (i2c_probe_func_quick_read(&dev->i2c_adap, addr_list[i]) == 1) + return addr_list[i]; i++; } - return i2c_dev; + return -ENODEV; } /********************************************************** @@ -576,14 +570,14 @@ static int em28xx_ir_init(struct em28xx *dev) struct rc_dev *rc; int err = -ENOMEM; u64 rc_type; - struct i2c_client *i2c_rc_dev = NULL; + u16 i2c_rc_dev_addr = 0; if (dev->board.has_snapshot_button) em28xx_register_snapshot_button(dev); if (dev->board.has_ir_i2c) { - i2c_rc_dev = em28xx_probe_i2c_ir(dev); - if (!i2c_rc_dev) { + i2c_rc_dev_addr = em28xx_probe_i2c_ir(dev); + if (!i2c_rc_dev_addr) { dev->board.has_ir_i2c = 0; em28xx_warn("No i2c IR remote control device found.\n"); return -ENODEV; @@ -636,7 +630,7 @@ static int em28xx_ir_init(struct em28xx *dev) goto error; } - ir->i2c_dev = i2c_rc_dev; + ir->i2c_dev_addr = i2c_rc_dev_addr; } else { /* internal device */ switch (dev->chip_id) { case CHIP_ID_EM2860: @@ -692,8 +686,6 @@ static int em28xx_ir_init(struct em28xx *dev) return 0; error: - if (ir && ir->i2c_dev) - kfree(ir->i2c_dev); dev->ir = NULL; rc_free_device(rc); kfree(ir); @@ -713,8 +705,6 @@ static int em28xx_ir_fini(struct em28xx *dev) if (ir->rc) rc_unregister_device(ir->rc); - kfree(ir->i2c_dev); - /* done */ kfree(ir); dev->ir = NULL; From 9b4539bebb86310afdc5563653ec4475ae110088 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 13 Jan 2013 10:20:45 -0300 Subject: [PATCH 1974/4607] [media] em28xx: input: use common work_struct callback function for IR RC key polling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove em28xx_i2c_ir_work() and check the device type in the common callback function em28xx_ir_work() instead. Simplifies em28xx_ir_start(). Reduces the code size with a minor performance drawback. Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-input.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 63b97285ddbd..1bef990b3f18 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -338,19 +338,14 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) } } -static void em28xx_i2c_ir_work(struct work_struct *work) -{ - struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); - - em28xx_i2c_ir_handle_key(ir); - schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); -} - static void em28xx_ir_work(struct work_struct *work) { struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); - em28xx_ir_handle_key(ir); + if (ir->i2c_dev_addr) /* external i2c device */ + em28xx_i2c_ir_handle_key(ir); + else /* internal device */ + em28xx_ir_handle_key(ir); schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); } @@ -358,10 +353,7 @@ static int em28xx_ir_start(struct rc_dev *rc) { struct em28xx_IR *ir = rc->priv; - if (ir->i2c_dev_addr) /* external i2c device */ - INIT_DELAYED_WORK(&ir->work, em28xx_i2c_ir_work); - else /* internal device */ - INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); schedule_delayed_work(&ir->work, 0); return 0; From 0e3d50bfcbd338254795a700dcff429a96cba1a6 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 5 Feb 2013 11:47:09 -0500 Subject: [PATCH 1975/4607] drm/radeon/dce6: fix display powergating Only enable it when we disable the display rather than at DPMS time since enabling it requires a full modeset to restore the display state. Fixes blank screens in certain cases. Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/atombios_crtc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 9175615bbd8a..21a892c6ab9c 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -252,8 +252,6 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) radeon_crtc->enabled = true; /* adjust pm to dpms changes BEFORE enabling crtcs */ radeon_pm_compute_clocks(rdev); - if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set) - atombios_powergate_crtc(crtc, ATOM_DISABLE); atombios_enable_crtc(crtc, ATOM_ENABLE); if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev)) atombios_enable_crtc_memreq(crtc, ATOM_ENABLE); @@ -271,8 +269,6 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode) atombios_enable_crtc_memreq(crtc, ATOM_DISABLE); atombios_enable_crtc(crtc, ATOM_DISABLE); radeon_crtc->enabled = false; - if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set) - atombios_powergate_crtc(crtc, ATOM_ENABLE); /* adjust pm to dpms changes AFTER disabling crtcs */ radeon_pm_compute_clocks(rdev); break; @@ -1844,6 +1840,8 @@ static void atombios_crtc_disable(struct drm_crtc *crtc) int i; atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + if (ASIC_IS_DCE6(rdev)) + atombios_powergate_crtc(crtc, ATOM_ENABLE); for (i = 0; i < rdev->num_crtc; i++) { if (rdev->mode_info.crtcs[i] && From cf1364b17e94624161775b0e681dd967ef366980 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 13 Jan 2013 15:31:33 -0300 Subject: [PATCH 1976/4607] [media] tuners/xc5000: fix MODE_AIR in xc5000_set_params() There is a missing break so we use XC_RF_MODE_CABLE instead of XC_RF_MODE_AIR. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/xc5000.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index dc93cf338f36..d6be1b613c52 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -785,6 +785,7 @@ static int xc5000_set_params(struct dvb_frontend *fe) return -EINVAL; } priv->rf_mode = XC_RF_MODE_AIR; + break; case SYS_DVBC_ANNEX_A: case SYS_DVBC_ANNEX_C: dprintk(1, "%s() QAM modulation\n", __func__); From 834be0d83f9451573e6fadb381fe0714211c7e90 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Wed, 30 Jan 2013 16:45:05 +0200 Subject: [PATCH 1977/4607] Revert "KVM: MMU: split kvm_mmu_free_page" This reverts commit bd4c86eaa6ff10abc4e00d0f45d2a28b10b09df4. There is not user for kvm_mmu_isolate_page() any more. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/mmu.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 42ba85c62fcb..0242a8a1b2e2 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -1461,28 +1461,14 @@ static inline void kvm_mod_used_mmu_pages(struct kvm *kvm, int nr) percpu_counter_add(&kvm_total_used_mmu_pages, nr); } -/* - * Remove the sp from shadow page cache, after call it, - * we can not find this sp from the cache, and the shadow - * page table is still valid. - * It should be under the protection of mmu lock. - */ -static void kvm_mmu_isolate_page(struct kvm_mmu_page *sp) +static void kvm_mmu_free_page(struct kvm_mmu_page *sp) { ASSERT(is_empty_shadow_page(sp->spt)); hlist_del(&sp->hash_link); - if (!sp->role.direct) - free_page((unsigned long)sp->gfns); -} - -/* - * Free the shadow page table and the sp, we can do it - * out of the protection of mmu lock. - */ -static void kvm_mmu_free_page(struct kvm_mmu_page *sp) -{ list_del(&sp->link); free_page((unsigned long)sp->spt); + if (!sp->role.direct) + free_page((unsigned long)sp->gfns); kmem_cache_free(mmu_page_header_cache, sp); } @@ -2126,7 +2112,6 @@ static void kvm_mmu_commit_zap_page(struct kvm *kvm, do { sp = list_first_entry(invalid_list, struct kvm_mmu_page, link); WARN_ON(!sp->role.invalid || sp->root_count); - kvm_mmu_isolate_page(sp); kvm_mmu_free_page(sp); } while (!list_empty(invalid_list)); } From 4293b5e5a68074431cafa74d549c1327ba1d0deb Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Thu, 31 Jan 2013 12:06:08 -0800 Subject: [PATCH 1978/4607] KVM: Remove duplicate text in api.txt Signed-off-by: Geoff Levand Signed-off-by: Marcelo Tosatti --- Documentation/virtual/kvm/api.txt | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 0e03b1968272..c2534c300a45 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -219,19 +219,6 @@ allocation of vcpu ids. For example, if userspace wants single-threaded guest vcpus, it should make all vcpu ids be a multiple of the number of vcpus per vcore. -On powerpc using book3s_hv mode, the vcpus are mapped onto virtual -threads in one or more virtual CPU cores. (This is because the -hardware requires all the hardware threads in a CPU core to be in the -same partition.) The KVM_CAP_PPC_SMT capability indicates the number -of vcpus per virtual core (vcore). The vcore id is obtained by -dividing the vcpu id by the number of vcpus per vcore. The vcpus in a -given vcore will always be in the same physical core as each other -(though that might be a different physical core from time to time). -Userspace can control the threading (SMT) mode of the guest by its -allocation of vcpu ids. For example, if userspace wants -single-threaded guest vcpus, it should make all vcpu ids be a multiple -of the number of vcpus per vcore. - For virtual cpus that have been created with S390 user controlled virtual machines, the resulting vcpu fd can be memory mapped at page offset KVM_S390_SIE_PAGE_OFFSET in order to obtain a memory map of the virtual From c08800a56cb8622bb61577abb4a120c6fdc4b9be Mon Sep 17 00:00:00 2001 From: Dongxiao Xu Date: Mon, 4 Feb 2013 11:50:43 +0800 Subject: [PATCH 1979/4607] KVM: VMX: disable SMEP feature when guest is in non-paging mode SMEP is disabled if CPU is in non-paging mode in hardware. However KVM always uses paging mode to emulate guest non-paging mode with TDP. To emulate this behavior, SMEP needs to be manually disabled when guest switches to non-paging mode. We met an issue that, SMP Linux guest with recent kernel (enable SMEP support, for example, 3.5.3) would crash with triple fault if setting unrestricted_guest=0. This is because KVM uses an identity mapping page table to emulate the non-paging mode, where the page table is set with USER flag. If SMEP is still enabled in this case, guest will meet unhandlable page fault and then crash. Reviewed-by: Gleb Natapov Reviewed-by: Paolo Bonzini Signed-off-by: Dongxiao Xu Signed-off-by: Xiantao Zhang Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 0cf74a641dec..fe9a9cfadbd6 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3227,6 +3227,14 @@ static int vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) if (!is_paging(vcpu)) { hw_cr4 &= ~X86_CR4_PAE; hw_cr4 |= X86_CR4_PSE; + /* + * SMEP is disabled if CPU is in non-paging mode in + * hardware. However KVM always uses paging mode to + * emulate guest non-paging mode with TDP. + * To emulate this behavior, SMEP needs to be manually + * disabled when guest switches to non-paging mode. + */ + hw_cr4 &= ~X86_CR4_SMEP; } else if (!(cr4 & X86_CR4_PAE)) { hw_cr4 &= ~X86_CR4_PAE; } From b0da5bec30eca7ffbb2c89afa6fe503fd418d3a6 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Sun, 3 Feb 2013 18:17:17 +0200 Subject: [PATCH 1980/4607] KVM: VMX: add missing exit names to VMX_EXIT_REASONS array Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/include/asm/vmx.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index 694586ca6456..5c9dbadd364a 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -105,7 +105,12 @@ { EXIT_REASON_APIC_ACCESS, "APIC_ACCESS" }, \ { EXIT_REASON_EPT_VIOLATION, "EPT_VIOLATION" }, \ { EXIT_REASON_EPT_MISCONFIG, "EPT_MISCONFIG" }, \ - { EXIT_REASON_WBINVD, "WBINVD" } + { EXIT_REASON_WBINVD, "WBINVD" }, \ + { EXIT_REASON_APIC_WRITE, "APIC_WRITE" }, \ + { EXIT_REASON_EOI_INDUCED, "EOI_INDUCED" }, \ + { EXIT_REASON_INVALID_STATE, "INVALID_STATE" }, \ + { EXIT_REASON_INVD, "INVD" }, \ + { EXIT_REASON_INVPCID, "INVPCID" } #ifdef __KERNEL__ From d6d71ee4a14ae602db343ec48c491851d7ec5267 Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Mon, 21 Jan 2013 04:37:57 -0800 Subject: [PATCH 1981/4607] PM: Introduce Intel PowerClamp Driver Intel PowerClamp driver performs synchronized idle injection across all online CPUs. The goal is to maintain a given package level C-state ratio. Compared to other throttling methods already exist in the kernel, such as ACPI PAD (taking CPUs offline) and clock modulation, this is often more efficient in terms of performance per watt. Please refer to Documentation/thermal/intel_powerclamp.txt for more details. Signed-off-by: Arjan van de Ven Signed-off-by: Jacob Pan Signed-off-by: Zhang Rui --- Documentation/thermal/intel_powerclamp.txt | 307 ++++++++ drivers/thermal/Kconfig | 10 + drivers/thermal/Makefile | 2 + drivers/thermal/intel_powerclamp.c | 794 +++++++++++++++++++++ 4 files changed, 1113 insertions(+) create mode 100644 Documentation/thermal/intel_powerclamp.txt create mode 100644 drivers/thermal/intel_powerclamp.c diff --git a/Documentation/thermal/intel_powerclamp.txt b/Documentation/thermal/intel_powerclamp.txt new file mode 100644 index 000000000000..332de4a39b5a --- /dev/null +++ b/Documentation/thermal/intel_powerclamp.txt @@ -0,0 +1,307 @@ + ======================= + INTEL POWERCLAMP DRIVER + ======================= +By: Arjan van de Ven + Jacob Pan + +Contents: + (*) Introduction + - Goals and Objectives + + (*) Theory of Operation + - Idle Injection + - Calibration + + (*) Performance Analysis + - Effectiveness and Limitations + - Power vs Performance + - Scalability + - Calibration + - Comparison with Alternative Techniques + + (*) Usage and Interfaces + - Generic Thermal Layer (sysfs) + - Kernel APIs (TBD) + +============ +INTRODUCTION +============ + +Consider the situation where a system’s power consumption must be +reduced at runtime, due to power budget, thermal constraint, or noise +level, and where active cooling is not preferred. Software managed +passive power reduction must be performed to prevent the hardware +actions that are designed for catastrophic scenarios. + +Currently, P-states, T-states (clock modulation), and CPU offlining +are used for CPU throttling. + +On Intel CPUs, C-states provide effective power reduction, but so far +they’re only used opportunistically, based on workload. With the +development of intel_powerclamp driver, the method of synchronizing +idle injection across all online CPU threads was introduced. The goal +is to achieve forced and controllable C-state residency. + +Test/Analysis has been made in the areas of power, performance, +scalability, and user experience. In many cases, clear advantage is +shown over taking the CPU offline or modulating the CPU clock. + + +=================== +THEORY OF OPERATION +=================== + +Idle Injection +-------------- + +On modern Intel processors (Nehalem or later), package level C-state +residency is available in MSRs, thus also available to the kernel. + +These MSRs are: + #define MSR_PKG_C2_RESIDENCY 0x60D + #define MSR_PKG_C3_RESIDENCY 0x3F8 + #define MSR_PKG_C6_RESIDENCY 0x3F9 + #define MSR_PKG_C7_RESIDENCY 0x3FA + +If the kernel can also inject idle time to the system, then a +closed-loop control system can be established that manages package +level C-state. The intel_powerclamp driver is conceived as such a +control system, where the target set point is a user-selected idle +ratio (based on power reduction), and the error is the difference +between the actual package level C-state residency ratio and the target idle +ratio. + +Injection is controlled by high priority kernel threads, spawned for +each online CPU. + +These kernel threads, with SCHED_FIFO class, are created to perform +clamping actions of controlled duty ratio and duration. Each per-CPU +thread synchronizes its idle time and duration, based on the rounding +of jiffies, so accumulated errors can be prevented to avoid a jittery +effect. Threads are also bound to the CPU such that they cannot be +migrated, unless the CPU is taken offline. In this case, threads +belong to the offlined CPUs will be terminated immediately. + +Running as SCHED_FIFO and relatively high priority, also allows such +scheme to work for both preemptable and non-preemptable kernels. +Alignment of idle time around jiffies ensures scalability for HZ +values. This effect can be better visualized using a Perf timechart. +The following diagram shows the behavior of kernel thread +kidle_inject/cpu. During idle injection, it runs monitor/mwait idle +for a given "duration", then relinquishes the CPU to other tasks, +until the next time interval. + +The NOHZ schedule tick is disabled during idle time, but interrupts +are not masked. Tests show that the extra wakeups from scheduler tick +have a dramatic impact on the effectiveness of the powerclamp driver +on large scale systems (Westmere system with 80 processors). + +CPU0 + ____________ ____________ +kidle_inject/0 | sleep | mwait | sleep | + _________| |________| |_______ + duration +CPU1 + ____________ ____________ +kidle_inject/1 | sleep | mwait | sleep | + _________| |________| |_______ + ^ + | + | + roundup(jiffies, interval) + +Only one CPU is allowed to collect statistics and update global +control parameters. This CPU is referred to as the controlling CPU in +this document. The controlling CPU is elected at runtime, with a +policy that favors BSP, taking into account the possibility of a CPU +hot-plug. + +In terms of dynamics of the idle control system, package level idle +time is considered largely as a non-causal system where its behavior +cannot be based on the past or current input. Therefore, the +intel_powerclamp driver attempts to enforce the desired idle time +instantly as given input (target idle ratio). After injection, +powerclamp moniors the actual idle for a given time window and adjust +the next injection accordingly to avoid over/under correction. + +When used in a causal control system, such as a temperature control, +it is up to the user of this driver to implement algorithms where +past samples and outputs are included in the feedback. For example, a +PID-based thermal controller can use the powerclamp driver to +maintain a desired target temperature, based on integral and +derivative gains of the past samples. + + + +Calibration +----------- +During scalability testing, it is observed that synchronized actions +among CPUs become challenging as the number of cores grows. This is +also true for the ability of a system to enter package level C-states. + +To make sure the intel_powerclamp driver scales well, online +calibration is implemented. The goals for doing such a calibration +are: + +a) determine the effective range of idle injection ratio +b) determine the amount of compensation needed at each target ratio + +Compensation to each target ratio consists of two parts: + + a) steady state error compensation + This is to offset the error occurring when the system can + enter idle without extra wakeups (such as external interrupts). + + b) dynamic error compensation + When an excessive amount of wakeups occurs during idle, an + additional idle ratio can be added to quiet interrupts, by + slowing down CPU activities. + +A debugfs file is provided for the user to examine compensation +progress and results, such as on a Westmere system. +[jacob@nex01 ~]$ cat +/sys/kernel/debug/intel_powerclamp/powerclamp_calib +controlling cpu: 0 +pct confidence steady dynamic (compensation) +0 0 0 0 +1 1 0 0 +2 1 1 0 +3 3 1 0 +4 3 1 0 +5 3 1 0 +6 3 1 0 +7 3 1 0 +8 3 1 0 +... +30 3 2 0 +31 3 2 0 +32 3 1 0 +33 3 2 0 +34 3 1 0 +35 3 2 0 +36 3 1 0 +37 3 2 0 +38 3 1 0 +39 3 2 0 +40 3 3 0 +41 3 1 0 +42 3 2 0 +43 3 1 0 +44 3 1 0 +45 3 2 0 +46 3 3 0 +47 3 0 0 +48 3 2 0 +49 3 3 0 + +Calibration occurs during runtime. No offline method is available. +Steady state compensation is used only when confidence levels of all +adjacent ratios have reached satisfactory level. A confidence level +is accumulated based on clean data collected at runtime. Data +collected during a period without extra interrupts is considered +clean. + +To compensate for excessive amounts of wakeup during idle, additional +idle time is injected when such a condition is detected. Currently, +we have a simple algorithm to double the injection ratio. A possible +enhancement might be to throttle the offending IRQ, such as delaying +EOI for level triggered interrupts. But it is a challenge to be +non-intrusive to the scheduler or the IRQ core code. + + +CPU Online/Offline +------------------ +Per-CPU kernel threads are started/stopped upon receiving +notifications of CPU hotplug activities. The intel_powerclamp driver +keeps track of clamping kernel threads, even after they are migrated +to other CPUs, after a CPU offline event. + + +===================== +Performance Analysis +===================== +This section describes the general performance data collected on +multiple systems, including Westmere (80P) and Ivy Bridge (4P, 8P). + +Effectiveness and Limitations +----------------------------- +The maximum range that idle injection is allowed is capped at 50 +percent. As mentioned earlier, since interrupts are allowed during +forced idle time, excessive interrupts could result in less +effectiveness. The extreme case would be doing a ping -f to generated +flooded network interrupts without much CPU acknowledgement. In this +case, little can be done from the idle injection threads. In most +normal cases, such as scp a large file, applications can be throttled +by the powerclamp driver, since slowing down the CPU also slows down +network protocol processing, which in turn reduces interrupts. + +When control parameters change at runtime by the controlling CPU, it +may take an additional period for the rest of the CPUs to catch up +with the changes. During this time, idle injection is out of sync, +thus not able to enter package C- states at the expected ratio. But +this effect is minor, in that in most cases change to the target +ratio is updated much less frequently than the idle injection +frequency. + +Scalability +----------- +Tests also show a minor, but measurable, difference between the 4P/8P +Ivy Bridge system and the 80P Westmere server under 50% idle ratio. +More compensation is needed on Westmere for the same amount of +target idle ratio. The compensation also increases as the idle ratio +gets larger. The above reason constitutes the need for the +calibration code. + +On the IVB 8P system, compared to an offline CPU, powerclamp can +achieve up to 40% better performance per watt. (measured by a spin +counter summed over per CPU counting threads spawned for all running +CPUs). + +==================== +Usage and Interfaces +==================== +The powerclamp driver is registered to the generic thermal layer as a +cooling device. Currently, it’s not bound to any thermal zones. + +jacob@chromoly:/sys/class/thermal/cooling_device14$ grep . * +cur_state:0 +max_state:50 +type:intel_powerclamp + +Example usage: +- To inject 25% idle time +$ sudo sh -c "echo 25 > /sys/class/thermal/cooling_device80/cur_state +" + +If the system is not busy and has more than 25% idle time already, +then the powerclamp driver will not start idle injection. Using Top +will not show idle injection kernel threads. + +If the system is busy (spin test below) and has less than 25% natural +idle time, powerclamp kernel threads will do idle injection, which +appear running to the scheduler. But the overall system idle is still +reflected. In this example, 24.1% idle is shown. This helps the +system admin or user determine the cause of slowdown, when a +powerclamp driver is in action. + + +Tasks: 197 total, 1 running, 196 sleeping, 0 stopped, 0 zombie +Cpu(s): 71.2%us, 4.7%sy, 0.0%ni, 24.1%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st +Mem: 3943228k total, 1689632k used, 2253596k free, 74960k buffers +Swap: 4087804k total, 0k used, 4087804k free, 945336k cached + + PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND + 3352 jacob 20 0 262m 644 428 S 286 0.0 0:17.16 spin + 3341 root -51 0 0 0 0 D 25 0.0 0:01.62 kidle_inject/0 + 3344 root -51 0 0 0 0 D 25 0.0 0:01.60 kidle_inject/3 + 3342 root -51 0 0 0 0 D 25 0.0 0:01.61 kidle_inject/1 + 3343 root -51 0 0 0 0 D 25 0.0 0:01.60 kidle_inject/2 + 2935 jacob 20 0 696m 125m 35m S 5 3.3 0:31.11 firefox + 1546 root 20 0 158m 20m 6640 S 3 0.5 0:26.97 Xorg + 2100 jacob 20 0 1223m 88m 30m S 3 2.3 0:23.68 compiz + +Tests have shown that by using the powerclamp driver as a cooling +device, a PID based userspace thermal controller can manage to +control CPU temperature effectively, when no other thermal influence +is added. For example, a UltraBook user can compile the kernel under +certain temperature (below most active trip points). diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index c31b9e4451a3..faf38c522fa8 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -131,4 +131,14 @@ config DB8500_CPUFREQ_COOLING bound cpufreq cooling device turns active to set CPU frequency low to cool down the CPU. +config INTEL_POWERCLAMP + tristate "Intel PowerClamp idle injection driver" + depends on THERMAL + depends on X86 + depends on CPU_SUP_INTEL + help + Enable this to enable Intel PowerClamp idle injection driver. This + enforce idle time which results in more package C-state residency. The + user interface is exposed via generic thermal framework. + endif diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index d8da683245fc..574f5f505b9f 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -18,3 +18,5 @@ obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o +obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o + diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c new file mode 100644 index 000000000000..a85ff38cb4e8 --- /dev/null +++ b/drivers/thermal/intel_powerclamp.c @@ -0,0 +1,794 @@ +/* + * intel_powerclamp.c - package c-state idle injection + * + * Copyright (c) 2012, Intel Corporation. + * + * Authors: + * Arjan van de Ven + * Jacob Pan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * + * TODO: + * 1. better handle wakeup from external interrupts, currently a fixed + * compensation is added to clamping duration when excessive amount + * of wakeups are observed during idle time. the reason is that in + * case of external interrupts without need for ack, clamping down + * cpu in non-irq context does not reduce irq. for majority of the + * cases, clamping down cpu does help reduce irq as well, we should + * be able to differenciate the two cases and give a quantitative + * solution for the irqs that we can control. perhaps based on + * get_cpu_iowait_time_us() + * + * 2. synchronization with other hw blocks + * + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MAX_TARGET_RATIO (50U) +/* For each undisturbed clamping period (no extra wake ups during idle time), + * we increment the confidence counter for the given target ratio. + * CONFIDENCE_OK defines the level where runtime calibration results are + * valid. + */ +#define CONFIDENCE_OK (3) +/* Default idle injection duration, driver adjust sleep time to meet target + * idle ratio. Similar to frequency modulation. + */ +#define DEFAULT_DURATION_JIFFIES (6) + +static unsigned int target_mwait; +static struct dentry *debug_dir; + +/* user selected target */ +static unsigned int set_target_ratio; +static unsigned int current_ratio; +static bool should_skip; +static bool reduce_irq; +static atomic_t idle_wakeup_counter; +static unsigned int control_cpu; /* The cpu assigned to collect stat and update + * control parameters. default to BSP but BSP + * can be offlined. + */ +static bool clamping; + + +static struct task_struct * __percpu *powerclamp_thread; +static struct thermal_cooling_device *cooling_dev; +static unsigned long *cpu_clamping_mask; /* bit map for tracking per cpu + * clamping thread + */ + +static unsigned int duration; +static unsigned int pkg_cstate_ratio_cur; +static unsigned int window_size; + +static int duration_set(const char *arg, const struct kernel_param *kp) +{ + int ret = 0; + unsigned long new_duration; + + ret = kstrtoul(arg, 10, &new_duration); + if (ret) + goto exit; + if (new_duration > 25 || new_duration < 6) { + pr_err("Out of recommended range %lu, between 6-25ms\n", + new_duration); + ret = -EINVAL; + } + + duration = clamp(new_duration, 6ul, 25ul); + smp_mb(); + +exit: + + return ret; +} + +static struct kernel_param_ops duration_ops = { + .set = duration_set, + .get = param_get_int, +}; + + +module_param_cb(duration, &duration_ops, &duration, 0644); +MODULE_PARM_DESC(duration, "forced idle time for each attempt in msec."); + +struct powerclamp_calibration_data { + unsigned long confidence; /* used for calibration, basically a counter + * gets incremented each time a clamping + * period is completed without extra wakeups + * once that counter is reached given level, + * compensation is deemed usable. + */ + unsigned long steady_comp; /* steady state compensation used when + * no extra wakeups occurred. + */ + unsigned long dynamic_comp; /* compensate excessive wakeup from idle + * mostly from external interrupts. + */ +}; + +static struct powerclamp_calibration_data cal_data[MAX_TARGET_RATIO]; + +static int window_size_set(const char *arg, const struct kernel_param *kp) +{ + int ret = 0; + unsigned long new_window_size; + + ret = kstrtoul(arg, 10, &new_window_size); + if (ret) + goto exit_win; + if (new_window_size > 10 || new_window_size < 2) { + pr_err("Out of recommended window size %lu, between 2-10\n", + new_window_size); + ret = -EINVAL; + } + + window_size = clamp(new_window_size, 2ul, 10ul); + smp_mb(); + +exit_win: + + return ret; +} + +static struct kernel_param_ops window_size_ops = { + .set = window_size_set, + .get = param_get_int, +}; + +module_param_cb(window_size, &window_size_ops, &window_size, 0644); +MODULE_PARM_DESC(window_size, "sliding window in number of clamping cycles\n" + "\tpowerclamp controls idle ratio within this window. larger\n" + "\twindow size results in slower response time but more smooth\n" + "\tclamping results. default to 2."); + +static void find_target_mwait(void) +{ + unsigned int eax, ebx, ecx, edx; + unsigned int highest_cstate = 0; + unsigned int highest_subcstate = 0; + int i; + + if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF) + return; + + cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx); + + if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) || + !(ecx & CPUID5_ECX_INTERRUPT_BREAK)) + return; + + edx >>= MWAIT_SUBSTATE_SIZE; + for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) { + if (edx & MWAIT_SUBSTATE_MASK) { + highest_cstate = i; + highest_subcstate = edx & MWAIT_SUBSTATE_MASK; + } + } + target_mwait = (highest_cstate << MWAIT_SUBSTATE_SIZE) | + (highest_subcstate - 1); + +} + +static u64 pkg_state_counter(void) +{ + u64 val; + u64 count = 0; + + static bool skip_c2; + static bool skip_c3; + static bool skip_c6; + static bool skip_c7; + + if (!skip_c2) { + if (!rdmsrl_safe(MSR_PKG_C2_RESIDENCY, &val)) + count += val; + else + skip_c2 = true; + } + + if (!skip_c3) { + if (!rdmsrl_safe(MSR_PKG_C3_RESIDENCY, &val)) + count += val; + else + skip_c3 = true; + } + + if (!skip_c6) { + if (!rdmsrl_safe(MSR_PKG_C6_RESIDENCY, &val)) + count += val; + else + skip_c6 = true; + } + + if (!skip_c7) { + if (!rdmsrl_safe(MSR_PKG_C7_RESIDENCY, &val)) + count += val; + else + skip_c7 = true; + } + + return count; +} + +static void noop_timer(unsigned long foo) +{ + /* empty... just the fact that we get the interrupt wakes us up */ +} + +static unsigned int get_compensation(int ratio) +{ + unsigned int comp = 0; + + /* we only use compensation if all adjacent ones are good */ + if (ratio == 1 && + cal_data[ratio].confidence >= CONFIDENCE_OK && + cal_data[ratio + 1].confidence >= CONFIDENCE_OK && + cal_data[ratio + 2].confidence >= CONFIDENCE_OK) { + comp = (cal_data[ratio].steady_comp + + cal_data[ratio + 1].steady_comp + + cal_data[ratio + 2].steady_comp) / 3; + } else if (ratio == MAX_TARGET_RATIO - 1 && + cal_data[ratio].confidence >= CONFIDENCE_OK && + cal_data[ratio - 1].confidence >= CONFIDENCE_OK && + cal_data[ratio - 2].confidence >= CONFIDENCE_OK) { + comp = (cal_data[ratio].steady_comp + + cal_data[ratio - 1].steady_comp + + cal_data[ratio - 2].steady_comp) / 3; + } else if (cal_data[ratio].confidence >= CONFIDENCE_OK && + cal_data[ratio - 1].confidence >= CONFIDENCE_OK && + cal_data[ratio + 1].confidence >= CONFIDENCE_OK) { + comp = (cal_data[ratio].steady_comp + + cal_data[ratio - 1].steady_comp + + cal_data[ratio + 1].steady_comp) / 3; + } + + /* REVISIT: simple penalty of double idle injection */ + if (reduce_irq) + comp = ratio; + /* do not exceed limit */ + if (comp + ratio >= MAX_TARGET_RATIO) + comp = MAX_TARGET_RATIO - ratio - 1; + + return comp; +} + +static void adjust_compensation(int target_ratio, unsigned int win) +{ + int delta; + struct powerclamp_calibration_data *d = &cal_data[target_ratio]; + + /* + * adjust compensations if confidence level has not been reached or + * there are too many wakeups during the last idle injection period, we + * cannot trust the data for compensation. + */ + if (d->confidence >= CONFIDENCE_OK || + atomic_read(&idle_wakeup_counter) > + win * num_online_cpus()) + return; + + delta = set_target_ratio - current_ratio; + /* filter out bad data */ + if (delta >= 0 && delta <= (1+target_ratio/10)) { + if (d->steady_comp) + d->steady_comp = + roundup(delta+d->steady_comp, 2)/2; + else + d->steady_comp = delta; + d->confidence++; + } +} + +static bool powerclamp_adjust_controls(unsigned int target_ratio, + unsigned int guard, unsigned int win) +{ + static u64 msr_last, tsc_last; + u64 msr_now, tsc_now; + u64 val64; + + /* check result for the last window */ + msr_now = pkg_state_counter(); + rdtscll(tsc_now); + + /* calculate pkg cstate vs tsc ratio */ + if (!msr_last || !tsc_last) + current_ratio = 1; + else if (tsc_now-tsc_last) { + val64 = 100*(msr_now-msr_last); + do_div(val64, (tsc_now-tsc_last)); + current_ratio = val64; + } + + /* update record */ + msr_last = msr_now; + tsc_last = tsc_now; + + adjust_compensation(target_ratio, win); + /* + * too many external interrupts, set flag such + * that we can take measure later. + */ + reduce_irq = atomic_read(&idle_wakeup_counter) >= + 2 * win * num_online_cpus(); + + atomic_set(&idle_wakeup_counter, 0); + /* if we are above target+guard, skip */ + return set_target_ratio + guard <= current_ratio; +} + +static int clamp_thread(void *arg) +{ + int cpunr = (unsigned long)arg; + DEFINE_TIMER(wakeup_timer, noop_timer, 0, 0); + static const struct sched_param param = { + .sched_priority = MAX_USER_RT_PRIO/2, + }; + unsigned int count = 0; + unsigned int target_ratio; + + set_bit(cpunr, cpu_clamping_mask); + set_freezable(); + init_timer_on_stack(&wakeup_timer); + sched_setscheduler(current, SCHED_FIFO, ¶m); + + while (true == clamping && !kthread_should_stop() && + cpu_online(cpunr)) { + int sleeptime; + unsigned long target_jiffies; + unsigned int guard; + unsigned int compensation = 0; + int interval; /* jiffies to sleep for each attempt */ + unsigned int duration_jiffies = msecs_to_jiffies(duration); + unsigned int window_size_now; + + try_to_freeze(); + /* + * make sure user selected ratio does not take effect until + * the next round. adjust target_ratio if user has changed + * target such that we can converge quickly. + */ + target_ratio = set_target_ratio; + guard = 1 + target_ratio/20; + window_size_now = window_size; + count++; + + /* + * systems may have different ability to enter package level + * c-states, thus we need to compensate the injected idle ratio + * to achieve the actual target reported by the HW. + */ + compensation = get_compensation(target_ratio); + interval = duration_jiffies*100/(target_ratio+compensation); + + /* align idle time */ + target_jiffies = roundup(jiffies, interval); + sleeptime = target_jiffies - jiffies; + if (sleeptime <= 0) + sleeptime = 1; + schedule_timeout_interruptible(sleeptime); + /* + * only elected controlling cpu can collect stats and update + * control parameters. + */ + if (cpunr == control_cpu && !(count%window_size_now)) { + should_skip = + powerclamp_adjust_controls(target_ratio, + guard, window_size_now); + smp_mb(); + } + + if (should_skip) + continue; + + target_jiffies = jiffies + duration_jiffies; + mod_timer(&wakeup_timer, target_jiffies); + if (unlikely(local_softirq_pending())) + continue; + /* + * stop tick sched during idle time, interrupts are still + * allowed. thus jiffies are updated properly. + */ + preempt_disable(); + tick_nohz_idle_enter(); + /* mwait until target jiffies is reached */ + while (time_before(jiffies, target_jiffies)) { + unsigned long ecx = 1; + unsigned long eax = target_mwait; + + /* + * REVISIT: may call enter_idle() to notify drivers who + * can save power during cpu idle. same for exit_idle() + */ + local_touch_nmi(); + stop_critical_timings(); + __monitor((void *)¤t_thread_info()->flags, 0, 0); + cpu_relax(); /* allow HT sibling to run */ + __mwait(eax, ecx); + start_critical_timings(); + atomic_inc(&idle_wakeup_counter); + } + tick_nohz_idle_exit(); + preempt_enable_no_resched(); + } + del_timer_sync(&wakeup_timer); + clear_bit(cpunr, cpu_clamping_mask); + + return 0; +} + +/* + * 1 HZ polling while clamping is active, useful for userspace + * to monitor actual idle ratio. + */ +static void poll_pkg_cstate(struct work_struct *dummy); +static DECLARE_DELAYED_WORK(poll_pkg_cstate_work, poll_pkg_cstate); +static void poll_pkg_cstate(struct work_struct *dummy) +{ + static u64 msr_last; + static u64 tsc_last; + static unsigned long jiffies_last; + + u64 msr_now; + unsigned long jiffies_now; + u64 tsc_now; + u64 val64; + + msr_now = pkg_state_counter(); + rdtscll(tsc_now); + jiffies_now = jiffies; + + /* calculate pkg cstate vs tsc ratio */ + if (!msr_last || !tsc_last) + pkg_cstate_ratio_cur = 1; + else { + if (tsc_now - tsc_last) { + val64 = 100 * (msr_now - msr_last); + do_div(val64, (tsc_now - tsc_last)); + pkg_cstate_ratio_cur = val64; + } + } + + /* update record */ + msr_last = msr_now; + jiffies_last = jiffies_now; + tsc_last = tsc_now; + + if (true == clamping) + schedule_delayed_work(&poll_pkg_cstate_work, HZ); +} + +static int start_power_clamp(void) +{ + unsigned long cpu; + struct task_struct *thread; + + /* check if pkg cstate counter is completely 0, abort in this case */ + if (!pkg_state_counter()) { + pr_err("pkg cstate counter not functional, abort\n"); + return -EINVAL; + } + + set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO); + /* prevent cpu hotplug */ + get_online_cpus(); + + /* prefer BSP */ + control_cpu = 0; + if (!cpu_online(control_cpu)) + control_cpu = smp_processor_id(); + + clamping = true; + schedule_delayed_work(&poll_pkg_cstate_work, 0); + + /* start one thread per online cpu */ + for_each_online_cpu(cpu) { + struct task_struct **p = + per_cpu_ptr(powerclamp_thread, cpu); + + thread = kthread_create_on_node(clamp_thread, + (void *) cpu, + cpu_to_node(cpu), + "kidle_inject/%ld", cpu); + /* bind to cpu here */ + if (likely(!IS_ERR(thread))) { + kthread_bind(thread, cpu); + wake_up_process(thread); + *p = thread; + } + + } + put_online_cpus(); + + return 0; +} + +static void end_power_clamp(void) +{ + int i; + struct task_struct *thread; + + clamping = false; + /* + * make clamping visible to other cpus and give per cpu clamping threads + * sometime to exit, or gets killed later. + */ + smp_mb(); + msleep(20); + if (bitmap_weight(cpu_clamping_mask, num_possible_cpus())) { + for_each_set_bit(i, cpu_clamping_mask, num_possible_cpus()) { + pr_debug("clamping thread for cpu %d alive, kill\n", i); + thread = *per_cpu_ptr(powerclamp_thread, i); + kthread_stop(thread); + } + } +} + +static int powerclamp_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + unsigned long cpu = (unsigned long)hcpu; + struct task_struct *thread; + struct task_struct **percpu_thread = + per_cpu_ptr(powerclamp_thread, cpu); + + if (false == clamping) + goto exit_ok; + + switch (action) { + case CPU_ONLINE: + thread = kthread_create_on_node(clamp_thread, + (void *) cpu, + cpu_to_node(cpu), + "kidle_inject/%lu", cpu); + if (likely(!IS_ERR(thread))) { + kthread_bind(thread, cpu); + wake_up_process(thread); + *percpu_thread = thread; + } + /* prefer BSP as controlling CPU */ + if (cpu == 0) { + control_cpu = 0; + smp_mb(); + } + break; + case CPU_DEAD: + if (test_bit(cpu, cpu_clamping_mask)) { + pr_err("cpu %lu dead but powerclamping thread is not\n", + cpu); + kthread_stop(*percpu_thread); + } + if (cpu == control_cpu) { + control_cpu = smp_processor_id(); + smp_mb(); + } + } + +exit_ok: + return NOTIFY_OK; +} + +static struct notifier_block powerclamp_cpu_notifier = { + .notifier_call = powerclamp_cpu_callback, +}; + +static int powerclamp_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = MAX_TARGET_RATIO; + + return 0; +} + +static int powerclamp_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + if (true == clamping) + *state = pkg_cstate_ratio_cur; + else + /* to save power, do not poll idle ratio while not clamping */ + *state = -1; /* indicates invalid state */ + + return 0; +} + +static int powerclamp_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long new_target_ratio) +{ + int ret = 0; + + new_target_ratio = clamp(new_target_ratio, 0UL, + (unsigned long) (MAX_TARGET_RATIO-1)); + if (set_target_ratio == 0 && new_target_ratio > 0) { + pr_info("Start idle injection to reduce power\n"); + set_target_ratio = new_target_ratio; + ret = start_power_clamp(); + goto exit_set; + } else if (set_target_ratio > 0 && new_target_ratio == 0) { + pr_info("Stop forced idle injection\n"); + set_target_ratio = 0; + end_power_clamp(); + } else /* adjust currently running */ { + set_target_ratio = new_target_ratio; + /* make new set_target_ratio visible to other cpus */ + smp_mb(); + } + +exit_set: + return ret; +} + +/* bind to generic thermal layer as cooling device*/ +static struct thermal_cooling_device_ops powerclamp_cooling_ops = { + .get_max_state = powerclamp_get_max_state, + .get_cur_state = powerclamp_get_cur_state, + .set_cur_state = powerclamp_set_cur_state, +}; + +/* runs on Nehalem and later */ +static const struct x86_cpu_id intel_powerclamp_ids[] = { + { X86_VENDOR_INTEL, 6, 0x1a}, + { X86_VENDOR_INTEL, 6, 0x1c}, + { X86_VENDOR_INTEL, 6, 0x1e}, + { X86_VENDOR_INTEL, 6, 0x1f}, + { X86_VENDOR_INTEL, 6, 0x25}, + { X86_VENDOR_INTEL, 6, 0x26}, + { X86_VENDOR_INTEL, 6, 0x2a}, + { X86_VENDOR_INTEL, 6, 0x2c}, + { X86_VENDOR_INTEL, 6, 0x2d}, + { X86_VENDOR_INTEL, 6, 0x2e}, + { X86_VENDOR_INTEL, 6, 0x2f}, + { X86_VENDOR_INTEL, 6, 0x3a}, + {} +}; +MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids); + +static int powerclamp_probe(void) +{ + if (!x86_match_cpu(intel_powerclamp_ids)) { + pr_err("Intel powerclamp does not run on family %d model %d\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + return -ENODEV; + } + if (!boot_cpu_has(X86_FEATURE_NONSTOP_TSC) || + !boot_cpu_has(X86_FEATURE_CONSTANT_TSC) || + !boot_cpu_has(X86_FEATURE_MWAIT) || + !boot_cpu_has(X86_FEATURE_ARAT)) + return -ENODEV; + + /* find the deepest mwait value */ + find_target_mwait(); + + return 0; +} + +static int powerclamp_debug_show(struct seq_file *m, void *unused) +{ + int i = 0; + + seq_printf(m, "controlling cpu: %d\n", control_cpu); + seq_printf(m, "pct confidence steady dynamic (compensation)\n"); + for (i = 0; i < MAX_TARGET_RATIO; i++) { + seq_printf(m, "%d\t%lu\t%lu\t%lu\n", + i, + cal_data[i].confidence, + cal_data[i].steady_comp, + cal_data[i].dynamic_comp); + } + + return 0; +} + +static int powerclamp_debug_open(struct inode *inode, + struct file *file) +{ + return single_open(file, powerclamp_debug_show, inode->i_private); +} + +static const struct file_operations powerclamp_debug_fops = { + .open = powerclamp_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static inline void powerclamp_create_debug_files(void) +{ + debug_dir = debugfs_create_dir("intel_powerclamp", NULL); + if (!debug_dir) + return; + + if (!debugfs_create_file("powerclamp_calib", S_IRUGO, debug_dir, + cal_data, &powerclamp_debug_fops)) + goto file_error; + + return; + +file_error: + debugfs_remove_recursive(debug_dir); +} + +static int powerclamp_init(void) +{ + int retval; + int bitmap_size; + + bitmap_size = BITS_TO_LONGS(num_possible_cpus()) * sizeof(long); + cpu_clamping_mask = kzalloc(bitmap_size, GFP_KERNEL); + if (!cpu_clamping_mask) + return -ENOMEM; + + /* probe cpu features and ids here */ + retval = powerclamp_probe(); + if (retval) + return retval; + /* set default limit, maybe adjusted during runtime based on feedback */ + window_size = 2; + register_hotcpu_notifier(&powerclamp_cpu_notifier); + powerclamp_thread = alloc_percpu(struct task_struct *); + cooling_dev = thermal_cooling_device_register("intel_powerclamp", NULL, + &powerclamp_cooling_ops); + if (IS_ERR(cooling_dev)) + return -ENODEV; + + if (!duration) + duration = jiffies_to_msecs(DEFAULT_DURATION_JIFFIES); + powerclamp_create_debug_files(); + + return 0; +} +module_init(powerclamp_init); + +static void powerclamp_exit(void) +{ + unregister_hotcpu_notifier(&powerclamp_cpu_notifier); + end_power_clamp(); + free_percpu(powerclamp_thread); + thermal_cooling_device_unregister(cooling_dev); + kfree(cpu_clamping_mask); + + cancel_delayed_work_sync(&poll_pkg_cstate_work); + debugfs_remove_recursive(debug_dir); +} +module_exit(powerclamp_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Arjan van de Ven "); +MODULE_AUTHOR("Jacob Pan "); +MODULE_DESCRIPTION("Package Level C-state Idle Injection for Intel CPUs"); From 3ad9524a15126c24fc37922f56a0fb5dd03c218f Mon Sep 17 00:00:00 2001 From: Amit Daniel Kachhap Date: Thu, 17 Jan 2013 01:42:18 +0000 Subject: [PATCH 1982/4607] thermal: exynos: Miscellaneous fixes to support falling threshold interrupt Below fixes are done to support falling threshold interrupt, * Falling interrupt status macro corrected according to exynos5 data sheet. * The get trend function modified to calculate trip temperature correctly. * The clearing of interrupt status in the isr is now done after handling the event that caused the interrupt. Signed-off-by: Amit Daniel Kachhap Signed-off-by: Zhang Rui --- drivers/thermal/exynos_thermal.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index 327102a4596a..cd71e24194b1 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -82,7 +82,7 @@ #define EXYNOS_TRIMINFO_RELOAD 0x1 #define EXYNOS_TMU_CLEAR_RISE_INT 0x111 -#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 16) +#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 12) #define EXYNOS_MUX_ADDR_VALUE 6 #define EXYNOS_MUX_ADDR_SHIFT 20 #define EXYNOS_TMU_TRIP_MODE_SHIFT 13 @@ -370,7 +370,14 @@ static int exynos_get_temp(struct thermal_zone_device *thermal, static int exynos_get_trend(struct thermal_zone_device *thermal, int trip, enum thermal_trend *trend) { - if (thermal->temperature >= trip) + int ret; + unsigned long trip_temp; + + ret = exynos_get_trip_temp(thermal, trip, &trip_temp); + if (ret < 0) + return ret; + + if (thermal->temperature >= trip_temp) *trend = THERMAL_TREND_RAISING; else *trend = THERMAL_TREND_DROPPING; @@ -705,20 +712,18 @@ static void exynos_tmu_work(struct work_struct *work) struct exynos_tmu_data *data = container_of(work, struct exynos_tmu_data, irq_work); + exynos_report_trigger(); mutex_lock(&data->lock); clk_enable(data->clk); - - if (data->soc == SOC_ARCH_EXYNOS) writel(EXYNOS_TMU_CLEAR_RISE_INT, data->base + EXYNOS_TMU_REG_INTCLEAR); else writel(EXYNOS4210_TMU_INTCLEAR_VAL, data->base + EXYNOS_TMU_REG_INTCLEAR); - clk_disable(data->clk); mutex_unlock(&data->lock); - exynos_report_trigger(); + enable_irq(data->irq); } From c8165dc0ea75855b0bff6e5edbe4957b8a63d021 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 24 Jan 2013 08:51:22 +0000 Subject: [PATCH 1983/4607] PM: intel_powerclamp: off by one in start_power_clamp() This value has already been clamped correctly to 0 through 49 in powerclamp_set_cur_state() so this patch doesn't actually change anything. But we should fix it anyway for consistency. set_target_ratio is used as an offset into an array with MAX_TARGET_RATIO (50) elements. Signed-off-by: Dan Carpenter Signed-off-by: Zhang Rui --- drivers/thermal/intel_powerclamp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c index a85ff38cb4e8..ab3ed907d2c3 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel_powerclamp.c @@ -504,7 +504,7 @@ static int start_power_clamp(void) return -EINVAL; } - set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO); + set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO - 1); /* prevent cpu hotplug */ get_online_cpus(); From e6e238c38bd4d42d5e2cddb2165e1a46e0fb1200 Mon Sep 17 00:00:00 2001 From: Amit Daniel Kachhap Date: Mon, 4 Feb 2013 00:30:15 +0000 Subject: [PATCH 1984/4607] thermal: sysfs: Add a new sysfs node emul_temp for thermal emulation This patch adds support to set the emulated temperature method in thermal zone (sensor). After setting this feature thermal zone may report this temperature and not the actual temperature. The emulation implementation may be based on sensor capability through platform specific handler or pure software emulation if no platform handler defined. This is useful in debugging different temperature threshold and its associated cooling action. Critical threshold's cannot be emulated. Writing 0 on this node should disable emulation. Signed-off-by: Amit Daniel Kachhap Acked-by: Kukjin Kim Signed-off-by: Zhang Rui --- Documentation/thermal/sysfs-api.txt | 13 +++++ drivers/thermal/Kconfig | 8 +++ drivers/thermal/thermal_sys.c | 79 +++++++++++++++++++++++++---- include/linux/thermal.h | 2 + 4 files changed, 91 insertions(+), 11 deletions(-) diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt index 526d4b90d6c1..6859661c9d31 100644 --- a/Documentation/thermal/sysfs-api.txt +++ b/Documentation/thermal/sysfs-api.txt @@ -55,6 +55,8 @@ temperature) and throttle appropriate devices. .get_trip_type: get the type of certain trip point. .get_trip_temp: get the temperature above which the certain trip point will be fired. + .set_emul_temp: set the emulation temperature which helps in debugging + different threshold temperature points. 1.1.2 void thermal_zone_device_unregister(struct thermal_zone_device *tz) @@ -153,6 +155,7 @@ Thermal zone device sys I/F, created once it's registered: |---trip_point_[0-*]_temp: Trip point temperature |---trip_point_[0-*]_type: Trip point type |---trip_point_[0-*]_hyst: Hysteresis value for this trip point + |---emul_temp: Emulated temperature set node Thermal cooling device sys I/F, created once it's registered: /sys/class/thermal/cooling_device[0-*]: @@ -252,6 +255,16 @@ passive Valid values: 0 (disabled) or greater than 1000 RW, Optional +emul_temp + Interface to set the emulated temperature method in thermal zone + (sensor). After setting this temperature, the thermal zone may pass + this temperature to platform emulation function if registered or + cache it locally. This is useful in debugging different temperature + threshold and its associated cooling action. This is write only node + and writing 0 on this node should disable emulation. + Unit: millidegree Celsius + WO, Optional + ***************************** * Cooling device attributes * ***************************** diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index faf38c522fa8..e4cf7fbc3a59 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -78,6 +78,14 @@ config CPU_THERMAL and not the ACPI interface. If you want this support, you should say Y here. +config THERMAL_EMULATION + bool "Thermal emulation mode support" + help + Enable this option to make a emul_temp sysfs node in thermal zone + directory to support temperature emulation. With emulation sysfs node, + user can manually input temperature and test the different trip + threshold behaviour for simulation purpose. + config SPEAR_THERMAL bool "SPEAr thermal sensor driver" depends on PLAT_SPEAR diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 0a1bf6b032ea..0675687c6de8 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -378,24 +378,54 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) monitor_thermal_zone(tz); } +static int thermal_zone_get_temp(struct thermal_zone_device *tz, + unsigned long *temp) +{ + int ret = 0, count; + unsigned long crit_temp = -1UL; + enum thermal_trip_type type; + + mutex_lock(&tz->lock); + + ret = tz->ops->get_temp(tz, temp); +#ifdef CONFIG_THERMAL_EMULATION + if (!tz->emul_temperature) + goto skip_emul; + + for (count = 0; count < tz->trips; count++) { + ret = tz->ops->get_trip_type(tz, count, &type); + if (!ret && type == THERMAL_TRIP_CRITICAL) { + ret = tz->ops->get_trip_temp(tz, count, &crit_temp); + break; + } + } + + if (ret) + goto skip_emul; + + if (*temp < crit_temp) + *temp = tz->emul_temperature; +skip_emul: +#endif + mutex_unlock(&tz->lock); + return ret; +} + static void update_temperature(struct thermal_zone_device *tz) { long temp; int ret; - mutex_lock(&tz->lock); - - ret = tz->ops->get_temp(tz, &temp); + ret = thermal_zone_get_temp(tz, &temp); if (ret) { dev_warn(&tz->device, "failed to read out thermal zone %d\n", tz->id); - goto exit; + return; } + mutex_lock(&tz->lock); tz->last_temperature = tz->temperature; tz->temperature = temp; - -exit: mutex_unlock(&tz->lock); } @@ -438,10 +468,7 @@ temp_show(struct device *dev, struct device_attribute *attr, char *buf) long temperature; int ret; - if (!tz->ops->get_temp) - return -EPERM; - - ret = tz->ops->get_temp(tz, &temperature); + ret = thermal_zone_get_temp(tz, &temperature); if (ret) return ret; @@ -701,6 +728,31 @@ policy_show(struct device *dev, struct device_attribute *devattr, char *buf) return sprintf(buf, "%s\n", tz->governor->name); } +#ifdef CONFIG_THERMAL_EMULATION +static ssize_t +emul_temp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + int ret = 0; + unsigned long temperature; + + if (kstrtoul(buf, 10, &temperature)) + return -EINVAL; + + if (!tz->ops->set_emul_temp) { + mutex_lock(&tz->lock); + tz->emul_temperature = temperature; + mutex_unlock(&tz->lock); + } else { + ret = tz->ops->set_emul_temp(tz, temperature); + } + + return ret ? ret : count; +} +static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); +#endif/*CONFIG_THERMAL_EMULATION*/ + static DEVICE_ATTR(type, 0444, type_show, NULL); static DEVICE_ATTR(temp, 0444, temp_show, NULL); static DEVICE_ATTR(mode, 0644, mode_show, mode_store); @@ -843,7 +895,7 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf) temp_input); struct thermal_zone_device *tz = temp->tz; - ret = tz->ops->get_temp(tz, &temperature); + ret = thermal_zone_get_temp(tz, &temperature); if (ret) return ret; @@ -1596,6 +1648,11 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, goto unregister; } +#ifdef CONFIG_THERMAL_EMULATION + result = device_create_file(&tz->device, &dev_attr_emul_temp); + if (result) + goto unregister; +#endif /* Create policy attribute */ result = device_create_file(&tz->device, &dev_attr_policy); if (result) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 9b78f8c6f773..f0bd7f90a90d 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -123,6 +123,7 @@ struct thermal_zone_device_ops { int (*set_trip_hyst) (struct thermal_zone_device *, int, unsigned long); int (*get_crit_temp) (struct thermal_zone_device *, unsigned long *); + int (*set_emul_temp) (struct thermal_zone_device *, unsigned long); int (*get_trend) (struct thermal_zone_device *, int, enum thermal_trend *); int (*notify) (struct thermal_zone_device *, int, @@ -165,6 +166,7 @@ struct thermal_zone_device { int polling_delay; int temperature; int last_temperature; + int emul_temperature; int passive; unsigned int forced_passive; const struct thermal_zone_device_ops *ops; From 475f41c3ab3dea3a5c60b90197baf57da01fdb5f Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 6 Feb 2013 09:34:32 +0800 Subject: [PATCH 1985/4607] Thermal: fix a wrong comment "level" parameter of get_cpu_frequency equals cooling state of cpu cooling device, and it starts from 0. Fix the misleading comment. Signed-off-by: Zhang Rui --- drivers/thermal/cpu_cooling.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 836828e29a87..455c77a961de 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -118,8 +118,8 @@ static int is_cpufreq_valid(int cpu) /** * get_cpu_frequency - get the absolute value of frequency from level. * @cpu: cpu for which frequency is fetched. - * @level: level of frequency of the CPU - * e.g level=1 --> 1st MAX FREQ, LEVEL=2 ---> 2nd MAX FREQ, .... etc + * @level: level of frequency, equals cooling state of cpu cooling device + * e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc */ static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level) { From 5e20b2e51dd13c569ea1ff65c2a57d00a84a81b0 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 6 Feb 2013 14:02:12 +0800 Subject: [PATCH 1986/4607] Thermal: fix a build warning when CONFIG_THERMAL_EMULATION cleared Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 0675687c6de8..6472e7e9f969 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -381,9 +381,12 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) static int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp) { - int ret = 0, count; + int ret = 0; +#ifdef CONFIG_THERMAL_EMULATION + int count; unsigned long crit_temp = -1UL; enum thermal_trip_type type; +#endif mutex_lock(&tz->lock); From 9dde8f86085d283042718f88eed017eccad73ab9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:02:51 +0000 Subject: [PATCH 1987/4607] thermal: rcar: use parenthesis on macro Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 89979ff10e27..47b2b227c91e 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -47,7 +47,7 @@ struct rcar_thermal_priv { }; #define MCELSIUS(temp) ((temp) * 1000) -#define rcar_zone_to_priv(zone) (zone->devdata) +#define rcar_zone_to_priv(zone) ((zone)->devdata) /* * basic functions From f8f53e1874c2dfddf4c6dc69008ba85d6de4d944 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:11 +0000 Subject: [PATCH 1988/4607] thermal: rcar: enable CPCTL to use hardware TSC deciding If CPCTL was 1 on R-Car thermal, the thermal comparator offset is automatically decided by hardware. And this CPCTL is the conditions which validate interrupt. This patch enabled CPCTL. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 87 ++++++++++------------------------ 1 file changed, 24 insertions(+), 63 deletions(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 47b2b227c91e..068b2a1c5c15 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -33,7 +33,7 @@ #define THSSR 0x30 /* THSCR */ -#define CPTAP 0xf +#define CPCTL (1 << 12) /* THSSR */ #define CTEMP 0x3f @@ -43,11 +43,11 @@ struct rcar_thermal_priv { void __iomem *base; struct device *dev; spinlock_t lock; - u32 comp; }; #define MCELSIUS(temp) ((temp) * 1000) #define rcar_zone_to_priv(zone) ((zone)->devdata) +#define rcar_priv_to_dev(priv) ((priv)->dev) /* * basic functions @@ -103,79 +103,41 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, unsigned long *temp) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); - int val, min, max, tmp; + struct device *dev = rcar_priv_to_dev(priv); + int i; + int ctemp, old, new; - tmp = -200; /* default */ - while (1) { - if (priv->comp < 1 || priv->comp > 12) { - dev_err(priv->dev, - "THSSR invalid data (%d)\n", priv->comp); - priv->comp = 4; /* for next thermal */ - return -EINVAL; - } - - /* - * THS comparator offset and the reference temperature - * - * Comparator | reference | Temperature field - * offset | temperature | measurement - * | (degrees C) | (degrees C) - * -------------+---------------+------------------- - * 1 | -45 | -45 to -30 - * 2 | -30 | -30 to -15 - * 3 | -15 | -15 to 0 - * 4 | 0 | 0 to +15 - * 5 | +15 | +15 to +30 - * 6 | +30 | +30 to +45 - * 7 | +45 | +45 to +60 - * 8 | +60 | +60 to +75 - * 9 | +75 | +75 to +90 - * 10 | +90 | +90 to +105 - * 11 | +105 | +105 to +120 - * 12 | +120 | +120 to +135 - */ - - /* calculate thermal limitation */ - min = (priv->comp * 15) - 60; - max = min + 15; + /* + * TSC decides a value of CPTAP automatically, + * and this is the conditions which validate interrupt. + */ + rcar_thermal_bset(priv, THSCR, CPCTL, CPCTL); + ctemp = 0; + old = ~0; + for (i = 0; i < 128; i++) { /* * we need to wait 300us after changing comparator offset * to get stable temperature. * see "Usage Notes" on datasheet */ - rcar_thermal_bset(priv, THSCR, CPTAP, priv->comp); udelay(300); - /* calculate current temperature */ - val = rcar_thermal_read(priv, THSSR) & CTEMP; - val = (val * 5) - 65; - - dev_dbg(priv->dev, "comp/min/max/val = %d/%d/%d/%d\n", - priv->comp, min, max, val); - - /* - * If val is same as min/max, then, - * it should try again on next comparator. - * But the val might be correct temperature. - * Keep it on "tmp" and compare with next val. - */ - if (tmp == val) - break; - - if (val <= min) { - tmp = min; - priv->comp--; /* try again */ - } else if (val >= max) { - tmp = max; - priv->comp++; /* try again */ - } else { - tmp = val; + new = rcar_thermal_read(priv, THSSR) & CTEMP; + if (new == old) { + ctemp = new; break; } + old = new; } - *temp = MCELSIUS(tmp); + if (!ctemp) { + dev_err(dev, "thermal sensor was broken\n"); + return -EINVAL; + } + + *temp = MCELSIUS((ctemp * 5) - 65); + return 0; } @@ -262,7 +224,6 @@ static int rcar_thermal_probe(struct platform_device *pdev) return -ENOMEM; } - priv->comp = 4; /* basic setup */ priv->dev = &pdev->dev; spin_lock_init(&priv->lock); priv->base = devm_ioremap_nocache(&pdev->dev, From b2bbc6a2ace78eaca2f6482b58b984519aa783ac Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:22 +0000 Subject: [PATCH 1989/4607] thermal: rcar: use mutex lock instead of spin lock Current R-Car thermal driver is using spin lock for each registers read/write, but it is pointless lock. This lock is required while reading temperature, but it needs long wait (= 300ms). So, this patch used mutex lock while reading temperature, instead of spin lock for each registers. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 068b2a1c5c15..e19b267f76d6 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -42,7 +42,7 @@ struct rcar_thermal_priv { void __iomem *base; struct device *dev; - spinlock_t lock; + struct mutex lock; }; #define MCELSIUS(temp) ((temp) * 1000) @@ -54,46 +54,26 @@ struct rcar_thermal_priv { */ static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) { - unsigned long flags; - u32 ret; - - spin_lock_irqsave(&priv->lock, flags); - - ret = ioread32(priv->base + reg); - - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; + return ioread32(priv->base + reg); } #if 0 /* no user at this point */ static void rcar_thermal_write(struct rcar_thermal_priv *priv, u32 reg, u32 data) { - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - iowrite32(data, priv->base + reg); - - spin_unlock_irqrestore(&priv->lock, flags); } #endif static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, u32 mask, u32 data) { - unsigned long flags; u32 val; - spin_lock_irqsave(&priv->lock, flags); - val = ioread32(priv->base + reg); val &= ~mask; val |= (data & mask); iowrite32(val, priv->base + reg); - - spin_unlock_irqrestore(&priv->lock, flags); } /* @@ -107,6 +87,8 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int i; int ctemp, old, new; + mutex_lock(&priv->lock); + /* * TSC decides a value of CPTAP automatically, * and this is the conditions which validate interrupt. @@ -138,6 +120,8 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, *temp = MCELSIUS((ctemp * 5) - 65); + mutex_unlock(&priv->lock); + return 0; } @@ -225,7 +209,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) } priv->dev = &pdev->dev; - spin_lock_init(&priv->lock); + mutex_init(&priv->lock); priv->base = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); if (!priv->base) { From 3676d1dd3d3069ca70b8075c0e86482cbaa01c2f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:33 +0000 Subject: [PATCH 1990/4607] thermal: rcar: multi channel support R-Car thermal sensor will be multi channel sensor in next generation. But "IRQ controlling method" and "register mapping" are different between old/new chip. This patch adds multi sensor support. Then, this driver assumes there is common register if platform has IRQ resource. The IRQ will be supported soon. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 128 ++++++++++++++++++++++++--------- 1 file changed, 94 insertions(+), 34 deletions(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index e19b267f76d6..1ba02770153a 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -38,16 +38,27 @@ /* THSSR */ #define CTEMP 0x3f +struct rcar_thermal_common { + void __iomem *base; + struct device *dev; + struct list_head head; +}; struct rcar_thermal_priv { void __iomem *base; - struct device *dev; + struct rcar_thermal_common *common; + struct thermal_zone_device *zone; struct mutex lock; + struct list_head list; }; +#define rcar_thermal_for_each_priv(pos, common) \ + list_for_each_entry(pos, &common->head, list) + #define MCELSIUS(temp) ((temp) * 1000) #define rcar_zone_to_priv(zone) ((zone)->devdata) -#define rcar_priv_to_dev(priv) ((priv)->dev) +#define rcar_priv_to_dev(priv) ((priv)->common->dev) +#define rcar_has_irq_support(priv) ((priv)->common->base) /* * basic functions @@ -129,6 +140,7 @@ static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, int trip, enum thermal_trip_type *type) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + struct device *dev = rcar_priv_to_dev(priv); /* see rcar_thermal_get_temp() */ switch (trip) { @@ -136,7 +148,7 @@ static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, *type = THERMAL_TRIP_CRITICAL; break; default: - dev_err(priv->dev, "rcar driver trip error\n"); + dev_err(dev, "rcar driver trip error\n"); return -EINVAL; } @@ -147,6 +159,7 @@ static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, int trip, unsigned long *temp) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + struct device *dev = rcar_priv_to_dev(priv); /* see rcar_thermal_get_temp() */ switch (trip) { @@ -154,7 +167,7 @@ static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, *temp = MCELSIUS(90); break; default: - dev_err(priv->dev, "rcar driver trip error\n"); + dev_err(dev, "rcar driver trip error\n"); return -EINVAL; } @@ -165,12 +178,12 @@ static int rcar_thermal_notify(struct thermal_zone_device *zone, int trip, enum thermal_trip_type type) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + struct device *dev = rcar_priv_to_dev(priv); switch (type) { case THERMAL_TRIP_CRITICAL: /* FIXME */ - dev_warn(priv->dev, - "Thermal reached to critical temperature\n"); + dev_warn(dev, "Thermal reached to critical temperature\n"); machine_power_off(); break; default: @@ -192,51 +205,98 @@ static struct thermal_zone_device_ops rcar_thermal_zone_ops = { */ static int rcar_thermal_probe(struct platform_device *pdev) { - struct thermal_zone_device *zone; + struct rcar_thermal_common *common; struct rcar_thermal_priv *priv; - struct resource *res; + struct device *dev = &pdev->dev; + struct resource *res, *irq; + int mres = 0; + int i; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Could not get platform resource\n"); - return -ENODEV; - } - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(&pdev->dev, "Could not allocate priv\n"); + common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); + if (!common) { + dev_err(dev, "Could not allocate common\n"); return -ENOMEM; } - priv->dev = &pdev->dev; - mutex_init(&priv->lock); - priv->base = devm_ioremap_nocache(&pdev->dev, - res->start, resource_size(res)); - if (!priv->base) { - dev_err(&pdev->dev, "Unable to ioremap thermal register\n"); - return -ENOMEM; + INIT_LIST_HEAD(&common->head); + common->dev = dev; + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (irq) { + /* + * platform has IRQ support. + * Then, drier use common register + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); + if (!res) { + dev_err(dev, "Could not get platform resource\n"); + return -ENODEV; + } + + /* + * rcar_has_irq_support() will be enabled + */ + common->base = devm_request_and_ioremap(dev, res); + if (!common->base) { + dev_err(dev, "Unable to ioremap thermal register\n"); + return -ENOMEM; + } } - zone = thermal_zone_device_register("rcar_thermal", 1, 0, priv, - &rcar_thermal_zone_ops, NULL, 0, - IDLE_INTERVAL); - if (IS_ERR(zone)) { - dev_err(&pdev->dev, "thermal zone device is NULL\n"); - return PTR_ERR(zone); + for (i = 0;; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); + if (!res) + break; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev, "Could not allocate priv\n"); + return -ENOMEM; + } + + priv->base = devm_request_and_ioremap(dev, res); + if (!priv->base) { + dev_err(dev, "Unable to ioremap priv register\n"); + return -ENOMEM; + } + + priv->common = common; + mutex_init(&priv->lock); + INIT_LIST_HEAD(&priv->list); + + priv->zone = thermal_zone_device_register("rcar_thermal", + 1, 0, priv, + &rcar_thermal_zone_ops, NULL, 0, + IDLE_INTERVAL); + if (IS_ERR(priv->zone)) { + dev_err(dev, "can't register thermal zone\n"); + goto error_unregister; + } + + list_move_tail(&priv->list, &common->head); } - platform_set_drvdata(pdev, zone); + platform_set_drvdata(pdev, common); - dev_info(&pdev->dev, "proved\n"); + dev_info(dev, "%d sensor proved\n", i); return 0; + +error_unregister: + rcar_thermal_for_each_priv(priv, common) + thermal_zone_device_unregister(priv->zone); + + return -ENODEV; } static int rcar_thermal_remove(struct platform_device *pdev) { - struct thermal_zone_device *zone = platform_get_drvdata(pdev); + struct rcar_thermal_common *common = platform_get_drvdata(pdev); + struct rcar_thermal_priv *priv; + + rcar_thermal_for_each_priv(priv, common) + thermal_zone_device_unregister(priv->zone); - thermal_zone_device_unregister(zone); platform_set_drvdata(pdev, NULL); return 0; From e9137a582fcbe36d9dedb8d7f6902a059154b14e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:46 +0000 Subject: [PATCH 1991/4607] thermal: rcar: add read/write functions for common/priv data R-Car thermal driver will use struct common in next feature (interrupt support). But the register address is different between struct priv and common. This patch adds read/write functions for struct common, and use macro technique to avoid wrong register access. This is preparation patch for next feature (interrupt support), therefore, there is no user to use this common read/write function at this point. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 48 +++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 1ba02770153a..cf6aa98956b9 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -29,8 +29,8 @@ #define IDLE_INTERVAL 5000 -#define THSCR 0x2c -#define THSSR 0x30 +#define REG_THSCR 0x2c +#define REG_THSSR 0x30 /* THSCR */ #define CPCTL (1 << 12) @@ -63,21 +63,55 @@ struct rcar_thermal_priv { /* * basic functions */ -static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) +#if 0 +#define rcar_thermal_common_read(c, r) \ + _rcar_thermal_common_read(c, COMMON_ ##r) +static u32 _rcar_thermal_common_read(struct rcar_thermal_common *common, + u32 reg) +{ + return ioread32(common->base + reg); +} + +#define rcar_thermal_common_write(c, r, d) \ + _rcar_thermal_common_write(c, COMMON_ ##r, d) +static void _rcar_thermal_common_write(struct rcar_thermal_common *common, + u32 reg, u32 data) +{ + iowrite32(data, common->base + reg); +} + +#define rcar_thermal_common_bset(c, r, m, d) \ + _rcar_thermal_common_bset(c, COMMON_ ##r, m, d) +static void _rcar_thermal_common_bset(struct rcar_thermal_common *common, + u32 reg, u32 mask, u32 data) +{ + u32 val; + + val = ioread32(common->base + reg); + val &= ~mask; + val |= (data & mask); + iowrite32(val, common->base + reg); +} +#endif + +#define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r) +static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) { return ioread32(priv->base + reg); } #if 0 /* no user at this point */ -static void rcar_thermal_write(struct rcar_thermal_priv *priv, - u32 reg, u32 data) +#define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d) +static void _rcar_thermal_write(struct rcar_thermal_priv *priv, + u32 reg, u32 data) { iowrite32(data, priv->base + reg); } #endif -static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, - u32 mask, u32 data) +#define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d) +static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, + u32 mask, u32 data) { u32 val; From e0a5172e9eec7f0d3c476e013c51dab62f3fc666 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:04:48 +0000 Subject: [PATCH 1992/4607] thermal: rcar: add interrupt support This patch adds interrupt support for R-Car thermal driver. New generation R-Car thermal sensor interrupt controller was different from old generation. This patch supports new generation sensor only, since the old generation interrupt controller had never been used before, and will never be used in the future. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 159 +++++++++++++++++++++++++++++++-- 1 file changed, 150 insertions(+), 9 deletions(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index cf6aa98956b9..80aae3cf65eb 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -19,6 +19,8 @@ */ #include #include +#include +#include #include #include #include @@ -29,8 +31,15 @@ #define IDLE_INTERVAL 5000 +#define COMMON_STR 0x00 +#define COMMON_ENR 0x04 +#define COMMON_INTMSK 0x0c + +#define REG_POSNEG 0x20 +#define REG_FILONOFF 0x28 #define REG_THSCR 0x2c #define REG_THSSR 0x30 +#define REG_INTCTRL 0x34 /* THSCR */ #define CPCTL (1 << 12) @@ -42,14 +51,18 @@ struct rcar_thermal_common { void __iomem *base; struct device *dev; struct list_head head; + spinlock_t lock; }; struct rcar_thermal_priv { void __iomem *base; struct rcar_thermal_common *common; struct thermal_zone_device *zone; + struct delayed_work work; struct mutex lock; struct list_head list; + int id; + int ctemp; }; #define rcar_thermal_for_each_priv(pos, common) \ @@ -59,11 +72,17 @@ struct rcar_thermal_priv { #define rcar_zone_to_priv(zone) ((zone)->devdata) #define rcar_priv_to_dev(priv) ((priv)->common->dev) #define rcar_has_irq_support(priv) ((priv)->common->base) +#define rcar_id_to_shift(priv) ((priv)->id * 8) + +#ifdef DEBUG +# define rcar_force_update_temp(priv) 1 +#else +# define rcar_force_update_temp(priv) 0 +#endif /* * basic functions */ -#if 0 #define rcar_thermal_common_read(c, r) \ _rcar_thermal_common_read(c, COMMON_ ##r) static u32 _rcar_thermal_common_read(struct rcar_thermal_common *common, @@ -92,7 +111,6 @@ static void _rcar_thermal_common_bset(struct rcar_thermal_common *common, val |= (data & mask); iowrite32(val, common->base + reg); } -#endif #define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r) static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) @@ -100,14 +118,12 @@ static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) return ioread32(priv->base + reg); } -#if 0 /* no user at this point */ #define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d) static void _rcar_thermal_write(struct rcar_thermal_priv *priv, u32 reg, u32 data) { iowrite32(data, priv->base + reg); } -#endif #define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d) static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, @@ -124,10 +140,8 @@ static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, /* * zone device functions */ -static int rcar_thermal_get_temp(struct thermal_zone_device *zone, - unsigned long *temp) +static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) { - struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); struct device *dev = rcar_priv_to_dev(priv); int i; int ctemp, old, new; @@ -163,8 +177,37 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, return -EINVAL; } - *temp = MCELSIUS((ctemp * 5) - 65); + /* + * enable IRQ + */ + if (rcar_has_irq_support(priv)) { + rcar_thermal_write(priv, FILONOFF, 0); + /* enable Rising/Falling edge interrupt */ + rcar_thermal_write(priv, POSNEG, 0x1); + rcar_thermal_write(priv, INTCTRL, (((ctemp - 0) << 8) | + ((ctemp - 1) << 0))); + } + + dev_dbg(dev, "thermal%d %d -> %d\n", priv->id, priv->ctemp, ctemp); + + priv->ctemp = ctemp; + + mutex_unlock(&priv->lock); + + return 0; +} + +static int rcar_thermal_get_temp(struct thermal_zone_device *zone, + unsigned long *temp) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + if (!rcar_has_irq_support(priv) || rcar_force_update_temp(priv)) + rcar_thermal_update_temp(priv); + + mutex_lock(&priv->lock); + *temp = MCELSIUS((priv->ctemp * 5) - 65); mutex_unlock(&priv->lock); return 0; @@ -234,6 +277,82 @@ static struct thermal_zone_device_ops rcar_thermal_zone_ops = { .notify = rcar_thermal_notify, }; +/* + * interrupt + */ +#define rcar_thermal_irq_enable(p) _rcar_thermal_irq_ctrl(p, 1) +#define rcar_thermal_irq_disable(p) _rcar_thermal_irq_ctrl(p, 0) +static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable) +{ + struct rcar_thermal_common *common = priv->common; + unsigned long flags; + u32 mask = 0x3 << rcar_id_to_shift(priv); /* enable Rising/Falling */ + + spin_lock_irqsave(&common->lock, flags); + + rcar_thermal_common_bset(common, INTMSK, mask, enable ? 0 : mask); + + spin_unlock_irqrestore(&common->lock, flags); +} + +static void rcar_thermal_work(struct work_struct *work) +{ + struct rcar_thermal_priv *priv; + + priv = container_of(work, struct rcar_thermal_priv, work.work); + + rcar_thermal_update_temp(priv); + rcar_thermal_irq_enable(priv); + thermal_zone_device_update(priv->zone); +} + +static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) +{ + struct device *dev = rcar_priv_to_dev(priv); + + status = (status >> rcar_id_to_shift(priv)) & 0x3; + + if (status & 0x3) { + dev_dbg(dev, "thermal%d %s%s\n", + priv->id, + (status & 0x2) ? "Rising " : "", + (status & 0x1) ? "Falling" : ""); + } + + return status; +} + +static irqreturn_t rcar_thermal_irq(int irq, void *data) +{ + struct rcar_thermal_common *common = data; + struct rcar_thermal_priv *priv; + unsigned long flags; + u32 status, mask; + + spin_lock_irqsave(&common->lock, flags); + + mask = rcar_thermal_common_read(common, INTMSK); + status = rcar_thermal_common_read(common, STR); + rcar_thermal_common_write(common, STR, 0x000F0F0F & mask); + + spin_unlock_irqrestore(&common->lock, flags); + + status = status & ~mask; + + /* + * check the status + */ + rcar_thermal_for_each_priv(priv, common) { + if (rcar_thermal_had_changed(priv, status)) { + rcar_thermal_irq_disable(priv); + schedule_delayed_work(&priv->work, + msecs_to_jiffies(300)); + } + } + + return IRQ_HANDLED; +} + /* * platform functions */ @@ -245,6 +364,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) struct resource *res, *irq; int mres = 0; int i; + int idle = IDLE_INTERVAL; common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); if (!common) { @@ -253,10 +373,13 @@ static int rcar_thermal_probe(struct platform_device *pdev) } INIT_LIST_HEAD(&common->head); + spin_lock_init(&common->lock); common->dev = dev; irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (irq) { + int ret; + /* * platform has IRQ support. * Then, drier use common register @@ -267,6 +390,13 @@ static int rcar_thermal_probe(struct platform_device *pdev) return -ENODEV; } + ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0, + dev_name(dev), common); + if (ret) { + dev_err(dev, "irq request failed\n "); + return ret; + } + /* * rcar_has_irq_support() will be enabled */ @@ -275,6 +405,11 @@ static int rcar_thermal_probe(struct platform_device *pdev) dev_err(dev, "Unable to ioremap thermal register\n"); return -ENOMEM; } + + /* enable temperature comparation */ + rcar_thermal_common_write(common, ENR, 0x00030303); + + idle = 0; /* polling delaye is not needed */ } for (i = 0;; i++) { @@ -295,19 +430,25 @@ static int rcar_thermal_probe(struct platform_device *pdev) } priv->common = common; + priv->id = i; mutex_init(&priv->lock); INIT_LIST_HEAD(&priv->list); + INIT_DELAYED_WORK(&priv->work, rcar_thermal_work); + rcar_thermal_update_temp(priv); priv->zone = thermal_zone_device_register("rcar_thermal", 1, 0, priv, &rcar_thermal_zone_ops, NULL, 0, - IDLE_INTERVAL); + idle); if (IS_ERR(priv->zone)) { dev_err(dev, "can't register thermal zone\n"); goto error_unregister; } list_move_tail(&priv->list, &common->head); + + if (rcar_has_irq_support(priv)) + rcar_thermal_irq_enable(priv); } platform_set_drvdata(pdev, common); From e6e053f4e47634c07993cb31893556d24e18b65e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:26:13 +0000 Subject: [PATCH 1993/4607] thermal: rcar: remove machine_power_off() from rcar_thermal_notify() Machine/System power-off is run in thermal frame work if it become critical temperature. This patch removed pointless machine_power_off() from thermal_zone_device_ops :: .notify Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 80aae3cf65eb..2eebcadb4c99 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -261,7 +261,6 @@ static int rcar_thermal_notify(struct thermal_zone_device *zone, case THERMAL_TRIP_CRITICAL: /* FIXME */ dev_warn(dev, "Thermal reached to critical temperature\n"); - machine_power_off(); break; default: break; From 82bed4d5f8189a2f9db5d1bb6489ac2615426d65 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 6 Feb 2013 14:03:13 +1100 Subject: [PATCH 1994/4607] block: remove new __devinit/exit annotations on ramsam driver Signed-off-by: Stephen Rothwell Signed-off-by: Jens Axboe --- drivers/block/rsxx/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/block/rsxx/core.c b/drivers/block/rsxx/core.c index f75219140e70..83dadbee0375 100644 --- a/drivers/block/rsxx/core.c +++ b/drivers/block/rsxx/core.c @@ -319,7 +319,7 @@ static int rsxx_compatibility_check(struct rsxx_cardinfo *card) return 0; } -static int __devinit rsxx_pci_probe(struct pci_dev *dev, +static int rsxx_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct rsxx_cardinfo *card; @@ -510,7 +510,7 @@ failed_ida_get: return st; } -static void __devexit rsxx_pci_remove(struct pci_dev *dev) +static void rsxx_pci_remove(struct pci_dev *dev) { struct rsxx_cardinfo *card = pci_get_drvdata(dev); unsigned long flags; @@ -608,7 +608,7 @@ static struct pci_driver rsxx_pci_driver = { .name = DRIVER_NAME, .id_table = rsxx_pci_ids, .probe = rsxx_pci_probe, - .remove = __devexit_p(rsxx_pci_remove), + .remove = rsxx_pci_remove, .suspend = rsxx_pci_suspend, .shutdown = rsxx_pci_shutdown, }; From 07b64b837d90cf20fad6b4965aa1fecdb4a88e9b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Jan 2013 08:18:55 -0300 Subject: [PATCH 1995/4607] [media] DocBook: fix various validation errors Fixed the following errors (with exception of the SVG errors): GEN /home/hans/work/src/v4l/media-git/Documentation/DocBook//v4l2.xml rm -rf Documentation/DocBook/index.html; echo '

Linux Kernel HTML Documentation

' >> Documentation/DocBook/index.html && echo '

Kernel Version: 3.8.0-rc1

' >> Documentation/DocBook/index.html && cat Documentation/DocBook/media_api.html >> Documentation/DocBook/index.html /tmp/x.xml:883: element revremark: validity error : Element structname is not declared in revremark list of possible children /tmp/x.xml:883: element revremark: validity error : Element xref is not declared in revremark list of possible children /tmp/x.xml:9580: element xref: validity error : Element xref was declared EMPTY this one has content /tmp/x.xml:13508: element link: validity error : Element link does not carry attribute linkend /tmp/x.xml:13508: element link: validity error : No declaration for attribute linked of element link /tmp/x.xml:16986: element imagedata: validity error : Value "SVG" for attribute format of imagedata is not among the enumerated set /tmp/x.xml:17003: element imagedata: validity error : Value "SVG" for attribute format of imagedata is not among the enumerated set /tmp/x.xml:17022: element imagedata: validity error : Value "SVG" for attribute format of imagedata is not among the enumerated set Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/common.xml | 2 +- Documentation/DocBook/media/v4l/io.xml | 4 +-- .../DocBook/media/v4l/pixfmt-srggb10alaw8.xml | 2 +- Documentation/DocBook/media/v4l/v4l2.xml | 4 +-- .../DocBook/media/v4l/vidioc-expbuf.xml | 30 ++++++++----------- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/Documentation/DocBook/media/v4l/common.xml b/Documentation/DocBook/media/v4l/common.xml index 73c6847436c9..ae06afbbb3a9 100644 --- a/Documentation/DocBook/media/v4l/common.xml +++ b/Documentation/DocBook/media/v4l/common.xml @@ -609,7 +609,7 @@ to zero and the VIDIOC_G_STD, Applications can make use of the and flags to determine whether the video standard ioctls are available for the device. -&ENOTTY;. + See for a rationale. Probably even USB cameras follow some well known video standard. It might have been better to explicitly indicate elsewhere if a device cannot live diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index 2c4646d504c2..e6c58559ca6b 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -477,7 +477,7 @@ rest should be evident. Experimental - This is an experimental + This is an experimental interface and may change in the future. @@ -488,7 +488,7 @@ DMA buffer from userspace using a file descriptor previously exported for a different or the same device (known as the importer role), or both. This section describes the DMABUF importer role API in V4L2. - Refer to DMABUF exporting for + Refer to DMABUF exporting for details about exporting V4L2 buffers as DMABUF file descriptors. Input and output devices support the streaming I/O method when the diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml index c934192e98ce..29acc2098cc2 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10alaw8.xml @@ -29,6 +29,6 @@ formats with 10 bits per color compressed to 8 bits each, using the A-LAW algorithm. Each color component consumes 8 bits of memory. In other respects this format is similar to - . + . diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index c3851a2fb50d..a3cce18384e9 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -143,9 +143,7 @@ applications. --> 3.9 2012-12-03 sa, sn - Added timestamp types to - v4l2_buffer, see . + Added timestamp types to v4l2_buffer. Added V4L2_EVENT_CTRL_CH_RANGE control event changes flag, see . diff --git a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml index 72dfbd20a802..e287c8fc803b 100644 --- a/Documentation/DocBook/media/v4l/vidioc-expbuf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-expbuf.xml @@ -83,15 +83,14 @@ descriptor. The application may pass it to other DMABUF-aware devices. Refer to DMABUF importing for details about importing DMABUF files into V4L2 nodes. It is recommended to close a DMABUF file when it is no longer used to allow the associated memory to be reclaimed. - - -
- Examples - - Exporting a buffer. - + + Examples + + + Exporting a buffer. + int buffer_export(int v4lfd, &v4l2-buf-type; bt, int index, int *dmafd) { &v4l2-exportbuffer; expbuf; @@ -108,12 +107,12 @@ int buffer_export(int v4lfd, &v4l2-buf-type; bt, int index, int *dmafd) return 0; } - - + + - - Exporting a buffer using the multi-planar API. - + + Exporting a buffer using the multi-planar API. + int buffer_export_mp(int v4lfd, &v4l2-buf-type; bt, int index, int dmafd[], int n_planes) { @@ -137,12 +136,9 @@ int buffer_export_mp(int v4lfd, &v4l2-buf-type; bt, int index, return 0; } - - -
-
+ + - struct <structname>v4l2_exportbuffer</structname> From a8b8a88a9e3042d41326c854272c881664acba1c Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 29 Jan 2013 14:36:31 +0100 Subject: [PATCH 1996/4607] iommu: Make sure DOMAIN_ATTR_MAX is really the maximum Move it to the end of the list. Signed-off-by: Joerg Roedel --- include/linux/iommu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index f3b99e1c1042..7e6ce7260b1c 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -58,8 +58,8 @@ struct iommu_domain { #define IOMMU_CAP_INTR_REMAP 0x2 /* isolates device intrs */ enum iommu_attr { - DOMAIN_ATTR_MAX, DOMAIN_ATTR_GEOMETRY, + DOMAIN_ATTR_MAX, }; #ifdef CONFIG_IOMMU_API From 57886518a8cdd319a04f5ea06a5f7ffcb8a93120 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 29 Jan 2013 13:41:09 +0100 Subject: [PATCH 1997/4607] iommu: Check for valid pgsize_bitmap in iommu_map/unmap In case the page-size bitmap is zero the code path in iommu_map and iommu_unmap is undefined. Make it defined and return -ENODEV in this case. Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 1065a1a19478..4e6d6f857e6f 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -734,7 +734,8 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova, size_t orig_size = size; int ret = 0; - if (unlikely(domain->ops->map == NULL)) + if (unlikely(domain->ops->unmap == NULL || + domain->ops->pgsize_bitmap == 0UL)) return -ENODEV; /* find out the minimum page size supported */ @@ -808,7 +809,8 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) size_t unmapped_page, unmapped = 0; unsigned int min_pagesz; - if (unlikely(domain->ops->unmap == NULL)) + if (unlikely(domain->ops->unmap == NULL || + domain->ops->pgsize_bitmap == 0UL)) return -ENODEV; /* find out the minimum page size supported */ From d2e121601619631517409cba34e50db3cbff5852 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 29 Jan 2013 13:49:04 +0100 Subject: [PATCH 1998/4607] iommu: Implement DOMAIN_ATTR_PAGING attribute This attribute of a domain can be queried to find out if the domain supports setting up page-tables using the iommu_map() and iommu_unmap() functions. Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 5 +++++ include/linux/iommu.h | 1 + 2 files changed, 6 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 4e6d6f857e6f..0e0e5f2e0ccc 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -869,6 +869,7 @@ int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data) { struct iommu_domain_geometry *geometry; + bool *paging; int ret = 0; switch (attr) { @@ -876,6 +877,10 @@ int iommu_domain_get_attr(struct iommu_domain *domain, geometry = data; *geometry = domain->geometry; + break; + case DOMAIN_ATTR_PAGING: + paging = data; + *paging = (domain->ops->pgsize_bitmap != 0UL); break; default: if (!domain->ops->domain_get_attr) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 7e6ce7260b1c..26066f54a849 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -59,6 +59,7 @@ struct iommu_domain { enum iommu_attr { DOMAIN_ATTR_GEOMETRY, + DOMAIN_ATTR_PAGING, DOMAIN_ATTR_MAX, }; From d7787d579cbef9f8079104759a2259fc916c688c Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Tue, 29 Jan 2013 14:26:20 +0100 Subject: [PATCH 1999/4607] iommu: Add domain window handling functions Add the iommu_domain_window_enable() and iommu_domain_window_disable() functions to the IOMMU-API. These functions will be used to setup domains that are based on subwindows and not on paging. Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 20 ++++++++++++++++++++ include/linux/iommu.h | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 0e0e5f2e0ccc..b3aced7356cc 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -852,6 +852,26 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) } EXPORT_SYMBOL_GPL(iommu_unmap); + +int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, + phys_addr_t paddr, u64 size) +{ + if (unlikely(domain->ops->domain_window_enable == NULL)) + return -ENODEV; + + return domain->ops->domain_window_enable(domain, wnd_nr, paddr, size); +} +EXPORT_SYMBOL_GPL(iommu_domain_window_enable); + +void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr) +{ + if (unlikely(domain->ops->domain_window_disable == NULL)) + return; + + return domain->ops->domain_window_disable(domain, wnd_nr); +} +EXPORT_SYMBOL_GPL(iommu_domain_window_disable); + static int __init iommu_init(void) { iommu_group_kset = kset_create_and_add("iommu_groups", diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 26066f54a849..5ea3d7250917 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -101,6 +101,12 @@ struct iommu_ops { enum iommu_attr attr, void *data); int (*domain_set_attr)(struct iommu_domain *domain, enum iommu_attr attr, void *data); + + /* Window handling functions */ + int (*domain_window_enable)(struct iommu_domain *domain, u32 wnd_nr, + phys_addr_t paddr, u64 size); + void (*domain_window_disable)(struct iommu_domain *domain, u32 wnd_nr); + unsigned long pgsize_bitmap; }; @@ -158,6 +164,10 @@ extern int iommu_domain_get_attr(struct iommu_domain *domain, enum iommu_attr, extern int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr, void *data); +/* Window handling function prototypes */ +extern int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr, + phys_addr_t offset, u64 size); +extern void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr); /** * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework * @domain: the iommu domain where the fault has happened @@ -240,6 +250,18 @@ static inline int iommu_unmap(struct iommu_domain *domain, unsigned long iova, return -ENODEV; } +static inline int iommu_domain_window_enable(struct iommu_domain *domain, + u32 wnd_nr, phys_addr_t paddr, + u64 size) +{ + return -ENODEV; +} + +static inline void iommu_domain_window_disable(struct iommu_domain *domain, + u32 wnd_nr) +{ +} + static inline phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova) { From 693567125bde1966a095267a9d8ca1b8d40f59ee Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Mon, 4 Feb 2013 14:00:01 +0100 Subject: [PATCH 2000/4607] iommu: Add DOMAIN_ATTR_WINDOWS domain attribute This attribute can be used to set and get the number of subwindows on IOMMUs that are window-based. Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 33 ++++++++++++++++++++++++++++++--- include/linux/iommu.h | 5 +++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index b3aced7356cc..b972d430d92b 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -891,6 +891,7 @@ int iommu_domain_get_attr(struct iommu_domain *domain, struct iommu_domain_geometry *geometry; bool *paging; int ret = 0; + u32 *count; switch (attr) { case DOMAIN_ATTR_GEOMETRY: @@ -901,6 +902,15 @@ int iommu_domain_get_attr(struct iommu_domain *domain, case DOMAIN_ATTR_PAGING: paging = data; *paging = (domain->ops->pgsize_bitmap != 0UL); + break; + case DOMAIN_ATTR_WINDOWS: + count = data; + + if (domain->ops->domain_get_windows != NULL) + *count = domain->ops->domain_get_windows(domain); + else + ret = -ENODEV; + break; default: if (!domain->ops->domain_get_attr) @@ -916,9 +926,26 @@ EXPORT_SYMBOL_GPL(iommu_domain_get_attr); int iommu_domain_set_attr(struct iommu_domain *domain, enum iommu_attr attr, void *data) { - if (!domain->ops->domain_set_attr) - return -EINVAL; + int ret = 0; + u32 *count; - return domain->ops->domain_set_attr(domain, attr, data); + switch (attr) { + case DOMAIN_ATTR_WINDOWS: + count = data; + + if (domain->ops->domain_set_windows != NULL) + ret = domain->ops->domain_set_windows(domain, *count); + else + ret = -ENODEV; + + break; + default: + if (domain->ops->domain_set_attr == NULL) + return -EINVAL; + + ret = domain->ops->domain_set_attr(domain, attr, data); + } + + return ret; } EXPORT_SYMBOL_GPL(iommu_domain_set_attr); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 5ea3d7250917..ba3b8a98a049 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -60,6 +60,7 @@ struct iommu_domain { enum iommu_attr { DOMAIN_ATTR_GEOMETRY, DOMAIN_ATTR_PAGING, + DOMAIN_ATTR_WINDOWS, DOMAIN_ATTR_MAX, }; @@ -106,6 +107,10 @@ struct iommu_ops { int (*domain_window_enable)(struct iommu_domain *domain, u32 wnd_nr, phys_addr_t paddr, u64 size); void (*domain_window_disable)(struct iommu_domain *domain, u32 wnd_nr); + /* Set the numer of window per domain */ + int (*domain_set_windows)(struct iommu_domain *domain, u32 w_count); + /* Get the numer of window per domain */ + u32 (*domain_get_windows)(struct iommu_domain *domain); unsigned long pgsize_bitmap; }; From c2c460f7c148aa1a59630f61dac2481f1efb4f4e Mon Sep 17 00:00:00 2001 From: Hideki EIRAKU Date: Mon, 21 Jan 2013 19:54:26 +0900 Subject: [PATCH 2001/4607] iommu/shmobile: Add iommu driver for Renesas IPMMU modules This is the Renesas IPMMU driver and IOMMU API implementation. The IPMMU module supports the MMU function and the PMB function. The MMU function provides address translation by pagetable compatible with ARMv6. The PMB function provides address translation including tile-linear translation. This patch implements the MMU function. The iommu driver does not register a platform driver directly because: - the register space of the MMU function and the PMB function have a common register (used for settings flush), so they should ideally have a way to appropriately share this register. - the MMU function uses the IOMMU API while the PMB function does not. - the two functions may be used independently. Signed-off-by: Hideki EIRAKU Signed-off-by: Joerg Roedel --- drivers/iommu/Kconfig | 74 +++++ drivers/iommu/Makefile | 2 + drivers/iommu/shmobile-iommu.c | 395 +++++++++++++++++++++++++ drivers/iommu/shmobile-ipmmu.c | 136 +++++++++ drivers/iommu/shmobile-ipmmu.h | 34 +++ include/linux/platform_data/sh_ipmmu.h | 18 ++ 6 files changed, 659 insertions(+) create mode 100644 drivers/iommu/shmobile-iommu.c create mode 100644 drivers/iommu/shmobile-ipmmu.c create mode 100644 drivers/iommu/shmobile-ipmmu.h create mode 100644 include/linux/platform_data/sh_ipmmu.h diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index e39f9dbf297b..d364494c9e7c 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -187,4 +187,78 @@ config EXYNOS_IOMMU_DEBUG Say N unless you need kernel log message for IOMMU debugging +config SHMOBILE_IPMMU + bool + +config SHMOBILE_IPMMU_TLB + bool + +config SHMOBILE_IOMMU + bool "IOMMU for Renesas IPMMU/IPMMUI" + default n + depends on (ARM && ARCH_SHMOBILE) + select IOMMU_API + select ARM_DMA_USE_IOMMU + select SHMOBILE_IPMMU + select SHMOBILE_IPMMU_TLB + help + Support for Renesas IPMMU/IPMMUI. This option enables + remapping of DMA memory accesses from all of the IP blocks + on the ICB. + + Warning: Drivers (including userspace drivers of UIO + devices) of the IP blocks on the ICB *must* use addresses + allocated from the IPMMU (iova) for DMA with this option + enabled. + + If unsure, say N. + +choice + prompt "IPMMU/IPMMUI address space size" + default SHMOBILE_IOMMU_ADDRSIZE_2048MB + depends on SHMOBILE_IOMMU + help + This option sets IPMMU/IPMMUI address space size by + adjusting the 1st level page table size. The page table size + is calculated as follows: + + page table size = number of page table entries * 4 bytes + number of page table entries = address space size / 1 MiB + + For example, when the address space size is 2048 MiB, the + 1st level page table size is 8192 bytes. + + config SHMOBILE_IOMMU_ADDRSIZE_2048MB + bool "2 GiB" + + config SHMOBILE_IOMMU_ADDRSIZE_1024MB + bool "1 GiB" + + config SHMOBILE_IOMMU_ADDRSIZE_512MB + bool "512 MiB" + + config SHMOBILE_IOMMU_ADDRSIZE_256MB + bool "256 MiB" + + config SHMOBILE_IOMMU_ADDRSIZE_128MB + bool "128 MiB" + + config SHMOBILE_IOMMU_ADDRSIZE_64MB + bool "64 MiB" + + config SHMOBILE_IOMMU_ADDRSIZE_32MB + bool "32 MiB" + +endchoice + +config SHMOBILE_IOMMU_L1SIZE + int + default 8192 if SHMOBILE_IOMMU_ADDRSIZE_2048MB + default 4096 if SHMOBILE_IOMMU_ADDRSIZE_1024MB + default 2048 if SHMOBILE_IOMMU_ADDRSIZE_512MB + default 1024 if SHMOBILE_IOMMU_ADDRSIZE_256MB + default 512 if SHMOBILE_IOMMU_ADDRSIZE_128MB + default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB + default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB + endif # IOMMU_SUPPORT diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index f66b816d455c..ef0e5207ad69 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -13,3 +13,5 @@ obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o +obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o +obj-$(CONFIG_SHMOBILE_IPMMU) += shmobile-ipmmu.o diff --git a/drivers/iommu/shmobile-iommu.c b/drivers/iommu/shmobile-iommu.c new file mode 100644 index 000000000000..b6e8b57cf0a8 --- /dev/null +++ b/drivers/iommu/shmobile-iommu.c @@ -0,0 +1,395 @@ +/* + * IOMMU for IPMMU/IPMMUI + * Copyright (C) 2012 Hideki EIRAKU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "shmobile-ipmmu.h" + +#define L1_SIZE CONFIG_SHMOBILE_IOMMU_L1SIZE +#define L1_LEN (L1_SIZE / 4) +#define L1_ALIGN L1_SIZE +#define L2_SIZE SZ_1K +#define L2_LEN (L2_SIZE / 4) +#define L2_ALIGN L2_SIZE + +struct shmobile_iommu_domain_pgtable { + uint32_t *pgtable; + dma_addr_t handle; +}; + +struct shmobile_iommu_archdata { + struct list_head attached_list; + struct dma_iommu_mapping *iommu_mapping; + spinlock_t attach_lock; + struct shmobile_iommu_domain *attached; + int num_attached_devices; + struct shmobile_ipmmu *ipmmu; +}; + +struct shmobile_iommu_domain { + struct shmobile_iommu_domain_pgtable l1, l2[L1_LEN]; + spinlock_t map_lock; + spinlock_t attached_list_lock; + struct list_head attached_list; +}; + +static struct shmobile_iommu_archdata *ipmmu_archdata; +static struct kmem_cache *l1cache, *l2cache; + +static int pgtable_alloc(struct shmobile_iommu_domain_pgtable *pgtable, + struct kmem_cache *cache, size_t size) +{ + pgtable->pgtable = kmem_cache_zalloc(cache, GFP_ATOMIC); + if (!pgtable->pgtable) + return -ENOMEM; + pgtable->handle = dma_map_single(NULL, pgtable->pgtable, size, + DMA_TO_DEVICE); + return 0; +} + +static void pgtable_free(struct shmobile_iommu_domain_pgtable *pgtable, + struct kmem_cache *cache, size_t size) +{ + dma_unmap_single(NULL, pgtable->handle, size, DMA_TO_DEVICE); + kmem_cache_free(cache, pgtable->pgtable); +} + +static uint32_t pgtable_read(struct shmobile_iommu_domain_pgtable *pgtable, + unsigned int index) +{ + return pgtable->pgtable[index]; +} + +static void pgtable_write(struct shmobile_iommu_domain_pgtable *pgtable, + unsigned int index, unsigned int count, uint32_t val) +{ + unsigned int i; + + for (i = 0; i < count; i++) + pgtable->pgtable[index + i] = val; + dma_sync_single_for_device(NULL, pgtable->handle + index * sizeof(val), + sizeof(val) * count, DMA_TO_DEVICE); +} + +static int shmobile_iommu_domain_init(struct iommu_domain *domain) +{ + struct shmobile_iommu_domain *sh_domain; + int i, ret; + + sh_domain = kmalloc(sizeof(*sh_domain), GFP_KERNEL); + if (!sh_domain) + return -ENOMEM; + ret = pgtable_alloc(&sh_domain->l1, l1cache, L1_SIZE); + if (ret < 0) { + kfree(sh_domain); + return ret; + } + for (i = 0; i < L1_LEN; i++) + sh_domain->l2[i].pgtable = NULL; + spin_lock_init(&sh_domain->map_lock); + spin_lock_init(&sh_domain->attached_list_lock); + INIT_LIST_HEAD(&sh_domain->attached_list); + domain->priv = sh_domain; + return 0; +} + +static void shmobile_iommu_domain_destroy(struct iommu_domain *domain) +{ + struct shmobile_iommu_domain *sh_domain = domain->priv; + int i; + + for (i = 0; i < L1_LEN; i++) { + if (sh_domain->l2[i].pgtable) + pgtable_free(&sh_domain->l2[i], l2cache, L2_SIZE); + } + pgtable_free(&sh_domain->l1, l1cache, L1_SIZE); + kfree(sh_domain); + domain->priv = NULL; +} + +static int shmobile_iommu_attach_device(struct iommu_domain *domain, + struct device *dev) +{ + struct shmobile_iommu_archdata *archdata = dev->archdata.iommu; + struct shmobile_iommu_domain *sh_domain = domain->priv; + int ret = -EBUSY; + + if (!archdata) + return -ENODEV; + spin_lock(&sh_domain->attached_list_lock); + spin_lock(&archdata->attach_lock); + if (archdata->attached != sh_domain) { + if (archdata->attached) + goto err; + ipmmu_tlb_set(archdata->ipmmu, sh_domain->l1.handle, L1_SIZE, + 0); + ipmmu_tlb_flush(archdata->ipmmu); + archdata->attached = sh_domain; + archdata->num_attached_devices = 0; + list_add(&archdata->attached_list, &sh_domain->attached_list); + } + archdata->num_attached_devices++; + ret = 0; +err: + spin_unlock(&archdata->attach_lock); + spin_unlock(&sh_domain->attached_list_lock); + return ret; +} + +static void shmobile_iommu_detach_device(struct iommu_domain *domain, + struct device *dev) +{ + struct shmobile_iommu_archdata *archdata = dev->archdata.iommu; + struct shmobile_iommu_domain *sh_domain = domain->priv; + + if (!archdata) + return; + spin_lock(&sh_domain->attached_list_lock); + spin_lock(&archdata->attach_lock); + archdata->num_attached_devices--; + if (!archdata->num_attached_devices) { + ipmmu_tlb_set(archdata->ipmmu, 0, 0, 0); + ipmmu_tlb_flush(archdata->ipmmu); + archdata->attached = NULL; + list_del(&archdata->attached_list); + } + spin_unlock(&archdata->attach_lock); + spin_unlock(&sh_domain->attached_list_lock); +} + +static void domain_tlb_flush(struct shmobile_iommu_domain *sh_domain) +{ + struct shmobile_iommu_archdata *archdata; + + spin_lock(&sh_domain->attached_list_lock); + list_for_each_entry(archdata, &sh_domain->attached_list, attached_list) + ipmmu_tlb_flush(archdata->ipmmu); + spin_unlock(&sh_domain->attached_list_lock); +} + +static int l2alloc(struct shmobile_iommu_domain *sh_domain, + unsigned int l1index) +{ + int ret; + + if (!sh_domain->l2[l1index].pgtable) { + ret = pgtable_alloc(&sh_domain->l2[l1index], l2cache, L2_SIZE); + if (ret < 0) + return ret; + } + pgtable_write(&sh_domain->l1, l1index, 1, + sh_domain->l2[l1index].handle | 0x1); + return 0; +} + +static void l2realfree(struct shmobile_iommu_domain_pgtable *l2) +{ + if (l2->pgtable) + pgtable_free(l2, l2cache, L2_SIZE); +} + +static void l2free(struct shmobile_iommu_domain *sh_domain, + unsigned int l1index, + struct shmobile_iommu_domain_pgtable *l2) +{ + pgtable_write(&sh_domain->l1, l1index, 1, 0); + if (sh_domain->l2[l1index].pgtable) { + *l2 = sh_domain->l2[l1index]; + sh_domain->l2[l1index].pgtable = NULL; + } +} + +static int shmobile_iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot) +{ + struct shmobile_iommu_domain_pgtable l2 = { .pgtable = NULL }; + struct shmobile_iommu_domain *sh_domain = domain->priv; + unsigned int l1index, l2index; + int ret; + + l1index = iova >> 20; + switch (size) { + case SZ_4K: + l2index = (iova >> 12) & 0xff; + spin_lock(&sh_domain->map_lock); + ret = l2alloc(sh_domain, l1index); + if (!ret) + pgtable_write(&sh_domain->l2[l1index], l2index, 1, + paddr | 0xff2); + spin_unlock(&sh_domain->map_lock); + break; + case SZ_64K: + l2index = (iova >> 12) & 0xf0; + spin_lock(&sh_domain->map_lock); + ret = l2alloc(sh_domain, l1index); + if (!ret) + pgtable_write(&sh_domain->l2[l1index], l2index, 0x10, + paddr | 0xff1); + spin_unlock(&sh_domain->map_lock); + break; + case SZ_1M: + spin_lock(&sh_domain->map_lock); + l2free(sh_domain, l1index, &l2); + pgtable_write(&sh_domain->l1, l1index, 1, paddr | 0xc02); + spin_unlock(&sh_domain->map_lock); + ret = 0; + break; + default: + ret = -EINVAL; + } + if (!ret) + domain_tlb_flush(sh_domain); + l2realfree(&l2); + return ret; +} + +static size_t shmobile_iommu_unmap(struct iommu_domain *domain, + unsigned long iova, size_t size) +{ + struct shmobile_iommu_domain_pgtable l2 = { .pgtable = NULL }; + struct shmobile_iommu_domain *sh_domain = domain->priv; + unsigned int l1index, l2index; + uint32_t l2entry = 0; + size_t ret = 0; + + l1index = iova >> 20; + if (!(iova & 0xfffff) && size >= SZ_1M) { + spin_lock(&sh_domain->map_lock); + l2free(sh_domain, l1index, &l2); + spin_unlock(&sh_domain->map_lock); + ret = SZ_1M; + goto done; + } + l2index = (iova >> 12) & 0xff; + spin_lock(&sh_domain->map_lock); + if (sh_domain->l2[l1index].pgtable) + l2entry = pgtable_read(&sh_domain->l2[l1index], l2index); + switch (l2entry & 3) { + case 1: + if (l2index & 0xf) + break; + pgtable_write(&sh_domain->l2[l1index], l2index, 0x10, 0); + ret = SZ_64K; + break; + case 2: + pgtable_write(&sh_domain->l2[l1index], l2index, 1, 0); + ret = SZ_4K; + break; + } + spin_unlock(&sh_domain->map_lock); +done: + if (ret) + domain_tlb_flush(sh_domain); + l2realfree(&l2); + return ret; +} + +static phys_addr_t shmobile_iommu_iova_to_phys(struct iommu_domain *domain, + unsigned long iova) +{ + struct shmobile_iommu_domain *sh_domain = domain->priv; + uint32_t l1entry = 0, l2entry = 0; + unsigned int l1index, l2index; + + l1index = iova >> 20; + l2index = (iova >> 12) & 0xff; + spin_lock(&sh_domain->map_lock); + if (sh_domain->l2[l1index].pgtable) + l2entry = pgtable_read(&sh_domain->l2[l1index], l2index); + else + l1entry = pgtable_read(&sh_domain->l1, l1index); + spin_unlock(&sh_domain->map_lock); + switch (l2entry & 3) { + case 1: + return (l2entry & ~0xffff) | (iova & 0xffff); + case 2: + return (l2entry & ~0xfff) | (iova & 0xfff); + default: + if ((l1entry & 3) == 2) + return (l1entry & ~0xfffff) | (iova & 0xfffff); + return 0; + } +} + +static int find_dev_name(struct shmobile_ipmmu *ipmmu, const char *dev_name) +{ + unsigned int i, n = ipmmu->num_dev_names; + + for (i = 0; i < n; i++) { + if (strcmp(ipmmu->dev_names[i], dev_name) == 0) + return 1; + } + return 0; +} + +static int shmobile_iommu_add_device(struct device *dev) +{ + struct shmobile_iommu_archdata *archdata = ipmmu_archdata; + struct dma_iommu_mapping *mapping; + + if (!find_dev_name(archdata->ipmmu, dev_name(dev))) + return 0; + mapping = archdata->iommu_mapping; + if (!mapping) { + mapping = arm_iommu_create_mapping(&platform_bus_type, 0, + L1_LEN << 20, 0); + if (IS_ERR(mapping)) + return PTR_ERR(mapping); + archdata->iommu_mapping = mapping; + } + dev->archdata.iommu = archdata; + if (arm_iommu_attach_device(dev, mapping)) + pr_err("arm_iommu_attach_device failed\n"); + return 0; +} + +static struct iommu_ops shmobile_iommu_ops = { + .domain_init = shmobile_iommu_domain_init, + .domain_destroy = shmobile_iommu_domain_destroy, + .attach_dev = shmobile_iommu_attach_device, + .detach_dev = shmobile_iommu_detach_device, + .map = shmobile_iommu_map, + .unmap = shmobile_iommu_unmap, + .iova_to_phys = shmobile_iommu_iova_to_phys, + .add_device = shmobile_iommu_add_device, + .pgsize_bitmap = SZ_1M | SZ_64K | SZ_4K, +}; + +int ipmmu_iommu_init(struct shmobile_ipmmu *ipmmu) +{ + static struct shmobile_iommu_archdata *archdata; + + l1cache = kmem_cache_create("shmobile-iommu-pgtable1", L1_SIZE, + L1_ALIGN, SLAB_HWCACHE_ALIGN, NULL); + if (!l1cache) + return -ENOMEM; + l2cache = kmem_cache_create("shmobile-iommu-pgtable2", L2_SIZE, + L2_ALIGN, SLAB_HWCACHE_ALIGN, NULL); + if (!l2cache) { + kmem_cache_destroy(l1cache); + return -ENOMEM; + } + archdata = kmalloc(sizeof(*archdata), GFP_KERNEL); + if (!archdata) { + kmem_cache_destroy(l1cache); + kmem_cache_destroy(l2cache); + return -ENOMEM; + } + spin_lock_init(&archdata->attach_lock); + archdata->attached = NULL; + archdata->ipmmu = ipmmu; + ipmmu_archdata = archdata; + bus_set_iommu(&platform_bus_type, &shmobile_iommu_ops); + return 0; +} diff --git a/drivers/iommu/shmobile-ipmmu.c b/drivers/iommu/shmobile-ipmmu.c new file mode 100644 index 000000000000..8321f89596c4 --- /dev/null +++ b/drivers/iommu/shmobile-ipmmu.c @@ -0,0 +1,136 @@ +/* + * IPMMU/IPMMUI + * Copyright (C) 2012 Hideki EIRAKU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include "shmobile-ipmmu.h" + +#define IMCTR1 0x000 +#define IMCTR2 0x004 +#define IMASID 0x010 +#define IMTTBR 0x014 +#define IMTTBCR 0x018 + +#define IMCTR1_TLBEN (1 << 0) +#define IMCTR1_FLUSH (1 << 1) + +static void ipmmu_reg_write(struct shmobile_ipmmu *ipmmu, unsigned long reg_off, + unsigned long data) +{ + iowrite32(data, ipmmu->ipmmu_base + reg_off); +} + +void ipmmu_tlb_flush(struct shmobile_ipmmu *ipmmu) +{ + if (!ipmmu) + return; + + mutex_lock(&ipmmu->flush_lock); + if (ipmmu->tlb_enabled) + ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH | IMCTR1_TLBEN); + else + ipmmu_reg_write(ipmmu, IMCTR1, IMCTR1_FLUSH); + mutex_unlock(&ipmmu->flush_lock); +} + +void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int size, + int asid) +{ + if (!ipmmu) + return; + + mutex_lock(&ipmmu->flush_lock); + switch (size) { + default: + ipmmu->tlb_enabled = 0; + break; + case 0x2000: + ipmmu_reg_write(ipmmu, IMTTBCR, 1); + ipmmu->tlb_enabled = 1; + break; + case 0x1000: + ipmmu_reg_write(ipmmu, IMTTBCR, 2); + ipmmu->tlb_enabled = 1; + break; + case 0x800: + ipmmu_reg_write(ipmmu, IMTTBCR, 3); + ipmmu->tlb_enabled = 1; + break; + case 0x400: + ipmmu_reg_write(ipmmu, IMTTBCR, 4); + ipmmu->tlb_enabled = 1; + break; + case 0x200: + ipmmu_reg_write(ipmmu, IMTTBCR, 5); + ipmmu->tlb_enabled = 1; + break; + case 0x100: + ipmmu_reg_write(ipmmu, IMTTBCR, 6); + ipmmu->tlb_enabled = 1; + break; + case 0x80: + ipmmu_reg_write(ipmmu, IMTTBCR, 7); + ipmmu->tlb_enabled = 1; + break; + } + ipmmu_reg_write(ipmmu, IMTTBR, phys); + ipmmu_reg_write(ipmmu, IMASID, asid); + mutex_unlock(&ipmmu->flush_lock); +} + +static int ipmmu_probe(struct platform_device *pdev) +{ + struct shmobile_ipmmu *ipmmu; + struct resource *res; + struct shmobile_ipmmu_platform_data *pdata = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "cannot get platform resources\n"); + return -ENOENT; + } + ipmmu = devm_kzalloc(&pdev->dev, sizeof(*ipmmu), GFP_KERNEL); + if (!ipmmu) { + dev_err(&pdev->dev, "cannot allocate device data\n"); + return -ENOMEM; + } + mutex_init(&ipmmu->flush_lock); + ipmmu->dev = &pdev->dev; + ipmmu->ipmmu_base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!ipmmu->ipmmu_base) { + dev_err(&pdev->dev, "ioremap_nocache failed\n"); + return -ENOMEM; + } + ipmmu->dev_names = pdata->dev_names; + ipmmu->num_dev_names = pdata->num_dev_names; + platform_set_drvdata(pdev, ipmmu); + ipmmu_reg_write(ipmmu, IMCTR1, 0x0); /* disable TLB */ + ipmmu_reg_write(ipmmu, IMCTR2, 0x0); /* disable PMB */ + ipmmu_iommu_init(ipmmu); + return 0; +} + +static struct platform_driver ipmmu_driver = { + .probe = ipmmu_probe, + .driver = { + .owner = THIS_MODULE, + .name = "ipmmu", + }, +}; + +static int __init ipmmu_init(void) +{ + return platform_driver_register(&ipmmu_driver); +} +subsys_initcall(ipmmu_init); diff --git a/drivers/iommu/shmobile-ipmmu.h b/drivers/iommu/shmobile-ipmmu.h new file mode 100644 index 000000000000..4d53684673e1 --- /dev/null +++ b/drivers/iommu/shmobile-ipmmu.h @@ -0,0 +1,34 @@ +/* shmobile-ipmmu.h + * + * Copyright (C) 2012 Hideki EIRAKU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#ifndef __SHMOBILE_IPMMU_H__ +#define __SHMOBILE_IPMMU_H__ + +struct shmobile_ipmmu { + struct device *dev; + void __iomem *ipmmu_base; + int tlb_enabled; + struct mutex flush_lock; + const char * const *dev_names; + unsigned int num_dev_names; +}; + +#ifdef CONFIG_SHMOBILE_IPMMU_TLB +void ipmmu_tlb_flush(struct shmobile_ipmmu *ipmmu); +void ipmmu_tlb_set(struct shmobile_ipmmu *ipmmu, unsigned long phys, int size, + int asid); +int ipmmu_iommu_init(struct shmobile_ipmmu *ipmmu); +#else +static inline int ipmmu_iommu_init(struct shmobile_ipmmu *ipmmu) +{ + return -EINVAL; +} +#endif + +#endif /* __SHMOBILE_IPMMU_H__ */ diff --git a/include/linux/platform_data/sh_ipmmu.h b/include/linux/platform_data/sh_ipmmu.h new file mode 100644 index 000000000000..39f7405cdac5 --- /dev/null +++ b/include/linux/platform_data/sh_ipmmu.h @@ -0,0 +1,18 @@ +/* sh_ipmmu.h + * + * Copyright (C) 2012 Hideki EIRAKU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#ifndef __SH_IPMMU_H__ +#define __SH_IPMMU_H__ + +struct shmobile_ipmmu_platform_data { + const char * const *dev_names; + unsigned int num_dev_names; +}; + +#endif /* __SH_IPMMU_H__ */ From 3cfb8439e4799896eae22743463f11fb0cc2f0a4 Mon Sep 17 00:00:00 2001 From: Hideki EIRAKU Date: Mon, 21 Jan 2013 19:54:27 +0900 Subject: [PATCH 2002/4607] ARM: mach-shmobile: sh7372: Add IPMMU device This patch adds an IPMMU device and notifies the IPMMU driver which devices are connected via the IPMMU module. All devices connected to the main memory bus via the IPMMU module MUST be registered when SHMOBILE_IPMMU and SHMOBILE_IOMMU are enabled because physical address cannot be used while the IPMMU module's MMU function is enabled. Signed-off-by: Hideki EIRAKU Acked-by: Paul Mundt Acked-by: Simon Horman Signed-off-by: Joerg Roedel --- arch/arm/mach-shmobile/setup-sh7372.c | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/arch/arm/mach-shmobile/setup-sh7372.c b/arch/arm/mach-shmobile/setup-sh7372.c index c917882424a7..04ad98ece1f1 100644 --- a/arch/arm/mach-shmobile/setup-sh7372.c +++ b/arch/arm/mach-shmobile/setup-sh7372.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -982,6 +983,43 @@ static struct platform_device spu1_device = { .num_resources = ARRAY_SIZE(spu1_resources), }; +/* IPMMUI (an IPMMU module for ICB/LMB) */ +static struct resource ipmmu_resources[] = { + [0] = { + .name = "IPMMUI", + .start = 0xfe951000, + .end = 0xfe9510ff, + .flags = IORESOURCE_MEM, + }, +}; + +static const char * const ipmmu_dev_names[] = { + "sh_mobile_lcdc_fb.0", + "sh_mobile_lcdc_fb.1", + "sh_mobile_ceu.0", + "uio_pdrv_genirq.0", + "uio_pdrv_genirq.1", + "uio_pdrv_genirq.2", + "uio_pdrv_genirq.3", + "uio_pdrv_genirq.4", + "uio_pdrv_genirq.5", +}; + +static struct shmobile_ipmmu_platform_data ipmmu_platform_data = { + .dev_names = ipmmu_dev_names, + .num_dev_names = ARRAY_SIZE(ipmmu_dev_names), +}; + +static struct platform_device ipmmu_device = { + .name = "ipmmu", + .id = -1, + .dev = { + .platform_data = &ipmmu_platform_data, + }, + .resource = ipmmu_resources, + .num_resources = ARRAY_SIZE(ipmmu_resources), +}; + static struct platform_device *sh7372_early_devices[] __initdata = { &scif0_device, &scif1_device, @@ -993,6 +1031,7 @@ static struct platform_device *sh7372_early_devices[] __initdata = { &cmt2_device, &tmu00_device, &tmu01_device, + &ipmmu_device, }; static struct platform_device *sh7372_late_devices[] __initdata = { From 9a27dee73f558a026e9bbfb5f8af6ae7ba6b7bdc Mon Sep 17 00:00:00 2001 From: Hideki EIRAKU Date: Mon, 21 Jan 2013 19:54:28 +0900 Subject: [PATCH 2003/4607] ARM: mach-shmobile: sh73a0: Add IPMMU device This patch adds an IPMMU device and notifies the IPMMU driver which devices are connected via the IPMMU module. All devices connected to the main memory bus via the IPMMU module MUST be registered when SHMOBILE_IPMMU and SHMOBILE_IOMMU are enabled because physical address cannot be used while the IPMMU module's MMU function is enabled. Signed-off-by: Hideki EIRAKU Acked-by: Paul Mundt Acked-by: Simon Horman Signed-off-by: Joerg Roedel --- arch/arm/mach-shmobile/setup-sh73a0.c | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c index db99a4ade80c..36c2b2ec2f33 100644 --- a/arch/arm/mach-shmobile/setup-sh73a0.c +++ b/arch/arm/mach-shmobile/setup-sh73a0.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -754,6 +755,35 @@ static struct platform_device pmu_device = { .resource = pmu_resources, }; +/* an IPMMU module for ICB */ +static struct resource ipmmu_resources[] = { + [0] = { + .name = "IPMMU", + .start = 0xfe951000, + .end = 0xfe9510ff, + .flags = IORESOURCE_MEM, + }, +}; + +static const char * const ipmmu_dev_names[] = { + "sh_mobile_lcdc_fb.0", +}; + +static struct shmobile_ipmmu_platform_data ipmmu_platform_data = { + .dev_names = ipmmu_dev_names, + .num_dev_names = ARRAY_SIZE(ipmmu_dev_names), +}; + +static struct platform_device ipmmu_device = { + .name = "ipmmu", + .id = -1, + .dev = { + .platform_data = &ipmmu_platform_data, + }, + .resource = ipmmu_resources, + .num_resources = ARRAY_SIZE(ipmmu_resources), +}; + static struct platform_device *sh73a0_early_devices[] __initdata = { &scif0_device, &scif1_device, @@ -767,6 +797,7 @@ static struct platform_device *sh73a0_early_devices[] __initdata = { &cmt10_device, &tmu00_device, &tmu01_device, + &ipmmu_device, }; static struct platform_device *sh73a0_late_devices[] __initdata = { From f671e0224a7f6432abdd9935bc34d959f5a67e21 Mon Sep 17 00:00:00 2001 From: Hideki EIRAKU Date: Mon, 21 Jan 2013 19:54:29 +0900 Subject: [PATCH 2004/4607] ARM: mach-shmobile: r8a7740: Add IPMMU device This patch adds an IPMMU device and notifies the IPMMU driver which devices are connected via the IPMMU module. All devices connected to the main memory bus via the IPMMU module MUST be registered when SHMOBILE_IPMMU and SHMOBILE_IOMMU are enabled because physical address cannot be used while the IPMMU module's MMU function is enabled. Signed-off-by: Hideki EIRAKU Acked-by: Paul Mundt Acked-by: Simon Horman Signed-off-by: Joerg Roedel --- arch/arm/mach-shmobile/setup-r8a7740.c | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/arch/arm/mach-shmobile/setup-r8a7740.c b/arch/arm/mach-shmobile/setup-r8a7740.c index 095222469d03..b85bea517d9b 100644 --- a/arch/arm/mach-shmobile/setup-r8a7740.c +++ b/arch/arm/mach-shmobile/setup-r8a7740.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -262,6 +263,37 @@ static struct platform_device cmt10_device = { .num_resources = ARRAY_SIZE(cmt10_resources), }; +/* IPMMUI (an IPMMU module for ICB/LMB) */ +static struct resource ipmmu_resources[] = { + [0] = { + .name = "IPMMUI", + .start = 0xfe951000, + .end = 0xfe9510ff, + .flags = IORESOURCE_MEM, + }, +}; + +static const char * const ipmmu_dev_names[] = { + "sh_mobile_lcdc_fb.0", + "sh_mobile_lcdc_fb.1", + "sh_mobile_ceu.0", +}; + +static struct shmobile_ipmmu_platform_data ipmmu_platform_data = { + .dev_names = ipmmu_dev_names, + .num_dev_names = ARRAY_SIZE(ipmmu_dev_names), +}; + +static struct platform_device ipmmu_device = { + .name = "ipmmu", + .id = -1, + .dev = { + .platform_data = &ipmmu_platform_data, + }, + .resource = ipmmu_resources, + .num_resources = ARRAY_SIZE(ipmmu_resources), +}; + static struct platform_device *r8a7740_early_devices[] __initdata = { &scif0_device, &scif1_device, @@ -273,6 +305,7 @@ static struct platform_device *r8a7740_early_devices[] __initdata = { &scif7_device, &scifb_device, &cmt10_device, + &ipmmu_device, }; /* DMA */ From 0568aeeeef467ac6e22d5d549cd50e8c93abf959 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Jan 2013 10:33:30 -0300 Subject: [PATCH 2005/4607] [media] DocBook: improve the error_idx field documentation The documentation of the error_idx field was incomplete and confusing. This patch improves it. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- .../DocBook/media/v4l/vidioc-g-ext-ctrls.xml | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml index 0a4b90fcf2da..42ffbff6d37e 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml @@ -199,13 +199,46 @@ also be zero. __u32 error_idx - Set by the driver in case of an error. If it is equal -to count, then no actual changes were made to -controls. In other words, the error was not associated with setting a particular -control. If it is another value, then only the controls up to error_idx-1 -were modified and control error_idx is the one that -caused the error. The error_idx value is undefined -if the ioctl returned 0 (success). + Set by the driver in case of an error. If the error is +associated with a particular control, then error_idx +is set to the index of that control. If the error is not related to a specific +control, or the validation step failed (see below), then +error_idx is set to count. +The value is undefined if the ioctl returned 0 (success). + +Before controls are read from/written to hardware a validation step +takes place: this checks if all controls in the list are valid controls, +if no attempt is made to write to a read-only control or read from a write-only +control, and any other up-front checks that can be done without accessing the +hardware. The exact validations done during this step are driver dependent +since some checks might require hardware access for some devices, thus making +it impossible to do those checks up-front. However, drivers should make a +best-effort to do as many up-front checks as possible. + +This check is done to avoid leaving the hardware in an inconsistent state due +to easy-to-avoid problems. But it leads to another problem: the application needs to +know whether an error came from the validation step (meaning that the hardware +was not touched) or from an error during the actual reading from/writing to hardware. + +The, in hindsight quite poor, solution for that is to set error_idx +to count if the validation failed. This has the +unfortunate side-effect that it is not possible to see which control failed the +validation. If the validation was successful and the error happened while +accessing the hardware, then error_idx is less than +count and only the controls up to +error_idx-1 were read or written correctly, and the +state of the remaining controls is undefined. + +Since VIDIOC_TRY_EXT_CTRLS does not access hardware +there is also no need to handle the validation step in this special way, +so error_idx will just be set to the control that +failed the validation step instead of to count. +This means that if VIDIOC_S_EXT_CTRLS fails with +error_idx set to count, +then you can call VIDIOC_TRY_EXT_CTRLS to try to discover +the actual control that failed the validation step. Unfortunately, there +is no TRY equivalent for VIDIOC_G_EXT_CTRLS. + __u32 From 00b230fe2310d7a0b338a8e339995e0cf3f5e977 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Jan 2013 10:49:12 -0300 Subject: [PATCH 2006/4607] [media] DocBook: mention that EINVAL can be returned for invalid menu indices The documentation suggested that if the control value is wrong only ERANGE can be returned. But in some cases (an invalid menu index) EINVAL can also be returned. Clarify this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/vidioc-g-ctrl.xml | 8 ++++++-- Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml | 10 +++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ctrl.xml b/Documentation/DocBook/media/v4l/vidioc-g-ctrl.xml index 12b1d0503e26..ee2820d6ca66 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ctrl.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ctrl.xml @@ -64,7 +64,9 @@ return an &EINVAL;. When the value is out of bounds drivers can choose to take the closest valid value or return an &ERANGE;, whatever seems more appropriate. However, VIDIOC_S_CTRL is a write-only ioctl, it does not -return the actual new value. +return the actual new value. If the value +is inappropriate for the control (e.g. if it refers to an unsupported +menu index of a menu control), then &EINVAL; is returned as well. These ioctls work only with user controls. For other control classes the &VIDIOC-G-EXT-CTRLS;, &VIDIOC-S-EXT-CTRLS; or @@ -99,7 +101,9 @@ application. EINVAL The &v4l2-control; id is -invalid. +invalid or the value is inappropriate for +the given control (i.e. if a menu item is selected that is not supported +by the driver according to &VIDIOC-QUERYMENU;). diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml index 42ffbff6d37e..4e16112df992 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml @@ -106,7 +106,9 @@ value or if an error is returned. &EINVAL;. When the value is out of bounds drivers can choose to take the closest valid value or return an &ERANGE;, whatever seems more appropriate. In the first case the new value is set in -&v4l2-ext-control;. +&v4l2-ext-control;. If the new control value is inappropriate (e.g. the +given menu index is not supported by the menu control), then this will +also result in an &EINVAL; error. The driver will only set/get these controls if all control values are correct. This prevents the situation where only some of the @@ -331,8 +333,10 @@ These controls are described in EINVAL The &v4l2-ext-control; id -is invalid or the &v4l2-ext-controls; -ctrl_class is invalid. This error code is +is invalid, the &v4l2-ext-controls; +ctrl_class is invalid, or the &v4l2-ext-control; +value was inappropriate (e.g. the given menu +index is not supported by the driver). This error code is also returned by the VIDIOC_S_EXT_CTRLS and VIDIOC_TRY_EXT_CTRLS ioctls if two or more control values are in conflict. From 09f9408dc394835157397f9a4d714ecb53d03976 Mon Sep 17 00:00:00 2001 From: Eddi De Pieri Date: Mon, 14 Jan 2013 18:21:32 -0300 Subject: [PATCH 2007/4607] [media] Support Digivox Mini HD (rtl2832) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for Digivox Mini HD (rtl2832) The tuner works, but with worst performance then realtek linux driver, due to incomplete implementation of fc2580.c Signed-off-by: Eddi De Pieri Tested-by: Lorenzo Dongarrà Acked-by: Antti Palosaari Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 4ab0dd8dd9a8..e127bd134be0 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1368,6 +1368,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd393, &rtl2832u_props, "GIGABYTE U7300", NULL) }, + { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1104, + &rtl2832u_props, "Digivox Micro Hd", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); From 6a05d66b8ff1e3a3470186d25cd825dff774bb82 Mon Sep 17 00:00:00 2001 From: Prabhakar Lad Date: Tue, 15 Jan 2013 03:55:40 -0300 Subject: [PATCH 2008/4607] [media] media: tvp514x: remove field description This patch removes the field description of fields that no longer exists, along side aligns the field description of fields. Signed-off-by: Lad, Prabhakar Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/tvp514x.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/include/media/tvp514x.h b/include/media/tvp514x.h index 74387e83f5b9..86ed7e806830 100644 --- a/include/media/tvp514x.h +++ b/include/media/tvp514x.h @@ -96,12 +96,9 @@ enum tvp514x_output { /** * struct tvp514x_platform_data - Platform data values and access functions. - * @power_set: Power state access function, zero is off, non-zero is on. - * @ifparm: Interface parameters access function. - * @priv_data_set: Device private data (pointer) access function. * @clk_polarity: Clock polarity of the current interface. - * @ hs_polarity: HSYNC Polarity configuration for current interface. - * @ vs_polarity: VSYNC Polarity configuration for current interface. + * @hs_polarity: HSYNC Polarity configuration for current interface. + * @vs_polarity: VSYNC Polarity configuration for current interface. */ struct tvp514x_platform_data { /* Interface control params */ From f85ed0ceeba78b6b15a857ce48888fdb52de28d0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 6 Feb 2013 08:29:39 -0200 Subject: [PATCH 2009/4607] Revert "[media] drivers/media/usb/dvb-usb/dib0700_core.c: fix left shift" On Wed, 6 Feb 2013 09:04:39 +0000 Olivier GRENIE wrote: > I do not agree with the patch. Let's take an example: adap->id = 0. Then: > * 1 << ~(adap->id) = 1 << ~(0) = 0 > * ~(1 << adap->id) = ~(1 << 0) = 0xFE > > The correct change should be: st->channel_state |= 1 << (1 - adap->id); Indeed, the original source code was not correct. Requested-by: Olivier GRENIE Cc: Patrick Boettcher Cc: Nickolai Zeldovich Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/dib0700_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index bd6a43785d5e..bf2a908d74cf 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -584,7 +584,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) if (onoff) st->channel_state |= 1 << (adap->id); else - st->channel_state &= ~(1 << (adap->id)); + st->channel_state |= 1 << ~(adap->id); } else { if (onoff) st->channel_state |= 1 << (adap->fe_adap[0].stream.props.endpoint-2); From 61a2353c70d1f33c52768fb7d3fd7ee173928086 Mon Sep 17 00:00:00 2001 From: Volokh Konstantin Date: Wed, 16 Jan 2013 09:00:48 -0300 Subject: [PATCH 2010/4607] [media] staging: media: go7007: memory clear fix memory clearing for v4l2_subdev allocation Signed-off-by: Volokh Konstantin Acked-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-driver.c | 2 +- drivers/staging/media/go7007/go7007-v4l2.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index c9dfc75d1d2c..00014e754c75 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -572,7 +572,7 @@ struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev) struct go7007 *go; int i; - go = kmalloc(sizeof(struct go7007), GFP_KERNEL); + go = kzalloc(sizeof(struct go7007), GFP_KERNEL); if (go == NULL) return NULL; go->dev = dev; diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 0d3713dceba5..94899759229f 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -98,7 +98,7 @@ static int go7007_open(struct file *file) if (go->status != STATUS_ONLINE) return -EBUSY; - gofh = kmalloc(sizeof(struct go7007_file), GFP_KERNEL); + gofh = kzalloc(sizeof(struct go7007_file), GFP_KERNEL); if (gofh == NULL) return -ENOMEM; ++go->ref_count; From 7a295d1289f2be16f80f0a5242db330d542e0037 Mon Sep 17 00:00:00 2001 From: Volokh Konstantin Date: Wed, 16 Jan 2013 09:00:49 -0300 Subject: [PATCH 2011/4607] [media] staging: media: go7007: firmware protection Protection for unfirmware load If no firmware was loaded (no exists,wrong or some error) then rmmod fails with OOPS, so need some protection stuff. Signed-off-by: Volokh Konstantin Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-usb.c | 2 +- drivers/staging/media/go7007/go7007-v4l2.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index 5443e25086e9..a6cad1589475 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -1245,7 +1245,6 @@ static void go7007_usb_disconnect(struct usb_interface *intf) struct urb *vurb, *aurb; int i; - go->status = STATUS_SHUTDOWN; usb_kill_urb(usb->intr_urb); /* Free USB-related structs */ @@ -1269,6 +1268,7 @@ static void go7007_usb_disconnect(struct usb_interface *intf) kfree(go->hpi_context); go7007_remove(go); + go->status = STATUS_SHUTDOWN; } static struct usb_driver go7007_usb_driver = { diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 94899759229f..39e6749353be 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -1832,5 +1832,6 @@ void go7007_v4l2_remove(struct go7007 *go) mutex_unlock(&go->hw_lock); if (go->video_dev) video_unregister_device(go->video_dev); - v4l2_device_unregister(&go->v4l2_dev); + if (go->status != STATUS_SHUTDOWN) + v4l2_device_unregister(&go->v4l2_dev); } From d18b6acfe2f1c279ab2fb510eabc405cf538fb68 Mon Sep 17 00:00:00 2001 From: Volokh Konstantin Date: Wed, 16 Jan 2013 09:00:50 -0300 Subject: [PATCH 2012/4607] [media] staging: media: go7007: i2c GPIO initialization Reset i2c stuff for GO7007_BOARDID_ADLINK_MPG24 need reset GPIO always when encoder initialize Signed-off-by: Volokh Konstantin Reviewed-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-driver.c | 5 +++++ drivers/staging/media/go7007/go7007-usb.c | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index 00014e754c75..0aaeb0aeb99e 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -173,6 +173,11 @@ static int go7007_init_encoder(struct go7007 *go) go7007_write_addr(go, 0x3c82, 0x0001); go7007_write_addr(go, 0x3c80, 0x00fe); } + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { + /* set GPIO5 to be an output, currently low */ + go7007_write_addr(go, 0x3c82, 0x0000); + go7007_write_addr(go, 0x3c80, 0x00df); + } return 0; } diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c index a6cad1589475..9dbf5ecd05a2 100644 --- a/drivers/staging/media/go7007/go7007-usb.c +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -1110,9 +1110,6 @@ static int go7007_usb_probe(struct usb_interface *intf, } else { u16 channel; - /* set GPIO5 to be an output, currently low */ - go7007_write_addr(go, 0x3c82, 0x0000); - go7007_write_addr(go, 0x3c80, 0x00df); /* read channel number from GPIO[1:0] */ go7007_read_addr(go, 0x3c81, &channel); channel &= 0x3; From 0a147c3bf75d429fc7922abb582c7c686b028bc4 Mon Sep 17 00:00:00 2001 From: Volokh Konstantin Date: Wed, 16 Jan 2013 09:00:51 -0300 Subject: [PATCH 2013/4607] [media] staging: media: go7007: call_all stream stuff Some Additional stuff for v4l2_subdev stream events partial need for new style framework. Also need for wis_tw2804 notification stuff Signed-off-by: Volokh Konstantin Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-v4l2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 39e6749353be..cb9fe33050c7 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -953,6 +953,7 @@ static int vidioc_streamon(struct file *file, void *priv, } mutex_unlock(&go->hw_lock); mutex_unlock(&gofh->lock); + call_all(&go->v4l2_dev, video, s_stream, 1); return retval; } @@ -968,6 +969,7 @@ static int vidioc_streamoff(struct file *file, void *priv, mutex_lock(&gofh->lock); go7007_streamoff(go); mutex_unlock(&gofh->lock); + call_all(&go->v4l2_dev, video, s_stream, 0); return 0; } From 42f9de6eab3892d7da544be1cc882530eab5b203 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Fri, 25 Jan 2013 19:23:30 -0300 Subject: [PATCH 2014/4607] [media] staging/media/go7007: Use kmemdup rather than duplicating its implementation Found with coccicheck. The semantic patch that makes this change is available in scripts/coccinelle/api/memdup.cocci. Signed-off-by: Peter Huewe Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/go7007-driver.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c index 0aaeb0aeb99e..66950916df23 100644 --- a/drivers/staging/media/go7007/go7007-driver.c +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -108,14 +108,13 @@ static int go7007_load_encoder(struct go7007 *go) return -1; } fw_len = fw_entry->size - 16; - bounce = kmalloc(fw_len, GFP_KERNEL); + bounce = kmemdup(fw_entry->data + 16, fw_len, GFP_KERNEL); if (bounce == NULL) { v4l2_err(go, "unable to allocate %d bytes for " "firmware transfer\n", fw_len); release_firmware(fw_entry); return -1; } - memcpy(bounce, fw_entry->data + 16, fw_len); release_firmware(fw_entry); if (go7007_interface_reset(go) < 0 || go7007_send_firmware(go, bounce, fw_len) < 0 || From fb1e7b053a41807b613247535f269108f3b4fc70 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 28 Jan 2013 10:19:42 -0300 Subject: [PATCH 2015/4607] [media] staging: go7007: fix test for V4L2_STD_SECAM The current test doesn't make a lot of sense. It's likely to be true, which is what we would want in most cases. From looking at how this is handled in other drivers, I think "&" was intended instead of "*". It's an easy mistake to make because they are next to each other on the keyboard. Signed-off-by: Dan Carpenter Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/go7007/wis-saa7113.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/go7007/wis-saa7113.c b/drivers/staging/media/go7007/wis-saa7113.c index 8810c1e6e1ed..891cde713a47 100644 --- a/drivers/staging/media/go7007/wis-saa7113.c +++ b/drivers/staging/media/go7007/wis-saa7113.c @@ -141,7 +141,7 @@ static int wis_saa7113_command(struct i2c_client *client, } else if (dec->norm & V4L2_STD_PAL) { write_reg(client, 0x0e, 0x01); write_reg(client, 0x10, 0x48); - } else if (dec->norm * V4L2_STD_SECAM) { + } else if (dec->norm & V4L2_STD_SECAM) { write_reg(client, 0x0e, 0x50); write_reg(client, 0x10, 0x48); } From 1e2043779d8e34013f1563f1b9f3d5604092c29e Mon Sep 17 00:00:00 2001 From: Scott Jiang Date: Fri, 18 Jan 2013 17:09:47 -0300 Subject: [PATCH 2016/4607] [media] add maintainer for blackfin media drivers Signed-off-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 93c863833998..533422988824 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1652,6 +1652,15 @@ W: http://blackfin.uclinux.org/ S: Supported F: drivers/i2c/busses/i2c-bfin-twi.c +BLACKFIN MEDIA DRIVER +M: Scott Jiang +L: uclinux-dist-devel@blackfin.uclinux.org +W: http://blackfin.uclinux.org/ +S: Supported +F: drivers/media/platform/blackfin/ +F: drivers/media/i2c/adv7183* +F: drivers/media/i2c/vs6624* + BLINKM RGB LED DRIVER M: Jan-Simon Moeller S: Maintained From d78a488221059d2bc8627c5175f1d358c57d74a0 Mon Sep 17 00:00:00 2001 From: Scott Jiang Date: Fri, 18 Jan 2013 17:09:48 -0300 Subject: [PATCH 2017/4607] [media] blackfin: add error frame support Mark current frame as error frame when ppi error interrupt report fifo error. Member next_frm in struct bcap_device can be optimized out. Signed-off-by: Scott Jiang Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/blackfin/bfin_capture.c | 37 ++++++++++--------- drivers/media/platform/blackfin/ppi.c | 11 ++++++ include/media/blackfin/ppi.h | 3 +- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 12e0faf53403..5f209d5810dc 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -91,8 +91,6 @@ struct bcap_device { int num_sensor_formats; /* pointing to current video buffer */ struct bcap_buffer *cur_frm; - /* pointing to next video buffer */ - struct bcap_buffer *next_frm; /* buffer queue used in videobuf2 */ struct vb2_queue buffer_queue; /* allocator-specific contexts for each plane */ @@ -455,10 +453,10 @@ static int bcap_stop_streaming(struct vb2_queue *vq) /* release all active buffers */ while (!list_empty(&bcap_dev->dma_queue)) { - bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, + bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, struct bcap_buffer, list); - list_del(&bcap_dev->next_frm->list); - vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR); + list_del(&bcap_dev->cur_frm->list); + vb2_buffer_done(&bcap_dev->cur_frm->vb, VB2_BUF_STATE_ERROR); } return 0; } @@ -535,10 +533,21 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) spin_lock(&bcap_dev->lock); - if (bcap_dev->cur_frm != bcap_dev->next_frm) { + if (!list_empty(&bcap_dev->dma_queue)) { v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - bcap_dev->cur_frm = bcap_dev->next_frm; + if (ppi->err) { + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + ppi->err = false; + } else { + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + } + bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, + struct bcap_buffer, list); + list_del(&bcap_dev->cur_frm->list); + } else { + /* clear error flag, we will get a new frame */ + if (ppi->err) + ppi->err = false; } ppi->ops->stop(ppi); @@ -546,13 +555,8 @@ static irqreturn_t bcap_isr(int irq, void *dev_id) if (bcap_dev->stop) { complete(&bcap_dev->comp); } else { - if (!list_empty(&bcap_dev->dma_queue)) { - bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, - struct bcap_buffer, list); - list_del(&bcap_dev->next_frm->list); - addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->next_frm->vb, 0); - ppi->ops->update_addr(ppi, (unsigned long)addr); - } + addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); + ppi->ops->update_addr(ppi, (unsigned long)addr); ppi->ops->start(ppi); } @@ -586,9 +590,8 @@ static int bcap_streamon(struct file *file, void *priv, } /* get the next frame from the dma queue */ - bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, + bcap_dev->cur_frm = list_entry(bcap_dev->dma_queue.next, struct bcap_buffer, list); - bcap_dev->cur_frm = bcap_dev->next_frm; /* remove buffer from the dma queue */ list_del(&bcap_dev->cur_frm->list); addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index 1e24584605f2..01b5b501347e 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -59,19 +59,30 @@ static irqreturn_t ppi_irq_err(int irq, void *dev_id) * others are W1C */ status = bfin_read16(®->status); + if (status & 0x3000) + ppi->err = true; bfin_write16(®->status, 0xff00); break; } case PPI_TYPE_EPPI: { struct bfin_eppi_regs *reg = info->base; + unsigned short status; + + status = bfin_read16(®->status); + if (status & 0x2) + ppi->err = true; bfin_write16(®->status, 0xffff); break; } case PPI_TYPE_EPPI3: { struct bfin_eppi3_regs *reg = info->base; + unsigned long stat; + stat = bfin_read32(®->stat); + if (stat & 0x2) + ppi->err = true; bfin_write32(®->stat, 0xc0ff); break; } diff --git a/include/media/blackfin/ppi.h b/include/media/blackfin/ppi.h index 65c467576b31..d0697f4edf87 100644 --- a/include/media/blackfin/ppi.h +++ b/include/media/blackfin/ppi.h @@ -86,7 +86,8 @@ struct ppi_if { unsigned long ppi_control; const struct ppi_ops *ops; const struct ppi_info *info; - bool err_int; + bool err_int; /* if we need request error interrupt */ + bool err; /* if ppi has fifo error */ void *priv; }; From 8268979a429f6c3f6584d7d650af84444336d468 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:08 -0300 Subject: [PATCH 2018/4607] [media] [V2,01/24] pci/cx88/cx88.h: use IS_ENABLED() macro replace: #if defined(CONFIG_VIDEO_CX88_DVB) || \ defined(CONFIG_VIDEO_CX88_DVB_MODULE) with: #if IS_ENABLED(CONFIG_VIDEO_CX88_DVB) This change was made for: CONFIG_VIDEO_CX88_DVB, CONFIG_VIDEO_CX88_BLACKBIRD, CONFIG_VIDEO_CX88_VP3054 Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx88/cx88.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h index ba0dba4a4d22..feff53c0a251 100644 --- a/drivers/media/pci/cx88/cx88.h +++ b/drivers/media/pci/cx88/cx88.h @@ -363,7 +363,7 @@ struct cx88_core { unsigned int tuner_formats; /* config info -- dvb */ -#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_CX88_DVB) int (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); #endif void (*gate_ctrl)(struct cx88_core *core, int open); @@ -562,8 +562,7 @@ struct cx8802_dev { /* for blackbird only */ struct list_head devlist; -#if defined(CONFIG_VIDEO_CX88_BLACKBIRD) || \ - defined(CONFIG_VIDEO_CX88_BLACKBIRD_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_CX88_BLACKBIRD) struct video_device *mpeg_dev; u32 mailbox; int width; @@ -574,13 +573,12 @@ struct cx8802_dev { struct cx2341x_handler cxhdl; #endif -#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_CX88_DVB) /* for dvb only */ struct videobuf_dvb_frontends frontends; #endif -#if defined(CONFIG_VIDEO_CX88_VP3054) || \ - defined(CONFIG_VIDEO_CX88_VP3054_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_CX88_VP3054) /* For VP3045 secondary I2C bus support */ struct vp3054_i2c_state *vp3054; #endif From 8c31522c64968ae790b0d767d27655c24d913837 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:09 -0300 Subject: [PATCH 2019/4607] [media] [V2,02/24] pci/saa7134/saa7134.h: use IS_ENABLED() macro replace: #if defined(CONFIG_VIDEO_SAA7134_DVB) || \ defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) with: #if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB) This change was made for: CONFIG_VIDEO_SAA7134_DVB Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/saa7134/saa7134.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 6eae4321229a..f804324e07fd 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -42,7 +42,7 @@ #include #include #include -#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB) #include #endif @@ -644,7 +644,7 @@ struct saa7134_dev { struct work_struct empress_workqueue; int empress_started; -#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) +#if IS_ENABLED(CONFIG_VIDEO_SAA7134_DVB) /* SAA7134_MPEG_DVB only */ struct videobuf_dvb_frontends frontends; int (*original_demod_sleep)(struct dvb_frontend *fe); From b168e810b0e8bef68ccdfb06f741cb4d7142578c Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:10 -0300 Subject: [PATCH 2020/4607] [media] [V2,03/24] pci/ttpci/av7110.c: use IS_ENABLED() macro replace: #if defined(CONFIG_INPUT_EVDEV) || \ defined(CONFIG_INPUT_EVDEV_MODULE) with: #if IS_ENABLED(CONFIG_INPUT_EVDEV) This change was made for: CONFIG_INPUT_EVDEV, CONFIG_DVB_SP8870 Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/ttpci/av7110.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index 9f281f1db45e..3dc7aa9b6f40 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -235,7 +235,7 @@ static void recover_arm(struct av7110 *av7110) restart_feeds(av7110); -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +#if IS_ENABLED(CONFIG_INPUT_EVDEV) av7110_check_ir_config(av7110, true); #endif } @@ -268,7 +268,7 @@ static int arm_thread(void *data) if (!av7110->arm_ready) continue; -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +#if IS_ENABLED(CONFIG_INPUT_EVDEV) av7110_check_ir_config(av7110, false); #endif @@ -1730,7 +1730,7 @@ static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe) static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) { -#if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE) +#if IS_ENABLED(CONFIG_DVB_SP8870) struct av7110* av7110 = fe->dvb->priv; return request_firmware(fw, name, &av7110->dev->pci->dev); @@ -2725,7 +2725,7 @@ static int av7110_attach(struct saa7146_dev* dev, mutex_init(&av7110->ioctl_mutex); -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +#if IS_ENABLED(CONFIG_INPUT_EVDEV) av7110_ir_init(av7110); #endif printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); @@ -2768,7 +2768,7 @@ static int av7110_detach(struct saa7146_dev* saa) struct av7110 *av7110 = saa->ext_priv; dprintk(4, "%p\n", av7110); -#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +#if IS_ENABLED(CONFIG_INPUT_EVDEV) av7110_ir_exit(av7110); #endif if (budgetpatch || av7110->full_ts) { From f1c16adce0d2a14376dcafd00c90a88c885b4fed Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:11 -0300 Subject: [PATCH 2021/4607] [media] [V2,04/24] platform/marvell-ccic/mcam-core.h: use IS_ENABLED() macro replace: #if defined(CONFIG_VIDEOBUF2_VMALLOC) || \ defined(CONFIG_VIDEOBUF2_VMALLOC_MODULE) with: #if IS_ENABLED(CONFIG_VIDEOBUF2_VMALLOC) This change was made for: CONFIG_VIDEOBUF2_VMALLOC, CONFIG_VIDEOBUF2_DMA_CONTIG, CONFIG_VIDEOBUF2_DMA_SG Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/marvell-ccic/mcam-core.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 5e802c6b398e..6c53ac9681da 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -15,15 +15,15 @@ * Create our own symbols for the supported buffer modes, but, for now, * base them entirely on which videobuf2 options have been selected. */ -#if defined(CONFIG_VIDEOBUF2_VMALLOC) || defined(CONFIG_VIDEOBUF2_VMALLOC_MODULE) +#if IS_ENABLED(CONFIG_VIDEOBUF2_VMALLOC) #define MCAM_MODE_VMALLOC 1 #endif -#if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) || defined(CONFIG_VIDEOBUF2_DMA_CONTIG_MODULE) +#if IS_ENABLED(CONFIG_VIDEOBUF2_DMA_CONTIG) #define MCAM_MODE_DMA_CONTIG 1 #endif -#if defined(CONFIG_VIDEOBUF2_DMA_SG) || defined(CONFIG_VIDEOBUF2_DMA_SG_MODULE) +#if IS_ENABLED(CONFIG_VIDEOBUF2_DMA_SG) #define MCAM_MODE_DMA_SG 1 #endif From 9ecf9b085a0926e07c78c08a07296bbfd1c37d07 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:29 -0300 Subject: [PATCH 2022/4607] [media] [V2,22/24] usb/hdpvr/hdpvr-core.c: use IS_ENABLED() macro replace: #if defined(CONFIG_I2C) || \ defined(CONFIG_I2C_MODULE) with: #if IS_ENABLED(CONFIG_I2C) This change was made for: CONFIG_I2C Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hdpvr/hdpvr-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c index 84dc26fe80ee..5c6193536399 100644 --- a/drivers/media/usb/hdpvr/hdpvr-core.c +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -391,7 +391,7 @@ static int hdpvr_probe(struct usb_interface *interface, goto error; } -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) retval = hdpvr_register_i2c_adapter(dev); if (retval < 0) { v4l2_err(&dev->v4l2_dev, "i2c adapter register failed\n"); @@ -419,7 +419,7 @@ static int hdpvr_probe(struct usb_interface *interface, return 0; reg_fail: -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_adapter(&dev->i2c_adapter); #endif error: @@ -451,7 +451,7 @@ static void hdpvr_disconnect(struct usb_interface *interface) mutex_lock(&dev->io_mutex); hdpvr_cancel_queue(dev); mutex_unlock(&dev->io_mutex); -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_adapter(&dev->i2c_adapter); #endif video_unregister_device(dev->video_dev); From 6a29b8080bd99793c86060d0dbebfd81c6aa4281 Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:30 -0300 Subject: [PATCH 2023/4607] [media] [V2,23/24] usb/hdpvr/hdpvr-i2c.c: use IS_ENABLED() macro replace: #if defined(CONFIG_I2C) || \ defined(CONFIG_I2C_MODULE) with: #if IS_ENABLED(CONFIG_I2C) This change was made for: CONFIG_I2C Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hdpvr/hdpvr-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c index 6c5054f3f674..a38f58c4c6bf 100644 --- a/drivers/media/usb/hdpvr/hdpvr-i2c.c +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -13,7 +13,7 @@ * */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) #include #include From ffe4db06fdd294a3326e2f1009863825c0f6401c Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sat, 19 Jan 2013 19:41:31 -0300 Subject: [PATCH 2024/4607] [media] [V2,24/24] v4l2-core/v4l2-common.c: use IS_ENABLED() macro replace: #if defined(CONFIG_MEDIA_TUNER_TEA5761) || \ defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) with: #if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5761) This change was made for: CONFIG_MEDIA_TUNER_TEA5761 Also replaced: #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) with: #if IS_ENABLED(CONFIG_I2C) Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 614316f9b7a4..aa044f491666 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -238,7 +238,7 @@ int v4l2_chip_match_host(const struct v4l2_dbg_match *match) } EXPORT_SYMBOL(v4l2_chip_match_host); -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_I2C) int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match) { int len; @@ -384,7 +384,7 @@ EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr); const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type) { static const unsigned short radio_addrs[] = { -#if defined(CONFIG_MEDIA_TUNER_TEA5761) || defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5761) 0x10, #endif 0x60, From 7b34be71db533f3e0cf93d53cf62d036cdb5418a Mon Sep 17 00:00:00 2001 From: Peter Senna Tschudin Date: Sun, 20 Jan 2013 01:32:56 -0300 Subject: [PATCH 2025/4607] [media] use IS_ENABLED() macro This patch introduces the use of IS_ENABLED() macro. For example, replacing: #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) with: #if IS_ENABLED(CONFIG_I2C) All changes made by this patch respect the same replacement pattern. Reported-by: Mauro Carvalho Chehab Signed-off-by: Peter Senna Tschudin Acked-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/bcm3510.h | 2 +- drivers/media/dvb-frontends/cx22700.h | 2 +- drivers/media/dvb-frontends/cx24110.h | 2 +- drivers/media/dvb-frontends/dib0070.h | 2 +- drivers/media/dvb-frontends/dib0090.h | 2 +- drivers/media/dvb-frontends/dib3000.h | 2 +- drivers/media/dvb-frontends/dib8000.h | 2 +- drivers/media/dvb-frontends/dib9000.h | 2 +- drivers/media/dvb-frontends/dvb-pll.h | 2 +- drivers/media/dvb-frontends/isl6405.h | 2 +- drivers/media/dvb-frontends/isl6421.h | 2 +- drivers/media/dvb-frontends/isl6423.h | 2 +- drivers/media/dvb-frontends/itd1000.h | 2 +- drivers/media/dvb-frontends/l64781.h | 2 +- drivers/media/dvb-frontends/lgdt330x.h | 2 +- drivers/media/dvb-frontends/mb86a16.h | 2 +- drivers/media/dvb-frontends/mt312.h | 2 +- drivers/media/dvb-frontends/mt352.h | 2 +- drivers/media/dvb-frontends/nxt200x.h | 2 +- drivers/media/dvb-frontends/nxt6000.h | 2 +- drivers/media/dvb-frontends/or51132.h | 2 +- drivers/media/dvb-frontends/or51211.h | 2 +- drivers/media/dvb-frontends/s5h1420.h | 2 +- drivers/media/dvb-frontends/sp8870.h | 2 +- drivers/media/dvb-frontends/sp887x.h | 2 +- drivers/media/dvb-frontends/stb0899_drv.h | 2 +- drivers/media/dvb-frontends/stb6100.h | 2 +- drivers/media/dvb-frontends/stv0297.h | 2 +- drivers/media/dvb-frontends/stv0299.h | 2 +- drivers/media/dvb-frontends/stv090x.h | 2 +- drivers/media/dvb-frontends/stv6110x.h | 2 +- drivers/media/dvb-frontends/tda1002x.h | 5 ++--- drivers/media/dvb-frontends/tda1004x.h | 2 +- drivers/media/dvb-frontends/tda10086.h | 2 +- drivers/media/dvb-frontends/tda665x.h | 2 +- drivers/media/dvb-frontends/tda8083.h | 2 +- drivers/media/dvb-frontends/tda8261.h | 2 +- drivers/media/dvb-frontends/tda826x.h | 2 +- drivers/media/dvb-frontends/tua6100.h | 2 +- drivers/media/dvb-frontends/ves1820.h | 2 +- drivers/media/dvb-frontends/ves1x93.h | 2 +- drivers/media/dvb-frontends/zl10353.h | 2 +- drivers/media/pci/cx88/cx88-dvb.c | 4 ++-- drivers/media/pci/cx88/cx88-vp3054-i2c.h | 2 +- drivers/media/tuners/mt2060.h | 2 +- drivers/media/tuners/mt2063.h | 2 +- drivers/media/tuners/mt20xx.h | 2 +- drivers/media/tuners/mt2131.h | 2 +- drivers/media/tuners/mt2266.h | 2 +- drivers/media/tuners/mxl5007t.h | 2 +- drivers/media/tuners/qt1010.h | 2 +- drivers/media/tuners/tda18271.h | 2 +- drivers/media/tuners/tda827x.h | 2 +- drivers/media/tuners/tda8290.h | 2 +- drivers/media/tuners/tda9887.h | 2 +- drivers/media/tuners/tea5761.h | 2 +- drivers/media/tuners/tea5767.h | 2 +- drivers/media/tuners/tuner-simple.h | 2 +- drivers/media/tuners/tuner-xc2028.h | 2 +- drivers/media/tuners/xc4000.h | 2 +- drivers/media/v4l2-core/v4l2-device.c | 2 +- 61 files changed, 63 insertions(+), 64 deletions(-) diff --git a/drivers/media/dvb-frontends/bcm3510.h b/drivers/media/dvb-frontends/bcm3510.h index f4575c0cc446..5bd56b1623bf 100644 --- a/drivers/media/dvb-frontends/bcm3510.h +++ b/drivers/media/dvb-frontends/bcm3510.h @@ -34,7 +34,7 @@ struct bcm3510_config int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; -#if defined(CONFIG_DVB_BCM3510) || (defined(CONFIG_DVB_BCM3510_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_BCM3510) extern struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/cx22700.h b/drivers/media/dvb-frontends/cx22700.h index 4757a930ca05..382a7b1f3618 100644 --- a/drivers/media/dvb-frontends/cx22700.h +++ b/drivers/media/dvb-frontends/cx22700.h @@ -31,7 +31,7 @@ struct cx22700_config u8 demod_address; }; -#if defined(CONFIG_DVB_CX22700) || (defined(CONFIG_DVB_CX22700_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_CX22700) extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/cx24110.h b/drivers/media/dvb-frontends/cx24110.h index fdcceee91f3a..527aff1f2723 100644 --- a/drivers/media/dvb-frontends/cx24110.h +++ b/drivers/media/dvb-frontends/cx24110.h @@ -46,7 +46,7 @@ static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) return 0; } -#if defined(CONFIG_DVB_CX24110) || (defined(CONFIG_DVB_CX24110_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_CX24110) extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/dib0070.h b/drivers/media/dvb-frontends/dib0070.h index 45c31fae3967..0c6befcc9143 100644 --- a/drivers/media/dvb-frontends/dib0070.h +++ b/drivers/media/dvb-frontends/dib0070.h @@ -48,7 +48,7 @@ struct dib0070_config { u8 vga_filter; }; -#if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TUNER_DIB0070) extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg); extern u16 dib0070_wbd_offset(struct dvb_frontend *); extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open); diff --git a/drivers/media/dvb-frontends/dib0090.h b/drivers/media/dvb-frontends/dib0090.h index 781dc49de45b..6a090954fa10 100644 --- a/drivers/media/dvb-frontends/dib0090.h +++ b/drivers/media/dvb-frontends/dib0090.h @@ -75,7 +75,7 @@ struct dib0090_config { u8 force_crystal_mode; }; -#if defined(CONFIG_DVB_TUNER_DIB0090) || (defined(CONFIG_DVB_TUNER_DIB0090_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TUNER_DIB0090) extern struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); extern struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast); diff --git a/drivers/media/dvb-frontends/dib3000.h b/drivers/media/dvb-frontends/dib3000.h index 404f63a6f26b..9b6c3bbc983a 100644 --- a/drivers/media/dvb-frontends/dib3000.h +++ b/drivers/media/dvb-frontends/dib3000.h @@ -41,7 +41,7 @@ struct dib_fe_xfer_ops int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl); }; -#if defined(CONFIG_DVB_DIB3000MB) || (defined(CONFIG_DVB_DIB3000MB_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DIB3000MB) extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops); #else diff --git a/drivers/media/dvb-frontends/dib8000.h b/drivers/media/dvb-frontends/dib8000.h index 39591bb172c1..9e7a2b170d55 100644 --- a/drivers/media/dvb-frontends/dib8000.h +++ b/drivers/media/dvb-frontends/dib8000.h @@ -37,7 +37,7 @@ struct dib8000_config { #define DEFAULT_DIB8000_I2C_ADDRESS 18 -#if defined(CONFIG_DVB_DIB8000) || (defined(CONFIG_DVB_DIB8000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DIB8000) extern struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); extern struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); diff --git a/drivers/media/dvb-frontends/dib9000.h b/drivers/media/dvb-frontends/dib9000.h index de1cc91fd833..f3639f045ff0 100644 --- a/drivers/media/dvb-frontends/dib9000.h +++ b/drivers/media/dvb-frontends/dib9000.h @@ -27,7 +27,7 @@ struct dib9000_config { #define DEFAULT_DIB9000_I2C_ADDRESS 18 -#if defined(CONFIG_DVB_DIB9000) || (defined(CONFIG_DVB_DIB9000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_DIB9000) extern struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg); extern int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr); extern struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe); diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h index 4de754f76ce9..f4b5a0601c3a 100644 --- a/drivers/media/dvb-frontends/dvb-pll.h +++ b/drivers/media/dvb-frontends/dvb-pll.h @@ -38,7 +38,7 @@ * @param pll_desc_id dvb_pll_desc to use. * @return Frontend pointer on success, NULL on failure */ -#if defined(CONFIG_DVB_PLL) || (defined(CONFIG_DVB_PLL_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_PLL) extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, diff --git a/drivers/media/dvb-frontends/isl6405.h b/drivers/media/dvb-frontends/isl6405.h index 1c793d37576b..8abb70c26fd9 100644 --- a/drivers/media/dvb-frontends/isl6405.h +++ b/drivers/media/dvb-frontends/isl6405.h @@ -55,7 +55,7 @@ #define ISL6405_ENT2 0x20 #define ISL6405_ISEL2 0x40 -#if defined(CONFIG_DVB_ISL6405) || (defined(CONFIG_DVB_ISL6405_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_ISL6405) /* override_set and override_clear control which system register bits (above) * to always set & clear */ diff --git a/drivers/media/dvb-frontends/isl6421.h b/drivers/media/dvb-frontends/isl6421.h index 47e4518a042d..e7ca7d12b50a 100644 --- a/drivers/media/dvb-frontends/isl6421.h +++ b/drivers/media/dvb-frontends/isl6421.h @@ -39,7 +39,7 @@ #define ISL6421_ISEL1 0x20 #define ISL6421_DCL 0x40 -#if defined(CONFIG_DVB_ISL6421) || (defined(CONFIG_DVB_ISL6421_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_ISL6421) /* override_set and override_clear control which system register bits (above) to always set & clear */ extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, u8 override_set, u8 override_clear); diff --git a/drivers/media/dvb-frontends/isl6423.h b/drivers/media/dvb-frontends/isl6423.h index e1a37fba01ca..80dfd9cc4f41 100644 --- a/drivers/media/dvb-frontends/isl6423.h +++ b/drivers/media/dvb-frontends/isl6423.h @@ -42,7 +42,7 @@ struct isl6423_config { u8 mod_extern; }; -#if defined(CONFIG_DVB_ISL6423) || (defined(CONFIG_DVB_ISL6423_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_ISL6423) extern struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, diff --git a/drivers/media/dvb-frontends/itd1000.h b/drivers/media/dvb-frontends/itd1000.h index 5e18df071b88..edae0902f4fd 100644 --- a/drivers/media/dvb-frontends/itd1000.h +++ b/drivers/media/dvb-frontends/itd1000.h @@ -29,7 +29,7 @@ struct itd1000_config { u8 i2c_address; }; -#if defined(CONFIG_DVB_TUNER_ITD1000) || (defined(CONFIG_DVB_TUNER_ITD1000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TUNER_ITD1000) extern struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg); #else static inline struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg) diff --git a/drivers/media/dvb-frontends/l64781.h b/drivers/media/dvb-frontends/l64781.h index 1305a9e7fb0b..6813b08a774d 100644 --- a/drivers/media/dvb-frontends/l64781.h +++ b/drivers/media/dvb-frontends/l64781.h @@ -31,7 +31,7 @@ struct l64781_config u8 demod_address; }; -#if defined(CONFIG_DVB_L64781) || (defined(CONFIG_DVB_L64781_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_L64781) extern struct dvb_frontend* l64781_attach(const struct l64781_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/lgdt330x.h b/drivers/media/dvb-frontends/lgdt330x.h index 9012504f0f2d..ca0eab562e1e 100644 --- a/drivers/media/dvb-frontends/lgdt330x.h +++ b/drivers/media/dvb-frontends/lgdt330x.h @@ -52,7 +52,7 @@ struct lgdt330x_config int clock_polarity_flip; }; -#if defined(CONFIG_DVB_LGDT330X) || (defined(CONFIG_DVB_LGDT330X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_LGDT330X) extern struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/mb86a16.h b/drivers/media/dvb-frontends/mb86a16.h index 6ea8c376394f..277ce061acf9 100644 --- a/drivers/media/dvb-frontends/mb86a16.h +++ b/drivers/media/dvb-frontends/mb86a16.h @@ -33,7 +33,7 @@ struct mb86a16_config { -#if defined(CONFIG_DVB_MB86A16) || (defined(CONFIG_DVB_MB86A16_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_MB86A16) extern struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, struct i2c_adapter *i2c_adap); diff --git a/drivers/media/dvb-frontends/mt312.h b/drivers/media/dvb-frontends/mt312.h index 29e3bb5496b8..5706621ad79d 100644 --- a/drivers/media/dvb-frontends/mt312.h +++ b/drivers/media/dvb-frontends/mt312.h @@ -36,7 +36,7 @@ struct mt312_config { unsigned int voltage_inverted:1; }; -#if defined(CONFIG_DVB_MT312) || (defined(CONFIG_DVB_MT312_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_MT312) struct dvb_frontend *mt312_attach(const struct mt312_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/mt352.h b/drivers/media/dvb-frontends/mt352.h index ca2562d6f289..451d904e1500 100644 --- a/drivers/media/dvb-frontends/mt352.h +++ b/drivers/media/dvb-frontends/mt352.h @@ -51,7 +51,7 @@ struct mt352_config int (*demod_init)(struct dvb_frontend* fe); }; -#if defined(CONFIG_DVB_MT352) || (defined(CONFIG_DVB_MT352_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_MT352) extern struct dvb_frontend* mt352_attach(const struct mt352_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/nxt200x.h b/drivers/media/dvb-frontends/nxt200x.h index f3c84583770f..b518d545609e 100644 --- a/drivers/media/dvb-frontends/nxt200x.h +++ b/drivers/media/dvb-frontends/nxt200x.h @@ -42,7 +42,7 @@ struct nxt200x_config int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); }; -#if defined(CONFIG_DVB_NXT200X) || (defined(CONFIG_DVB_NXT200X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_NXT200X) extern struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/nxt6000.h b/drivers/media/dvb-frontends/nxt6000.h index 878eb38a075e..b5867c2ae681 100644 --- a/drivers/media/dvb-frontends/nxt6000.h +++ b/drivers/media/dvb-frontends/nxt6000.h @@ -33,7 +33,7 @@ struct nxt6000_config u8 clock_inversion:1; }; -#if defined(CONFIG_DVB_NXT6000) || (defined(CONFIG_DVB_NXT6000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_NXT6000) extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/or51132.h b/drivers/media/dvb-frontends/or51132.h index 1b8e04d973c8..938958386cb1 100644 --- a/drivers/media/dvb-frontends/or51132.h +++ b/drivers/media/dvb-frontends/or51132.h @@ -34,7 +34,7 @@ struct or51132_config int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); }; -#if defined(CONFIG_DVB_OR51132) || (defined(CONFIG_DVB_OR51132_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_OR51132) extern struct dvb_frontend* or51132_attach(const struct or51132_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/or51211.h b/drivers/media/dvb-frontends/or51211.h index 3ce0508b898e..9a8ae936b62d 100644 --- a/drivers/media/dvb-frontends/or51211.h +++ b/drivers/media/dvb-frontends/or51211.h @@ -37,7 +37,7 @@ struct or51211_config void (*sleep)(struct dvb_frontend * fe); }; -#if defined(CONFIG_DVB_OR51211) || (defined(CONFIG_DVB_OR51211_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_OR51211) extern struct dvb_frontend* or51211_attach(const struct or51211_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/s5h1420.h b/drivers/media/dvb-frontends/s5h1420.h index ff308136d865..210049b5cf30 100644 --- a/drivers/media/dvb-frontends/s5h1420.h +++ b/drivers/media/dvb-frontends/s5h1420.h @@ -40,7 +40,7 @@ struct s5h1420_config u8 serial_mpeg:1; }; -#if defined(CONFIG_DVB_S5H1420) || (defined(CONFIG_DVB_S5H1420_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_S5H1420) extern struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, struct i2c_adapter *i2c); extern struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe); diff --git a/drivers/media/dvb-frontends/sp8870.h b/drivers/media/dvb-frontends/sp8870.h index a764a793c7d8..065ec67d4e30 100644 --- a/drivers/media/dvb-frontends/sp8870.h +++ b/drivers/media/dvb-frontends/sp8870.h @@ -35,7 +35,7 @@ struct sp8870_config int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; -#if defined(CONFIG_DVB_SP8870) || (defined(CONFIG_DVB_SP8870_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_SP8870) extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/sp887x.h b/drivers/media/dvb-frontends/sp887x.h index 04eff6e0eef3..2cdc4e8bc9cd 100644 --- a/drivers/media/dvb-frontends/sp887x.h +++ b/drivers/media/dvb-frontends/sp887x.h @@ -17,7 +17,7 @@ struct sp887x_config int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; -#if defined(CONFIG_DVB_SP887X) || (defined(CONFIG_DVB_SP887X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_SP887X) extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/stb0899_drv.h b/drivers/media/dvb-frontends/stb0899_drv.h index 98b200ce0c34..8d26ff6eb1db 100644 --- a/drivers/media/dvb-frontends/stb0899_drv.h +++ b/drivers/media/dvb-frontends/stb0899_drv.h @@ -142,7 +142,7 @@ struct stb0899_config { int (*tuner_set_rfsiggain)(struct dvb_frontend *fe, u32 rf_gain); }; -#if defined(CONFIG_DVB_STB0899) || (defined(CONFIG_DVB_STB0899_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STB0899) extern struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c); diff --git a/drivers/media/dvb-frontends/stb6100.h b/drivers/media/dvb-frontends/stb6100.h index 2ab096614b3f..3a1e40f3b8be 100644 --- a/drivers/media/dvb-frontends/stb6100.h +++ b/drivers/media/dvb-frontends/stb6100.h @@ -94,7 +94,7 @@ struct stb6100_state { u32 reference; }; -#if defined(CONFIG_DVB_STB6100) || (defined(CONFIG_DVB_STB6100_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STB6100) extern struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, const struct stb6100_config *config, diff --git a/drivers/media/dvb-frontends/stv0297.h b/drivers/media/dvb-frontends/stv0297.h index 3f8f9468f387..c8ff3639ce00 100644 --- a/drivers/media/dvb-frontends/stv0297.h +++ b/drivers/media/dvb-frontends/stv0297.h @@ -42,7 +42,7 @@ struct stv0297_config u8 stop_during_read:1; }; -#if defined(CONFIG_DVB_STV0297) || (defined(CONFIG_DVB_STV0297_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV0297) extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/stv0299.h b/drivers/media/dvb-frontends/stv0299.h index ba219b767a69..06f70fc8327b 100644 --- a/drivers/media/dvb-frontends/stv0299.h +++ b/drivers/media/dvb-frontends/stv0299.h @@ -95,7 +95,7 @@ struct stv0299_config int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); }; -#if defined(CONFIG_DVB_STV0299) || (defined(CONFIG_DVB_STV0299_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV0299) extern struct dvb_frontend *stv0299_attach(const struct stv0299_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/dvb-frontends/stv090x.h b/drivers/media/dvb-frontends/stv090x.h index 29cdc2b71314..0bd6adcfee8a 100644 --- a/drivers/media/dvb-frontends/stv090x.h +++ b/drivers/media/dvb-frontends/stv090x.h @@ -103,7 +103,7 @@ struct stv090x_config { void (*tuner_i2c_lock) (struct dvb_frontend *fe, int lock); }; -#if defined(CONFIG_DVB_STV090x) || (defined(CONFIG_DVB_STV090x_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV090x) extern struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, struct i2c_adapter *i2c, diff --git a/drivers/media/dvb-frontends/stv6110x.h b/drivers/media/dvb-frontends/stv6110x.h index 47516753929a..bc4766db29c5 100644 --- a/drivers/media/dvb-frontends/stv6110x.h +++ b/drivers/media/dvb-frontends/stv6110x.h @@ -53,7 +53,7 @@ struct stv6110x_devctl { }; -#if defined(CONFIG_DVB_STV6110x) || (defined(CONFIG_DVB_STV6110x_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_STV6110x) extern struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, const struct stv6110x_config *config, diff --git a/drivers/media/dvb-frontends/tda1002x.h b/drivers/media/dvb-frontends/tda1002x.h index 04d19418bf20..e404b6e44802 100644 --- a/drivers/media/dvb-frontends/tda1002x.h +++ b/drivers/media/dvb-frontends/tda1002x.h @@ -57,7 +57,7 @@ struct tda10023_config { u16 deltaf; }; -#if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA10021) extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, struct i2c_adapter* i2c, u8 pwm); #else @@ -69,8 +69,7 @@ static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config* } #endif // CONFIG_DVB_TDA10021 -#if defined(CONFIG_DVB_TDA10023) || \ - (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA10023) extern struct dvb_frontend *tda10023_attach( const struct tda10023_config *config, struct i2c_adapter *i2c, u8 pwm); diff --git a/drivers/media/dvb-frontends/tda1004x.h b/drivers/media/dvb-frontends/tda1004x.h index 4e27ffb0f14e..dd283fbb61c0 100644 --- a/drivers/media/dvb-frontends/tda1004x.h +++ b/drivers/media/dvb-frontends/tda1004x.h @@ -117,7 +117,7 @@ struct tda1004x_state { enum tda1004x_demod demod_type; }; -#if defined(CONFIG_DVB_TDA1004X) || (defined(CONFIG_DVB_TDA1004X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA1004X) extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c); diff --git a/drivers/media/dvb-frontends/tda10086.h b/drivers/media/dvb-frontends/tda10086.h index 61148c558d8d..458fe91c1b88 100644 --- a/drivers/media/dvb-frontends/tda10086.h +++ b/drivers/media/dvb-frontends/tda10086.h @@ -46,7 +46,7 @@ struct tda10086_config enum tda10086_xtal xtal_freq; }; -#if defined(CONFIG_DVB_TDA10086) || (defined(CONFIG_DVB_TDA10086_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA10086) extern struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/tda665x.h b/drivers/media/dvb-frontends/tda665x.h index ec7927aa75ae..03a0da6d5cf2 100644 --- a/drivers/media/dvb-frontends/tda665x.h +++ b/drivers/media/dvb-frontends/tda665x.h @@ -31,7 +31,7 @@ struct tda665x_config { u32 ref_divider; }; -#if defined(CONFIG_DVB_TDA665x) || (defined(CONFIG_DVB_TDA665x_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA665x) extern struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, const struct tda665x_config *config, diff --git a/drivers/media/dvb-frontends/tda8083.h b/drivers/media/dvb-frontends/tda8083.h index 5a03c14a10e8..de6b1860dfdd 100644 --- a/drivers/media/dvb-frontends/tda8083.h +++ b/drivers/media/dvb-frontends/tda8083.h @@ -35,7 +35,7 @@ struct tda8083_config u8 demod_address; }; -#if defined(CONFIG_DVB_TDA8083) || (defined(CONFIG_DVB_TDA8083_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA8083) extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/tda8261.h b/drivers/media/dvb-frontends/tda8261.h index 006e45351b94..55cf4ffcbfdf 100644 --- a/drivers/media/dvb-frontends/tda8261.h +++ b/drivers/media/dvb-frontends/tda8261.h @@ -34,7 +34,7 @@ struct tda8261_config { enum tda8261_step step_size; }; -#if defined(CONFIG_DVB_TDA8261) || (defined(CONFIG_DVB_TDA8261_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA8261) extern struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, const struct tda8261_config *config, diff --git a/drivers/media/dvb-frontends/tda826x.h b/drivers/media/dvb-frontends/tda826x.h index 89e97926ab23..5f0f20e7e4f8 100644 --- a/drivers/media/dvb-frontends/tda826x.h +++ b/drivers/media/dvb-frontends/tda826x.h @@ -35,7 +35,7 @@ * @param has_loopthrough Set to 1 if the card has a loopthrough RF connector. * @return FE pointer on success, NULL on failure. */ -#if defined(CONFIG_DVB_TDA826X) || (defined(CONFIG_DVB_TDA826X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TDA826X) extern struct dvb_frontend* tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough); diff --git a/drivers/media/dvb-frontends/tua6100.h b/drivers/media/dvb-frontends/tua6100.h index f83dbd5e42ae..83a9c30e67ca 100644 --- a/drivers/media/dvb-frontends/tua6100.h +++ b/drivers/media/dvb-frontends/tua6100.h @@ -34,7 +34,7 @@ #include #include "dvb_frontend.h" -#if defined(CONFIG_DVB_TUA6100) || (defined(CONFIG_DVB_TUA6100_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_TUA6100) extern struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c); #else static inline struct dvb_frontend* tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c) diff --git a/drivers/media/dvb-frontends/ves1820.h b/drivers/media/dvb-frontends/ves1820.h index e902ed634ec3..c073f353ac38 100644 --- a/drivers/media/dvb-frontends/ves1820.h +++ b/drivers/media/dvb-frontends/ves1820.h @@ -41,7 +41,7 @@ struct ves1820_config u8 selagc:1; }; -#if defined(CONFIG_DVB_VES1820) || (defined(CONFIG_DVB_VES1820_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_VES1820) extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, struct i2c_adapter* i2c, u8 pwm); #else diff --git a/drivers/media/dvb-frontends/ves1x93.h b/drivers/media/dvb-frontends/ves1x93.h index 8a5a49e808f6..2307caea6aec 100644 --- a/drivers/media/dvb-frontends/ves1x93.h +++ b/drivers/media/dvb-frontends/ves1x93.h @@ -40,7 +40,7 @@ struct ves1x93_config u8 invert_pwm:1; }; -#if defined(CONFIG_DVB_VES1X93) || (defined(CONFIG_DVB_VES1X93_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_VES1X93) extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, struct i2c_adapter* i2c); #else diff --git a/drivers/media/dvb-frontends/zl10353.h b/drivers/media/dvb-frontends/zl10353.h index 6e3ca9eed048..50c1004aef36 100644 --- a/drivers/media/dvb-frontends/zl10353.h +++ b/drivers/media/dvb-frontends/zl10353.h @@ -47,7 +47,7 @@ struct zl10353_config u8 pll_0; /* default: 0x15 */ }; -#if defined(CONFIG_DVB_ZL10353) || (defined(CONFIG_DVB_ZL10353_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_DVB_ZL10353) extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, struct i2c_adapter *i2c); #else diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 50b5ac5b3983..672b267a2d3e 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -265,7 +265,7 @@ static struct mb86a16_config twinhan_vp1027 = { .demod_address = 0x08, }; -#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_VIDEO_CX88_VP3054) static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) { static const u8 clock_config [] = { 0x89, 0x38, 0x38 }; @@ -1127,7 +1127,7 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: -#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_VIDEO_CX88_VP3054) /* MT352 is on a secondary I2C bus made from some GPIO lines */ fe0->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config, &dev->vp3054->adap); diff --git a/drivers/media/pci/cx88/cx88-vp3054-i2c.h b/drivers/media/pci/cx88/cx88-vp3054-i2c.h index be99c931dc3e..95d0c60a35e1 100644 --- a/drivers/media/pci/cx88/cx88-vp3054-i2c.h +++ b/drivers/media/pci/cx88/cx88-vp3054-i2c.h @@ -30,7 +30,7 @@ struct vp3054_i2c_state { }; /* ----------------------------------------------------------------------- */ -#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_VIDEO_CX88_VP3054) int vp3054_i2c_probe(struct cx8802_dev *dev); void vp3054_i2c_remove(struct cx8802_dev *dev); #else diff --git a/drivers/media/tuners/mt2060.h b/drivers/media/tuners/mt2060.h index cb60caffb6b6..c64fc19cb278 100644 --- a/drivers/media/tuners/mt2060.h +++ b/drivers/media/tuners/mt2060.h @@ -30,7 +30,7 @@ struct mt2060_config { u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */ }; -#if defined(CONFIG_MEDIA_TUNER_MT2060) || (defined(CONFIG_MEDIA_TUNER_MT2060_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MT2060) extern struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1); #else static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) diff --git a/drivers/media/tuners/mt2063.h b/drivers/media/tuners/mt2063.h index ab24170c1571..e1acfc8e7ae3 100644 --- a/drivers/media/tuners/mt2063.h +++ b/drivers/media/tuners/mt2063.h @@ -8,7 +8,7 @@ struct mt2063_config { u32 refclock; }; -#if defined(CONFIG_MEDIA_TUNER_MT2063) || (defined(CONFIG_MEDIA_TUNER_MT2063_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MT2063) struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe, struct mt2063_config *config, struct i2c_adapter *i2c); diff --git a/drivers/media/tuners/mt20xx.h b/drivers/media/tuners/mt20xx.h index 259553a24903..f56241ccaa00 100644 --- a/drivers/media/tuners/mt20xx.h +++ b/drivers/media/tuners/mt20xx.h @@ -20,7 +20,7 @@ #include #include "dvb_frontend.h" -#if defined(CONFIG_MEDIA_TUNER_MT20XX) || (defined(CONFIG_MEDIA_TUNER_MT20XX_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MT20XX) extern struct dvb_frontend *microtune_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap, u8 i2c_addr); diff --git a/drivers/media/tuners/mt2131.h b/drivers/media/tuners/mt2131.h index 6632de640df0..09ceaf68e47c 100644 --- a/drivers/media/tuners/mt2131.h +++ b/drivers/media/tuners/mt2131.h @@ -30,7 +30,7 @@ struct mt2131_config { u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */ }; -#if defined(CONFIG_MEDIA_TUNER_MT2131) || (defined(CONFIG_MEDIA_TUNER_MT2131_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MT2131) extern struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2131_config *cfg, diff --git a/drivers/media/tuners/mt2266.h b/drivers/media/tuners/mt2266.h index 4d083882d044..fad6dd657d77 100644 --- a/drivers/media/tuners/mt2266.h +++ b/drivers/media/tuners/mt2266.h @@ -24,7 +24,7 @@ struct mt2266_config { u8 i2c_address; }; -#if defined(CONFIG_MEDIA_TUNER_MT2266) || (defined(CONFIG_MEDIA_TUNER_MT2266_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MT2266) extern struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg); #else static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg) diff --git a/drivers/media/tuners/mxl5007t.h b/drivers/media/tuners/mxl5007t.h index aa3eea0b5262..37b0942e2385 100644 --- a/drivers/media/tuners/mxl5007t.h +++ b/drivers/media/tuners/mxl5007t.h @@ -77,7 +77,7 @@ struct mxl5007t_config { unsigned int clk_out_enable:1; }; -#if defined(CONFIG_MEDIA_TUNER_MXL5007T) || (defined(CONFIG_MEDIA_TUNER_MXL5007T_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_MXL5007T) extern struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 addr, struct mxl5007t_config *cfg); diff --git a/drivers/media/tuners/qt1010.h b/drivers/media/tuners/qt1010.h index 807fb7b6146b..8ab5d479749f 100644 --- a/drivers/media/tuners/qt1010.h +++ b/drivers/media/tuners/qt1010.h @@ -36,7 +36,7 @@ struct qt1010_config { * @param cfg tuner hw based configuration * @return fe pointer on success, NULL on failure */ -#if defined(CONFIG_MEDIA_TUNER_QT1010) || (defined(CONFIG_MEDIA_TUNER_QT1010_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_QT1010) extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct qt1010_config *cfg); diff --git a/drivers/media/tuners/tda18271.h b/drivers/media/tuners/tda18271.h index 89b6c6d93fec..4c418d63f540 100644 --- a/drivers/media/tuners/tda18271.h +++ b/drivers/media/tuners/tda18271.h @@ -121,7 +121,7 @@ enum tda18271_mode { TDA18271_DIGITAL, }; -#if defined(CONFIG_MEDIA_TUNER_TDA18271) || (defined(CONFIG_MEDIA_TUNER_TDA18271_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA18271) extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, struct i2c_adapter *i2c, struct tda18271_config *cfg); diff --git a/drivers/media/tuners/tda827x.h b/drivers/media/tuners/tda827x.h index 7d72ce0a0c2d..9432b5b6121b 100644 --- a/drivers/media/tuners/tda827x.h +++ b/drivers/media/tuners/tda827x.h @@ -50,7 +50,7 @@ struct tda827x_config * @param cfg optional callback function pointers. * @return FE pointer on success, NULL on failure. */ -#if defined(CONFIG_MEDIA_TUNER_TDA827X) || (defined(CONFIG_MEDIA_TUNER_TDA827X_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA827X) extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, struct tda827x_config *cfg); diff --git a/drivers/media/tuners/tda8290.h b/drivers/media/tuners/tda8290.h index 7e288b26fcc3..e12ecbaa35a4 100644 --- a/drivers/media/tuners/tda8290.h +++ b/drivers/media/tuners/tda8290.h @@ -28,7 +28,7 @@ struct tda829x_config { #define TDA829X_DONT_PROBE 1 }; -#if defined(CONFIG_MEDIA_TUNER_TDA8290) || (defined(CONFIG_MEDIA_TUNER_TDA8290_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA8290) extern int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr); extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, diff --git a/drivers/media/tuners/tda9887.h b/drivers/media/tuners/tda9887.h index acc419e8c4fc..37a4a1123e0c 100644 --- a/drivers/media/tuners/tda9887.h +++ b/drivers/media/tuners/tda9887.h @@ -21,7 +21,7 @@ #include "dvb_frontend.h" /* ------------------------------------------------------------------------ */ -#if defined(CONFIG_MEDIA_TUNER_TDA9887) || (defined(CONFIG_MEDIA_TUNER_TDA9887_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA9887) extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap, u8 i2c_addr); diff --git a/drivers/media/tuners/tea5761.h b/drivers/media/tuners/tea5761.h index 2e2ff82c95a4..933228ffb509 100644 --- a/drivers/media/tuners/tea5761.h +++ b/drivers/media/tuners/tea5761.h @@ -20,7 +20,7 @@ #include #include "dvb_frontend.h" -#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5761) extern int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr); extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, diff --git a/drivers/media/tuners/tea5767.h b/drivers/media/tuners/tea5767.h index d30ab1b483de..c39101199383 100644 --- a/drivers/media/tuners/tea5767.h +++ b/drivers/media/tuners/tea5767.h @@ -39,7 +39,7 @@ struct tea5767_ctrl { enum tea5767_xtal xtal_freq; }; -#if defined(CONFIG_MEDIA_TUNER_TEA5767) || (defined(CONFIG_MEDIA_TUNER_TEA5767_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_TEA5767) extern int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr); extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, diff --git a/drivers/media/tuners/tuner-simple.h b/drivers/media/tuners/tuner-simple.h index 381fa5d35a9b..ffd12cfe650b 100644 --- a/drivers/media/tuners/tuner-simple.h +++ b/drivers/media/tuners/tuner-simple.h @@ -20,7 +20,7 @@ #include #include "dvb_frontend.h" -#if defined(CONFIG_MEDIA_TUNER_SIMPLE) || (defined(CONFIG_MEDIA_TUNER_SIMPLE_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_SIMPLE) extern struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap, u8 i2c_addr, diff --git a/drivers/media/tuners/tuner-xc2028.h b/drivers/media/tuners/tuner-xc2028.h index 9ebfb2d0ff14..181d087faec4 100644 --- a/drivers/media/tuners/tuner-xc2028.h +++ b/drivers/media/tuners/tuner-xc2028.h @@ -56,7 +56,7 @@ struct xc2028_config { #define XC2028_RESET_CLK 1 #define XC2028_I2C_FLUSH 2 -#if defined(CONFIG_MEDIA_TUNER_XC2028) || (defined(CONFIG_MEDIA_TUNER_XC2028_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_XC2028) extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, struct xc2028_config *cfg); #else diff --git a/drivers/media/tuners/xc4000.h b/drivers/media/tuners/xc4000.h index e6a44d151cbd..97c23de5296c 100644 --- a/drivers/media/tuners/xc4000.h +++ b/drivers/media/tuners/xc4000.h @@ -50,7 +50,7 @@ struct xc4000_config { * it's passed back to a bridge during tuner_callback(). */ -#if defined(CONFIG_MEDIA_TUNER_XC4000) || (defined(CONFIG_MEDIA_TUNER_XC4000_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_MEDIA_TUNER_XC4000) extern struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct xc4000_config *cfg); diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c index 98a7f5e9cb1b..8ed5da2170bf 100644 --- a/drivers/media/v4l2-core/v4l2-device.c +++ b/drivers/media/v4l2-core/v4l2-device.c @@ -112,7 +112,7 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) /* Unregister subdevs */ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) { v4l2_device_unregister_subdev(sd); -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +#if IS_ENABLED(CONFIG_I2C) if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) { struct i2c_client *client = v4l2_get_subdevdata(sd); From f23999eccb5f1b6ec858279670307b5b1abe887a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Jan 2013 06:09:07 -0300 Subject: [PATCH 2026/4607] [media] media: Convert to devm_ioremap_resource() Convert all uses of devm_request_and_ioremap() to the newly introduced devm_ioremap_resource() which provides more consistent error handling. devm_ioremap_resource() provides its own error messages so all explicit error messages can be removed from the failure code paths. Signed-off-by: Thierry Reding Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/exynos-gsc/gsc-core.c | 8 +++----- drivers/media/platform/mx2_emmaprp.c | 6 +++--- drivers/media/platform/s3c-camif/camif-core.c | 8 +++----- drivers/media/platform/s5p-fimc/fimc-core.c | 8 +++----- drivers/media/platform/s5p-fimc/fimc-lite.c | 8 +++----- drivers/media/platform/s5p-fimc/mipi-csis.c | 8 +++----- drivers/media/platform/s5p-g2d/g2d.c | 8 +++----- drivers/media/platform/s5p-jpeg/jpeg-core.c | 8 +++----- drivers/media/platform/s5p-mfc/s5p_mfc.c | 8 +++----- drivers/media/platform/soc_camera/mx2_camera.c | 12 ++++++------ 10 files changed, 33 insertions(+), 49 deletions(-) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 99b841d2f8fc..82d9f6ac12f3 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1099,11 +1099,9 @@ static int gsc_probe(struct platform_device *pdev) gsc->clock = ERR_PTR(-EINVAL); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - gsc->regs = devm_request_and_ioremap(dev, res); - if (!gsc->regs) { - dev_err(dev, "failed to map registers\n"); - return -ENOENT; - } + gsc->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(gsc->regs)) + return PTR_ERR(gsc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index 6b155d7be8e0..4b9e0a28616a 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -941,9 +941,9 @@ static int emmaprp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcdev); - pcdev->base_emma = devm_request_and_ioremap(&pdev->dev, res_emma); - if (!pcdev->base_emma) { - ret = -ENXIO; + pcdev->base_emma = devm_ioremap_resource(&pdev->dev, res_emma); + if (IS_ERR(pcdev->base_emma)) { + ret = PTR_ERR(pcdev->base_emma); goto rel_vdev; } diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index 8f0ca02c29a9..0d0fab1a7b5e 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -434,11 +434,9 @@ static int s3c_camif_probe(struct platform_device *pdev) mres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - camif->io_base = devm_request_and_ioremap(dev, mres); - if (!camif->io_base) { - dev_err(dev, "failed to obtain I/O memory\n"); - return -ENOENT; - } + camif->io_base = devm_ioremap_resource(dev, mres); + if (IS_ERR(camif->io_base)) + return PTR_ERR(camif->io_base); ret = camif_request_irqs(pdev, camif); if (ret < 0) diff --git a/drivers/media/platform/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c index 29f7bb71c7e1..e3916bde45cf 100644 --- a/drivers/media/platform/s5p-fimc/fimc-core.c +++ b/drivers/media/platform/s5p-fimc/fimc-core.c @@ -893,11 +893,9 @@ static int fimc_probe(struct platform_device *pdev) mutex_init(&fimc->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_request_and_ioremap(&pdev->dev, res); - if (fimc->regs == NULL) { - dev_err(&pdev->dev, "Failed to obtain io memory\n"); - return -ENOENT; - } + fimc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fimc->regs)) + return PTR_ERR(fimc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index e18babfa89e0..bfc4206935c8 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -1500,11 +1500,9 @@ static int fimc_lite_probe(struct platform_device *pdev) mutex_init(&fimc->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - fimc->regs = devm_request_and_ioremap(&pdev->dev, res); - if (fimc->regs == NULL) { - dev_err(&pdev->dev, "Failed to obtain io memory\n"); - return -ENOENT; - } + fimc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fimc->regs)) + return PTR_ERR(fimc->regs); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 613482fd74a9..1cc6501c213f 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -733,11 +733,9 @@ static int s5pcsis_probe(struct platform_device *pdev) } mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - state->regs = devm_request_and_ioremap(&pdev->dev, mem_res); - if (state->regs == NULL) { - dev_err(&pdev->dev, "Failed to request and remap io memory\n"); - return -ENXIO; - } + state->regs = devm_ioremap_resource(&pdev->dev, mem_res); + if (IS_ERR(state->regs)) + return PTR_ERR(state->regs); state->irq = platform_get_irq(pdev, 0); if (state->irq < 0) { diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 7e415297e174..aaaf276a5a6c 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -713,11 +713,9 @@ static int g2d_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->regs = devm_request_and_ioremap(&pdev->dev, res); - if (dev->regs == NULL) { - dev_err(&pdev->dev, "Failed to obtain io memory\n"); - return -ENOENT; - } + dev->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->regs)) + return PTR_ERR(dev->regs); dev->clk = clk_get(&pdev->dev, "sclk_fimg2d"); if (IS_ERR(dev->clk)) { diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c index 17983c4c9a9a..3b023752bcb4 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-core.c +++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c @@ -1325,11 +1325,9 @@ static int s5p_jpeg_probe(struct platform_device *pdev) /* memory-mapped registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - jpeg->regs = devm_request_and_ioremap(&pdev->dev, res); - if (jpeg->regs == NULL) { - dev_err(&pdev->dev, "Failed to obtain io memory\n"); - return -ENOENT; - } + jpeg->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(jpeg->regs)) + return PTR_ERR(jpeg->regs); /* interrupt service routine registration */ jpeg->irq = ret = platform_get_irq(pdev, 0); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 3669e3b933ca..e84703c314ce 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1087,11 +1087,9 @@ static int s5p_mfc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->regs_base = devm_request_and_ioremap(&pdev->dev, res); - if (dev->regs_base == NULL) { - dev_err(&pdev->dev, "Failed to obtain io memory\n"); - return -ENOENT; - } + dev->regs_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->regs_base)) + return PTR_ERR(dev->regs_base); res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 27b2e96f27a6..ea56b7589211 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -1444,9 +1444,9 @@ static int mx27_camera_emma_init(struct platform_device *pdev) goto out; } - pcdev->base_emma = devm_request_and_ioremap(pcdev->dev, res_emma); - if (!pcdev->base_emma) { - err = -EADDRNOTAVAIL; + pcdev->base_emma = devm_ioremap_resource(pcdev->dev, res_emma); + if (IS_ERR(pcdev->base_emma)) { + err = PTR_ERR(pcdev->base_emma); goto out; } @@ -1547,9 +1547,9 @@ static int mx2_camera_probe(struct platform_device *pdev) INIT_LIST_HEAD(&pcdev->discard); spin_lock_init(&pcdev->lock); - pcdev->base_csi = devm_request_and_ioremap(&pdev->dev, res_csi); - if (!pcdev->base_csi) { - err = -EADDRNOTAVAIL; + pcdev->base_csi = devm_ioremap_resource(&pdev->dev, res_csi); + if (IS_ERR(pcdev->base_csi)) { + err = PTR_ERR(pcdev->base_csi); goto exit; } From 05c79d0d1d0d44c2195d63d1e7c62e358eadeadf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 21 Jan 2013 06:51:41 -0300 Subject: [PATCH 2027/4607] [media] uvcvideo: Implement videobuf2 .wait_prepare and .wait_finish operations Those optional operations are used to release and reacquire the queue lock when videobuf2 needs to perform operations that sleep for a long time, such as waiting for a buffer to be complete. Implement them to avoid blocking qbuf or streamoff calls when a dqbuf is in progress. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_queue.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 778addc5caff..6c233a54ce40 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -115,11 +115,27 @@ static int uvc_buffer_finish(struct vb2_buffer *vb) return 0; } +static void uvc_wait_prepare(struct vb2_queue *vq) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + + mutex_unlock(&queue->mutex); +} + +static void uvc_wait_finish(struct vb2_queue *vq) +{ + struct uvc_video_queue *queue = vb2_get_drv_priv(vq); + + mutex_lock(&queue->mutex); +} + static struct vb2_ops uvc_queue_qops = { .queue_setup = uvc_queue_setup, .buf_prepare = uvc_buffer_prepare, .buf_queue = uvc_buffer_queue, .buf_finish = uvc_buffer_finish, + .wait_prepare = uvc_wait_prepare, + .wait_finish = uvc_wait_finish, }; int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, From 8cd5d42ac6d75143ab4bf44a15f76245a7f6c884 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 18 Jan 2013 13:52:38 -0300 Subject: [PATCH 2028/4607] [media] s5p-csis: Fix clock handling on error path in probe() Move e_clkput label after the clk_disable() call so a not acquired clock is not attempted to be disabled. This fixes runtime warnings like: s5p-mipi-csis 11880000.csis: failed to get clock: sclk_csis ------------[ cut here ]------------ WARNING: at drivers/clk/clk.c:478 clk_disable+0x24/0x34() Modules linked in: [] (unwind_backtrace+0x0/0x13c) from [] (warn_slowpath_common+0x54/0x64) [] (warn_slowpath_common+0x54/0x64) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null+0x1c/0x24) from [] (clk_disable+0x24/0x34) [] (clk_disable+0x24/0x34) from [] (s5pcsis_probe+0x25c/0x4c8) [] (s5pcsis_probe+0x25c/0x4c8) from [] (platform_drv_probe+0x18/0x1c) [] (platform_drv_probe+0x18/0x1c) from [] (driver_probe_device+0xa4/0x368) [] (driver_probe_device+0xa4/0x368) from [] (__driver_attach+0x8c/0x90) [] (__driver_attach+0x8c/0x90) from [] (bus_for_each_dev+0x60/0x8c) [] (bus_for_each_dev+0x60/0x8c) from [] (bus_add_driver+0x20c/0x2d4) [] (bus_add_driver+0x20c/0x2d4) from [] (driver_register+0x78/0x194) [] (driver_register+0x78/0x194) from [] (do_one_initcall+0x34/0x188) [] (do_one_initcall+0x34/0x188) from [] (kernel_init+0x180/0x2f0) [] (kernel_init+0x180/0x2f0) from [] (ret_from_fork+0x14/0x3c) ---[ end trace 0c5a55345c42530b ]--- Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/mipi-csis.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 1cc6501c213f..981863d05aaa 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -771,7 +771,7 @@ static int s5pcsis_probe(struct platform_device *pdev) 0, dev_name(&pdev->dev), state); if (ret) { dev_err(&pdev->dev, "Interrupt request failed\n"); - goto e_clkput; + goto e_clkdis; } v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); @@ -789,7 +789,7 @@ static int s5pcsis_probe(struct platform_device *pdev) ret = media_entity_init(&state->sd.entity, CSIS_PADS_NUM, state->pads, 0); if (ret < 0) - goto e_clkput; + goto e_clkdis; /* This allows to retrieve the platform device id by the host driver */ v4l2_set_subdevdata(&state->sd, pdev); @@ -802,8 +802,9 @@ static int s5pcsis_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); return 0; -e_clkput: +e_clkdis: clk_disable(state->clock[CSIS_CLK_MUX]); +e_clkput: s5pcsis_clk_put(state); return ret; } From eb36209297b7aadbd3144c74cb79d35704c3086e Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Fri, 11 Jan 2013 11:29:34 -0300 Subject: [PATCH 2029/4607] [media] s5p-mfc: end-of-stream handling in encoder bug fix In some circumstances after issuing the V4L2_ENC_CMD_STOP the application could freeze. This patch prevents this behavior. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-mfc/s5p_mfc_enc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index f92f6ddd739f..2356fd52a169 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -1534,6 +1534,8 @@ int vidioc_encoder_cmd(struct file *file, void *priv, if (list_empty(&ctx->src_queue)) { mfc_debug(2, "EOS: empty src queue, entering finishing state"); ctx->state = MFCINST_FINISHING; + if (s5p_mfc_ctx_ready(ctx)) + set_work_bit_irqsave(ctx); spin_unlock_irqrestore(&dev->irqlock, flags); s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); } else { From 248ac368ce4b3cd36515122d888403909d7a2500 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 11 Jan 2013 07:06:28 -0300 Subject: [PATCH 2030/4607] [media] s5p-fimc: Fix fimc-lite entities deregistration Clear the proper array when deregistering FIMC-LITE devices. Now fimc[] array is erroneously accessed instead of fimc_lite[] and fimc_md_unregister_entities() function call can result in an oops from NULL pointer dereference, since fmd->fimc[] is cleared earlier. This might happen in normal conditions when the driver's probing is deferred and then retried. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-fimc/fimc-mdevice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index f49f6f17a3f7..a17fcb2d5d41 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -472,7 +472,7 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) if (fmd->fimc_lite[i] == NULL) continue; v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev); - fmd->fimc[i]->pipeline_ops = NULL; + fmd->fimc_lite[i]->pipeline_ops = NULL; fmd->fimc_lite[i] = NULL; } for (i = 0; i < CSIS_MAX_ENTITIES; i++) { From 98783e453c1084527388ec1a7f6367cd6aabbe63 Mon Sep 17 00:00:00 2001 From: Wang Shilong Date: Wed, 6 Feb 2013 14:14:26 +0800 Subject: [PATCH 2031/4607] Ext2: remove the overhead check about sb in the function ext2_new_blocks It can be guranteed that inode->i_sb should not be null in vfs. So here the check about it is overhead. Signed-off-by: Wang Shilong Signed-off-by: Jan Kara --- fs/ext2/balloc.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 2616d0ea5c5c..ea88181932df 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -1239,10 +1239,6 @@ ext2_fsblk_t ext2_new_blocks(struct inode *inode, ext2_fsblk_t goal, *errp = -ENOSPC; sb = inode->i_sb; - if (!sb) { - printk("ext2_new_blocks: nonexistent device"); - return 0; - } /* * Check quota for allocation of this block. From 5815d0c4d55a80310138698c9dc516c759b6be87 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Tue, 22 Jan 2013 15:51:55 -0300 Subject: [PATCH 2032/4607] [media] v4l2-core: do not enable the buffer ioctls for radio devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The buffer ioctls (VIDIOC_REQBUFS, VIDIOC_QUERYBUF, VIDIOC_QBUF, VIDIOC_DQBUF, VIDIOC_EXPBUF, VIDIOC_CREATE_BUFS, VIDIOC_PREPARE_BUF) are not applicable for radio devices. Hence, they should be set valid only for non-radio devices in determine_valid_ioctls(). Signed-off-by: Frank Schäfer Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 98dcad9c8a3b..51b3a7713dcd 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -568,11 +568,6 @@ static void determine_valid_ioctls(struct video_device *vdev) if (ops->vidioc_s_priority || test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); - SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); - SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); - SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf); - SET_VALID_IOCTL(ops, VIDIOC_EXPBUF, vidioc_expbuf); - SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); /* Note: the control handler can also be passed through the filehandle, @@ -605,8 +600,6 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event); SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event); SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event); - SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); - SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator) set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls); @@ -672,6 +665,13 @@ static void determine_valid_ioctls(struct video_device *vdev) } if (!is_radio) { /* ioctls valid for video or vbi */ + SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); + SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); + SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf); + SET_VALID_IOCTL(ops, VIDIOC_EXPBUF, vidioc_expbuf); + SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); + SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); + SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); if (ops->vidioc_s_std) set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls); if (ops->vidioc_g_std || vdev->current_norm) From 8dc97ea20c2bdf406348640abbd35eb89c843957 Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Tue, 5 Feb 2013 14:34:37 -0300 Subject: [PATCH 2033/4607] [media] sta2x11_vip: convert to videobuf2, control framework, file handler This patch re-write the driver and use the videobuf2 interface instead of the old videobuf. Moreover, it uses also the control framework which allows the driver to inherit controls from its subdevice (ADV7180). Finally the driver does not implement custom file operation but it uses the generic ones from videobuf2 and v4l2_fh Signed-off-by: Federico Vaga Acked-by: Giancarlo Asnaghi Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/sta2x11/Kconfig | 2 +- drivers/media/pci/sta2x11/sta2x11_vip.c | 1091 +++++++++-------------- 2 files changed, 443 insertions(+), 650 deletions(-) diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index 6749f67cab8a..a94ccad02066 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -2,7 +2,7 @@ config STA2X11_VIP tristate "STA2X11 VIP Video For Linux" depends on STA2X11 select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT - select VIDEOBUF_DMA_CONTIG + select VIDEOBUF2_DMA_CONTIG depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS help Say Y for support for STA2X11 VIP (Video Input Port) capture diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c index 1352e50e457b..4b703fe8c953 100644 --- a/drivers/media/pci/sta2x11/sta2x11_vip.c +++ b/drivers/media/pci/sta2x11/sta2x11_vip.c @@ -1,7 +1,11 @@ /* * This is the driver for the STA2x11 Video Input Port. * + * Copyright (C) 2012 ST Microelectronics + * author: Federico Vaga * Copyright (C) 2010 WindRiver Systems, Inc. + * authors: Andreas Kies + * Vlad Lungu * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -19,36 +23,30 @@ * The full GNU General Public License is included in this distribution in * the file called "COPYING". * - * Author: Andreas Kies - * Vlad Lungu - * */ #include #include #include #include -#include - #include - #include - #include #include -#include #include #include #include #include #include #include +#include #include -#include +#include +#include +#include #include "sta2x11_vip.h" -#define DRV_NAME "sta2x11_vip" #define DRV_VERSION "1.3" #ifndef PCI_DEVICE_ID_STMICRO_VIP @@ -63,8 +61,8 @@ #define DVP_TFS 0x08 #define DVP_BFO 0x0C #define DVP_BFS 0x10 -#define DVP_VTP 0x14 -#define DVP_VBP 0x18 +#define DVP_VTP 0x14 +#define DVP_VBP 0x18 #define DVP_VMP 0x1C #define DVP_ITM 0x98 #define DVP_ITS 0x9C @@ -84,13 +82,21 @@ #define DVP_HLFLN_SD 0x00000001 -#define REG_WRITE(vip, reg, value) iowrite32((value), (vip->iomem)+(reg)) -#define REG_READ(vip, reg) ioread32((vip->iomem)+(reg)) - #define SAVE_COUNT 8 #define AUX_COUNT 3 #define IRQ_COUNT 1 + +struct vip_buffer { + struct vb2_buffer vb; + struct list_head list; + dma_addr_t dma; +}; +static inline struct vip_buffer *to_vip_buffer(struct vb2_buffer *vb2) +{ + return container_of(vb2, struct vip_buffer, vb); +} + /** * struct sta2x11_vip - All internal data for one instance of device * @v4l2_dev: device registered in v4l layer @@ -99,29 +105,26 @@ * @adapter: contains I2C adapter information * @register_save_area: All relevant register are saved here during suspend * @decoder: contains information about video DAC + * @ctrl_hdl: handler for control framework * @format: pixel format, fixed UYVY * @std: video standard (e.g. PAL/NTSC) * @input: input line for video signal ( 0 or 1 ) - * @users: Number of open of device ( max. 1 ) * @disabled: Device is in power down state - * @mutex: ensures exclusive opening of device * @slock: for excluse acces of registers - * @vb_vidq: queue maintained by videobuf layer - * @capture: linked list of capture buffer - * @active: struct videobuf_buffer currently beingg filled - * @started: device is ready to capture frame - * @closing: device will be shut down + * @alloc_ctx: context for videobuf2 + * @vb_vidq: queue maintained by videobuf2 layer + * @buffer_list: list of buffer in use + * @sequence: sequence number of acquired buffer + * @active: current active buffer + * @lock: used in videobuf2 callback * @tcount: Number of top frames * @bcount: Number of bottom frames * @overflow: Number of FIFO overflows - * @mem_spare: small buffer of unused frame - * @dma_spare: dma addres of mem_spare * @iomem: hardware base address * @config: I2C and gpio config from platform * * All non-local data is accessed via this structure. */ - struct sta2x11_vip { struct v4l2_device v4l2_dev; struct video_device *video_dev; @@ -129,21 +132,27 @@ struct sta2x11_vip { struct i2c_adapter *adapter; unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT]; struct v4l2_subdev *decoder; + struct v4l2_ctrl_handler ctrl_hdl; + + struct v4l2_pix_format format; v4l2_std_id std; unsigned int input; - int users; int disabled; - struct mutex mutex; /* exclusive access during open */ - spinlock_t slock; /* spin lock for hardware and queue access */ - struct videobuf_queue vb_vidq; - struct list_head capture; - struct videobuf_buffer *active; - int started, closing, tcount, bcount; + spinlock_t slock; + + struct vb2_alloc_ctx *alloc_ctx; + struct vb2_queue vb_vidq; + struct list_head buffer_list; + unsigned int sequence; + struct vip_buffer *active; /* current active buffer */ + spinlock_t lock; /* Used in videobuf2 callback */ + + /* Interrupt counters */ + int tcount, bcount; int overflow; - void *mem_spare; - dma_addr_t dma_spare; - void *iomem; + + void *iomem; /* I/O Memory */ struct vip_config *config; }; @@ -206,318 +215,195 @@ static struct v4l2_pix_format formats_60[] = { .colorspace = V4L2_COLORSPACE_SMPTE170M}, }; -/** - * buf_setup - Get size and number of video buffer - * @vq: queue in videobuf - * @count: Number of buffers (1..MAX_FRAMES). - * 0 use default value. - * @size: size of buffer in bytes - * - * returns size and number of buffers - * a preset value of 0 returns the default number. - * return value: 0, always succesfull. - */ -static int buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +/* Write VIP register */ +static inline void reg_write(struct sta2x11_vip *vip, unsigned int reg, u32 val) { - struct sta2x11_vip *vip = vq->priv_data; - - *size = vip->format.width * vip->format.height * 2; - if (0 == *count || MAX_FRAMES < *count) - *count = MAX_FRAMES; - return 0; -}; - -/** - * buf_prepare - prepare buffer for usage - * @vq: queue in videobuf layer - * @vb: buffer to be prepared - * @field: type of video data (interlaced/non-interlaced) - * - * Allocate or realloc buffer - * return value: 0, successful. - * - * -EINVAL, supplied buffer is too small. - * - * other, buffer could not be locked. - */ -static int buf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) + iowrite32((val), (vip->iomem)+(reg)); +} +/* Read VIP register */ +static inline u32 reg_read(struct sta2x11_vip *vip, unsigned int reg) { - struct sta2x11_vip *vip = vq->priv_data; - int ret; + return ioread32((vip->iomem)+(reg)); +} +/* Start DMA acquisition */ +static void start_dma(struct sta2x11_vip *vip, struct vip_buffer *vip_buf) +{ + unsigned long offset = 0; - vb->size = vip->format.width * vip->format.height * 2; - if ((0 != vb->baddr) && (vb->bsize < vb->size)) - return -EINVAL; - vb->width = vip->format.width; - vb->height = vip->format.height; - vb->field = field; + if (vip->format.field == V4L2_FIELD_INTERLACED) + offset = vip->format.width * 2; - if (VIDEOBUF_NEEDS_INIT == vb->state) { - ret = videobuf_iolock(vq, vb, NULL); - if (ret) - goto fail; - } - vb->state = VIDEOBUF_PREPARED; - return 0; -fail: - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; - return ret; + spin_lock_irq(&vip->slock); + /* Enable acquisition */ + reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) | DVP_CTL_ENA); + /* Set Top and Bottom Field memory address */ + reg_write(vip, DVP_VTP, (u32)vip_buf->dma); + reg_write(vip, DVP_VBP, (u32)vip_buf->dma + offset); + spin_unlock_irq(&vip->slock); } -/** - * buf_queu - queue buffer for filling - * @vq: queue in videobuf layer - * @vb: buffer to be queued - * - * if capturing is already running, the buffer will be queued. Otherwise - * capture is started and the buffer is used directly. - */ -static void buf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +/* Fetch the next buffer to activate */ +static void vip_active_buf_next(struct sta2x11_vip *vip) { - struct sta2x11_vip *vip = vq->priv_data; - u32 dma; - - vb->state = VIDEOBUF_QUEUED; - - if (vip->active) { - list_add_tail(&vb->queue, &vip->capture); + /* Get the next buffer */ + spin_lock(&vip->lock); + if (list_empty(&vip->buffer_list)) {/* No available buffer */ + spin_unlock(&vip->lock); return; } - - vip->started = 1; + vip->active = list_first_entry(&vip->buffer_list, + struct vip_buffer, + list); + /* Reset Top and Bottom counter */ vip->tcount = 0; vip->bcount = 0; - vip->active = vb; - vb->state = VIDEOBUF_ACTIVE; - - dma = videobuf_to_dma_contig(vb); - - REG_WRITE(vip, DVP_TFO, (0 << 16) | (0)); - /* despite of interlace mode, upper and lower frames start at zero */ - REG_WRITE(vip, DVP_BFO, (0 << 16) | (0)); - - switch (vip->format.field) { - case V4L2_FIELD_INTERLACED: - REG_WRITE(vip, DVP_TFS, - ((vip->format.height / 2 - 1) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_BFS, ((vip->format.height / 2 - 1) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_VTP, dma); - REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2); - REG_WRITE(vip, DVP_VMP, 4 * vip->format.width); - break; - case V4L2_FIELD_TOP: - REG_WRITE(vip, DVP_TFS, - ((vip->format.height - 1) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_BFS, ((0) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_VTP, dma); - REG_WRITE(vip, DVP_VBP, dma); - REG_WRITE(vip, DVP_VMP, 2 * vip->format.width); - break; - case V4L2_FIELD_BOTTOM: - REG_WRITE(vip, DVP_TFS, ((0) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_BFS, - ((vip->format.height) << 16) | - (2 * vip->format.width - 1)); - REG_WRITE(vip, DVP_VTP, dma); - REG_WRITE(vip, DVP_VBP, dma); - REG_WRITE(vip, DVP_VMP, 2 * vip->format.width); - break; - - default: - pr_warning("VIP: unknown field format\n"); - return; + spin_unlock(&vip->lock); + if (vb2_is_streaming(&vip->vb_vidq)) { /* streaming is on */ + start_dma(vip, vip->active); /* start dma capture */ } - - REG_WRITE(vip, DVP_CTL, DVP_CTL_ENA); } -/** - * buff_release - release buffer - * @vq: queue in videobuf layer - * @vb: buffer to be released - * - * release buffer in videobuf layer - */ -static void buf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) + +/* Videobuf2 Operations */ +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { + struct sta2x11_vip *vip = vb2_get_drv_priv(vq); - videobuf_dma_contig_free(vq, vb); - vb->state = VIDEOBUF_NEEDS_INIT; -} + if (!(*nbuffers) || *nbuffers < MAX_FRAMES) + *nbuffers = MAX_FRAMES; -static struct videobuf_queue_ops vip_qops = { - .buf_setup = buf_setup, - .buf_prepare = buf_prepare, - .buf_queue = buf_queue, - .buf_release = buf_release, -}; + *nplanes = 1; + sizes[0] = vip->format.sizeimage; + alloc_ctxs[0] = vip->alloc_ctx; -/** - * vip_open - open video device - * @file: descriptor of device - * - * open device, make sure it is only opened once. - * return value: 0, no error. - * - * -EBUSY, device is already opened - * - * -ENOMEM, no memory for auxiliary DMA buffer - */ -static int vip_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct sta2x11_vip *vip = video_get_drvdata(dev); - - mutex_lock(&vip->mutex); - vip->users++; - - if (vip->users > 1) { - vip->users--; - mutex_unlock(&vip->mutex); - return -EBUSY; - } - - file->private_data = dev; - vip->overflow = 0; - vip->started = 0; - vip->closing = 0; + vip->sequence = 0; vip->active = NULL; + vip->tcount = 0; + vip->bcount = 0; - INIT_LIST_HEAD(&vip->capture); - vip->mem_spare = dma_alloc_coherent(&vip->pdev->dev, 64, - &vip->dma_spare, GFP_KERNEL); - if (!vip->mem_spare) { - vip->users--; - mutex_unlock(&vip->mutex); - return -ENOMEM; - } + return 0; +}; +static int buffer_init(struct vb2_buffer *vb) +{ + struct vip_buffer *vip_buf = to_vip_buffer(vb); - mutex_unlock(&vip->mutex); - videobuf_queue_dma_contig_init_cached(&vip->vb_vidq, - &vip_qops, - &vip->pdev->dev, - &vip->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct videobuf_buffer), - vip, NULL); - REG_READ(vip, DVP_ITS); - REG_WRITE(vip, DVP_HLFLN, DVP_HLFLN_SD); - REG_WRITE(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST); - REG_WRITE(vip, DVP_CTL, DVP_CTL_RST); - REG_WRITE(vip, DVP_CTL, 0); - REG_READ(vip, DVP_ITS); + vip_buf->dma = vb2_dma_contig_plane_dma_addr(vb, 0); + INIT_LIST_HEAD(&vip_buf->list); return 0; } -/** - * vip_close - close video device - * @file: descriptor of device - * - * close video device, wait until all pending operations are finished - * ( maximum FRAME_MAX buffers pending ) - * Turn off interrupts. - * - * return value: 0, always succesful. - */ -static int vip_close(struct file *file) +static int buffer_prepare(struct vb2_buffer *vb) { - struct video_device *dev = video_devdata(file); - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); + struct vip_buffer *vip_buf = to_vip_buffer(vb); + unsigned long size; + + size = vip->format.sizeimage; + if (vb2_plane_size(vb, 0) < size) { + v4l2_err(&vip->v4l2_dev, "buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(&vip_buf->vb, 0, size); + + return 0; +} +static void buffer_queue(struct vb2_buffer *vb) +{ + struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); + struct vip_buffer *vip_buf = to_vip_buffer(vb); + + spin_lock(&vip->lock); + list_add_tail(&vip_buf->list, &vip->buffer_list); + if (!vip->active) { /* No active buffer, active the first one */ + vip->active = list_first_entry(&vip->buffer_list, + struct vip_buffer, + list); + if (vb2_is_streaming(&vip->vb_vidq)) /* streaming is on */ + start_dma(vip, vip_buf); /* start dma capture */ + } + spin_unlock(&vip->lock); +} +static int buffer_finish(struct vb2_buffer *vb) +{ + struct sta2x11_vip *vip = vb2_get_drv_priv(vb->vb2_queue); + struct vip_buffer *vip_buf = to_vip_buffer(vb); + + /* Buffer handled, remove it from the list */ + spin_lock(&vip->lock); + list_del_init(&vip_buf->list); + spin_unlock(&vip->lock); + + vip_active_buf_next(vip); + + return 0; +} + +static int start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct sta2x11_vip *vip = vb2_get_drv_priv(vq); - vip->closing = 1; - if (vip->active) - videobuf_waiton(&vip->vb_vidq, vip->active, 0, 0); spin_lock_irq(&vip->slock); - - REG_WRITE(vip, DVP_ITM, 0); - REG_WRITE(vip, DVP_CTL, DVP_CTL_RST); - REG_WRITE(vip, DVP_CTL, 0); - REG_READ(vip, DVP_ITS); - - vip->started = 0; - vip->active = NULL; - + /* Enable interrupt VSYNC Top and Bottom*/ + reg_write(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST); spin_unlock_irq(&vip->slock); - videobuf_stop(&vip->vb_vidq); - videobuf_mmap_free(&vip->vb_vidq); + if (count) + start_dma(vip, vip->active); - dma_free_coherent(&vip->pdev->dev, 64, vip->mem_spare, vip->dma_spare); - file->private_data = NULL; - mutex_lock(&vip->mutex); - vip->users--; - mutex_unlock(&vip->mutex); return 0; } -/** - * vip_read - read from video input - * @file: descriptor of device - * @data: user buffer - * @count: number of bytes to be read - * @ppos: position within stream - * - * read video data from video device. - * handling is done in generic videobuf layer - * return value: provided by videobuf layer - */ -static ssize_t vip_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) +/* abort streaming and wait for last buffer */ +static int stop_streaming(struct vb2_queue *vq) { - struct video_device *dev = file->private_data; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = vb2_get_drv_priv(vq); + struct vip_buffer *vip_buf, *node; - return videobuf_read_stream(&vip->vb_vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); + /* Disable acquisition */ + reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA); + /* Disable all interrupts */ + reg_write(vip, DVP_ITM, 0); + + /* Release all active buffers */ + spin_lock(&vip->lock); + list_for_each_entry_safe(vip_buf, node, &vip->buffer_list, list) { + vb2_buffer_done(&vip_buf->vb, VB2_BUF_STATE_ERROR); + list_del(&vip_buf->list); + } + spin_unlock(&vip->lock); + return 0; } -/** - * vip_mmap - map user buffer - * @file: descriptor of device - * @vma: user buffer - * - * map user space buffer into kernel mode, including DMA address. - * handling is done in generic videobuf layer. - * return value: provided by videobuf layer - */ -static int vip_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *dev = file->private_data; - struct sta2x11_vip *vip = video_get_drvdata(dev); +static struct vb2_ops vip_video_qops = { + .queue_setup = queue_setup, + .buf_init = buffer_init, + .buf_prepare = buffer_prepare, + .buf_finish = buffer_finish, + .buf_queue = buffer_queue, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, +}; - return videobuf_mmap_mapper(&vip->vb_vidq, vma); -} -/** - * vip_poll - poll for event - * @file: descriptor of device - * @wait: contains events to be waited for - * - * wait for event related to video device. - * handling is done in generic videobuf layer. - * return value: provided by videobuf layer - */ -static unsigned int vip_poll(struct file *file, struct poll_table_struct *wait) -{ - struct video_device *dev = file->private_data; - struct sta2x11_vip *vip = video_get_drvdata(dev); +/* File Operations */ +static const struct v4l2_file_operations vip_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll +}; - return videobuf_poll_stream(file, &vip->vb_vidq, wait); -} /** * vidioc_querycap - return capabilities of device - * @file: descriptor of device (not used) - * @priv: points to current videodevice + * @file: descriptor of device * @cap: contains return values * * the capabilities of the device are returned @@ -527,25 +413,22 @@ static unsigned int vip_poll(struct file *file, struct poll_table_struct *wait) static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); - memset(cap, 0, sizeof(struct v4l2_capability)); - strcpy(cap->driver, DRV_NAME); - strcpy(cap->card, DRV_NAME); - cap->version = 0; + strcpy(cap->driver, KBUILD_MODNAME); + strcpy(cap->card, KBUILD_MODNAME); snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", pci_name(vip->pdev)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } /** * vidioc_s_std - set video standard - * @file: descriptor of device (not used) - * @priv: points to current videodevice + * @file: descriptor of device * @std: contains standard to be set * * the video standard is set @@ -558,8 +441,7 @@ static int vidioc_querycap(struct file *file, void *priv, */ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); v4l2_std_id oldstd = vip->std, newstd; int status; @@ -592,8 +474,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) /** * vidioc_g_std - get video standard - * @file: descriptor of device (not used) - * @priv: points to current videodevice + * @file: descriptor of device * @std: contains return values * * the current video standard is returned @@ -602,8 +483,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) */ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); *std = vip->std; return 0; @@ -611,8 +491,7 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) /** * vidioc_querystd - get possible video standards - * @file: descriptor of device (not used) - * @priv: points to current videodevice + * @file: descriptor of device * @std: contains return values * * all possible video standards are returned @@ -621,79 +500,11 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) */ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); return v4l2_subdev_call(vip->decoder, video, querystd, std); - } -/** - * vidioc_queryctl - get possible control settings - * @file: descriptor of device (not used) - * @priv: points to current videodevice - * @ctrl: contains return values - * - * return possible values for a control - * return value: delivered by video DAC routine. - */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *ctrl) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return v4l2_subdev_call(vip->decoder, core, queryctrl, ctrl); -} - -/** - * vidioc_g_ctl - get control value - * @file: descriptor of device (not used) - * @priv: points to current videodevice - * @ctrl: contains return values - * - * return setting for a control value - * return value: delivered by video DAC routine. - */ -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return v4l2_subdev_call(vip->decoder, core, g_ctrl, ctrl); -} - -/** - * vidioc_s_ctl - set control value - * @file: descriptor of device (not used) - * @priv: points to current videodevice - * @ctrl: contains value to be set - * - * set value for a specific control - * return value: delivered by video DAC routine. - */ -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return v4l2_subdev_call(vip->decoder, core, s_ctrl, ctrl); -} - -/** - * vidioc_enum_input - return name of input line - * @file: descriptor of device (not used) - * @priv: points to current videodevice - * @inp: contains return values - * - * the user friendly name of the input line is returned - * - * return value: 0, no error. - * - * -EINVAL, input line number out of range - */ static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { @@ -709,8 +520,7 @@ static int vidioc_enum_input(struct file *file, void *priv, /** * vidioc_s_input - set input line - * @file: descriptor of device ( not used) - * @priv: points to current videodevice + * @file: descriptor of device * @i: new input line number * * the current active input line is set @@ -721,8 +531,7 @@ static int vidioc_enum_input(struct file *file, void *priv, */ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); int ret; if (i > 1) @@ -737,8 +546,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) /** * vidioc_g_input - return input line - * @file: descriptor of device ( not used) - * @priv: points to current videodevice + * @file: descriptor of device * @i: returned input line number * * the current active input line is returned @@ -747,8 +555,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) */ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); *i = vip->input; return 0; @@ -756,8 +563,6 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) /** * vidioc_enum_fmt_vid_cap - return video capture format - * @file: descriptor of device ( not used) - * @priv: points to current videodevice * @f: returned format information * * returns name and format of video capture @@ -780,8 +585,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, /** * vidioc_try_fmt_vid_cap - set video capture format - * @file: descriptor of device ( not used) - * @priv: points to current videodevice + * @file: descriptor of device * @f: new format * * new video format is set which includes width and @@ -797,12 +601,13 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); int interlace_lim; - if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) + if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) { + v4l2_warn(&vip->v4l2_dev, "Invalid format, only UYVY supported\n"); return -EINVAL; + } if (V4L2_STD_525_60 & vip->std) interlace_lim = 240; @@ -810,6 +615,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, interlace_lim = 288; switch (f->fmt.pix.field) { + default: case V4L2_FIELD_ANY: if (interlace_lim < f->fmt.pix.height) f->fmt.pix.field = V4L2_FIELD_INTERLACED; @@ -823,10 +629,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, break; case V4L2_FIELD_INTERLACED: break; - default: - return -EINVAL; } + /* It is the only supported format */ + f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; f->fmt.pix.height &= ~1; if (2 * interlace_lim < f->fmt.pix.height) f->fmt.pix.height = 2 * interlace_lim; @@ -842,8 +648,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /** * vidioc_s_fmt_vid_cap - set current video format parameters - * @file: descriptor of device ( not used) - * @priv: points to current videodevice + * @file: descriptor of device * @f: returned format information * * set new capture format @@ -854,22 +659,63 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); + unsigned int t_stop, b_stop, pitch; int ret; ret = vidioc_try_fmt_vid_cap(file, priv, f); if (ret) return ret; - memcpy(&vip->format, &f->fmt.pix, sizeof(struct v4l2_pix_format)); + if (vb2_is_busy(&vip->vb_vidq)) { + /* Can't change format during acquisition */ + v4l2_err(&vip->v4l2_dev, "device busy\n"); + return -EBUSY; + } + vip->format = f->fmt.pix; + switch (vip->format.field) { + case V4L2_FIELD_INTERLACED: + t_stop = ((vip->format.height / 2 - 1) << 16) | + (2 * vip->format.width - 1); + b_stop = t_stop; + pitch = 4 * vip->format.width; + break; + case V4L2_FIELD_TOP: + t_stop = ((vip->format.height - 1) << 16) | + (2 * vip->format.width - 1); + b_stop = (0 << 16) | (2 * vip->format.width - 1); + pitch = 2 * vip->format.width; + break; + case V4L2_FIELD_BOTTOM: + t_stop = (0 << 16) | (2 * vip->format.width - 1); + b_stop = (vip->format.height << 16) | + (2 * vip->format.width - 1); + pitch = 2 * vip->format.width; + break; + default: + v4l2_err(&vip->v4l2_dev, "unknown field format\n"); + return -EINVAL; + } + + spin_lock_irq(&vip->slock); + /* Y-X Top Field Offset */ + reg_write(vip, DVP_TFO, 0); + /* Y-X Bottom Field Offset */ + reg_write(vip, DVP_BFO, 0); + /* Y-X Top Field Stop*/ + reg_write(vip, DVP_TFS, t_stop); + /* Y-X Bottom Field Stop */ + reg_write(vip, DVP_BFS, b_stop); + /* Video Memory Pitch */ + reg_write(vip, DVP_VMP, pitch); + spin_unlock_irq(&vip->slock); + return 0; } /** * vidioc_g_fmt_vid_cap - get current video format parameters - * @file: descriptor of device ( not used) - * @priv: points to current videodevice + * @file: descriptor of device * @f: contains format information * * returns current video format parameters @@ -879,150 +725,47 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); + struct sta2x11_vip *vip = video_drvdata(file); + + f->fmt.pix = vip->format; - memcpy(&f->fmt.pix, &vip->format, sizeof(struct v4l2_pix_format)); return 0; } -/** - * vidioc_reqfs - request buffer - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @p: video buffer - * - * Handling is done in generic videobuf layer. - */ -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_reqbufs(&vip->vb_vidq, p); -} - -/** - * vidioc_querybuf - query buffer - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @p: video buffer - * - * query buffer state. - * Handling is done in generic videobuf layer. - */ -static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_querybuf(&vip->vb_vidq, p); -} - -/** - * vidioc_qbuf - queue a buffer - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @p: video buffer - * - * Handling is done in generic videobuf layer. - */ -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_qbuf(&vip->vb_vidq, p); -} - -/** - * vidioc_dqbuf - dequeue a buffer - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @p: video buffer - * - * Handling is done in generic videobuf layer. - */ -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_dqbuf(&vip->vb_vidq, p, file->f_flags & O_NONBLOCK); -} - -/** - * vidioc_streamon - turn on streaming - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @type: type of capture - * - * turn on streaming. - * Handling is done in generic videobuf layer. - */ -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_streamon(&vip->vb_vidq); -} - -/** - * vidioc_streamoff - turn off streaming - * @file: descriptor of device ( not used) - * @priv: points to current videodevice - * @type: type of capture - * - * turn off streaming. - * Handling is done in generic videobuf layer. - */ -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct video_device *dev = priv; - struct sta2x11_vip *vip = video_get_drvdata(dev); - - return videobuf_streamoff(&vip->vb_vidq); -} - -static const struct v4l2_file_operations vip_fops = { - .owner = THIS_MODULE, - .open = vip_open, - .release = vip_close, - .ioctl = video_ioctl2, - .read = vip_read, - .mmap = vip_mmap, - .poll = vip_poll -}; - static const struct v4l2_ioctl_ops vip_ioctl_ops = { .vidioc_querycap = vidioc_querycap, - .vidioc_s_std = vidioc_s_std, - .vidioc_g_std = vidioc_g_std, - .vidioc_querystd = vidioc_querystd, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_input = vidioc_g_input, + /* FMT handling */ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + /* Buffer handlers */ + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + /* Stream on/off */ + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + /* Standard handling */ + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, + /* Input handling */ + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + /* Log status ioctl */ + .vidioc_log_status = v4l2_ctrl_log_status, + /* Event handling */ + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static struct video_device video_dev_template = { - .name = DRV_NAME, + .name = KBUILD_MODNAME, .release = video_device_release, .fops = &vip_fops, .ioctl_ops = &vip_ioctl_ops, @@ -1036,9 +779,7 @@ static struct video_device video_dev_template = { * * check for both frame interrupts set ( top and bottom ). * check FIFO overflow, but limit number of log messages after open. - * signal a complete buffer if done. - * dequeue a new buffer if available. - * disable VIP if no buffer available. + * signal a complete buffer if done * * return value: IRQ_NONE, interrupt was not generated by VIP * @@ -1046,90 +787,124 @@ static struct video_device video_dev_template = { */ static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip) { - u32 status, dma; - unsigned long flags; - struct videobuf_buffer *vb; + unsigned int status; - status = REG_READ(vip, DVP_ITS); + status = reg_read(vip, DVP_ITS); - if (!status) { - pr_debug("VIP: irq ignored\n"); + if (!status) /* No interrupt to handle */ return IRQ_NONE; - } - if (!vip->started) - return IRQ_HANDLED; + if (status & DVP_IT_FIFO) + if (vip->overflow++ > 5) + pr_info("VIP: fifo overflow\n"); - if (status & DVP_IT_VSB) - vip->bcount++; - - if (status & DVP_IT_VST) - vip->tcount++; - - if ((DVP_IT_VSB | DVP_IT_VST) == (status & (DVP_IT_VST | DVP_IT_VSB))) { + if ((status & DVP_IT_VST) && (status & DVP_IT_VSB)) { /* this is bad, we are too slow, hope the condition is gone * on the next frame */ - pr_info("VIP: both irqs\n"); return IRQ_HANDLED; } - if (status & DVP_IT_FIFO) { - if (5 > vip->overflow++) - pr_info("VIP: fifo overflow\n"); - } - - if (2 > vip->tcount) + if (status & DVP_IT_VST) + if ((++vip->tcount) < 2) + return IRQ_HANDLED; + if (status & DVP_IT_VSB) { + vip->bcount++; return IRQ_HANDLED; - - if (status & DVP_IT_VSB) - return IRQ_HANDLED; - - spin_lock_irqsave(&vip->slock, flags); - - REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) & ~DVP_CTL_ENA); - if (vip->active) { - v4l2_get_timestamp(&vip->active->ts); - vip->active->field_count++; - vip->active->state = VIDEOBUF_DONE; - wake_up(&vip->active->done); - vip->active = NULL; } - if (!vip->closing) { - if (list_empty(&vip->capture)) - goto done; - vb = list_first_entry(&vip->capture, struct videobuf_buffer, - queue); - if (NULL == vb) { - pr_info("VIP: no buffer\n"); - goto done; - } - vb->state = VIDEOBUF_ACTIVE; - list_del(&vb->queue); - vip->active = vb; - dma = videobuf_to_dma_contig(vb); - switch (vip->format.field) { - case V4L2_FIELD_INTERLACED: - REG_WRITE(vip, DVP_VTP, dma); - REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2); - break; - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - REG_WRITE(vip, DVP_VTP, dma); - REG_WRITE(vip, DVP_VBP, dma); - break; - default: - pr_warning("VIP: unknown field format\n"); - goto done; - break; - } - REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) | DVP_CTL_ENA); + if (vip->active) { /* Acquisition is over on this buffer */ + /* Disable acquisition */ + reg_write(vip, DVP_CTL, reg_read(vip, DVP_CTL) & ~DVP_CTL_ENA); + /* Remove the active buffer from the list */ + do_gettimeofday(&vip->active->vb.v4l2_buf.timestamp); + vip->active->vb.v4l2_buf.sequence = vip->sequence++; + vb2_buffer_done(&vip->active->vb, VB2_BUF_STATE_DONE); } -done: - spin_unlock_irqrestore(&vip->slock, flags); + return IRQ_HANDLED; } +static void sta2x11_vip_init_register(struct sta2x11_vip *vip) +{ + /* Register initialization */ + spin_lock_irq(&vip->slock); + /* Clean interrupt */ + reg_read(vip, DVP_ITS); + /* Enable Half Line per vertical */ + reg_write(vip, DVP_HLFLN, DVP_HLFLN_SD); + /* Reset VIP control */ + reg_write(vip, DVP_CTL, DVP_CTL_RST); + /* Clear VIP control */ + reg_write(vip, DVP_CTL, 0); + spin_unlock_irq(&vip->slock); +} +static void sta2x11_vip_clear_register(struct sta2x11_vip *vip) +{ + spin_lock_irq(&vip->slock); + /* Disable interrupt */ + reg_write(vip, DVP_ITM, 0); + /* Reset VIP Control */ + reg_write(vip, DVP_CTL, DVP_CTL_RST); + /* Clear VIP Control */ + reg_write(vip, DVP_CTL, 0); + /* Clean VIP Interrupt */ + reg_read(vip, DVP_ITS); + spin_unlock_irq(&vip->slock); +} +static int sta2x11_vip_init_buffer(struct sta2x11_vip *vip) +{ + int err; + + err = dma_set_coherent_mask(&vip->pdev->dev, DMA_BIT_MASK(29)); + if (err) { + v4l2_err(&vip->v4l2_dev, "Cannot configure coherent mask"); + return err; + } + memset(&vip->vb_vidq, 0, sizeof(struct vb2_queue)); + vip->vb_vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vip->vb_vidq.io_modes = VB2_MMAP | VB2_READ; + vip->vb_vidq.drv_priv = vip; + vip->vb_vidq.buf_struct_size = sizeof(struct vip_buffer); + vip->vb_vidq.ops = &vip_video_qops; + vip->vb_vidq.mem_ops = &vb2_dma_contig_memops; + err = vb2_queue_init(&vip->vb_vidq); + if (err) + return err; + INIT_LIST_HEAD(&vip->buffer_list); + spin_lock_init(&vip->lock); + + + vip->alloc_ctx = vb2_dma_contig_init_ctx(&vip->pdev->dev); + if (IS_ERR(vip->alloc_ctx)) { + v4l2_err(&vip->v4l2_dev, "Can't allocate buffer context"); + return PTR_ERR(vip->alloc_ctx); + } + + return 0; +} +static void sta2x11_vip_release_buffer(struct sta2x11_vip *vip) +{ + vb2_dma_contig_cleanup_ctx(vip->alloc_ctx); +} +static int sta2x11_vip_init_controls(struct sta2x11_vip *vip) +{ + /* + * Inititialize an empty control so VIP can inerithing controls + * from ADV7180 + */ + v4l2_ctrl_handler_init(&vip->ctrl_hdl, 0); + + vip->v4l2_dev.ctrl_handler = &vip->ctrl_hdl; + if (vip->ctrl_hdl.error) { + int err = vip->ctrl_hdl.error; + + v4l2_ctrl_handler_free(&vip->ctrl_hdl); + return err; + } + + return 0; +} + /** * vip_gpio_reserve - reserve gpio pin * @dev: device @@ -1212,10 +987,17 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, struct sta2x11_vip *vip; struct vip_config *config; + /* Check if hardware support 26-bit DMA */ + if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(26))) { + dev_err(&pdev->dev, "26-bit DMA addressing not available\n"); + return -EINVAL; + } + /* Enable PCI */ ret = pci_enable_device(pdev); if (ret) return ret; + /* Get VIP platform data */ config = dev_get_platdata(&pdev->dev); if (!config) { dev_info(&pdev->dev, "VIP slot disabled\n"); @@ -1223,6 +1005,7 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, goto disable; } + /* Power configuration */ ret = vip_gpio_reserve(&pdev->dev, config->pwr_pin, 0, config->pwr_name); if (ret) @@ -1237,7 +1020,6 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, goto disable; } } - if (config->pwr_pin != -1) { /* Datasheet says 5ms between PWR and RST */ usleep_range(5000, 25000); @@ -1251,17 +1033,20 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, } usleep_range(5000, 25000); + /* Allocate a new VIP instance */ vip = kzalloc(sizeof(struct sta2x11_vip), GFP_KERNEL); if (!vip) { ret = -ENOMEM; goto release_gpios; } - vip->pdev = pdev; vip->std = V4L2_STD_PAL; vip->format = formats_50[0]; vip->config = config; + ret = sta2x11_vip_init_controls(vip); + if (ret) + goto free_mem; if (v4l2_device_register(&pdev->dev, &vip->v4l2_dev)) goto free_mem; @@ -1271,46 +1056,52 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, pci_set_master(pdev); - ret = pci_request_regions(pdev, DRV_NAME); + ret = pci_request_regions(pdev, KBUILD_MODNAME); if (ret) goto unreg; vip->iomem = pci_iomap(pdev, 0, 0x100); if (!vip->iomem) { - ret = -ENOMEM; /* FIXME */ + ret = -ENOMEM; goto release; } pci_enable_msi(pdev); - INIT_LIST_HEAD(&vip->capture); + /* Initialize buffer */ + ret = sta2x11_vip_init_buffer(vip); + if (ret) + goto unmap; + spin_lock_init(&vip->slock); - mutex_init(&vip->mutex); - vip->started = 0; - vip->disabled = 0; ret = request_irq(pdev->irq, (irq_handler_t) vip_irq, - IRQF_SHARED, DRV_NAME, vip); + IRQF_SHARED, KBUILD_MODNAME, vip); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); ret = -ENODEV; - goto unmap; + goto release_buf; } + /* Alloc, initialize and register video device */ vip->video_dev = video_device_alloc(); if (!vip->video_dev) { ret = -ENOMEM; goto release_irq; } - *(vip->video_dev) = video_dev_template; + vip->video_dev = &video_dev_template; + vip->video_dev->v4l2_dev = &vip->v4l2_dev; + vip->video_dev->queue = &vip->vb_vidq; + set_bit(V4L2_FL_USE_FH_PRIO, &vip->video_dev->flags); video_set_drvdata(vip->video_dev, vip); ret = video_register_device(vip->video_dev, VFL_TYPE_GRABBER, -1); if (ret) goto vrelease; + /* Get ADV7180 subdevice */ vip->adapter = i2c_get_adapter(vip->config->i2c_id); if (!vip->adapter) { ret = -ENODEV; @@ -1328,10 +1119,11 @@ static int sta2x11_vip_init_one(struct pci_dev *pdev, } i2c_put_adapter(vip->adapter); - v4l2_subdev_call(vip->decoder, core, init, 0); - pr_info("STA2X11 Video Input Port (VIP) loaded\n"); + sta2x11_vip_init_register(vip); + + dev_info(&pdev->dev, "STA2X11 Video Input Port (VIP) loaded\n"); return 0; vunreg: @@ -1343,10 +1135,12 @@ vrelease: video_device_release(vip->video_dev); release_irq: free_irq(pdev->irq, vip); +release_buf: + sta2x11_vip_release_buffer(vip); pci_disable_msi(pdev); unmap: + vb2_queue_release(&vip->vb_vidq); pci_iounmap(pdev, vip->iomem); - mutex_destroy(&vip->mutex); release: pci_release_regions(pdev); unreg: @@ -1382,16 +1176,18 @@ static void sta2x11_vip_remove_one(struct pci_dev *pdev) struct sta2x11_vip *vip = container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); + sta2x11_vip_clear_register(vip); + video_set_drvdata(vip->video_dev, NULL); video_unregister_device(vip->video_dev); /*do not call video_device_release() here, is already done */ free_irq(pdev->irq, vip); pci_disable_msi(pdev); + vb2_queue_release(&vip->vb_vidq); pci_iounmap(pdev, vip->iomem); pci_release_regions(pdev); v4l2_device_unregister(&vip->v4l2_dev); - mutex_destroy(&vip->mutex); vip_gpio_release(&pdev->dev, vip->config->pwr_pin, vip->config->pwr_name); @@ -1416,9 +1212,6 @@ static void sta2x11_vip_remove_one(struct pci_dev *pdev) * * return value: 0 always indicate success, * even if device could not be disabled. (workaround for hardware problem) - * - * reurn value : 0, always succesful, even if hardware does not not support - * power down mode. */ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) { @@ -1429,15 +1222,15 @@ static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) int i; spin_lock_irqsave(&vip->slock, flags); - vip->register_save_area[0] = REG_READ(vip, DVP_CTL); - REG_WRITE(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS); - vip->register_save_area[SAVE_COUNT] = REG_READ(vip, DVP_ITM); - REG_WRITE(vip, DVP_ITM, 0); + vip->register_save_area[0] = reg_read(vip, DVP_CTL); + reg_write(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS); + vip->register_save_area[SAVE_COUNT] = reg_read(vip, DVP_ITM); + reg_write(vip, DVP_ITM, 0); for (i = 1; i < SAVE_COUNT; i++) - vip->register_save_area[i] = REG_READ(vip, 4 * i); + vip->register_save_area[i] = reg_read(vip, 4 * i); for (i = 0; i < AUX_COUNT; i++) vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] = - REG_READ(vip, registers_to_save[i]); + reg_read(vip, registers_to_save[i]); spin_unlock_irqrestore(&vip->slock, flags); /* save pci state */ pci_save_state(pdev); @@ -1477,7 +1270,7 @@ static int sta2x11_vip_resume(struct pci_dev *pdev) if (vip->disabled) { ret = pci_enable_device(pdev); if (ret) { - pr_warning("VIP: Can't enable device.\n"); + pr_warn("VIP: Can't enable device.\n"); return ret; } vip->disabled = 0; @@ -1488,7 +1281,7 @@ static int sta2x11_vip_resume(struct pci_dev *pdev) * do not call pci_disable_device on sta2x11 because it * break all other Bus masters on this EP */ - pr_warning("VIP: Can't enable device.\n"); + pr_warn("VIP: Can't enable device.\n"); vip->disabled = 1; return ret; } @@ -1497,12 +1290,12 @@ static int sta2x11_vip_resume(struct pci_dev *pdev) spin_lock_irqsave(&vip->slock, flags); for (i = 1; i < SAVE_COUNT; i++) - REG_WRITE(vip, 4 * i, vip->register_save_area[i]); + reg_write(vip, 4 * i, vip->register_save_area[i]); for (i = 0; i < AUX_COUNT; i++) - REG_WRITE(vip, registers_to_save[i], + reg_write(vip, registers_to_save[i], vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i]); - REG_WRITE(vip, DVP_CTL, vip->register_save_area[0]); - REG_WRITE(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]); + reg_write(vip, DVP_CTL, vip->register_save_area[0]); + reg_write(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]); spin_unlock_irqrestore(&vip->slock, flags); return 0; } @@ -1515,7 +1308,7 @@ static DEFINE_PCI_DEVICE_TABLE(sta2x11_vip_pci_tbl) = { }; static struct pci_driver sta2x11_vip_driver = { - .name = DRV_NAME, + .name = KBUILD_MODNAME, .probe = sta2x11_vip_init_one, .remove = sta2x11_vip_remove_one, .id_table = sta2x11_vip_pci_tbl, From 9f3b935bfdf0e92b07a6ce0baf0eaa40b98866dc Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Wed, 23 Jan 2013 10:07:07 -0300 Subject: [PATCH 2034/4607] [media] adv7180: remove {query/g_/s_}ctrl All drivers which use this subdevice use also the control framework. The v4l2_subdev_core_ops operations {query/g_/s_}ctrl are useless because device drivers will inherit controls from this subdevice. Signed-off-by: Federico Vaga Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7180.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 64d71fb87a96..34f39d3b3e3e 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -402,9 +402,6 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { static const struct v4l2_subdev_core_ops adv7180_core_ops = { .g_chip_ident = adv7180_g_chip_ident, .s_std = adv7180_s_std, - .queryctrl = v4l2_subdev_queryctrl, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, }; static const struct v4l2_subdev_ops adv7180_ops = { From ddf289f98b5ef664e4606205cf31d147eab76b18 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Sun, 20 Jan 2013 09:26:47 -0300 Subject: [PATCH 2035/4607] [media] em28xx: overhaul em28xx_capture_area_set() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - move the bit shifting of width+height values inside the function - fix the debug message format and output values - add comment about the size limit (e.g. EM277x supports >2MPix) - make void, because error checking is incomplete and we never check the returned value (we would continue anyway) Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 80f87bbbd554..ee00f9e23420 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -733,22 +733,24 @@ static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1); } -static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, +static void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, u16 width, u16 height) { - u8 cwidth = width; - u8 cheight = height; - u8 overflow = (height >> 7 & 0x02) | (width >> 8 & 0x01); + u8 cwidth = width >> 2; + u8 cheight = height >> 2; + u8 overflow = (height >> 9 & 0x02) | (width >> 10 & 0x01); + /* NOTE: size limit: 2047x1023 = 2MPix */ - em28xx_coredbg("em28xx Area Set: (%d,%d)\n", - (width | (overflow & 2) << 7), - (height | (overflow & 1) << 8)); + em28xx_coredbg("capture area set to (%d,%d): %dx%d\n", + hstart, vstart, + ((overflow & 2) << 9 | cwidth << 2), + ((overflow & 1) << 10 | cheight << 2)); em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1); em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1); em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1); em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1); - return em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); + em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); } static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) @@ -801,9 +803,9 @@ int em28xx_resolution_set(struct em28xx *dev) it out, we end up with the same format as the rest of the VBI region */ if (em28xx_vbi_supported(dev) == 1) - em28xx_capture_area_set(dev, 0, 2, width >> 2, height >> 2); + em28xx_capture_area_set(dev, 0, 2, width, height); else - em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); + em28xx_capture_area_set(dev, 0, 0, width, height); return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } From 088e8806c411f76216002a8b37c7eb8563614822 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 30 Dec 2012 10:15:48 -0700 Subject: [PATCH 2036/4607] ARM: OMAP2xxx: PM: enter WFI via inline asm if CORE stays active There shouldn't be any need to jump to SRAM code if the OMAP CORE clockdomain (and consequently the SDRAM controller and CORE PLL) stays active during MPU WFI. The SRAM code should only be needed when the RAM enters self-refresh. So in the case where CORE stays active, just call WFI directly from the mach-omap2/pm24xx.c code. This removes some unnecessary SRAM code. This second version replaces the inline WFI with the corresponding coprocessor register call, using tlbflush.h as an example. This is because the assembler doesn't recognize WFI as a valid ARMv6 instruction. Signed-off-by: Paul Walmsley Cc: Richard Woodruff Cc: Kevin Hilman --- arch/arm/mach-omap2/pm24xx.c | 12 ++++++------ arch/arm/mach-omap2/sleep24xx.S | 19 ------------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/arch/arm/mach-omap2/pm24xx.c b/arch/arm/mach-omap2/pm24xx.c index c333fa6dffa8..8914b9e32ee7 100644 --- a/arch/arm/mach-omap2/pm24xx.c +++ b/arch/arm/mach-omap2/pm24xx.c @@ -54,7 +54,6 @@ #include "powerdomain.h" #include "clockdomain.h" -static void (*omap2_sram_idle)(void); static void (*omap2_sram_suspend)(u32 dllctrl, void __iomem *sdrc_dlla_ctrl, void __iomem *sdrc_power); @@ -172,6 +171,8 @@ static int omap2_allow_mpu_retention(void) static void omap2_enter_mpu_retention(void) { + const int zero = 0; + /* Putting MPU into the WFI state while a transfer is active * seems to cause the I2C block to timeout. Why? Good question. */ if (omap2_i2c_active()) @@ -196,7 +197,8 @@ static void omap2_enter_mpu_retention(void) OMAP2_PM_PWSTCTRL); } - omap2_sram_idle(); + /* WFI */ + asm("mcr p15, 0, %0, c7, c0, 4" : : "r" (zero) : "memory", "cc"); } static int omap2_can_sleep(void) @@ -356,11 +358,9 @@ int __init omap2_pm_init(void) /* * We copy the assembler sleep/wakeup routines to SRAM. * These routines need to be in SRAM as that's the only - * memory the MPU can see when it wakes up. + * memory the MPU can see when it wakes up after the entire + * chip enters idle. */ - omap2_sram_idle = omap_sram_push(omap24xx_idle_loop_suspend, - omap24xx_idle_loop_suspend_sz); - omap2_sram_suspend = omap_sram_push(omap24xx_cpu_suspend, omap24xx_cpu_suspend_sz); diff --git a/arch/arm/mach-omap2/sleep24xx.S b/arch/arm/mach-omap2/sleep24xx.S index ce0ccd26efbd..1d3cb25c9629 100644 --- a/arch/arm/mach-omap2/sleep24xx.S +++ b/arch/arm/mach-omap2/sleep24xx.S @@ -36,25 +36,6 @@ .text -/* - * Forces OMAP into idle state - * - * omap24xx_idle_loop_suspend() - This bit of code just executes the WFI - * for normal idles. - * - * Note: This code get's copied to internal SRAM at boot. When the OMAP - * wakes up it continues execution at the point it went to sleep. - */ - .align 3 -ENTRY(omap24xx_idle_loop_suspend) - stmfd sp!, {r0, lr} @ save registers on stack - mov r0, #0 @ clear for mcr setup - mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt - ldmfd sp!, {r0, pc} @ restore regs and return - -ENTRY(omap24xx_idle_loop_suspend_sz) - .word . - omap24xx_idle_loop_suspend - /* * omap24xx_cpu_suspend() - Forces OMAP into deep sleep state by completing * SDRC shutdown then ARM shutdown. Upon wake MPU is back on so just restore From db27c0c0d05cafc0eb0bed4d58daea65ed5e6839 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 30 Dec 2012 10:22:25 -0700 Subject: [PATCH 2037/4607] ARM: OMAP2+: hwmod: add support for blocking WFI when a device is active Apparently, on some OMAPs, the MPU can't be allowed to enter WFI while certain peripherals are active. It's not clear why, and it's likely that there is simply some other bug in the driver or integration code. But since the likelihood that anyone will have the time to track these problems down in the future seems quite small, we'll provide a flag, HWMOD_BLOCK_WFI, to mark these issues in the hwmod data. Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 8 ++++++++ arch/arm/mach-omap2/omap_hwmod.h | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 4653efb87a27..6804d474a47d 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -139,6 +139,8 @@ #include #include +#include + #include "clock.h" #include "omap_hwmod.h" @@ -2134,6 +2136,8 @@ static int _enable(struct omap_hwmod *oh) _enable_clocks(oh); if (soc_ops.enable_module) soc_ops.enable_module(oh); + if (oh->flags & HWMOD_BLOCK_WFI) + disable_hlt(); if (soc_ops.update_context_lost) soc_ops.update_context_lost(oh); @@ -2195,6 +2199,8 @@ static int _idle(struct omap_hwmod *oh) _idle_sysc(oh); _del_initiator_dep(oh, mpu_oh); + if (oh->flags & HWMOD_BLOCK_WFI) + enable_hlt(); if (soc_ops.disable_module) soc_ops.disable_module(oh); @@ -2303,6 +2309,8 @@ static int _shutdown(struct omap_hwmod *oh) if (oh->_state == _HWMOD_STATE_ENABLED) { _del_initiator_dep(oh, mpu_oh); /* XXX what about the other system initiators here? dma, dsp */ + if (oh->flags & HWMOD_BLOCK_WFI) + enable_hlt(); if (soc_ops.disable_module) soc_ops.disable_module(oh); _disable_clocks(oh); diff --git a/arch/arm/mach-omap2/omap_hwmod.h b/arch/arm/mach-omap2/omap_hwmod.h index 3ae852a522f9..80c00e706d69 100644 --- a/arch/arm/mach-omap2/omap_hwmod.h +++ b/arch/arm/mach-omap2/omap_hwmod.h @@ -451,6 +451,14 @@ struct omap_hwmod_omap4_prcm { * enabled. This prevents the hwmod code from being able to * enable and reset the IP block early. XXX Eventually it should * be possible to query the clock framework for this information. + * HWMOD_BLOCK_WFI: Some OMAP peripherals apparently don't work + * correctly if the MPU is allowed to go idle while the + * peripherals are active. This is apparently true for the I2C on + * OMAP2420, and also the EMAC on AM3517/3505. It's unlikely that + * this is really true -- we're probably not configuring something + * correctly, or this is being abused to deal with some PM latency + * issues -- but we're currently suffering from a shortage of + * folks who are able to track these issues down properly. */ #define HWMOD_SWSUP_SIDLE (1 << 0) #define HWMOD_SWSUP_MSTANDBY (1 << 1) @@ -462,6 +470,7 @@ struct omap_hwmod_omap4_prcm { #define HWMOD_CONTROL_OPT_CLKS_IN_RESET (1 << 7) #define HWMOD_16BIT_REG (1 << 8) #define HWMOD_EXT_OPT_MAIN_CLK (1 << 9) +#define HWMOD_BLOCK_WFI (1 << 10) /* * omap_hwmod._int_flags definitions From 1e3d8fe771881de323396aaa1efd20243fa974cb Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 30 Dec 2012 10:31:52 -0700 Subject: [PATCH 2038/4607] ARM: OMAP2420: hwmod data/PM: use hwmod to block WFI when I2C active Use the HWMOD_BLOCK_WFI flag in the hwmod data to prevent the MPU from entering WFI when the I2C devices are active. No idea why this is needed; this could certainly bear further investigation if anyone is interested. The objective here is to remove some custom code from the OMAP24xx PM code. Signed-off-by: Paul Walmsley Cc: Kevin Hilman --- arch/arm/mach-omap2/omap_hwmod_2420_data.c | 7 ++++++- arch/arm/mach-omap2/pm24xx.c | 13 ------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c index b5efe58c0be0..6a764af6c6d3 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c @@ -121,7 +121,12 @@ static struct omap_hwmod omap2420_i2c1_hwmod = { }, .class = &i2c_class, .dev_attr = &i2c_dev_attr, - .flags = HWMOD_16BIT_REG, + /* + * From mach-omap2/pm24xx.c: "Putting MPU into the WFI state + * while a transfer is active seems to cause the I2C block to + * timeout. Why? Good question." + */ + .flags = (HWMOD_16BIT_REG | HWMOD_BLOCK_WFI), }; /* I2C2 */ diff --git a/arch/arm/mach-omap2/pm24xx.c b/arch/arm/mach-omap2/pm24xx.c index 8914b9e32ee7..bc44bcd64451 100644 --- a/arch/arm/mach-omap2/pm24xx.c +++ b/arch/arm/mach-omap2/pm24xx.c @@ -139,14 +139,6 @@ no_sleep: return 0; } -static int omap2_i2c_active(void) -{ - u32 l; - - l = omap2_cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); - return l & (OMAP2420_EN_I2C2_MASK | OMAP2420_EN_I2C1_MASK); -} - static int sti_console_enabled; static int omap2_allow_mpu_retention(void) @@ -173,11 +165,6 @@ static void omap2_enter_mpu_retention(void) { const int zero = 0; - /* Putting MPU into the WFI state while a transfer is active - * seems to cause the I2C block to timeout. Why? Good question. */ - if (omap2_i2c_active()) - return; - /* The peripherals seem not to be able to wake up the MPU when * it is in retention mode. */ if (omap2_allow_mpu_retention()) { From 814a18a5d026464f56b3616704b985f9942b29a6 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Wed, 6 Feb 2013 13:48:56 -0700 Subject: [PATCH 2039/4607] ARM: OMAP AM3517/05: hwmod data: block WFI when EMAC active According to Mark Greer, on OMAP AM3517/3505 chips, the EMAC is unable to wake the ARM up from WFI: http://www.spinics.net/lists/arm-kernel/msg174734.html Further troubleshooting was unable to narrow the problem down. So we don't have much choice other than to block WFI when the EMAC is active with the HWMOD_BLOCK_WFI flag. Based on Mark's original patch. We're removing the omap_device-based pm_lats code, so a different approach was needed. This third version contains some corrections thanks to Mark's review. Signed-off-by: Paul Walmsley Cc: Mark A. Greer Acked-by: Mark A. Greer --- arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c index 8bb2628df34e..ac7e03ec952f 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c @@ -3493,7 +3493,12 @@ static struct omap_hwmod am35xx_emac_hwmod = { .name = "davinci_emac", .mpu_irqs = am35xx_emac_mpu_irqs, .class = &am35xx_emac_class, - .flags = HWMOD_NO_IDLEST, + /* + * According to Mark Greer, the MPU will not return from WFI + * when the EMAC signals an interrupt. + * http://www.spinics.net/lists/arm-kernel/msg174734.html + */ + .flags = (HWMOD_NO_IDLEST | HWMOD_BLOCK_WFI), }; /* l3_core -> davinci emac interface */ From cc883afcc02e1c9c8ab64d20f0288355b857f966 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 29 Jan 2013 15:48:37 -0700 Subject: [PATCH 2040/4607] MAINTAINERS: update SGI & ia64 Altix stuff Dimitri and Robin have taken over GRU maintenance. Linux on Altix is no longer maintained except as part of ia64, and there's already a separate IA64 maintainer entry. Signed-off-by: Bjorn Helgaas Signed-off-by: Tony Luck --- MAINTAINERS | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index fa309ab7ccbf..0526e212bed0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6768,7 +6768,8 @@ S: Supported F: drivers/net/ethernet/sfc/ SGI GRU DRIVER -M: Jack Steiner +M: Dimitri Sivanich +M: Robin Holt S: Maintained F: drivers/misc/sgi-gru/ @@ -6963,14 +6964,6 @@ L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/smscufx.c -SN-IA64 (Itanium) SUB-PLATFORM -M: Jes Sorensen -L: linux-altix@sgi.com -L: linux-ia64@vger.kernel.org -W: http://www.sgi.com/altix -S: Maintained -F: arch/ia64/sn/ - SOC-CAMERA V4L2 SUBSYSTEM M: Guennadi Liakhovetski L: linux-media@vger.kernel.org From 6048009818047297c510e300c6e8e6f623d4eac9 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Mon, 4 Feb 2013 17:54:43 +0530 Subject: [PATCH 2041/4607] ARM: OMAP4: PM: Warn users about usage of older bootloaders OMAP4 CHIP level PM works only with newer bootloaders. The dependency on the bootloader comes from the fact that the kernel is missing reset and initialization code for some devices. While the right thing to do is to add reset and init code in the kernel, for some co-processor IP blocks like DSP and IVA it means downloading firmware into each one of them to execute idle instructions. While a feasible solution is worked upon on how such IP blocks can be better handled in the kernel, in the interim, to avoid any further frustration to users testing PM on OMAP4 and finding it broken, warn them about the bootloader being a possible cause. Signed-off-by: Rajendra Nayak Cc: Tero Kristo Cc: Santosh Shilimkar Cc: R Sricharan [paul@pwsan.com: tweaked warning messages and comments slightly] Acked-by: Kevin Hilman [paul@pwsan.com: fixed checkpatch warning] Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/pm44xx.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c index aa6fd98f606e..ea62e75ef21d 100644 --- a/arch/arm/mach-omap2/pm44xx.c +++ b/arch/arm/mach-omap2/pm44xx.c @@ -77,10 +77,20 @@ static int omap4_pm_suspend(void) omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state); pwrdm_set_logic_retst(pwrst->pwrdm, pwrst->saved_logic_state); } - if (ret) + if (ret) { pr_crit("Could not enter target state in pm_suspend\n"); - else + /* + * OMAP4 chip PM currently works only with certain (newer) + * versions of bootloaders. This is due to missing code in the + * kernel to properly reset and initialize some devices. + * Warn the user about the bootloader version being one of the + * possible causes. + * http://www.spinics.net/lists/arm-kernel/msg218641.html + */ + pr_warn("A possible cause could be an old bootloader - try u-boot >= v2012.07\n"); + } else { pr_info("Successfully put all powerdomains to target state\n"); + } return 0; } @@ -146,6 +156,13 @@ int __init omap4_pm_init(void) } pr_err("Power Management for TI OMAP4.\n"); + /* + * OMAP4 chip PM currently works only with certain (newer) + * versions of bootloaders. This is due to missing code in the + * kernel to properly reset and initialize some devices. + * http://www.spinics.net/lists/arm-kernel/msg218641.html + */ + pr_warn("OMAP4 PM: u-boot >= v2012.07 is required for full PM support\n"); ret = pwrdm_for_each(pwrdms_setup, NULL); if (ret) { From 944f7b1dedb859f76a88c8d34ce23a90bf6285a0 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:49:46 +0900 Subject: [PATCH 2042/4607] leds-lp55xx: clean up init_device() in lp5521/5523 To make _probe() simple, device initialization code is moved to _init_device() at each driver. This patch is a preceding step for lp55xx common driver architecture. leds-lp5521: When 'lp5521_init_device()' gets failed, error handling should be 'fail1' rather than 'fail2'. fail1: releasing platform resource and return code fail2: releasing allocated LED devices with handling 'fail1' The 'lp5521_init_device()' is called before creating LED devices. Thus, 'goto fail1' is proper error handler of this function. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 102 ++++++++++++++++++++----------------- drivers/leds/leds-lp5523.c | 47 ++++++++++------- 2 files changed, 84 insertions(+), 65 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index cb8a5220200b..24f439f78bd8 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -687,6 +687,59 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } +static int lp5521_init_device(struct lp5521_chip *chip) +{ + struct lp5521_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int ret; + u8 buf; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) + return ret; + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 2000); /* Keep enable down at least 1ms */ + pdata->enable(1); + usleep_range(1000, 2000); /* 500us abs min. */ + } + + lp5521_write(client, LP5521_REG_RESET, 0xff); + usleep_range(10000, 20000); /* + * Exact value is not available. 10 - 20ms + * appears to be enough for reset. + */ + + /* + * Make sure that the chip is reset by reading back the r channel + * current reg. This is dummy read is required on some platforms - + * otherwise further access to the R G B channels in the + * LP5521_REG_ENABLE register will not have any effect - strange! + */ + ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf); + if (ret) { + dev_err(&client->dev, "error in resetting chip\n"); + return ret; + } + if (buf != LP5521_REG_R_CURR_DEFAULT) { + dev_err(&client->dev, + "unexpected data in register (expected 0x%x got 0x%x)\n", + LP5521_REG_R_CURR_DEFAULT, buf); + ret = -EINVAL; + return ret; + } + usleep_range(10000, 20000); + + ret = lp5521_detect(client); + if (ret) + dev_err(&client->dev, "Chip not found\n"); + + return ret; +} + static int lp5521_init_led(struct lp5521_led *led, struct i2c_client *client, int chan, struct lp5521_platform_data *pdata) @@ -742,7 +795,6 @@ static int lp5521_probe(struct i2c_client *client, struct lp5521_chip *chip; struct lp5521_platform_data *pdata; int ret, i, led; - u8 buf; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -762,51 +814,9 @@ static int lp5521_probe(struct i2c_client *client, chip->pdata = pdata; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - - lp5521_write(client, LP5521_REG_RESET, 0xff); - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ - - /* - * Make sure that the chip is reset by reading back the r channel - * current reg. This is dummy read is required on some platforms - - * otherwise further access to the R G B channels in the - * LP5521_REG_ENABLE register will not have any effect - strange! - */ - ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf); - if (ret) { - dev_err(&client->dev, "error in resetting chip\n"); - goto fail2; - } - if (buf != LP5521_REG_R_CURR_DEFAULT) { - dev_err(&client->dev, - "unexpected data in register (expected 0x%x got 0x%x)\n", - LP5521_REG_R_CURR_DEFAULT, buf); - ret = -EINVAL; - goto fail2; - } - usleep_range(10000, 20000); - - ret = lp5521_detect(client); - - if (ret) { - dev_err(&client->dev, "Chip not found\n"); - goto fail2; - } + ret = lp5521_init_device(chip); + if (ret) + goto fail1; dev_info(&client->dev, "%s programmable led chip found\n", id->name); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 7f5be8948cde..491ea725bb0b 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -896,6 +896,33 @@ static int lp5523_init_led(struct lp5523_led *led, struct device *dev, return 0; } +static int lp5523_init_device(struct lp5523_chip *chip) +{ + struct lp5523_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int ret; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) + return ret; + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 2000); /* Keep enable down at least 1ms */ + pdata->enable(1); + usleep_range(1000, 2000); /* 500us abs min. */ + } + + lp5523_write(client, LP5523_REG_RESET, 0xff); + usleep_range(10000, 20000); /* + * Exact value is not available. 10 - 20ms + * appears to be enough for reset. + */ + return lp5523_detect(client); +} + static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -921,25 +948,7 @@ static int lp5523_probe(struct i2c_client *client, chip->pdata = pdata; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - - lp5523_write(client, LP5523_REG_RESET, 0xff); - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ - ret = lp5523_detect(client); + ret = lp5523_init_device(chip); if (ret) goto fail1; From 1a9914855d2868112257dbc5771ddc6ebc9b2cab Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:50:36 +0900 Subject: [PATCH 2043/4607] leds-lp55xx: clean up deinit_device() in lp5521/5523 Device de-initialization code is moved to _deinit_device() at each driver. This patch is a preceding step for lp55xx common driver architecture. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 20 ++++++++++++-------- drivers/leds/leds-lp5523.c | 20 ++++++++++++-------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 24f439f78bd8..fd963fe0a944 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -740,6 +740,16 @@ static int lp5521_init_device(struct lp5521_chip *chip) return ret; } +static void lp5521_deinit_device(struct lp5521_chip *chip) +{ + struct lp5521_platform_data *pdata = chip->pdata; + + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); +} + static int lp5521_init_led(struct lp5521_led *led, struct i2c_client *client, int chan, struct lp5521_platform_data *pdata) @@ -865,10 +875,7 @@ fail2: cancel_work_sync(&chip->leds[i].brightness_work); } fail1: - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); + lp5521_deinit_device(chip); return ret; } @@ -885,10 +892,7 @@ static int lp5521_remove(struct i2c_client *client) cancel_work_sync(&chip->leds[i].brightness_work); } - if (chip->pdata->enable) - chip->pdata->enable(0); - if (chip->pdata->release_resources) - chip->pdata->release_resources(); + lp5521_deinit_device(chip); return 0; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 491ea725bb0b..ddb482aebe14 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -923,6 +923,16 @@ static int lp5523_init_device(struct lp5523_chip *chip) return lp5523_detect(client); } +static void lp5523_deinit_device(struct lp5523_chip *chip) +{ + struct lp5523_platform_data *pdata = chip->pdata; + + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); +} + static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1009,10 +1019,7 @@ fail2: flush_work(&chip->leds[i].brightness_work); } fail1: - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); + lp5523_deinit_device(chip); return ret; } @@ -1031,10 +1038,7 @@ static int lp5523_remove(struct i2c_client *client) flush_work(&chip->leds[i].brightness_work); } - if (chip->pdata->enable) - chip->pdata->enable(0); - if (chip->pdata->release_resources) - chip->pdata->release_resources(); + lp5523_deinit_device(chip); return 0; } From f652480802b636f86e194f9680347676b655d856 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:53:40 +0900 Subject: [PATCH 2044/4607] leds-lp55xx: clean up init leds in lp5521/5523 To make LED initialization code simple, new function, _register_leds() is added at each driver. This patch is a preceding step for lp55xx common driver architecture. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 69 +++++++++++++++++++++-------------- drivers/leds/leds-lp5523.c | 73 +++++++++++++++++++++++--------------- 2 files changed, 86 insertions(+), 56 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index fd963fe0a944..f4cd0fe67fef 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -799,12 +799,50 @@ static int lp5521_init_led(struct lp5521_led *led, return 0; } +static int lp5521_register_leds(struct lp5521_chip *chip) +{ + struct lp5521_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int i; + int led; + int ret; + + /* Initialize leds */ + chip->num_channels = pdata->num_channels; + chip->num_leds = 0; + led = 0; + for (i = 0; i < pdata->num_channels; i++) { + /* Do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + ret = lp5521_init_led(&chip->leds[led], client, i, pdata); + if (ret) { + dev_err(&client->dev, "error initializing leds\n"); + return ret; + } + chip->num_leds++; + + chip->leds[led].id = led; + /* Set initial LED current */ + lp5521_set_led_current(chip, led, + chip->leds[led].led_current); + + INIT_WORK(&(chip->leds[led].brightness_work), + lp5521_led_brightness_work); + + led++; + } + + return 0; +} + static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lp5521_chip *chip; struct lp5521_platform_data *pdata; - int ret, i, led; + int ret, i; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -836,32 +874,9 @@ static int lp5521_probe(struct i2c_client *client, goto fail1; } - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - ret = lp5521_init_led(&chip->leds[led], client, i, pdata); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - goto fail2; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set initial LED current */ - lp5521_set_led_current(chip, led, - chip->leds[led].led_current); - - INIT_WORK(&(chip->leds[led].brightness_work), - lp5521_led_brightness_work); - - led++; - } + ret = lp5521_register_leds(chip); + if (ret) + goto fail2; ret = lp5521_register_sysfs(client); if (ret) { diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index ddb482aebe14..f5e893289816 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -896,6 +896,46 @@ static int lp5523_init_led(struct lp5523_led *led, struct device *dev, return 0; } +static int lp5523_register_leds(struct lp5523_chip *chip, const char *name) +{ + struct lp5523_platform_data *pdata = chip->pdata; + struct i2c_client *client = chip->client; + int i; + int led; + int ret; + + /* Initialize leds */ + chip->num_channels = pdata->num_channels; + chip->num_leds = 0; + led = 0; + for (i = 0; i < pdata->num_channels; i++) { + /* Do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + INIT_WORK(&chip->leds[led].brightness_work, + lp5523_led_brightness_work); + + ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata, + name); + if (ret) { + dev_err(&client->dev, "error initializing leds\n"); + return ret; + } + chip->num_leds++; + + chip->leds[led].id = led; + /* Set LED current */ + lp5523_write(client, + LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, + chip->leds[led].led_current); + + led++; + } + + return 0; +} + static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -938,7 +978,7 @@ static int lp5523_probe(struct i2c_client *client, { struct lp5523_chip *chip; struct lp5523_platform_data *pdata; - int ret, i, led; + int ret, i; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -978,34 +1018,9 @@ static int lp5523_probe(struct i2c_client *client, goto fail1; } - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - INIT_WORK(&chip->leds[led].brightness_work, - lp5523_led_brightness_work); - - ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata, - id->name); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - goto fail2; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set LED current */ - lp5523_write(client, - LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, - chip->leds[led].led_current); - - led++; - } + ret = lp5523_register_leds(chip, id->name); + if (ret) + goto fail2; ret = lp5523_register_sysfs(client); if (ret) { From 1904f83d568dba794be9de1311bafb5a4424812a Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:56:23 +0900 Subject: [PATCH 2045/4607] leds-lp55xx: clean up deinit leds in lp5521/5523 To make LED unregistration code simple, new function, _unregister_leds() is added in each driver. This patch is a preceding step for lp55xx common driver architecture. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 23 +++++++++++++---------- drivers/leds/leds-lp5523.c | 21 ++++++++++++--------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index f4cd0fe67fef..ec1ffe6316c1 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -837,12 +837,22 @@ static int lp5521_register_leds(struct lp5521_chip *chip) return 0; } +static void lp5521_unregister_leds(struct lp5521_chip *chip) +{ + int i; + + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + cancel_work_sync(&chip->leds[i].brightness_work); + } +} + static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lp5521_chip *chip; struct lp5521_platform_data *pdata; - int ret, i; + int ret; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -885,10 +895,7 @@ static int lp5521_probe(struct i2c_client *client, } return ret; fail2: - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); - } + lp5521_unregister_leds(chip); fail1: lp5521_deinit_device(chip); return ret; @@ -897,15 +904,11 @@ fail1: static int lp5521_remove(struct i2c_client *client) { struct lp5521_chip *chip = i2c_get_clientdata(client); - int i; lp5521_run_led_pattern(PATTERN_OFF, chip); lp5521_unregister_sysfs(client); - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); - } + lp5521_unregister_leds(chip); lp5521_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index f5e893289816..2fc19bbddb72 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -936,6 +936,16 @@ static int lp5523_register_leds(struct lp5523_chip *chip, const char *name) return 0; } +static void lp5523_unregister_leds(struct lp5523_chip *chip) +{ + int i; + + for (i = 0; i < chip->num_leds; i++) { + led_classdev_unregister(&chip->leds[i].cdev); + flush_work(&chip->leds[i].brightness_work); + } +} + static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -1029,10 +1039,7 @@ static int lp5523_probe(struct i2c_client *client, } return ret; fail2: - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - flush_work(&chip->leds[i].brightness_work); - } + lp5523_unregister_leds(chip); fail1: lp5523_deinit_device(chip); return ret; @@ -1041,17 +1048,13 @@ fail1: static int lp5523_remove(struct i2c_client *client) { struct lp5523_chip *chip = i2c_get_clientdata(client); - int i; /* Disable engine mode */ lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); lp5523_unregister_sysfs(client); - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - flush_work(&chip->leds[i].brightness_work); - } + lp5523_unregister_leds(chip); lp5523_deinit_device(chip); return 0; From 86eb7748cef00faa3eaefc8fc450ed30281a09e7 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:57:02 +0900 Subject: [PATCH 2046/4607] leds-lp55xx: add device reset function in lp5521/5523 Use explicit each driver function rather than raw command. These function will be merged into the lp55xx common driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 10 +++++++++- drivers/leds/leds-lp5523.c | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index ec1ffe6316c1..ec89ed641005 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -687,6 +687,13 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } +static void lp5521_reset_device(struct lp5521_chip *chip) +{ + struct i2c_client *client = chip->client; + + lp5521_write(client, LP5521_REG_RESET, 0xff); +} + static int lp5521_init_device(struct lp5521_chip *chip) { struct lp5521_platform_data *pdata = chip->pdata; @@ -707,7 +714,8 @@ static int lp5521_init_device(struct lp5521_chip *chip) usleep_range(1000, 2000); /* 500us abs min. */ } - lp5521_write(client, LP5521_REG_RESET, 0xff); + lp5521_reset_device(chip); + usleep_range(10000, 20000); /* * Exact value is not available. 10 - 20ms * appears to be enough for reset. diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 2fc19bbddb72..cac492b0abf3 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -946,6 +946,13 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } +static void lp5523_reset_device(struct lp5523_chip *chip) +{ + struct i2c_client *client = chip->client; + + lp5523_write(client, LP5523_REG_RESET, 0xff); +} + static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -965,7 +972,8 @@ static int lp5523_init_device(struct lp5523_chip *chip) usleep_range(1000, 2000); /* 500us abs min. */ } - lp5523_write(client, LP5523_REG_RESET, 0xff); + lp5523_reset_device(chip); + usleep_range(10000, 20000); /* * Exact value is not available. 10 - 20ms * appears to be enough for reset. From f6c64c6fc8d793b414446f1a655a37b7bfce68e3 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 17:58:01 +0900 Subject: [PATCH 2047/4607] leds-lp55xx: do chip specific configuration on device init Chip specific function is configured when the device is initialized. So _configure() is moved to each device init function. If chip configuration gets failed, the device is de-initialized in each _init_device(), not probe(). For compile error fix, function type declarations are added. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 26 +++++++++++++++++--------- drivers/leds/leds-lp5523.c | 26 +++++++++++++++++++------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index ec89ed641005..e042a094a07f 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -694,6 +694,7 @@ static void lp5521_reset_device(struct lp5521_chip *chip) lp5521_write(client, LP5521_REG_RESET, 0xff); } +static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { struct lp5521_platform_data *pdata = chip->pdata; @@ -742,9 +743,22 @@ static int lp5521_init_device(struct lp5521_chip *chip) usleep_range(10000, 20000); ret = lp5521_detect(client); - if (ret) + if (ret) { dev_err(&client->dev, "Chip not found\n"); + goto err; + } + ret = lp5521_configure(client); + if (ret < 0) { + dev_err(&client->dev, "error configuring chip\n"); + goto err_config; + } + + return 0; + +err_config: + lp5521_deinit_device(chip); +err: return ret; } @@ -882,16 +896,10 @@ static int lp5521_probe(struct i2c_client *client, ret = lp5521_init_device(chip); if (ret) - goto fail1; + goto err_init; dev_info(&client->dev, "%s programmable led chip found\n", id->name); - ret = lp5521_configure(client); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto fail1; - } - ret = lp5521_register_leds(chip); if (ret) goto fail2; @@ -904,8 +912,8 @@ static int lp5521_probe(struct i2c_client *client, return ret; fail2: lp5521_unregister_leds(chip); -fail1: lp5521_deinit_device(chip); +err_init: return ret; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index cac492b0abf3..fefe27c3f377 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -953,6 +953,7 @@ static void lp5523_reset_device(struct lp5523_chip *chip) lp5523_write(client, LP5523_REG_RESET, 0xff); } +static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -978,7 +979,22 @@ static int lp5523_init_device(struct lp5523_chip *chip) * Exact value is not available. 10 - 20ms * appears to be enough for reset. */ - return lp5523_detect(client); + ret = lp5523_detect(client); + if (ret) + goto err; + + ret = lp5523_configure(client); + if (ret < 0) { + dev_err(&client->dev, "error configuring chip\n"); + goto err_config; + } + + return 0; + +err_config: + lp5523_deinit_device(chip); +err: + return ret; } static void lp5523_deinit_device(struct lp5523_chip *chip) @@ -1018,7 +1034,7 @@ static int lp5523_probe(struct i2c_client *client, ret = lp5523_init_device(chip); if (ret) - goto fail1; + goto err_init; dev_info(&client->dev, "%s Programmable led chip found\n", id->name); @@ -1030,11 +1046,6 @@ static int lp5523_probe(struct i2c_client *client, goto fail1; } } - ret = lp5523_configure(client); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto fail1; - } ret = lp5523_register_leds(chip, id->name); if (ret) @@ -1050,6 +1061,7 @@ fail2: lp5523_unregister_leds(chip); fail1: lp5523_deinit_device(chip); +err_init: return ret; } From c93d08fa75020835741c7b1d0523ff854e8acde1 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:01:23 +0900 Subject: [PATCH 2048/4607] leds-lp55xx: add new common driver for lp5521/5523 This patch supports basic common driver code for LP5521, LP5523/55231 devices. ( Driver Structure Data ) lp55xx_led and lp55xx_chip In lp55xx common driver, two different data structure is used. o lp55xx_led control multi output LED channels such as led current, channel index. o lp55xx_chip general chip control such like the I2C and platform data. For example, LP5521 has maximum 3 LED channels. LP5523/55231 has 9 output channels. lp55xx_chip for LP5521 ... lp55xx_led #1 lp55xx_led #2 lp55xx_led #3 lp55xx_chip for LP5523 ... lp55xx_led #1 lp55xx_led #2 . . lp55xx_led #9 ( Platform Data ) LP5521 and LP5523/55231 have own specific platform data. However, this data can be handled with just one platform data structure. The lp55xx platform data is declared in the header. This structure is derived from leds-lp5521.h and leds-lp5523.h Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 9 +++ drivers/leds/Makefile | 1 + drivers/leds/leds-lp55xx-common.c | 59 +++++++++++++++ drivers/leds/leds-lp55xx-common.h | 61 ++++++++++++++++ include/linux/platform_data/leds-lp55xx.h | 87 +++++++++++++++++++++++ 5 files changed, 217 insertions(+) create mode 100644 drivers/leds/leds-lp55xx-common.c create mode 100644 drivers/leds/leds-lp55xx-common.h create mode 100644 include/linux/platform_data/leds-lp55xx.h diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index b58bc8a14b9c..3d7822b3498f 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -193,9 +193,17 @@ config LEDS_LP3944 To compile this driver as a module, choose M here: the module will be called leds-lp3944. +config LEDS_LP55XX_COMMON + tristate "Common Driver for TI/National LP5521 and LP5523/55231" + depends on LEDS_LP5521 || LEDS_LP5523 + help + This option supports common operations for LP5521 and LP5523/55231 + devices. + config LEDS_LP5521 tristate "LED Support for N.S. LP5521 LED driver chip" depends on LEDS_CLASS && I2C + select LEDS_LP55XX_COMMON help If you say yes here you get support for the National Semiconductor LP5521 LED driver. It is 3 channel chip with programmable engines. @@ -205,6 +213,7 @@ config LEDS_LP5521 config LEDS_LP5523 tristate "LED Support for TI/National LP5523/55231 LED driver chip" depends on LEDS_CLASS && I2C + select LEDS_LP55XX_COMMON help If you say yes here you get support for TI/National Semiconductor LP5523/55231 LED driver. diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 3fb9641b6194..215e7e3b6173 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o +obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c new file mode 100644 index 000000000000..1c716ecfa817 --- /dev/null +++ b/drivers/leds/leds-lp55xx-common.c @@ -0,0 +1,59 @@ +/* + * LP5521/LP5523/LP55231 Common Driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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. + * + * Derived from leds-lp5521.c, leds-lp5523.c + */ + +#include +#include +#include +#include + +#include "leds-lp55xx-common.h" + +int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) +{ + return i2c_smbus_write_byte_data(chip->cl, reg, val); +} +EXPORT_SYMBOL_GPL(lp55xx_write); + +int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val) +{ + s32 ret; + + ret = i2c_smbus_read_byte_data(chip->cl, reg); + if (ret < 0) + return ret; + + *val = ret; + return 0; +} +EXPORT_SYMBOL_GPL(lp55xx_read); + +int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val) +{ + int ret; + u8 tmp; + + ret = lp55xx_read(chip, reg, &tmp); + if (ret) + return ret; + + tmp &= ~mask; + tmp |= val & mask; + + return lp55xx_write(chip, reg, tmp); +} +EXPORT_SYMBOL_GPL(lp55xx_update_bits); + +MODULE_AUTHOR("Milo Kim "); +MODULE_DESCRIPTION("LP55xx Common Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h new file mode 100644 index 000000000000..369cb9c91f17 --- /dev/null +++ b/drivers/leds/leds-lp55xx-common.h @@ -0,0 +1,61 @@ +/* + * LP55XX Common Driver Header + * + * Copyright (C) 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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. + * + * Derived from leds-lp5521.c, leds-lp5523.c + */ + +#ifndef _LEDS_LP55XX_COMMON_H +#define _LEDS_LP55XX_COMMON_H + +struct lp55xx_led; +struct lp55xx_chip; + +/* + * struct lp55xx_chip + * @cl : I2C communication for access registers + * @pdata : Platform specific data + * @lock : Lock for user-space interface + * @num_leds : Number of registered LEDs + */ +struct lp55xx_chip { + struct i2c_client *cl; + struct lp55xx_platform_data *pdata; + struct mutex lock; /* lock for user-space interface */ + int num_leds; +}; + +/* + * struct lp55xx_led + * @chan_nr : Channel number + * @cdev : LED class device + * @led_current : Current setting at each led channel + * @max_current : Maximun current at each led channel + * @brightness_work : Workqueue for brightness control + * @brightness : Brightness value + * @chip : The lp55xx chip data + */ +struct lp55xx_led { + int chan_nr; + struct led_classdev cdev; + u8 led_current; + u8 max_current; + struct work_struct brightness_work; + u8 brightness; + struct lp55xx_chip *chip; +}; + +/* register access */ +extern int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val); +extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val); +extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, + u8 mask, u8 val); + +#endif /* _LEDS_LP55XX_COMMON_H */ diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h new file mode 100644 index 000000000000..1509570d5a3f --- /dev/null +++ b/include/linux/platform_data/leds-lp55xx.h @@ -0,0 +1,87 @@ +/* + * LP55XX Platform Data Header + * + * Copyright (C) 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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. + * + * Derived from leds-lp5521.h, leds-lp5523.h + */ + +#ifndef _LEDS_LP55XX_H +#define _LEDS_LP55XX_H + +/* Clock configuration */ +#define LP55XX_CLOCK_AUTO 0 +#define LP55XX_CLOCK_INT 1 +#define LP55XX_CLOCK_EXT 2 + +/* Bits in LP5521 CONFIG register. 'update_config' in lp55xx_platform_data */ +#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ +#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ +#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ +#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ +#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ +#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ +#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */ +#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */ +#define LP5521_CLK_INT 1 /* Internal clock */ +#define LP5521_CLK_AUTO 2 /* Automatic clock selection */ + +struct lp55xx_led_config { + const char *name; + u8 chan_nr; + u8 led_current; /* mA x10, 0 if led is not connected */ + u8 max_current; +}; + +struct lp55xx_predef_pattern { + u8 *r; + u8 *g; + u8 *b; + u8 size_r; + u8 size_g; + u8 size_b; +}; + +/* + * struct lp55xx_platform_data + * @led_config : Configurable led class device + * @num_channels : Number of LED channels + * @label : Used for naming LEDs + * @clock_mode : Input clock mode. LP55XX_CLOCK_AUTO or _INT or _EXT + * @setup_resources : Platform specific function before enabling the chip + * @release_resources : Platform specific function after disabling the chip + * @enable : EN pin control by platform side + * @patterns : Predefined pattern data for RGB channels + * @num_patterns : Number of patterns + * @update_config : Value of CONFIG register + */ +struct lp55xx_platform_data { + + /* LED channel configuration */ + struct lp55xx_led_config *led_config; + u8 num_channels; + const char *label; + + /* Clock configuration */ + u8 clock_mode; + + /* Platform specific functions */ + int (*setup_resources)(void); + void (*release_resources)(void); + void (*enable)(bool state); + + /* Predefined pattern data */ + struct lp55xx_predef_pattern *patterns; + unsigned int num_patterns; + + /* _CONFIG register */ + u8 update_config; +}; + +#endif /* _LEDS_LP55XX_H */ From 945c700746cbfa3375bf88123c2cf6c210f4cc2c Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:02:26 +0900 Subject: [PATCH 2049/4607] leds-lp55xx: replace name of data structure Change the name of chip data structure and platform data. This patch is a preceding step for cleaning up lp5521/5523 probe and remove. These data will be replaced with new lp55xx common data structures in next patch. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 36 ++++++++++++++++++------------------ drivers/leds/leds-lp5523.c | 38 +++++++++++++++++++------------------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index e042a094a07f..8ef8f44bf86e 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -872,35 +872,35 @@ static void lp5521_unregister_leds(struct lp5521_chip *chip) static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5521_chip *chip; - struct lp5521_platform_data *pdata; + struct lp5521_chip *old_chip; + struct lp5521_platform_data *old_pdata; int ret; - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) + old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); + if (!old_chip) return -ENOMEM; - i2c_set_clientdata(client, chip); - chip->client = client; + i2c_set_clientdata(client, old_chip); + old_chip->client = client; - pdata = client->dev.platform_data; + old_pdata = client->dev.platform_data; - if (!pdata) { + if (!old_pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&chip->lock); + mutex_init(&old_chip->lock); - chip->pdata = pdata; + old_chip->pdata = old_pdata; - ret = lp5521_init_device(chip); + ret = lp5521_init_device(old_chip); if (ret) goto err_init; dev_info(&client->dev, "%s programmable led chip found\n", id->name); - ret = lp5521_register_leds(chip); + ret = lp5521_register_leds(old_chip); if (ret) goto fail2; @@ -911,22 +911,22 @@ static int lp5521_probe(struct i2c_client *client, } return ret; fail2: - lp5521_unregister_leds(chip); - lp5521_deinit_device(chip); + lp5521_unregister_leds(old_chip); + lp5521_deinit_device(old_chip); err_init: return ret; } static int lp5521_remove(struct i2c_client *client) { - struct lp5521_chip *chip = i2c_get_clientdata(client); + struct lp5521_chip *old_chip = i2c_get_clientdata(client); - lp5521_run_led_pattern(PATTERN_OFF, chip); + lp5521_run_led_pattern(PATTERN_OFF, old_chip); lp5521_unregister_sysfs(client); - lp5521_unregister_leds(chip); + lp5521_unregister_leds(old_chip); - lp5521_deinit_device(chip); + lp5521_deinit_device(old_chip); return 0; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index fefe27c3f377..49b976271d96 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -1010,44 +1010,44 @@ static void lp5523_deinit_device(struct lp5523_chip *chip) static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5523_chip *chip; - struct lp5523_platform_data *pdata; + struct lp5523_chip *old_chip; + struct lp5523_platform_data *old_pdata; int ret, i; - chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); - if (!chip) + old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); + if (!old_chip) return -ENOMEM; - i2c_set_clientdata(client, chip); - chip->client = client; + i2c_set_clientdata(client, old_chip); + old_chip->client = client; - pdata = client->dev.platform_data; + old_pdata = client->dev.platform_data; - if (!pdata) { + if (!old_pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&chip->lock); + mutex_init(&old_chip->lock); - chip->pdata = pdata; + old_chip->pdata = old_pdata; - ret = lp5523_init_device(chip); + ret = lp5523_init_device(old_chip); if (ret) goto err_init; dev_info(&client->dev, "%s Programmable led chip found\n", id->name); /* Initialize engines */ - for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { - ret = lp5523_init_engine(&chip->engines[i], i + 1); + for (i = 0; i < ARRAY_SIZE(old_chip->engines); i++) { + ret = lp5523_init_engine(&old_chip->engines[i], i + 1); if (ret) { dev_err(&client->dev, "error initializing engine\n"); goto fail1; } } - ret = lp5523_register_leds(chip, id->name); + ret = lp5523_register_leds(old_chip, id->name); if (ret) goto fail2; @@ -1058,25 +1058,25 @@ static int lp5523_probe(struct i2c_client *client, } return ret; fail2: - lp5523_unregister_leds(chip); + lp5523_unregister_leds(old_chip); fail1: - lp5523_deinit_device(chip); + lp5523_deinit_device(old_chip); err_init: return ret; } static int lp5523_remove(struct i2c_client *client) { - struct lp5523_chip *chip = i2c_get_clientdata(client); + struct lp5523_chip *old_chip = i2c_get_clientdata(client); /* Disable engine mode */ lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); lp5523_unregister_sysfs(client); - lp5523_unregister_leds(chip); + lp5523_unregister_leds(old_chip); - lp5523_deinit_device(chip); + lp5523_deinit_device(old_chip); return 0; } From 6a0c9a47963cc72c68713923ead60d1e72e7136c Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:03:08 +0900 Subject: [PATCH 2050/4607] leds-lp55xx: use common lp55xx data structure in _probe() LP5521 and LP5523 data structures have common features. Use common lp55xx data structures rather than chip specific data. Legacy code in probe is replaced with this new data structures. lp55xx_chip : Common data between lp5521_chip and lp5523_chip lp55xx_led : Common LED structure between lp5521_led and lp5523_led lp55xx_platform_data : Common platform data between lp5521_platform_data and lp5523_platform_data Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 36 ++++++++++++++++++++++-------------- drivers/leds/leds-lp5523.c | 36 ++++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 8ef8f44bf86e..1eab1557a612 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -34,6 +34,9 @@ #include #include #include +#include + +#include "leds-lp55xx-common.h" #define LP5521_PROGRAM_LENGTH 32 /* in bytes */ @@ -872,27 +875,32 @@ static void lp5521_unregister_leds(struct lp5521_chip *chip) static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5521_chip *old_chip; - struct lp5521_platform_data *old_pdata; + struct lp5521_chip *old_chip = NULL; int ret; + struct lp55xx_chip *chip; + struct lp55xx_led *led; + struct lp55xx_platform_data *pdata = client->dev.platform_data; - old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); - if (!old_chip) - return -ENOMEM; - - i2c_set_clientdata(client, old_chip); - old_chip->client = client; - - old_pdata = client->dev.platform_data; - - if (!old_pdata) { + if (!pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&old_chip->lock); + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; - old_chip->pdata = old_pdata; + led = devm_kzalloc(&client->dev, + sizeof(*led) * pdata->num_channels, GFP_KERNEL); + if (!led) + return -ENOMEM; + + chip->cl = client; + chip->pdata = pdata; + + mutex_init(&chip->lock); + + i2c_set_clientdata(client, led); ret = lp5521_init_device(old_chip); if (ret) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 49b976271d96..71bda3670ff0 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -34,6 +34,9 @@ #include #include #include +#include + +#include "leds-lp55xx-common.h" #define LP5523_REG_ENABLE 0x00 #define LP5523_REG_OP_MODE 0x01 @@ -1010,27 +1013,32 @@ static void lp5523_deinit_device(struct lp5523_chip *chip) static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5523_chip *old_chip; - struct lp5523_platform_data *old_pdata; + struct lp5523_chip *old_chip = NULL; int ret, i; + struct lp55xx_chip *chip; + struct lp55xx_led *led; + struct lp55xx_platform_data *pdata = client->dev.platform_data; - old_chip = devm_kzalloc(&client->dev, sizeof(*old_chip), GFP_KERNEL); - if (!old_chip) - return -ENOMEM; - - i2c_set_clientdata(client, old_chip); - old_chip->client = client; - - old_pdata = client->dev.platform_data; - - if (!old_pdata) { + if (!pdata) { dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - mutex_init(&old_chip->lock); + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; - old_chip->pdata = old_pdata; + led = devm_kzalloc(&client->dev, + sizeof(*led) * pdata->num_channels, GFP_KERNEL); + if (!led) + return -ENOMEM; + + chip->cl = client; + chip->pdata = pdata; + + mutex_init(&chip->lock); + + i2c_set_clientdata(client, led); ret = lp5523_init_device(old_chip); if (ret) From 9448217403462c4b17bc56690a0348a0c02e5ba2 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:03:55 +0900 Subject: [PATCH 2051/4607] leds-lp5521: clean up lp5521_configure() This patch is a preceding step for making common lp55xx init function. LP5521_REG_R_CURRENT register code moved: Chip specific code moved from lp5521_init_device() to lp5521_configure(). Remove engine init function: LP5521 has internal program engines which are used for running LED patterns. (blinking, ramp up/down and other emotional visual effects) Engine initialization is done by reset command in lp5521_init_device(). Remove this duplicate code. Return code: Do not use 'OR' arithmetic for the result. If some error occus, just return it. Enable latency: Use explicit named function, lp5521_wait_enable_done(). According to the datasheet, 500us is guaranteed time. Thus wait time is changed from 1000us to 500us. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 78 +++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 1eab1557a612..341a41030fd8 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -125,6 +125,12 @@ struct lp5521_chip { u8 num_leds; }; +static inline void lp5521_wait_enable_done(void) +{ + /* it takes more 488 us to update ENABLE register */ + usleep_range(500, 600); +} + static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev) { return container_of(cdev, struct lp5521_led, cdev); @@ -229,43 +235,56 @@ static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) curr); } -static void lp5521_init_engine(struct lp5521_chip *chip) -{ - int i; - for (i = 0; i < ARRAY_SIZE(chip->engines); i++) { - chip->engines[i].id = i + 1; - chip->engines[i].engine_mask = LP5521_ENG_MASK_BASE >> (i * 2); - chip->engines[i].prog_page = i; - } -} - static int lp5521_configure(struct i2c_client *client) { struct lp5521_chip *chip = i2c_get_clientdata(client); int ret; u8 cfg; + u8 val; - lp5521_init_engine(chip); + /* + * Make sure that the chip is reset by reading back the r channel + * current reg. This is dummy read is required on some platforms - + * otherwise further access to the R G B channels in the + * LP5521_REG_ENABLE register will not have any effect - strange! + */ + ret = lp5521_read(client, LP5521_REG_R_CURRENT, &val); + if (ret) { + dev_err(&client->dev, "error in resetting chip\n"); + return ret; + } + if (val != LP5521_REG_R_CURR_DEFAULT) { + dev_err(&client->dev, + "unexpected data in register (expected 0x%x got 0x%x)\n", + LP5521_REG_R_CURR_DEFAULT, val); + ret = -EINVAL; + return ret; + } + usleep_range(10000, 20000); /* Set all PWMs to direct control mode */ ret = lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); cfg = chip->pdata->update_config ? : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); - ret |= lp5521_write(client, LP5521_REG_CONFIG, cfg); + ret = lp5521_write(client, LP5521_REG_CONFIG, cfg); + if (ret) + return ret; /* Initialize all channels PWM to zero -> leds off */ - ret |= lp5521_write(client, LP5521_REG_R_PWM, 0); - ret |= lp5521_write(client, LP5521_REG_G_PWM, 0); - ret |= lp5521_write(client, LP5521_REG_B_PWM, 0); + lp5521_write(client, LP5521_REG_R_PWM, 0); + lp5521_write(client, LP5521_REG_G_PWM, 0); + lp5521_write(client, LP5521_REG_B_PWM, 0); /* Set engines are set to run state when OP_MODE enables engines */ - ret |= lp5521_write(client, LP5521_REG_ENABLE, + ret = lp5521_write(client, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); - /* enable takes 500us. 1 - 2 ms leaves some margin */ - usleep_range(1000, 2000); + if (ret) + return ret; - return ret; + lp5521_wait_enable_done(); + + return 0; } static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) @@ -703,7 +722,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) struct lp5521_platform_data *pdata = chip->pdata; struct i2c_client *client = chip->client; int ret; - u8 buf; if (pdata->setup_resources) { ret = pdata->setup_resources(); @@ -725,26 +743,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) * appears to be enough for reset. */ - /* - * Make sure that the chip is reset by reading back the r channel - * current reg. This is dummy read is required on some platforms - - * otherwise further access to the R G B channels in the - * LP5521_REG_ENABLE register will not have any effect - strange! - */ - ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf); - if (ret) { - dev_err(&client->dev, "error in resetting chip\n"); - return ret; - } - if (buf != LP5521_REG_R_CURR_DEFAULT) { - dev_err(&client->dev, - "unexpected data in register (expected 0x%x got 0x%x)\n", - LP5521_REG_R_CURR_DEFAULT, buf); - ret = -EINVAL; - return ret; - } - usleep_range(10000, 20000); - ret = lp5521_detect(client); if (ret) { dev_err(&client->dev, "Chip not found\n"); From 632418bf65503405df3f9a6a1616f5a95f91db85 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:04:50 +0900 Subject: [PATCH 2052/4607] leds-lp5523: clean up lp5523_configure() This patch is a preceding step for making common lp55xx init function. Return code: Do not use 'OR' arithmetic for the result. If some error occurs, just return it. Remove engine verification code: To check whether internal engine works or not, many lines of code are executed. However, this job is unnecessary during the chip initialization because the engine usage is not mandatory but optional function. LED engines are enabled when specific LED pattern is loaded. Therefore, this verification code is removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 69 ++++++-------------------------------- 1 file changed, 10 insertions(+), 59 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 71bda3670ff0..2ca41c5af719 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -199,77 +199,28 @@ static int lp5523_detect(struct i2c_client *client) static int lp5523_configure(struct i2c_client *client) { - struct lp5523_chip *chip = i2c_get_clientdata(client); int ret = 0; - u8 status; - /* one pattern per engine setting led mux start and stop addresses */ - static const u8 pattern[][LP5523_PROGRAM_LENGTH] = { - { 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0}, - { 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0}, - { 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0}, - }; + ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); + if (ret) + return ret; - ret |= lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ usleep_range(1000, 2000); - ret |= lp5523_write(client, LP5523_REG_CONFIG, + ret = lp5523_write(client, LP5523_REG_CONFIG, LP5523_AUTO_INC | LP5523_PWR_SAVE | LP5523_CP_AUTO | LP5523_AUTO_CLK | LP5523_PWM_PWR_SAVE); - - /* turn on all leds */ - ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01); - ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff); - - /* hardcode 32 bytes of memory for each engine from program memory */ - ret |= lp5523_write(client, LP5523_REG_CH1_PROG_START, 0x00); - ret |= lp5523_write(client, LP5523_REG_CH2_PROG_START, 0x10); - ret |= lp5523_write(client, LP5523_REG_CH3_PROG_START, 0x20); - - /* write led mux address space for each channel */ - ret |= lp5523_load_program(&chip->engines[0], pattern[0]); - ret |= lp5523_load_program(&chip->engines[1], pattern[1]); - ret |= lp5523_load_program(&chip->engines[2], pattern[2]); - - if (ret) { - dev_err(&client->dev, "could not load mux programs\n"); - return -1; - } - - /* set all engines exec state and mode to run 00101010 */ - ret |= lp5523_write(client, LP5523_REG_ENABLE, - (LP5523_CMD_RUN | LP5523_ENABLE)); - - ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_RUN); - - if (ret) { - dev_err(&client->dev, "could not start mux programs\n"); - return -1; - } - - /* Let the programs run for couple of ms and check the engine status */ - usleep_range(3000, 6000); - ret = lp5523_read(client, LP5523_REG_STATUS, &status); - if (ret < 0) + if (ret) return ret; - status &= LP5523_ENG_STATUS_MASK; + /* turn on all leds */ + ret = lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01); + if (ret) + return ret; - if (status == LP5523_ENG_STATUS_MASK) { - dev_dbg(&client->dev, "all engines configured\n"); - } else { - dev_info(&client->dev, "status == %x\n", status); - dev_err(&client->dev, "cound not configure LED engine\n"); - return -1; - } - - dev_info(&client->dev, "disabling engines\n"); - - ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); - - return ret; + return lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff); } static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode) From a85908dd7799e4fa242812ce27a8f774c721d1fb Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:07:20 +0900 Subject: [PATCH 2053/4607] leds-lp55xx: use lp55xx common init function - platform data LP5521/5523 platform data functions are moved to lp55xx common driver. New init function, lp55xx_init_device() is created. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 14 ------------- drivers/leds/leds-lp5523.c | 14 ------------- drivers/leds/leds-lp55xx-common.c | 34 +++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 3 +++ 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 341a41030fd8..124ce80fa115 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -719,23 +719,9 @@ static void lp5521_reset_device(struct lp5521_chip *chip) static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { - struct lp5521_platform_data *pdata = chip->pdata; struct i2c_client *client = chip->client; int ret; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - lp5521_reset_device(chip); usleep_range(10000, 20000); /* diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 2ca41c5af719..8e602047ce35 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -910,23 +910,9 @@ static void lp5523_reset_device(struct lp5523_chip *chip) static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { - struct lp5523_platform_data *pdata = chip->pdata; struct i2c_client *client = chip->client; int ret; - if (pdata->setup_resources) { - ret = pdata->setup_resources(); - if (ret < 0) - return ret; - } - - if (pdata->enable) { - pdata->enable(0); - usleep_range(1000, 2000); /* Keep enable down at least 1ms */ - pdata->enable(1); - usleep_range(1000, 2000); /* 500us abs min. */ - } - lp5523_reset_device(chip); usleep_range(10000, 20000); /* diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 1c716ecfa817..05a854c0d9b2 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -12,6 +12,7 @@ * Derived from leds-lp5521.c, leds-lp5523.c */ +#include #include #include #include @@ -54,6 +55,39 @@ int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val) } EXPORT_SYMBOL_GPL(lp55xx_update_bits); +int lp55xx_init_device(struct lp55xx_chip *chip) +{ + struct lp55xx_platform_data *pdata; + struct device *dev = &chip->cl->dev; + int ret = 0; + + WARN_ON(!chip); + + pdata = chip->pdata; + + if (!pdata) + return -EINVAL; + + if (pdata->setup_resources) { + ret = pdata->setup_resources(); + if (ret < 0) { + dev_err(dev, "setup resoure err: %d\n", ret); + goto err; + } + } + + if (pdata->enable) { + pdata->enable(0); + usleep_range(1000, 2000); /* Keep enable down at least 1ms */ + pdata->enable(1); + usleep_range(1000, 2000); /* 500us abs min. */ + } + +err: + return ret; +} +EXPORT_SYMBOL_GPL(lp55xx_init_device); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 369cb9c91f17..09d1882ce58e 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -58,4 +58,7 @@ extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val); extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val); +/* common device init functions */ +extern int lp55xx_init_device(struct lp55xx_chip *chip); + #endif /* _LEDS_LP55XX_COMMON_H */ From 48068d5de16c23c256c085b2cd3ff03bec393900 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:08:49 +0900 Subject: [PATCH 2054/4607] leds-lp55xx: use lp55xx common init function - reset LP5521/5523 reset device functions are moved to lp55xx common driver. Value of register address and value are chip dependent. Those are configured in each driver. In init function, reset command is executed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 26 ++++++++++++-------------- drivers/leds/leds-lp5523.c | 23 ++++++++++------------- drivers/leds/leds-lp55xx-common.c | 22 +++++++++++++++++++++- drivers/leds/leds-lp55xx-common.h | 20 ++++++++++++++++++++ 4 files changed, 63 insertions(+), 28 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 124ce80fa115..e1f1dfcd1547 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -98,6 +98,9 @@ /* Pattern Mode */ #define PATTERN_OFF 0 +/* Reset register value */ +#define LP5521_RESET 0xFF + struct lp5521_engine { int id; u8 mode; @@ -709,26 +712,12 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static void lp5521_reset_device(struct lp5521_chip *chip) -{ - struct i2c_client *client = chip->client; - - lp5521_write(client, LP5521_REG_RESET, 0xff); -} - static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { struct i2c_client *client = chip->client; int ret; - lp5521_reset_device(chip); - - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ - ret = lp5521_detect(client); if (ret) { dev_err(&client->dev, "Chip not found\n"); @@ -856,6 +845,14 @@ static void lp5521_unregister_leds(struct lp5521_chip *chip) } } +/* Chip specific configurations */ +static struct lp55xx_device_config lp5521_cfg = { + .reset = { + .addr = LP5521_REG_RESET, + .val = LP5521_RESET, + }, +}; + static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -881,6 +878,7 @@ static int lp5521_probe(struct i2c_client *client, chip->cl = client; chip->pdata = pdata; + chip->cfg = &lp5521_cfg; mutex_init(&chip->lock); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 8e602047ce35..00547783db77 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -87,6 +87,7 @@ #define LP5523_AUTO_CLK 0x02 #define LP5523_EN_LEDTEST 0x80 #define LP5523_LEDTEST_DONE 0x80 +#define LP5523_RESET 0xFF #define LP5523_DEFAULT_CURRENT 50 /* microAmps */ #define LP5523_PROGRAM_LENGTH 32 /* in bytes */ @@ -900,25 +901,12 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } -static void lp5523_reset_device(struct lp5523_chip *chip) -{ - struct i2c_client *client = chip->client; - - lp5523_write(client, LP5523_REG_RESET, 0xff); -} - static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { struct i2c_client *client = chip->client; int ret; - lp5523_reset_device(chip); - - usleep_range(10000, 20000); /* - * Exact value is not available. 10 - 20ms - * appears to be enough for reset. - */ ret = lp5523_detect(client); if (ret) goto err; @@ -947,6 +935,14 @@ static void lp5523_deinit_device(struct lp5523_chip *chip) pdata->release_resources(); } +/* Chip specific configurations */ +static struct lp55xx_device_config lp5523_cfg = { + .reset = { + .addr = LP5523_REG_RESET, + .val = LP5523_RESET, + }, +}; + static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -972,6 +968,7 @@ static int lp5523_probe(struct i2c_client *client, chip->cl = client; chip->pdata = pdata; + chip->cfg = &lp5523_cfg; mutex_init(&chip->lock); diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 05a854c0d9b2..bbf2bbf03807 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -20,6 +20,16 @@ #include "leds-lp55xx-common.h" +static void lp55xx_reset_device(struct lp55xx_chip *chip) +{ + struct lp55xx_device_config *cfg = chip->cfg; + u8 addr = cfg->reset.addr; + u8 val = cfg->reset.val; + + /* no error checking here because no ACK from the device after reset */ + lp55xx_write(chip, addr, val); +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -58,14 +68,16 @@ EXPORT_SYMBOL_GPL(lp55xx_update_bits); int lp55xx_init_device(struct lp55xx_chip *chip) { struct lp55xx_platform_data *pdata; + struct lp55xx_device_config *cfg; struct device *dev = &chip->cl->dev; int ret = 0; WARN_ON(!chip); pdata = chip->pdata; + cfg = chip->cfg; - if (!pdata) + if (!pdata || !cfg) return -EINVAL; if (pdata->setup_resources) { @@ -83,6 +95,14 @@ int lp55xx_init_device(struct lp55xx_chip *chip) usleep_range(1000, 2000); /* 500us abs min. */ } + lp55xx_reset_device(chip); + + /* + * Exact value is not available. 10 - 20ms + * appears to be enough for reset. + */ + usleep_range(10000, 20000); + err: return ret; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 09d1882ce58e..a73ee0b9a0bd 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -18,18 +18,38 @@ struct lp55xx_led; struct lp55xx_chip; +/* + * struct lp55xx_reg + * @addr : Register address + * @val : Register value + */ +struct lp55xx_reg { + u8 addr; + u8 val; +}; + +/* + * struct lp55xx_device_config + * @reset : Chip specific reset command + */ +struct lp55xx_device_config { + const struct lp55xx_reg reset; +}; + /* * struct lp55xx_chip * @cl : I2C communication for access registers * @pdata : Platform specific data * @lock : Lock for user-space interface * @num_leds : Number of registered LEDs + * @cfg : Device specific configuration data */ struct lp55xx_chip { struct i2c_client *cl; struct lp55xx_platform_data *pdata; struct mutex lock; /* lock for user-space interface */ int num_leds; + struct lp55xx_device_config *cfg; }; /* From e3a700d8aae190e09fb06abe0ddd2e172a682508 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:09:56 +0900 Subject: [PATCH 2055/4607] leds-lp55xx: use lp55xx common init function - detect LP5521/5523 chip detection functions are replaced with lp55xx common function, lp55xx_detect_device(). Chip dependent address and values are configurable in each driver. In init function, chip detection is executed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 31 ++++--------------------------- drivers/leds/leds-lp5523.c | 26 ++++---------------------- drivers/leds/leds-lp55xx-common.c | 29 +++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 2 ++ 4 files changed, 39 insertions(+), 49 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index e1f1dfcd1547..0e27f7eb5d09 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -328,26 +328,6 @@ static void lp5521_led_brightness_work(struct work_struct *work) mutex_unlock(&chip->lock); } -/* Detect the chip by setting its ENABLE register and reading it back. */ -static int lp5521_detect(struct i2c_client *client) -{ - int ret; - u8 buf; - - ret = lp5521_write(client, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT); - if (ret) - return ret; - /* enable takes 500us. 1 - 2 ms leaves some margin */ - usleep_range(1000, 2000); - ret = lp5521_read(client, LP5521_REG_ENABLE, &buf); - if (ret) - return ret; - if (buf != LP5521_ENABLE_DEFAULT) - return -ENODEV; - - return 0; -} - /* Set engine mode and create appropriate sysfs attributes, if required. */ static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode) { @@ -718,12 +698,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) struct i2c_client *client = chip->client; int ret; - ret = lp5521_detect(client); - if (ret) { - dev_err(&client->dev, "Chip not found\n"); - goto err; - } - ret = lp5521_configure(client); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); @@ -734,7 +708,6 @@ static int lp5521_init_device(struct lp5521_chip *chip) err_config: lp5521_deinit_device(chip); -err: return ret; } @@ -851,6 +824,10 @@ static struct lp55xx_device_config lp5521_cfg = { .addr = LP5521_REG_RESET, .val = LP5521_RESET, }, + .enable = { + .addr = LP5521_REG_ENABLE, + .val = LP5521_ENABLE_DEFAULT, + }, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 00547783db77..b3698e85d51f 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -181,23 +181,6 @@ static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) return 0; } -static int lp5523_detect(struct i2c_client *client) -{ - int ret; - u8 buf; - - ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); - if (ret) - return ret; - ret = lp5523_read(client, LP5523_REG_ENABLE, &buf); - if (ret) - return ret; - if (buf == 0x40) - return 0; - else - return -ENODEV; -} - static int lp5523_configure(struct i2c_client *client) { int ret = 0; @@ -907,10 +890,6 @@ static int lp5523_init_device(struct lp5523_chip *chip) struct i2c_client *client = chip->client; int ret; - ret = lp5523_detect(client); - if (ret) - goto err; - ret = lp5523_configure(client); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); @@ -921,7 +900,6 @@ static int lp5523_init_device(struct lp5523_chip *chip) err_config: lp5523_deinit_device(chip); -err: return ret; } @@ -941,6 +919,10 @@ static struct lp55xx_device_config lp5523_cfg = { .addr = LP5523_REG_RESET, .val = LP5523_RESET, }, + .enable = { + .addr = LP5523_REG_ENABLE, + .val = LP5523_ENABLE, + }, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index bbf2bbf03807..6fede0b96715 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -30,6 +30,29 @@ static void lp55xx_reset_device(struct lp55xx_chip *chip) lp55xx_write(chip, addr, val); } +static int lp55xx_detect_device(struct lp55xx_chip *chip) +{ + struct lp55xx_device_config *cfg = chip->cfg; + u8 addr = cfg->enable.addr; + u8 val = cfg->enable.val; + int ret; + + ret = lp55xx_write(chip, addr, val); + if (ret) + return ret; + + usleep_range(1000, 2000); + + ret = lp55xx_read(chip, addr, &val); + if (ret) + return ret; + + if (val != cfg->enable.val) + return -ENODEV; + + return 0; +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -103,6 +126,12 @@ int lp55xx_init_device(struct lp55xx_chip *chip) */ usleep_range(10000, 20000); + ret = lp55xx_detect_device(chip); + if (ret) { + dev_err(dev, "device detection err: %d\n", ret); + goto err; + } + err: return ret; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index a73ee0b9a0bd..81753012ba27 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -31,9 +31,11 @@ struct lp55xx_reg { /* * struct lp55xx_device_config * @reset : Chip specific reset command + * @enable : Chip specific enable command */ struct lp55xx_device_config { const struct lp55xx_reg reset; + const struct lp55xx_reg enable; }; /* From ffbdccdbbaee814963a09d25b1cc598cfe131366 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:57:36 +0900 Subject: [PATCH 2056/4607] leds-lp55xx: use lp55xx common init function - post int LP5521/5523 chip configuration is replaced with lp55xx common function, lp55xx_post_init_device(). Name change: lp5521/5523_configure() to lp5521/5523_post_init_device() These are called in init function. Register access function Argument type is changed from 'i2c_client' to 'lp55xx_chip'. Use exported R/W functions of lp55xx common driver. Temporary variables in lp5521/5523_init_device() These functions will be removed but temporary variables are needed for blocking build warnings - incompatible pointer. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 29 ++++++++++++++--------------- drivers/leds/leds-lp5523.c | 16 +++++++++------- drivers/leds/leds-lp55xx-common.c | 15 +++++++++++++++ drivers/leds/leds-lp55xx-common.h | 4 ++++ 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 0e27f7eb5d09..faab44900c23 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -238,11 +238,9 @@ static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) curr); } -static int lp5521_configure(struct i2c_client *client) +static int lp5521_post_init_device(struct lp55xx_chip *chip) { - struct lp5521_chip *chip = i2c_get_clientdata(client); int ret; - u8 cfg; u8 val; /* @@ -251,13 +249,13 @@ static int lp5521_configure(struct i2c_client *client) * otherwise further access to the R G B channels in the * LP5521_REG_ENABLE register will not have any effect - strange! */ - ret = lp5521_read(client, LP5521_REG_R_CURRENT, &val); + ret = lp55xx_read(chip, LP5521_REG_R_CURRENT, &val); if (ret) { - dev_err(&client->dev, "error in resetting chip\n"); + dev_err(&chip->cl->dev, "error in resetting chip\n"); return ret; } if (val != LP5521_REG_R_CURR_DEFAULT) { - dev_err(&client->dev, + dev_err(&chip->cl->dev, "unexpected data in register (expected 0x%x got 0x%x)\n", LP5521_REG_R_CURR_DEFAULT, val); ret = -EINVAL; @@ -266,22 +264,21 @@ static int lp5521_configure(struct i2c_client *client) usleep_range(10000, 20000); /* Set all PWMs to direct control mode */ - ret = lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); - cfg = chip->pdata->update_config ? + val = chip->pdata->update_config ? : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT); - ret = lp5521_write(client, LP5521_REG_CONFIG, cfg); + ret = lp55xx_write(chip, LP5521_REG_CONFIG, val); if (ret) return ret; /* Initialize all channels PWM to zero -> leds off */ - lp5521_write(client, LP5521_REG_R_PWM, 0); - lp5521_write(client, LP5521_REG_G_PWM, 0); - lp5521_write(client, LP5521_REG_B_PWM, 0); + lp55xx_write(chip, LP5521_REG_R_PWM, 0); + lp55xx_write(chip, LP5521_REG_G_PWM, 0); + lp55xx_write(chip, LP5521_REG_B_PWM, 0); /* Set engines are set to run state when OP_MODE enables engines */ - ret = lp5521_write(client, LP5521_REG_ENABLE, - LP5521_ENABLE_RUN_PROGRAM); + ret = lp55xx_write(chip, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); if (ret) return ret; @@ -696,9 +693,10 @@ static void lp5521_deinit_device(struct lp5521_chip *chip); static int lp5521_init_device(struct lp5521_chip *chip) { struct i2c_client *client = chip->client; + struct lp55xx_chip *temp; int ret; - ret = lp5521_configure(client); + ret = lp5521_post_init_device(temp); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); goto err_config; @@ -828,6 +826,7 @@ static struct lp55xx_device_config lp5521_cfg = { .addr = LP5521_REG_ENABLE, .val = LP5521_ENABLE_DEFAULT, }, + .post_init_device = lp5521_post_init_device, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index b3698e85d51f..110565b066e2 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -181,18 +181,18 @@ static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) return 0; } -static int lp5523_configure(struct i2c_client *client) +static int lp5523_post_init_device(struct lp55xx_chip *chip) { - int ret = 0; + int ret; - ret = lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE); + ret = lp55xx_write(chip, LP5523_REG_ENABLE, LP5523_ENABLE); if (ret) return ret; /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ usleep_range(1000, 2000); - ret = lp5523_write(client, LP5523_REG_CONFIG, + ret = lp55xx_write(chip, LP5523_REG_CONFIG, LP5523_AUTO_INC | LP5523_PWR_SAVE | LP5523_CP_AUTO | LP5523_AUTO_CLK | LP5523_PWM_PWR_SAVE); @@ -200,11 +200,11 @@ static int lp5523_configure(struct i2c_client *client) return ret; /* turn on all leds */ - ret = lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01); + ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_MSB, 0x01); if (ret) return ret; - return lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff); + return lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff); } static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode) @@ -888,9 +888,10 @@ static void lp5523_deinit_device(struct lp5523_chip *chip); static int lp5523_init_device(struct lp5523_chip *chip) { struct i2c_client *client = chip->client; + struct lp55xx_chip *temp; int ret; - ret = lp5523_configure(client); + ret = lp5523_post_init_device(temp); if (ret < 0) { dev_err(&client->dev, "error configuring chip\n"); goto err_config; @@ -923,6 +924,7 @@ static struct lp55xx_device_config lp5523_cfg = { .addr = LP5523_REG_ENABLE, .val = LP5523_ENABLE, }, + .post_init_device = lp5523_post_init_device, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 6fede0b96715..74beb363b787 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -53,6 +53,16 @@ static int lp55xx_detect_device(struct lp55xx_chip *chip) return 0; } +static int lp55xx_post_init_device(struct lp55xx_chip *chip) +{ + struct lp55xx_device_config *cfg = chip->cfg; + + if (!cfg->post_init_device) + return 0; + + return cfg->post_init_device(chip); +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -132,6 +142,11 @@ int lp55xx_init_device(struct lp55xx_chip *chip) goto err; } + /* chip specific initialization */ + ret = lp55xx_post_init_device(chip); + + return 0; + err: return ret; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 81753012ba27..ffedc7723d84 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -32,10 +32,14 @@ struct lp55xx_reg { * struct lp55xx_device_config * @reset : Chip specific reset command * @enable : Chip specific enable command + * @post_init_device : Chip specific initialization code */ struct lp55xx_device_config { const struct lp55xx_reg reset; const struct lp55xx_reg enable; + + /* define if the device has specific initialization process */ + int (*post_init_device) (struct lp55xx_chip *chip); }; /* From 22ebeb488b3dbbb64b81146b366551107ae34af8 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 18:58:35 +0900 Subject: [PATCH 2057/4607] leds-lp55xx: clean up init function lp5521/5523_init_device() are replaced with lp55xx common function, lp55xx_init_device(). Error handler in init_device: deinit function are matched with 'err_post_init' section in lp55xx_init_device(). Remove LP5523 engine intialization code: Engine functionality is not mandatory but optional. Moreover engine initialization is done internally with device reset command. Therefore, this code is unnecessary. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 22 +-------------- drivers/leds/leds-lp5523.c | 46 ++----------------------------- drivers/leds/leds-lp55xx-common.c | 9 ++++++ 3 files changed, 12 insertions(+), 65 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index faab44900c23..74dc208fb99f 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -689,26 +689,6 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static void lp5521_deinit_device(struct lp5521_chip *chip); -static int lp5521_init_device(struct lp5521_chip *chip) -{ - struct i2c_client *client = chip->client; - struct lp55xx_chip *temp; - int ret; - - ret = lp5521_post_init_device(temp); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto err_config; - } - - return 0; - -err_config: - lp5521_deinit_device(chip); - return ret; -} - static void lp5521_deinit_device(struct lp5521_chip *chip) { struct lp5521_platform_data *pdata = chip->pdata; @@ -860,7 +840,7 @@ static int lp5521_probe(struct i2c_client *client, i2c_set_clientdata(client, led); - ret = lp5521_init_device(old_chip); + ret = lp55xx_init_device(chip); if (ret) goto err_init; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 110565b066e2..80b7fb4a3ad6 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -773,18 +773,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) /*--------------------------------------------------------------*/ /* Probe, Attach, Remove */ /*--------------------------------------------------------------*/ -static int __init lp5523_init_engine(struct lp5523_engine *engine, int id) -{ - if (id < 1 || id > LP5523_ENGINES) - return -1; - engine->id = id; - engine->engine_mask = LP5523_ENG_MASK_BASE >> SHIFT_MASK(id); - engine->prog_page = id - 1; - engine->mux_page = id + 2; - - return 0; -} - static int lp5523_init_led(struct lp5523_led *led, struct device *dev, int chan, struct lp5523_platform_data *pdata, const char *chip_name) @@ -884,26 +872,6 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } -static void lp5523_deinit_device(struct lp5523_chip *chip); -static int lp5523_init_device(struct lp5523_chip *chip) -{ - struct i2c_client *client = chip->client; - struct lp55xx_chip *temp; - int ret; - - ret = lp5523_post_init_device(temp); - if (ret < 0) { - dev_err(&client->dev, "error configuring chip\n"); - goto err_config; - } - - return 0; - -err_config: - lp5523_deinit_device(chip); - return ret; -} - static void lp5523_deinit_device(struct lp5523_chip *chip) { struct lp5523_platform_data *pdata = chip->pdata; @@ -931,7 +899,7 @@ static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct lp5523_chip *old_chip = NULL; - int ret, i; + int ret; struct lp55xx_chip *chip; struct lp55xx_led *led; struct lp55xx_platform_data *pdata = client->dev.platform_data; @@ -958,21 +926,12 @@ static int lp5523_probe(struct i2c_client *client, i2c_set_clientdata(client, led); - ret = lp5523_init_device(old_chip); + ret = lp55xx_init_device(chip); if (ret) goto err_init; dev_info(&client->dev, "%s Programmable led chip found\n", id->name); - /* Initialize engines */ - for (i = 0; i < ARRAY_SIZE(old_chip->engines); i++) { - ret = lp5523_init_engine(&old_chip->engines[i], i + 1); - if (ret) { - dev_err(&client->dev, "error initializing engine\n"); - goto fail1; - } - } - ret = lp5523_register_leds(old_chip, id->name); if (ret) goto fail2; @@ -985,7 +944,6 @@ static int lp5523_probe(struct i2c_client *client, return ret; fail2: lp5523_unregister_leds(old_chip); -fail1: lp5523_deinit_device(old_chip); err_init: return ret; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 74beb363b787..c06745f160c3 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -144,9 +144,18 @@ int lp55xx_init_device(struct lp55xx_chip *chip) /* chip specific initialization */ ret = lp55xx_post_init_device(chip); + if (ret) { + dev_err(dev, "post init device err: %d\n", ret); + goto err_post_init; + } return 0; +err_post_init: + if (pdata->enable) + pdata->enable(0); + if (pdata->release_resources) + pdata->release_resources(); err: return ret; } From 6ce6176263393dd80b9a537c1e1462b8529f240b Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:03:02 +0900 Subject: [PATCH 2058/4607] leds-lp55xx: use lp55xx common deinit function Two separate de-init functions are merged into one common function. And it is used in err_post_init of lp55xx_init_device(). Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 16 ++++------------ drivers/leds/leds-lp5523.c | 16 ++++------------ drivers/leds/leds-lp55xx-common.c | 17 +++++++++++++---- drivers/leds/leds-lp55xx-common.h | 3 ++- 4 files changed, 23 insertions(+), 29 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 74dc208fb99f..dd4526e168fa 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -689,16 +689,6 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static void lp5521_deinit_device(struct lp5521_chip *chip) -{ - struct lp5521_platform_data *pdata = chip->pdata; - - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); -} - static int lp5521_init_led(struct lp5521_led *led, struct i2c_client *client, int chan, struct lp5521_platform_data *pdata) @@ -858,7 +848,7 @@ static int lp5521_probe(struct i2c_client *client, return ret; fail2: lp5521_unregister_leds(old_chip); - lp5521_deinit_device(old_chip); + lp55xx_deinit_device(chip); err_init: return ret; } @@ -866,13 +856,15 @@ err_init: static int lp5521_remove(struct i2c_client *client) { struct lp5521_chip *old_chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(client); + struct lp55xx_chip *chip = led->chip; lp5521_run_led_pattern(PATTERN_OFF, old_chip); lp5521_unregister_sysfs(client); lp5521_unregister_leds(old_chip); + lp55xx_deinit_device(chip); - lp5521_deinit_device(old_chip); return 0; } diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 80b7fb4a3ad6..3f506e3d4986 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -872,16 +872,6 @@ static void lp5523_unregister_leds(struct lp5523_chip *chip) } } -static void lp5523_deinit_device(struct lp5523_chip *chip) -{ - struct lp5523_platform_data *pdata = chip->pdata; - - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -944,7 +934,7 @@ static int lp5523_probe(struct i2c_client *client, return ret; fail2: lp5523_unregister_leds(old_chip); - lp5523_deinit_device(old_chip); + lp55xx_deinit_device(chip); err_init: return ret; } @@ -952,6 +942,8 @@ err_init: static int lp5523_remove(struct i2c_client *client) { struct lp5523_chip *old_chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(client); + struct lp55xx_chip *chip = led->chip; /* Disable engine mode */ lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); @@ -959,8 +951,8 @@ static int lp5523_remove(struct i2c_client *client) lp5523_unregister_sysfs(client); lp5523_unregister_leds(old_chip); + lp55xx_deinit_device(chip); - lp5523_deinit_device(old_chip); return 0; } diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index c06745f160c3..bcabf2cb949a 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -152,15 +152,24 @@ int lp55xx_init_device(struct lp55xx_chip *chip) return 0; err_post_init: - if (pdata->enable) - pdata->enable(0); - if (pdata->release_resources) - pdata->release_resources(); + lp55xx_deinit_device(chip); err: return ret; } EXPORT_SYMBOL_GPL(lp55xx_init_device); +void lp55xx_deinit_device(struct lp55xx_chip *chip) +{ + struct lp55xx_platform_data *pdata = chip->pdata; + + if (pdata->enable) + pdata->enable(0); + + if (pdata->release_resources) + pdata->release_resources(); +} +EXPORT_SYMBOL_GPL(lp55xx_deinit_device); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index ffedc7723d84..908b00a56b7e 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -84,7 +84,8 @@ extern int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val); extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val); -/* common device init functions */ +/* common device init/deinit functions */ extern int lp55xx_init_device(struct lp55xx_chip *chip); +extern void lp55xx_deinit_device(struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ From 9e9b3db1b2f725bacaf1b7e8708a0c78265bde97 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:06:27 +0900 Subject: [PATCH 2059/4607] leds-lp55xx: use lp55xx common led registration function LED class devices are registered in lp5521_register_leds() and lp5523_register_leds(). Two separate functions are merged into consolidated lp55xx function, lp55xx_register_leds(). Error handling fix: Unregistering LEDS are handled in lp55xx_register_leds() when LED registration failure occurs. So each driver error handler is changed to 'err_register_leds' Chip dependency: 'brightness_work_fn' and 'set_led_current' To make the structure abstract, both functions are configured in each driver. Those functions should be done by each driver because register control is chip-dependant work. lp55xx_init_led: skeleton Will be filled in next patch Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 43 ++---------------------- drivers/leds/leds-lp5523.c | 45 ++----------------------- drivers/leds/leds-lp55xx-common.c | 55 +++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 11 +++++++ 4 files changed, 72 insertions(+), 82 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index dd4526e168fa..dc58f4106d09 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -738,44 +738,6 @@ static int lp5521_init_led(struct lp5521_led *led, return 0; } -static int lp5521_register_leds(struct lp5521_chip *chip) -{ - struct lp5521_platform_data *pdata = chip->pdata; - struct i2c_client *client = chip->client; - int i; - int led; - int ret; - - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - ret = lp5521_init_led(&chip->leds[led], client, i, pdata); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - return ret; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set initial LED current */ - lp5521_set_led_current(chip, led, - chip->leds[led].led_current); - - INIT_WORK(&(chip->leds[led].brightness_work), - lp5521_led_brightness_work); - - led++; - } - - return 0; -} - static void lp5521_unregister_leds(struct lp5521_chip *chip) { int i; @@ -836,9 +798,9 @@ static int lp5521_probe(struct i2c_client *client, dev_info(&client->dev, "%s programmable led chip found\n", id->name); - ret = lp5521_register_leds(old_chip); + ret = lp55xx_register_leds(led, chip); if (ret) - goto fail2; + goto err_register_leds; ret = lp5521_register_sysfs(client); if (ret) { @@ -848,6 +810,7 @@ static int lp5521_probe(struct i2c_client *client, return ret; fail2: lp5521_unregister_leds(old_chip); +err_register_leds: lp55xx_deinit_device(chip); err_init: return ret; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 3f506e3d4986..41ac502ff82f 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -822,46 +822,6 @@ static int lp5523_init_led(struct lp5523_led *led, struct device *dev, return 0; } -static int lp5523_register_leds(struct lp5523_chip *chip, const char *name) -{ - struct lp5523_platform_data *pdata = chip->pdata; - struct i2c_client *client = chip->client; - int i; - int led; - int ret; - - /* Initialize leds */ - chip->num_channels = pdata->num_channels; - chip->num_leds = 0; - led = 0; - for (i = 0; i < pdata->num_channels; i++) { - /* Do not initialize channels that are not connected */ - if (pdata->led_config[i].led_current == 0) - continue; - - INIT_WORK(&chip->leds[led].brightness_work, - lp5523_led_brightness_work); - - ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata, - name); - if (ret) { - dev_err(&client->dev, "error initializing leds\n"); - return ret; - } - chip->num_leds++; - - chip->leds[led].id = led; - /* Set LED current */ - lp5523_write(client, - LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, - chip->leds[led].led_current); - - led++; - } - - return 0; -} - static void lp5523_unregister_leds(struct lp5523_chip *chip) { int i; @@ -922,9 +882,9 @@ static int lp5523_probe(struct i2c_client *client, dev_info(&client->dev, "%s Programmable led chip found\n", id->name); - ret = lp5523_register_leds(old_chip, id->name); + ret = lp55xx_register_leds(led, chip); if (ret) - goto fail2; + goto err_register_leds; ret = lp5523_register_sysfs(client); if (ret) { @@ -934,6 +894,7 @@ static int lp5523_probe(struct i2c_client *client, return ret; fail2: lp5523_unregister_leds(old_chip); +err_register_leds: lp55xx_deinit_device(chip); err_init: return ret; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index bcabf2cb949a..1fab1d1c4502 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -63,6 +63,12 @@ static int lp55xx_post_init_device(struct lp55xx_chip *chip) return cfg->post_init_device(chip); } +static int lp55xx_init_led(struct lp55xx_led *led, + struct lp55xx_chip *chip, int chan) +{ + return 0; +} + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -170,6 +176,55 @@ void lp55xx_deinit_device(struct lp55xx_chip *chip) } EXPORT_SYMBOL_GPL(lp55xx_deinit_device); +int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) +{ + struct lp55xx_platform_data *pdata = chip->pdata; + struct lp55xx_device_config *cfg = chip->cfg; + int num_channels = pdata->num_channels; + struct lp55xx_led *each; + u8 led_current; + int ret; + int i; + + if (!cfg->brightness_work_fn) { + dev_err(&chip->cl->dev, "empty brightness configuration\n"); + return -EINVAL; + } + + for (i = 0; i < num_channels; i++) { + + /* do not initialize channels that are not connected */ + if (pdata->led_config[i].led_current == 0) + continue; + + led_current = pdata->led_config[i].led_current; + each = led + i; + ret = lp55xx_init_led(each, chip, i); + if (ret) + goto err_init_led; + + INIT_WORK(&each->brightness_work, cfg->brightness_work_fn); + + chip->num_leds++; + each->chip = chip; + + /* setting led current at each channel */ + if (cfg->set_led_current) + cfg->set_led_current(each, led_current); + } + + return 0; + +err_init_led: + for (i = 0; i < chip->num_leds; i++) { + each = led + i; + led_classdev_unregister(&each->cdev); + flush_work(&each->brightness_work); + } + return ret; +} +EXPORT_SYMBOL_GPL(lp55xx_register_leds); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 908b00a56b7e..219780a2d4eb 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -33,6 +33,8 @@ struct lp55xx_reg { * @reset : Chip specific reset command * @enable : Chip specific enable command * @post_init_device : Chip specific initialization code + * @brightness_work_fn : Brightness work function + * @set_led_current : LED current set function */ struct lp55xx_device_config { const struct lp55xx_reg reset; @@ -40,6 +42,12 @@ struct lp55xx_device_config { /* define if the device has specific initialization process */ int (*post_init_device) (struct lp55xx_chip *chip); + + /* access brightness register */ + void (*brightness_work_fn)(struct work_struct *work); + + /* current setting function */ + void (*set_led_current) (struct lp55xx_led *led, u8 led_current); }; /* @@ -88,4 +96,7 @@ extern int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, extern int lp55xx_init_device(struct lp55xx_chip *chip); extern void lp55xx_deinit_device(struct lp55xx_chip *chip); +/* common LED class device functions */ +extern int lp55xx_register_leds(struct lp55xx_led *led, + struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ From 0e2023463a3c9412728cb2c36c79aca0bb731cc8 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:07:34 +0900 Subject: [PATCH 2060/4607] leds-lp55xx: use lp55xx_init_led() common function lp5521_init_led() and lp5523_init_led() are replaced with one common function, lp55xx_init_led(). Max channels is configurable, so it's used in lp55xx_init_led(). 'LP5523_LEDS' are changed to 'LP5523_MAX_LEDS'. lp55xx_set_brightness, lp55xx_led_attributes: skeleton Will be filled in next patches. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 50 +---------------------- drivers/leds/leds-lp5523.c | 62 ++++------------------------- drivers/leds/leds-lp55xx-common.c | 66 +++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 2 + 4 files changed, 76 insertions(+), 104 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index dc58f4106d09..bda03049fb3c 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -689,55 +689,6 @@ static void lp5521_unregister_sysfs(struct i2c_client *client) &lp5521_led_attribute_group); } -static int lp5521_init_led(struct lp5521_led *led, - struct i2c_client *client, - int chan, struct lp5521_platform_data *pdata) -{ - struct device *dev = &client->dev; - char name[32]; - int res; - - if (chan >= LP5521_MAX_LEDS) - return -EINVAL; - - if (pdata->led_config[chan].led_current == 0) - return 0; - - led->led_current = pdata->led_config[chan].led_current; - led->max_current = pdata->led_config[chan].max_current; - led->chan_nr = pdata->led_config[chan].chan_nr; - - if (led->chan_nr >= LP5521_MAX_LEDS) { - dev_err(dev, "Use channel numbers between 0 and %d\n", - LP5521_MAX_LEDS - 1); - return -EINVAL; - } - - led->cdev.brightness_set = lp5521_set_brightness; - if (pdata->led_config[chan].name) { - led->cdev.name = pdata->led_config[chan].name; - } else { - snprintf(name, sizeof(name), "%s:channel%d", - pdata->label ?: client->name, chan); - led->cdev.name = name; - } - - res = led_classdev_register(dev, &led->cdev); - if (res < 0) { - dev_err(dev, "couldn't register led on channel %d\n", chan); - return res; - } - - res = sysfs_create_group(&led->cdev.dev->kobj, - &lp5521_led_attribute_group); - if (res < 0) { - dev_err(dev, "couldn't register current attribute\n"); - led_classdev_unregister(&led->cdev); - return res; - } - return 0; -} - static void lp5521_unregister_leds(struct lp5521_chip *chip) { int i; @@ -758,6 +709,7 @@ static struct lp55xx_device_config lp5521_cfg = { .addr = LP5521_REG_ENABLE, .val = LP5521_ENABLE_DEFAULT, }, + .max_channel = LP5521_MAX_LEDS, .post_init_device = lp5521_post_init_device, }; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 41ac502ff82f..ca2f8134909f 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -94,7 +94,7 @@ #define LP5523_PROGRAM_PAGES 6 #define LP5523_ADC_SHORTCIRC_LIM 80 -#define LP5523_LEDS 9 +#define LP5523_MAX_LEDS 9 #define LP5523_ENGINES 3 #define LP5523_ENG_MASK_BASE 0x30 /* 00110000 */ @@ -136,7 +136,7 @@ struct lp5523_chip { struct mutex lock; /* Serialize control */ struct i2c_client *client; struct lp5523_engine engines[LP5523_ENGINES]; - struct lp5523_led leds[LP5523_LEDS]; + struct lp5523_led leds[LP5523_MAX_LEDS]; struct lp5523_platform_data *pdata; u8 num_channels; u8 num_leds; @@ -285,7 +285,7 @@ static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) int i; u16 tmp_mux = 0; - len = min_t(int, len, LP5523_LEDS); + len = min_t(int, len, LP5523_MAX_LEDS); for (i = 0; i < len; i++) { switch (buf[i]) { case '1': @@ -308,7 +308,7 @@ static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) static void lp5523_mux_to_array(u16 led_mux, char *array) { int i, pos = 0; - for (i = 0; i < LP5523_LEDS; i++) + for (i = 0; i < LP5523_MAX_LEDS; i++) pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i)); array[pos] = '\0'; @@ -324,7 +324,7 @@ static ssize_t show_engine_leds(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct lp5523_chip *chip = i2c_get_clientdata(client); - char mux[LP5523_LEDS + 1]; + char mux[LP5523_MAX_LEDS + 1]; lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux); @@ -417,7 +417,7 @@ static ssize_t lp5523_selftest(struct device *dev, vdd--; /* There may be some fluctuation in measurement */ - for (i = 0; i < LP5523_LEDS; i++) { + for (i = 0; i < LP5523_MAX_LEDS; i++) { /* Skip non-existing channels */ if (chip->pdata->led_config[i].led_current == 0) continue; @@ -773,55 +773,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) /*--------------------------------------------------------------*/ /* Probe, Attach, Remove */ /*--------------------------------------------------------------*/ -static int lp5523_init_led(struct lp5523_led *led, struct device *dev, - int chan, struct lp5523_platform_data *pdata, - const char *chip_name) -{ - char name[32]; - int res; - - if (chan >= LP5523_LEDS) - return -EINVAL; - - if (pdata->led_config[chan].led_current) { - led->led_current = pdata->led_config[chan].led_current; - led->max_current = pdata->led_config[chan].max_current; - led->chan_nr = pdata->led_config[chan].chan_nr; - - if (led->chan_nr >= LP5523_LEDS) { - dev_err(dev, "Use channel numbers between 0 and %d\n", - LP5523_LEDS - 1); - return -EINVAL; - } - - if (pdata->led_config[chan].name) { - led->cdev.name = pdata->led_config[chan].name; - } else { - snprintf(name, sizeof(name), "%s:channel%d", - pdata->label ? : chip_name, chan); - led->cdev.name = name; - } - - led->cdev.brightness_set = lp5523_set_brightness; - res = led_classdev_register(dev, &led->cdev); - if (res < 0) { - dev_err(dev, "couldn't register led on channel %d\n", - chan); - return res; - } - res = sysfs_create_group(&led->cdev.dev->kobj, - &lp5523_led_attribute_group); - if (res < 0) { - dev_err(dev, "couldn't register current attribute\n"); - led_classdev_unregister(&led->cdev); - return res; - } - } else { - led->led_current = 0; - } - return 0; -} - static void lp5523_unregister_leds(struct lp5523_chip *chip) { int i; @@ -842,6 +793,7 @@ static struct lp55xx_device_config lp5523_cfg = { .addr = LP5523_REG_ENABLE, .val = LP5523_ENABLE, }, + .max_channel = LP5523_MAX_LEDS, .post_init_device = lp5523_post_init_device, }; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 1fab1d1c4502..75ab1c3c03ed 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -63,9 +63,75 @@ static int lp55xx_post_init_device(struct lp55xx_chip *chip) return cfg->post_init_device(chip); } +static struct attribute *lp55xx_led_attributes[] = { + NULL, +}; + +static struct attribute_group lp55xx_led_attr_group = { + .attrs = lp55xx_led_attributes +}; + +static void lp55xx_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ +} + static int lp55xx_init_led(struct lp55xx_led *led, struct lp55xx_chip *chip, int chan) { + struct lp55xx_platform_data *pdata = chip->pdata; + struct lp55xx_device_config *cfg = chip->cfg; + struct device *dev = &chip->cl->dev; + char name[32]; + int ret; + int max_channel = cfg->max_channel; + + if (chan >= max_channel) { + dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel); + return -EINVAL; + } + + if (pdata->led_config[chan].led_current == 0) + return 0; + + led->led_current = pdata->led_config[chan].led_current; + led->max_current = pdata->led_config[chan].max_current; + led->chan_nr = pdata->led_config[chan].chan_nr; + + if (led->chan_nr >= max_channel) { + dev_err(dev, "Use channel numbers between 0 and %d\n", + max_channel - 1); + return -EINVAL; + } + + led->cdev.brightness_set = lp55xx_set_brightness; + + if (pdata->led_config[chan].name) { + led->cdev.name = pdata->led_config[chan].name; + } else { + snprintf(name, sizeof(name), "%s:channel%d", + pdata->label ? : chip->cl->name, chan); + led->cdev.name = name; + } + + /* + * register led class device for each channel and + * add device attributes + */ + + ret = led_classdev_register(dev, &led->cdev); + if (ret) { + dev_err(dev, "led register err: %d\n", ret); + return ret; + } + + ret = sysfs_create_group(&led->cdev.dev->kobj, &lp55xx_led_attr_group); + if (ret) { + dev_err(dev, "led sysfs err: %d\n", ret); + led_classdev_unregister(&led->cdev); + return ret; + } + return 0; } diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 219780a2d4eb..70d2bdf54b8e 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -32,6 +32,7 @@ struct lp55xx_reg { * struct lp55xx_device_config * @reset : Chip specific reset command * @enable : Chip specific enable command + * @max_channel : Maximum number of channels * @post_init_device : Chip specific initialization code * @brightness_work_fn : Brightness work function * @set_led_current : LED current set function @@ -39,6 +40,7 @@ struct lp55xx_reg { struct lp55xx_device_config { const struct lp55xx_reg reset; const struct lp55xx_reg enable; + const int max_channel; /* define if the device has specific initialization process */ int (*post_init_device) (struct lp55xx_chip *chip); From a6e4679a09a0a2bcfa63074272fc9fb2a40f11ad Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:08:40 +0900 Subject: [PATCH 2061/4607] leds-lp55xx: use lp55xx_set_brightness() lp5521_set_brightness() and lp5523_set_brightness() are replaced with common function, lp55xx_set_brightness(). This function is invoked when the brightness of each LED channel is updated. LP5521 and LP5523 have different register address for the brightness control, so this work is done by chip specific brightness_work_fn(). lp5521/5523_led_brightness_work(): use lp55xx_led and lp55xx_chip data structure. use lp55xx write function. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 19 ++++--------------- drivers/leds/leds-lp5523.c | 23 ++++------------------- drivers/leds/leds-lp55xx-common.c | 9 +++++++++ 3 files changed, 17 insertions(+), 34 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index bda03049fb3c..6efbb7ec0e2d 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -151,8 +151,6 @@ static inline struct lp5521_chip *led_to_lp5521(struct lp5521_led *led) leds[led->id]); } -static void lp5521_led_brightness_work(struct work_struct *work); - static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -303,24 +301,14 @@ static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) return 0; } -static void lp5521_set_brightness(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lp5521_led *led = cdev_to_led(cdev); - led->brightness = (u8)brightness; - schedule_work(&led->brightness_work); -} - static void lp5521_led_brightness_work(struct work_struct *work) { - struct lp5521_led *led = container_of(work, - struct lp5521_led, + struct lp55xx_led *led = container_of(work, struct lp55xx_led, brightness_work); - struct lp5521_chip *chip = led_to_lp5521(led); - struct i2c_client *client = chip->client; + struct lp55xx_chip *chip = led->chip; mutex_lock(&chip->lock); - lp5521_write(client, LP5521_REG_LED_PWM_BASE + led->chan_nr, + lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr, led->brightness); mutex_unlock(&chip->lock); } @@ -711,6 +699,7 @@ static struct lp55xx_device_config lp5521_cfg = { }, .max_channel = LP5521_MAX_LEDS, .post_init_device = lp5521_post_init_device, + .brightness_work_fn = lp5521_led_brightness_work, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index ca2f8134909f..43db2429616b 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -163,8 +163,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode); static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode); static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern); -static void lp5523_led_brightness_work(struct work_struct *work); - static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -468,29 +466,15 @@ release_lock: return pos; } -static void lp5523_set_brightness(struct led_classdev *cdev, - enum led_brightness brightness) -{ - struct lp5523_led *led = cdev_to_led(cdev); - - led->brightness = (u8)brightness; - - schedule_work(&led->brightness_work); -} - static void lp5523_led_brightness_work(struct work_struct *work) { - struct lp5523_led *led = container_of(work, - struct lp5523_led, + struct lp55xx_led *led = container_of(work, struct lp55xx_led, brightness_work); - struct lp5523_chip *chip = led_to_lp5523(led); - struct i2c_client *client = chip->client; + struct lp55xx_chip *chip = led->chip; mutex_lock(&chip->lock); - - lp5523_write(client, LP5523_REG_LED_PWM_BASE + led->chan_nr, + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr, led->brightness); - mutex_unlock(&chip->lock); } @@ -795,6 +779,7 @@ static struct lp55xx_device_config lp5523_cfg = { }, .max_channel = LP5523_MAX_LEDS, .post_init_device = lp5523_post_init_device, + .brightness_work_fn = lp5523_led_brightness_work, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 75ab1c3c03ed..8244d78447f4 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -20,6 +20,11 @@ #include "leds-lp55xx-common.h" +static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev) +{ + return container_of(cdev, struct lp55xx_led, cdev); +} + static void lp55xx_reset_device(struct lp55xx_chip *chip) { struct lp55xx_device_config *cfg = chip->cfg; @@ -74,6 +79,10 @@ static struct attribute_group lp55xx_led_attr_group = { static void lp55xx_set_brightness(struct led_classdev *cdev, enum led_brightness brightness) { + struct lp55xx_led *led = cdev_to_lp55xx_led(cdev); + + led->brightness = (u8)brightness; + schedule_work(&led->brightness_work); } static int lp55xx_init_led(struct lp55xx_led *led, From a96bfa135ddc8045166fc6311ce4d21bfcb8d13d Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:09:32 +0900 Subject: [PATCH 2062/4607] leds-lp55xx: provide common LED current setting LED current is configurable via the sysfs. Max current is a read-only attribute. These attributes code can be shared in lp55xx common driver. Device attributes: 'led_current' and 'max_current' move to lp55xx common driver Replaced functions: show_max_current() => lp55xx_show_max_current() show_current() => lp55xx_show_current() store_current() => lp55xx_store_current() LED setting function: set_led_current() Current registers are device specific, so configurable function is added in each driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 74 ++++--------------------------- drivers/leds/leds-lp5523.c | 69 ++++------------------------ drivers/leds/leds-lp55xx-common.c | 53 ++++++++++++++++++++++ 3 files changed, 69 insertions(+), 127 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 6efbb7ec0e2d..7133af824b5e 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -134,6 +134,13 @@ static inline void lp5521_wait_enable_done(void) usleep_range(500, 600); } +static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) +{ + led->led_current = led_current; + lp55xx_write(led->chip, LP5521_REG_LED_CURRENT_BASE + led->chan_nr, + led_current); +} + static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev) { return container_of(cdev, struct lp5521_led, cdev); @@ -229,13 +236,6 @@ static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) return lp5521_write(client, LP5521_REG_OP_MODE, mode); } -static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr) -{ - return lp5521_write(chip->client, - LP5521_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr, - curr); -} - static int lp5521_post_init_device(struct lp55xx_chip *chip) { int ret; @@ -462,54 +462,6 @@ store_mode(1) store_mode(2) store_mode(3) -static ssize_t show_max_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5521_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->max_current); -} - -static ssize_t show_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5521_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->led_current); -} - -static ssize_t store_current(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5521_led *led = cdev_to_led(led_cdev); - struct lp5521_chip *chip = led_to_lp5521(led); - ssize_t ret; - unsigned long curr; - - if (kstrtoul(buf, 0, &curr)) - return -EINVAL; - - if (curr > led->max_current) - return -EINVAL; - - mutex_lock(&chip->lock); - ret = lp5521_set_led_current(chip, led->id, curr); - mutex_unlock(&chip->lock); - - if (ret < 0) - return ret; - - led->led_current = (u8)curr; - - return len; -} - static ssize_t lp5521_selftest(struct device *dev, struct device_attribute *attr, char *buf) @@ -615,18 +567,7 @@ static ssize_t store_led_pattern(struct device *dev, return len; } -/* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); -static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); - -static struct attribute *lp5521_led_attributes[] = { - &dev_attr_led_current.attr, - &dev_attr_max_current.attr, - NULL, -}; - static struct attribute_group lp5521_led_attribute_group = { - .attrs = lp5521_led_attributes }; /* device attributes */ @@ -700,6 +641,7 @@ static struct lp55xx_device_config lp5521_cfg = { .max_channel = LP5521_MAX_LEDS, .post_init_device = lp5521_post_init_device, .brightness_work_fn = lp5521_led_brightness_work, + .set_led_current = lp5521_set_led_current, }; static int lp5521_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 43db2429616b..dfb335420568 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -142,6 +142,13 @@ struct lp5523_chip { u8 num_leds; }; +static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) +{ + led->led_current = led_current; + lp55xx_write(led->chip, LP5523_REG_LED_CURRENT_BASE + led->chan_nr, + led_current); +} + static inline struct lp5523_led *cdev_to_led(struct led_classdev *cdev) { return container_of(cdev, struct lp5523_led, cdev); @@ -602,68 +609,7 @@ store_mode(1) store_mode(2) store_mode(3) -static ssize_t show_max_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5523_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->max_current); -} - -static ssize_t show_current(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5523_led *led = cdev_to_led(led_cdev); - - return sprintf(buf, "%d\n", led->led_current); -} - -static ssize_t store_current(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct lp5523_led *led = cdev_to_led(led_cdev); - struct lp5523_chip *chip = led_to_lp5523(led); - ssize_t ret; - unsigned long curr; - - if (kstrtoul(buf, 0, &curr)) - return -EINVAL; - - if (curr > led->max_current) - return -EINVAL; - - mutex_lock(&chip->lock); - ret = lp5523_write(chip->client, - LP5523_REG_LED_CURRENT_BASE + led->chan_nr, - (u8)curr); - mutex_unlock(&chip->lock); - - if (ret < 0) - return ret; - - led->led_current = (u8)curr; - - return len; -} - -/* led class device attributes */ -static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); -static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); - -static struct attribute *lp5523_led_attributes[] = { - &dev_attr_led_current.attr, - &dev_attr_max_current.attr, - NULL, -}; - static struct attribute_group lp5523_led_attribute_group = { - .attrs = lp5523_led_attributes }; /* device attributes */ @@ -780,6 +726,7 @@ static struct lp55xx_device_config lp5523_cfg = { .max_channel = LP5523_MAX_LEDS, .post_init_device = lp5523_post_init_device, .brightness_work_fn = lp5523_led_brightness_work, + .set_led_current = lp5523_set_led_current, }; static int lp5523_probe(struct i2c_client *client, diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 8244d78447f4..6b3d03709f5f 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -25,6 +25,11 @@ static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev) return container_of(cdev, struct lp55xx_led, cdev); } +static struct lp55xx_led *dev_to_lp55xx_led(struct device *dev) +{ + return cdev_to_lp55xx_led(dev_get_drvdata(dev)); +} + static void lp55xx_reset_device(struct lp55xx_chip *chip) { struct lp55xx_device_config *cfg = chip->cfg; @@ -68,7 +73,55 @@ static int lp55xx_post_init_device(struct lp55xx_chip *chip) return cfg->post_init_device(chip); } +static ssize_t lp55xx_show_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lp55xx_led *led = dev_to_lp55xx_led(dev); + + return sprintf(buf, "%d\n", led->led_current); +} + +static ssize_t lp55xx_store_current(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lp55xx_led *led = dev_to_lp55xx_led(dev); + struct lp55xx_chip *chip = led->chip; + unsigned long curr; + + if (kstrtoul(buf, 0, &curr)) + return -EINVAL; + + if (curr > led->max_current) + return -EINVAL; + + if (!chip->cfg->set_led_current) + return len; + + mutex_lock(&chip->lock); + chip->cfg->set_led_current(led, (u8)curr); + mutex_unlock(&chip->lock); + + return len; +} + +static ssize_t lp55xx_show_max_current(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lp55xx_led *led = dev_to_lp55xx_led(dev); + + return sprintf(buf, "%d\n", led->max_current); +} + +static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, lp55xx_show_current, + lp55xx_store_current); +static DEVICE_ATTR(max_current, S_IRUGO , lp55xx_show_max_current, NULL); + static struct attribute *lp55xx_led_attributes[] = { + &dev_attr_led_current.attr, + &dev_attr_max_current.attr, NULL, }; From c3a68ebfcd22abc186f2328149732c801449b297 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:11:18 +0900 Subject: [PATCH 2063/4607] leds-lp55xx: use lp55xx_unregister_leds() To unregister led class devices and sysfs attributes, LP5521 and LP5523 have each driver function. This patch makes both drivers simple using common driver function, lp55xx_unregister_leds(). And some unused variables are removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 24 ++---------------------- drivers/leds/leds-lp5523.c | 25 ++----------------------- drivers/leds/leds-lp55xx-common.c | 16 ++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 2 ++ 4 files changed, 22 insertions(+), 45 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 7133af824b5e..cec252eae716 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -567,9 +567,6 @@ static ssize_t store_led_pattern(struct device *dev, return len; } -static struct attribute_group lp5521_led_attribute_group = { -}; - /* device attributes */ static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); @@ -607,25 +604,9 @@ static int lp5521_register_sysfs(struct i2c_client *client) static void lp5521_unregister_sysfs(struct i2c_client *client) { - struct lp5521_chip *chip = i2c_get_clientdata(client); struct device *dev = &client->dev; - int i; sysfs_remove_group(&dev->kobj, &lp5521_group); - - for (i = 0; i < chip->num_leds; i++) - sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, - &lp5521_led_attribute_group); -} - -static void lp5521_unregister_leds(struct lp5521_chip *chip) -{ - int i; - - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - cancel_work_sync(&chip->leds[i].brightness_work); - } } /* Chip specific configurations */ @@ -647,7 +628,6 @@ static struct lp55xx_device_config lp5521_cfg = { static int lp5521_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5521_chip *old_chip = NULL; int ret; struct lp55xx_chip *chip; struct lp55xx_led *led; @@ -692,7 +672,7 @@ static int lp5521_probe(struct i2c_client *client, } return ret; fail2: - lp5521_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); err_init: @@ -708,7 +688,7 @@ static int lp5521_remove(struct i2c_client *client) lp5521_run_led_pattern(PATTERN_OFF, old_chip); lp5521_unregister_sysfs(client); - lp5521_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index dfb335420568..70630156eff9 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -609,9 +609,6 @@ store_mode(1) store_mode(2) store_mode(3) -static struct attribute_group lp5523_led_attribute_group = { -}; - /* device attributes */ static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, show_engine1_mode, store_engine1_mode); @@ -662,15 +659,9 @@ static int lp5523_register_sysfs(struct i2c_client *client) static void lp5523_unregister_sysfs(struct i2c_client *client) { - struct lp5523_chip *chip = i2c_get_clientdata(client); struct device *dev = &client->dev; - int i; sysfs_remove_group(&dev->kobj, &lp5523_group); - - for (i = 0; i < chip->num_leds; i++) - sysfs_remove_group(&chip->leds[i].cdev.dev->kobj, - &lp5523_led_attribute_group); } /*--------------------------------------------------------------*/ @@ -703,16 +694,6 @@ static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) /*--------------------------------------------------------------*/ /* Probe, Attach, Remove */ /*--------------------------------------------------------------*/ -static void lp5523_unregister_leds(struct lp5523_chip *chip) -{ - int i; - - for (i = 0; i < chip->num_leds; i++) { - led_classdev_unregister(&chip->leds[i].cdev); - flush_work(&chip->leds[i].brightness_work); - } -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -732,7 +713,6 @@ static struct lp55xx_device_config lp5523_cfg = { static int lp5523_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct lp5523_chip *old_chip = NULL; int ret; struct lp55xx_chip *chip; struct lp55xx_led *led; @@ -777,7 +757,7 @@ static int lp5523_probe(struct i2c_client *client, } return ret; fail2: - lp5523_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); err_init: @@ -786,7 +766,6 @@ err_init: static int lp5523_remove(struct i2c_client *client) { - struct lp5523_chip *old_chip = i2c_get_clientdata(client); struct lp55xx_led *led = i2c_get_clientdata(client); struct lp55xx_chip *chip = led->chip; @@ -795,7 +774,7 @@ static int lp5523_remove(struct i2c_client *client) lp5523_unregister_sysfs(client); - lp5523_unregister_leds(old_chip); + lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); return 0; diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 6b3d03709f5f..dcd64f5285e8 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -353,6 +353,22 @@ err_init_led: } EXPORT_SYMBOL_GPL(lp55xx_register_leds); +void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) +{ + int i; + struct lp55xx_led *each; + struct kobject *kobj; + + for (i = 0; i < chip->num_leds; i++) { + each = led + i; + kobj = &led->cdev.dev->kobj; + sysfs_remove_group(kobj, &lp55xx_led_attr_group); + led_classdev_unregister(&each->cdev); + flush_work(&each->brightness_work); + } +} +EXPORT_SYMBOL_GPL(lp55xx_unregister_leds); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 70d2bdf54b8e..32d96828cbc6 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -101,4 +101,6 @@ extern void lp55xx_deinit_device(struct lp55xx_chip *chip); /* common LED class device functions */ extern int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip); +extern void lp55xx_unregister_leds(struct lp55xx_led *led, + struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ From d9067d28461cb2e817cacb84c727959cbd57d247 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:12:47 +0900 Subject: [PATCH 2064/4607] leds-lp55xx: fix error condition in lp55xx_register_leds() Use lp55xx_unregister_leds() rather than duplicate code. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index dcd64f5285e8..cd19027895e5 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -344,11 +344,7 @@ int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) return 0; err_init_led: - for (i = 0; i < chip->num_leds; i++) { - each = led + i; - led_classdev_unregister(&each->cdev); - flush_work(&each->brightness_work); - } + lp55xx_unregister_leds(led, chip); return ret; } EXPORT_SYMBOL_GPL(lp55xx_register_leds); From b3b6f8119d752c969c6394314dc7ab80e6611111 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:15:27 +0900 Subject: [PATCH 2065/4607] leds-lp55xx: add new lp55xx_register_sysfs() for the firmware interface LP55xx family chips have internal program memory which run various patterns. Using this memory, LEDs continue on blinking/dimming without continuous I2C commands. That means the I2C HOST can be entered into sleep once the memory is updated. An application can get hex data from a file and write them into the program memory through the I2C. This is general firwmare interface. This patch is the initial step for adding the firmware interface. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 16 ++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index cd19027895e5..98407ca45e4f 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -197,6 +197,14 @@ static int lp55xx_init_led(struct lp55xx_led *led, return 0; } +static struct attribute *lp55xx_engine_attributes[] = { + NULL, +}; + +static const struct attribute_group lp55xx_engine_attr_group = { + .attrs = lp55xx_engine_attributes, +}; + int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val) { return i2c_smbus_write_byte_data(chip->cl, reg, val); @@ -365,6 +373,14 @@ void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) } EXPORT_SYMBOL_GPL(lp55xx_unregister_leds); +int lp55xx_register_sysfs(struct lp55xx_chip *chip) +{ + struct device *dev = &chip->cl->dev; + + return sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group); +} +EXPORT_SYMBOL_GPL(lp55xx_register_sysfs); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 32d96828cbc6..d0be837643f0 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -103,4 +103,8 @@ extern int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip); extern void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip); + +/* common device attributes functions */ +extern int lp55xx_register_sysfs(struct lp55xx_chip *chip); + #endif /* _LEDS_LP55XX_COMMON_H */ From 10c06d178df11b0b2b746321a80ea14241997127 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:17:20 +0900 Subject: [PATCH 2066/4607] leds-lp55xx: support firmware interface This patch provides additional device attributes which enable loading the firmware. ('select_engine' and 'run_engine') To run a LED pattern, two parts of driver should be enabled. Common features : lp55xx-common =============================== Firmware interface for loading LED patterns Chip specific features : leds-lp5521, leds-lp5523 ================================================= Register addresses for loading firmware data Register addresses for running selected engine Pattern programming sequence ============================ LP55xx chips have three program engines. To load and run a LED pattern, the programming sequence is as follows. (1) Select an engine number (1/2/3) (2) Set engine mode to load (3) Write pattern data into selected area (4) Set engine mode to run This sequence is almost same as the firmware interface. (1) Select an engine number : 'select_engine' dev attribute (2) Mode change to load : 'loading' of firmware class (3) Write pattern data into selected area : 'data' of firmware class (4) Mode change to run : 'run_engine' dev attribute (1) and (4) are device specific features which provide callback functions (2) and (3) are common features. For example, echo 1 or 2 or 3 > /sys/bus/i2c/devices/xxxx/select_engine echo 1 > /sys/class/firmware/lp5521/loading echo "4000600040FF6000" > /sys/class/firmware/lp5521/data echo 0 > /sys/class/firmware/lp5521/loading echo 1 > /sys/bus/i2c/devices/xxxx/run_engine As soon as 'loading' is set to 0, registered callback is called. Inside the callback, the selected engine is loaded and memory is updated. To run programmed pattern, 'run_engine' attribute should be enabled. Device specific data structure ============================== o Firmware callback load selected engine and update program memory o Run engine change the engine mode o 'engine_idx' and firmware data, 'fw' Those are used in the driver internally with callback functions Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 1 + drivers/leds/leds-lp55xx-common.c | 117 ++++++++++++++++++++++++++++++ drivers/leds/leds-lp55xx-common.h | 19 +++++ 3 files changed, 137 insertions(+) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 3d7822b3498f..fc680a9ed841 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -196,6 +196,7 @@ config LEDS_LP3944 config LEDS_LP55XX_COMMON tristate "Common Driver for TI/National LP5521 and LP5523/55231" depends on LEDS_LP5521 || LEDS_LP5523 + select FW_LOADER help This option supports common operations for LP5521 and LP5523/55231 devices. diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 98407ca45e4f..578902ab604f 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -197,7 +198,123 @@ static int lp55xx_init_led(struct lp55xx_led *led, return 0; } +static void lp55xx_firmware_loaded(const struct firmware *fw, void *context) +{ + struct lp55xx_chip *chip = context; + struct device *dev = &chip->cl->dev; + + if (!fw) { + dev_err(dev, "firmware request failed\n"); + goto out; + } + + /* handling firmware data is chip dependent */ + mutex_lock(&chip->lock); + + chip->fw = fw; + if (chip->cfg->firmware_cb) + chip->cfg->firmware_cb(chip); + + mutex_unlock(&chip->lock); + +out: + /* firmware should be released for other channel use */ + release_firmware(chip->fw); +} + +static int lp55xx_request_firmware(struct lp55xx_chip *chip) +{ + const char *name = chip->cl->name; + struct device *dev = &chip->cl->dev; + + return request_firmware_nowait(THIS_MODULE, true, name, dev, + GFP_KERNEL, chip, lp55xx_firmware_loaded); +} + +static ssize_t lp55xx_show_engine_select(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + + return sprintf(buf, "%d\n", chip->engine_idx); +} + +static ssize_t lp55xx_store_engine_select(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + unsigned long val; + int ret; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + /* select the engine to be run */ + + switch (val) { + case LP55XX_ENGINE_1: + case LP55XX_ENGINE_2: + case LP55XX_ENGINE_3: + mutex_lock(&chip->lock); + chip->engine_idx = val; + ret = lp55xx_request_firmware(chip); + mutex_unlock(&chip->lock); + break; + default: + dev_err(dev, "%lu: invalid engine index. (1, 2, 3)\n", val); + return -EINVAL; + } + + if (ret) { + dev_err(dev, "request firmware err: %d\n", ret); + return ret; + } + + return len; +} + +static inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start) +{ + if (chip->cfg->run_engine) + chip->cfg->run_engine(chip, start); +} + +static ssize_t lp55xx_store_engine_run(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + /* run or stop the selected engine */ + + if (val <= 0) { + lp55xx_run_engine(chip, false); + return len; + } + + mutex_lock(&chip->lock); + lp55xx_run_engine(chip, true); + mutex_unlock(&chip->lock); + + return len; +} + +static DEVICE_ATTR(select_engine, S_IRUGO | S_IWUSR, + lp55xx_show_engine_select, lp55xx_store_engine_select); +static DEVICE_ATTR(run_engine, S_IWUSR, NULL, lp55xx_store_engine_run); + static struct attribute *lp55xx_engine_attributes[] = { + &dev_attr_select_engine.attr, + &dev_attr_run_engine.attr, NULL, }; diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index d0be837643f0..8473abf9830c 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -15,6 +15,13 @@ #ifndef _LEDS_LP55XX_COMMON_H #define _LEDS_LP55XX_COMMON_H +enum lp55xx_engine_index { + LP55XX_ENGINE_INVALID, + LP55XX_ENGINE_1, + LP55XX_ENGINE_2, + LP55XX_ENGINE_3, +}; + struct lp55xx_led; struct lp55xx_chip; @@ -36,6 +43,8 @@ struct lp55xx_reg { * @post_init_device : Chip specific initialization code * @brightness_work_fn : Brightness work function * @set_led_current : LED current set function + * @firmware_cb : Call function when the firmware is loaded + * @run_engine : Run internal engine for pattern */ struct lp55xx_device_config { const struct lp55xx_reg reset; @@ -50,6 +59,12 @@ struct lp55xx_device_config { /* current setting function */ void (*set_led_current) (struct lp55xx_led *led, u8 led_current); + + /* access program memory when the firmware is loaded */ + void (*firmware_cb)(struct lp55xx_chip *chip); + + /* used for running firmware LED patterns */ + void (*run_engine) (struct lp55xx_chip *chip, bool start); }; /* @@ -59,6 +74,8 @@ struct lp55xx_device_config { * @lock : Lock for user-space interface * @num_leds : Number of registered LEDs * @cfg : Device specific configuration data + * @engine_idx : Selected engine number + * @fw : Firmware data for running a LED pattern */ struct lp55xx_chip { struct i2c_client *cl; @@ -66,6 +83,8 @@ struct lp55xx_chip { struct mutex lock; /* lock for user-space interface */ int num_leds; struct lp55xx_device_config *cfg; + enum lp55xx_engine_index engine_idx; + const struct firmware *fw; }; /* From 9ce7cb170f97f83a78dc948bf7d25690f15e1328 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:18:10 +0900 Subject: [PATCH 2067/4607] leds-lp5521: use generic firmware interface LP55xx common driver provides generic firmware interface for running a LED pattern. LP5521 and LP5523 have many device attributes for running patterns. This patch cleans up those complex code. Removed device attributes: engine1_mode engine2_mode engine3_mode engine1_load engine2_load engine3_load led_pattern All device attributes and functions are replaced with two callback functions, 'firmware_cb' and 'run_engine'. New engine functions: lp5521_load/stop/run_engine(), lp5521_update_program_memory() and lp5521_wait_opmode_done() Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 412 +++++++++++++++---------------------- 1 file changed, 162 insertions(+), 250 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index cec252eae716..89371e065c13 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "leds-lp55xx-common.h" @@ -101,12 +102,28 @@ /* Reset register value */ #define LP5521_RESET 0xFF -struct lp5521_engine { - int id; - u8 mode; - u8 prog_page; - u8 engine_mask; -}; +/* Program Memory Operations */ +#define LP5521_MODE_R_M 0x30 /* Operation Mode Register */ +#define LP5521_MODE_G_M 0x0C +#define LP5521_MODE_B_M 0x03 +#define LP5521_LOAD_R 0x10 +#define LP5521_LOAD_G 0x04 +#define LP5521_LOAD_B 0x01 + +#define LP5521_R_IS_LOADING(mode) \ + ((mode & LP5521_MODE_R_M) == LP5521_LOAD_R) +#define LP5521_G_IS_LOADING(mode) \ + ((mode & LP5521_MODE_G_M) == LP5521_LOAD_G) +#define LP5521_B_IS_LOADING(mode) \ + ((mode & LP5521_MODE_B_M) == LP5521_LOAD_B) + +#define LP5521_EXEC_R_M 0x30 /* Enable Register */ +#define LP5521_EXEC_G_M 0x0C +#define LP5521_EXEC_B_M 0x03 +#define LP5521_EXEC_M 0x3F +#define LP5521_RUN_R 0x20 +#define LP5521_RUN_G 0x08 +#define LP5521_RUN_B 0x02 struct lp5521_led { int id; @@ -122,12 +139,17 @@ struct lp5521_chip { struct lp5521_platform_data *pdata; struct mutex lock; /* Serialize control */ struct i2c_client *client; - struct lp5521_engine engines[LP5521_MAX_ENGINES]; struct lp5521_led leds[LP5521_MAX_LEDS]; u8 num_channels; u8 num_leds; }; +static inline void lp5521_wait_opmode_done(void) +{ + /* operation mode change needs to be longer than 153 us */ + usleep_range(200, 300); +} + static inline void lp5521_wait_enable_done(void) { /* it takes more 488 us to update ENABLE register */ @@ -141,23 +163,6 @@ static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static inline struct lp5521_led *cdev_to_led(struct led_classdev *cdev) -{ - return container_of(cdev, struct lp5521_led, cdev); -} - -static inline struct lp5521_chip *engine_to_lp5521(struct lp5521_engine *engine) -{ - return container_of(engine, struct lp5521_chip, - engines[engine->id - 1]); -} - -static inline struct lp5521_chip *led_to_lp5521(struct lp5521_led *led) -{ - return container_of(led, struct lp5521_chip, - leds[led->id]); -} - static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -175,65 +180,153 @@ static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf) return 0; } -static int lp5521_set_engine_mode(struct lp5521_engine *engine, u8 mode) +static void lp5521_load_engine(struct lp55xx_chip *chip) { - struct lp5521_chip *chip = engine_to_lp5521(engine); - struct i2c_client *client = chip->client; - int ret; - u8 engine_state; + enum lp55xx_engine_index idx = chip->engine_idx; + u8 mask[] = { + [LP55XX_ENGINE_1] = LP5521_MODE_R_M, + [LP55XX_ENGINE_2] = LP5521_MODE_G_M, + [LP55XX_ENGINE_3] = LP5521_MODE_B_M, + }; - /* Only transition between RUN and DIRECT mode are handled here */ - if (mode == LP5521_CMD_LOAD) - return 0; + u8 val[] = { + [LP55XX_ENGINE_1] = LP5521_LOAD_R, + [LP55XX_ENGINE_2] = LP5521_LOAD_G, + [LP55XX_ENGINE_3] = LP5521_LOAD_B, + }; - if (mode == LP5521_CMD_DISABLED) - mode = LP5521_CMD_DIRECT; + lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], val[idx]); - ret = lp5521_read(client, LP5521_REG_OP_MODE, &engine_state); - if (ret < 0) - return ret; - - /* set mode only for this engine */ - engine_state &= ~(engine->engine_mask); - mode &= engine->engine_mask; - engine_state |= mode; - return lp5521_write(client, LP5521_REG_OP_MODE, engine_state); + lp5521_wait_opmode_done(); } -static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern) +static void lp5521_stop_engine(struct lp55xx_chip *chip) +{ + lp55xx_write(chip, LP5521_REG_OP_MODE, 0); + lp5521_wait_opmode_done(); +} + +static void lp5521_run_engine(struct lp55xx_chip *chip, bool start) { - struct lp5521_chip *chip = engine_to_lp5521(eng); - struct i2c_client *client = chip->client; int ret; - int addr; u8 mode; + u8 exec; - /* move current engine to direct mode and remember the state */ - ret = lp5521_set_engine_mode(eng, LP5521_CMD_DIRECT); + /* stop engine */ + if (!start) { + lp5521_stop_engine(chip); + lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); + lp5521_wait_opmode_done(); + return; + } + + /* + * To run the engine, + * operation mode and enable register should updated at the same time + */ + + ret = lp55xx_read(chip, LP5521_REG_OP_MODE, &mode); if (ret) - return ret; + return; - /* Mode change requires min 500 us delay. 1 - 2 ms with margin */ - usleep_range(1000, 2000); - ret = lp5521_read(client, LP5521_REG_OP_MODE, &mode); + ret = lp55xx_read(chip, LP5521_REG_ENABLE, &exec); if (ret) - return ret; + return; - /* For loading, all the engines to load mode */ - lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); - /* Mode change requires min 500 us delay. 1 - 2 ms with margin */ - usleep_range(1000, 2000); - lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); - /* Mode change requires min 500 us delay. 1 - 2 ms with margin */ - usleep_range(1000, 2000); + /* change operation mode to RUN only when each engine is loading */ + if (LP5521_R_IS_LOADING(mode)) { + mode = (mode & ~LP5521_MODE_R_M) | LP5521_RUN_R; + exec = (exec & ~LP5521_EXEC_R_M) | LP5521_RUN_R; + } - addr = LP5521_PROG_MEM_BASE + eng->prog_page * LP5521_PROG_MEM_SIZE; - i2c_smbus_write_i2c_block_data(client, - addr, - LP5521_PROG_MEM_SIZE, - pattern); + if (LP5521_G_IS_LOADING(mode)) { + mode = (mode & ~LP5521_MODE_G_M) | LP5521_RUN_G; + exec = (exec & ~LP5521_EXEC_G_M) | LP5521_RUN_G; + } - return lp5521_write(client, LP5521_REG_OP_MODE, mode); + if (LP5521_B_IS_LOADING(mode)) { + mode = (mode & ~LP5521_MODE_B_M) | LP5521_RUN_B; + exec = (exec & ~LP5521_EXEC_B_M) | LP5521_RUN_B; + } + + lp55xx_write(chip, LP5521_REG_OP_MODE, mode); + lp5521_wait_opmode_done(); + + lp55xx_update_bits(chip, LP5521_REG_ENABLE, LP5521_EXEC_M, exec); + lp5521_wait_enable_done(); +} + +static int lp5521_update_program_memory(struct lp55xx_chip *chip, + const u8 *data, size_t size) +{ + enum lp55xx_engine_index idx = chip->engine_idx; + u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; + u8 addr[] = { + [LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM, + [LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM, + [LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM, + }; + unsigned cmd; + char c[3]; + int program_size; + int nrchars; + int offset = 0; + int ret; + int i; + + /* clear program memory before updating */ + for (i = 0; i < LP5521_PROGRAM_LENGTH; i++) + lp55xx_write(chip, addr[idx] + i, 0); + + i = 0; + while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) { + /* separate sscanfs because length is working only for %s */ + ret = sscanf(data + offset, "%2s%n ", c, &nrchars); + if (ret != 1) + goto err; + + ret = sscanf(c, "%2x", &cmd); + if (ret != 1) + goto err; + + pattern[i] = (u8)cmd; + offset += nrchars; + i++; + } + + /* Each instruction is 16bit long. Check that length is even */ + if (i % 2) + goto err; + + program_size = i; + for (i = 0; i < program_size; i++) + lp55xx_write(chip, addr[idx] + i, pattern[i]); + + return 0; + +err: + dev_err(&chip->cl->dev, "wrong pattern format\n"); + return -EINVAL; +} + +static void lp5521_firmware_loaded(struct lp55xx_chip *chip) +{ + const struct firmware *fw = chip->fw; + + if (fw->size > LP5521_PROGRAM_LENGTH) { + dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", + fw->size); + return; + } + + /* + * Program momery sequence + * 1) set engine mode to "LOAD" + * 2) write firmware data into program memory + */ + + lp5521_load_engine(chip); + lp5521_update_program_memory(chip, fw->data, fw->size); } static int lp5521_post_init_device(struct lp55xx_chip *chip) @@ -313,155 +406,6 @@ static void lp5521_led_brightness_work(struct work_struct *work) mutex_unlock(&chip->lock); } -/* Set engine mode and create appropriate sysfs attributes, if required. */ -static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode) -{ - int ret = 0; - - /* if in that mode already do nothing, except for run */ - if (mode == engine->mode && mode != LP5521_CMD_RUN) - return 0; - - if (mode == LP5521_CMD_RUN) { - ret = lp5521_set_engine_mode(engine, LP5521_CMD_RUN); - } else if (mode == LP5521_CMD_LOAD) { - lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); - lp5521_set_engine_mode(engine, LP5521_CMD_LOAD); - } else if (mode == LP5521_CMD_DISABLED) { - lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED); - } - - engine->mode = mode; - - return ret; -} - -static int lp5521_do_store_load(struct lp5521_engine *engine, - const char *buf, size_t len) -{ - struct lp5521_chip *chip = engine_to_lp5521(engine); - struct i2c_client *client = chip->client; - int ret, nrchars, offset = 0, i = 0; - char c[3]; - unsigned cmd; - u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; - - while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) { - /* separate sscanfs because length is working only for %s */ - ret = sscanf(buf + offset, "%2s%n ", c, &nrchars); - if (ret != 2) - goto fail; - ret = sscanf(c, "%2x", &cmd); - if (ret != 1) - goto fail; - pattern[i] = (u8)cmd; - - offset += nrchars; - i++; - } - - /* Each instruction is 16bit long. Check that length is even */ - if (i % 2) - goto fail; - - mutex_lock(&chip->lock); - if (engine->mode == LP5521_CMD_LOAD) - ret = lp5521_load_program(engine, pattern); - else - ret = -EINVAL; - mutex_unlock(&chip->lock); - - if (ret) { - dev_err(&client->dev, "failed loading pattern\n"); - return ret; - } - - return len; -fail: - dev_err(&client->dev, "wrong pattern format\n"); - return -EINVAL; -} - -static ssize_t store_engine_load(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); - return lp5521_do_store_load(&chip->engines[nr - 1], buf, len); -} - -#define store_load(nr) \ -static ssize_t store_engine##nr##_load(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_load(dev, attr, buf, len, nr); \ -} -store_load(1) -store_load(2) -store_load(3) - -static ssize_t show_engine_mode(struct device *dev, - struct device_attribute *attr, - char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); - switch (chip->engines[nr - 1].mode) { - case LP5521_CMD_RUN: - return sprintf(buf, "run\n"); - case LP5521_CMD_LOAD: - return sprintf(buf, "load\n"); - case LP5521_CMD_DISABLED: - return sprintf(buf, "disabled\n"); - default: - return sprintf(buf, "disabled\n"); - } -} - -#define show_mode(nr) \ -static ssize_t show_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_engine_mode(dev, attr, buf, nr); \ -} -show_mode(1) -show_mode(2) -show_mode(3) - -static ssize_t store_engine_mode(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); - struct lp5521_engine *engine = &chip->engines[nr - 1]; - mutex_lock(&chip->lock); - - if (!strncmp(buf, "run", 3)) - lp5521_set_mode(engine, LP5521_CMD_RUN); - else if (!strncmp(buf, "load", 4)) - lp5521_set_mode(engine, LP5521_CMD_LOAD); - else if (!strncmp(buf, "disabled", 8)) - lp5521_set_mode(engine, LP5521_CMD_DISABLED); - - mutex_unlock(&chip->lock); - return len; -} - -#define store_mode(nr) \ -static ssize_t store_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_mode(dev, attr, buf, len, nr); \ -} -store_mode(1) -store_mode(2) -store_mode(3) - static ssize_t lp5521_selftest(struct device *dev, struct device_attribute *attr, char *buf) @@ -550,45 +494,11 @@ static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip) } } -static ssize_t store_led_pattern(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct lp5521_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); - unsigned long val; - int ret; - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - lp5521_run_led_pattern(val, chip); - - return len; -} - /* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, - show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, - show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, - show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); -static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, store_led_pattern); static struct attribute *lp5521_attributes[] = { - &dev_attr_engine1_mode.attr, - &dev_attr_engine2_mode.attr, - &dev_attr_engine3_mode.attr, &dev_attr_selftest.attr, - &dev_attr_engine1_load.attr, - &dev_attr_engine2_load.attr, - &dev_attr_engine3_load.attr, - &dev_attr_led_pattern.attr, NULL }; @@ -623,6 +533,8 @@ static struct lp55xx_device_config lp5521_cfg = { .post_init_device = lp5521_post_init_device, .brightness_work_fn = lp5521_led_brightness_work, .set_led_current = lp5521_set_led_current, + .firmware_cb = lp5521_firmware_loaded, + .run_engine = lp5521_run_engine, }; static int lp5521_probe(struct i2c_client *client, From db6eaf8388a413a5ee1b4547ce78506b9c6456b0 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:18:51 +0900 Subject: [PATCH 2068/4607] leds-lp5523: use generic firmware interface LP55xx common driver provides generic firmware interface for running a LED pattern. LP5521 and LP5523 have many device attributes for running patterns. This patch cleans up those complex code. Removed device attributes: engine1_mode engine2_mode engine3_mode engine1_load engine2_load engine3_load engine1_leds engine2_leds engine3_leds All device attributes and functions are replaced with two callback functions, 'firmware_cb' and 'run_engine'. New engine functions: lp5523_load/stop/run_engine(), lp5523_update_program_memory() and lp5523_wait_opmode_done() Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5523.c | 529 ++++++++++++------------------------- 1 file changed, 170 insertions(+), 359 deletions(-) diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 70630156eff9..4b1ad9013a51 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "leds-lp55xx-common.h" @@ -108,20 +109,39 @@ #define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led))) #define SHIFT_MASK(id) (((id) - 1) * 2) +/* Memory Page Selection */ +#define LP5523_PAGE_ENG1 0 +#define LP5523_PAGE_ENG2 1 +#define LP5523_PAGE_ENG3 2 + +/* Program Memory Operations */ +#define LP5523_MODE_ENG1_M 0x30 /* Operation Mode Register */ +#define LP5523_MODE_ENG2_M 0x0C +#define LP5523_MODE_ENG3_M 0x03 +#define LP5523_LOAD_ENG1 0x10 +#define LP5523_LOAD_ENG2 0x04 +#define LP5523_LOAD_ENG3 0x01 + +#define LP5523_ENG1_IS_LOADING(mode) \ + ((mode & LP5523_MODE_ENG1_M) == LP5523_LOAD_ENG1) +#define LP5523_ENG2_IS_LOADING(mode) \ + ((mode & LP5523_MODE_ENG2_M) == LP5523_LOAD_ENG2) +#define LP5523_ENG3_IS_LOADING(mode) \ + ((mode & LP5523_MODE_ENG3_M) == LP5523_LOAD_ENG3) + +#define LP5523_EXEC_ENG1_M 0x30 /* Enable Register */ +#define LP5523_EXEC_ENG2_M 0x0C +#define LP5523_EXEC_ENG3_M 0x03 +#define LP5523_EXEC_M 0x3F +#define LP5523_RUN_ENG1 0x20 +#define LP5523_RUN_ENG2 0x08 +#define LP5523_RUN_ENG3 0x02 + enum lp5523_chip_id { LP5523, LP55231, }; -struct lp5523_engine { - int id; - u8 mode; - u8 prog_page; - u8 mux_page; - u16 led_mux; - u8 engine_mask; -}; - struct lp5523_led { int id; u8 chan_nr; @@ -135,13 +155,17 @@ struct lp5523_led { struct lp5523_chip { struct mutex lock; /* Serialize control */ struct i2c_client *client; - struct lp5523_engine engines[LP5523_ENGINES]; struct lp5523_led leds[LP5523_MAX_LEDS]; struct lp5523_platform_data *pdata; u8 num_channels; u8 num_leds; }; +static inline void lp5523_wait_opmode_done(void) +{ + usleep_range(1000, 2000); +} + static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) { led->led_current = led_current; @@ -149,27 +173,6 @@ static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static inline struct lp5523_led *cdev_to_led(struct led_classdev *cdev) -{ - return container_of(cdev, struct lp5523_led, cdev); -} - -static inline struct lp5523_chip *engine_to_lp5523(struct lp5523_engine *engine) -{ - return container_of(engine, struct lp5523_chip, - engines[engine->id - 1]); -} - -static inline struct lp5523_chip *led_to_lp5523(struct lp5523_led *led) -{ - return container_of(led, struct lp5523_chip, - leds[led->id]); -} - -static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode); -static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode); -static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern); - static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) { return i2c_smbus_write_byte_data(client, reg, value); @@ -212,178 +215,163 @@ static int lp5523_post_init_device(struct lp55xx_chip *chip) return lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff); } -static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode) +static void lp5523_load_engine(struct lp55xx_chip *chip) { - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret; - u8 engine_state; + enum lp55xx_engine_index idx = chip->engine_idx; + u8 mask[] = { + [LP55XX_ENGINE_1] = LP5523_MODE_ENG1_M, + [LP55XX_ENGINE_2] = LP5523_MODE_ENG2_M, + [LP55XX_ENGINE_3] = LP5523_MODE_ENG3_M, + }; - ret = lp5523_read(client, LP5523_REG_OP_MODE, &engine_state); - if (ret) - goto fail; + u8 val[] = { + [LP55XX_ENGINE_1] = LP5523_LOAD_ENG1, + [LP55XX_ENGINE_2] = LP5523_LOAD_ENG2, + [LP55XX_ENGINE_3] = LP5523_LOAD_ENG3, + }; - engine_state &= ~(engine->engine_mask); + u8 page_sel[] = { + [LP55XX_ENGINE_1] = LP5523_PAGE_ENG1, + [LP55XX_ENGINE_2] = LP5523_PAGE_ENG2, + [LP55XX_ENGINE_3] = LP5523_PAGE_ENG3, + }; - /* set mode only for this engine */ - mode &= engine->engine_mask; + lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]); - engine_state |= mode; + lp5523_wait_opmode_done(); - ret |= lp5523_write(client, LP5523_REG_OP_MODE, engine_state); -fail: - return ret; + lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, page_sel[idx]); } -static int lp5523_load_mux(struct lp5523_engine *engine, u16 mux) +static void lp5523_stop_engine(struct lp55xx_chip *chip) { - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret = 0; - - ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); - - ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL, engine->mux_page); - ret |= lp5523_write(client, LP5523_REG_PROG_MEM, - (u8)(mux >> 8)); - ret |= lp5523_write(client, LP5523_REG_PROG_MEM + 1, (u8)(mux)); - engine->led_mux = mux; - - return ret; + lp55xx_write(chip, LP5523_REG_OP_MODE, 0); + lp5523_wait_opmode_done(); } -static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern) -{ - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - - int ret = 0; - - ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); - - ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL, - engine->prog_page); - ret |= i2c_smbus_write_i2c_block_data(client, LP5523_REG_PROG_MEM, - LP5523_PROGRAM_LENGTH, pattern); - - return ret; -} - -static int lp5523_run_program(struct lp5523_engine *engine) -{ - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret; - - ret = lp5523_write(client, LP5523_REG_ENABLE, - LP5523_CMD_RUN | LP5523_ENABLE); - if (ret) - goto fail; - - ret = lp5523_set_engine_mode(engine, LP5523_CMD_RUN); -fail: - return ret; -} - -static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len) +static void lp5523_turn_off_channels(struct lp55xx_chip *chip) { int i; - u16 tmp_mux = 0; - len = min_t(int, len, LP5523_MAX_LEDS); - for (i = 0; i < len; i++) { - switch (buf[i]) { - case '1': - tmp_mux |= (1 << i); - break; - case '0': - break; - case '\n': - i = len; - break; - default: - return -1; - } + for (i = 0; i < LP5523_MAX_LEDS; i++) + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0); +} + +static void lp5523_run_engine(struct lp55xx_chip *chip, bool start) +{ + int ret; + u8 mode; + u8 exec; + + /* stop engine */ + if (!start) { + lp5523_stop_engine(chip); + lp5523_turn_off_channels(chip); + return; } - *mux = tmp_mux; + + /* + * To run the engine, + * operation mode and enable register should updated at the same time + */ + + ret = lp55xx_read(chip, LP5523_REG_OP_MODE, &mode); + if (ret) + return; + + ret = lp55xx_read(chip, LP5523_REG_ENABLE, &exec); + if (ret) + return; + + /* change operation mode to RUN only when each engine is loading */ + if (LP5523_ENG1_IS_LOADING(mode)) { + mode = (mode & ~LP5523_MODE_ENG1_M) | LP5523_RUN_ENG1; + exec = (exec & ~LP5523_EXEC_ENG1_M) | LP5523_RUN_ENG1; + } + + if (LP5523_ENG2_IS_LOADING(mode)) { + mode = (mode & ~LP5523_MODE_ENG2_M) | LP5523_RUN_ENG2; + exec = (exec & ~LP5523_EXEC_ENG2_M) | LP5523_RUN_ENG2; + } + + if (LP5523_ENG3_IS_LOADING(mode)) { + mode = (mode & ~LP5523_MODE_ENG3_M) | LP5523_RUN_ENG3; + exec = (exec & ~LP5523_EXEC_ENG3_M) | LP5523_RUN_ENG3; + } + + lp55xx_write(chip, LP5523_REG_OP_MODE, mode); + lp5523_wait_opmode_done(); + + lp55xx_update_bits(chip, LP5523_REG_ENABLE, LP5523_EXEC_M, exec); +} + +static int lp5523_update_program_memory(struct lp55xx_chip *chip, + const u8 *data, size_t size) +{ + u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; + unsigned cmd; + char c[3]; + int update_size; + int nrchars; + int offset = 0; + int ret; + int i; + + /* clear program memory before updating */ + for (i = 0; i < LP5523_PROGRAM_LENGTH; i++) + lp55xx_write(chip, LP5523_REG_PROG_MEM + i, 0); + + i = 0; + while ((offset < size - 1) && (i < LP5523_PROGRAM_LENGTH)) { + /* separate sscanfs because length is working only for %s */ + ret = sscanf(data + offset, "%2s%n ", c, &nrchars); + if (ret != 1) + goto err; + + ret = sscanf(c, "%2x", &cmd); + if (ret != 1) + goto err; + + pattern[i] = (u8)cmd; + offset += nrchars; + i++; + } + + /* Each instruction is 16bit long. Check that length is even */ + if (i % 2) + goto err; + + update_size = i; + for (i = 0; i < update_size; i++) + lp55xx_write(chip, LP5523_REG_PROG_MEM + i, pattern[i]); return 0; + +err: + dev_err(&chip->cl->dev, "wrong pattern format\n"); + return -EINVAL; } -static void lp5523_mux_to_array(u16 led_mux, char *array) +static void lp5523_firmware_loaded(struct lp55xx_chip *chip) { - int i, pos = 0; - for (i = 0; i < LP5523_MAX_LEDS; i++) - pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i)); + const struct firmware *fw = chip->fw; - array[pos] = '\0'; + if (fw->size > LP5523_PROGRAM_LENGTH) { + dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", + fw->size); + return; + } + + /* + * Program momery sequence + * 1) set engine mode to "LOAD" + * 2) write firmware data into program memory + */ + + lp5523_load_engine(chip); + lp5523_update_program_memory(chip, fw->data, fw->size); } -/*--------------------------------------------------------------*/ -/* Sysfs interface */ -/*--------------------------------------------------------------*/ - -static ssize_t show_engine_leds(struct device *dev, - struct device_attribute *attr, - char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - char mux[LP5523_MAX_LEDS + 1]; - - lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux); - - return sprintf(buf, "%s\n", mux); -} - -#define show_leds(nr) \ -static ssize_t show_engine##nr##_leds(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_engine_leds(dev, attr, buf, nr); \ -} -show_leds(1) -show_leds(2) -show_leds(3) - -static ssize_t store_engine_leds(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - u16 mux = 0; - ssize_t ret; - - if (lp5523_mux_parse(buf, &mux, len)) - return -EINVAL; - - mutex_lock(&chip->lock); - ret = -EINVAL; - if (chip->engines[nr - 1].mode != LP5523_CMD_LOAD) - goto leave; - - if (lp5523_load_mux(&chip->engines[nr - 1], mux)) - goto leave; - - ret = len; -leave: - mutex_unlock(&chip->lock); - return ret; -} - -#define store_leds(nr) \ -static ssize_t store_engine##nr##_leds(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_leds(dev, attr, buf, len, nr); \ -} -store_leds(1) -store_leds(2) -store_leds(3) - static ssize_t lp5523_selftest(struct device *dev, struct device_attribute *attr, char *buf) @@ -485,159 +473,10 @@ static void lp5523_led_brightness_work(struct work_struct *work) mutex_unlock(&chip->lock); } -static int lp5523_do_store_load(struct lp5523_engine *engine, - const char *buf, size_t len) -{ - struct lp5523_chip *chip = engine_to_lp5523(engine); - struct i2c_client *client = chip->client; - int ret, nrchars, offset = 0, i = 0; - char c[3]; - unsigned cmd; - u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; - - if (engine->mode != LP5523_CMD_LOAD) - return -EINVAL; - - while ((offset < len - 1) && (i < LP5523_PROGRAM_LENGTH)) { - /* separate sscanfs because length is working only for %s */ - ret = sscanf(buf + offset, "%2s%n ", c, &nrchars); - ret = sscanf(c, "%2x", &cmd); - if (ret != 1) - goto fail; - pattern[i] = (u8)cmd; - - offset += nrchars; - i++; - } - - /* Each instruction is 16bit long. Check that length is even */ - if (i % 2) - goto fail; - - mutex_lock(&chip->lock); - ret = lp5523_load_program(engine, pattern); - mutex_unlock(&chip->lock); - - if (ret) { - dev_err(&client->dev, "failed loading pattern\n"); - return ret; - } - - return len; -fail: - dev_err(&client->dev, "wrong pattern format\n"); - return -EINVAL; -} - -static ssize_t store_engine_load(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - return lp5523_do_store_load(&chip->engines[nr - 1], buf, len); -} - -#define store_load(nr) \ -static ssize_t store_engine##nr##_load(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_load(dev, attr, buf, len, nr); \ -} -store_load(1) -store_load(2) -store_load(3) - -static ssize_t show_engine_mode(struct device *dev, - struct device_attribute *attr, - char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - switch (chip->engines[nr - 1].mode) { - case LP5523_CMD_RUN: - return sprintf(buf, "run\n"); - case LP5523_CMD_LOAD: - return sprintf(buf, "load\n"); - case LP5523_CMD_DISABLED: - return sprintf(buf, "disabled\n"); - default: - return sprintf(buf, "disabled\n"); - } -} - -#define show_mode(nr) \ -static ssize_t show_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_engine_mode(dev, attr, buf, nr); \ -} -show_mode(1) -show_mode(2) -show_mode(3) - -static ssize_t store_engine_mode(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); - struct lp5523_engine *engine = &chip->engines[nr - 1]; - mutex_lock(&chip->lock); - - if (!strncmp(buf, "run", 3)) - lp5523_set_mode(engine, LP5523_CMD_RUN); - else if (!strncmp(buf, "load", 4)) - lp5523_set_mode(engine, LP5523_CMD_LOAD); - else if (!strncmp(buf, "disabled", 8)) - lp5523_set_mode(engine, LP5523_CMD_DISABLED); - - mutex_unlock(&chip->lock); - return len; -} - -#define store_mode(nr) \ -static ssize_t store_engine##nr##_mode(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t len) \ -{ \ - return store_engine_mode(dev, attr, buf, len, nr); \ -} -store_mode(1) -store_mode(2) -store_mode(3) - -/* device attributes */ -static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUSR, - show_engine1_mode, store_engine1_mode); -static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUSR, - show_engine2_mode, store_engine2_mode); -static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUSR, - show_engine3_mode, store_engine3_mode); -static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUSR, - show_engine1_leds, store_engine1_leds); -static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUSR, - show_engine2_leds, store_engine2_leds); -static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUSR, - show_engine3_leds, store_engine3_leds); -static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load); -static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); -static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL); static struct attribute *lp5523_attributes[] = { - &dev_attr_engine1_mode.attr, - &dev_attr_engine2_mode.attr, - &dev_attr_engine3_mode.attr, &dev_attr_selftest.attr, - &dev_attr_engine1_load.attr, - &dev_attr_engine1_leds.attr, - &dev_attr_engine2_load.attr, - &dev_attr_engine2_leds.attr, - &dev_attr_engine3_load.attr, - &dev_attr_engine3_leds.attr, NULL, }; @@ -664,36 +503,6 @@ static void lp5523_unregister_sysfs(struct i2c_client *client) sysfs_remove_group(&dev->kobj, &lp5523_group); } -/*--------------------------------------------------------------*/ -/* Set chip operating mode */ -/*--------------------------------------------------------------*/ -static void lp5523_set_mode(struct lp5523_engine *engine, u8 mode) -{ - /* if in that mode already do nothing, except for run */ - if (mode == engine->mode && mode != LP5523_CMD_RUN) - return; - - switch (mode) { - case LP5523_CMD_RUN: - lp5523_run_program(engine); - break; - case LP5523_CMD_LOAD: - lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); - lp5523_set_engine_mode(engine, LP5523_CMD_LOAD); - break; - case LP5523_CMD_DISABLED: - lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED); - break; - default: - return; - } - - engine->mode = mode; -} - -/*--------------------------------------------------------------*/ -/* Probe, Attach, Remove */ -/*--------------------------------------------------------------*/ /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -708,6 +517,8 @@ static struct lp55xx_device_config lp5523_cfg = { .post_init_device = lp5523_post_init_device, .brightness_work_fn = lp5523_led_brightness_work, .set_led_current = lp5523_set_led_current, + .firmware_cb = lp5523_firmware_loaded, + .run_engine = lp5523_run_engine, }; static int lp5523_probe(struct i2c_client *client, From 240085e255cd2818aff2ccde3066b7db1f29076a Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:20:01 +0900 Subject: [PATCH 2069/4607] leds-lp55xx: support device specific attributes To support device specific attributes, new common driver function is added. Eventually those are created on registering the sysfs with common dev attrs. Furthermore, this patch makes adding device attributes simple in each driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 13 ++++++++++++- drivers/leds/leds-lp55xx-common.h | 4 ++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 578902ab604f..9638ad4dc635 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -493,8 +493,19 @@ EXPORT_SYMBOL_GPL(lp55xx_unregister_leds); int lp55xx_register_sysfs(struct lp55xx_chip *chip) { struct device *dev = &chip->cl->dev; + struct lp55xx_device_config *cfg = chip->cfg; + int ret; - return sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group); + if (!cfg->run_engine || !cfg->firmware_cb) + goto dev_specific_attrs; + + ret = sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group); + if (ret) + return ret; + +dev_specific_attrs: + return cfg->dev_attr_group ? + sysfs_create_group(&dev->kobj, cfg->dev_attr_group) : 0; } EXPORT_SYMBOL_GPL(lp55xx_register_sysfs); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 8473abf9830c..64eb78da1c4b 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -45,6 +45,7 @@ struct lp55xx_reg { * @set_led_current : LED current set function * @firmware_cb : Call function when the firmware is loaded * @run_engine : Run internal engine for pattern + * @dev_attr_group : Device specific attributes */ struct lp55xx_device_config { const struct lp55xx_reg reset; @@ -65,6 +66,9 @@ struct lp55xx_device_config { /* used for running firmware LED patterns */ void (*run_engine) (struct lp55xx_chip *chip, bool start); + + /* additional device specific attributes */ + const struct attribute_group *dev_attr_group; }; /* From e73c0ce6beaa71bee39b2d11bff0253be84c71a9 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:20:45 +0900 Subject: [PATCH 2070/4607] leds-lp55xx: use common device attribute driver function lp5521/5523_register_sysfs() are replaced with lp55xx common driver function, lp55xx_register_sysfs(). Chip specific device attributes are configurable using 'dev_attr_group'. Error condition name is changed: use specific error condition, 'err_register_sysfs' rather than unclear name, 'fail2'. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 17 +++++++---------- drivers/leds/leds-lp5523.c | 23 +++++++---------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 89371e065c13..abc33139e6fa 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -506,12 +506,6 @@ static const struct attribute_group lp5521_group = { .attrs = lp5521_attributes, }; -static int lp5521_register_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - return sysfs_create_group(&dev->kobj, &lp5521_group); -} - static void lp5521_unregister_sysfs(struct i2c_client *client) { struct device *dev = &client->dev; @@ -535,6 +529,7 @@ static struct lp55xx_device_config lp5521_cfg = { .set_led_current = lp5521_set_led_current, .firmware_cb = lp5521_firmware_loaded, .run_engine = lp5521_run_engine, + .dev_attr_group = &lp5521_group, }; static int lp5521_probe(struct i2c_client *client, @@ -577,13 +572,15 @@ static int lp5521_probe(struct i2c_client *client, if (ret) goto err_register_leds; - ret = lp5521_register_sysfs(client); + ret = lp55xx_register_sysfs(chip); if (ret) { dev_err(&client->dev, "registering sysfs failed\n"); - goto fail2; + goto err_register_sysfs; } - return ret; -fail2: + + return 0; + +err_register_sysfs: lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 4b1ad9013a51..4192a1ec6062 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -484,18 +484,6 @@ static const struct attribute_group lp5523_group = { .attrs = lp5523_attributes, }; -static int lp5523_register_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - int ret; - - ret = sysfs_create_group(&dev->kobj, &lp5523_group); - if (ret < 0) - return ret; - - return 0; -} - static void lp5523_unregister_sysfs(struct i2c_client *client) { struct device *dev = &client->dev; @@ -519,6 +507,7 @@ static struct lp55xx_device_config lp5523_cfg = { .set_led_current = lp5523_set_led_current, .firmware_cb = lp5523_firmware_loaded, .run_engine = lp5523_run_engine, + .dev_attr_group = &lp5523_group, }; static int lp5523_probe(struct i2c_client *client, @@ -561,13 +550,15 @@ static int lp5523_probe(struct i2c_client *client, if (ret) goto err_register_leds; - ret = lp5523_register_sysfs(client); + ret = lp55xx_register_sysfs(chip); if (ret) { dev_err(&client->dev, "registering sysfs failed\n"); - goto fail2; + goto err_register_sysfs; } - return ret; -fail2: + + return 0; + +err_register_sysfs: lp55xx_unregister_leds(led, chip); err_register_leds: lp55xx_deinit_device(chip); From 9ca3bd8022d76a0d1b386cedcecaf49004a58644 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:21:43 +0900 Subject: [PATCH 2071/4607] leds-lp55xx: code refactoring on selftest function LP5521 and LP5523 have a selftest function which is run via the sysfs. Use lp55xx driver data and R/W functions rather than lp5521/5523 private data and functions. Additionally, if-statements are changed for code simplicity. Unused functions, lp5521/5523_read() are removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 30 ++++++++------------- drivers/leds/leds-lp5523.c | 53 ++++++++++++++------------------------ 2 files changed, 31 insertions(+), 52 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index abc33139e6fa..1f6d9c7eb4a2 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -168,18 +168,6 @@ static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) return i2c_smbus_write_byte_data(client, reg, value); } -static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf) -{ - s32 ret; - - ret = i2c_smbus_read_byte_data(client, reg); - if (ret < 0) - return ret; - - *buf = ret; - return 0; -} - static void lp5521_load_engine(struct lp55xx_chip *chip) { enum lp55xx_engine_index idx = chip->engine_idx; @@ -378,19 +366,23 @@ static int lp5521_post_init_device(struct lp55xx_chip *chip) return 0; } -static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf) +static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf) { + struct lp55xx_platform_data *pdata = chip->pdata; int ret; u8 status; - ret = lp5521_read(chip->client, LP5521_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5521_REG_STATUS, &status); if (ret < 0) return ret; + if (pdata->clock_mode != LP55XX_CLOCK_EXT) + return 0; + /* Check that ext clock is really in use if requested */ - if (chip->pdata && chip->pdata->clock_mode == LP5521_CLOCK_EXT) - if ((status & LP5521_EXT_CLK_USED) == 0) - return -EIO; + if ((status & LP5521_EXT_CLK_USED) == 0) + return -EIO; + return 0; } @@ -410,8 +402,8 @@ static ssize_t lp5521_selftest(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lp5521_chip *chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; int ret; mutex_lock(&chip->lock); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 4192a1ec6062..577059934f58 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -178,17 +178,6 @@ static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) return i2c_smbus_write_byte_data(client, reg, value); } -static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf) -{ - s32 ret = i2c_smbus_read_byte_data(client, reg); - - if (ret < 0) - return ret; - - *buf = ret; - return 0; -} - static int lp5523_post_init_device(struct lp55xx_chip *chip) { int ret; @@ -376,35 +365,35 @@ static ssize_t lp5523_selftest(struct device *dev, struct device_attribute *attr, char *buf) { - struct i2c_client *client = to_i2c_client(dev); - struct lp5523_chip *chip = i2c_get_clientdata(client); + struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); + struct lp55xx_chip *chip = led->chip; + struct lp55xx_platform_data *pdata = chip->pdata; int i, ret, pos = 0; - int led = 0; u8 status, adc, vdd; mutex_lock(&chip->lock); - ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); if (ret < 0) goto fail; /* Check that ext clock is really in use if requested */ - if ((chip->pdata) && (chip->pdata->clock_mode == LP5523_CLOCK_EXT)) + if (pdata->clock_mode == LP55XX_CLOCK_EXT) { if ((status & LP5523_EXT_CLK_USED) == 0) goto fail; + } /* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */ - lp5523_write(chip->client, LP5523_REG_LED_TEST_CTRL, - LP5523_EN_LEDTEST | 16); + lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | 16); usleep_range(3000, 6000); /* ADC conversion time is typically 2.7 ms */ - ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); if (ret < 0) goto fail; if (!(status & LP5523_LEDTEST_DONE)) usleep_range(3000, 6000); /* Was not ready. Wait little bit */ - ret = lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &vdd); + ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &vdd); if (ret < 0) goto fail; @@ -412,41 +401,39 @@ static ssize_t lp5523_selftest(struct device *dev, for (i = 0; i < LP5523_MAX_LEDS; i++) { /* Skip non-existing channels */ - if (chip->pdata->led_config[i].led_current == 0) + if (pdata->led_config[i].led_current == 0) continue; /* Set default current */ - lp5523_write(chip->client, - LP5523_REG_LED_CURRENT_BASE + i, - chip->pdata->led_config[i].led_current); + lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + i, + pdata->led_config[i].led_current); - lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0xff); + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0xff); /* let current stabilize 2 - 4ms before measurements start */ usleep_range(2000, 4000); - lp5523_write(chip->client, - LP5523_REG_LED_TEST_CTRL, + lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | i); /* ADC conversion time is 2.7 ms typically */ usleep_range(3000, 6000); - ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status); + ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); if (ret < 0) goto fail; if (!(status & LP5523_LEDTEST_DONE)) usleep_range(3000, 6000);/* Was not ready. Wait. */ - ret = lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &adc); + + ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &adc); if (ret < 0) goto fail; if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM) pos += sprintf(buf + pos, "LED %d FAIL\n", i); - lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0x00); + lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0x00); /* Restore current */ - lp5523_write(chip->client, - LP5523_REG_LED_CURRENT_BASE + i, - chip->leds[led].led_current); + lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + i, + led->led_current); led++; } if (pos == 0) From ba6fa84651ff9a609e0ceb8265e3335ab6ed656d Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:23:04 +0900 Subject: [PATCH 2072/4607] leds-lp55xx: add new function for removing device attribtues lp55xx_unregister_sysfs() is used for removing lp55xx device attributes. Chip specific and engine attributes are removed on unloading the driver. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 12 ++++++++++++ drivers/leds/leds-lp55xx-common.h | 1 + 2 files changed, 13 insertions(+) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 9638ad4dc635..782ab84fe65f 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -509,6 +509,18 @@ dev_specific_attrs: } EXPORT_SYMBOL_GPL(lp55xx_register_sysfs); +void lp55xx_unregister_sysfs(struct lp55xx_chip *chip) +{ + struct device *dev = &chip->cl->dev; + struct lp55xx_device_config *cfg = chip->cfg; + + if (cfg->dev_attr_group) + sysfs_remove_group(&dev->kobj, cfg->dev_attr_group); + + sysfs_remove_group(&dev->kobj, &lp55xx_engine_attr_group); +} +EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs); + MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP55xx Common Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp55xx-common.h b/drivers/leds/leds-lp55xx-common.h index 64eb78da1c4b..ece4761a1302 100644 --- a/drivers/leds/leds-lp55xx-common.h +++ b/drivers/leds/leds-lp55xx-common.h @@ -129,5 +129,6 @@ extern void lp55xx_unregister_leds(struct lp55xx_led *led, /* common device attributes functions */ extern int lp55xx_register_sysfs(struct lp55xx_chip *chip); +extern void lp55xx_unregister_sysfs(struct lp55xx_chip *chip); #endif /* _LEDS_LP55XX_COMMON_H */ From 87cc4bde2a97cd8acccf34f333d0980dc5c2aa8a Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:23:51 +0900 Subject: [PATCH 2073/4607] leds-lp55xx: clean up _remove() Replace lp5521/5523_unregister_sysfs() with lp55xx_unregister_sysfs(). On unloading the driver, running engines should be stopped. Use explicit driver function, lp5521/5523_stop_engine(). Unused functions are removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 79 +------------------------------------- drivers/leds/leds-lp5523.c | 19 +-------- 2 files changed, 4 insertions(+), 94 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 1f6d9c7eb4a2..46721c3a8e8b 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -412,36 +412,6 @@ static ssize_t lp5521_selftest(struct device *dev, return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); } -static void lp5521_clear_program_memory(struct i2c_client *cl) -{ - int i; - u8 rgb_mem[] = { - LP5521_REG_R_PROG_MEM, - LP5521_REG_G_PROG_MEM, - LP5521_REG_B_PROG_MEM, - }; - - for (i = 0; i < ARRAY_SIZE(rgb_mem); i++) { - lp5521_write(cl, rgb_mem[i], 0); - lp5521_write(cl, rgb_mem[i] + 1, 0); - } -} - -static void lp5521_write_program_memory(struct i2c_client *cl, - u8 base, u8 *rgb, int size) -{ - int i; - - if (!rgb || size <= 0) - return; - - for (i = 0; i < size; i++) - lp5521_write(cl, base + i, *(rgb + i)); - - lp5521_write(cl, base + i, 0); - lp5521_write(cl, base + i + 1, 0); -} - static inline struct lp5521_led_pattern *lp5521_get_pattern (struct lp5521_chip *chip, u8 offset) { @@ -450,42 +420,6 @@ static inline struct lp5521_led_pattern *lp5521_get_pattern return ptn; } -static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip) -{ - struct lp5521_led_pattern *ptn; - struct i2c_client *cl = chip->client; - int num_patterns = chip->pdata->num_patterns; - - if (mode > num_patterns || !(chip->pdata->patterns)) - return; - - if (mode == PATTERN_OFF) { - lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT); - usleep_range(1000, 2000); - lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); - } else { - ptn = lp5521_get_pattern(chip, mode); - if (!ptn) - return; - - lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); - usleep_range(1000, 2000); - - lp5521_clear_program_memory(cl); - - lp5521_write_program_memory(cl, LP5521_REG_R_PROG_MEM, - ptn->r, ptn->size_r); - lp5521_write_program_memory(cl, LP5521_REG_G_PROG_MEM, - ptn->g, ptn->size_g); - lp5521_write_program_memory(cl, LP5521_REG_B_PROG_MEM, - ptn->b, ptn->size_b); - - lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN); - usleep_range(1000, 2000); - lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); - } -} - /* device attributes */ static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); @@ -498,13 +432,6 @@ static const struct attribute_group lp5521_group = { .attrs = lp5521_attributes, }; -static void lp5521_unregister_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - - sysfs_remove_group(&dev->kobj, &lp5521_group); -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5521_cfg = { .reset = { @@ -582,13 +509,11 @@ err_init: static int lp5521_remove(struct i2c_client *client) { - struct lp5521_chip *old_chip = i2c_get_clientdata(client); struct lp55xx_led *led = i2c_get_clientdata(client); struct lp55xx_chip *chip = led->chip; - lp5521_run_led_pattern(PATTERN_OFF, old_chip); - lp5521_unregister_sysfs(client); - + lp5521_stop_engine(chip); + lp55xx_unregister_sysfs(chip); lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 577059934f58..cf587c1b2c41 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -173,11 +173,6 @@ static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static int lp5523_write(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - static int lp5523_post_init_device(struct lp55xx_chip *chip) { int ret; @@ -471,13 +466,6 @@ static const struct attribute_group lp5523_group = { .attrs = lp5523_attributes, }; -static void lp5523_unregister_sysfs(struct i2c_client *client) -{ - struct device *dev = &client->dev; - - sysfs_remove_group(&dev->kobj, &lp5523_group); -} - /* Chip specific configurations */ static struct lp55xx_device_config lp5523_cfg = { .reset = { @@ -558,11 +546,8 @@ static int lp5523_remove(struct i2c_client *client) struct lp55xx_led *led = i2c_get_clientdata(client); struct lp55xx_chip *chip = led->chip; - /* Disable engine mode */ - lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED); - - lp5523_unregister_sysfs(client); - + lp5523_stop_engine(chip); + lp55xx_unregister_sysfs(chip); lp55xx_unregister_leds(led, chip); lp55xx_deinit_device(chip); From 93ca4093adb757d5140071e72b2e9bfbb519b6c1 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:24:37 +0900 Subject: [PATCH 2074/4607] leds-lp55xx: clean up unused data and functions Old data structures and I2C function are not used any more. Each driver uses the lp55xx common data and functions. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 32 -------------------------------- drivers/leds/leds-lp5523.c | 19 ------------------- 2 files changed, 51 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 46721c3a8e8b..f05eb6e31d58 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -125,25 +125,6 @@ #define LP5521_RUN_G 0x08 #define LP5521_RUN_B 0x02 -struct lp5521_led { - int id; - u8 chan_nr; - u8 led_current; - u8 max_current; - struct led_classdev cdev; - struct work_struct brightness_work; - u8 brightness; -}; - -struct lp5521_chip { - struct lp5521_platform_data *pdata; - struct mutex lock; /* Serialize control */ - struct i2c_client *client; - struct lp5521_led leds[LP5521_MAX_LEDS]; - u8 num_channels; - u8 num_leds; -}; - static inline void lp5521_wait_opmode_done(void) { /* operation mode change needs to be longer than 153 us */ @@ -163,11 +144,6 @@ static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) led_current); } -static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - static void lp5521_load_engine(struct lp55xx_chip *chip) { enum lp55xx_engine_index idx = chip->engine_idx; @@ -412,14 +388,6 @@ static ssize_t lp5521_selftest(struct device *dev, return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); } -static inline struct lp5521_led_pattern *lp5521_get_pattern - (struct lp5521_chip *chip, u8 offset) -{ - struct lp5521_led_pattern *ptn; - ptn = chip->pdata->patterns + (offset - 1); - return ptn; -} - /* device attributes */ static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index cf587c1b2c41..b14bde2db24f 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -142,25 +142,6 @@ enum lp5523_chip_id { LP55231, }; -struct lp5523_led { - int id; - u8 chan_nr; - u8 led_current; - u8 max_current; - struct led_classdev cdev; - struct work_struct brightness_work; - u8 brightness; -}; - -struct lp5523_chip { - struct mutex lock; /* Serialize control */ - struct i2c_client *client; - struct lp5523_led leds[LP5523_MAX_LEDS]; - struct lp5523_platform_data *pdata; - u8 num_channels; - u8 num_leds; -}; - static inline void lp5523_wait_opmode_done(void) { usleep_range(1000, 2000); From 12f022d27bcdd606527ae1b3c9ad20cc8c90ce98 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:25:26 +0900 Subject: [PATCH 2075/4607] leds-lp55xx: clean up definitions Remove unused definitions and change hex values to capital letters Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 26 ++---------------- drivers/leds/leds-lp5523.c | 56 ++++++-------------------------------- 2 files changed, 12 insertions(+), 70 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index f05eb6e31d58..b2a0d877cdc1 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -39,18 +39,9 @@ #include "leds-lp55xx-common.h" -#define LP5521_PROGRAM_LENGTH 32 /* in bytes */ - -#define LP5521_MAX_LEDS 3 /* Maximum number of LEDs */ -#define LP5521_MAX_ENGINES 3 /* Maximum number of engines */ - -#define LP5521_ENG_MASK_BASE 0x30 /* 00110000 */ -#define LP5521_ENG_STATUS_MASK 0x07 /* 00000111 */ - -#define LP5521_CMD_LOAD 0x15 /* 00010101 */ -#define LP5521_CMD_RUN 0x2a /* 00101010 */ -#define LP5521_CMD_DIRECT 0x3f /* 00111111 */ -#define LP5521_CMD_DISABLED 0x00 /* 00000000 */ +#define LP5521_PROGRAM_LENGTH 32 +#define LP5521_MAX_LEDS 3 +#define LP5521_CMD_DIRECT 0x3F /* Registers */ #define LP5521_REG_ENABLE 0x00 @@ -62,22 +53,14 @@ #define LP5521_REG_G_CURRENT 0x06 #define LP5521_REG_B_CURRENT 0x07 #define LP5521_REG_CONFIG 0x08 -#define LP5521_REG_R_CHANNEL_PC 0x09 -#define LP5521_REG_G_CHANNEL_PC 0x0A -#define LP5521_REG_B_CHANNEL_PC 0x0B #define LP5521_REG_STATUS 0x0C #define LP5521_REG_RESET 0x0D -#define LP5521_REG_GPO 0x0E #define LP5521_REG_R_PROG_MEM 0x10 #define LP5521_REG_G_PROG_MEM 0x30 #define LP5521_REG_B_PROG_MEM 0x50 -#define LP5521_PROG_MEM_BASE LP5521_REG_R_PROG_MEM -#define LP5521_PROG_MEM_SIZE 0x20 - /* Base register to set LED current */ #define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT - /* Base register to set the brightness */ #define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM @@ -96,9 +79,6 @@ /* default R channel current register value */ #define LP5521_REG_R_CURR_DEFAULT 0xAF -/* Pattern Mode */ -#define PATTERN_OFF 0 - /* Reset register value */ #define LP5521_RESET 0xFF diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index b14bde2db24f..e145ce984b1d 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -39,76 +39,38 @@ #include "leds-lp55xx-common.h" +#define LP5523_PROGRAM_LENGTH 32 +#define LP5523_MAX_LEDS 9 + +/* Registers */ #define LP5523_REG_ENABLE 0x00 #define LP5523_REG_OP_MODE 0x01 -#define LP5523_REG_RATIOMETRIC_MSB 0x02 -#define LP5523_REG_RATIOMETRIC_LSB 0x03 #define LP5523_REG_ENABLE_LEDS_MSB 0x04 #define LP5523_REG_ENABLE_LEDS_LSB 0x05 -#define LP5523_REG_LED_CNTRL_BASE 0x06 #define LP5523_REG_LED_PWM_BASE 0x16 #define LP5523_REG_LED_CURRENT_BASE 0x26 #define LP5523_REG_CONFIG 0x36 -#define LP5523_REG_CHANNEL1_PC 0x37 -#define LP5523_REG_CHANNEL2_PC 0x38 -#define LP5523_REG_CHANNEL3_PC 0x39 -#define LP5523_REG_STATUS 0x3a -#define LP5523_REG_GPO 0x3b -#define LP5523_REG_VARIABLE 0x3c -#define LP5523_REG_RESET 0x3d -#define LP5523_REG_TEMP_CTRL 0x3e -#define LP5523_REG_TEMP_READ 0x3f -#define LP5523_REG_TEMP_WRITE 0x40 +#define LP5523_REG_STATUS 0x3A +#define LP5523_REG_RESET 0x3D #define LP5523_REG_LED_TEST_CTRL 0x41 #define LP5523_REG_LED_TEST_ADC 0x42 -#define LP5523_REG_ENG1_VARIABLE 0x45 -#define LP5523_REG_ENG2_VARIABLE 0x46 -#define LP5523_REG_ENG3_VARIABLE 0x47 -#define LP5523_REG_MASTER_FADER1 0x48 -#define LP5523_REG_MASTER_FADER2 0x49 -#define LP5523_REG_MASTER_FADER3 0x4a -#define LP5523_REG_CH1_PROG_START 0x4c -#define LP5523_REG_CH2_PROG_START 0x4d -#define LP5523_REG_CH3_PROG_START 0x4e -#define LP5523_REG_PROG_PAGE_SEL 0x4f +#define LP5523_REG_PROG_PAGE_SEL 0x4F #define LP5523_REG_PROG_MEM 0x50 -#define LP5523_CMD_LOAD 0x15 /* 00010101 */ -#define LP5523_CMD_RUN 0x2a /* 00101010 */ -#define LP5523_CMD_DISABLED 0x00 /* 00000000 */ - +/* Bit description in registers */ #define LP5523_ENABLE 0x40 #define LP5523_AUTO_INC 0x40 #define LP5523_PWR_SAVE 0x20 #define LP5523_PWM_PWR_SAVE 0x04 -#define LP5523_CP_1 0x08 -#define LP5523_CP_1_5 0x10 #define LP5523_CP_AUTO 0x18 -#define LP5523_INT_CLK 0x01 #define LP5523_AUTO_CLK 0x02 + #define LP5523_EN_LEDTEST 0x80 #define LP5523_LEDTEST_DONE 0x80 #define LP5523_RESET 0xFF - -#define LP5523_DEFAULT_CURRENT 50 /* microAmps */ -#define LP5523_PROGRAM_LENGTH 32 /* in bytes */ -#define LP5523_PROGRAM_PAGES 6 #define LP5523_ADC_SHORTCIRC_LIM 80 - -#define LP5523_MAX_LEDS 9 -#define LP5523_ENGINES 3 - -#define LP5523_ENG_MASK_BASE 0x30 /* 00110000 */ - -#define LP5523_ENG_STATUS_MASK 0x07 /* 00000111 */ - -#define LP5523_IRQ_FLAGS IRQF_TRIGGER_FALLING - #define LP5523_EXT_CLK_USED 0x08 -#define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led))) -#define SHIFT_MASK(id) (((id) - 1) * 2) - /* Memory Page Selection */ #define LP5523_PAGE_ENG1 0 #define LP5523_PAGE_ENG2 1 From 79bcc10b8cf3db99958743ecb174e7637e1dd932 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:26:14 +0900 Subject: [PATCH 2076/4607] leds-lp55xx: clean up headers Remove unused headers and sort them alphabetically Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 21 +++++++-------------- drivers/leds/leds-lp5523.c | 21 +++++++-------------- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index b2a0d877cdc1..3b6dcb0be7e8 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -20,22 +20,15 @@ * 02110-1301 USA */ -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include #include "leds-lp55xx-common.h" diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index e145ce984b1d..d44c001e5e73 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -20,22 +20,15 @@ * 02110-1301 USA */ -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include #include "leds-lp55xx-common.h" From df4094d24e6328824a2dfe8e6f641bff9a484d68 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:26:59 +0900 Subject: [PATCH 2077/4607] leds-lp5521/5523: use new lp55xx common header The LP55xx common driver provides a new header, leds-lp55xx.h. This driver enables removing duplicate code for both drivers and making coherent driver structure. LP5521 and LP5523/55231 platform data were merged into one common file. Therefore, the LP5521/5523 platform code need to be fixed. This patch has been already acked. For ux500: https://lkml.org/lkml/2012/10/11/417 Acked-by: Linus Walleij For omap: https://lkml.org/lkml/2012/10/11/334 Acked-by: Tony Lindgren Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- arch/arm/mach-omap2/board-rx51-peripherals.c | 8 +-- arch/arm/mach-ux500/board-mop500.c | 14 ++-- include/linux/leds-lp5521.h | 73 -------------------- include/linux/leds-lp5523.h | 49 ------------- 4 files changed, 11 insertions(+), 133 deletions(-) delete mode 100644 include/linux/leds-lp5521.h delete mode 100644 include/linux/leds-lp5523.h diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index cf07e289b4ea..1a2c4db13e32 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include <../drivers/staging/iio/light/tsl2563.h> #include @@ -160,7 +160,7 @@ static struct tsl2563_platform_data rx51_tsl2563_platform_data = { #endif #if defined(CONFIG_LEDS_LP5523) || defined(CONFIG_LEDS_LP5523_MODULE) -static struct lp5523_led_config rx51_lp5523_led_config[] = { +static struct lp55xx_led_config rx51_lp5523_led_config[] = { { .chan_nr = 0, .led_current = 50, @@ -207,10 +207,10 @@ static void rx51_lp5523_enable(bool state) gpio_set_value(RX51_LP5523_CHIP_EN_GPIO, !!state); } -static struct lp5523_platform_data rx51_lp5523_platform_data = { +static struct lp55xx_platform_data rx51_lp5523_platform_data = { .led_config = rx51_lp5523_led_config, .num_channels = ARRAY_SIZE(rx51_lp5523_led_config), - .clock_mode = LP5523_CLOCK_AUTO, + .clock_mode = LP55XX_CLOCK_AUTO, .setup_resources = rx51_lp5523_setup, .release_resources = rx51_lp5523_release, .enable = rx51_lp5523_enable, diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index d453522edb0d..b04684e16e24 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include @@ -320,7 +320,7 @@ static struct tc3589x_platform_data mop500_tc35892_data = { .irq_base = MOP500_EGPIO_IRQ_BASE, }; -static struct lp5521_led_config lp5521_pri_led[] = { +static struct lp55xx_led_config lp5521_pri_led[] = { [0] = { .chan_nr = 0, .led_current = 0x2f, @@ -338,14 +338,14 @@ static struct lp5521_led_config lp5521_pri_led[] = { }, }; -static struct lp5521_platform_data __initdata lp5521_pri_data = { +static struct lp55xx_platform_data __initdata lp5521_pri_data = { .label = "lp5521_pri", .led_config = &lp5521_pri_led[0], .num_channels = 3, - .clock_mode = LP5521_CLOCK_EXT, + .clock_mode = LP55XX_CLOCK_EXT, }; -static struct lp5521_led_config lp5521_sec_led[] = { +static struct lp55xx_led_config lp5521_sec_led[] = { [0] = { .chan_nr = 0, .led_current = 0x2f, @@ -363,11 +363,11 @@ static struct lp5521_led_config lp5521_sec_led[] = { }, }; -static struct lp5521_platform_data __initdata lp5521_sec_data = { +static struct lp55xx_platform_data __initdata lp5521_sec_data = { .label = "lp5521_sec", .led_config = &lp5521_sec_led[0], .num_channels = 3, - .clock_mode = LP5521_CLOCK_EXT, + .clock_mode = LP55XX_CLOCK_EXT, }; static struct i2c_board_info __initdata mop500_i2c0_devices[] = { diff --git a/include/linux/leds-lp5521.h b/include/linux/leds-lp5521.h deleted file mode 100644 index 3f071ec019b2..000000000000 --- a/include/linux/leds-lp5521.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * LP5521 LED chip driver. - * - * Copyright (C) 2010 Nokia Corporation - * - * Contact: Samu Onkalo - * - * 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. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef __LINUX_LP5521_H -#define __LINUX_LP5521_H - -/* See Documentation/leds/leds-lp5521.txt */ - -struct lp5521_led_config { - char *name; - u8 chan_nr; - u8 led_current; /* mA x10, 0 if led is not connected */ - u8 max_current; -}; - -struct lp5521_led_pattern { - u8 *r; - u8 *g; - u8 *b; - u8 size_r; - u8 size_g; - u8 size_b; -}; - -#define LP5521_CLOCK_AUTO 0 -#define LP5521_CLOCK_INT 1 -#define LP5521_CLOCK_EXT 2 - -/* Bits in CONFIG register */ -#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ -#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ -#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ -#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ -#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ -#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ -#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */ -#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */ -#define LP5521_CLK_INT 1 /* Internal clock */ -#define LP5521_CLK_AUTO 2 /* Automatic clock selection */ - -struct lp5521_platform_data { - struct lp5521_led_config *led_config; - u8 num_channels; - u8 clock_mode; - int (*setup_resources)(void); - void (*release_resources)(void); - void (*enable)(bool state); - const char *label; - u8 update_config; - struct lp5521_led_pattern *patterns; - int num_patterns; -}; - -#endif /* __LINUX_LP5521_H */ diff --git a/include/linux/leds-lp5523.h b/include/linux/leds-lp5523.h deleted file mode 100644 index 727877fb406d..000000000000 --- a/include/linux/leds-lp5523.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * LP5523 LED Driver - * - * Copyright (C) 2010 Nokia Corporation - * - * Contact: Samu Onkalo - * - * 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. - * - * 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef __LINUX_LP5523_H -#define __LINUX_LP5523_H - -/* See Documentation/leds/leds-lp5523.txt */ - -struct lp5523_led_config { - const char *name; - u8 chan_nr; - u8 led_current; /* mA x10, 0 if led is not connected */ - u8 max_current; -}; - -#define LP5523_CLOCK_AUTO 0 -#define LP5523_CLOCK_INT 1 -#define LP5523_CLOCK_EXT 2 - -struct lp5523_platform_data { - struct lp5523_led_config *led_config; - u8 num_channels; - u8 clock_mode; - int (*setup_resources)(void); - void (*release_resources)(void); - void (*enable)(bool state); - const char *label; -}; - -#endif /* __LINUX_LP5523_H */ From a2387cb9f6974fede9bf2d731a78ca158acb5424 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:28:00 +0900 Subject: [PATCH 2078/4607] leds-lp5521/5523: add author and copyright description Now LP5521 and LP5523 drivers are based on new lp55xx structure. So the author and copyrights are updated. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp5521.c | 3 +++ drivers/leds/leds-lp5523.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 3b6dcb0be7e8..1001347ba70b 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -2,8 +2,10 @@ * LP5521 LED chip driver. * * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2012 Texas Instruments * * Contact: Samu Onkalo + * Milo(Woogyom) Kim * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -479,5 +481,6 @@ static struct i2c_driver lp5521_driver = { module_i2c_driver(lp5521_driver); MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo"); +MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP5521 LED engine"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index d44c001e5e73..229f734040af 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -2,8 +2,10 @@ * lp5523.c - LP5523 LED Driver * * Copyright (C) 2010 Nokia Corporation + * Copyright (C) 2012 Texas Instruments * * Contact: Samu Onkalo + * Milo(Woogyom) Kim * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -510,5 +512,6 @@ static struct i2c_driver lp5523_driver = { module_i2c_driver(lp5523_driver); MODULE_AUTHOR("Mathias Nyman "); +MODULE_AUTHOR("Milo Kim "); MODULE_DESCRIPTION("LP5523 LED engine"); MODULE_LICENSE("GPL"); From 109b833071b44a4a6f5dc56385025543ed15a500 Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 19:28:44 +0900 Subject: [PATCH 2079/4607] leds-lp55xx: fix problem on removing LED attributes LP55XX common device attributes, 'led_current' and 'max_current' are created while loading the driver. Those are LED device attributes which are removed automatically on releasing led class devices - led_classdev_unregister(). Therefore, this duplicate code should be removed. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- drivers/leds/leds-lp55xx-common.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index 782ab84fe65f..d9eb84157423 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -478,12 +478,9 @@ void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip) { int i; struct lp55xx_led *each; - struct kobject *kobj; for (i = 0; i < chip->num_leds; i++) { each = led + i; - kobj = &led->cdev.dev->kobj; - sysfs_remove_group(kobj, &lp55xx_led_attr_group); led_classdev_unregister(&each->cdev); flush_work(&each->brightness_work); } From c0285f8ca8b8a4a1830b6071d6680bde447ff51c Mon Sep 17 00:00:00 2001 From: "Milo(Woogyom) Kim" Date: Tue, 5 Feb 2013 20:22:48 +0900 Subject: [PATCH 2080/4607] Documentation: leds: update LP55xx family devices Update changed platform data information. Add leds-lp55xx.txt which includes the firmware interface description. Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Bryan Wu --- Documentation/leds/00-INDEX | 2 + Documentation/leds/leds-lp5521.txt | 63 ++------------- Documentation/leds/leds-lp5523.txt | 27 ++----- Documentation/leds/leds-lp55xx.txt | 118 +++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 78 deletions(-) create mode 100644 Documentation/leds/leds-lp55xx.txt diff --git a/Documentation/leds/00-INDEX b/Documentation/leds/00-INDEX index 5fefe374892f..5246090ef15c 100644 --- a/Documentation/leds/00-INDEX +++ b/Documentation/leds/00-INDEX @@ -6,5 +6,7 @@ leds-lp5521.txt - notes on how to use the leds-lp5521 driver. leds-lp5523.txt - notes on how to use the leds-lp5523 driver. +leds-lp55xx.txt + - description about lp55xx common driver. leds-lm3556.txt - notes on how to use the leds-lm3556 driver. diff --git a/Documentation/leds/leds-lp5521.txt b/Documentation/leds/leds-lp5521.txt index 0e542ab3d4a0..270f57196339 100644 --- a/Documentation/leds/leds-lp5521.txt +++ b/Documentation/leds/leds-lp5521.txt @@ -17,19 +17,8 @@ lp5521:channelx, where x is 0 .. 2 All three channels can be also controlled using the engine micro programs. More details of the instructions can be found from the public data sheet. -Control interface for the engines: -x is 1 .. 3 -enginex_mode : disabled, load, run -enginex_load : store program (visible only in engine load mode) - -Example (start to blink the channel 2 led): -cd /sys/class/leds/lp5521:channel2/device -echo "load" > engine3_mode -echo "037f4d0003ff6000" > engine3_load -echo "run" > engine3_mode - -stop the engine: -echo "disabled" > engine3_mode +LP5521 has the internal program memory for running various LED patterns. +For the details, please refer to 'firmware' section in leds-lp55xx.txt sysfs contains a selftest entry. The test communicates with the chip and checks that @@ -47,7 +36,7 @@ The name of each channel can be configurable. If the name field is not defined, the default name will be set to 'xxxx:channelN' (XXXX : pdata->label or i2c client name, N : channel number) -static struct lp5521_led_config lp5521_led_config[] = { +static struct lp55xx_led_config lp5521_led_config[] = { { .name = "red", .chan_nr = 0, @@ -81,10 +70,10 @@ static void lp5521_enable(bool state) /* Control of chip enable signal */ } -static struct lp5521_platform_data lp5521_platform_data = { +static struct lp55xx_platform_data lp5521_platform_data = { .led_config = lp5521_led_config, .num_channels = ARRAY_SIZE(lp5521_led_config), - .clock_mode = LP5521_CLOCK_EXT, + .clock_mode = LP55XX_CLOCK_EXT, .setup_resources = lp5521_setup, .release_resources = lp5521_release, .enable = lp5521_enable, @@ -105,47 +94,9 @@ example of update_config : LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT | \ LP5521_CLK_INT) -static struct lp5521_platform_data lp5521_pdata = { +static struct lp55xx_platform_data lp5521_pdata = { .led_config = lp5521_led_config, .num_channels = ARRAY_SIZE(lp5521_led_config), - .clock_mode = LP5521_CLOCK_INT, + .clock_mode = LP55XX_CLOCK_INT, .update_config = LP5521_CONFIGS, }; - -LED patterns : LP5521 has autonomous operation without external control. -Pattern data can be defined in the platform data. - -example of led pattern data : - -/* RGB(50,5,0) 500ms on, 500ms off, infinite loop */ -static u8 pattern_red[] = { - 0x40, 0x32, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00, - }; - -static u8 pattern_green[] = { - 0x40, 0x05, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00, - }; - -static struct lp5521_led_pattern board_led_patterns[] = { - { - .r = pattern_red, - .g = pattern_green, - .size_r = ARRAY_SIZE(pattern_red), - .size_g = ARRAY_SIZE(pattern_green), - }, -}; - -static struct lp5521_platform_data lp5521_platform_data = { - .led_config = lp5521_led_config, - .num_channels = ARRAY_SIZE(lp5521_led_config), - .clock_mode = LP5521_CLOCK_EXT, - .patterns = board_led_patterns, - .num_patterns = ARRAY_SIZE(board_led_patterns), -}; - -Then predefined led pattern(s) can be executed via the sysfs. -To start the pattern #1, -# echo 1 > /sys/bus/i2c/devices/xxxx/led_pattern -(xxxx : i2c bus & slave address) -To end the pattern, -# echo 0 > /sys/bus/i2c/devices/xxxx/led_pattern diff --git a/Documentation/leds/leds-lp5523.txt b/Documentation/leds/leds-lp5523.txt index c2743f59f9ac..899fdad509fe 100644 --- a/Documentation/leds/leds-lp5523.txt +++ b/Documentation/leds/leds-lp5523.txt @@ -27,25 +27,8 @@ c) Default If both fields are NULL, 'lp5523' is used by default. /sys/class/leds/lp5523:channelN (N: 0 ~ 8) -The chip provides 3 engines. Each engine can control channels without -interaction from the main CPU. Details of the micro engine code can be found -from the public data sheet. Leds can be muxed to different channels. - -Control interface for the engines: -x is 1 .. 3 -enginex_mode : disabled, load, run -enginex_load : microcode load (visible only in load mode) -enginex_leds : led mux control (visible only in load mode) - -cd /sys/class/leds/lp5523:channel2/device -echo "load" > engine3_mode -echo "9d80400004ff05ff437f0000" > engine3_load -echo "111111111" > engine3_leds -echo "run" > engine3_mode - -sysfs contains a selftest entry. It measures each channel -voltage level and checks if it looks reasonable. If the level is too high, -the led is missing; if the level is too low, there is a short circuit. +LP5523 has the internal program memory for running various LED patterns. +For the details, please refer to 'firmware' section in leds-lp55xx.txt Selftest uses always the current from the platform data. @@ -58,7 +41,7 @@ Example platform data: Note - chan_nr can have values between 0 and 8. -static struct lp5523_led_config lp5523_led_config[] = { +static struct lp55xx_led_config lp5523_led_config[] = { { .name = "D1", .chan_nr = 0, @@ -88,10 +71,10 @@ static void lp5523_enable(bool state) /* Control chip enable signal */ } -static struct lp5523_platform_data lp5523_platform_data = { +static struct lp55xx_platform_data lp5523_platform_data = { .led_config = lp5523_led_config, .num_channels = ARRAY_SIZE(lp5523_led_config), - .clock_mode = LP5523_CLOCK_EXT, + .clock_mode = LP55XX_CLOCK_EXT, .setup_resources = lp5523_setup, .release_resources = lp5523_release, .enable = lp5523_enable, diff --git a/Documentation/leds/leds-lp55xx.txt b/Documentation/leds/leds-lp55xx.txt new file mode 100644 index 000000000000..ced41868d2d1 --- /dev/null +++ b/Documentation/leds/leds-lp55xx.txt @@ -0,0 +1,118 @@ +LP5521/LP5523/LP55231 Common Driver +=================================== + +Authors: Milo(Woogyom) Kim + +Description +----------- +LP5521, LP5523/55231 have common features as below. + + Register access via the I2C + Device initialization/deinitialization + Create LED class devices for multiple output channels + Device attributes for user-space interface + Program memory for running LED patterns + +The LP55xx common driver provides these features using exported functions. + lp55xx_init_device() / lp55xx_deinit_device() + lp55xx_register_leds() / lp55xx_unregister_leds() + lp55xx_regsister_sysfs() / lp55xx_unregister_sysfs() + +( Driver Structure Data ) + +In lp55xx common driver, two different data structure is used. + +o lp55xx_led + control multi output LED channels such as led current, channel index. +o lp55xx_chip + general chip control such like the I2C and platform data. + +For example, LP5521 has maximum 3 LED channels. +LP5523/55231 has 9 output channels. + +lp55xx_chip for LP5521 ... lp55xx_led #1 + lp55xx_led #2 + lp55xx_led #3 + +lp55xx_chip for LP5523 ... lp55xx_led #1 + lp55xx_led #2 + . + . + lp55xx_led #9 + +( Chip Dependent Code ) + +To support device specific configurations, special structure +'lpxx_device_config' is used. + + Maximum number of channels + Reset command, chip enable command + Chip specific initialization + Brightness control register access + Setting LED output current + Program memory address access for running patterns + Additional device specific attributes + +( Firmware Interface ) + +LP55xx family devices have the internal program memory for running +various LED patterns. +This pattern data is saved as a file in the user-land or +hex byte string is written into the memory through the I2C. +LP55xx common driver supports the firmware interface. + +LP55xx chips have three program engines. +To load and run the pattern, the programming sequence is following. + (1) Select an engine number (1/2/3) + (2) Mode change to load + (3) Write pattern data into selected area + (4) Mode change to run + +The LP55xx common driver provides simple interfaces as below. +select_engine : Select which engine is used for running program +run_engine : Start program which is loaded via the firmware interface +firmware : Load program data + +For example, run blinking pattern in engine #1 of LP5521 +echo 1 > /sys/bus/i2c/devices/xxxx/select_engine +echo 1 > /sys/class/firmware/lp5521/loading +echo "4000600040FF6000" > /sys/class/firmware/lp5521/data +echo 0 > /sys/class/firmware/lp5521/loading +echo 1 > /sys/bus/i2c/devices/xxxx/run_engine + +For example, run blinking pattern in engine #3 of LP55231 +echo 3 > /sys/bus/i2c/devices/xxxx/select_engine +echo 1 > /sys/class/firmware/lp55231/loading +echo "9d0740ff7e0040007e00a0010000" > /sys/class/firmware/lp55231/data +echo 0 > /sys/class/firmware/lp55231/loading +echo 1 > /sys/bus/i2c/devices/xxxx/run_engine + +To start blinking patterns in engine #2 and #3 simultaneously, +for idx in 2 3 +do + echo $idx > /sys/class/leds/red/device/select_engine + sleep 0.1 + echo 1 > /sys/class/firmware/lp5521/loading + echo "4000600040FF6000" > /sys/class/firmware/lp5521/data + echo 0 > /sys/class/firmware/lp5521/loading +done +echo 1 > /sys/class/leds/red/device/run_engine + +Here is another example for LP5523. +echo 2 > /sys/bus/i2c/devices/xxxx/select_engine +echo 1 > /sys/class/firmware/lp5523/loading +echo "9d80400004ff05ff437f0000" > /sys/class/firmware/lp5523/data +echo 0 > /sys/class/firmware/lp5523/loading +echo 1 > /sys/bus/i2c/devices/xxxx/run_engine + +As soon as 'loading' is set to 0, registered callback is called. +Inside the callback, the selected engine is loaded and memory is updated. +To run programmed pattern, 'run_engine' attribute should be enabled. + +( 'run_engine' and 'firmware_cb' ) +The sequence of running the program data is common. +But each device has own specific register addresses for commands. +To support this, 'run_engine' and 'firmware_cb' are configurable in each driver. +run_engine : Control the selected engine +firmware_cb : The callback function after loading the firmware is done. + Chip specific commands for loading and updating program memory. From 5037878e2223278aa627162aa0bf106dffac19d4 Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Mon, 4 Feb 2013 16:00:28 +0200 Subject: [PATCH 2081/4607] KVM: VMX: cleanup vmx_set_cr0(). When calculating hw_cr0 teh current code masks bits that should be always on and re-adds them back immediately after. Cleanup the code by masking only those bits that should be dropped from hw_cr0. This allow us to get rid of some defines. Signed-off-by: Gleb Natapov Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/vmx.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index fe9a9cfadbd6..fe09fdc5624c 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -95,12 +95,8 @@ module_param(enable_apicv_reg_vid, bool, S_IRUGO); static bool __read_mostly nested = 0; module_param(nested, bool, S_IRUGO); -#define KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST \ - (X86_CR0_WP | X86_CR0_NE | X86_CR0_NW | X86_CR0_CD) -#define KVM_GUEST_CR0_MASK \ - (KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST | X86_CR0_PG | X86_CR0_PE) -#define KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST \ - (X86_CR0_WP | X86_CR0_NE) +#define KVM_GUEST_CR0_MASK (X86_CR0_NW | X86_CR0_CD) +#define KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST (X86_CR0_WP | X86_CR0_NE) #define KVM_VM_CR0_ALWAYS_ON \ (KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST | X86_CR0_PG | X86_CR0_PE) #define KVM_CR4_GUEST_OWNED_BITS \ @@ -3137,11 +3133,11 @@ static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) struct vcpu_vmx *vmx = to_vmx(vcpu); unsigned long hw_cr0; + hw_cr0 = (cr0 & ~KVM_GUEST_CR0_MASK); if (enable_unrestricted_guest) - hw_cr0 = (cr0 & ~KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST) - | KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST; + hw_cr0 |= KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST; else { - hw_cr0 = (cr0 & ~KVM_GUEST_CR0_MASK) | KVM_VM_CR0_ALWAYS_ON; + hw_cr0 |= KVM_VM_CR0_ALWAYS_ON; if (vmx->rmode.vm86_active && (cr0 & X86_CR0_PE)) enter_pmode(vcpu); From ad0ba85fab7da9e634c9da4f96315c8d0b9febcb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 23 Jan 2013 00:35:04 -0800 Subject: [PATCH 2082/4607] leds: leds-pwm: make it depend on PWM and not HAVE_PWM The correct dependency for the leds-pwm is PWM and not HAVE_PWM since PWM drivers now have their own subsystem. Signed-off-by: Peter Ujfalusi Signed-off-by: Bryan Wu --- drivers/leds/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index fc680a9ed841..78b354f32129 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -320,7 +320,7 @@ config LEDS_DAC124S085 config LEDS_PWM tristate "PWM driven LED Support" depends on LEDS_CLASS - depends on HAVE_PWM + depends on PWM help This option enables support for pwm driven LEDs From ef754e88e35f86d9704f79ac8dace8c66f367164 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 27 Jan 2013 01:14:14 -0800 Subject: [PATCH 2083/4607] leds: tca6507: Use of_get_child_count() Signed-off-by: Axel Lin Signed-off-by: Bryan Wu --- drivers/leds/leds-tca6507.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c index 220fc7fbf1f0..070ba0741b21 100644 --- a/drivers/leds/leds-tca6507.c +++ b/drivers/leds/leds-tca6507.c @@ -674,14 +674,10 @@ tca6507_led_dt_init(struct i2c_client *client) struct device_node *np = client->dev.of_node, *child; struct tca6507_platform_data *pdata; struct led_info *tca_leds; - int count = 0; + int count; - for_each_child_of_node(np, child) - count++; - if (!count) - return ERR_PTR(-ENODEV); - - if (count > NUM_LEDS) + count = of_get_child_count(np); + if (!count || count > NUM_LEDS) return ERR_PTR(-ENODEV); tca_leds = devm_kzalloc(&client->dev, From 61d4eb2724283e85b37ed2fe13390366d7a6db74 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 27 Jan 2013 04:56:57 -0800 Subject: [PATCH 2084/4607] leds: 88pm860x: Add missing of_node_put() of_find_node_by_name() returns a node pointer with refcount incremented, use of_node_put() on it when done. of_find_node_by_name() will call of_node_put() against from parameter, thus we also need to call of_node_get(from) before calling of_find_node_by_name(). Signed-off-by: Axel Lin Signed-off-by: Bryan Wu --- drivers/leds/leds-88pm860x.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 6be2edd41173..f5b9ea315790 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -128,8 +128,10 @@ static void pm860x_led_set(struct led_classdev *cdev, static int pm860x_led_dt_init(struct platform_device *pdev, struct pm860x_led *data) { - struct device_node *nproot = pdev->dev.parent->of_node, *np; + struct device_node *nproot, *np; int iset = 0; + + nproot = of_node_get(pdev->dev.parent->of_node); if (!nproot) return -ENODEV; nproot = of_find_node_by_name(nproot, "leds"); @@ -145,6 +147,7 @@ static int pm860x_led_dt_init(struct platform_device *pdev, break; } } + of_node_put(nproot); return 0; } #else From 4b07c5d5123f76487c61cf9dc3f987d0b8c88a94 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Mon, 4 Feb 2013 18:01:21 -0800 Subject: [PATCH 2085/4607] leds: leds-sunfire: use dev_err()/pr_err() instead of printk() Fixed the checkpatch errors and warnings as below: ERROR: spaces required around that '=' (ctx:VxW) WARNING: Prefer netdev_err(netdev, ... then dev_err(dev, ... then pr_err(... to printk(KERN_ERR ... Signed-off-by: Jingoo Han Signed-off-by: Bryan Wu --- drivers/leds/leds-sunfire.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c index 07ff5a3a6cee..89792990088d 100644 --- a/drivers/leds/leds-sunfire.c +++ b/drivers/leds/leds-sunfire.c @@ -3,6 +3,8 @@ * Copyright (C) 2008 David S. Miller */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -14,9 +16,6 @@ #include #include -#define DRIVER_NAME "leds-sunfire" -#define PFX DRIVER_NAME ": " - MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); MODULE_DESCRIPTION("Sun Fire LED driver"); MODULE_LICENSE("GPL"); @@ -130,14 +129,14 @@ static int sunfire_led_generic_probe(struct platform_device *pdev, int i, err; if (pdev->num_resources != 1) { - printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n", + dev_err(&pdev->dev, "Wrong number of resources %d, should be 1\n", pdev->num_resources); return -EINVAL; } p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); if (!p) { - printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n"); + dev_err(&pdev->dev, "Could not allocate struct sunfire_drvdata\n"); return -ENOMEM; } @@ -152,7 +151,7 @@ static int sunfire_led_generic_probe(struct platform_device *pdev, err = led_classdev_register(&pdev->dev, lp); if (err) { - printk(KERN_ERR PFX "Could not register %s LED\n", + dev_err(&pdev->dev, "Could not register %s LED\n", lp->name); for (i--; i >= 0; i--) led_classdev_unregister(&p->leds[i].led_cdev); @@ -188,7 +187,7 @@ static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = { { .name = "clockboard-right", .handler = clockboard_right_set, - .default_trigger= "heartbeat", + .default_trigger = "heartbeat", }, }; @@ -209,7 +208,7 @@ static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = { { .name = "fhc-right", .handler = fhc_right_set, - .default_trigger= "heartbeat", + .default_trigger = "heartbeat", }, }; @@ -244,13 +243,13 @@ static int __init sunfire_leds_init(void) int err = platform_driver_register(&sunfire_clockboard_led_driver); if (err) { - printk(KERN_ERR PFX "Could not register clock board LED driver\n"); + pr_err("Could not register clock board LED driver\n"); return err; } err = platform_driver_register(&sunfire_fhc_led_driver); if (err) { - printk(KERN_ERR PFX "Could not register FHC LED driver\n"); + pr_err("Could not register FHC LED driver\n"); platform_driver_unregister(&sunfire_clockboard_led_driver); } From caf6900f2d8aaebe404c976753f6813ccd31d95e Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Tue, 5 Feb 2013 15:11:09 +0800 Subject: [PATCH 2086/4607] KVM: MMU: lazily drop large spte Currently, kvm zaps the large spte if write-protected is needed, the later read can fault on that spte. Actually, we can make the large spte readonly instead of making them not present, the page fault caused by read access can be avoided The idea is from Avi: | As I mentioned before, write-protecting a large spte is a good idea, | since it moves some work from protect-time to fault-time, so it reduces | jitter. This removes the need for the return value. Reviewed-by: Gleb Natapov Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/mmu.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 0242a8a1b2e2..2a8d99a1e034 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -1106,8 +1106,7 @@ static void drop_large_spte(struct kvm_vcpu *vcpu, u64 *sptep) /* * Write-protect on the specified @sptep, @pt_protect indicates whether - * spte writ-protection is caused by protecting shadow page table. - * @flush indicates whether tlb need be flushed. + * spte write-protection is caused by protecting shadow page table. * * Note: write protection is difference between drity logging and spte * protection: @@ -1116,10 +1115,9 @@ static void drop_large_spte(struct kvm_vcpu *vcpu, u64 *sptep) * - for spte protection, the spte can be writable only after unsync-ing * shadow page. * - * Return true if the spte is dropped. + * Return true if tlb need be flushed. */ -static bool -spte_write_protect(struct kvm *kvm, u64 *sptep, bool *flush, bool pt_protect) +static bool spte_write_protect(struct kvm *kvm, u64 *sptep, bool pt_protect) { u64 spte = *sptep; @@ -1129,17 +1127,11 @@ spte_write_protect(struct kvm *kvm, u64 *sptep, bool *flush, bool pt_protect) rmap_printk("rmap_write_protect: spte %p %llx\n", sptep, *sptep); - if (__drop_large_spte(kvm, sptep)) { - *flush |= true; - return true; - } - if (pt_protect) spte &= ~SPTE_MMU_WRITEABLE; spte = spte & ~PT_WRITABLE_MASK; - *flush |= mmu_spte_update(sptep, spte); - return false; + return mmu_spte_update(sptep, spte); } static bool __rmap_write_protect(struct kvm *kvm, unsigned long *rmapp, @@ -1151,11 +1143,8 @@ static bool __rmap_write_protect(struct kvm *kvm, unsigned long *rmapp, for (sptep = rmap_get_first(*rmapp, &iter); sptep;) { BUG_ON(!(*sptep & PT_PRESENT_MASK)); - if (spte_write_protect(kvm, sptep, &flush, pt_protect)) { - sptep = rmap_get_first(*rmapp, &iter); - continue; - } + flush |= spte_write_protect(kvm, sptep, pt_protect); sptep = rmap_get_next(&iter); } @@ -2596,6 +2585,8 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write, break; } + drop_large_spte(vcpu, iterator.sptep); + if (!is_shadow_present_pte(*iterator.sptep)) { u64 base_addr = iterator.addr; From 55dd98c3a81e243447d5fbf496fa8b909ef3b7f6 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Tue, 5 Feb 2013 15:26:54 +0800 Subject: [PATCH 2087/4607] KVM: MMU: cleanup mapping-level Use min() to cleanup mapping_level Reviewed-by: Gleb Natapov Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/mmu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 2a8d99a1e034..5356d8d2d496 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -832,8 +832,7 @@ static int mapping_level(struct kvm_vcpu *vcpu, gfn_t large_gfn) if (host_level == PT_PAGE_TABLE_LEVEL) return host_level; - max_level = kvm_x86_ops->get_lpage_level() < host_level ? - kvm_x86_ops->get_lpage_level() : host_level; + max_level = min(kvm_x86_ops->get_lpage_level(), host_level); for (level = PT_DIRECTORY_LEVEL; level <= max_level; ++level) if (has_wrprotected_page(vcpu->kvm, large_gfn, level)) From f761620377ebc791afba7ded078947d2116f48ce Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Tue, 5 Feb 2013 15:27:27 +0800 Subject: [PATCH 2088/4607] KVM: MMU: remove pt_access in mmu_set_spte It is only used in debug code, so drop it Reviewed-by: Gleb Natapov Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/mmu.c | 17 +++++++---------- arch/x86/kvm/paging_tmpl.h | 9 ++++----- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 5356d8d2d496..e956e9bed294 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -2388,16 +2388,15 @@ done: } static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep, - unsigned pt_access, unsigned pte_access, - int write_fault, int *emulate, int level, gfn_t gfn, - pfn_t pfn, bool speculative, bool host_writable) + unsigned pte_access, int write_fault, int *emulate, + int level, gfn_t gfn, pfn_t pfn, bool speculative, + bool host_writable) { int was_rmapped = 0; int rmap_count; - pgprintk("%s: spte %llx access %x write_fault %d gfn %llx\n", - __func__, *sptep, pt_access, - write_fault, gfn); + pgprintk("%s: spte %llx write_fault %d gfn %llx\n", __func__, + *sptep, write_fault, gfn); if (is_rmap_spte(*sptep)) { /* @@ -2513,7 +2512,7 @@ static int direct_pte_prefetch_many(struct kvm_vcpu *vcpu, return -1; for (i = 0; i < ret; i++, gfn++, start++) - mmu_set_spte(vcpu, start, ACC_ALL, access, 0, NULL, + mmu_set_spte(vcpu, start, access, 0, NULL, sp->role.level, gfn, page_to_pfn(pages[i]), true, true); @@ -2574,9 +2573,7 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write, for_each_shadow_entry(vcpu, (u64)gfn << PAGE_SHIFT, iterator) { if (iterator.level == level) { - unsigned pte_access = ACC_ALL; - - mmu_set_spte(vcpu, iterator.sptep, ACC_ALL, pte_access, + mmu_set_spte(vcpu, iterator.sptep, ACC_ALL, write, &emulate, level, gfn, pfn, prefault, map_writable); direct_pte_prefetch(vcpu, iterator.sptep); diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index 34c5c99323f4..105dd5bd550e 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -326,8 +326,8 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, * we call mmu_set_spte() with host_writable = true because * pte_prefetch_gfn_to_pfn always gets a writable pfn. */ - mmu_set_spte(vcpu, spte, sp->role.access, pte_access, 0, - NULL, PT_PAGE_TABLE_LEVEL, gfn, pfn, true, true); + mmu_set_spte(vcpu, spte, pte_access, 0, NULL, PT_PAGE_TABLE_LEVEL, + gfn, pfn, true, true); return true; } @@ -470,9 +470,8 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr, } clear_sp_write_flooding_count(it.sptep); - mmu_set_spte(vcpu, it.sptep, access, gw->pte_access, - write_fault, &emulate, it.level, - gw->gfn, pfn, prefault, map_writable); + mmu_set_spte(vcpu, it.sptep, gw->pte_access, write_fault, &emulate, + it.level, gw->gfn, pfn, prefault, map_writable); FNAME(pte_prefetch)(vcpu, gw, it.sptep); return emulate; From 24db2734ad8123b6858ca98d690483ecdcceebb5 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Tue, 5 Feb 2013 15:28:02 +0800 Subject: [PATCH 2089/4607] KVM: MMU: cleanup __direct_map Use link_shadow_page to link the sp to the spte in __direct_map Reviewed-by: Gleb Natapov Signed-off-by: Xiao Guangrong Signed-off-by: Marcelo Tosatti --- arch/x86/kvm/mmu.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index e956e9bed294..1cda1f332654 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -1947,9 +1947,9 @@ static void link_shadow_page(u64 *sptep, struct kvm_mmu_page *sp) { u64 spte; - spte = __pa(sp->spt) - | PT_PRESENT_MASK | PT_ACCESSED_MASK - | PT_WRITABLE_MASK | PT_USER_MASK; + spte = __pa(sp->spt) | PT_PRESENT_MASK | PT_WRITABLE_MASK | + shadow_user_mask | shadow_x_mask | shadow_accessed_mask; + mmu_spte_set(sptep, spte); } @@ -2592,11 +2592,7 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write, iterator.level - 1, 1, ACC_ALL, iterator.sptep); - mmu_spte_set(iterator.sptep, - __pa(sp->spt) - | PT_PRESENT_MASK | PT_WRITABLE_MASK - | shadow_user_mask | shadow_x_mask - | shadow_accessed_mask); + link_shadow_page(iterator.sptep, sp); } } return emulate; From 2a2483072393b27f4336ab068a1f48ca19ff1c1e Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 24 Jan 2013 14:14:19 +1000 Subject: [PATCH 2090/4607] vgacon/vt: clear buffer attributes when we load a 512 character font (v2) When we switch from 256->512 byte font rendering mode, it means the current contents of the screen is being reinterpreted. The bit that holds the high bit of the 9-bit font, may have been previously set, and thus the new font misrenders. The problem case we see is grub2 writes spaces with the bit set, so it ends up with data like 0x820, which gets reinterpreted into 0x120 char which the font translates into G with a circumflex. This flashes up on screen at boot and is quite ugly. A current side effect of this patch though is that any rendering on the screen changes color to a slightly darker color, but at least the screen no longer corrupts. v2: as suggested by hpa, always clear the attribute space, whether we are are going to or from 512 chars. Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/tty/vt/vt.c | 2 +- drivers/video/console/vgacon.c | 22 +++++++++++++++------- include/linux/vt_kern.h | 1 + 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8fd89687d068..c8067aef4b86 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -638,7 +638,7 @@ static inline void save_screen(struct vc_data *vc) * Redrawing of screen */ -static void clear_buffer_attributes(struct vc_data *vc) +void clear_buffer_attributes(struct vc_data *vc) { unsigned short *p = (unsigned short *)vc->vc_origin; int count = vc->vc_screenbuf_size / 2; diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index d449a74d4a31..5855d17d19ac 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -1064,7 +1064,7 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) unsigned short video_port_status = vga_video_port_reg + 6; int font_select = 0x00, beg, i; char *charmap; - + bool clear_attribs = false; if (vga_video_type != VIDEO_TYPE_EGAM) { charmap = (char *) VGA_MAP_MEM(colourmap, 0); beg = 0x0e; @@ -1169,12 +1169,6 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) /* if 512 char mode is already enabled don't re-enable it. */ if ((set) && (ch512 != vga_512_chars)) { - /* attribute controller */ - for (i = 0; i < MAX_NR_CONSOLES; i++) { - struct vc_data *c = vc_cons[i].d; - if (c && c->vc_sw == &vga_con) - c->vc_hi_font_mask = ch512 ? 0x0800 : 0; - } vga_512_chars = ch512; /* 256-char: enable intensity bit 512-char: disable intensity bit */ @@ -1185,8 +1179,22 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) it means, but it works, and it appears necessary */ inb_p(video_port_status); vga_wattr(state->vgabase, VGA_AR_ENABLE_DISPLAY, 0); + clear_attribs = true; } raw_spin_unlock_irq(&vga_lock); + + if (clear_attribs) { + for (i = 0; i < MAX_NR_CONSOLES; i++) { + struct vc_data *c = vc_cons[i].d; + if (c && c->vc_sw == &vga_con) { + /* force hi font mask to 0, so we always clear + the bit on either transition */ + c->vc_hi_font_mask = 0x00; + clear_buffer_attributes(c); + c->vc_hi_font_mask = ch512 ? 0x0800 : 0; + } + } + } return 0; } diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 50ae7d0c279e..1f55665d7b49 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -47,6 +47,7 @@ int con_set_cmap(unsigned char __user *cmap); int con_get_cmap(unsigned char __user *cmap); void scrollback(struct vc_data *vc, int lines); void scrollfront(struct vc_data *vc, int lines); +void clear_buffer_attributes(struct vc_data *vc); void update_region(struct vc_data *vc, unsigned long start, int count); void redraw_screen(struct vc_data *vc, int is_switch); #define update_screen(x) redraw_screen(x, 0) From ae1287865f5361fa138d4d3b1b6277908b54eac9 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 24 Jan 2013 16:12:41 +1000 Subject: [PATCH 2091/4607] fbcon: don't lose the console font across generic->chip driver switch If grub2 loads efifb/vesafb, then when systemd starts it can set the console font on that framebuffer device, however when we then load the native KMS driver, the first thing it does is tear down the generic framebuffer driver. The thing is the generic code is doing the right thing, it frees the font because otherwise it would leak memory. However we can assume that if you are removing the generic firmware driver (vesa/efi/offb), that a new driver *should* be loading soon after, so we effectively leak the font. However the old code left a dangling pointer in vc->vc_font.data and we can now reuse that dangling pointer to load the font into the new driver, now that we aren't freeing it. Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=892340 Cc: Kay Sievers Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/video/console/fbcon.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index fdefa8fd72c4..f8dbe76a1b6f 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -990,7 +990,7 @@ static const char *fbcon_startup(void) } /* Setup default font */ - if (!p->fontdata) { + if (!p->fontdata && !vc->vc_font.data) { if (!fontname[0] || !(font = find_font(fontname))) font = get_default_font(info->var.xres, info->var.yres, @@ -1000,6 +1000,8 @@ static const char *fbcon_startup(void) vc->vc_font.height = font->height; vc->vc_font.data = (void *)(p->fontdata = font->data); vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ + } else { + p->fontdata = vc->vc_font.data; } cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); @@ -1159,9 +1161,9 @@ static void fbcon_init(struct vc_data *vc, int init) ops->p = &fb_display[fg_console]; } -static void fbcon_free_font(struct display *p) +static void fbcon_free_font(struct display *p, bool freefont) { - if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) + if (freefont && p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int)); p->fontdata = NULL; p->userfont = 0; @@ -1173,8 +1175,8 @@ static void fbcon_deinit(struct vc_data *vc) struct fb_info *info; struct fbcon_ops *ops; int idx; + bool free_font = true; - fbcon_free_font(p); idx = con2fb_map[vc->vc_num]; if (idx == -1) @@ -1185,6 +1187,8 @@ static void fbcon_deinit(struct vc_data *vc) if (!info) goto finished; + if (info->flags & FBINFO_MISC_FIRMWARE) + free_font = false; ops = info->fbcon_par; if (!ops) @@ -1196,6 +1200,8 @@ static void fbcon_deinit(struct vc_data *vc) ops->flags &= ~FBCON_FLAGS_INIT; finished: + fbcon_free_font(p, free_font); + if (!con_is_bound(&fb_con)) fbcon_exit(); From 9f23de52b64f7fb801fd76f3dd8651a0dc89187b Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 7 Feb 2013 10:10:04 +1000 Subject: [PATCH 2092/4607] drm/usb: bind driver to correct device While looking at plymouth on udl I noticed that plymouth was trying to use its fb plugin not its drm one, it was trying to drmOpen a driver called usb not udl, noticed that we actually had out driver pointing at the wrong device. Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c index 3cec30611417..34a156f0c336 100644 --- a/drivers/gpu/drm/drm_usb.c +++ b/drivers/gpu/drm/drm_usb.c @@ -18,7 +18,7 @@ int drm_get_usb_dev(struct usb_interface *interface, usbdev = interface_to_usbdev(interface); dev->usbdev = usbdev; - dev->dev = &usbdev->dev; + dev->dev = &interface->dev; mutex_lock(&drm_global_mutex); From bcb39af4486be07e896fc374a2336bad3104ae0a Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 7 Feb 2013 11:19:15 +1000 Subject: [PATCH 2093/4607] drm/udl: make usage as a console safer Okay you don't really want to use udl devices as your console, but if you are unlucky enough to do so, you run into a lot of schedule while atomic due to printk being called from all sorts of funky places. So check if we are in an atomic context, and queue the damage for later, the next printk should cause it to appear. This isn't ideal, but it is simple, and seems to work okay in my testing here. (dirty area idea came from xenfb) fixes a bunch of sleeping while atomic issues running fbcon on udl devices. Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/udl/udl_drv.h | 2 ++ drivers/gpu/drm/udl/udl_fb.c | 44 +++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 87aa5f5d3c88..cc6d90f28c71 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -75,6 +75,8 @@ struct udl_framebuffer { struct drm_framebuffer base; struct udl_gem_object *obj; bool active_16; /* active on the 16-bit channel */ + int x1, y1, x2, y2; /* dirty rect */ + spinlock_t dirty_lock; }; #define to_udl_fb(x) container_of(x, struct udl_framebuffer, base) diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index d4ab3beaada0..c35880ff2207 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -153,6 +153,9 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, struct urb *urb; int aligned_x; int bpp = (fb->base.bits_per_pixel / 8); + int x2, y2; + bool store_for_later = false; + unsigned long flags; if (!fb->active_16) return 0; @@ -169,8 +172,6 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, } } - start_cycles = get_cycles(); - aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long)); width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long)); x = aligned_x; @@ -180,19 +181,53 @@ int udl_handle_damage(struct udl_framebuffer *fb, int x, int y, (y + height > fb->base.height)) return -EINVAL; + /* if we are in atomic just store the info + can't test inside spin lock */ + if (in_atomic()) + store_for_later = true; + + x2 = x + width - 1; + y2 = y + height - 1; + + spin_lock_irqsave(&fb->dirty_lock, flags); + + if (fb->y1 < y) + y = fb->y1; + if (fb->y2 > y2) + y2 = fb->y2; + if (fb->x1 < x) + x = fb->x1; + if (fb->x2 > x2) + x2 = fb->x2; + + if (store_for_later) { + fb->x1 = x; + fb->x2 = x2; + fb->y1 = y; + fb->y2 = y2; + spin_unlock_irqrestore(&fb->dirty_lock, flags); + return 0; + } + + fb->x1 = fb->y1 = INT_MAX; + fb->x2 = fb->y2 = 0; + + spin_unlock_irqrestore(&fb->dirty_lock, flags); + start_cycles = get_cycles(); + urb = udl_get_urb(dev); if (!urb) return 0; cmd = urb->transfer_buffer; - for (i = y; i < y + height ; i++) { + for (i = y; i <= y2 ; i++) { const int line_offset = fb->base.pitches[0] * i; const int byte_offset = line_offset + (x * bpp); const int dev_byte_offset = (fb->base.width * bpp * i) + (x * bpp); if (udl_render_hline(dev, bpp, &urb, (char *) fb->obj->vmapping, &cmd, byte_offset, dev_byte_offset, - width * bpp, + (x2 - x + 1) * bpp, &bytes_identical, &bytes_sent)) goto error; } @@ -434,6 +469,7 @@ udl_framebuffer_init(struct drm_device *dev, { int ret; + spin_lock_init(&ufb->dirty_lock); ufb->obj = obj; ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs); drm_helper_mode_fill_fb_struct(&ufb->base, mode_cmd); From 9fff24aa2c5c504aadead1ff9599e813604c2e53 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Wed, 6 Feb 2013 22:30:23 -0500 Subject: [PATCH 2094/4607] jbd2: track request delay statistics Track the delay between when we first request that the commit begin and when it actually begins, so we can see how much of a gap exists. In theory, this should just be the remaining scheduling quantuum of the thread which requested the commit (assuming it was not a synchronous operation which triggered the commit request) plus scheduling overhead; however, it's possible that real time processes might get in the way of letting the kjournald thread from executing. Signed-off-by: "Theodore Ts'o" --- fs/jbd2/commit.c | 8 ++++++++ fs/jbd2/journal.c | 12 +++++++++--- fs/jbd2/transaction.c | 1 + include/linux/jbd2.h | 7 +++++++ include/trace/events/jbd2.h | 8 ++++++-- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 3091d42992f0..750c70148eff 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -435,7 +435,12 @@ void jbd2_journal_commit_transaction(journal_t *journal) trace_jbd2_commit_locking(journal, commit_transaction); stats.run.rs_wait = commit_transaction->t_max_wait; + stats.run.rs_request_delay = 0; stats.run.rs_locked = jiffies; + if (commit_transaction->t_requested) + stats.run.rs_request_delay = + jbd2_time_diff(commit_transaction->t_requested, + stats.run.rs_locked); stats.run.rs_running = jbd2_time_diff(commit_transaction->t_start, stats.run.rs_locked); @@ -1116,7 +1121,10 @@ restart_loop: */ spin_lock(&journal->j_history_lock); journal->j_stats.ts_tid++; + if (commit_transaction->t_requested) + journal->j_stats.ts_requested++; journal->j_stats.run.rs_wait += stats.run.rs_wait; + journal->j_stats.run.rs_request_delay += stats.run.rs_request_delay; journal->j_stats.run.rs_running += stats.run.rs_running; journal->j_stats.run.rs_locked += stats.run.rs_locked; journal->j_stats.run.rs_flushing += stats.run.rs_flushing; diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 1a80e3146a59..4ba2e81e35ac 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -533,6 +533,7 @@ int __jbd2_log_start_commit(journal_t *journal, tid_t target) jbd_debug(1, "JBD2: requesting commit %d/%d\n", journal->j_commit_request, journal->j_commit_sequence); + journal->j_running_transaction->t_requested = jiffies; wake_up(&journal->j_wait_commit); return 1; } else if (!tid_geq(journal->j_commit_request, target)) @@ -898,13 +899,18 @@ static int jbd2_seq_info_show(struct seq_file *seq, void *v) if (v != SEQ_START_TOKEN) return 0; - seq_printf(seq, "%lu transaction, each up to %u blocks\n", - s->stats->ts_tid, - s->journal->j_max_transaction_buffers); + seq_printf(seq, "%lu transactions (%lu requested), " + "each up to %u blocks\n", + s->stats->ts_tid, s->stats->ts_requested, + s->journal->j_max_transaction_buffers); if (s->stats->ts_tid == 0) return 0; seq_printf(seq, "average: \n %ums waiting for transaction\n", jiffies_to_msecs(s->stats->run.rs_wait / s->stats->ts_tid)); + seq_printf(seq, " %ums request delay\n", + (s->stats->ts_requested == 0) ? 0 : + jiffies_to_msecs(s->stats->run.rs_request_delay / + s->stats->ts_requested)); seq_printf(seq, " %ums running transaction\n", jiffies_to_msecs(s->stats->run.rs_running / s->stats->ts_tid)); seq_printf(seq, " %ums transaction was being locked\n", diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index df9f29760efa..735609e2d636 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -100,6 +100,7 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction) journal->j_running_transaction = transaction; transaction->t_max_wait = 0; transaction->t_start = jiffies; + transaction->t_requested = 0; return transaction; } diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index e30b66346942..e0aafc46064f 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -580,6 +580,11 @@ struct transaction_s */ unsigned long t_start; + /* + * When commit was requested + */ + unsigned long t_requested; + /* * Checkpointing stats [j_checkpoint_sem] */ @@ -637,6 +642,7 @@ struct transaction_s struct transaction_run_stats_s { unsigned long rs_wait; + unsigned long rs_request_delay; unsigned long rs_running; unsigned long rs_locked; unsigned long rs_flushing; @@ -649,6 +655,7 @@ struct transaction_run_stats_s { struct transaction_stats_s { unsigned long ts_tid; + unsigned long ts_requested; struct transaction_run_stats_s run; }; diff --git a/include/trace/events/jbd2.h b/include/trace/events/jbd2.h index 127993dbf322..5419f57beb1f 100644 --- a/include/trace/events/jbd2.h +++ b/include/trace/events/jbd2.h @@ -142,6 +142,7 @@ TRACE_EVENT(jbd2_run_stats, __field( dev_t, dev ) __field( unsigned long, tid ) __field( unsigned long, wait ) + __field( unsigned long, request_delay ) __field( unsigned long, running ) __field( unsigned long, locked ) __field( unsigned long, flushing ) @@ -155,6 +156,7 @@ TRACE_EVENT(jbd2_run_stats, __entry->dev = dev; __entry->tid = tid; __entry->wait = stats->rs_wait; + __entry->request_delay = stats->rs_request_delay; __entry->running = stats->rs_running; __entry->locked = stats->rs_locked; __entry->flushing = stats->rs_flushing; @@ -164,10 +166,12 @@ TRACE_EVENT(jbd2_run_stats, __entry->blocks_logged = stats->rs_blocks_logged; ), - TP_printk("dev %d,%d tid %lu wait %u running %u locked %u flushing %u " - "logging %u handle_count %u blocks %u blocks_logged %u", + TP_printk("dev %d,%d tid %lu wait %u request_delay %u running %u " + "locked %u flushing %u logging %u handle_count %u " + "blocks %u blocks_logged %u", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->tid, jiffies_to_msecs(__entry->wait), + jiffies_to_msecs(__entry->request_delay), jiffies_to_msecs(__entry->running), jiffies_to_msecs(__entry->locked), jiffies_to_msecs(__entry->flushing), From 078d5039a13dedbd2ed14153a6d764fd75baae07 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 7 Feb 2013 00:02:15 -0500 Subject: [PATCH 2095/4607] jbd2: revert "jbd2: add COW fields to struct jbd2_journal_handle" This reverts commit 93737456d68ddcb86232f669b83da673dd12e351. The cow-snapshots effort is no longer active, so remove these extra fields to shrink down the handle structure. Signed-off-by: "Theodore Ts'o" Reviewed-by: Jan Kara --- include/linux/jbd2.h | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index e0aafc46064f..24db7256a5ff 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -397,35 +397,13 @@ struct jbd2_journal_handle int h_err; /* Flags [no locking] */ - unsigned int h_sync:1; /* sync-on-close */ - unsigned int h_jdata:1; /* force data journaling */ - unsigned int h_aborted:1; /* fatal error on handle */ - unsigned int h_cowing:1; /* COWing block to snapshot */ - - /* Number of buffers requested by user: - * (before adding the COW credits factor) */ - unsigned int h_base_credits:14; - - /* Number of buffers the user is allowed to dirty: - * (counts only buffers dirtied when !h_cowing) */ - unsigned int h_user_credits:14; - + unsigned int h_sync: 1; /* sync-on-close */ + unsigned int h_jdata: 1; /* force data journaling */ + unsigned int h_aborted: 1; /* fatal error on handle */ #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map h_lockdep_map; #endif - -#ifdef CONFIG_JBD2_DEBUG - /* COW debugging counters: */ - unsigned int h_cow_moved; /* blocks moved to snapshot */ - unsigned int h_cow_copied; /* blocks copied to snapshot */ - unsigned int h_cow_ok_jh; /* blocks already COWed during current - transaction */ - unsigned int h_cow_ok_bitmap; /* blocks not set in COW bitmap */ - unsigned int h_cow_ok_mapped;/* blocks already mapped in snapshot */ - unsigned int h_cow_bitmaps; /* COW bitmaps created */ - unsigned int h_cow_excluded; /* blocks set in exclude bitmap */ -#endif }; From 1262e24a59a052f9a98383e47e7c903712490d5c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 7 Feb 2013 07:37:29 +0100 Subject: [PATCH 2096/4607] drivers/block/mtip32xx: add missing GENERIC_HARDIRQS dependency The MTIP32XX driver calls devm_request_irq() and therefore needs a GENERIC_HARDIRQS dependency to prevent building it on s390. Signed-off-by: Heiko Carstens Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/mtip32xx/Kconfig b/drivers/block/mtip32xx/Kconfig index 0ba837fc62a8..1fca1f996b45 100644 --- a/drivers/block/mtip32xx/Kconfig +++ b/drivers/block/mtip32xx/Kconfig @@ -4,6 +4,6 @@ config BLK_DEV_PCIESSD_MTIP32XX tristate "Block Device Driver for Micron PCIe SSDs" - depends on PCI + depends on PCI && GENERIC_HARDIRQS help This enables the block driver for Micron PCIe SSDs. From e5e9fdaad4443ba45ada91c0aae8b36a38ca9c4d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 7 Feb 2013 10:04:21 +0100 Subject: [PATCH 2097/4607] rsxx: add slab.h include to dma.c kbuild test robot says: tree: git://git.kernel.dk/linux-block.git for-3.9/drivers head: 1262e24a59a052f9a98383e47e7c903712490d5c commit: 8722ff8cdbfac9c1b20e67bb067b455c48cb8e93 [6/8] block: IBM RamSan 70/80 device driver config: make ARCH=alpha allyesconfig All error/warnings: drivers/block/rsxx/dma.c: In function 'rsxx_complete_dma': >> drivers/block/rsxx/dma.c:251:2: error: implicit declaration of function 'kmem_cache_free' [-Werror=implicit-function-declaration] drivers/block/rsxx/dma.c: In function 'rsxx_queue_discard': >> drivers/block/rsxx/dma.c:567:2: error: implicit declaration of function 'kmem_cache_alloc' [-Werror=implicit-function-declaration] >> drivers/block/rsxx/dma.c:567:6: warning: assignment makes pointer from integer without a cast [enabled by default] drivers/block/rsxx/dma.c: In function 'rsxx_queue_dma': >> drivers/block/rsxx/dma.c:601:6: warning: assignment makes pointer from integer without a cast [enabled by default] drivers/block/rsxx/dma.c: In function 'rsxx_dma_init': >> drivers/block/rsxx/dma.c:985:2: error: implicit declaration of function 'KMEM_CACHE' [-Werror=implicit-function-declaration] >> drivers/block/rsxx/dma.c:985:29: error: 'rsxx_dma' undeclared (first use in this function) drivers/block/rsxx/dma.c:985:29: note: each undeclared identifier is reported only once for each function it appears in >> drivers/block/rsxx/dma.c:985:39: error: 'SLAB_HWCACHE_ALIGN' undeclared (first use in this function) drivers/block/rsxx/dma.c: In function 'rsxx_dma_cleanup': >> drivers/block/rsxx/dma.c:995:2: error: implicit declaration of function 'kmem_cache_destroy' [-Werror=implicit-function-declaration] cc1: some warnings being treated as errors Signed-off-by: Jens Axboe --- drivers/block/rsxx/dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/block/rsxx/dma.c b/drivers/block/rsxx/dma.c index 08da35ea1d85..872f50358624 100644 --- a/drivers/block/rsxx/dma.c +++ b/drivers/block/rsxx/dma.c @@ -22,6 +22,7 @@ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include "rsxx_priv.h" struct rsxx_dma { From c2668c082ac1b4f30b145762a75ec2b3e016b952 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 6 Feb 2013 17:35:41 -0300 Subject: [PATCH 2098/4607] [media] s5c73m3: Remove __dev* attributes Remove no longer supported __devinit, __devexit attributes. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/s5c73m3/s5c73m3-core.c | 8 ++++---- drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index c143c9ec7ba9..5dbb65e1f6b7 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1561,8 +1561,8 @@ static int s5c73m3_configure_gpios(struct s5c73m3 *state, return 0; } -static int __devinit s5c73m3_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int s5c73m3_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct device *dev = &client->dev; const struct s5c73m3_platform_data *pdata = client->dev.platform_data; @@ -1666,7 +1666,7 @@ out_err1: return ret; } -static int __devexit s5c73m3_remove(struct i2c_client *client) +static int s5c73m3_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct s5c73m3 *state = sensor_sd_to_s5c73m3(sd); @@ -1693,7 +1693,7 @@ static struct i2c_driver s5c73m3_i2c_driver = { .name = DRIVER_NAME, }, .probe = s5c73m3_probe, - .remove = __devexit_p(s5c73m3_remove), + .remove = s5c73m3_remove, .id_table = s5c73m3_id, }; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c index 889139c2ac5e..6f3a9c00fe65 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c @@ -111,7 +111,7 @@ int s5c73m3_spi_read(struct s5c73m3 *state, void *addr, return 0; } -static int __devinit s5c73m3_spi_probe(struct spi_device *spi) +static int s5c73m3_spi_probe(struct spi_device *spi) { int r; struct s5c73m3 *state = container_of(spi->dev.driver, struct s5c73m3, @@ -132,7 +132,7 @@ static int __devinit s5c73m3_spi_probe(struct spi_device *spi) return 0; } -static int __devexit s5c73m3_spi_remove(struct spi_device *spi) +static int s5c73m3_spi_remove(struct spi_device *spi) { return 0; } @@ -141,7 +141,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state) { struct spi_driver *spidrv = &state->spidrv; - spidrv->remove = __devexit_p(s5c73m3_spi_remove); + spidrv->remove = s5c73m3_spi_remove; spidrv->probe = s5c73m3_spi_probe; spidrv->driver.name = S5C73M3_SPI_DRV_NAME; spidrv->driver.bus = &spi_bus_type; From 8e3dffc651cb668e1ff4d8b89cc1c3dde7540d3b Mon Sep 17 00:00:00 2001 From: Wang Shilong Date: Thu, 7 Feb 2013 22:57:53 +0800 Subject: [PATCH 2099/4607] Ext2: mark inode dirty after the function dquot_free_block_nodirty is called We should mark inode dirty after the function dquot_free_block_nodirty is called.Besides,add a check whether it is necessary to call dquot_free_block_nodirty functon. Signed-off-by: Wang Shilong Signed-off-by: Jan Kara --- fs/ext2/balloc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index ea88181932df..132da4c0692f 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -568,8 +568,11 @@ do_more: } error_return: brelse(bitmap_bh); - release_blocks(sb, freed); - dquot_free_block_nodirty(inode, freed); + if (freed) { + release_blocks(sb, freed); + dquot_free_block_nodirty(inode, freed); + mark_inode_dirty(inode); + } } /** @@ -1412,9 +1415,11 @@ allocated: *errp = 0; brelse(bitmap_bh); - dquot_free_block_nodirty(inode, *count-num); - mark_inode_dirty(inode); - *count = num; + if (num < *count) { + dquot_free_block_nodirty(inode, *count-num); + mark_inode_dirty(inode); + *count = num; + } return ret_block; io_error: From 712ddc52ffa1f5324cd8f682d9e5b047b91f34d3 Mon Sep 17 00:00:00 2001 From: Wang Shilong Date: Thu, 7 Feb 2013 22:57:54 +0800 Subject: [PATCH 2100/4607] Ext2: remove the static function release_blocks to optimize the kernel Because the static function 'release_blocks' is only called when releasing blocks,it will be more simple and efficient to call the function 'percpu_counter_add' directly. Signed-off-by: Wang Shilong Signed-off-by: Jan Kara --- fs/ext2/balloc.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 132da4c0692f..9f9992b37924 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -159,15 +159,6 @@ read_block_bitmap(struct super_block *sb, unsigned int block_group) return bh; } -static void release_blocks(struct super_block *sb, int count) -{ - if (count) { - struct ext2_sb_info *sbi = EXT2_SB(sb); - - percpu_counter_add(&sbi->s_freeblocks_counter, count); - } -} - static void group_adjust_blocks(struct super_block *sb, int group_no, struct ext2_group_desc *desc, struct buffer_head *bh, int count) { @@ -569,7 +560,7 @@ do_more: error_return: brelse(bitmap_bh); if (freed) { - release_blocks(sb, freed); + percpu_counter_add(&sbi->s_freeblocks_counter, freed); dquot_free_block_nodirty(inode, freed); mark_inode_dirty(inode); } From 77e383504791008cdbab1b33d70b395ca3a361c8 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 6 Feb 2013 13:55:17 +0530 Subject: [PATCH 2101/4607] iommu/exynos: Make exynos_sysmmu_disable static 'exynos_sysmmu_disable' is used only in this file and can be made static. Signed-off-by: Sachin Kamat Signed-off-by: Joerg Roedel --- drivers/iommu/exynos-iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index 7fe44f83cc37..238a3caa949a 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -511,7 +511,7 @@ int exynos_sysmmu_enable(struct device *dev, unsigned long pgtable) return ret; } -bool exynos_sysmmu_disable(struct device *dev) +static bool exynos_sysmmu_disable(struct device *dev) { struct sysmmu_drvdata *data = dev_get_drvdata(dev->archdata.iommu); bool disabled; From a3b7256d643dac125f4162cd4f9b603868559b61 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Wed, 6 Feb 2013 19:38:15 +0200 Subject: [PATCH 2102/4607] iommu/tegra: smmu: Fix incorrect mask for regbase This fixes kernel crash because of BUG() in register address validation. Signed-off-by: Hiroshi Doyu Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 774728313f51..117427e6004e 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -1191,7 +1191,7 @@ static int tegra_smmu_probe(struct platform_device *pdev) smmu->rege[i] = smmu->regs[i] + resource_size(res) - 1; } /* Same as "mc" 1st regiter block start address */ - smmu->regbase = (void __iomem *)((u32)smmu->regs[0] & ~PAGE_MASK); + smmu->regbase = (void __iomem *)((u32)smmu->regs[0] & PAGE_MASK); err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size); if (err) From 37a407101e08e444a193837a1e1d4f6f66c8dad4 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 6 Feb 2013 09:50:10 +0100 Subject: [PATCH 2103/4607] iommu/vt-d: Zero out allocated memory in dmar_enable_qi kmemcheck complained about the use of uninitialized memory. Fix by using kzalloc instead of kmalloc. Cc: David Woodhouse Signed-off-by: Hannes Reinecke Signed-off-by: Joerg Roedel --- drivers/iommu/dmar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 86e2f4a62b9a..2623a570ad2f 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -1040,7 +1040,7 @@ int dmar_enable_qi(struct intel_iommu *iommu) qi->desc = page_address(desc_page); - qi->desc_status = kmalloc(QI_LENGTH * sizeof(int), GFP_ATOMIC); + qi->desc_status = kzalloc(QI_LENGTH * sizeof(int), GFP_ATOMIC); if (!qi->desc_status) { free_page((unsigned long) qi->desc); kfree(qi); From f528d980c17b8714aedc918ba86e058af914d66b Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Wed, 6 Feb 2013 12:55:23 +0100 Subject: [PATCH 2104/4607] iommu/amd: Initialize device table after dma_ops When dma_ops are initialized the unity mappings are created. The init_device_table_dma() function makes sure DMA from all devices is blocked by default. This opens a short window in time where DMA to unity mapped regions is blocked by the IOMMU. Make sure this does not happen by initializing the device table after dma_ops. Cc: stable@vger.kernel.org Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_init.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index faf10ba1ed9a..b6ecddb63cd0 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -1876,11 +1876,6 @@ static int amd_iommu_init_dma(void) struct amd_iommu *iommu; int ret; - init_device_table_dma(); - - for_each_iommu(iommu) - iommu_flush_all_caches(iommu); - if (iommu_pass_through) ret = amd_iommu_init_passthrough(); else @@ -1889,6 +1884,11 @@ static int amd_iommu_init_dma(void) if (ret) return ret; + init_device_table_dma(); + + for_each_iommu(iommu) + iommu_flush_all_caches(iommu); + amd_iommu_init_api(); amd_iommu_init_notifier(); From 96477b4cd705c5416346aef262b0a1116cfcdd80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 12 Dec 2012 13:34:03 +0200 Subject: [PATCH 2105/4607] x86-32: Add support for 64bit get_user() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement __get_user_8() for x86-32. It will return the 64-bit result in edx:eax register pair, and ecx is used to pass in the address and return the error value. For consistency, change the register assignment for all other __get_user_x() variants, so that address is passed in ecx/rcx, the error value is returned in ecx/rcx, and eax/rax contains the actual value. [ hpa: I modified the patch so that it does NOT change the calling conventions for the existing callsites, this also means that the code is completely unchanged for 64 bits. Instead, continue to use eax for address input/error output and use the ecx:edx register pair for the output. ] This is a partial refresh of a patch [1] by Jamie Lokier from 2004. Only the minimal changes to implement 64bit get_user() were picked from the original patch. [1] http://article.gmane.org/gmane.linux.kernel/198823 Originally-by: Jamie Lokier Signed-off-by: Ville Syrjälä Link: http://lkml.kernel.org/r/1355312043-11467-1-git-send-email-ville.syrjala@linux.intel.com Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/uaccess.h | 19 +++++++++++++---- arch/x86/kernel/i386_ksyms_32.c | 1 + arch/x86/lib/getuser.S | 37 ++++++++++++++++++++++++++++----- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 1709801d18ec..1e963267d44e 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -151,8 +151,15 @@ extern int __get_user_bad(void); * On error, the variable @x is set to zero. */ #ifdef CONFIG_X86_32 -#define __get_user_8(__ret_gu, __val_gu, ptr) \ - __get_user_x(X, __ret_gu, __val_gu, ptr) +#define __get_user_8(ret, x, ptr) \ +do { \ + register unsigned long long __xx asm("%edx"); \ + asm volatile("call __get_user_8" \ + : "=a" (ret), "=r" (__xx) \ + : "0" (ptr)); \ + (x) = __xx; \ +} while (0) + #else #define __get_user_8(__ret_gu, __val_gu, ptr) \ __get_user_x(8, __ret_gu, __val_gu, ptr) @@ -162,6 +169,7 @@ extern int __get_user_bad(void); ({ \ int __ret_gu; \ unsigned long __val_gu; \ + unsigned long long __val_gu8; \ __chk_user_ptr(ptr); \ might_fault(); \ switch (sizeof(*(ptr))) { \ @@ -175,13 +183,16 @@ extern int __get_user_bad(void); __get_user_x(4, __ret_gu, __val_gu, ptr); \ break; \ case 8: \ - __get_user_8(__ret_gu, __val_gu, ptr); \ + __get_user_8(__ret_gu, __val_gu8, ptr); \ break; \ default: \ __get_user_x(X, __ret_gu, __val_gu, ptr); \ break; \ } \ - (x) = (__typeof__(*(ptr)))__val_gu; \ + if (sizeof(*(ptr)) == 8) \ + (x) = (__typeof__(*(ptr)))__val_gu8; \ + else \ + (x) = (__typeof__(*(ptr)))__val_gu; \ __ret_gu; \ }) diff --git a/arch/x86/kernel/i386_ksyms_32.c b/arch/x86/kernel/i386_ksyms_32.c index 9c3bd4a2050e..0fa69127209a 100644 --- a/arch/x86/kernel/i386_ksyms_32.c +++ b/arch/x86/kernel/i386_ksyms_32.c @@ -26,6 +26,7 @@ EXPORT_SYMBOL(csum_partial_copy_generic); EXPORT_SYMBOL(__get_user_1); EXPORT_SYMBOL(__get_user_2); EXPORT_SYMBOL(__get_user_4); +EXPORT_SYMBOL(__get_user_8); EXPORT_SYMBOL(__put_user_1); EXPORT_SYMBOL(__put_user_2); diff --git a/arch/x86/lib/getuser.S b/arch/x86/lib/getuser.S index 156b9c804670..d3bf9f99ca77 100644 --- a/arch/x86/lib/getuser.S +++ b/arch/x86/lib/getuser.S @@ -15,11 +15,10 @@ * __get_user_X * * Inputs: %[r|e]ax contains the address. - * The register is modified, but all changes are undone - * before returning because the C code doesn't know about it. * * Outputs: %[r|e]ax is error code (0 or -EFAULT) * %[r|e]dx contains zero-extended value + * %ecx contains the high half for 32-bit __get_user_8 * * * These functions should not modify any other registers, @@ -79,22 +78,35 @@ ENTRY(__get_user_4) CFI_ENDPROC ENDPROC(__get_user_4) -#ifdef CONFIG_X86_64 ENTRY(__get_user_8) CFI_STARTPROC +#ifdef CONFIG_X86_64 add $7,%_ASM_AX jc bad_get_user GET_THREAD_INFO(%_ASM_DX) cmp TI_addr_limit(%_ASM_DX),%_ASM_AX - jae bad_get_user + jae bad_get_user ASM_STAC 4: movq -7(%_ASM_AX),%_ASM_DX xor %eax,%eax ASM_CLAC ret +#else + add $7,%_ASM_AX + jc bad_get_user_8 + GET_THREAD_INFO(%_ASM_DX) + cmp TI_addr_limit(%_ASM_DX),%_ASM_AX + jae bad_get_user_8 + ASM_STAC +4: mov -7(%_ASM_AX),%edx +5: mov -3(%_ASM_AX),%ecx + xor %eax,%eax + ASM_CLAC + ret +#endif CFI_ENDPROC ENDPROC(__get_user_8) -#endif + bad_get_user: CFI_STARTPROC @@ -105,9 +117,24 @@ bad_get_user: CFI_ENDPROC END(bad_get_user) +#ifdef CONFIG_X86_32 +bad_get_user_8: + CFI_STARTPROC + xor %edx,%edx + xor %ecx,%ecx + mov $(-EFAULT),%_ASM_AX + ASM_CLAC + ret + CFI_ENDPROC +END(bad_get_user_8) +#endif + _ASM_EXTABLE(1b,bad_get_user) _ASM_EXTABLE(2b,bad_get_user) _ASM_EXTABLE(3b,bad_get_user) #ifdef CONFIG_X86_64 _ASM_EXTABLE(4b,bad_get_user) +#else + _ASM_EXTABLE(4b,bad_get_user_8) + _ASM_EXTABLE(5b,bad_get_user_8) #endif From e90a4ea534b110a43df87a05587c53cd78569467 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 18 Jan 2013 16:31:14 +0000 Subject: [PATCH 2106/4607] drm/udl: Inline memcmp() for RLE compression of xfer As we use a variable length the compiler does not realise that it is a fixed value of either 2 or 4 bytes. Instead of performing the inline comparison itself, the compiler inserts a function call to the generic memcmp routine which is optimised for long comparisons of variable length. That turns out to be quite expensive... Signed-off-by: Chris Wilson Signed-off-by: Dave Airlie --- drivers/gpu/drm/udl/udl_transfer.c | 44 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/udl/udl_transfer.c b/drivers/gpu/drm/udl/udl_transfer.c index 142fee5f983f..f343db73e095 100644 --- a/drivers/gpu/drm/udl/udl_transfer.c +++ b/drivers/gpu/drm/udl/udl_transfer.c @@ -75,15 +75,19 @@ static int udl_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) } #endif -static inline u16 pixel32_to_be16p(const uint8_t *pixel) +static inline u16 pixel32_to_be16(const uint32_t pixel) { - uint32_t pix = *(uint32_t *)pixel; - u16 retval; + return (((pixel >> 3) & 0x001f) | + ((pixel >> 5) & 0x07e0) | + ((pixel >> 8) & 0xf800)); +} - retval = (((pix >> 3) & 0x001f) | - ((pix >> 5) & 0x07e0) | - ((pix >> 8) & 0xf800)); - return retval; +static bool pixel_repeats(const void *pixel, const uint32_t repeat, int bpp) +{ + if (bpp == 2) + return *(const uint16_t *)pixel == repeat; + else + return *(const uint32_t *)pixel == repeat; } /* @@ -152,29 +156,33 @@ static void udl_compress_hline16( prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp); while (pixel < cmd_pixel_end) { - const u8 * const repeating_pixel = pixel; + const u8 *const start = pixel; + u32 repeating_pixel; - if (bpp == 2) - *(uint16_t *)cmd = cpu_to_be16p((uint16_t *)pixel); - else if (bpp == 4) - *(uint16_t *)cmd = cpu_to_be16(pixel32_to_be16p(pixel)); + if (bpp == 2) { + repeating_pixel = *(uint16_t *)pixel; + *(uint16_t *)cmd = cpu_to_be16(repeating_pixel); + } else { + repeating_pixel = *(uint32_t *)pixel; + *(uint16_t *)cmd = cpu_to_be16(pixel32_to_be16(repeating_pixel)); + } cmd += 2; pixel += bpp; if (unlikely((pixel < cmd_pixel_end) && - (!memcmp(pixel, repeating_pixel, bpp)))) { + (pixel_repeats(pixel, repeating_pixel, bpp)))) { /* go back and fill in raw pixel count */ - *raw_pixels_count_byte = (((repeating_pixel - + *raw_pixels_count_byte = (((start - raw_pixel_start) / bpp) + 1) & 0xFF; - while ((pixel < cmd_pixel_end) - && (!memcmp(pixel, repeating_pixel, bpp))) { + while ((pixel < cmd_pixel_end) && + (pixel_repeats(pixel, repeating_pixel, bpp))) { pixel += bpp; } /* immediately after raw data is repeat byte */ - *cmd++ = (((pixel - repeating_pixel) / bpp) - 1) & 0xFF; + *cmd++ = (((pixel - start) / bpp) - 1) & 0xFF; /* Then start another raw pixel span */ raw_pixel_start = pixel; @@ -223,6 +231,8 @@ int udl_render_hline(struct drm_device *dev, int bpp, struct urb **urb_ptr, u8 *cmd = *urb_buf_ptr; u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length; + BUG_ON(!(bpp == 2 || bpp == 4)); + line_start = (u8 *) (front + byte_offset); next_pixel = line_start; line_end = next_pixel + byte_width; From 677d23b70bf949f75746c80cbae92c233c6b5e2a Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 7 Feb 2013 12:30:25 +1000 Subject: [PATCH 2107/4607] drm/udl: disable fb_defio by default There seems to be a bad interaction between gem/shmem and defio on top, I get list corruption on the page lru in the shmem code. Turn it off for now until we get some more digging done. Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/udl/udl_fb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index c35880ff2207..1eb060cc71d1 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -22,9 +22,9 @@ #include -#define DL_DEFIO_WRITE_DELAY 5 /* fb_deferred_io.delay in jiffies */ +#define DL_DEFIO_WRITE_DELAY (HZ/20) /* fb_deferred_io.delay in jiffies */ -static int fb_defio = 1; /* Optionally enable experimental fb_defio mmap support */ +static int fb_defio = 0; /* Optionally enable experimental fb_defio mmap support */ static int fb_bpp = 16; module_param(fb_bpp, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP); From 313751fa7bb9364f320727c2af0735dc9104d188 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Wed, 30 Jan 2013 07:55:53 +0000 Subject: [PATCH 2108/4607] x86-64: Replace left over sti/cli in ia32 audit exit code For some reason they didn't get replaced so far by their paravirt equivalents, resulting in code to be run with interrupts disabled that doesn't expect so (causing, in the observed case, a BUG_ON() to trigger) when syscall auditing is enabled. David (Cc-ed) came up with an identical fix, so likely this can be taken to count as an ack from him. Reported-by: Peter Moody Signed-off-by: Jan Beulich Cc: David Vrabel Cc: Konrad Rzeszutek Wilk Link: http://lkml.kernel.org/r/5108E01902000078000BA9C5@nat28.tlf.novell.com Signed-off-by: Ingo Molnar Cc: stable@vger.kernel.org Cc: Konrad Rzeszutek Wilk Cc: David Vrabel Tested-by: Peter Moody --- arch/x86/ia32/ia32entry.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 102ff7cb3e41..142c4ceff112 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -207,7 +207,7 @@ sysexit_from_sys_call: testl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET) jnz ia32_ret_from_sys_call TRACE_IRQS_ON - sti + ENABLE_INTERRUPTS(CLBR_NONE) movl %eax,%esi /* second arg, syscall return value */ cmpl $-MAX_ERRNO,%eax /* is it an error ? */ jbe 1f @@ -217,7 +217,7 @@ sysexit_from_sys_call: call __audit_syscall_exit movq RAX-ARGOFFSET(%rsp),%rax /* reload syscall return value */ movl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),%edi - cli + DISABLE_INTERRUPTS(CLBR_NONE) TRACE_IRQS_OFF testl %edi,TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET) jz \exit From 521dfda999fd4cfe801c49d28ccc09350c0389f5 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 31 Jan 2013 20:23:49 -0800 Subject: [PATCH 2109/4607] x86, doc: Boot protocol 2.12 is in 3.8 The boot protocol 2.12 changes were pulled for 3.8, so update the documentation accordingly. Signed-off-by: H. Peter Anvin --- Documentation/x86/boot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt index 3edb4c2887a1..e540fd67f767 100644 --- a/Documentation/x86/boot.txt +++ b/Documentation/x86/boot.txt @@ -57,7 +57,7 @@ Protocol 2.10: (Kernel 2.6.31) Added a protocol for relaxed alignment Protocol 2.11: (Kernel 3.6) Added a field for offset of EFI handover protocol entry point. -Protocol 2.12: (Kernel 3.9) Added the xloadflags field and extension fields +Protocol 2.12: (Kernel 3.8) Added the xloadflags field and extension fields to struct boot_params for for loading bzImage and ramdisk above 4G in 64bit. From 84b603abd23300df8ee5fb1c23c83634c2aaedea Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Mon, 4 Feb 2013 10:13:15 +0100 Subject: [PATCH 2110/4607] x86/intel/cacheinfo: Shut up annoying warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've been getting the following warning when doing randbuilds since forever. Now it finally pissed me off just the perfect amount so that I can fix it. arch/x86/kernel/cpu/intel_cacheinfo.c:489:27: warning: ‘cache_disable_0’ defined but not used [-Wunused-variable] arch/x86/kernel/cpu/intel_cacheinfo.c:491:27: warning: ‘cache_disable_1’ defined but not used [-Wunused-variable] arch/x86/kernel/cpu/intel_cacheinfo.c:524:27: warning: ‘subcaches’ defined but not used [-Wunused-variable] It happens because in randconfigs where CONFIG_SYSFS is not set, the whole sysfs-interface to L3 cache index disabling is remaining unused and gcc correctly warns about it. Make it optional, depending on CONFIG_SYSFS too, as is the case with other sysfs-related machinery in this file. Signed-off-by: Borislav Petkov Cc: Andreas Herrmann Link: http://lkml.kernel.org/r/1359969195-27362-1-git-send-email-bp@alien8.de Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/intel_cacheinfo.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c index fe9edec6698a..84c1309c4c0c 100644 --- a/arch/x86/kernel/cpu/intel_cacheinfo.c +++ b/arch/x86/kernel/cpu/intel_cacheinfo.c @@ -298,8 +298,7 @@ struct _cache_attr { unsigned int); }; -#ifdef CONFIG_AMD_NB - +#if defined(CONFIG_AMD_NB) && defined(CONFIG_SYSFS) /* * L3 cache descriptors */ @@ -524,9 +523,9 @@ store_subcaches(struct _cpuid4_info *this_leaf, const char *buf, size_t count, static struct _cache_attr subcaches = __ATTR(subcaches, 0644, show_subcaches, store_subcaches); -#else /* CONFIG_AMD_NB */ +#else #define amd_init_l3_cache(x, y) -#endif /* CONFIG_AMD_NB */ +#endif /* CONFIG_AMD_NB && CONFIG_SYSFS */ static int __cpuinit cpuid4_cache_lookup_regs(int index, From 50e244cc793d511b86adea24972f3a7264cae114 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 25 Jan 2013 10:28:15 +1000 Subject: [PATCH 2111/4607] fb: rework locking to fix lock ordering on takeover Adjust the console layer to allow a take over call where the caller already holds the locks. Make the fb layer lock in order. This is partly a band aid, the fb layer is terminally confused about the locking rules it uses for its notifiers it seems. [akpm@linux-foundation.org: remove stray non-ascii char, tidy comment] [akpm@linux-foundation.org: export do_take_over_console()] [airlied: cleanup another non-ascii char] Signed-off-by: Alan Cox Cc: Florian Tobias Schandinat Cc: Stephen Rothwell Cc: Jiri Kosina Cc: stable Tested-by: Sedat Dilek Reviewed-by: Daniel Vetter Signed-off-by: Andrew Morton Signed-off-by: Dave Airlie --- drivers/tty/vt/vt.c | 93 ++++++++++++++++++++++++++--------- drivers/video/console/fbcon.c | 29 ++++++++++- drivers/video/fbmem.c | 5 +- drivers/video/fbsysfs.c | 3 ++ include/linux/console.h | 1 + 5 files changed, 104 insertions(+), 27 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 8fd89687d068..c076af0b300b 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -2987,7 +2987,7 @@ int __init vty_init(const struct file_operations *console_fops) static struct class *vtconsole_class; -static int bind_con_driver(const struct consw *csw, int first, int last, +static int do_bind_con_driver(const struct consw *csw, int first, int last, int deflt) { struct module *owner = csw->owner; @@ -2998,7 +2998,7 @@ static int bind_con_driver(const struct consw *csw, int first, int last, if (!try_module_get(owner)) return -ENODEV; - console_lock(); + WARN_CONSOLE_UNLOCKED(); /* check if driver is registered */ for (i = 0; i < MAX_NR_CON_DRIVER; i++) { @@ -3083,11 +3083,22 @@ static int bind_con_driver(const struct consw *csw, int first, int last, retval = 0; err: - console_unlock(); module_put(owner); return retval; }; + +static int bind_con_driver(const struct consw *csw, int first, int last, + int deflt) +{ + int ret; + + console_lock(); + ret = do_bind_con_driver(csw, first, last, deflt); + console_unlock(); + return ret; +} + #ifdef CONFIG_VT_HW_CONSOLE_BINDING static int con_is_graphics(const struct consw *csw, int first, int last) { @@ -3199,9 +3210,9 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) if (!con_is_bound(csw)) con_driver->flag &= ~CON_DRIVER_FLAG_INIT; - console_unlock(); /* ignore return value, binding should not fail */ - bind_con_driver(defcsw, first, last, deflt); + do_bind_con_driver(defcsw, first, last, deflt); + console_unlock(); err: module_put(owner); return retval; @@ -3492,28 +3503,18 @@ int con_debug_leave(void) } EXPORT_SYMBOL_GPL(con_debug_leave); -/** - * register_con_driver - register console driver to console layer - * @csw: console driver - * @first: the first console to take over, minimum value is 0 - * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 - * - * DESCRIPTION: This function registers a console driver which can later - * bind to a range of consoles specified by @first and @last. It will - * also initialize the console driver by calling con_startup(). - */ -int register_con_driver(const struct consw *csw, int first, int last) +static int do_register_con_driver(const struct consw *csw, int first, int last) { struct module *owner = csw->owner; struct con_driver *con_driver; const char *desc; int i, retval = 0; + WARN_CONSOLE_UNLOCKED(); + if (!try_module_get(owner)) return -ENODEV; - console_lock(); - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { con_driver = ®istered_con_driver[i]; @@ -3566,10 +3567,29 @@ int register_con_driver(const struct consw *csw, int first, int last) } err: - console_unlock(); module_put(owner); return retval; } + +/** + * register_con_driver - register console driver to console layer + * @csw: console driver + * @first: the first console to take over, minimum value is 0 + * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 + * + * DESCRIPTION: This function registers a console driver which can later + * bind to a range of consoles specified by @first and @last. It will + * also initialize the console driver by calling con_startup(). + */ +int register_con_driver(const struct consw *csw, int first, int last) +{ + int retval; + + console_lock(); + retval = do_register_con_driver(csw, first, last); + console_unlock(); + return retval; +} EXPORT_SYMBOL(register_con_driver); /** @@ -3623,17 +3643,44 @@ EXPORT_SYMBOL(unregister_con_driver); * when a driver wants to take over some existing consoles * and become default driver for newly opened ones. * - * take_over_console is basically a register followed by unbind + * take_over_console is basically a register followed by unbind + */ +int do_take_over_console(const struct consw *csw, int first, int last, int deflt) +{ + int err; + + err = do_register_con_driver(csw, first, last); + /* + * If we get an busy error we still want to bind the console driver + * and return success, as we may have unbound the console driver + * but not unregistered it. + */ + if (err == -EBUSY) + err = 0; + if (!err) + do_bind_con_driver(csw, first, last, deflt); + + return err; +} +EXPORT_SYMBOL_GPL(do_take_over_console); + +/* + * If we support more console drivers, this function is used + * when a driver wants to take over some existing consoles + * and become default driver for newly opened ones. + * + * take_over_console is basically a register followed by unbind */ int take_over_console(const struct consw *csw, int first, int last, int deflt) { int err; err = register_con_driver(csw, first, last); - /* if we get an busy error we still want to bind the console driver + /* + * If we get an busy error we still want to bind the console driver * and return success, as we may have unbound the console driver -  * but not unregistered it. - */ + * but not unregistered it. + */ if (err == -EBUSY) err = 0; if (!err) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index fdefa8fd72c4..4bd7820cf4d0 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -529,6 +529,33 @@ static int search_for_mapped_con(void) return retval; } +static int do_fbcon_takeover(int show_logo) +{ + int err, i; + + if (!num_registered_fb) + return -ENODEV; + + if (!show_logo) + logo_shown = FBCON_LOGO_DONTSHOW; + + for (i = first_fb_vc; i <= last_fb_vc; i++) + con2fb_map[i] = info_idx; + + err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc, + fbcon_is_default); + + if (err) { + for (i = first_fb_vc; i <= last_fb_vc; i++) + con2fb_map[i] = -1; + info_idx = -1; + } else { + fbcon_has_console_bind = 1; + } + + return err; +} + static int fbcon_takeover(int show_logo) { int err, i; @@ -3115,7 +3142,7 @@ static int fbcon_fb_registered(struct fb_info *info) } if (info_idx != -1) - ret = fbcon_takeover(1); + ret = do_fbcon_takeover(1); } else { for (i = first_fb_vc; i <= last_fb_vc; i++) { if (con2fb_map_boot[i] == idx) diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 3ff0105a496a..d8d9831b3fdb 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1650,7 +1650,9 @@ static int do_register_framebuffer(struct fb_info *fb_info) event.info = fb_info; if (!lock_fb_info(fb_info)) return -ENODEV; + console_lock(); fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); + console_unlock(); unlock_fb_info(fb_info); return 0; } @@ -1853,11 +1855,8 @@ int fb_new_modelist(struct fb_info *info) err = 1; if (!list_empty(&info->modelist)) { - if (!lock_fb_info(info)) - return -ENODEV; event.info = info; err = fb_notifier_call_chain(FB_EVENT_NEW_MODELIST, &event); - unlock_fb_info(info); } return err; diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index a55e3669d135..ef476b02fbe5 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -177,6 +177,8 @@ static ssize_t store_modes(struct device *device, if (i * sizeof(struct fb_videomode) != count) return -EINVAL; + if (!lock_fb_info(fb_info)) + return -ENODEV; console_lock(); list_splice(&fb_info->modelist, &old_list); fb_videomode_to_modelist((const struct fb_videomode *)buf, i, @@ -188,6 +190,7 @@ static ssize_t store_modes(struct device *device, fb_destroy_modelist(&old_list); console_unlock(); + unlock_fb_info(fb_info); return 0; } diff --git a/include/linux/console.h b/include/linux/console.h index dedb082fe50f..4ef4307dfb82 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -78,6 +78,7 @@ int con_is_bound(const struct consw *csw); int register_con_driver(const struct consw *csw, int first, int last); int unregister_con_driver(const struct consw *csw); int take_over_console(const struct consw *sw, int first, int last, int deflt); +int do_take_over_console(const struct consw *sw, int first, int last, int deflt); void give_up_console(const struct consw *sw); #ifdef CONFIG_HW_CONSOLE int con_debug_enter(struct vc_data *vc); From e93a9a868792ad71cdd09d75e5a02d8067473c4e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 25 Jan 2013 10:28:18 +1000 Subject: [PATCH 2112/4607] fb: Yet another band-aid for fixing lockdep mess I've still got lockdep warnings even after Alan's patch, and it seems that yet more band aids are required to paper over similar paths for unbind_con_driver() and unregister_con_driver(). After this hack, lockdep warnings are finally gone. Signed-off-by: Takashi Iwai Cc: Alan Cox Cc: Florian Tobias Schandinat Cc: Jiri Kosina Cc: stable Tested-by: Sedat Dilek Signed-off-by: Andrew Morton Signed-off-by: Dave Airlie --- drivers/tty/vt/vt.c | 43 +++++++++++++++++++++++------------ drivers/video/console/fbcon.c | 4 ++-- drivers/video/fbmem.c | 4 ++++ include/linux/console.h | 1 + include/linux/vt_kern.h | 2 ++ 5 files changed, 37 insertions(+), 17 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index c076af0b300b..457c41fe7eb9 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3134,6 +3134,18 @@ static int con_is_graphics(const struct consw *csw, int first, int last) * or 0 on success. */ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) +{ + int retval; + + console_lock(); + retval = do_unbind_con_driver(csw, first, last, deflt); + console_unlock(); + return retval; +} +EXPORT_SYMBOL(unbind_con_driver); + +/* unlocked version of unbind_con_driver() */ +int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt) { struct module *owner = csw->owner; const struct consw *defcsw = NULL; @@ -3143,7 +3155,7 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) if (!try_module_get(owner)) return -ENODEV; - console_lock(); + WARN_CONSOLE_UNLOCKED(); /* check if driver is registered and if it is unbindable */ for (i = 0; i < MAX_NR_CON_DRIVER; i++) { @@ -3156,10 +3168,8 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) } } - if (retval) { - console_unlock(); + if (retval) goto err; - } retval = -ENODEV; @@ -3175,15 +3185,11 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) } } - if (retval) { - console_unlock(); + if (retval) goto err; - } - if (!con_is_bound(csw)) { - console_unlock(); + if (!con_is_bound(csw)) goto err; - } first = max(first, con_driver->first); last = min(last, con_driver->last); @@ -3212,13 +3218,12 @@ int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) /* ignore return value, binding should not fail */ do_bind_con_driver(defcsw, first, last, deflt); - console_unlock(); err: module_put(owner); return retval; } -EXPORT_SYMBOL(unbind_con_driver); +EXPORT_SYMBOL_GPL(do_unbind_con_driver); static int vt_bind(struct con_driver *con) { @@ -3605,9 +3610,18 @@ EXPORT_SYMBOL(register_con_driver); */ int unregister_con_driver(const struct consw *csw) { - int i, retval = -ENODEV; + int retval; console_lock(); + retval = do_unregister_con_driver(csw); + console_unlock(); + return retval; +} +EXPORT_SYMBOL(unregister_con_driver); + +int do_unregister_con_driver(const struct consw *csw) +{ + int i, retval = -ENODEV; /* cannot unregister a bound driver */ if (con_is_bound(csw)) @@ -3633,10 +3647,9 @@ int unregister_con_driver(const struct consw *csw) } } err: - console_unlock(); return retval; } -EXPORT_SYMBOL(unregister_con_driver); +EXPORT_SYMBOL_GPL(do_unregister_con_driver); /* * If we support more console drivers, this function is used diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 4bd7820cf4d0..2aef9cac4d18 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -3004,7 +3004,7 @@ static int fbcon_unbind(void) { int ret; - ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, + ret = do_unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, fbcon_is_default); if (!ret) @@ -3077,7 +3077,7 @@ static int fbcon_fb_unregistered(struct fb_info *info) primary_device = -1; if (!num_registered_fb) - unregister_con_driver(&fb_con); + do_unregister_con_driver(&fb_con); return 0; } diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index d8d9831b3fdb..070b9a13d892 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1668,8 +1668,10 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) if (!lock_fb_info(fb_info)) return -ENODEV; + console_lock(); event.info = fb_info; ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); + console_unlock(); unlock_fb_info(fb_info); if (ret) @@ -1684,7 +1686,9 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) num_registered_fb--; fb_cleanup_device(fb_info); event.info = fb_info; + console_lock(); fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); + console_unlock(); /* this may free fb info */ put_fb_info(fb_info); diff --git a/include/linux/console.h b/include/linux/console.h index 4ef4307dfb82..47b858cffc47 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -77,6 +77,7 @@ extern const struct consw prom_con; /* SPARC PROM console */ int con_is_bound(const struct consw *csw); int register_con_driver(const struct consw *csw, int first, int last); int unregister_con_driver(const struct consw *csw); +int do_unregister_con_driver(const struct consw *csw); int take_over_console(const struct consw *sw, int first, int last, int deflt); int do_take_over_console(const struct consw *sw, int first, int last, int deflt); void give_up_console(const struct consw *sw); diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 50ae7d0c279e..dbbc6bfee71f 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -130,6 +130,8 @@ void vt_event_post(unsigned int event, unsigned int old, unsigned int new); int vt_waitactive(int n); void change_console(struct vc_data *new_vc); void reset_vc(struct vc_data *vc); +extern int do_unbind_con_driver(const struct consw *csw, int first, int last, + int deflt); extern int unbind_con_driver(const struct consw *csw, int first, int last, int deflt); int vty_init(const struct file_operations *console_fops); From 054430e773c9a1e26f38e30156eff02dedfffc17 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 25 Jan 2013 11:38:56 +1000 Subject: [PATCH 2113/4607] fbcon: fix locking harder Okay so Alan's patch handled the case where there was no registered fbcon, however the other path entered in set_con2fb_map pit. In there we called fbcon_takeover, but we also took the console lock in a couple of places. So push the console lock out to the callers of set_con2fb_map, this means fbmem and switcheroo needed to take the lock around the fb notifier entry points that lead to this. This should fix the efifb regression seen by Maarten. Tested-by: Maarten Lankhorst Tested-by: Lu Hua Signed-off-by: Dave Airlie --- drivers/gpu/vga/vga_switcheroo.c | 3 +++ drivers/video/console/fbcon.c | 11 ++++++++--- drivers/video/fbmem.c | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index fa60add0ff63..cf787e1d9322 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -337,8 +338,10 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) if (new_client->fb_info) { struct fb_event event; + console_lock(); event.info = new_client->fb_info; fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event); + console_unlock(); } ret = vgasr_priv.handler->switchto(new_client->id); diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 2aef9cac4d18..2e2d959acae6 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -842,6 +842,8 @@ static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, * * Maps a virtual console @unit to a frame buffer device * @newidx. + * + * This should be called with the console lock held. */ static int set_con2fb_map(int unit, int newidx, int user) { @@ -859,7 +861,7 @@ static int set_con2fb_map(int unit, int newidx, int user) if (!search_for_mapped_con() || !con_is_bound(&fb_con)) { info_idx = newidx; - return fbcon_takeover(0); + return do_fbcon_takeover(0); } if (oldidx != -1) @@ -867,7 +869,6 @@ static int set_con2fb_map(int unit, int newidx, int user) found = search_fb_in_map(newidx); - console_lock(); con2fb_map[unit] = newidx; if (!err && !found) err = con2fb_acquire_newinfo(vc, info, unit, oldidx); @@ -894,7 +895,6 @@ static int set_con2fb_map(int unit, int newidx, int user) if (!search_fb_in_map(info_idx)) info_idx = newidx; - console_unlock(); return err; } @@ -3019,6 +3019,7 @@ static inline int fbcon_unbind(void) } #endif /* CONFIG_VT_HW_CONSOLE_BINDING */ +/* called with console_lock held */ static int fbcon_fb_unbind(int idx) { int i, new_idx = -1, ret = 0; @@ -3045,6 +3046,7 @@ static int fbcon_fb_unbind(int idx) return ret; } +/* called with console_lock held */ static int fbcon_fb_unregistered(struct fb_info *info) { int i, idx; @@ -3082,6 +3084,7 @@ static int fbcon_fb_unregistered(struct fb_info *info) return 0; } +/* called with console_lock held */ static void fbcon_remap_all(int idx) { int i; @@ -3126,6 +3129,7 @@ static inline void fbcon_select_primary(struct fb_info *info) } #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */ +/* called with console_lock held */ static int fbcon_fb_registered(struct fb_info *info) { int ret = 0, i, idx; @@ -3278,6 +3282,7 @@ static int fbcon_event_notify(struct notifier_block *self, ret = fbcon_fb_unregistered(info); break; case FB_EVENT_SET_CONSOLE_MAP: + /* called with console lock held */ con2fb = event->data; ret = set_con2fb_map(con2fb->console - 1, con2fb->framebuffer, 1); diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 070b9a13d892..dc61c12ecf8c 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -1177,8 +1177,10 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, event.data = &con2fb; if (!lock_fb_info(info)) return -ENODEV; + console_lock(); event.info = info; ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event); + console_unlock(); unlock_fb_info(info); break; case FBIOBLANK: From 5845b81bdad374f98f809a658ec747d92c9595c4 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 8 Feb 2013 12:07:01 +1000 Subject: [PATCH 2114/4607] Revert "Revert "console: implement lockdep support for console_lock"" This reverts commit ff0d05bf73620eb7dc8aee7423e992ef87870bdf. Now that we have all the locking fixes in place, we can revert the revert. This re-enables lockdep tracking for the console lock, daee779718a319ff9f83e1ba3339334ac650bb22. Signed-off-by: Dave Airlie --- kernel/printk.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kernel/printk.c b/kernel/printk.c index 267ce780abe8..357f714ddd49 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -87,6 +87,12 @@ static DEFINE_SEMAPHORE(console_sem); struct console *console_drivers; EXPORT_SYMBOL_GPL(console_drivers); +#ifdef CONFIG_LOCKDEP +static struct lockdep_map console_lock_dep_map = { + .name = "console_lock" +}; +#endif + /* * This is used for debugging the mess that is the VT code by * keeping track if we have the console semaphore held. It's @@ -1918,6 +1924,7 @@ void console_lock(void) return; console_locked = 1; console_may_schedule = 1; + mutex_acquire(&console_lock_dep_map, 0, 0, _RET_IP_); } EXPORT_SYMBOL(console_lock); @@ -1939,6 +1946,7 @@ int console_trylock(void) } console_locked = 1; console_may_schedule = 0; + mutex_acquire(&console_lock_dep_map, 0, 1, _RET_IP_); return 1; } EXPORT_SYMBOL(console_trylock); @@ -2099,6 +2107,7 @@ skip: local_irq_restore(flags); } console_locked = 0; + mutex_release(&console_lock_dep_map, 1, _RET_IP_); /* Release the exclusive_console once it is used */ if (unlikely(exclusive_console)) From 174ea471c395d70ee0c839ad59ca49f3b702de58 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 5 Feb 2013 05:07:06 +0000 Subject: [PATCH 2115/4607] powerpc: fix ics_rtas_init and start_secondary section mismatch It seems, we're fine with just annotating the two functions. Thus, this fixes the following build warnings on ppc64: WARNING: arch/powerpc/sysdev/xics/built-in.o(.text+0x1664): The function .ics_rtas_init() references the function __init .xics_register_ics(). This is often because .ics_rtas_init lacks a __init annotation or the annotation of .xics_register_ics is wrong. WARNING: arch/powerpc/sysdev/built-in.o(.text+0x6044): The function .ics_rtas_init() references the function __init .xics_register_ics(). This is often because .ics_rtas_init lacks a __init annotation or the annotation of .xics_register_ics is wrong. WARNING: arch/powerpc/kernel/built-in.o(.text+0x2db30): The function .start_secondary() references the function __cpuinit .vdso_getcpu_init(). This is often because .start_secondary lacks a __cpuinit annotation or the annotation of .vdso_getcpu_init is wrong. Cc: Benjamin Herrenschmidt Signed-off-by: Daniel Borkmann Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/smp.c | 2 +- arch/powerpc/sysdev/xics/ics-rtas.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 793401e65088..76bd9da8cb71 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -610,7 +610,7 @@ static struct device_node *cpu_to_l2cache(int cpu) } /* Activate a secondary processor. */ -void start_secondary(void *unused) +__cpuinit void start_secondary(void *unused) { unsigned int cpu = smp_processor_id(); struct device_node *l2_cache; diff --git a/arch/powerpc/sysdev/xics/ics-rtas.c b/arch/powerpc/sysdev/xics/ics-rtas.c index c782f85cf7e4..936575d99c5c 100644 --- a/arch/powerpc/sysdev/xics/ics-rtas.c +++ b/arch/powerpc/sysdev/xics/ics-rtas.c @@ -213,7 +213,7 @@ static int ics_rtas_host_match(struct ics *ics, struct device_node *node) return !of_device_is_compatible(node, "chrp,iic"); } -int ics_rtas_init(void) +__init int ics_rtas_init(void) { ibm_get_xive = rtas_token("ibm,get-xive"); ibm_set_xive = rtas_token("ibm,set-xive"); From a1dabadebb5731a6222bd6073e2770f0d0d13b73 Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Mon, 28 Jan 2013 16:02:46 +0000 Subject: [PATCH 2116/4607] pseries/iommu: Restore_default_window does not use liobn parameter The parameter is unused, and complicates a following fix. Just remove it. Signed-off-by: Nishanth Aravamudan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/iommu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index b4bb9e141149..a8e99f9c3b41 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -884,7 +884,7 @@ static int create_ddw(struct pci_dev *dev, const u32 *ddw_avail, } static void restore_default_window(struct pci_dev *dev, - u32 ddw_restore_token, unsigned long liobn) + u32 ddw_restore_token) { struct eeh_dev *edev; u32 cfg_addr; @@ -1100,7 +1100,7 @@ out_free_prop: out_restore_window: if (ddw_restore_token) - restore_default_window(dev, ddw_restore_token, liobn); + restore_default_window(dev, ddw_restore_token); out_unlock: mutex_unlock(&direct_window_init_mutex); From 14b6f00f8a4fdec5ccd45a0710284de301a61628 Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Mon, 28 Jan 2013 16:03:58 +0000 Subject: [PATCH 2117/4607] pseries/iommu: Remove DDW on kexec pseries/iommu: remove DDW on kexec We currently insert a property in the device-tree when we successfully configure DDW for a given slot. This was meant to be an optimization to speed up kexec/kdump, so that we don't need to make the RTAS calls again to re-configured DDW in the new kernel. However, we end up tripping a plpar_tce_stuff failure on kexec/kdump because we unconditionally parse the ibm,dma-window property for the node at bus/dev setup time. This property contains the 32-bit DMA window LIOBN, which is distinct from the DDW window's. We pass that LIOBN (via iommu_table_init -> iommu_table_clear -> tce_free -> tce_freemulti_pSeriesLP) to plpar_tce_stuff, which fails because that 32-bit window is no longer present after 25ebc45b93452d0bc60271f178237123c4b26808 ("powerpc/pseries/iommu: remove default window before attempting DDW manipulation"). I believe the simplest, easiest-to-maintain fix is to just change our initcall to, rather than detecting and updating the new kernel's DDW knowledge, just remove all DDW configurations. When the drivers re-initialize, we will set everything back up as it was before. Signed-off-by: Nishanth Aravamudan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/iommu.c | 88 +++++++++++++++----------- 1 file changed, 50 insertions(+), 38 deletions(-) diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index a8e99f9c3b41..1b2a174e7c59 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -787,33 +787,68 @@ static u64 find_existing_ddw(struct device_node *pdn) return dma_addr; } +static void __restore_default_window(struct eeh_dev *edev, + u32 ddw_restore_token) +{ + u32 cfg_addr; + u64 buid; + int ret; + + /* + * Get the config address and phb buid of the PE window. + * Rely on eeh to retrieve this for us. + * Retrieve them from the pci device, not the node with the + * dma-window property + */ + cfg_addr = edev->config_addr; + if (edev->pe_config_addr) + cfg_addr = edev->pe_config_addr; + buid = edev->phb->buid; + + do { + ret = rtas_call(ddw_restore_token, 3, 1, NULL, cfg_addr, + BUID_HI(buid), BUID_LO(buid)); + } while (rtas_busy_delay(ret)); + pr_info("ibm,reset-pe-dma-windows(%x) %x %x %x returned %d\n", + ddw_restore_token, cfg_addr, BUID_HI(buid), BUID_LO(buid), ret); +} + static int find_existing_ddw_windows(void) { - int len; struct device_node *pdn; - struct direct_window *window; const struct dynamic_dma_window_prop *direct64; + const u32 *ddw_extensions; if (!firmware_has_feature(FW_FEATURE_LPAR)) return 0; for_each_node_with_property(pdn, DIRECT64_PROPNAME) { - direct64 = of_get_property(pdn, DIRECT64_PROPNAME, &len); + direct64 = of_get_property(pdn, DIRECT64_PROPNAME, NULL); if (!direct64) continue; - window = kzalloc(sizeof(*window), GFP_KERNEL); - if (!window || len < sizeof(struct dynamic_dma_window_prop)) { - kfree(window); - remove_ddw(pdn); - continue; - } + /* + * We need to ensure the IOMMU table is active when we + * return from the IOMMU setup so that the common code + * can clear the table or find the holes. To that end, + * first, remove any existing DDW configuration. + */ + remove_ddw(pdn); - window->device = pdn; - window->prop = direct64; - spin_lock(&direct_window_list_lock); - list_add(&window->list, &direct_window_list); - spin_unlock(&direct_window_list_lock); + /* + * Second, if we are running on a new enough level of + * firmware where the restore API is present, use it to + * restore the 32-bit window, which was removed in + * create_ddw. + * If the API is not present, then create_ddw couldn't + * have removed the 32-bit window in the first place, so + * removing the DDW configuration should be sufficient. + */ + ddw_extensions = of_get_property(pdn, "ibm,ddw-extensions", + NULL); + if (ddw_extensions && ddw_extensions[0] > 0) + __restore_default_window(of_node_to_eeh_dev(pdn), + ddw_extensions[1]); } return 0; @@ -886,30 +921,7 @@ static int create_ddw(struct pci_dev *dev, const u32 *ddw_avail, static void restore_default_window(struct pci_dev *dev, u32 ddw_restore_token) { - struct eeh_dev *edev; - u32 cfg_addr; - u64 buid; - int ret; - - /* - * Get the config address and phb buid of the PE window. - * Rely on eeh to retrieve this for us. - * Retrieve them from the pci device, not the node with the - * dma-window property - */ - edev = pci_dev_to_eeh_dev(dev); - cfg_addr = edev->config_addr; - if (edev->pe_config_addr) - cfg_addr = edev->pe_config_addr; - buid = edev->phb->buid; - - do { - ret = rtas_call(ddw_restore_token, 3, 1, NULL, cfg_addr, - BUID_HI(buid), BUID_LO(buid)); - } while (rtas_busy_delay(ret)); - dev_info(&dev->dev, - "ibm,reset-pe-dma-windows(%x) %x %x %x returned %d\n", - ddw_restore_token, cfg_addr, BUID_HI(buid), BUID_LO(buid), ret); + __restore_default_window(pci_dev_to_eeh_dev(dev), ddw_restore_token); } /* From 2468dcf641e4f3e1b0153e3e11ca20740b2f4ce8 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Thu, 7 Feb 2013 15:46:58 +0000 Subject: [PATCH 2118/4607] powerpc: Add support for context switching the TAR register This patch adds support for enabling and context switching the Target Address Register in Power8. The TAR is a new special purpose register that can be used for computed branches with the bctar[l] (branch conditional to TAR) instruction in the same manner as the count and link registers. Signed-off-by: Ian Munsie Signed-off-by: Matt Evans Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/cputable.h | 2 +- arch/powerpc/include/asm/processor.h | 3 +++ arch/powerpc/include/asm/reg.h | 3 +++ arch/powerpc/kernel/asm-offsets.c | 4 ++++ arch/powerpc/kernel/cpu_setup_power.S | 7 +++++++ arch/powerpc/kernel/entry_64.S | 20 ++++++++++++++++++++ 6 files changed, 38 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 06f7fb93c547..5f1938fcce40 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -414,7 +414,7 @@ extern const char *powerpc_base_platform; CPU_FTR_DSCR | CPU_FTR_SAO | \ CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ - CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_DAWR) + CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_DAWR | CPU_FTR_BCTAR) #define CPU_FTRS_CELL (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 7938658c168d..42ac53cebacd 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -257,6 +257,9 @@ struct thread_struct { int dscr_inherit; unsigned long ppr; /* used to save/restore SMT priority */ #endif +#ifdef CONFIG_PPC_BOOK3S_64 + unsigned long tar; +#endif }; #define ARCH_MIN_TASKALIGN 16 diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 1f59fbb7b054..e09ac51961d7 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -237,6 +237,9 @@ #define SPRN_HRMOR 0x139 /* Real mode offset register */ #define SPRN_HSRR0 0x13A /* Hypervisor Save/Restore 0 */ #define SPRN_HSRR1 0x13B /* Hypervisor Save/Restore 1 */ +#define SPRN_FSCR 0x099 /* Facility Status & Control Register */ +#define FSCR_TAR (1<<8) /* Enable Target Adress Register */ +#define SPRN_TAR 0x32f /* Target Address Register */ #define SPRN_LPCR 0x13E /* LPAR Control Register */ #define LPCR_VPM0 (1ul << (63-0)) #define LPCR_VPM1 (1ul << (63-1)) diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index e39ca556e87f..beddba432518 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -122,6 +122,10 @@ int main(void) DEFINE(THREAD_KVM_VCPU, offsetof(struct thread_struct, kvm_vcpu)); #endif +#ifdef CONFIG_PPC_BOOK3S_64 + DEFINE(THREAD_TAR, offsetof(struct thread_struct, tar)); +#endif + DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_LOCAL_FLAGS, offsetof(struct thread_info, local_flags)); DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); diff --git a/arch/powerpc/kernel/cpu_setup_power.S b/arch/powerpc/kernel/cpu_setup_power.S index 57cf14065aec..d29facbf9a28 100644 --- a/arch/powerpc/kernel/cpu_setup_power.S +++ b/arch/powerpc/kernel/cpu_setup_power.S @@ -56,6 +56,7 @@ _GLOBAL(__setup_cpu_power8) mfspr r3,SPRN_LPCR oris r3, r3, LPCR_AIL_3@h bl __init_LPCR + bl __init_FSCR bl __init_TLB mtlr r11 blr @@ -112,6 +113,12 @@ __init_LPCR: isync blr +__init_FSCR: + mfspr r3,SPRN_FSCR + ori r3,r3,FSCR_TAR + mtspr SPRN_FSCR,r3 + blr + __init_TLB: /* Clear the TLB */ li r6,128 diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 44c733f4f829..9ae8451bbc83 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -448,6 +448,19 @@ END_FTR_SECTION_IFSET(CPU_FTR_DSCR) std r23,_CCR(r1) std r1,KSP(r3) /* Set old stack pointer */ +#ifdef CONFIG_PPC_BOOK3S_64 +BEGIN_FTR_SECTION + /* + * Back up the TAR across context switches. Note that the TAR is not + * available for use in the kernel. (To provide this, the TAR should + * be backed up/restored on exception entry/exit instead, and be in + * pt_regs. FIXME, this should be in pt_regs anyway (for debug).) + */ + mfspr r0,SPRN_TAR + std r0,THREAD_TAR(r3) +END_FTR_SECTION_IFSET(CPU_FTR_BCTAR) +#endif + #ifdef CONFIG_SMP /* We need a sync somewhere here to make sure that if the * previous task gets rescheduled on another CPU, it sees all @@ -530,6 +543,13 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT) mr r1,r8 /* start using new stack pointer */ std r7,PACAKSAVE(r13) +#ifdef CONFIG_PPC_BOOK3S_64 +BEGIN_FTR_SECTION + ld r0,THREAD_TAR(r4) + mtspr SPRN_TAR,r0 +END_FTR_SECTION_IFSET(CPU_FTR_BCTAR) +#endif + #ifdef CONFIG_ALTIVEC BEGIN_FTR_SECTION ld r0,THREAD_VRSAVE(r4) From 6504d0d9900a2c05ea1fbab2ec008bf442993d94 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Thu, 24 Jan 2013 21:46:07 +0000 Subject: [PATCH 2119/4607] drm/i2c: give i2c it's own Kconfig Move this out of nouveau directory. As we start to add more encoder slaves used by other drivers, it makes sense to put the Kconfig bits in one place. Signed-off-by: Rob Clark Signed-off-by: Dave Airlie --- drivers/gpu/drm/Kconfig | 2 ++ drivers/gpu/drm/i2c/Kconfig | 22 ++++++++++++++++++++++ drivers/gpu/drm/nouveau/Kconfig | 23 ----------------------- 3 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 drivers/gpu/drm/i2c/Kconfig diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 3399b209260a..ed9e3af17b31 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -69,6 +69,8 @@ config DRM_KMS_CMA_HELPER help Choose this if you need the KMS CMA helper functions +source "drivers/gpu/drm/i2c/Kconfig" + config DRM_TDFX tristate "3dfx Banshee/Voodoo3+" depends on DRM && PCI diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig new file mode 100644 index 000000000000..16118363509a --- /dev/null +++ b/drivers/gpu/drm/i2c/Kconfig @@ -0,0 +1,22 @@ +menu "I2C encoder or helper chips" + depends on DRM && DRM_KMS_HELPER && I2C + +config DRM_I2C_CH7006 + tristate "Chrontel ch7006 TV encoder" + default m if DRM_NOUVEAU + help + Support for Chrontel ch7006 and similar TV encoders, found + on some nVidia video cards. + + This driver is currently only useful if you're also using + the nouveau driver. + +config DRM_I2C_SIL164 + tristate "Silicon Image sil164 TMDS transmitter" + default m if DRM_NOUVEAU + help + Support for sil164 and similar single-link (or dual-link + when used in pairs) TMDS transmitters, used in some nVidia + video cards. + +endmenu diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index 8a55beeb8bdc..47ccc1ad5405 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -52,26 +52,3 @@ config DRM_NOUVEAU_BACKLIGHT help Say Y here if you want to control the backlight of your display (e.g. a laptop panel). - -menu "I2C encoder or helper chips" - depends on DRM && DRM_KMS_HELPER && I2C - -config DRM_I2C_CH7006 - tristate "Chrontel ch7006 TV encoder" - default m if DRM_NOUVEAU - help - Support for Chrontel ch7006 and similar TV encoders, found - on some nVidia video cards. - - This driver is currently only useful if you're also using - the nouveau driver. - -config DRM_I2C_SIL164 - tristate "Silicon Image sil164 TMDS transmitter" - default m if DRM_NOUVEAU - help - Support for sil164 and similar single-link (or dual-link - when used in pairs) TMDS transmitters, used in some nVidia - video cards. - -endmenu From 89177644a7b6306e6084a89eab7e290f4bfef397 Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Tue, 15 Jan 2013 20:47:42 +0000 Subject: [PATCH 2120/4607] drm: add prime helpers Instead of reimplementing all of the dma_buf functionality in every driver, create helpers drm_prime_import and drm_prime_export that implement them in terms of new, lower-level hook functions: gem_prime_pin: callback when a buffer is created, used to pin buffers into GTT gem_prime_get_sg_table: convert a drm_gem_object to an sg_table for export gem_prime_import_sg_table: convert an sg_table into a drm_gem_object gem_prime_vmap, gem_prime_vunmap: map and unmap an object These hooks are optional; drivers can opt in by using drm_gem_prime_import and drm_gem_prime_export as the .gem_prime_import and .gem_prime_export fields of struct drm_driver. v2: - Drop .begin_cpu_access. None of the drivers this code replaces implemented it. Having it here was a leftover from when I was trying to include i915 in this rework. - Use mutex_lock instead of mutex_lock_interruptible, as these three drivers did. This patch series shouldn't change that behavior. - Rename helpers to gem_prime_get_sg_table and gem_prime_import_sg_table. Rename struct sg_table* variables to 'sgt' for clarity. - Update drm.tmpl for these new hooks. v3: - Pass the vaddr down to the driver. This lets drivers that just call vunmap on the pointer avoid having to store the pointer in their GEM private structures. - Move documentation into a /** DOC */ comment in drm_prime.c and include it in drm.tmpl with a !P line. I tried to use !F lines to include documentation of the individual functions from drmP.h, but the docproc / kernel-doc scripts barf on that file, so hopefully this is good enough for now. - apply refcount fix from commit be8a42ae60addd8b6092535c11b42d099d6470ec ("drm/prime: drop reference on imported dma-buf come from gem") Signed-off-by: Aaron Plattner Cc: Daniel Vetter Cc: David Airlie Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 4 + drivers/gpu/drm/drm_prime.c | 186 ++++++++++++++++++++++++++++++++- include/drm/drmP.h | 12 +++ 3 files changed, 201 insertions(+), 1 deletion(-) diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 6c11d77c8d02..b26de523ab70 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -743,6 +743,10 @@ char *date; These two operations are mandatory for GEM drivers that support DRM PRIME. + + DRM PRIME Helper Functions Reference +!Pdrivers/gpu/drm/drm_prime.c PRIME Helpers + GEM Objects Mapping diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 7f125738f44e..366910ddcfcb 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -53,7 +53,8 @@ * Self-importing: if userspace is using PRIME as a replacement for flink * then it will get a fd->handle request for a GEM object that it created. * Drivers should detect this situation and return back the gem object - * from the dma-buf private. + * from the dma-buf private. Prime will do this automatically for drivers that + * use the drm_gem_prime_{import,export} helpers. */ struct drm_prime_member { @@ -62,6 +63,137 @@ struct drm_prime_member { uint32_t handle; }; +static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, + enum dma_data_direction dir) +{ + struct drm_gem_object *obj = attach->dmabuf->priv; + struct sg_table *sgt; + + mutex_lock(&obj->dev->struct_mutex); + + sgt = obj->dev->driver->gem_prime_get_sg_table(obj); + + if (!IS_ERR_OR_NULL(sgt)) + dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir); + + mutex_unlock(&obj->dev->struct_mutex); + return sgt; +} + +static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach, + struct sg_table *sgt, enum dma_data_direction dir) +{ + dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir); + sg_free_table(sgt); + kfree(sgt); +} + +static void drm_gem_dmabuf_release(struct dma_buf *dma_buf) +{ + struct drm_gem_object *obj = dma_buf->priv; + + if (obj->export_dma_buf == dma_buf) { + /* drop the reference on the export fd holds */ + obj->export_dma_buf = NULL; + drm_gem_object_unreference_unlocked(obj); + } +} + +static void *drm_gem_dmabuf_vmap(struct dma_buf *dma_buf) +{ + struct drm_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->dev; + + return dev->driver->gem_prime_vmap(obj); +} + +static void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr) +{ + struct drm_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->dev; + + dev->driver->gem_prime_vunmap(obj, vaddr); +} + +static void *drm_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf, + unsigned long page_num) +{ + return NULL; +} + +static void drm_gem_dmabuf_kunmap_atomic(struct dma_buf *dma_buf, + unsigned long page_num, void *addr) +{ + +} +static void *drm_gem_dmabuf_kmap(struct dma_buf *dma_buf, + unsigned long page_num) +{ + return NULL; +} + +static void drm_gem_dmabuf_kunmap(struct dma_buf *dma_buf, + unsigned long page_num, void *addr) +{ + +} + +static int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, + struct vm_area_struct *vma) +{ + return -EINVAL; +} + +static const struct dma_buf_ops drm_gem_prime_dmabuf_ops = { + .map_dma_buf = drm_gem_map_dma_buf, + .unmap_dma_buf = drm_gem_unmap_dma_buf, + .release = drm_gem_dmabuf_release, + .kmap = drm_gem_dmabuf_kmap, + .kmap_atomic = drm_gem_dmabuf_kmap_atomic, + .kunmap = drm_gem_dmabuf_kunmap, + .kunmap_atomic = drm_gem_dmabuf_kunmap_atomic, + .mmap = drm_gem_dmabuf_mmap, + .vmap = drm_gem_dmabuf_vmap, + .vunmap = drm_gem_dmabuf_vunmap, +}; + +/** + * DOC: PRIME Helpers + * + * Drivers can implement @gem_prime_export and @gem_prime_import in terms of + * simpler APIs by using the helper functions @drm_gem_prime_export and + * @drm_gem_prime_import. These functions implement dma-buf support in terms of + * five lower-level driver callbacks: + * + * Export callbacks: + * + * - @gem_prime_pin (optional): prepare a GEM object for exporting + * + * - @gem_prime_get_sg_table: provide a scatter/gather table of pinned pages + * + * - @gem_prime_vmap: vmap a buffer exported by your driver + * + * - @gem_prime_vunmap: vunmap a buffer exported by your driver + * + * Import callback: + * + * - @gem_prime_import_sg_table (import): produce a GEM object from another + * driver's scatter/gather table + */ + +struct dma_buf *drm_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags) +{ + if (dev->driver->gem_prime_pin) { + int ret = dev->driver->gem_prime_pin(obj); + if (ret) + return ERR_PTR(ret); + } + return dma_buf_export(obj, &drm_gem_prime_dmabuf_ops, obj->size, + 0600); +} +EXPORT_SYMBOL(drm_gem_prime_export); + int drm_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd) @@ -117,6 +249,58 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, } EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); +struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf) +{ + struct dma_buf_attachment *attach; + struct sg_table *sgt; + struct drm_gem_object *obj; + int ret; + + if (!dev->driver->gem_prime_import_sg_table) + return ERR_PTR(-EINVAL); + + if (dma_buf->ops == &drm_gem_prime_dmabuf_ops) { + obj = dma_buf->priv; + if (obj->dev == dev) { + /* + * Importing dmabuf exported from out own gem increases + * refcount on gem itself instead of f_count of dmabuf. + */ + drm_gem_object_reference(obj); + dma_buf_put(dma_buf); + return obj; + } + } + + attach = dma_buf_attach(dma_buf, dev->dev); + if (IS_ERR(attach)) + return ERR_PTR(PTR_ERR(attach)); + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR_OR_NULL(sgt)) { + ret = PTR_ERR(sgt); + goto fail_detach; + } + + obj = dev->driver->gem_prime_import_sg_table(dev, dma_buf->size, sgt); + if (IS_ERR(obj)) { + ret = PTR_ERR(obj); + goto fail_unmap; + } + + obj->import_attach = attach; + + return obj; + +fail_unmap: + dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); +fail_detach: + dma_buf_detach(dma_buf, attach); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(drm_gem_prime_import); + int drm_gem_prime_fd_to_handle(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle) { diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3aaa50d64451..2d94d7413d71 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -930,6 +930,14 @@ struct drm_driver { /* import dmabuf -> GEM */ struct drm_gem_object * (*gem_prime_import)(struct drm_device *dev, struct dma_buf *dma_buf); + /* low-level interface used by drm_gem_prime_{import,export} */ + int (*gem_prime_pin)(struct drm_gem_object *obj); + struct sg_table *(*gem_prime_get_sg_table)(struct drm_gem_object *obj); + struct drm_gem_object *(*gem_prime_import_sg_table)( + struct drm_device *dev, size_t size, + struct sg_table *sgt); + void *(*gem_prime_vmap)(struct drm_gem_object *obj); + void (*gem_prime_vunmap)(struct drm_gem_object *obj, void *vaddr); /* vga arb irq handler */ void (*vgaarb_irq)(struct drm_device *dev, bool state); @@ -1562,9 +1570,13 @@ extern int drm_clients_info(struct seq_file *m, void* data); extern int drm_gem_name_info(struct seq_file *m, void *data); +extern struct dma_buf *drm_gem_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags); extern int drm_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd); +extern struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); extern int drm_gem_prime_fd_to_handle(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle); From ab9ccb96a6e6f95bcde6b8b2a524370efdbfdcd6 Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Tue, 15 Jan 2013 20:47:43 +0000 Subject: [PATCH 2121/4607] drm/nouveau: use prime helpers Simplify the Nouveau prime implementation by using the default behavior provided by drm_gem_prime_import and drm_gem_prime_export. v2: Rename functions to nouveau_gem_prime_get_sg_table and nouveau_gem_prime_import_sg_table. Signed-off-by: Aaron Plattner Cc: Daniel Vetter Cc: David Airlie Signed-off-by: Dave Airlie --- drivers/gpu/drm/nouveau/nouveau_bo.h | 1 - drivers/gpu/drm/nouveau/nouveau_drm.c | 9 +- drivers/gpu/drm/nouveau/nouveau_gem.c | 2 - drivers/gpu/drm/nouveau/nouveau_gem.h | 10 +- drivers/gpu/drm/nouveau/nouveau_prime.c | 173 +++--------------------- 5 files changed, 34 insertions(+), 161 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index 81d00fe03b56..653dbbbd4fa1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -33,7 +33,6 @@ struct nouveau_bo { int pin_refcnt; struct ttm_bo_kmap_obj dma_buf_vmap; - int vmapping_count; }; static inline struct nouveau_bo * diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 8b090f1eb51d..8e8e8ce75528 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -650,8 +650,13 @@ driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_export = nouveau_gem_prime_export, - .gem_prime_import = nouveau_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_pin = nouveau_gem_prime_pin, + .gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table, + .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, + .gem_prime_vmap = nouveau_gem_prime_vmap, + .gem_prime_vunmap = nouveau_gem_prime_vunmap, .gem_init_object = nouveau_gem_object_new, .gem_free_object = nouveau_gem_object_del, diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 8bf695c52f95..24e0aabda03c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -24,8 +24,6 @@ * */ -#include - #include #include "nouveau_drm.h" diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.h b/drivers/gpu/drm/nouveau/nouveau_gem.h index 5c1049236d22..8d7a3f0aeb86 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.h +++ b/drivers/gpu/drm/nouveau/nouveau_gem.h @@ -35,9 +35,11 @@ extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *, extern int nouveau_gem_ioctl_info(struct drm_device *, void *, struct drm_file *); -extern struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags); -extern struct drm_gem_object *nouveau_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf); +extern int nouveau_gem_prime_pin(struct drm_gem_object *); +extern struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *); +extern struct drm_gem_object *nouveau_gem_prime_import_sg_table( + struct drm_device *, size_t size, struct sg_table *); +extern void *nouveau_gem_prime_vmap(struct drm_gem_object *); +extern void nouveau_gem_prime_vunmap(struct drm_gem_object *, void *); #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c index b8e05ae38212..f53e10874cae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -22,126 +22,42 @@ * Authors: Dave Airlie */ -#include - #include #include "nouveau_drm.h" #include "nouveau_gem.h" -static struct sg_table *nouveau_gem_map_dma_buf(struct dma_buf_attachment *attachment, - enum dma_data_direction dir) +struct sg_table *nouveau_gem_prime_get_sg_table(struct drm_gem_object *obj) { - struct nouveau_bo *nvbo = attachment->dmabuf->priv; - struct drm_device *dev = nvbo->gem->dev; + struct nouveau_bo *nvbo = nouveau_gem_object(obj); int npages = nvbo->bo.num_pages; - struct sg_table *sg; - int nents; - mutex_lock(&dev->struct_mutex); - sg = drm_prime_pages_to_sg(nvbo->bo.ttm->pages, npages); - nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir); - mutex_unlock(&dev->struct_mutex); - return sg; + return drm_prime_pages_to_sg(nvbo->bo.ttm->pages, npages); } -static void nouveau_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, - struct sg_table *sg, enum dma_data_direction dir) +void *nouveau_gem_prime_vmap(struct drm_gem_object *obj) { - dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); - sg_free_table(sg); - kfree(sg); -} - -static void nouveau_gem_dmabuf_release(struct dma_buf *dma_buf) -{ - struct nouveau_bo *nvbo = dma_buf->priv; - - if (nvbo->gem->export_dma_buf == dma_buf) { - nvbo->gem->export_dma_buf = NULL; - drm_gem_object_unreference_unlocked(nvbo->gem); - } -} - -static void *nouveau_gem_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} - -static void nouveau_gem_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr) -{ - -} -static void *nouveau_gem_kmap(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} - -static void nouveau_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr) -{ - -} - -static int nouveau_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) -{ - return -EINVAL; -} - -static void *nouveau_gem_prime_vmap(struct dma_buf *dma_buf) -{ - struct nouveau_bo *nvbo = dma_buf->priv; - struct drm_device *dev = nvbo->gem->dev; + struct nouveau_bo *nvbo = nouveau_gem_object(obj); int ret; - mutex_lock(&dev->struct_mutex); - if (nvbo->vmapping_count) { - nvbo->vmapping_count++; - goto out_unlock; - } - ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.num_pages, &nvbo->dma_buf_vmap); - if (ret) { - mutex_unlock(&dev->struct_mutex); + if (ret) return ERR_PTR(ret); - } - nvbo->vmapping_count = 1; -out_unlock: - mutex_unlock(&dev->struct_mutex); + return nvbo->dma_buf_vmap.virtual; } -static void nouveau_gem_prime_vunmap(struct dma_buf *dma_buf, void *vaddr) +void nouveau_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) { - struct nouveau_bo *nvbo = dma_buf->priv; - struct drm_device *dev = nvbo->gem->dev; + struct nouveau_bo *nvbo = nouveau_gem_object(obj); - mutex_lock(&dev->struct_mutex); - nvbo->vmapping_count--; - if (nvbo->vmapping_count == 0) { - ttm_bo_kunmap(&nvbo->dma_buf_vmap); - } - mutex_unlock(&dev->struct_mutex); + ttm_bo_kunmap(&nvbo->dma_buf_vmap); } -static const struct dma_buf_ops nouveau_dmabuf_ops = { - .map_dma_buf = nouveau_gem_map_dma_buf, - .unmap_dma_buf = nouveau_gem_unmap_dma_buf, - .release = nouveau_gem_dmabuf_release, - .kmap = nouveau_gem_kmap, - .kmap_atomic = nouveau_gem_kmap_atomic, - .kunmap = nouveau_gem_kunmap, - .kunmap_atomic = nouveau_gem_kunmap_atomic, - .mmap = nouveau_gem_prime_mmap, - .vmap = nouveau_gem_prime_vmap, - .vunmap = nouveau_gem_prime_vunmap, -}; - -static int -nouveau_prime_new(struct drm_device *dev, - size_t size, - struct sg_table *sg, - struct nouveau_bo **pnvbo) +struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev, + size_t size, + struct sg_table *sg) { struct nouveau_bo *nvbo; u32 flags = 0; @@ -150,24 +66,22 @@ nouveau_prime_new(struct drm_device *dev, flags = TTM_PL_FLAG_TT; ret = nouveau_bo_new(dev, size, 0, flags, 0, 0, - sg, pnvbo); + sg, &nvbo); if (ret) - return ret; - nvbo = *pnvbo; + return ERR_PTR(ret); nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_GART; nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size); if (!nvbo->gem) { - nouveau_bo_ref(NULL, pnvbo); - return -ENOMEM; + nouveau_bo_ref(NULL, &nvbo); + return ERR_PTR(-ENOMEM); } nvbo->gem->driver_private = nvbo; - return 0; + return nvbo->gem; } -struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, int flags) +int nouveau_gem_prime_pin(struct drm_gem_object *obj) { struct nouveau_bo *nvbo = nouveau_gem_object(obj); int ret = 0; @@ -175,52 +89,7 @@ struct dma_buf *nouveau_gem_prime_export(struct drm_device *dev, /* pin buffer into GTT */ ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_TT); if (ret) - return ERR_PTR(-EINVAL); + return -EINVAL; - return dma_buf_export(nvbo, &nouveau_dmabuf_ops, obj->size, flags); + return 0; } - -struct drm_gem_object *nouveau_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf) -{ - struct dma_buf_attachment *attach; - struct sg_table *sg; - struct nouveau_bo *nvbo; - int ret; - - if (dma_buf->ops == &nouveau_dmabuf_ops) { - nvbo = dma_buf->priv; - if (nvbo->gem) { - if (nvbo->gem->dev == dev) { - drm_gem_object_reference(nvbo->gem); - dma_buf_put(dma_buf); - return nvbo->gem; - } - } - } - /* need to attach */ - attach = dma_buf_attach(dma_buf, dev->dev); - if (IS_ERR(attach)) - return ERR_PTR(PTR_ERR(attach)); - - sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); - if (IS_ERR(sg)) { - ret = PTR_ERR(sg); - goto fail_detach; - } - - ret = nouveau_prime_new(dev, dma_buf->size, sg, &nvbo); - if (ret) - goto fail_unmap; - - nvbo->gem->import_attach = attach; - - return nvbo->gem; - -fail_unmap: - dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); -fail_detach: - dma_buf_detach(dma_buf, attach); - return ERR_PTR(ret); -} - From 1e6d17a5df848cf8e483b689c6295776e9d6d997 Mon Sep 17 00:00:00 2001 From: Aaron Plattner Date: Tue, 15 Jan 2013 20:47:44 +0000 Subject: [PATCH 2122/4607] drm/radeon: use prime helpers Simplify the Radeon prime implementation by using the default behavior provided by drm_gem_prime_import and drm_gem_prime_export. v2: - Rename functions to radeon_gem_prime_get_sg_table and radeon_gem_prime_import_sg_table. - Delete the now-unused vmapping_count variable. Signed-off-by: Aaron Plattner Cc: Daniel Vetter Cc: David Airlie Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon.h | 1 - drivers/gpu/drm/radeon/radeon_drv.c | 21 ++-- drivers/gpu/drm/radeon/radeon_prime.c | 170 ++++---------------------- 3 files changed, 35 insertions(+), 157 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 59bfbd3868c9..bb43a849759b 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -350,7 +350,6 @@ struct radeon_bo { struct drm_gem_object gem_base; struct ttm_bo_kmap_obj dma_buf_vmap; - int vmapping_count; }; #define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, gem_base) diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 833484da12d9..167758488ed6 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -118,11 +118,13 @@ int radeon_mode_dumb_create(struct drm_file *file_priv, int radeon_mode_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle); -struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, - int flags); -struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf); +struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj); +struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, + size_t size, + struct sg_table *sg); +int radeon_gem_prime_pin(struct drm_gem_object *obj); +void *radeon_gem_prime_vmap(struct drm_gem_object *obj); +void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); extern long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); @@ -409,8 +411,13 @@ static struct drm_driver kms_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_export = radeon_gem_prime_export, - .gem_prime_import = radeon_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_pin = radeon_gem_prime_pin, + .gem_prime_get_sg_table = radeon_gem_prime_get_sg_table, + .gem_prime_import_sg_table = radeon_gem_prime_import_sg_table, + .gem_prime_vmap = radeon_gem_prime_vmap, + .gem_prime_vunmap = radeon_gem_prime_vunmap, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c index 26c23bb651c6..4940af7e75e6 100644 --- a/drivers/gpu/drm/radeon/radeon_prime.c +++ b/drivers/gpu/drm/radeon/radeon_prime.c @@ -28,199 +28,71 @@ #include "radeon.h" #include -#include - -static struct sg_table *radeon_gem_map_dma_buf(struct dma_buf_attachment *attachment, - enum dma_data_direction dir) +struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj) { - struct radeon_bo *bo = attachment->dmabuf->priv; - struct drm_device *dev = bo->rdev->ddev; + struct radeon_bo *bo = gem_to_radeon_bo(obj); int npages = bo->tbo.num_pages; - struct sg_table *sg; - int nents; - mutex_lock(&dev->struct_mutex); - sg = drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages); - nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir); - mutex_unlock(&dev->struct_mutex); - return sg; + return drm_prime_pages_to_sg(bo->tbo.ttm->pages, npages); } -static void radeon_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, - struct sg_table *sg, enum dma_data_direction dir) +void *radeon_gem_prime_vmap(struct drm_gem_object *obj) { - dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); - sg_free_table(sg); - kfree(sg); -} - -static void radeon_gem_dmabuf_release(struct dma_buf *dma_buf) -{ - struct radeon_bo *bo = dma_buf->priv; - - if (bo->gem_base.export_dma_buf == dma_buf) { - DRM_ERROR("unreference dmabuf %p\n", &bo->gem_base); - bo->gem_base.export_dma_buf = NULL; - drm_gem_object_unreference_unlocked(&bo->gem_base); - } -} - -static void *radeon_gem_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} - -static void radeon_gem_kunmap_atomic(struct dma_buf *dma_buf, unsigned long page_num, void *addr) -{ - -} -static void *radeon_gem_kmap(struct dma_buf *dma_buf, unsigned long page_num) -{ - return NULL; -} - -static void radeon_gem_kunmap(struct dma_buf *dma_buf, unsigned long page_num, void *addr) -{ - -} - -static int radeon_gem_prime_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) -{ - return -EINVAL; -} - -static void *radeon_gem_prime_vmap(struct dma_buf *dma_buf) -{ - struct radeon_bo *bo = dma_buf->priv; - struct drm_device *dev = bo->rdev->ddev; + struct radeon_bo *bo = gem_to_radeon_bo(obj); int ret; - mutex_lock(&dev->struct_mutex); - if (bo->vmapping_count) { - bo->vmapping_count++; - goto out_unlock; - } - ret = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->dma_buf_vmap); - if (ret) { - mutex_unlock(&dev->struct_mutex); + if (ret) return ERR_PTR(ret); - } - bo->vmapping_count = 1; -out_unlock: - mutex_unlock(&dev->struct_mutex); + return bo->dma_buf_vmap.virtual; } -static void radeon_gem_prime_vunmap(struct dma_buf *dma_buf, void *vaddr) +void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) { - struct radeon_bo *bo = dma_buf->priv; - struct drm_device *dev = bo->rdev->ddev; + struct radeon_bo *bo = gem_to_radeon_bo(obj); - mutex_lock(&dev->struct_mutex); - bo->vmapping_count--; - if (bo->vmapping_count == 0) { - ttm_bo_kunmap(&bo->dma_buf_vmap); - } - mutex_unlock(&dev->struct_mutex); + ttm_bo_kunmap(&bo->dma_buf_vmap); } -const static struct dma_buf_ops radeon_dmabuf_ops = { - .map_dma_buf = radeon_gem_map_dma_buf, - .unmap_dma_buf = radeon_gem_unmap_dma_buf, - .release = radeon_gem_dmabuf_release, - .kmap = radeon_gem_kmap, - .kmap_atomic = radeon_gem_kmap_atomic, - .kunmap = radeon_gem_kunmap, - .kunmap_atomic = radeon_gem_kunmap_atomic, - .mmap = radeon_gem_prime_mmap, - .vmap = radeon_gem_prime_vmap, - .vunmap = radeon_gem_prime_vunmap, -}; -static int radeon_prime_create(struct drm_device *dev, - size_t size, - struct sg_table *sg, - struct radeon_bo **pbo) +struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, + size_t size, + struct sg_table *sg) { struct radeon_device *rdev = dev->dev_private; struct radeon_bo *bo; int ret; ret = radeon_bo_create(rdev, size, PAGE_SIZE, false, - RADEON_GEM_DOMAIN_GTT, sg, pbo); + RADEON_GEM_DOMAIN_GTT, sg, &bo); if (ret) - return ret; - bo = *pbo; + return ERR_PTR(ret); bo->gem_base.driver_private = bo; mutex_lock(&rdev->gem.mutex); list_add_tail(&bo->list, &rdev->gem.objects); mutex_unlock(&rdev->gem.mutex); - return 0; + return &bo->gem_base; } -struct dma_buf *radeon_gem_prime_export(struct drm_device *dev, - struct drm_gem_object *obj, - int flags) +int radeon_gem_prime_pin(struct drm_gem_object *obj) { struct radeon_bo *bo = gem_to_radeon_bo(obj); int ret = 0; ret = radeon_bo_reserve(bo, false); if (unlikely(ret != 0)) - return ERR_PTR(ret); + return ret; /* pin buffer into GTT */ ret = radeon_bo_pin(bo, RADEON_GEM_DOMAIN_GTT, NULL); if (ret) { radeon_bo_unreserve(bo); - return ERR_PTR(ret); + return ret; } radeon_bo_unreserve(bo); - return dma_buf_export(bo, &radeon_dmabuf_ops, obj->size, flags); -} - -struct drm_gem_object *radeon_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf) -{ - struct dma_buf_attachment *attach; - struct sg_table *sg; - struct radeon_bo *bo; - int ret; - - if (dma_buf->ops == &radeon_dmabuf_ops) { - bo = dma_buf->priv; - if (bo->gem_base.dev == dev) { - drm_gem_object_reference(&bo->gem_base); - dma_buf_put(dma_buf); - return &bo->gem_base; - } - } - - /* need to attach */ - attach = dma_buf_attach(dma_buf, dev->dev); - if (IS_ERR(attach)) - return ERR_CAST(attach); - - sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); - if (IS_ERR(sg)) { - ret = PTR_ERR(sg); - goto fail_detach; - } - - ret = radeon_prime_create(dev, dma_buf->size, sg, &bo); - if (ret) - goto fail_unmap; - - bo->gem_base.import_attach = attach; - - return &bo->gem_base; - -fail_unmap: - dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); -fail_detach: - dma_buf_detach(dma_buf, attach); - return ERR_PTR(ret); + + return 0; } From 03f6509df9218c760ae74f41a609233220d33f19 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 14 Jan 2013 16:05:56 +0000 Subject: [PATCH 2123/4607] drm: Allow vblank support without DRIVER_HAVE_IRQ Drivers that register interrupt handlers without the DRM core helpers don't initialize the .irq_enabled field and drm_dev_to_irq() may fail when called on them. This shouldn't preclude them from implementing the vblank IOCTL. Signed-off-by: Thierry Reding Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_irq.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 19c01ca3cc76..71f820565637 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1218,8 +1218,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, int ret; unsigned int flags, seq, crtc, high_crtc; - if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled)) - return -EINVAL; + if (drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) + if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled)) + return -EINVAL; if (vblwait->request.type & _DRM_VBLANK_SIGNAL) return -EINVAL; From 9fe0423eac2c0781a3ece8779b086acd3e76f2c8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2013 19:10:32 +0000 Subject: [PATCH 2124/4607] drm/pci: Use the standard #defines for PCIe Link Capability bits Use the standard #defines rather than bare numbers for the PCIe Link Capabilities speed bits. Signed-off-by: Bjorn Helgaas Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 754bc96e10c7..11c8adde40b0 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -504,9 +504,9 @@ int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) *mask |= DRM_PCIE_SPEED_80; } else { - if (lnkcap & 1) + if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB) *mask |= DRM_PCIE_SPEED_25; - if (lnkcap & 2) + if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB) *mask |= DRM_PCIE_SPEED_50; } From f8acf6f4c8fe1fd4de1f669ac6a3c71e89f13523 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2013 19:10:37 +0000 Subject: [PATCH 2125/4607] drm/pci: Set all supported speeds in speed cap mask for pre-3.0 devices For devices that conform to PCIe r3.0 and have a Link Capabilities 2 register, we test and report every bit in the Supported Link Speeds Vector field. For a device that supports both 2.5GT/s and 5.0GT/s, we set both DRM_PCIE_SPEED_25 and DRM_PCIE_SPEED_50 in the returned mask. For pre-r3.0 devices, the Link Capabilities 0010b encoding (PCI_EXP_LNKCAP_SLS_5_0GB) means that both 5.0GT/s and 2.5GT/s are supported, so set both DRM_PCIE_SPEED_25 and DRM_PCIE_SPEED_50 in this case as well. Signed-off-by: Bjorn Helgaas Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 11c8adde40b0..50e26f2d198e 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -507,7 +507,7 @@ int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB) *mask |= DRM_PCIE_SPEED_25; if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB) - *mask |= DRM_PCIE_SPEED_50; + *mask |= (DRM_PCIE_SPEED_25 | DRM_PCIE_SPEED_50); } DRM_INFO("probing gen 2 caps for device %x:%x = %x/%x\n", root->vendor, root->device, lnkcap, lnkcap2); From dd66cc2e1f4765d0e6f39eb1e7d8d64d3f1cc522 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 4 Jan 2013 19:10:42 +0000 Subject: [PATCH 2126/4607] drm/pci: Use PCI Express Capability accessors Use PCI Express Capability access functions to simplify this code a bit. For non-PCIe devices or pre-PCIe 3.0 devices that don't implement the Link Capabilities 2 register, pcie_capability_read_dword() reads a zero. Since we're only testing whether the bits we care about are set, there's no need to mask out the other bits we *don't* care about. Signed-off-by: Bjorn Helgaas Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_pci.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 50e26f2d198e..86102a08f65c 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -469,41 +469,30 @@ EXPORT_SYMBOL(drm_pci_exit); int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) { struct pci_dev *root; - int pos; - u32 lnkcap = 0, lnkcap2 = 0; + u32 lnkcap, lnkcap2; *mask = 0; if (!dev->pdev) return -EINVAL; - if (!pci_is_pcie(dev->pdev)) - return -EINVAL; - root = dev->pdev->bus->self; - pos = pci_pcie_cap(root); - if (!pos) - return -EINVAL; - /* we've been informed via and serverworks don't make the cut */ if (root->vendor == PCI_VENDOR_ID_VIA || root->vendor == PCI_VENDOR_ID_SERVERWORKS) return -EINVAL; - pci_read_config_dword(root, pos + PCI_EXP_LNKCAP, &lnkcap); - pci_read_config_dword(root, pos + PCI_EXP_LNKCAP2, &lnkcap2); + pcie_capability_read_dword(root, PCI_EXP_LNKCAP, &lnkcap); + pcie_capability_read_dword(root, PCI_EXP_LNKCAP2, &lnkcap2); - lnkcap &= PCI_EXP_LNKCAP_SLS; - lnkcap2 &= 0xfe; - - if (lnkcap2) { /* PCIE GEN 3.0 */ + if (lnkcap2) { /* PCIe r3.0-compliant */ if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) *mask |= DRM_PCIE_SPEED_25; if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) *mask |= DRM_PCIE_SPEED_50; if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) *mask |= DRM_PCIE_SPEED_80; - } else { + } else { /* pre-r3.0 */ if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB) *mask |= DRM_PCIE_SPEED_25; if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB) From 85a7ce67f3ebfd5975ffd1febcabfe4999ca911d Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Thu, 27 Dec 2012 01:01:46 +0000 Subject: [PATCH 2127/4607] drm: make frame duration time calculation more precise It is a bit more precise to compute the total number of pixels first and then divide, rather than multiplying the line pixel count by the already-rounded line duration. Signed-off-by: Daniel Kurtz Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_irq.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 71f820565637..38e79927b2d7 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -505,6 +505,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc) /* Valid dotclock? */ if (dotclock > 0) { + int frame_size; /* Convert scanline length in pixels and video dot clock to * line duration, frame duration and pixel duration in * nanoseconds: @@ -512,7 +513,10 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc) pixeldur_ns = (s64) div64_u64(1000000000, dotclock); linedur_ns = (s64) div64_u64(((u64) crtc->hwmode.crtc_htotal * 1000000000), dotclock); - framedur_ns = (s64) crtc->hwmode.crtc_vtotal * linedur_ns; + frame_size = crtc->hwmode.crtc_htotal * + crtc->hwmode.crtc_vtotal; + framedur_ns = (s64) div64_u64((u64) frame_size * 1000000000, + dotclock); } else DRM_ERROR("crtc %d: Can't calculate constants, dotclock = 0!\n", crtc->base.id); From 53dc9a67769d0a9733adb5156adfc07edcbc1ea3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 25 Jan 2013 16:01:21 -0500 Subject: [PATCH 2128/4607] f2fs: init_dent_inode() should take qstr for one thing, it doesn't (and shouldn't) use anything else from dentry; for another, on some call chains the dentry is fake and should be eliminated completely. Signed-off-by: Al Viro --- fs/f2fs/dir.c | 10 +++++----- fs/f2fs/f2fs.h | 2 +- fs/f2fs/node.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 951ed52748f6..51332291b4bd 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -265,7 +265,7 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, mutex_unlock_op(sbi, DENTRY_OPS); } -void init_dent_inode(struct dentry *dentry, struct page *ipage) +void init_dent_inode(const struct qstr *name, struct page *ipage) { struct f2fs_node *rn; @@ -274,10 +274,10 @@ void init_dent_inode(struct dentry *dentry, struct page *ipage) wait_on_page_writeback(ipage); - /* copy dentry info. to this inode page */ + /* copy name info. to this inode page */ rn = (struct f2fs_node *)page_address(ipage); - rn->i.i_namelen = cpu_to_le32(dentry->d_name.len); - memcpy(rn->i.i_name, dentry->d_name.name, dentry->d_name.len); + rn->i.i_namelen = cpu_to_le32(name->len); + memcpy(rn->i.i_name, name->name, name->len); set_page_dirty(ipage); } @@ -310,7 +310,7 @@ static int init_inode_metadata(struct inode *inode, struct dentry *dentry) if (IS_ERR(ipage)) return PTR_ERR(ipage); set_cold_node(inode, ipage); - init_dent_inode(dentry, ipage); + init_dent_inode(&dentry->d_name, ipage); f2fs_put_page(ipage, 1); } if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) { diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 13c6dfbb7183..732862596311 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -867,7 +867,7 @@ struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); ino_t f2fs_inode_by_name(struct inode *, struct qstr *); void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, struct page *, struct inode *); -void init_dent_inode(struct dentry *, struct page *); +void init_dent_inode(const struct qstr *, struct page *); int f2fs_add_link(struct dentry *, struct inode *); void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *); int f2fs_make_empty(struct inode *, struct inode *); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 5066bfd256c9..5caa94676f60 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -790,7 +790,7 @@ int new_inode_page(struct inode *inode, struct dentry *dentry) set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); mutex_lock_op(sbi, NODE_NEW); page = new_node_page(&dn, 0); - init_dent_inode(dentry, page); + init_dent_inode(&dentry->d_name, page); mutex_unlock_op(sbi, NODE_NEW); if (IS_ERR(page)) return PTR_ERR(page); From c004363dd6aa89f1ccbebd694f261f86db0c840a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 25 Jan 2013 16:04:58 -0500 Subject: [PATCH 2129/4607] f2fs: switch new_inode_page() from dentry to qstr Signed-off-by: Al Viro --- fs/f2fs/dir.c | 2 +- fs/f2fs/f2fs.h | 2 +- fs/f2fs/node.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 51332291b4bd..ca7948a2770d 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -287,7 +287,7 @@ static int init_inode_metadata(struct inode *inode, struct dentry *dentry) if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { int err; - err = new_inode_page(inode, dentry); + err = new_inode_page(inode, &dentry->d_name); if (err) return err; diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 732862596311..bfdc10741ff1 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -894,7 +894,7 @@ void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int); int truncate_inode_blocks(struct inode *, pgoff_t); int remove_inode_page(struct inode *); -int new_inode_page(struct inode *, struct dentry *); +int new_inode_page(struct inode *, const struct qstr *); struct page *new_node_page(struct dnode_of_data *, unsigned int); void ra_node_page(struct f2fs_sb_info *, nid_t); struct page *get_node_page(struct f2fs_sb_info *, pgoff_t); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 5caa94676f60..6625ca819716 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -780,7 +780,7 @@ int remove_inode_page(struct inode *inode) return 0; } -int new_inode_page(struct inode *inode, struct dentry *dentry) +int new_inode_page(struct inode *inode, const struct qstr *name) { struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct page *page; @@ -790,7 +790,7 @@ int new_inode_page(struct inode *inode, struct dentry *dentry) set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); mutex_lock_op(sbi, NODE_NEW); page = new_node_page(&dn, 0); - init_dent_inode(&dentry->d_name, page); + init_dent_inode(name, page); mutex_unlock_op(sbi, NODE_NEW); if (IS_ERR(page)) return PTR_ERR(page); From 69f24eac55725859a89c440ee2d19f36fa09e8fc Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 25 Jan 2013 16:08:53 -0500 Subject: [PATCH 2130/4607] f2fs: switch init_inode_metadata() to passing parent and name separately ... sure, it's tempting to just pass dentry. Except that we don't _have_ anything resembling a real dentry on one of the paths to it. Signed-off-by: Al Viro --- fs/f2fs/dir.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index ca7948a2770d..208a804180d6 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -281,13 +281,12 @@ void init_dent_inode(const struct qstr *name, struct page *ipage) set_page_dirty(ipage); } -static int init_inode_metadata(struct inode *inode, struct dentry *dentry) +static int init_inode_metadata(struct inode *inode, + struct inode *dir, const struct qstr *name) { - struct inode *dir = dentry->d_parent->d_inode; - if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { int err; - err = new_inode_page(inode, &dentry->d_name); + err = new_inode_page(inode, name); if (err) return err; @@ -310,7 +309,7 @@ static int init_inode_metadata(struct inode *inode, struct dentry *dentry) if (IS_ERR(ipage)) return PTR_ERR(ipage); set_cold_node(inode, ipage); - init_dent_inode(&dentry->d_name, ipage); + init_dent_inode(name, ipage); f2fs_put_page(ipage, 1); } if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) { @@ -433,7 +432,7 @@ start: ++level; goto start; add_dentry: - err = init_inode_metadata(inode, dentry); + err = init_inode_metadata(inode, dir, &dentry->d_name); if (err) goto fail; From b7f7a5e0be94d13875a1c6c9aa65eeb11a46fc1b Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 25 Jan 2013 16:15:43 -0500 Subject: [PATCH 2131/4607] f2fs: get rid of fake on-stack dentries those should never be used for a lot of reasons... Signed-off-by: Al Viro --- fs/f2fs/dir.c | 12 +++++------- fs/f2fs/f2fs.h | 8 +++++++- fs/f2fs/recovery.c | 12 +++++------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 208a804180d6..47df9252ad6a 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -370,7 +370,7 @@ next: goto next; } -int f2fs_add_link(struct dentry *dentry, struct inode *inode) +int __f2fs_add_link(struct inode *dir, const struct qstr *name, struct inode *inode) { unsigned int bit_pos; unsigned int level; @@ -379,17 +379,15 @@ int f2fs_add_link(struct dentry *dentry, struct inode *inode) f2fs_hash_t dentry_hash; struct f2fs_dir_entry *de; unsigned int nbucket, nblock; - struct inode *dir = dentry->d_parent->d_inode; struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); - const char *name = dentry->d_name.name; - size_t namelen = dentry->d_name.len; + size_t namelen = name->len; struct page *dentry_page = NULL; struct f2fs_dentry_block *dentry_blk = NULL; int slots = GET_DENTRY_SLOTS(namelen); int err = 0; int i; - dentry_hash = f2fs_dentry_hash(name, dentry->d_name.len); + dentry_hash = f2fs_dentry_hash(name->name, name->len); level = 0; current_depth = F2FS_I(dir)->i_current_depth; if (F2FS_I(dir)->chash == dentry_hash) { @@ -432,7 +430,7 @@ start: ++level; goto start; add_dentry: - err = init_inode_metadata(inode, dir, &dentry->d_name); + err = init_inode_metadata(inode, dir, name); if (err) goto fail; @@ -441,7 +439,7 @@ add_dentry: de = &dentry_blk->dentry[bit_pos]; de->hash_code = dentry_hash; de->name_len = cpu_to_le16(namelen); - memcpy(dentry_blk->filename[bit_pos], name, namelen); + memcpy(dentry_blk->filename[bit_pos], name->name, name->len); de->ino = cpu_to_le32(inode->i_ino); set_de_type(de, inode); for (i = 0; i < slots; i++) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index bfdc10741ff1..6895ecc351ed 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -868,11 +868,17 @@ ino_t f2fs_inode_by_name(struct inode *, struct qstr *); void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, struct page *, struct inode *); void init_dent_inode(const struct qstr *, struct page *); -int f2fs_add_link(struct dentry *, struct inode *); +int __f2fs_add_link(struct inode *, const struct qstr *, struct inode *); void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *); int f2fs_make_empty(struct inode *, struct inode *); bool f2fs_empty_dir(struct inode *); +static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) +{ + return __f2fs_add_link(dentry->d_parent->d_inode, &dentry->d_name, + inode); +} + /* * super.c */ diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index b571fee677d5..62000422879a 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -42,7 +42,7 @@ static int recover_dentry(struct page *ipage, struct inode *inode) { struct f2fs_node *raw_node = (struct f2fs_node *)kmap(ipage); struct f2fs_inode *raw_inode = &(raw_node->i); - struct dentry dent, parent; + struct qstr name; struct f2fs_dir_entry *de; struct page *page; struct inode *dir; @@ -57,17 +57,15 @@ static int recover_dentry(struct page *ipage, struct inode *inode) goto out; } - parent.d_inode = dir; - dent.d_parent = &parent; - dent.d_name.len = le32_to_cpu(raw_inode->i_namelen); - dent.d_name.name = raw_inode->i_name; + name.len = le32_to_cpu(raw_inode->i_namelen); + name.name = raw_inode->i_name; - de = f2fs_find_entry(dir, &dent.d_name, &page); + de = f2fs_find_entry(dir, &name, &page); if (de) { kunmap(page); f2fs_put_page(page, 0); } else { - f2fs_add_link(&dent, inode); + __f2fs_add_link(dir, &name, inode); } iput(dir); out: From 76cc1887496fe80138c6b07c37d7f81e4cf27cde Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:05:26 +0000 Subject: [PATCH 2132/4607] thermal: rcar: add Device Tree support Support for loading the Renesas R-Car thermal module via devicetree. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- .../bindings/thermal/rcar-thermal.txt | 29 +++++++++++++++++++ drivers/thermal/rcar_thermal.c | 7 +++++ 2 files changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/rcar-thermal.txt diff --git a/Documentation/devicetree/bindings/thermal/rcar-thermal.txt b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt new file mode 100644 index 000000000000..28ef498a66e5 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt @@ -0,0 +1,29 @@ +* Renesas R-Car Thermal + +Required properties: +- compatible : "renesas,rcar-thermal" +- reg : Address range of the thermal registers. + The 1st reg will be recognized as common register + if it has "interrupts". + +Option properties: + +- interrupts : use interrupt + +Example (non interrupt support): + +thermal@e61f0100 { + compatible = "renesas,rcar-thermal"; + reg = <0xe61f0100 0x38>; +}; + +Example (interrupt support): + +thermal@e61f0000 { + compatible = "renesas,rcar-thermal"; + reg = <0xe61f0000 0x14 + 0xe61f0100 0x38 + 0xe61f0200 0x38 + 0xe61f0300 0x38>; + interrupts = <0 69 4>; +}; diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 2eebcadb4c99..909bb4bb837f 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -476,9 +476,16 @@ static int rcar_thermal_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id rcar_thermal_dt_ids[] __devinitconst = { + { .compatible = "renesas,rcar-thermal", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids); + static struct platform_driver rcar_thermal_driver = { .driver = { .name = "rcar_thermal", + .of_match_table = rcar_thermal_dt_ids, }, .probe = rcar_thermal_probe, .remove = rcar_thermal_remove, From 7060aa36645c51d1205ef0e0cbf7b564f1f52f36 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Wed, 6 Feb 2013 06:35:24 +0000 Subject: [PATCH 2133/4607] thermal: Add support for the thermal sensor on Kirkwood SoCs This patch adds support for Kirkwood 88F6282 and 88F6283 thermal sensor. Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: Andrew Lunn Signed-off-by: Zhang Rui --- .../bindings/thermal/kirkwood-thermal.txt | 15 ++ drivers/thermal/Kconfig | 8 ++ drivers/thermal/Makefile | 1 + drivers/thermal/kirkwood_thermal.c | 134 ++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/kirkwood-thermal.txt create mode 100644 drivers/thermal/kirkwood_thermal.c diff --git a/Documentation/devicetree/bindings/thermal/kirkwood-thermal.txt b/Documentation/devicetree/bindings/thermal/kirkwood-thermal.txt new file mode 100644 index 000000000000..8c0f5eb86da7 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/kirkwood-thermal.txt @@ -0,0 +1,15 @@ +* Kirkwood Thermal + +This version is for Kirkwood 88F8262 & 88F6283 SoCs. Other kirkwoods +don't contain a thermal sensor. + +Required properties: +- compatible : "marvell,kirkwood-thermal" +- reg : Address range of the thermal registers + +Example: + + thermal@10078 { + compatible = "marvell,kirkwood-thermal"; + reg = <0x10078 0x4>; + }; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index e4cf7fbc3a59..5070fbee24ef 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -101,6 +101,14 @@ config RCAR_THERMAL Enable this to plug the R-Car thermal sensor driver into the Linux thermal framework +config KIRKWOOD_THERMAL + tristate "Temperature sensor on Marvell Kirkwood SoCs" + depends on ARCH_KIRKWOOD + depends on OF + help + Support for the Kirkwood thermal sensor driver into the Linux thermal + framework. Only kirkwood 88F6282 and 88F6283 have this sensor. + config EXYNOS_THERMAL tristate "Temperature sensor on Samsung EXYNOS" depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 574f5f505b9f..b32c35de67e7 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o # platform thermal drivers obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o +obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c new file mode 100644 index 000000000000..65cb4f09e8f6 --- /dev/null +++ b/drivers/thermal/kirkwood_thermal.c @@ -0,0 +1,134 @@ +/* + * Kirkwood thermal sensor driver + * + * Copyright (C) 2012 Nobuhiro Iwamatsu + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define KIRKWOOD_THERMAL_VALID_OFFSET 9 +#define KIRKWOOD_THERMAL_VALID_MASK 0x1 +#define KIRKWOOD_THERMAL_TEMP_OFFSET 10 +#define KIRKWOOD_THERMAL_TEMP_MASK 0x1FF + +/* Kirkwood Thermal Sensor Dev Structure */ +struct kirkwood_thermal_priv { + void __iomem *sensor; +}; + +static int kirkwood_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + unsigned long reg; + struct kirkwood_thermal_priv *priv = thermal->devdata; + + reg = readl_relaxed(priv->sensor); + + /* Valid check */ + if (!(reg >> KIRKWOOD_THERMAL_VALID_OFFSET) & + KIRKWOOD_THERMAL_VALID_MASK) { + dev_err(&thermal->device, + "Temperature sensor reading not valid\n"); + return -EIO; + } + + /* + * Calculate temperature. See Section 8.10.1 of the 88AP510, + * datasheet, which has the same sensor. + * Documentation/arm/Marvell/README + */ + reg = (reg >> KIRKWOOD_THERMAL_TEMP_OFFSET) & + KIRKWOOD_THERMAL_TEMP_MASK; + *temp = ((2281638UL - (7298*reg)) / 10); + + return 0; +} + +static struct thermal_zone_device_ops ops = { + .get_temp = kirkwood_get_temp, +}; + +static const struct of_device_id kirkwood_thermal_id_table[] = { + { .compatible = "marvell,kirkwood-thermal" }, + {} +}; + +static int kirkwood_thermal_probe(struct platform_device *pdev) +{ + struct thermal_zone_device *thermal = NULL; + struct kirkwood_thermal_priv *priv; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get platform resource\n"); + return -ENODEV; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->sensor = devm_request_and_ioremap(&pdev->dev, res); + if (!priv->sensor) { + dev_err(&pdev->dev, "Failed to request_ioremap memory\n"); + return -EADDRNOTAVAIL; + } + + thermal = thermal_zone_device_register("kirkwood_thermal", 0, 0, + priv, &ops, NULL, 0, 0); + if (IS_ERR(thermal)) { + dev_err(&pdev->dev, + "Failed to register thermal zone device\n"); + return PTR_ERR(thermal); + } + + platform_set_drvdata(pdev, thermal); + + return 0; +} + +static int kirkwood_thermal_exit(struct platform_device *pdev) +{ + struct thermal_zone_device *kirkwood_thermal = + platform_get_drvdata(pdev); + + thermal_zone_device_unregister(kirkwood_thermal); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +MODULE_DEVICE_TABLE(of, kirkwood_thermal_id_table); + +static struct platform_driver kirkwood_thermal_driver = { + .probe = kirkwood_thermal_probe, + .remove = kirkwood_thermal_exit, + .driver = { + .name = "kirkwood_thermal", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(kirkwood_thermal_id_table), + }, +}; + +module_platform_driver(kirkwood_thermal_driver); + +MODULE_AUTHOR("Nobuhiro Iwamatsu "); +MODULE_DESCRIPTION("kirkwood thermal driver"); +MODULE_LICENSE("GPL"); From 74ffa64c23616706381c30064a47888382bba30f Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Wed, 6 Feb 2013 06:35:26 +0000 Subject: [PATCH 2134/4607] Thermal: Dove: Add Themal sensor support for Dove. The Marvell Dove SoC has a thermal sensor. Add a driver using the thermal framework. Signed-off-by: Andrew Lunn Signed-off-by: Sebastian Hesselbarth Signed-off-by: Zhang Rui --- .../bindings/thermal/dove-thermal.txt | 18 ++ drivers/thermal/Kconfig | 8 + drivers/thermal/Makefile | 1 + drivers/thermal/dove_thermal.c | 209 ++++++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/dove-thermal.txt create mode 100644 drivers/thermal/dove_thermal.c diff --git a/Documentation/devicetree/bindings/thermal/dove-thermal.txt b/Documentation/devicetree/bindings/thermal/dove-thermal.txt new file mode 100644 index 000000000000..6f474677d472 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/dove-thermal.txt @@ -0,0 +1,18 @@ +* Dove Thermal + +This driver is for Dove SoCs which contain a thermal sensor. + +Required properties: +- compatible : "marvell,dove-thermal" +- reg : Address range of the thermal registers + +The reg properties should contain two ranges. The first is for the +three Thermal Manager registers, while the second range contains the +Thermal Diode Control Registers. + +Example: + + thermal@10078 { + compatible = "marvell,dove-thermal"; + reg = <0xd001c 0x0c>, <0xd005c 0x08>; + }; diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 5070fbee24ef..24d913aa1003 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -126,6 +126,14 @@ config EXYNOS_THERMAL_EMUL device directory to support emulation mode. With emulation mode sysfs node, you can manually input temperature to TMU for simulation purpose. +config DOVE_THERMAL + tristate "Temperature sensor on Marvell Dove SoCs" + depends on ARCH_DOVE + depends on OF + help + Support for the Dove thermal sensor driver in the Linux thermal + framework. + config DB8500_THERMAL bool "DB8500 thermal management" depends on ARCH_U8500 diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index b32c35de67e7..f5c01cce7768 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o +obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c new file mode 100644 index 000000000000..7b0bfa0e7a9c --- /dev/null +++ b/drivers/thermal/dove_thermal.c @@ -0,0 +1,209 @@ +/* + * Dove thermal sensor driver + * + * Copyright (C) 2013 Andrew Lunn + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DOVE_THERMAL_TEMP_OFFSET 1 +#define DOVE_THERMAL_TEMP_MASK 0x1FF + +/* Dove Thermal Manager Control and Status Register */ +#define PMU_TM_DISABLE_OFFS 0 +#define PMU_TM_DISABLE_MASK (0x1 << PMU_TM_DISABLE_OFFS) + +/* Dove Theraml Diode Control 0 Register */ +#define PMU_TDC0_SW_RST_MASK (0x1 << 1) +#define PMU_TDC0_SEL_VCAL_OFFS 5 +#define PMU_TDC0_SEL_VCAL_MASK (0x3 << PMU_TDC0_SEL_VCAL_OFFS) +#define PMU_TDC0_REF_CAL_CNT_OFFS 11 +#define PMU_TDC0_REF_CAL_CNT_MASK (0x1FF << PMU_TDC0_REF_CAL_CNT_OFFS) +#define PMU_TDC0_AVG_NUM_OFFS 25 +#define PMU_TDC0_AVG_NUM_MASK (0x7 << PMU_TDC0_AVG_NUM_OFFS) + +/* Dove Thermal Diode Control 1 Register */ +#define PMU_TEMP_DIOD_CTRL1_REG 0x04 +#define PMU_TDC1_TEMP_VALID_MASK (0x1 << 10) + +/* Dove Thermal Sensor Dev Structure */ +struct dove_thermal_priv { + void __iomem *sensor; + void __iomem *control; +}; + +static int dove_init_sensor(const struct dove_thermal_priv *priv) +{ + u32 reg; + u32 i; + + /* Configure the Diode Control Register #0 */ + reg = readl_relaxed(priv->control); + + /* Use average of 2 */ + reg &= ~PMU_TDC0_AVG_NUM_MASK; + reg |= (0x1 << PMU_TDC0_AVG_NUM_OFFS); + + /* Reference calibration value */ + reg &= ~PMU_TDC0_REF_CAL_CNT_MASK; + reg |= (0x0F1 << PMU_TDC0_REF_CAL_CNT_OFFS); + + /* Set the high level reference for calibration */ + reg &= ~PMU_TDC0_SEL_VCAL_MASK; + reg |= (0x2 << PMU_TDC0_SEL_VCAL_OFFS); + writel(reg, priv->control); + + /* Reset the sensor */ + reg = readl_relaxed(priv->control); + writel((reg | PMU_TDC0_SW_RST_MASK), priv->control); + writel(reg, priv->control); + + /* Enable the sensor */ + reg = readl_relaxed(priv->sensor); + reg &= ~PMU_TM_DISABLE_MASK; + writel(reg, priv->sensor); + + /* Poll the sensor for the first reading */ + for (i = 0; i < 1000000; i++) { + reg = readl_relaxed(priv->sensor); + if (reg & DOVE_THERMAL_TEMP_MASK) + break; + } + + if (i == 1000000) + return -EIO; + + return 0; +} + +static int dove_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + unsigned long reg; + struct dove_thermal_priv *priv = thermal->devdata; + + /* Valid check */ + reg = readl_relaxed(priv->control + PMU_TEMP_DIOD_CTRL1_REG); + if ((reg & PMU_TDC1_TEMP_VALID_MASK) == 0x0) { + dev_err(&thermal->device, + "Temperature sensor reading not valid\n"); + return -EIO; + } + + /* + * Calculate temperature. See Section 8.10.1 of 88AP510, + * Documentation/arm/Marvell/README + */ + reg = readl_relaxed(priv->sensor); + reg = (reg >> DOVE_THERMAL_TEMP_OFFSET) & DOVE_THERMAL_TEMP_MASK; + *temp = ((2281638UL - (7298*reg)) / 10); + + return 0; +} + +static struct thermal_zone_device_ops ops = { + .get_temp = dove_get_temp, +}; + +static const struct of_device_id dove_thermal_id_table[] = { + { .compatible = "marvell,dove-thermal" }, + {} +}; + +static int dove_thermal_probe(struct platform_device *pdev) +{ + struct thermal_zone_device *thermal = NULL; + struct dove_thermal_priv *priv; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get platform resource\n"); + return -ENODEV; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->sensor = devm_request_and_ioremap(&pdev->dev, res); + if (!priv->sensor) { + dev_err(&pdev->dev, "Failed to request_ioremap memory\n"); + return -EADDRNOTAVAIL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "Failed to get platform resource\n"); + return -ENODEV; + } + priv->control = devm_request_and_ioremap(&pdev->dev, res); + if (!priv->control) { + dev_err(&pdev->dev, "Failed to request_ioremap memory\n"); + return -EADDRNOTAVAIL; + } + + ret = dove_init_sensor(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize sensor\n"); + return ret; + } + + thermal = thermal_zone_device_register("dove_thermal", 0, 0, + priv, &ops, NULL, 0, 0); + if (IS_ERR(thermal)) { + dev_err(&pdev->dev, + "Failed to register thermal zone device\n"); + return PTR_ERR(thermal); + } + + platform_set_drvdata(pdev, thermal); + + return 0; +} + +static int dove_thermal_exit(struct platform_device *pdev) +{ + struct thermal_zone_device *dove_thermal = + platform_get_drvdata(pdev); + + thermal_zone_device_unregister(dove_thermal); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +MODULE_DEVICE_TABLE(of, dove_thermal_id_table); + +static struct platform_driver dove_thermal_driver = { + .probe = dove_thermal_probe, + .remove = dove_thermal_exit, + .driver = { + .name = "dove_thermal", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(dove_thermal_id_table), + }, +}; + +module_platform_driver(dove_thermal_driver); + +MODULE_AUTHOR("Andrew Lunn "); +MODULE_DESCRIPTION("Dove thermal driver"); +MODULE_LICENSE("GPL"); From 4f0a6847815837b63b05fc23878ba391701d8f6a Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Fri, 8 Feb 2013 01:13:06 +0000 Subject: [PATCH 2135/4607] Thermal: exynos: Add support for temperature falling interrupt. This patch introduces using temperature falling interrupt in exynos thermal driver. Former patch, it only use polling way to check whether if system themperature is fallen. However, exynos SOC also provides temperature falling interrupt way to do same things by hw. This feature is not supported in exynos4210. Acked-by: Kukjin Kim Signed-off-by: Jonghwa Lee Signed-off-by: Amit Daniel Kachhap Signed-off-by: Zhang Rui --- drivers/thermal/exynos_thermal.c | 81 +++++++++++--------- include/linux/platform_data/exynos_thermal.h | 3 + 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index cd71e24194b1..f4dd68a25022 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -94,6 +94,7 @@ #define SENSOR_NAME_LEN 16 #define MAX_TRIP_COUNT 8 #define MAX_COOLING_DEVICE 4 +#define MAX_THRESHOLD_LEVS 4 #define ACTIVE_INTERVAL 500 #define IDLE_INTERVAL 10000 @@ -133,6 +134,7 @@ struct exynos_tmu_data { struct thermal_trip_point_conf { int trip_val[MAX_TRIP_COUNT]; int trip_count; + u8 trigger_falling; }; struct thermal_cooling_conf { @@ -182,7 +184,8 @@ static int exynos_set_mode(struct thermal_zone_device *thermal, mutex_lock(&th_zone->therm_dev->lock); - if (mode == THERMAL_DEVICE_ENABLED) + if (mode == THERMAL_DEVICE_ENABLED && + !th_zone->sensor_conf->trip_data.trigger_falling) th_zone->therm_dev->polling_delay = IDLE_INTERVAL; else th_zone->therm_dev->polling_delay = 0; @@ -428,7 +431,8 @@ static void exynos_report_trigger(void) break; } - if (th_zone->mode == THERMAL_DEVICE_ENABLED) { + if (th_zone->mode == THERMAL_DEVICE_ENABLED && + !th_zone->sensor_conf->trip_data.trigger_falling) { if (i > 0) th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL; else @@ -467,7 +471,8 @@ static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name, EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0, - IDLE_INTERVAL); + sensor_conf->trip_data.trigger_falling ? + 0 : IDLE_INTERVAL); if (IS_ERR(th_zone->therm_dev)) { pr_err("Failed to register thermal zone device\n"); @@ -574,8 +579,9 @@ static int exynos_tmu_initialize(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_platform_data *pdata = data->pdata; - unsigned int status, trim_info, rising_threshold; - int ret = 0, threshold_code; + unsigned int status, trim_info; + unsigned int rising_threshold = 0, falling_threshold = 0; + int ret = 0, threshold_code, i, trigger_levs = 0; mutex_lock(&data->lock); clk_enable(data->clk); @@ -600,6 +606,11 @@ static int exynos_tmu_initialize(struct platform_device *pdev) (data->temp_error2 != 0)) data->temp_error1 = pdata->efuse_value; + /* Count trigger levels to be enabled */ + for (i = 0; i < MAX_THRESHOLD_LEVS; i++) + if (pdata->trigger_levels[i]) + trigger_levs++; + if (data->soc == SOC_ARCH_EXYNOS4210) { /* Write temperature code for threshold */ threshold_code = temp_to_code(data, pdata->threshold); @@ -609,44 +620,38 @@ static int exynos_tmu_initialize(struct platform_device *pdev) } writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); - - writeb(pdata->trigger_levels[0], - data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0); - writeb(pdata->trigger_levels[1], - data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL1); - writeb(pdata->trigger_levels[2], - data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL2); - writeb(pdata->trigger_levels[3], - data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL3); + for (i = 0; i < trigger_levs; i++) + writeb(pdata->trigger_levels[i], + data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); writel(EXYNOS4210_TMU_INTCLEAR_VAL, data->base + EXYNOS_TMU_REG_INTCLEAR); } else if (data->soc == SOC_ARCH_EXYNOS) { - /* Write temperature code for threshold */ - threshold_code = temp_to_code(data, pdata->trigger_levels[0]); - if (threshold_code < 0) { - ret = threshold_code; - goto out; + /* Write temperature code for rising and falling threshold */ + for (i = 0; i < trigger_levs; i++) { + threshold_code = temp_to_code(data, + pdata->trigger_levels[i]); + if (threshold_code < 0) { + ret = threshold_code; + goto out; + } + rising_threshold |= threshold_code << 8 * i; + if (pdata->threshold_falling) { + threshold_code = temp_to_code(data, + pdata->trigger_levels[i] - + pdata->threshold_falling); + if (threshold_code > 0) + falling_threshold |= + threshold_code << 8 * i; + } } - rising_threshold = threshold_code; - threshold_code = temp_to_code(data, pdata->trigger_levels[1]); - if (threshold_code < 0) { - ret = threshold_code; - goto out; - } - rising_threshold |= (threshold_code << 8); - threshold_code = temp_to_code(data, pdata->trigger_levels[2]); - if (threshold_code < 0) { - ret = threshold_code; - goto out; - } - rising_threshold |= (threshold_code << 16); writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); - writel(0, data->base + EXYNOS_THD_TEMP_FALL); + writel(falling_threshold, + data->base + EXYNOS_THD_TEMP_FALL); - writel(EXYNOS_TMU_CLEAR_RISE_INT|EXYNOS_TMU_CLEAR_FALL_INT, + writel(EXYNOS_TMU_CLEAR_RISE_INT | EXYNOS_TMU_CLEAR_FALL_INT, data->base + EXYNOS_TMU_REG_INTCLEAR); } out: @@ -679,6 +684,8 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on) pdata->trigger_level2_en << 8 | pdata->trigger_level1_en << 4 | pdata->trigger_level0_en; + if (pdata->threshold_falling) + interrupt_en |= interrupt_en << 16; } else { con |= EXYNOS_TMU_CORE_OFF; interrupt_en = 0; /* Disable all interrupts */ @@ -716,7 +723,8 @@ static void exynos_tmu_work(struct work_struct *work) mutex_lock(&data->lock); clk_enable(data->clk); if (data->soc == SOC_ARCH_EXYNOS) - writel(EXYNOS_TMU_CLEAR_RISE_INT, + writel(EXYNOS_TMU_CLEAR_RISE_INT | + EXYNOS_TMU_CLEAR_FALL_INT, data->base + EXYNOS_TMU_REG_INTCLEAR); else writel(EXYNOS4210_TMU_INTCLEAR_VAL, @@ -772,6 +780,7 @@ static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = { #if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412) static struct exynos_tmu_platform_data const exynos_default_tmu_data = { + .threshold_falling = 10, .trigger_levels[0] = 85, .trigger_levels[1] = 103, .trigger_levels[2] = 110, @@ -1015,6 +1024,8 @@ static int __devinit exynos_tmu_probe(struct platform_device *pdev) exynos_sensor_conf.trip_data.trip_val[i] = pdata->threshold + pdata->trigger_levels[i]; + exynos_sensor_conf.trip_data.trigger_falling = pdata->threshold_falling; + exynos_sensor_conf.cooling_data.freq_clip_count = pdata->freq_tab_count; for (i = 0; i < pdata->freq_tab_count; i++) { diff --git a/include/linux/platform_data/exynos_thermal.h b/include/linux/platform_data/exynos_thermal.h index a7bdb2f63b73..da7e6274b175 100644 --- a/include/linux/platform_data/exynos_thermal.h +++ b/include/linux/platform_data/exynos_thermal.h @@ -53,6 +53,8 @@ struct freq_clip_table { * struct exynos_tmu_platform_data * @threshold: basic temperature for generating interrupt * 25 <= threshold <= 125 [unit: degree Celsius] + * @threshold_falling: differntial value for setting threshold + * of temperature falling interrupt. * @trigger_levels: array for each interrupt levels * [unit: degree Celsius] * 0: temperature for trigger_level0 interrupt @@ -97,6 +99,7 @@ struct freq_clip_table { */ struct exynos_tmu_platform_data { u8 threshold; + u8 threshold_falling; u8 trigger_levels[4]; bool trigger_level0_en; bool trigger_level1_en; From ce760ed3f440cfa51785c64dad9b4bd87673bb11 Mon Sep 17 00:00:00 2001 From: Amit Daniel Kachhap Date: Fri, 8 Feb 2013 01:13:07 +0000 Subject: [PATCH 2136/4607] thermal: exynos: Use the new thermal trend type for quick cooling action. This patch uses the quick thermal cooling trend type macros. This is needed as exynos5 and other thermal sensors now supports only interrupt method for thresold temperature check. Acked-by: Kukjin Kim Signed-off-by: Amit Daniel Kachhap Signed-off-by: Zhang Rui --- drivers/thermal/exynos_thermal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index f4dd68a25022..64aa8b445054 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -295,7 +295,7 @@ static int exynos_bind(struct thermal_zone_device *thermal, case MONITOR_ZONE: case WARN_ZONE: if (thermal_zone_bind_cooling_device(thermal, i, cdev, - level, level)) { + level, 0)) { pr_err("error binding cdev inst %d\n", i); ret = -EINVAL; } @@ -381,9 +381,9 @@ static int exynos_get_trend(struct thermal_zone_device *thermal, return ret; if (thermal->temperature >= trip_temp) - *trend = THERMAL_TREND_RAISING; + *trend = THERMAL_TREND_RAISE_FULL; else - *trend = THERMAL_TREND_DROPPING; + *trend = THERMAL_TREND_DROP_FULL; return 0; } From 9d185d0417e518fd9cfce90ec2ad75f70771bbaa Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Fri, 8 Feb 2013 20:33:42 +0800 Subject: [PATCH 2137/4607] Thermal: rename thermal governor Kconfig option to avoid generic naming Currently, we have three Kconfig options for thermal governors, aka, CONFIG_FAIR_SHARE, CONFIG_USER_SPACE and CONFIG_STEP_WISE. But these names are too generic that may bring confusion to users. Rename them to CONFIG_THERMAL_GOV_FAIR_SHARE, CONFIG_THERMAL_GOV_USER_SPACE, CONFIG_THERMAL_GOV_STEP_WISE to avoid the generic naming. Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 12 ++++++------ drivers/thermal/Makefile | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 24d913aa1003..a764f165b589 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -29,14 +29,14 @@ choice config THERMAL_DEFAULT_GOV_STEP_WISE bool "step_wise" - select STEP_WISE + select THERMAL_GOV_STEP_WISE help Use the step_wise governor as default. This throttles the devices one step at a time. config THERMAL_DEFAULT_GOV_FAIR_SHARE bool "fair_share" - select FAIR_SHARE + select THERMAL_GOV_FAIR_SHARE help Use the fair_share governor as default. This throttles the devices based on their 'contribution' to a zone. The @@ -44,24 +44,24 @@ config THERMAL_DEFAULT_GOV_FAIR_SHARE config THERMAL_DEFAULT_GOV_USER_SPACE bool "user_space" - select USER_SPACE + select THERMAL_GOV_USER_SPACE help Select this if you want to let the user space manage the lpatform thermals. endchoice -config FAIR_SHARE +config THERMAL_GOV_FAIR_SHARE bool "Fair-share thermal governor" help Enable this to manage platform thermals using fair-share governor. -config STEP_WISE +config THERMAL_GOV_STEP_WISE bool "Step_wise thermal governor" help Enable this to manage platform thermals using a simple linear -config USER_SPACE +config THERMAL_GOV_USER_SPACE bool "User_space thermal governor" help Enable this to let the user space manage the platform thermals. diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index f5c01cce7768..d3a2b38c31e8 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -5,9 +5,9 @@ obj-$(CONFIG_THERMAL) += thermal_sys.o # governors -obj-$(CONFIG_FAIR_SHARE) += fair_share.o -obj-$(CONFIG_STEP_WISE) += step_wise.o -obj-$(CONFIG_USER_SPACE) += user_space.o +obj-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o +obj-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o +obj-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o # cpufreq cooling obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o From 9c677b9b74d4314ed7f222bf802d6d1e7585eb65 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Dec 2012 11:23:42 +0000 Subject: [PATCH 2138/4607] mfd: ab8500: prepare to handle AB8500 GPIO's IRQs correctly In an upcoming patch, the gpio-ab8500 driver will relinquish all IRQ handling capability and pass it back into the AB8500 core driver. This will aid in reducing massive code duplication within the kernel. Also, most of the functionality is already in the AB8500 core driver, as the GPIO IRQs are actually sandwiched between lots of other IRQs which the core driver already handles. All we're doing here is providing the core driver with knowledge that each GPIO has two IRQs assigned to it; one for rising and a separate one for falling. Cc: Samuel Ortiz Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/ab8500-core.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 4778bb124efe..3d5da51b6a94 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -368,16 +368,40 @@ static void ab8500_irq_mask(struct irq_data *data) int mask = 1 << (offset % 8); ab8500->mask[index] |= mask; + + /* The AB8500 GPIOs have two interrupts each (rising & falling). */ + if (offset >= AB8500_INT_GPIO6R && offset <= AB8500_INT_GPIO41R) + ab8500->mask[index + 2] |= mask; + if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R) + ab8500->mask[index + 1] |= mask; + if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R) + ab8500->mask[index] |= (mask >> 1); } static void ab8500_irq_unmask(struct irq_data *data) { struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); + unsigned int type = irqd_get_trigger_type(data); int offset = data->hwirq; int index = offset / 8; int mask = 1 << (offset % 8); - ab8500->mask[index] &= ~mask; + if (type & IRQ_TYPE_EDGE_RISING) + ab8500->mask[index] &= ~mask; + + /* The AB8500 GPIOs have two interrupts each (rising & falling). */ + if (type & IRQ_TYPE_EDGE_FALLING) { + if (offset >= AB8500_INT_GPIO6R && offset <= AB8500_INT_GPIO41R) + ab8500->mask[index + 2] &= ~mask; + else if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R) + ab8500->mask[index + 1] &= ~mask; + else if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R) + ab8500->mask[index] &= ~(mask >> 1); + else + ab8500->mask[index] &= ~mask; + } else + /* Satisfies the case where type is not set. */ + ab8500->mask[index] &= ~mask; } static struct irq_chip ab8500_irq_chip = { From e2ddf46ab4ab40a657a1808cf4e358c46ae1ba68 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 6 Feb 2013 21:54:34 +0100 Subject: [PATCH 2139/4607] mfd: ab8500: actually handle the AB8500 GPIO IRQs correctly The patch: "mfd: ab8500: prepare to handle AB8500 GPIO's IRQs correctly" altered the AB8500 IRQ mask/unmask functions such that they would handle masking on/off the falling edge IRQ if this was requested by the consumer. However the bit mask for hwirqs 43 and 44 was shifting the bit mask incorrectly, resulting in the wrong IRQ being mased/unmasked. Further while the patch would mask/unmask the correct line, when the interrupt actually came in, it would still be treated as a valid hwirq. The offsetting applied when masking/unmasking was not applied when handling the IRQ, i.e. the falling edge lines were not routed back to the rising edge lines. This fixes both cases. The end result has been tested with the SIM detect IRQ, GPIO12, hwirq 46 and 62. Cc: Samuel Ortiz Cc: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/ab8500-core.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 3d5da51b6a94..6d6666b0f1b2 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -375,7 +375,8 @@ static void ab8500_irq_mask(struct irq_data *data) if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R) ab8500->mask[index + 1] |= mask; if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R) - ab8500->mask[index] |= (mask >> 1); + /* Here the falling IRQ is one bit lower */ + ab8500->mask[index] |= (mask << 1); } static void ab8500_irq_unmask(struct irq_data *data) @@ -396,12 +397,14 @@ static void ab8500_irq_unmask(struct irq_data *data) else if (offset >= AB9540_INT_GPIO50R && offset <= AB9540_INT_GPIO54R) ab8500->mask[index + 1] &= ~mask; else if (offset == AB8540_INT_GPIO43R || offset == AB8540_INT_GPIO44R) - ab8500->mask[index] &= ~(mask >> 1); + /* Here the falling IRQ is one bit lower */ + ab8500->mask[index] &= ~(mask << 1); else ab8500->mask[index] &= ~mask; - } else + } else { /* Satisfies the case where type is not set. */ ab8500->mask[index] &= ~mask; + } } static struct irq_chip ab8500_irq_chip = { @@ -435,6 +438,19 @@ static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500, line = (i << 3) + int_bit; latch_val &= ~(1 << int_bit); + /* + * This handles the falling edge hwirqs from the GPIO + * lines. Route them back to the line registered for the + * rising IRQ, as this is merely a flag for the same IRQ + * in linux terms. + */ + if (line >= AB8500_INT_GPIO6F && line <= AB8500_INT_GPIO41F) + line -= 16; + if (line >= AB9540_INT_GPIO50F && line <= AB9540_INT_GPIO54F) + line -= 8; + if (line == AB8540_INT_GPIO43F || line == AB8540_INT_GPIO44F) + line += 1; + handle_nested_irq(ab8500->irq_base + line); } while (latch_val); From 40f6e5a2b52eda7864b7167fb5af1c310b464766 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 7 Jan 2013 12:23:48 +0000 Subject: [PATCH 2140/4607] mfd: ab8500: provide a irq_set_type() function In the AB8500 IRQ mask and unmask functions, we rely on testing for IRQ_TYPE_EDGE_RISING and IRQ_TYPE_EDGE_FALLING interrupts to physically mask and unmask the correct interrupt lines. In order for us to do that, the trigger needs to be set in the associated flags. However, unless a irq_set_type() function pointer is passed when registering the IRQ chip, the IRQ subsystem will refuse to do it. For that reason, we're providing one. Cc: Samuel Ortiz Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/ab8500-core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 6d6666b0f1b2..0f1b9b788d07 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -407,6 +407,11 @@ static void ab8500_irq_unmask(struct irq_data *data) } } +static int ab8500_irq_set_type(struct irq_data *data, unsigned int type) +{ + return 0; +} + static struct irq_chip ab8500_irq_chip = { .name = "ab8500", .irq_bus_lock = ab8500_irq_lock, @@ -414,6 +419,7 @@ static struct irq_chip ab8500_irq_chip = { .irq_mask = ab8500_irq_mask, .irq_disable = ab8500_irq_mask, .irq_unmask = ab8500_irq_unmask, + .irq_set_type = ab8500_irq_set_type, }; static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500, From 7d56a46e876aa89dcac921d3afab5cccace15e63 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 16 Jan 2013 14:49:45 +0000 Subject: [PATCH 2141/4607] mfd: ab8500: ensure new AB8500 pinctrl driver is probed correctly The old, BROKEN AB8500 GPIO driver has been revamped as a shiny new pinctrl driver and has been renamed as such. So, if we would like to make use of it, we need to register it via its new name. Cc: Samuel Ortiz Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/ab8500-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 0f1b9b788d07..4cb716a00178 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1097,7 +1097,7 @@ static struct mfd_cell ab8500_bm_devs[] = { static struct mfd_cell ab8500_devs[] = { { - .name = "ab8500-gpio", + .name = "pinctrl-ab8500", .of_compatible = "stericsson,ab8500-gpio", }, { From e64d905e28031031c52db403826cd3bfe060b181 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 16 Jan 2013 15:09:36 +0000 Subject: [PATCH 2142/4607] mfd: ab8500: allow AB9540 based devices to use ABX500 pinctrl The old AB8500 GPIO driver has been un-BROKEN and converted into a multi-platform pinctrl driver. If any AB9540 based devices wish to request any GPIO pins that it offers, they can after this patch. Cc: Samuel Ortiz Signed-off-by: Lee Jones Signed-off-by: Linus Walleij --- drivers/mfd/ab8500-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 4cb716a00178..e2a45abf726d 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1114,7 +1114,8 @@ static struct mfd_cell ab8500_devs[] = { static struct mfd_cell ab9540_devs[] = { { - .name = "ab8500-gpio", + .name = "pinctrl-ab9540", + .of_compatible = "stericsson,ab9540-gpio", }, { .name = "ab9540-usb", From 941a98ae290f1671c7ec615154ec678da70b3395 Mon Sep 17 00:00:00 2001 From: Vaibhav Bedia Date: Tue, 29 Jan 2013 16:45:00 +0530 Subject: [PATCH 2143/4607] ARM: OMAP2+: AM33XX: CM: Get rid of unnecessary header inclusions cm33xx.h unnecessarily includes a lot of header files. Get rid of these and directly include "iomap.h" which is needed to keep things compiling. Signed-off-by: Vaibhav Bedia Acked-by: Santosh Shilimkar Acked-by: Peter Korsgaard Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/cm33xx.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/arch/arm/mach-omap2/cm33xx.h b/arch/arm/mach-omap2/cm33xx.h index 5fa0b62e1a79..8009e1366ed8 100644 --- a/arch/arm/mach-omap2/cm33xx.h +++ b/arch/arm/mach-omap2/cm33xx.h @@ -17,16 +17,11 @@ #ifndef __ARCH_ARM_MACH_OMAP2_CM_33XX_H #define __ARCH_ARM_MACH_OMAP2_CM_33XX_H -#include -#include -#include -#include - #include "common.h" #include "cm.h" #include "cm-regbits-33xx.h" -#include "cm33xx.h" +#include "iomap.h" /* CM base address */ #define AM33XX_CM_BASE 0x44e00000 From 1a7cb4d9c37143ca75567ee4dc10608059c610dc Mon Sep 17 00:00:00 2001 From: Vaibhav Bedia Date: Tue, 29 Jan 2013 16:45:01 +0530 Subject: [PATCH 2144/4607] ARM: OMAP2+: AM33XX: CM/PRM: Use __ASSEMBLER__ macros in header files This is necessary to ensure that macros declared here can be reused from assembly files. Signed-off-by: Vaibhav Bedia Acked-by: Santosh Shilimkar Acked-by: Peter Korsgaard Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/cm33xx.h | 2 ++ arch/arm/mach-omap2/prm33xx.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/arm/mach-omap2/cm33xx.h b/arch/arm/mach-omap2/cm33xx.h index 8009e1366ed8..64f4bafe7bd9 100644 --- a/arch/arm/mach-omap2/cm33xx.h +++ b/arch/arm/mach-omap2/cm33xx.h @@ -376,6 +376,7 @@ #define AM33XX_CM_CEFUSE_CEFUSE_CLKCTRL AM33XX_CM_REGADDR(AM33XX_CM_CEFUSE_MOD, 0x0020) +#ifndef __ASSEMBLER__ extern bool am33xx_cm_is_clkdm_in_hwsup(s16 inst, u16 cdoffs); extern void am33xx_cm_clkdm_enable_hwsup(s16 inst, u16 cdoffs); extern void am33xx_cm_clkdm_disable_hwsup(s16 inst, u16 cdoffs); @@ -412,4 +413,5 @@ static inline int am33xx_cm_wait_module_ready(u16 inst, s16 cdoffs, } #endif +#endif /* ASSEMBLER */ #endif diff --git a/arch/arm/mach-omap2/prm33xx.h b/arch/arm/mach-omap2/prm33xx.h index 3f25c563a821..1c40373b0234 100644 --- a/arch/arm/mach-omap2/prm33xx.h +++ b/arch/arm/mach-omap2/prm33xx.h @@ -117,6 +117,7 @@ #define AM33XX_PM_CEFUSE_PWRSTST_OFFSET 0x0004 #define AM33XX_PM_CEFUSE_PWRSTST AM33XX_PRM_REGADDR(AM33XX_PRM_CEFUSE_MOD, 0x0004) +#ifndef __ASSEMBLER__ extern u32 am33xx_prm_read_reg(s16 inst, u16 idx); extern void am33xx_prm_write_reg(u32 val, s16 inst, u16 idx); extern u32 am33xx_prm_rmw_reg_bits(u32 mask, u32 bits, s16 inst, s16 idx); @@ -126,4 +127,5 @@ extern int am33xx_prm_is_hardreset_asserted(u8 shift, s16 inst, extern int am33xx_prm_assert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs); extern int am33xx_prm_deassert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs, u16 rstst_offs); +#endif /* ASSEMBLER */ #endif From ca903b6f98281d694b2ae8deec1330d4f392a67d Mon Sep 17 00:00:00 2001 From: Vaibhav Bedia Date: Tue, 29 Jan 2013 16:45:02 +0530 Subject: [PATCH 2145/4607] ARM: OMAP2+: AM33XX: hwmod: Register OCMC RAM hwmod OCMC RAM lies in the PER power domain and this memory support retention. Signed-off-by: Vaibhav Bedia Acked-by: Santosh Shilimkar Acked-by: Peter Korsgaard Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod_33xx_data.c | 47 +++++++++++++--------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c index 646c14d9fdb9..8280f1162e68 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c @@ -414,7 +414,6 @@ static struct omap_hwmod am33xx_adc_tsc_hwmod = { * - cEFUSE (doesn't fall under any ocp_if) * - clkdiv32k * - debugss - * - ocmc ram * - ocp watch point * - aes0 * - sha0 @@ -481,25 +480,6 @@ static struct omap_hwmod am33xx_debugss_hwmod = { }, }; -/* ocmcram */ -static struct omap_hwmod_class am33xx_ocmcram_hwmod_class = { - .name = "ocmcram", -}; - -static struct omap_hwmod am33xx_ocmcram_hwmod = { - .name = "ocmcram", - .class = &am33xx_ocmcram_hwmod_class, - .clkdm_name = "l3_clkdm", - .flags = (HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET), - .main_clk = "l3_gclk", - .prcm = { - .omap4 = { - .clkctrl_offs = AM33XX_CM_PER_OCMCRAM_CLKCTRL_OFFSET, - .modulemode = MODULEMODE_SWCTRL, - }, - }, -}; - /* ocpwp */ static struct omap_hwmod_class am33xx_ocpwp_hwmod_class = { .name = "ocpwp", @@ -570,6 +550,25 @@ static struct omap_hwmod am33xx_sha0_hwmod = { #endif +/* ocmcram */ +static struct omap_hwmod_class am33xx_ocmcram_hwmod_class = { + .name = "ocmcram", +}; + +static struct omap_hwmod am33xx_ocmcram_hwmod = { + .name = "ocmcram", + .class = &am33xx_ocmcram_hwmod_class, + .clkdm_name = "l3_clkdm", + .flags = (HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET), + .main_clk = "l3_gclk", + .prcm = { + .omap4 = { + .clkctrl_offs = AM33XX_CM_PER_OCMCRAM_CLKCTRL_OFFSET, + .modulemode = MODULEMODE_SWCTRL, + }, + }, +}; + /* 'smartreflex' class */ static struct omap_hwmod_class am33xx_smartreflex_hwmod_class = { .name = "smartreflex", @@ -3328,6 +3327,13 @@ static struct omap_hwmod_ocp_if am33xx_l3_s__usbss = { .flags = OCPIF_SWSUP_IDLE, }; +/* l3 main -> ocmc */ +static struct omap_hwmod_ocp_if am33xx_l3_main__ocmc = { + .master = &am33xx_l3_main_hwmod, + .slave = &am33xx_ocmcram_hwmod, + .user = OCP_USER_MPU | OCP_USER_SDMA, +}; + static struct omap_hwmod_ocp_if *am33xx_hwmod_ocp_ifs[] __initdata = { &am33xx_l4_fw__emif_fw, &am33xx_l3_main__emif, @@ -3398,6 +3404,7 @@ static struct omap_hwmod_ocp_if *am33xx_hwmod_ocp_ifs[] __initdata = { &am33xx_l3_main__tptc0, &am33xx_l3_main__tptc1, &am33xx_l3_main__tptc2, + &am33xx_l3_main__ocmc, &am33xx_l3_s__usbss, &am33xx_l4_hs__cpgmac0, &am33xx_cpgmac0__mdio, From 0bfbbded8dbd82ec252be2ea0a9e221a1b22e8ba Mon Sep 17 00:00:00 2001 From: Vaibhav Bedia Date: Tue, 29 Jan 2013 16:45:03 +0530 Subject: [PATCH 2146/4607] ARM: OMAP2+: AM33XX: hwmod: Update TPTC0 hwmod with the right flags Third Party Transfer Controller (TPTC0) needs to be idled and put to standby under SW control. Add the appropriate flags in the TPTC0 hwmod entry. Signed-off-by: Vaibhav Bedia Acked-by: Santosh Shilimkar Acked-by: Peter Korsgaard Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod_33xx_data.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c index 8280f1162e68..94254e84527f 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c @@ -1823,6 +1823,7 @@ static struct omap_hwmod am33xx_tptc0_hwmod = { .class = &am33xx_tptc_hwmod_class, .clkdm_name = "l3_clkdm", .mpu_irqs = am33xx_tptc0_irqs, + .flags = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY, .main_clk = "l3_gclk", .prcm = { .omap4 = { From f13966608f9f65dd635eb400cc93f856a595531a Mon Sep 17 00:00:00 2001 From: Vaibhav Bedia Date: Tue, 29 Jan 2013 16:45:04 +0530 Subject: [PATCH 2147/4607] ARM: OMAP2+: AM33XX: hwmod: Fixup cpgmac0 hwmod entry The current HWMOD code expects the memory region with the IP's SYSCONFIG register to be marked with ADDR_TYPE_RT flag. CPGMAC0 hwmod entry specifies two memory regions and marks both with the flag ADDR_TYPE_RT although only the 2nd region has the SYSCONFIG register. This leads to the HWMOD code accessing the wrong memory address for idle and standby operations. Fix this by removing the ADDR_TYPE_RT flag from the 1st memory region in CPGMAC0 hwmod entry. Signed-off-by: Vaibhav Bedia Acked-by: Peter Korsgaard Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod_33xx_data.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c index 94254e84527f..40bfde3fe3f8 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c @@ -2496,7 +2496,6 @@ static struct omap_hwmod_addr_space am33xx_cpgmac0_addr_space[] = { { .pa_start = 0x4a100000, .pa_end = 0x4a100000 + SZ_2K - 1, - .flags = ADDR_TYPE_RT, }, /* cpsw wr */ { From 3077fe69d7055b2ab118b299613394e13f4983a8 Mon Sep 17 00:00:00 2001 From: Vaibhav Bedia Date: Tue, 29 Jan 2013 16:45:05 +0530 Subject: [PATCH 2148/4607] ARM: OMAP2+: AM33XX: hwmod: Update the WKUP-M3 hwmod with reset status bit WKUP-M3 has a reset status bit (RM_WKUP_STST.WKUP_M3_LRST) Update the WKUP-M3 hwmod data to reflect the same. Signed-off-by: Vaibhav Bedia Acked-by: Peter Korsgaard Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod_33xx_data.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c index 40bfde3fe3f8..9e34d4cbc586 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c @@ -269,6 +269,7 @@ static struct omap_hwmod am33xx_wkup_m3_hwmod = { .omap4 = { .clkctrl_offs = AM33XX_CM_WKUP_WKUP_M3_CLKCTRL_OFFSET, .rstctrl_offs = AM33XX_RM_WKUP_RSTCTRL_OFFSET, + .rstst_offs = AM33XX_RM_WKUP_RSTST_OFFSET, .modulemode = MODULEMODE_SWCTRL, }, }, From 3c06f1b8c3ca74669b77c0aaee428b5c46d3e552 Mon Sep 17 00:00:00 2001 From: Vaibhav Bedia Date: Tue, 29 Jan 2013 16:45:06 +0530 Subject: [PATCH 2149/4607] ARM: OMAP2+: AM33XX: Update the hardreset API WKUP-M3 has a reset status bit (RM_WKUP_STST.WKUP_M3_LRST) Update the hardreset API to ensure that the reset line properly deasserted. Signed-off-by: Vaibhav Bedia Acked-by: Santosh Shilimkar Acked-by: Peter Korsgaard Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod.c | 5 +---- arch/arm/mach-omap2/prm33xx.c | 15 +++++++++------ arch/arm/mach-omap2/prm33xx.h | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 4653efb87a27..6549439d8d5f 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -3041,11 +3041,8 @@ static int _am33xx_assert_hardreset(struct omap_hwmod *oh, static int _am33xx_deassert_hardreset(struct omap_hwmod *oh, struct omap_hwmod_rst_info *ohri) { - if (ohri->st_shift) - pr_err("omap_hwmod: %s: %s: hwmod data error: OMAP4 does not support st_shift\n", - oh->name, ohri->name); - return am33xx_prm_deassert_hardreset(ohri->rst_shift, + ohri->st_shift, oh->clkdm->pwrdm.ptr->prcm_offs, oh->prcm.omap4.rstctrl_offs, oh->prcm.omap4.rstst_offs); diff --git a/arch/arm/mach-omap2/prm33xx.c b/arch/arm/mach-omap2/prm33xx.c index 1ac73883f891..44c0d7216aa7 100644 --- a/arch/arm/mach-omap2/prm33xx.c +++ b/arch/arm/mach-omap2/prm33xx.c @@ -110,11 +110,11 @@ int am33xx_prm_assert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs) * -EINVAL upon an argument error, -EEXIST if the submodule was already out * of reset, or -EBUSY if the submodule did not exit reset promptly. */ -int am33xx_prm_deassert_hardreset(u8 shift, s16 inst, +int am33xx_prm_deassert_hardreset(u8 shift, u8 st_shift, s16 inst, u16 rstctrl_offs, u16 rstst_offs) { int c; - u32 mask = 1 << shift; + u32 mask = 1 << st_shift; /* Check the current status to avoid de-asserting the line twice */ if (am33xx_prm_is_hardreset_asserted(shift, inst, rstctrl_offs) == 0) @@ -122,11 +122,14 @@ int am33xx_prm_deassert_hardreset(u8 shift, s16 inst, /* Clear the reset status by writing 1 to the status bit */ am33xx_prm_rmw_reg_bits(0xffffffff, mask, inst, rstst_offs); - /* de-assert the reset control line */ - am33xx_prm_rmw_reg_bits(mask, 0, inst, rstctrl_offs); - /* wait the status to be set */ - omap_test_timeout(am33xx_prm_is_hardreset_asserted(shift, inst, + /* de-assert the reset control line */ + mask = 1 << shift; + + am33xx_prm_rmw_reg_bits(mask, 0, inst, rstctrl_offs); + + /* wait the status to be set */ + omap_test_timeout(am33xx_prm_is_hardreset_asserted(st_shift, inst, rstst_offs), MAX_MODULE_HARDRESET_WAIT, c); diff --git a/arch/arm/mach-omap2/prm33xx.h b/arch/arm/mach-omap2/prm33xx.h index 1c40373b0234..9b9918dfb119 100644 --- a/arch/arm/mach-omap2/prm33xx.h +++ b/arch/arm/mach-omap2/prm33xx.h @@ -125,7 +125,7 @@ extern void am33xx_prm_global_warm_sw_reset(void); extern int am33xx_prm_is_hardreset_asserted(u8 shift, s16 inst, u16 rstctrl_offs); extern int am33xx_prm_assert_hardreset(u8 shift, s16 inst, u16 rstctrl_offs); -extern int am33xx_prm_deassert_hardreset(u8 shift, s16 inst, +extern int am33xx_prm_deassert_hardreset(u8 shift, u8 st_shift, s16 inst, u16 rstctrl_offs, u16 rstst_offs); #endif /* ASSEMBLER */ #endif From f6575c90f6fc637697f130ea4a05892296c9a473 Mon Sep 17 00:00:00 2001 From: Vaibhav Bedia Date: Tue, 29 Jan 2013 16:45:07 +0530 Subject: [PATCH 2150/4607] ARM: DTS: AM33XX: Add nodes for OCMC RAM and WKUP-M3 Since AM33XX supports only DT-boot, this is needed for the appropriate device nodes to be created. Note: OCMC RAM is part of the PER power domain and supports retention. The assembly code for low power entry/exit will run from OCMC RAM. To ensure that the OMAP PM code does not attempt to disable the clock to OCMC RAM as part of the suspend process add the no_idle_on_suspend flag. Signed-off-by: Vaibhav Bedia Acked-by: Santosh Shilimkar Acked-by: Peter Korsgaard Signed-off-by: Paul Walmsley --- arch/arm/boot/dts/am33xx.dtsi | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index c2f14e875eb6..0957645b73af 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -385,5 +385,19 @@ mac-address = [ 00 00 00 00 00 00 ]; }; }; + + ocmcram: ocmcram@40300000 { + compatible = "ti,am3352-ocmcram"; + reg = <0x40300000 0x10000>; + ti,hwmods = "ocmcram"; + ti,no_idle_on_suspend; + }; + + wkup_m3: wkup_m3@44d00000 { + compatible = "ti,am3353-wkup-m3"; + reg = <0x44d00000 0x4000 /* M3 UMEM */ + 0x44d80000 0x2000>; /* M3 DMEM */ + ti,hwmods = "wkup_m3"; + }; }; }; From bee76659e268ab9165e4dceee5b5410f3b22cd1c Mon Sep 17 00:00:00 2001 From: Philip Avinash Date: Wed, 2 Jan 2013 18:54:48 +0530 Subject: [PATCH 2151/4607] ARM: OMAP: AM33xx hwmod: Corrects PWM subsystem HWMOD entries EQEP IP block integration data is not present in HWMOD data. Also address ranges specified for EACP & EHRPWM are not correct & HWMOD flags of ADDR_TYPE_RT are added to PWM subsystem register address space. This patch: 1. Corrects register address mapping for ECAP & EHRPWM 2. Removes HWMOD flags in PWM submodule register address space. 3. Adds EQEP HWMOD entries. Signed-off-by: Philip Avinash [paul@pwsan.com: tweaked patch description] Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod_33xx_data.c | 158 +++++++++++++++++++-- 1 file changed, 145 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c index 9e34d4cbc586..4b1cc4d4c9a3 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c @@ -784,7 +784,7 @@ static struct omap_hwmod am33xx_elm_hwmod = { }; /* - * 'epwmss' class: ecap0,1,2, ehrpwm0,1,2 + * 'epwmss' class: ehrpwm0,1,2 eqep0,1,2 ecap0,1,2 */ static struct omap_hwmod_class_sysconfig am33xx_epwmss_sysc = { .rev_offs = 0x0, @@ -864,6 +864,66 @@ static struct omap_hwmod am33xx_ehrpwm2_hwmod = { }, }; +/* eqep0 */ +static struct omap_hwmod_irq_info am33xx_eqep0_irqs[] = { + { .irq = 79 + OMAP_INTC_START, }, + { .irq = -1 }, +}; + +static struct omap_hwmod am33xx_eqep0_hwmod = { + .name = "eqep0", + .class = &am33xx_epwmss_hwmod_class, + .clkdm_name = "l4ls_clkdm", + .mpu_irqs = am33xx_eqep0_irqs, + .main_clk = "l4ls_gclk", + .prcm = { + .omap4 = { + .clkctrl_offs = AM33XX_CM_PER_EPWMSS0_CLKCTRL_OFFSET, + .modulemode = MODULEMODE_SWCTRL, + }, + }, +}; + +/* eqep1 */ +static struct omap_hwmod_irq_info am33xx_eqep1_irqs[] = { + { .irq = 88 + OMAP_INTC_START, }, + { .irq = -1 }, +}; + +static struct omap_hwmod am33xx_eqep1_hwmod = { + .name = "eqep1", + .class = &am33xx_epwmss_hwmod_class, + .clkdm_name = "l4ls_clkdm", + .mpu_irqs = am33xx_eqep1_irqs, + .main_clk = "l4ls_gclk", + .prcm = { + .omap4 = { + .clkctrl_offs = AM33XX_CM_PER_EPWMSS1_CLKCTRL_OFFSET, + .modulemode = MODULEMODE_SWCTRL, + }, + }, +}; + +/* eqep2 */ +static struct omap_hwmod_irq_info am33xx_eqep2_irqs[] = { + { .irq = 89 + OMAP_INTC_START, }, + { .irq = -1 }, +}; + +static struct omap_hwmod am33xx_eqep2_hwmod = { + .name = "eqep2", + .class = &am33xx_epwmss_hwmod_class, + .clkdm_name = "l4ls_clkdm", + .mpu_irqs = am33xx_eqep2_irqs, + .main_clk = "l4ls_gclk", + .prcm = { + .omap4 = { + .clkctrl_offs = AM33XX_CM_PER_EPWMSS2_CLKCTRL_OFFSET, + .modulemode = MODULEMODE_SWCTRL, + }, + }, +}; + /* ecap0 */ static struct omap_hwmod_irq_info am33xx_ecap0_irqs[] = { { .irq = 31 + OMAP_INTC_START, }, @@ -2559,8 +2619,7 @@ static struct omap_hwmod_addr_space am33xx_ehrpwm0_addr_space[] = { }, { .pa_start = 0x48300200, - .pa_end = 0x48300200 + SZ_256 - 1, - .flags = ADDR_TYPE_RT + .pa_end = 0x48300200 + SZ_128 - 1, }, { } }; @@ -2585,8 +2644,7 @@ static struct omap_hwmod_addr_space am33xx_ehrpwm1_addr_space[] = { }, { .pa_start = 0x48302200, - .pa_end = 0x48302200 + SZ_256 - 1, - .flags = ADDR_TYPE_RT + .pa_end = 0x48302200 + SZ_128 - 1, }, { } }; @@ -2611,8 +2669,7 @@ static struct omap_hwmod_addr_space am33xx_ehrpwm2_addr_space[] = { }, { .pa_start = 0x48304200, - .pa_end = 0x48304200 + SZ_256 - 1, - .flags = ADDR_TYPE_RT + .pa_end = 0x48304200 + SZ_128 - 1, }, { } }; @@ -2625,6 +2682,81 @@ static struct omap_hwmod_ocp_if am33xx_l4_ls__ehrpwm2 = { .user = OCP_USER_MPU, }; +/* + * Splitting the resources to handle access of PWMSS config space + * and module specific part independently + */ +static struct omap_hwmod_addr_space am33xx_eqep0_addr_space[] = { + { + .pa_start = 0x48300000, + .pa_end = 0x48300000 + SZ_16 - 1, + .flags = ADDR_TYPE_RT + }, + { + .pa_start = 0x48300180, + .pa_end = 0x48300180 + SZ_128 - 1, + }, + { } +}; + +static struct omap_hwmod_ocp_if am33xx_l4_ls__eqep0 = { + .master = &am33xx_l4_ls_hwmod, + .slave = &am33xx_eqep0_hwmod, + .clk = "l4ls_gclk", + .addr = am33xx_eqep0_addr_space, + .user = OCP_USER_MPU, +}; + +/* + * Splitting the resources to handle access of PWMSS config space + * and module specific part independently + */ +static struct omap_hwmod_addr_space am33xx_eqep1_addr_space[] = { + { + .pa_start = 0x48302000, + .pa_end = 0x48302000 + SZ_16 - 1, + .flags = ADDR_TYPE_RT + }, + { + .pa_start = 0x48302180, + .pa_end = 0x48302180 + SZ_128 - 1, + }, + { } +}; + +static struct omap_hwmod_ocp_if am33xx_l4_ls__eqep1 = { + .master = &am33xx_l4_ls_hwmod, + .slave = &am33xx_eqep1_hwmod, + .clk = "l4ls_gclk", + .addr = am33xx_eqep1_addr_space, + .user = OCP_USER_MPU, +}; + +/* + * Splitting the resources to handle access of PWMSS config space + * and module specific part independently + */ +static struct omap_hwmod_addr_space am33xx_eqep2_addr_space[] = { + { + .pa_start = 0x48304000, + .pa_end = 0x48304000 + SZ_16 - 1, + .flags = ADDR_TYPE_RT + }, + { + .pa_start = 0x48304180, + .pa_end = 0x48304180 + SZ_128 - 1, + }, + { } +}; + +static struct omap_hwmod_ocp_if am33xx_l4_ls__eqep2 = { + .master = &am33xx_l4_ls_hwmod, + .slave = &am33xx_eqep2_hwmod, + .clk = "l4ls_gclk", + .addr = am33xx_eqep2_addr_space, + .user = OCP_USER_MPU, +}; + /* * Splitting the resources to handle access of PWMSS config space * and module specific part independently @@ -2637,8 +2769,7 @@ static struct omap_hwmod_addr_space am33xx_ecap0_addr_space[] = { }, { .pa_start = 0x48300100, - .pa_end = 0x48300100 + SZ_256 - 1, - .flags = ADDR_TYPE_RT + .pa_end = 0x48300100 + SZ_128 - 1, }, { } }; @@ -2663,8 +2794,7 @@ static struct omap_hwmod_addr_space am33xx_ecap1_addr_space[] = { }, { .pa_start = 0x48302100, - .pa_end = 0x48302100 + SZ_256 - 1, - .flags = ADDR_TYPE_RT + .pa_end = 0x48302100 + SZ_128 - 1, }, { } }; @@ -2689,8 +2819,7 @@ static struct omap_hwmod_addr_space am33xx_ecap2_addr_space[] = { }, { .pa_start = 0x48304100, - .pa_end = 0x48304100 + SZ_256 - 1, - .flags = ADDR_TYPE_RT + .pa_end = 0x48304100 + SZ_128 - 1, }, { } }; @@ -3395,6 +3524,9 @@ static struct omap_hwmod_ocp_if *am33xx_hwmod_ocp_ifs[] __initdata = { &am33xx_l4_ls__ehrpwm0, &am33xx_l4_ls__ehrpwm1, &am33xx_l4_ls__ehrpwm2, + &am33xx_l4_ls__eqep0, + &am33xx_l4_ls__eqep1, + &am33xx_l4_ls__eqep2, &am33xx_l4_ls__ecap0, &am33xx_l4_ls__ecap1, &am33xx_l4_ls__ecap2, From 7d0ee461fd173fea8ca254924263f189a3f0dc08 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Fri, 25 Jan 2013 19:10:12 -0300 Subject: [PATCH 2152/4607] [media] staging/media/solo6x10: Use PTR_RET rather than if(IS_ERR(...)) + PTR_ERR Found with coccicheck. The semantic patch that makes this change is available in scripts/coccinelle/api/ptr_ret.cocci. Signed-off-by: Peter Huewe Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/solo6x10/v4l2.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c index 571c3a348d30..ca774cc57539 100644 --- a/drivers/staging/media/solo6x10/v4l2.c +++ b/drivers/staging/media/solo6x10/v4l2.c @@ -415,10 +415,7 @@ static int solo_start_thread(struct solo_filehandle *fh) { fh->kthread = kthread_run(solo_thread, fh, SOLO6X10_NAME "_disp"); - if (IS_ERR(fh->kthread)) - return PTR_ERR(fh->kthread); - - return 0; + return PTR_RET(fh->kthread); } static void solo_stop_thread(struct solo_filehandle *fh) From 9652d19afc23b80509e23f1d7c3f37786e50e401 Mon Sep 17 00:00:00 2001 From: Philip Avinash Date: Wed, 2 Jan 2013 18:54:49 +0530 Subject: [PATCH 2153/4607] ARM: OMAP: AM33xx hwmod: Add parent-child relationship for PWM subsystem As part of PWM subsystem integration, PWM subsystem are sharing resources like clock across submodules (ECAP, EQEP & EHRPWM). To handle resource sharing & IP integration rework on parent child relation between PWMSS and ECAP, EQEP & EHRPWM child devices to support runtime PM. Signed-off-by: Philip Avinash Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod_33xx_data.c | 564 ++++++++++----------- 1 file changed, 268 insertions(+), 296 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c index 4b1cc4d4c9a3..8441538872cd 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c @@ -783,9 +783,7 @@ static struct omap_hwmod am33xx_elm_hwmod = { }, }; -/* - * 'epwmss' class: ehrpwm0,1,2 eqep0,1,2 ecap0,1,2 - */ +/* pwmss */ static struct omap_hwmod_class_sysconfig am33xx_epwmss_sysc = { .rev_offs = 0x0, .sysc_offs = 0x4, @@ -801,18 +799,23 @@ static struct omap_hwmod_class am33xx_epwmss_hwmod_class = { .sysc = &am33xx_epwmss_sysc, }; -/* ehrpwm0 */ -static struct omap_hwmod_irq_info am33xx_ehrpwm0_irqs[] = { - { .name = "int", .irq = 86 + OMAP_INTC_START, }, - { .name = "tzint", .irq = 58 + OMAP_INTC_START, }, - { .irq = -1 }, +static struct omap_hwmod_class am33xx_ecap_hwmod_class = { + .name = "ecap", }; -static struct omap_hwmod am33xx_ehrpwm0_hwmod = { - .name = "ehrpwm0", +static struct omap_hwmod_class am33xx_eqep_hwmod_class = { + .name = "eqep", +}; + +static struct omap_hwmod_class am33xx_ehrpwm_hwmod_class = { + .name = "ehrpwm", +}; + +/* epwmss0 */ +static struct omap_hwmod am33xx_epwmss0_hwmod = { + .name = "epwmss0", .class = &am33xx_epwmss_hwmod_class, .clkdm_name = "l4ls_clkdm", - .mpu_irqs = am33xx_ehrpwm0_irqs, .main_clk = "l4ls_gclk", .prcm = { .omap4 = { @@ -822,108 +825,6 @@ static struct omap_hwmod am33xx_ehrpwm0_hwmod = { }, }; -/* ehrpwm1 */ -static struct omap_hwmod_irq_info am33xx_ehrpwm1_irqs[] = { - { .name = "int", .irq = 87 + OMAP_INTC_START, }, - { .name = "tzint", .irq = 59 + OMAP_INTC_START, }, - { .irq = -1 }, -}; - -static struct omap_hwmod am33xx_ehrpwm1_hwmod = { - .name = "ehrpwm1", - .class = &am33xx_epwmss_hwmod_class, - .clkdm_name = "l4ls_clkdm", - .mpu_irqs = am33xx_ehrpwm1_irqs, - .main_clk = "l4ls_gclk", - .prcm = { - .omap4 = { - .clkctrl_offs = AM33XX_CM_PER_EPWMSS1_CLKCTRL_OFFSET, - .modulemode = MODULEMODE_SWCTRL, - }, - }, -}; - -/* ehrpwm2 */ -static struct omap_hwmod_irq_info am33xx_ehrpwm2_irqs[] = { - { .name = "int", .irq = 39 + OMAP_INTC_START, }, - { .name = "tzint", .irq = 60 + OMAP_INTC_START, }, - { .irq = -1 }, -}; - -static struct omap_hwmod am33xx_ehrpwm2_hwmod = { - .name = "ehrpwm2", - .class = &am33xx_epwmss_hwmod_class, - .clkdm_name = "l4ls_clkdm", - .mpu_irqs = am33xx_ehrpwm2_irqs, - .main_clk = "l4ls_gclk", - .prcm = { - .omap4 = { - .clkctrl_offs = AM33XX_CM_PER_EPWMSS2_CLKCTRL_OFFSET, - .modulemode = MODULEMODE_SWCTRL, - }, - }, -}; - -/* eqep0 */ -static struct omap_hwmod_irq_info am33xx_eqep0_irqs[] = { - { .irq = 79 + OMAP_INTC_START, }, - { .irq = -1 }, -}; - -static struct omap_hwmod am33xx_eqep0_hwmod = { - .name = "eqep0", - .class = &am33xx_epwmss_hwmod_class, - .clkdm_name = "l4ls_clkdm", - .mpu_irqs = am33xx_eqep0_irqs, - .main_clk = "l4ls_gclk", - .prcm = { - .omap4 = { - .clkctrl_offs = AM33XX_CM_PER_EPWMSS0_CLKCTRL_OFFSET, - .modulemode = MODULEMODE_SWCTRL, - }, - }, -}; - -/* eqep1 */ -static struct omap_hwmod_irq_info am33xx_eqep1_irqs[] = { - { .irq = 88 + OMAP_INTC_START, }, - { .irq = -1 }, -}; - -static struct omap_hwmod am33xx_eqep1_hwmod = { - .name = "eqep1", - .class = &am33xx_epwmss_hwmod_class, - .clkdm_name = "l4ls_clkdm", - .mpu_irqs = am33xx_eqep1_irqs, - .main_clk = "l4ls_gclk", - .prcm = { - .omap4 = { - .clkctrl_offs = AM33XX_CM_PER_EPWMSS1_CLKCTRL_OFFSET, - .modulemode = MODULEMODE_SWCTRL, - }, - }, -}; - -/* eqep2 */ -static struct omap_hwmod_irq_info am33xx_eqep2_irqs[] = { - { .irq = 89 + OMAP_INTC_START, }, - { .irq = -1 }, -}; - -static struct omap_hwmod am33xx_eqep2_hwmod = { - .name = "eqep2", - .class = &am33xx_epwmss_hwmod_class, - .clkdm_name = "l4ls_clkdm", - .mpu_irqs = am33xx_eqep2_irqs, - .main_clk = "l4ls_gclk", - .prcm = { - .omap4 = { - .clkctrl_offs = AM33XX_CM_PER_EPWMSS2_CLKCTRL_OFFSET, - .modulemode = MODULEMODE_SWCTRL, - }, - }, -}; - /* ecap0 */ static struct omap_hwmod_irq_info am33xx_ecap0_irqs[] = { { .irq = 31 + OMAP_INTC_START, }, @@ -932,13 +833,50 @@ static struct omap_hwmod_irq_info am33xx_ecap0_irqs[] = { static struct omap_hwmod am33xx_ecap0_hwmod = { .name = "ecap0", - .class = &am33xx_epwmss_hwmod_class, + .class = &am33xx_ecap_hwmod_class, .clkdm_name = "l4ls_clkdm", .mpu_irqs = am33xx_ecap0_irqs, .main_clk = "l4ls_gclk", +}; + +/* eqep0 */ +static struct omap_hwmod_irq_info am33xx_eqep0_irqs[] = { + { .irq = 79 + OMAP_INTC_START, }, + { .irq = -1 }, +}; + +static struct omap_hwmod am33xx_eqep0_hwmod = { + .name = "eqep0", + .class = &am33xx_eqep_hwmod_class, + .clkdm_name = "l4ls_clkdm", + .mpu_irqs = am33xx_eqep0_irqs, + .main_clk = "l4ls_gclk", +}; + +/* ehrpwm0 */ +static struct omap_hwmod_irq_info am33xx_ehrpwm0_irqs[] = { + { .name = "int", .irq = 86 + OMAP_INTC_START, }, + { .name = "tzint", .irq = 58 + OMAP_INTC_START, }, + { .irq = -1 }, +}; + +static struct omap_hwmod am33xx_ehrpwm0_hwmod = { + .name = "ehrpwm0", + .class = &am33xx_ehrpwm_hwmod_class, + .clkdm_name = "l4ls_clkdm", + .mpu_irqs = am33xx_ehrpwm0_irqs, + .main_clk = "l4ls_gclk", +}; + +/* epwmss1 */ +static struct omap_hwmod am33xx_epwmss1_hwmod = { + .name = "epwmss1", + .class = &am33xx_epwmss_hwmod_class, + .clkdm_name = "l4ls_clkdm", + .main_clk = "l4ls_gclk", .prcm = { .omap4 = { - .clkctrl_offs = AM33XX_CM_PER_EPWMSS0_CLKCTRL_OFFSET, + .clkctrl_offs = AM33XX_CM_PER_EPWMSS1_CLKCTRL_OFFSET, .modulemode = MODULEMODE_SWCTRL, }, }, @@ -952,13 +890,50 @@ static struct omap_hwmod_irq_info am33xx_ecap1_irqs[] = { static struct omap_hwmod am33xx_ecap1_hwmod = { .name = "ecap1", - .class = &am33xx_epwmss_hwmod_class, + .class = &am33xx_ecap_hwmod_class, .clkdm_name = "l4ls_clkdm", .mpu_irqs = am33xx_ecap1_irqs, .main_clk = "l4ls_gclk", +}; + +/* eqep1 */ +static struct omap_hwmod_irq_info am33xx_eqep1_irqs[] = { + { .irq = 88 + OMAP_INTC_START, }, + { .irq = -1 }, +}; + +static struct omap_hwmod am33xx_eqep1_hwmod = { + .name = "eqep1", + .class = &am33xx_eqep_hwmod_class, + .clkdm_name = "l4ls_clkdm", + .mpu_irqs = am33xx_eqep1_irqs, + .main_clk = "l4ls_gclk", +}; + +/* ehrpwm1 */ +static struct omap_hwmod_irq_info am33xx_ehrpwm1_irqs[] = { + { .name = "int", .irq = 87 + OMAP_INTC_START, }, + { .name = "tzint", .irq = 59 + OMAP_INTC_START, }, + { .irq = -1 }, +}; + +static struct omap_hwmod am33xx_ehrpwm1_hwmod = { + .name = "ehrpwm1", + .class = &am33xx_ehrpwm_hwmod_class, + .clkdm_name = "l4ls_clkdm", + .mpu_irqs = am33xx_ehrpwm1_irqs, + .main_clk = "l4ls_gclk", +}; + +/* epwmss2 */ +static struct omap_hwmod am33xx_epwmss2_hwmod = { + .name = "epwmss2", + .class = &am33xx_epwmss_hwmod_class, + .clkdm_name = "l4ls_clkdm", + .main_clk = "l4ls_gclk", .prcm = { .omap4 = { - .clkctrl_offs = AM33XX_CM_PER_EPWMSS1_CLKCTRL_OFFSET, + .clkctrl_offs = AM33XX_CM_PER_EPWMSS2_CLKCTRL_OFFSET, .modulemode = MODULEMODE_SWCTRL, }, }, @@ -972,16 +947,39 @@ static struct omap_hwmod_irq_info am33xx_ecap2_irqs[] = { static struct omap_hwmod am33xx_ecap2_hwmod = { .name = "ecap2", - .mpu_irqs = am33xx_ecap2_irqs, - .class = &am33xx_epwmss_hwmod_class, + .class = &am33xx_ecap_hwmod_class, .clkdm_name = "l4ls_clkdm", + .mpu_irqs = am33xx_ecap2_irqs, + .main_clk = "l4ls_gclk", +}; + +/* eqep2 */ +static struct omap_hwmod_irq_info am33xx_eqep2_irqs[] = { + { .irq = 89 + OMAP_INTC_START, }, + { .irq = -1 }, +}; + +static struct omap_hwmod am33xx_eqep2_hwmod = { + .name = "eqep2", + .class = &am33xx_eqep_hwmod_class, + .clkdm_name = "l4ls_clkdm", + .mpu_irqs = am33xx_eqep2_irqs, + .main_clk = "l4ls_gclk", +}; + +/* ehrpwm2 */ +static struct omap_hwmod_irq_info am33xx_ehrpwm2_irqs[] = { + { .name = "int", .irq = 39 + OMAP_INTC_START, }, + { .name = "tzint", .irq = 60 + OMAP_INTC_START, }, + { .irq = -1 }, +}; + +static struct omap_hwmod am33xx_ehrpwm2_hwmod = { + .name = "ehrpwm2", + .class = &am33xx_ehrpwm_hwmod_class, + .clkdm_name = "l4ls_clkdm", + .mpu_irqs = am33xx_ehrpwm2_irqs, .main_clk = "l4ls_gclk", - .prcm = { - .omap4 = { - .clkctrl_offs = AM33XX_CM_PER_EPWMSS2_CLKCTRL_OFFSET, - .modulemode = MODULEMODE_SWCTRL, - }, - }, }; /* @@ -2607,166 +2605,24 @@ static struct omap_hwmod_ocp_if am33xx_l4_ls__elm = { .user = OCP_USER_MPU, }; -/* - * Splitting the resources to handle access of PWMSS config space - * and module specific part independently - */ -static struct omap_hwmod_addr_space am33xx_ehrpwm0_addr_space[] = { +static struct omap_hwmod_addr_space am33xx_epwmss0_addr_space[] = { { .pa_start = 0x48300000, .pa_end = 0x48300000 + SZ_16 - 1, .flags = ADDR_TYPE_RT }, - { - .pa_start = 0x48300200, - .pa_end = 0x48300200 + SZ_128 - 1, - }, { } }; -static struct omap_hwmod_ocp_if am33xx_l4_ls__ehrpwm0 = { +static struct omap_hwmod_ocp_if am33xx_l4_ls__epwmss0 = { .master = &am33xx_l4_ls_hwmod, - .slave = &am33xx_ehrpwm0_hwmod, + .slave = &am33xx_epwmss0_hwmod, .clk = "l4ls_gclk", - .addr = am33xx_ehrpwm0_addr_space, + .addr = am33xx_epwmss0_addr_space, .user = OCP_USER_MPU, }; -/* - * Splitting the resources to handle access of PWMSS config space - * and module specific part independently - */ -static struct omap_hwmod_addr_space am33xx_ehrpwm1_addr_space[] = { - { - .pa_start = 0x48302000, - .pa_end = 0x48302000 + SZ_16 - 1, - .flags = ADDR_TYPE_RT - }, - { - .pa_start = 0x48302200, - .pa_end = 0x48302200 + SZ_128 - 1, - }, - { } -}; - -static struct omap_hwmod_ocp_if am33xx_l4_ls__ehrpwm1 = { - .master = &am33xx_l4_ls_hwmod, - .slave = &am33xx_ehrpwm1_hwmod, - .clk = "l4ls_gclk", - .addr = am33xx_ehrpwm1_addr_space, - .user = OCP_USER_MPU, -}; - -/* - * Splitting the resources to handle access of PWMSS config space - * and module specific part independently - */ -static struct omap_hwmod_addr_space am33xx_ehrpwm2_addr_space[] = { - { - .pa_start = 0x48304000, - .pa_end = 0x48304000 + SZ_16 - 1, - .flags = ADDR_TYPE_RT - }, - { - .pa_start = 0x48304200, - .pa_end = 0x48304200 + SZ_128 - 1, - }, - { } -}; - -static struct omap_hwmod_ocp_if am33xx_l4_ls__ehrpwm2 = { - .master = &am33xx_l4_ls_hwmod, - .slave = &am33xx_ehrpwm2_hwmod, - .clk = "l4ls_gclk", - .addr = am33xx_ehrpwm2_addr_space, - .user = OCP_USER_MPU, -}; - -/* - * Splitting the resources to handle access of PWMSS config space - * and module specific part independently - */ -static struct omap_hwmod_addr_space am33xx_eqep0_addr_space[] = { - { - .pa_start = 0x48300000, - .pa_end = 0x48300000 + SZ_16 - 1, - .flags = ADDR_TYPE_RT - }, - { - .pa_start = 0x48300180, - .pa_end = 0x48300180 + SZ_128 - 1, - }, - { } -}; - -static struct omap_hwmod_ocp_if am33xx_l4_ls__eqep0 = { - .master = &am33xx_l4_ls_hwmod, - .slave = &am33xx_eqep0_hwmod, - .clk = "l4ls_gclk", - .addr = am33xx_eqep0_addr_space, - .user = OCP_USER_MPU, -}; - -/* - * Splitting the resources to handle access of PWMSS config space - * and module specific part independently - */ -static struct omap_hwmod_addr_space am33xx_eqep1_addr_space[] = { - { - .pa_start = 0x48302000, - .pa_end = 0x48302000 + SZ_16 - 1, - .flags = ADDR_TYPE_RT - }, - { - .pa_start = 0x48302180, - .pa_end = 0x48302180 + SZ_128 - 1, - }, - { } -}; - -static struct omap_hwmod_ocp_if am33xx_l4_ls__eqep1 = { - .master = &am33xx_l4_ls_hwmod, - .slave = &am33xx_eqep1_hwmod, - .clk = "l4ls_gclk", - .addr = am33xx_eqep1_addr_space, - .user = OCP_USER_MPU, -}; - -/* - * Splitting the resources to handle access of PWMSS config space - * and module specific part independently - */ -static struct omap_hwmod_addr_space am33xx_eqep2_addr_space[] = { - { - .pa_start = 0x48304000, - .pa_end = 0x48304000 + SZ_16 - 1, - .flags = ADDR_TYPE_RT - }, - { - .pa_start = 0x48304180, - .pa_end = 0x48304180 + SZ_128 - 1, - }, - { } -}; - -static struct omap_hwmod_ocp_if am33xx_l4_ls__eqep2 = { - .master = &am33xx_l4_ls_hwmod, - .slave = &am33xx_eqep2_hwmod, - .clk = "l4ls_gclk", - .addr = am33xx_eqep2_addr_space, - .user = OCP_USER_MPU, -}; - -/* - * Splitting the resources to handle access of PWMSS config space - * and module specific part independently - */ static struct omap_hwmod_addr_space am33xx_ecap0_addr_space[] = { - { - .pa_start = 0x48300000, - .pa_end = 0x48300000 + SZ_16 - 1, - .flags = ADDR_TYPE_RT - }, { .pa_start = 0x48300100, .pa_end = 0x48300100 + SZ_128 - 1, @@ -2774,24 +2630,65 @@ static struct omap_hwmod_addr_space am33xx_ecap0_addr_space[] = { { } }; -static struct omap_hwmod_ocp_if am33xx_l4_ls__ecap0 = { - .master = &am33xx_l4_ls_hwmod, +static struct omap_hwmod_ocp_if am33xx_epwmss0__ecap0 = { + .master = &am33xx_epwmss0_hwmod, .slave = &am33xx_ecap0_hwmod, .clk = "l4ls_gclk", .addr = am33xx_ecap0_addr_space, .user = OCP_USER_MPU, }; -/* - * Splitting the resources to handle access of PWMSS config space - * and module specific part independently - */ -static struct omap_hwmod_addr_space am33xx_ecap1_addr_space[] = { +static struct omap_hwmod_addr_space am33xx_eqep0_addr_space[] = { + { + .pa_start = 0x48300180, + .pa_end = 0x48300180 + SZ_128 - 1, + }, + { } +}; + +static struct omap_hwmod_ocp_if am33xx_epwmss0__eqep0 = { + .master = &am33xx_epwmss0_hwmod, + .slave = &am33xx_eqep0_hwmod, + .clk = "l4ls_gclk", + .addr = am33xx_eqep0_addr_space, + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_addr_space am33xx_ehrpwm0_addr_space[] = { + { + .pa_start = 0x48300200, + .pa_end = 0x48300200 + SZ_128 - 1, + }, + { } +}; + +static struct omap_hwmod_ocp_if am33xx_epwmss0__ehrpwm0 = { + .master = &am33xx_epwmss0_hwmod, + .slave = &am33xx_ehrpwm0_hwmod, + .clk = "l4ls_gclk", + .addr = am33xx_ehrpwm0_addr_space, + .user = OCP_USER_MPU, +}; + + +static struct omap_hwmod_addr_space am33xx_epwmss1_addr_space[] = { { .pa_start = 0x48302000, .pa_end = 0x48302000 + SZ_16 - 1, .flags = ADDR_TYPE_RT }, + { } +}; + +static struct omap_hwmod_ocp_if am33xx_l4_ls__epwmss1 = { + .master = &am33xx_l4_ls_hwmod, + .slave = &am33xx_epwmss1_hwmod, + .clk = "l4ls_gclk", + .addr = am33xx_epwmss1_addr_space, + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_addr_space am33xx_ecap1_addr_space[] = { { .pa_start = 0x48302100, .pa_end = 0x48302100 + SZ_128 - 1, @@ -2799,24 +2696,64 @@ static struct omap_hwmod_addr_space am33xx_ecap1_addr_space[] = { { } }; -static struct omap_hwmod_ocp_if am33xx_l4_ls__ecap1 = { - .master = &am33xx_l4_ls_hwmod, +static struct omap_hwmod_ocp_if am33xx_epwmss1__ecap1 = { + .master = &am33xx_epwmss1_hwmod, .slave = &am33xx_ecap1_hwmod, .clk = "l4ls_gclk", .addr = am33xx_ecap1_addr_space, .user = OCP_USER_MPU, }; -/* - * Splitting the resources to handle access of PWMSS config space - * and module specific part independently - */ -static struct omap_hwmod_addr_space am33xx_ecap2_addr_space[] = { +static struct omap_hwmod_addr_space am33xx_eqep1_addr_space[] = { + { + .pa_start = 0x48302180, + .pa_end = 0x48302180 + SZ_128 - 1, + }, + { } +}; + +static struct omap_hwmod_ocp_if am33xx_epwmss1__eqep1 = { + .master = &am33xx_epwmss1_hwmod, + .slave = &am33xx_eqep1_hwmod, + .clk = "l4ls_gclk", + .addr = am33xx_eqep1_addr_space, + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_addr_space am33xx_ehrpwm1_addr_space[] = { + { + .pa_start = 0x48302200, + .pa_end = 0x48302200 + SZ_128 - 1, + }, + { } +}; + +static struct omap_hwmod_ocp_if am33xx_epwmss1__ehrpwm1 = { + .master = &am33xx_epwmss1_hwmod, + .slave = &am33xx_ehrpwm1_hwmod, + .clk = "l4ls_gclk", + .addr = am33xx_ehrpwm1_addr_space, + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_addr_space am33xx_epwmss2_addr_space[] = { { .pa_start = 0x48304000, .pa_end = 0x48304000 + SZ_16 - 1, .flags = ADDR_TYPE_RT }, + { } +}; + +static struct omap_hwmod_ocp_if am33xx_l4_ls__epwmss2 = { + .master = &am33xx_l4_ls_hwmod, + .slave = &am33xx_epwmss2_hwmod, + .clk = "l4ls_gclk", + .addr = am33xx_epwmss2_addr_space, + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_addr_space am33xx_ecap2_addr_space[] = { { .pa_start = 0x48304100, .pa_end = 0x48304100 + SZ_128 - 1, @@ -2824,14 +2761,46 @@ static struct omap_hwmod_addr_space am33xx_ecap2_addr_space[] = { { } }; -static struct omap_hwmod_ocp_if am33xx_l4_ls__ecap2 = { - .master = &am33xx_l4_ls_hwmod, +static struct omap_hwmod_ocp_if am33xx_epwmss2__ecap2 = { + .master = &am33xx_epwmss2_hwmod, .slave = &am33xx_ecap2_hwmod, .clk = "l4ls_gclk", .addr = am33xx_ecap2_addr_space, .user = OCP_USER_MPU, }; +static struct omap_hwmod_addr_space am33xx_eqep2_addr_space[] = { + { + .pa_start = 0x48304180, + .pa_end = 0x48304180 + SZ_128 - 1, + }, + { } +}; + +static struct omap_hwmod_ocp_if am33xx_epwmss2__eqep2 = { + .master = &am33xx_epwmss2_hwmod, + .slave = &am33xx_eqep2_hwmod, + .clk = "l4ls_gclk", + .addr = am33xx_eqep2_addr_space, + .user = OCP_USER_MPU, +}; + +static struct omap_hwmod_addr_space am33xx_ehrpwm2_addr_space[] = { + { + .pa_start = 0x48304200, + .pa_end = 0x48304200 + SZ_128 - 1, + }, + { } +}; + +static struct omap_hwmod_ocp_if am33xx_epwmss2__ehrpwm2 = { + .master = &am33xx_epwmss2_hwmod, + .slave = &am33xx_ehrpwm2_hwmod, + .clk = "l4ls_gclk", + .addr = am33xx_ehrpwm2_addr_space, + .user = OCP_USER_MPU, +}; + /* l3s cfg -> gpmc */ static struct omap_hwmod_addr_space am33xx_gpmc_addr_space[] = { { @@ -3521,15 +3490,18 @@ static struct omap_hwmod_ocp_if *am33xx_hwmod_ocp_ifs[] __initdata = { &am33xx_l4_ls__uart6, &am33xx_l4_ls__spinlock, &am33xx_l4_ls__elm, - &am33xx_l4_ls__ehrpwm0, - &am33xx_l4_ls__ehrpwm1, - &am33xx_l4_ls__ehrpwm2, - &am33xx_l4_ls__eqep0, - &am33xx_l4_ls__eqep1, - &am33xx_l4_ls__eqep2, - &am33xx_l4_ls__ecap0, - &am33xx_l4_ls__ecap1, - &am33xx_l4_ls__ecap2, + &am33xx_l4_ls__epwmss0, + &am33xx_epwmss0__ecap0, + &am33xx_epwmss0__eqep0, + &am33xx_epwmss0__ehrpwm0, + &am33xx_l4_ls__epwmss1, + &am33xx_epwmss1__ecap1, + &am33xx_epwmss1__eqep1, + &am33xx_epwmss1__ehrpwm1, + &am33xx_l4_ls__epwmss2, + &am33xx_epwmss2__ecap2, + &am33xx_epwmss2__eqep2, + &am33xx_epwmss2__ehrpwm2, &am33xx_l3_s__gpmc, &am33xx_l3_main__lcdc, &am33xx_l4_ls__mcspi0, From 092bda62772dd0018bf48f2554f8f16348f16410 Mon Sep 17 00:00:00 2001 From: Hebbar Gururaja Date: Fri, 8 Feb 2013 08:21:10 -0700 Subject: [PATCH 2154/4607] ARM: OMAP2+: AM33xx: hwmod: add missing HWMOD_NO_IDLEST flags struct omap_hwmod records belonging to wkup m3 domain is missing HWMOD_NO_IDLEST flags; add them. This patch is a prerequisite for a subsequent patch, 'ARM: OMAP2: am33xx-hwmod: Fix "register offset NULL check" bug'. That patch would otherwise attempt to read from reserved bits. Signed-off-by: Hebbar Gururaja [paul@pwsan.com: add some more explanation in the patch description] Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod_33xx_data.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c index 8441538872cd..26eee4a556ad 100644 --- a/arch/arm/mach-omap2/omap_hwmod_33xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_33xx_data.c @@ -262,7 +262,8 @@ static struct omap_hwmod am33xx_wkup_m3_hwmod = { .name = "wkup_m3", .class = &am33xx_wkup_m3_hwmod_class, .clkdm_name = "l4_wkup_aon_clkdm", - .flags = HWMOD_INIT_NO_RESET, /* Keep hardreset asserted */ + /* Keep hardreset asserted */ + .flags = HWMOD_INIT_NO_RESET | HWMOD_NO_IDLEST, .mpu_irqs = am33xx_wkup_m3_irqs, .main_clk = "dpll_core_m4_div2_ck", .prcm = { From 169c82a294e3722eb1e82b7dac58b35fe2119b80 Mon Sep 17 00:00:00 2001 From: Hebbar Gururaja Date: Fri, 8 Feb 2013 08:21:13 -0700 Subject: [PATCH 2155/4607] ARM: OMAP2: am33xx-hwmod: Fix "register offset NULL check" bug am33xx_cm_wait_module_ready() checks if register offset is NULL. int am33xx_cm_wait_module_ready(u16 inst, s16 cdoffs, u16 clkctrl_offs) { int i = 0; if (!clkctrl_offs) return 0; In case of AM33xx, CLKCTRL register offset for different clock domains are not uniformly placed. An example of this would be the RTC clock domain with CLKCTRL offset at 0x00. In such cases the module ready check is skipped which leads to a data abort during boot-up when RTC registers is accessed. Remove this check here to avoid checking module readiness for modules with clkctrl register offset at 0x00. Koen Kooi notes that this patch fixes a crash on boot with CONFIG_RTC_DRV_OMAP=y with v3.8-rc5. Signed-off-by: Hebbar Gururaja Cc: Koen Kooi [paul@pwsan.com: noted Koen's test in the patch description] Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/cm33xx.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/mach-omap2/cm33xx.c b/arch/arm/mach-omap2/cm33xx.c index 058ce3c0873e..325a51576576 100644 --- a/arch/arm/mach-omap2/cm33xx.c +++ b/arch/arm/mach-omap2/cm33xx.c @@ -241,9 +241,6 @@ int am33xx_cm_wait_module_ready(u16 inst, s16 cdoffs, u16 clkctrl_offs) { int i = 0; - if (!clkctrl_offs) - return 0; - omap_test_timeout(_is_module_ready(inst, cdoffs, clkctrl_offs), MAX_MODULE_READY_TIME, i); From c240ac9bc7470d4d060df3688b8e590cbbab9eb5 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Mon, 28 Jan 2013 17:45:31 -0300 Subject: [PATCH 2156/4607] [media] Documentation/media-framework.txt: fix a sentence Signed-off-by: Antonio Ospite Signed-off-by: Mauro Carvalho Chehab --- Documentation/media-framework.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 802875413873..77bd0a42f19d 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -336,7 +336,7 @@ Calls to media_entity_pipeline_start() can be nested. The pipeline pointer must be identical for all nested calls to the function. media_entity_pipeline_start() may return an error. In that case, it will -clean up any the changes it did by itself. +clean up any of the changes it did by itself. When stopping the stream, drivers must notify the entities with From d2008b56cfd6fa76ac6990d9c12a045ce5026c85 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 29 Jan 2013 08:19:27 -0300 Subject: [PATCH 2157/4607] [media] ttusbir: do not set led twice on resume led_classdev_resume already sets the led. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ttusbir.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index 78be8a914225..f9226b83de41 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -408,9 +408,8 @@ static int ttusbir_resume(struct usb_interface *intf) struct ttusbir *tt = usb_get_intfdata(intf); int i, rc; - led_classdev_resume(&tt->led); tt->is_led_on = true; - ttusbir_set_led(tt); + led_classdev_resume(&tt->led); for (i = 0; i < NUM_URBS; i++) { rc = usb_submit_urb(tt->urb[i], GFP_KERNEL); From 1e801adc7a70c2f67214b2617088a41f4bebe55e Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 29 Jan 2013 08:19:28 -0300 Subject: [PATCH 2158/4607] [media] ttusbir: add missing endian conversion spotted by sparse. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ttusbir.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index f9226b83de41..cf0d47f57fb2 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -213,19 +213,20 @@ static int ttusbir_probe(struct usb_interface *intf, /* find the correct alt setting */ for (i = 0; i < intf->num_altsetting && altsetting == -1; i++) { - int bulk_out_endp = -1, iso_in_endp = -1; + int max_packet, bulk_out_endp = -1, iso_in_endp = -1; idesc = &intf->altsetting[i].desc; for (j = 0; j < idesc->bNumEndpoints; j++) { desc = &intf->altsetting[i].endpoint[j].desc; + max_packet = le16_to_cpu(desc->wMaxPacketSize); if (usb_endpoint_dir_in(desc) && usb_endpoint_xfer_isoc(desc) && - desc->wMaxPacketSize == 0x10) + max_packet == 0x10) iso_in_endp = j; else if (usb_endpoint_dir_out(desc) && usb_endpoint_xfer_bulk(desc) && - desc->wMaxPacketSize == 0x20) + max_packet == 0x20) bulk_out_endp = j; if (bulk_out_endp != -1 && iso_in_endp != -1) { From 8dfef674e6c954f9b6476c1b252b385c48c9ee26 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 29 Jan 2013 08:19:29 -0300 Subject: [PATCH 2159/4607] [media] mceusb: make transmit work on the Philips IR transceiver The GET_REVISION command puts the device in an unresponsive state, although it continues to report any IR activity. Note that GET_REVISION command is not documented, nor is any possible response to it parsed. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 9afb9331217d..14fea35e5d67 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -291,7 +291,8 @@ static struct usb_device_id mceusb_dev_table[] = { /* Philips/Spinel plus IR transceiver for ASUS */ { USB_DEVICE(VENDOR_PHILIPS, 0x2088) }, /* Philips IR transceiver (Dell branded) */ - { USB_DEVICE(VENDOR_PHILIPS, 0x2093) }, + { USB_DEVICE(VENDOR_PHILIPS, 0x2093), + .driver_info = MCE_GEN2_TX_INV }, /* Realtek MCE IR Receiver and card reader */ { USB_DEVICE(VENDOR_REALTEK, 0x0161), .driver_info = MULTIFUNCTION }, @@ -1121,16 +1122,13 @@ static void mceusb_gen1_init(struct mceusb_dev *ir) mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION)); kfree(data); -}; +} static void mceusb_gen2_init(struct mceusb_dev *ir) { /* device resume */ mce_async_out(ir, DEVICE_RESUME, sizeof(DEVICE_RESUME)); - /* get hw/sw revision? */ - mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION)); - /* get wake version (protocol, key, address) */ mce_async_out(ir, GET_WAKEVERSION, sizeof(GET_WAKEVERSION)); From db8ee1064c97879bff614d653158dff1894d2e37 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 29 Jan 2013 08:19:30 -0300 Subject: [PATCH 2160/4607] [media] mceusb: make transmit work on HP transceiver This transceiver expects the set IR TX ports and IR data as seperate packets, like the Windows driver does. Remove unnecessary kzalloc. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 14fea35e5d67..bdd1ed8e406e 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -62,7 +62,6 @@ #define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */ #define MCE_IRDATA_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */ #define MCE_IRDATA_TRAILER 0x80 /* End of IR data */ -#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */ #define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */ #define MCE_DEFAULT_TX_MASK 0x03 /* Vals: TX1=0x01, TX2=0x02, ALL=0x03 */ #define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */ @@ -366,7 +365,8 @@ static struct usb_device_id mceusb_dev_table[] = { /* Formosa Industrial Computing */ { USB_DEVICE(VENDOR_FORMOSA, 0xe042) }, /* Fintek eHome Infrared Transceiver (HP branded) */ - { USB_DEVICE(VENDOR_FINTEK, 0x5168) }, + { USB_DEVICE(VENDOR_FINTEK, 0x5168), + .driver_info = MCE_GEN2_TX_INV }, /* Fintek eHome Infrared Transceiver */ { USB_DEVICE(VENDOR_FINTEK, 0x0602) }, /* Fintek eHome Infrared Transceiver (in the AOpen MP45) */ @@ -789,19 +789,19 @@ static void mce_flush_rx_buffer(struct mceusb_dev *ir, int size) static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) { struct mceusb_dev *ir = dev->priv; - int i, ret = 0; + int i, length, ret = 0; int cmdcount = 0; - unsigned char *cmdbuf; /* MCE command buffer */ - - cmdbuf = kzalloc(sizeof(unsigned) * MCE_CMDBUF_SIZE, GFP_KERNEL); - if (!cmdbuf) - return -ENOMEM; + unsigned char cmdbuf[MCE_CMDBUF_SIZE]; /* MCE tx init header */ cmdbuf[cmdcount++] = MCE_CMD_PORT_IR; cmdbuf[cmdcount++] = MCE_CMD_SETIRTXPORTS; cmdbuf[cmdcount++] = ir->tx_mask; + /* Send the set TX ports command */ + mce_async_out(ir, cmdbuf, cmdcount); + cmdcount = 0; + /* Generate mce packet data */ for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) { txbuf[i] = txbuf[i] / MCE_TIME_UNIT; @@ -810,8 +810,7 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) /* Insert mce packet header every 4th entry */ if ((cmdcount < MCE_CMDBUF_SIZE) && - (cmdcount - MCE_TX_HEADER_LENGTH) % - MCE_CODE_LENGTH == 0) + (cmdcount % MCE_CODE_LENGTH) == 0) cmdbuf[cmdcount++] = MCE_IRDATA_HEADER; /* Insert mce packet data */ @@ -830,9 +829,8 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) } /* Fix packet length in last header */ - cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] = - MCE_COMMAND_IRDATA + (cmdcount - MCE_TX_HEADER_LENGTH) % - MCE_CODE_LENGTH - 1; + length = cmdcount % MCE_CODE_LENGTH; + cmdbuf[cmdcount - length] -= MCE_CODE_LENGTH - length; /* Check if we have room for the empty packet at the end */ if (cmdcount >= MCE_CMDBUF_SIZE) { @@ -847,7 +845,6 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) mce_async_out(ir, cmdbuf, cmdcount); out: - kfree(cmdbuf); return ret ? ret : count; } From 06eae25f162e2a0d9e60f0ad3ec3d14c738fbe68 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Tue, 29 Jan 2013 08:19:31 -0300 Subject: [PATCH 2161/4607] [media] redrat3: fix transmit return value and overrun If more than 127 different lengths are transmitted then the driver causes an overrun on sample_lens. Try to send as much as possible and return the amount sent. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/redrat3.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index 1800326f93e6..1b37fe2779f8 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -195,9 +195,6 @@ struct redrat3_dev { dma_addr_t dma_in; dma_addr_t dma_out; - /* locks this structure */ - struct mutex lock; - /* rx signal timeout timer */ struct timer_list rx_timeout; u32 hw_timeout; @@ -922,8 +919,7 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, return -EAGAIN; } - if (count > (RR3_DRIVER_MAXLENS * 2)) - return -EINVAL; + count = min_t(unsigned, count, RR3_MAX_SIG_SIZE - RR3_TX_TRAILER_LEN); /* rr3 will disable rc detector on transmit */ rr3->det_enabled = false; @@ -936,24 +932,22 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf, } for (i = 0; i < count; i++) { + cur_sample_len = redrat3_us_to_len(txbuf[i]); for (lencheck = 0; lencheck < curlencheck; lencheck++) { - cur_sample_len = redrat3_us_to_len(txbuf[i]); if (sample_lens[lencheck] == cur_sample_len) break; } if (lencheck == curlencheck) { - cur_sample_len = redrat3_us_to_len(txbuf[i]); rr3_dbg(dev, "txbuf[%d]=%u, pos %d, enc %u\n", i, txbuf[i], curlencheck, cur_sample_len); - if (curlencheck < 255) { + if (curlencheck < RR3_DRIVER_MAXLENS) { /* now convert the value to a proper * rr3 value.. */ sample_lens[curlencheck] = cur_sample_len; curlencheck++; } else { - dev_err(dev, "signal too long\n"); - ret = -EINVAL; - goto out; + count = i - 1; + break; } } } @@ -1087,6 +1081,7 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3) rc->tx_ir = redrat3_transmit_ir; rc->s_tx_carrier = redrat3_set_tx_carrier; rc->driver_name = DRIVER_NAME; + rc->rx_resolution = US_TO_NS(2); rc->map_name = RC_MAP_HAUPPAUGE; ret = rc_register_device(rc); @@ -1202,7 +1197,6 @@ static int redrat3_dev_probe(struct usb_interface *intf, rr3->bulk_out_buf, ep_out->wMaxPacketSize, (usb_complete_t)redrat3_write_bulk_callback, rr3); - mutex_init(&rr3->lock); rr3->udev = udev; redrat3_reset(rr3); From d058e23704ad7e0b6876a94b0d8428dcef510b49 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:12:13 -0300 Subject: [PATCH 2162/4607] [media] media: ov7670: add support for ov7675 ov7675 and ov7670 share the same registers but there is no way to distinguish them at runtime. However, they require different tweaks to achieve the desired resolution. For this reason this patch adds a new ov7675 entry to the ov7670_id table. Signed-off-by: Javier Martin Cc: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 101 ++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 29 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 882ddf6f66d3..51b198f79077 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -183,6 +183,27 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ #define REG_BD60MAX 0xab /* 60hz banding step limit */ +enum ov7670_model { + MODEL_OV7670 = 0, + MODEL_OV7675, +}; + +struct ov7670_win_size { + int width; + int height; + unsigned char com7_bit; + int hstart; /* Start/stop values for the camera. Note */ + int hstop; /* that they do not always make complete */ + int vstart; /* sense to humans, but evidently the sensor */ + int vstop; /* will do the right thing... */ + struct regval_list *regs; /* Regs to tweak */ +}; + +struct ov7670_devtype { + /* formats supported for each model */ + struct ov7670_win_size *win_sizes; + unsigned int n_win_sizes; +}; /* * Information we maintain about a known sensor. @@ -198,6 +219,7 @@ struct ov7670_info { int clock_speed; /* External clock speed (MHz) */ u8 clkrc; /* Clock divider value */ bool use_smbus; /* Use smbus I/O instead of I2C */ + const struct ov7670_devtype *devtype; /* Device specifics */ }; static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) @@ -652,65 +674,70 @@ static struct regval_list ov7670_qcif_regs[] = { { 0xff, 0xff }, }; -static struct ov7670_win_size { - int width; - int height; - unsigned char com7_bit; - int hstart; /* Start/stop values for the camera. Note */ - int hstop; /* that they do not always make complete */ - int vstart; /* sense to humans, but evidently the sensor */ - int vstop; /* will do the right thing... */ - struct regval_list *regs; /* Regs to tweak */ -/* h/vref stuff */ -} ov7670_win_sizes[] = { +static struct ov7670_win_size ov7670_win_sizes[] = { /* VGA */ { .width = VGA_WIDTH, .height = VGA_HEIGHT, .com7_bit = COM7_FMT_VGA, - .hstart = 158, /* These values from */ - .hstop = 14, /* Omnivision */ + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ .vstart = 10, .vstop = 490, - .regs = NULL, + .regs = NULL, }, /* CIF */ { .width = CIF_WIDTH, .height = CIF_HEIGHT, .com7_bit = COM7_FMT_CIF, - .hstart = 170, /* Empirically determined */ + .hstart = 170, /* Empirically determined */ .hstop = 90, .vstart = 14, .vstop = 494, - .regs = NULL, + .regs = NULL, }, /* QVGA */ { .width = QVGA_WIDTH, .height = QVGA_HEIGHT, .com7_bit = COM7_FMT_QVGA, - .hstart = 168, /* Empirically determined */ + .hstart = 168, /* Empirically determined */ .hstop = 24, .vstart = 12, .vstop = 492, - .regs = NULL, + .regs = NULL, }, /* QCIF */ { .width = QCIF_WIDTH, .height = QCIF_HEIGHT, .com7_bit = COM7_FMT_VGA, /* see comment above */ - .hstart = 456, /* Empirically determined */ + .hstart = 456, /* Empirically determined */ .hstop = 24, .vstart = 14, .vstop = 494, - .regs = ov7670_qcif_regs, - }, + .regs = ov7670_qcif_regs, + } }; -#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes)) - +static struct ov7670_win_size ov7675_win_sizes[] = { + /* + * Currently, only VGA is supported. Theoretically it could be possible + * to support CIF, QVGA and QCIF too. Taking values for ov7670 as a + * base and tweak them empirically could be required. + */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .com7_bit = COM7_FMT_VGA, + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ + .vstart = 14, /* Empirically determined */ + .vstop = 494, + .regs = NULL, + } +}; /* * Store a set of start/stop values into the camera. @@ -761,6 +788,8 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, { int index; struct ov7670_win_size *wsize; + struct ov7670_info *info = to_state(sd); + unsigned int n_win_sizes = info->devtype->n_win_sizes; for (index = 0; index < N_OV7670_FMTS; index++) if (ov7670_formats[index].mbus_code == fmt->code) @@ -780,11 +809,11 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, * Round requested image size down to the nearest * we support, but not below the smallest. */ - for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; - wsize++) + for (wsize = info->devtype->win_sizes; + wsize < info->devtype->win_sizes + n_win_sizes; wsize++) if (fmt->width >= wsize->width && fmt->height >= wsize->height) break; - if (wsize >= ov7670_win_sizes + N_WIN_SIZES) + if (wsize >= info->devtype->win_sizes + n_win_sizes) wsize--; /* Take the smallest one */ if (ret_wsize != NULL) *ret_wsize = wsize; @@ -931,13 +960,14 @@ static int ov7670_enum_framesizes(struct v4l2_subdev *sd, int i; int num_valid = -1; __u32 index = fsize->index; + unsigned int n_win_sizes = info->devtype->n_win_sizes; /* * If a minimum width/height was requested, filter out the capture * windows that fall outside that. */ - for (i = 0; i < N_WIN_SIZES; i++) { - struct ov7670_win_size *win = &ov7670_win_sizes[index]; + for (i = 0; i < n_win_sizes; i++) { + struct ov7670_win_size *win = &info->devtype->win_sizes[index]; if (info->min_width && win->width < info->min_width) continue; if (info->min_height && win->height < info->min_height) @@ -1510,6 +1540,17 @@ static const struct v4l2_subdev_ops ov7670_ops = { /* ----------------------------------------------------------------------- */ +static const struct ov7670_devtype ov7670_devdata[] = { + [MODEL_OV7670] = { + .win_sizes = ov7670_win_sizes, + .n_win_sizes = ARRAY_SIZE(ov7670_win_sizes), + }, + [MODEL_OV7675] = { + .win_sizes = ov7675_win_sizes, + .n_win_sizes = ARRAY_SIZE(ov7675_win_sizes), + }, +}; + static int ov7670_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1551,6 +1592,7 @@ static int ov7670_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); + info->devtype = &ov7670_devdata[id->driver_data]; info->fmt = &ov7670_formats[0]; info->sat = 128; /* Review this */ info->clkrc = info->clock_speed / 30; @@ -1568,7 +1610,8 @@ static int ov7670_remove(struct i2c_client *client) } static const struct i2c_device_id ov7670_id[] = { - { "ov7670", 0 }, + { "ov7670", MODEL_OV7670 }, + { "ov7675", MODEL_OV7675 }, { } }; MODULE_DEVICE_TABLE(i2c, ov7670_id); From f748cd3ec8039a01d260ba0d51687afdf93c6e67 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:14:27 -0300 Subject: [PATCH 2163/4607] [media] media: ov7670: make try_fmt() consistent with 'min_height' and 'min_width' 'min_height' and 'min_width' are variables that allow to specify the minimum resolution that the sensor will achieve. This patch make v4l2 fmt callbacks consider this parameters in order to return valid data to user space. Acked-by: Jonathan Corbet Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 51b198f79077..88e64739fefd 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -786,10 +786,11 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, struct ov7670_format_struct **ret_fmt, struct ov7670_win_size **ret_wsize) { - int index; + int index, i; struct ov7670_win_size *wsize; struct ov7670_info *info = to_state(sd); unsigned int n_win_sizes = info->devtype->n_win_sizes; + unsigned int win_sizes_limit = n_win_sizes; for (index = 0; index < N_OV7670_FMTS; index++) if (ov7670_formats[index].mbus_code == fmt->code) @@ -805,15 +806,30 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, * Fields: the OV devices claim to be progressive. */ fmt->field = V4L2_FIELD_NONE; + + /* + * Don't consider values that don't match min_height and min_width + * constraints. + */ + if (info->min_width || info->min_height) + for (i = 0; i < n_win_sizes; i++) { + wsize = info->devtype->win_sizes + i; + + if (wsize->width < info->min_width || + wsize->height < info->min_height) { + win_sizes_limit = i; + break; + } + } /* * Round requested image size down to the nearest * we support, but not below the smallest. */ for (wsize = info->devtype->win_sizes; - wsize < info->devtype->win_sizes + n_win_sizes; wsize++) + wsize < info->devtype->win_sizes + win_sizes_limit; wsize++) if (fmt->width >= wsize->width && fmt->height >= wsize->height) break; - if (wsize >= info->devtype->win_sizes + n_win_sizes) + if (wsize >= info->devtype->win_sizes + win_sizes_limit) wsize--; /* Take the smallest one */ if (ret_wsize != NULL) *ret_wsize = wsize; From f6dd927f34d64014c4b196132b5cdf9f2e2a3ae5 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:16:59 -0300 Subject: [PATCH 2164/4607] [media] media: ov7670: calculate framerate properly for ov7675 According to the datasheet ov7675 uses a formula to achieve the desired framerate that is different from the operations done in the current code. In fact, this formula should apply to ov7670 too. This would mean that current code is wrong but, in order to preserve compatibility, the new formula will be used for ov7675 only. Signed-off-by: Javier Martin Cc: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 136 ++++++++++++++++++++++++++++++++----- 1 file changed, 118 insertions(+), 18 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 88e64739fefd..dea2917a2b12 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -47,6 +47,8 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); */ #define OV7670_I2C_ADDR 0x42 +#define PLL_FACTOR 4 + /* Registers */ #define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ #define REG_BLUE 0x01 /* blue gain */ @@ -164,6 +166,12 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define REG_GFIX 0x69 /* Fix gain control */ +#define REG_DBLV 0x6b /* PLL control an debugging */ +#define DBLV_BYPASS 0x00 /* Bypass PLL */ +#define DBLV_X4 0x01 /* clock x4 */ +#define DBLV_X6 0x10 /* clock x6 */ +#define DBLV_X8 0x11 /* clock x8 */ + #define REG_REG76 0x76 /* OV's name */ #define R76_BLKPCOR 0x80 /* Black pixel correction enable */ #define R76_WHTPCOR 0x40 /* White pixel correction enable */ @@ -203,6 +211,9 @@ struct ov7670_devtype { /* formats supported for each model */ struct ov7670_win_size *win_sizes; unsigned int n_win_sizes; + /* callbacks for frame rate control */ + int (*set_framerate)(struct v4l2_subdev *, struct v4l2_fract *); + void (*get_framerate)(struct v4l2_subdev *, struct v4l2_fract *); }; /* @@ -739,6 +750,98 @@ static struct ov7670_win_size ov7675_win_sizes[] = { } }; +static void ov7675_get_framerate(struct v4l2_subdev *sd, + struct v4l2_fract *tpf) +{ + struct ov7670_info *info = to_state(sd); + u32 clkrc = info->clkrc; + u32 pll_factor = PLL_FACTOR; + + clkrc++; + if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8) + clkrc = (clkrc >> 1); + + tpf->numerator = 1; + tpf->denominator = (5 * pll_factor * info->clock_speed) / + (4 * clkrc); +} + +static int ov7675_set_framerate(struct v4l2_subdev *sd, + struct v4l2_fract *tpf) +{ + struct ov7670_info *info = to_state(sd); + u32 clkrc; + u32 pll_factor = PLL_FACTOR; + int ret; + + /* + * The formula is fps = 5/4*pixclk for YUV/RGB and + * fps = 5/2*pixclk for RAW. + * + * pixclk = clock_speed / (clkrc + 1) * PLLfactor + * + */ + if (tpf->numerator == 0 || tpf->denominator == 0) { + clkrc = 0; + } else { + clkrc = (5 * pll_factor * info->clock_speed * tpf->numerator) / + (4 * tpf->denominator); + if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8) + clkrc = (clkrc << 1); + clkrc--; + } + + /* + * The datasheet claims that clkrc = 0 will divide the input clock by 1 + * but we've checked with an oscilloscope that it divides by 2 instead. + * So, if clkrc = 0 just bypass the divider. + */ + if (clkrc <= 0) + clkrc = CLK_EXT; + else if (clkrc > CLK_SCALE) + clkrc = CLK_SCALE; + info->clkrc = clkrc; + + /* Recalculate frame rate */ + ov7675_get_framerate(sd, tpf); + + ret = ov7670_write(sd, REG_CLKRC, info->clkrc); + if (ret < 0) + return ret; + return ov7670_write(sd, REG_DBLV, DBLV_X4); +} + +static void ov7670_get_framerate_legacy(struct v4l2_subdev *sd, + struct v4l2_fract *tpf) +{ + struct ov7670_info *info = to_state(sd); + + tpf->numerator = 1; + tpf->denominator = info->clock_speed; + if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) + tpf->denominator /= (info->clkrc & CLK_SCALE); +} + +static int ov7670_set_framerate_legacy(struct v4l2_subdev *sd, + struct v4l2_fract *tpf) +{ + struct ov7670_info *info = to_state(sd); + int div; + + if (tpf->numerator == 0 || tpf->denominator == 0) + div = 1; /* Reset to full rate */ + else + div = (tpf->numerator * info->clock_speed) / tpf->denominator; + if (div == 0) + div = 1; + else if (div > CLK_SCALE) + div = CLK_SCALE; + info->clkrc = (info->clkrc & 0x80) | div; + tpf->numerator = 1; + tpf->denominator = info->clock_speed / div; + return ov7670_write(sd, REG_CLKRC, info->clkrc); +} + /* * Store a set of start/stop values into the camera. */ @@ -913,10 +1016,8 @@ static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) memset(cp, 0, sizeof(struct v4l2_captureparm)); cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = info->clock_speed; - if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) - cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); + info->devtype->get_framerate(sd, &cp->timeperframe); + return 0; } @@ -925,25 +1026,13 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) struct v4l2_captureparm *cp = &parms->parm.capture; struct v4l2_fract *tpf = &cp->timeperframe; struct ov7670_info *info = to_state(sd); - int div; if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (cp->extendedmode != 0) return -EINVAL; - if (tpf->numerator == 0 || tpf->denominator == 0) - div = 1; /* Reset to full rate */ - else - div = (tpf->numerator * info->clock_speed) / tpf->denominator; - if (div == 0) - div = 1; - else if (div > CLK_SCALE) - div = CLK_SCALE; - info->clkrc = (info->clkrc & 0x80) | div; - tpf->numerator = 1; - tpf->denominator = info->clock_speed / div; - return ov7670_write(sd, REG_CLKRC, info->clkrc); + return info->devtype->set_framerate(sd, tpf); } @@ -1560,16 +1649,21 @@ static const struct ov7670_devtype ov7670_devdata[] = { [MODEL_OV7670] = { .win_sizes = ov7670_win_sizes, .n_win_sizes = ARRAY_SIZE(ov7670_win_sizes), + .set_framerate = ov7670_set_framerate_legacy, + .get_framerate = ov7670_get_framerate_legacy, }, [MODEL_OV7675] = { .win_sizes = ov7675_win_sizes, .n_win_sizes = ARRAY_SIZE(ov7675_win_sizes), + .set_framerate = ov7675_set_framerate, + .get_framerate = ov7675_get_framerate, }, }; static int ov7670_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_fract tpf; struct v4l2_subdev *sd; struct ov7670_info *info; int ret; @@ -1611,7 +1705,13 @@ static int ov7670_probe(struct i2c_client *client, info->devtype = &ov7670_devdata[id->driver_data]; info->fmt = &ov7670_formats[0]; info->sat = 128; /* Review this */ - info->clkrc = info->clock_speed / 30; + info->clkrc = 0; + + /* Set default frame rate to 30 fps */ + tpf.numerator = 1; + tpf.denominator = 30; + info->devtype->set_framerate(sd, &tpf); + return 0; } From 04ee6d92047e1ac68d4eb615119343f4f0fc57db Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:23:42 -0300 Subject: [PATCH 2165/4607] [media] media: ov7670: add possibility to bypass pll for ov7675 For a frame rate of 30 fps a pixclk of 24MHz is needed. For those cases where the ov7670 has a clean 24MHz input (xvclk) the PLL can be bypassed. This will result in a value of clkrc of 1, which means that in practice pixclk = xvclk (input clock) Acked-by: Jonathan Corbet Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 28 ++++++++++++++++++++++++++-- include/media/ov7670.h | 1 + 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index dea2917a2b12..3e503396aaa4 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -230,6 +230,7 @@ struct ov7670_info { int clock_speed; /* External clock speed (MHz) */ u8 clkrc; /* Clock divider value */ bool use_smbus; /* Use smbus I/O instead of I2C */ + bool pll_bypass; const struct ov7670_devtype *devtype; /* Device specifics */ }; @@ -755,7 +756,12 @@ static void ov7675_get_framerate(struct v4l2_subdev *sd, { struct ov7670_info *info = to_state(sd); u32 clkrc = info->clkrc; - u32 pll_factor = PLL_FACTOR; + int pll_factor; + + if (info->pll_bypass) + pll_factor = 1; + else + pll_factor = PLL_FACTOR; clkrc++; if (info->fmt->mbus_code == V4L2_MBUS_FMT_SBGGR8_1X8) @@ -771,7 +777,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, { struct ov7670_info *info = to_state(sd); u32 clkrc; - u32 pll_factor = PLL_FACTOR; + int pll_factor; int ret; /* @@ -781,6 +787,16 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, * pixclk = clock_speed / (clkrc + 1) * PLLfactor * */ + if (info->pll_bypass) { + pll_factor = 1; + ret = ov7670_write(sd, REG_DBLV, DBLV_BYPASS); + } else { + pll_factor = PLL_FACTOR; + ret = ov7670_write(sd, REG_DBLV, DBLV_X4); + } + if (ret < 0) + return ret; + if (tpf->numerator == 0 || tpf->denominator == 0) { clkrc = 0; } else { @@ -808,6 +824,7 @@ static int ov7675_set_framerate(struct v4l2_subdev *sd, ret = ov7670_write(sd, REG_CLKRC, info->clkrc); if (ret < 0) return ret; + return ov7670_write(sd, REG_DBLV, DBLV_X4); } @@ -1688,6 +1705,13 @@ static int ov7670_probe(struct i2c_client *client, if (config->clock_speed) info->clock_speed = config->clock_speed; + + /* + * It should be allowed for ov7670 too when it is migrated to + * the new frame rate formula. + */ + if (config->pll_bypass && id->driver_data != MODEL_OV7670) + info->pll_bypass = true; } /* Make sure it's an ov7670 */ diff --git a/include/media/ov7670.h b/include/media/ov7670.h index b133bc123031..a68c8bb3ceba 100644 --- a/include/media/ov7670.h +++ b/include/media/ov7670.h @@ -15,6 +15,7 @@ struct ov7670_config { int min_height; /* Filter out smaller sizes */ int clock_speed; /* External clock speed (MHz) */ bool use_smbus; /* Use smbus I/O instead of I2C */ + bool pll_bypass; /* Choose whether to bypass the PLL */ }; #endif From ee95258ed3926f3aa2cf8d62e62cd51be466fe26 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:26:38 -0300 Subject: [PATCH 2166/4607] [media] media: ov7670: Add possibility to disable pixclk during hblank Some bridge drivers capture pixels during blanking periods if pixclk is enabled. In order to avoid capturing bogus data we need to disable pixclk in the sensor during those blanking periods. Acked-by: Jonathan Corbet Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 7 +++++++ include/media/ov7670.h | 1 + 2 files changed, 8 insertions(+) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 3e503396aaa4..52c024a334df 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -231,6 +231,7 @@ struct ov7670_info { u8 clkrc; /* Clock divider value */ bool use_smbus; /* Use smbus I/O instead of I2C */ bool pll_bypass; + bool pclk_hb_disable; const struct ov7670_devtype *devtype; /* Device specifics */ }; @@ -1712,6 +1713,9 @@ static int ov7670_probe(struct i2c_client *client, */ if (config->pll_bypass && id->driver_data != MODEL_OV7670) info->pll_bypass = true; + + if (config->pclk_hb_disable) + info->pclk_hb_disable = true; } /* Make sure it's an ov7670 */ @@ -1736,6 +1740,9 @@ static int ov7670_probe(struct i2c_client *client, tpf.denominator = 30; info->devtype->set_framerate(sd, &tpf); + if (info->pclk_hb_disable) + ov7670_write(sd, REG_COM10, COM10_PCLK_HB); + return 0; } diff --git a/include/media/ov7670.h b/include/media/ov7670.h index a68c8bb3ceba..1913d5123072 100644 --- a/include/media/ov7670.h +++ b/include/media/ov7670.h @@ -16,6 +16,7 @@ struct ov7670_config { int clock_speed; /* External clock speed (MHz) */ bool use_smbus; /* Use smbus I/O instead of I2C */ bool pll_bypass; /* Choose whether to bypass the PLL */ + bool pclk_hb_disable; /* Disable toggling pixclk during horizontal blanking */ }; #endif From 492959c77f66c2238298115f4fabf1bb9ca997eb Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:31:17 -0300 Subject: [PATCH 2167/4607] [media] ov7670: use the control framework Cc: Jonathan Corbet Signed-off-by: Javier Martin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 312 ++++++++++++++----------------------- 1 file changed, 113 insertions(+), 199 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 52c024a334df..93a051e8873f 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -222,9 +223,23 @@ struct ov7670_devtype { struct ov7670_format_struct; /* coming later */ struct ov7670_info { struct v4l2_subdev sd; + struct v4l2_ctrl_handler hdl; + struct { + /* gain cluster */ + struct v4l2_ctrl *auto_gain; + struct v4l2_ctrl *gain; + }; + struct { + /* exposure cluster */ + struct v4l2_ctrl *auto_exposure; + struct v4l2_ctrl *exposure; + }; + struct { + /* saturation/hue cluster */ + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *hue; + }; struct ov7670_format_struct *fmt; /* Current format */ - unsigned char sat; /* Saturation value */ - int hue; /* Hue value */ int min_width; /* Filter out smaller sizes */ int min_height; /* Filter out smaller sizes */ int clock_speed; /* External clock speed (MHz) */ @@ -240,6 +255,11 @@ static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) return container_of(sd, struct ov7670_info, sd); } +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct ov7670_info, hdl)->sd; +} + /* @@ -1195,23 +1215,23 @@ static int ov7670_cosine(int theta) static void ov7670_calc_cmatrix(struct ov7670_info *info, - int matrix[CMATRIX_LEN]) + int matrix[CMATRIX_LEN], int sat, int hue) { int i; /* * Apply the current saturation setting first. */ for (i = 0; i < CMATRIX_LEN; i++) - matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; + matrix[i] = (info->fmt->cmatrix[i] * sat) >> 7; /* * Then, if need be, rotate the hue value. */ - if (info->hue != 0) { + if (hue != 0) { int sinth, costh, tmpmatrix[CMATRIX_LEN]; memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); - sinth = ov7670_sine(info->hue); - costh = ov7670_cosine(info->hue); + sinth = ov7670_sine(hue); + costh = ov7670_cosine(hue); matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; @@ -1224,60 +1244,21 @@ static void ov7670_calc_cmatrix(struct ov7670_info *info, -static int ov7670_s_sat(struct v4l2_subdev *sd, int value) +static int ov7670_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue) { struct ov7670_info *info = to_state(sd); int matrix[CMATRIX_LEN]; int ret; - info->sat = value; - ov7670_calc_cmatrix(info, matrix); + ov7670_calc_cmatrix(info, matrix, sat, hue); ret = ov7670_store_cmatrix(sd, matrix); return ret; } -static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value) -{ - struct ov7670_info *info = to_state(sd); - - *value = info->sat; - return 0; -} - -static int ov7670_s_hue(struct v4l2_subdev *sd, int value) -{ - struct ov7670_info *info = to_state(sd); - int matrix[CMATRIX_LEN]; - int ret; - - if (value < -180 || value > 180) - return -EINVAL; - info->hue = value; - ov7670_calc_cmatrix(info, matrix); - ret = ov7670_store_cmatrix(sd, matrix); - return ret; -} - - -static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value) -{ - struct ov7670_info *info = to_state(sd); - - *value = info->hue; - return 0; -} - /* * Some weird registers seem to store values in a sign/magnitude format! */ -static unsigned char ov7670_sm_to_abs(unsigned char v) -{ - if ((v & 0x80) == 0) - return v + 128; - return 128 - (v & 0x7f); -} - static unsigned char ov7670_abs_to_sm(unsigned char v) { @@ -1299,40 +1280,11 @@ static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) return ret; } -static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value) -{ - unsigned char v = 0; - int ret = ov7670_read(sd, REG_BRIGHT, &v); - - *value = ov7670_sm_to_abs(v); - return ret; -} - static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) { return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); } -static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value) -{ - unsigned char v = 0; - int ret = ov7670_read(sd, REG_CONTRAS, &v); - - *value = v; - return ret; -} - -static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char v = 0; - - ret = ov7670_read(sd, REG_MVFP, &v); - *value = (v & MVFP_MIRROR) == MVFP_MIRROR; - return ret; -} - - static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) { unsigned char v = 0; @@ -1348,19 +1300,6 @@ static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) return ret; } - - -static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char v = 0; - - ret = ov7670_read(sd, REG_MVFP, &v); - *value = (v & MVFP_FLIP) == MVFP_FLIP; - return ret; -} - - static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) { unsigned char v = 0; @@ -1409,16 +1348,6 @@ static int ov7670_s_gain(struct v4l2_subdev *sd, int value) /* * Tweak autogain. */ -static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com8; - - ret = ov7670_read(sd, REG_COM8, &com8); - *value = (com8 & COM8_AGC) != 0; - return ret; -} - static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) { int ret; @@ -1435,22 +1364,6 @@ static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) return ret; } -/* - * Exposure is spread all over the place: top 6 bits in AECHH, middle - * 8 in AECH, and two stashed in COM1 just for the hell of it. - */ -static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com1, aech, aechh; - - ret = ov7670_read(sd, REG_COM1, &com1) + - ov7670_read(sd, REG_AECH, &aech) + - ov7670_read(sd, REG_AECHH, &aechh); - *value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03); - return ret; -} - static int ov7670_s_exp(struct v4l2_subdev *sd, int value) { int ret; @@ -1477,20 +1390,6 @@ static int ov7670_s_exp(struct v4l2_subdev *sd, int value) /* * Tweak autoexposure. */ -static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value) -{ - int ret; - unsigned char com8; - enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value; - - ret = ov7670_read(sd, REG_COM8, &com8); - if (com8 & COM8_AEC) - *atype = V4L2_EXPOSURE_AUTO; - else - *atype = V4L2_EXPOSURE_MANUAL; - return ret; -} - static int ov7670_s_autoexp(struct v4l2_subdev *sd, enum v4l2_exposure_auto_type value) { @@ -1509,89 +1408,59 @@ static int ov7670_s_autoexp(struct v4l2_subdev *sd, } - -static int ov7670_queryctrl(struct v4l2_subdev *sd, - struct v4l2_queryctrl *qc) +static int ov7670_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - /* Fill in min, max, step and default value for these controls. */ - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); - case V4L2_CID_VFLIP: - case V4L2_CID_HFLIP: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); - case V4L2_CID_GAIN: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + struct v4l2_subdev *sd = to_sd(ctrl); + struct ov7670_info *info = to_state(sd); + + switch (ctrl->id) { case V4L2_CID_AUTOGAIN: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_EXPOSURE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500); - case V4L2_CID_EXPOSURE_AUTO: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + return ov7670_g_gain(sd, &info->gain->val); } return -EINVAL; } -static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +static int ov7670_s_ctrl(struct v4l2_ctrl *ctrl) { + struct v4l2_subdev *sd = to_sd(ctrl); + struct ov7670_info *info = to_state(sd); + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - return ov7670_g_brightness(sd, &ctrl->value); + return ov7670_s_brightness(sd, ctrl->val); case V4L2_CID_CONTRAST: - return ov7670_g_contrast(sd, &ctrl->value); + return ov7670_s_contrast(sd, ctrl->val); case V4L2_CID_SATURATION: - return ov7670_g_sat(sd, &ctrl->value); - case V4L2_CID_HUE: - return ov7670_g_hue(sd, &ctrl->value); + return ov7670_s_sat_hue(sd, + info->saturation->val, info->hue->val); case V4L2_CID_VFLIP: - return ov7670_g_vflip(sd, &ctrl->value); + return ov7670_s_vflip(sd, ctrl->val); case V4L2_CID_HFLIP: - return ov7670_g_hflip(sd, &ctrl->value); - case V4L2_CID_GAIN: - return ov7670_g_gain(sd, &ctrl->value); + return ov7670_s_hflip(sd, ctrl->val); case V4L2_CID_AUTOGAIN: - return ov7670_g_autogain(sd, &ctrl->value); - case V4L2_CID_EXPOSURE: - return ov7670_g_exp(sd, &ctrl->value); + /* Only set manual gain if auto gain is not explicitly + turned on. */ + if (!ctrl->val) { + /* ov7670_s_gain turns off auto gain */ + return ov7670_s_gain(sd, info->gain->val); + } + return ov7670_s_autogain(sd, ctrl->val); case V4L2_CID_EXPOSURE_AUTO: - return ov7670_g_autoexp(sd, &ctrl->value); + /* Only set manual exposure if auto exposure is not explicitly + turned on. */ + if (ctrl->val == V4L2_EXPOSURE_MANUAL) { + /* ov7670_s_exp turns off auto exposure */ + return ov7670_s_exp(sd, info->exposure->val); + } + return ov7670_s_autoexp(sd, ctrl->val); } return -EINVAL; } -static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - return ov7670_s_brightness(sd, ctrl->value); - case V4L2_CID_CONTRAST: - return ov7670_s_contrast(sd, ctrl->value); - case V4L2_CID_SATURATION: - return ov7670_s_sat(sd, ctrl->value); - case V4L2_CID_HUE: - return ov7670_s_hue(sd, ctrl->value); - case V4L2_CID_VFLIP: - return ov7670_s_vflip(sd, ctrl->value); - case V4L2_CID_HFLIP: - return ov7670_s_hflip(sd, ctrl->value); - case V4L2_CID_GAIN: - return ov7670_s_gain(sd, ctrl->value); - case V4L2_CID_AUTOGAIN: - return ov7670_s_autogain(sd, ctrl->value); - case V4L2_CID_EXPOSURE: - return ov7670_s_exp(sd, ctrl->value); - case V4L2_CID_EXPOSURE_AUTO: - return ov7670_s_autoexp(sd, - (enum v4l2_exposure_auto_type) ctrl->value); - } - return -EINVAL; -} +static const struct v4l2_ctrl_ops ov7670_ctrl_ops = { + .s_ctrl = ov7670_s_ctrl, + .g_volatile_ctrl = ov7670_g_volatile_ctrl, +}; static int ov7670_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) @@ -1635,9 +1504,13 @@ static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r static const struct v4l2_subdev_core_ops ov7670_core_ops = { .g_chip_ident = ov7670_g_chip_ident, - .g_ctrl = ov7670_g_ctrl, - .s_ctrl = ov7670_s_ctrl, - .queryctrl = ov7670_queryctrl, + .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, + .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, + .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, + .g_ctrl = v4l2_subdev_g_ctrl, + .s_ctrl = v4l2_subdev_s_ctrl, + .queryctrl = v4l2_subdev_queryctrl, + .querymenu = v4l2_subdev_querymenu, .reset = ov7670_reset, .init = ov7670_init, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1732,7 +1605,6 @@ static int ov7670_probe(struct i2c_client *client, info->devtype = &ov7670_devdata[id->driver_data]; info->fmt = &ov7670_formats[0]; - info->sat = 128; /* Review this */ info->clkrc = 0; /* Set default frame rate to 30 fps */ @@ -1743,6 +1615,46 @@ static int ov7670_probe(struct i2c_client *client, if (info->pclk_hb_disable) ov7670_write(sd, REG_COM10, COM10_PCLK_HB); + v4l2_ctrl_handler_init(&info->hdl, 10); + v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + info->saturation = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_SATURATION, 0, 256, 1, 128); + info->hue = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_HUE, -180, 180, 5, 0); + info->gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 128); + info->auto_gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + info->exposure = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 65535, 1, 500); + info->auto_exposure = v4l2_ctrl_new_std_menu(&info->hdl, &ov7670_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, + V4L2_EXPOSURE_AUTO); + sd->ctrl_handler = &info->hdl; + if (info->hdl.error) { + int err = info->hdl.error; + + v4l2_ctrl_handler_free(&info->hdl); + kfree(info); + return err; + } + /* + * We have checked empirically that hw allows to read back the gain + * value chosen by auto gain but that's not the case for auto exposure. + */ + v4l2_ctrl_auto_cluster(2, &info->auto_gain, 0, true); + v4l2_ctrl_auto_cluster(2, &info->auto_exposure, + V4L2_EXPOSURE_MANUAL, false); + v4l2_ctrl_cluster(2, &info->saturation); + v4l2_ctrl_handler_setup(&info->hdl); + return 0; } @@ -1750,9 +1662,11 @@ static int ov7670_probe(struct i2c_client *client, static int ov7670_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov7670_info *info = to_state(sd); v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); + v4l2_ctrl_handler_free(&info->hdl); + kfree(info); return 0; } From 593403c59b2c1977cf22313d4bb7e912b35ee797 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 07:58:48 -0300 Subject: [PATCH 2168/4607] [media] mcam-core: implement the control framework Signed-off-by: Hans Verkuil Signed-off-by: Javier Martin Cc: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/marvell-ccic/mcam-core.c | 54 ++++--------------- .../media/platform/marvell-ccic/mcam-core.h | 2 + 2 files changed, 11 insertions(+), 45 deletions(-) diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 7012913f34a2..92a33f081852 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1225,47 +1226,6 @@ static int mcam_vidioc_dqbuf(struct file *filp, void *priv, return ret; } - - -static int mcam_vidioc_queryctrl(struct file *filp, void *priv, - struct v4l2_queryctrl *qc) -{ - struct mcam_camera *cam = priv; - int ret; - - mutex_lock(&cam->s_mutex); - ret = sensor_call(cam, core, queryctrl, qc); - mutex_unlock(&cam->s_mutex); - return ret; -} - - -static int mcam_vidioc_g_ctrl(struct file *filp, void *priv, - struct v4l2_control *ctrl) -{ - struct mcam_camera *cam = priv; - int ret; - - mutex_lock(&cam->s_mutex); - ret = sensor_call(cam, core, g_ctrl, ctrl); - mutex_unlock(&cam->s_mutex); - return ret; -} - - -static int mcam_vidioc_s_ctrl(struct file *filp, void *priv, - struct v4l2_control *ctrl) -{ - struct mcam_camera *cam = priv; - int ret; - - mutex_lock(&cam->s_mutex); - ret = sensor_call(cam, core, s_ctrl, ctrl); - mutex_unlock(&cam->s_mutex); - return ret; -} - - static int mcam_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1513,9 +1473,6 @@ static const struct v4l2_ioctl_ops mcam_v4l_ioctl_ops = { .vidioc_dqbuf = mcam_vidioc_dqbuf, .vidioc_streamon = mcam_vidioc_streamon, .vidioc_streamoff = mcam_vidioc_streamoff, - .vidioc_queryctrl = mcam_vidioc_queryctrl, - .vidioc_g_ctrl = mcam_vidioc_g_ctrl, - .vidioc_s_ctrl = mcam_vidioc_s_ctrl, .vidioc_g_parm = mcam_vidioc_g_parm, .vidioc_s_parm = mcam_vidioc_s_parm, .vidioc_enum_framesizes = mcam_vidioc_enum_framesizes, @@ -1782,14 +1739,19 @@ int mccic_register(struct mcam_camera *cam) /* * Get the v4l2 setup done. */ + ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); + if (ret) + goto out_unregister; + cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; + mutex_lock(&cam->s_mutex); cam->vdev = mcam_v4l_template; cam->vdev.debug = 0; cam->vdev.v4l2_dev = &cam->v4l2_dev; + video_set_drvdata(&cam->vdev, cam); ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); if (ret) goto out; - video_set_drvdata(&cam->vdev, cam); /* * If so requested, try to get our DMA buffers now. @@ -1801,6 +1763,7 @@ int mccic_register(struct mcam_camera *cam) } out: + v4l2_ctrl_handler_free(&cam->ctrl_handler); mutex_unlock(&cam->s_mutex); return ret; out_unregister: @@ -1825,6 +1788,7 @@ void mccic_shutdown(struct mcam_camera *cam) if (cam->buffer_mode == B_vmalloc) mcam_free_dma_bufs(cam); video_unregister_device(&cam->vdev); + v4l2_ctrl_handler_free(&cam->ctrl_handler); v4l2_device_unregister(&cam->v4l2_dev); } diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 6c53ac9681da..01dec9e5fc2b 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -112,6 +113,7 @@ struct mcam_camera { * should not be touched by the platform code. */ struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; enum mcam_state state; unsigned long flags; /* Buffer status, mainly (dev_lock) */ int users; /* How many open FDs */ From dbf8f4e5839869d1ead50b661a2cbab3b7225002 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 08:06:35 -0300 Subject: [PATCH 2169/4607] [media] via-camera: implement the control framework And added a missing kfree to clean up the via_camera struct. Signed-off-by: Hans Verkuil Signed-off-by: Javier Martin Cc: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/via-camera.c | 60 +++++++---------------------- 1 file changed, 14 insertions(+), 46 deletions(-) diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index 63e8c3461239..b051c4a28554 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,7 @@ enum viacam_opstate { S_IDLE = 0, S_RUNNING = 1 }; struct via_camera { struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; struct video_device vdev; struct v4l2_subdev *sensor; struct platform_device *platdev; @@ -817,47 +819,6 @@ static int viacam_g_chip_ident(struct file *file, void *priv, return sensor_call(cam, core, g_chip_ident, ident); } -/* - * Control ops are passed through to the sensor. - */ -static int viacam_queryctrl(struct file *filp, void *priv, - struct v4l2_queryctrl *qc) -{ - struct via_camera *cam = priv; - int ret; - - mutex_lock(&cam->lock); - ret = sensor_call(cam, core, queryctrl, qc); - mutex_unlock(&cam->lock); - return ret; -} - - -static int viacam_g_ctrl(struct file *filp, void *priv, - struct v4l2_control *ctrl) -{ - struct via_camera *cam = priv; - int ret; - - mutex_lock(&cam->lock); - ret = sensor_call(cam, core, g_ctrl, ctrl); - mutex_unlock(&cam->lock); - return ret; -} - - -static int viacam_s_ctrl(struct file *filp, void *priv, - struct v4l2_control *ctrl) -{ - struct via_camera *cam = priv; - int ret; - - mutex_lock(&cam->lock); - ret = sensor_call(cam, core, s_ctrl, ctrl); - mutex_unlock(&cam->lock); - return ret; -} - /* * Only one input. */ @@ -1214,9 +1175,6 @@ static int viacam_enum_frameintervals(struct file *filp, void *priv, static const struct v4l2_ioctl_ops viacam_ioctl_ops = { .vidioc_g_chip_ident = viacam_g_chip_ident, - .vidioc_queryctrl = viacam_queryctrl, - .vidioc_g_ctrl = viacam_g_ctrl, - .vidioc_s_ctrl = viacam_s_ctrl, .vidioc_enum_input = viacam_enum_input, .vidioc_g_input = viacam_g_input, .vidioc_s_input = viacam_s_input, @@ -1418,8 +1376,12 @@ static int viacam_probe(struct platform_device *pdev) ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev); if (ret) { dev_err(&pdev->dev, "Unable to register v4l2 device\n"); - return ret; + goto out_free; } + ret = v4l2_ctrl_handler_init(&cam->ctrl_handler, 10); + if (ret) + goto out_unregister; + cam->v4l2_dev.ctrl_handler = &cam->ctrl_handler; /* * Convince the system that we can do DMA. */ @@ -1436,7 +1398,7 @@ static int viacam_probe(struct platform_device *pdev) */ ret = via_sensor_power_setup(cam); if (ret) - goto out_unregister; + goto out_ctrl_hdl_free; via_sensor_power_up(cam); /* @@ -1485,8 +1447,12 @@ out_irq: free_irq(viadev->pdev->irq, cam); out_power_down: via_sensor_power_release(cam); +out_ctrl_hdl_free: + v4l2_ctrl_handler_free(&cam->ctrl_handler); out_unregister: v4l2_device_unregister(&cam->v4l2_dev); +out_free: + kfree(cam); return ret; } @@ -1499,6 +1465,8 @@ static int viacam_remove(struct platform_device *pdev) v4l2_device_unregister(&cam->v4l2_dev); free_irq(viadev->pdev->irq, cam); via_sensor_power_release(cam); + v4l2_ctrl_handler_free(&cam->ctrl_handler); + kfree(cam); via_cam_info = NULL; return 0; } From cd19010c03cc9cce2366d5065720a3ab546833dd Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Fri, 8 Feb 2013 20:41:44 +0530 Subject: [PATCH 2170/4607] ARM: OMAP2+: PM: Fix the dt return condition in pm_late_init() Commit 1416408d {ARM: OMAP2+: PM: share some suspend-related functions across OMAP2, 3, 4} moved suspend code to common place but now with that change, for DT build on OMAP4, suspend hooks are not getting registered which results in no suspend support. The DT return condition is limited to PMIC and smartreflex initialization and hence restrict it so that suspend ops gets registered. Cc: Paul Walmsley Cc: Kevin Hilman Signed-off-by: Santosh Shilimkar Reviewed-by: Felipe Balbi Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/pm.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index f4b3143a8b1d..1ec429964b7f 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -345,19 +345,19 @@ int __init omap2_common_pm_late_init(void) * a completely different mechanism. * Disable this part if a DT blob is available. */ - if (of_have_populated_dt()) - return 0; + if (!of_have_populated_dt()) { - /* Init the voltage layer */ - omap_pmic_late_init(); - omap_voltage_late_init(); + /* Init the voltage layer */ + omap_pmic_late_init(); + omap_voltage_late_init(); - /* Initialize the voltages */ - omap3_init_voltages(); - omap4_init_voltages(); + /* Initialize the voltages */ + omap3_init_voltages(); + omap4_init_voltages(); - /* Smartreflex device init */ - omap_devinit_smartreflex(); + /* Smartreflex device init */ + omap_devinit_smartreflex(); + } #ifdef CONFIG_SUSPEND suspend_set_ops(&omap_pm_ops); From de03277d6ae26d09b3af8617f291c4bb3db7d2eb Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Tue, 29 Jan 2013 08:21:40 -0300 Subject: [PATCH 2171/4607] [media] ov7670: remove legacy ctrl callbacks via-camera and mcam-core were the only bridge drivers that used ov7670. Since now they have been moved to use the ctrl framework, the old legacy callbacks in the ov7670 can be removed. Signed-off-by: Javier Martin Cc: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ov7670.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 93a051e8873f..05ed5b8e7f88 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1504,13 +1504,6 @@ static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r static const struct v4l2_subdev_core_ops ov7670_core_ops = { .g_chip_ident = ov7670_g_chip_ident, - .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, - .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, - .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, - .g_ctrl = v4l2_subdev_g_ctrl, - .s_ctrl = v4l2_subdev_s_ctrl, - .queryctrl = v4l2_subdev_queryctrl, - .querymenu = v4l2_subdev_querymenu, .reset = ov7670_reset, .init = ov7670_init, #ifdef CONFIG_VIDEO_ADV_DEBUG From 88b404c435ffb6c103faf85cc1b41077dcd03bf9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 30 Jan 2013 03:03:43 -0300 Subject: [PATCH 2172/4607] [media] tm6000: check an allocation for failure This allocation had no error checking. It didn't need to be under the mutex so I moved it out form there. That makes the error handling easier and is a potential speed up. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/tm6000/tm6000-core.c b/drivers/media/usb/tm6000/tm6000-core.c index 22cc0116deb6..7c32353c59db 100644 --- a/drivers/media/usb/tm6000/tm6000-core.c +++ b/drivers/media/usb/tm6000/tm6000-core.c @@ -40,10 +40,13 @@ int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req, u8 *data = NULL; int delay = 5000; - mutex_lock(&dev->usb_lock); - - if (len) + if (len) { data = kzalloc(len, GFP_KERNEL); + if (!data) + return -ENOMEM; + } + + mutex_lock(&dev->usb_lock); if (req_type & USB_DIR_IN) pipe = usb_rcvctrlpipe(dev->udev, 0); From 9690fd80d3fb1bbfe04d7e1d8dde0cfd353c7b8d Mon Sep 17 00:00:00 2001 From: Vadim Frolov Date: Wed, 30 Jan 2013 05:14:59 -0300 Subject: [PATCH 2173/4607] [media] saa7134: Add capture card Hawell HW-9004V1 This patch adds new capture board Hawell HW-9004V1. This card has 4 SAA71300 chips. In order to work it is needed to initialize its registers (gpio mask and value). The value of these registers were dumped under Windows using flytest. Signed-off-by: Vadim Frolov Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 1 + drivers/media/pci/saa7134/saa7134-cards.c | 17 +++++++++++++++++ drivers/media/pci/saa7134/saa7134.h | 1 + 3 files changed, 19 insertions(+) diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 94d9025aa82d..b3ad68309109 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -189,3 +189,4 @@ 188 -> Sensoray 811/911 [6000:0811,6000:0911] 189 -> Kworld PC150-U [17de:a134] 190 -> Asus My Cinema PS3-100 [1043:48cd] +191 -> Hawell HW-9004V1 diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c index bc08f1dbc293..dc68cf1070f7 100644 --- a/drivers/media/pci/saa7134/saa7134-cards.c +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -5773,6 +5773,23 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0000000, }, }, + [SAA7134_BOARD_HAWELL_HW_9004V1] = { + /* Hawell HW-9004V1 */ + /* Vadim Frolov */ + .name = "Hawell HW-9004V1", + .audio_clock = 0x00200000, + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x618E700, + .inputs = {{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x6010000, + } }, + }, }; diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index f804324e07fd..71eefef5e324 100644 --- a/drivers/media/pci/saa7134/saa7134.h +++ b/drivers/media/pci/saa7134/saa7134.h @@ -333,6 +333,7 @@ struct saa7134_card_ir { #define SAA7134_BOARD_SENSORAY811_911 188 #define SAA7134_BOARD_KWORLD_PC150U 189 #define SAA7134_BOARD_ASUSTeK_PS3_100 190 +#define SAA7134_BOARD_HAWELL_HW_9004V1 191 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 From 20e3dbef8e7acf679ba179e1a077303e3f76f975 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 8 Feb 2013 14:51:36 -0200 Subject: [PATCH 2174/4607] [media] Documentation: update V4L cardlists Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.au0828 | 2 +- Documentation/video4linux/CARDLIST.cx23885 | 2 ++ Documentation/video4linux/CARDLIST.em28xx | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/video4linux/CARDLIST.au0828 b/Documentation/video4linux/CARDLIST.au0828 index a8a65753e544..55a21deab7db 100644 --- a/Documentation/video4linux/CARDLIST.au0828 +++ b/Documentation/video4linux/CARDLIST.au0828 @@ -1,5 +1,5 @@ 0 -> Unknown board (au0828) - 1 -> Hauppauge HVR950Q (au0828) [2040:7200,2040:7210,2040:7217,2040:721b,2040:721e,2040:721f,2040:7280,0fd9:0008,2040:7260,2040:7213] + 1 -> Hauppauge HVR950Q (au0828) [2040:7200,2040:7210,2040:7217,2040:721b,2040:721e,2040:721f,2040:7280,0fd9:0008,2040:7260,2040:7213,2040:7270] 2 -> Hauppauge HVR850 (au0828) [2040:7240] 3 -> DViCO FusionHDTV USB (au0828) [0fe9:d620] 4 -> Hauppauge HVR950Q rev xxF8 (au0828) [2040:7201,2040:7211,2040:7281] diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 index 1299b5e82d7f..9f056d512e35 100644 --- a/Documentation/video4linux/CARDLIST.cx23885 +++ b/Documentation/video4linux/CARDLIST.cx23885 @@ -36,3 +36,5 @@ 35 -> TeVii S471 [d471:9022] 36 -> Hauppauge WinTV-HVR1255 [0070:2259] 37 -> Prof Revolution DVB-S2 8000 [8000:3034] + 38 -> Hauppauge WinTV-HVR4400 [0070:c108,0070:c138,0070:c12a,0070:c1f8] + 39 -> AVerTV Hybrid Express Slim HC81R [1461:d939] diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index d99262dda533..3f12865b2a88 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -76,7 +76,7 @@ 76 -> KWorld PlusTV 340U or UB435-Q (ATSC) (em2870) [1b80:a340] 77 -> EM2874 Leadership ISDBT (em2874) 78 -> PCTV nanoStick T2 290e (em28174) - 79 -> Terratec Cinergy H5 (em2884) [0ccd:008e,0ccd:00ac,0ccd:10a2,0ccd:10ad] + 79 -> Terratec Cinergy H5 (em2884) [0ccd:10a2,0ccd:10ad] 80 -> PCTV DVB-S2 Stick (460e) (em28174) 81 -> Hauppauge WinTV HVR 930C (em2884) [2040:1605] 82 -> Terratec Cinergy HTC Stick (em2884) [0ccd:00b2] @@ -84,3 +84,4 @@ 84 -> MaxMedia UB425-TC (em2874) [1b80:e425] 85 -> PCTV QuatroStick (510e) (em2884) [2304:0242] 86 -> PCTV QuatroStick nano (520e) (em2884) [2013:0251] + 87 -> Terratec Cinergy HTC USB XS (em2884) [0ccd:008e,0ccd:00ac] From 47ebe3f93250fce6f4eaa16309ae5eee9d4099b3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 30 Jan 2013 09:25:25 -0300 Subject: [PATCH 2175/4607] [media] soc-camera: fix compilation breakage in 3 drivers A recent commit broke compilation of 3 camera drivers: for PXA2x0, OMAP1 and MX1 by using a wrong pointer. Fix them. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx1_camera.c | 2 +- drivers/media/platform/soc_camera/omap1_camera.c | 4 ++-- drivers/media/platform/soc_camera/pxa_camera.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index 4b661e87d8b1..25b2a285dc86 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -372,7 +372,7 @@ static void mx1_camera_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct mx1_buffer), icd, &icd->host_lock); + sizeof(struct mx1_buffer), icd, &ici->host_lock); } static int mclk_get_divisor(struct mx1_camera_dev *pcdev) diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index dcf7be814776..2547bf88f79f 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -1383,12 +1383,12 @@ static void omap1_cam_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &icd->host_lock); + sizeof(struct omap1_cam_buf), icd, &ici->host_lock); else videobuf_queue_sg_init(q, &omap1_videobuf_ops, icd->parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct omap1_cam_buf), icd, &icd->host_lock); + sizeof(struct omap1_cam_buf), icd, &ici->host_lock); /* use videobuf mode (auto)selected with the module parameter */ pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG; diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 1fbec5202e4f..9c97f7e985e1 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -842,7 +842,7 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q, */ videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - sizeof(struct pxa_buffer), icd, &icd->host_lock); + sizeof(struct pxa_buffer), icd, &ici->host_lock); } static u32 mclk_get_divisor(struct platform_device *pdev, From 0a3237704dec476be3cdfbe8fc9df9cc65b14442 Mon Sep 17 00:00:00 2001 From: Jose Alberto Reguero Date: Sun, 3 Feb 2013 18:30:38 -0300 Subject: [PATCH 2176/4607] [media] [PATH,1/2] mxl5007 move reset to attach This patch move the soft reset to the attach function because with dual tuners, when one tuner do reset, the other one is perturbed, and the stream has errors. Signed-off-by: Jose Alberto Reguero Reviewed-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/mxl5007t.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c index 69e453ef0a1a..eb61304c260d 100644 --- a/drivers/media/tuners/mxl5007t.c +++ b/drivers/media/tuners/mxl5007t.c @@ -531,10 +531,6 @@ static int mxl5007t_tuner_init(struct mxl5007t_state *state, struct reg_pair_t *init_regs; int ret; - ret = mxl5007t_soft_reset(state); - if (mxl_fail(ret)) - goto fail; - /* calculate initialization reg array */ init_regs = mxl5007t_calc_init_regs(state, mode); @@ -900,7 +896,20 @@ struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, /* existing tuner instance */ break; } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = mxl5007t_soft_reset(state); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (mxl_fail(ret)) + goto fail; + fe->tuner_priv = state; + mutex_unlock(&mxl5007t_list_mutex); memcpy(&fe->ops.tuner_ops, &mxl5007t_tuner_ops, From da92c29340172fddb6ea838cd964614195031071 Mon Sep 17 00:00:00 2001 From: Matti Kurkela Date: Tue, 5 Feb 2013 07:08:42 -0300 Subject: [PATCH 2177/4607] [media] ttusb2: Kconfig patch to auto-select frontends for TechnoTrend CT-3650 The ttusb2 module is already updated to recognize the TechnoTrend CT-3650 CI DVB C/T USB2.0 receiver in addition to the Pinnacle 400e. But if MEDIA_SUBDRV_AUTOSELECT is used, the required tuner and demodulator modules are not automatically selected. Here's a patch to fix that and add a note of the CT-3650 to the online help of the ttusb2 module. This patch applies cleanly to 3.7.6 and other 3.7.x kernels. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb/Kconfig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig index c423eb80c3a5..c5d95662e2e1 100644 --- a/drivers/media/usb/dvb-usb/Kconfig +++ b/drivers/media/usb/dvb-usb/Kconfig @@ -202,8 +202,12 @@ config DVB_USB_TTUSB2 select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT help - Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The + Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver and + the TechnoTrend CT-3650 CI DVB-C/T USB2.0 receiver. The firmware protocol used by this module is similar to the one used by the old ttusb-driver - that's why the module is called dvb-usb-ttusb2. From 343d9c283c9847da043fda3e76e3197f27b667dd Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 8 Feb 2013 13:00:22 -0500 Subject: [PATCH 2178/4607] jbd2: add tracepoints which provide per-handle statistics Handles which stay open a long time are problematic when it comes time to close down a transaction so it can be committed. These tracepoints will help us determine which ones are the problematic ones, and to validate whether changes makes things better or worse. Signed-off-by: "Theodore Ts'o" --- fs/jbd2/transaction.c | 28 ++++++++++- include/linux/jbd2.h | 8 ++- include/trace/events/jbd2.h | 98 +++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 3 deletions(-) diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 735609e2d636..b7e2385c6e92 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -30,6 +30,8 @@ #include #include +#include + static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh); static void __jbd2_journal_unfile_buffer(struct journal_head *jh); @@ -307,6 +309,8 @@ repeat: */ update_t_max_wait(transaction, ts); handle->h_transaction = transaction; + handle->h_requested_credits = nblocks; + handle->h_start_jiffies = jiffies; atomic_inc(&transaction->t_updates); atomic_inc(&transaction->t_handle_count); jbd_debug(4, "Handle %p given %d credits (total %d, free %d)\n", @@ -353,7 +357,8 @@ static handle_t *new_handle(int nblocks) * Return a pointer to a newly allocated handle, or an ERR_PTR() value * on failure. */ -handle_t *jbd2__journal_start(journal_t *journal, int nblocks, gfp_t gfp_mask) +handle_t *jbd2__journal_start(journal_t *journal, int nblocks, gfp_t gfp_mask, + unsigned int type, unsigned int line_no) { handle_t *handle = journal_current_handle(); int err; @@ -379,6 +384,11 @@ handle_t *jbd2__journal_start(journal_t *journal, int nblocks, gfp_t gfp_mask) current->journal_info = NULL; handle = ERR_PTR(err); } + handle->h_type = type; + handle->h_line_no = line_no; + trace_jbd2_handle_start(journal->j_fs_dev->bd_dev, + handle->h_transaction->t_tid, type, + line_no, nblocks); return handle; } EXPORT_SYMBOL(jbd2__journal_start); @@ -386,7 +396,7 @@ EXPORT_SYMBOL(jbd2__journal_start); handle_t *jbd2_journal_start(journal_t *journal, int nblocks) { - return jbd2__journal_start(journal, nblocks, GFP_NOFS); + return jbd2__journal_start(journal, nblocks, GFP_NOFS, 0, 0); } EXPORT_SYMBOL(jbd2_journal_start); @@ -448,7 +458,14 @@ int jbd2_journal_extend(handle_t *handle, int nblocks) goto unlock; } + trace_jbd2_handle_extend(journal->j_fs_dev->bd_dev, + handle->h_transaction->t_tid, + handle->h_type, handle->h_line_no, + handle->h_buffer_credits, + nblocks); + handle->h_buffer_credits += nblocks; + handle->h_requested_credits += nblocks; atomic_add(nblocks, &transaction->t_outstanding_credits); result = 0; @@ -1377,6 +1394,13 @@ int jbd2_journal_stop(handle_t *handle) } jbd_debug(4, "Handle %p going down\n", handle); + trace_jbd2_handle_stats(journal->j_fs_dev->bd_dev, + handle->h_transaction->t_tid, + handle->h_type, handle->h_line_no, + jiffies - handle->h_start_jiffies, + handle->h_sync, handle->h_requested_credits, + (handle->h_requested_credits - + handle->h_buffer_credits)); /* * Implement synchronous transaction batching. If the handle diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 24db7256a5ff..fa5fea17b619 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -400,6 +400,11 @@ struct jbd2_journal_handle unsigned int h_sync: 1; /* sync-on-close */ unsigned int h_jdata: 1; /* force data journaling */ unsigned int h_aborted: 1; /* fatal error on handle */ + unsigned int h_type: 8; /* for handle statistics */ + unsigned int h_line_no: 16; /* for handle statistics */ + + unsigned long h_start_jiffies; + unsigned int h_requested_credits; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map h_lockdep_map; @@ -1071,7 +1076,8 @@ static inline handle_t *journal_current_handle(void) */ extern handle_t *jbd2_journal_start(journal_t *, int nblocks); -extern handle_t *jbd2__journal_start(journal_t *, int nblocks, gfp_t gfp_mask); +extern handle_t *jbd2__journal_start(journal_t *, int nblocks, gfp_t gfp_mask, + unsigned int type, unsigned int line_no); extern int jbd2_journal_restart(handle_t *, int nblocks); extern int jbd2__journal_restart(handle_t *, int nblocks, gfp_t gfp_mask); extern int jbd2_journal_extend (handle_t *, int nblocks); diff --git a/include/trace/events/jbd2.h b/include/trace/events/jbd2.h index 5419f57beb1f..070df49e4a1d 100644 --- a/include/trace/events/jbd2.h +++ b/include/trace/events/jbd2.h @@ -132,6 +132,104 @@ TRACE_EVENT(jbd2_submit_inode_data, (unsigned long) __entry->ino) ); +TRACE_EVENT(jbd2_handle_start, + TP_PROTO(dev_t dev, unsigned long tid, unsigned int type, + unsigned int line_no, int requested_blocks), + + TP_ARGS(dev, tid, type, line_no, requested_blocks), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( unsigned long, tid ) + __field( unsigned int, type ) + __field( unsigned int, line_no ) + __field( int, requested_blocks) + ), + + TP_fast_assign( + __entry->dev = dev; + __entry->tid = tid; + __entry->type = type; + __entry->line_no = line_no; + __entry->requested_blocks = requested_blocks; + ), + + TP_printk("dev %d,%d tid %lu type %u line_no %u " + "requested_blocks %d", + MAJOR(__entry->dev), MINOR(__entry->dev), __entry->tid, + __entry->type, __entry->line_no, __entry->requested_blocks) +); + +TRACE_EVENT(jbd2_handle_extend, + TP_PROTO(dev_t dev, unsigned long tid, unsigned int type, + unsigned int line_no, int buffer_credits, + int requested_blocks), + + TP_ARGS(dev, tid, type, line_no, buffer_credits, requested_blocks), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( unsigned long, tid ) + __field( unsigned int, type ) + __field( unsigned int, line_no ) + __field( int, buffer_credits ) + __field( int, requested_blocks) + ), + + TP_fast_assign( + __entry->dev = dev; + __entry->tid = tid; + __entry->type = type; + __entry->line_no = line_no; + __entry->buffer_credits = buffer_credits; + __entry->requested_blocks = requested_blocks; + ), + + TP_printk("dev %d,%d tid %lu type %u line_no %u " + "buffer_credits %d requested_blocks %d", + MAJOR(__entry->dev), MINOR(__entry->dev), __entry->tid, + __entry->type, __entry->line_no, __entry->buffer_credits, + __entry->requested_blocks) +); + +TRACE_EVENT(jbd2_handle_stats, + TP_PROTO(dev_t dev, unsigned long tid, unsigned int type, + unsigned int line_no, int interval, int sync, + int requested_blocks, int dirtied_blocks), + + TP_ARGS(dev, tid, type, line_no, interval, sync, + requested_blocks, dirtied_blocks), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( unsigned long, tid ) + __field( unsigned int, type ) + __field( unsigned int, line_no ) + __field( int, interval ) + __field( int, sync ) + __field( int, requested_blocks) + __field( int, dirtied_blocks ) + ), + + TP_fast_assign( + __entry->dev = dev; + __entry->tid = tid; + __entry->type = type; + __entry->line_no = line_no; + __entry->interval = interval; + __entry->sync = sync; + __entry->requested_blocks = requested_blocks; + __entry->dirtied_blocks = dirtied_blocks; + ), + + TP_printk("dev %d,%d tid %lu type %u line_no %u interval %d " + "sync %d requested_blocks %d dirtied_blocks %d", + MAJOR(__entry->dev), MINOR(__entry->dev), __entry->tid, + __entry->type, __entry->line_no, __entry->interval, + __entry->sync, __entry->requested_blocks, + __entry->dirtied_blocks) +); + TRACE_EVENT(jbd2_run_stats, TP_PROTO(dev_t dev, unsigned long tid, struct transaction_run_stats_s *stats), From 722887ddc8982ff40e40b650fbca9ae1e56259bc Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 8 Feb 2013 13:00:31 -0500 Subject: [PATCH 2179/4607] ext4: move the jbd2 wrapper functions out of super.c Move the jbd2 wrapper functions which start and stop handles out of super.c, where they don't really logically belong, and into ext4_jbd2.c. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 2 + fs/ext4/ext4_jbd2.c | 101 +++++++++++++++++++++++++++++++++++++++++ fs/ext4/super.c | 107 +------------------------------------------- 3 files changed, 105 insertions(+), 105 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index d93393eb5f2d..a5ae87c51401 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2149,6 +2149,8 @@ extern void *ext4_kvzalloc(size_t size, gfp_t flags); extern void ext4_kvfree(void *ptr); extern int ext4_alloc_flex_bg_array(struct super_block *sb, ext4_group_t ngroup); +extern const char *ext4_decode_error(struct super_block *sb, int errno, + char nbuf[16]); extern __printf(4, 5) void __ext4_error(struct super_block *, const char *, unsigned int, const char *, ...); diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index b4323ba846b5..6f6114525535 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -6,6 +6,107 @@ #include +/* Just increment the non-pointer handle value */ +static handle_t *ext4_get_nojournal(void) +{ + handle_t *handle = current->journal_info; + unsigned long ref_cnt = (unsigned long)handle; + + BUG_ON(ref_cnt >= EXT4_NOJOURNAL_MAX_REF_COUNT); + + ref_cnt++; + handle = (handle_t *)ref_cnt; + + current->journal_info = handle; + return handle; +} + + +/* Decrement the non-pointer handle value */ +static void ext4_put_nojournal(handle_t *handle) +{ + unsigned long ref_cnt = (unsigned long)handle; + + BUG_ON(ref_cnt == 0); + + ref_cnt--; + handle = (handle_t *)ref_cnt; + + current->journal_info = handle; +} + +/* + * Wrappers for jbd2_journal_start/end. + */ +handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks) +{ + journal_t *journal; + + trace_ext4_journal_start(sb, nblocks, _RET_IP_); + if (sb->s_flags & MS_RDONLY) + return ERR_PTR(-EROFS); + + WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE); + journal = EXT4_SB(sb)->s_journal; + if (!journal) + return ext4_get_nojournal(); + /* + * Special case here: if the journal has aborted behind our + * backs (eg. EIO in the commit thread), then we still need to + * take the FS itself readonly cleanly. + */ + if (is_journal_aborted(journal)) { + ext4_abort(sb, "Detected aborted journal"); + return ERR_PTR(-EROFS); + } + return jbd2_journal_start(journal, nblocks); +} + +int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle) +{ + struct super_block *sb; + int err; + int rc; + + if (!ext4_handle_valid(handle)) { + ext4_put_nojournal(handle); + return 0; + } + sb = handle->h_transaction->t_journal->j_private; + err = handle->h_err; + rc = jbd2_journal_stop(handle); + + if (!err) + err = rc; + if (err) + __ext4_std_error(sb, where, line, err); + return err; +} + +void ext4_journal_abort_handle(const char *caller, unsigned int line, + const char *err_fn, struct buffer_head *bh, + handle_t *handle, int err) +{ + char nbuf[16]; + const char *errstr = ext4_decode_error(NULL, err, nbuf); + + BUG_ON(!ext4_handle_valid(handle)); + + if (bh) + BUFFER_TRACE(bh, "abort"); + + if (!handle->h_err) + handle->h_err = err; + + if (is_handle_aborted(handle)) + return; + + printk(KERN_ERR "EXT4-fs: %s:%d: aborting transaction: %s in %s\n", + caller, line, errstr, err_fn); + + jbd2_journal_abort_handle(handle); +} + int __ext4_journal_get_write_access(const char *where, unsigned int line, handle_t *handle, struct buffer_head *bh) { diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 2e1f94704b1f..cb9d67fbc8f0 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -69,8 +69,6 @@ static void ext4_mark_recovery_complete(struct super_block *sb, static void ext4_clear_journal_err(struct super_block *sb, struct ext4_super_block *es); static int ext4_sync_fs(struct super_block *sb, int wait); -static const char *ext4_decode_error(struct super_block *sb, int errno, - char nbuf[16]); static int ext4_remount(struct super_block *sb, int *flags, char *data); static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf); static int ext4_unfreeze(struct super_block *sb); @@ -296,107 +294,6 @@ void ext4_itable_unused_set(struct super_block *sb, } -/* Just increment the non-pointer handle value */ -static handle_t *ext4_get_nojournal(void) -{ - handle_t *handle = current->journal_info; - unsigned long ref_cnt = (unsigned long)handle; - - BUG_ON(ref_cnt >= EXT4_NOJOURNAL_MAX_REF_COUNT); - - ref_cnt++; - handle = (handle_t *)ref_cnt; - - current->journal_info = handle; - return handle; -} - - -/* Decrement the non-pointer handle value */ -static void ext4_put_nojournal(handle_t *handle) -{ - unsigned long ref_cnt = (unsigned long)handle; - - BUG_ON(ref_cnt == 0); - - ref_cnt--; - handle = (handle_t *)ref_cnt; - - current->journal_info = handle; -} - -/* - * Wrappers for jbd2_journal_start/end. - */ -handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks) -{ - journal_t *journal; - - trace_ext4_journal_start(sb, nblocks, _RET_IP_); - if (sb->s_flags & MS_RDONLY) - return ERR_PTR(-EROFS); - - WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE); - journal = EXT4_SB(sb)->s_journal; - if (!journal) - return ext4_get_nojournal(); - /* - * Special case here: if the journal has aborted behind our - * backs (eg. EIO in the commit thread), then we still need to - * take the FS itself readonly cleanly. - */ - if (is_journal_aborted(journal)) { - ext4_abort(sb, "Detected aborted journal"); - return ERR_PTR(-EROFS); - } - return jbd2_journal_start(journal, nblocks); -} - -int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle) -{ - struct super_block *sb; - int err; - int rc; - - if (!ext4_handle_valid(handle)) { - ext4_put_nojournal(handle); - return 0; - } - sb = handle->h_transaction->t_journal->j_private; - err = handle->h_err; - rc = jbd2_journal_stop(handle); - - if (!err) - err = rc; - if (err) - __ext4_std_error(sb, where, line, err); - return err; -} - -void ext4_journal_abort_handle(const char *caller, unsigned int line, - const char *err_fn, struct buffer_head *bh, - handle_t *handle, int err) -{ - char nbuf[16]; - const char *errstr = ext4_decode_error(NULL, err, nbuf); - - BUG_ON(!ext4_handle_valid(handle)); - - if (bh) - BUFFER_TRACE(bh, "abort"); - - if (!handle->h_err) - handle->h_err = err; - - if (is_handle_aborted(handle)) - return; - - printk(KERN_ERR "EXT4-fs: %s:%d: aborting transaction: %s in %s\n", - caller, line, errstr, err_fn); - - jbd2_journal_abort_handle(handle); -} - static void __save_error_info(struct super_block *sb, const char *func, unsigned int line) { @@ -582,8 +479,8 @@ void ext4_error_file(struct file *file, const char *function, ext4_handle_error(inode->i_sb); } -static const char *ext4_decode_error(struct super_block *sb, int errno, - char nbuf[16]) +const char *ext4_decode_error(struct super_block *sb, int errno, + char nbuf[16]) { char *errstr = NULL; From 625958207752519bd0e49d0dcdde61a20ac081b3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 6 Feb 2013 07:53:31 -0300 Subject: [PATCH 2180/4607] [media] cx2341x: move from media/i2c to media/common The cx2341x module is a helper module for conexant-based MPEG encoders. It isn't an i2c module at all, instead it should be in common since it is used by 7 pci and usb drivers to handle the MPEG setup. It also shouldn't be visible in the config menu as it is always selected automatically by those drivers that need it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/Kconfig | 3 +++ drivers/media/common/Makefile | 1 + drivers/media/{i2c => common}/cx2341x.c | 0 drivers/media/i2c/Kconfig | 14 -------------- drivers/media/i2c/Makefile | 1 - 5 files changed, 4 insertions(+), 15 deletions(-) rename drivers/media/{i2c => common}/cx2341x.c (100%) diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index d2a436ce77f8..dfac52f312cf 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -5,6 +5,9 @@ config MEDIA_COMMON_OPTIONS comment "common driver options" depends on MEDIA_COMMON_OPTIONS +config VIDEO_CX2341X + tristate + source "drivers/media/common/b2c2/Kconfig" source "drivers/media/common/saa7146/Kconfig" source "drivers/media/common/siano/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index b8e2e3a33a31..9b8ea4fae06f 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1 +1,2 @@ obj-y += b2c2/ saa7146/ siano/ +obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o diff --git a/drivers/media/i2c/cx2341x.c b/drivers/media/common/cx2341x.c similarity index 100% rename from drivers/media/i2c/cx2341x.c rename to drivers/media/common/cx2341x.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index ecdf7e3fdcf4..97f14c24a40c 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -317,20 +317,6 @@ config VIDEO_SAA717X source "drivers/media/i2c/cx25840/Kconfig" -comment "MPEG video encoders" - -config VIDEO_CX2341X - tristate "Conexant CX2341x MPEG encoders" - depends on VIDEO_V4L2 - ---help--- - Support for the Conexant CX23416 MPEG encoders - and CX23415 MPEG encoder/decoders. - - This module currently supports the encoding functions only. - - To compile this driver as a module, choose M here: the - module will be called cx2341x. - comment "Video encoders" config VIDEO_SAA7127 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index b35e25798cd3..3a8334f205f3 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -64,6 +64,5 @@ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o -obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o From f1c50f2489f40494658a6b7326bd6d5a26f72711 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 6 Feb 2013 07:57:57 -0300 Subject: [PATCH 2181/4607] [media] btcx-risc: move from media/i2c to media/common The btcx-risc module is a helper module for bttv/conexant based TV cards. It isn't an i2c module at all, instead it should be in common since it is used by 4 pci drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/Kconfig | 4 ++++ drivers/media/common/Makefile | 1 + drivers/media/{i2c => common}/btcx-risc.c | 0 drivers/media/{i2c => common}/btcx-risc.h | 0 drivers/media/i2c/Kconfig | 4 ---- drivers/media/i2c/Makefile | 1 - drivers/media/pci/bt8xx/Makefile | 1 + drivers/media/pci/cx23885/Makefile | 1 + drivers/media/pci/cx25821/Makefile | 1 + drivers/media/pci/cx88/Makefile | 1 + 10 files changed, 9 insertions(+), 5 deletions(-) rename drivers/media/{i2c => common}/btcx-risc.c (100%) rename drivers/media/{i2c => common}/btcx-risc.h (100%) diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index dfac52f312cf..28c8a602e7ef 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -8,6 +8,10 @@ comment "common driver options" config VIDEO_CX2341X tristate +config VIDEO_BTCX + depends on PCI + tristate + source "drivers/media/common/b2c2/Kconfig" source "drivers/media/common/saa7146/Kconfig" source "drivers/media/common/siano/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index 9b8ea4fae06f..cfe34499d993 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,2 +1,3 @@ obj-y += b2c2/ saa7146/ siano/ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o +obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o diff --git a/drivers/media/i2c/btcx-risc.c b/drivers/media/common/btcx-risc.c similarity index 100% rename from drivers/media/i2c/btcx-risc.c rename to drivers/media/common/btcx-risc.c diff --git a/drivers/media/i2c/btcx-risc.h b/drivers/media/common/btcx-risc.h similarity index 100% rename from drivers/media/i2c/btcx-risc.h rename to drivers/media/common/btcx-risc.h diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 97f14c24a40c..cd2b17148ad6 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -2,10 +2,6 @@ # Generic video config states # -config VIDEO_BTCX - depends on PCI - tristate - config VIDEO_TVEEPROM tristate depends on I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 3a8334f205f3..3691e70f4e75 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -63,6 +63,5 @@ obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o -obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o obj-$(CONFIG_VIDEO_AK881X) += ak881x.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile index 5f06597c6a6e..f9fe7c4e7d53 100644 --- a/drivers/media/pci/bt8xx/Makefile +++ b/drivers/media/pci/bt8xx/Makefile @@ -8,4 +8,5 @@ obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends ccflags-y += -Idrivers/media/i2c +ccflags-y += -Idrivers/media/common ccflags-y += -Idrivers/media/tuners diff --git a/drivers/media/pci/cx23885/Makefile b/drivers/media/pci/cx23885/Makefile index a2cbdcf15a8c..2a2cafb8cf5b 100644 --- a/drivers/media/pci/cx23885/Makefile +++ b/drivers/media/pci/cx23885/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885.o obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o ccflags-y += -Idrivers/media/i2c +ccflags-y += -Idrivers/media/common ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile index 5bf3ea4c1556..caa32b7b51f8 100644 --- a/drivers/media/pci/cx25821/Makefile +++ b/drivers/media/pci/cx25821/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o ccflags-y += -Idrivers/media/i2c +ccflags-y += -Idrivers/media/common ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends diff --git a/drivers/media/pci/cx88/Makefile b/drivers/media/pci/cx88/Makefile index d3679c3ee248..8619c1becee2 100644 --- a/drivers/media/pci/cx88/Makefile +++ b/drivers/media/pci/cx88/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o ccflags-y += -Idrivers/media/i2c +ccflags-y += -Idrivers/media/common ccflags-y += -Idrivers/media/tuners ccflags-y += -Idrivers/media/dvb-core ccflags-y += -Idrivers/media/dvb-frontends From a393edad2a1d5a53918bfb5ac1608fb8d17068dc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 6 Feb 2013 08:03:41 -0300 Subject: [PATCH 2182/4607] [media] tveeprom: move from media/i2c to media/common The tveeprom module is a helper module for Hauppauge-based eeproms. It's used by many drivers and the i2c part is actually optional, so this driver is better placed in the media/common directory. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/Kconfig | 4 ++++ drivers/media/common/Makefile | 1 + drivers/media/{i2c => common}/tveeprom.c | 0 drivers/media/i2c/Kconfig | 8 -------- drivers/media/i2c/Makefile | 1 - 5 files changed, 5 insertions(+), 9 deletions(-) rename drivers/media/{i2c => common}/tveeprom.c (100%) diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index 28c8a602e7ef..56c25e6299e9 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -12,6 +12,10 @@ config VIDEO_BTCX depends on PCI tristate +config VIDEO_TVEEPROM + tristate + depends on I2C + source "drivers/media/common/b2c2/Kconfig" source "drivers/media/common/saa7146/Kconfig" source "drivers/media/common/siano/Kconfig" diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index cfe34499d993..8f8d18755d15 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,3 +1,4 @@ obj-y += b2c2/ saa7146/ siano/ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o +obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o diff --git a/drivers/media/i2c/tveeprom.c b/drivers/media/common/tveeprom.c similarity index 100% rename from drivers/media/i2c/tveeprom.c rename to drivers/media/common/tveeprom.c diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index cd2b17148ad6..7b771baa2212 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -1,11 +1,3 @@ -# -# Generic video config states -# - -config VIDEO_TVEEPROM - tristate - depends on I2C - # # Multimedia Video device configuration # diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 3691e70f4e75..cfefd30cc1bc 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -49,7 +49,6 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o -obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o From cc318180c8ec87ce692d1fbb22923f9e05871197 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 8 Feb 2013 16:17:10 -0200 Subject: [PATCH 2183/4607] [media] tveeprom: Fix lots of bad whitespace While running checkpatch.pl after the last patch, I noticed lots of those: WARNING: please, no space before tabs #151: FILE: drivers/media/common/tveeprom.c:99: +^I{ TUNER_ABSENT, ^I^I"None" },$ (together with other checkpatch.pl errors/warnings) While I won't be fixing everything, as I have already an script to fix the above, let's do it, in order to clean it a little bit. While here, also drop cmacs-specific format text at the end. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tveeprom.c | 290 ++++++++++++++++---------------- 1 file changed, 142 insertions(+), 148 deletions(-) diff --git a/drivers/media/common/tveeprom.c b/drivers/media/common/tveeprom.c index 3b6cf034976a..cc1e172dfece 100644 --- a/drivers/media/common/tveeprom.c +++ b/drivers/media/common/tveeprom.c @@ -96,170 +96,170 @@ static struct HAUPPAUGE_TUNER hauppauge_tuner[] = { /* 0-9 */ - { TUNER_ABSENT, "None" }, - { TUNER_ABSENT, "External" }, - { TUNER_ABSENT, "Unspecified" }, - { TUNER_PHILIPS_PAL, "Philips FI1216" }, - { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FI1236" }, - { TUNER_PHILIPS_PAL_I, "Philips FI1246" }, + { TUNER_ABSENT, "None" }, + { TUNER_ABSENT, "External" }, + { TUNER_ABSENT, "Unspecified" }, + { TUNER_PHILIPS_PAL, "Philips FI1216" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, + { TUNER_PHILIPS_NTSC, "Philips FI1236" }, + { TUNER_PHILIPS_PAL_I, "Philips FI1246" }, { TUNER_PHILIPS_PAL_DK, "Philips FI1256" }, - { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, + { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, /* 10-19 */ - { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, + { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, { TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2" }, - { TUNER_TEMIC_NTSC, "Temic 4032FY5" }, - { TUNER_TEMIC_PAL, "Temic 4002FH5" }, - { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, - { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, - { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, + { TUNER_TEMIC_NTSC, "Temic 4032FY5" }, + { TUNER_TEMIC_PAL, "Temic 4002FH5" }, + { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, + { TUNER_PHILIPS_PAL, "Philips FR1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, + { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, /* 20-29 */ { TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2" }, - { TUNER_PHILIPS_PAL, "Philips FM1216" }, - { TUNER_PHILIPS_SECAM, "Philips FM1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FM1236" }, - { TUNER_PHILIPS_PAL_I, "Philips FM1246" }, + { TUNER_PHILIPS_PAL, "Philips FM1216" }, + { TUNER_PHILIPS_SECAM, "Philips FM1216MF" }, + { TUNER_PHILIPS_NTSC, "Philips FM1236" }, + { TUNER_PHILIPS_PAL_I, "Philips FM1246" }, { TUNER_PHILIPS_PAL_DK, "Philips FM1256" }, - { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" }, - { TUNER_ABSENT, "Samsung TCPN9082D" }, - { TUNER_ABSENT, "Samsung TCPM9092P" }, - { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" }, + { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" }, + { TUNER_ABSENT, "Samsung TCPN9082D" }, + { TUNER_ABSENT, "Samsung TCPM9092P" }, + { TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5" }, /* 30-39 */ - { TUNER_ABSENT, "Samsung TCPN9085D" }, - { TUNER_ABSENT, "Samsung TCPB9085P" }, - { TUNER_ABSENT, "Samsung TCPL9091P" }, - { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" }, - { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" }, - { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" }, - { TUNER_PHILIPS_NTSC, "Philips TD1536" }, - { TUNER_PHILIPS_NTSC, "Philips TD1536D" }, - { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */ - { TUNER_ABSENT, "Philips FI1256MP" }, + { TUNER_ABSENT, "Samsung TCPN9085D" }, + { TUNER_ABSENT, "Samsung TCPB9085P" }, + { TUNER_ABSENT, "Samsung TCPL9091P" }, + { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" }, + { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" }, + { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536D" }, + { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */ + { TUNER_ABSENT, "Philips FI1256MP" }, /* 40-49 */ - { TUNER_ABSENT, "Samsung TCPQ9091P" }, - { TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5" }, - { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" }, - { TUNER_TEMIC_4046FM5, "Temic 4046FM5" }, + { TUNER_ABSENT, "Samsung TCPQ9091P" }, + { TUNER_TEMIC_4006FN5_MULTI_PAL,"Temic 4006FN5" }, + { TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5" }, + { TUNER_TEMIC_4046FM5, "Temic 4046FM5" }, { TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5" }, - { TUNER_ABSENT, "Philips TD1536D FH 44"}, - { TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, - { TUNER_LG_PAL_FM, "LG TP18PSB01D"}, - { TUNER_LG_PAL, "LG TP18PSB11D"}, - { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, + { TUNER_ABSENT, "Philips TD1536D FH 44"}, + { TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, + { TUNER_LG_PAL_FM, "LG TP18PSB01D"}, + { TUNER_LG_PAL, "LG TP18PSB11D"}, + { TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, /* 50-59 */ - { TUNER_LG_PAL_I, "LG TAPC-I701D"}, - { TUNER_ABSENT, "Temic 4042FI5"}, - { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"}, - { TUNER_ABSENT, "LG TPI8NSR11F"}, - { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"}, - { TUNER_ABSENT, "Philips FI1236 MK3"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"}, - { TUNER_ABSENT, "Philips FM1216MP MK3"}, + { TUNER_LG_PAL_I, "LG TAPC-I701D"}, + { TUNER_ABSENT, "Temic 4042FI5"}, + { TUNER_MICROTUNE_4049FM5, "Microtune 4049 FM5"}, + { TUNER_ABSENT, "LG TPI8NSR11F"}, + { TUNER_ABSENT, "Microtune 4049 FM5 Alt I2C"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216ME MK3"}, + { TUNER_ABSENT, "Philips FI1236 MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216 ME MK3"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK3"}, + { TUNER_ABSENT, "Philips FM1216MP MK3"}, /* 60-69 */ - { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, - { TUNER_ABSENT, "LG M001D MK3"}, - { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, - { TUNER_ABSENT, "LG M701D MK3"}, - { TUNER_ABSENT, "Temic 4146FM5"}, - { TUNER_ABSENT, "Temic 4136FY5"}, - { TUNER_ABSENT, "Temic 4106FH5"}, - { TUNER_ABSENT, "Philips FQ1216LMP MK3"}, - { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"}, - { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, + { TUNER_ABSENT, "LG M001D MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, + { TUNER_ABSENT, "LG M701D MK3"}, + { TUNER_ABSENT, "Temic 4146FM5"}, + { TUNER_ABSENT, "Temic 4136FY5"}, + { TUNER_ABSENT, "Temic 4106FH5"}, + { TUNER_ABSENT, "Philips FQ1216LMP MK3"}, + { TUNER_LG_NTSC_TAPE, "LG TAPE H001F MK3"}, + { TUNER_LG_NTSC_TAPE, "LG TAPE H701F MK3"}, /* 70-79 */ - { TUNER_ABSENT, "LG TALN H200T"}, - { TUNER_ABSENT, "LG TALN H250T"}, - { TUNER_ABSENT, "LG TALN M200T"}, - { TUNER_ABSENT, "LG TALN Z200T"}, - { TUNER_ABSENT, "LG TALN S200T"}, - { TUNER_ABSENT, "Thompson DTT7595"}, - { TUNER_ABSENT, "Thompson DTT7592"}, - { TUNER_ABSENT, "Silicon TDA8275C1 8290"}, - { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, - { TUNER_ABSENT, "Thompson DTT757"}, + { TUNER_ABSENT, "LG TALN H200T"}, + { TUNER_ABSENT, "LG TALN H250T"}, + { TUNER_ABSENT, "LG TALN M200T"}, + { TUNER_ABSENT, "LG TALN Z200T"}, + { TUNER_ABSENT, "LG TALN S200T"}, + { TUNER_ABSENT, "Thompson DTT7595"}, + { TUNER_ABSENT, "Thompson DTT7592"}, + { TUNER_ABSENT, "Silicon TDA8275C1 8290"}, + { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, + { TUNER_ABSENT, "Thompson DTT757"}, /* 80-89 */ - { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"}, - { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, - { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, - { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, - { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"}, - { TUNER_TCL_2002N, "TCL 2002N 6A"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"}, - { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"}, - { TUNER_ABSENT, "Samsung TCPE 4121P30A"}, - { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"}, + { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"}, + { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, + { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, + { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, + { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"}, + { TUNER_TCL_2002N, "TCL 2002N 6A"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"}, + { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"}, + { TUNER_ABSENT, "Samsung TCPE 4121P30A"}, + { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"}, /* 90-99 */ - { TUNER_ABSENT, "LG TALN H202T"}, - { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"}, - { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"}, - { TUNER_ABSENT, "Philips FQ1286A MK4"}, - { TUNER_ABSENT, "Philips FQ1216ME MK5"}, - { TUNER_ABSENT, "Philips FQ1236 MK5"}, - { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, - { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, - { TUNER_ABSENT, "TCL 2002MI_3H"}, - { TUNER_TCL_2002N, "TCL 2002N 5H"}, + { TUNER_ABSENT, "LG TALN H202T"}, + { TUNER_PHILIPS_FQ1216AME_MK4, "Philips FQ1216AME MK4"}, + { TUNER_PHILIPS_FQ1236A_MK4, "Philips FQ1236A MK4"}, + { TUNER_ABSENT, "Philips FQ1286A MK4"}, + { TUNER_ABSENT, "Philips FQ1216ME MK5"}, + { TUNER_ABSENT, "Philips FQ1236 MK5"}, + { TUNER_SAMSUNG_TCPG_6121P30A, "Samsung TCPG 6121P30A"}, + { TUNER_TCL_2002MB, "TCL 2002MB_3H"}, + { TUNER_ABSENT, "TCL 2002MI_3H"}, + { TUNER_TCL_2002N, "TCL 2002N 5H"}, /* 100-109 */ - { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"}, - { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, - { TUNER_ABSENT, "Panasonic ENV57H12D5"}, - { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, + { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"}, + { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, + { TUNER_ABSENT, "Panasonic ENV57H12D5"}, + { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"}, - { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, - { TUNER_ABSENT, "TCL MQNM05-4"}, - { TUNER_ABSENT, "LG TAPC-W701D"}, - { TUNER_ABSENT, "TCL 9886P-WM"}, - { TUNER_ABSENT, "TCL 1676NM-WM"}, + { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, + { TUNER_ABSENT, "TCL MQNM05-4"}, + { TUNER_ABSENT, "LG TAPC-W701D"}, + { TUNER_ABSENT, "TCL 9886P-WM"}, + { TUNER_ABSENT, "TCL 1676NM-WM"}, /* 110-119 */ - { TUNER_ABSENT, "Thompson DTT75105"}, - { TUNER_ABSENT, "Conexant_CX24109"}, - { TUNER_TCL_2002N, "TCL M2523_5N_E"}, - { TUNER_TCL_2002MB, "TCL M2523_3DB_E"}, - { TUNER_ABSENT, "Philips 8275A"}, - { TUNER_ABSENT, "Microtune MT2060"}, - { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"}, - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"}, - { TUNER_ABSENT, "TCL M2523_3DI_E"}, - { TUNER_ABSENT, "Samsung THPD5222FG30A"}, + { TUNER_ABSENT, "Thompson DTT75105"}, + { TUNER_ABSENT, "Conexant_CX24109"}, + { TUNER_TCL_2002N, "TCL M2523_5N_E"}, + { TUNER_TCL_2002MB, "TCL M2523_3DB_E"}, + { TUNER_ABSENT, "Philips 8275A"}, + { TUNER_ABSENT, "Microtune MT2060"}, + { TUNER_PHILIPS_FM1236_MK3, "Philips FM1236 MK5"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FM1216ME MK5"}, + { TUNER_ABSENT, "TCL M2523_3DI_E"}, + { TUNER_ABSENT, "Samsung THPD5222FG30A"}, /* 120-129 */ - { TUNER_XC2028, "Xceive XC3028"}, + { TUNER_XC2028, "Xceive XC3028"}, { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"}, - { TUNER_ABSENT, "Philips FQD1216LME"}, - { TUNER_ABSENT, "Conexant CX24118A"}, - { TUNER_ABSENT, "TCL DMF11WIP"}, - { TUNER_ABSENT, "TCL MFNM05_4H_E"}, - { TUNER_ABSENT, "TCL MNM05_4H_E"}, - { TUNER_ABSENT, "TCL MPE05_2H_E"}, - { TUNER_ABSENT, "TCL MQNM05_4_U"}, - { TUNER_ABSENT, "TCL M2523_5NH_E"}, + { TUNER_ABSENT, "Philips FQD1216LME"}, + { TUNER_ABSENT, "Conexant CX24118A"}, + { TUNER_ABSENT, "TCL DMF11WIP"}, + { TUNER_ABSENT, "TCL MFNM05_4H_E"}, + { TUNER_ABSENT, "TCL MNM05_4H_E"}, + { TUNER_ABSENT, "TCL MPE05_2H_E"}, + { TUNER_ABSENT, "TCL MQNM05_4_U"}, + { TUNER_ABSENT, "TCL M2523_5NH_E"}, /* 130-139 */ - { TUNER_ABSENT, "TCL M2523_3DBH_E"}, - { TUNER_ABSENT, "TCL M2523_3DIH_E"}, - { TUNER_ABSENT, "TCL MFPE05_2_U"}, + { TUNER_ABSENT, "TCL M2523_3DBH_E"}, + { TUNER_ABSENT, "TCL M2523_3DIH_E"}, + { TUNER_ABSENT, "TCL MFPE05_2_U"}, { TUNER_PHILIPS_FMD1216MEX_MK3, "Philips FMD1216MEX"}, - { TUNER_ABSENT, "Philips FRH2036B"}, - { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, - { TUNER_ABSENT, "MaxLinear MXL5005"}, - { TUNER_ABSENT, "MaxLinear MXL5003"}, - { TUNER_ABSENT, "Xceive XC2028"}, - { TUNER_ABSENT, "Microtune MT2131"}, + { TUNER_ABSENT, "Philips FRH2036B"}, + { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, + { TUNER_ABSENT, "MaxLinear MXL5005"}, + { TUNER_ABSENT, "MaxLinear MXL5003"}, + { TUNER_ABSENT, "Xceive XC2028"}, + { TUNER_ABSENT, "Microtune MT2131"}, /* 140-149 */ - { TUNER_ABSENT, "Philips 8275A_8295"}, - { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, - { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, - { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, - { TUNER_ABSENT, "Microtune MT2266"}, - { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, - { TUNER_ABSENT, "LG TAPQ_H702F"}, - { TUNER_ABSENT, "TCL M09WPP_4N_E"}, - { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, - { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, + { TUNER_ABSENT, "Philips 8275A_8295"}, + { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, + { TUNER_ABSENT, "Microtune MT2266"}, + { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, + { TUNER_ABSENT, "LG TAPQ_H702F"}, + { TUNER_ABSENT, "TCL M09WPP_4N_E"}, + { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, + { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, /* 150-159 */ { TUNER_XC5000, "Xceive XC5000"}, { TUNER_ABSENT, "Xceive XC3028L"}, @@ -784,9 +784,3 @@ int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len) return 0; } EXPORT_SYMBOL(tveeprom_read); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ From d0758237eeadde80ca36856345016bbc05b0e2b4 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Fri, 8 Feb 2013 08:35:14 -0700 Subject: [PATCH 2184/4607] ARM: OMAP4: clock data: Add missing clkdm association for dpll_usb dpll_usb needs the clkdm association so the clkdm can be turned on before a relock. All other dplls for omap4 belong to the ALWON (always on) domain. The association was present as part of the older data file (clock44xx_data.c) but looks like got accidently dropped with the common clk convertion. More details of the patch which fixed this up in the older data file can be dound here.. http://www.spinics.net/lists/linux-omap/msg63076.html Adding the .clkdm_name as part of the clk_hw_omap struct also means a new .init needs to be part of the clk_ops for dpll_usb to initialise the clkdm. Signed-off-by: Rajendra Nayak Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/cclock44xx_data.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/cclock44xx_data.c b/arch/arm/mach-omap2/cclock44xx_data.c index a2cc046b47f4..e71a19ce3048 100644 --- a/arch/arm/mach-omap2/cclock44xx_data.c +++ b/arch/arm/mach-omap2/cclock44xx_data.c @@ -595,15 +595,26 @@ static const char *dpll_usb_ck_parents[] = { static struct clk dpll_usb_ck; +static const struct clk_ops dpll_usb_ck_ops = { + .enable = &omap3_noncore_dpll_enable, + .disable = &omap3_noncore_dpll_disable, + .recalc_rate = &omap3_dpll_recalc, + .round_rate = &omap2_dpll_round_rate, + .set_rate = &omap3_noncore_dpll_set_rate, + .get_parent = &omap2_init_dpll_parent, + .init = &omap2_init_clk_clkdm, +}; + static struct clk_hw_omap dpll_usb_ck_hw = { .hw = { .clk = &dpll_usb_ck, }, .dpll_data = &dpll_usb_dd, + .clkdm_name = "l3_init_clkdm", .ops = &clkhwops_omap3_dpll, }; -DEFINE_STRUCT_CLK(dpll_usb_ck, dpll_usb_ck_parents, dpll_ck_ops); +DEFINE_STRUCT_CLK(dpll_usb_ck, dpll_usb_ck_parents, dpll_usb_ck_ops); static const char *dpll_usb_clkdcoldo_ck_parents[] = { "dpll_usb_ck", From c2db409cbc8751ccc7e6d2cc2e41af0d12ea637f Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Wed, 30 Jan 2013 15:25:32 +0000 Subject: [PATCH 2185/4607] i2c: i801: SMBus patch for Intel Avoton DeviceIDs This patch adds the PCU SMBus DeviceID for the Intel Avoton SOC. Signed-off-by: Seth Heasley Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-i801 | 1 + drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-i801.c | 3 +++ 3 files changed, 5 insertions(+) diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index 157416e78cc4..8d71d5705b2a 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -22,6 +22,7 @@ Supported adapters: * Intel Panther Point (PCH) * Intel Lynx Point (PCH) * Intel Lynx Point-LP (PCH) + * Intel Avoton (SOC) Datasheets: Publicly available at the Intel website On Intel Patsburg and later chipsets, both the normal host SMBus controller diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bdca5111eb9d..77d28873b76f 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -106,6 +106,7 @@ config I2C_I801 Panther Point (PCH) Lynx Point (PCH) Lynx Point-LP (PCH) + Avoton (SOC) This driver can also be built as a module. If so, the module will be called i2c-i801. diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 3092387f6ef4..b00c29d8a5f1 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -53,6 +53,7 @@ Panther Point (PCH) 0x1e22 32 hard yes yes yes Lynx Point (PCH) 0x8c22 32 hard yes yes yes Lynx Point-LP (PCH) 0x9c22 32 hard yes yes yes + Avoton (SOC) 0x1f3c 32 hard yes yes yes Features supported by this driver: Software PEC no @@ -162,6 +163,7 @@ #define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71 #define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72 #define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22 +#define PCI_DEVICE_ID_INTEL_AVOTON_SMBUS 0x1f3c #define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 @@ -798,6 +800,7 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMBUS) }, { 0, } }; From e6066dba444903344c843dbdcb77a4823c51e7e3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 6 Feb 2013 08:14:47 -0300 Subject: [PATCH 2186/4607] [media] [REVIEW] em28xx: fix bytesperline calculation in TRY_FMT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bytesperline calculation was incorrect: it used the old width instead of the provided width. Fixed. Signed-off-by: Hans Verkuil Acked-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 2eabf2a94752..32bd7de5dec1 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -906,7 +906,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = width; f->fmt.pix.height = height; f->fmt.pix.pixelformat = fmt->fourcc; - f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; + f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; if (dev->progressive) From 5853d1acdeb3e40daaac6c465d92fc069272cb6a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 7 Feb 2013 02:55:54 -0300 Subject: [PATCH 2187/4607] [media] s5p-tv: Include missing irqreturn.h header Without this patch we get the following compilation errors: drivers/media/platform/s5p-tv/mixer.h:345:13: error: Expected ; at end of declaration drivers/media/platform/s5p-tv/mixer.h:345:13: error: got mxr_irq_handler Signed-off-by: Sachin Kamat Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/s5p-tv/mixer.h b/drivers/media/platform/s5p-tv/mixer.h index b671e20e9318..04e6490a45be 100644 --- a/drivers/media/platform/s5p-tv/mixer.h +++ b/drivers/media/platform/s5p-tv/mixer.h @@ -19,6 +19,7 @@ #endif #include +#include #include #include #include From 3e58ac14ad2c443d86c5bed0137a010fe4d16fe2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 7 Feb 2013 02:55:55 -0300 Subject: [PATCH 2188/4607] [media] s5p-tv: Include missing platform_device.h header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this patch we get the following build error: drivers/media/platform/s5p-tv/mixer_video.c: In function ‘find_and_register_subdev’: drivers/media/platform/s5p-tv/mixer_video.c:42:34: error: ‘platform_bus_type’ undeclared (first use in this function) Signed-off-by: Sachin Kamat Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/s5p-tv/mixer_video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index c087b66099fd..82142a2d6d93 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include From bf5bbed15c41228ea1abbb8d3931050922bfc37f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 7 Feb 2013 04:24:49 -0300 Subject: [PATCH 2189/4607] [media] dvb-usb: check for invalid length in ttusb_process_muxpack() This patch is driven by a static checker warning. The ttusb_process_muxpack() function is only called from ttusb_process_frame(). Before calling, it verifies that len >= 2. The problem is that len == 2 is not valid and would lead to an array underflow. Odd number values for len are also invalid and would lead to reading past the end of the array. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c index 5b682cc4c814..e40718552850 100644 --- a/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c @@ -561,6 +561,13 @@ static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack, { u16 csum = 0, cc; int i; + + if (len < 4 || len & 0x1) { + pr_warn("%s: muxpack has invalid len %d\n", __func__, len); + numinvalid++; + return; + } + for (i = 0; i < len; i += 2) csum ^= le16_to_cpup((__le16 *) (muxpack + i)); if (csum) { From 82f0efbcd3c4e6bf7cdfeed5c901b812e6d30f92 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 7 Feb 2013 07:05:43 -0300 Subject: [PATCH 2190/4607] [media] tm6000: fix an uninitialized variable tm6000_poll could use an uninitialized buf pointer. Move the buf-handling code inside the 'if' that initializes the buf pointer. Signed-off-by: Hans Verkuil Reported-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/tm6000/tm6000-video.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index eab23413a909..1a6857929c15 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -1455,14 +1455,14 @@ __tm6000_poll(struct file *file, struct poll_table_struct *wait) if (list_empty(&fh->vb_vidq.stream)) return res | POLLERR; buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream); + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return res | POLLIN | POLLRDNORM; } else if (req_events & (POLLIN | POLLRDNORM)) { /* read() capture */ return res | videobuf_poll_stream(file, &fh->vb_vidq, wait); } - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == VIDEOBUF_DONE || - buf->vb.state == VIDEOBUF_ERROR) - return res | POLLIN | POLLRDNORM; return res; } From 0917a60430cdd4b5d3505c240a04bb0f5927c74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20B=C3=BCsch?= Date: Thu, 7 Feb 2013 12:13:13 -0300 Subject: [PATCH 2191/4607] [media] fc0011: fp/fa value overflow fix Assign the maximum instead of masking with the maximum on value overflow. Signed-off-by: Michael Buesch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0011.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index e4882546c283..3089f2ebcbfe 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -247,8 +247,8 @@ static int fc0011_set_params(struct dvb_frontend *fe) fa += 8; } if (fp > 0x1F) { - fp &= 0x1F; - fa &= 0xF; + fp = 0x1F; + fa = 0xF; } if (fa >= fp) { dev_warn(&priv->i2c->dev, From db5c05b2a1c02e401778de348451bae49b65806e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 8 Feb 2013 17:47:01 -0200 Subject: [PATCH 2192/4607] Revert "[media] [PATH,1/2] mxl5007 move reset to attach" This patch was applied by mistake. Michael thinks that it it needs more work than simply moving the soft reset. So, it should be held back for further review. This reverts commit 0a3237704dec476be3cdfbe8fc9df9cc65b14442. Requested by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/mxl5007t.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c index eb61304c260d..69e453ef0a1a 100644 --- a/drivers/media/tuners/mxl5007t.c +++ b/drivers/media/tuners/mxl5007t.c @@ -531,6 +531,10 @@ static int mxl5007t_tuner_init(struct mxl5007t_state *state, struct reg_pair_t *init_regs; int ret; + ret = mxl5007t_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + /* calculate initialization reg array */ init_regs = mxl5007t_calc_init_regs(state, mode); @@ -896,20 +900,7 @@ struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, /* existing tuner instance */ break; } - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - ret = mxl5007t_soft_reset(state); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - - if (mxl_fail(ret)) - goto fail; - fe->tuner_priv = state; - mutex_unlock(&mxl5007t_list_mutex); memcpy(&fe->ops.tuner_ops, &mxl5007t_tuner_ops, From 03a497d4b4c5205f6a365c4673e21298e681a8a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20B=C3=BCsch?= Date: Thu, 7 Feb 2013 12:16:55 -0300 Subject: [PATCH 2193/4607] [media] fc0011: Fix xin value clamping Fix the xin value clamping and use clamp_t(). Signed-off-by: Michael Buesch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0011.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index 3089f2ebcbfe..f87aa5a8b8ea 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -183,8 +183,7 @@ static int fc0011_set_params(struct dvb_frontend *fe) unsigned int i, vco_retries; u32 freq = p->frequency / 1000; u32 bandwidth = p->bandwidth_hz / 1000; - u32 fvco, xin, xdiv, xdivr; - u16 frac; + u32 fvco, xin, frac, xdiv, xdivr; u8 fa, fp, vco_sel, vco_cal; u8 regs[FC11_NR_REGS] = { }; @@ -227,12 +226,8 @@ static int fc0011_set_params(struct dvb_frontend *fe) frac += 32786; if (!frac) xin = 0; - else if (frac < 511) - xin = 512; - else if (frac < 65026) - xin = frac; else - xin = 65024; + xin = clamp_t(u32, frac, 512, 65024); regs[FC11_REG_XINHI] = xin >> 8; regs[FC11_REG_XINLO] = xin; From aadb4640109b76cc9d0e1d8ce6b7bc3258a89170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20B=C3=BCsch?= Date: Thu, 7 Feb 2013 12:19:30 -0300 Subject: [PATCH 2194/4607] [media] fc0011: Add some sanity checks and cleanups Add some sanity checks to the calculations and make the REG_16 register write consistent with the other ones. Signed-off-by: Michael Buesch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0011.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index f87aa5a8b8ea..3932aa81e18c 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -220,6 +220,7 @@ static int fc0011_set_params(struct dvb_frontend *fe) /* Calc XIN. The PLL reference frequency is 18 MHz. */ xdiv = fvco / 18000; + WARN_ON(xdiv > 0xFF); frac = fvco - xdiv * 18000; frac = (frac << 15) / 18000; if (frac >= 16384) @@ -346,6 +347,8 @@ static int fc0011_set_params(struct dvb_frontend *fe) vco_cal &= FC11_VCOCAL_VALUEMASK; switch (vco_sel) { + default: + WARN_ON(1); case 0: if (vco_cal < 8) { regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); @@ -427,7 +430,8 @@ static int fc0011_set_params(struct dvb_frontend *fe) err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); if (err) return err; - err = fc0011_writereg(priv, FC11_REG_16, 0xB); + regs[FC11_REG_16] = 0xB; + err = fc0011_writereg(priv, FC11_REG_16, regs[FC11_REG_16]); if (err) return err; From a92591a7112042f92b609be42bc332d989776e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20B=C3=BCsch?= Date: Thu, 7 Feb 2013 12:21:06 -0300 Subject: [PATCH 2195/4607] [media] fc0011: Return early, if the frequency is already tuned Return early, if we already tuned to a frequency. Signed-off-by: Michael Buesch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0011.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index 3932aa81e18c..18caab11c94b 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -187,6 +187,9 @@ static int fc0011_set_params(struct dvb_frontend *fe) u8 fa, fp, vco_sel, vco_cal; u8 regs[FC11_NR_REGS] = { }; + if (priv->frequency == p->frequency) + return 0; + regs[FC11_REG_7] = 0x0F; regs[FC11_REG_8] = 0x3E; regs[FC11_REG_10] = 0xB8; From 4880f56438ef56457edd5548b257382761591998 Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Sun, 3 Feb 2013 22:34:48 -0300 Subject: [PATCH 2196/4607] [media] stv0900: remove unnecessary null pointer check The address of a variable is impossible to be null, so we remove the check. Signed-off-by: Cong Ding Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/stv0900_core.c | 14 ++++---------- drivers/media/dvb-frontends/stv0900_sw.c | 7 ++----- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index 0fb34e1909d1..e5a87b57d855 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -524,11 +524,8 @@ void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency, struct dvb_frontend_ops *frontend_ops = NULL; struct dvb_tuner_ops *tuner_ops = NULL; - if (&fe->ops) - frontend_ops = &fe->ops; - - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; + frontend_ops = &fe->ops; + tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->set_frequency) { if ((tuner_ops->set_frequency(fe, frequency)) < 0) @@ -552,11 +549,8 @@ void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) struct dvb_frontend_ops *frontend_ops = NULL; struct dvb_tuner_ops *tuner_ops = NULL; - if (&fe->ops) - frontend_ops = &fe->ops; - - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; + frontend_ops = &fe->ops; + tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->set_bandwidth) { if ((tuner_ops->set_bandwidth(fe, bandwidth)) < 0) diff --git a/drivers/media/dvb-frontends/stv0900_sw.c b/drivers/media/dvb-frontends/stv0900_sw.c index 4af20780fb9c..0a40edfad739 100644 --- a/drivers/media/dvb-frontends/stv0900_sw.c +++ b/drivers/media/dvb-frontends/stv0900_sw.c @@ -1167,11 +1167,8 @@ static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe) struct dvb_tuner_ops *tuner_ops = NULL; u32 freq = 0; - if (&fe->ops) - frontend_ops = &fe->ops; - - if (&frontend_ops->tuner_ops) - tuner_ops = &frontend_ops->tuner_ops; + frontend_ops = &fe->ops; + tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->get_frequency) { if ((tuner_ops->get_frequency(fe, &freq)) < 0) From 4c190e2f913f038c9c91ee63b59cd037260ba353 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 6 Feb 2013 08:28:55 -0500 Subject: [PATCH 2197/4607] sunrpc: trim off trailing checksum before returning decrypted or integrity authenticated buffer When GSSAPI integrity signatures are in use, or when we're using GSSAPI privacy with the v2 token format, there is a trailing checksum on the xdr_buf that is returned. It's checked during the authentication stage, and afterward nothing cares about it. Ordinarily, it's not a problem since the XDR code generally ignores it, but it will be when we try to compute a checksum over the buffer to help prevent XID collisions in the duplicate reply cache. Fix the code to trim off the checksums after verifying them. Note that in unwrap_integ_data, we must avoid trying to reverify the checksum if the request was deferred since it will no longer be present when it's revisited. Signed-off-by: Jeff Layton --- include/linux/sunrpc/xdr.h | 1 + net/sunrpc/auth_gss/gss_krb5_wrap.c | 2 ++ net/sunrpc/auth_gss/svcauth_gss.c | 10 +++++-- net/sunrpc/xdr.c | 41 +++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 224d06047e45..15f9204ee70b 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -152,6 +152,7 @@ xdr_adjust_iovec(struct kvec *iov, __be32 *p) extern void xdr_shift_buf(struct xdr_buf *, size_t); extern void xdr_buf_from_iov(struct kvec *, struct xdr_buf *); extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, unsigned int, unsigned int); +extern void xdr_buf_trim(struct xdr_buf *, unsigned int); extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, unsigned int); extern int read_bytes_from_xdr_buf(struct xdr_buf *, unsigned int, void *, unsigned int); extern int write_bytes_to_xdr_buf(struct xdr_buf *, unsigned int, void *, unsigned int); diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c index 107c4528654f..88edec929d73 100644 --- a/net/sunrpc/auth_gss/gss_krb5_wrap.c +++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c @@ -574,6 +574,8 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf) buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip; buf->len -= GSS_KRB5_TOK_HDR_LEN + headskip; + /* Trim off the checksum blob */ + xdr_buf_trim(buf, GSS_KRB5_TOK_HDR_LEN + tailskip); return GSS_S_COMPLETE; } diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 73e957386600..a5b41e2ac25a 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -817,13 +817,17 @@ read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj) * The server uses base of head iovec as read pointer, while the * client uses separate pointer. */ static int -unwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) +unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) { int stat = -EINVAL; u32 integ_len, maj_stat; struct xdr_netobj mic; struct xdr_buf integ_buf; + /* Did we already verify the signature on the original pass through? */ + if (rqstp->rq_deferred) + return 0; + integ_len = svc_getnl(&buf->head[0]); if (integ_len & 3) return stat; @@ -846,6 +850,8 @@ unwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) goto out; if (svc_getnl(&buf->head[0]) != seq) goto out; + /* trim off the mic at the end before returning */ + xdr_buf_trim(buf, mic.len + 4); stat = 0; out: kfree(mic.data); @@ -1190,7 +1196,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) /* placeholders for length and seq. number: */ svc_putnl(resv, 0); svc_putnl(resv, 0); - if (unwrap_integ_data(&rqstp->rq_arg, + if (unwrap_integ_data(rqstp, &rqstp->rq_arg, gc->gc_seq, rsci->mechctx)) goto garbage_args; break; diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 56055632f151..75edcfad6e26 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -879,6 +879,47 @@ xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, } EXPORT_SYMBOL_GPL(xdr_buf_subsegment); +/** + * xdr_buf_trim - lop at most "len" bytes off the end of "buf" + * @buf: buf to be trimmed + * @len: number of bytes to reduce "buf" by + * + * Trim an xdr_buf by the given number of bytes by fixing up the lengths. Note + * that it's possible that we'll trim less than that amount if the xdr_buf is + * too small, or if (for instance) it's all in the head and the parser has + * already read too far into it. + */ +void xdr_buf_trim(struct xdr_buf *buf, unsigned int len) +{ + size_t cur; + unsigned int trim = len; + + if (buf->tail[0].iov_len) { + cur = min_t(size_t, buf->tail[0].iov_len, trim); + buf->tail[0].iov_len -= cur; + trim -= cur; + if (!trim) + goto fix_len; + } + + if (buf->page_len) { + cur = min_t(unsigned int, buf->page_len, trim); + buf->page_len -= cur; + trim -= cur; + if (!trim) + goto fix_len; + } + + if (buf->head[0].iov_len) { + cur = min_t(size_t, buf->head[0].iov_len, trim); + buf->head[0].iov_len -= cur; + trim -= cur; + } +fix_len: + buf->len -= (len - trim); +} +EXPORT_SYMBOL_GPL(xdr_buf_trim); + static void __read_bytes_from_xdr_buf(struct xdr_buf *subbuf, void *obj, unsigned int len) { unsigned int this_len; From 01a7decf75930925322c5efc87af0b5e58eb8650 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 4 Feb 2013 11:57:27 -0500 Subject: [PATCH 2198/4607] nfsd: keep a checksum of the first 256 bytes of request Now that we're allowing more DRC entries, it becomes a lot easier to hit problems with XID collisions. In order to mitigate those, calculate a checksum of up to the first 256 bytes of each request coming in and store that in the cache entry, along with the total length of the request. This initially used crc32, but Chuck Lever and Jim Rees pointed out that crc32 is probably more heavyweight than we really need for generating these checksums, and recommended looking at using the same routines that are used to generate checksums for IP packets. On an x86_64 KVM guest measurements with ftrace showed ~800ns to use csum_partial vs ~1750ns for crc32. The difference probably isn't terribly significant, but for now we may as well use csum_partial. Signed-off-by: Jeff Layton Stones-thrown-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/nfsd/cache.h | 5 +++++ fs/nfsd/nfscache.c | 47 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/fs/nfsd/cache.h b/fs/nfsd/cache.h index 9c7232b45103..87fd1410b737 100644 --- a/fs/nfsd/cache.h +++ b/fs/nfsd/cache.h @@ -29,6 +29,8 @@ struct svc_cacherep { u32 c_prot; u32 c_proc; u32 c_vers; + unsigned int c_len; + __wsum c_csum; unsigned long c_timestamp; union { struct kvec u_vec; @@ -73,6 +75,9 @@ enum { /* Cache entries expire after this time period */ #define RC_EXPIRE (120 * HZ) +/* Checksum this amount of the request */ +#define RC_CSUMLEN (256U) + int nfsd_reply_cache_init(void); void nfsd_reply_cache_shutdown(void); int nfsd_cache_lookup(struct svc_rqst *); diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c index f7544698e6e6..40db57eb2b06 100644 --- a/fs/nfsd/nfscache.c +++ b/fs/nfsd/nfscache.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "nfsd.h" #include "cache.h" @@ -130,6 +131,7 @@ int nfsd_reply_cache_init(void) INIT_LIST_HEAD(&lru_head); max_drc_entries = nfsd_cache_size_limit(); num_drc_entries = 0; + return 0; out_nomem: printk(KERN_ERR "nfsd: failed to allocate reply cache\n"); @@ -237,13 +239,46 @@ nfsd_reply_cache_shrink(struct shrinker *shrink, struct shrink_control *sc) return num; } +/* + * Walk an xdr_buf and get a CRC for at most the first RC_CSUMLEN bytes + */ +static __wsum +nfsd_cache_csum(struct svc_rqst *rqstp) +{ + int idx; + unsigned int base; + __wsum csum; + struct xdr_buf *buf = &rqstp->rq_arg; + const unsigned char *p = buf->head[0].iov_base; + size_t csum_len = min_t(size_t, buf->head[0].iov_len + buf->page_len, + RC_CSUMLEN); + size_t len = min(buf->head[0].iov_len, csum_len); + + /* rq_arg.head first */ + csum = csum_partial(p, len, 0); + csum_len -= len; + + /* Continue into page array */ + idx = buf->page_base / PAGE_SIZE; + base = buf->page_base & ~PAGE_MASK; + while (csum_len) { + p = page_address(buf->pages[idx]) + base; + len = min(PAGE_SIZE - base, csum_len); + csum = csum_partial(p, len, csum); + csum_len -= len; + base = 0; + ++idx; + } + return csum; +} + /* * Search the request hash for an entry that matches the given rqstp. * Must be called with cache_lock held. Returns the found entry or * NULL on failure. */ static struct svc_cacherep * -nfsd_cache_search(struct svc_rqst *rqstp) +nfsd_cache_search(struct svc_rqst *rqstp, __wsum csum) { struct svc_cacherep *rp; struct hlist_node *hn; @@ -257,6 +292,7 @@ nfsd_cache_search(struct svc_rqst *rqstp) hlist_for_each_entry(rp, hn, rh, c_hash) { if (xid == rp->c_xid && proc == rp->c_proc && proto == rp->c_prot && vers == rp->c_vers && + rqstp->rq_arg.len == rp->c_len && csum == rp->c_csum && rpc_cmp_addr(svc_addr(rqstp), (struct sockaddr *)&rp->c_addr) && rpc_get_port(svc_addr(rqstp)) == rpc_get_port((struct sockaddr *)&rp->c_addr)) return rp; @@ -277,6 +313,7 @@ nfsd_cache_lookup(struct svc_rqst *rqstp) u32 proto = rqstp->rq_prot, vers = rqstp->rq_vers, proc = rqstp->rq_proc; + __wsum csum; unsigned long age; int type = rqstp->rq_cachetype; int rtn; @@ -287,10 +324,12 @@ nfsd_cache_lookup(struct svc_rqst *rqstp) return RC_DOIT; } + csum = nfsd_cache_csum(rqstp); + spin_lock(&cache_lock); rtn = RC_DOIT; - rp = nfsd_cache_search(rqstp); + rp = nfsd_cache_search(rqstp, csum); if (rp) goto found_entry; @@ -318,7 +357,7 @@ nfsd_cache_lookup(struct svc_rqst *rqstp) * Must search again just in case someone inserted one * after we dropped the lock above. */ - found = nfsd_cache_search(rqstp); + found = nfsd_cache_search(rqstp, csum); if (found) { nfsd_reply_cache_free_locked(rp); rp = found; @@ -344,6 +383,8 @@ setup_entry: rpc_set_port((struct sockaddr *)&rp->c_addr, rpc_get_port(svc_addr(rqstp))); rp->c_prot = proto; rp->c_vers = vers; + rp->c_len = rqstp->rq_arg.len; + rp->c_csum = csum; hash_refile(rp); lru_put_end(rp); From 0b61650044e9044d3843047fe015fc0ea7943430 Mon Sep 17 00:00:00 2001 From: Roland Eggner Date: Fri, 1 Feb 2013 19:27:04 +0100 Subject: [PATCH 2199/4607] kconfig: nconf: rewrite help texts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit >From: Roland Eggner Rewrite all help texts. During several years lazy (incomplete) updates have left behind a rather thick layer of dust. Intentions: (1) Global help called by should document all _currently_ implemented keybindings. (2) Different help texts called by resp. should be consistent with (1) and with implementation: • on plain menu entry • in radiolist window • in input windows for text, decimal or hexadecimal values • in filename selection windows • SymSearch specific help called by followed by (3) More reasonable window titles: Rename window title s/README/Global help/ Rename variable s/nconf_readme/nconf_global_help/ Rename window title s/Instructions/Short help/ (4) Consider which hints are most useful for first-time-users. Signed-off-by: Roland Eggner Reviewed-by: "Yann E. MORIN" [yann.morin.1998@free.fr: a few additional fixes] Signed-off-by: "Yann E. MORIN" --- scripts/kconfig/nconf.c | 338 ++++++++++++++++++++-------------------- 1 file changed, 165 insertions(+), 173 deletions(-) diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index ce93e879a29c..65c672cce823 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -13,210 +13,202 @@ #include "nconf.h" #include -static const char nconf_readme[] = N_( -"Overview\n" -"--------\n" -"This interface let you select features and parameters for the build.\n" -"Features can either be built-in, modularized, or ignored. Parameters\n" -"must be entered in as decimal or hexadecimal numbers or text.\n" +static const char nconf_global_help[] = N_( +"Help windows\n" +"------------\n" +"o Global help: Unless in a data entry window, pressing will give \n" +" you the global help window, which you are just reading.\n" "\n" -"Menu items beginning with following braces represent features that\n" -" [ ] can be built in or removed\n" -" < > can be built in, modularized or removed\n" -" { } can be built in or modularized (selected by other feature)\n" -" - - are selected by other feature,\n" -" XXX cannot be selected. Use Symbol Info to find out why,\n" -"while *, M or whitespace inside braces means to build in, build as\n" -"a module or to exclude the feature respectively.\n" +"o A short version of the global help is available by pressing .\n" "\n" -"To change any of these features, highlight it with the cursor\n" -"keys and press to build it in, to make it a module or\n" -" to removed it. You may also press the to cycle\n" -"through the available options (ie. Y->N->M->Y).\n" +"o Local help: To get help related to the current menu entry, use any\n" +" of , or if in a data entry window then press .\n" "\n" -"Some additional keyboard hints:\n" "\n" -"Menus\n" +"Menu entries\n" +"------------\n" +"This interface lets you select features and parameters for the kernel\n" +"build. Kernel features can either be built-in, modularized, or removed.\n" +"Parameters must be entered as text or decimal or hexadecimal numbers.\n" +"\n" +"Menu entries beginning with following braces represent features that\n" +" [ ] can be built in or removed\n" +" < > can be built in, modularized or removed\n" +" { } can be built in or modularized, are selected by another feature\n" +" - - are selected by another feature\n" +" XXX cannot be selected. Symbol Info tells you why.\n" +"*, M or whitespace inside braces means to build in, build as a module\n" +"or to exclude the feature respectively.\n" +"\n" +"To change any of these features, highlight it with the movement keys\n" +"listed below and press to build it in, to make it a module or\n" +" to remove it. You may press the key to cycle through the\n" +"available options.\n" +"\n" +"A trailing \"--->\" designates a submenu.\n" +"\n" +"\n" +"Menu navigation keys\n" +"----------------------------------------------------------------------\n" +"Linewise up \n" +"Linewise down \n" +"Pagewise up \n" +"Pagewise down \n" +"First entry \n" +"Last entry \n" +"Enter a submenu \n" +"Go back to parent menu \n" +"Close a help window \n" +"Close entry window, apply \n" +"Close entry window, forget \n" +"Start incremental, case-insensitive search for STRING in menu entries,\n" +" no regex support, STRING is displayed in upper left corner\n" +" STRING\n" +" Remove last character \n" +" Jump to next hit \n" +" Jump to previous hit \n" +"Exit menu search mode \n" +"Search for configuration variables with or without leading CONFIG_\n" +" RegExpr\n" +"Verbose search help \n" +"----------------------------------------------------------------------\n" +"\n" +"Unless in a data entry window, key <1> may be used instead of ,\n" +"<2> instead of , etc.\n" +"\n" +"\n" +"Radiolist (Choice list)\n" +"-----------------------\n" +"Use the movement keys listed above to select the option you wish to set\n" +"and press .\n" +"\n" +"\n" +"Data entry\n" "----------\n" -"o Use the Up/Down arrow keys (cursor keys) to highlight the item\n" -" you wish to change use or . Goto submenu by \n" -" pressing of . Use or to go back.\n" -" Submenus are designated by \"--->\".\n" -"\n" -" Searching: pressing '/' triggers interactive search mode.\n" -" nconfig performs a case insensitive search for the string\n" -" in the menu prompts (no regex support).\n" -" Pressing the up/down keys highlights the previous/next\n" -" matching item. Backspace removes one character from the\n" -" match string. Pressing either '/' again or ESC exits\n" -" search mode. All other keys behave normally.\n" -"\n" -" You may also use the and keys to scroll\n" -" unseen options into view.\n" -"\n" -"o To exit a menu use the just press or .\n" -"\n" -"o To get help with an item, press \n" -" Shortcut: Press or .\n" +"Enter the requested information and press . Hexadecimal values\n" +"may be entered without the \"0x\" prefix.\n" "\n" "\n" -"Radiolists (Choice lists)\n" -"-----------\n" -"o Use the cursor keys to select the option you wish to set and press\n" -" or the .\n" +"Text Box (Help Window)\n" +"----------------------\n" +"Use movement keys as listed in table above.\n" "\n" -" Shortcut: Press the first letter of the option you wish to set then\n" -" press or .\n" -"\n" -"o To see available help for the item, press \n" -" Shortcut: Press or .\n" +"Press any of to exit.\n" "\n" "\n" -"Data Entry\n" -"-----------\n" -"o Enter the requested information and press \n" -" If you are entering hexadecimal values, it is not necessary to\n" -" add the '0x' prefix to the entry.\n" -"\n" -"o For help, press .\n" -"\n" -"\n" -"Text Box (Help Window)\n" -"--------\n" -"o Use the cursor keys to scroll up/down/left/right. The VI editor\n" -" keys h,j,k,l function here as do , and for\n" -" those who are familiar with less and lynx.\n" -"\n" -"o Press , , , , or to exit.\n" -"\n" -"\n" -"Alternate Configuration Files\n" +"Alternate configuration files\n" "-----------------------------\n" -"nconfig supports the use of alternate configuration files for\n" -"those who, for various reasons, find it necessary to switch\n" -"between different configurations.\n" +"nconfig supports switching between different configurations.\n" +"Press to save your current configuration. Press and enter\n" +"a file name to load a previously saved configuration.\n" "\n" -"At the end of the main menu you will find two options. One is\n" -"for saving the current configuration to a file of your choosing.\n" -"The other option is for loading a previously saved alternate\n" -"configuration.\n" "\n" -"Even if you don't use alternate configuration files, but you\n" -"find during a nconfig session that you have completely messed\n" -"up your settings, you may use the \"Load Alternate...\" option to\n" -"restore your previously saved settings from \".config\" without\n" -"restarting nconfig.\n" +"Terminal configuration\n" +"----------------------\n" +"If you use nconfig in a xterm window, make sure your TERM environment\n" +"variable specifies a terminal configuration which supports at least\n" +"16 colors. Otherwise nconfig will look rather bad.\n" "\n" -"Other information\n" -"-----------------\n" -"If you use nconfig in an XTERM window make sure you have your\n" -"$TERM variable set to point to a xterm definition which supports color.\n" -"Otherwise, nconfig will look rather bad. nconfig will not\n" -"display correctly in a RXVT window because rxvt displays only one\n" -"intensity of color, bright.\n" +"If the \"stty size\" command reports the current terminalsize correctly,\n" +"nconfig will adapt to sizes larger than the traditional 80x25 \"standard\"\n" +"and display longer menus properly.\n" "\n" -"nconfig will display larger menus on screens or xterms which are\n" -"set to display more than the standard 25 row by 80 column geometry.\n" -"In order for this to work, the \"stty size\" command must be able to\n" -"display the screen's current row and column geometry. I STRONGLY\n" -"RECOMMEND that you make sure you do NOT have the shell variables\n" -"LINES and COLUMNS exported into your environment. Some distributions\n" -"export those variables via /etc/profile. Some ncurses programs can\n" -"become confused when those variables (LINES & COLUMNS) don't reflect\n" -"the true screen size.\n" "\n" -"Optional personality available\n" -"------------------------------\n" -"If you prefer to have all of the options listed in a single menu, rather\n" -"than the default multimenu hierarchy, run the nconfig with NCONFIG_MODE\n" -"environment variable set to single_menu. Example:\n" +"Single menu mode\n" +"----------------\n" +"If you prefer to have all of the menu entries listed in a single menu,\n" +"rather than the default multimenu hierarchy, run nconfig with\n" +"NCONFIG_MODE environment variable set to single_menu. Example:\n" "\n" "make NCONFIG_MODE=single_menu nconfig\n" "\n" -" will then unroll the appropriate category, or enfold it if it\n" -"is already unrolled.\n" +" will then unfold the appropriate category, or fold it if it\n" +"is already unfolded. Folded menu entries will be designated by a\n" +"leading \"++>\" and unfolded entries by a leading \"-->\".\n" "\n" -"Note that this mode can eventually be a little more CPU expensive\n" -"(especially with a larger number of unrolled categories) than the\n" -"default mode.\n" +"Note that this mode can eventually be a little more CPU expensive than\n" +"the default mode, especially with a larger number of unfolded submenus.\n" "\n"), menu_no_f_instructions[] = N_( -" You do not have function keys support. Please follow the\n" -" following instructions:\n" -" Arrow keys navigate the menu.\n" -" or selects submenus --->.\n" -" Capital Letters are hotkeys.\n" -" Pressing includes, excludes, modularizes features.\n" -" Pressing SpaceBar toggles between the above options.\n" -" Press or to go back one menu,\n" -" or for Help, for Search.\n" -" <1> is interchangeable with , <2> with , etc.\n" -" Legend: [*] built-in [ ] excluded module < > module capable.\n" -" always leaves the current window.\n"), +"Legend: [*] built-in [ ] excluded module < > module capable.\n" +"Submenus are designated by a trailing \"--->\".\n" +"\n" +"Use the following keys to navigate the menus:\n" +"Move up or down with and .\n" +"Enter a submenu with or .\n" +"Exit a submenu to its parent menu with or .\n" +"Pressing includes, excludes, modularizes features.\n" +"Pressing cycles through the available options.\n" +"To search for menu entries press .\n" +" always leaves the current window.\n" +"\n" +"You do not have function keys support.\n" +"Press <1> instead of , <2> instead of , etc.\n" +"For verbose global help use key <1>.\n" +"For help related to the current menu entry press or .\n"), menu_instructions[] = N_( -" Arrow keys navigate the menu.\n" -" or selects submenus --->.\n" -" Capital Letters are hotkeys.\n" -" Pressing includes, excludes, modularizes features.\n" -" Pressing SpaceBar toggles between the above options\n" -" Press , or to go back one menu,\n" -" , or for Help, for Search.\n" -" <1> is interchangeable with , <2> with , etc.\n" -" Legend: [*] built-in [ ] excluded module < > module capable.\n" -" always leaves the current window\n"), +"Legend: [*] built-in [ ] excluded module < > module capable.\n" +"Submenus are designated by a trailing \"--->\".\n" +"\n" +"Use the following keys to navigate the menus:\n" +"Move up or down with or .\n" +"Enter a submenu with or .\n" +"Exit a submenu to its parent menu with or .\n" +"Pressing includes, excludes, modularizes features.\n" +"Pressing cycles through the available options.\n" +"To search for menu entries press .\n" +" always leaves the current window.\n" +"\n" +"Pressing <1> may be used instead of , <2> instead of , etc.\n" +"For verbose global help press .\n" +"For help related to the current menu entry press or .\n"), radiolist_instructions[] = N_( -" Use the arrow keys to navigate this window or\n" -" press the hotkey of the item you wish to select\n" -" followed by the .\n" -" Press , or for additional information about this option.\n"), +"Press , , or to navigate a radiolist, select\n" +"with .\n" +"For help related to the current entry press or .\n" +"For global help press .\n"), inputbox_instructions_int[] = N_( "Please enter a decimal value.\n" "Fractions will not be accepted.\n" -"Press to accept, to cancel."), +"Press to apply, to cancel."), inputbox_instructions_hex[] = N_( "Please enter a hexadecimal value.\n" -"Press to accept, to cancel."), +"Press to apply, to cancel."), inputbox_instructions_string[] = N_( "Please enter a string value.\n" -"Press to accept, to cancel."), +"Press to apply, to cancel."), setmod_text[] = N_( -"This feature depends on another which\n" -"has been configured as a module.\n" -"As a result, this feature will be built as a module."), +"This feature depends on another feature which has been configured as a\n" +"module. As a result, the current feature will be built as a module too."), load_config_text[] = N_( "Enter the name of the configuration file you wish to load.\n" -"Accept the name shown to restore the configuration you\n" -"last retrieved. Leave blank to abort."), +"Accept the name shown to restore the configuration you last\n" +"retrieved. Leave empty to abort."), load_config_help[] = N_( -"\n" "For various reasons, one may wish to keep several different\n" "configurations available on a single machine.\n" "\n" "If you have saved a previous configuration in a file other than the\n" -"default one, entering its name here will allow you to modify that\n" -"configuration.\n" +"default one, entering its name here will allow you to load and modify\n" +"that configuration.\n" "\n" -"If you are uncertain, then you have probably never used alternate\n" -"configuration files. You should therefor leave this blank to abort.\n"), +"Leave empty to abort.\n"), save_config_text[] = N_( "Enter a filename to which this configuration should be saved\n" -"as an alternate. Leave blank to abort."), +"as an alternate. Leave empty to abort."), save_config_help[] = N_( -"\n" -"For various reasons, one may wish to keep different configurations\n" -"available on a single machine.\n" +"For various reasons, one may wish to keep several different\n" +"configurations available on a single machine.\n" "\n" "Entering a file name here will allow you to later retrieve, modify\n" "and use the current configuration as an alternate to whatever\n" "configuration options you have selected at that time.\n" "\n" -"If you are uncertain what all this means then you should probably\n" -"leave this blank.\n"), +"Leave empty to abort.\n"), search_help[] = N_( -"\n" -"Search for symbols and display their relations. Regular expressions\n" -"are allowed.\n" -"Example: search for \"^FOO\"\n" +"Search for symbols (configuration variable names CONFIG_*) and display\n" +"their relations. Regular expressions are supported.\n" +"Example: Search for \"^FOO\".\n" "Result:\n" "-----------------------------------------------------------------\n" "Symbol: FOO [ = m]\n" @@ -230,26 +222,26 @@ search_help[] = N_( "Selects: LIBCRC32\n" "Selected by: BAR\n" "-----------------------------------------------------------------\n" -"o The line 'Prompt:' shows the text used in the menu structure for\n" -" this symbol\n" -"o The 'Defined at' line tell at what file / line number the symbol\n" -" is defined\n" -"o The 'Depends on:' line tell what symbols needs to be defined for\n" -" this symbol to be visible in the menu (selectable)\n" -"o The 'Location:' lines tell where in the menu structure this symbol\n" -" is located\n" -" A location followed by a [ = y] indicate that this is a selectable\n" -" menu item - and current value is displayed inside brackets.\n" -"o The 'Selects:' line tell what symbol will be automatically\n" -" selected if this symbol is selected (y or m)\n" -"o The 'Selected by' line tell what symbol has selected this symbol\n" +"o The line 'Prompt:' shows the text displayed for this symbol in\n" +" the menu hierarchy.\n" +"o The 'Defined at' line tells at what file / line number the symbol is\n" +" defined.\n" +"o The 'Depends on:' line lists symbols that need to be defined for\n" +" this symbol to be visible and selectable in the menu.\n" +"o The 'Location:' lines tell, where in the menu structure this symbol\n" +" is located. A location followed by a [ = y] indicates that this is\n" +" a selectable menu item, and the current value is displayed inside\n" +" brackets.\n" +"o The 'Selects:' line tells, what symbol will be automatically selected\n" +" if this symbol is selected (y or m).\n" +"o The 'Selected by' line tells what symbol has selected this symbol.\n" "\n" "Only relevant lines are shown.\n" "\n\n" "Search examples:\n" -"Examples: USB => find all symbols containing USB\n" -" ^USB => find all symbols starting with USB\n" -" USB$ => find all symbols ending with USB\n" +"USB => find all symbols containing USB\n" +"^USB => find all symbols starting with USB\n" +"USB$ => find all symbols ending with USB\n" "\n"); struct mitem { @@ -393,7 +385,7 @@ static void print_function_line(void) static void handle_f1(int *key, struct menu *current_item) { show_scroll_win(main_window, - _("README"), _(nconf_readme)); + _("Global help"), _(nconf_global_help)); return; } @@ -408,7 +400,7 @@ static void handle_f2(int *key, struct menu *current_item) static void handle_f3(int *key, struct menu *current_item) { show_scroll_win(main_window, - _("Instructions"), + _("Short help"), _(current_instructions)); return; } From 2c68115cb3d2863004f1dd5c01e3adbd54facd4c Mon Sep 17 00:00:00 2001 From: Roland Eggner Date: Fri, 1 Feb 2013 19:30:47 +0100 Subject: [PATCH 2200/4607] kconfig: nconf: rewrite labels of function keys line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit >From: Roland Eggner More reasonable labels of function keys line. Rename labels and keep menu width, as required for fitting on COLUMNS=80 terminals: • s/Insts/Help 2/ • s/Config/ShowAll/ Signed-off-by: Roland Eggner Reviewed-by: "Yann E. MORIN" Signed-off-by: "Yann E. MORIN" --- scripts/kconfig/nconf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index 65c672cce823..dbf31edd22b2 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -312,19 +312,19 @@ struct function_keys function_keys[] = { }, { .key_str = "F2", - .func = "Sym Info", + .func = "SymInfo", .key = F_SYMBOL, .handler = handle_f2, }, { .key_str = "F3", - .func = "Insts", + .func = "Help 2", .key = F_INSTS, .handler = handle_f3, }, { .key_str = "F4", - .func = "Config", + .func = "ShowAll", .key = F_CONF, .handler = handle_f4, }, @@ -348,7 +348,7 @@ struct function_keys function_keys[] = { }, { .key_str = "F8", - .func = "Sym Search", + .func = "SymSearch", .key = F_SEARCH, .handler = handle_f8, }, From 9924a92a8c217576bd2a2b1bbbb854462f1a00ae Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 8 Feb 2013 21:59:22 -0500 Subject: [PATCH 2201/4607] ext4: pass context information to jbd2__journal_start() So we can better understand what bits of ext4 are responsible for long-running jbd2 handles, use jbd2__journal_start() so we can pass context information for logging purposes. The recommended way for finding the longer-running handles is: T=/sys/kernel/debug/tracing EVENT=$T/events/jbd2/jbd2_handle_stats echo "interval > 5" > $EVENT/filter echo 1 > $EVENT/enable ./run-my-fs-benchmark cat $T/trace > /tmp/problem-handles This will list handles that were active for longer than 20ms. Having longer-running handles is bad, because a commit started at the wrong time could stall for those 20+ milliseconds, which could delay an fsync() or an O_SYNC operation. Here is an example line from the trace file describing a handle which lived on for 311 jiffies, or over 1.2 seconds: postmark-2917 [000] .... 196.435786: jbd2_handle_stats: dev 254,32 tid 570 type 2 line_no 2541 interval 311 sync 0 requested_blocks 1 dirtied_blocks 0 Signed-off-by: "Theodore Ts'o" --- fs/ext4/acl.c | 5 +++-- fs/ext4/ext4_jbd2.c | 5 +++-- fs/ext4/ext4_jbd2.h | 31 ++++++++++++++++++++++++++++--- fs/ext4/extents.c | 11 ++++++----- fs/ext4/file.c | 2 +- fs/ext4/ialloc.c | 2 +- fs/ext4/indirect.c | 7 ++++--- fs/ext4/inline.c | 10 +++++----- fs/ext4/inode.c | 33 ++++++++++++++++++++------------- fs/ext4/ioctl.c | 4 ++-- fs/ext4/migrate.c | 4 ++-- fs/ext4/move_extent.c | 2 +- fs/ext4/namei.c | 42 ++++++++++++++++++++++++------------------ fs/ext4/resize.c | 10 +++++----- fs/ext4/super.c | 10 +++++----- fs/ext4/xattr.c | 2 +- 16 files changed, 111 insertions(+), 69 deletions(-) diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index e6e0d988439b..406cf8bb12d5 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -324,7 +324,7 @@ ext4_acl_chmod(struct inode *inode) if (error) return error; retry: - handle = ext4_journal_start(inode, + handle = ext4_journal_start(inode, EXT4_HT_XATTR, EXT4_DATA_TRANS_BLOCKS(inode->i_sb)); if (IS_ERR(handle)) { error = PTR_ERR(handle); @@ -422,7 +422,8 @@ ext4_xattr_set_acl(struct dentry *dentry, const char *name, const void *value, acl = NULL; retry: - handle = ext4_journal_start(inode, EXT4_DATA_TRANS_BLOCKS(inode->i_sb)); + handle = ext4_journal_start(inode, EXT4_HT_XATTR, + EXT4_DATA_TRANS_BLOCKS(inode->i_sb)); if (IS_ERR(handle)) { error = PTR_ERR(handle); goto release_and_out; diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index 6f6114525535..7058975e3a55 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -38,7 +38,8 @@ static void ext4_put_nojournal(handle_t *handle) /* * Wrappers for jbd2_journal_start/end. */ -handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks) +handle_t *__ext4_journal_start_sb(struct super_block *sb, unsigned int line, + int type, int nblocks) { journal_t *journal; @@ -59,7 +60,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks) ext4_abort(sb, "Detected aborted journal"); return ERR_PTR(-EROFS); } - return jbd2_journal_start(journal, nblocks); + return jbd2__journal_start(journal, nblocks, GFP_NOFS, type, line); } int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle) diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index 7177f9b21cb2..302814b85945 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -110,6 +110,22 @@ #define EXT4_MAXQUOTAS_INIT_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_INIT_BLOCKS(sb)) #define EXT4_MAXQUOTAS_DEL_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_DEL_BLOCKS(sb)) +/* + * Ext4 handle operation types -- for logging purposes + */ +#define EXT4_HT_MISC 0 +#define EXT4_HT_INODE 1 +#define EXT4_HT_WRITE_PAGE 2 +#define EXT4_HT_MAP_BLOCKS 3 +#define EXT4_HT_DIR 4 +#define EXT4_HT_TRUNCATE 5 +#define EXT4_HT_QUOTA 6 +#define EXT4_HT_RESIZE 7 +#define EXT4_HT_MIGRATE 8 +#define EXT4_HT_MOVE_EXTENTS 9 +#define EXT4_HT_XATTR 10 +#define EXT4_HT_MAX 11 + /** * struct ext4_journal_cb_entry - Base structure for callback information. * @@ -234,7 +250,8 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line, #define ext4_handle_dirty_super(handle, sb) \ __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb)) -handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks); +handle_t *__ext4_journal_start_sb(struct super_block *sb, unsigned int line, + int type, int nblocks); int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle); #define EXT4_NOJOURNAL_MAX_REF_COUNT ((unsigned long) 4096) @@ -268,9 +285,17 @@ static inline int ext4_handle_has_enough_credits(handle_t *handle, int needed) return 1; } -static inline handle_t *ext4_journal_start(struct inode *inode, int nblocks) +#define ext4_journal_start_sb(sb, type, nblocks) \ + __ext4_journal_start_sb((sb), __LINE__, (type), (nblocks)) + +#define ext4_journal_start(inode, type, nblocks) \ + __ext4_journal_start((inode), __LINE__, (type), (nblocks)) + +static inline handle_t *__ext4_journal_start(struct inode *inode, + unsigned int line, int type, + int nblocks) { - return ext4_journal_start_sb(inode->i_sb, nblocks); + return __ext4_journal_start_sb(inode->i_sb, line, type, nblocks); } #define ext4_journal_stop(handle) \ diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index db55c62e1f01..b6b54d658dc2 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -2656,7 +2656,7 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, ext_debug("truncate since %u to %u\n", start, end); /* probably first extent we're gonna free will be last in block */ - handle = ext4_journal_start(inode, depth + 1); + handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, depth + 1); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -4287,7 +4287,7 @@ void ext4_ext_truncate(struct inode *inode) * probably first extent we're gonna free will be last in block */ err = ext4_writepage_trans_blocks(inode); - handle = ext4_journal_start(inode, err); + handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, err); if (IS_ERR(handle)) return; @@ -4454,7 +4454,8 @@ retry: while (ret >= 0 && ret < max_blocks) { map.m_lblk = map.m_lblk + ret; map.m_len = max_blocks = max_blocks - ret; - handle = ext4_journal_start(inode, credits); + handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS, + credits); if (IS_ERR(handle)) { ret = PTR_ERR(handle); break; @@ -4532,7 +4533,7 @@ int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset, while (ret >= 0 && ret < max_blocks) { map.m_lblk += ret; map.m_len = (max_blocks -= ret); - handle = ext4_journal_start(inode, credits); + handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS, credits); if (IS_ERR(handle)) { ret = PTR_ERR(handle); break; @@ -4710,7 +4711,7 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length) inode_dio_wait(inode); credits = ext4_writepage_trans_blocks(inode); - handle = ext4_journal_start(inode, credits); + handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto out_dio; diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 405565a62277..2cf8ab810687 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -240,7 +240,7 @@ static int ext4_file_open(struct inode * inode, struct file * filp) handle_t *handle; int err; - handle = ext4_journal_start_sb(sb, 1); + handle = ext4_journal_start_sb(sb, EXT4_HT_MISC, 1); if (IS_ERR(handle)) return PTR_ERR(handle); err = ext4_journal_get_write_access(handle, sbi->s_sbh); diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 3f32c8012447..10bd6fecc9ff 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -1137,7 +1137,7 @@ int ext4_init_inode_table(struct super_block *sb, ext4_group_t group, if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_ZEROED)) goto out; - handle = ext4_journal_start_sb(sb, 1); + handle = ext4_journal_start_sb(sb, EXT4_HT_MISC, 1); if (IS_ERR(handle)) { ret = PTR_ERR(handle); goto out; diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 193281098fb6..c541ab8b64dd 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -791,7 +791,7 @@ ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb, if (final_size > inode->i_size) { /* Credits for sb + inode write */ - handle = ext4_journal_start(inode, 2); + handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); if (IS_ERR(handle)) { ret = PTR_ERR(handle); goto out; @@ -851,7 +851,7 @@ locked: int err; /* Credits for sb + inode write */ - handle = ext4_journal_start(inode, 2); + handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); if (IS_ERR(handle)) { /* This is really bad luck. We've written the data * but cannot extend i_size. Bail out and pretend @@ -950,7 +950,8 @@ static handle_t *start_transaction(struct inode *inode) { handle_t *result; - result = ext4_journal_start(inode, ext4_blocks_for_truncate(inode)); + result = ext4_journal_start(inode, EXT4_HT_TRUNCATE, + ext4_blocks_for_truncate(inode)); if (!IS_ERR(result)) return result; diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 93a3408fc89b..bc5f871f0893 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -545,7 +545,7 @@ static int ext4_convert_inline_data_to_extent(struct address_space *mapping, return ret; retry: - handle = ext4_journal_start(inode, needed_blocks); + handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, needed_blocks); if (IS_ERR(handle)) { ret = PTR_ERR(handle); handle = NULL; @@ -657,7 +657,7 @@ int ext4_try_to_write_inline_data(struct address_space *mapping, * The possible write could happen in the inode, * so try to reserve the space in inode first. */ - handle = ext4_journal_start(inode, 1); + handle = ext4_journal_start(inode, EXT4_HT_INODE, 1); if (IS_ERR(handle)) { ret = PTR_ERR(handle); handle = NULL; @@ -853,7 +853,7 @@ int ext4_da_write_inline_data_begin(struct address_space *mapping, if (ret) return ret; - handle = ext4_journal_start(inode, 1); + handle = ext4_journal_start(inode, EXT4_HT_INODE, 1); if (IS_ERR(handle)) { ret = PTR_ERR(handle); handle = NULL; @@ -1770,7 +1770,7 @@ void ext4_inline_data_truncate(struct inode *inode, int *has_inline) needed_blocks = ext4_writepage_trans_blocks(inode); - handle = ext4_journal_start(inode, needed_blocks); + handle = ext4_journal_start(inode, EXT4_HT_INODE, needed_blocks); if (IS_ERR(handle)) return; @@ -1862,7 +1862,7 @@ int ext4_convert_inline_data(struct inode *inode) if (error) return error; - handle = ext4_journal_start(inode, needed_blocks); + handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, needed_blocks); if (IS_ERR(handle)) { error = PTR_ERR(handle); goto out_free; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 07d9defeaf8c..5042c8773ad7 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -234,7 +234,8 @@ void ext4_evict_inode(struct inode *inode) * protection against it */ sb_start_intwrite(inode->i_sb); - handle = ext4_journal_start(inode, ext4_blocks_for_truncate(inode)+3); + handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, + ext4_blocks_for_truncate(inode)+3); if (IS_ERR(handle)) { ext4_std_error(inode->i_sb, PTR_ERR(handle)); /* @@ -656,7 +657,8 @@ static int _ext4_get_block(struct inode *inode, sector_t iblock, if (map.m_len > DIO_MAX_BLOCKS) map.m_len = DIO_MAX_BLOCKS; dio_credits = ext4_chunk_trans_blocks(inode, map.m_len); - handle = ext4_journal_start(inode, dio_credits); + handle = ext4_journal_start(inode, EXT4_HT_MAP_BLOCKS, + dio_credits); if (IS_ERR(handle)) { ret = PTR_ERR(handle); return ret; @@ -881,7 +883,7 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping, } retry: - handle = ext4_journal_start(inode, needed_blocks); + handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, needed_blocks); if (IS_ERR(handle)) { ret = PTR_ERR(handle); goto out; @@ -1881,7 +1883,8 @@ static int __ext4_journalled_writepage(struct page *page, * references to buffers so we are safe */ unlock_page(page); - handle = ext4_journal_start(inode, ext4_writepage_trans_blocks(inode)); + handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, + ext4_writepage_trans_blocks(inode)); if (IS_ERR(handle)) { ret = PTR_ERR(handle); goto out; @@ -2312,7 +2315,8 @@ retry: needed_blocks = ext4_da_writepages_trans_blocks(inode); /* start a new transaction*/ - handle = ext4_journal_start(inode, needed_blocks); + handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, + needed_blocks); if (IS_ERR(handle)) { ret = PTR_ERR(handle); ext4_msg(inode->i_sb, KERN_CRIT, "%s: jbd2_start: " @@ -2468,7 +2472,7 @@ retry: * to journalling the i_disksize update if writes to the end * of file which has an already mapped buffer. */ - handle = ext4_journal_start(inode, 1); + handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, 1); if (IS_ERR(handle)) { ret = PTR_ERR(handle); goto out; @@ -4215,8 +4219,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) /* (user+group)*(old+new) structure, inode write (sb, * inode block, ? - but truncate inode update has it) */ - handle = ext4_journal_start(inode, (EXT4_MAXQUOTAS_INIT_BLOCKS(inode->i_sb)+ - EXT4_MAXQUOTAS_DEL_BLOCKS(inode->i_sb))+3); + handle = ext4_journal_start(inode, EXT4_HT_QUOTA, + (EXT4_MAXQUOTAS_INIT_BLOCKS(inode->i_sb) + + EXT4_MAXQUOTAS_DEL_BLOCKS(inode->i_sb)) + 3); if (IS_ERR(handle)) { error = PTR_ERR(handle); goto err_out; @@ -4251,7 +4256,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) (attr->ia_size < inode->i_size)) { handle_t *handle; - handle = ext4_journal_start(inode, 3); + handle = ext4_journal_start(inode, EXT4_HT_INODE, 3); if (IS_ERR(handle)) { error = PTR_ERR(handle); goto err_out; @@ -4271,7 +4276,8 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) attr->ia_size); if (error) { /* Do as much error cleanup as possible */ - handle = ext4_journal_start(inode, 3); + handle = ext4_journal_start(inode, + EXT4_HT_INODE, 3); if (IS_ERR(handle)) { ext4_orphan_del(NULL, inode); goto err_out; @@ -4612,7 +4618,7 @@ void ext4_dirty_inode(struct inode *inode, int flags) { handle_t *handle; - handle = ext4_journal_start(inode, 2); + handle = ext4_journal_start(inode, EXT4_HT_INODE, 2); if (IS_ERR(handle)) goto out; @@ -4713,7 +4719,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) /* Finally we can mark the inode as dirty. */ - handle = ext4_journal_start(inode, 1); + handle = ext4_journal_start(inode, EXT4_HT_INODE, 1); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -4791,7 +4797,8 @@ int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) else get_block = ext4_get_block; retry_alloc: - handle = ext4_journal_start(inode, ext4_writepage_trans_blocks(inode)); + handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, + ext4_writepage_trans_blocks(inode)); if (IS_ERR(handle)) { ret = VM_FAULT_SIGBUS; goto out; diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 4784ac244fc6..31f4f56a32d6 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -104,7 +104,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } else if (oldflags & EXT4_EOFBLOCKS_FL) ext4_truncate(inode); - handle = ext4_journal_start(inode, 1); + handle = ext4_journal_start(inode, EXT4_HT_INODE, 1); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto flags_out; @@ -173,7 +173,7 @@ flags_out: } mutex_lock(&inode->i_mutex); - handle = ext4_journal_start(inode, 1); + handle = ext4_journal_start(inode, EXT4_HT_INODE, 1); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto unlock_out; diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c index db8226d595fa..4e4fcfd342f8 100644 --- a/fs/ext4/migrate.c +++ b/fs/ext4/migrate.c @@ -456,7 +456,7 @@ int ext4_ext_migrate(struct inode *inode) */ return retval; - handle = ext4_journal_start(inode, + handle = ext4_journal_start(inode, EXT4_HT_MIGRATE, EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + EXT4_MAXQUOTAS_INIT_BLOCKS(inode->i_sb) @@ -507,7 +507,7 @@ int ext4_ext_migrate(struct inode *inode) ext4_set_inode_state(inode, EXT4_STATE_EXT_MIGRATE); up_read((&EXT4_I(inode)->i_data_sem)); - handle = ext4_journal_start(inode, 1); + handle = ext4_journal_start(inode, EXT4_HT_MIGRATE, 1); if (IS_ERR(handle)) { /* * It is impossible to update on-disk structures without diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index e4cdb5188f34..0d6734394eac 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -923,7 +923,7 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode, again: *err = 0; jblocks = ext4_writepage_trans_blocks(orig_inode) * 2; - handle = ext4_journal_start(orig_inode, jblocks); + handle = ext4_journal_start(orig_inode, EXT4_HT_MOVE_EXTENTS, jblocks); if (IS_ERR(handle)) { *err = PTR_ERR(handle); return 0; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 34ed624d2c56..51841032ca03 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2262,9 +2262,10 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, umode_t mode, dquot_initialize(dir); retry: - handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + - EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + - EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb)); + handle = ext4_journal_start(dir, EXT4_HT_DIR, + (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb))); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2298,9 +2299,10 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry, dquot_initialize(dir); retry: - handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + - EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + - EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb)); + handle = ext4_journal_start(dir, EXT4_HT_DIR, + (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb))); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2414,9 +2416,10 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) dquot_initialize(dir); retry: - handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + - EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + - EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb)); + handle = ext4_journal_start(dir, EXT4_HT_DIR, + (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb))); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2729,7 +2732,8 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry) dquot_initialize(dir); dquot_initialize(dentry->d_inode); - handle = ext4_journal_start(dir, EXT4_DELETE_TRANS_BLOCKS(dir->i_sb)); + handle = ext4_journal_start(dir, EXT4_HT_DIR, + EXT4_DELETE_TRANS_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2791,7 +2795,8 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry) dquot_initialize(dir); dquot_initialize(dentry->d_inode); - handle = ext4_journal_start(dir, EXT4_DELETE_TRANS_BLOCKS(dir->i_sb)); + handle = ext4_journal_start(dir, EXT4_HT_DIR, + EXT4_DELETE_TRANS_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2870,7 +2875,7 @@ static int ext4_symlink(struct inode *dir, EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb); } retry: - handle = ext4_journal_start(dir, credits); + handle = ext4_journal_start(dir, EXT4_HT_DIR, credits); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -2908,7 +2913,7 @@ retry: * Now inode is being linked into dir (EXT4_DATA_TRANS_BLOCKS * + EXT4_INDEX_EXTRA_TRANS_BLOCKS), inode is also modified */ - handle = ext4_journal_start(dir, + handle = ext4_journal_start(dir, EXT4_HT_DIR, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 1); if (IS_ERR(handle)) { @@ -2955,8 +2960,9 @@ static int ext4_link(struct dentry *old_dentry, dquot_initialize(dir); retry: - handle = ext4_journal_start(dir, EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + - EXT4_INDEX_EXTRA_TRANS_BLOCKS); + handle = ext4_journal_start(dir, EXT4_HT_DIR, + (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + + EXT4_INDEX_EXTRA_TRANS_BLOCKS)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -3039,9 +3045,9 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, * in separate transaction */ if (new_dentry->d_inode) dquot_initialize(new_dentry->d_inode); - handle = ext4_journal_start(old_dir, 2 * - EXT4_DATA_TRANS_BLOCKS(old_dir->i_sb) + - EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2); + handle = ext4_journal_start(old_dir, EXT4_HT_DIR, + (2 * EXT4_DATA_TRANS_BLOCKS(old_dir->i_sb) + + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2)); if (IS_ERR(handle)) return PTR_ERR(handle); diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 8eefb636beb8..c7f4d7584669 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -466,7 +466,7 @@ static int setup_new_flex_group_blocks(struct super_block *sb, meta_bg = EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG); /* This transaction may be extended/restarted along the way */ - handle = ext4_journal_start_sb(sb, EXT4_MAX_TRANS_DATA); + handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, EXT4_MAX_TRANS_DATA); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -1031,7 +1031,7 @@ static void update_backups(struct super_block *sb, int blk_off, char *data, handle_t *handle; int err = 0, err2; - handle = ext4_journal_start_sb(sb, EXT4_MAX_TRANS_DATA); + handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, EXT4_MAX_TRANS_DATA); if (IS_ERR(handle)) { group = 1; err = PTR_ERR(handle); @@ -1412,7 +1412,7 @@ static int ext4_flex_group_add(struct super_block *sb, * modify each of the reserved GDT dindirect blocks. */ credit = flex_gd->count * 4 + reserved_gdb; - handle = ext4_journal_start_sb(sb, credit); + handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, credit); if (IS_ERR(handle)) { err = PTR_ERR(handle); goto exit; @@ -1624,7 +1624,7 @@ static int ext4_group_extend_no_check(struct super_block *sb, /* We will update the superblock, one block bitmap, and * one group descriptor via ext4_group_add_blocks(). */ - handle = ext4_journal_start_sb(sb, 3); + handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, 3); if (IS_ERR(handle)) { err = PTR_ERR(handle); ext4_warning(sb, "error %d on journal start", err); @@ -1788,7 +1788,7 @@ static int ext4_convert_meta_bg(struct super_block *sb, struct inode *inode) credits += 3; /* block bitmap, bg descriptor, resize inode */ } - handle = ext4_journal_start_sb(sb, credits); + handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, credits); if (IS_ERR(handle)) return PTR_ERR(handle); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index cb9d67fbc8f0..ef6ac59e4961 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -4762,7 +4762,7 @@ static int ext4_write_dquot(struct dquot *dquot) struct inode *inode; inode = dquot_to_inode(dquot); - handle = ext4_journal_start(inode, + handle = ext4_journal_start(inode, EXT4_HT_QUOTA, EXT4_QUOTA_TRANS_BLOCKS(dquot->dq_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -4778,7 +4778,7 @@ static int ext4_acquire_dquot(struct dquot *dquot) int ret, err; handle_t *handle; - handle = ext4_journal_start(dquot_to_inode(dquot), + handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA, EXT4_QUOTA_INIT_BLOCKS(dquot->dq_sb)); if (IS_ERR(handle)) return PTR_ERR(handle); @@ -4794,7 +4794,7 @@ static int ext4_release_dquot(struct dquot *dquot) int ret, err; handle_t *handle; - handle = ext4_journal_start(dquot_to_inode(dquot), + handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA, EXT4_QUOTA_DEL_BLOCKS(dquot->dq_sb)); if (IS_ERR(handle)) { /* Release dquot anyway to avoid endless cycle in dqput() */ @@ -4826,7 +4826,7 @@ static int ext4_write_info(struct super_block *sb, int type) handle_t *handle; /* Data block + inode block */ - handle = ext4_journal_start(sb->s_root->d_inode, 2); + handle = ext4_journal_start(sb->s_root->d_inode, EXT4_HT_QUOTA, 2); if (IS_ERR(handle)) return PTR_ERR(handle); ret = dquot_commit_info(sb, type); @@ -4972,7 +4972,7 @@ static int ext4_quota_off(struct super_block *sb, int type) /* Update modification times of quota files when userspace can * start looking at them */ - handle = ext4_journal_start(inode, 1); + handle = ext4_journal_start(inode, EXT4_HT_QUOTA, 1); if (IS_ERR(handle)) goto out; inode->i_mtime = inode->i_ctime = CURRENT_TIME; diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index c68990c392c7..2efc5600b03b 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -1175,7 +1175,7 @@ retry: if (ext4_has_inline_data(inode)) credits += ext4_writepage_trans_blocks(inode) + 1; - handle = ext4_journal_start(inode, credits); + handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits); if (IS_ERR(handle)) { error = PTR_ERR(handle); } else { From 1a989d0f1de8f5a150b35e1e8181cc1abc139162 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:24 +0900 Subject: [PATCH 2202/4607] gpiolib: link all gpio_chips using a list Add a list member to gpio_chip that allows all chips to be parsed quickly. The current method requires parsing the entire GPIO integer space, which is painfully slow. Using a list makes many chip operations that involve lookup or parsing faster, and also simplifies the code. It is also necessary to eventually get rid of the global gpio_desc[] array. The list of gpio_chips is always ordered by base GPIO number to ensure chips traversal is done in the right order. Signed-off-by: Alexandre Courbot Reviewed-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 51 ++++++++++++++++++++++++++++++++------ include/asm-generic/gpio.h | 2 ++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e14eb88bbe8d..453ac77771ca 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,8 @@ struct gpio_desc { }; static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; +static LIST_HEAD(gpio_chips); + #ifdef CONFIG_GPIO_SYSFS static DEFINE_IDR(dirent_idr); #endif @@ -1014,6 +1017,43 @@ static inline void gpiochip_unexport(struct gpio_chip *chip) #endif /* CONFIG_GPIO_SYSFS */ +/* + * Add a new chip to the global chips list, keeping the list of chips sorted + * by base order. + * + * Return -EBUSY if the new chip overlaps with some other chip's integer + * space. + */ +static int gpiochip_add_to_list(struct gpio_chip *chip) +{ + struct list_head *pos = &gpio_chips; + struct gpio_chip *_chip; + int err = 0; + + /* find where to insert our chip */ + list_for_each(pos, &gpio_chips) { + _chip = list_entry(pos, struct gpio_chip, list); + /* shall we insert before _chip? */ + if (_chip->base >= chip->base + chip->ngpio) + break; + } + + /* are we stepping on the chip right before? */ + if (pos != &gpio_chips && pos->prev != &gpio_chips) { + _chip = list_entry(pos->prev, struct gpio_chip, list); + if (_chip->base + _chip->ngpio > chip->base) { + dev_err(chip->dev, + "GPIO integer space overlap, cannot add chip\n"); + err = -EBUSY; + } + } + + if (!err) + list_add_tail(&chip->list, pos); + + return err; +} + /** * gpiochip_add() - register a gpio_chip * @chip: the chip to register, with chip->base initialized @@ -1055,13 +1095,8 @@ int gpiochip_add(struct gpio_chip *chip) chip->base = base; } - /* these GPIO numbers must not be managed by another gpio_chip */ - for (id = base; id < base + chip->ngpio; id++) { - if (gpio_desc[id].chip != NULL) { - status = -EBUSY; - break; - } - } + status = gpiochip_add_to_list(chip); + if (status == 0) { for (id = base; id < base + chip->ngpio; id++) { gpio_desc[id].chip = chip; @@ -1135,6 +1170,8 @@ int gpiochip_remove(struct gpio_chip *chip) if (status == 0) { for (id = chip->base; id < chip->base + chip->ngpio; id++) gpio_desc[id].chip = NULL; + + list_del(&chip->list); } spin_unlock_irqrestore(&gpio_lock, flags); diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 2034e691c7ab..b562f95cdc2f 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -53,6 +53,7 @@ struct device_node; * @label: for diagnostics * @dev: optional device providing the GPIOs * @owner: helps prevent removal of modules exporting active GPIOs + * @list: links gpio_chips together for traversal * @request: optional hook for chip-specific activation, such as * enabling module power and clock; may sleep * @free: optional hook for chip-specific deactivation, such as @@ -98,6 +99,7 @@ struct gpio_chip { const char *label; struct device *dev; struct module *owner; + struct list_head list; int (*request)(struct gpio_chip *chip, unsigned offset); From 65493e3ac429623df021e0859d97691f4b42615a Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:25 +0900 Subject: [PATCH 2203/4607] gpiolib: use gpio_chips list in gpiolib_sysfs_init Use the small list of GPIO chips instead of parsing the whole GPIO number space. Signed-off-by: Alexandre Courbot Reviewed-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 453ac77771ca..c1d8f7bdcd8f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -974,7 +974,7 @@ static int __init gpiolib_sysfs_init(void) { int status; unsigned long flags; - unsigned gpio; + struct gpio_chip *chip; status = class_register(&gpio_class); if (status < 0) @@ -987,10 +987,7 @@ static int __init gpiolib_sysfs_init(void) * registered, and so arch_initcall() can always gpio_export(). */ spin_lock_irqsave(&gpio_lock, flags); - for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) { - struct gpio_chip *chip; - - chip = gpio_desc[gpio].chip; + list_for_each_entry(chip, &gpio_chips, list) { if (!chip || chip->exported) continue; From 125eef96f6cfadddbac8f6b9fccc9848988e7c6e Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:26 +0900 Subject: [PATCH 2204/4607] gpiolib: use gpio_chips list in gpiochip_find Using the GPIO chips list is much faster than parsing the entire GPIO number space. Signed-off-by: Alexandre Courbot Reviewed-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c1d8f7bdcd8f..5a79cb955d9f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1195,20 +1195,17 @@ struct gpio_chip *gpiochip_find(void *data, int (*match)(struct gpio_chip *chip, void *data)) { - struct gpio_chip *chip = NULL; + struct gpio_chip *chip; unsigned long flags; - int i; spin_lock_irqsave(&gpio_lock, flags); - for (i = 0; i < ARCH_NR_GPIOS; i++) { - if (!gpio_desc[i].chip) - continue; - - if (match(gpio_desc[i].chip, data)) { - chip = gpio_desc[i].chip; + list_for_each_entry(chip, &gpio_chips, list) + if (match(chip, data)) break; - } - } + + /* No match? */ + if (&chip->list == &gpio_chips) + chip = NULL; spin_unlock_irqrestore(&gpio_lock, flags); return chip; From cb1650d4e0da27e88c1a1bd8fe98c40ae1a5d313 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:27 +0900 Subject: [PATCH 2205/4607] gpiolib: use gpio_chips list in sysfs ops This makes the code both simpler and faster compared to parsing the GPIO number space. Signed-off-by: Alexandre Courbot Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5a79cb955d9f..585d7c3ce12e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1890,45 +1890,28 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) { struct gpio_chip *chip = NULL; - unsigned int gpio; - void *ret = NULL; - loff_t index = 0; + loff_t index = *pos; /* REVISIT this isn't locked against gpio_chip removal ... */ - for (gpio = 0; gpio_is_valid(gpio); gpio++) { - if (gpio_desc[gpio].chip == chip) - continue; - - chip = gpio_desc[gpio].chip; - if (!chip) - continue; - - if (index++ >= *pos) { - ret = chip; - break; - } - } - s->private = ""; - return ret; + list_for_each_entry(chip, &gpio_chips, list) + if (index-- == 0) + return chip; + + return NULL; } static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) { struct gpio_chip *chip = v; - unsigned int gpio; void *ret = NULL; - /* skip GPIOs provided by the current chip */ - for (gpio = chip->base + chip->ngpio; gpio_is_valid(gpio); gpio++) { - chip = gpio_desc[gpio].chip; - if (chip) { - ret = chip; - break; - } - } + if (list_is_last(&chip->list, &gpio_chips)) + ret = NULL; + else + ret = list_entry(chip->list.next, struct gpio_chip, list); s->private = "\n"; ++*pos; From 83cabe33eb05b51a6239a3df344d89cafac2306c Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:28 +0900 Subject: [PATCH 2206/4607] gpiolib: use gpio_chips list in gpiochip_find_base Re-implement gpiochip_find_base using the list of chips instead of the global gpio_desc[] array. This makes it both simpler and more efficient, and is needed to remove the global descriptors array. The new code should preserve the exact same GPIO number assignment policy as the code it is replacing. There shouldn't be any visible change to the assigned GPIO numbers. Signed-off-by: Alexandre Courbot [grant.likely: Added comment about assignment policy] Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 585d7c3ce12e..8ccf68bb8a40 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -126,30 +126,25 @@ struct gpio_chip *gpio_to_chip(unsigned gpio) /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ static int gpiochip_find_base(int ngpio) { - int i; - int spare = 0; - int base = -ENOSPC; + struct gpio_chip *chip; + int base = ARCH_NR_GPIOS - ngpio; - for (i = ARCH_NR_GPIOS - 1; i >= 0 ; i--) { - struct gpio_desc *desc = &gpio_desc[i]; - struct gpio_chip *chip = desc->chip; - - if (!chip) { - spare++; - if (spare == ngpio) { - base = i; - break; - } - } else { - spare = 0; - if (chip) - i -= chip->ngpio - 1; - } + list_for_each_entry_reverse(chip, &gpio_chips, list) { + /* found a free space? */ + if (chip->base + chip->ngpio <= base) + break; + else + /* nope, check the space right before the chip */ + base = chip->base - ngpio; } - if (gpio_is_valid(base)) + if (gpio_is_valid(base)) { pr_debug("%s: found new base at %d\n", __func__, base); - return base; + return base; + } else { + pr_err("%s: cannot find free range\n", __func__); + return -ENOSPC; + } } /* caller ensures gpio is valid and requested, chip->get_direction may sleep */ From 47564bfb95bf370d73906fc4ae57c271e8ba96cd Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 9 Feb 2013 09:24:14 -0500 Subject: [PATCH 2207/4607] ext4: grab page before starting transaction handle in write_begin() The grab_cache_page_write_begin() function can potentially sleep for a long time, since it may need to do memory allocation which can block if the system is under significant memory pressure, and because it may be blocked on page writeback. If it does take a long time to grab the page, it's better that we not hold an active jbd2 handle. So grab a handle on the page first, and _then_ start the transaction handle. This commit fixes the following long transaction handle hold time: postmark-2917 [000] .... 196.435786: jbd2_handle_stats: dev 254,32 tid 570 type 2 line_no 2541 interval 311 sync 0 requested_blocks 1 dirtied_blocks 0 Signed-off-by: "Theodore Ts'o" Reviewed-by: Jan Kara --- fs/ext4/inode.c | 111 +++++++++++++++++++++++++++++------------------- 1 file changed, 68 insertions(+), 43 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 5042c8773ad7..2fa18bb0bf3c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -875,32 +875,40 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping, ret = ext4_try_to_write_inline_data(mapping, inode, pos, len, flags, pagep); if (ret < 0) - goto out; - if (ret == 1) { - ret = 0; - goto out; - } + return ret; + if (ret == 1) + return 0; } -retry: + /* + * grab_cache_page_write_begin() can take a long time if the + * system is thrashing due to memory pressure, or if the page + * is being written back. So grab it first before we start + * the transaction handle. This also allows us to allocate + * the page (if needed) without using GFP_NOFS. + */ +retry_grab: + page = grab_cache_page_write_begin(mapping, index, flags); + if (!page) + return -ENOMEM; + unlock_page(page); + +retry_journal: handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, needed_blocks); if (IS_ERR(handle)) { - ret = PTR_ERR(handle); - goto out; + page_cache_release(page); + return PTR_ERR(handle); } - /* We cannot recurse into the filesystem as the transaction is already - * started */ - flags |= AOP_FLAG_NOFS; - - page = grab_cache_page_write_begin(mapping, index, flags); - if (!page) { + lock_page(page); + if (page->mapping != mapping) { + /* The page got truncated from under us */ + unlock_page(page); + page_cache_release(page); ext4_journal_stop(handle); - ret = -ENOMEM; - goto out; + goto retry_grab; } - - *pagep = page; + wait_on_page_writeback(page); if (ext4_should_dioread_nolock(inode)) ret = __block_write_begin(page, pos, len, ext4_get_block_write); @@ -915,7 +923,6 @@ retry: if (ret) { unlock_page(page); - page_cache_release(page); /* * __block_write_begin may have instantiated a few blocks * outside i_size. Trim these off again. Don't need @@ -939,11 +946,14 @@ retry: if (inode->i_nlink) ext4_orphan_del(NULL, inode); } - } - if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) - goto retry; -out: + if (ret == -ENOSPC && + ext4_should_retry_alloc(inode->i_sb, &retries)) + goto retry_journal; + page_cache_release(page); + return ret; + } + *pagep = page; return ret; } @@ -2458,42 +2468,52 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping, pos, len, flags, pagep, fsdata); if (ret < 0) - goto out; - if (ret == 1) { - ret = 0; - goto out; - } + return ret; + if (ret == 1) + return 0; } -retry: + /* + * grab_cache_page_write_begin() can take a long time if the + * system is thrashing due to memory pressure, or if the page + * is being written back. So grab it first before we start + * the transaction handle. This also allows us to allocate + * the page (if needed) without using GFP_NOFS. + */ +retry_grab: + page = grab_cache_page_write_begin(mapping, index, flags); + if (!page) + return -ENOMEM; + unlock_page(page); + /* * With delayed allocation, we don't log the i_disksize update * if there is delayed block allocation. But we still need * to journalling the i_disksize update if writes to the end * of file which has an already mapped buffer. */ +retry_journal: handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, 1); if (IS_ERR(handle)) { - ret = PTR_ERR(handle); - goto out; + page_cache_release(page); + return PTR_ERR(handle); } - /* We cannot recurse into the filesystem as the transaction is already - * started */ - flags |= AOP_FLAG_NOFS; - page = grab_cache_page_write_begin(mapping, index, flags); - if (!page) { + lock_page(page); + if (page->mapping != mapping) { + /* The page got truncated from under us */ + unlock_page(page); + page_cache_release(page); ext4_journal_stop(handle); - ret = -ENOMEM; - goto out; + goto retry_grab; } - *pagep = page; + /* In case writeback began while the page was unlocked */ + wait_on_page_writeback(page); ret = __block_write_begin(page, pos, len, ext4_da_get_block_prep); if (ret < 0) { unlock_page(page); ext4_journal_stop(handle); - page_cache_release(page); /* * block_write_begin may have instantiated a few blocks * outside i_size. Trim these off again. Don't need @@ -2501,11 +2521,16 @@ retry: */ if (pos + len > inode->i_size) ext4_truncate_failed_write(inode); + + if (ret == -ENOSPC && + ext4_should_retry_alloc(inode->i_sb, &retries)) + goto retry_journal; + + page_cache_release(page); + return ret; } - if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) - goto retry; -out: + *pagep = page; return ret; } From 931b68649d31b6b52608110f34856f8eb77adb36 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 9 Feb 2013 09:43:39 -0500 Subject: [PATCH 2208/4607] ext4: start handle at the last possible moment in ext4_unlink() Don't start the jbd2 transaction handle until after the directory entry has been found, to minimize the amount of time that a handle is held active. Signed-off-by: "Theodore Ts'o" Reviewed-by: Jan Kara --- fs/ext4/namei.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 51841032ca03..4a27069d54ad 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2787,7 +2787,7 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry) struct inode *inode; struct buffer_head *bh; struct ext4_dir_entry_2 *de; - handle_t *handle; + handle_t *handle = NULL; trace_ext4_unlink_enter(dir, dentry); /* Initialize quotas before so that eventual writes go @@ -2795,14 +2795,6 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry) dquot_initialize(dir); dquot_initialize(dentry->d_inode); - handle = ext4_journal_start(dir, EXT4_HT_DIR, - EXT4_DELETE_TRANS_BLOCKS(dir->i_sb)); - if (IS_ERR(handle)) - return PTR_ERR(handle); - - if (IS_DIRSYNC(dir)) - ext4_handle_sync(handle); - retval = -ENOENT; bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL); if (!bh) @@ -2814,6 +2806,17 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry) if (le32_to_cpu(de->inode) != inode->i_ino) goto end_unlink; + handle = ext4_journal_start(dir, EXT4_HT_DIR, + EXT4_DELETE_TRANS_BLOCKS(dir->i_sb)); + if (IS_ERR(handle)) { + retval = PTR_ERR(handle); + handle = NULL; + goto end_unlink; + } + + if (IS_DIRSYNC(dir)) + ext4_handle_sync(handle); + if (!inode->i_nlink) { ext4_warning(inode->i_sb, "Deleting nonexistent file (%lu), %d", @@ -2834,8 +2837,9 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry) retval = 0; end_unlink: - ext4_journal_stop(handle); brelse(bh); + if (handle) + ext4_journal_stop(handle); trace_ext4_unlink_exit(dentry, retval); return retval; } From 8dcfaad244cdfa245cc2b4ddf42cea5fd8b80ece Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 9 Feb 2013 09:45:11 -0500 Subject: [PATCH 2209/4607] ext4: start handle at the last possible moment in ext4_rmdir() Don't start the jbd2 transaction handle until after the directory entry has been found, to minimize the amount of time that a handle is held active. Signed-off-by: "Theodore Ts'o" Reviewed-by: Jan Kara --- fs/ext4/namei.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 4a27069d54ad..36a4afd12f39 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2725,26 +2725,18 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry) struct inode *inode; struct buffer_head *bh; struct ext4_dir_entry_2 *de; - handle_t *handle; + handle_t *handle = NULL; /* Initialize quotas before so that eventual writes go in * separate transaction */ dquot_initialize(dir); dquot_initialize(dentry->d_inode); - handle = ext4_journal_start(dir, EXT4_HT_DIR, - EXT4_DELETE_TRANS_BLOCKS(dir->i_sb)); - if (IS_ERR(handle)) - return PTR_ERR(handle); - retval = -ENOENT; bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL); if (!bh) goto end_rmdir; - if (IS_DIRSYNC(dir)) - ext4_handle_sync(handle); - inode = dentry->d_inode; retval = -EIO; @@ -2755,6 +2747,17 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry) if (!empty_dir(inode)) goto end_rmdir; + handle = ext4_journal_start(dir, EXT4_HT_DIR, + EXT4_DELETE_TRANS_BLOCKS(dir->i_sb)); + if (IS_ERR(handle)) { + retval = PTR_ERR(handle); + handle = NULL; + goto end_rmdir; + } + + if (IS_DIRSYNC(dir)) + ext4_handle_sync(handle); + retval = ext4_delete_entry(handle, dir, de, bh); if (retval) goto end_rmdir; @@ -2776,8 +2779,9 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry) ext4_mark_inode_dirty(handle, dir); end_rmdir: - ext4_journal_stop(handle); brelse(bh); + if (handle) + ext4_journal_stop(handle); return retval; } From 4b217630d0ec277c961e57f6d2985433b352c2ce Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 9 Feb 2013 12:50:27 -0500 Subject: [PATCH 2210/4607] ext4: fix the number of credits needed for ext4_ext_migrate() The migration ioctl creates a temporary inode. Since this inode is never linked to a directory, we don't need to reserve journal credits required for modifying the directory. Signed-off-by: "Theodore Ts'o" Reviewed-by: Jan Kara --- fs/ext4/migrate.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c index 4e4fcfd342f8..480acf4a085f 100644 --- a/fs/ext4/migrate.c +++ b/fs/ext4/migrate.c @@ -456,11 +456,14 @@ int ext4_ext_migrate(struct inode *inode) */ return retval; + /* + * Worst case we can touch the allocation bitmaps, a bgd + * block, and a block to link in the orphan list. We do need + * need to worry about credits for modifying the quota inode. + */ handle = ext4_journal_start(inode, EXT4_HT_MIGRATE, - EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + - EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + - EXT4_MAXQUOTAS_INIT_BLOCKS(inode->i_sb) - + 1); + 4 + EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb)); + if (IS_ERR(handle)) { retval = PTR_ERR(handle); return retval; From 64044abf05d0842a7fed30e102fa411a744c7d9f Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 9 Feb 2013 15:06:24 -0500 Subject: [PATCH 2211/4607] ext4: fix the number of credits needed for ext4_unlink() and ext4_rmdir() The ext4_unlink() and ext4_rmdir() don't actually release the blocks associated with the file/directory. This gets done in a separate jbd2 handle called via ext4_evict_inode(). Thus, we don't need to reserve lots of journal credits for the truncate. Note that using too many journal credits is non-optimal because it can leading to the journal transmit getting closed too early, before it is strictly necessary. Signed-off-by: "Theodore Ts'o" Reviewed-by: Jan Kara --- fs/ext4/ext4_jbd2.h | 6 ------ fs/ext4/namei.c | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index 302814b85945..c1fc2dca14ae 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -59,12 +59,6 @@ #define EXT4_META_TRANS_BLOCKS(sb) (EXT4_XATTR_TRANS_BLOCKS + \ EXT4_MAXQUOTAS_TRANS_BLOCKS(sb)) -/* Delete operations potentially hit one directory's namespace plus an - * entire inode, plus arbitrary amounts of bitmap/indirection data. Be - * generous. We can grow the delete transaction later if necessary. */ - -#define EXT4_DELETE_TRANS_BLOCKS(sb) (2 * EXT4_DATA_TRANS_BLOCKS(sb) + 64) - /* Define an arbitrary limit for the amount of data we will anticipate * writing to any given transaction. For unbounded transactions such as * write(2) and truncate(2) we can write more than this, but we always diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 36a4afd12f39..5f3d2b569c63 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2748,7 +2748,7 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry) goto end_rmdir; handle = ext4_journal_start(dir, EXT4_HT_DIR, - EXT4_DELETE_TRANS_BLOCKS(dir->i_sb)); + EXT4_DATA_TRANS_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) { retval = PTR_ERR(handle); handle = NULL; @@ -2811,7 +2811,7 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry) goto end_unlink; handle = ext4_journal_start(dir, EXT4_HT_DIR, - EXT4_DELETE_TRANS_BLOCKS(dir->i_sb)); + EXT4_DATA_TRANS_BLOCKS(dir->i_sb)); if (IS_ERR(handle)) { retval = PTR_ERR(handle); handle = NULL; From 95eaefbdececae5e781d76d03fe7472a857c8c7a Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 9 Feb 2013 15:23:03 -0500 Subject: [PATCH 2212/4607] ext4: fix the number of credits needed for acl ops with inline data Operations which modify extended attributes may need extra journal credits if inline data is used, since there is a chance that some extended attributes may need to get pushed to an external attribute block. Changes to reflect this was made in xattr.c, but they were missed in fs/ext4/acl.c. To fix this, abstract the calculation of the number of credits needed for xattr operations to an inline function defined in ext4_jbd2.h, and use it in acl.c and xattr.c. Also move the function declarations used in inline.c from xattr.h (where they are non-obviously hidden, and caused problems since ext4_jbd2.h needs to use the function ext4_has_inline_data), and move them to ext4.h. Signed-off-by: "Theodore Ts'o" Reviewed-by: Tao Ma Reviewed-by: Jan Kara --- fs/ext4/acl.c | 4 +-- fs/ext4/ext4.h | 69 +++++++++++++++++++++++++++++++++++++++++++++ fs/ext4/ext4_jbd2.h | 14 +++++++++ fs/ext4/xattr.c | 9 +----- fs/ext4/xattr.h | 68 -------------------------------------------- 5 files changed, 86 insertions(+), 78 deletions(-) diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index 406cf8bb12d5..39a54a0e9fe4 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -325,7 +325,7 @@ ext4_acl_chmod(struct inode *inode) return error; retry: handle = ext4_journal_start(inode, EXT4_HT_XATTR, - EXT4_DATA_TRANS_BLOCKS(inode->i_sb)); + ext4_jbd2_credits_xattr(inode)); if (IS_ERR(handle)) { error = PTR_ERR(handle); ext4_std_error(inode->i_sb, error); @@ -423,7 +423,7 @@ ext4_xattr_set_acl(struct dentry *dentry, const char *name, const void *value, retry: handle = ext4_journal_start(inode, EXT4_HT_XATTR, - EXT4_DATA_TRANS_BLOCKS(inode->i_sb)); + ext4_jbd2_credits_xattr(inode)); if (IS_ERR(handle)) { error = PTR_ERR(handle); goto release_and_out; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index a5ae87c51401..61ecf059f70c 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2456,6 +2456,75 @@ extern const struct file_operations ext4_file_operations; extern loff_t ext4_llseek(struct file *file, loff_t offset, int origin); extern void ext4_unwritten_wait(struct inode *inode); +/* inline.c */ +extern int ext4_has_inline_data(struct inode *inode); +extern int ext4_get_inline_size(struct inode *inode); +extern int ext4_get_max_inline_size(struct inode *inode); +extern int ext4_find_inline_data_nolock(struct inode *inode); +extern void ext4_write_inline_data(struct inode *inode, + struct ext4_iloc *iloc, + void *buffer, loff_t pos, + unsigned int len); +extern int ext4_prepare_inline_data(handle_t *handle, struct inode *inode, + unsigned int len); +extern int ext4_init_inline_data(handle_t *handle, struct inode *inode, + unsigned int len); +extern int ext4_destroy_inline_data(handle_t *handle, struct inode *inode); + +extern int ext4_readpage_inline(struct inode *inode, struct page *page); +extern int ext4_try_to_write_inline_data(struct address_space *mapping, + struct inode *inode, + loff_t pos, unsigned len, + unsigned flags, + struct page **pagep); +extern int ext4_write_inline_data_end(struct inode *inode, + loff_t pos, unsigned len, + unsigned copied, + struct page *page); +extern struct buffer_head * +ext4_journalled_write_inline_data(struct inode *inode, + unsigned len, + struct page *page); +extern int ext4_da_write_inline_data_begin(struct address_space *mapping, + struct inode *inode, + loff_t pos, unsigned len, + unsigned flags, + struct page **pagep, + void **fsdata); +extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos, + unsigned len, unsigned copied, + struct page *page); +extern int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry, + struct inode *inode); +extern int ext4_try_create_inline_dir(handle_t *handle, + struct inode *parent, + struct inode *inode); +extern int ext4_read_inline_dir(struct file *filp, + void *dirent, filldir_t filldir, + int *has_inline_data); +extern struct buffer_head *ext4_find_inline_entry(struct inode *dir, + const struct qstr *d_name, + struct ext4_dir_entry_2 **res_dir, + int *has_inline_data); +extern int ext4_delete_inline_entry(handle_t *handle, + struct inode *dir, + struct ext4_dir_entry_2 *de_del, + struct buffer_head *bh, + int *has_inline_data); +extern int empty_inline_dir(struct inode *dir, int *has_inline_data); +extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode, + struct ext4_dir_entry_2 **parent_de, + int *retval); +extern int ext4_inline_data_fiemap(struct inode *inode, + struct fiemap_extent_info *fieinfo, + int *has_inline); +extern int ext4_try_to_evict_inline_data(handle_t *handle, + struct inode *inode, + int needed); +extern void ext4_inline_data_truncate(struct inode *inode, int *has_inline); + +extern int ext4_convert_inline_data(struct inode *inode); + /* namei.c */ extern const struct inode_operations ext4_dir_inode_operations; extern const struct inode_operations ext4_special_inode_operations; diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index c1fc2dca14ae..4c216b1bf20c 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -104,6 +104,20 @@ #define EXT4_MAXQUOTAS_INIT_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_INIT_BLOCKS(sb)) #define EXT4_MAXQUOTAS_DEL_BLOCKS(sb) (MAXQUOTAS*EXT4_QUOTA_DEL_BLOCKS(sb)) +static inline int ext4_jbd2_credits_xattr(struct inode *inode) +{ + int credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb); + + /* + * In case of inline data, we may push out the data to a block, + * so we need to reserve credits for this eventuality + */ + if (ext4_has_inline_data(inode)) + credits += ext4_writepage_trans_blocks(inode) + 1; + return credits; +} + + /* * Ext4 handle operation types -- for logging purposes */ diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 2efc5600b03b..cc31da027596 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -1165,16 +1165,9 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name, { handle_t *handle; int error, retries = 0; - int credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb); + int credits = ext4_jbd2_credits_xattr(inode); retry: - /* - * In case of inline data, we may push out the data to a block, - * So reserve the journal space first. - */ - if (ext4_has_inline_data(inode)) - credits += ext4_writepage_trans_blocks(inode) + 1; - handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits); if (IS_ERR(handle)) { error = PTR_ERR(handle); diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 69eda787a96a..aa25deb5c6cd 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h @@ -125,74 +125,6 @@ extern int ext4_xattr_ibody_inline_set(handle_t *handle, struct inode *inode, struct ext4_xattr_info *i, struct ext4_xattr_ibody_find *is); -extern int ext4_has_inline_data(struct inode *inode); -extern int ext4_get_inline_size(struct inode *inode); -extern int ext4_get_max_inline_size(struct inode *inode); -extern int ext4_find_inline_data_nolock(struct inode *inode); -extern void ext4_write_inline_data(struct inode *inode, - struct ext4_iloc *iloc, - void *buffer, loff_t pos, - unsigned int len); -extern int ext4_prepare_inline_data(handle_t *handle, struct inode *inode, - unsigned int len); -extern int ext4_init_inline_data(handle_t *handle, struct inode *inode, - unsigned int len); -extern int ext4_destroy_inline_data(handle_t *handle, struct inode *inode); - -extern int ext4_readpage_inline(struct inode *inode, struct page *page); -extern int ext4_try_to_write_inline_data(struct address_space *mapping, - struct inode *inode, - loff_t pos, unsigned len, - unsigned flags, - struct page **pagep); -extern int ext4_write_inline_data_end(struct inode *inode, - loff_t pos, unsigned len, - unsigned copied, - struct page *page); -extern struct buffer_head * -ext4_journalled_write_inline_data(struct inode *inode, - unsigned len, - struct page *page); -extern int ext4_da_write_inline_data_begin(struct address_space *mapping, - struct inode *inode, - loff_t pos, unsigned len, - unsigned flags, - struct page **pagep, - void **fsdata); -extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos, - unsigned len, unsigned copied, - struct page *page); -extern int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry, - struct inode *inode); -extern int ext4_try_create_inline_dir(handle_t *handle, - struct inode *parent, - struct inode *inode); -extern int ext4_read_inline_dir(struct file *filp, - void *dirent, filldir_t filldir, - int *has_inline_data); -extern struct buffer_head *ext4_find_inline_entry(struct inode *dir, - const struct qstr *d_name, - struct ext4_dir_entry_2 **res_dir, - int *has_inline_data); -extern int ext4_delete_inline_entry(handle_t *handle, - struct inode *dir, - struct ext4_dir_entry_2 *de_del, - struct buffer_head *bh, - int *has_inline_data); -extern int empty_inline_dir(struct inode *dir, int *has_inline_data); -extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode, - struct ext4_dir_entry_2 **parent_de, - int *retval); -extern int ext4_inline_data_fiemap(struct inode *inode, - struct fiemap_extent_info *fieinfo, - int *has_inline); -extern int ext4_try_to_evict_inline_data(handle_t *handle, - struct inode *inode, - int needed); -extern void ext4_inline_data_truncate(struct inode *inode, int *has_inline); - -extern int ext4_convert_inline_data(struct inode *inode); - #ifdef CONFIG_EXT4_FS_SECURITY extern int ext4_init_security(handle_t *handle, struct inode *inode, struct inode *dir, const struct qstr *qstr); From 1139575a927010390c6b38e4215a6d741b056074 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 9 Feb 2013 16:27:09 -0500 Subject: [PATCH 2213/4607] ext4: start handle at the last possible moment when creating inodes In ext4_{create,mknod,mkdir,symlink}(), don't start the journal handle until the inode has been succesfully allocated. In order to do this, we need to start the handle in the ext4_new_inode(). So create a new variant of this function, ext4_new_inode_start_handle(), so the handle can be created at the last possible minute, before we need to modify the inode allocation bitmap block. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 17 +++++++-- fs/ext4/ialloc.c | 15 ++++++-- fs/ext4/namei.c | 94 +++++++++++++++++++++++------------------------- 3 files changed, 71 insertions(+), 55 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 61ecf059f70c..fc1c0375c9f2 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2004,9 +2004,20 @@ extern int ext4fs_dirhash(const char *name, int len, struct dx_hash_info *hinfo); /* ialloc.c */ -extern struct inode *ext4_new_inode(handle_t *, struct inode *, umode_t, - const struct qstr *qstr, __u32 goal, - uid_t *owner); +extern struct inode *__ext4_new_inode(handle_t *, struct inode *, umode_t, + const struct qstr *qstr, __u32 goal, + uid_t *owner, int handle_type, + unsigned int line_no, int nblocks); + +#define ext4_new_inode(handle, dir, mode, qstr, goal, owner) \ + __ext4_new_inode((handle), (dir), (mode), (qstr), (goal), (owner), \ + 0, 0, 0) +#define ext4_new_inode_start_handle(dir, mode, qstr, goal, owner, \ + type, nblocks) \ + __ext4_new_inode(NULL, (dir), (mode), (qstr), (goal), (owner), \ + (type), __LINE__, (nblocks)) + + extern void ext4_free_inode(handle_t *, struct inode *); extern struct inode * ext4_orphan_get(struct super_block *, unsigned long); extern unsigned long ext4_count_free_inodes(struct super_block *); diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 10bd6fecc9ff..91d8fe3aced3 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -634,8 +634,10 @@ static int find_group_other(struct super_block *sb, struct inode *parent, * For other inodes, search forward from the parent directory's block * group to find a free inode. */ -struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, umode_t mode, - const struct qstr *qstr, __u32 goal, uid_t *owner) +struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir, + umode_t mode, const struct qstr *qstr, + __u32 goal, uid_t *owner, int handle_type, + unsigned int line_no, int nblocks) { struct super_block *sb; struct buffer_head *inode_bitmap_bh = NULL; @@ -725,6 +727,15 @@ repeat_in_this_group: "inode=%lu", ino + 1); continue; } + if (!handle) { + BUG_ON(nblocks <= 0); + handle = __ext4_journal_start_sb(dir->i_sb, line_no, + handle_type, nblocks); + if (IS_ERR(handle)) { + err = PTR_ERR(handle); + goto fail; + } + } BUFFER_TRACE(inode_bitmap_bh, "get_write_access"); err = ext4_journal_get_write_access(handle, inode_bitmap_bh); if (err) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 5f3d2b569c63..3e1529c39808 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2257,30 +2257,28 @@ static int ext4_create(struct inode *dir, struct dentry *dentry, umode_t mode, { handle_t *handle; struct inode *inode; - int err, retries = 0; + int err, credits, retries = 0; dquot_initialize(dir); + credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb)); retry: - handle = ext4_journal_start(dir, EXT4_HT_DIR, - (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + - EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + - EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb))); - if (IS_ERR(handle)) - return PTR_ERR(handle); - - if (IS_DIRSYNC(dir)) - ext4_handle_sync(handle); - - inode = ext4_new_inode(handle, dir, mode, &dentry->d_name, 0, NULL); + inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0, + NULL, EXT4_HT_DIR, credits); + handle = ext4_journal_current_handle(); err = PTR_ERR(inode); if (!IS_ERR(inode)) { inode->i_op = &ext4_file_inode_operations; inode->i_fop = &ext4_file_operations; ext4_set_aops(inode); err = ext4_add_nondir(handle, dentry, inode); + if (!err && IS_DIRSYNC(dir)) + ext4_handle_sync(handle); } - ext4_journal_stop(handle); + if (handle) + ext4_journal_stop(handle); if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) goto retry; return err; @@ -2291,32 +2289,30 @@ static int ext4_mknod(struct inode *dir, struct dentry *dentry, { handle_t *handle; struct inode *inode; - int err, retries = 0; + int err, credits, retries = 0; if (!new_valid_dev(rdev)) return -EINVAL; dquot_initialize(dir); + credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb)); retry: - handle = ext4_journal_start(dir, EXT4_HT_DIR, - (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + - EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + - EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb))); - if (IS_ERR(handle)) - return PTR_ERR(handle); - - if (IS_DIRSYNC(dir)) - ext4_handle_sync(handle); - - inode = ext4_new_inode(handle, dir, mode, &dentry->d_name, 0, NULL); + inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0, + NULL, EXT4_HT_DIR, credits); + handle = ext4_journal_current_handle(); err = PTR_ERR(inode); if (!IS_ERR(inode)) { init_special_inode(inode, inode->i_mode, rdev); inode->i_op = &ext4_special_inode_operations; err = ext4_add_nondir(handle, dentry, inode); + if (!err && IS_DIRSYNC(dir)) + ext4_handle_sync(handle); } - ext4_journal_stop(handle); + if (handle) + ext4_journal_stop(handle); if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) goto retry; return err; @@ -2408,26 +2404,21 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { handle_t *handle; struct inode *inode; - int err, retries = 0; + int err, credits, retries = 0; if (EXT4_DIR_LINK_MAX(dir)) return -EMLINK; dquot_initialize(dir); + credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb)); retry: - handle = ext4_journal_start(dir, EXT4_HT_DIR, - (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + - EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3 + - EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb))); - if (IS_ERR(handle)) - return PTR_ERR(handle); - - if (IS_DIRSYNC(dir)) - ext4_handle_sync(handle); - - inode = ext4_new_inode(handle, dir, S_IFDIR | mode, - &dentry->d_name, 0, NULL); + inode = ext4_new_inode_start_handle(dir, S_IFDIR | mode, + &dentry->d_name, + 0, NULL, EXT4_HT_DIR, credits); + handle = ext4_journal_current_handle(); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_stop; @@ -2455,8 +2446,12 @@ out_clear_inode: goto out_clear_inode; unlock_new_inode(inode); d_instantiate(dentry, inode); + if (IS_DIRSYNC(dir)) + ext4_handle_sync(handle); + out_stop: - ext4_journal_stop(handle); + if (handle) + ext4_journal_stop(handle); if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) goto retry; return err; @@ -2883,15 +2878,10 @@ static int ext4_symlink(struct inode *dir, EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb); } retry: - handle = ext4_journal_start(dir, EXT4_HT_DIR, credits); - if (IS_ERR(handle)) - return PTR_ERR(handle); - - if (IS_DIRSYNC(dir)) - ext4_handle_sync(handle); - - inode = ext4_new_inode(handle, dir, S_IFLNK|S_IRWXUGO, - &dentry->d_name, 0, NULL); + inode = ext4_new_inode_start_handle(dir, S_IFLNK|S_IRWXUGO, + &dentry->d_name, 0, NULL, + EXT4_HT_DIR, credits); + handle = ext4_journal_current_handle(); err = PTR_ERR(inode); if (IS_ERR(inode)) goto out_stop; @@ -2944,8 +2934,12 @@ retry: } EXT4_I(inode)->i_disksize = inode->i_size; err = ext4_add_nondir(handle, dentry, inode); + if (!err && IS_DIRSYNC(dir)) + ext4_handle_sync(handle); + out_stop: - ext4_journal_stop(handle); + if (handle) + ext4_journal_stop(handle); if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) goto retry; return err; From a0b30c12297eb63e9b994164f9c0937d29b9352d Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 9 Feb 2013 16:28:20 -0500 Subject: [PATCH 2214/4607] ext4: use module parameters instead of debugfs for mballoc_debug There are multiple reasons to move away from debugfs. First of all, we are only using it for a single parameter, and it is much more complicated to set up (some 30 lines of code compared to 3), and one more thing that might fail while loading the ext4 module. Secondly, as a module paramter it can be specified as a boot option if ext4 is built into the kernel, or as a parameter when the module is loaded, and it can also be manipulated dynamically under /sys/module/ext4/parameters/mballoc_debug. So it is more flexible. Ultimately we want to move away from using mb_debug() towards tracepoints, but for now this is still a useful simplification of the code base. Signed-off-by: "Theodore Ts'o" --- fs/ext4/mballoc.c | 47 +++++++++-------------------------------------- fs/ext4/mballoc.h | 4 ++-- 2 files changed, 11 insertions(+), 40 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index e350885aec30..6540ebe058e3 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -23,11 +23,18 @@ #include "ext4_jbd2.h" #include "mballoc.h" -#include #include +#include #include #include +#ifdef CONFIG_EXT4_DEBUG +ushort ext4_mballoc_debug __read_mostly; + +module_param_named(mballoc_debug, ext4_mballoc_debug, ushort, 0644); +MODULE_PARM_DESC(mballoc_debug, "Debugging level for ext4's mballoc"); +#endif + /* * MUSTDO: * - test ext4_ext_search_left() and ext4_ext_search_right() @@ -2660,40 +2667,6 @@ static void ext4_free_data_callback(struct super_block *sb, mb_debug(1, "freed %u blocks in %u structures\n", count, count2); } -#ifdef CONFIG_EXT4_DEBUG -u8 mb_enable_debug __read_mostly; - -static struct dentry *debugfs_dir; -static struct dentry *debugfs_debug; - -static void __init ext4_create_debugfs_entry(void) -{ - debugfs_dir = debugfs_create_dir("ext4", NULL); - if (debugfs_dir) - debugfs_debug = debugfs_create_u8("mballoc-debug", - S_IRUGO | S_IWUSR, - debugfs_dir, - &mb_enable_debug); -} - -static void ext4_remove_debugfs_entry(void) -{ - debugfs_remove(debugfs_debug); - debugfs_remove(debugfs_dir); -} - -#else - -static void __init ext4_create_debugfs_entry(void) -{ -} - -static void ext4_remove_debugfs_entry(void) -{ -} - -#endif - int __init ext4_init_mballoc(void) { ext4_pspace_cachep = KMEM_CACHE(ext4_prealloc_space, @@ -2715,7 +2688,6 @@ int __init ext4_init_mballoc(void) kmem_cache_destroy(ext4_ac_cachep); return -ENOMEM; } - ext4_create_debugfs_entry(); return 0; } @@ -2730,7 +2702,6 @@ void ext4_exit_mballoc(void) kmem_cache_destroy(ext4_ac_cachep); kmem_cache_destroy(ext4_free_data_cachep); ext4_groupinfo_destroy_slabs(); - ext4_remove_debugfs_entry(); } @@ -3876,7 +3847,7 @@ static void ext4_mb_show_ac(struct ext4_allocation_context *ac) struct super_block *sb = ac->ac_sb; ext4_group_t ngroups, i; - if (!mb_enable_debug || + if (!ext4_mballoc_debug || (EXT4_SB(sb)->s_mount_flags & EXT4_MF_FS_ABORTED)) return; diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h index 3ccd889ba953..08481ee84cd5 100644 --- a/fs/ext4/mballoc.h +++ b/fs/ext4/mballoc.h @@ -37,11 +37,11 @@ /* */ #ifdef CONFIG_EXT4_DEBUG -extern u8 mb_enable_debug; +extern ushort ext4_mballoc_debug; #define mb_debug(n, fmt, a...) \ do { \ - if ((n) <= mb_enable_debug) { \ + if ((n) <= ext4_mballoc_debug) { \ printk(KERN_DEBUG "(%s, %d): %s: ", \ __FILE__, __LINE__, __func__); \ printk(fmt, ## a); \ From b6e96d0067d81f6a300bedee661b5ece8164e210 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 9 Feb 2013 16:29:20 -0500 Subject: [PATCH 2215/4607] jbd2: use module parameters instead of debugfs for jbd_debug There are multiple reasons to move away from debugfs. First of all, we are only using it for a single parameter, and it is much more complicated to set up (some 30 lines of code compared to 3), and one more thing that might fail while loading the jbd2 module. Secondly, as a module paramter it can be specified as a boot option if jbd2 is built into the kernel, or as a parameter when the module is loaded, and it can also be manipulated dynamically under /sys/module/jbd2/parameters/jbd2_debug. So it is more flexible. Ultimately we want to move away from using jbd_debug() towards tracepoints, but for now this is still a useful simplification of the code base. Signed-off-by: "Theodore Ts'o" --- fs/jbd2/journal.c | 50 +++++++------------------------------------- include/linux/jbd2.h | 3 +-- 2 files changed, 9 insertions(+), 44 deletions(-) diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 4ba2e81e35ac..ed10991ab006 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -51,6 +50,14 @@ #include #include +#ifdef CONFIG_JBD2_DEBUG +ushort jbd2_journal_enable_debug __read_mostly; +EXPORT_SYMBOL(jbd2_journal_enable_debug); + +module_param_named(jbd2_debug, jbd2_journal_enable_debug, ushort, 0644); +MODULE_PARM_DESC(jbd2_debug, "Debugging level for jbd2"); +#endif + EXPORT_SYMBOL(jbd2_journal_extend); EXPORT_SYMBOL(jbd2_journal_stop); EXPORT_SYMBOL(jbd2_journal_lock_updates); @@ -2495,45 +2502,6 @@ restart: spin_unlock(&journal->j_list_lock); } -/* - * debugfs tunables - */ -#ifdef CONFIG_JBD2_DEBUG -u8 jbd2_journal_enable_debug __read_mostly; -EXPORT_SYMBOL(jbd2_journal_enable_debug); - -#define JBD2_DEBUG_NAME "jbd2-debug" - -static struct dentry *jbd2_debugfs_dir; -static struct dentry *jbd2_debug; - -static void __init jbd2_create_debugfs_entry(void) -{ - jbd2_debugfs_dir = debugfs_create_dir("jbd2", NULL); - if (jbd2_debugfs_dir) - jbd2_debug = debugfs_create_u8(JBD2_DEBUG_NAME, - S_IRUGO | S_IWUSR, - jbd2_debugfs_dir, - &jbd2_journal_enable_debug); -} - -static void __exit jbd2_remove_debugfs_entry(void) -{ - debugfs_remove(jbd2_debug); - debugfs_remove(jbd2_debugfs_dir); -} - -#else - -static void __init jbd2_create_debugfs_entry(void) -{ -} - -static void __exit jbd2_remove_debugfs_entry(void) -{ -} - -#endif #ifdef CONFIG_PROC_FS @@ -2619,7 +2587,6 @@ static int __init journal_init(void) ret = journal_init_caches(); if (ret == 0) { - jbd2_create_debugfs_entry(); jbd2_create_jbd_stats_proc_entry(); } else { jbd2_journal_destroy_caches(); @@ -2634,7 +2601,6 @@ static void __exit journal_exit(void) if (n) printk(KERN_EMERG "JBD2: leaked %d journal_heads!\n", n); #endif - jbd2_remove_debugfs_entry(); jbd2_remove_jbd_stats_proc_entry(); jbd2_journal_destroy_caches(); } diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index fa5fea17b619..50e5a5e6a712 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -20,7 +20,6 @@ #ifndef __KERNEL__ #include "jfs_compat.h" #define JBD2_DEBUG -#define jfs_debug jbd_debug #else #include @@ -57,7 +56,7 @@ * CONFIG_JBD2_DEBUG is on. */ #define JBD2_EXPENSIVE_CHECKING -extern u8 jbd2_journal_enable_debug; +extern ushort jbd2_journal_enable_debug; #define jbd_debug(n, f, a...) \ do { \ From 991eb5852bf28fbc63d000fa428f95eb47128ab2 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 25 Dec 2012 09:10:51 -0200 Subject: [PATCH 2216/4607] ARM: dts: imx27-3ds: Rename it to imx27-pdk imx27-pdk is the name found on Freescale website, so use it instead of 3ds. Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/{imx27-3ds.dts => imx27-pdk.dts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename arch/arm/boot/dts/{imx27-3ds.dts => imx27-pdk.dts} (86%) diff --git a/arch/arm/boot/dts/imx27-3ds.dts b/arch/arm/boot/dts/imx27-pdk.dts similarity index 86% rename from arch/arm/boot/dts/imx27-3ds.dts rename to arch/arm/boot/dts/imx27-pdk.dts index fa04c7b18bcb..0290978f080b 100644 --- a/arch/arm/boot/dts/imx27-3ds.dts +++ b/arch/arm/boot/dts/imx27-pdk.dts @@ -13,8 +13,8 @@ /include/ "imx27.dtsi" / { - model = "mx27_3ds"; - compatible = "freescale,imx27-3ds", "fsl,imx27"; + model = "Freescale i.MX27 Product Development Kit"; + compatible = "fsl,imx27-pdk", "fsl,imx27"; memory { reg = <0x0 0x0>; From 9c9651cd7412032657e4949dd5cbdd8f1364befd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 25 Dec 2012 09:10:52 -0200 Subject: [PATCH 2217/4607] ARM: boot: dts: Add an entry for imx27-pdk.dtb Add an entry for imx27-pdk.dtb, so that it can be generated by default. Also, add an entry into Documentation/devicetree/bindings/arm/fsl.txt. Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- Documentation/devicetree/bindings/arm/fsl.txt | 4 ++++ arch/arm/boot/dts/Makefile | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/arm/fsl.txt b/Documentation/devicetree/bindings/arm/fsl.txt index f79818711e83..4c25c987c75e 100644 --- a/Documentation/devicetree/bindings/arm/fsl.txt +++ b/Documentation/devicetree/bindings/arm/fsl.txt @@ -5,6 +5,10 @@ i.MX23 Evaluation Kit Required root node properties: - compatible = "fsl,imx23-evk", "fsl,imx23"; +i.MX27 Product Development Kit +Required root node properties: + - compatible = "fsl,imx27-pdk", "fsl,imx27"; + i.MX28 Evaluation Kit Required root node properties: - compatible = "fsl,imx28-evk", "fsl,imx28"; diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 5ebb44fe826a..c3638bd7bdff 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -79,7 +79,8 @@ dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \ armada-370-mirabox.dtb \ armada-xp-db.dtb \ armada-xp-openblocks-ax3-4.dtb -dtb-$(CONFIG_ARCH_MXC) += imx51-babbage.dtb \ +dtb-$(CONFIG_ARCH_MXC) += imx27-pdk.dtb \ + imx51-babbage.dtb \ imx53-ard.dtb \ imx53-evk.dtb \ imx53-qsb.dtb \ From 8ba472357a87503df2214ddc0270706ae34da4a1 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 31 Dec 2012 11:30:59 +0800 Subject: [PATCH 2218/4607] ARM: dts: add missing imx dtb targets Add missing imx dtb targets, so that make dtbs can cover all imx dtbs. It's pretty useful for testing dts changes. Signed-off-by: Shawn Guo --- arch/arm/boot/dts/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index c3638bd7bdff..f38304abcee8 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -79,7 +79,11 @@ dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \ armada-370-mirabox.dtb \ armada-xp-db.dtb \ armada-xp-openblocks-ax3-4.dtb -dtb-$(CONFIG_ARCH_MXC) += imx27-pdk.dtb \ +dtb-$(CONFIG_ARCH_MXC) += \ + imx25-karo-tx25.dtb \ + imx27-apf27.dtb \ + imx27-pdk.dtb \ + imx31-bug.dtb \ imx51-babbage.dtb \ imx53-ard.dtb \ imx53-evk.dtb \ From be4ccfcec3e958ef1eae59430e9962a514f34681 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 31 Dec 2012 11:32:48 +0800 Subject: [PATCH 2219/4607] ARM: dts: imx: use nodes label in board dts Following omap3-evm.dts way, it changes all imx dts files to use label in board dts to refer to nodes defined by soc dtsi. Thus, the board dts files become easier to read and edit with the least indentation levels. Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx25-karo-tx25.dts | 36 +- arch/arm/boot/dts/imx25.dtsi | 2 +- arch/arm/boot/dts/imx27-apf27.dts | 82 +++-- arch/arm/boot/dts/imx27-pdk.dts | 24 +- arch/arm/boot/dts/imx31-bug.dts | 14 +- arch/arm/boot/dts/imx51-babbage.dts | 456 +++++++++++++------------- arch/arm/boot/dts/imx53-ard.dts | 126 ++++--- arch/arm/boot/dts/imx53-evk.dts | 194 ++++++----- arch/arm/boot/dts/imx53-qsb.dts | 380 +++++++++++---------- arch/arm/boot/dts/imx53-smd.dts | 294 ++++++++--------- arch/arm/boot/dts/imx6q-arm2.dts | 124 ++++--- arch/arm/boot/dts/imx6q-sabreauto.dts | 68 ++-- arch/arm/boot/dts/imx6q-sabrelite.dts | 216 ++++++------ arch/arm/boot/dts/imx6q-sabresd.dts | 102 +++--- arch/arm/boot/dts/imx6q.dtsi | 2 +- 15 files changed, 1016 insertions(+), 1104 deletions(-) diff --git a/arch/arm/boot/dts/imx25-karo-tx25.dts b/arch/arm/boot/dts/imx25-karo-tx25.dts index d81f8a0b9794..eb9b9d1c8349 100644 --- a/arch/arm/boot/dts/imx25-karo-tx25.dts +++ b/arch/arm/boot/dts/imx25-karo-tx25.dts @@ -19,26 +19,18 @@ memory { reg = <0x80000000 0x02000000 0x90000000 0x02000000>; }; - - soc { - aips@43f00000 { - uart1: serial@43f90000 { - status = "okay"; - }; - }; - - spba@50000000 { - fec: ethernet@50038000 { - status = "okay"; - phy-mode = "rmii"; - }; - }; - - emi@80000000 { - nand@bb000000 { - nand-on-flash-bbt; - status = "okay"; - }; - }; - }; +}; + +&uart1 { + status = "okay"; +}; + +&fec { + status = "okay"; + phy-mode = "rmii"; +}; + +&nfc { + nand-on-flash-bbt; + status = "okay"; }; diff --git a/arch/arm/boot/dts/imx25.dtsi b/arch/arm/boot/dts/imx25.dtsi index e1b13ebc96d6..94f33059158a 100644 --- a/arch/arm/boot/dts/imx25.dtsi +++ b/arch/arm/boot/dts/imx25.dtsi @@ -499,7 +499,7 @@ reg = <0x80000000 0x3b002000>; ranges; - nand@bb000000 { + nfc: nand@bb000000 { #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/boot/dts/imx27-apf27.dts b/arch/arm/boot/dts/imx27-apf27.dts index c0327c054de2..b464c807d8d9 100644 --- a/arch/arm/boot/dts/imx27-apf27.dts +++ b/arch/arm/boot/dts/imx27-apf27.dts @@ -32,58 +32,54 @@ clock-frequency = <0>; }; }; +}; - soc { - aipi@10000000 { - serial@1000a000 { - status = "okay"; - }; +&uart1 { + status = "okay"; +}; - ethernet@1002b000 { - status = "okay"; - }; - }; +&fec { + status = "okay"; +}; - nand@d8000000 { - status = "okay"; - nand-bus-width = <16>; - nand-ecc-mode = "hw"; - nand-on-flash-bbt; +&nfc { + status = "okay"; + nand-bus-width = <16>; + nand-ecc-mode = "hw"; + nand-on-flash-bbt; - partition@0 { - label = "u-boot"; - reg = <0x0 0x100000>; - }; + partition@0 { + label = "u-boot"; + reg = <0x0 0x100000>; + }; - partition@100000 { - label = "env"; - reg = <0x100000 0x80000>; - }; + partition@100000 { + label = "env"; + reg = <0x100000 0x80000>; + }; - partition@180000 { - label = "env2"; - reg = <0x180000 0x80000>; - }; + partition@180000 { + label = "env2"; + reg = <0x180000 0x80000>; + }; - partition@200000 { - label = "firmware"; - reg = <0x200000 0x80000>; - }; + partition@200000 { + label = "firmware"; + reg = <0x200000 0x80000>; + }; - partition@280000 { - label = "dtb"; - reg = <0x280000 0x80000>; - }; + partition@280000 { + label = "dtb"; + reg = <0x280000 0x80000>; + }; - partition@300000 { - label = "kernel"; - reg = <0x300000 0x500000>; - }; + partition@300000 { + label = "kernel"; + reg = <0x300000 0x500000>; + }; - partition@800000 { - label = "rootfs"; - reg = <0x800000 0xf800000>; - }; - }; + partition@800000 { + label = "rootfs"; + reg = <0x800000 0xf800000>; }; }; diff --git a/arch/arm/boot/dts/imx27-pdk.dts b/arch/arm/boot/dts/imx27-pdk.dts index 0290978f080b..41cd1105608e 100644 --- a/arch/arm/boot/dts/imx27-pdk.dts +++ b/arch/arm/boot/dts/imx27-pdk.dts @@ -19,19 +19,13 @@ memory { reg = <0x0 0x0>; }; - - soc { - aipi@10000000 { /* aipi1 */ - uart1: serial@1000a000 { - fsl,uart-has-rtscts; - status = "okay"; - }; - }; - - aipi@10020000 { /* aipi2 */ - ethernet@1002b000 { - status = "okay"; - }; - }; - }; +}; + +&uart1 { + fsl,uart-has-rtscts; + status = "okay"; +}; + +&fec { + status = "okay"; }; diff --git a/arch/arm/boot/dts/imx31-bug.dts b/arch/arm/boot/dts/imx31-bug.dts index 7f67402328d3..9ac6f6ba1d64 100644 --- a/arch/arm/boot/dts/imx31-bug.dts +++ b/arch/arm/boot/dts/imx31-bug.dts @@ -19,13 +19,9 @@ memory { reg = <0x80000000 0x8000000>; /* 128M */ }; - - soc { - aips@43f00000 { /* AIPS1 */ - uart5: serial@43fb4000 { - fsl,uart-has-rtscts; - status = "okay"; - }; - }; - }; +}; + +&uart5 { + fsl,uart-has-rtscts; + status = "okay"; }; diff --git a/arch/arm/boot/dts/imx51-babbage.dts b/arch/arm/boot/dts/imx51-babbage.dts index 567e7ee72f91..fb33aff5b4b9 100644 --- a/arch/arm/boot/dts/imx51-babbage.dts +++ b/arch/arm/boot/dts/imx51-babbage.dts @@ -21,239 +21,20 @@ reg = <0x90000000 0x20000000>; }; - soc { - display@di0 { - compatible = "fsl,imx-parallel-display"; - crtcs = <&ipu 0>; - interface-pix-fmt = "rgb24"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ipu_disp1_1>; - }; + display@di0 { + compatible = "fsl,imx-parallel-display"; + crtcs = <&ipu 0>; + interface-pix-fmt = "rgb24"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu_disp1_1>; + }; - display@di1 { - compatible = "fsl,imx-parallel-display"; - crtcs = <&ipu 1>; - interface-pix-fmt = "rgb565"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ipu_disp2_1>; - }; - - aips@70000000 { /* aips-1 */ - spba@70000000 { - esdhc@70004000 { /* ESDHC1 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_esdhc1_1>; - fsl,cd-controller; - fsl,wp-controller; - status = "okay"; - }; - - esdhc@70008000 { /* ESDHC2 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_esdhc2_1>; - cd-gpios = <&gpio1 6 0>; - wp-gpios = <&gpio1 5 0>; - status = "okay"; - }; - - uart3: serial@7000c000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart3_1>; - fsl,uart-has-rtscts; - status = "okay"; - }; - - ecspi@70010000 { /* ECSPI1 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ecspi1_1>; - fsl,spi-num-chipselects = <2>; - cs-gpios = <&gpio4 24 0>, <&gpio4 25 0>; - status = "okay"; - - pmic: mc13892@0 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,mc13892"; - spi-max-frequency = <6000000>; - reg = <0>; - interrupt-parent = <&gpio1>; - interrupts = <8 0x4>; - - regulators { - sw1_reg: sw1 { - regulator-min-microvolt = <600000>; - regulator-max-microvolt = <1375000>; - regulator-boot-on; - regulator-always-on; - }; - - sw2_reg: sw2 { - regulator-min-microvolt = <900000>; - regulator-max-microvolt = <1850000>; - regulator-boot-on; - regulator-always-on; - }; - - sw3_reg: sw3 { - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1850000>; - regulator-boot-on; - regulator-always-on; - }; - - sw4_reg: sw4 { - regulator-min-microvolt = <1100000>; - regulator-max-microvolt = <1850000>; - regulator-boot-on; - regulator-always-on; - }; - - vpll_reg: vpll { - regulator-min-microvolt = <1050000>; - regulator-max-microvolt = <1800000>; - regulator-boot-on; - regulator-always-on; - }; - - vdig_reg: vdig { - regulator-min-microvolt = <1650000>; - regulator-max-microvolt = <1650000>; - regulator-boot-on; - }; - - vsd_reg: vsd { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <3150000>; - }; - - vusb2_reg: vusb2 { - regulator-min-microvolt = <2400000>; - regulator-max-microvolt = <2775000>; - regulator-boot-on; - regulator-always-on; - }; - - vvideo_reg: vvideo { - regulator-min-microvolt = <2775000>; - regulator-max-microvolt = <2775000>; - }; - - vaudio_reg: vaudio { - regulator-min-microvolt = <2300000>; - regulator-max-microvolt = <3000000>; - }; - - vcam_reg: vcam { - regulator-min-microvolt = <2500000>; - regulator-max-microvolt = <3000000>; - }; - - vgen1_reg: vgen1 { - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <1200000>; - }; - - vgen2_reg: vgen2 { - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <3150000>; - regulator-always-on; - }; - - vgen3_reg: vgen3 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <2900000>; - regulator-always-on; - }; - }; - }; - - flash: at45db321d@1 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "atmel,at45db321d", "atmel,at45", "atmel,dataflash"; - spi-max-frequency = <25000000>; - reg = <1>; - - partition@0 { - label = "U-Boot"; - reg = <0x0 0x40000>; - read-only; - }; - - partition@40000 { - label = "Kernel"; - reg = <0x40000 0x3c0000>; - }; - }; - }; - - ssi2: ssi@70014000 { - fsl,mode = "i2s-slave"; - status = "okay"; - }; - }; - - iomuxc@73fa8000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; - - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - 694 0x20d5 /* MX51_PAD_GPIO1_0__SD1_CD */ - 697 0x20d5 /* MX51_PAD_GPIO1_1__SD1_WP */ - 737 0x100 /* MX51_PAD_GPIO1_5__GPIO1_5 */ - 740 0x100 /* MX51_PAD_GPIO1_6__GPIO1_6 */ - 121 0x5 /* MX51_PAD_EIM_A27__GPIO2_21 */ - 402 0x85 /* MX51_PAD_CSPI1_SS0__GPIO4_24 */ - 405 0x85 /* MX51_PAD_CSPI1_SS1__GPIO4_25 */ - >; - }; - }; - }; - - uart1: serial@73fbc000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart1_1>; - fsl,uart-has-rtscts; - status = "okay"; - }; - - uart2: serial@73fc0000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart2_1>; - status = "okay"; - }; - }; - - aips@80000000 { /* aips-2 */ - i2c@83fc4000 { /* I2C2 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c2_1>; - status = "okay"; - - sgtl5000: codec@0a { - compatible = "fsl,sgtl5000"; - reg = <0x0a>; - clock-frequency = <26000000>; - VDDA-supply = <&vdig_reg>; - VDDIO-supply = <&vvideo_reg>; - }; - }; - - audmux@83fd0000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_audmux_1>; - status = "okay"; - }; - - ethernet@83fec000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_fec_1>; - phy-mode = "mii"; - status = "okay"; - }; - }; + display@di1 { + compatible = "fsl,imx-parallel-display"; + crtcs = <&ipu 1>; + interface-pix-fmt = "rgb565"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ipu_disp2_1>; }; gpio-keys { @@ -281,3 +62,214 @@ mux-ext-port = <3>; }; }; + +&esdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc1_1>; + fsl,cd-controller; + fsl,wp-controller; + status = "okay"; +}; + +&esdhc2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc2_1>; + cd-gpios = <&gpio1 6 0>; + wp-gpios = <&gpio1 5 0>; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3_1>; + fsl,uart-has-rtscts; + status = "okay"; +}; + +&ecspi1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1_1>; + fsl,spi-num-chipselects = <2>; + cs-gpios = <&gpio4 24 0>, <&gpio4 25 0>; + status = "okay"; + + pmic: mc13892@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,mc13892"; + spi-max-frequency = <6000000>; + reg = <0>; + interrupt-parent = <&gpio1>; + interrupts = <8 0x4>; + + regulators { + sw1_reg: sw1 { + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1375000>; + regulator-boot-on; + regulator-always-on; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <900000>; + regulator-max-microvolt = <1850000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3_reg: sw3 { + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1850000>; + regulator-boot-on; + regulator-always-on; + }; + + sw4_reg: sw4 { + regulator-min-microvolt = <1100000>; + regulator-max-microvolt = <1850000>; + regulator-boot-on; + regulator-always-on; + }; + + vpll_reg: vpll { + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + vdig_reg: vdig { + regulator-min-microvolt = <1650000>; + regulator-max-microvolt = <1650000>; + regulator-boot-on; + }; + + vsd_reg: vsd { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3150000>; + }; + + vusb2_reg: vusb2 { + regulator-min-microvolt = <2400000>; + regulator-max-microvolt = <2775000>; + regulator-boot-on; + regulator-always-on; + }; + + vvideo_reg: vvideo { + regulator-min-microvolt = <2775000>; + regulator-max-microvolt = <2775000>; + }; + + vaudio_reg: vaudio { + regulator-min-microvolt = <2300000>; + regulator-max-microvolt = <3000000>; + }; + + vcam_reg: vcam { + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <3000000>; + }; + + vgen1_reg: vgen1 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + vgen2_reg: vgen2 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3150000>; + regulator-always-on; + }; + + vgen3_reg: vgen3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2900000>; + regulator-always-on; + }; + }; + }; + + flash: at45db321d@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "atmel,at45db321d", "atmel,at45", "atmel,dataflash"; + spi-max-frequency = <25000000>; + reg = <1>; + + partition@0 { + label = "U-Boot"; + reg = <0x0 0x40000>; + read-only; + }; + + partition@40000 { + label = "Kernel"; + reg = <0x40000 0x3c0000>; + }; + }; +}; + +&ssi2 { + fsl,mode = "i2s-slave"; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + 694 0x20d5 /* MX51_PAD_GPIO1_0__SD1_CD */ + 697 0x20d5 /* MX51_PAD_GPIO1_1__SD1_WP */ + 737 0x100 /* MX51_PAD_GPIO1_5__GPIO1_5 */ + 740 0x100 /* MX51_PAD_GPIO1_6__GPIO1_6 */ + 121 0x5 /* MX51_PAD_EIM_A27__GPIO2_21 */ + 402 0x85 /* MX51_PAD_CSPI1_SS0__GPIO4_24 */ + 405 0x85 /* MX51_PAD_CSPI1_SS1__GPIO4_25 */ + >; + }; + }; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1_1>; + fsl,uart-has-rtscts; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2_1>; + status = "okay"; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2_1>; + status = "okay"; + + sgtl5000: codec@0a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + clock-frequency = <26000000>; + VDDA-supply = <&vdig_reg>; + VDDIO-supply = <&vvideo_reg>; + }; +}; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux_1>; + status = "okay"; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec_1>; + phy-mode = "mii"; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx53-ard.dts b/arch/arm/boot/dts/imx53-ard.dts index 4be76f223526..e049fd0319e8 100644 --- a/arch/arm/boot/dts/imx53-ard.dts +++ b/arch/arm/boot/dts/imx53-ard.dts @@ -21,72 +21,6 @@ reg = <0x70000000 0x40000000>; }; - soc { - aips@50000000 { /* AIPS1 */ - spba@50000000 { - esdhc@50004000 { /* ESDHC1 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_esdhc1_2>; - cd-gpios = <&gpio1 1 0>; - wp-gpios = <&gpio1 9 0>; - status = "okay"; - }; - }; - - iomuxc@53fa8000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; - - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - 1077 0x80000000 /* MX53_PAD_GPIO_1__GPIO1_1 */ - 1085 0x80000000 /* MX53_PAD_GPIO_9__GPIO1_9 */ - 486 0x80000000 /* MX53_PAD_EIM_EB3__GPIO2_31 */ - 739 0x80000000 /* MX53_PAD_GPIO_10__GPIO4_0 */ - 218 0x80000000 /* MX53_PAD_DISP0_DAT16__GPIO5_10 */ - 226 0x80000000 /* MX53_PAD_DISP0_DAT17__GPIO5_11 */ - 233 0x80000000 /* MX53_PAD_DISP0_DAT18__GPIO5_12 */ - 241 0x80000000 /* MX53_PAD_DISP0_DAT19__GPIO5_13 */ - 429 0x80000000 /* MX53_PAD_EIM_D16__EMI_WEIM_D_16 */ - 435 0x80000000 /* MX53_PAD_EIM_D17__EMI_WEIM_D_17 */ - 441 0x80000000 /* MX53_PAD_EIM_D18__EMI_WEIM_D_18 */ - 448 0x80000000 /* MX53_PAD_EIM_D19__EMI_WEIM_D_19 */ - 456 0x80000000 /* MX53_PAD_EIM_D20__EMI_WEIM_D_20 */ - 464 0x80000000 /* MX53_PAD_EIM_D21__EMI_WEIM_D_21 */ - 471 0x80000000 /* MX53_PAD_EIM_D22__EMI_WEIM_D_22 */ - 477 0x80000000 /* MX53_PAD_EIM_D23__EMI_WEIM_D_23 */ - 492 0x80000000 /* MX53_PAD_EIM_D24__EMI_WEIM_D_24 */ - 500 0x80000000 /* MX53_PAD_EIM_D25__EMI_WEIM_D_25 */ - 508 0x80000000 /* MX53_PAD_EIM_D26__EMI_WEIM_D_26 */ - 516 0x80000000 /* MX53_PAD_EIM_D27__EMI_WEIM_D_27 */ - 524 0x80000000 /* MX53_PAD_EIM_D28__EMI_WEIM_D_28 */ - 532 0x80000000 /* MX53_PAD_EIM_D29__EMI_WEIM_D_29 */ - 540 0x80000000 /* MX53_PAD_EIM_D30__EMI_WEIM_D_30 */ - 548 0x80000000 /* MX53_PAD_EIM_D31__EMI_WEIM_D_31 */ - 637 0x80000000 /* MX53_PAD_EIM_DA0__EMI_NAND_WEIM_DA_0 */ - 642 0x80000000 /* MX53_PAD_EIM_DA1__EMI_NAND_WEIM_DA_1 */ - 647 0x80000000 /* MX53_PAD_EIM_DA2__EMI_NAND_WEIM_DA_2 */ - 652 0x80000000 /* MX53_PAD_EIM_DA3__EMI_NAND_WEIM_DA_3 */ - 657 0x80000000 /* MX53_PAD_EIM_DA4__EMI_NAND_WEIM_DA_4 */ - 662 0x80000000 /* MX53_PAD_EIM_DA5__EMI_NAND_WEIM_DA_5 */ - 667 0x80000000 /* MX53_PAD_EIM_DA6__EMI_NAND_WEIM_DA_6 */ - 611 0x80000000 /* MX53_PAD_EIM_OE__EMI_WEIM_OE */ - 616 0x80000000 /* MX53_PAD_EIM_RW__EMI_WEIM_RW */ - 607 0x80000000 /* MX53_PAD_EIM_CS1__EMI_WEIM_CS_1 */ - >; - }; - }; - }; - - uart1: serial@53fbc000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart1_2>; - status = "okay"; - }; - }; - }; - eim-cs1@f4000000 { #address-cells = <1>; #size-cells = <1>; @@ -162,3 +96,63 @@ }; }; }; + +&esdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc1_2>; + cd-gpios = <&gpio1 1 0>; + wp-gpios = <&gpio1 9 0>; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + 1077 0x80000000 /* MX53_PAD_GPIO_1__GPIO1_1 */ + 1085 0x80000000 /* MX53_PAD_GPIO_9__GPIO1_9 */ + 486 0x80000000 /* MX53_PAD_EIM_EB3__GPIO2_31 */ + 739 0x80000000 /* MX53_PAD_GPIO_10__GPIO4_0 */ + 218 0x80000000 /* MX53_PAD_DISP0_DAT16__GPIO5_10 */ + 226 0x80000000 /* MX53_PAD_DISP0_DAT17__GPIO5_11 */ + 233 0x80000000 /* MX53_PAD_DISP0_DAT18__GPIO5_12 */ + 241 0x80000000 /* MX53_PAD_DISP0_DAT19__GPIO5_13 */ + 429 0x80000000 /* MX53_PAD_EIM_D16__EMI_WEIM_D_16 */ + 435 0x80000000 /* MX53_PAD_EIM_D17__EMI_WEIM_D_17 */ + 441 0x80000000 /* MX53_PAD_EIM_D18__EMI_WEIM_D_18 */ + 448 0x80000000 /* MX53_PAD_EIM_D19__EMI_WEIM_D_19 */ + 456 0x80000000 /* MX53_PAD_EIM_D20__EMI_WEIM_D_20 */ + 464 0x80000000 /* MX53_PAD_EIM_D21__EMI_WEIM_D_21 */ + 471 0x80000000 /* MX53_PAD_EIM_D22__EMI_WEIM_D_22 */ + 477 0x80000000 /* MX53_PAD_EIM_D23__EMI_WEIM_D_23 */ + 492 0x80000000 /* MX53_PAD_EIM_D24__EMI_WEIM_D_24 */ + 500 0x80000000 /* MX53_PAD_EIM_D25__EMI_WEIM_D_25 */ + 508 0x80000000 /* MX53_PAD_EIM_D26__EMI_WEIM_D_26 */ + 516 0x80000000 /* MX53_PAD_EIM_D27__EMI_WEIM_D_27 */ + 524 0x80000000 /* MX53_PAD_EIM_D28__EMI_WEIM_D_28 */ + 532 0x80000000 /* MX53_PAD_EIM_D29__EMI_WEIM_D_29 */ + 540 0x80000000 /* MX53_PAD_EIM_D30__EMI_WEIM_D_30 */ + 548 0x80000000 /* MX53_PAD_EIM_D31__EMI_WEIM_D_31 */ + 637 0x80000000 /* MX53_PAD_EIM_DA0__EMI_NAND_WEIM_DA_0 */ + 642 0x80000000 /* MX53_PAD_EIM_DA1__EMI_NAND_WEIM_DA_1 */ + 647 0x80000000 /* MX53_PAD_EIM_DA2__EMI_NAND_WEIM_DA_2 */ + 652 0x80000000 /* MX53_PAD_EIM_DA3__EMI_NAND_WEIM_DA_3 */ + 657 0x80000000 /* MX53_PAD_EIM_DA4__EMI_NAND_WEIM_DA_4 */ + 662 0x80000000 /* MX53_PAD_EIM_DA5__EMI_NAND_WEIM_DA_5 */ + 667 0x80000000 /* MX53_PAD_EIM_DA6__EMI_NAND_WEIM_DA_6 */ + 611 0x80000000 /* MX53_PAD_EIM_OE__EMI_WEIM_OE */ + 616 0x80000000 /* MX53_PAD_EIM_RW__EMI_WEIM_RW */ + 607 0x80000000 /* MX53_PAD_EIM_CS1__EMI_WEIM_CS_1 */ + >; + }; + }; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1_2>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx53-evk.dts b/arch/arm/boot/dts/imx53-evk.dts index a124d1e25258..85a89b52f9b8 100644 --- a/arch/arm/boot/dts/imx53-evk.dts +++ b/arch/arm/boot/dts/imx53-evk.dts @@ -21,107 +21,6 @@ reg = <0x70000000 0x80000000>; }; - soc { - aips@50000000 { /* AIPS1 */ - spba@50000000 { - esdhc@50004000 { /* ESDHC1 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_esdhc1_1>; - cd-gpios = <&gpio3 13 0>; - wp-gpios = <&gpio3 14 0>; - status = "okay"; - }; - - ecspi@50010000 { /* ECSPI1 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ecspi1_1>; - fsl,spi-num-chipselects = <2>; - cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>; - status = "okay"; - - flash: at45db321d@1 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "atmel,at45db321d", "atmel,at45", "atmel,dataflash"; - spi-max-frequency = <25000000>; - reg = <1>; - - partition@0 { - label = "U-Boot"; - reg = <0x0 0x40000>; - read-only; - }; - - partition@40000 { - label = "Kernel"; - reg = <0x40000 0x3c0000>; - }; - }; - }; - - esdhc@50020000 { /* ESDHC3 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_esdhc3_1>; - cd-gpios = <&gpio3 11 0>; - wp-gpios = <&gpio3 12 0>; - status = "okay"; - }; - }; - - iomuxc@53fa8000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; - - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - 424 0x80000000 /* MX53_PAD_EIM_EB2__GPIO2_30 */ - 449 0x80000000 /* MX53_PAD_EIM_D19__GPIO3_19 */ - 693 0x80000000 /* MX53_PAD_EIM_DA11__GPIO3_11 */ - 697 0x80000000 /* MX53_PAD_EIM_DA12__GPIO3_12 */ - 701 0x80000000 /* MX53_PAD_EIM_DA13__GPIO3_13 */ - 705 0x80000000 /* MX53_PAD_EIM_DA14__GPIO3_14 */ - 868 0x80000000 /* MX53_PAD_PATA_DA_0__GPIO7_6 */ - 873 0x80000000 /* MX53_PAD_PATA_DA_1__GPIO7_7 */ - >; - }; - }; - }; - - uart1: serial@53fbc000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart1_1>; - status = "okay"; - }; - }; - - aips@60000000 { /* AIPS2 */ - i2c@63fc4000 { /* I2C2 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c2_1>; - status = "okay"; - - pmic: mc13892@08 { - compatible = "fsl,mc13892", "fsl,mc13xxx"; - reg = <0x08>; - }; - - codec: sgtl5000@0a { - compatible = "fsl,sgtl5000"; - reg = <0x0a>; - }; - }; - - ethernet@63fec000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_fec_1>; - phy-mode = "rmii"; - phy-reset-gpios = <&gpio7 6 0>; - status = "okay"; - }; - }; - }; - leds { compatible = "gpio-leds"; @@ -132,3 +31,96 @@ }; }; }; + +&esdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc1_1>; + cd-gpios = <&gpio3 13 0>; + wp-gpios = <&gpio3 14 0>; + status = "okay"; +}; + +&ecspi1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1_1>; + fsl,spi-num-chipselects = <2>; + cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>; + status = "okay"; + + flash: at45db321d@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "atmel,at45db321d", "atmel,at45", "atmel,dataflash"; + spi-max-frequency = <25000000>; + reg = <1>; + + partition@0 { + label = "U-Boot"; + reg = <0x0 0x40000>; + read-only; + }; + + partition@40000 { + label = "Kernel"; + reg = <0x40000 0x3c0000>; + }; + }; +}; + +&esdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc3_1>; + cd-gpios = <&gpio3 11 0>; + wp-gpios = <&gpio3 12 0>; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + 424 0x80000000 /* MX53_PAD_EIM_EB2__GPIO2_30 */ + 449 0x80000000 /* MX53_PAD_EIM_D19__GPIO3_19 */ + 693 0x80000000 /* MX53_PAD_EIM_DA11__GPIO3_11 */ + 697 0x80000000 /* MX53_PAD_EIM_DA12__GPIO3_12 */ + 701 0x80000000 /* MX53_PAD_EIM_DA13__GPIO3_13 */ + 705 0x80000000 /* MX53_PAD_EIM_DA14__GPIO3_14 */ + 868 0x80000000 /* MX53_PAD_PATA_DA_0__GPIO7_6 */ + 873 0x80000000 /* MX53_PAD_PATA_DA_1__GPIO7_7 */ + >; + }; + }; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1_1>; + status = "okay"; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2_1>; + status = "okay"; + + pmic: mc13892@08 { + compatible = "fsl,mc13892", "fsl,mc13xxx"; + reg = <0x08>; + }; + + codec: sgtl5000@0a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + }; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec_1>; + phy-mode = "rmii"; + phy-reset-gpios = <&gpio7 6 0>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx53-qsb.dts b/arch/arm/boot/dts/imx53-qsb.dts index b0075537195b..05cc5620436b 100644 --- a/arch/arm/boot/dts/imx53-qsb.dts +++ b/arch/arm/boot/dts/imx53-qsb.dts @@ -21,200 +21,6 @@ reg = <0x70000000 0x40000000>; }; - soc { - aips@50000000 { /* AIPS1 */ - spba@50000000 { - esdhc@50004000 { /* ESDHC1 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_esdhc1_1>; - cd-gpios = <&gpio3 13 0>; - status = "okay"; - }; - - ssi2: ssi@50014000 { - fsl,mode = "i2s-slave"; - status = "okay"; - }; - - esdhc@50020000 { /* ESDHC3 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_esdhc3_1>; - cd-gpios = <&gpio3 11 0>; - wp-gpios = <&gpio3 12 0>; - status = "okay"; - }; - }; - - iomuxc@53fa8000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; - - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - 1071 0x80000000 /* MX53_PAD_GPIO_0__CCM_SSI_EXT1_CLK */ - 1141 0x80000000 /* MX53_PAD_GPIO_8__GPIO1_8 */ - 982 0x80000000 /* MX53_PAD_PATA_DATA14__GPIO2_14 */ - 989 0x80000000 /* MX53_PAD_PATA_DATA15__GPIO2_15 */ - 693 0x80000000 /* MX53_PAD_EIM_DA11__GPIO3_11 */ - 697 0x80000000 /* MX53_PAD_EIM_DA12__GPIO3_12 */ - 701 0x80000000 /* MX53_PAD_EIM_DA13__GPIO3_13 */ - 868 0x80000000 /* MX53_PAD_PATA_DA_0__GPIO7_6 */ - 1149 0x80000000 /* MX53_PAD_GPIO_16__GPIO7_11 */ - >; - }; - - led_pin_gpio7_7: led_gpio7_7@0 { - fsl,pins = < - 873 0x80000000 /* MX53_PAD_PATA_DA_1__GPIO7_7 */ - >; - }; - }; - - }; - - uart1: serial@53fbc000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart1_1>; - status = "okay"; - }; - }; - - aips@60000000 { /* AIPS2 */ - i2c@63fc4000 { /* I2C2 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c2_1>; - status = "okay"; - - sgtl5000: codec@0a { - compatible = "fsl,sgtl5000"; - reg = <0x0a>; - VDDA-supply = <®_3p2v>; - VDDIO-supply = <®_3p2v>; - }; - }; - - i2c@63fc8000 { /* I2C1 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c1_1>; - status = "okay"; - - accelerometer: mma8450@1c { - compatible = "fsl,mma8450"; - reg = <0x1c>; - }; - - pmic: dialog@48 { - compatible = "dlg,da9053-aa", "dlg,da9052"; - reg = <0x48>; - interrupt-parent = <&gpio7>; - interrupts = <11 0x8>; /* low-level active IRQ at GPIO7_11 */ - - regulators { - buck1_reg: buck1 { - regulator-min-microvolt = <500000>; - regulator-max-microvolt = <2075000>; - regulator-always-on; - }; - - buck2_reg: buck2 { - regulator-min-microvolt = <500000>; - regulator-max-microvolt = <2075000>; - regulator-always-on; - }; - - buck3_reg: buck3 { - regulator-min-microvolt = <925000>; - regulator-max-microvolt = <2500000>; - regulator-always-on; - }; - - buck4_reg: buck4 { - regulator-min-microvolt = <925000>; - regulator-max-microvolt = <2500000>; - regulator-always-on; - }; - - ldo1_reg: ldo1 { - regulator-min-microvolt = <600000>; - regulator-max-microvolt = <1800000>; - regulator-boot-on; - regulator-always-on; - }; - - ldo2_reg: ldo2 { - regulator-min-microvolt = <600000>; - regulator-max-microvolt = <1800000>; - regulator-always-on; - }; - - ldo3_reg: ldo3 { - regulator-min-microvolt = <600000>; - regulator-max-microvolt = <1800000>; - regulator-always-on; - }; - - ldo4_reg: ldo4 { - regulator-min-microvolt = <1725000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - ldo5_reg: ldo5 { - regulator-min-microvolt = <1725000>; - regulator-max-microvolt = <3300000>; - regulator-always-on; - }; - - ldo6_reg: ldo6 { - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <3600000>; - regulator-always-on; - }; - - ldo7_reg: ldo7 { - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <3600000>; - regulator-always-on; - }; - - ldo8_reg: ldo8 { - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <3600000>; - regulator-always-on; - }; - - ldo9_reg: ldo9 { - regulator-min-microvolt = <1200000>; - regulator-max-microvolt = <3600000>; - regulator-always-on; - }; - - ldo10_reg: ldo10 { - regulator-min-microvolt = <1250000>; - regulator-max-microvolt = <3650000>; - regulator-always-on; - }; - }; - }; - }; - - audmux@63fd0000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_audmux_1>; - status = "okay"; - }; - - ethernet@63fec000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_fec_1>; - phy-mode = "rmii"; - phy-reset-gpios = <&gpio7 6 0>; - status = "okay"; - }; - }; - }; - gpio-keys { compatible = "gpio-keys"; @@ -276,3 +82,189 @@ mux-ext-port = <5>; }; }; + +&esdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc1_1>; + cd-gpios = <&gpio3 13 0>; + status = "okay"; +}; + +&ssi2 { + fsl,mode = "i2s-slave"; + status = "okay"; +}; + +&esdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc3_1>; + cd-gpios = <&gpio3 11 0>; + wp-gpios = <&gpio3 12 0>; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + 1071 0x80000000 /* MX53_PAD_GPIO_0__CCM_SSI_EXT1_CLK */ + 1141 0x80000000 /* MX53_PAD_GPIO_8__GPIO1_8 */ + 982 0x80000000 /* MX53_PAD_PATA_DATA14__GPIO2_14 */ + 989 0x80000000 /* MX53_PAD_PATA_DATA15__GPIO2_15 */ + 693 0x80000000 /* MX53_PAD_EIM_DA11__GPIO3_11 */ + 697 0x80000000 /* MX53_PAD_EIM_DA12__GPIO3_12 */ + 701 0x80000000 /* MX53_PAD_EIM_DA13__GPIO3_13 */ + 868 0x80000000 /* MX53_PAD_PATA_DA_0__GPIO7_6 */ + 1149 0x80000000 /* MX53_PAD_GPIO_16__GPIO7_11 */ + >; + }; + + led_pin_gpio7_7: led_gpio7_7@0 { + fsl,pins = < + 873 0x80000000 /* MX53_PAD_PATA_DA_1__GPIO7_7 */ + >; + }; + }; + +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1_1>; + status = "okay"; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2_1>; + status = "okay"; + + sgtl5000: codec@0a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + VDDA-supply = <®_3p2v>; + VDDIO-supply = <®_3p2v>; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1_1>; + status = "okay"; + + accelerometer: mma8450@1c { + compatible = "fsl,mma8450"; + reg = <0x1c>; + }; + + pmic: dialog@48 { + compatible = "dlg,da9053-aa", "dlg,da9052"; + reg = <0x48>; + interrupt-parent = <&gpio7>; + interrupts = <11 0x8>; /* low-level active IRQ at GPIO7_11 */ + + regulators { + buck1_reg: buck1 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <2075000>; + regulator-always-on; + }; + + buck2_reg: buck2 { + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <2075000>; + regulator-always-on; + }; + + buck3_reg: buck3 { + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <2500000>; + regulator-always-on; + }; + + buck4_reg: buck4 { + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <2500000>; + regulator-always-on; + }; + + ldo1_reg: ldo1 { + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo2_reg: ldo2 { + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo3_reg: ldo3 { + regulator-min-microvolt = <600000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo4_reg: ldo4 { + regulator-min-microvolt = <1725000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + ldo5_reg: ldo5 { + regulator-min-microvolt = <1725000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + ldo6_reg: ldo6 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3600000>; + regulator-always-on; + }; + + ldo7_reg: ldo7 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3600000>; + regulator-always-on; + }; + + ldo8_reg: ldo8 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3600000>; + regulator-always-on; + }; + + ldo9_reg: ldo9 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3600000>; + regulator-always-on; + }; + + ldo10_reg: ldo10 { + regulator-min-microvolt = <1250000>; + regulator-max-microvolt = <3650000>; + regulator-always-on; + }; + }; + }; +}; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux_1>; + status = "okay"; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec_1>; + phy-mode = "rmii"; + phy-reset-gpios = <&gpio7 6 0>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx53-smd.dts b/arch/arm/boot/dts/imx53-smd.dts index 06c68580c842..995554c324b8 100644 --- a/arch/arm/boot/dts/imx53-smd.dts +++ b/arch/arm/boot/dts/imx53-smd.dts @@ -21,157 +21,6 @@ reg = <0x70000000 0x40000000>; }; - soc { - aips@50000000 { /* AIPS1 */ - spba@50000000 { - esdhc@50004000 { /* ESDHC1 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_esdhc1_1>; - cd-gpios = <&gpio3 13 0>; - wp-gpios = <&gpio4 11 0>; - status = "okay"; - }; - - esdhc@50008000 { /* ESDHC2 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_esdhc2_1>; - non-removable; - status = "okay"; - }; - - uart3: serial@5000c000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart3_1>; - fsl,uart-has-rtscts; - status = "okay"; - }; - - ecspi@50010000 { /* ECSPI1 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ecspi1_1>; - fsl,spi-num-chipselects = <2>; - cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>; - status = "okay"; - - zigbee: mc1323@0 { - compatible = "fsl,mc1323"; - spi-max-frequency = <8000000>; - reg = <0>; - }; - - flash: m25p32@1 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "st,m25p32", "st,m25p"; - spi-max-frequency = <20000000>; - reg = <1>; - - partition@0 { - label = "U-Boot"; - reg = <0x0 0x40000>; - read-only; - }; - - partition@40000 { - label = "Kernel"; - reg = <0x40000 0x3c0000>; - }; - }; - }; - - esdhc@50020000 { /* ESDHC3 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_esdhc3_1>; - non-removable; - status = "okay"; - }; - }; - - iomuxc@53fa8000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; - - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - 982 0x80000000 /* MX53_PAD_PATA_DATA14__GPIO2_14 */ - 989 0x80000000 /* MX53_PAD_PATA_DATA15__GPIO2_15 */ - 424 0x80000000 /* MX53_PAD_EIM_EB2__GPIO2_30 */ - 701 0x80000000 /* MX53_PAD_EIM_DA13__GPIO3_13 */ - 449 0x80000000 /* MX53_PAD_EIM_D19__GPIO3_19 */ - 43 0x80000000 /* MX53_PAD_KEY_ROW2__GPIO4_11 */ - 868 0x80000000 /* MX53_PAD_PATA_DA_0__GPIO7_6 */ - >; - }; - }; - }; - - uart1: serial@53fbc000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart1_1>; - status = "okay"; - }; - - uart2: serial@53fc0000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart2_1>; - status = "okay"; - }; - }; - - aips@60000000 { /* AIPS2 */ - i2c@63fc4000 { /* I2C2 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c2_1>; - status = "okay"; - - codec: sgtl5000@0a { - compatible = "fsl,sgtl5000"; - reg = <0x0a>; - }; - - magnetometer: mag3110@0e { - compatible = "fsl,mag3110"; - reg = <0x0e>; - }; - - touchkey: mpr121@5a { - compatible = "fsl,mpr121"; - reg = <0x5a>; - }; - }; - - i2c@63fc8000 { /* I2C1 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c1_1>; - status = "okay"; - - accelerometer: mma8450@1c { - compatible = "fsl,mma8450"; - reg = <0x1c>; - }; - - camera: ov5642@3c { - compatible = "ovti,ov5642"; - reg = <0x3c>; - }; - - pmic: dialog@48 { - compatible = "dialog,da9053", "dialog,da9052"; - reg = <0x48>; - }; - }; - - ethernet@63fec000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_fec_1>; - phy-mode = "rmii"; - phy-reset-gpios = <&gpio7 6 0>; - status = "okay"; - }; - }; - }; - gpio-keys { compatible = "gpio-keys"; @@ -188,3 +37,146 @@ }; }; }; + +&esdhc1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc1_1>; + cd-gpios = <&gpio3 13 0>; + wp-gpios = <&gpio4 11 0>; + status = "okay"; +}; + +&esdhc2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc2_1>; + non-removable; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3_1>; + fsl,uart-has-rtscts; + status = "okay"; +}; + +&ecspi1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1_1>; + fsl,spi-num-chipselects = <2>; + cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>; + status = "okay"; + + zigbee: mc1323@0 { + compatible = "fsl,mc1323"; + spi-max-frequency = <8000000>; + reg = <0>; + }; + + flash: m25p32@1 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25p32", "st,m25p"; + spi-max-frequency = <20000000>; + reg = <1>; + + partition@0 { + label = "U-Boot"; + reg = <0x0 0x40000>; + read-only; + }; + + partition@40000 { + label = "Kernel"; + reg = <0x40000 0x3c0000>; + }; + }; +}; + +&esdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc3_1>; + non-removable; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + 982 0x80000000 /* MX53_PAD_PATA_DATA14__GPIO2_14 */ + 989 0x80000000 /* MX53_PAD_PATA_DATA15__GPIO2_15 */ + 424 0x80000000 /* MX53_PAD_EIM_EB2__GPIO2_30 */ + 701 0x80000000 /* MX53_PAD_EIM_DA13__GPIO3_13 */ + 449 0x80000000 /* MX53_PAD_EIM_D19__GPIO3_19 */ + 43 0x80000000 /* MX53_PAD_KEY_ROW2__GPIO4_11 */ + 868 0x80000000 /* MX53_PAD_PATA_DA_0__GPIO7_6 */ + >; + }; + }; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1_1>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2_1>; + status = "okay"; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2_1>; + status = "okay"; + + codec: sgtl5000@0a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + }; + + magnetometer: mag3110@0e { + compatible = "fsl,mag3110"; + reg = <0x0e>; + }; + + touchkey: mpr121@5a { + compatible = "fsl,mpr121"; + reg = <0x5a>; + }; +}; + +&i2c1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1_1>; + status = "okay"; + + accelerometer: mma8450@1c { + compatible = "fsl,mma8450"; + reg = <0x1c>; + }; + + camera: ov5642@3c { + compatible = "ovti,ov5642"; + reg = <0x3c>; + }; + + pmic: dialog@48 { + compatible = "dialog,da9053", "dialog,da9052"; + reg = <0x48>; + }; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec_1>; + phy-mode = "rmii"; + phy-reset-gpios = <&gpio7 6 0>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6q-arm2.dts b/arch/arm/boot/dts/imx6q-arm2.dts index 5bfa02a3f85c..53eb241fa5ad 100644 --- a/arch/arm/boot/dts/imx6q-arm2.dts +++ b/arch/arm/boot/dts/imx6q-arm2.dts @@ -21,71 +21,6 @@ reg = <0x10000000 0x80000000>; }; - soc { - gpmi-nand@00112000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_gpmi_nand_1>; - status = "disabled"; /* gpmi nand conflicts with SD */ - }; - - aips-bus@02000000 { /* AIPS1 */ - iomuxc@020e0000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; - - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - 176 0x80000000 /* MX6Q_PAD_EIM_D25__GPIO_3_25 */ - >; - }; - }; - - arm2 { - pinctrl_usdhc3_arm2: usdhc3grp-arm2 { - fsl,pins = < - 1363 0x80000000 /* MX6Q_PAD_NANDF_CS0__GPIO_6_11 */ - 1369 0x80000000 /* MX6Q_PAD_NANDF_CS1__GPIO_6_14 */ - >; - }; - }; - }; - }; - - aips-bus@02100000 { /* AIPS2 */ - ethernet@02188000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet_2>; - phy-mode = "rgmii"; - status = "okay"; - }; - - usdhc@02198000 { /* uSDHC3 */ - cd-gpios = <&gpio6 11 0>; - wp-gpios = <&gpio6 14 0>; - vmmc-supply = <®_3p3v>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc3_1 - &pinctrl_usdhc3_arm2>; - status = "okay"; - }; - - usdhc@0219c000 { /* uSDHC4 */ - non-removable; - vmmc-supply = <®_3p3v>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc4_1>; - status = "okay"; - }; - - uart4: serial@021f0000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart4_1>; - status = "okay"; - }; - }; - }; - regulators { compatible = "simple-bus"; @@ -108,3 +43,62 @@ }; }; }; + +&gpmi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpmi_nand_1>; + status = "disabled"; /* gpmi nand conflicts with SD */ +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + 176 0x80000000 /* MX6Q_PAD_EIM_D25__GPIO_3_25 */ + >; + }; + }; + + arm2 { + pinctrl_usdhc3_arm2: usdhc3grp-arm2 { + fsl,pins = < + 1363 0x80000000 /* MX6Q_PAD_NANDF_CS0__GPIO_6_11 */ + 1369 0x80000000 /* MX6Q_PAD_NANDF_CS1__GPIO_6_14 */ + >; + }; + }; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet_2>; + phy-mode = "rgmii"; + status = "okay"; +}; + +&usdhc3 { + cd-gpios = <&gpio6 11 0>; + wp-gpios = <&gpio6 14 0>; + vmmc-supply = <®_3p3v>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3_1 + &pinctrl_usdhc3_arm2>; + status = "okay"; +}; + +&usdhc4 { + non-removable; + vmmc-supply = <®_3p3v>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc4_1>; + status = "okay"; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4_1>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6q-sabreauto.dts b/arch/arm/boot/dts/imx6q-sabreauto.dts index 826e4ad1477e..656d489122fe 100644 --- a/arch/arm/boot/dts/imx6q-sabreauto.dts +++ b/arch/arm/boot/dts/imx6q-sabreauto.dts @@ -20,45 +20,39 @@ memory { reg = <0x10000000 0x80000000>; }; +}; - soc { - aips-bus@02000000 { /* AIPS1 */ - iomuxc@020e0000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - 1376 0x80000000 /* MX6Q_PAD_NANDF_CS2__GPIO_6_15 */ - 13 0x80000000 /* MX6Q_PAD_SD2_DAT2__GPIO_1_13 */ - >; - }; - }; - }; - }; - - aips-bus@02100000 { /* AIPS2 */ - uart4: serial@021f0000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart4_1>; - status = "okay"; - }; - - ethernet@02188000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet_2>; - phy-mode = "rgmii"; - status = "okay"; - }; - - usdhc@02198000 { /* uSDHC3 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc3_1>; - cd-gpios = <&gpio6 15 0>; - wp-gpios = <&gpio1 13 0>; - status = "okay"; - }; + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + 1376 0x80000000 /* MX6Q_PAD_NANDF_CS2__GPIO_6_15 */ + 13 0x80000000 /* MX6Q_PAD_SD2_DAT2__GPIO_1_13 */ + >; }; }; }; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4_1>; + status = "okay"; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet_2>; + phy-mode = "rgmii"; + status = "okay"; +}; + +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3_1>; + cd-gpios = <&gpio6 15 0>; + wp-gpios = <&gpio1 13 0>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index d152328285a1..2ce355cd05e5 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -21,118 +21,6 @@ reg = <0x10000000 0x40000000>; }; - soc { - aips-bus@02000000 { /* AIPS1 */ - spba-bus@02000000 { - ecspi@02008000 { /* eCSPI1 */ - fsl,spi-num-chipselects = <1>; - cs-gpios = <&gpio3 19 0>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_ecspi1_1>; - status = "okay"; - - flash: m25p80@0 { - compatible = "sst,sst25vf016b"; - spi-max-frequency = <20000000>; - reg = <0>; - }; - }; - - ssi1: ssi@02028000 { - fsl,mode = "i2s-slave"; - status = "okay"; - }; - }; - - iomuxc@020e0000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; - - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - 1450 0x80000000 /* MX6Q_PAD_NANDF_D6__GPIO_2_6 */ - 1458 0x80000000 /* MX6Q_PAD_NANDF_D7__GPIO_2_7 */ - 121 0x80000000 /* MX6Q_PAD_EIM_D19__GPIO_3_19 */ - 144 0x80000000 /* MX6Q_PAD_EIM_D22__GPIO_3_22 */ - 152 0x80000000 /* MX6Q_PAD_EIM_D23__GPIO_3_23 */ - 1262 0x80000000 /* MX6Q_PAD_SD3_DAT5__GPIO_7_0 */ - 1270 0x1f0b0 /* MX6Q_PAD_SD3_DAT4__GPIO_7_1 */ - 953 0x80000000 /* MX6Q_PAD_GPIO_0__CCM_CLKO */ - >; - }; - }; - }; - }; - - aips-bus@02100000 { /* AIPS2 */ - usb@02184000 { /* USB OTG */ - vbus-supply = <®_usb_otg_vbus>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usbotg_1>; - disable-over-current; - status = "okay"; - }; - - usb@02184200 { /* USB1 */ - status = "okay"; - }; - - ethernet@02188000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet_1>; - phy-mode = "rgmii"; - phy-reset-gpios = <&gpio3 23 0>; - status = "okay"; - }; - - usdhc@02198000 { /* uSDHC3 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc3_2>; - cd-gpios = <&gpio7 0 0>; - wp-gpios = <&gpio7 1 0>; - vmmc-supply = <®_3p3v>; - status = "okay"; - }; - - usdhc@0219c000 { /* uSDHC4 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc4_2>; - cd-gpios = <&gpio2 6 0>; - wp-gpios = <&gpio2 7 0>; - vmmc-supply = <®_3p3v>; - status = "okay"; - }; - - audmux@021d8000 { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_audmux_1>; - }; - - uart2: serial@021e8000 { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart2_1>; - }; - - i2c@021a0000 { /* I2C1 */ - status = "okay"; - clock-frequency = <100000>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c1_1>; - - codec: sgtl5000@0a { - compatible = "fsl,sgtl5000"; - reg = <0x0a>; - clocks = <&clks 169>; - VDDA-supply = <®_2p5v>; - VDDIO-supply = <®_3p3v>; - }; - }; - }; - }; - regulators { compatible = "simple-bus"; @@ -176,3 +64,107 @@ mux-ext-port = <4>; }; }; + +&ecspi1 { + fsl,spi-num-chipselects = <1>; + cs-gpios = <&gpio3 19 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1_1>; + status = "okay"; + + flash: m25p80@0 { + compatible = "sst,sst25vf016b"; + spi-max-frequency = <20000000>; + reg = <0>; + }; +}; + +&ssi1 { + fsl,mode = "i2s-slave"; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + 1450 0x80000000 /* MX6Q_PAD_NANDF_D6__GPIO_2_6 */ + 1458 0x80000000 /* MX6Q_PAD_NANDF_D7__GPIO_2_7 */ + 121 0x80000000 /* MX6Q_PAD_EIM_D19__GPIO_3_19 */ + 144 0x80000000 /* MX6Q_PAD_EIM_D22__GPIO_3_22 */ + 152 0x80000000 /* MX6Q_PAD_EIM_D23__GPIO_3_23 */ + 1262 0x80000000 /* MX6Q_PAD_SD3_DAT5__GPIO_7_0 */ + 1270 0x1f0b0 /* MX6Q_PAD_SD3_DAT4__GPIO_7_1 */ + 953 0x80000000 /* MX6Q_PAD_GPIO_0__CCM_CLKO */ + >; + }; + }; +}; + +&usbotg { + vbus-supply = <®_usb_otg_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg_1>; + disable-over-current; + status = "okay"; +}; + +&usbh1 { + status = "okay"; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet_1>; + phy-mode = "rgmii"; + phy-reset-gpios = <&gpio3 23 0>; + status = "okay"; +}; + +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3_2>; + cd-gpios = <&gpio7 0 0>; + wp-gpios = <&gpio7 1 0>; + vmmc-supply = <®_3p3v>; + status = "okay"; +}; + +&usdhc4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc4_2>; + cd-gpios = <&gpio2 6 0>; + wp-gpios = <&gpio2 7 0>; + vmmc-supply = <®_3p3v>; + status = "okay"; +}; + +&audmux { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux_1>; +}; + +&uart2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2_1>; +}; + +&i2c1 { + status = "okay"; + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1_1>; + + codec: sgtl5000@0a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + clocks = <&clks 169>; + VDDA-supply = <®_2p5v>; + VDDIO-supply = <®_3p3v>; + }; +}; diff --git a/arch/arm/boot/dts/imx6q-sabresd.dts b/arch/arm/boot/dts/imx6q-sabresd.dts index a42402562b7b..2dea304a7980 100644 --- a/arch/arm/boot/dts/imx6q-sabresd.dts +++ b/arch/arm/boot/dts/imx6q-sabresd.dts @@ -21,61 +21,6 @@ reg = <0x10000000 0x40000000>; }; - soc { - aips-bus@02000000 { /* AIPS1 */ - spba-bus@02000000 { - uart1: serial@02020000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_uart1_1>; - status = "okay"; - }; - }; - - iomuxc@020e0000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_hog>; - - hog { - pinctrl_hog: hoggrp { - fsl,pins = < - 1004 0x80000000 /* MX6Q_PAD_GPIO_4__GPIO_1_4 */ - 1012 0x80000000 /* MX6Q_PAD_GPIO_5__GPIO_1_5 */ - 1402 0x80000000 /* MX6Q_PAD_NANDF_D0__GPIO_2_0 */ - 1410 0x80000000 /* MX6Q_PAD_NANDF_D1__GPIO_2_1 */ - 1418 0x80000000 /* MX6Q_PAD_NANDF_D2__GPIO_2_2 */ - 1426 0x80000000 /* MX6Q_PAD_NANDF_D3__GPIO_2_3 */ - >; - }; - }; - }; - }; - - aips-bus@02100000 { /* AIPS2 */ - ethernet@02188000 { - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_enet_1>; - phy-mode = "rgmii"; - status = "okay"; - }; - - usdhc@02194000 { /* uSDHC2 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc2_1>; - cd-gpios = <&gpio2 2 0>; - wp-gpios = <&gpio2 3 0>; - status = "okay"; - }; - - usdhc@02198000 { /* uSDHC3 */ - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usdhc3_1>; - cd-gpios = <&gpio2 0 0>; - wp-gpios = <&gpio2 1 0>; - status = "okay"; - }; - }; - }; - gpio-keys { compatible = "gpio-keys"; @@ -92,3 +37,50 @@ }; }; }; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1_1>; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + 1004 0x80000000 /* MX6Q_PAD_GPIO_4__GPIO_1_4 */ + 1012 0x80000000 /* MX6Q_PAD_GPIO_5__GPIO_1_5 */ + 1402 0x80000000 /* MX6Q_PAD_NANDF_D0__GPIO_2_0 */ + 1410 0x80000000 /* MX6Q_PAD_NANDF_D1__GPIO_2_1 */ + 1418 0x80000000 /* MX6Q_PAD_NANDF_D2__GPIO_2_2 */ + 1426 0x80000000 /* MX6Q_PAD_NANDF_D3__GPIO_2_3 */ + >; + }; + }; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet_1>; + phy-mode = "rgmii"; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc2_1>; + cd-gpios = <&gpio2 2 0>; + wp-gpios = <&gpio2 3 0>; + status = "okay"; +}; + +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3_1>; + cd-gpios = <&gpio2 0 0>; + wp-gpios = <&gpio2 1 0>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index ff1205ea5719..2d6064775630 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -108,7 +108,7 @@ clocks = <&clks 106>; }; - nfc: gpmi-nand@00112000 { + gpmi: gpmi-nand@00112000 { compatible = "fsl,imx6q-gpmi-nand"; #address-cells = <1>; #size-cells = <1>; From 860c06f6c0620307b595aacb3ead9c439e9dd785 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 3 Jan 2013 14:27:35 -0200 Subject: [PATCH 2220/4607] ARM: mx25pdk: Add device tree support Add basic device tree support for mx25pdk board. Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- Documentation/devicetree/bindings/arm/fsl.txt | 4 +++ arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/imx25-pdk.dts | 36 +++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 arch/arm/boot/dts/imx25-pdk.dts diff --git a/Documentation/devicetree/bindings/arm/fsl.txt b/Documentation/devicetree/bindings/arm/fsl.txt index 4c25c987c75e..e935d7d4ac43 100644 --- a/Documentation/devicetree/bindings/arm/fsl.txt +++ b/Documentation/devicetree/bindings/arm/fsl.txt @@ -5,6 +5,10 @@ i.MX23 Evaluation Kit Required root node properties: - compatible = "fsl,imx23-evk", "fsl,imx23"; +i.MX25 Product Development Kit +Required root node properties: + - compatible = "fsl,imx25-pdk", "fsl,imx25"; + i.MX27 Product Development Kit Required root node properties: - compatible = "fsl,imx27-pdk", "fsl,imx27"; diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index f38304abcee8..7a414358b35e 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -81,6 +81,7 @@ dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \ armada-xp-openblocks-ax3-4.dtb dtb-$(CONFIG_ARCH_MXC) += \ imx25-karo-tx25.dtb \ + imx25-pdk.dtb \ imx27-apf27.dtb \ imx27-pdk.dtb \ imx31-bug.dtb \ diff --git a/arch/arm/boot/dts/imx25-pdk.dts b/arch/arm/boot/dts/imx25-pdk.dts new file mode 100644 index 000000000000..a02a860afd18 --- /dev/null +++ b/arch/arm/boot/dts/imx25-pdk.dts @@ -0,0 +1,36 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/dts-v1/; +/include/ "imx25.dtsi" + +/ { + model = "Freescale i.MX25 Product Development Kit"; + compatible = "fsl,imx25-pdk", "fsl,imx25"; + + memory { + reg = <0x80000000 0x4000000>; + }; +}; + +&uart1 { + status = "okay"; +}; + +&fec { + phy-mode = "rmii"; + status = "okay"; +}; + +&nfc { + nand-on-flash-bbt; + status = "okay"; +}; From 152fab67e4ca98511b0854cccd7dfd830a3d5c97 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 4 Jan 2013 10:29:09 -0200 Subject: [PATCH 2221/4607] ARM: dts: imx25-karo-tx25: Put status entry in the end Just to keep consistency with other dts files, place 'status' entry as the last one. Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx25-karo-tx25.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx25-karo-tx25.dts b/arch/arm/boot/dts/imx25-karo-tx25.dts index eb9b9d1c8349..1a9d0491cdce 100644 --- a/arch/arm/boot/dts/imx25-karo-tx25.dts +++ b/arch/arm/boot/dts/imx25-karo-tx25.dts @@ -26,8 +26,8 @@ }; &fec { - status = "okay"; phy-mode = "rmii"; + status = "okay"; }; &nfc { From 6012555c2462a0fea1149fb2a54d214b6d1fbe48 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 3 Jan 2013 20:37:33 +0800 Subject: [PATCH 2222/4607] ARM: dts: imx: Add imx51 KPP entry 1) Add KPP device node entry. 2) Add one KPP pinctrl entry. Signed-off-by: Liu Ying Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx51.dtsi | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi index 1f5d45eff45e..6bda4dcf6e9d 100644 --- a/arch/arm/boot/dts/imx51.dtsi +++ b/arch/arm/boot/dts/imx51.dtsi @@ -221,6 +221,14 @@ #interrupt-cells = <2>; }; + kpp: kpp@73f94000 { + compatible = "fsl,imx51-kpp", "fsl,imx21-kpp"; + reg = <0x73f94000 0x4000>; + interrupts = <60>; + clocks = <&clks 0>; + status = "disabled"; + }; + wdog1: wdog@73f98000 { compatible = "fsl,imx51-wdt", "fsl,imx21-wdt"; reg = <0x73f98000 0x4000>; @@ -410,6 +418,21 @@ >; }; }; + + kpp { + pinctrl_kpp_1: kppgrp-1 { + fsl,pins = < + 438 0xe0 /* MX51_PAD_KEY_ROW0__KEY_ROW0 */ + 439 0xe0 /* MX51_PAD_KEY_ROW1__KEY_ROW1 */ + 440 0xe0 /* MX51_PAD_KEY_ROW2__KEY_ROW2 */ + 441 0xe0 /* MX51_PAD_KEY_ROW3__KEY_ROW3 */ + 442 0xe8 /* MX51_PAD_KEY_COL0__KEY_COL0 */ + 444 0xe8 /* MX51_PAD_KEY_COL1__KEY_COL1 */ + 446 0xe8 /* MX51_PAD_KEY_COL2__KEY_COL2 */ + 448 0xe8 /* MX51_PAD_KEY_COL3__KEY_COL3 */ + >; + }; + }; }; pwm1: pwm@73fb4000 { From 67eb7c0bc8ee66aa74ed891b9c97f8da9c44a9c9 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 3 Jan 2013 20:37:34 +0800 Subject: [PATCH 2223/4607] ARM i.MX51 babbage: Add keypad support The keypad is on the accessory board of i.MX51 babbage main board and is driven by Keypad Port(KPP) in SoC. The keymap is the same to i.MX25 3stack platform as the accessory board schematic tells that it is designed in this way. Signed-off-by: Liu Ying Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx51-babbage.dts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/arch/arm/boot/dts/imx51-babbage.dts b/arch/arm/boot/dts/imx51-babbage.dts index fb33aff5b4b9..aab6e43219af 100644 --- a/arch/arm/boot/dts/imx51-babbage.dts +++ b/arch/arm/boot/dts/imx51-babbage.dts @@ -273,3 +273,25 @@ phy-mode = "mii"; status = "okay"; }; + +&kpp { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_kpp_1>; + linux,keymap = <0x00000067 /* KEY_UP */ + 0x0001006c /* KEY_DOWN */ + 0x00020072 /* KEY_VOLUMEDOWN */ + 0x00030066 /* KEY_HOME */ + 0x0100006a /* KEY_RIGHT */ + 0x01010069 /* KEY_LEFT */ + 0x0102001c /* KEY_ENTER */ + 0x01030073 /* KEY_VOLUMEUP */ + 0x02000040 /* KEY_F6 */ + 0x02010042 /* KEY_F8 */ + 0x02020043 /* KEY_F9 */ + 0x02030044 /* KEY_F10 */ + 0x0300003b /* KEY_F1 */ + 0x0301003c /* KEY_F2 */ + 0x0302003d /* KEY_F3 */ + 0x03030074>; /* KEY_POWER */ + status = "okay"; +}; From 11ab21e90a4aa3e9bb33b345a12e63ceff742655 Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Wed, 9 Jan 2013 14:44:23 +0100 Subject: [PATCH 2224/4607] ARM: dts: imx53: pinctrl update Add pinctrl for cspi, csi. Add new pingroups for can1 and uart3. Signed-off-by: Steffen Trumtrar Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx53.dtsi | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi index edc3f1eb6699..a9a3adde4e5a 100644 --- a/arch/arm/boot/dts/imx53.dtsi +++ b/arch/arm/boot/dts/imx53.dtsi @@ -274,6 +274,44 @@ }; }; + csi { + pinctrl_csi_1: csigrp-1 { + fsl,pins = < + 286 0x1d5 /* MX53_PAD_CSI0_DATA_EN__IPU_CSI0_DATA_EN */ + 291 0x1d5 /* MX53_PAD_CSI0_VSYNC__IPU_CSI0_VSYNC */ + 280 0x1d5 /* MX53_PAD_CSI0_MCLK__IPU_CSI0_HSYNC */ + 276 0x1d5 /* MX53_PAD_CSI0_PIXCLK__IPU_CSI0_PIXCLK */ + 409 0x1d5 /* MX53_PAD_CSI0_DAT19__IPU_CSI0_D_19 */ + 402 0x1d5 /* MX53_PAD_CSI0_DAT18__IPU_CSI0_D_18 */ + 395 0x1d5 /* MX53_PAD_CSI0_DAT17__IPU_CSI0_D_17 */ + 388 0x1d5 /* MX53_PAD_CSI0_DAT16__IPU_CSI0_D_16 */ + 381 0x1d5 /* MX53_PAD_CSI0_DAT15__IPU_CSI0_D_15 */ + 374 0x1d5 /* MX53_PAD_CSI0_DAT14__IPU_CSI0_D_14 */ + 367 0x1d5 /* MX53_PAD_CSI0_DAT13__IPU_CSI0_D_13 */ + 360 0x1d5 /* MX53_PAD_CSI0_DAT12__IPU_CSI0_D_12 */ + 352 0x1d5 /* MX53_PAD_CSI0_DAT11__IPU_CSI0_D_11 */ + 344 0x1d5 /* MX53_PAD_CSI0_DAT10__IPU_CSI0_D_10 */ + 336 0x1d5 /* MX53_PAD_CSI0_DAT9__IPU_CSI0_D_9 */ + 328 0x1d5 /* MX53_PAD_CSI0_DAT8__IPU_CSI0_D_8 */ + 320 0x1d5 /* MX53_PAD_CSI0_DAT7__IPU_CSI0_D_7 */ + 312 0x1d5 /* MX53_PAD_CSI0_DAT6__IPU_CSI0_D_6 */ + 304 0x1d5 /* MX53_PAD_CSI0_DAT5__IPU_CSI0_D_5 */ + 296 0x1d5 /* MX53_PAD_CSI0_DAT4__IPU_CSI0_D_4 */ + 276 0x1d5 /* MX53_PAD_CSI0_PIXCLK__IPU_CSI0_PIXCLK */ + >; + }; + }; + + cspi { + pinctrl_cspi_1: cspigrp-1 { + fsl,pins = < + 998 0x1d5 /* MX53_PAD_SD1_DATA0__CSPI_MISO */ + 1008 0x1d5 /* MX53_PAD_SD1_CMD__CSPI_MOSI */ + 1022 0x1d5 /* MX53_PAD_SD1_CLK__CSPI_SCLK */ + >; + }; + }; + ecspi1 { pinctrl_ecspi1_1: ecspi1grp-1 { fsl,pins = < @@ -349,6 +387,13 @@ 853 0x80000000 /* MX53_PAD_PATA_DIOR__CAN1_RXCAN */ >; }; + + pinctrl_can1_2: can1grp-2 { + fsl,pins = < + 37 0x80000000 /* MX53_PAD_KEY_COL2__CAN1_TXCAN */ + 44 0x80000000 /* MX53_PAD_KEY_ROW2__CAN1_RXCAN */ + >; + }; }; can2 { @@ -421,6 +466,14 @@ 880 0x1c5 /* MX53_PAD_PATA_DA_2__UART3_RTS */ >; }; + + pinctrl_uart3_2: uart3grp-2 { + fsl,pins = < + 884 0x1c5 /* MX53_PAD_PATA_CS_0__UART3_TXD_MUX */ + 888 0x1c5 /* MX53_PAD_PATA_CS_1__UART3_RXD_MUX */ + >; + }; + }; uart4 { From b17d5169d52bde2c619fc254234ae3e3a6feb206 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 9 Jan 2013 14:44:24 +0100 Subject: [PATCH 2225/4607] ARM i.MX53: add dts for the TQ tqma53 module The tqma53 is an embedded module that has some features on board (e.g. emmc), but mostly just provides access to them on its interface. Going along with the imx53.dtsi, the tqma53.dtsi specifies the existing devices and their pinctrl for this module. All devices that are not on the module are disabled by default and need to be enabled in a baseboard DT. Signed-off-by: Sascha Hauer Signed-off-by: Steffen Trumtrar Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx53-tqma53.dtsi | 172 ++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 arch/arm/boot/dts/imx53-tqma53.dtsi diff --git a/arch/arm/boot/dts/imx53-tqma53.dtsi b/arch/arm/boot/dts/imx53-tqma53.dtsi new file mode 100644 index 000000000000..8278ec5ec222 --- /dev/null +++ b/arch/arm/boot/dts/imx53-tqma53.dtsi @@ -0,0 +1,172 @@ +/* + * Copyright 2012 Sascha Hauer , Pengutronix + * Copyright 2012 Steffen Trumtrar , Pengutronix + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/include/ "imx53.dtsi" + +/ { + model = "TQ TQMa53"; + compatible = "tq,tqma53", "fsl,imx53"; + + memory { + reg = <0x70000000 0x40000000>; /* Up to 1GiB */ + }; + + regulators { + compatible = "simple-bus"; + + reg_3p3v: 3p3v { + compatible = "regulator-fixed"; + regulator-name = "3P3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; +}; + +&esdhc2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc2_1>; + wp-gpios = <&gpio1 2 0>; + cd-gpios = <&gpio1 4 0>; + status = "disabled"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3_2>; + status = "disabled"; +}; + +&ecspi1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1_1>; + fsl,spi-num-chipselects = <4>; + cs-gpios = <&gpio2 30 0>, <&gpio3 19 0>, + <&gpio3 24 0>, <&gpio3 25 0>; + status = "disabled"; +}; + +&esdhc3 { /* EMMC */ + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_esdhc3_1>; + vmmc-supply = <®_3p3v>; + non-removable; + bus-width = <8>; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + i2s { + pinctrl_i2s_1: i2s-grp1 { + fsl,pins = < + 1 0x10000 /* I2S_MCLK */ + 10 0x10000 /* I2S_SCLK */ + 17 0x10000 /* I2S_DOUT */ + 23 0x10000 /* I2S_LRCLK*/ + 30 0x10000 /* I2S_DIN */ + >; + }; + }; + + hog { + pinctrl_hog: hoggrp { + fsl,pins = < + 610 0x10000 /* MX53_PAD_EIM_CS1__IPU_DI1_PIN6 (VSYNC)*/ + 711 0x10000 /* MX53_PAD_EIM_DA15__IPU_DI1_PIN4 (HSYNC)*/ + 873 0x10000 /* MX53_PAD_PATA_DA_1__GPIO7_7 (LCD_BLT_EN)*/ + 878 0x10000 /* MX53_PAD_PATA_DA_2__GPIO7_8 (LCD_RESET)*/ + 922 0x10000 /* MX53_PAD_PATA_DATA5__GPIO2_5 (LCD_POWER)*/ + 928 0x10000 /* MX53_PAD_PATA_DATA6__GPIO2_6 (PMIC_INT)*/ + 982 0x10000 /* MX53_PAD_PATA_DATA14__GPIO2_14 (CSI_RST)*/ + 989 0x10000 /* MX53_PAD_PATA_DATA15__GPIO2_15 (CSI_PWDN)*/ + 1069 0x10000 /* MX53_PAD_GPIO_0__GPIO1_0 (SYSTEM_DOWN)*/ + 1093 0x10000 /* MX53_PAD_GPIO_3__GPIO1_3 */ + >; + }; + }; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1_2>; + fsl,uart-has-rtscts; + status = "disabled"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2_1>; + status = "disabled"; +}; + +&can1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can1_2>; + status = "disabled"; +}; + +&can2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can2_1>; + status = "disabled"; +}; + +&i2c3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3_1>; + status = "disabled"; +}; + +&cspi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_cspi_1>; + fsl,spi-num-chipselects = <3>; + cs-gpios = <&gpio1 18 0>, <&gpio1 19 0>, + <&gpio1 21 0>; + status = "disabled"; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2_1>; + status = "okay"; + + pmic: mc34708@8 { + compatible = "fsl,mc34708"; + reg = <0x8>; + fsl,mc13xxx-uses-rtc; + interrupt-parent = <&gpio2>; + interrupts = <6 8>; /* PDATA_DATA6, low active */ + }; + + sensor1: lm75@48 { + compatible = "lm75"; + reg = <0x48>; + }; + + eeprom: 24c64@50 { + compatible = "at,24c64"; + pagesize = <32>; + reg = <0x50>; + }; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec_1>; + phy-mode = "rmii"; + status = "disabled"; +}; From be3a568d77fac942bbc7fb5ed77963bc254e60ff Mon Sep 17 00:00:00 2001 From: Steffen Trumtrar Date: Thu, 10 Jan 2013 11:27:27 +0100 Subject: [PATCH 2226/4607] ARM i.MX53: dts: add oftree for MBa53 baseboard The MBa53 is a baseboard for the TQMA53 embedded module. This enables/adds only supported devices, i.e. it is not feature complete, because of missing drivers in mainline linux. Signed-off-by: Steffen Trumtrar Signed-off-by: Shawn Guo --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/imx53-mba53.dts | 130 ++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 arch/arm/boot/dts/imx53-mba53.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 7a414358b35e..b41910ea5b58 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -88,6 +88,7 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx51-babbage.dtb \ imx53-ard.dtb \ imx53-evk.dtb \ + imx53-mba53.dtb \ imx53-qsb.dtb \ imx53-smd.dtb \ imx6q-arm2.dtb \ diff --git a/arch/arm/boot/dts/imx53-mba53.dts b/arch/arm/boot/dts/imx53-mba53.dts new file mode 100644 index 000000000000..e54fffd48369 --- /dev/null +++ b/arch/arm/boot/dts/imx53-mba53.dts @@ -0,0 +1,130 @@ +/* + * Copyright 2012 Sascha Hauer , Pengutronix + * Copyright 2012 Steffen Trumtrar , Pengutronix + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/dts-v1/; +/include/ "imx53-tqma53.dtsi" + +/ { + model = "TQ MBa53 starter kit"; + compatible = "tq,mba53", "tq,tqma53", "fsl,imx53"; +}; + +&iomuxc { + lvds1 { + pinctrl_lvds1_1: lvds1-grp1 { + fsl,pins = <730 0x10000 /* LVDS0_TX3 */ + 732 0x10000 /* LVDS0_CLK */ + 734 0x10000 /* LVDS0_TX2 */ + 736 0x10000 /* LVDS0_TX1 */ + 738 0x10000>; /* LVDS0_TX0 */ + }; + + pinctrl_lvds1_2: lvds1-grp2 { + fsl,pins = <720 0x10000 /* LVDS1_TX3 */ + 722 0x10000 /* LVDS1_TX2 */ + 724 0x10000 /* LVDS1_CLK */ + 726 0x10000 /* LVDS1_TX1 */ + 728 0x10000>; /* LVDS1_TX0 */ + }; + }; + + disp1 { + pinctrl_disp1_1: disp1-grp1 { + fsl,pins = <689 0x10000 /* DISP1_DRDY */ + 482 0x10000 /* DISP1_HSYNC */ + 489 0x10000 /* DISP1_VSYNC */ + 684 0x10000 /* DISP1_DAT_0 */ + 515 0x10000 /* DISP1_DAT_22 */ + 523 0x10000 /* DISP1_DAT_23 */ + 543 0x10000 /* DISP1_DAT_21 */ + 553 0x10000 /* DISP1_DAT_20 */ + 558 0x10000 /* DISP1_DAT_19 */ + 564 0x10000 /* DISP1_DAT_18 */ + 570 0x10000 /* DISP1_DAT_17 */ + 575 0x10000 /* DISP1_DAT_16 */ + 580 0x10000 /* DISP1_DAT_15 */ + 585 0x10000 /* DISP1_DAT_14 */ + 590 0x10000 /* DISP1_DAT_13 */ + 595 0x10000 /* DISP1_DAT_12 */ + 628 0x10000 /* DISP1_DAT_11 */ + 634 0x10000 /* DISP1_DAT_10 */ + 639 0x10000 /* DISP1_DAT_9 */ + 644 0x10000 /* DISP1_DAT_8 */ + 649 0x10000 /* DISP1_DAT_7 */ + 654 0x10000 /* DISP1_DAT_6 */ + 659 0x10000 /* DISP1_DAT_5 */ + 664 0x10000 /* DISP1_DAT_4 */ + 669 0x10000 /* DISP1_DAT_3 */ + 674 0x10000 /* DISP1_DAT_2 */ + 679 0x10000 /* DISP1_DAT_1 */ + 684 0x10000>; /* DISP1_DAT_0 */ + }; + }; +}; + +&cspi { + status = "okay"; +}; + +&i2c2 { + codec: sgtl5000@a { + compatible = "fsl,sgtl5000"; + reg = <0x0a>; + }; + + expander: pca9554@20 { + compatible = "pca9554"; + reg = <0x20>; + interrupts = <109>; + }; + + sensor2: lm75@49 { + compatible = "lm75"; + reg = <0x49>; + }; +}; + +&fec { + status = "okay"; +}; + +&esdhc2 { + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&ecspi1 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&can1 { + status = "okay"; +}; + +&can2 { + status = "okay"; +}; + +&i2c3 { + status = "okay"; +}; From d6b9c591940cd7c03724999c72f31fd92c0dee59 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 17 Jan 2013 12:13:25 -0200 Subject: [PATCH 2227/4607] ARM: dts: imx6q: Remove silicon version from SDMA firmware Remove silicon version from SDMA firmware. This makes it consistent with other i.MX SoCs firmware names. Signed-off-by: Fabio Estevam Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6q.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index 2d6064775630..ddc78de6a0be 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -797,7 +797,7 @@ interrupts = <0 2 0x04>; clocks = <&clks 155>, <&clks 155>; clock-names = "ipg", "ahb"; - fsl,sdma-ram-script-name = "imx/sdma/sdma-imx6q-to1.bin"; + fsl,sdma-ram-script-name = "imx/sdma/sdma-imx6q.bin"; }; }; From a5120e89e7e187a91852896f586876c7a2030804 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 18 Jan 2013 10:38:05 +0800 Subject: [PATCH 2228/4607] ARM i.MX6: change mxs usbphy clock usage This mxs usbphy is only needs to be on after system boots up, and software never needs to control it anymore. Meanwhile, usbphy's parent needs to be notified if usb is suspend or not. So we design below mxs usbphy usage: - usbphy1_gate and usbphy2_gate: Their parents are dummy clock, we only needs to enable it after system boots up. - usbphy1 and usbphy2 Usage reserved bit for this clock, in that case, the refcount will be updated, but without hardware changing. Signed-off-by: Peter Chen Signed-off-by: Shawn Guo --- .../devicetree/bindings/clock/imx6q-clock.txt | 2 ++ arch/arm/mach-imx/clk-imx6q.c | 26 ++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/imx6q-clock.txt b/Documentation/devicetree/bindings/clock/imx6q-clock.txt index f73fdf595568..969b38e06ad3 100644 --- a/Documentation/devicetree/bindings/clock/imx6q-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx6q-clock.txt @@ -203,6 +203,8 @@ clocks and IDs. pcie_ref 188 pcie_ref_125m 189 enet_ref 190 + usbphy1_gate 191 + usbphy2_gate 192 Examples: diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c index c0c4e723b7f5..905bec2a08a4 100644 --- a/arch/arm/mach-imx/clk-imx6q.c +++ b/arch/arm/mach-imx/clk-imx6q.c @@ -154,8 +154,8 @@ enum mx6q_clks { usdhc4, vdo_axi, vpu_axi, cko1, pll1_sys, pll2_bus, pll3_usb_otg, pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg, ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5, - sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, - clk_max + sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate, + usbphy2_gate, clk_max }; static struct clk *clk[clk_max]; @@ -208,8 +208,21 @@ int __init mx6q_clocks_init(void) clk[pll7_usb_host] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_usb_host","osc", base + 0x20, 0x3); clk[pll8_mlb] = imx_clk_pllv3(IMX_PLLV3_MLB, "pll8_mlb", "osc", base + 0xd0, 0x0); - clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 6); - clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 6); + /* + * Bit 20 is the reserved and read-only bit, we do this only for: + * - Do nothing for usbphy clk_enable/disable + * - Keep refcount when do usbphy clk_enable/disable, in that case, + * the clk framework may need to enable/disable usbphy's parent + */ + clk[usbphy1] = imx_clk_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); + clk[usbphy2] = imx_clk_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); + + /* + * usbphy*_gate needs to be on after system boots up, and software + * never needs to control it anymore. + */ + clk[usbphy1_gate] = imx_clk_gate("usbphy1_gate", "dummy", base + 0x10, 6); + clk[usbphy2_gate] = imx_clk_gate("usbphy2_gate", "dummy", base + 0x20, 6); clk[sata_ref] = imx_clk_fixed_factor("sata_ref", "pll6_enet", 1, 5); clk[pcie_ref] = imx_clk_fixed_factor("pcie_ref", "pll6_enet", 1, 4); @@ -436,6 +449,11 @@ int __init mx6q_clocks_init(void) for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clk[clks_init_on[i]]); + if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { + clk_prepare_enable(clk[usbphy1_gate]); + clk_prepare_enable(clk[usbphy2_gate]); + } + /* Set initial power mode */ imx6q_set_lpm(WAIT_CLOCKED); From 1982d5b6c1b78363b5142eb0cb81c38d7604fc61 Mon Sep 17 00:00:00 2001 From: Laurent Cans Date: Sun, 20 Jan 2013 23:55:29 +0100 Subject: [PATCH 2229/4607] ARM: dts: Add apf51 basic support Signed-off-by: Laurent Cans Signed-off-by: Gwenhael Goavec-Merou Signed-off-by: Shawn Guo --- .../devicetree/bindings/arm/armadeus.txt | 6 +++ arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/imx51-apf51.dts | 52 +++++++++++++++++++ arch/arm/boot/dts/imx51.dtsi | 30 +++++++++++ 4 files changed, 89 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/armadeus.txt create mode 100644 arch/arm/boot/dts/imx51-apf51.dts diff --git a/Documentation/devicetree/bindings/arm/armadeus.txt b/Documentation/devicetree/bindings/arm/armadeus.txt new file mode 100644 index 000000000000..9821283ff516 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/armadeus.txt @@ -0,0 +1,6 @@ +Armadeus i.MX Platforms Device Tree Bindings +----------------------------------------------- + +APF51: i.MX51 based module. +Required root node properties: + - compatible = "armadeus,imx51-apf51", "fsl,imx51"; diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index b41910ea5b58..4a46c78fb62d 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -85,6 +85,7 @@ dtb-$(CONFIG_ARCH_MXC) += \ imx27-apf27.dtb \ imx27-pdk.dtb \ imx31-bug.dtb \ + imx51-apf51.dtb \ imx51-babbage.dtb \ imx53-ard.dtb \ imx53-evk.dtb \ diff --git a/arch/arm/boot/dts/imx51-apf51.dts b/arch/arm/boot/dts/imx51-apf51.dts new file mode 100644 index 000000000000..92d3a66a69e2 --- /dev/null +++ b/arch/arm/boot/dts/imx51-apf51.dts @@ -0,0 +1,52 @@ +/* + * Copyright 2012 Armadeus Systems - + * Copyright 2012 Laurent Cans + * + * Based on mx51-babbage.dts + * Copyright 2011 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/dts-v1/; +/include/ "imx51.dtsi" + +/ { + model = "Armadeus Systems APF51 module"; + compatible = "armadeus,imx51-apf51", "fsl,imx51"; + + memory { + reg = <0x90000000 0x20000000>; + }; + + clocks { + ckih1 { + clock-frequency = <0>; + }; + + osc { + clock-frequency = <33554432>; + }; + }; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_fec_2>; + phy-mode = "mii"; + phy-reset-gpios = <&gpio3 0 0>; + phy-reset-duration = <1>; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart3_2>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi index 6bda4dcf6e9d..fcf035bf7c5a 100644 --- a/arch/arm/boot/dts/imx51.dtsi +++ b/arch/arm/boot/dts/imx51.dtsi @@ -281,6 +281,29 @@ 260 0x80000000 /* MX51_PAD_NANDF_RDY_INT__FEC_TX_CLK */ >; }; + + pinctrl_fec_2: fecgrp-2 { + fsl,pins = < + 589 0x80000000 /* MX51_PAD_DI_GP3__FEC_TX_ER */ + 592 0x80000000 /* MX51_PAD_DI2_PIN4__FEC_CRS */ + 594 0x80000000 /* MX51_PAD_DI2_PIN2__FEC_MDC */ + 596 0x80000000 /* MX51_PAD_DI2_PIN3__FEC_MDIO */ + 598 0x80000000 /* MX51_PAD_DI2_DISP_CLK__FEC_RDATA1 */ + 602 0x80000000 /* MX51_PAD_DI_GP4__FEC_RDATA2 */ + 604 0x80000000 /* MX51_PAD_DISP2_DAT0__FEC_RDATA3 */ + 609 0x80000000 /* MX51_PAD_DISP2_DAT1__FEC_RX_ER */ + 618 0x80000000 /* MX51_PAD_DISP2_DAT6__FEC_TDATA1 */ + 623 0x80000000 /* MX51_PAD_DISP2_DAT7__FEC_TDATA2 */ + 628 0x80000000 /* MX51_PAD_DISP2_DAT8__FEC_TDATA3 */ + 634 0x80000000 /* MX51_PAD_DISP2_DAT9__FEC_TX_EN */ + 639 0x80000000 /* MX51_PAD_DISP2_DAT10__FEC_COL */ + 644 0x80000000 /* MX51_PAD_DISP2_DAT11__FEC_RX_CLK */ + 649 0x80000000 /* MX51_PAD_DISP2_DAT12__FEC_RX_DV */ + 653 0x80000000 /* MX51_PAD_DISP2_DAT13__FEC_TX_CLK */ + 657 0x80000000 /* MX51_PAD_DISP2_DAT14__FEC_RDATA0 */ + 662 0x80000000 /* MX51_PAD_DISP2_DAT15__FEC_TDATA0 */ + >; + }; }; ecspi1 { @@ -417,6 +440,13 @@ 49 0x1c5 /* MX51_PAD_EIM_D24__UART3_CTS */ >; }; + + pinctrl_uart3_2: uart3grp-2 { + fsl,pins = < + 434 0x1c5 /* MX51_PAD_UART3_RXD__UART3_RXD */ + 430 0x1c5 /* MX51_PAD_UART3_TXD__UART3_TXD */ + >; + }; }; kpp { From 96574a6dc6bee2320276d7468162a72a0e5c3a34 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 8 Jan 2013 14:25:14 +0800 Subject: [PATCH 2230/4607] ARM: imx: enable imx6q-cpufreq support Update operating-points per hardware document and add support for 1 GHz and 1.2 GHz frequencies. 400 MHz, 800 MHz and 1 GHz should be supported by all i.MX6Q chips, while 1.2 GHz support needs to know from OTP fuse bit. Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6q.dtsi | 20 +++++++---- arch/arm/mach-imx/mach-imx6q.c | 65 ++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index ddc78de6a0be..ec092d294406 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -38,12 +38,19 @@ next-level-cache = <&L2>; operating-points = < /* kHz uV */ - 792000 1100000 + 1200000 1275000 + 996000 1250000 + 792000 1150000 396000 950000 - 198000 850000 >; clock-latency = <61036>; /* two CLK32 periods */ - cpu0-supply = <®_cpu>; + clocks = <&clks 104>, <&clks 6>, <&clks 16>, + <&clks 17>, <&clks 170>; + clock-names = "arm", "pll2_pfd2_396m", "step", + "pll1_sw", "pll1_sys"; + arm-supply = <®_arm>; + pu-supply = <®_pu>; + soc-supply = <®_soc>; }; cpu@1 { @@ -471,7 +478,7 @@ anatop-max-voltage = <2750000>; }; - reg_cpu: regulator-vddcore@140 { + reg_arm: regulator-vddcore@140 { compatible = "fsl,anatop-regulator"; regulator-name = "cpu"; regulator-min-microvolt = <725000>; @@ -485,7 +492,7 @@ anatop-max-voltage = <1450000>; }; - regulator-vddpu@140 { + reg_pu: regulator-vddpu@140 { compatible = "fsl,anatop-regulator"; regulator-name = "vddpu"; regulator-min-microvolt = <725000>; @@ -499,7 +506,7 @@ anatop-max-voltage = <1450000>; }; - regulator-vddsoc@140 { + reg_soc: regulator-vddsoc@140 { compatible = "fsl,anatop-regulator"; regulator-name = "vddsoc"; regulator-min-microvolt = <725000>; @@ -965,6 +972,7 @@ }; ocotp@021bc000 { + compatible = "fsl,imx6q-ocotp"; reg = <0x021bc000 0x4000>; }; diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 4eb1b3ac794c..2f974f5096fd 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -209,9 +211,72 @@ static struct cpuidle_driver imx6q_cpuidle_driver = { .state_count = 1, }; +#define OCOTP_CFG3 0x440 +#define OCOTP_CFG3_SPEED_SHIFT 16 +#define OCOTP_CFG3_SPEED_1P2GHZ 0x3 + +static void __init imx6q_opp_check_1p2ghz(struct device *cpu_dev) +{ + struct device_node *np; + void __iomem *base; + u32 val; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp"); + if (!np) { + pr_warn("failed to find ocotp node\n"); + return; + } + + base = of_iomap(np, 0); + if (!base) { + pr_warn("failed to map ocotp\n"); + goto put_node; + } + + val = readl_relaxed(base + OCOTP_CFG3); + val >>= OCOTP_CFG3_SPEED_SHIFT; + if ((val & 0x3) != OCOTP_CFG3_SPEED_1P2GHZ) + if (opp_disable(cpu_dev, 1200000000)) + pr_warn("failed to disable 1.2 GHz OPP\n"); + +put_node: + of_node_put(np); +} + +static void __init imx6q_opp_init(struct device *cpu_dev) +{ + struct device_node *np; + + np = of_find_node_by_path("/cpus/cpu@0"); + if (!np) { + pr_warn("failed to find cpu0 node\n"); + return; + } + + cpu_dev->of_node = np; + if (of_init_opp_table(cpu_dev)) { + pr_warn("failed to init OPP table\n"); + goto put_node; + } + + imx6q_opp_check_1p2ghz(cpu_dev); + +put_node: + of_node_put(np); +} + +struct platform_device imx6q_cpufreq_pdev = { + .name = "imx6q-cpufreq", +}; + static void __init imx6q_init_late(void) { imx_cpuidle_init(&imx6q_cpuidle_driver); + + if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) { + imx6q_opp_init(&imx6q_cpufreq_pdev.dev); + platform_device_register(&imx6q_cpufreq_pdev); + } } static void __init imx6q_map_io(void) From 28c55dc1acc863cb29832b5be2464ebcdafdc3d5 Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Tue, 29 Jan 2013 16:46:10 +0100 Subject: [PATCH 2231/4607] W1: Add device tree support to MXC onewire master. Signed-off-by: Martin Fuzzey Acked-by: Sascha Hauer Acked-by: Evgeniy Polyakov Signed-off-by: Shawn Guo --- .../devicetree/bindings/w1/fsl-imx-owire.txt | 19 +++++++++++++++++++ drivers/w1/masters/mxc_w1.c | 9 ++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/w1/fsl-imx-owire.txt diff --git a/Documentation/devicetree/bindings/w1/fsl-imx-owire.txt b/Documentation/devicetree/bindings/w1/fsl-imx-owire.txt new file mode 100644 index 000000000000..ecf42c07684d --- /dev/null +++ b/Documentation/devicetree/bindings/w1/fsl-imx-owire.txt @@ -0,0 +1,19 @@ +* Freescale i.MX One wire bus master controller + +Required properties: +- compatible : should be "fsl,imx21-owire" +- reg : Address and length of the register set for the device + +Optional properties: +- clocks : phandle of clock that supplies the module (required if platform + clock bindings use device tree) + +Example: + +- From imx53.dtsi: +owire: owire@63fa4000 { + compatible = "fsl,imx53-owire", "fsl,imx21-owire"; + reg = <0x63fa4000 0x4000>; + clocks = <&clks 159>; + status = "disabled"; +}; diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index 708a25fc9961..949e56669548 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c @@ -186,9 +186,16 @@ static int mxc_w1_remove(struct platform_device *pdev) return 0; } +static struct of_device_id mxc_w1_dt_ids[] = { + { .compatible = "fsl,imx21-owire" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxc_w1_dt_ids); + static struct platform_driver mxc_w1_driver = { .driver = { - .name = "mxc_w1", + .name = "mxc_w1", + .of_match_table = mxc_w1_dt_ids, }, .probe = mxc_w1_probe, .remove = mxc_w1_remove, From f1550a1ce79f1424beca55e78122e594f5ef9094 Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Tue, 29 Jan 2013 16:46:12 +0100 Subject: [PATCH 2232/4607] ARM: i.MX53: Add clocks for i.mx53 onewire master. Signed-off-by: Martin Fuzzey Acked-by: Sascha Hauer Acked-by: Evgeniy Polyakov Signed-off-by: Shawn Guo --- Documentation/devicetree/bindings/clock/imx5-clock.txt | 1 + arch/arm/mach-imx/clk-imx51-imx53.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/imx5-clock.txt b/Documentation/devicetree/bindings/clock/imx5-clock.txt index 04ad47876be0..2a0c904c46ae 100644 --- a/Documentation/devicetree/bindings/clock/imx5-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx5-clock.txt @@ -171,6 +171,7 @@ clocks and IDs. can_sel 156 can1_serial_gate 157 can1_ipg_gate 158 + owire_gate 159 Examples (for mx53): diff --git a/arch/arm/mach-imx/clk-imx51-imx53.c b/arch/arm/mach-imx/clk-imx51-imx53.c index fb7cb841b64c..0f39f8c93b94 100644 --- a/arch/arm/mach-imx/clk-imx51-imx53.c +++ b/arch/arm/mach-imx/clk-imx51-imx53.c @@ -83,6 +83,7 @@ enum imx5_clks { ssi2_root_gate, ssi3_root_gate, ssi_ext1_gate, ssi_ext2_gate, epit1_ipg_gate, epit1_hf_gate, epit2_ipg_gate, epit2_hf_gate, can_sel, can1_serial_gate, can1_ipg_gate, + owire_gate, clk_max }; @@ -233,12 +234,13 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil, clk[epit1_hf_gate] = imx_clk_gate2("epit1_hf_gate", "per_root", MXC_CCM_CCGR2, 4); clk[epit2_ipg_gate] = imx_clk_gate2("epit2_ipg_gate", "ipg", MXC_CCM_CCGR2, 6); clk[epit2_hf_gate] = imx_clk_gate2("epit2_hf_gate", "per_root", MXC_CCM_CCGR2, 8); + clk[owire_gate] = imx_clk_gate2("owire_gate", "per_root", MXC_CCM_CCGR2, 22); for (i = 0; i < ARRAY_SIZE(clk); i++) if (IS_ERR(clk[i])) pr_err("i.MX5 clk %d: register failed with %ld\n", i, PTR_ERR(clk[i])); - + clk_register_clkdev(clk[gpt_hf_gate], "per", "imx-gpt.0"); clk_register_clkdev(clk[gpt_ipg_gate], "ipg", "imx-gpt.0"); clk_register_clkdev(clk[uart1_per_gate], "per", "imx21-uart.0"); From a82b7b9c8b6a44b0f3983fb21dff90a9d18d9539 Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Tue, 29 Jan 2013 16:46:19 +0100 Subject: [PATCH 2233/4607] ARM: dts: Add device tree entry for onewire master on i.MX53 Signed-off-by: Martin Fuzzey Acked-by: Sascha Hauer Acked-by: Evgeniy Polyakov Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx53.dtsi | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi index a9a3adde4e5a..d05aa215c7f9 100644 --- a/arch/arm/boot/dts/imx53.dtsi +++ b/arch/arm/boot/dts/imx53.dtsi @@ -432,6 +432,14 @@ }; }; + owire { + pinctrl_owire_1: owiregrp-1 { + fsl,pins = < + 1166 0x80000000 /* MX53_PAD_GPIO_18__OWIRE_LINE */ + >; + }; + }; + uart1 { pinctrl_uart1_1: uart1grp-1 { fsl,pins = < @@ -623,6 +631,13 @@ status = "disabled"; }; + owire: owire@63fa4000 { + compatible = "fsl,imx53-owire", "fsl,imx21-owire"; + reg = <0x63fa4000 0x4000>; + clocks = <&clks 159>; + status = "disabled"; + }; + ecspi2: ecspi@63fac000 { #address-cells = <1>; #size-cells = <0>; From 46743dd66224a2bc3bfb4a45b27bfc2a41bfda24 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Wed, 30 Jan 2013 17:33:44 -0500 Subject: [PATCH 2234/4607] ARM: dts: i.MX6: Add regulator delay support For ANATOP LDOs, vddcpu, vddsoc and vddpu have step time settings in the misc2 register, need to add necessary step time info for these three LDOs, then regulator driver can add necessary delay based on these settings. offset 0x170: bit [24-25]: vddcpu bit [26-27]: vddpu bit [28-29]: vddsoc field definition: 0'b00: 64 cycles of 24M clock; 0'b01: 128 cycles of 24M clock; 0'b02: 256 cycles of 24M clock; 0'b03: 512 cycles of 24M clock; Signed-off-by: Anson Huang Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6q.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi index ec092d294406..ad10d82c6160 100644 --- a/arch/arm/boot/dts/imx6q.dtsi +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -487,6 +487,9 @@ anatop-reg-offset = <0x140>; anatop-vol-bit-shift = <0>; anatop-vol-bit-width = <5>; + anatop-delay-reg-offset = <0x170>; + anatop-delay-bit-shift = <24>; + anatop-delay-bit-width = <2>; anatop-min-bit-val = <1>; anatop-min-voltage = <725000>; anatop-max-voltage = <1450000>; @@ -501,6 +504,9 @@ anatop-reg-offset = <0x140>; anatop-vol-bit-shift = <9>; anatop-vol-bit-width = <5>; + anatop-delay-reg-offset = <0x170>; + anatop-delay-bit-shift = <26>; + anatop-delay-bit-width = <2>; anatop-min-bit-val = <1>; anatop-min-voltage = <725000>; anatop-max-voltage = <1450000>; @@ -515,6 +521,9 @@ anatop-reg-offset = <0x140>; anatop-vol-bit-shift = <18>; anatop-vol-bit-width = <5>; + anatop-delay-reg-offset = <0x170>; + anatop-delay-bit-shift = <28>; + anatop-delay-bit-width = <2>; anatop-min-bit-val = <1>; anatop-min-voltage = <725000>; anatop-max-voltage = <1450000>; From 4bacf2a3fc07a89b4c0dca5a90e68eace9ea8813 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 4 Feb 2013 13:51:00 +0800 Subject: [PATCH 2235/4607] ARM: dts: rename imx6q.dtsi to imx6qdl.dtsi i.MX6 Quad and i.MX6 DualLite is similar enough to share one dtsi file, so rename imx6q.dtsi to imx6qdl.dtsi preparing for the addition of imx6dl support. Another member of i.MX6 series i.MX6 SoloLite is different enough from the other two, so it will stand as a separate dtsi. That's why we rename to imx6qdl.dtsi not imx6.dtsi. Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6q-arm2.dts | 2 +- arch/arm/boot/dts/imx6q-sabreauto.dts | 2 +- arch/arm/boot/dts/imx6q-sabrelite.dts | 2 +- arch/arm/boot/dts/imx6q-sabresd.dts | 2 +- arch/arm/boot/dts/{imx6q.dtsi => imx6qdl.dtsi} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename arch/arm/boot/dts/{imx6q.dtsi => imx6qdl.dtsi} (100%) diff --git a/arch/arm/boot/dts/imx6q-arm2.dts b/arch/arm/boot/dts/imx6q-arm2.dts index 53eb241fa5ad..cd5813da1cee 100644 --- a/arch/arm/boot/dts/imx6q-arm2.dts +++ b/arch/arm/boot/dts/imx6q-arm2.dts @@ -11,7 +11,7 @@ */ /dts-v1/; -/include/ "imx6q.dtsi" +/include/ "imx6qdl.dtsi" / { model = "Freescale i.MX6 Quad Armadillo2 Board"; diff --git a/arch/arm/boot/dts/imx6q-sabreauto.dts b/arch/arm/boot/dts/imx6q-sabreauto.dts index 656d489122fe..5baa82427f3f 100644 --- a/arch/arm/boot/dts/imx6q-sabreauto.dts +++ b/arch/arm/boot/dts/imx6q-sabreauto.dts @@ -11,7 +11,7 @@ */ /dts-v1/; -/include/ "imx6q.dtsi" +/include/ "imx6qdl.dtsi" / { model = "Freescale i.MX6 Quad SABRE Automotive Board"; diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index 2ce355cd05e5..6aaaf69e88eb 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -11,7 +11,7 @@ */ /dts-v1/; -/include/ "imx6q.dtsi" +/include/ "imx6qdl.dtsi" / { model = "Freescale i.MX6 Quad SABRE Lite Board"; diff --git a/arch/arm/boot/dts/imx6q-sabresd.dts b/arch/arm/boot/dts/imx6q-sabresd.dts index 2dea304a7980..a48528f6f042 100644 --- a/arch/arm/boot/dts/imx6q-sabresd.dts +++ b/arch/arm/boot/dts/imx6q-sabresd.dts @@ -11,7 +11,7 @@ */ /dts-v1/; -/include/ "imx6q.dtsi" +/include/ "imx6qdl.dtsi" / { model = "Freescale i.MX6Q SABRE Smart Device Board"; diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi similarity index 100% rename from arch/arm/boot/dts/imx6q.dtsi rename to arch/arm/boot/dts/imx6qdl.dtsi From 7c1da5854f3a4af7e44c059fdde750119c05f1a4 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 4 Feb 2013 23:09:16 +0800 Subject: [PATCH 2236/4607] ARM: dts: add dtsi for imx6q and imx6dl Add dtsi for imx6q and imx6dl with non-common blocks moved into there. Major differences between imx6dl and imx6q: * Dual vs. Quad cores * single vs. dual IPU * 128 vs. 256 KB OCRAM * imx6q: ECSPI5, OpenVG (GC355), SATA * imx6dl: I2C4, PXP, EPDC, LCDIF * iomuxc/pads definition Signed-off-by: Shawn Guo --- arch/arm/boot/dts/imx6dl.dtsi | 59 +++++ arch/arm/boot/dts/imx6q-arm2.dts | 2 +- arch/arm/boot/dts/imx6q-sabreauto.dts | 2 +- arch/arm/boot/dts/imx6q-sabrelite.dts | 2 +- arch/arm/boot/dts/imx6q-sabresd.dts | 2 +- arch/arm/boot/dts/imx6q.dtsi | 296 ++++++++++++++++++++++++++ arch/arm/boot/dts/imx6qdl.dtsi | 277 ------------------------ 7 files changed, 359 insertions(+), 281 deletions(-) create mode 100644 arch/arm/boot/dts/imx6dl.dtsi create mode 100644 arch/arm/boot/dts/imx6q.dtsi diff --git a/arch/arm/boot/dts/imx6dl.dtsi b/arch/arm/boot/dts/imx6dl.dtsi new file mode 100644 index 000000000000..63fafe2a606c --- /dev/null +++ b/arch/arm/boot/dts/imx6dl.dtsi @@ -0,0 +1,59 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * 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/ "imx6qdl.dtsi" + +/ { + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + next-level-cache = <&L2>; + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + reg = <1>; + next-level-cache = <&L2>; + }; + }; + + soc { + aips1: aips-bus@02000000 { + pxp: pxp@020f0000 { + reg = <0x020f0000 0x4000>; + interrupts = <0 98 0x04>; + }; + + epdc: epdc@020f4000 { + reg = <0x020f4000 0x4000>; + interrupts = <0 97 0x04>; + }; + + lcdif: lcdif@020f8000 { + reg = <0x020f8000 0x4000>; + interrupts = <0 39 0x04>; + }; + }; + + aips2: aips-bus@02100000 { + i2c4: i2c@021f8000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx1-i2c"; + reg = <0x021f8000 0x4000>; + interrupts = <0 35 0x04>; + status = "disabled"; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/imx6q-arm2.dts b/arch/arm/boot/dts/imx6q-arm2.dts index cd5813da1cee..53eb241fa5ad 100644 --- a/arch/arm/boot/dts/imx6q-arm2.dts +++ b/arch/arm/boot/dts/imx6q-arm2.dts @@ -11,7 +11,7 @@ */ /dts-v1/; -/include/ "imx6qdl.dtsi" +/include/ "imx6q.dtsi" / { model = "Freescale i.MX6 Quad Armadillo2 Board"; diff --git a/arch/arm/boot/dts/imx6q-sabreauto.dts b/arch/arm/boot/dts/imx6q-sabreauto.dts index 5baa82427f3f..656d489122fe 100644 --- a/arch/arm/boot/dts/imx6q-sabreauto.dts +++ b/arch/arm/boot/dts/imx6q-sabreauto.dts @@ -11,7 +11,7 @@ */ /dts-v1/; -/include/ "imx6qdl.dtsi" +/include/ "imx6q.dtsi" / { model = "Freescale i.MX6 Quad SABRE Automotive Board"; diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts index 6aaaf69e88eb..2ce355cd05e5 100644 --- a/arch/arm/boot/dts/imx6q-sabrelite.dts +++ b/arch/arm/boot/dts/imx6q-sabrelite.dts @@ -11,7 +11,7 @@ */ /dts-v1/; -/include/ "imx6qdl.dtsi" +/include/ "imx6q.dtsi" / { model = "Freescale i.MX6 Quad SABRE Lite Board"; diff --git a/arch/arm/boot/dts/imx6q-sabresd.dts b/arch/arm/boot/dts/imx6q-sabresd.dts index a48528f6f042..2dea304a7980 100644 --- a/arch/arm/boot/dts/imx6q-sabresd.dts +++ b/arch/arm/boot/dts/imx6q-sabresd.dts @@ -11,7 +11,7 @@ */ /dts-v1/; -/include/ "imx6qdl.dtsi" +/include/ "imx6q.dtsi" / { model = "Freescale i.MX6Q SABRE Smart Device Board"; diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi new file mode 100644 index 000000000000..cba021eb035e --- /dev/null +++ b/arch/arm/boot/dts/imx6q.dtsi @@ -0,0 +1,296 @@ + +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * 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/ "imx6qdl.dtsi" + +/ { + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + next-level-cache = <&L2>; + operating-points = < + /* kHz uV */ + 1200000 1275000 + 996000 1250000 + 792000 1150000 + 396000 950000 + >; + clock-latency = <61036>; /* two CLK32 periods */ + clocks = <&clks 104>, <&clks 6>, <&clks 16>, + <&clks 17>, <&clks 170>; + clock-names = "arm", "pll2_pfd2_396m", "step", + "pll1_sw", "pll1_sys"; + arm-supply = <®_arm>; + pu-supply = <®_pu>; + soc-supply = <®_soc>; + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + reg = <1>; + next-level-cache = <&L2>; + }; + + cpu@2 { + compatible = "arm,cortex-a9"; + reg = <2>; + next-level-cache = <&L2>; + }; + + cpu@3 { + compatible = "arm,cortex-a9"; + reg = <3>; + next-level-cache = <&L2>; + }; + }; + + soc { + aips-bus@02000000 { /* AIPS1 */ + spba-bus@02000000 { + ecspi5: ecspi@02018000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi"; + reg = <0x02018000 0x4000>; + interrupts = <0 35 0x04>; + clocks = <&clks 116>, <&clks 116>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + }; + + iomuxc: iomuxc@020e0000 { + compatible = "fsl,imx6q-iomuxc"; + reg = <0x020e0000 0x4000>; + + /* shared pinctrl settings */ + audmux { + pinctrl_audmux_1: audmux-1 { + fsl,pins = < + 18 0x80000000 /* MX6Q_PAD_SD2_DAT0__AUDMUX_AUD4_RXD */ + 1586 0x80000000 /* MX6Q_PAD_SD2_DAT3__AUDMUX_AUD4_TXC */ + 11 0x80000000 /* MX6Q_PAD_SD2_DAT2__AUDMUX_AUD4_TXD */ + 3 0x80000000 /* MX6Q_PAD_SD2_DAT1__AUDMUX_AUD4_TXFS */ + >; + }; + }; + + ecspi1 { + pinctrl_ecspi1_1: ecspi1grp-1 { + fsl,pins = < + 101 0x100b1 /* MX6Q_PAD_EIM_D17__ECSPI1_MISO */ + 109 0x100b1 /* MX6Q_PAD_EIM_D18__ECSPI1_MOSI */ + 94 0x100b1 /* MX6Q_PAD_EIM_D16__ECSPI1_SCLK */ + >; + }; + }; + + enet { + pinctrl_enet_1: enetgrp-1 { + fsl,pins = < + 695 0x1b0b0 /* MX6Q_PAD_ENET_MDIO__ENET_MDIO */ + 756 0x1b0b0 /* MX6Q_PAD_ENET_MDC__ENET_MDC */ + 24 0x1b0b0 /* MX6Q_PAD_RGMII_TXC__ENET_RGMII_TXC */ + 30 0x1b0b0 /* MX6Q_PAD_RGMII_TD0__ENET_RGMII_TD0 */ + 34 0x1b0b0 /* MX6Q_PAD_RGMII_TD1__ENET_RGMII_TD1 */ + 39 0x1b0b0 /* MX6Q_PAD_RGMII_TD2__ENET_RGMII_TD2 */ + 44 0x1b0b0 /* MX6Q_PAD_RGMII_TD3__ENET_RGMII_TD3 */ + 56 0x1b0b0 /* MX6Q_PAD_RGMII_TX_CTL__RGMII_TX_CTL */ + 702 0x1b0b0 /* MX6Q_PAD_ENET_REF_CLK__ENET_TX_CLK */ + 74 0x1b0b0 /* MX6Q_PAD_RGMII_RXC__ENET_RGMII_RXC */ + 52 0x1b0b0 /* MX6Q_PAD_RGMII_RD0__ENET_RGMII_RD0 */ + 61 0x1b0b0 /* MX6Q_PAD_RGMII_RD1__ENET_RGMII_RD1 */ + 66 0x1b0b0 /* MX6Q_PAD_RGMII_RD2__ENET_RGMII_RD2 */ + 70 0x1b0b0 /* MX6Q_PAD_RGMII_RD3__ENET_RGMII_RD3 */ + 48 0x1b0b0 /* MX6Q_PAD_RGMII_RX_CTL__RGMII_RX_CTL */ + 1033 0x4001b0a8 /* MX6Q_PAD_GPIO_16__ENET_ANATOP_ETHERNET_REF_OUT*/ + >; + }; + + pinctrl_enet_2: enetgrp-2 { + fsl,pins = < + 890 0x1b0b0 /* MX6Q_PAD_KEY_COL1__ENET_MDIO */ + 909 0x1b0b0 /* MX6Q_PAD_KEY_COL2__ENET_MDC */ + 24 0x1b0b0 /* MX6Q_PAD_RGMII_TXC__ENET_RGMII_TXC */ + 30 0x1b0b0 /* MX6Q_PAD_RGMII_TD0__ENET_RGMII_TD0 */ + 34 0x1b0b0 /* MX6Q_PAD_RGMII_TD1__ENET_RGMII_TD1 */ + 39 0x1b0b0 /* MX6Q_PAD_RGMII_TD2__ENET_RGMII_TD2 */ + 44 0x1b0b0 /* MX6Q_PAD_RGMII_TD3__ENET_RGMII_TD3 */ + 56 0x1b0b0 /* MX6Q_PAD_RGMII_TX_CTL__RGMII_TX_CTL */ + 702 0x1b0b0 /* MX6Q_PAD_ENET_REF_CLK__ENET_TX_CLK */ + 74 0x1b0b0 /* MX6Q_PAD_RGMII_RXC__ENET_RGMII_RXC */ + 52 0x1b0b0 /* MX6Q_PAD_RGMII_RD0__ENET_RGMII_RD0 */ + 61 0x1b0b0 /* MX6Q_PAD_RGMII_RD1__ENET_RGMII_RD1 */ + 66 0x1b0b0 /* MX6Q_PAD_RGMII_RD2__ENET_RGMII_RD2 */ + 70 0x1b0b0 /* MX6Q_PAD_RGMII_RD3__ENET_RGMII_RD3 */ + 48 0x1b0b0 /* MX6Q_PAD_RGMII_RX_CTL__RGMII_RX_CTL */ + >; + }; + }; + + gpmi-nand { + pinctrl_gpmi_nand_1: gpmi-nand-1 { + fsl,pins = < + 1328 0xb0b1 /* MX6Q_PAD_NANDF_CLE__RAWNAND_CLE */ + 1336 0xb0b1 /* MX6Q_PAD_NANDF_ALE__RAWNAND_ALE */ + 1344 0xb0b1 /* MX6Q_PAD_NANDF_WP_B__RAWNAND_RESETN */ + 1352 0xb000 /* MX6Q_PAD_NANDF_RB0__RAWNAND_READY0 */ + 1360 0xb0b1 /* MX6Q_PAD_NANDF_CS0__RAWNAND_CE0N */ + 1365 0xb0b1 /* MX6Q_PAD_NANDF_CS1__RAWNAND_CE1N */ + 1371 0xb0b1 /* MX6Q_PAD_NANDF_CS2__RAWNAND_CE2N */ + 1378 0xb0b1 /* MX6Q_PAD_NANDF_CS3__RAWNAND_CE3N */ + 1387 0xb0b1 /* MX6Q_PAD_SD4_CMD__RAWNAND_RDN */ + 1393 0xb0b1 /* MX6Q_PAD_SD4_CLK__RAWNAND_WRN */ + 1397 0xb0b1 /* MX6Q_PAD_NANDF_D0__RAWNAND_D0 */ + 1405 0xb0b1 /* MX6Q_PAD_NANDF_D1__RAWNAND_D1 */ + 1413 0xb0b1 /* MX6Q_PAD_NANDF_D2__RAWNAND_D2 */ + 1421 0xb0b1 /* MX6Q_PAD_NANDF_D3__RAWNAND_D3 */ + 1429 0xb0b1 /* MX6Q_PAD_NANDF_D4__RAWNAND_D4 */ + 1437 0xb0b1 /* MX6Q_PAD_NANDF_D5__RAWNAND_D5 */ + 1445 0xb0b1 /* MX6Q_PAD_NANDF_D6__RAWNAND_D6 */ + 1453 0xb0b1 /* MX6Q_PAD_NANDF_D7__RAWNAND_D7 */ + 1463 0x00b1 /* MX6Q_PAD_SD4_DAT0__RAWNAND_DQS */ + >; + }; + }; + + i2c1 { + pinctrl_i2c1_1: i2c1grp-1 { + fsl,pins = < + 137 0x4001b8b1 /* MX6Q_PAD_EIM_D21__I2C1_SCL */ + 196 0x4001b8b1 /* MX6Q_PAD_EIM_D28__I2C1_SDA */ + >; + }; + }; + + uart1 { + pinctrl_uart1_1: uart1grp-1 { + fsl,pins = < + 1140 0x1b0b1 /* MX6Q_PAD_CSI0_DAT10__UART1_TXD */ + 1148 0x1b0b1 /* MX6Q_PAD_CSI0_DAT11__UART1_RXD */ + >; + }; + }; + + uart2 { + pinctrl_uart2_1: uart2grp-1 { + fsl,pins = < + 183 0x1b0b1 /* MX6Q_PAD_EIM_D26__UART2_TXD */ + 191 0x1b0b1 /* MX6Q_PAD_EIM_D27__UART2_RXD */ + >; + }; + }; + + uart4 { + pinctrl_uart4_1: uart4grp-1 { + fsl,pins = < + 877 0x1b0b1 /* MX6Q_PAD_KEY_COL0__UART4_TXD */ + 885 0x1b0b1 /* MX6Q_PAD_KEY_ROW0__UART4_RXD */ + >; + }; + }; + + usbotg { + pinctrl_usbotg_1: usbotggrp-1 { + fsl,pins = < + 1592 0x17059 /* MX6Q_PAD_GPIO_1__ANATOP_USBOTG_ID */ + >; + }; + }; + + usdhc2 { + pinctrl_usdhc2_1: usdhc2grp-1 { + fsl,pins = < + 1577 0x17059 /* MX6Q_PAD_SD2_CMD__USDHC2_CMD */ + 1569 0x10059 /* MX6Q_PAD_SD2_CLK__USDHC2_CLK */ + 16 0x17059 /* MX6Q_PAD_SD2_DAT0__USDHC2_DAT0 */ + 0 0x17059 /* MX6Q_PAD_SD2_DAT1__USDHC2_DAT1 */ + 8 0x17059 /* MX6Q_PAD_SD2_DAT2__USDHC2_DAT2 */ + 1583 0x17059 /* MX6Q_PAD_SD2_DAT3__USDHC2_DAT3 */ + 1430 0x17059 /* MX6Q_PAD_NANDF_D4__USDHC2_DAT4 */ + 1438 0x17059 /* MX6Q_PAD_NANDF_D5__USDHC2_DAT5 */ + 1446 0x17059 /* MX6Q_PAD_NANDF_D6__USDHC2_DAT6 */ + 1454 0x17059 /* MX6Q_PAD_NANDF_D7__USDHC2_DAT7 */ + >; + }; + }; + + usdhc3 { + pinctrl_usdhc3_1: usdhc3grp-1 { + fsl,pins = < + 1273 0x17059 /* MX6Q_PAD_SD3_CMD__USDHC3_CMD */ + 1281 0x10059 /* MX6Q_PAD_SD3_CLK__USDHC3_CLK */ + 1289 0x17059 /* MX6Q_PAD_SD3_DAT0__USDHC3_DAT0 */ + 1297 0x17059 /* MX6Q_PAD_SD3_DAT1__USDHC3_DAT1 */ + 1305 0x17059 /* MX6Q_PAD_SD3_DAT2__USDHC3_DAT2 */ + 1312 0x17059 /* MX6Q_PAD_SD3_DAT3__USDHC3_DAT3 */ + 1265 0x17059 /* MX6Q_PAD_SD3_DAT4__USDHC3_DAT4 */ + 1257 0x17059 /* MX6Q_PAD_SD3_DAT5__USDHC3_DAT5 */ + 1249 0x17059 /* MX6Q_PAD_SD3_DAT6__USDHC3_DAT6 */ + 1241 0x17059 /* MX6Q_PAD_SD3_DAT7__USDHC3_DAT7 */ + >; + }; + + pinctrl_usdhc3_2: usdhc3grp-2 { + fsl,pins = < + 1273 0x17059 /* MX6Q_PAD_SD3_CMD__USDHC3_CMD */ + 1281 0x10059 /* MX6Q_PAD_SD3_CLK__USDHC3_CLK */ + 1289 0x17059 /* MX6Q_PAD_SD3_DAT0__USDHC3_DAT0 */ + 1297 0x17059 /* MX6Q_PAD_SD3_DAT1__USDHC3_DAT1 */ + 1305 0x17059 /* MX6Q_PAD_SD3_DAT2__USDHC3_DAT2 */ + 1312 0x17059 /* MX6Q_PAD_SD3_DAT3__USDHC3_DAT3 */ + >; + }; + }; + + usdhc4 { + pinctrl_usdhc4_1: usdhc4grp-1 { + fsl,pins = < + 1386 0x17059 /* MX6Q_PAD_SD4_CMD__USDHC4_CMD */ + 1392 0x10059 /* MX6Q_PAD_SD4_CLK__USDHC4_CLK */ + 1462 0x17059 /* MX6Q_PAD_SD4_DAT0__USDHC4_DAT0 */ + 1470 0x17059 /* MX6Q_PAD_SD4_DAT1__USDHC4_DAT1 */ + 1478 0x17059 /* MX6Q_PAD_SD4_DAT2__USDHC4_DAT2 */ + 1486 0x17059 /* MX6Q_PAD_SD4_DAT3__USDHC4_DAT3 */ + 1493 0x17059 /* MX6Q_PAD_SD4_DAT4__USDHC4_DAT4 */ + 1501 0x17059 /* MX6Q_PAD_SD4_DAT5__USDHC4_DAT5 */ + 1509 0x17059 /* MX6Q_PAD_SD4_DAT6__USDHC4_DAT6 */ + 1517 0x17059 /* MX6Q_PAD_SD4_DAT7__USDHC4_DAT7 */ + >; + }; + + pinctrl_usdhc4_2: usdhc4grp-2 { + fsl,pins = < + 1386 0x17059 /* MX6Q_PAD_SD4_CMD__USDHC4_CMD */ + 1392 0x10059 /* MX6Q_PAD_SD4_CLK__USDHC4_CLK */ + 1462 0x17059 /* MX6Q_PAD_SD4_DAT0__USDHC4_DAT0 */ + 1470 0x17059 /* MX6Q_PAD_SD4_DAT1__USDHC4_DAT1 */ + 1478 0x17059 /* MX6Q_PAD_SD4_DAT2__USDHC4_DAT2 */ + 1486 0x17059 /* MX6Q_PAD_SD4_DAT3__USDHC4_DAT3 */ + >; + }; + }; + }; + }; + + ipu2: ipu@02800000 { + #crtc-cells = <1>; + compatible = "fsl,imx6q-ipu"; + reg = <0x02800000 0x400000>; + interrupts = <0 8 0x4 0 7 0x4>; + clocks = <&clks 133>, <&clks 134>, <&clks 137>; + clock-names = "bus", "di0", "di1"; + }; + }; +}; diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index ad10d82c6160..06ec460b4581 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -28,50 +28,6 @@ gpio6 = &gpio7; }; - cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu@0 { - compatible = "arm,cortex-a9"; - reg = <0>; - next-level-cache = <&L2>; - operating-points = < - /* kHz uV */ - 1200000 1275000 - 996000 1250000 - 792000 1150000 - 396000 950000 - >; - clock-latency = <61036>; /* two CLK32 periods */ - clocks = <&clks 104>, <&clks 6>, <&clks 16>, - <&clks 17>, <&clks 170>; - clock-names = "arm", "pll2_pfd2_396m", "step", - "pll1_sw", "pll1_sys"; - arm-supply = <®_arm>; - pu-supply = <®_pu>; - soc-supply = <®_soc>; - }; - - cpu@1 { - compatible = "arm,cortex-a9"; - reg = <1>; - next-level-cache = <&L2>; - }; - - cpu@2 { - compatible = "arm,cortex-a9"; - reg = <2>; - next-level-cache = <&L2>; - }; - - cpu@3 { - compatible = "arm,cortex-a9"; - reg = <3>; - next-level-cache = <&L2>; - }; - }; - intc: interrupt-controller@00a01000 { compatible = "arm,cortex-a9-gic"; #interrupt-cells = <3>; @@ -208,17 +164,6 @@ status = "disabled"; }; - ecspi5: ecspi@02018000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi"; - reg = <0x02018000 0x4000>; - interrupts = <0 35 0x04>; - clocks = <&clks 116>, <&clks 116>; - clock-names = "ipg", "per"; - status = "disabled"; - }; - uart1: serial@02020000 { compatible = "fsl,imx6q-uart", "fsl,imx21-uart"; reg = <0x02020000 0x4000>; @@ -584,219 +529,6 @@ reg = <0x020e0000 0x38>; }; - iomuxc: iomuxc@020e0000 { - compatible = "fsl,imx6q-iomuxc"; - reg = <0x020e0000 0x4000>; - - /* shared pinctrl settings */ - audmux { - pinctrl_audmux_1: audmux-1 { - fsl,pins = < - 18 0x80000000 /* MX6Q_PAD_SD2_DAT0__AUDMUX_AUD4_RXD */ - 1586 0x80000000 /* MX6Q_PAD_SD2_DAT3__AUDMUX_AUD4_TXC */ - 11 0x80000000 /* MX6Q_PAD_SD2_DAT2__AUDMUX_AUD4_TXD */ - 3 0x80000000 /* MX6Q_PAD_SD2_DAT1__AUDMUX_AUD4_TXFS */ - >; - }; - }; - - ecspi1 { - pinctrl_ecspi1_1: ecspi1grp-1 { - fsl,pins = < - 101 0x100b1 /* MX6Q_PAD_EIM_D17__ECSPI1_MISO */ - 109 0x100b1 /* MX6Q_PAD_EIM_D18__ECSPI1_MOSI */ - 94 0x100b1 /* MX6Q_PAD_EIM_D16__ECSPI1_SCLK */ - >; - }; - }; - - enet { - pinctrl_enet_1: enetgrp-1 { - fsl,pins = < - 695 0x1b0b0 /* MX6Q_PAD_ENET_MDIO__ENET_MDIO */ - 756 0x1b0b0 /* MX6Q_PAD_ENET_MDC__ENET_MDC */ - 24 0x1b0b0 /* MX6Q_PAD_RGMII_TXC__ENET_RGMII_TXC */ - 30 0x1b0b0 /* MX6Q_PAD_RGMII_TD0__ENET_RGMII_TD0 */ - 34 0x1b0b0 /* MX6Q_PAD_RGMII_TD1__ENET_RGMII_TD1 */ - 39 0x1b0b0 /* MX6Q_PAD_RGMII_TD2__ENET_RGMII_TD2 */ - 44 0x1b0b0 /* MX6Q_PAD_RGMII_TD3__ENET_RGMII_TD3 */ - 56 0x1b0b0 /* MX6Q_PAD_RGMII_TX_CTL__RGMII_TX_CTL */ - 702 0x1b0b0 /* MX6Q_PAD_ENET_REF_CLK__ENET_TX_CLK */ - 74 0x1b0b0 /* MX6Q_PAD_RGMII_RXC__ENET_RGMII_RXC */ - 52 0x1b0b0 /* MX6Q_PAD_RGMII_RD0__ENET_RGMII_RD0 */ - 61 0x1b0b0 /* MX6Q_PAD_RGMII_RD1__ENET_RGMII_RD1 */ - 66 0x1b0b0 /* MX6Q_PAD_RGMII_RD2__ENET_RGMII_RD2 */ - 70 0x1b0b0 /* MX6Q_PAD_RGMII_RD3__ENET_RGMII_RD3 */ - 48 0x1b0b0 /* MX6Q_PAD_RGMII_RX_CTL__RGMII_RX_CTL */ - 1033 0x4001b0a8 /* MX6Q_PAD_GPIO_16__ENET_ANATOP_ETHERNET_REF_OUT*/ - >; - }; - - pinctrl_enet_2: enetgrp-2 { - fsl,pins = < - 890 0x1b0b0 /* MX6Q_PAD_KEY_COL1__ENET_MDIO */ - 909 0x1b0b0 /* MX6Q_PAD_KEY_COL2__ENET_MDC */ - 24 0x1b0b0 /* MX6Q_PAD_RGMII_TXC__ENET_RGMII_TXC */ - 30 0x1b0b0 /* MX6Q_PAD_RGMII_TD0__ENET_RGMII_TD0 */ - 34 0x1b0b0 /* MX6Q_PAD_RGMII_TD1__ENET_RGMII_TD1 */ - 39 0x1b0b0 /* MX6Q_PAD_RGMII_TD2__ENET_RGMII_TD2 */ - 44 0x1b0b0 /* MX6Q_PAD_RGMII_TD3__ENET_RGMII_TD3 */ - 56 0x1b0b0 /* MX6Q_PAD_RGMII_TX_CTL__RGMII_TX_CTL */ - 702 0x1b0b0 /* MX6Q_PAD_ENET_REF_CLK__ENET_TX_CLK */ - 74 0x1b0b0 /* MX6Q_PAD_RGMII_RXC__ENET_RGMII_RXC */ - 52 0x1b0b0 /* MX6Q_PAD_RGMII_RD0__ENET_RGMII_RD0 */ - 61 0x1b0b0 /* MX6Q_PAD_RGMII_RD1__ENET_RGMII_RD1 */ - 66 0x1b0b0 /* MX6Q_PAD_RGMII_RD2__ENET_RGMII_RD2 */ - 70 0x1b0b0 /* MX6Q_PAD_RGMII_RD3__ENET_RGMII_RD3 */ - 48 0x1b0b0 /* MX6Q_PAD_RGMII_RX_CTL__RGMII_RX_CTL */ - >; - }; - }; - - gpmi-nand { - pinctrl_gpmi_nand_1: gpmi-nand-1 { - fsl,pins = < - 1328 0xb0b1 /* MX6Q_PAD_NANDF_CLE__RAWNAND_CLE */ - 1336 0xb0b1 /* MX6Q_PAD_NANDF_ALE__RAWNAND_ALE */ - 1344 0xb0b1 /* MX6Q_PAD_NANDF_WP_B__RAWNAND_RESETN */ - 1352 0xb000 /* MX6Q_PAD_NANDF_RB0__RAWNAND_READY0 */ - 1360 0xb0b1 /* MX6Q_PAD_NANDF_CS0__RAWNAND_CE0N */ - 1365 0xb0b1 /* MX6Q_PAD_NANDF_CS1__RAWNAND_CE1N */ - 1371 0xb0b1 /* MX6Q_PAD_NANDF_CS2__RAWNAND_CE2N */ - 1378 0xb0b1 /* MX6Q_PAD_NANDF_CS3__RAWNAND_CE3N */ - 1387 0xb0b1 /* MX6Q_PAD_SD4_CMD__RAWNAND_RDN */ - 1393 0xb0b1 /* MX6Q_PAD_SD4_CLK__RAWNAND_WRN */ - 1397 0xb0b1 /* MX6Q_PAD_NANDF_D0__RAWNAND_D0 */ - 1405 0xb0b1 /* MX6Q_PAD_NANDF_D1__RAWNAND_D1 */ - 1413 0xb0b1 /* MX6Q_PAD_NANDF_D2__RAWNAND_D2 */ - 1421 0xb0b1 /* MX6Q_PAD_NANDF_D3__RAWNAND_D3 */ - 1429 0xb0b1 /* MX6Q_PAD_NANDF_D4__RAWNAND_D4 */ - 1437 0xb0b1 /* MX6Q_PAD_NANDF_D5__RAWNAND_D5 */ - 1445 0xb0b1 /* MX6Q_PAD_NANDF_D6__RAWNAND_D6 */ - 1453 0xb0b1 /* MX6Q_PAD_NANDF_D7__RAWNAND_D7 */ - 1463 0x00b1 /* MX6Q_PAD_SD4_DAT0__RAWNAND_DQS */ - >; - }; - }; - - i2c1 { - pinctrl_i2c1_1: i2c1grp-1 { - fsl,pins = < - 137 0x4001b8b1 /* MX6Q_PAD_EIM_D21__I2C1_SCL */ - 196 0x4001b8b1 /* MX6Q_PAD_EIM_D28__I2C1_SDA */ - >; - }; - }; - - uart1 { - pinctrl_uart1_1: uart1grp-1 { - fsl,pins = < - 1140 0x1b0b1 /* MX6Q_PAD_CSI0_DAT10__UART1_TXD */ - 1148 0x1b0b1 /* MX6Q_PAD_CSI0_DAT11__UART1_RXD */ - >; - }; - }; - - uart2 { - pinctrl_uart2_1: uart2grp-1 { - fsl,pins = < - 183 0x1b0b1 /* MX6Q_PAD_EIM_D26__UART2_TXD */ - 191 0x1b0b1 /* MX6Q_PAD_EIM_D27__UART2_RXD */ - >; - }; - }; - - uart4 { - pinctrl_uart4_1: uart4grp-1 { - fsl,pins = < - 877 0x1b0b1 /* MX6Q_PAD_KEY_COL0__UART4_TXD */ - 885 0x1b0b1 /* MX6Q_PAD_KEY_ROW0__UART4_RXD */ - >; - }; - }; - - usbotg { - pinctrl_usbotg_1: usbotggrp-1 { - fsl,pins = < - 1592 0x17059 /* MX6Q_PAD_GPIO_1__ANATOP_USBOTG_ID */ - >; - }; - }; - - usdhc2 { - pinctrl_usdhc2_1: usdhc2grp-1 { - fsl,pins = < - 1577 0x17059 /* MX6Q_PAD_SD2_CMD__USDHC2_CMD */ - 1569 0x10059 /* MX6Q_PAD_SD2_CLK__USDHC2_CLK */ - 16 0x17059 /* MX6Q_PAD_SD2_DAT0__USDHC2_DAT0 */ - 0 0x17059 /* MX6Q_PAD_SD2_DAT1__USDHC2_DAT1 */ - 8 0x17059 /* MX6Q_PAD_SD2_DAT2__USDHC2_DAT2 */ - 1583 0x17059 /* MX6Q_PAD_SD2_DAT3__USDHC2_DAT3 */ - 1430 0x17059 /* MX6Q_PAD_NANDF_D4__USDHC2_DAT4 */ - 1438 0x17059 /* MX6Q_PAD_NANDF_D5__USDHC2_DAT5 */ - 1446 0x17059 /* MX6Q_PAD_NANDF_D6__USDHC2_DAT6 */ - 1454 0x17059 /* MX6Q_PAD_NANDF_D7__USDHC2_DAT7 */ - >; - }; - }; - - usdhc3 { - pinctrl_usdhc3_1: usdhc3grp-1 { - fsl,pins = < - 1273 0x17059 /* MX6Q_PAD_SD3_CMD__USDHC3_CMD */ - 1281 0x10059 /* MX6Q_PAD_SD3_CLK__USDHC3_CLK */ - 1289 0x17059 /* MX6Q_PAD_SD3_DAT0__USDHC3_DAT0 */ - 1297 0x17059 /* MX6Q_PAD_SD3_DAT1__USDHC3_DAT1 */ - 1305 0x17059 /* MX6Q_PAD_SD3_DAT2__USDHC3_DAT2 */ - 1312 0x17059 /* MX6Q_PAD_SD3_DAT3__USDHC3_DAT3 */ - 1265 0x17059 /* MX6Q_PAD_SD3_DAT4__USDHC3_DAT4 */ - 1257 0x17059 /* MX6Q_PAD_SD3_DAT5__USDHC3_DAT5 */ - 1249 0x17059 /* MX6Q_PAD_SD3_DAT6__USDHC3_DAT6 */ - 1241 0x17059 /* MX6Q_PAD_SD3_DAT7__USDHC3_DAT7 */ - >; - }; - - pinctrl_usdhc3_2: usdhc3grp-2 { - fsl,pins = < - 1273 0x17059 /* MX6Q_PAD_SD3_CMD__USDHC3_CMD */ - 1281 0x10059 /* MX6Q_PAD_SD3_CLK__USDHC3_CLK */ - 1289 0x17059 /* MX6Q_PAD_SD3_DAT0__USDHC3_DAT0 */ - 1297 0x17059 /* MX6Q_PAD_SD3_DAT1__USDHC3_DAT1 */ - 1305 0x17059 /* MX6Q_PAD_SD3_DAT2__USDHC3_DAT2 */ - 1312 0x17059 /* MX6Q_PAD_SD3_DAT3__USDHC3_DAT3 */ - >; - }; - }; - - usdhc4 { - pinctrl_usdhc4_1: usdhc4grp-1 { - fsl,pins = < - 1386 0x17059 /* MX6Q_PAD_SD4_CMD__USDHC4_CMD */ - 1392 0x10059 /* MX6Q_PAD_SD4_CLK__USDHC4_CLK */ - 1462 0x17059 /* MX6Q_PAD_SD4_DAT0__USDHC4_DAT0 */ - 1470 0x17059 /* MX6Q_PAD_SD4_DAT1__USDHC4_DAT1 */ - 1478 0x17059 /* MX6Q_PAD_SD4_DAT2__USDHC4_DAT2 */ - 1486 0x17059 /* MX6Q_PAD_SD4_DAT3__USDHC4_DAT3 */ - 1493 0x17059 /* MX6Q_PAD_SD4_DAT4__USDHC4_DAT4 */ - 1501 0x17059 /* MX6Q_PAD_SD4_DAT5__USDHC4_DAT5 */ - 1509 0x17059 /* MX6Q_PAD_SD4_DAT6__USDHC4_DAT6 */ - 1517 0x17059 /* MX6Q_PAD_SD4_DAT7__USDHC4_DAT7 */ - >; - }; - - pinctrl_usdhc4_2: usdhc4grp-2 { - fsl,pins = < - 1386 0x17059 /* MX6Q_PAD_SD4_CMD__USDHC4_CMD */ - 1392 0x10059 /* MX6Q_PAD_SD4_CLK__USDHC4_CLK */ - 1462 0x17059 /* MX6Q_PAD_SD4_DAT0__USDHC4_DAT0 */ - 1470 0x17059 /* MX6Q_PAD_SD4_DAT1__USDHC4_DAT1 */ - 1478 0x17059 /* MX6Q_PAD_SD4_DAT2__USDHC4_DAT2 */ - 1486 0x17059 /* MX6Q_PAD_SD4_DAT3__USDHC4_DAT3 */ - >; - }; - }; - }; - dcic1: dcic@020e4000 { reg = <0x020e4000 0x4000>; interrupts = <0 124 0x04>; @@ -1064,14 +796,5 @@ clocks = <&clks 130>, <&clks 131>, <&clks 132>; clock-names = "bus", "di0", "di1"; }; - - ipu2: ipu@02800000 { - #crtc-cells = <1>; - compatible = "fsl,imx6q-ipu"; - reg = <0x02800000 0x400000>; - interrupts = <0 8 0x4 0 7 0x4>; - clocks = <&clks 133>, <&clks 134>, <&clks 137>; - clock-names = "bus", "di0", "di1"; - }; }; }; From 1dd2ad7f2b414805de2020d5bf2d5adc3cf6243c Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Thu, 25 Oct 2012 17:17:00 +0200 Subject: [PATCH 2237/4607] openrisc: remove unused current_regs Signed-off-by: Jonas Bonn --- arch/openrisc/include/asm/processor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/openrisc/include/asm/processor.h b/arch/openrisc/include/asm/processor.h index 33691380608e..cab746fa9e87 100644 --- a/arch/openrisc/include/asm/processor.h +++ b/arch/openrisc/include/asm/processor.h @@ -70,7 +70,6 @@ struct thread_struct { */ #define task_pt_regs(task) user_regs(task_thread_info(task)) -#define current_regs() user_regs(current_thread_info()) #define INIT_SP (sizeof(init_stack) + (unsigned long) &init_stack) From 54bd7c510ba027763130eaeb09004ef5780c06e6 Mon Sep 17 00:00:00 2001 From: Stefan Kristiansson Date: Fri, 12 Oct 2012 09:38:18 +0300 Subject: [PATCH 2238/4607] openrisc: avoid using function parameter regs in reset vector The kernel might be invoked through the reset vector, so to preserve parameters passed to it, temp regs that are not in the function parameter range needs to be used. Signed-off-by: Stefan Kristiansson Signed-off-by: Jonas Bonn --- arch/openrisc/kernel/head.S | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/openrisc/kernel/head.S b/arch/openrisc/kernel/head.S index 1088b5fca3bd..46aa940ebd20 100644 --- a/arch/openrisc/kernel/head.S +++ b/arch/openrisc/kernel/head.S @@ -291,9 +291,9 @@ /* Jump to .init code at _start which lives in the .head section * and will be discarded after boot. */ - LOAD_SYMBOL_2_GPR(r4, _start) - tophys (r3,r4) /* MMU disabled */ - l.jr r3 + LOAD_SYMBOL_2_GPR(r15, _start) + tophys (r13,r15) /* MMU disabled */ + l.jr r13 l.nop /* ---[ 0x200: BUS exception ]------------------------------------------- */ From 1b3737d4c5ed2ea90efe30e7f6261f8311d20962 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Wed, 6 Feb 2013 12:58:08 +0000 Subject: [PATCH 2239/4607] openrisc: remove CONFIG_SYMBOL_PREFIX Remove the SYMBOL_PREFIX Kconfig symbol as it's empty anyway. Signed-off-by: James Hogan Cc: Jonas Bonn Cc: linux@lists.openrisc.net Cc: Mike Frysinger Signed-off-by: Jonas Bonn --- arch/openrisc/Kconfig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index 0ac66f67521f..35a4e5f6e71c 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -26,10 +26,6 @@ config OPENRISC config MMU def_bool y -config SYMBOL_PREFIX - string - default "" - config HAVE_DMA_ATTRS def_bool y From 7f81ea7e28c3e4e5b762111dc676b24152f85a3a Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 10 Feb 2013 00:58:20 -0500 Subject: [PATCH 2240/4607] openrisc idle: delete pm_idle pm_idle() on openrisc was dead code. Signed-off-by: Len Brown Cc: linux@lists.openrisc.net Signed-off-by: Jonas Bonn --- arch/openrisc/kernel/idle.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/openrisc/kernel/idle.c b/arch/openrisc/kernel/idle.c index 7d618feb1b72..5e8a3b6d6bc6 100644 --- a/arch/openrisc/kernel/idle.c +++ b/arch/openrisc/kernel/idle.c @@ -39,11 +39,6 @@ void (*powersave) (void) = NULL; -static inline void pm_idle(void) -{ - barrier(); -} - void cpu_idle(void) { set_thread_flag(TIF_POLLING_NRFLAG); From 6d266f63a11bce427504d203834df3c0bb9be9a5 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 10 Feb 2013 11:22:22 -0700 Subject: [PATCH 2241/4607] ARM: OMAP2+: hwmod: add enable_preprogram hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After setup/enable, some IP blocks need some additional setting to indicate the PRCM that they are inactive until they are configured. Some examples on OMAP4 include the AESS and FSUSB IP blocks. To fix this cleanly, this patch adds another optional function pointer, enable_preprogram, to the IP block's hwmod data. The function that is pointed to is called by the hwmod code immediately after the IP block is reset. This version of the patch includes a patch description fix from Felipe. Signed-off-by: Paul Walmsley Signed-off-by: Sebastien Guiriec Cc: Benoît Cousson Cc: Péter Ujfalusi Cc: Felipe Balbi --- arch/arm/mach-omap2/omap_hwmod.c | 18 ++++++++++++++++++ arch/arm/mach-omap2/omap_hwmod.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 4653efb87a27..f37d22c597f9 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -2052,6 +2052,23 @@ static int _omap4_get_context_lost(struct omap_hwmod *oh) return oh->prcm.omap4.context_lost_counter; } +/** + * _enable_preprogram - Pre-program an IP block during the _enable() process + * @oh: struct omap_hwmod * + * + * Some IP blocks (such as AESS) require some additional programming + * after enable before they can enter idle. If a function pointer to + * do so is present in the hwmod data, then call it and pass along the + * return value; otherwise, return 0. + */ +static int __init _enable_preprogram(struct omap_hwmod *oh) +{ + if (!oh->class->enable_preprogram) + return 0; + + return oh->class->enable_preprogram(oh); +} + /** * _enable - enable an omap_hwmod * @oh: struct omap_hwmod * @@ -2156,6 +2173,7 @@ static int _enable(struct omap_hwmod *oh) _update_sysc_cache(oh); _enable_sysc(oh); } + r = _enable_preprogram(oh); } else { if (soc_ops.disable_module) soc_ops.disable_module(oh); diff --git a/arch/arm/mach-omap2/omap_hwmod.h b/arch/arm/mach-omap2/omap_hwmod.h index 3ae852a522f9..41066b4b7a7b 100644 --- a/arch/arm/mach-omap2/omap_hwmod.h +++ b/arch/arm/mach-omap2/omap_hwmod.h @@ -501,6 +501,7 @@ struct omap_hwmod_omap4_prcm { * @rev: revision of the IP class * @pre_shutdown: ptr to fn to be executed immediately prior to device shutdown * @reset: ptr to fn to be executed in place of the standard hwmod reset fn + * @enable_preprogram: ptr to fn to be executed during device enable * * Represent the class of a OMAP hardware "modules" (e.g. timer, * smartreflex, gpio, uart...) @@ -524,6 +525,7 @@ struct omap_hwmod_class { u32 rev; int (*pre_shutdown)(struct omap_hwmod *oh); int (*reset)(struct omap_hwmod *oh); + int (*enable_preprogram)(struct omap_hwmod *oh); }; /** From cf8ba17154b0977e9195b160aef6c934270a08c1 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 10 Feb 2013 11:22:23 -0700 Subject: [PATCH 2242/4607] ASoC: TI AESS: add autogating-enable function, callable from architecture code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a basic header file for the TI AESS IP block, located in the OMAP4 Audio Back-End subsystem. Currently, this header file only contains a function to enable the AESS internal clock auto-gating. This will be used by a subsequent patch to ensure that the AESS won't block the entire chip low-power-idle mode. We wish to be able to place the AESS into idle even when no AESS driver has been compiled in. Signed-off-by: Paul Walmsley Cc: Liam Girdwood Cc: Mark Brown Cc: Péter Ujfalusi Cc: Tony Lindgren Acked-by: Mark Brown --- include/sound/aess.h | 53 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 include/sound/aess.h diff --git a/include/sound/aess.h b/include/sound/aess.h new file mode 100644 index 000000000000..cee0d09fadbd --- /dev/null +++ b/include/sound/aess.h @@ -0,0 +1,53 @@ +/* + * AESS IP block reset + * + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef __SOUND_AESS_H__ +#define __SOUND_AESS_H__ + +#include +#include + +/* + * AESS_AUTO_GATING_ENABLE_OFFSET: offset in bytes of the AESS IP + * block's AESS_AUTO_GATING_ENABLE__1 register from the IP block's + * base address + */ +#define AESS_AUTO_GATING_ENABLE_OFFSET 0x07c + +/* Register bitfields in the AESS_AUTO_GATING_ENABLE__1 register */ +#define AESS_AUTO_GATING_ENABLE_SHIFT 0 + +/** + * aess_enable_autogating - enable AESS internal autogating + * @oh: struct omap_hwmod * + * + * Enable internal autogating on the AESS. This allows the AESS to + * indicate that it is idle to the OMAP PRCM. Returns 0. + */ +static inline void aess_enable_autogating(void __iomem *base) +{ + u32 v; + + /* Set AESS_AUTO_GATING_ENABLE__1.ENABLE to allow idle entry */ + v = 1 << AESS_AUTO_GATING_ENABLE_SHIFT; + writel(v, base + AESS_AUTO_GATING_ENABLE_OFFSET); +} + +#endif /* __SOUND_AESS_H__ */ From c02060d869247215c2ea15fd650c333d30f5b210 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sun, 10 Feb 2013 11:22:23 -0700 Subject: [PATCH 2243/4607] ARM: OMAP4+: AESS: enable internal auto-gating during initial setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable the AESS auto-gating control bit during AESS hwmod setup. This fixes the following boot warning on OMAP4: omap_hwmod: aess: _wait_target_disable failed Without this patch, the AESS IP block does not indicate to the PRCM that it is idle after it is reset. This prevents some types of SoC power management until something sets the auto-gating control bit. Signed-off-by: Paul Walmsley Signed-off-by: Sebastien Guiriec Cc: Benoît Cousson Cc: Péter Ujfalusi Cc: Tony Lindgren --- arch/arm/mach-omap2/Makefile | 2 +- arch/arm/mach-omap2/omap_hwmod.h | 6 +++ arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 1 + arch/arm/mach-omap2/omap_hwmod_reset.c | 52 ++++++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-omap2/omap_hwmod_reset.c diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 947cafe65aef..d88788facf52 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -8,7 +8,7 @@ obj-y := id.o io.o control.o mux.o devices.o fb.o serial.o gpmc.o timer.o pm.o \ omap_device.o sram.o omap-2-3-common = irq.o -hwmod-common = omap_hwmod.o \ +hwmod-common = omap_hwmod.o omap_hwmod_reset.o \ omap_hwmod_common_data.o clock-common = clock.o clock_common_data.o \ clkt_dpll.o clkt_clksel.o diff --git a/arch/arm/mach-omap2/omap_hwmod.h b/arch/arm/mach-omap2/omap_hwmod.h index 41066b4b7a7b..6ec73cbc30c4 100644 --- a/arch/arm/mach-omap2/omap_hwmod.h +++ b/arch/arm/mach-omap2/omap_hwmod.h @@ -672,6 +672,12 @@ extern void __init omap_hwmod_init(void); const char *omap_hwmod_get_main_clk(struct omap_hwmod *oh); +/* + * + */ + +extern int omap_hwmod_aess_preprogram(struct omap_hwmod *oh); + /* * Chip variant-specific hwmod init routines - XXX should be converted * to use initcalls once the initial boot ordering is straightened out diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 793f54ac7d14..c9c251e23147 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -322,6 +322,7 @@ static struct omap_hwmod_class_sysconfig omap44xx_aess_sysc = { static struct omap_hwmod_class omap44xx_aess_hwmod_class = { .name = "aess", .sysc = &omap44xx_aess_sysc, + .enable_preprogram = omap_hwmod_aess_preprogram, }; /* aess */ diff --git a/arch/arm/mach-omap2/omap_hwmod_reset.c b/arch/arm/mach-omap2/omap_hwmod_reset.c new file mode 100644 index 000000000000..bba43fa627d3 --- /dev/null +++ b/arch/arm/mach-omap2/omap_hwmod_reset.c @@ -0,0 +1,52 @@ +/* + * OMAP IP block custom reset and preprogramming stubs + * + * Copyright (C) 2012 Texas Instruments, Inc. + * Paul Walmsley + * + * A small number of IP blocks need custom reset and preprogramming + * functions. The stubs in this file provide a standard way for the + * hwmod code to call these functions, which are to be located under + * drivers/. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#include + +#include + +#include "omap_hwmod.h" + +/** + * omap_hwmod_aess_preprogram - enable AESS internal autogating + * @oh: struct omap_hwmod * + * + * The AESS will not IdleAck to the PRCM until its internal autogating + * is enabled. Since internal autogating is disabled by default after + * AESS reset, we must enable autogating after the hwmod code resets + * the AESS. Returns 0. + */ +int omap_hwmod_aess_preprogram(struct omap_hwmod *oh) +{ + void __iomem *va; + + va = omap_hwmod_get_mpu_rt_va(oh); + if (!va) + return -EINVAL; + + aess_enable_autogating(va); + + return 0; +} From 9f0c5996b73b31b482136462c8118148469c2030 Mon Sep 17 00:00:00 2001 From: Sebastien Guiriec Date: Sun, 10 Feb 2013 11:22:24 -0700 Subject: [PATCH 2244/4607] ARM: OMAP4: hwmod data: Update AESS data with memory bank area Add AESS memory bank data in hwmod in order to provide memory address information to the driver. This version also changes the AESS main clock to use a non-CLKCTRL-based functional clock. These are being removed from the clock data, since they should be handled by the IP block integration code. Without this change, the kernel crashes during boot. Thanks to Tony Lindgren for reporting this during a test merge. Signed-off-by: Sebastien Guiriec [paul@pwsan.com: updated to change the AESS main_clk] Cc: Tony Lindgren Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 44 +++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index c9c251e23147..a30c113a7f5e 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -349,7 +349,7 @@ static struct omap_hwmod omap44xx_aess_hwmod = { .clkdm_name = "abe_clkdm", .mpu_irqs = omap44xx_aess_irqs, .sdma_reqs = omap44xx_aess_sdma_reqs, - .main_clk = "aess_fck", + .main_clk = "aess_fclk", .prcm = { .omap4 = { .clkctrl_offs = OMAP4_CM1_ABE_AESS_CLKCTRL_OFFSET, @@ -4250,6 +4250,27 @@ static struct omap_hwmod_ocp_if omap44xx_l4_cfg__ocp_wp_noc = { static struct omap_hwmod_addr_space omap44xx_aess_addrs[] = { { + .name = "dmem", + .pa_start = 0x40180000, + .pa_end = 0x4018ffff + }, + { + .name = "cmem", + .pa_start = 0x401a0000, + .pa_end = 0x401a1fff + }, + { + .name = "smem", + .pa_start = 0x401c0000, + .pa_end = 0x401c5fff + }, + { + .name = "pmem", + .pa_start = 0x401e0000, + .pa_end = 0x401e1fff + }, + { + .name = "mpu", .pa_start = 0x401f1000, .pa_end = 0x401f13ff, .flags = ADDR_TYPE_RT @@ -4268,6 +4289,27 @@ static struct omap_hwmod_ocp_if __maybe_unused omap44xx_l4_abe__aess = { static struct omap_hwmod_addr_space omap44xx_aess_dma_addrs[] = { { + .name = "dmem_dma", + .pa_start = 0x49080000, + .pa_end = 0x4908ffff + }, + { + .name = "cmem_dma", + .pa_start = 0x490a0000, + .pa_end = 0x490a1fff + }, + { + .name = "smem_dma", + .pa_start = 0x490c0000, + .pa_end = 0x490c5fff + }, + { + .name = "pmem_dma", + .pa_start = 0x490e0000, + .pa_end = 0x490e1fff + }, + { + .name = "dma", .pa_start = 0x490f1000, .pa_end = 0x490f13ff, .flags = ADDR_TYPE_RT From 5cebb23c6cbcfcae1d0586d07898677716f133bc Mon Sep 17 00:00:00 2001 From: Sebastien Guiriec Date: Sun, 10 Feb 2013 11:17:16 -0700 Subject: [PATCH 2245/4607] ARM: OMAP4: hwmod data: Enable AESS hwmod device Enable AESS data in hwmod in order to be able to probe audio driver. Signed-off-by: Sebastien Guiriec Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index a30c113a7f5e..f9084949b1ee 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -6325,7 +6325,7 @@ static struct omap_hwmod_ocp_if *omap44xx_hwmod_ocp_ifs[] __initdata = { &omap44xx_l3_main_1__l3_main_3, &omap44xx_l3_main_2__l3_main_3, &omap44xx_l4_cfg__l3_main_3, - /* &omap44xx_aess__l4_abe, */ + &omap44xx_aess__l4_abe, &omap44xx_dsp__l4_abe, &omap44xx_l3_main_1__l4_abe, &omap44xx_mpu__l4_abe, @@ -6334,8 +6334,8 @@ static struct omap_hwmod_ocp_if *omap44xx_hwmod_ocp_ifs[] __initdata = { &omap44xx_l4_cfg__l4_wkup, &omap44xx_mpu__mpu_private, &omap44xx_l4_cfg__ocp_wp_noc, - /* &omap44xx_l4_abe__aess, */ - /* &omap44xx_l4_abe__aess_dma, */ + &omap44xx_l4_abe__aess, + &omap44xx_l4_abe__aess_dma, &omap44xx_l3_main_2__c2c, &omap44xx_l4_wkup__counter_32k, &omap44xx_l4_cfg__ctrl_module_core, From 626f0a2ff6bc9820b17c17958d5862262556892f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 30 Nov 2012 18:48:35 +0100 Subject: [PATCH 2246/4607] i2c: mxs: Implement arbitrary clock speed derivation algorithm This patch drops the i2c timing tables from this driver and instead derives the timing based from the requested clock sleep. The timing tables were completely wrong anyway when observed on a scope. This new algorithm is also only derived by using a scope, but it seems to produce much more accurate result. Signed-off-by: Marek Vasut Tested-by: Shawn Guo [wsa: changed messages from dev_err to dev_warn] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mxs.c | 94 +++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index e55736077d23..22d8ad353409 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -93,35 +93,6 @@ #define MXS_CMD_I2C_READ (MXS_I2C_CTRL0_SEND_NAK_ON_LAST | \ MXS_I2C_CTRL0_MASTER_MODE) -struct mxs_i2c_speed_config { - uint32_t timing0; - uint32_t timing1; - uint32_t timing2; -}; - -/* - * Timing values for the default 24MHz clock supplied into the i2c block. - * - * The bus can operate at 95kHz or at 400kHz with the following timing - * register configurations. The 100kHz mode isn't present because it's - * values are not stated in the i.MX233/i.MX28 datasheet. The 95kHz mode - * shall be close enough replacement. Therefore when the bus is configured - * for 100kHz operation, 95kHz timing settings are actually loaded. - * - * For details, see i.MX233 [25.4.2 - 25.4.4] and i.MX28 [27.5.2 - 27.5.4]. - */ -static const struct mxs_i2c_speed_config mxs_i2c_95kHz_config = { - .timing0 = 0x00780030, - .timing1 = 0x00800030, - .timing2 = 0x00300030, -}; - -static const struct mxs_i2c_speed_config mxs_i2c_400kHz_config = { - .timing0 = 0x000f0007, - .timing1 = 0x001f000f, - .timing2 = 0x00300030, -}; - /** * struct mxs_i2c_dev - per device, private MXS-I2C data * @@ -137,7 +108,9 @@ struct mxs_i2c_dev { struct completion cmd_complete; int cmd_err; struct i2c_adapter adapter; - const struct mxs_i2c_speed_config *speed; + + uint32_t timing0; + uint32_t timing1; /* DMA support components */ int dma_channel; @@ -153,9 +126,16 @@ static void mxs_i2c_reset(struct mxs_i2c_dev *i2c) { stmp_reset_block(i2c->regs); - writel(i2c->speed->timing0, i2c->regs + MXS_I2C_TIMING0); - writel(i2c->speed->timing1, i2c->regs + MXS_I2C_TIMING1); - writel(i2c->speed->timing2, i2c->regs + MXS_I2C_TIMING2); + /* + * Configure timing for the I2C block. The I2C TIMING2 register has to + * be programmed with this particular magic number. The rest is derived + * from the XTAL speed and requested I2C speed. + * + * For details, see i.MX233 [25.4.2 - 25.4.4] and i.MX28 [27.5.2 - 27.5.4]. + */ + writel(i2c->timing0, i2c->regs + MXS_I2C_TIMING0); + writel(i2c->timing1, i2c->regs + MXS_I2C_TIMING1); + writel(0x00300030, i2c->regs + MXS_I2C_TIMING2); writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET); } @@ -553,6 +533,43 @@ static bool mxs_i2c_dma_filter(struct dma_chan *chan, void *param) return true; } +static void mxs_i2c_derive_timing(struct mxs_i2c_dev *i2c, int speed) +{ + /* The I2C block clock run at 24MHz */ + const uint32_t clk = 24000000; + uint32_t base; + uint16_t high_count, low_count, rcv_count, xmit_count; + struct device *dev = i2c->dev; + + if (speed > 540000) { + dev_warn(dev, "Speed too high (%d Hz), using 540 kHz\n", speed); + speed = 540000; + } else if (speed < 12000) { + dev_warn(dev, "Speed too low (%d Hz), using 12 kHz\n", speed); + speed = 12000; + } + + /* + * The timing derivation algorithm. There is no documentation for this + * algorithm available, it was derived by using the scope and fiddling + * with constants until the result observed on the scope was good enough + * for 20kHz, 50kHz, 100kHz, 200kHz, 300kHz and 400kHz. It should be + * possible to assume the algorithm works for other frequencies as well. + * + * Note it was necessary to cap the frequency on both ends as it's not + * possible to configure completely arbitrary frequency for the I2C bus + * clock. + */ + base = ((clk / speed) - 38) / 2; + high_count = base + 3; + low_count = base - 3; + rcv_count = (high_count * 3) / 4; + xmit_count = low_count / 4; + + i2c->timing0 = (high_count << 16) | rcv_count; + i2c->timing1 = (low_count << 16) | xmit_count; +} + static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c) { uint32_t speed; @@ -572,12 +589,12 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c) } ret = of_property_read_u32(node, "clock-frequency", &speed); - if (ret) + if (ret) { dev_warn(dev, "No I2C speed selected, using 100kHz\n"); - else if (speed == 400000) - i2c->speed = &mxs_i2c_400kHz_config; - else if (speed != 100000) - dev_warn(dev, "Unsupported I2C speed selected, using 100kHz\n"); + speed = 100000; + } + + mxs_i2c_derive_timing(i2c, speed); return 0; } @@ -621,7 +638,6 @@ static int mxs_i2c_probe(struct platform_device *pdev) return err; i2c->dev = dev; - i2c->speed = &mxs_i2c_95kHz_config; init_completion(&i2c->cmd_complete); From 05cf936846ca6aef1b34ba26054728fdbc154558 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Jan 2013 10:45:54 +0100 Subject: [PATCH 2247/4607] i2c: sh_mobile: cosmetic: trivially simplify 2 functions Reduce 2 boolean functions from "if (condition) return 1; return 0;" to "return condition;" Signed-off-by: Guennadi Liakhovetski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index b6e7a83a8296..34024f3a19f5 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -349,20 +349,14 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, return ret; } -static int sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd) +static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd) { - if (pd->pos == -1) - return 1; - - return 0; + return pd->pos == -1; } -static int sh_mobile_i2c_is_last_byte(struct sh_mobile_i2c_data *pd) +static bool sh_mobile_i2c_is_last_byte(struct sh_mobile_i2c_data *pd) { - if (pd->pos == (pd->msg->len - 1)) - return 1; - - return 0; + return pd->pos == pd->msg->len - 1; } static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd, From 5687265b3127024089dc0b25956772405b9f53d3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Jan 2013 10:45:55 +0100 Subject: [PATCH 2248/4607] i2c: sh_mobile: fix timeout error handling In a timeout case return an error immediately from the driver's .master_xfer() method, instead of continuing and letting higher layers fail. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 34024f3a19f5..daaf0ebe8177 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -521,8 +521,11 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, k = wait_event_timeout(pd->wait, pd->sr & (ICSR_TACK | SW_DONE), 5 * HZ); - if (!k) + if (!k) { dev_err(pd->dev, "Transfer request timed out\n"); + err = -ETIMEDOUT; + break; + } retry_count = 1000; again: From 4b3823184f80c1d3950e5e63e2303653f2decdf8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Jan 2013 10:45:56 +0100 Subject: [PATCH 2249/4607] i2c: sh_mobile: eliminate an open-coded "goto" loop Eliminate an open-coded "goto" loop by introducing a function. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 60 +++++++++++++++++------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index daaf0ebe8177..2767ea8e98a7 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -495,6 +495,37 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg) return 0; } +static int poll_busy(struct sh_mobile_i2c_data *pd) +{ + int i; + + for (i = 1000; i; i--) { + u_int8_t val = iic_rd(pd, ICSR); + + dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr); + + /* the interrupt handler may wake us up before the + * transfer is finished, so poll the hardware + * until we're done. + */ + if (!(val & ICSR_BUSY)) { + /* handle missing acknowledge and arbitration lost */ + if ((val | pd->sr) & (ICSR_TACK | ICSR_AL)) + return -EIO; + break; + } + + udelay(10); + } + + if (!i) { + dev_err(pd->dev, "Polling timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) @@ -502,8 +533,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, struct sh_mobile_i2c_data *pd = i2c_get_adapdata(adapter); struct i2c_msg *msg; int err = 0; - u_int8_t val; - int i, k, retry_count; + int i, k; activate_ch(pd); @@ -527,31 +557,9 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, break; } - retry_count = 1000; -again: - val = iic_rd(pd, ICSR); - - dev_dbg(pd->dev, "val 0x%02x pd->sr 0x%02x\n", val, pd->sr); - - /* the interrupt handler may wake us up before the - * transfer is finished, so poll the hardware - * until we're done. - */ - if (val & ICSR_BUSY) { - udelay(10); - if (retry_count--) - goto again; - - err = -EIO; - dev_err(pd->dev, "Polling timed out\n"); + err = poll_busy(pd); + if (err < 0) break; - } - - /* handle missing acknowledge and arbitration lost */ - if ((val | pd->sr) & (ICSR_TACK | ICSR_AL)) { - err = -EIO; - break; - } } deactivate_ch(pd); From e789029761503f0cce03e8767a56ae099b88e1bd Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Jan 2013 10:45:57 +0100 Subject: [PATCH 2250/4607] i2c: sh_mobile: don't send a stop condition by default inside transfers By default there should be no stop bit on I2C between single messages within transfers. Fix the driver to comply and only send a stop bit at the end of transfers or if I2C_M_STOP is set. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sh_mobile.c | 79 ++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 20 deletions(-) diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 2767ea8e98a7..debf745c0268 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -38,21 +38,21 @@ /* Transmit operation: */ /* */ /* 0 byte transmit */ -/* BUS: S A8 ACK P */ +/* BUS: S A8 ACK P(*) */ /* IRQ: DTE WAIT */ /* ICIC: */ /* ICCR: 0x94 0x90 */ /* ICDR: A8 */ /* */ /* 1 byte transmit */ -/* BUS: S A8 ACK D8(1) ACK P */ +/* BUS: S A8 ACK D8(1) ACK P(*) */ /* IRQ: DTE WAIT WAIT */ /* ICIC: -DTE */ /* ICCR: 0x94 0x90 */ /* ICDR: A8 D8(1) */ /* */ /* 2 byte transmit */ -/* BUS: S A8 ACK D8(1) ACK D8(2) ACK P */ +/* BUS: S A8 ACK D8(1) ACK D8(2) ACK P(*) */ /* IRQ: DTE WAIT WAIT WAIT */ /* ICIC: -DTE */ /* ICCR: 0x94 0x90 */ @@ -66,20 +66,20 @@ /* 0 byte receive - not supported since slave may hold SDA low */ /* */ /* 1 byte receive [TX] | [RX] */ -/* BUS: S A8 ACK | D8(1) ACK P */ +/* BUS: S A8 ACK | D8(1) ACK P(*) */ /* IRQ: DTE WAIT | WAIT DTE */ /* ICIC: -DTE | +DTE */ /* ICCR: 0x94 0x81 | 0xc0 */ /* ICDR: A8 | D8(1) */ /* */ /* 2 byte receive [TX]| [RX] */ -/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK P */ +/* BUS: S A8 ACK | D8(1) ACK D8(2) ACK P(*) */ /* IRQ: DTE WAIT | WAIT WAIT DTE */ /* ICIC: -DTE | +DTE */ /* ICCR: 0x94 0x81 | 0xc0 */ /* ICDR: A8 | D8(1) D8(2) */ /* */ -/* 3 byte receive [TX] | [RX] */ +/* 3 byte receive [TX] | [RX] (*) */ /* BUS: S A8 ACK | D8(1) ACK D8(2) ACK D8(3) ACK P */ /* IRQ: DTE WAIT | WAIT WAIT WAIT DTE */ /* ICIC: -DTE | +DTE */ @@ -94,7 +94,7 @@ /* SDA ___\___XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXAAAAAAAAA___/ */ /* SCL \_/1\_/2\_/3\_/4\_/5\_/6\_/7\_/8\___/9\_____/ */ /* */ -/* S D7 D6 D5 D4 D3 D2 D1 D0 P */ +/* S D7 D6 D5 D4 D3 D2 D1 D0 P(*) */ /* ___ */ /* WAIT IRQ ________________________________/ \___________ */ /* TACK IRQ ____________________________________/ \_______ */ @@ -103,6 +103,11 @@ /* _______________________________________________ */ /* BUSY __/ \_ */ /* */ +/* (*) The STOP condition is only sent by the master at the end of the last */ +/* I2C message or if the I2C_M_STOP flag is set. Similarly, the BUSY bit is */ +/* only cleared after the STOP condition, so, between messages we have to */ +/* poll for the DTE bit. */ +/* */ enum sh_mobile_i2c_op { OP_START = 0, @@ -132,6 +137,7 @@ struct sh_mobile_i2c_data { struct i2c_msg *msg; int pos; int sr; + bool send_stop; }; #define IIC_FLAG_HAS_ICIC67 (1 << 0) @@ -322,7 +328,7 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, break; case OP_TX_STOP: /* write data and issue a stop afterwards */ iic_wr(pd, ICDR, data); - iic_wr(pd, ICCR, 0x90); + iic_wr(pd, ICCR, pd->send_stop ? 0x90 : 0x94); break; case OP_TX_TO_RX: /* select read mode */ iic_wr(pd, ICCR, 0x81); @@ -469,22 +475,25 @@ static irqreturn_t sh_mobile_i2c_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg) +static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg, + bool do_init) { if (usr_msg->len == 0 && (usr_msg->flags & I2C_M_RD)) { dev_err(pd->dev, "Unsupported zero length i2c read\n"); return -EIO; } - /* Initialize channel registers */ - iic_set_clr(pd, ICCR, 0, ICCR_ICE); + if (do_init) { + /* Initialize channel registers */ + iic_set_clr(pd, ICCR, 0, ICCR_ICE); - /* Enable channel and configure rx ack */ - iic_set_clr(pd, ICCR, ICCR_ICE, 0); + /* Enable channel and configure rx ack */ + iic_set_clr(pd, ICCR, ICCR_ICE, 0); - /* Set the clock */ - iic_wr(pd, ICCL, pd->iccl & 0xff); - iic_wr(pd, ICCH, pd->icch & 0xff); + /* Set the clock */ + iic_wr(pd, ICCL, pd->iccl & 0xff); + iic_wr(pd, ICCH, pd->icch & 0xff); + } pd->msg = usr_msg; pd->pos = -1; @@ -495,6 +504,30 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg) return 0; } +static int poll_dte(struct sh_mobile_i2c_data *pd) +{ + int i; + + for (i = 1000; i; i--) { + u_int8_t val = iic_rd(pd, ICSR); + + if (val & ICSR_DTE) + break; + + if (val & ICSR_TACK) + return -EIO; + + udelay(10); + } + + if (!i) { + dev_warn(pd->dev, "Timeout polling for DTE!\n"); + return -ETIMEDOUT; + } + + return 0; +} + static int poll_busy(struct sh_mobile_i2c_data *pd) { int i; @@ -539,13 +572,16 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, /* Process all messages */ for (i = 0; i < num; i++) { + bool do_start = pd->send_stop || !i; msg = &msgs[i]; + pd->send_stop = i == num - 1 || msg->flags & I2C_M_STOP; - err = start_ch(pd, msg); + err = start_ch(pd, msg, do_start); if (err) break; - i2c_op(pd, OP_START, 0); + if (do_start) + i2c_op(pd, OP_START, 0); /* The interrupt handler takes care of the rest... */ k = wait_event_timeout(pd->wait, @@ -557,7 +593,10 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, break; } - err = poll_busy(pd); + if (pd->send_stop) + err = poll_busy(pd); + else + err = poll_dte(pd); if (err < 0) break; } @@ -571,7 +610,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, static u32 sh_mobile_i2c_func(struct i2c_adapter *adapter) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; } static struct i2c_algorithm sh_mobile_i2c_algorithm = { From 13f35ac14cd0a9a1c4f0034c4c40d0ae98844ce9 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Mon, 4 Feb 2013 14:54:10 -0500 Subject: [PATCH 2251/4607] i2c: Adding support for Intel iSMT SMBus 2.0 host controller The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus. The iSMT hardware can act as both a master and a target, although this driver only supports being a master. Signed-off-by: Neil Horman Signed-off-by: Bill Brown Tested-by: Seth Heasley Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-ismt | 36 ++ drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-ismt.c | 963 ++++++++++++++++++++++++++++++ 4 files changed, 1010 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-ismt create mode 100644 drivers/i2c/busses/i2c-ismt.c diff --git a/Documentation/i2c/busses/i2c-ismt b/Documentation/i2c/busses/i2c-ismt new file mode 100644 index 000000000000..737355822c0b --- /dev/null +++ b/Documentation/i2c/busses/i2c-ismt @@ -0,0 +1,36 @@ +Kernel driver i2c-ismt + +Supported adapters: + * Intel S12xx series SOCs + +Authors: + Bill Brown + + +Module Parameters +----------------- + +* bus_speed (unsigned int) +Allows changing of the bus speed. Normally, the bus speed is set by the BIOS +and never needs to be changed. However, some SMBus analyzers are too slow for +monitoring the bus during debug, thus the need for this module parameter. +Specify the bus speed in kHz. +Available bus frequency settings: + 0 no change + 80 kHz + 100 kHz + 400 kHz + 1000 kHz + + +Description +----------- + +The S12xx series of SOCs have a pair of integrated SMBus 2.0 controllers +targeted primarily at the microserver and storage markets. + +The S12xx series contain a pair of PCI functions. An output of lspci will show +something similar to the following: + + 00:13.0 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 0 + 00:13.1 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 1 diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 77d28873b76f..87df863ba110 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -122,6 +122,16 @@ config I2C_ISCH This driver can also be built as a module. If so, the module will be called i2c-isch. +config I2C_ISMT + tristate "Intel iSMT SMBus Controller" + depends on PCI && X86 + help + If you say yes to this option, support will be included for the Intel + iSMT SMBus host controller interface. + + This driver can also be built as a module. If so, the module will be + called i2c-ismt. + config I2C_PIIX4 tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 6181f3ff263f..23c9d55bc880 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o obj-$(CONFIG_I2C_I801) += i2c-i801.o obj-$(CONFIG_I2C_ISCH) += i2c-isch.o +obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c new file mode 100644 index 000000000000..3f7a9cb6dfdd --- /dev/null +++ b/drivers/i2c/busses/i2c-ismt.c @@ -0,0 +1,963 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2012 Intel Corporation. All rights reserved. + * + * GPL LICENSE SUMMARY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Supports the SMBus Message Transport (SMT) in the Intel Atom Processor + * S12xx Product Family. + * + * Features supported by this driver: + * Hardware PEC yes + * Block buffer yes + * Block process call transaction no + * Slave mode no + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* PCI Address Constants */ +#define SMBBAR 0 + +/* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */ +#define PCI_DEVICE_ID_INTEL_S1200_SMT0 0x0c59 +#define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a + +#define ISMT_DESC_ENTRIES 32 /* number of descriptor entries */ +#define ISMT_MAX_RETRIES 3 /* number of SMBus retries to attempt */ + +/* Hardware Descriptor Constants - Control Field */ +#define ISMT_DESC_CWRL 0x01 /* Command/Write Length */ +#define ISMT_DESC_BLK 0X04 /* Perform Block Transaction */ +#define ISMT_DESC_FAIR 0x08 /* Set fairness flag upon successful arbit. */ +#define ISMT_DESC_PEC 0x10 /* Packet Error Code */ +#define ISMT_DESC_I2C 0x20 /* I2C Enable */ +#define ISMT_DESC_INT 0x40 /* Interrupt */ +#define ISMT_DESC_SOE 0x80 /* Stop On Error */ + +/* Hardware Descriptor Constants - Status Field */ +#define ISMT_DESC_SCS 0x01 /* Success */ +#define ISMT_DESC_DLTO 0x04 /* Data Low Time Out */ +#define ISMT_DESC_NAK 0x08 /* NAK Received */ +#define ISMT_DESC_CRC 0x10 /* CRC Error */ +#define ISMT_DESC_CLTO 0x20 /* Clock Low Time Out */ +#define ISMT_DESC_COL 0x40 /* Collisions */ +#define ISMT_DESC_LPR 0x80 /* Large Packet Received */ + +/* Macros */ +#define ISMT_DESC_ADDR_RW(addr, rw) (((addr) << 1) | (rw)) + +/* iSMT General Register address offsets (SMBBAR + ) */ +#define ISMT_GR_GCTRL 0x000 /* General Control */ +#define ISMT_GR_SMTICL 0x008 /* SMT Interrupt Cause Location */ +#define ISMT_GR_ERRINTMSK 0x010 /* Error Interrupt Mask */ +#define ISMT_GR_ERRAERMSK 0x014 /* Error AER Mask */ +#define ISMT_GR_ERRSTS 0x018 /* Error Status */ +#define ISMT_GR_ERRINFO 0x01c /* Error Information */ + +/* iSMT Master Registers */ +#define ISMT_MSTR_MDBA 0x100 /* Master Descriptor Base Address */ +#define ISMT_MSTR_MCTRL 0x108 /* Master Control */ +#define ISMT_MSTR_MSTS 0x10c /* Master Status */ +#define ISMT_MSTR_MDS 0x110 /* Master Descriptor Size */ +#define ISMT_MSTR_RPOLICY 0x114 /* Retry Policy */ + +/* iSMT Miscellaneous Registers */ +#define ISMT_SPGT 0x300 /* SMBus PHY Global Timing */ + +/* General Control Register (GCTRL) bit definitions */ +#define ISMT_GCTRL_TRST 0x04 /* Target Reset */ +#define ISMT_GCTRL_KILL 0x08 /* Kill */ +#define ISMT_GCTRL_SRST 0x40 /* Soft Reset */ + +/* Master Control Register (MCTRL) bit definitions */ +#define ISMT_MCTRL_SS 0x01 /* Start/Stop */ +#define ISMT_MCTRL_MEIE 0x10 /* Master Error Interrupt Enable */ +#define ISMT_MCTRL_FMHP 0x00ff0000 /* Firmware Master Head Ptr (FMHP) */ + +/* Master Status Register (MSTS) bit definitions */ +#define ISMT_MSTS_HMTP 0xff0000 /* HW Master Tail Pointer (HMTP) */ +#define ISMT_MSTS_MIS 0x20 /* Master Interrupt Status (MIS) */ +#define ISMT_MSTS_MEIS 0x10 /* Master Error Int Status (MEIS) */ +#define ISMT_MSTS_IP 0x01 /* In Progress */ + +/* Master Descriptor Size (MDS) bit definitions */ +#define ISMT_MDS_MASK 0xff /* Master Descriptor Size mask (MDS) */ + +/* SMBus PHY Global Timing Register (SPGT) bit definitions */ +#define ISMT_SPGT_SPD_MASK 0xc0000000 /* SMBus Speed mask */ +#define ISMT_SPGT_SPD_80K 0x00 /* 80 kHz */ +#define ISMT_SPGT_SPD_100K (0x1 << 30) /* 100 kHz */ +#define ISMT_SPGT_SPD_400K (0x2 << 30) /* 400 kHz */ +#define ISMT_SPGT_SPD_1M (0x3 << 30) /* 1 MHz */ + + +/* MSI Control Register (MSICTL) bit definitions */ +#define ISMT_MSICTL_MSIE 0x01 /* MSI Enable */ + +/* iSMT Hardware Descriptor */ +struct ismt_desc { + u8 tgtaddr_rw; /* target address & r/w bit */ + u8 wr_len_cmd; /* write length in bytes or a command */ + u8 rd_len; /* read length */ + u8 control; /* control bits */ + u8 status; /* status bits */ + u8 retry; /* collision retry and retry count */ + u8 rxbytes; /* received bytes */ + u8 txbytes; /* transmitted bytes */ + u32 dptr_low; /* lower 32 bit of the data pointer */ + u32 dptr_high; /* upper 32 bit of the data pointer */ +} __packed; + +struct ismt_priv { + struct i2c_adapter adapter; + void *smba; /* PCI BAR */ + struct pci_dev *pci_dev; + struct ismt_desc *hw; /* descriptor virt base addr */ + dma_addr_t io_rng_dma; /* descriptor HW base addr */ + u8 head; /* ring buffer head pointer */ + struct completion cmp; /* interrupt completion */ + u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* temp R/W data buffer */ + bool using_msi; /* type of interrupt flag */ +}; + +/** + * ismt_ids - PCI device IDs supported by this driver + */ +static const DEFINE_PCI_DEVICE_TABLE(ismt_ids) = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, ismt_ids); + +/* Bus speed control bits for slow debuggers - refer to the docs for usage */ +static unsigned int bus_speed; +module_param(bus_speed, uint, S_IRUGO); +MODULE_PARM_DESC(bus_speed, "Bus Speed in kHz (0 = BIOS default)"); + +/** + * __ismt_desc_dump() - dump the contents of a specific descriptor + */ +static void __ismt_desc_dump(struct device *dev, const struct ismt_desc *desc) +{ + + dev_dbg(dev, "Descriptor struct: %p\n", desc); + dev_dbg(dev, "\ttgtaddr_rw=0x%02X\n", desc->tgtaddr_rw); + dev_dbg(dev, "\twr_len_cmd=0x%02X\n", desc->wr_len_cmd); + dev_dbg(dev, "\trd_len= 0x%02X\n", desc->rd_len); + dev_dbg(dev, "\tcontrol= 0x%02X\n", desc->control); + dev_dbg(dev, "\tstatus= 0x%02X\n", desc->status); + dev_dbg(dev, "\tretry= 0x%02X\n", desc->retry); + dev_dbg(dev, "\trxbytes= 0x%02X\n", desc->rxbytes); + dev_dbg(dev, "\ttxbytes= 0x%02X\n", desc->txbytes); + dev_dbg(dev, "\tdptr_low= 0x%08X\n", desc->dptr_low); + dev_dbg(dev, "\tdptr_high= 0x%08X\n", desc->dptr_high); +} +/** + * ismt_desc_dump() - dump the contents of a descriptor for debug purposes + * @priv: iSMT private data + */ +static void ismt_desc_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + struct ismt_desc *desc = &priv->hw[priv->head]; + + dev_dbg(dev, "Dump of the descriptor struct: 0x%X\n", priv->head); + __ismt_desc_dump(dev, desc); +} + +/** + * ismt_gen_reg_dump() - dump the iSMT General Registers + * @priv: iSMT private data + */ +static void ismt_gen_reg_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + + dev_dbg(dev, "Dump of the iSMT General Registers\n"); + dev_dbg(dev, " GCTRL.... : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_GCTRL, + readl(priv->smba + ISMT_GR_GCTRL)); + dev_dbg(dev, " SMTICL... : (0x%p)=0x%016llX\n", + priv->smba + ISMT_GR_SMTICL, + (long long unsigned int)readq(priv->smba + ISMT_GR_SMTICL)); + dev_dbg(dev, " ERRINTMSK : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRINTMSK, + readl(priv->smba + ISMT_GR_ERRINTMSK)); + dev_dbg(dev, " ERRAERMSK : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRAERMSK, + readl(priv->smba + ISMT_GR_ERRAERMSK)); + dev_dbg(dev, " ERRSTS... : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRSTS, + readl(priv->smba + ISMT_GR_ERRSTS)); + dev_dbg(dev, " ERRINFO.. : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRINFO, + readl(priv->smba + ISMT_GR_ERRINFO)); +} + +/** + * ismt_mstr_reg_dump() - dump the iSMT Master Registers + * @priv: iSMT private data + */ +static void ismt_mstr_reg_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + + dev_dbg(dev, "Dump of the iSMT Master Registers\n"); + dev_dbg(dev, " MDBA..... : (0x%p)=0x%016llX\n", + priv->smba + ISMT_MSTR_MDBA, + (long long unsigned int)readq(priv->smba + ISMT_MSTR_MDBA)); + dev_dbg(dev, " MCTRL.... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MCTRL, + readl(priv->smba + ISMT_MSTR_MCTRL)); + dev_dbg(dev, " MSTS..... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MSTS, + readl(priv->smba + ISMT_MSTR_MSTS)); + dev_dbg(dev, " MDS...... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MDS, + readl(priv->smba + ISMT_MSTR_MDS)); + dev_dbg(dev, " RPOLICY.. : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_RPOLICY, + readl(priv->smba + ISMT_MSTR_RPOLICY)); + dev_dbg(dev, " SPGT..... : (0x%p)=0x%X\n", + priv->smba + ISMT_SPGT, + readl(priv->smba + ISMT_SPGT)); +} + +/** + * ismt_submit_desc() - add a descriptor to the ring + * @priv: iSMT private data + */ +static void ismt_submit_desc(struct ismt_priv *priv) +{ + uint fmhp; + uint val; + + ismt_desc_dump(priv); + ismt_gen_reg_dump(priv); + ismt_mstr_reg_dump(priv); + + /* Set the FMHP (Firmware Master Head Pointer)*/ + fmhp = ((priv->head + 1) % ISMT_DESC_ENTRIES) << 16; + val = readl(priv->smba + ISMT_MSTR_MCTRL); + writel((val & ~ISMT_MCTRL_FMHP) | fmhp, + priv->smba + ISMT_MSTR_MCTRL); + + /* Set the start bit */ + val = readl(priv->smba + ISMT_MSTR_MCTRL); + writel(val | ISMT_MCTRL_SS, + priv->smba + ISMT_MSTR_MCTRL); +} + +/** + * ismt_process_desc() - handle the completion of the descriptor + * @desc: the iSMT hardware descriptor + * @data: data buffer from the upper layer + * @priv: ismt_priv struct holding our dma buffer + * @size: SMBus transaction type + * @read_write: flag to indicate if this is a read or write + */ +static int ismt_process_desc(const struct ismt_desc *desc, + union i2c_smbus_data *data, + struct ismt_priv *priv, int size, + char read_write) +{ + u8 *dma_buffer = priv->dma_buffer; + + dev_dbg(&priv->pci_dev->dev, "Processing completed descriptor\n"); + __ismt_desc_dump(&priv->pci_dev->dev, desc); + + if (desc->status & ISMT_DESC_SCS) { + if (read_write == I2C_SMBUS_WRITE && + size != I2C_SMBUS_PROC_CALL) + return 0; + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + data->byte = dma_buffer[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = dma_buffer[0] | (dma_buffer[1] << 8); + break; + case I2C_SMBUS_BLOCK_DATA: + memcpy(&data->block[1], dma_buffer, desc->rxbytes); + data->block[0] = desc->rxbytes; + break; + } + return 0; + } + + if (likely(desc->status & ISMT_DESC_NAK)) + return -ENXIO; + + if (desc->status & ISMT_DESC_CRC) + return -EBADMSG; + + if (desc->status & ISMT_DESC_COL) + return -EAGAIN; + + if (desc->status & ISMT_DESC_LPR) + return -EPROTO; + + if (desc->status & (ISMT_DESC_DLTO | ISMT_DESC_CLTO)) + return -ETIMEDOUT; + + return -EIO; +} + +/** + * ismt_access() - process an SMBus command + * @adap: the i2c host adapter + * @addr: address of the i2c/SMBus target + * @flags: command options + * @read_write: read from or write to device + * @command: the i2c/SMBus command to issue + * @size: SMBus transaction type + * @data: read/write data buffer + */ +static int ismt_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + int ret; + dma_addr_t dma_addr = 0; /* address of the data buffer */ + u8 dma_size = 0; + enum dma_data_direction dma_direction = 0; + struct ismt_desc *desc; + struct ismt_priv *priv = i2c_get_adapdata(adap); + struct device *dev = &priv->pci_dev->dev; + + desc = &priv->hw[priv->head]; + + /* Initialize the descriptor */ + memset(desc, 0, sizeof(struct ismt_desc)); + desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write); + + /* Initialize common control bits */ + if (likely(priv->using_msi)) + desc->control = ISMT_DESC_INT | ISMT_DESC_FAIR; + else + desc->control = ISMT_DESC_FAIR; + + if ((flags & I2C_CLIENT_PEC) && (size != I2C_SMBUS_QUICK) + && (size != I2C_SMBUS_I2C_BLOCK_DATA)) + desc->control |= ISMT_DESC_PEC; + + switch (size) { + case I2C_SMBUS_QUICK: + dev_dbg(dev, "I2C_SMBUS_QUICK\n"); + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + /* + * Send Byte + * The command field contains the write data + */ + dev_dbg(dev, "I2C_SMBUS_BYTE: WRITE\n"); + desc->control |= ISMT_DESC_CWRL; + desc->wr_len_cmd = command; + } else { + /* Receive Byte */ + dev_dbg(dev, "I2C_SMBUS_BYTE: READ\n"); + dma_size = 1; + dma_direction = DMA_FROM_DEVICE; + desc->rd_len = 1; + } + break; + + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_WRITE) { + /* + * Write Byte + * Command plus 1 data byte + */ + dev_dbg(dev, "I2C_SMBUS_BYTE_DATA: WRITE\n"); + desc->wr_len_cmd = 2; + dma_size = 2; + dma_direction = DMA_TO_DEVICE; + priv->dma_buffer[0] = command; + priv->dma_buffer[1] = data->byte; + } else { + /* Read Byte */ + dev_dbg(dev, "I2C_SMBUS_BYTE_DATA: READ\n"); + desc->control |= ISMT_DESC_CWRL; + desc->wr_len_cmd = command; + desc->rd_len = 1; + dma_size = 1; + dma_direction = DMA_FROM_DEVICE; + } + break; + + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_WRITE) { + /* Write Word */ + dev_dbg(dev, "I2C_SMBUS_WORD_DATA: WRITE\n"); + desc->wr_len_cmd = 3; + dma_size = 3; + dma_direction = DMA_TO_DEVICE; + priv->dma_buffer[0] = command; + priv->dma_buffer[1] = data->word & 0xff; + priv->dma_buffer[2] = data->word >> 8; + } else { + /* Read Word */ + dev_dbg(dev, "I2C_SMBUS_WORD_DATA: READ\n"); + desc->wr_len_cmd = command; + desc->control |= ISMT_DESC_CWRL; + desc->rd_len = 2; + dma_size = 2; + dma_direction = DMA_FROM_DEVICE; + } + break; + + case I2C_SMBUS_PROC_CALL: + dev_dbg(dev, "I2C_SMBUS_PROC_CALL\n"); + desc->wr_len_cmd = 3; + desc->rd_len = 2; + dma_size = 3; + dma_direction = DMA_BIDIRECTIONAL; + priv->dma_buffer[0] = command; + priv->dma_buffer[1] = data->word & 0xff; + priv->dma_buffer[2] = data->word >> 8; + break; + + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_WRITE) { + /* Block Write */ + dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA: WRITE\n"); + dma_size = data->block[0] + 1; + dma_direction = DMA_TO_DEVICE; + desc->wr_len_cmd = dma_size; + desc->control |= ISMT_DESC_BLK; + priv->dma_buffer[0] = command; + memcpy(&priv->dma_buffer[1], &data->block[1], dma_size); + } else { + /* Block Read */ + dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA: READ\n"); + dma_size = I2C_SMBUS_BLOCK_MAX; + dma_direction = DMA_FROM_DEVICE; + desc->rd_len = dma_size; + desc->wr_len_cmd = command; + desc->control |= (ISMT_DESC_BLK | ISMT_DESC_CWRL); + } + break; + + default: + dev_err(dev, "Unsupported transaction %d\n", + size); + return -EOPNOTSUPP; + } + + /* map the data buffer */ + if (dma_size != 0) { + dev_dbg(dev, " dev=%p\n", dev); + dev_dbg(dev, " data=%p\n", data); + dev_dbg(dev, " dma_buffer=%p\n", priv->dma_buffer); + dev_dbg(dev, " dma_size=%d\n", dma_size); + dev_dbg(dev, " dma_direction=%d\n", dma_direction); + + dma_addr = dma_map_single(dev, + priv->dma_buffer, + dma_size, + dma_direction); + + if (dma_mapping_error(dev, dma_addr)) { + dev_err(dev, "Error in mapping dma buffer %p\n", + priv->dma_buffer); + return -EIO; + } + + dev_dbg(dev, " dma_addr = 0x%016llX\n", + dma_addr); + + desc->dptr_low = lower_32_bits(dma_addr); + desc->dptr_high = upper_32_bits(dma_addr); + } + + INIT_COMPLETION(priv->cmp); + + /* Add the descriptor */ + ismt_submit_desc(priv); + + /* Now we wait for interrupt completion, 1s */ + ret = wait_for_completion_timeout(&priv->cmp, HZ*1); + + /* unmap the data buffer */ + if (dma_size != 0) + dma_unmap_single(&adap->dev, dma_addr, dma_size, dma_direction); + + if (unlikely(!ret)) { + dev_err(dev, "completion wait timed out\n"); + ret = -ETIMEDOUT; + goto out; + } + + /* do any post processing of the descriptor here */ + ret = ismt_process_desc(desc, data, priv, size, read_write); + +out: + /* Update the ring pointer */ + priv->head++; + priv->head %= ISMT_DESC_ENTRIES; + + return ret; +} + +/** + * ismt_func() - report which i2c commands are supported by this adapter + * @adap: the i2c host adapter + */ +static u32 ismt_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_QUICK | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_PEC; +} + +/** + * smbus_algorithm - the adapter algorithm and supported functionality + * @smbus_xfer: the adapter algorithm + * @functionality: functionality supported by the adapter + */ +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = ismt_access, + .functionality = ismt_func, +}; + +/** + * ismt_handle_isr() - interrupt handler bottom half + * @priv: iSMT private data + */ +static irqreturn_t ismt_handle_isr(struct ismt_priv *priv) +{ + complete(&priv->cmp); + + return IRQ_HANDLED; +} + + +/** + * ismt_do_interrupt() - IRQ interrupt handler + * @vec: interrupt vector + * @data: iSMT private data + */ +static irqreturn_t ismt_do_interrupt(int vec, void *data) +{ + u32 val; + struct ismt_priv *priv = data; + + /* + * check to see it's our interrupt, return IRQ_NONE if not ours + * since we are sharing interrupt + */ + val = readl(priv->smba + ISMT_MSTR_MSTS); + + if (!(val & (ISMT_MSTS_MIS | ISMT_MSTS_MEIS))) + return IRQ_NONE; + else + writel(val | ISMT_MSTS_MIS | ISMT_MSTS_MEIS, + priv->smba + ISMT_MSTR_MSTS); + + return ismt_handle_isr(priv); +} + +/** + * ismt_do_msi_interrupt() - MSI interrupt handler + * @vec: interrupt vector + * @data: iSMT private data + */ +static irqreturn_t ismt_do_msi_interrupt(int vec, void *data) +{ + return ismt_handle_isr(data); +} + +/** + * ismt_hw_init() - initialize the iSMT hardware + * @priv: iSMT private data + */ +static void ismt_hw_init(struct ismt_priv *priv) +{ + u32 val; + struct device *dev = &priv->pci_dev->dev; + + /* initialize the Master Descriptor Base Address (MDBA) */ + writeq(priv->io_rng_dma, priv->smba + ISMT_MSTR_MDBA); + + /* initialize the Master Control Register (MCTRL) */ + writel(ISMT_MCTRL_MEIE, priv->smba + ISMT_MSTR_MCTRL); + + /* initialize the Master Status Register (MSTS) */ + writel(0, priv->smba + ISMT_MSTR_MSTS); + + /* initialize the Master Descriptor Size (MDS) */ + val = readl(priv->smba + ISMT_MSTR_MDS); + writel((val & ~ISMT_MDS_MASK) | (ISMT_DESC_ENTRIES - 1), + priv->smba + ISMT_MSTR_MDS); + + /* + * Set the SMBus speed (could use this for slow HW debuggers) + */ + + val = readl(priv->smba + ISMT_SPGT); + + switch (bus_speed) { + case 0: + break; + + case 80: + dev_dbg(dev, "Setting SMBus clock to 80 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_80K), + priv->smba + ISMT_SPGT); + break; + + case 100: + dev_dbg(dev, "Setting SMBus clock to 100 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_100K), + priv->smba + ISMT_SPGT); + break; + + case 400: + dev_dbg(dev, "Setting SMBus clock to 400 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_400K), + priv->smba + ISMT_SPGT); + break; + + case 1000: + dev_dbg(dev, "Setting SMBus clock to 1000 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_1M), + priv->smba + ISMT_SPGT); + break; + + default: + dev_warn(dev, "Invalid SMBus clock speed, only 0, 80, 100, 400, and 1000 are valid\n"); + break; + } + + val = readl(priv->smba + ISMT_SPGT); + + switch (val & ISMT_SPGT_SPD_MASK) { + case ISMT_SPGT_SPD_80K: + bus_speed = 80; + break; + case ISMT_SPGT_SPD_100K: + bus_speed = 100; + break; + case ISMT_SPGT_SPD_400K: + bus_speed = 400; + break; + case ISMT_SPGT_SPD_1M: + bus_speed = 1000; + break; + } + dev_dbg(dev, "SMBus clock is running at %d kHz\n", bus_speed); +} + +/** + * ismt_dev_init() - initialize the iSMT data structures + * @priv: iSMT private data + */ +static int ismt_dev_init(struct ismt_priv *priv) +{ + /* allocate memory for the descriptor */ + priv->hw = dmam_alloc_coherent(&priv->pci_dev->dev, + (ISMT_DESC_ENTRIES + * sizeof(struct ismt_desc)), + &priv->io_rng_dma, + GFP_KERNEL); + if (!priv->hw) + return -ENOMEM; + + memset(priv->hw, 0, (ISMT_DESC_ENTRIES * sizeof(struct ismt_desc))); + + priv->head = 0; + init_completion(&priv->cmp); + + return 0; +} + +/** + * ismt_int_init() - initialize interrupts + * @priv: iSMT private data + */ +static int ismt_int_init(struct ismt_priv *priv) +{ + int err; + + /* Try using MSI interrupts */ + err = pci_enable_msi(priv->pci_dev); + if (err) { + dev_warn(&priv->pci_dev->dev, + "Unable to use MSI interrupts, falling back to legacy\n"); + goto intx; + } + + err = devm_request_irq(&priv->pci_dev->dev, + priv->pci_dev->irq, + ismt_do_msi_interrupt, + 0, + "ismt-msi", + priv); + if (err) { + pci_disable_msi(priv->pci_dev); + goto intx; + } + + priv->using_msi = true; + goto done; + + /* Try using legacy interrupts */ +intx: + err = devm_request_irq(&priv->pci_dev->dev, + priv->pci_dev->irq, + ismt_do_interrupt, + IRQF_SHARED, + "ismt-intx", + priv); + if (err) { + dev_err(&priv->pci_dev->dev, "no usable interrupts\n"); + return -ENODEV; + } + + priv->using_msi = false; + +done: + return 0; +} + +static struct pci_driver ismt_driver; + +/** + * ismt_probe() - probe for iSMT devices + * @pdev: PCI-Express device + * @id: PCI-Express device ID + */ +static int +ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int err; + struct ismt_priv *priv; + unsigned long start, len; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + pci_set_drvdata(pdev, priv); + i2c_set_adapdata(&priv->adapter, priv); + priv->adapter.owner = THIS_MODULE; + + priv->adapter.class = I2C_CLASS_HWMON; + + priv->adapter.algo = &smbus_algorithm; + + /* set up the sysfs linkage to our parent device */ + priv->adapter.dev.parent = &pdev->dev; + + /* number of retries on lost arbitration */ + priv->adapter.retries = ISMT_MAX_RETRIES; + + priv->pci_dev = pdev; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Failed to enable SMBus PCI device (%d)\n", + err); + return err; + } + + /* enable bus mastering */ + pci_set_master(pdev); + + /* Determine the address of the SMBus area */ + start = pci_resource_start(pdev, SMBBAR); + len = pci_resource_len(pdev, SMBBAR); + if (!start || !len) { + dev_err(&pdev->dev, + "SMBus base address uninitialized, upgrade BIOS\n"); + return -ENODEV; + } + + snprintf(priv->adapter.name, sizeof(priv->adapter.name), + "SMBus iSMT adapter at %lx", start); + + dev_dbg(&priv->pci_dev->dev, " start=0x%lX\n", start); + dev_dbg(&priv->pci_dev->dev, " len=0x%lX\n", len); + + err = acpi_check_resource_conflict(&pdev->resource[SMBBAR]); + if (err) { + dev_err(&pdev->dev, "ACPI resource conflict!\n"); + return err; + } + + err = pci_request_region(pdev, SMBBAR, ismt_driver.name); + if (err) { + dev_err(&pdev->dev, + "Failed to request SMBus region 0x%lx-0x%lx\n", + start, start + len); + return err; + } + + priv->smba = pcim_iomap(pdev, SMBBAR, len); + if (!priv->smba) { + dev_err(&pdev->dev, "Unable to ioremap SMBus BAR\n"); + err = -ENODEV; + goto fail; + } + + if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) || + (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)) { + if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) || + (pci_set_consistent_dma_mask(pdev, + DMA_BIT_MASK(32)) != 0)) { + dev_err(&pdev->dev, "pci_set_dma_mask fail %p\n", + pdev); + goto fail; + } + } + + err = ismt_dev_init(priv); + if (err) + goto fail; + + ismt_hw_init(priv); + + err = ismt_int_init(priv); + if (err) + goto fail; + + err = i2c_add_adapter(&priv->adapter); + if (err) { + dev_err(&pdev->dev, "Failed to add SMBus iSMT adapter\n"); + err = -ENODEV; + goto fail; + } + return 0; + +fail: + pci_release_region(pdev, SMBBAR); + return err; +} + +/** + * ismt_remove() - release driver resources + * @pdev: PCI-Express device + */ +static void ismt_remove(struct pci_dev *pdev) +{ + struct ismt_priv *priv = pci_get_drvdata(pdev); + + i2c_del_adapter(&priv->adapter); + pci_release_region(pdev, SMBBAR); +} + +/** + * ismt_suspend() - place the device in suspend + * @pdev: PCI-Express device + * @mesg: PM message + */ +#ifdef CONFIG_PM +static int ismt_suspend(struct pci_dev *pdev, pm_message_t mesg) +{ + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, mesg)); + return 0; +} + +/** + * ismt_resume() - PCI resume code + * @pdev: PCI-Express device + */ +static int ismt_resume(struct pci_dev *pdev) +{ + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + return pci_enable_device(pdev); +} + +#else + +#define ismt_suspend NULL +#define ismt_resume NULL + +#endif + +static struct pci_driver ismt_driver = { + .name = "ismt_smbus", + .id_table = ismt_ids, + .probe = ismt_probe, + .remove = ismt_remove, + .suspend = ismt_suspend, + .resume = ismt_resume, +}; + +module_pci_driver(ismt_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Bill E. Brown "); +MODULE_DESCRIPTION("Intel SMBus Message Transport (iSMT) driver"); From 4182b434bf760355a1516b1d3d9f73aa419eeeec Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 9 Feb 2013 19:14:00 +0100 Subject: [PATCH 2252/4607] i2c: at91: fix unsed variable warning when building with !CONFIG_OF Commit 70d46a2 "i2c: at91: add dt support to i2c-at91" added DT only support for at91sam9x5. Building i2c-at91 without CONFIG_OF now warns about at91sam9x5_config as being unused. drivers/i2c/busses/i2c-at91.c:556:30: warning: 'at91sam9x5_config' defined but not used [-Wunused-variable] Move at91sam9x5_config under the defined(CONFIG_OF) guard as new AT91 SoCs will be DT only. Signed-off-by: Joachim Eastwood Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-at91.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 3e287d838936..3e4ad68e3bd7 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -553,13 +553,6 @@ static struct at91_twi_pdata at91sam9g10_config = { .has_dma_support = false, }; -static struct at91_twi_pdata at91sam9x5_config = { - .clk_max_div = 7, - .clk_offset = 4, - .has_unre_flag = false, - .has_dma_support = true, -}; - static const struct platform_device_id at91_twi_devtypes[] = { { .name = "i2c-at91rm9200", @@ -582,6 +575,13 @@ static const struct platform_device_id at91_twi_devtypes[] = { }; #if defined(CONFIG_OF) +static struct at91_twi_pdata at91sam9x5_config = { + .clk_max_div = 7, + .clk_offset = 4, + .has_unre_flag = false, + .has_dma_support = true, +}; + static const struct of_device_id atmel_twi_dt_ids[] = { { .compatible = "atmel,at91rm9200-i2c", From 9350393239153c4a98cbed4d69b9ed81e37d5e74 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sun, 10 Feb 2013 15:57:38 +1030 Subject: [PATCH 2253/4607] virtio: make config_ops const It is just a table of function pointers, make it const for cleanliness and security reasons. Signed-off-by: Stephen Hemminger Signed-off-by: Rusty Russell --- drivers/lguest/lguest_device.c | 2 +- drivers/remoteproc/remoteproc_virtio.c | 2 +- drivers/s390/kvm/kvm_virtio.c | 2 +- drivers/virtio/virtio_mmio.c | 2 +- drivers/virtio/virtio_pci.c | 2 +- include/linux/virtio.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c index fc92ccbd71dc..b3256ff0d426 100644 --- a/drivers/lguest/lguest_device.c +++ b/drivers/lguest/lguest_device.c @@ -396,7 +396,7 @@ static const char *lg_bus_name(struct virtio_device *vdev) } /* The ops structure which hooks everything together. */ -static struct virtio_config_ops lguest_config_ops = { +static const struct virtio_config_ops lguest_config_ops = { .get_features = lg_get_features, .finalize_features = lg_finalize_features, .get = lg_get, diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 9e198e590675..afed9b7731c4 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -222,7 +222,7 @@ static void rproc_virtio_finalize_features(struct virtio_device *vdev) rvdev->gfeatures = vdev->features[0]; } -static struct virtio_config_ops rproc_virtio_config_ops = { +static const struct virtio_config_ops rproc_virtio_config_ops = { .get_features = rproc_virtio_get_features, .finalize_features = rproc_virtio_finalize_features, .find_vqs = rproc_virtio_find_vqs, diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 8491111aec12..7d08fba7542d 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -275,7 +275,7 @@ static const char *kvm_bus_name(struct virtio_device *vdev) /* * The config ops structure as defined by virtio config */ -static struct virtio_config_ops kvm_vq_configspace_ops = { +static const struct virtio_config_ops kvm_vq_configspace_ops = { .get_features = kvm_get_features, .finalize_features = kvm_finalize_features, .get = kvm_get, diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index 833e6dbf38db..1ba0d6831015 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -423,7 +423,7 @@ static const char *vm_bus_name(struct virtio_device *vdev) return vm_dev->pdev->name; } -static struct virtio_config_ops virtio_mmio_config_ops = { +static const struct virtio_config_ops virtio_mmio_config_ops = { .get = vm_get, .set = vm_set, .get_status = vm_get_status, diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 0c142892c105..52c5ce6aec8d 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -652,7 +652,7 @@ static int vp_set_vq_affinity(struct virtqueue *vq, int cpu) return 0; } -static struct virtio_config_ops virtio_pci_config_ops = { +static const struct virtio_config_ops virtio_pci_config_ops = { .get = vp_get, .set = vp_set, .get_status = vp_get_status, diff --git a/include/linux/virtio.h b/include/linux/virtio.h index cf8adb1f5b2c..eb34cf774473 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -78,7 +78,7 @@ struct virtio_device { int index; struct device dev; struct virtio_device_id id; - struct virtio_config_ops *config; + const struct virtio_config_ops *config; struct list_head vqs; /* Note that this is a Linux set_bit-style bitmap. */ unsigned long features[1]; From 35cdc9eb65837687bdfc9ea1d2515eb03ea5048a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sun, 10 Feb 2013 15:57:39 +1030 Subject: [PATCH 2254/4607] virtio: make pci_device_id const Use DEVICE_TABLE macro which also makes table const. Signed-off-by: Stephen Hemminger Signed-off-by: Rusty Russell --- drivers/virtio/virtio_pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 52c5ce6aec8d..a7ce73029f59 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -91,9 +91,9 @@ struct virtio_pci_vq_info }; /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */ -static struct pci_device_id virtio_pci_id_table[] = { - { 0x1af4, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 }, +static DEFINE_PCI_DEVICE_TABLE(virtio_pci_id_table) = { + { PCI_DEVICE(0x1af4, PCI_ANY_ID) }, + { 0 } }; MODULE_DEVICE_TABLE(pci, virtio_pci_id_table); From 79fd50c67f91136add9726fb7719b57a66c6f763 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 7 Feb 2013 13:20:52 +0100 Subject: [PATCH 2255/4607] KVM: s390: Fix handling of iscs. There are two ways to express an interruption subclass: - As a bitmask, as used in cr6. - As a number, as used in the I/O interruption word. Unfortunately, we have treated the I/O interruption word as if it contained the bitmask as well, which went unnoticed so far as - (not-yet-released) qemu made the same mistake, and - Linux guest kernels don't check the isc value in the I/O interruption word for subchannel interrupts. Make sure that we treat the I/O interruption word correctly. Reviewed-by: Christian Borntraeger Signed-off-by: Cornelia Huck Signed-off-by: Gleb Natapov --- arch/s390/kvm/interrupt.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 9a128357fd15..2f6ccb065c4a 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -55,6 +55,13 @@ static int psw_interrupts_disabled(struct kvm_vcpu *vcpu) return 1; } +static u64 int_word_to_isc_bits(u32 int_word) +{ + u8 isc = (int_word & 0x38000000) >> 27; + + return (0x80 >> isc) << 24; +} + static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu, struct kvm_s390_interrupt_info *inti) { @@ -96,7 +103,8 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu, case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: if (psw_ioint_disabled(vcpu)) return 0; - if (vcpu->arch.sie_block->gcr[6] & inti->io.io_int_word) + if (vcpu->arch.sie_block->gcr[6] & + int_word_to_isc_bits(inti->io.io_int_word)) return 1; return 0; default: @@ -724,7 +732,8 @@ struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, list_for_each_entry(iter, &fi->list, list) { if (!is_ioint(iter->type)) continue; - if (cr6 && ((cr6 & iter->io.io_int_word) == 0)) + if (cr6 && + ((cr6 & int_word_to_isc_bits(iter->io.io_int_word)) == 0)) continue; if (schid) { if (((schid & 0x00000000ffff0000) >> 16) != @@ -811,11 +820,14 @@ int kvm_s390_inject_vm(struct kvm *kvm, if (!is_ioint(inti->type)) list_add_tail(&inti->list, &fi->list); else { + u64 isc_bits = int_word_to_isc_bits(inti->io.io_int_word); + /* Keep I/O interrupts sorted in isc order. */ list_for_each_entry(iter, &fi->list, list) { if (!is_ioint(iter->type)) continue; - if (iter->io.io_int_word <= inti->io.io_int_word) + if (int_word_to_isc_bits(iter->io.io_int_word) + <= isc_bits) continue; break; } From 257090f70233084488f7b3ebe99be8c159a23281 Mon Sep 17 00:00:00 2001 From: Yang Zhang Date: Sun, 10 Feb 2013 22:57:18 +0800 Subject: [PATCH 2256/4607] KVM: VMX: disable apicv by default Without Posted Interrupt, current code is broken. Just disable by default until Posted Interrupt is ready. Signed-off-by: Yang Zhang Signed-off-by: Gleb Natapov --- arch/x86/kvm/vmx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index fe09fdc5624c..c7944782ecf6 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -84,8 +84,7 @@ module_param(vmm_exclusive, bool, S_IRUGO); static bool __read_mostly fasteoi = 1; module_param(fasteoi, bool, S_IRUGO); -static bool __read_mostly enable_apicv_reg_vid = 1; -module_param(enable_apicv_reg_vid, bool, S_IRUGO); +static bool __read_mostly enable_apicv_reg_vid; /* * If nested=1, nested virtualization is supported, i.e., guests may use From 7a905b1485adf863607b5fc9e32a3fa3838bcc23 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Thu, 7 Feb 2013 18:55:57 +0900 Subject: [PATCH 2257/4607] KVM: Remove user_alloc from struct kvm_memory_slot This field was needed to differentiate memory slots created by the new API, KVM_SET_USER_MEMORY_REGION, from those by the old equivalent, KVM_SET_MEMORY_REGION, whose support was dropped long before: commit b74a07beed0e64bfba413dcb70dd6749c57f43dc KVM: Remove kernel-allocated memory regions Although we also have private memory slots to which KVM allocates memory with vm_mmap(), !user_alloc slots in other words, the slot id should be enough for differentiating them. Note: corresponding function parameters will be removed later. Reviewed-by: Marcelo Tosatti Signed-off-by: Takuya Yoshikawa Signed-off-by: Gleb Natapov --- arch/x86/kvm/x86.c | 37 ++++++++++++++++--------------------- include/linux/kvm_host.h | 1 - virt/kvm/kvm_main.c | 1 - 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 373e17a0d398..3c5bb6fe5280 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6897,33 +6897,28 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, bool user_alloc) { int npages = memslot->npages; - int map_flags = MAP_PRIVATE | MAP_ANONYMOUS; - /* Prevent internal slot pages from being moved by fork()/COW. */ - if (memslot->id >= KVM_USER_MEM_SLOTS) - map_flags = MAP_SHARED | MAP_ANONYMOUS; - - /*To keep backward compatibility with older userspace, - *x86 needs to handle !user_alloc case. + /* + * Only private memory slots need to be mapped here since + * KVM_SET_MEMORY_REGION ioctl is no longer supported. */ - if (!user_alloc) { - if (npages && !old.npages) { - unsigned long userspace_addr; + if ((memslot->id >= KVM_USER_MEM_SLOTS) && npages && !old.npages) { + unsigned long userspace_addr; - userspace_addr = vm_mmap(NULL, 0, - npages * PAGE_SIZE, - PROT_READ | PROT_WRITE, - map_flags, - 0); + /* + * MAP_SHARED to prevent internal slot pages from being moved + * by fork()/COW. + */ + userspace_addr = vm_mmap(NULL, 0, npages * PAGE_SIZE, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, 0); - if (IS_ERR((void *)userspace_addr)) - return PTR_ERR((void *)userspace_addr); + if (IS_ERR((void *)userspace_addr)) + return PTR_ERR((void *)userspace_addr); - memslot->userspace_addr = userspace_addr; - } + memslot->userspace_addr = userspace_addr; } - return 0; } @@ -6935,7 +6930,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, int nr_mmu_pages = 0, npages = mem->memory_size >> PAGE_SHIFT; - if (!user_alloc && !old.user_alloc && old.npages && !npages) { + if ((mem->slot >= KVM_USER_MEM_SLOTS) && old.npages && !npages) { int ret; ret = vm_munmap(old.userspace_addr, diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 0350e0d5e031..722cae78bbc4 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -273,7 +273,6 @@ struct kvm_memory_slot { unsigned long userspace_addr; u32 flags; short id; - bool user_alloc; }; static inline unsigned long kvm_dirty_bitmap_bytes(struct kvm_memory_slot *memslot) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 2e93630b4add..adc68feb5c5a 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -839,7 +839,6 @@ int __kvm_set_memory_region(struct kvm *kvm, r = -ENOMEM; if (change == KVM_MR_CREATE) { - new.user_alloc = user_alloc; new.userspace_addr = mem->userspace_addr; if (kvm_arch_create_memslot(&new, npages)) From 136867f517cbc3f8a91f035677911a6b503c3323 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Tue, 5 Feb 2013 19:57:22 -0700 Subject: [PATCH 2258/4607] x86/kvm: Fix compile warning in kvm_register_steal_time() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following compile warning in kvm_register_steal_time(): CC arch/x86/kernel/kvm.o arch/x86/kernel/kvm.c: In function ‘kvm_register_steal_time’: arch/x86/kernel/kvm.c:302:3: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 3 has type ‘phys_addr_t’ [-Wformat] Introduced via: 5dfd486c4750 x86, kvm: Fix kvm's use of __pa() on percpu areas d76565344512 x86, mm: Create slow_virt_to_phys() f3c4fbb68e93 x86, mm: Use new pagetable helpers in try_preserve_large_page() 4cbeb51b860c x86, mm: Pagetable level size/shift/mask helpers a25b9316841c x86, mm: Make DEBUG_VIRTUAL work earlier in boot Signed-off-by: Shuah Khan Acked-by: Gleb Natapov Cc: Marcelo Tosatti Cc: Dave Hansen Cc: Rik van Riel Cc: shuahkhan@gmail.com Cc: avi@redhat.com Cc: gleb@redhat.com Cc: mst@redhat.com Link: http://lkml.kernel.org/r/1360119442.8356.8.camel@lorien2 Signed-off-by: Ingo Molnar --- arch/x86/kernel/kvm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index aa7e58b82b39..9cec20253093 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -298,8 +298,8 @@ static void kvm_register_steal_time(void) memset(st, 0, sizeof(*st)); wrmsrl(MSR_KVM_STEAL_TIME, (slow_virt_to_phys(st) | KVM_MSR_ENABLED)); - printk(KERN_INFO "kvm-stealtime: cpu %d, msr %lx\n", - cpu, slow_virt_to_phys(st)); + pr_info("kvm-stealtime: cpu %d, msr %llx\n", + cpu, (unsigned long long) slow_virt_to_phys(st)); } static DEFINE_PER_CPU(unsigned long, kvm_apic_eoi) = KVM_PV_EOI_DISABLED; From cfdbc2e16e65c1ec1c23057640607cee98d1a1bd Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:20 +0530 Subject: [PATCH 2259/4607] ARC: Build system: Makefiles, Kconfig, Linker script Arnd in his review pointed out that arch Kconfig organisation has several deficiencies: * Build time entries for things which can be runtime extracted from DT (e.g. SDRAM size, core clk frequency..) * Not multi-platform-image-build friendly (choice .. endchoice constructs) * cpu variants support (750/770) is exclusive. The first 2 have been fixed in subsequent patches. Due to the nature of the 750 and 770, it is not possible to build for both together, w/o special runtime glue code which would hurt performance. Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Cc: Sam Ravnborg Acked-by: Sam Ravnborg --- arch/arc/Kbuild | 2 + arch/arc/Kconfig | 328 +++++++++++++++++++++++++++++++++ arch/arc/Kconfig.debug | 34 ++++ arch/arc/Makefile | 115 ++++++++++++ arch/arc/boot/Makefile | 26 +++ arch/arc/include/asm/Kbuild | 1 + arch/arc/kernel/Makefile | 16 ++ arch/arc/kernel/arcksyms.c | 56 ++++++ arch/arc/kernel/asm-offsets.c | 61 ++++++ arch/arc/kernel/vmlinux.lds.S | 116 ++++++++++++ arch/arc/lib/Makefile | 9 + arch/arc/mm/Makefile | 10 + arch/arc/plat-arcfpga/Kconfig | 33 ++++ arch/arc/plat-arcfpga/Makefile | 9 + 14 files changed, 816 insertions(+) create mode 100644 arch/arc/Kbuild create mode 100644 arch/arc/Kconfig create mode 100644 arch/arc/Kconfig.debug create mode 100644 arch/arc/Makefile create mode 100644 arch/arc/boot/Makefile create mode 100644 arch/arc/kernel/Makefile create mode 100644 arch/arc/kernel/arcksyms.c create mode 100644 arch/arc/kernel/asm-offsets.c create mode 100644 arch/arc/kernel/vmlinux.lds.S create mode 100644 arch/arc/lib/Makefile create mode 100644 arch/arc/mm/Makefile create mode 100644 arch/arc/plat-arcfpga/Kconfig create mode 100644 arch/arc/plat-arcfpga/Makefile diff --git a/arch/arc/Kbuild b/arch/arc/Kbuild new file mode 100644 index 000000000000..082d329d3245 --- /dev/null +++ b/arch/arc/Kbuild @@ -0,0 +1,2 @@ +obj-y += kernel/ +obj-y += mm/ diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig new file mode 100644 index 000000000000..b0b09aea98ff --- /dev/null +++ b/arch/arc/Kconfig @@ -0,0 +1,328 @@ +# +# Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) +# +# 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. +# + +config ARC + def_bool y + select ARCH_NO_VIRT_TO_BUS + # ARC Busybox based initramfs absolutely relies on DEVTMPFS for /dev + select DEVTMPFS if !INITRAMFS_SOURCE="" + select GENERIC_ATOMIC64 + select GENERIC_CLOCKEVENTS + select GENERIC_FIND_FIRST_BIT + # for now, we don't need GENERIC_IRQ_PROBE, CONFIG_GENERIC_IRQ_CHIP + select GENERIC_IRQ_SHOW + select GENERIC_PENDING_IRQ if SMP + select GENERIC_SMP_IDLE_THREAD + select HAVE_GENERIC_HARDIRQS + select MODULES_USE_ELF_RELA + +config SCHED_OMIT_FRAME_POINTER + def_bool y + +config GENERIC_CSUM + def_bool y + +config RWSEM_GENERIC_SPINLOCK + def_bool y + +config ARCH_FLATMEM_ENABLE + def_bool y + +config MMU + def_bool y + +config NO_IOPORT + def_bool y + +config GENERIC_CALIBRATE_DELAY + def_bool y + +config GENERIC_HWEIGHT + def_bool y + +config BINFMT_ELF + def_bool y + +config HAVE_LATENCYTOP_SUPPORT + def_bool y + +config NO_DMA + def_bool n + +source "init/Kconfig" +source "kernel/Kconfig.freezer" + +menu "ARC Architecture Configuration" + +choice + prompt "ARC Platform" + default ARC_PLAT_FPGA_LEGACY + +config ARC_PLAT_FPGA_LEGACY + bool "\"Legacy\" ARC FPGA dev platform" + help + Support for ARC development platforms, provided by Synopsys. + These are based on FPGA or ISS. e.g. + - ARCAngel4 + - ML509 + - MetaWare ISS + +#New platform adds here +endchoice + +menu "ARC CPU Configuration" + +choice + prompt "ARC Core" + default ARC_CPU_770 + +config ARC_CPU_750D + bool "ARC750D" + help + Support for ARC750 core + +config ARC_CPU_770 + bool "ARC770" + select ARC_CPU_REL_4_10 + help + Support for ARC770 core introduced with Rel 4.10 (Summer 2011) + This core has a bunch of cool new features: + -MMU-v3: Variable Page Sz (4k, 8k, 16k), bigger J-TLB (128x4) + Shared Address Spaces (for sharing TLB entires in MMU) + -Caches: New Prog Model, Region Flush + -Insns: endian swap, load-locked/store-conditional, time-stamp-ctr + +endchoice + +config CPU_BIG_ENDIAN + bool "Enable Big Endian Mode" + default n + help + Build kernel for Big Endian Mode of ARC CPU + +menuconfig ARC_CACHE + bool "Enable Cache Support" + default y + +if ARC_CACHE + +config ARC_CACHE_LINE_SHIFT + int "Cache Line Length (as power of 2)" + range 5 7 + default "6" + help + Starting with ARC700 4.9, Cache line length is configurable, + This option specifies "N", with Line-len = 2 power N + So line lengths of 32, 64, 128 are specified by 5,6,7, respectively + Linux only supports same line lengths for I and D caches. + +config ARC_HAS_ICACHE + bool "Use Instruction Cache" + default y + +config ARC_HAS_DCACHE + bool "Use Data Cache" + default y + +config ARC_CACHE_PAGES + bool "Per Page Cache Control" + default y + depends on ARC_HAS_ICACHE || ARC_HAS_DCACHE + help + This can be used to over-ride the global I/D Cache Enable on a + per-page basis (but only for pages accessed via MMU such as + Kernel Virtual address or User Virtual Address) + TLB entries have a per-page Cache Enable Bit. + Note that Global I/D ENABLE + Per Page DISABLE works but corollary + Global DISABLE + Per Page ENABLE won't work + +endif #ARC_CACHE + +config ARC_HAS_HW_MPY + bool "Use Hardware Multiplier (Normal or Faster XMAC)" + default y + help + Influences how gcc generates code for MPY operations. + If enabled, MPYxx insns are generated, provided by Standard/XMAC + Multipler. Otherwise software multipy lib is used + +choice + prompt "ARC700 MMU Version" + default ARC_MMU_V3 if ARC_CPU_770 + default ARC_MMU_V2 if ARC_CPU_750D + +config ARC_MMU_V1 + bool "MMU v1" + help + Orig ARC700 MMU + +config ARC_MMU_V2 + bool "MMU v2" + help + Fixed the deficiency of v1 - possible thrashing in memcpy sceanrio + when 2 D-TLB and 1 I-TLB entries index into same 2way set. + +config ARC_MMU_V3 + bool "MMU v3" + depends on ARC_CPU_770 + help + Introduced with ARC700 4.10: New Features + Variable Page size (1k-16k), var JTLB size 128 x (2 or 4) + Shared Address Spaces (SASID) + +endchoice + + +choice + prompt "MMU Page Size" + default ARC_PAGE_SIZE_8K + +config ARC_PAGE_SIZE_8K + bool "8KB" + help + Choose between 8k vs 16k + +config ARC_PAGE_SIZE_16K + bool "16KB" + depends on ARC_MMU_V3 + +config ARC_PAGE_SIZE_4K + bool "4KB" + depends on ARC_MMU_V3 + +endchoice + +config ARC_FPU_SAVE_RESTORE + bool "Enable FPU state persistence across context switch" + default n + help + Double Precision Floating Point unit had dedictaed regs which + need to be saved/restored across context-switch. + Note that ARC FPU is overly simplistic, unlike say x86, which has + hardware pieces to allow software to conditionally save/restore, + based on actual usage of FPU by a task. Thus our implemn does + this for all tasks in system. + +menuconfig ARC_CPU_REL_4_10 + bool "Enable support for Rel 4.10 features" + default n + help + -ARC770 (and dependent features) enabled + -ARC750 also shares some of the new features with 770 + +config ARC_HAS_LLSC + bool "Insn: LLOCK/SCOND (efficient atomic ops)" + default y + depends on ARC_CPU_770 + # if SMP, enable LLSC ONLY if ARC implementation has coherent atomics + depends on !SMP || ARC_HAS_COH_LLSC + +config ARC_HAS_SWAPE + bool "Insn: SWAPE (endian-swap)" + default y + depends on ARC_CPU_REL_4_10 + +config ARC_HAS_RTSC + bool "Insn: RTSC (64-bit r/o cycle counter)" + default y + depends on ARC_CPU_REL_4_10 + +endmenu # "ARC CPU Configuration" + +menu "Platform Board Configuration" + +source "arch/arc/plat-arcfpga/Kconfig" + +#New platform adds here + +config ARC_PLAT_CLK + int "Clk speed in Hz" + default "80000000" + +config LINUX_LINK_BASE + hex "Linux Link Address" + default "0x80000000" + help + ARC700 divides the 32 bit phy address space into two equal halves + -Lower 2G (0 - 0x7FFF_FFFF ) is user virtual, translated by MMU + -Upper 2G (0x8000_0000 onwards) is untranslated, for kernel + Typically Linux kernel is linked at the start of untransalted addr, + hence the default value of 0x8zs. + However some customers have peripherals mapped at this addr, so + Linux needs to be scooted a bit. + If you don't know what the above means, leave this setting alone. + +config ARC_PLAT_SDRAM_SIZE + hex "SD RAM Size" + default "0x10000000" + help + Implies the amount of SDRAM/DRAM Linux is going to claim/own. + The actual memory itself could be larger than this number. But for + all software purposes, this is the amt of memory. + +endmenu # "Platform Board Configuration" + +config ARC_STACK_NONEXEC + bool "Make stack non-executable" + default n + help + To disable the execute permissions of stack/heap of processes + which are enabled by default. + +config HZ + int "Timer Frequency" + default 100 + +menuconfig ARC_DBG + bool "ARC debugging" + default y + +config ARC_DBG_TLB_PARANOIA + bool "Paranoia Checks in Low Level TLB Handlers" + depends on ARC_DBG + default n + +config ARC_DBG_TLB_MISS_COUNT + bool "Profile TLB Misses" + default n + select DEBUG_FS + depends on ARC_DBG + help + Counts number of I and D TLB Misses and exports them via Debugfs + The counters can be cleared via Debugfs as well + +config CMDLINE + string "Kernel command line to built-in" + default "print-fatal-signals=1" + help + The default command line which will be appended to the optional + u-boot provided command line (see below) + +config CMDLINE_UBOOT + bool "Support U-boot kernel command line passing" + default n + help + If you are using U-boot (www.denx.de) and wish to pass the kernel + command line from the U-boot environment to the Linux kernel then + switch this option on. + ARC U-boot will setup the cmdline in RAM/flash and set r2 to point + to it. kernel startup code will copy the string into cmdline buffer + and also append CONFIG_CMDLINE. + +source "kernel/Kconfig.preempt" + +endmenu # "ARC Architecture Configuration" + +source "mm/Kconfig" +source "net/Kconfig" +source "drivers/Kconfig" +source "fs/Kconfig" +source "arch/arc/Kconfig.debug" +source "security/Kconfig" +source "crypto/Kconfig" +source "lib/Kconfig" diff --git a/arch/arc/Kconfig.debug b/arch/arc/Kconfig.debug new file mode 100644 index 000000000000..962c6099659e --- /dev/null +++ b/arch/arc/Kconfig.debug @@ -0,0 +1,34 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config EARLY_PRINTK + bool "Early printk" if EMBEDDED + default y + help + Write kernel log output directly into the VGA buffer or to a serial + port. + + This is useful for kernel debugging when your machine crashes very + early before the console code is initialized. For normal operation + it is not recommended because it looks ugly and doesn't cooperate + with klogd/syslogd or the X server. You should normally N here, + unless you want to debug such a crash. + +config DEBUG_STACKOVERFLOW + bool "Check for stack overflows" + depends on DEBUG_KERNEL + help + This option will cause messages to be printed if free stack space + drops below a certain limit. + +config 16KSTACKS + bool "Use 16Kb for kernel stacks instead of 8Kb" + help + If you say Y here the kernel will use a 16Kb stacksize for the + kernel stack attached to each process/thread. The default is 8K. + This increases the resident kernel footprint and will cause less + threads to run on the system and also increase the pressure + on the VM subsystem for higher order allocations. + +endmenu diff --git a/arch/arc/Makefile b/arch/arc/Makefile new file mode 100644 index 000000000000..4d52a3bb68a0 --- /dev/null +++ b/arch/arc/Makefile @@ -0,0 +1,115 @@ +# +# Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) +# +# 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. +# + +UTS_MACHINE := arc + +KBUILD_DEFCONFIG := fpga_defconfig + +# For ARC FPGA Platforms +platform-$(CONFIG_ARC_PLAT_FPGA_LEGACY) := arcfpga +#New platform adds here + +PLATFORM := $(platform-y) +export PLATFORM + +cflags-y += -Iarch/arc/plat-$(PLATFORM)/include +cflags-y += -mA7 -fno-common -pipe -fno-builtin -D__linux__ + +atleast_gcc44 := $(call cc-ifversion, -gt, 0402, y) +cflags-$(atleast_gcc44) += -fsection-anchors + +cflags-$(CONFIG_ARC_HAS_LLSC) += -mlock +cflags-$(CONFIG_ARC_HAS_SWAPE) += -mswape +cflags-$(CONFIG_ARC_HAS_RTSC) += -mrtsc +cflags-$(CONFIG_ARC_DW2_UNWIND) += -fasynchronous-unwind-tables + +ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE +# Generic build system uses -O2, we want -O3 +cflags-y += -O3 +endif + +# small data is default for elf32 tool-chain. If not usable, disable it +# This also allows repurposing GP as scratch reg to gcc reg allocator +disable_small_data := y +cflags-$(disable_small_data) += -mno-sdata -fcall-used-gp + +cflags-$(CONFIG_CPU_BIG_ENDIAN) += -mbig-endian +ldflags-$(CONFIG_CPU_BIG_ENDIAN) += -EB + +# STAR 9000518362: +# arc-linux-uclibc-ld (buildroot) or arceb-elf32-ld (EZChip) don't accept +# --build-id w/o "-marclinux". +# Default arc-elf32-ld is OK +ldflags-y += -marclinux + +ARC_LIBGCC := -mA7 +cflags-$(CONFIG_ARC_HAS_HW_MPY) += -multcost=16 + +ifndef CONFIG_ARC_HAS_HW_MPY + cflags-y += -mno-mpy + +# newlib for ARC700 assumes MPY to be always present, which is generally true +# However, if someone really doesn't want MPY, we need to use the 600 ver +# which coupled with -mno-mpy will use mpy emulation +# With gcc 4.4.7, -mno-mpy is enough to make any other related adjustments, +# e.g. increased cost of MPY. With gcc 4.2.1 this had to be explicitly hinted + + ARC_LIBGCC := -marc600 + ifneq ($(atleast_gcc44),y) + cflags-y += -multcost=30 + endif +endif + +LIBGCC := $(shell $(CC) $(ARC_LIBGCC) $(cflags-y) --print-libgcc-file-name) + +# Modules with short calls might break for calls into builtin-kernel +KBUILD_CFLAGS_MODULE += -mlong-calls + +# Finally dump eveything into kernel build system +KBUILD_CFLAGS += $(cflags-y) +KBUILD_AFLAGS += $(KBUILD_CFLAGS) +LDFLAGS += $(ldflags-y) + +# Needed for Linker script preprocessing +KBUILD_CPPFLAGS += -Iarch/arc/plat-$(PLATFORM)/include + +head-y := arch/arc/kernel/head.o + +# See arch/arc/Kbuild for content of core part of the kernel +core-y += arch/arc/ + +# w/o this ifneq, make ARCH=arc clean was crapping out +ifneq ($(platform-y),) +core-y += arch/arc/plat-$(PLATFORM)/ +endif + +libs-y += arch/arc/lib/ $(LIBGCC) + +#default target for make without any arguements. +KBUILD_IMAGE := bootpImage + +all: $(KBUILD_IMAGE) +boot := arch/arc/boot + +bootpImage: vmlinux + +uImage: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +archclean: + $(Q)$(MAKE) $(clean)=$(boot) + +# Hacks to enable final link due to absence of link-time branch relexation +# and gcc choosing optimal(shorter) branches at -O3 +# +# vineetg Feb 2010: -mlong-calls switched off for overall kernel build +# However lib/decompress_inflate.o (.init.text) calls +# zlib_inflate_workspacesize (.text) causing relocation errors. +# Thus forcing all exten calls in this file to be long calls +export CFLAGS_decompress_inflate.o = -mmedium-calls +export CFLAGS_initramfs.o = -mmedium-calls diff --git a/arch/arc/boot/Makefile b/arch/arc/boot/Makefile new file mode 100644 index 000000000000..7d514c24e095 --- /dev/null +++ b/arch/arc/boot/Makefile @@ -0,0 +1,26 @@ +targets := vmlinux.bin vmlinux.bin.gz uImage + +# uImage build relies on mkimage being availble on your host for ARC target +# You will need to build u-boot for ARC, rename mkimage to arc-elf32-mkimage +# and make sure it's reacable from your PATH +MKIMAGE := $(srctree)/scripts/mkuboot.sh + +OBJCOPYFLAGS= -O binary -R .note -R .note.gnu.build-id -R .comment -S + +LINUX_START_TEXT = $$(readelf -h vmlinux | \ + grep "Entry point address" | grep -o 0x.*) + +UIMAGE_LOADADDR = $(CONFIG_LINUX_LINK_BASE) +UIMAGE_ENTRYADDR = $(LINUX_START_TEXT) +UIMAGE_COMPRESSION = gzip + +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE + $(call if_changed,gzip) + +$(obj)/uImage: $(obj)/vmlinux.bin.gz FORCE + $(call if_changed,uimage) + +PHONY += FORCE diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild index a90a3c6c97e4..105ec1188aaf 100644 --- a/arch/arc/include/asm/Kbuild +++ b/arch/arc/include/asm/Kbuild @@ -33,6 +33,7 @@ generic-y += mman.h generic-y += msgbuf.h generic-y += param.h generic-y += parport.h +generic-y += pci.h generic-y += percpu.h generic-y += poll.h generic-y += posix_types.h diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile new file mode 100644 index 000000000000..6d834312f755 --- /dev/null +++ b/arch/arc/kernel/Makefile @@ -0,0 +1,16 @@ +# +# Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) +# +# 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. + +obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o +obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o clk.o + +obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o +CFLAGS_fpu.o += -mdpfp + +obj-y += ctx_sw_asm.o + +extra-y := vmlinux.lds head.o diff --git a/arch/arc/kernel/arcksyms.c b/arch/arc/kernel/arcksyms.c new file mode 100644 index 000000000000..4d9e77724bed --- /dev/null +++ b/arch/arc/kernel/arcksyms.c @@ -0,0 +1,56 @@ +/* + * arcksyms.c - Exporting symbols not exportable from their own sources + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 + +/* libgcc functions, not part of kernel sources */ +extern void __ashldi3(void); +extern void __ashrdi3(void); +extern void __divsi3(void); +extern void __divsf3(void); +extern void __lshrdi3(void); +extern void __modsi3(void); +extern void __muldi3(void); +extern void __ucmpdi2(void); +extern void __udivsi3(void); +extern void __umodsi3(void); +extern void __cmpdi2(void); +extern void __fixunsdfsi(void); +extern void __muldf3(void); +extern void __divdf3(void); +extern void __floatunsidf(void); +extern void __floatunsisf(void); + +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__divsi3); +EXPORT_SYMBOL(__divsf3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__modsi3); +EXPORT_SYMBOL(__muldi3); +EXPORT_SYMBOL(__ucmpdi2); +EXPORT_SYMBOL(__udivsi3); +EXPORT_SYMBOL(__umodsi3); +EXPORT_SYMBOL(__cmpdi2); +EXPORT_SYMBOL(__fixunsdfsi); +EXPORT_SYMBOL(__muldf3); +EXPORT_SYMBOL(__divdf3); +EXPORT_SYMBOL(__floatunsidf); +EXPORT_SYMBOL(__floatunsisf); + +/* ARC optimised assembler routines */ +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strlen); diff --git a/arch/arc/kernel/asm-offsets.c b/arch/arc/kernel/asm-offsets.c new file mode 100644 index 000000000000..64b2c2fa7b54 --- /dev/null +++ b/arch/arc/kernel/asm-offsets.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +int main(void) +{ + DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); + DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, stack)); + + BLANK(); + + DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); + DEFINE(THREAD_CALLEE_REG, offsetof(struct thread_struct, callee_reg)); + DEFINE(THREAD_FAULT_ADDR, + offsetof(struct thread_struct, fault_address)); + + BLANK(); + + DEFINE(THREAD_INFO_FLAGS, offsetof(struct thread_info, flags)); + DEFINE(THREAD_INFO_PREEMPT_COUNT, + offsetof(struct thread_info, preempt_count)); + + BLANK(); + + DEFINE(TASK_ACT_MM, offsetof(struct task_struct, active_mm)); + DEFINE(TASK_TGID, offsetof(struct task_struct, tgid)); + + DEFINE(MM_CTXT, offsetof(struct mm_struct, context)); + DEFINE(MM_PGD, offsetof(struct mm_struct, pgd)); + + DEFINE(MM_CTXT_ASID, offsetof(mm_context_t, asid)); + + BLANK(); + + DEFINE(PT_status32, offsetof(struct pt_regs, status32)); + DEFINE(PT_orig_r8, offsetof(struct pt_regs, orig_r8)); + DEFINE(PT_sp, offsetof(struct pt_regs, sp)); + DEFINE(PT_r0, offsetof(struct pt_regs, r0)); + DEFINE(PT_r1, offsetof(struct pt_regs, r1)); + DEFINE(PT_r2, offsetof(struct pt_regs, r2)); + DEFINE(PT_r3, offsetof(struct pt_regs, r3)); + DEFINE(PT_r4, offsetof(struct pt_regs, r4)); + DEFINE(PT_r5, offsetof(struct pt_regs, r5)); + DEFINE(PT_r6, offsetof(struct pt_regs, r6)); + DEFINE(PT_r7, offsetof(struct pt_regs, r7)); + + return 0; +} diff --git a/arch/arc/kernel/vmlinux.lds.S b/arch/arc/kernel/vmlinux.lds.S new file mode 100644 index 000000000000..8c72bc37568b --- /dev/null +++ b/arch/arc/kernel/vmlinux.lds.S @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include +#include + +OUTPUT_ARCH(arc) +ENTRY(_stext) + +#ifdef CONFIG_CPU_BIG_ENDIAN +jiffies = jiffies_64 + 4; +#else +jiffies = jiffies_64; +#endif + +SECTIONS +{ + . = CONFIG_LINUX_LINK_BASE; + + _int_vec_base_lds = .; + .vector : { + *(.vector) + . = ALIGN(PAGE_SIZE); + } + + /* + * The reason for having a seperate subsection .init.ramfs is to + * prevent objump from including it in kernel dumps + * + * Reason for having .init.ramfs above .init is to make sure that the + * binary blob is tucked away to one side, reducing the displacement + * between .init.text and .text, avoiding any possible relocation + * errors because of calls from .init.text to .text + * Yes such calls do exist. e.g. + * decompress_inflate.c:gunzip( ) -> zlib_inflate_workspace( ) + */ + + __init_begin = .; + + .init.ramfs : { INIT_RAM_FS } + + . = ALIGN(PAGE_SIZE); + _stext = .; + + HEAD_TEXT_SECTION + INIT_TEXT_SECTION(L1_CACHE_BYTES) + + /* INIT_DATA_SECTION open-coded: special INIT_RAM_FS handling */ + .init.data : { + INIT_DATA + INIT_SETUP(L1_CACHE_BYTES) + INIT_CALLS + CON_INITCALL + SECURITY_INITCALL + } + + PERCPU_SECTION(L1_CACHE_BYTES) + + /* + * .exit.text is discard at runtime, not link time, to deal with + * references from .debug_frame + * It will be init freed, being inside [__init_start : __init_end] + */ + .exit.text : { EXIT_TEXT } + .exit.data : { EXIT_DATA } + + . = ALIGN(PAGE_SIZE); + __init_end = .; + + .text : { + _text = .; + TEXT_TEXT + SCHED_TEXT + LOCK_TEXT + KPROBES_TEXT + *(.fixup) + *(.gnu.warning) + } + EXCEPTION_TABLE(L1_CACHE_BYTES) + _etext = .; + + _sdata = .; + RO_DATA_SECTION(PAGE_SIZE) + + /* + * 1. this is .data essentially + * 2. THREAD_SIZE for init.task, must be kernel-stk sz aligned + */ + RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE) + + _edata = .; + + BSS_SECTION(0, 0, 0) + + NOTES + + . = ALIGN(PAGE_SIZE); + _end = . ; + + STABS_DEBUG + DWARF_DEBUG + DISCARDS + + .arcextmap 0 : { + *(.gnu.linkonce.arcextmap.*) + *(.arcextmap.*) + } +} diff --git a/arch/arc/lib/Makefile b/arch/arc/lib/Makefile new file mode 100644 index 000000000000..db46e200baba --- /dev/null +++ b/arch/arc/lib/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) +# +# 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. + +lib-y := strchr-700.o strcmp.o strcpy-700.o strlen.o +lib-y += memcmp.o memcpy-700.o memset.o diff --git a/arch/arc/mm/Makefile b/arch/arc/mm/Makefile new file mode 100644 index 000000000000..168dc146a8f6 --- /dev/null +++ b/arch/arc/mm/Makefile @@ -0,0 +1,10 @@ +# +# Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) +# +# 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. +# + +obj-y := extable.o ioremap.o dma.o fault.o init.o +obj-y += tlb.o tlbex.o cache_arc700.o diff --git a/arch/arc/plat-arcfpga/Kconfig b/arch/arc/plat-arcfpga/Kconfig new file mode 100644 index 000000000000..7af3a4e7f7d2 --- /dev/null +++ b/arch/arc/plat-arcfpga/Kconfig @@ -0,0 +1,33 @@ +# +# Copyright (C) 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) +# +# 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. +# + +if ARC_PLAT_FPGA_LEGACY + +choice + prompt "FPGA Board" + +config ARC_BOARD_ANGEL4 + bool "ARC Angel4" + help + ARC Angel4 FPGA Ref Platform (Xilinx Virtex Based) + +config ARC_BOARD_ML509 + bool "ML509" + help + ARC ML509 FPGA Ref Platform (Xilinx Virtex-5 Based) + +endchoice + +config ARC_SERIAL_BAUD + int "UART Baud rate" + default "115200" + depends on SERIAL_ARC || SERIAL_ARC_CONSOLE + help + Baud rate for the ARC UART + +endif diff --git a/arch/arc/plat-arcfpga/Makefile b/arch/arc/plat-arcfpga/Makefile new file mode 100644 index 000000000000..385eb9d83b63 --- /dev/null +++ b/arch/arc/plat-arcfpga/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (C) 2011-2012 Synopsys, Inc. (www.synopsys.com) +# +# 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. +# + +obj-y := platform.o irq.o From ac4c244d4e5d914f9a5642cdcc03b18780e55dbc Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:16 +0530 Subject: [PATCH 2260/4607] ARC: irqflags - Interrupt enabling/disabling at in-core intc ARC700 has an in-core intc which provides 2 priorities (a.k.a.) "levels" of interrupts (per IRQ) hencforth referred to as L1/L2 interrupts. CPU flags register STATUS32 has Interrupt Enable bits per level (E1/E2) to globally enable (or disable) all IRQs at a level. Hence the implementation of arch_local_irq_{save,restore,enable,disable}( ) The STATUS32 reg can be r/w only using the AUX Interface of ARC, hence the use of LR/SR instructions. Further, E1/E2 bits in there can only be updated using the FLAG insn. The intc supports 32 interrupts - and per IRQ enabling is controlled by a bit in the AUX_IENABLE register, hence the implmentation of arch_{,un}mask_irq( ) routines. Signed-off-by: Vineet Gupta Cc: Thomas Gleixner --- arch/arc/include/asm/arcregs.h | 114 ++++++++++++++++++++++++ arch/arc/include/asm/irqflags.h | 149 ++++++++++++++++++++++++++++++++ arch/arc/kernel/irq.c | 32 +++++++ 3 files changed, 295 insertions(+) create mode 100644 arch/arc/include/asm/arcregs.h create mode 100644 arch/arc/include/asm/irqflags.h create mode 100644 arch/arc/kernel/irq.c diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h new file mode 100644 index 000000000000..8ca8faf4b80e --- /dev/null +++ b/arch/arc/include/asm/arcregs.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_ARCREGS_H +#define _ASM_ARC_ARCREGS_H + +#ifdef __KERNEL__ + +/* status32 Bits Positions */ +#define STATUS_H_BIT 0 /* CPU Halted */ +#define STATUS_E1_BIT 1 /* Int 1 enable */ +#define STATUS_E2_BIT 2 /* Int 2 enable */ +#define STATUS_A1_BIT 3 /* Int 1 active */ +#define STATUS_A2_BIT 4 /* Int 2 active */ +#define STATUS_AE_BIT 5 /* Exception active */ +#define STATUS_DE_BIT 6 /* PC is in delay slot */ +#define STATUS_U_BIT 7 /* User/Kernel mode */ +#define STATUS_L_BIT 12 /* Loop inhibit */ + +/* These masks correspond to the status word(STATUS_32) bits */ +#define STATUS_H_MASK (1< + +#ifndef __ASSEMBLY__ + +/****************************************************************** + * IRQ Control Macros + ******************************************************************/ + +/* + * Save IRQ state and disable IRQs + */ +static inline long arch_local_irq_save(void) +{ + unsigned long temp, flags; + + __asm__ __volatile__( + " lr %1, [status32] \n" + " bic %0, %1, %2 \n" + " and.f 0, %1, %2 \n" + " flag.nz %0 \n" + : "=r"(temp), "=r"(flags) + : "n"((STATUS_E1_MASK | STATUS_E2_MASK)) + : "cc"); + + return flags; +} + +/* + * restore saved IRQ state + */ +static inline void arch_local_irq_restore(unsigned long flags) +{ + + __asm__ __volatile__( + " flag %0 \n" + : + : "r"(flags)); +} + +/* + * Unconditionally Enable IRQs + */ +extern void arch_local_irq_enable(void); + +/* + * Unconditionally Disable IRQs + */ +static inline void arch_local_irq_disable(void) +{ + unsigned long temp; + + __asm__ __volatile__( + " lr %0, [status32] \n" + " and %0, %0, %1 \n" + " flag %0 \n" + : "=&r"(temp) + : "n"(~(STATUS_E1_MASK | STATUS_E2_MASK))); +} + +/* + * save IRQ state + */ +static inline long arch_local_save_flags(void) +{ + unsigned long temp; + + __asm__ __volatile__( + " lr %0, [status32] \n" + : "=&r"(temp)); + + return temp; +} + +/* + * Query IRQ state + */ +static inline int arch_irqs_disabled_flags(unsigned long flags) +{ + return !(flags & (STATUS_E1_MASK)); +} + +static inline int arch_irqs_disabled(void) +{ + return arch_irqs_disabled_flags(arch_local_save_flags()); +} + +static inline void arch_mask_irq(unsigned int irq) +{ + unsigned int ienb; + + ienb = read_aux_reg(AUX_IENABLE); + ienb &= ~(1 << irq); + write_aux_reg(AUX_IENABLE, ienb); +} + +static inline void arch_unmask_irq(unsigned int irq) +{ + unsigned int ienb; + + ienb = read_aux_reg(AUX_IENABLE); + ienb |= (1 << irq); + write_aux_reg(AUX_IENABLE, ienb); +} + +#else + +.macro IRQ_DISABLE scratch + lr \scratch, [status32] + bic \scratch, \scratch, (STATUS_E1_MASK | STATUS_E2_MASK) + flag \scratch +.endm + +.macro IRQ_DISABLE_SAVE scratch, save + lr \scratch, [status32] + mov \save, \scratch /* Make a copy */ + bic \scratch, \scratch, (STATUS_E1_MASK | STATUS_E2_MASK) + flag \scratch +.endm + +.macro IRQ_ENABLE scratch + lr \scratch, [status32] + or \scratch, \scratch, (STATUS_E1_MASK | STATUS_E2_MASK) + flag \scratch +.endm + +#endif /* __ASSEMBLY__ */ + +#endif /* KERNEL */ + +#endif diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c new file mode 100644 index 000000000000..c4e9b25d305f --- /dev/null +++ b/arch/arc/kernel/irq.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011-12 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include + +void arch_local_irq_enable(void) +{ + unsigned long flags; + + /* + * ARC IDE Drivers tries to re-enable interrupts from hard-isr + * context which is simply wrong + */ + if (in_irq()) { + WARN_ONCE(1, "IRQ enabled from hard-isr"); + return; + } + + flags = arch_local_save_flags(); + flags |= (STATUS_E1_MASK | STATUS_E2_MASK); + arch_local_irq_restore(flags); +} +EXPORT_SYMBOL(arch_local_irq_enable); From 14e968bad788de922a755a84b92cb29f8c1342e4 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:16 +0530 Subject: [PATCH 2261/4607] ARC: Atomic/bitops/cmpxchg/barriers This covers the UP / SMP (with no hardware assist for atomic r-m-w) as well as ARC700 LLOCK/SCOND insns based. Signed-off-by: Vineet Gupta --- arch/arc/include/asm/atomic.h | 232 +++++++++++++++ arch/arc/include/asm/barrier.h | 42 +++ arch/arc/include/asm/bitops.h | 516 +++++++++++++++++++++++++++++++++ arch/arc/include/asm/cmpxchg.h | 143 +++++++++ arch/arc/include/asm/smp.h | 34 +++ 5 files changed, 967 insertions(+) create mode 100644 arch/arc/include/asm/atomic.h create mode 100644 arch/arc/include/asm/barrier.h create mode 100644 arch/arc/include/asm/bitops.h create mode 100644 arch/arc/include/asm/cmpxchg.h create mode 100644 arch/arc/include/asm/smp.h diff --git a/arch/arc/include/asm/atomic.h b/arch/arc/include/asm/atomic.h new file mode 100644 index 000000000000..83f03ca6caf6 --- /dev/null +++ b/arch/arc/include/asm/atomic.h @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_ATOMIC_H +#define _ASM_ARC_ATOMIC_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ + +#include +#include +#include +#include +#include + +#define atomic_read(v) ((v)->counter) + +#ifdef CONFIG_ARC_HAS_LLSC + +#define atomic_set(v, i) (((v)->counter) = (i)) + +static inline void atomic_add(int i, atomic_t *v) +{ + unsigned int temp; + + __asm__ __volatile__( + "1: llock %0, [%1] \n" + " add %0, %0, %2 \n" + " scond %0, [%1] \n" + " bnz 1b \n" + : "=&r"(temp) /* Early clobber, to prevent reg reuse */ + : "r"(&v->counter), "ir"(i) + : "cc"); +} + +static inline void atomic_sub(int i, atomic_t *v) +{ + unsigned int temp; + + __asm__ __volatile__( + "1: llock %0, [%1] \n" + " sub %0, %0, %2 \n" + " scond %0, [%1] \n" + " bnz 1b \n" + : "=&r"(temp) + : "r"(&v->counter), "ir"(i) + : "cc"); +} + +/* add and also return the new value */ +static inline int atomic_add_return(int i, atomic_t *v) +{ + unsigned int temp; + + __asm__ __volatile__( + "1: llock %0, [%1] \n" + " add %0, %0, %2 \n" + " scond %0, [%1] \n" + " bnz 1b \n" + : "=&r"(temp) + : "r"(&v->counter), "ir"(i) + : "cc"); + + return temp; +} + +static inline int atomic_sub_return(int i, atomic_t *v) +{ + unsigned int temp; + + __asm__ __volatile__( + "1: llock %0, [%1] \n" + " sub %0, %0, %2 \n" + " scond %0, [%1] \n" + " bnz 1b \n" + : "=&r"(temp) + : "r"(&v->counter), "ir"(i) + : "cc"); + + return temp; +} + +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) +{ + unsigned int temp; + + __asm__ __volatile__( + "1: llock %0, [%1] \n" + " bic %0, %0, %2 \n" + " scond %0, [%1] \n" + " bnz 1b \n" + : "=&r"(temp) + : "r"(addr), "ir"(mask) + : "cc"); +} + +#else /* !CONFIG_ARC_HAS_LLSC */ + +#ifndef CONFIG_SMP + + /* violating atomic_xxx API locking protocol in UP for optimization sake */ +#define atomic_set(v, i) (((v)->counter) = (i)) + +#else + +static inline void atomic_set(atomic_t *v, int i) +{ + /* + * Independent of hardware support, all of the atomic_xxx() APIs need + * to follow the same locking rules to make sure that a "hardware" + * atomic insn (e.g. LD) doesn't clobber an "emulated" atomic insn + * sequence + * + * Thus atomic_set() despite being 1 insn (and seemingly atomic) + * requires the locking. + */ + unsigned long flags; + + atomic_ops_lock(flags); + v->counter = i; + atomic_ops_unlock(flags); +} +#endif + +/* + * Non hardware assisted Atomic-R-M-W + * Locking would change to irq-disabling only (UP) and spinlocks (SMP) + */ + +static inline void atomic_add(int i, atomic_t *v) +{ + unsigned long flags; + + atomic_ops_lock(flags); + v->counter += i; + atomic_ops_unlock(flags); +} + +static inline void atomic_sub(int i, atomic_t *v) +{ + unsigned long flags; + + atomic_ops_lock(flags); + v->counter -= i; + atomic_ops_unlock(flags); +} + +static inline int atomic_add_return(int i, atomic_t *v) +{ + unsigned long flags; + unsigned long temp; + + atomic_ops_lock(flags); + temp = v->counter; + temp += i; + v->counter = temp; + atomic_ops_unlock(flags); + + return temp; +} + +static inline int atomic_sub_return(int i, atomic_t *v) +{ + unsigned long flags; + unsigned long temp; + + atomic_ops_lock(flags); + temp = v->counter; + temp -= i; + v->counter = temp; + atomic_ops_unlock(flags); + + return temp; +} + +static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) +{ + unsigned long flags; + + atomic_ops_lock(flags); + *addr &= ~mask; + atomic_ops_unlock(flags); +} + +#endif /* !CONFIG_ARC_HAS_LLSC */ + +/** + * __atomic_add_unless - add unless the number is a given value + * @v: pointer of type atomic_t + * @a: the amount to add to v... + * @u: ...unless v is equal to u. + * + * Atomically adds @a to @v, so long as it was not @u. + * Returns the old value of @v + */ +#define __atomic_add_unless(v, a, u) \ +({ \ + int c, old; \ + c = atomic_read(v); \ + while (c != (u) && (old = atomic_cmpxchg((v), c, c + (a))) != c)\ + c = old; \ + c; \ +}) + +#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0) + +#define atomic_inc(v) atomic_add(1, v) +#define atomic_dec(v) atomic_sub(1, v) + +#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0) +#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) +#define atomic_inc_return(v) atomic_add_return(1, (v)) +#define atomic_dec_return(v) atomic_sub_return(1, (v)) +#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0) + +#define atomic_add_negative(i, v) (atomic_add_return(i, v) < 0) + +#define ATOMIC_INIT(i) { (i) } + +#include + +#endif + +#endif + +#endif diff --git a/arch/arc/include/asm/barrier.h b/arch/arc/include/asm/barrier.h new file mode 100644 index 000000000000..f6cb7c4ffb35 --- /dev/null +++ b/arch/arc/include/asm/barrier.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_BARRIER_H +#define __ASM_BARRIER_H + +#ifndef __ASSEMBLY__ + +/* TODO-vineetg: Need to see what this does, don't we need sync anywhere */ +#define mb() __asm__ __volatile__ ("" : : : "memory") +#define rmb() mb() +#define wmb() mb() +#define set_mb(var, value) do { var = value; mb(); } while (0) +#define set_wmb(var, value) do { var = value; wmb(); } while (0) +#define read_barrier_depends() mb() + +/* TODO-vineetg verify the correctness of macros here */ +#ifdef CONFIG_SMP +#define smp_mb() mb() +#define smp_rmb() rmb() +#define smp_wmb() wmb() +#else +#define smp_mb() barrier() +#define smp_rmb() barrier() +#define smp_wmb() barrier() +#endif + +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#define smp_read_barrier_depends() do { } while (0) + +#endif + +#endif diff --git a/arch/arc/include/asm/bitops.h b/arch/arc/include/asm/bitops.h new file mode 100644 index 000000000000..647a83a8e756 --- /dev/null +++ b/arch/arc/include/asm/bitops.h @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_BITOPS_H +#define _ASM_BITOPS_H + +#ifndef _LINUX_BITOPS_H +#error only can be included directly +#endif + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ + +#include +#include + +/* + * Hardware assisted read-modify-write using ARC700 LLOCK/SCOND insns. + * The Kconfig glue ensures that in SMP, this is only set if the container + * SoC/platform has cross-core coherent LLOCK/SCOND + */ +#if defined(CONFIG_ARC_HAS_LLSC) + +static inline void set_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned int temp; + + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + __asm__ __volatile__( + "1: llock %0, [%1] \n" + " bset %0, %0, %2 \n" + " scond %0, [%1] \n" + " bnz 1b \n" + : "=&r"(temp) + : "r"(m), "ir"(nr) + : "cc"); +} + +static inline void clear_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned int temp; + + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + __asm__ __volatile__( + "1: llock %0, [%1] \n" + " bclr %0, %0, %2 \n" + " scond %0, [%1] \n" + " bnz 1b \n" + : "=&r"(temp) + : "r"(m), "ir"(nr) + : "cc"); +} + +static inline void change_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned int temp; + + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + __asm__ __volatile__( + "1: llock %0, [%1] \n" + " bxor %0, %0, %2 \n" + " scond %0, [%1] \n" + " bnz 1b \n" + : "=&r"(temp) + : "r"(m), "ir"(nr) + : "cc"); +} + +/* + * Semantically: + * Test the bit + * if clear + * set it and return 0 (old value) + * else + * return 1 (old value). + * + * Since ARC lacks a equivalent h/w primitive, the bit is set unconditionally + * and the old value of bit is returned + */ +static inline int test_and_set_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long old, temp; + + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + __asm__ __volatile__( + "1: llock %0, [%2] \n" + " bset %1, %0, %3 \n" + " scond %1, [%2] \n" + " bnz 1b \n" + : "=&r"(old), "=&r"(temp) + : "r"(m), "ir"(nr) + : "cc"); + + return (old & (1 << nr)) != 0; +} + +static inline int +test_and_clear_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned int old, temp; + + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + __asm__ __volatile__( + "1: llock %0, [%2] \n" + " bclr %1, %0, %3 \n" + " scond %1, [%2] \n" + " bnz 1b \n" + : "=&r"(old), "=&r"(temp) + : "r"(m), "ir"(nr) + : "cc"); + + return (old & (1 << nr)) != 0; +} + +static inline int +test_and_change_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned int old, temp; + + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + __asm__ __volatile__( + "1: llock %0, [%2] \n" + " bxor %1, %0, %3 \n" + " scond %1, [%2] \n" + " bnz 1b \n" + : "=&r"(old), "=&r"(temp) + : "r"(m), "ir"(nr) + : "cc"); + + return (old & (1 << nr)) != 0; +} + +#else /* !CONFIG_ARC_HAS_LLSC */ + +#include + +/* + * Non hardware assisted Atomic-R-M-W + * Locking would change to irq-disabling only (UP) and spinlocks (SMP) + * + * There's "significant" micro-optimization in writing our own variants of + * bitops (over generic variants) + * + * (1) The generic APIs have "signed" @nr while we have it "unsigned" + * This avoids extra code to be generated for pointer arithmatic, since + * is "not sure" that index is NOT -ve + * (2) Utilize the fact that ARCompact bit fidding insn (BSET/BCLR/ASL) etc + * only consider bottom 5 bits of @nr, so NO need to mask them off. + * (GCC Quirk: however for constant @nr we still need to do the masking + * at compile time) + */ + +static inline void set_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long temp, flags; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + bitops_lock(flags); + + temp = *m; + *m = temp | (1UL << nr); + + bitops_unlock(flags); +} + +static inline void clear_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long temp, flags; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + bitops_lock(flags); + + temp = *m; + *m = temp & ~(1UL << nr); + + bitops_unlock(flags); +} + +static inline void change_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long temp, flags; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + bitops_lock(flags); + + temp = *m; + *m = temp ^ (1UL << nr); + + bitops_unlock(flags); +} + +static inline int test_and_set_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long old, flags; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + bitops_lock(flags); + + old = *m; + *m = old | (1 << nr); + + bitops_unlock(flags); + + return (old & (1 << nr)) != 0; +} + +static inline int +test_and_clear_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long old, flags; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + bitops_lock(flags); + + old = *m; + *m = old & ~(1 << nr); + + bitops_unlock(flags); + + return (old & (1 << nr)) != 0; +} + +static inline int +test_and_change_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long old, flags; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + bitops_lock(flags); + + old = *m; + *m = old ^ (1 << nr); + + bitops_unlock(flags); + + return (old & (1 << nr)) != 0; +} + +#endif /* CONFIG_ARC_HAS_LLSC */ + +/*************************************** + * Non atomic variants + **************************************/ + +static inline void __set_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long temp; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + temp = *m; + *m = temp | (1UL << nr); +} + +static inline void __clear_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long temp; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + temp = *m; + *m = temp & ~(1UL << nr); +} + +static inline void __change_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long temp; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + temp = *m; + *m = temp ^ (1UL << nr); +} + +static inline int +__test_and_set_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long old; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + old = *m; + *m = old | (1 << nr); + + return (old & (1 << nr)) != 0; +} + +static inline int +__test_and_clear_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long old; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + old = *m; + *m = old & ~(1 << nr); + + return (old & (1 << nr)) != 0; +} + +static inline int +__test_and_change_bit(unsigned long nr, volatile unsigned long *m) +{ + unsigned long old; + m += nr >> 5; + + if (__builtin_constant_p(nr)) + nr &= 0x1f; + + old = *m; + *m = old ^ (1 << nr); + + return (old & (1 << nr)) != 0; +} + +/* + * This routine doesn't need to be atomic. + */ +static inline int +__constant_test_bit(unsigned int nr, const volatile unsigned long *addr) +{ + return ((1UL << (nr & 31)) & + (((const volatile unsigned int *)addr)[nr >> 5])) != 0; +} + +static inline int +__test_bit(unsigned int nr, const volatile unsigned long *addr) +{ + unsigned long mask; + + addr += nr >> 5; + + /* ARC700 only considers 5 bits in bit-fiddling insn */ + mask = 1 << nr; + + return ((mask & *addr) != 0); +} + +#define test_bit(nr, addr) (__builtin_constant_p(nr) ? \ + __constant_test_bit((nr), (addr)) : \ + __test_bit((nr), (addr))) + +/* + * Count the number of zeros, starting from MSB + * Helper for fls( ) friends + * This is a pure count, so (1-32) or (0-31) doesn't apply + * It could be 0 to 32, based on num of 0's in there + * clz(0x8000_0000) = 0, clz(0xFFFF_FFFF)=0, clz(0) = 32, clz(1) = 31 + */ +static inline __attribute__ ((const)) int clz(unsigned int x) +{ + unsigned int res; + + __asm__ __volatile__( + " norm.f %0, %1 \n" + " mov.n %0, 0 \n" + " add.p %0, %0, 1 \n" + : "=r"(res) + : "r"(x) + : "cc"); + + return res; +} + +static inline int constant_fls(int x) +{ + int r = 32; + + if (!x) + return 0; + if (!(x & 0xffff0000u)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xff000000u)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xf0000000u)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xc0000000u)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000u)) { + x <<= 1; + r -= 1; + } + return r; +} + +/* + * fls = Find Last Set in word + * @result: [1-32] + * fls(1) = 1, fls(0x80000000) = 32, fls(0) = 0 + */ +static inline __attribute__ ((const)) int fls(unsigned long x) +{ + if (__builtin_constant_p(x)) + return constant_fls(x); + + return 32 - clz(x); +} + +/* + * __fls: Similar to fls, but zero based (0-31) + */ +static inline __attribute__ ((const)) int __fls(unsigned long x) +{ + if (!x) + return 0; + else + return fls(x) - 1; +} + +/* + * ffs = Find First Set in word (LSB to MSB) + * @result: [1-32], 0 if all 0's + */ +#define ffs(x) ({ unsigned long __t = (x); fls(__t & -__t); }) + +/* + * __ffs: Similar to ffs, but zero based (0-31) + */ +static inline __attribute__ ((const)) int __ffs(unsigned long word) +{ + if (!word) + return word; + + return ffs(word) - 1; +} + +/* + * ffz = Find First Zero in word. + * @return:[0-31], 32 if all 1's + */ +#define ffz(x) __ffs(~(x)) + +/* TODO does this affect uni-processor code */ +#define smp_mb__before_clear_bit() barrier() +#define smp_mb__after_clear_bit() barrier() + +#include +#include +#include +#include + +#include +#include +#include + +#endif /* !__ASSEMBLY__ */ + +#endif /* __KERNEL__ */ + +#endif diff --git a/arch/arc/include/asm/cmpxchg.h b/arch/arc/include/asm/cmpxchg.h new file mode 100644 index 000000000000..03cd6894855d --- /dev/null +++ b/arch/arc/include/asm/cmpxchg.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_CMPXCHG_H +#define __ASM_ARC_CMPXCHG_H + +#include +#include + +#ifdef CONFIG_ARC_HAS_LLSC + +static inline unsigned long +__cmpxchg(volatile void *ptr, unsigned long expected, unsigned long new) +{ + unsigned long prev; + + __asm__ __volatile__( + "1: llock %0, [%1] \n" + " brne %0, %2, 2f \n" + " scond %3, [%1] \n" + " bnz 1b \n" + "2: \n" + : "=&r"(prev) + : "r"(ptr), "ir"(expected), + "r"(new) /* can't be "ir". scond can't take limm for "b" */ + : "cc"); + + return prev; +} + +#else + +static inline unsigned long +__cmpxchg(volatile void *ptr, unsigned long expected, unsigned long new) +{ + unsigned long flags; + int prev; + volatile unsigned long *p = ptr; + + atomic_ops_lock(flags); + prev = *p; + if (prev == expected) + *p = new; + atomic_ops_unlock(flags); + return prev; +} + +#endif /* CONFIG_ARC_HAS_LLSC */ + +#define cmpxchg(ptr, o, n) ((typeof(*(ptr)))__cmpxchg((ptr), \ + (unsigned long)(o), (unsigned long)(n))) + +/* + * Since not supported natively, ARC cmpxchg() uses atomic_ops_lock (UP/SMP) + * just to gaurantee semantics. + * atomic_cmpxchg() needs to use the same locks as it's other atomic siblings + * which also happens to be atomic_ops_lock. + * + * Thus despite semantically being different, implementation of atomic_cmpxchg() + * is same as cmpxchg(). + */ +#define atomic_cmpxchg(v, o, n) ((int)cmpxchg(&((v)->counter), (o), (n))) + + +/* + * xchg (reg with memory) based on "Native atomic" EX insn + */ +static inline unsigned long __xchg(unsigned long val, volatile void *ptr, + int size) +{ + extern unsigned long __xchg_bad_pointer(void); + + switch (size) { + case 4: + __asm__ __volatile__( + " ex %0, [%1] \n" + : "+r"(val) + : "r"(ptr) + : "memory"); + + return val; + } + return __xchg_bad_pointer(); +} + +#define _xchg(ptr, with) ((typeof(*(ptr)))__xchg((unsigned long)(with), (ptr), \ + sizeof(*(ptr)))) + +/* + * On ARC700, EX insn is inherently atomic, so by default "vanilla" xchg() need + * not require any locking. However there's a quirk. + * ARC lacks native CMPXCHG, thus emulated (see above), using external locking - + * incidently it "reuses" the same atomic_ops_lock used by atomic APIs. + * Now, llist code uses cmpxchg() and xchg() on same data, so xchg() needs to + * abide by same serializing rules, thus ends up using atomic_ops_lock as well. + * + * This however is only relevant if SMP and/or ARC lacks LLSC + * if (UP or LLSC) + * xchg doesn't need serialization + * else <==> !(UP or LLSC) <==> (!UP and !LLSC) <==> (SMP and !LLSC) + * xchg needs serialization + */ + +#if !defined(CONFIG_ARC_HAS_LLSC) && defined(CONFIG_SMP) + +#define xchg(ptr, with) \ +({ \ + unsigned long flags; \ + typeof(*(ptr)) old_val; \ + \ + atomic_ops_lock(flags); \ + old_val = _xchg(ptr, with); \ + atomic_ops_unlock(flags); \ + old_val; \ +}) + +#else + +#define xchg(ptr, with) _xchg(ptr, with) + +#endif + +/* + * "atomic" variant of xchg() + * REQ: It needs to follow the same serialization rules as other atomic_xxx() + * Since xchg() doesn't always do that, it would seem that following defintion + * is incorrect. But here's the rationale: + * SMP : Even xchg() takes the atomic_ops_lock, so OK. + * LLSC: atomic_ops_lock are not relevent at all (even if SMP, since LLSC + * is natively "SMP safe", no serialization required). + * UP : other atomics disable IRQ, so no way a difft ctxt atomic_xchg() + * could clobber them. atomic_xchg() itself would be 1 insn, so it + * can't be clobbered by others. Thus no serialization required when + * atomic_xchg is involved. + */ +#define atomic_xchg(v, new) (xchg(&((v)->counter), new)) + +#endif diff --git a/arch/arc/include/asm/smp.h b/arch/arc/include/asm/smp.h new file mode 100644 index 000000000000..4341f3ba7d92 --- /dev/null +++ b/arch/arc/include/asm/smp.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_SMP_H +#define __ASM_ARC_SMP_H + +/* + * ARC700 doesn't support atomic Read-Modify-Write ops. + * Originally Interrupts had to be disabled around code to gaurantee atomicity. + * The LLOCK/SCOND insns allow writing interrupt-hassle-free based atomic ops + * based on retry-if-irq-in-atomic (with hardware assist). + * However despite these, we provide the IRQ disabling variant + * + * (1) These insn were introduced only in 4.10 release. So for older released + * support needed. + */ +#ifndef CONFIG_ARC_HAS_LLSC + +#include + +#define atomic_ops_lock(flags) local_irq_save(flags) +#define atomic_ops_unlock(flags) local_irq_restore(flags) + +#define bitops_lock(flags) local_irq_save(flags) +#define bitops_unlock(flags) local_irq_restore(flags) + +#endif /* !CONFIG_ARC_HAS_LLSC */ + +#endif From 10a6007bda48e3524e24ce1ad46dc7be1add6a0e Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:16 +0530 Subject: [PATCH 2262/4607] asm-generic headers: uaccess.h to conditionally define segment_eq() This is because mm_segment_t is exported by arch code, while seqment_eq assumes it will have .seg element. Acked-by: Arnd Bergmann Signed-off-by: Vineet Gupta --- include/asm-generic/uaccess.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/asm-generic/uaccess.h b/include/asm-generic/uaccess.h index 9788568f7978..5f6ee6138f9a 100644 --- a/include/asm-generic/uaccess.h +++ b/include/asm-generic/uaccess.h @@ -7,7 +7,6 @@ * address space, e.g. all NOMMU machines. */ #include -#include #include #include @@ -32,7 +31,9 @@ static inline void set_fs(mm_segment_t fs) } #endif +#ifndef segment_eq #define segment_eq(a, b) ((a).seg == (b).seg) +#endif #define VERIFY_READ 0 #define VERIFY_WRITE 1 From 43697cb0973da144156e7d11ddd035aee226ee30 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:16 +0530 Subject: [PATCH 2263/4607] ARC: uaccess friends Signed-off-by: Vineet Gupta --- arch/arc/include/asm/segment.h | 24 ++ arch/arc/include/asm/uaccess.h | 646 +++++++++++++++++++++++++++++++++ arch/arc/mm/extable.c | 63 ++++ 3 files changed, 733 insertions(+) create mode 100644 arch/arc/include/asm/segment.h create mode 100644 arch/arc/include/asm/uaccess.h create mode 100644 arch/arc/mm/extable.c diff --git a/arch/arc/include/asm/segment.h b/arch/arc/include/asm/segment.h new file mode 100644 index 000000000000..da2c45979817 --- /dev/null +++ b/arch/arc/include/asm/segment.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 __ASMARC_SEGMENT_H +#define __ASMARC_SEGMENT_H + +#ifndef __ASSEMBLY__ + +typedef unsigned long mm_segment_t; + +#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) + +#define KERNEL_DS MAKE_MM_SEG(0) +#define USER_DS MAKE_MM_SEG(TASK_SIZE) + +#define segment_eq(a, b) ((a) == (b)) + +#endif /* __ASSEMBLY__ */ +#endif /* __ASMARC_SEGMENT_H */ diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h new file mode 100644 index 000000000000..f13bca44f27a --- /dev/null +++ b/arch/arc/include/asm/uaccess.h @@ -0,0 +1,646 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: June 2010 + * -__clear_user( ) called multiple times during elf load was byte loop + * converted to do as much word clear as possible. + * + * vineetg: Dec 2009 + * -Hand crafted constant propagation for "constant" copy sizes + * -stock kernel shrunk by 33K at -O3 + * + * vineetg: Sept 2009 + * -Added option to (UN)inline copy_(to|from)_user to reduce code sz + * -kernel shrunk by 200K even at -O3 (gcc 4.2.1) + * -Enabled when doing -Os + * + * Amit Bhor, Sameer Dhavale: Codito Technologies 2004 + */ + +#ifndef _ASM_ARC_UACCESS_H +#define _ASM_ARC_UACCESS_H + +#include +#include +#include /* for generic string functions */ + + +#define __kernel_ok (segment_eq(get_fs(), KERNEL_DS)) + +/* + * Algorthmically, for __user_ok() we want do: + * (start < TASK_SIZE) && (start+len < TASK_SIZE) + * where TASK_SIZE could either be retrieved from thread_info->addr_limit or + * emitted directly in code. + * + * This can however be rewritten as follows: + * (len <= TASK_SIZE) && (start+len < TASK_SIZE) + * + * Because it essentially checks if buffer end is within limit and @len is + * non-ngeative, which implies that buffer start will be within limit too. + * + * The reason for rewriting being, for majorit yof cases, @len is generally + * compile time constant, causing first sub-expression to be compile time + * subsumed. + * + * The second part would generate weird large LIMMs e.g. (0x6000_0000 - 0x10), + * so we check for TASK_SIZE using get_fs() since the addr_limit load from mem + * would already have been done at this call site for __kernel_ok() + * + */ +#define __user_ok(addr, sz) (((sz) <= TASK_SIZE) && \ + (((addr)+(sz)) <= get_fs())) +#define __access_ok(addr, sz) (unlikely(__kernel_ok) || \ + likely(__user_ok((addr), (sz)))) + +static inline unsigned long +__arc_copy_from_user(void *to, const void __user *from, unsigned long n) +{ + long res = 0; + char val; + unsigned long tmp1, tmp2, tmp3, tmp4; + unsigned long orig_n = n; + + if (n == 0) + return 0; + + /* unaligned */ + if (((unsigned long)to & 0x3) || ((unsigned long)from & 0x3)) { + + unsigned char tmp; + + __asm__ __volatile__ ( + " mov.f lp_count, %0 \n" + " lpnz 2f \n" + "1: ldb.ab %1, [%3, 1] \n" + " stb.ab %1, [%2, 1] \n" + " sub %0,%0,1 \n" + "2: ;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "3: j 2b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 1b, 3b \n" + " .previous \n" + + : "+r" (n), + /* + * Note as an '&' earlyclobber operand to make sure the + * temporary register inside the loop is not the same as + * FROM or TO. + */ + "=&r" (tmp), "+r" (to), "+r" (from) + : + : "lp_count", "lp_start", "lp_end", "memory"); + + return n; + } + + /* + * Hand-crafted constant propagation to reduce code sz of the + * laddered copy 16x,8,4,2,1 + */ + if (__builtin_constant_p(orig_n)) { + res = orig_n; + + if (orig_n / 16) { + orig_n = orig_n % 16; + + __asm__ __volatile__( + " lsr lp_count, %7,4 \n" + " lp 3f \n" + "1: ld.ab %3, [%2, 4] \n" + "11: ld.ab %4, [%2, 4] \n" + "12: ld.ab %5, [%2, 4] \n" + "13: ld.ab %6, [%2, 4] \n" + " st.ab %3, [%1, 4] \n" + " st.ab %4, [%1, 4] \n" + " st.ab %5, [%1, 4] \n" + " st.ab %6, [%1, 4] \n" + " sub %0,%0,16 \n" + "3: ;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 3b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 1b, 4b \n" + " .word 11b,4b \n" + " .word 12b,4b \n" + " .word 13b,4b \n" + " .previous \n" + : "+r" (res), "+r"(to), "+r"(from), + "=r"(tmp1), "=r"(tmp2), "=r"(tmp3), "=r"(tmp4) + : "ir"(n) + : "lp_count", "memory"); + } + if (orig_n / 8) { + orig_n = orig_n % 8; + + __asm__ __volatile__( + "14: ld.ab %3, [%2,4] \n" + "15: ld.ab %4, [%2,4] \n" + " st.ab %3, [%1,4] \n" + " st.ab %4, [%1,4] \n" + " sub %0,%0,8 \n" + "31: ;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 31b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 14b,4b \n" + " .word 15b,4b \n" + " .previous \n" + : "+r" (res), "+r"(to), "+r"(from), + "=r"(tmp1), "=r"(tmp2) + : + : "memory"); + } + if (orig_n / 4) { + orig_n = orig_n % 4; + + __asm__ __volatile__( + "16: ld.ab %3, [%2,4] \n" + " st.ab %3, [%1,4] \n" + " sub %0,%0,4 \n" + "32: ;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 32b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 16b,4b \n" + " .previous \n" + : "+r" (res), "+r"(to), "+r"(from), "=r"(tmp1) + : + : "memory"); + } + if (orig_n / 2) { + orig_n = orig_n % 2; + + __asm__ __volatile__( + "17: ldw.ab %3, [%2,2] \n" + " stw.ab %3, [%1,2] \n" + " sub %0,%0,2 \n" + "33: ;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 33b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 17b,4b \n" + " .previous \n" + : "+r" (res), "+r"(to), "+r"(from), "=r"(tmp1) + : + : "memory"); + } + if (orig_n & 1) { + __asm__ __volatile__( + "18: ldb.ab %3, [%2,2] \n" + " stb.ab %3, [%1,2] \n" + " sub %0,%0,1 \n" + "34: ; nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 34b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 18b,4b \n" + " .previous \n" + : "+r" (res), "+r"(to), "+r"(from), "=r"(tmp1) + : + : "memory"); + } + } else { /* n is NOT constant, so laddered copy of 16x,8,4,2,1 */ + + __asm__ __volatile__( + " mov %0,%3 \n" + " lsr.f lp_count, %3,4 \n" /* 16x bytes */ + " lpnz 3f \n" + "1: ld.ab %5, [%2, 4] \n" + "11: ld.ab %6, [%2, 4] \n" + "12: ld.ab %7, [%2, 4] \n" + "13: ld.ab %8, [%2, 4] \n" + " st.ab %5, [%1, 4] \n" + " st.ab %6, [%1, 4] \n" + " st.ab %7, [%1, 4] \n" + " st.ab %8, [%1, 4] \n" + " sub %0,%0,16 \n" + "3: and.f %3,%3,0xf \n" /* stragglers */ + " bz 34f \n" + " bbit0 %3,3,31f \n" /* 8 bytes left */ + "14: ld.ab %5, [%2,4] \n" + "15: ld.ab %6, [%2,4] \n" + " st.ab %5, [%1,4] \n" + " st.ab %6, [%1,4] \n" + " sub.f %0,%0,8 \n" + "31: bbit0 %3,2,32f \n" /* 4 bytes left */ + "16: ld.ab %5, [%2,4] \n" + " st.ab %5, [%1,4] \n" + " sub.f %0,%0,4 \n" + "32: bbit0 %3,1,33f \n" /* 2 bytes left */ + "17: ldw.ab %5, [%2,2] \n" + " stw.ab %5, [%1,2] \n" + " sub.f %0,%0,2 \n" + "33: bbit0 %3,0,34f \n" + "18: ldb.ab %5, [%2,1] \n" /* 1 byte left */ + " stb.ab %5, [%1,1] \n" + " sub.f %0,%0,1 \n" + "34: ;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 34b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 1b, 4b \n" + " .word 11b,4b \n" + " .word 12b,4b \n" + " .word 13b,4b \n" + " .word 14b,4b \n" + " .word 15b,4b \n" + " .word 16b,4b \n" + " .word 17b,4b \n" + " .word 18b,4b \n" + " .previous \n" + : "=r" (res), "+r"(to), "+r"(from), "+r"(n), "=r"(val), + "=r"(tmp1), "=r"(tmp2), "=r"(tmp3), "=r"(tmp4) + : + : "lp_count", "memory"); + } + + return res; +} + +extern unsigned long slowpath_copy_to_user(void __user *to, const void *from, + unsigned long n); + +static inline unsigned long +__arc_copy_to_user(void __user *to, const void *from, unsigned long n) +{ + long res = 0; + char val; + unsigned long tmp1, tmp2, tmp3, tmp4; + unsigned long orig_n = n; + + if (n == 0) + return 0; + + /* unaligned */ + if (((unsigned long)to & 0x3) || ((unsigned long)from & 0x3)) { + + unsigned char tmp; + + __asm__ __volatile__( + " mov.f lp_count, %0 \n" + " lpnz 3f \n" + " ldb.ab %1, [%3, 1] \n" + "1: stb.ab %1, [%2, 1] \n" + " sub %0, %0, 1 \n" + "3: ;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 3b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 1b, 4b \n" + " .previous \n" + + : "+r" (n), + /* Note as an '&' earlyclobber operand to make sure the + * temporary register inside the loop is not the same as + * FROM or TO. + */ + "=&r" (tmp), "+r" (to), "+r" (from) + : + : "lp_count", "lp_start", "lp_end", "memory"); + + return n; + } + + if (__builtin_constant_p(orig_n)) { + res = orig_n; + + if (orig_n / 16) { + orig_n = orig_n % 16; + + __asm__ __volatile__( + " lsr lp_count, %7,4 \n" + " lp 3f \n" + " ld.ab %3, [%2, 4] \n" + " ld.ab %4, [%2, 4] \n" + " ld.ab %5, [%2, 4] \n" + " ld.ab %6, [%2, 4] \n" + "1: st.ab %3, [%1, 4] \n" + "11: st.ab %4, [%1, 4] \n" + "12: st.ab %5, [%1, 4] \n" + "13: st.ab %6, [%1, 4] \n" + " sub %0, %0, 16 \n" + "3:;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 3b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 1b, 4b \n" + " .word 11b,4b \n" + " .word 12b,4b \n" + " .word 13b,4b \n" + " .previous \n" + : "+r" (res), "+r"(to), "+r"(from), + "=r"(tmp1), "=r"(tmp2), "=r"(tmp3), "=r"(tmp4) + : "ir"(n) + : "lp_count", "memory"); + } + if (orig_n / 8) { + orig_n = orig_n % 8; + + __asm__ __volatile__( + " ld.ab %3, [%2,4] \n" + " ld.ab %4, [%2,4] \n" + "14: st.ab %3, [%1,4] \n" + "15: st.ab %4, [%1,4] \n" + " sub %0, %0, 8 \n" + "31:;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 31b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 14b,4b \n" + " .word 15b,4b \n" + " .previous \n" + : "+r" (res), "+r"(to), "+r"(from), + "=r"(tmp1), "=r"(tmp2) + : + : "memory"); + } + if (orig_n / 4) { + orig_n = orig_n % 4; + + __asm__ __volatile__( + " ld.ab %3, [%2,4] \n" + "16: st.ab %3, [%1,4] \n" + " sub %0, %0, 4 \n" + "32:;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 32b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 16b,4b \n" + " .previous \n" + : "+r" (res), "+r"(to), "+r"(from), "=r"(tmp1) + : + : "memory"); + } + if (orig_n / 2) { + orig_n = orig_n % 2; + + __asm__ __volatile__( + " ldw.ab %3, [%2,2] \n" + "17: stw.ab %3, [%1,2] \n" + " sub %0, %0, 2 \n" + "33:;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 33b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 17b,4b \n" + " .previous \n" + : "+r" (res), "+r"(to), "+r"(from), "=r"(tmp1) + : + : "memory"); + } + if (orig_n & 1) { + __asm__ __volatile__( + " ldb.ab %3, [%2,1] \n" + "18: stb.ab %3, [%1,1] \n" + " sub %0, %0, 1 \n" + "34: ;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 34b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 18b,4b \n" + " .previous \n" + : "+r" (res), "+r"(to), "+r"(from), "=r"(tmp1) + : + : "memory"); + } + } else { /* n is NOT constant, so laddered copy of 16x,8,4,2,1 */ + + __asm__ __volatile__( + " mov %0,%3 \n" + " lsr.f lp_count, %3,4 \n" /* 16x bytes */ + " lpnz 3f \n" + " ld.ab %5, [%2, 4] \n" + " ld.ab %6, [%2, 4] \n" + " ld.ab %7, [%2, 4] \n" + " ld.ab %8, [%2, 4] \n" + "1: st.ab %5, [%1, 4] \n" + "11: st.ab %6, [%1, 4] \n" + "12: st.ab %7, [%1, 4] \n" + "13: st.ab %8, [%1, 4] \n" + " sub %0, %0, 16 \n" + "3: and.f %3,%3,0xf \n" /* stragglers */ + " bz 34f \n" + " bbit0 %3,3,31f \n" /* 8 bytes left */ + " ld.ab %5, [%2,4] \n" + " ld.ab %6, [%2,4] \n" + "14: st.ab %5, [%1,4] \n" + "15: st.ab %6, [%1,4] \n" + " sub.f %0, %0, 8 \n" + "31: bbit0 %3,2,32f \n" /* 4 bytes left */ + " ld.ab %5, [%2,4] \n" + "16: st.ab %5, [%1,4] \n" + " sub.f %0, %0, 4 \n" + "32: bbit0 %3,1,33f \n" /* 2 bytes left */ + " ldw.ab %5, [%2,2] \n" + "17: stw.ab %5, [%1,2] \n" + " sub.f %0, %0, 2 \n" + "33: bbit0 %3,0,34f \n" + " ldb.ab %5, [%2,1] \n" /* 1 byte left */ + "18: stb.ab %5, [%1,1] \n" + " sub.f %0, %0, 1 \n" + "34: ;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: j 34b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 1b, 4b \n" + " .word 11b,4b \n" + " .word 12b,4b \n" + " .word 13b,4b \n" + " .word 14b,4b \n" + " .word 15b,4b \n" + " .word 16b,4b \n" + " .word 17b,4b \n" + " .word 18b,4b \n" + " .previous \n" + : "=r" (res), "+r"(to), "+r"(from), "+r"(n), "=r"(val), + "=r"(tmp1), "=r"(tmp2), "=r"(tmp3), "=r"(tmp4) + : + : "lp_count", "memory"); + } + + return res; +} + +static inline unsigned long __arc_clear_user(void __user *to, unsigned long n) +{ + long res = n; + unsigned char *d_char = to; + + __asm__ __volatile__( + " bbit0 %0, 0, 1f \n" + "75: stb.ab %2, [%0,1] \n" + " sub %1, %1, 1 \n" + "1: bbit0 %0, 1, 2f \n" + "76: stw.ab %2, [%0,2] \n" + " sub %1, %1, 2 \n" + "2: asr.f lp_count, %1, 2 \n" + " lpnz 3f \n" + "77: st.ab %2, [%0,4] \n" + " sub %1, %1, 4 \n" + "3: bbit0 %1, 1, 4f \n" + "78: stw.ab %2, [%0,2] \n" + " sub %1, %1, 2 \n" + "4: bbit0 %1, 0, 5f \n" + "79: stb.ab %2, [%0,1] \n" + " sub %1, %1, 1 \n" + "5: \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "3: j 5b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 75b, 3b \n" + " .word 76b, 3b \n" + " .word 77b, 3b \n" + " .word 78b, 3b \n" + " .word 79b, 3b \n" + " .previous \n" + : "+r"(d_char), "+r"(res) + : "i"(0) + : "lp_count", "lp_start", "lp_end", "memory"); + + return res; +} + +static inline long +__arc_strncpy_from_user(char *dst, const char __user *src, long count) +{ + long res = count; + char val; + unsigned int hw_count; + + if (count == 0) + return 0; + + __asm__ __volatile__( + " lp 2f \n" + "1: ldb.ab %3, [%2, 1] \n" + " breq.d %3, 0, 2f \n" + " stb.ab %3, [%1, 1] \n" + "2: sub %0, %6, %4 \n" + "3: ;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: mov %0, %5 \n" + " j 3b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 1b, 4b \n" + " .previous \n" + : "=r"(res), "+r"(dst), "+r"(src), "=&r"(val), "=l"(hw_count) + : "g"(-EFAULT), "ir"(count), "4"(count) /* this "4" seeds lp_count */ + : "memory"); + + return res; +} + +static inline long __arc_strnlen_user(const char __user *s, long n) +{ + long res, tmp1, cnt; + char val; + + __asm__ __volatile__( + " mov %2, %1 \n" + "1: ldb.ab %3, [%0, 1] \n" + " breq.d %3, 0, 2f \n" + " sub.f %2, %2, 1 \n" + " bnz 1b \n" + " sub %2, %2, 1 \n" + "2: sub %0, %1, %2 \n" + "3: ;nop \n" + " .section .fixup, \"ax\" \n" + " .align 4 \n" + "4: mov %0, 0 \n" + " j 3b \n" + " .previous \n" + " .section __ex_table, \"a\" \n" + " .align 4 \n" + " .word 1b, 4b \n" + " .previous \n" + : "=r"(res), "=r"(tmp1), "=r"(cnt), "=r"(val) + : "0"(s), "1"(n) + : "memory"); + + return res; +} + +#ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE +#define __copy_from_user(t, f, n) __arc_copy_from_user(t, f, n) +#define __copy_to_user(t, f, n) __arc_copy_to_user(t, f, n) +#define __clear_user(d, n) __arc_clear_user(d, n) +#define __strncpy_from_user(d, s, n) __arc_strncpy_from_user(d, s, n) +#define __strnlen_user(s, n) __arc_strnlen_user(s, n) +#else +extern long arc_copy_from_user_noinline(void *to, const void __user * from, + unsigned long n); +extern long arc_copy_to_user_noinline(void __user *to, const void *from, + unsigned long n); +extern unsigned long arc_clear_user_noinline(void __user *to, + unsigned long n); +extern long arc_strncpy_from_user_noinline (char *dst, const char __user *src, + long count); +extern long arc_strnlen_user_noinline(const char __user *src, long n); + +#define __copy_from_user(t, f, n) arc_copy_from_user_noinline(t, f, n) +#define __copy_to_user(t, f, n) arc_copy_to_user_noinline(t, f, n) +#define __clear_user(d, n) arc_clear_user_noinline(d, n) +#define __strncpy_from_user(d, s, n) arc_strncpy_from_user_noinline(d, s, n) +#define __strnlen_user(s, n) arc_strnlen_user_noinline(s, n) + +#endif + +#include + +extern int fixup_exception(struct pt_regs *regs); + +#endif diff --git a/arch/arc/mm/extable.c b/arch/arc/mm/extable.c new file mode 100644 index 000000000000..014172ba8432 --- /dev/null +++ b/arch/arc/mm/extable.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Borrowed heavily from MIPS + */ + +#include +#include + +int fixup_exception(struct pt_regs *regs) +{ + const struct exception_table_entry *fixup; + + fixup = search_exception_tables(instruction_pointer(regs)); + if (fixup) { + regs->ret = fixup->fixup; + + return 1; + } + + return 0; +} + +#ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE + +long arc_copy_from_user_noinline(void *to, const void __user * from, + unsigned long n) +{ + return __arc_copy_from_user(to, from, n); +} +EXPORT_SYMBOL(arc_copy_from_user_noinline); + +long arc_copy_to_user_noinline(void __user *to, const void *from, + unsigned long n) +{ + return __arc_copy_to_user(to, from, n); +} +EXPORT_SYMBOL(arc_copy_to_user_noinline); + +unsigned long arc_clear_user_noinline(void __user *to, + unsigned long n) +{ + return __arc_clear_user(to, n); +} +EXPORT_SYMBOL(arc_clear_user_noinline); + +long arc_strncpy_from_user_noinline (char *dst, const char __user *src, + long count) +{ + return __arc_strncpy_from_user(dst, src, count); +} +EXPORT_SYMBOL(arc_strncpy_from_user_noinline); + +long arc_strnlen_user_noinline(const char __user *src, long n) +{ + return __arc_strnlen_user(src, n); +} +EXPORT_SYMBOL(arc_strnlen_user_noinline); +#endif From 05d88a493746819821733e07bed918a6e09f735b Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:16 +0530 Subject: [PATCH 2264/4607] asm-generic: uaccess: Allow arches to over-ride __{get,put}_user_fn() As of now these default to calling the arch provided __copy_{to,from}_user() routines which being general purpose (w.r.t buffer alignment and lengths) would lead to alignment checks in generated code (for arches which don't support unaligned load/stores). Given that in this case we already know that data involved is "unit" sized and aligned, using the vanilla copy backend is a bit wasteful. This change thus allows arches to over-ride the aforementioned routines. Signed-off-by: Vineet Gupta Acked-by: Arnd Bergmann --- include/asm-generic/uaccess.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/asm-generic/uaccess.h b/include/asm-generic/uaccess.h index 5f6ee6138f9a..c184aa8ec8cd 100644 --- a/include/asm-generic/uaccess.h +++ b/include/asm-generic/uaccess.h @@ -169,12 +169,18 @@ static inline __must_check long __copy_to_user(void __user *to, -EFAULT; \ }) +#ifndef __put_user_fn + static inline int __put_user_fn(size_t size, void __user *ptr, void *x) { size = __copy_to_user(ptr, x, size); return size ? -EFAULT : size; } +#define __put_user_fn(sz, u, k) __put_user_fn(sz, u, k) + +#endif + extern int __put_user_bad(void) __attribute__((noreturn)); #define __get_user(x, ptr) \ @@ -225,12 +231,17 @@ extern int __put_user_bad(void) __attribute__((noreturn)); -EFAULT; \ }) +#ifndef __get_user_fn static inline int __get_user_fn(size_t size, const void __user *ptr, void *x) { size = __copy_from_user(x, ptr, size); return size ? -EFAULT : size; } +#define __get_user_fn(sz, u, k) __get_user_fn(sz, u, k) + +#endif + extern int __get_user_bad(void) __attribute__((noreturn)); #ifndef __copy_from_user_inatomic From 0a5eae458e923af9968679fd75fd1f7670200bc3 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:16 +0530 Subject: [PATCH 2265/4607] ARC: [optim] uaccess __{get,put}_user() optimised Override asm-generic implementations. We basically gain on 2 fronts * checks for alignment no longer needed as we are only doing "unit" sized copies. (Careful observer could argue that While the kernel buffers are aligned, the user buffer in theory might not be - however in that case the user space is already broken when it tries to deref a hword/word straddling word boundary - so we are not making it any worse). * __copy_{to,from}_user( ) returns bytes that couldn't be copied, whereas get_user() returns 0 for success or -EFAULT (not size). Thus the code to do leftover bytes calculation can be avoided as well. The savings were significant: ~17k of code. bloat-o-meter vmlinux_uaccess_pre vmlinux_uaccess_post add/remove: 0/4 grow/shrink: 8/118 up/down: 1262/-18758 (-17496) ^^^^^^^^^ Signed-off-by: Vineet Gupta Acked-by: Arnd Bergmann --- arch/arc/include/asm/uaccess.h | 105 +++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h index f13bca44f27a..32420824375b 100644 --- a/arch/arc/include/asm/uaccess.h +++ b/arch/arc/include/asm/uaccess.h @@ -57,6 +57,111 @@ #define __access_ok(addr, sz) (unlikely(__kernel_ok) || \ likely(__user_ok((addr), (sz)))) +/*********** Single byte/hword/word copies ******************/ + +#define __get_user_fn(sz, u, k) \ +({ \ + long __ret = 0; /* success by default */ \ + switch (sz) { \ + case 1: __arc_get_user_one(*(k), u, "ldb", __ret); break; \ + case 2: __arc_get_user_one(*(k), u, "ldw", __ret); break; \ + case 4: __arc_get_user_one(*(k), u, "ld", __ret); break; \ + case 8: __arc_get_user_one_64(*(k), u, __ret); break; \ + } \ + __ret; \ +}) + +/* + * Returns 0 on success, -EFAULT if not. + * @ret already contains 0 - given that errors will be less likely + * (hence +r asm constraint below). + * In case of error, fixup code will make it -EFAULT + */ +#define __arc_get_user_one(dst, src, op, ret) \ + __asm__ __volatile__( \ + "1: "op" %1,[%2]\n" \ + "2: ;nop\n" \ + " .section .fixup, \"ax\"\n" \ + " .align 4\n" \ + "3: mov %0, %3\n" \ + " j 2b\n" \ + " .previous\n" \ + " .section __ex_table, \"a\"\n" \ + " .align 4\n" \ + " .word 1b,3b\n" \ + " .previous\n" \ + \ + : "+r" (ret), "=r" (dst) \ + : "r" (src), "ir" (-EFAULT)) + +#define __arc_get_user_one_64(dst, src, ret) \ + __asm__ __volatile__( \ + "1: ld %1,[%2]\n" \ + "4: ld %R1,[%2, 4]\n" \ + "2: ;nop\n" \ + " .section .fixup, \"ax\"\n" \ + " .align 4\n" \ + "3: mov %0, %3\n" \ + " j 2b\n" \ + " .previous\n" \ + " .section __ex_table, \"a\"\n" \ + " .align 4\n" \ + " .word 1b,3b\n" \ + " .word 4b,3b\n" \ + " .previous\n" \ + \ + : "+r" (ret), "=r" (dst) \ + : "r" (src), "ir" (-EFAULT)) + +#define __put_user_fn(sz, u, k) \ +({ \ + long __ret = 0; /* success by default */ \ + switch (sz) { \ + case 1: __arc_put_user_one(*(k), u, "stb", __ret); break; \ + case 2: __arc_put_user_one(*(k), u, "stw", __ret); break; \ + case 4: __arc_put_user_one(*(k), u, "st", __ret); break; \ + case 8: __arc_put_user_one_64(*(k), u, __ret); break; \ + } \ + __ret; \ +}) + +#define __arc_put_user_one(src, dst, op, ret) \ + __asm__ __volatile__( \ + "1: "op" %1,[%2]\n" \ + "2: ;nop\n" \ + " .section .fixup, \"ax\"\n" \ + " .align 4\n" \ + "3: mov %0, %3\n" \ + " j 2b\n" \ + " .previous\n" \ + " .section __ex_table, \"a\"\n" \ + " .align 4\n" \ + " .word 1b,3b\n" \ + " .previous\n" \ + \ + : "+r" (ret) \ + : "r" (src), "r" (dst), "ir" (-EFAULT)) + +#define __arc_put_user_one_64(src, dst, ret) \ + __asm__ __volatile__( \ + "1: st %1,[%2]\n" \ + "4: st %R1,[%2, 4]\n" \ + "2: ;nop\n" \ + " .section .fixup, \"ax\"\n" \ + " .align 4\n" \ + "3: mov %0, %3\n" \ + " j 2b\n" \ + " .previous\n" \ + " .section __ex_table, \"a\"\n" \ + " .align 4\n" \ + " .word 1b,3b\n" \ + " .word 4b,3b\n" \ + " .previous\n" \ + \ + : "+r" (ret) \ + : "r" (src), "r" (dst), "ir" (-EFAULT)) + + static inline unsigned long __arc_copy_from_user(void *to, const void __user *from, unsigned long n) { From 64e69073c35439fa19c2ad2a4a18834e0314f071 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:16 +0530 Subject: [PATCH 2266/4607] asm-generic headers: Allow yet more arch overrides in checksum.h arches can have more efficient implementation of these routines Acked-by: Arnd Bergmann Signed-off-by: Vineet Gupta --- include/asm-generic/checksum.h | 4 ++++ lib/checksum.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/include/asm-generic/checksum.h b/include/asm-generic/checksum.h index c084767c88bc..59811df58c5b 100644 --- a/include/asm-generic/checksum.h +++ b/include/asm-generic/checksum.h @@ -38,12 +38,15 @@ extern __wsum csum_partial_copy_from_user(const void __user *src, void *dst, csum_partial_copy((src), (dst), (len), (sum)) #endif +#ifndef ip_fast_csum /* * This is a version of ip_compute_csum() optimized for IP headers, * which always checksum on 4 octet boundaries. */ extern __sum16 ip_fast_csum(const void *iph, unsigned int ihl); +#endif +#ifndef csum_fold /* * Fold a partial checksum */ @@ -54,6 +57,7 @@ static inline __sum16 csum_fold(__wsum csum) sum = (sum & 0xffff) + (sum >> 16); return (__force __sum16)~sum; } +#endif #ifndef csum_tcpudp_nofold /* diff --git a/lib/checksum.c b/lib/checksum.c index 12dceb27ff20..129775eb6de6 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -102,6 +102,7 @@ out: } #endif +#ifndef ip_fast_csum /* * This is a version of ip_compute_csum() optimized for IP headers, * which always checksum on 4 octet boundaries. @@ -111,6 +112,7 @@ __sum16 ip_fast_csum(const void *iph, unsigned int ihl) return (__force __sum16)~do_csum(iph, ihl*4); } EXPORT_SYMBOL(ip_fast_csum); +#endif /* * computes the checksum of a memory block at buff, length len, From ca15c8ecd588dda4377d18d6d27bc1e87b4177cb Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:17 +0530 Subject: [PATCH 2267/4607] ARC: Checksum/byteorder/swab routines TBD: do_csum still needs to be written in asm Signed-off-by: Vineet Gupta Acked-by: Arnd Bergmann --- arch/arc/include/asm/byteorder.h | 18 ++++++ arch/arc/include/asm/checksum.h | 101 +++++++++++++++++++++++++++++++ arch/arc/include/asm/swab.h | 98 ++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 arch/arc/include/asm/byteorder.h create mode 100644 arch/arc/include/asm/checksum.h create mode 100644 arch/arc/include/asm/swab.h diff --git a/arch/arc/include/asm/byteorder.h b/arch/arc/include/asm/byteorder.h new file mode 100644 index 000000000000..9da71d415c38 --- /dev/null +++ b/arch/arc/include/asm/byteorder.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_BYTEORDER_H +#define __ASM_ARC_BYTEORDER_H + +#ifdef CONFIG_CPU_BIG_ENDIAN +#include +#else +#include +#endif + +#endif /* ASM_ARC_BYTEORDER_H */ diff --git a/arch/arc/include/asm/checksum.h b/arch/arc/include/asm/checksum.h new file mode 100644 index 000000000000..10957298b7a3 --- /dev/null +++ b/arch/arc/include/asm/checksum.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Joern Rennecke : Jan 2012 + * -Insn Scheduling improvements to csum core routines. + * = csum_fold( ) largely derived from ARM version. + * = ip_fast_cum( ) to have module scheduling + * -gcc 4.4.x broke networking. Alias analysis needed to be primed. + * worked around by adding memory clobber to ip_fast_csum( ) + * + * vineetg: May 2010 + * -Rewrote ip_fast_cscum( ) and csum_fold( ) with fast inline asm + */ + +#ifndef _ASM_ARC_CHECKSUM_H +#define _ASM_ARC_CHECKSUM_H + +/* + * Fold a partial checksum + * + * The 2 swords comprising the 32bit sum are added, any carry to 16th bit + * added back and final sword result inverted. + */ +static inline __sum16 csum_fold(__wsum s) +{ + unsigned r = s << 16 | s >> 16; /* ror */ + s = ~s; + s -= r; + return s >> 16; +} + +/* + * This is a version of ip_compute_csum() optimized for IP headers, + * which always checksum on 4 octet boundaries. + */ +static inline __sum16 +ip_fast_csum(const void *iph, unsigned int ihl) +{ + const void *ptr = iph; + unsigned int tmp, tmp2, sum; + + __asm__( + " ld.ab %0, [%3, 4] \n" + " ld.ab %2, [%3, 4] \n" + " sub %1, %4, 2 \n" + " lsr.f lp_count, %1, 1 \n" + " bcc 0f \n" + " add.f %0, %0, %2 \n" + " ld.ab %2, [%3, 4] \n" + "0: lp 1f \n" + " ld.ab %1, [%3, 4] \n" + " adc.f %0, %0, %2 \n" + " ld.ab %2, [%3, 4] \n" + " adc.f %0, %0, %1 \n" + "1: adc.f %0, %0, %2 \n" + " add.cs %0,%0,1 \n" + : "=&r"(sum), "=r"(tmp), "=&r"(tmp2), "+&r" (ptr) + : "r"(ihl) + : "cc", "lp_count", "memory"); + + return csum_fold(sum); +} + +/* + * TCP pseudo Header is 12 bytes: + * SA [4], DA [4], zeroes [1], Proto[1], TCP Seg(hdr+data) Len [2] + */ +static inline __wsum +csum_tcpudp_nofold(__be32 saddr, __be32 daddr, unsigned short len, + unsigned short proto, __wsum sum) +{ + __asm__ __volatile__( + " add.f %0, %0, %1 \n" + " adc.f %0, %0, %2 \n" + " adc.f %0, %0, %3 \n" + " adc.f %0, %0, %4 \n" + " adc %0, %0, 0 \n" + : "+&r"(sum) + : "r"(saddr), "r"(daddr), +#ifdef CONFIG_CPU_BIG_ENDIAN + "r"(len), +#else + "r"(len << 8), +#endif + "r"(htons(proto)) + : "cc"); + + return sum; +} + +#define csum_fold csum_fold +#define ip_fast_csum ip_fast_csum +#define csum_tcpudp_nofold csum_tcpudp_nofold + +#include + +#endif /* _ASM_ARC_CHECKSUM_H */ diff --git a/arch/arc/include/asm/swab.h b/arch/arc/include/asm/swab.h new file mode 100644 index 000000000000..095599a73195 --- /dev/null +++ b/arch/arc/include/asm/swab.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: May 2011 + * -Support single cycle endian-swap insn in ARC700 4.10 + * + * vineetg: June 2009 + * -Better htonl implementation (5 instead of 9 ALU instructions) + * -Hardware assisted single cycle bswap (Use Case of ARC custom instrn) + */ + +#ifndef __ASM_ARC_SWAB_H +#define __ASM_ARC_SWAB_H + +#include + +/* Native single cycle endian swap insn */ +#ifdef CONFIG_ARC_HAS_SWAPE + +#define __arch_swab32(x) \ +({ \ + unsigned int tmp = x; \ + __asm__( \ + " swape %0, %1 \n" \ + : "=r" (tmp) \ + : "r" (tmp)); \ + tmp; \ +}) + +#else + +/* Several ways of Endian-Swap Emulation for ARC + * 0: kernel generic + * 1: ARC optimised "C" + * 2: ARC Custom instruction + */ +#define ARC_BSWAP_TYPE 1 + +#if (ARC_BSWAP_TYPE == 1) /******* Software only ********/ + +/* The kernel default implementation of htonl is + * return x<<24 | x>>24 | + * (x & (__u32)0x0000ff00UL)<<8 | (x & (__u32)0x00ff0000UL)>>8; + * + * This generates 9 instructions on ARC (excluding the ld/st) + * + * 8051fd8c: ld r3,[r7,20] ; Mem op : Get the value to be swapped + * 8051fd98: asl r5,r3,24 ; get 3rd Byte + * 8051fd9c: lsr r2,r3,24 ; get 0th Byte + * 8051fda0: and r4,r3,0xff00 + * 8051fda8: asl r4,r4,8 ; get 1st Byte + * 8051fdac: and r3,r3,0x00ff0000 + * 8051fdb4: or r2,r2,r5 ; combine 0th and 3rd Bytes + * 8051fdb8: lsr r3,r3,8 ; 2nd Byte at correct place in Dst Reg + * 8051fdbc: or r2,r2,r4 ; combine 0,3 Bytes with 1st Byte + * 8051fdc0: or r2,r2,r3 ; combine 0,3,1 Bytes with 2nd Byte + * 8051fdc4: st r2,[r1,20] ; Mem op : save result back to mem + * + * Joern suggested a better "C" algorithm which is great since + * (1) It is portable to any architecure + * (2) At the same time it takes advantage of ARC ISA (rotate intrns) + */ + +#define __arch_swab32(x) \ +({ unsigned long __in = (x), __tmp; \ + __tmp = __in << 8 | __in >> 24; /* ror tmp,in,24 */ \ + __in = __in << 24 | __in >> 8; /* ror in,in,8 */ \ + __tmp ^= __in; \ + __tmp &= 0xff00ff; \ + __tmp ^ __in; \ +}) + +#elif (ARC_BSWAP_TYPE == 2) /* Custom single cycle bwap instruction */ + +#define __arch_swab32(x) \ +({ \ + unsigned int tmp = x; \ + __asm__( \ + " .extInstruction bswap, 7, 0x00, SUFFIX_NONE, SYNTAX_2OP \n"\ + " bswap %0, %1 \n"\ + : "=r" (tmp) \ + : "r" (tmp)); \ + tmp; \ +}) + +#endif /* ARC_BSWAP_TYPE=zzz */ + +#endif /* CONFIG_ARC_HAS_SWAPE */ + +#if !defined(__STRICT_ANSI__) || defined(__KERNEL__) +#define __SWAB_64_THRU_32__ +#endif + +#endif From 3be80aaef861a60b85a9323462ebb5f623774f7a Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:17 +0530 Subject: [PATCH 2268/4607] ARC: Fundamental ARCH data-types/defines * L1_CACHE_SHIFT * PAGE_SIZE, PAGE_OFFSET * struct pt_regs, struct user_regs_struct * struct thread_struct, cpu_relax(), task_pt_regs(), start_thread(), ... * struct thread_info, THREAD_SIZE, INIT_THREAD_INFO(), TIF_*, ... * BUG() * ELF_* * Elf_* To disallow user-space visibility into some of the core kernel data-types such as struct pt_regs, #ifdef __KERNEL__ which also makes the UAPI header spit (further patch in the series) to NOT export it to asm/uapi/ptrace.h Signed-off-by: Vineet Gupta Cc: Jonas Bonn Cc: Al Viro Acked-by: Arnd Bergmann --- arch/arc/include/asm/bug.h | 37 ++++++++ arch/arc/include/asm/cache.h | 21 +++++ arch/arc/include/asm/elf.h | 97 +++++++++++++++++++ arch/arc/include/asm/exec.h | 15 +++ arch/arc/include/asm/kdebug.h | 19 ++++ arch/arc/include/asm/linkage.h | 30 ++++++ arch/arc/include/asm/module.h | 17 ++++ arch/arc/include/asm/page.h | 42 +++++++++ arch/arc/include/asm/processor.h | 143 +++++++++++++++++++++++++++++ arch/arc/include/asm/ptrace.h | 126 +++++++++++++++++++++++++ arch/arc/include/asm/sections.h | 17 ++++ arch/arc/include/asm/thread_info.h | 121 ++++++++++++++++++++++++ 12 files changed, 685 insertions(+) create mode 100644 arch/arc/include/asm/bug.h create mode 100644 arch/arc/include/asm/cache.h create mode 100644 arch/arc/include/asm/elf.h create mode 100644 arch/arc/include/asm/exec.h create mode 100644 arch/arc/include/asm/kdebug.h create mode 100644 arch/arc/include/asm/linkage.h create mode 100644 arch/arc/include/asm/module.h create mode 100644 arch/arc/include/asm/page.h create mode 100644 arch/arc/include/asm/processor.h create mode 100644 arch/arc/include/asm/ptrace.h create mode 100644 arch/arc/include/asm/sections.h create mode 100644 arch/arc/include/asm/thread_info.h diff --git a/arch/arc/include/asm/bug.h b/arch/arc/include/asm/bug.h new file mode 100644 index 000000000000..2ad8f9b1c54b --- /dev/null +++ b/arch/arc/include/asm/bug.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_BUG_H +#define _ASM_ARC_BUG_H + +#ifndef __ASSEMBLY__ + +#include + +struct task_struct; + +void show_regs(struct pt_regs *regs); +void show_stacktrace(struct task_struct *tsk, struct pt_regs *regs); +void show_kernel_fault_diag(const char *str, struct pt_regs *regs, + unsigned long address, unsigned long cause_reg); +void die(const char *str, struct pt_regs *regs, unsigned long address, + unsigned long cause_reg); + +#define BUG() do { \ + dump_stack(); \ + pr_warn("Kernel BUG in %s: %s: %d!\n", \ + __FILE__, __func__, __LINE__); \ +} while (0) + +#define HAVE_ARCH_BUG + +#include + +#endif /* !__ASSEMBLY__ */ + +#endif diff --git a/arch/arc/include/asm/cache.h b/arch/arc/include/asm/cache.h new file mode 100644 index 000000000000..30c72a4d2d9f --- /dev/null +++ b/arch/arc/include/asm/cache.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 __ARC_ASM_CACHE_H +#define __ARC_ASM_CACHE_H + +/* In case $$ not config, setup a dummy number for rest of kernel */ +#ifndef CONFIG_ARC_CACHE_LINE_SHIFT +#define L1_CACHE_SHIFT 6 +#else +#define L1_CACHE_SHIFT CONFIG_ARC_CACHE_LINE_SHIFT +#endif + +#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) + +#endif /* _ASM_CACHE_H */ diff --git a/arch/arc/include/asm/elf.h b/arch/arc/include/asm/elf.h new file mode 100644 index 000000000000..147284ff1f96 --- /dev/null +++ b/arch/arc/include/asm/elf.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_ELF_H +#define __ASM_ARC_ELF_H + +#include + +#define EM_ARCOMPACT 93 + +/* Machine specific ELF Hdr flags */ +#define EF_ARC_OSABI_MSK 0x00000f00 +#define EF_ARC_OSABI_ORIG 0x00000000 /* MUST be zero for back-compat */ +#define EF_ARC_OSABI_V2 0x00000200 + +/* ARC Relocations (kernel Modules only) */ +#define R_ARC_32 0x4 +#define R_ARC_32_ME 0x1B +#define R_ARC_S25H_PCREL 0x10 +#define R_ARC_S25W_PCREL 0x11 + +typedef unsigned long elf_greg_t; +typedef unsigned long elf_fpregset_t; + + +/* core dump regs is in the order pt_regs, callee_regs, stop_pc (for gdb) */ +#define ELF_NGREG ((sizeof(struct pt_regs) + sizeof(struct callee_regs) \ + + sizeof(unsigned long)) / sizeof(elf_greg_t)) + +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +/* + * To ensure that + * -we don't load something for the wrong architecture. + * -The userspace is using the correct syscall ABI + */ +struct elf32_hdr; +extern int elf_check_arch(const struct elf32_hdr *); +#define elf_check_arch elf_check_arch + +/* + * These are used to set parameters in the core dumps. + */ +#define ELF_ARCH EM_ARCOMPACT +#define ELF_CLASS ELFCLASS32 + +#ifdef CONFIG_CPU_BIG_ENDIAN +#define ELF_DATA ELFDATA2MSB +#else +#define ELF_DATA ELFDATA2LSB +#endif + +#ifdef __KERNEL__ + +#define CORE_DUMP_USE_REGSET + +#define ELF_EXEC_PAGESIZE PAGE_SIZE + +/* + * This is the location that an ET_DYN program is loaded if exec'ed. Typical + * use of this is to invoke "./ld.so someprog" to test out a new version of + * the loader. We need to make sure that it is out of the way of the program + * that it will "exec", and that there is sufficient room for the brk. + */ +#define ELF_ET_DYN_BASE (2 * TASK_SIZE / 3) + +/* + * When the program starts, a1 contains a pointer to a function to be + * registered with atexit, as per the SVR4 ABI. A value of 0 means we + * have no such handler. + */ +#define ELF_PLAT_INIT(_r, load_addr) ((_r)->r0 = 0) + +/* + * This yields a mask that user programs can use to figure out what + * instruction set this cpu supports. + */ +#define ELF_HWCAP (0) + +/* + * This yields a string that ld.so will use to load implementation + * specific libraries for optimization. This is more specific in + * intent than poking at uname or /proc/cpuinfo. + */ +#define ELF_PLATFORM (NULL) + +#define SET_PERSONALITY(ex) \ + set_personality(PER_LINUX | (current->personality & (~PER_MASK))) + +#endif /* __KERNEL__ */ + +#endif diff --git a/arch/arc/include/asm/exec.h b/arch/arc/include/asm/exec.h new file mode 100644 index 000000000000..28abc6905e07 --- /dev/null +++ b/arch/arc/include/asm/exec.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_EXEC_H +#define __ASM_ARC_EXEC_H + +/* Align to 16b */ +#define arch_align_stack(p) ((unsigned long)(p) & ~0xf) + +#endif diff --git a/arch/arc/include/asm/kdebug.h b/arch/arc/include/asm/kdebug.h new file mode 100644 index 000000000000..3fbe6c472c0a --- /dev/null +++ b/arch/arc/include/asm/kdebug.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_KDEBUG_H +#define _ASM_ARC_KDEBUG_H + +enum die_val { + DIE_UNUSED, + DIE_TRAP, + DIE_IERR, + DIE_OOPS +}; + +#endif diff --git a/arch/arc/include/asm/linkage.h b/arch/arc/include/asm/linkage.h new file mode 100644 index 000000000000..a45d1bb50e41 --- /dev/null +++ b/arch/arc/include/asm/linkage.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_LINKAGE_H +#define __ASM_LINKAGE_H + +#ifdef __ASSEMBLY__ + +/* Can't use the ENTRY macro in linux/linkage.h + * gas considers ';' as comment vs. newline + */ +.macro ARC_ENTRY name + .global \name + .align 4 + \name: +.endm + +.macro ARC_EXIT name +#define ASM_PREV_SYM_ADDR(name) .-##name + .size \ name, ASM_PREV_SYM_ADDR(\name) +.endm + +#endif /* __ASSEMBLY__ */ + +#endif diff --git a/arch/arc/include/asm/module.h b/arch/arc/include/asm/module.h new file mode 100644 index 000000000000..165d768d00cf --- /dev/null +++ b/arch/arc/include/asm/module.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Amit Bhor, Sameer Dhavale: Codito Technologies 2004 + + */ + +#ifndef _ASM_ARC_MODULE_H +#define _ASM_ARC_MODULE_H + +#include + +#endif /* _ASM_ARC_MODULE_H */ diff --git a/arch/arc/include/asm/page.h b/arch/arc/include/asm/page.h new file mode 100644 index 000000000000..7cd1224d8e7d --- /dev/null +++ b/arch/arc/include/asm/page.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_PAGE_H +#define __ASM_ARC_PAGE_H + +/* PAGE_SHIFT determines the page size */ +#if defined(CONFIG_ARC_PAGE_SIZE_16K) +#define PAGE_SHIFT 14 +#elif defined(CONFIG_ARC_PAGE_SIZE_4K) +#define PAGE_SHIFT 12 +#else +/* + * Default 8k + * done this way (instead of under CONFIG_ARC_PAGE_SIZE_8K) because adhoc + * user code (busybox appletlib.h) expects PAGE_SHIFT to be defined w/o + * using the correct uClibc header and in their build our autoconf.h is + * not available + */ +#define PAGE_SHIFT 13 +#endif + +#ifdef __ASSEMBLY__ +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_OFFSET (0x80000000) +#else +#define PAGE_SIZE (1UL << PAGE_SHIFT) /* Default 8K */ +#define PAGE_OFFSET (0x80000000UL) /* Kernel starts at 2G onwards */ +#endif + +#define PAGE_MASK (~(PAGE_SIZE-1)) + +#endif /* !__ASSEMBLY__ */ + +#endif /* __KERNEL__ */ + +#endif diff --git a/arch/arc/include/asm/processor.h b/arch/arc/include/asm/processor.h new file mode 100644 index 000000000000..bf88cfbc9128 --- /dev/null +++ b/arch/arc/include/asm/processor.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: March 2009 + * -Implemented task_pt_regs( ) + * + * Amit Bhor, Sameer Dhavale, Ashwin Chaugule: Codito Technologies 2004 + */ + +#ifndef __ASM_ARC_PROCESSOR_H +#define __ASM_ARC_PROCESSOR_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ + +#include /* for STATUS_E1_MASK et all */ + +/* Arch specific stuff which needs to be saved per task. + * However these items are not so important so as to earn a place in + * struct thread_info + */ +struct thread_struct { + unsigned long ksp; /* kernel mode stack pointer */ + unsigned long callee_reg; /* pointer to callee regs */ + unsigned long fault_address; /* dbls as brkpt holder as well */ + unsigned long cause_code; /* Exception Cause Code (ECR) */ +}; + +#define INIT_THREAD { \ + .ksp = sizeof(init_stack) + (unsigned long) init_stack, \ +} + +/* Forward declaration, a strange C thing */ +struct task_struct; + +/* + * Return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *t); + +#define task_pt_regs(p) \ + ((struct pt_regs *)(THREAD_SIZE - 4 + (void *)task_stack_page(p)) - 1) + +/* Free all resources held by a thread. */ +#define release_thread(thread) do { } while (0) + +/* Prepare to copy thread state - unlazy all lazy status */ +#define prepare_to_copy(tsk) do { } while (0) + +#define cpu_relax() do { } while (0) + +/* + * Create a new kernel thread + */ + +extern int kernel_thread(int (*fn) (void *), void *arg, unsigned long flags); + +#define copy_segments(tsk, mm) do { } while (0) +#define release_segments(mm) do { } while (0) + +#define KSTK_EIP(tsk) (task_pt_regs(tsk)->ret) + +/* + * Where abouts of Task's sp, fp, blink when it was last seen in kernel mode. + * These can't be derived from pt_regs as that would give correp user-mode val + */ +#define KSTK_ESP(tsk) (tsk->thread.ksp) +#define KSTK_BLINK(tsk) (*((unsigned int *)((KSTK_ESP(tsk)) + (13+1+1)*4))) +#define KSTK_FP(tsk) (*((unsigned int *)((KSTK_ESP(tsk)) + (13+1)*4))) + +/* + * Do necessary setup to start up a newly executed thread. + * + * E1,E2 so that Interrupts are enabled in user mode + * L set, so Loop inhibited to begin with + * lp_start and lp_end seeded with bogus non-zero values so to easily catch + * the ARC700 sr to lp_start hardware bug + */ +#define start_thread(_regs, _pc, _usp) \ +do { \ + set_fs(USER_DS); /* reads from user space */ \ + (_regs)->ret = (_pc); \ + /* Interrupts enabled in User Mode */ \ + (_regs)->status32 = STATUS_U_MASK | STATUS_L_MASK \ + | STATUS_E1_MASK | STATUS_E2_MASK; \ + (_regs)->sp = (_usp); \ + /* bogus seed values for debugging */ \ + (_regs)->lp_start = 0x10; \ + (_regs)->lp_end = 0x80; \ +} while (0) + +extern unsigned int get_wchan(struct task_struct *p); + +/* + * Default implementation of macro that returns current + * instruction pointer ("program counter"). + * Should the PC register be read instead ? This macro does not seem to + * be used in many places so this wont be all that bad. + */ +#define current_text_addr() ({ __label__ _l; _l: &&_l; }) + +#endif /* !__ASSEMBLY__ */ + +/* Kernels Virtual memory area. + * Unlike other architectures(MIPS, sh, cris ) ARC 700 does not have a + * "kernel translated" region (like KSEG2 in MIPS). So we use a upper part + * of the translated bottom 2GB for kernel virtual memory and protect + * these pages from user accesses by disabling Ru, Eu and Wu. + */ +#define VMALLOC_SIZE (0x10000000) /* 256M */ +#define VMALLOC_START (PAGE_OFFSET - VMALLOC_SIZE) +#define VMALLOC_END (PAGE_OFFSET) + +/* Most of the architectures seem to be keeping some kind of padding between + * userspace TASK_SIZE and PAGE_OFFSET. i.e TASK_SIZE != PAGE_OFFSET. + */ +#define USER_KERNEL_GUTTER 0x10000000 + +/* User address space: + * On ARC700, CPU allows the entire lower half of 32 bit address space to be + * translated. Thus potentially 2G (0:0x7FFF_FFFF) could be User vaddr space. + * However we steal 256M for kernel addr (0x7000_0000:0x7FFF_FFFF) and another + * 256M (0x6000_0000:0x6FFF_FFFF) is gutter between user/kernel spaces + * Thus total User vaddr space is (0:0x5FFF_FFFF) + */ +#define TASK_SIZE (PAGE_OFFSET - VMALLOC_SIZE - USER_KERNEL_GUTTER) + +#define STACK_TOP TASK_SIZE +#define STACK_TOP_MAX STACK_TOP + +/* This decides where the kernel will search for a free chunk of vm + * space during mmap's. + */ +#define TASK_UNMAPPED_BASE (TASK_SIZE / 3) + +#endif /* __KERNEL__ */ + +#endif /* __ASM_ARC_PROCESSOR_H */ diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h new file mode 100644 index 000000000000..446e55eee7be --- /dev/null +++ b/arch/arc/include/asm/ptrace.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Amit Bhor, Sameer Dhavale: Codito Technologies 2004 + */ + +#ifndef __ASM_ARC_PTRACE_H +#define __ASM_ARC_PTRACE_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ + +/* THE pt_regs: Defines how regs are saved during entry into kernel */ + +struct pt_regs { + /* + * 1 word gutter after reg-file has been saved + * Technically not needed, Since SP always points to a "full" location + * (vs. "empty"). But pt_regs is shared with tools.... + */ + long res; + + /* Real registers */ + long bta; /* bta_l1, bta_l2, erbta */ + long lp_start; + long lp_end; + long lp_count; + long status32; /* status32_l1, status32_l2, erstatus */ + long ret; /* ilink1, ilink2 or eret */ + long blink; + long fp; + long r26; /* gp */ + long r12; + long r11; + long r10; + long r9; + long r8; + long r7; + long r6; + long r5; + long r4; + long r3; + long r2; + long r1; + long r0; + long sp; /* user/kernel sp depending on where we came from */ + long orig_r0; + long orig_r8; /*to distinguish bet excp, sys call, int1 or int2 */ +}; + +/* Callee saved registers - need to be saved only when you are scheduled out */ + +struct callee_regs { + long res; /* Again this is not needed */ + long r25; + long r24; + long r23; + long r22; + long r21; + long r20; + long r19; + long r18; + long r17; + long r16; + long r15; + long r14; + long r13; +}; + +#define instruction_pointer(regs) ((regs)->ret) +#define profile_pc(regs) instruction_pointer(regs) + +/* return 1 if user mode or 0 if kernel mode */ +#define user_mode(regs) (regs->status32 & STATUS_U_MASK) + +#define user_stack_pointer(regs)\ +({ unsigned int sp; \ + if (user_mode(regs)) \ + sp = (regs)->sp;\ + else \ + sp = -1; \ + sp; \ +}) +#endif /* !__ASSEMBLY__ */ + +#endif /* __KERNEL__ */ + +#ifndef __ASSEMBLY__ +/* + * Userspace ABI: Register state needed by + * -ptrace (gdbserver) + * -sigcontext (SA_SIGNINFO signal frame) + * + * This is to decouple pt_regs from user-space ABI, to be able to change it + * w/o affecting the ABI. + * Although the layout (initial padding) is similar to pt_regs to have some + * optimizations when copying pt_regs to/from user_regs_struct. + * + * Also, sigcontext only care about the scratch regs as that is what we really + * save/restore for signal handling. +*/ +struct user_regs_struct { + + struct scratch { + long pad; + long bta, lp_start, lp_end, lp_count; + long status32, ret, blink, fp, gp; + long r12, r11, r10, r9, r8, r7, r6, r5, r4, r3, r2, r1, r0; + long sp; + } scratch; + struct callee { + long pad; + long r25, r24, r23, r22, r21, r20; + long r19, r18, r17, r16, r15, r14, r13; + } callee; + long efa; /* break pt addr, for break points in delay slots */ + long stop_pc; /* give dbg stop_pc directly after checking orig_r8 */ +}; +#endif /* !__ASSEMBLY__ */ + +#endif /* __ASM_PTRACE_H */ diff --git a/arch/arc/include/asm/sections.h b/arch/arc/include/asm/sections.h new file mode 100644 index 000000000000..fc15f2e1a12e --- /dev/null +++ b/arch/arc/include/asm/sections.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_SECTIONS_H +#define _ASM_ARC_SECTIONS_H + +#include + +extern char _int_vec_base_lds[]; +extern char __arc_dccm_base[]; + +#endif diff --git a/arch/arc/include/asm/thread_info.h b/arch/arc/include/asm/thread_info.h new file mode 100644 index 000000000000..2d50a4cdd7f3 --- /dev/null +++ b/arch/arc/include/asm/thread_info.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Vineetg: Oct 2009 + * No need for ARC specific thread_info allocator (kmalloc/free). This is + * anyways one page allocation, thus slab alloc can be short-circuited and + * the generic version (get_free_page) would be loads better. + * + * Sameer Dhavale: Codito Technologies 2004 + */ + +#ifndef _ASM_THREAD_INFO_H +#define _ASM_THREAD_INFO_H + +#ifdef __KERNEL__ + +#include + +#ifdef CONFIG_16KSTACKS +#define THREAD_SIZE_ORDER 1 +#else +#define THREAD_SIZE_ORDER 0 +#endif + +#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER) + +#ifndef __ASSEMBLY__ + +#include +#include + +/* + * low level task data that entry.S needs immediate access to + * - this struct should fit entirely inside of one cache line + * - this struct shares the supervisor stack pages + * - if the contents of this structure are changed, the assembly constants + * must also be changed + */ +struct thread_info { + unsigned long flags; /* low level flags */ + int preempt_count; /* 0 => preemptable, <0 => BUG */ + struct task_struct *task; /* main task structure */ + mm_segment_t addr_limit; /* thread address space */ + struct exec_domain *exec_domain;/* execution domain */ + __u32 cpu; /* current CPU */ + unsigned long thr_ptr; /* TLS ptr */ + struct restart_block restart_block; +}; + +/* + * macros/functions for gaining access to the thread information structure + * + * preempt_count needs to be 1 initially, until the scheduler is functional. + */ +#define INIT_THREAD_INFO(tsk) \ +{ \ + .task = &tsk, \ + .exec_domain = &default_exec_domain, \ + .flags = 0, \ + .cpu = 0, \ + .preempt_count = INIT_PREEMPT_COUNT, \ + .addr_limit = KERNEL_DS, \ + .restart_block = { \ + .fn = do_no_restart_syscall, \ + }, \ +} + +#define init_thread_info (init_thread_union.thread_info) +#define init_stack (init_thread_union.stack) + +static inline __attribute_const__ struct thread_info *current_thread_info(void) +{ + register unsigned long sp asm("sp"); + return (struct thread_info *)(sp & ~(THREAD_SIZE - 1)); +} + +#endif /* !__ASSEMBLY__ */ + +#define PREEMPT_ACTIVE 0x10000000 + +/* + * thread information flags + * - these are process state flags that various assembly files may need to + * access + * - pending work-to-be-done flags are in LSW + * - other flags in MSW + */ +#define TIF_RESTORE_SIGMASK 0 /* restore sig mask in do_signal() */ +#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ +#define TIF_SIGPENDING 2 /* signal pending */ +#define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +#define TIF_SYSCALL_AUDIT 4 /* syscall auditing active */ +#define TIF_SYSCALL_TRACE 15 /* syscall trace active */ + +/* true if poll_idle() is polling TIF_NEED_RESCHED */ +#define TIF_MEMDIE 16 + +#define _TIF_SYSCALL_TRACE (1< Date: Fri, 18 Jan 2013 15:12:18 +0530 Subject: [PATCH 2269/4607] ARC: Spinlock/rwlock/mutex primitives Signed-off-by: Vineet Gupta Acked-by: Arnd Bergmann --- arch/arc/include/asm/mutex.h | 9 ++ arch/arc/include/asm/spinlock.h | 144 ++++++++++++++++++++++++++ arch/arc/include/asm/spinlock_types.h | 35 +++++++ 3 files changed, 188 insertions(+) create mode 100644 arch/arc/include/asm/mutex.h create mode 100644 arch/arc/include/asm/spinlock.h create mode 100644 arch/arc/include/asm/spinlock_types.h diff --git a/arch/arc/include/asm/mutex.h b/arch/arc/include/asm/mutex.h new file mode 100644 index 000000000000..3be5e64da139 --- /dev/null +++ b/arch/arc/include/asm/mutex.h @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 diff --git a/arch/arc/include/asm/spinlock.h b/arch/arc/include/asm/spinlock.h new file mode 100644 index 000000000000..f158197ac5b0 --- /dev/null +++ b/arch/arc/include/asm/spinlock.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_SPINLOCK_H +#define __ASM_SPINLOCK_H + +#include +#include +#include + +#define arch_spin_is_locked(x) ((x)->slock != __ARCH_SPIN_LOCK_UNLOCKED__) +#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock) +#define arch_spin_unlock_wait(x) \ + do { while (arch_spin_is_locked(x)) cpu_relax(); } while (0) + +static inline void arch_spin_lock(arch_spinlock_t *lock) +{ + unsigned int tmp = __ARCH_SPIN_LOCK_LOCKED__; + + __asm__ __volatile__( + "1: ex %0, [%1] \n" + " breq %0, %2, 1b \n" + : "+&r" (tmp) + : "r"(&(lock->slock)), "ir"(__ARCH_SPIN_LOCK_LOCKED__) + : "memory"); +} + +static inline int arch_spin_trylock(arch_spinlock_t *lock) +{ + unsigned int tmp = __ARCH_SPIN_LOCK_LOCKED__; + + __asm__ __volatile__( + "1: ex %0, [%1] \n" + : "+r" (tmp) + : "r"(&(lock->slock)) + : "memory"); + + return (tmp == __ARCH_SPIN_LOCK_UNLOCKED__); +} + +static inline void arch_spin_unlock(arch_spinlock_t *lock) +{ + lock->slock = __ARCH_SPIN_LOCK_UNLOCKED__; + smp_mb(); +} + +/* + * Read-write spinlocks, allowing multiple readers but only one writer. + * + * The spinlock itself is contained in @counter and access to it is + * serialized with @lock_mutex. + * + * Unfair locking as Writers could be starved indefinitely by Reader(s) + */ + +/* Would read_trylock() succeed? */ +#define arch_read_can_lock(x) ((x)->counter > 0) + +/* Would write_trylock() succeed? */ +#define arch_write_can_lock(x) ((x)->counter == __ARCH_RW_LOCK_UNLOCKED__) + +/* 1 - lock taken successfully */ +static inline int arch_read_trylock(arch_rwlock_t *rw) +{ + int ret = 0; + + arch_spin_lock(&(rw->lock_mutex)); + + /* + * zero means writer holds the lock exclusively, deny Reader. + * Otherwise grant lock to first/subseq reader + */ + if (rw->counter > 0) { + rw->counter--; + ret = 1; + } + + arch_spin_unlock(&(rw->lock_mutex)); + + smp_mb(); + return ret; +} + +/* 1 - lock taken successfully */ +static inline int arch_write_trylock(arch_rwlock_t *rw) +{ + int ret = 0; + + arch_spin_lock(&(rw->lock_mutex)); + + /* + * If reader(s) hold lock (lock < __ARCH_RW_LOCK_UNLOCKED__), + * deny writer. Otherwise if unlocked grant to writer + * Hence the claim that Linux rwlocks are unfair to writers. + * (can be starved for an indefinite time by readers). + */ + if (rw->counter == __ARCH_RW_LOCK_UNLOCKED__) { + rw->counter = 0; + ret = 1; + } + arch_spin_unlock(&(rw->lock_mutex)); + + return ret; +} + +static inline void arch_read_lock(arch_rwlock_t *rw) +{ + while (!arch_read_trylock(rw)) + cpu_relax(); +} + +static inline void arch_write_lock(arch_rwlock_t *rw) +{ + while (!arch_write_trylock(rw)) + cpu_relax(); +} + +static inline void arch_read_unlock(arch_rwlock_t *rw) +{ + arch_spin_lock(&(rw->lock_mutex)); + rw->counter++; + arch_spin_unlock(&(rw->lock_mutex)); +} + +static inline void arch_write_unlock(arch_rwlock_t *rw) +{ + arch_spin_lock(&(rw->lock_mutex)); + rw->counter = __ARCH_RW_LOCK_UNLOCKED__; + arch_spin_unlock(&(rw->lock_mutex)); +} + +#define arch_read_lock_flags(lock, flags) arch_read_lock(lock) +#define arch_write_lock_flags(lock, flags) arch_write_lock(lock) + +#define arch_spin_relax(lock) cpu_relax() +#define arch_read_relax(lock) cpu_relax() +#define arch_write_relax(lock) cpu_relax() + +#endif /* __ASM_SPINLOCK_H */ diff --git a/arch/arc/include/asm/spinlock_types.h b/arch/arc/include/asm/spinlock_types.h new file mode 100644 index 000000000000..8276bfd61704 --- /dev/null +++ b/arch/arc/include/asm/spinlock_types.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_SPINLOCK_TYPES_H +#define __ASM_SPINLOCK_TYPES_H + +typedef struct { + volatile unsigned int slock; +} arch_spinlock_t; + +#define __ARCH_SPIN_LOCK_UNLOCKED__ 0 +#define __ARCH_SPIN_LOCK_LOCKED__ 1 + +#define __ARCH_SPIN_LOCK_UNLOCKED { __ARCH_SPIN_LOCK_UNLOCKED__ } +#define __ARCH_SPIN_LOCK_LOCKED { __ARCH_SPIN_LOCK_LOCKED__ } + +/* + * Unlocked: 0x01_00_00_00 + * Read lock(s): 0x00_FF_00_00 to say 0x01 + * Write lock: 0x0, but only possible if prior value "unlocked" 0x0100_0000 + */ +typedef struct { + volatile unsigned int counter; + arch_spinlock_t lock_mutex; +} arch_rwlock_t; + +#define __ARCH_RW_LOCK_UNLOCKED__ 0x01000000 +#define __ARCH_RW_LOCK_UNLOCKED { .counter = __ARCH_RW_LOCK_UNLOCKED__ } + +#endif From 5210d1e6889c8183ecad269e86e2d9c524015b5f Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:18 +0530 Subject: [PATCH 2270/4607] ARC: String library Hand optimised asm code for ARC700 pipeline. Originally written/optimized by Joern Rennecke Signed-off-by: Vineet Gupta Cc: Joern Rennecke --- arch/arc/include/asm/string.h | 40 +++++++++++ arch/arc/lib/memcmp.S | 124 ++++++++++++++++++++++++++++++++++ arch/arc/lib/memcpy-700.S | 66 ++++++++++++++++++ arch/arc/lib/memset.S | 59 ++++++++++++++++ arch/arc/lib/strchr-700.S | 123 +++++++++++++++++++++++++++++++++ arch/arc/lib/strcmp.S | 96 ++++++++++++++++++++++++++ arch/arc/lib/strcpy-700.S | 70 +++++++++++++++++++ arch/arc/lib/strlen.S | 83 +++++++++++++++++++++++ 8 files changed, 661 insertions(+) create mode 100644 arch/arc/include/asm/string.h create mode 100644 arch/arc/lib/memcmp.S create mode 100644 arch/arc/lib/memcpy-700.S create mode 100644 arch/arc/lib/memset.S create mode 100644 arch/arc/lib/strchr-700.S create mode 100644 arch/arc/lib/strcmp.S create mode 100644 arch/arc/lib/strcpy-700.S create mode 100644 arch/arc/lib/strlen.S diff --git a/arch/arc/include/asm/string.h b/arch/arc/include/asm/string.h new file mode 100644 index 000000000000..87676c8f1412 --- /dev/null +++ b/arch/arc/include/asm/string.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: May 2011 + * -We had half-optimised memset/memcpy, got better versions of those + * -Added memcmp, strchr, strcpy, strcmp, strlen + * + * Amit Bhor: Codito Technologies 2004 + */ + +#ifndef _ASM_ARC_STRING_H +#define _ASM_ARC_STRING_H + +#include + +#ifdef __KERNEL__ + +#define __HAVE_ARCH_MEMSET +#define __HAVE_ARCH_MEMCPY +#define __HAVE_ARCH_MEMCMP +#define __HAVE_ARCH_STRCHR +#define __HAVE_ARCH_STRCPY +#define __HAVE_ARCH_STRCMP +#define __HAVE_ARCH_STRLEN + +extern void *memset(void *ptr, int, __kernel_size_t); +extern void *memcpy(void *, const void *, __kernel_size_t); +extern void memzero(void *ptr, __kernel_size_t n); +extern int memcmp(const void *, const void *, __kernel_size_t); +extern char *strchr(const char *s, int c); +extern char *strcpy(char *dest, const char *src); +extern int strcmp(const char *cs, const char *ct); +extern __kernel_size_t strlen(const char *); + +#endif /* __KERNEL__ */ +#endif /* _ASM_ARC_STRING_H */ diff --git a/arch/arc/lib/memcmp.S b/arch/arc/lib/memcmp.S new file mode 100644 index 000000000000..bc813d55b6c3 --- /dev/null +++ b/arch/arc/lib/memcmp.S @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 + +#ifdef __LITTLE_ENDIAN__ +#define WORD2 r2 +#define SHIFT r3 +#else /* BIG ENDIAN */ +#define WORD2 r3 +#define SHIFT r2 +#endif + +ARC_ENTRY memcmp + or r12,r0,r1 + asl_s r12,r12,30 + sub r3,r2,1 + brls r2,r12,.Lbytewise + ld r4,[r0,0] + ld r5,[r1,0] + lsr.f lp_count,r3,3 + lpne .Loop_end + ld_s WORD2,[r0,4] + ld_s r12,[r1,4] + brne r4,r5,.Leven + ld.a r4,[r0,8] + ld.a r5,[r1,8] + brne WORD2,r12,.Lodd +.Loop_end: + asl_s SHIFT,SHIFT,3 + bhs_s .Last_cmp + brne r4,r5,.Leven + ld r4,[r0,4] + ld r5,[r1,4] +#ifdef __LITTLE_ENDIAN__ + nop_s + ; one more load latency cycle +.Last_cmp: + xor r0,r4,r5 + bset r0,r0,SHIFT + sub_s r1,r0,1 + bic_s r1,r1,r0 + norm r1,r1 + b.d .Leven_cmp + and r1,r1,24 +.Leven: + xor r0,r4,r5 + sub_s r1,r0,1 + bic_s r1,r1,r0 + norm r1,r1 + ; slow track insn + and r1,r1,24 +.Leven_cmp: + asl r2,r4,r1 + asl r12,r5,r1 + lsr_s r2,r2,1 + lsr_s r12,r12,1 + j_s.d [blink] + sub r0,r2,r12 + .balign 4 +.Lodd: + xor r0,WORD2,r12 + sub_s r1,r0,1 + bic_s r1,r1,r0 + norm r1,r1 + ; slow track insn + and r1,r1,24 + asl_s r2,r2,r1 + asl_s r12,r12,r1 + lsr_s r2,r2,1 + lsr_s r12,r12,1 + j_s.d [blink] + sub r0,r2,r12 +#else /* BIG ENDIAN */ +.Last_cmp: + neg_s SHIFT,SHIFT + lsr r4,r4,SHIFT + lsr r5,r5,SHIFT + ; slow track insn +.Leven: + sub.f r0,r4,r5 + mov.ne r0,1 + j_s.d [blink] + bset.cs r0,r0,31 +.Lodd: + cmp_s WORD2,r12 + + mov_s r0,1 + j_s.d [blink] + bset.cs r0,r0,31 +#endif /* ENDIAN */ + .balign 4 +.Lbytewise: + breq r2,0,.Lnil + ldb r4,[r0,0] + ldb r5,[r1,0] + lsr.f lp_count,r3 + lpne .Lbyte_end + ldb_s r3,[r0,1] + ldb r12,[r1,1] + brne r4,r5,.Lbyte_even + ldb.a r4,[r0,2] + ldb.a r5,[r1,2] + brne r3,r12,.Lbyte_odd +.Lbyte_end: + bcc .Lbyte_even + brne r4,r5,.Lbyte_even + ldb_s r3,[r0,1] + ldb_s r12,[r1,1] +.Lbyte_odd: + j_s.d [blink] + sub r0,r3,r12 +.Lbyte_even: + j_s.d [blink] + sub r0,r4,r5 +.Lnil: + j_s.d [blink] + mov r0,0 +ARC_EXIT memcmp diff --git a/arch/arc/lib/memcpy-700.S b/arch/arc/lib/memcpy-700.S new file mode 100644 index 000000000000..b64cc10ac918 --- /dev/null +++ b/arch/arc/lib/memcpy-700.S @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 + +ARC_ENTRY memcpy + or r3,r0,r1 + asl_s r3,r3,30 + mov_s r5,r0 + brls.d r2,r3,.Lcopy_bytewise + sub.f r3,r2,1 + ld_s r12,[r1,0] + asr.f lp_count,r3,3 + bbit0.d r3,2,.Lnox4 + bmsk_s r2,r2,1 + st.ab r12,[r5,4] + ld.a r12,[r1,4] +.Lnox4: + lppnz .Lendloop + ld_s r3,[r1,4] + st.ab r12,[r5,4] + ld.a r12,[r1,8] + st.ab r3,[r5,4] +.Lendloop: + breq r2,0,.Last_store + ld r3,[r5,0] +#ifdef __LITTLE_ENDIAN__ + add3 r2,-1,r2 + ; uses long immediate + xor_s r12,r12,r3 + bmsk r12,r12,r2 + xor_s r12,r12,r3 +#else /* BIG ENDIAN */ + sub3 r2,31,r2 + ; uses long immediate + xor_s r3,r3,r12 + bmsk r3,r3,r2 + xor_s r12,r12,r3 +#endif /* ENDIAN */ +.Last_store: + j_s.d [blink] + st r12,[r5,0] + + .balign 4 +.Lcopy_bytewise: + jcs [blink] + ldb_s r12,[r1,0] + lsr.f lp_count,r3 + bhs_s .Lnox1 + stb.ab r12,[r5,1] + ldb.a r12,[r1,1] +.Lnox1: + lppnz .Lendbloop + ldb_s r3,[r1,1] + stb.ab r12,[r5,1] + ldb.a r12,[r1,2] + stb.ab r3,[r5,1] +.Lendbloop: + j_s.d [blink] + stb r12,[r5,0] +ARC_EXIT memcpy diff --git a/arch/arc/lib/memset.S b/arch/arc/lib/memset.S new file mode 100644 index 000000000000..9b2d88d2e141 --- /dev/null +++ b/arch/arc/lib/memset.S @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 + +#define SMALL 7 /* Must be at least 6 to deal with alignment/loop issues. */ + +ARC_ENTRY memset + mov_s r4,r0 + or r12,r0,r2 + bmsk.f r12,r12,1 + extb_s r1,r1 + asl r3,r1,8 + beq.d .Laligned + or_s r1,r1,r3 + brls r2,SMALL,.Ltiny + add r3,r2,r0 + stb r1,[r3,-1] + bclr_s r3,r3,0 + stw r1,[r3,-2] + bmsk.f r12,r0,1 + add_s r2,r2,r12 + sub.ne r2,r2,4 + stb.ab r1,[r4,1] + and r4,r4,-2 + stw.ab r1,[r4,2] + and r4,r4,-4 +.Laligned: ; This code address should be aligned for speed. + asl r3,r1,16 + lsr.f lp_count,r2,2 + or_s r1,r1,r3 + lpne .Loop_end + st.ab r1,[r4,4] +.Loop_end: + j_s [blink] + + .balign 4 +.Ltiny: + mov.f lp_count,r2 + lpne .Ltiny_end + stb.ab r1,[r4,1] +.Ltiny_end: + j_s [blink] +ARC_EXIT memset + +; memzero: @r0 = mem, @r1 = size_t +; memset: @r0 = mem, @r1 = char, @r2 = size_t + +ARC_ENTRY memzero + ; adjust bzero args to memset args + mov r2, r1 + mov r1, 0 + b memset ;tail call so need to tinker with blink +ARC_EXIT memzero diff --git a/arch/arc/lib/strchr-700.S b/arch/arc/lib/strchr-700.S new file mode 100644 index 000000000000..99c10475d477 --- /dev/null +++ b/arch/arc/lib/strchr-700.S @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + */ + +/* ARC700 has a relatively long pipeline and branch prediction, so we want + to avoid branches that are hard to predict. On the other hand, the + presence of the norm instruction makes it easier to operate on whole + words branch-free. */ + +#include + +ARC_ENTRY strchr + extb_s r1,r1 + asl r5,r1,8 + bmsk r2,r0,1 + or r5,r5,r1 + mov_s r3,0x01010101 + breq.d r2,r0,.Laligned + asl r4,r5,16 + sub_s r0,r0,r2 + asl r7,r2,3 + ld_s r2,[r0] +#ifdef __LITTLE_ENDIAN__ + asl r7,r3,r7 +#else + lsr r7,r3,r7 +#endif + or r5,r5,r4 + ror r4,r3 + sub r12,r2,r7 + bic_s r12,r12,r2 + and r12,r12,r4 + brne.d r12,0,.Lfound0_ua + xor r6,r2,r5 + ld.a r2,[r0,4] + sub r12,r6,r7 + bic r12,r12,r6 + and r7,r12,r4 + breq r7,0,.Loop ; For speed, we want this branch to be unaligned. + b .Lfound_char ; Likewise this one. +; /* We require this code address to be unaligned for speed... */ +.Laligned: + ld_s r2,[r0] + or r5,r5,r4 + ror r4,r3 +; /* ... so that this code address is aligned, for itself and ... */ +.Loop: + sub r12,r2,r3 + bic_s r12,r12,r2 + and r12,r12,r4 + brne.d r12,0,.Lfound0 + xor r6,r2,r5 + ld.a r2,[r0,4] + sub r12,r6,r3 + bic r12,r12,r6 + and r7,r12,r4 + breq r7,0,.Loop /* ... so that this branch is unaligned. */ + ; Found searched-for character. r0 has already advanced to next word. +#ifdef __LITTLE_ENDIAN__ +/* We only need the information about the first matching byte + (i.e. the least significant matching byte) to be exact, + hence there is no problem with carry effects. */ +.Lfound_char: + sub r3,r7,1 + bic r3,r3,r7 + norm r2,r3 + sub_s r0,r0,1 + asr_s r2,r2,3 + j.d [blink] + sub_s r0,r0,r2 + + .balign 4 +.Lfound0_ua: + mov r3,r7 +.Lfound0: + sub r3,r6,r3 + bic r3,r3,r6 + and r2,r3,r4 + or_s r12,r12,r2 + sub_s r3,r12,1 + bic_s r3,r3,r12 + norm r3,r3 + add_s r0,r0,3 + asr_s r12,r3,3 + asl.f 0,r2,r3 + sub_s r0,r0,r12 + j_s.d [blink] + mov.pl r0,0 +#else /* BIG ENDIAN */ +.Lfound_char: + lsr r7,r7,7 + + bic r2,r7,r6 + norm r2,r2 + sub_s r0,r0,4 + asr_s r2,r2,3 + j.d [blink] + add_s r0,r0,r2 + +.Lfound0_ua: + mov_s r3,r7 +.Lfound0: + asl_s r2,r2,7 + or r7,r6,r4 + bic_s r12,r12,r2 + sub r2,r7,r3 + or r2,r2,r6 + bic r12,r2,r12 + bic.f r3,r4,r12 + norm r3,r3 + + add.pl r3,r3,1 + asr_s r12,r3,3 + asl.f 0,r2,r3 + add_s r0,r0,r12 + j_s.d [blink] + mov.mi r0,0 +#endif /* ENDIAN */ +ARC_EXIT strchr diff --git a/arch/arc/lib/strcmp.S b/arch/arc/lib/strcmp.S new file mode 100644 index 000000000000..5dc802b45cf3 --- /dev/null +++ b/arch/arc/lib/strcmp.S @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + */ + +/* This is optimized primarily for the ARC700. + It would be possible to speed up the loops by one cycle / word + respective one cycle / byte by forcing double source 1 alignment, unrolling + by a factor of two, and speculatively loading the second word / byte of + source 1; however, that would increase the overhead for loop setup / finish, + and strcmp might often terminate early. */ + +#include + +ARC_ENTRY strcmp + or r2,r0,r1 + bmsk_s r2,r2,1 + brne r2,0,.Lcharloop + mov_s r12,0x01010101 + ror r5,r12 +.Lwordloop: + ld.ab r2,[r0,4] + ld.ab r3,[r1,4] + nop_s + sub r4,r2,r12 + bic r4,r4,r2 + and r4,r4,r5 + brne r4,0,.Lfound0 + breq r2,r3,.Lwordloop +#ifdef __LITTLE_ENDIAN__ + xor r0,r2,r3 ; mask for difference + sub_s r1,r0,1 + bic_s r0,r0,r1 ; mask for least significant difference bit + sub r1,r5,r0 + xor r0,r5,r1 ; mask for least significant difference byte + and_s r2,r2,r0 + and_s r3,r3,r0 +#endif /* LITTLE ENDIAN */ + cmp_s r2,r3 + mov_s r0,1 + j_s.d [blink] + bset.lo r0,r0,31 + + .balign 4 +#ifdef __LITTLE_ENDIAN__ +.Lfound0: + xor r0,r2,r3 ; mask for difference + or r0,r0,r4 ; or in zero indicator + sub_s r1,r0,1 + bic_s r0,r0,r1 ; mask for least significant difference bit + sub r1,r5,r0 + xor r0,r5,r1 ; mask for least significant difference byte + and_s r2,r2,r0 + and_s r3,r3,r0 + sub.f r0,r2,r3 + mov.hi r0,1 + j_s.d [blink] + bset.lo r0,r0,31 +#else /* BIG ENDIAN */ + /* The zero-detection above can mis-detect 0x01 bytes as zeroes + because of carry-propagateion from a lower significant zero byte. + We can compensate for this by checking that bit0 is zero. + This compensation is not necessary in the step where we + get a low estimate for r2, because in any affected bytes + we already have 0x00 or 0x01, which will remain unchanged + when bit 7 is cleared. */ + .balign 4 +.Lfound0: + lsr r0,r4,8 + lsr_s r1,r2 + bic_s r2,r2,r0 ; get low estimate for r2 and get ... + bic_s r0,r0,r1 ; + or_s r3,r3,r0 ; ... high estimate r3 so that r2 > r3 will ... + cmp_s r3,r2 ; ... be independent of trailing garbage + or_s r2,r2,r0 ; likewise for r3 > r2 + bic_s r3,r3,r0 + rlc r0,0 ; r0 := r2 > r3 ? 1 : 0 + cmp_s r2,r3 + j_s.d [blink] + bset.lo r0,r0,31 +#endif /* ENDIAN */ + + .balign 4 +.Lcharloop: + ldb.ab r2,[r0,1] + ldb.ab r3,[r1,1] + nop_s + breq r2,0,.Lcmpend + breq r2,r3,.Lcharloop +.Lcmpend: + j_s.d [blink] + sub r0,r2,r3 +ARC_EXIT strcmp diff --git a/arch/arc/lib/strcpy-700.S b/arch/arc/lib/strcpy-700.S new file mode 100644 index 000000000000..b7ca4ae81d88 --- /dev/null +++ b/arch/arc/lib/strcpy-700.S @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + */ + +/* If dst and src are 4 byte aligned, copy 8 bytes at a time. + If the src is 4, but not 8 byte aligned, we first read 4 bytes to get + it 8 byte aligned. Thus, we can do a little read-ahead, without + dereferencing a cache line that we should not touch. + Note that short and long instructions have been scheduled to avoid + branch stalls. + The beq_s to r3z could be made unaligned & long to avoid a stall + there, but the it is not likely to be taken often, and it + would also be likey to cost an unaligned mispredict at the next call. */ + +#include + +ARC_ENTRY strcpy + or r2,r0,r1 + bmsk_s r2,r2,1 + brne.d r2,0,charloop + mov_s r10,r0 + ld_s r3,[r1,0] + mov r8,0x01010101 + bbit0.d r1,2,loop_start + ror r12,r8 + sub r2,r3,r8 + bic_s r2,r2,r3 + tst_s r2,r12 + bne r3z + mov_s r4,r3 + .balign 4 +loop: + ld.a r3,[r1,4] + st.ab r4,[r10,4] +loop_start: + ld.a r4,[r1,4] + sub r2,r3,r8 + bic_s r2,r2,r3 + tst_s r2,r12 + bne_s r3z + st.ab r3,[r10,4] + sub r2,r4,r8 + bic r2,r2,r4 + tst r2,r12 + beq loop + mov_s r3,r4 +#ifdef __LITTLE_ENDIAN__ +r3z: bmsk.f r1,r3,7 + lsr_s r3,r3,8 +#else +r3z: lsr.f r1,r3,24 + asl_s r3,r3,8 +#endif + bne.d r3z + stb.ab r1,[r10,1] + j_s [blink] + + .balign 4 +charloop: + ldb.ab r3,[r1,1] + + + brne.d r3,0,charloop + stb.ab r3,[r10,1] + j [blink] +ARC_EXIT strcpy diff --git a/arch/arc/lib/strlen.S b/arch/arc/lib/strlen.S new file mode 100644 index 000000000000..39759e099696 --- /dev/null +++ b/arch/arc/lib/strlen.S @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 + +ARC_ENTRY strlen + or r3,r0,7 + ld r2,[r3,-7] + ld.a r6,[r3,-3] + mov r4,0x01010101 + ; uses long immediate +#ifdef __LITTLE_ENDIAN__ + asl_s r1,r0,3 + btst_s r0,2 + asl r7,r4,r1 + ror r5,r4 + sub r1,r2,r7 + bic_s r1,r1,r2 + mov.eq r7,r4 + sub r12,r6,r7 + bic r12,r12,r6 + or.eq r12,r12,r1 + and r12,r12,r5 + brne r12,0,.Learly_end +#else /* BIG ENDIAN */ + ror r5,r4 + btst_s r0,2 + mov_s r1,31 + sub3 r7,r1,r0 + sub r1,r2,r4 + bic_s r1,r1,r2 + bmsk r1,r1,r7 + sub r12,r6,r4 + bic r12,r12,r6 + bmsk.ne r12,r12,r7 + or.eq r12,r12,r1 + and r12,r12,r5 + brne r12,0,.Learly_end +#endif /* ENDIAN */ + +.Loop: + ld_s r2,[r3,4] + ld.a r6,[r3,8] + ; stall for load result + sub r1,r2,r4 + bic_s r1,r1,r2 + sub r12,r6,r4 + bic r12,r12,r6 + or r12,r12,r1 + and r12,r12,r5 + breq r12,0,.Loop +.Lend: + and.f r1,r1,r5 + sub.ne r3,r3,4 + mov.eq r1,r12 +#ifdef __LITTLE_ENDIAN__ + sub_s r2,r1,1 + bic_s r2,r2,r1 + norm r1,r2 + sub_s r0,r0,3 + lsr_s r1,r1,3 + sub r0,r3,r0 + j_s.d [blink] + sub r0,r0,r1 +#else /* BIG ENDIAN */ + lsr_s r1,r1,7 + mov.eq r2,r6 + bic_s r1,r1,r2 + norm r1,r1 + sub r0,r3,r0 + lsr_s r1,r1,3 + j_s.d [blink] + add r0,r0,r1 +#endif /* ENDIAN */ +.Learly_end: + b.d .Lend + sub_s.ne r1,r1,r1 +ARC_EXIT strlen From 9d42c84f9182da615e7ec0964ce585f23c822349 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:18 +0530 Subject: [PATCH 2271/4607] ARC: Low level IRQ/Trap/Exception Handling Signed-off-by: Vineet Gupta Cc: Al Viro --- arch/arc/include/asm/entry.h | 495 ++++++++++++++++++++++++++++++ arch/arc/kernel/entry.S | 571 +++++++++++++++++++++++++++++++++++ 2 files changed, 1066 insertions(+) create mode 100644 arch/arc/include/asm/entry.h create mode 100644 arch/arc/kernel/entry.S diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h new file mode 100644 index 000000000000..63705b12d911 --- /dev/null +++ b/arch/arc/include/asm/entry.h @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Vineetg: Aug 28th 2008: Bug #94984 + * -Zero Overhead Loop Context shd be cleared when entering IRQ/EXcp/Trap + * Normally CPU does this automatically, however when doing FAKE rtie, + * we also need to explicitly do this. The problem in macros + * FAKE_RET_FROM_EXCPN and FAKE_RET_FROM_EXCPN_LOCK_IRQ was that this bit + * was being "CLEARED" rather then "SET". Actually "SET" clears ZOL context + * + * Vineetg: May 5th 2008 + * - Defined Stack Switching Macro to be reused in all intr/excp hdlrs + * - Shaved off 11 instructions from RESTORE_ALL_INT1 by using the + * address Write back load ld.ab instead of seperate ld/add instn + * + * Amit Bhor, Sameer Dhavale: Codito Technologies 2004 + */ + +#ifndef __ASM_ARC_ENTRY_H +#define __ASM_ARC_ENTRY_H + +#ifdef __ASSEMBLY__ +#include /* For NR_syscalls defination */ +#include +#include +#include +#include /* For THREAD_SIZE */ + +/* Note on the LD/ST addr modes with addr reg wback + * + * LD.a same as LD.aw + * + * LD.a reg1, [reg2, x] => Pre Incr + * Eff Addr for load = [reg2 + x] + * + * LD.ab reg1, [reg2, x] => Post Incr + * Eff Addr for load = [reg2] + */ + +/*-------------------------------------------------------------- + * Save caller saved registers (scratch registers) ( r0 - r12 ) + * Registers are pushed / popped in the order defined in struct ptregs + * in asm/ptrace.h + *-------------------------------------------------------------*/ +.macro SAVE_CALLER_SAVED + st.a r0, [sp, -4] + st.a r1, [sp, -4] + st.a r2, [sp, -4] + st.a r3, [sp, -4] + st.a r4, [sp, -4] + st.a r5, [sp, -4] + st.a r6, [sp, -4] + st.a r7, [sp, -4] + st.a r8, [sp, -4] + st.a r9, [sp, -4] + st.a r10, [sp, -4] + st.a r11, [sp, -4] + st.a r12, [sp, -4] +.endm + +/*-------------------------------------------------------------- + * Restore caller saved registers (scratch registers) + *-------------------------------------------------------------*/ +.macro RESTORE_CALLER_SAVED + ld.ab r12, [sp, 4] + ld.ab r11, [sp, 4] + ld.ab r10, [sp, 4] + ld.ab r9, [sp, 4] + ld.ab r8, [sp, 4] + ld.ab r7, [sp, 4] + ld.ab r6, [sp, 4] + ld.ab r5, [sp, 4] + ld.ab r4, [sp, 4] + ld.ab r3, [sp, 4] + ld.ab r2, [sp, 4] + ld.ab r1, [sp, 4] + ld.ab r0, [sp, 4] +.endm + + +/*-------------------------------------------------------------- + * Save callee saved registers (non scratch registers) ( r13 - r25 ) + * on kernel stack. + * User mode callee regs need to be saved in case of + * -fork and friends for replicating from parent to child + * -before going into do_signal( ) for ptrace/core-dump + * Special case handling is required for r25 in case it is used by kernel + * for caching task ptr. Low level exception/ISR save user mode r25 + * into task->thread.user_r25. So it needs to be retrieved from there and + * saved into kernel stack with rest of callee reg-file + *-------------------------------------------------------------*/ +.macro SAVE_CALLEE_SAVED_USER + st.a r13, [sp, -4] + st.a r14, [sp, -4] + st.a r15, [sp, -4] + st.a r16, [sp, -4] + st.a r17, [sp, -4] + st.a r18, [sp, -4] + st.a r19, [sp, -4] + st.a r20, [sp, -4] + st.a r21, [sp, -4] + st.a r22, [sp, -4] + st.a r23, [sp, -4] + st.a r24, [sp, -4] + st.a r25, [sp, -4] + + /* move up by 1 word to "create" callee_regs->"stack_place_holder" */ + sub sp, sp, 4 +.endm + +/*-------------------------------------------------------------- + * Save callee saved registers (non scratch registers) ( r13 - r25 ) + * kernel mode callee regs needed to be saved in case of context switch + * If r25 is used for caching task pointer then that need not be saved + * as it can be re-created from current task global + *-------------------------------------------------------------*/ +.macro SAVE_CALLEE_SAVED_KERNEL + st.a r13, [sp, -4] + st.a r14, [sp, -4] + st.a r15, [sp, -4] + st.a r16, [sp, -4] + st.a r17, [sp, -4] + st.a r18, [sp, -4] + st.a r19, [sp, -4] + st.a r20, [sp, -4] + st.a r21, [sp, -4] + st.a r22, [sp, -4] + st.a r23, [sp, -4] + st.a r24, [sp, -4] + st.a r25, [sp, -4] + sub sp, sp, 4 +.endm + +/*-------------------------------------------------------------- + * RESTORE_CALLEE_SAVED_KERNEL: + * Loads callee (non scratch) Reg File by popping from Kernel mode stack. + * This is reverse of SAVE_CALLEE_SAVED, + * + * NOTE: + * Ideally this shd only be called in switch_to for loading + * switched-IN task's CALLEE Reg File. + * For all other cases RESTORE_CALLEE_SAVED_FAST must be used + * which simply pops the stack w/o touching regs. + *-------------------------------------------------------------*/ +.macro RESTORE_CALLEE_SAVED_KERNEL + + add sp, sp, 4 /* skip "callee_regs->stack_place_holder" */ + ld.ab r25, [sp, 4] + ld.ab r24, [sp, 4] + ld.ab r23, [sp, 4] + ld.ab r22, [sp, 4] + ld.ab r21, [sp, 4] + ld.ab r20, [sp, 4] + ld.ab r19, [sp, 4] + ld.ab r18, [sp, 4] + ld.ab r17, [sp, 4] + ld.ab r16, [sp, 4] + ld.ab r15, [sp, 4] + ld.ab r14, [sp, 4] + ld.ab r13, [sp, 4] + +.endm + +/*-------------------------------------------------------------- + * Super FAST Restore callee saved regs by simply re-adjusting SP + *-------------------------------------------------------------*/ +.macro DISCARD_CALLEE_SAVED_USER + add sp, sp, 14 * 4 +.endm + +/*-------------------------------------------------------------- + * Restore User mode r25 saved in task_struct->thread.user_r25 + *-------------------------------------------------------------*/ +.macro RESTORE_USER_R25 + ld r25, [r25, TASK_THREAD + THREAD_USER_R25] +.endm + +/*------------------------------------------------------------- + * given a tsk struct, get to the base of it's kernel mode stack + * tsk->thread_info is really a PAGE, whose bottom hoists stack + * which grows upwards towards thread_info + *------------------------------------------------------------*/ + +.macro GET_TSK_STACK_BASE tsk, out + + /* Get task->thread_info (this is essentially start of a PAGE) */ + ld \out, [\tsk, TASK_THREAD_INFO] + + /* Go to end of page where stack begins (grows upwards) */ + add2 \out, \out, (THREAD_SIZE - 4)/4 /* one word GUTTER */ + +.endm + +/*-------------------------------------------------------------- + * Switch to Kernel Mode stack if SP points to User Mode stack + * + * Entry : r9 contains pre-IRQ/exception/trap status32 + * Exit : SP is set to kernel mode stack pointer + * Clobbers: r9 + *-------------------------------------------------------------*/ + +.macro SWITCH_TO_KERNEL_STK + + /* User Mode when this happened ? Yes: Proceed to switch stack */ + bbit1 r9, STATUS_U_BIT, 88f + + /* OK we were already in kernel mode when this event happened, thus can + * assume SP is kernel mode SP. _NO_ need to do any stack switching + */ + + /* Save Pre Intr/Exception KERNEL MODE SP on kernel stack + * safe-keeping not really needed, but it keeps the epilogue code + * (SP restore) simpler/uniform. + */ + b.d 77f + + st.a sp, [sp, -12] ; Make room for orig_r0 and orig_r8 + +88: /*------Intr/Ecxp happened in user mode, "switch" stack ------ */ + + GET_CURR_TASK_ON_CPU r9 + + /* With current tsk in r9, get it's kernel mode stack base */ + GET_TSK_STACK_BASE r9, r9 + +#ifdef PT_REGS_CANARY + st 0xabcdabcd, [r9, 0] +#endif + + /* Save Pre Intr/Exception User SP on kernel stack */ + st.a sp, [r9, -12] ; Make room for orig_r0 and orig_r8 + + /* CAUTION: + * SP should be set at the very end when we are done with everything + * In case of 2 levels of interrupt we depend on value of SP to assume + * that everything else is done (loading r25 etc) + */ + + /* set SP to point to kernel mode stack */ + mov sp, r9 + +77: /* ----- Stack Switched to kernel Mode, Now save REG FILE ----- */ + +.endm + +/*------------------------------------------------------------ + * "FAKE" a rtie to return from CPU Exception context + * This is to re-enable Exceptions within exception + * Look at EV_ProtV to see how this is actually used + *-------------------------------------------------------------*/ + +.macro FAKE_RET_FROM_EXCPN reg + + ld \reg, [sp, PT_status32] + bic \reg, \reg, (STATUS_U_MASK|STATUS_DE_MASK) + bset \reg, \reg, STATUS_L_BIT + sr \reg, [erstatus] + mov \reg, 55f + sr \reg, [eret] + + rtie +55: +.endm + +/* + * @reg [OUT] &thread_info of "current" + */ +.macro GET_CURR_THR_INFO_FROM_SP reg + and \reg, sp, ~(THREAD_SIZE - 1) +.endm + +/* + * @reg [OUT] thread_info->flags of "current" + */ +.macro GET_CURR_THR_INFO_FLAGS reg + GET_CURR_THR_INFO_FROM_SP \reg + ld \reg, [\reg, THREAD_INFO_FLAGS] +.endm + +/*-------------------------------------------------------------- + * For early Exception Prologue, a core reg is temporarily needed to + * code the rest of prolog (stack switching). This is done by stashing + * it to memory (non-SMP case) or SCRATCH0 Aux Reg (SMP). + * + * Before saving the full regfile - this reg is restored back, only + * to be saved again on kernel mode stack, as part of ptregs. + *-------------------------------------------------------------*/ +.macro EXCPN_PROLOG_FREEUP_REG reg + st \reg, [@ex_saved_reg1] +.endm + +.macro EXCPN_PROLOG_RESTORE_REG reg + ld \reg, [@ex_saved_reg1] +.endm + +/*-------------------------------------------------------------- + * Save all registers used by Exceptions (TLB Miss, Prot-V, Mem err etc) + * Requires SP to be already switched to kernel mode Stack + * sp points to the next free element on the stack at exit of this macro. + * Registers are pushed / popped in the order defined in struct ptregs + * in asm/ptrace.h + * Note that syscalls are implemented via TRAP which is also a exception + * from CPU's point of view + *-------------------------------------------------------------*/ +.macro SAVE_ALL_EXCEPTION marker + + /* Restore r9 used to code the early prologue */ + EXCPN_PROLOG_RESTORE_REG r9 + + /* Save the complete regfile now */ + + /* orig_r8 marker: + * syscalls -> 1 to NR_SYSCALLS + * Exceptions -> NR_SYSCALLS + 1 + * Break-point-> NR_SYSCALLS + 2 + */ + st \marker, [sp, 8] + st r0, [sp, 4] /* orig_r0, needed only for sys calls */ + SAVE_CALLER_SAVED + st.a r26, [sp, -4] /* gp */ + st.a fp, [sp, -4] + st.a blink, [sp, -4] + lr r9, [eret] + st.a r9, [sp, -4] + lr r9, [erstatus] + st.a r9, [sp, -4] + st.a lp_count, [sp, -4] + lr r9, [lp_end] + st.a r9, [sp, -4] + lr r9, [lp_start] + st.a r9, [sp, -4] + lr r9, [erbta] + st.a r9, [sp, -4] + +#ifdef PT_REGS_CANARY + mov r9, 0xdeadbeef + st r9, [sp, -4] +#endif + + /* move up by 1 word to "create" pt_regs->"stack_place_holder" */ + sub sp, sp, 4 +.endm + +/*-------------------------------------------------------------- + * Save scratch regs for exceptions + *-------------------------------------------------------------*/ +.macro SAVE_ALL_SYS + SAVE_ALL_EXCEPTION (NR_syscalls + 1) +.endm + +/*-------------------------------------------------------------- + * Save scratch regs for sys calls + *-------------------------------------------------------------*/ +.macro SAVE_ALL_TRAP + SAVE_ALL_EXCEPTION r8 +.endm + +/*-------------------------------------------------------------- + * Restore all registers used by system call or Exceptions + * SP should always be pointing to the next free stack element + * when entering this macro. + * + * NOTE: + * + * It is recommended that lp_count/ilink1/ilink2 not be used as a dest reg + * for memory load operations. If used in that way interrupts are deffered + * by hardware and that is not good. + *-------------------------------------------------------------*/ +.macro RESTORE_ALL_SYS + + add sp, sp, 4 /* hop over unused "pt_regs->stack_place_holder" */ + + ld.ab r9, [sp, 4] + sr r9, [erbta] + ld.ab r9, [sp, 4] + sr r9, [lp_start] + ld.ab r9, [sp, 4] + sr r9, [lp_end] + ld.ab r9, [sp, 4] + mov lp_count, r9 + ld.ab r9, [sp, 4] + sr r9, [erstatus] + ld.ab r9, [sp, 4] + sr r9, [eret] + ld.ab blink, [sp, 4] + ld.ab fp, [sp, 4] + ld.ab r26, [sp, 4] /* gp */ + RESTORE_CALLER_SAVED + + ld sp, [sp] /* restore original sp */ + /* orig_r0 and orig_r8 skipped automatically */ +.endm + + +/*-------------------------------------------------------------- + * Save all registers used by interrupt handlers. + *-------------------------------------------------------------*/ +.macro SAVE_ALL_INT1 + + /* restore original r9 , saved in int1_saved_reg + * It will be saved on stack in macro: SAVE_CALLER_SAVED + */ + ld r9, [@int1_saved_reg] + + /* now we are ready to save the remaining context :) */ + st -1, [sp, 8] /* orig_r8, -1 for interuppt level one */ + st 0, [sp, 4] /* orig_r0 , N/A for IRQ */ + SAVE_CALLER_SAVED + st.a r26, [sp, -4] /* gp */ + st.a fp, [sp, -4] + st.a blink, [sp, -4] + st.a ilink1, [sp, -4] + lr r9, [status32_l1] + st.a r9, [sp, -4] + st.a lp_count, [sp, -4] + lr r9, [lp_end] + st.a r9, [sp, -4] + lr r9, [lp_start] + st.a r9, [sp, -4] + lr r9, [bta_l1] + st.a r9, [sp, -4] + +#ifdef PT_REGS_CANARY + mov r9, 0xdeadbee1 + st r9, [sp, -4] +#endif + /* move up by 1 word to "create" pt_regs->"stack_place_holder" */ + sub sp, sp, 4 +.endm + +/*-------------------------------------------------------------- + * Restore all registers used by interrupt handlers. + * + * NOTE: + * + * It is recommended that lp_count/ilink1/ilink2 not be used as a dest reg + * for memory load operations. If used in that way interrupts are deffered + * by hardware and that is not good. + *-------------------------------------------------------------*/ + +.macro RESTORE_ALL_INT1 + add sp, sp, 4 /* hop over unused "pt_regs->stack_place_holder" */ + + ld.ab r9, [sp, 4] /* Actual reg file */ + sr r9, [bta_l1] + ld.ab r9, [sp, 4] + sr r9, [lp_start] + ld.ab r9, [sp, 4] + sr r9, [lp_end] + ld.ab r9, [sp, 4] + mov lp_count, r9 + ld.ab r9, [sp, 4] + sr r9, [status32_l1] + ld.ab r9, [sp, 4] + mov ilink1, r9 + ld.ab blink, [sp, 4] + ld.ab fp, [sp, 4] + ld.ab r26, [sp, 4] /* gp */ + RESTORE_CALLER_SAVED + + ld sp, [sp] /* restore original sp */ + /* orig_r0 and orig_r8 skipped automatically */ +.endm + +/* Get CPU-ID of this core */ +.macro GET_CPU_ID reg + lr \reg, [identity] + lsr \reg, \reg, 8 + bmsk \reg, \reg, 7 +.endm + +.macro GET_CURR_TASK_ON_CPU reg + ld \reg, [@_current_task] +.endm + +.macro SET_CURR_TASK_ON_CPU tsk, tmp + st \tsk, [@_current_task] +.endm + +/* ------------------------------------------------------------------ + * Get the ptr to some field of Current Task at @off in task struct + */ + +.macro GET_CURR_TASK_FIELD_PTR off, reg + GET_CURR_TASK_ON_CPU \reg + add \reg, \reg, \off +.endm + +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_ARC_ENTRY_H */ diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S new file mode 100644 index 000000000000..a4acc9ee1311 --- /dev/null +++ b/arch/arc/kernel/entry.S @@ -0,0 +1,571 @@ +/* + * Low Level Interrupts/Traps/Exceptions(non-TLB) Handling for ARC + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: Nov 2010: + * -Vector table jumps (@8 bytes) converted into branches (@4 bytes) + * -To maintain the slot size of 8 bytes/vector, added nop, which is + * not executed at runtime. + * + * vineetg: Nov 2009 (Everything needed for TIF_RESTORE_SIGMASK) + * -do_signal()invoked upon TIF_RESTORE_SIGMASK as well + * -Wrappers for sys_{,rt_}sigsuspend() nolonger needed as they don't + * need ptregs anymore + * + * Vineetg: Oct 2009 + * -In a rare scenario, Process gets a Priv-V exception and gets scheduled + * out. Since we don't do FAKE RTIE for Priv-V, CPU excpetion state remains + * active (AE bit enabled). This causes a double fault for a subseq valid + * exception. Thus FAKE RTIE needed in low level Priv-Violation handler. + * Instr Error could also cause similar scenario, so same there as well. + * + * Vineetg: Aug 28th 2008: Bug #94984 + * -Zero Overhead Loop Context shd be cleared when entering IRQ/EXcp/Trap + * Normally CPU does this automatically, however when doing FAKE rtie, + * we need to explicitly do this. The problem in macros + * FAKE_RET_FROM_EXCPN and FAKE_RET_FROM_EXCPN_LOCK_IRQ was that this bit + * was being "CLEARED" rather then "SET". Since it is Loop INHIBIT Bit, + * setting it and not clearing it clears ZOL context + * + * Vineetg: Dec 22, 2007 + * Minor Surgery of Low Level ISR to make it SMP safe + * - MMU_SCRATCH0 Reg used for freeing up r9 in Level 1 ISR + * - _current_task is made an array of NR_CPUS + * - Access of _current_task wrapped inside a macro so that if hardware + * team agrees for a dedicated reg, no other code is touched + * + * Amit Bhor, Rahul Trivedi, Kanika Nema, Sameer Dhavale : Codito Tech 2004 + */ + +/*------------------------------------------------------------------ + * Function ABI + *------------------------------------------------------------------ + * + * Arguments r0 - r7 + * Caller Saved Registers r0 - r12 + * Callee Saved Registers r13- r25 + * Global Pointer (gp) r26 + * Frame Pointer (fp) r27 + * Stack Pointer (sp) r28 + * Interrupt link register (ilink1) r29 + * Interrupt link register (ilink2) r30 + * Branch link register (blink) r31 + *------------------------------------------------------------------ + */ + + .cpu A7 + +;############################ Vector Table ################################# + +.macro VECTOR lbl +#if 1 /* Just in case, build breaks */ + j \lbl +#else + b \lbl + nop +#endif +.endm + + .section .vector, "ax",@progbits + .align 4 + +/* Each entry in the vector table must occupy 2 words. Since it is a jump + * across sections (.vector to .text) we are gauranteed that 'j somewhere' + * will use the 'j limm' form of the intrsuction as long as somewhere is in + * a section other than .vector. + */ + +; ********* Critical System Events ********************** +VECTOR res_service ; 0x0, Restart Vector (0x0) +VECTOR mem_service ; 0x8, Mem exception (0x1) +VECTOR instr_service ; 0x10, Instrn Error (0x2) + +; ******************** Device ISRs ********************** +VECTOR handle_interrupt_level1 + +VECTOR handle_interrupt_level1 + +VECTOR handle_interrupt_level1 + +VECTOR handle_interrupt_level1 + +.rept 25 +VECTOR handle_interrupt_level1 ; Other devices +.endr + +/* FOR ARC600: timer = 0x3, uart = 0x8, emac = 0x10 */ + +; ******************** Exceptions ********************** +VECTOR EV_MachineCheck ; 0x100, Fatal Machine check (0x20) +VECTOR EV_TLBMissI ; 0x108, Intruction TLB miss (0x21) +VECTOR EV_TLBMissD ; 0x110, Data TLB miss (0x22) +VECTOR EV_TLBProtV ; 0x118, Protection Violation (0x23) + ; or Misaligned Access +VECTOR EV_PrivilegeV ; 0x120, Privilege Violation (0x24) +VECTOR EV_Trap ; 0x128, Trap exception (0x25) +VECTOR EV_Extension ; 0x130, Extn Intruction Excp (0x26) + +.rept 24 +VECTOR reserved ; Reserved Exceptions +.endr + +#include /* ARC_{EXTRY,EXIT} */ +#include /* SAVE_ALL_{INT1,INT2,TRAP...} */ +#include +#include +#include + +;##################### Scratch Mem for IRQ stack switching ############# + + .section .data ; NOT .global + .align 32 + .type int1_saved_reg, @object + .size int1_saved_reg, 4 +int1_saved_reg: + .zero 4 + +; --------------------------------------------- + .section .text, "ax",@progbits + +res_service: ; processor restart + flag 0x1 ; not implemented + nop + nop + +reserved: ; processor restart + rtie ; jump to processor initializations + +;##################### Interrupt Handling ############################## + +; --------------------------------------------- +; Level 1 ISR +; --------------------------------------------- +ARC_ENTRY handle_interrupt_level1 + + /* free up r9 as scratchpad */ + st r9, [@int1_saved_reg] + + ;Which mode (user/kernel) was the system in when intr occured + lr r9, [status32_l1] + + SWITCH_TO_KERNEL_STK + SAVE_ALL_INT1 + + lr r0, [icause1] + and r0, r0, 0x1f + + bl.d @arch_do_IRQ + mov r1, sp + + mov r8,0x1 + sr r8, [AUX_IRQ_LV12] ; clear bit in Sticky Status Reg + + b ret_from_exception +ARC_EXIT handle_interrupt_level1 + +;################### Non TLB Exception Handling ############################# + +; --------------------------------------------- +; Instruction Error Exception Handler +; --------------------------------------------- + +ARC_ENTRY instr_service + + EXCPN_PROLOG_FREEUP_REG r9 + + lr r9, [erstatus] + + SWITCH_TO_KERNEL_STK + SAVE_ALL_SYS + + lr r0, [ecr] + lr r1, [efa] + + mov r2, sp + + FAKE_RET_FROM_EXCPN r9 + + bl do_insterror_or_kprobe + b ret_from_exception +ARC_EXIT instr_service + +; --------------------------------------------- +; Memory Error Exception Handler +; --------------------------------------------- + +ARC_ENTRY mem_service + + EXCPN_PROLOG_FREEUP_REG r9 + + lr r9, [erstatus] + + SWITCH_TO_KERNEL_STK + SAVE_ALL_SYS + + lr r0, [ecr] + lr r1, [efa] + mov r2, sp + bl do_memory_error + b ret_from_exception +ARC_EXIT mem_service + +; --------------------------------------------- +; Machine Check Exception Handler +; --------------------------------------------- + +ARC_ENTRY EV_MachineCheck + + EXCPN_PROLOG_FREEUP_REG r9 + lr r9, [erstatus] + + SWITCH_TO_KERNEL_STK + SAVE_ALL_SYS + + lr r0, [ecr] + lr r1, [efa] + mov r2, sp + + brne r0, 0x200100, 1f + bl do_tlb_overlap_fault + b ret_from_exception + +1: + ; DEAD END: can't do much, display Regs and HALT + SAVE_CALLEE_SAVED_USER + + GET_CURR_TASK_FIELD_PTR TASK_THREAD, r10 + st sp, [r10, THREAD_CALLEE_REG] + + j do_machine_check_fault + +ARC_EXIT EV_MachineCheck + +; --------------------------------------------- +; Protection Violation Exception Handler +; --------------------------------------------- + +ARC_ENTRY EV_TLBProtV + + EXCPN_PROLOG_FREEUP_REG r9 + + ;Which mode (user/kernel) was the system in when Exception occured + lr r9, [erstatus] + + SWITCH_TO_KERNEL_STK + SAVE_ALL_SYS + + ;---------(3) Save some more regs----------------- + ; vineetg: Mar 6th: Random Seg Fault issue #1 + ; ecr and efa were not saved in case an Intr sneaks in + ; after fake rtie + ; + lr r3, [ecr] + lr r4, [efa] + + ; --------(4) Return from CPU Exception Mode --------- + ; Fake a rtie, but rtie to next label + ; That way, subsequently, do_page_fault ( ) executes in pure kernel + ; mode with further Exceptions enabled + + FAKE_RET_FROM_EXCPN r9 + + ;------ (5) Type of Protection Violation? ---------- + ; + ; ProtV Hardware Exception is triggered for Access Faults of 2 types + ; -Access Violaton (WRITE to READ ONLY Page) - for linux COW + ; -Unaligned Access (READ/WRITE on odd boundary) + ; + cmp r3, 0x230400 ; Misaligned data access ? + beq 4f + + ;========= (6a) Access Violation Processing ======== + cmp r3, 0x230100 + mov r1, 0x0 ; if LD exception ? write = 0 + mov.ne r1, 0x1 ; else write = 1 + + mov r2, r4 ; faulting address + mov r0, sp ; pt_regs + bl do_page_fault + b ret_from_exception + + ;========== (6b) Non aligned access ============ +4: + mov r0, r3 ; cause code + mov r1, r4 ; faulting address + mov r2, sp ; pt_regs + + bl do_misaligned_access + b ret_from_exception + +ARC_EXIT EV_TLBProtV + +; --------------------------------------------- +; Privilege Violation Exception Handler +; --------------------------------------------- +ARC_ENTRY EV_PrivilegeV + + EXCPN_PROLOG_FREEUP_REG r9 + + lr r9, [erstatus] + + SWITCH_TO_KERNEL_STK + SAVE_ALL_SYS + + lr r0, [ecr] + lr r1, [efa] + mov r2, sp + + FAKE_RET_FROM_EXCPN r9 + + bl do_privilege_fault + b ret_from_exception +ARC_EXIT EV_PrivilegeV + +; --------------------------------------------- +; Extension Instruction Exception Handler +; --------------------------------------------- +ARC_ENTRY EV_Extension + + EXCPN_PROLOG_FREEUP_REG r9 + lr r9, [erstatus] + + SWITCH_TO_KERNEL_STK + SAVE_ALL_SYS + + lr r0, [ecr] + lr r1, [efa] + mov r2, sp + bl do_extension_fault + b ret_from_exception +ARC_EXIT EV_Extension + +;################### Break Point TRAP ########################## + + ; ======= (5b) Trap is due to Break-Point ========= + +trap_with_param: + + ;make sure orig_r8 is a positive value + st NR_syscalls + 2, [sp, PT_orig_r8] + + mov r0, r12 + lr r1, [efa] + mov r2, sp + + ; Now that we have read EFA, its safe to do "fake" rtie + ; and get out of CPU exception mode + FAKE_RET_FROM_EXCPN r11 + + ; Save callee regs in case gdb wants to have a look + ; SP will grow up by size of CALLEE Reg-File + ; NOTE: clobbers r12 + SAVE_CALLEE_SAVED_USER + + ; save location of saved Callee Regs @ thread_struct->pc + GET_CURR_TASK_FIELD_PTR TASK_THREAD, r10 + st sp, [r10, THREAD_CALLEE_REG] + + ; Call the trap handler + bl do_non_swi_trap + + ; unwind stack to discard Callee saved Regs + DISCARD_CALLEE_SAVED_USER + + b ret_from_exception + +;##################### Trap Handling ############################## +; +; EV_Trap caused by TRAP_S and TRAP0 instructions. +;------------------------------------------------------------------ +; (1) System Calls +; :parameters in r0-r7. +; :r8 has the system call number +; (2) Break Points +;------------------------------------------------------------------ + +ARC_ENTRY EV_Trap + + ; Need at least 1 reg to code the early exception prolog + EXCPN_PROLOG_FREEUP_REG r9 + + ;Which mode (user/kernel) was the system in when intr occured + lr r9, [erstatus] + + SWITCH_TO_KERNEL_STK + SAVE_ALL_TRAP + + ;------- (4) What caused the Trap -------------- + lr r12, [ecr] + and.f 0, r12, ECR_PARAM_MASK + bnz trap_with_param + + ; ======= (5a) Trap is due to System Call ======== + + ; Before doing anything, return from CPU Exception Mode + FAKE_RET_FROM_EXCPN r11 + + ;============ This is normal System Call case ========== + ; Sys-call num shd not exceed the total system calls avail + cmp r8, NR_syscalls + mov.hi r0, -ENOSYS + bhi ret_from_system_call + + ; Offset into the syscall_table and call handler + ld.as r9,[sys_call_table, r8] + jl [r9] ; Entry into Sys Call Handler + + ; fall through to ret_from_system_call +ARC_EXIT EV_Trap + +ARC_ENTRY ret_from_system_call + + st r0, [sp, PT_r0] ; sys call return value in pt_regs + + ; fall through yet again to ret_from_exception + +;############# Return from Intr/Excp/Trap (Linux Specifics) ############## +; +; If ret to user mode do we need to handle signals, schedule() et al. + +ARC_ENTRY ret_from_exception + + ; Pre-{IRQ,Trap,Exception} K/U mode from pt_regs->status32 + ld r8, [sp, PT_status32] ; returning to User/Kernel Mode + +#ifdef CONFIG_PREEMPT + bbit0 r8, STATUS_U_BIT, resume_kernel_mode +#else + bbit0 r8, STATUS_U_BIT, restore_regs +#endif + + ; Before returning to User mode check-for-and-complete any pending work + ; such as rescheduling/signal-delivery etc. +resume_user_mode_begin: + + ; Disable IRQs to ensures that chk for pending work itself is atomic + ; (and we don't end up missing a NEED_RESCHED/SIGPENDING due to an + ; interim IRQ). + IRQ_DISABLE r10 + + ; Fast Path return to user mode if no pending work + GET_CURR_THR_INFO_FLAGS r9 + and.f 0, r9, _TIF_WORK_MASK + bz restore_regs + + ; --- (Slow Path #1) task preemption --- + bbit0 r9, TIF_NEED_RESCHED, .Lchk_pend_signals + mov blink, resume_user_mode_begin ; tail-call to U mode ret chks + b @schedule ; BTST+Bnz causes relo error in link + +.Lchk_pend_signals: + IRQ_ENABLE r10 + + ; --- (Slow Path #2) pending signal --- + mov r0, sp ; pt_regs for arg to do_signal()/do_notify_resume() + + bbit0 r9, TIF_SIGPENDING, .Lchk_notify_resume + + ; save CALLEE Regs. + ; (i) If this signal causes coredump - full regfile needed + ; (ii) If signal is SIGTRAP/SIGSTOP, task is being traced thus + ; tracer might call PEEKUSR(CALLEE reg) + ; + ; NOTE: SP will grow up by size of CALLEE Reg-File + SAVE_CALLEE_SAVED_USER ; clobbers r12 + + ; save location of saved Callee Regs @ thread_struct->callee + GET_CURR_TASK_FIELD_PTR TASK_THREAD, r10 + st sp, [r10, THREAD_CALLEE_REG] + + bl @do_signal + + ; unwind SP for cheap discard of Callee saved Regs + DISCARD_CALLEE_SAVED_USER + + b resume_user_mode_begin ; loop back to start of U mode ret + + ; --- (Slow Path #3) notify_resume --- +.Lchk_notify_resume: + btst r9, TIF_NOTIFY_RESUME + blnz @do_notify_resume + b resume_user_mode_begin ; unconditionally back to U mode ret chks + ; for single exit point from this block + +#ifdef CONFIG_PREEMPT + +resume_kernel_mode: + + ; Can't preempt if preemption disabled + GET_CURR_THR_INFO_FROM_SP r10 + ld r8, [r10, THREAD_INFO_PREEMPT_COUNT] + brne r8, 0, restore_regs + + ; check if this task's NEED_RESCHED flag set + ld r9, [r10, THREAD_INFO_FLAGS] + bbit0 r9, TIF_NEED_RESCHED, restore_regs + + IRQ_DISABLE r9 + + ; Invoke PREEMPTION + bl preempt_schedule_irq + + ; preempt_schedule_irq() always returns with IRQ disabled +#endif + + ; fall through + +;############# Return from Intr/Excp/Trap (ARC Specifics) ############## +; +; Restore the saved sys context (common exit-path for EXCPN/IRQ/Trap) +; IRQ shd definitely not happen between now and rtie + +restore_regs : + + ; Disable Interrupts while restoring reg-file back + ; XXX can this be optimised out + IRQ_DISABLE_SAVE r9, r10 ;@r10 has prisitine (pre-disable) copy + + ; Restore REG File. In case multiple Events outstanding, + ; use the same priorty as rtie: EXCPN, L2 IRQ, L1 IRQ, None + ; Note that we use realtime STATUS32 (not pt_regs->status32) to + ; decide that. + + ; if Returning from Exception + bbit0 r10, STATUS_AE_BIT, not_exception + RESTORE_ALL_SYS + rtie + + ; Not Exception so maybe Interrupts (Level 1 or 2) + +not_exception: + + bbit0 r10, STATUS_A1_BIT, not_level1_interrupt + + ;return from level 1 + + RESTORE_ALL_INT1 +debug_marker_l1: + rtie + +not_level1_interrupt: + + ;this case is for syscalls or Exceptions (with fake rtie) + + RESTORE_ALL_SYS +debug_marker_syscall: + rtie + +ARC_EXIT ret_from_exception + +ARC_ENTRY ret_from_fork + ; when the forked child comes here from the __switch_to function + ; r0 has the last task pointer. + ; put last task in scheduler queue + bl @schedule_tail + b @ret_from_exception +ARC_EXIT ret_from_fork From bacdf4809afade180a8f2171adb4cf7ec715d139 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:18 +0530 Subject: [PATCH 2272/4607] ARC: Interrupt Handling This contains: -bootup arch IRQ init: init_IRQ(), arc_init_IRQ() -generic IRQ subsystem glue: arch_do_IRQ() -basic IRQ chip setup for in-core intc Signed-off-by: Vineet Gupta Cc: Thomas Gleixner --- arch/arc/include/asm/arcregs.h | 3 + arch/arc/include/asm/hw_irq.h | 7 +++ arch/arc/include/asm/irq.h | 22 +++++++ arch/arc/kernel/irq.c | 107 ++++++++++++++++++++++++++++++++- arch/arc/plat-arcfpga/irq.c | 15 +++++ 5 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 arch/arc/include/asm/hw_irq.h create mode 100644 arch/arc/include/asm/irq.h create mode 100644 arch/arc/plat-arcfpga/irq.c diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index 8ca8faf4b80e..3fccb04e6d93 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -11,6 +11,9 @@ #ifdef __KERNEL__ +/* Build Configuration Registers */ +#define ARC_REG_VECBASE_BCR 0x68 + /* status32 Bits Positions */ #define STATUS_H_BIT 0 /* CPU Halted */ #define STATUS_E1_BIT 1 /* Int 1 enable */ diff --git a/arch/arc/include/asm/hw_irq.h b/arch/arc/include/asm/hw_irq.h new file mode 100644 index 000000000000..fd565ab89695 --- /dev/null +++ b/arch/arc/include/asm/hw_irq.h @@ -0,0 +1,7 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + */ diff --git a/arch/arc/include/asm/irq.h b/arch/arc/include/asm/irq.h new file mode 100644 index 000000000000..ca4aeba1eed3 --- /dev/null +++ b/arch/arc/include/asm/irq.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_IRQ_H +#define __ASM_ARC_IRQ_H + +/* Platform Independent IRQs */ +#define TIMER0_IRQ 3 +#define TIMER1_IRQ 4 + +#include + +extern void __init arc_init_IRQ(void); +extern void __init plat_init_IRQ(void); +extern int __init get_hw_config_num_irq(void); + +#endif diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c index c4e9b25d305f..2f399eb88f9d 100644 --- a/arch/arc/kernel/irq.c +++ b/arch/arc/kernel/irq.c @@ -9,8 +9,111 @@ #include #include -#include -#include +#include +#include + +/* + * Early Hardware specific Interrupt setup + * -Called very early (start_kernel -> setup_arch -> setup_processor) + * -Platform Independent (must for any ARC700) + * -Needed for each CPU (hence not foldable into init_IRQ) + * + * what it does ? + * -setup Vector Table Base Reg - in case Linux not linked at 0x8000_0000 + * -Disable all IRQs (on CPU side) + */ +void __init arc_init_IRQ(void) +{ + int level_mask = level_mask; + + write_aux_reg(AUX_INTR_VEC_BASE, _int_vec_base_lds); + + /* Disable all IRQs: enable them as devices request */ + write_aux_reg(AUX_IENABLE, 0); +} + +/* + * ARC700 core includes a simple on-chip intc supporting + * -per IRQ enable/disable + * -2 levels of interrupts (high/low) + * -all interrupts being level triggered + * + * To reduce platform code, we assume all IRQs directly hooked-up into intc. + * Platforms with external intc, hence cascaded IRQs, are free to over-ride + * below, per IRQ. + */ + +static void arc_mask_irq(struct irq_data *data) +{ + arch_mask_irq(data->irq); +} + +static void arc_unmask_irq(struct irq_data *data) +{ + arch_unmask_irq(data->irq); +} + +static struct irq_chip onchip_intc = { + .name = "ARC In-core Intc", + .irq_mask = arc_mask_irq, + .irq_unmask = arc_unmask_irq, +}; + +void __init init_onchip_IRQ(void) +{ + int i; + + for (i = 0; i < NR_IRQS; i++) + irq_set_chip_and_handler(i, &onchip_intc, handle_level_irq); + +#ifdef CONFIG_SMP + irq_set_chip_and_handler(TIMER0_IRQ, &onchip_intc, handle_percpu_irq); +#endif +} + +/* + * Late Interrupt system init called from start_kernel for Boot CPU only + * + * Since slab must already be initialized, platforms can start doing any + * needed request_irq( )s + */ +void __init init_IRQ(void) +{ + init_onchip_IRQ(); + plat_init_IRQ(); +} + +/* + * "C" Entry point for any ARC ISR, called from low level vector handler + * @irq is the vector number read from ICAUSE reg of on-chip intc + */ +void arch_do_IRQ(unsigned int irq, struct pt_regs *regs) +{ + struct pt_regs *old_regs = set_irq_regs(regs); + + irq_enter(); + generic_handle_irq(irq); + irq_exit(); + set_irq_regs(old_regs); +} + +int __init get_hw_config_num_irq(void) +{ + uint32_t val = read_aux_reg(ARC_REG_VECBASE_BCR); + + switch (val & 0x03) { + case 0: + return 16; + case 1: + return 32; + case 2: + return 8; + default: + return 0; + } + + return 0; +} void arch_local_irq_enable(void) { diff --git a/arch/arc/plat-arcfpga/irq.c b/arch/arc/plat-arcfpga/irq.c new file mode 100644 index 000000000000..ed726360b9f6 --- /dev/null +++ b/arch/arc/plat-arcfpga/irq.c @@ -0,0 +1,15 @@ +/* + * ARC FPGA Platform IRQ hookups + * + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 + +void __init plat_init_IRQ(void) +{ +} From 054419ed8405da7aa93f88f698d696980efd3e37 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:18 +0530 Subject: [PATCH 2273/4607] ARC: Non-MMU Exception Handling Signed-off-by: Vineet Gupta --- arch/arc/include/asm/hw_irq.h | 7 -- arch/arc/kernel/traps.c | 125 ++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 7 deletions(-) delete mode 100644 arch/arc/include/asm/hw_irq.h create mode 100644 arch/arc/kernel/traps.c diff --git a/arch/arc/include/asm/hw_irq.h b/arch/arc/include/asm/hw_irq.h deleted file mode 100644 index fd565ab89695..000000000000 --- a/arch/arc/include/asm/hw_irq.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) - * - * 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. - */ diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c new file mode 100644 index 000000000000..fd2457cec226 --- /dev/null +++ b/arch/arc/kernel/traps.c @@ -0,0 +1,125 @@ +/* + * Traps/Non-MMU Exception handling for ARC + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Rahul Trivedi: Codito Technologies 2004 + */ + +#include +#include +#include +#include +#include + +void __init trap_init(void) +{ + return; +} + +void die(const char *str, struct pt_regs *regs, unsigned long address, + unsigned long cause_reg) +{ + show_kernel_fault_diag(str, regs, address, cause_reg); + + /* DEAD END */ + __asm__("flag 1"); +} + +/* + * Helper called for bulk of exceptions NOT needing specific handling + * -for user faults enqueues requested signal + * -for kernel, chk if due to copy_(to|from)_user, otherwise die() + */ +static noinline int handle_exception(unsigned long cause, char *str, + struct pt_regs *regs, siginfo_t *info) +{ + if (user_mode(regs)) { + struct task_struct *tsk = current; + + tsk->thread.fault_address = (__force unsigned int)info->si_addr; + tsk->thread.cause_code = cause; + + force_sig_info(info->si_signo, info, tsk); + + } else { + /* If not due to copy_(to|from)_user, we are doomed */ + if (fixup_exception(regs)) + return 0; + + die(str, regs, (unsigned long)info->si_addr, cause); + } + + return 1; +} + +#define DO_ERROR_INFO(signr, str, name, sicode) \ +int name(unsigned long cause, unsigned long address, struct pt_regs *regs) \ +{ \ + siginfo_t info = { \ + .si_signo = signr, \ + .si_errno = 0, \ + .si_code = sicode, \ + .si_addr = (void __user *)address, \ + }; \ + return handle_exception(cause, str, regs, &info);\ +} + +/* + * Entry points for exceptions NOT needing specific handling + */ +DO_ERROR_INFO(SIGILL, "Priv Op/Disabled Extn", do_privilege_fault, ILL_PRVOPC) +DO_ERROR_INFO(SIGILL, "Invalid Extn Insn", do_extension_fault, ILL_ILLOPC) +DO_ERROR_INFO(SIGILL, "Illegal Insn (or Seq)", insterror_is_error, ILL_ILLOPC) +DO_ERROR_INFO(SIGBUS, "Invalid Mem Access", do_memory_error, BUS_ADRERR) +DO_ERROR_INFO(SIGTRAP, "Breakpoint Set", trap_is_brkpt, TRAP_BRKPT) + +DO_ERROR_INFO(SIGSEGV, "Misaligned Access", do_misaligned_access, SEGV_ACCERR) + +/* + * Entry point for miscll errors such as Nested Exceptions + * -Duplicate TLB entry is handled seperately though + */ +void do_machine_check_fault(unsigned long cause, unsigned long address, + struct pt_regs *regs) +{ + die("Machine Check Exception", regs, address, cause); +} + +/* + * Entry point for traps induced by ARCompact TRAP_S insn + * This is same family as TRAP0/SWI insn (use the same vector). + * The only difference being SWI insn take no operand, while TRAP_S does + * which reflects in ECR Reg as 8 bit param. + * Thus TRAP_S can be used for specific purpose + * -1 used for software breakpointing (gdb) + * -2 used by kprobes + */ +void do_non_swi_trap(unsigned long cause, unsigned long address, + struct pt_regs *regs) +{ + unsigned int param = cause & 0xff; + + switch (param) { + case 1: + trap_is_brkpt(cause, address, regs); + break; + + default: + break; + } +} + +/* + * Entry point for Instruction Error Exception + */ +void do_insterror_or_kprobe(unsigned long cause, + unsigned long address, + struct pt_regs *regs) +{ + insterror_is_error(cause, address, regs); +} From 4adeefe161a74369e44cc8e663f240ece0470dc3 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:18 +0530 Subject: [PATCH 2274/4607] ARC: Syscall support (no-legacy-syscall ABI) This includes support for generic clone/for/vfork/execve Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Cc: Al Viro Acked-by: Arnd Bergmann --- arch/arc/Kconfig | 1 + arch/arc/include/asm/ptrace.h | 5 +++ arch/arc/include/asm/syscall.h | 72 +++++++++++++++++++++++++++++++++ arch/arc/include/asm/syscalls.h | 29 +++++++++++++ arch/arc/include/asm/unistd.h | 34 ++++++++++++++++ arch/arc/kernel/entry.S | 27 +++++++++++++ arch/arc/kernel/process.c | 42 +++++++++++++++++++ arch/arc/kernel/sys.c | 18 +++++++++ 8 files changed, 228 insertions(+) create mode 100644 arch/arc/include/asm/syscall.h create mode 100644 arch/arc/include/asm/syscalls.h create mode 100644 arch/arc/include/asm/unistd.h create mode 100644 arch/arc/kernel/process.c create mode 100644 arch/arc/kernel/sys.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index b0b09aea98ff..8789de1c7c8f 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -9,6 +9,7 @@ config ARC def_bool y select ARCH_NO_VIRT_TO_BUS + select CLONE_BACKWARDS # ARC Busybox based initramfs absolutely relies on DEVTMPFS for /dev select DEVTMPFS if !INITRAMFS_SOURCE="" select GENERIC_ATOMIC64 diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index 446e55eee7be..4c9359477ded 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h @@ -86,6 +86,11 @@ struct callee_regs { sp = -1; \ sp; \ }) + +/* return 1 if in syscall, 0 if Intr or Exception */ +#define in_syscall(regs) (((regs->orig_r8) >= 0 && \ + (regs->orig_r8 <= NR_syscalls)) ? 1 : 0) + #endif /* !__ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/arch/arc/include/asm/syscall.h b/arch/arc/include/asm/syscall.h new file mode 100644 index 000000000000..33ab3048e9b2 --- /dev/null +++ b/arch/arc/include/asm/syscall.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_SYSCALL_H +#define _ASM_ARC_SYSCALL_H 1 + +#include +#include +#include +#include /* in_syscall() */ + +static inline long +syscall_get_nr(struct task_struct *task, struct pt_regs *regs) +{ + if (user_mode(regs) && in_syscall(regs)) + return regs->orig_r8; + else + return -1; +} + +static inline void +syscall_rollback(struct task_struct *task, struct pt_regs *regs) +{ + /* XXX: I can't fathom how pt_regs->r8 will be clobbered ? */ + regs->r8 = regs->orig_r8; +} + +static inline long +syscall_get_error(struct task_struct *task, struct pt_regs *regs) +{ + /* 0 if syscall succeeded, otherwise -Errorcode */ + return IS_ERR_VALUE(regs->r0) ? regs->r0 : 0; +} + +static inline long +syscall_get_return_value(struct task_struct *task, struct pt_regs *regs) +{ + return regs->r0; +} + +static inline void +syscall_set_return_value(struct task_struct *task, struct pt_regs *regs, + int error, long val) +{ + regs->r0 = (long) error ?: val; +} + +/* + * @i: argument index [0,5] + * @n: number of arguments; n+i must be [1,6]. + */ +static inline void +syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, + unsigned int i, unsigned int n, unsigned long *args) +{ + unsigned long *inside_ptregs = &(regs->r0); + inside_ptregs -= i; + + BUG_ON((i + n) > 6); + + while (n--) { + args[i++] = (*inside_ptregs); + inside_ptregs--; + } +} + +#endif diff --git a/arch/arc/include/asm/syscalls.h b/arch/arc/include/asm/syscalls.h new file mode 100644 index 000000000000..e53a5340ba4f --- /dev/null +++ b/arch/arc/include/asm/syscalls.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_SYSCALLS_H +#define _ASM_ARC_SYSCALLS_H 1 + +#ifdef __KERNEL__ + +#include +#include +#include + +int sys_clone_wrapper(int, int, int, int, int); +int sys_fork_wrapper(void); +int sys_vfork_wrapper(void); +int sys_cacheflush(uint32_t, uint32_t uint32_t); +int sys_arc_settls(void *); +int sys_arc_gettls(void); + +#include + +#endif /* __KERNEL__ */ + +#endif diff --git a/arch/arc/include/asm/unistd.h b/arch/arc/include/asm/unistd.h new file mode 100644 index 000000000000..6f30484f34b7 --- /dev/null +++ b/arch/arc/include/asm/unistd.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + */ + +/******** no-legacy-syscalls-ABI *******/ + +#define __ARCH_WANT_SYS_EXECVE +#define __ARCH_WANT_SYS_CLONE +#define __ARCH_WANT_SYS_VFORK +#define __ARCH_WANT_SYS_FORK + +#define sys_mmap2 sys_mmap_pgoff + +#include + +#define NR_syscalls __NR_syscalls + +/* ARC specific syscall */ +#define __NR_cacheflush (__NR_arch_specific_syscall + 0) +#define __NR_arc_settls (__NR_arch_specific_syscall + 1) +#define __NR_arc_gettls (__NR_arch_specific_syscall + 2) + +__SYSCALL(__NR_cacheflush, sys_cacheflush) +__SYSCALL(__NR_arc_settls, sys_arc_settls) +__SYSCALL(__NR_arc_gettls, sys_arc_gettls) + + +/* Generic syscall (fs/filesystems.c - lost in asm-generic/unistd.h */ +#define __NR_sysfs (__NR_arch_specific_syscall + 3) +__SYSCALL(__NR_sysfs, sys_sysfs) diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index a4acc9ee1311..0b0a190547a9 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -569,3 +569,30 @@ ARC_ENTRY ret_from_fork bl @schedule_tail b @ret_from_exception ARC_EXIT ret_from_fork + +;################### Special Sys Call Wrappers ########################## + +; TBD: call do_fork directly from here +ARC_ENTRY sys_fork_wrapper + SAVE_CALLEE_SAVED_USER + bl @sys_fork + DISCARD_CALLEE_SAVED_USER + + b ret_from_system_call +ARC_EXIT sys_fork_wrapper + +ARC_ENTRY sys_vfork_wrapper + SAVE_CALLEE_SAVED_USER + bl @sys_vfork + DISCARD_CALLEE_SAVED_USER + + b ret_from_system_call +ARC_EXIT sys_vfork_wrapper + +ARC_ENTRY sys_clone_wrapper + SAVE_CALLEE_SAVED_USER + bl @sys_clone + DISCARD_CALLEE_SAVED_USER + + b ret_from_system_call +ARC_EXIT sys_clone_wrapper diff --git a/arch/arc/kernel/process.c b/arch/arc/kernel/process.c new file mode 100644 index 000000000000..4d14e5638c8c --- /dev/null +++ b/arch/arc/kernel/process.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Amit Bhor, Kanika Nema: Codito Technologies 2004 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SYSCALL_DEFINE1(arc_settls, void *, user_tls_data_ptr) +{ + task_thread_info(current)->thr_ptr = (unsigned int)user_tls_data_ptr; + return 0; +} + +/* + * We return the user space TLS data ptr as sys-call return code + * Ideally it should be copy to user. + * However we can cheat by the fact that some sys-calls do return + * absurdly high values + * Since the tls dat aptr is not going to be in range of 0xFFFF_xxxx + * it won't be considered a sys-call error + * and it will be loads better than copy-to-user, which is a definite + * D-TLB Miss + */ +SYSCALL_DEFINE0(arc_gettls) +{ + return task_thread_info(current)->thr_ptr; +} diff --git a/arch/arc/kernel/sys.c b/arch/arc/kernel/sys.c new file mode 100644 index 000000000000..f6bdd07583f3 --- /dev/null +++ b/arch/arc/kernel/sys.c @@ -0,0 +1,18 @@ + +#include +#include +#include + +#include + +#define sys_clone sys_clone_wrapper +#define sys_fork sys_fork_wrapper +#define sys_vfork sys_vfork_wrapper + +#undef __SYSCALL +#define __SYSCALL(nr, call) [nr] = (call), + +void *sys_call_table[NR_syscalls] = { + [0 ... NR_syscalls-1] = sys_ni_syscall, +#include +}; From bf90e1eab682dcb79b7765989fb65835ce9d6165 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:18 +0530 Subject: [PATCH 2275/4607] ARC: Process-creation/scheduling/idle-loop Signed-off-by: Vineet Gupta Cc: Al Viro Cc: Thomas Gleixner --- arch/arc/Kconfig | 2 + arch/arc/include/asm/arcregs.h | 20 ++++ arch/arc/include/asm/processor.h | 9 +- arch/arc/include/asm/ptrace.h | 8 ++ arch/arc/include/asm/switch_to.h | 41 +++++++ arch/arc/kernel/ctx_sw.c | 91 +++++++++++++++ arch/arc/kernel/ctx_sw_asm.S | 58 ++++++++++ arch/arc/kernel/entry.S | 15 ++- arch/arc/kernel/fpu.c | 55 +++++++++ arch/arc/kernel/process.c | 193 +++++++++++++++++++++++++++++++ 10 files changed, 484 insertions(+), 8 deletions(-) create mode 100644 arch/arc/include/asm/switch_to.h create mode 100644 arch/arc/kernel/ctx_sw.c create mode 100644 arch/arc/kernel/ctx_sw_asm.S create mode 100644 arch/arc/kernel/fpu.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 8789de1c7c8f..a4e9806ace23 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -17,6 +17,8 @@ config ARC select GENERIC_FIND_FIRST_BIT # for now, we don't need GENERIC_IRQ_PROBE, CONFIG_GENERIC_IRQ_CHIP select GENERIC_IRQ_SHOW + select GENERIC_KERNEL_EXECVE + select GENERIC_KERNEL_THREAD select GENERIC_PENDING_IRQ if SMP select GENERIC_SMP_IDLE_THREAD select HAVE_GENERIC_HARDIRQS diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index 3fccb04e6d93..d76411882481 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -47,6 +47,17 @@ #define AUX_ITRIGGER 0x40d #define AUX_IPULSE 0x415 +/* + * Floating Pt Registers + * Status regs are read-only (build-time) so need not be saved/restored + */ +#define ARC_AUX_FP_STAT 0x300 +#define ARC_AUX_DPFP_1L 0x301 +#define ARC_AUX_DPFP_1H 0x302 +#define ARC_AUX_DPFP_2L 0x303 +#define ARC_AUX_DPFP_2H 0x304 +#define ARC_AUX_DPFP_STAT 0x305 + #ifndef __ASSEMBLY__ /* @@ -110,6 +121,15 @@ #endif +#ifdef CONFIG_ARC_FPU_SAVE_RESTORE +/* These DPFP regs need to be saved/restored across ctx-sw */ +struct arc_fpu { + struct { + unsigned int l, h; + } aux_dpfp[2]; +}; +#endif + #endif /* __ASEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/arch/arc/include/asm/processor.h b/arch/arc/include/asm/processor.h index bf88cfbc9128..860252ec3fa7 100644 --- a/arch/arc/include/asm/processor.h +++ b/arch/arc/include/asm/processor.h @@ -29,6 +29,9 @@ struct thread_struct { unsigned long callee_reg; /* pointer to callee regs */ unsigned long fault_address; /* dbls as brkpt holder as well */ unsigned long cause_code; /* Exception Cause Code (ECR) */ +#ifdef CONFIG_ARC_FPU_SAVE_RESTORE + struct arc_fpu fpu; +#endif }; #define INIT_THREAD { \ @@ -54,12 +57,6 @@ unsigned long thread_saved_pc(struct task_struct *t); #define cpu_relax() do { } while (0) -/* - * Create a new kernel thread - */ - -extern int kernel_thread(int (*fn) (void *), void *arg, unsigned long flags); - #define copy_segments(tsk, mm) do { } while (0) #define release_segments(mm) do { } while (0) diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index 4c9359477ded..3afadefe335f 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h @@ -91,6 +91,14 @@ struct callee_regs { #define in_syscall(regs) (((regs->orig_r8) >= 0 && \ (regs->orig_r8 <= NR_syscalls)) ? 1 : 0) +#define current_pt_regs() \ +({ \ + /* open-coded current_thread_info() */ \ + register unsigned long sp asm ("sp"); \ + unsigned long pg_start = (sp & ~(THREAD_SIZE - 1)); \ + (struct pt_regs *)(pg_start + THREAD_SIZE - 4) - 1; \ +}) + #endif /* !__ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/arch/arc/include/asm/switch_to.h b/arch/arc/include/asm/switch_to.h new file mode 100644 index 000000000000..1b171ab5fec0 --- /dev/null +++ b/arch/arc/include/asm/switch_to.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_SWITCH_TO_H +#define _ASM_ARC_SWITCH_TO_H + +#ifndef __ASSEMBLY__ + +#include + +#ifdef CONFIG_ARC_FPU_SAVE_RESTORE + +extern void fpu_save_restore(struct task_struct *p, struct task_struct *n); +#define ARC_FPU_PREV(p, n) fpu_save_restore(p, n) +#define ARC_FPU_NEXT(t) + +#else + +#define ARC_FPU_PREV(p, n) +#define ARC_FPU_NEXT(n) + +#endif /* !CONFIG_ARC_FPU_SAVE_RESTORE */ + +struct task_struct *__switch_to(struct task_struct *p, struct task_struct *n); + +#define switch_to(prev, next, last) \ +do { \ + ARC_FPU_PREV(prev, next); \ + last = __switch_to(prev, next);\ + ARC_FPU_NEXT(next); \ + mb(); \ +} while (0) + +#endif + +#endif diff --git a/arch/arc/kernel/ctx_sw.c b/arch/arc/kernel/ctx_sw.c new file mode 100644 index 000000000000..647e37a5165e --- /dev/null +++ b/arch/arc/kernel/ctx_sw.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Vineetg: Aug 2009 + * -"C" version of lowest level context switch asm macro called by schedular + * gcc doesn't generate the dward CFI info for hand written asm, hence can't + * backtrace out of it (e.g. tasks sleeping in kernel). + * So we cheat a bit by writing almost similar code in inline-asm. + * -This is a hacky way of doing things, but there is no other simple way. + * I don't want/intend to extend unwinding code to understand raw asm + */ + +#include +#include + +struct task_struct *__sched +__switch_to(struct task_struct *prev_task, struct task_struct *next_task) +{ + unsigned int tmp; + unsigned int prev = (unsigned int)prev_task; + unsigned int next = (unsigned int)next_task; + int num_words_to_skip = 1; + + __asm__ __volatile__( + /* FP/BLINK save generated by gcc (standard function prologue */ + "st.a r13, [sp, -4] \n\t" + "st.a r14, [sp, -4] \n\t" + "st.a r15, [sp, -4] \n\t" + "st.a r16, [sp, -4] \n\t" + "st.a r17, [sp, -4] \n\t" + "st.a r18, [sp, -4] \n\t" + "st.a r19, [sp, -4] \n\t" + "st.a r20, [sp, -4] \n\t" + "st.a r21, [sp, -4] \n\t" + "st.a r22, [sp, -4] \n\t" + "st.a r23, [sp, -4] \n\t" + "st.a r24, [sp, -4] \n\t" + "st.a r25, [sp, -4] \n\t" + "sub sp, sp, %4 \n\t" /* create gutter at top */ + + /* set ksp of outgoing task in tsk->thread.ksp */ + "st.as sp, [%3, %1] \n\t" + + "sync \n\t" + + /* + * setup _current_task with incoming tsk. + * optionally, set r25 to that as well + * For SMP extra work to get to &_current_task[cpu] + * (open coded SET_CURR_TASK_ON_CPU) + */ + "st %2, [@_current_task] \n\t" + + /* get ksp of incoming task from tsk->thread.ksp */ + "ld.as sp, [%2, %1] \n\t" + + /* start loading it's CALLEE reg file */ + + "add sp, sp, %4 \n\t" /* skip gutter at top */ + + "ld.ab r25, [sp, 4] \n\t" + "ld.ab r24, [sp, 4] \n\t" + "ld.ab r23, [sp, 4] \n\t" + "ld.ab r22, [sp, 4] \n\t" + "ld.ab r21, [sp, 4] \n\t" + "ld.ab r20, [sp, 4] \n\t" + "ld.ab r19, [sp, 4] \n\t" + "ld.ab r18, [sp, 4] \n\t" + "ld.ab r17, [sp, 4] \n\t" + "ld.ab r16, [sp, 4] \n\t" + "ld.ab r15, [sp, 4] \n\t" + "ld.ab r14, [sp, 4] \n\t" + "ld.ab r13, [sp, 4] \n\t" + + /* last (ret value) = prev : although for ARC it mov r0, r0 */ + "mov %0, %3 \n\t" + + /* FP/BLINK restore generated by gcc (standard func epilogue */ + + : "=r"(tmp) + : "n"((TASK_THREAD + THREAD_KSP) / 4), "r"(next), "r"(prev), + "n"(num_words_to_skip * 4) + : "blink" + ); + + return (struct task_struct *)tmp; +} diff --git a/arch/arc/kernel/ctx_sw_asm.S b/arch/arc/kernel/ctx_sw_asm.S new file mode 100644 index 000000000000..d8972345e4c2 --- /dev/null +++ b/arch/arc/kernel/ctx_sw_asm.S @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Vineetg: Aug 2009 + * -Moved core context switch macro out of entry.S into this file. + * -This is the more "natural" hand written assembler + */ + +#include /* For the SAVE_* macros */ +#include +#include + +;################### Low Level Context Switch ########################## + + .section .sched.text,"ax",@progbits + .align 4 + .global __switch_to + .type __switch_to, @function +__switch_to: + + /* Save regs on kernel mode stack of task */ + st.a blink, [sp, -4] + st.a fp, [sp, -4] + SAVE_CALLEE_SAVED_KERNEL + + /* Save the now KSP in task->thread.ksp */ + st.as sp, [r0, (TASK_THREAD + THREAD_KSP)/4] + + /* + * Return last task in r0 (return reg) + * On ARC, Return reg = First Arg reg = r0. + * Since we already have last task in r0, + * don't need to do anything special to return it + */ + + /* hardware memory barrier */ + sync + + /* + * switch to new task, contained in r1 + * Temp reg r3 is required to get the ptr to store val + */ + SET_CURR_TASK_ON_CPU r1, r3 + + /* reload SP with kernel mode stack pointer in task->thread.ksp */ + ld.as sp, [r1, (TASK_THREAD + THREAD_KSP)/4] + + /* restore the registers */ + RESTORE_CALLEE_SAVED_KERNEL + ld.ab fp, [sp, 4] + ld.ab blink, [sp, 4] + j [blink] + +ARC_EXIT __switch_to diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 0b0a190547a9..ed08ac14fbc4 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -566,8 +566,19 @@ ARC_ENTRY ret_from_fork ; when the forked child comes here from the __switch_to function ; r0 has the last task pointer. ; put last task in scheduler queue - bl @schedule_tail - b @ret_from_exception + bl @schedule_tail + + ; If kernel thread, jump to it's entry-point + ld r9, [sp, PT_status32] + brne r9, 0, 1f + + jl.d [r14] + mov r0, r13 ; arg to payload + +1: + ; special case of kernel_thread entry point returning back due to + ; kernel_execve() - pretend return from syscall to ret to userland + b ret_from_exception ARC_EXIT ret_from_fork ;################### Special Sys Call Wrappers ########################## diff --git a/arch/arc/kernel/fpu.c b/arch/arc/kernel/fpu.c new file mode 100644 index 000000000000..f352e512cbd1 --- /dev/null +++ b/arch/arc/kernel/fpu.c @@ -0,0 +1,55 @@ +/* + * fpu.c - save/restore of Floating Point Unit Registers on task switch + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include + +/* + * To save/restore FPU regs, simplest scheme would use LR/SR insns. + * However since SR serializes the pipeline, an alternate "hack" can be used + * which uses the FPU Exchange insn (DEXCL) to r/w FPU regs. + * + * Store to 64bit dpfp1 reg from a pair of core regs: + * dexcl1 0, r1, r0 ; where r1:r0 is the 64 bit val + * + * Read from dpfp1 into pair of core regs (w/o clobbering dpfp1) + * mov_s r3, 0 + * daddh11 r1, r3, r3 ; get "hi" into r1 (dpfp1 unchanged) + * dexcl1 r0, r1, r3 ; get "low" into r0 (dpfp1 low clobbered) + * dexcl1 0, r1, r0 ; restore dpfp1 to orig value + * + * However we can tweak the read, so that read-out of outgoing task's FPU regs + * and write of incoming task's regs happen in one shot. So all the work is + * done before context switch + */ + +void fpu_save_restore(struct task_struct *prev, struct task_struct *next) +{ + unsigned int *saveto = &prev->thread.fpu.aux_dpfp[0].l; + unsigned int *readfrom = &next->thread.fpu.aux_dpfp[0].l; + + const unsigned int zero = 0; + + __asm__ __volatile__( + "daddh11 %0, %2, %2\n" + "dexcl1 %1, %3, %4\n" + : "=&r" (*(saveto + 1)), /* early clobber must here */ + "=&r" (*(saveto)) + : "r" (zero), "r" (*(readfrom + 1)), "r" (*(readfrom)) + ); + + __asm__ __volatile__( + "daddh22 %0, %2, %2\n" + "dexcl2 %1, %3, %4\n" + : "=&r"(*(saveto + 3)), /* early clobber must here */ + "=&r"(*(saveto + 2)) + : "r" (zero), "r" (*(readfrom + 3)), "r" (*(readfrom + 2)) + ); +} diff --git a/arch/arc/kernel/process.c b/arch/arc/kernel/process.c index 4d14e5638c8c..279e080b6b54 100644 --- a/arch/arc/kernel/process.c +++ b/arch/arc/kernel/process.c @@ -40,3 +40,196 @@ SYSCALL_DEFINE0(arc_gettls) { return task_thread_info(current)->thr_ptr; } + +static inline void arch_idle(void) +{ + /* sleep, but enable all interrupts before committing */ + __asm__("sleep 0x3"); +} + +void cpu_idle(void) +{ + /* Since we SLEEP in idle loop, TIF_POLLING_NRFLAG can't be set */ + + /* endless idle loop with no priority at all */ + while (1) { + tick_nohz_idle_enter(); + rcu_idle_enter(); + +doze: + local_irq_disable(); + if (!need_resched()) { + arch_idle(); + goto doze; + } else { + local_irq_enable(); + } + + rcu_idle_exit(); + tick_nohz_idle_exit(); + + schedule_preempt_disabled(); + } +} + +asmlinkage void ret_from_fork(void); + +/* Layout of Child kernel mode stack as setup at the end of this function is + * + * | ... | + * | ... | + * | unused | + * | | + * ------------------ <==== top of Stack (thread.ksp) + * | UNUSED 1 word| + * ------------------ + * | r25 | + * ~ ~ + * | --to-- | (CALLEE Regs of user mode) + * | r13 | + * ------------------ + * | fp | + * | blink | @ret_from_fork + * ------------------ + * | | + * ~ ~ + * ~ ~ + * | | + * ------------------ + * | r12 | + * ~ ~ + * | --to-- | (scratch Regs of user mode) + * | r0 | + * ------------------ + * | UNUSED 1 word| + * ------------------ <===== END of PAGE + */ +int copy_thread(unsigned long clone_flags, + unsigned long usp, unsigned long arg, + struct task_struct *p) +{ + struct pt_regs *c_regs; /* child's pt_regs */ + unsigned long *childksp; /* to unwind out of __switch_to() */ + struct callee_regs *c_callee; /* child's callee regs */ + struct callee_regs *parent_callee; /* paren't callee */ + struct pt_regs *regs = current_pt_regs(); + + /* Mark the specific anchors to begin with (see pic above) */ + c_regs = task_pt_regs(p); + childksp = (unsigned long *)c_regs - 2; /* 2 words for FP/BLINK */ + c_callee = ((struct callee_regs *)childksp) - 1; + + /* + * __switch_to() uses thread.ksp to start unwinding stack + * For kernel threads we don't need to create callee regs, the + * stack layout nevertheless needs to remain the same. + * Also, since __switch_to anyways unwinds callee regs, we use + * this to populate kernel thread entry-pt/args into callee regs, + * so that ret_from_kernel_thread() becomes simpler. + */ + p->thread.ksp = (unsigned long)c_callee; /* THREAD_KSP */ + + /* __switch_to expects FP(0), BLINK(return addr) at top */ + childksp[0] = 0; /* fp */ + childksp[1] = (unsigned long)ret_from_fork; /* blink */ + + if (unlikely(p->flags & PF_KTHREAD)) { + memset(c_regs, 0, sizeof(struct pt_regs)); + + c_callee->r13 = arg; /* argument to kernel thread */ + c_callee->r14 = usp; /* function */ + + return 0; + } + + /*--------- User Task Only --------------*/ + + /* __switch_to expects FP(0), BLINK(return addr) at top of stack */ + childksp[0] = 0; /* for POP fp */ + childksp[1] = (unsigned long)ret_from_fork; /* for POP blink */ + + /* Copy parents pt regs on child's kernel mode stack */ + *c_regs = *regs; + + if (usp) + c_regs->sp = usp; + + c_regs->r0 = 0; /* fork returns 0 in child */ + + parent_callee = ((struct callee_regs *)regs) - 1; + *c_callee = *parent_callee; + + if (unlikely(clone_flags & CLONE_SETTLS)) { + /* + * set task's userland tls data ptr from 4th arg + * clone C-lib call is difft from clone sys-call + */ + task_thread_info(p)->thr_ptr = regs->r3; + } else { + /* Normal fork case: set parent's TLS ptr in child */ + task_thread_info(p)->thr_ptr = + task_thread_info(current)->thr_ptr; + } + + return 0; +} + +/* + * Some archs flush debug and FPU info here + */ +void flush_thread(void) +{ +} + +/* + * Free any architecture-specific thread data structures, etc. + */ +void exit_thread(void) +{ +} + +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) +{ + return 0; +} + +/* + * API: expected by schedular Code: If thread is sleeping where is that. + * What is this good for? it will be always the scheduler or ret_from_fork. + * So we hard code that anyways. + */ +unsigned long thread_saved_pc(struct task_struct *t) +{ + struct pt_regs *regs = task_pt_regs(t); + unsigned long blink = 0; + + /* + * If the thread being queried for in not itself calling this, then it + * implies it is not executing, which in turn implies it is sleeping, + * which in turn implies it got switched OUT by the schedular. + * In that case, it's kernel mode blink can reliably retrieved as per + * the picture above (right above pt_regs). + */ + if (t != current && t->state != TASK_RUNNING) + blink = *((unsigned int *)regs - 1); + + return blink; +} + +int elf_check_arch(const struct elf32_hdr *x) +{ + unsigned int eflags; + + if (x->e_machine != EM_ARCOMPACT) + return 0; + + eflags = x->e_flags; + if ((eflags & EF_ARC_OSABI_MSK) < EF_ARC_OSABI_V2) { + pr_err("ABI mismatch - you need newer toolchain\n"); + force_sigsegv(SIGSEGV, current); + return 0; + } + + return 1; +} +EXPORT_SYMBOL(elf_check_arch); From d8005e6b95268cbb50db3773d5f180c32a9434fe Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:18 +0530 Subject: [PATCH 2276/4607] ARC: Timers/counters/delay management ARC700 includes 2 in-core 32bit timers TIMER0 and TIMER1. Both have exactly same capabilies. * programmable to count from TIMER_CNT to TIMER_LIMIT * for count 0 and LIMIT ~1, provides a free-running counter by auto-wrapping when limit is reached. * optionally interrupt when LIMIT is reached (oneshot event semantics) * rearming the interrupt provides periodic semantics * run at CPU clk ARC Linux uses TIMER0 for clockevent (periodic/oneshot) and TIMER1 for clocksource (free-running clock). Newer cores provide RTSC insn which gives a 64bit cpu clk snapshot hence is more apt for clocksource when available. SMP poses a bit of challenge for global timekeeping clocksource / sched_clock() backend: -TIMER1 based local clocks are out-of-sync hence can't be used (thus we default to jiffies based cs as well as sched_clock() one/both of which platform can override with it's specific hardware assist) -RTSC is only allowed in SMP if it's cross-core-sync (Kconfig glue ensures that) and thus usable for both requirements. Signed-off-by: Vineet Gupta Cc: Thomas Gleixner --- arch/arc/include/asm/arcregs.h | 11 ++ arch/arc/include/asm/clk.h | 20 +++ arch/arc/include/asm/delay.h | 68 ++++++++ arch/arc/include/asm/irq.h | 2 + arch/arc/include/asm/timex.h | 18 ++ arch/arc/kernel/clk.c | 11 ++ arch/arc/kernel/time.c | 295 +++++++++++++++++++++++++++++++++ 7 files changed, 425 insertions(+) create mode 100644 arch/arc/include/asm/clk.h create mode 100644 arch/arc/include/asm/delay.h create mode 100644 arch/arc/include/asm/timex.h create mode 100644 arch/arc/kernel/clk.c create mode 100644 arch/arc/kernel/time.c diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index d76411882481..5131bb3d4fcd 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -47,6 +47,17 @@ #define AUX_ITRIGGER 0x40d #define AUX_IPULSE 0x415 +/* Timer related Aux registers */ +#define ARC_REG_TIMER0_LIMIT 0x23 /* timer 0 limit */ +#define ARC_REG_TIMER0_CTRL 0x22 /* timer 0 control */ +#define ARC_REG_TIMER0_CNT 0x21 /* timer 0 count */ +#define ARC_REG_TIMER1_LIMIT 0x102 /* timer 1 limit */ +#define ARC_REG_TIMER1_CTRL 0x101 /* timer 1 control */ +#define ARC_REG_TIMER1_CNT 0x100 /* timer 1 count */ + +#define TIMER_CTRL_IE (1 << 0) /* Interupt when Count reachs limit */ +#define TIMER_CTRL_NH (1 << 1) /* Count only when CPU NOT halted */ + /* * Floating Pt Registers * Status regs are read-only (build-time) so need not be saved/restored diff --git a/arch/arc/include/asm/clk.h b/arch/arc/include/asm/clk.h new file mode 100644 index 000000000000..61956436c75c --- /dev/null +++ b/arch/arc/include/asm/clk.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_CLK_H +#define _ASM_ARC_CLK_H + +/* Although we can't really hide core_freq, the accessor is still better way */ +extern unsigned long core_freq; + +static inline unsigned long arc_get_core_freq(void) +{ + return core_freq; +} + +#endif diff --git a/arch/arc/include/asm/delay.h b/arch/arc/include/asm/delay.h new file mode 100644 index 000000000000..442ce5d0f709 --- /dev/null +++ b/arch/arc/include/asm/delay.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Delay routines using pre computed loops_per_jiffy value. + * + * vineetg: Feb 2012 + * -Rewrote in "C" to avoid dealing with availability of H/w MPY + * -Also reduced the num of MPY operations from 3 to 2 + * + * Amit Bhor: Codito Technologies 2004 + */ + +#ifndef __ASM_ARC_UDELAY_H +#define __ASM_ARC_UDELAY_H + +#include /* HZ */ + +static inline void __delay(unsigned long loops) +{ + __asm__ __volatile__( + "1: sub.f %0, %0, 1 \n" + " jpnz 1b \n" + : "+r"(loops) + : + : "cc"); +} + +extern void __bad_udelay(void); + +/* + * Normal Math for computing loops in "N" usecs + * -we have precomputed @loops_per_jiffy + * -1 sec has HZ jiffies + * loops per "N" usecs = ((loops_per_jiffy * HZ / 1000000) * N) + * + * Approximate Division by multiplication: + * -Mathematically if we multiply and divide a number by same value the + * result remains unchanged: In this case, we use 2^32 + * -> (loops_per_N_usec * 2^32 ) / 2^32 + * -> (((loops_per_jiffy * HZ / 1000000) * N) * 2^32) / 2^32 + * -> (loops_per_jiffy * HZ * N * 4295) / 2^32 + * + * -Divide by 2^32 is very simply right shift by 32 + * -We simply need to ensure that the multiply per above eqn happens in + * 64-bit precision (if CPU doesn't support it - gcc can emaulate it) + */ + +static inline void __udelay(unsigned long usecs) +{ + unsigned long loops; + + /* (long long) cast ensures 64 bit MPY - real or emulated + * HZ * 4295 is pre-evaluated by gcc - hence only 2 mpy ops + */ + loops = ((long long)(usecs * 4295 * HZ) * + (long long)(loops_per_jiffy)) >> 32; + + __delay(loops); +} + +#define udelay(n) (__builtin_constant_p(n) ? ((n) > 20000 ? __bad_udelay() \ + : __udelay(n)) : __udelay(n)) + +#endif /* __ASM_ARC_UDELAY_H */ diff --git a/arch/arc/include/asm/irq.h b/arch/arc/include/asm/irq.h index ca4aeba1eed3..514b30287445 100644 --- a/arch/arc/include/asm/irq.h +++ b/arch/arc/include/asm/irq.h @@ -19,4 +19,6 @@ extern void __init arc_init_IRQ(void); extern void __init plat_init_IRQ(void); extern int __init get_hw_config_num_irq(void); +void __cpuinit arc_local_timer_setup(unsigned int cpu); + #endif diff --git a/arch/arc/include/asm/timex.h b/arch/arc/include/asm/timex.h new file mode 100644 index 000000000000..0a82960a75e9 --- /dev/null +++ b/arch/arc/include/asm/timex.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_TIMEX_H +#define _ASM_ARC_TIMEX_H + +#define CLOCK_TICK_RATE 80000000 /* slated to be removed */ + +#include + +/* XXX: get_cycles() to be implemented with RTSC insn */ + +#endif /* _ASM_ARC_TIMEX_H */ diff --git a/arch/arc/kernel/clk.c b/arch/arc/kernel/clk.c new file mode 100644 index 000000000000..64925db9eb5e --- /dev/null +++ b/arch/arc/kernel/clk.c @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 + +unsigned long core_freq = CONFIG_ARC_PLAT_CLK; diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c new file mode 100644 index 000000000000..05dba11fdb2d --- /dev/null +++ b/arch/arc/kernel/time.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: Jan 1011 + * -sched_clock( ) no longer jiffies based. Uses the same clocksource + * as gtod + * + * Rajeshwarr/Vineetg: Mar 2008 + * -Implemented CONFIG_GENERIC_TIME (rather deleted arch specific code) + * for arch independent gettimeofday() + * -Implemented CONFIG_GENERIC_CLOCKEVENTS as base for hrtimers + * + * Vineetg: Mar 2008: Forked off from time.c which now is time-jiff.c + */ + +/* ARC700 has two 32bit independent prog Timers: TIMER0 and TIMER1 + * Each can programmed to go from @count to @limit and optionally + * interrupt when that happens. + * A write to Control Register clears the Interrupt + * + * We've designated TIMER0 for events (clockevents) + * while TIMER1 for free running (clocksource) + * + * Newer ARC700 cores have 64bit clk fetching RTSC insn, preferred over TIMER1 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ARC_TIMER_MAX 0xFFFFFFFF + +/********** Clock Source Device *********/ + +#ifdef CONFIG_ARC_HAS_RTSC + +int __cpuinit arc_counter_setup(void) +{ + /* RTSC insn taps into cpu clk, needs no setup */ + + /* For SMP, only allowed if cross-core-sync, hence usable as cs */ + return 1; +} + +static cycle_t arc_counter_read(struct clocksource *cs) +{ + unsigned long flags; + union { +#ifdef CONFIG_CPU_BIG_ENDIAN + struct { u32 high, low; }; +#else + struct { u32 low, high; }; +#endif + cycle_t full; + } stamp; + + flags = arch_local_irq_save(); + + __asm__ __volatile( + " .extCoreRegister tsch, 58, r, cannot_shortcut \n" + " rtsc %0, 0 \n" + " mov %1, tsch \n" /* TSCH is extn core reg 58 */ + : "=r" (stamp.low), "=r" (stamp.high)); + + arch_local_irq_restore(flags); + + return stamp.full; +} + +static struct clocksource arc_counter = { + .name = "ARC RTSC", + .rating = 300, + .read = arc_counter_read, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +#else /* !CONFIG_ARC_HAS_RTSC */ + +static bool is_usable_as_clocksource(void) +{ +#ifdef CONFIG_SMP + return 0; +#else + return 1; +#endif +} + +/* + * set 32bit TIMER1 to keep counting monotonically and wraparound + */ +int __cpuinit arc_counter_setup(void) +{ + write_aux_reg(ARC_REG_TIMER1_LIMIT, ARC_TIMER_MAX); + write_aux_reg(ARC_REG_TIMER1_CNT, 0); + write_aux_reg(ARC_REG_TIMER1_CTRL, TIMER_CTRL_NH); + + return is_usable_as_clocksource(); +} + +static cycle_t arc_counter_read(struct clocksource *cs) +{ + return (cycle_t) read_aux_reg(ARC_REG_TIMER1_CNT); +} + +static struct clocksource arc_counter = { + .name = "ARC Timer1", + .rating = 300, + .read = arc_counter_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +#endif + +/********** Clock Event Device *********/ + +/* + * Arm the timer to interrupt after @limit cycles + * The distinction for oneshot/periodic is done in arc_event_timer_ack() below + */ +static void arc_timer_event_setup(unsigned int limit) +{ + write_aux_reg(ARC_REG_TIMER0_LIMIT, limit); + write_aux_reg(ARC_REG_TIMER0_CNT, 0); /* start from 0 */ + + write_aux_reg(ARC_REG_TIMER0_CTRL, TIMER_CTRL_IE | TIMER_CTRL_NH); +} + +/* + * Acknowledge the interrupt (oneshot) and optionally re-arm it (periodic) + * -Any write to CTRL Reg will ack the intr (NH bit: Count when not halted) + * -Rearming is done by setting the IE bit + * + * Small optimisation: Normal code would have been + * if (irq_reenable) + * CTRL_REG = (IE | NH); + * else + * CTRL_REG = NH; + * However since IE is BIT0 we can fold the branch + */ +static void arc_timer_event_ack(unsigned int irq_reenable) +{ + write_aux_reg(ARC_REG_TIMER0_CTRL, irq_reenable | TIMER_CTRL_NH); +} + +static int arc_clkevent_set_next_event(unsigned long delta, + struct clock_event_device *dev) +{ + arc_timer_event_setup(delta); + return 0; +} + +static void arc_clkevent_set_mode(enum clock_event_mode mode, + struct clock_event_device *dev) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + arc_timer_event_setup(arc_get_core_freq() / HZ); + break; + case CLOCK_EVT_MODE_ONESHOT: + break; + default: + break; + } + + return; +} + +static DEFINE_PER_CPU(struct clock_event_device, arc_clockevent_device) = { + .name = "ARC Timer0", + .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, + .mode = CLOCK_EVT_MODE_UNUSED, + .rating = 300, + .irq = TIMER0_IRQ, /* hardwired, no need for resources */ + .set_next_event = arc_clkevent_set_next_event, + .set_mode = arc_clkevent_set_mode, +}; + +static irqreturn_t timer_irq_handler(int irq, void *dev_id) +{ + struct clock_event_device *clk = &__get_cpu_var(arc_clockevent_device); + + arc_timer_event_ack(clk->mode == CLOCK_EVT_MODE_PERIODIC); + clk->event_handler(clk); + return IRQ_HANDLED; +} + +static struct irqaction arc_timer_irq = { + .name = "Timer0 (clock-evt-dev)", + .flags = IRQF_TIMER | IRQF_PERCPU, + .handler = timer_irq_handler, +}; + +/* + * Setup the local event timer for @cpu + * N.B. weak so that some exotic ARC SoCs can completely override it + */ +void __attribute__((weak)) __cpuinit arc_local_timer_setup(unsigned int cpu) +{ + struct clock_event_device *clk = &per_cpu(arc_clockevent_device, cpu); + + clockevents_calc_mult_shift(clk, arc_get_core_freq(), 5); + + clk->max_delta_ns = clockevent_delta2ns(ARC_TIMER_MAX, clk); + clk->cpumask = cpumask_of(cpu); + + clockevents_register_device(clk); + + /* + * setup the per-cpu timer IRQ handler - for all cpus + * For non boot CPU explicitly unmask at intc + * setup_irq() -> .. -> irq_startup() already does this on boot-cpu + */ + if (!cpu) + setup_irq(TIMER0_IRQ, &arc_timer_irq); + else + arch_unmask_irq(TIMER0_IRQ); +} + +/* + * Called from start_kernel() - boot CPU only + * + * -Sets up h/w timers as applicable on boot cpu + * -Also sets up any global state needed for timer subsystem: + * - for "counting" timer, registers a clocksource, usable across CPUs + * (provided that underlying counter h/w is synchronized across cores) + * - for "event" timer, sets up TIMER0 IRQ (as that is platform agnostic) + */ +void __init time_init(void) +{ + /* + * sets up the timekeeping free-flowing counter which also returns + * whether the counter is usable as clocksource + */ + if (arc_counter_setup()) + /* + * CLK upto 4.29 GHz can be safely represented in 32 bits + * because Max 32 bit number is 4,294,967,295 + */ + clocksource_register_hz(&arc_counter, arc_get_core_freq()); + + /* sets up the periodic event timer */ + arc_local_timer_setup(smp_processor_id()); +} + +#ifdef CONFIG_ARC_HAS_RTSC +/* + * sched_clock math assist + * ns = cycles * (ns_per_sec / cpu_freq_hz) + * ns = cycles * (10^6 / cpu_freq_khz) + * ns = cycles * (10^6 * 2^SF / cpu_freq_khz) / 2^SF + * ns = cycles * cyc2ns_scale >> SF + */ +#define CYC2NS_SF 10 /* 2^10, carefully chosen */ +#define CYC2NS_SCALE ((1000000 << CYC2NS_SF) / (arc_get_core_freq() / 1000)) + +static unsigned long long cycles2ns(unsigned long long cyc) +{ + return (cyc * CYC2NS_SCALE ) >> CYC2NS_SF; +} + +/* + * Scheduler clock - a monotonically increasing clock in nanosec units. + * It's return value must NOT wrap around. + * + * - Since 32bit TIMER1 will overflow almost immediately (53sec @ 80MHz), it + * can't be used directly. + * - Using getrawmonotonic (TIMER1 based, but with state for last + current + * snapshots), is no-good either because of seqlock deadlock possibilities + * - So only with native 64bit timer we do this, otherwise fallback to generic + * jiffies based version - which despite not being fine grained gaurantees + * the monotonically increasing semantics. + */ +unsigned long long sched_clock(void) +{ + return cycles2ns(arc_counter_read(NULL)); +} +#endif From b08369a174a183e88baa98ab5e3566a617a3a7f8 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 28 Jan 2013 10:44:12 +0100 Subject: [PATCH 2277/4607] i2c: isch: Add module parameter for backbone clock rate if divider is unset It was observed the Host Clock Divider was not written by the driver. It was still set to (default) 0, if not already set by BIOS, which caused garbage on SMBus. This driver adds a parameters which is used to calculate the divider appropriately for a default bitrate of 100 KHz. This new divider is only applied if the clock divider is still default 0. Signed-off-by: Alexander Stein Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-isch.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c index 4099f79c2280..8c38aaa7417c 100644 --- a/drivers/i2c/busses/i2c-isch.c +++ b/drivers/i2c/busses/i2c-isch.c @@ -40,6 +40,7 @@ /* SCH SMBus address offsets */ #define SMBHSTCNT (0 + sch_smba) #define SMBHSTSTS (1 + sch_smba) +#define SMBHSTCLK (2 + sch_smba) #define SMBHSTADD (4 + sch_smba) /* TSA */ #define SMBHSTCMD (5 + sch_smba) #define SMBHSTDAT0 (6 + sch_smba) @@ -58,6 +59,9 @@ static unsigned short sch_smba; static struct i2c_adapter sch_adapter; +static int backbone_speed = 33000; /* backbone speed in kHz */ +module_param(backbone_speed, int, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(backbone_speed, "Backbone speed in kHz, (default = 33000)"); /* * Start the i2c transaction -- the i2c_access will prepare the transaction @@ -156,6 +160,19 @@ static s32 sch_access(struct i2c_adapter *adap, u16 addr, dev_dbg(&sch_adapter.dev, "SMBus busy (%02x)\n", temp); return -EAGAIN; } + temp = inw(SMBHSTCLK); + if (!temp) { + /* + * We can't determine if we have 33 or 25 MHz clock for + * SMBus, so expect 33 MHz and calculate a bus clock of + * 100 kHz. If we actually run at 25 MHz the bus will be + * run ~75 kHz instead which should do no harm. + */ + dev_notice(&sch_adapter.dev, + "Clock divider unitialized. Setting defaults\n"); + outw(backbone_speed / (4 * 100), SMBHSTCLK); + } + dev_dbg(&sch_adapter.dev, "access size: %d %s\n", size, (read_write)?"READ":"WRITE"); switch (size) { From 974d6a3797001c88e59ccb78567c6d71ac526c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Decr=C3=AAme?= Date: Mon, 28 Jan 2013 22:21:05 +0100 Subject: [PATCH 2278/4607] i2c: sis630: Add SIS964 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-sis630 | 9 +++ drivers/i2c/busses/Kconfig | 4 +- drivers/i2c/busses/i2c-sis630.c | 86 +++++++++++++++++++---------- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/Documentation/i2c/busses/i2c-sis630 b/Documentation/i2c/busses/i2c-sis630 index 0b9697366930..ee7943631074 100644 --- a/Documentation/i2c/busses/i2c-sis630 +++ b/Documentation/i2c/busses/i2c-sis630 @@ -4,9 +4,11 @@ Supported adapters: * Silicon Integrated Systems Corp (SiS) 630 chipset (Datasheet: available at http://www.sfr-fresh.com/linux) 730 chipset + 964 chipset * Possible other SiS chipsets ? Author: Alexander Malysh + Amaury Decrême - SiS964 support Module Parameters ----------------- @@ -18,6 +20,7 @@ Module Parameters * high_clock = [1|0] Forcibly set Host Master Clock to 56KHz (default, what your BIOS use). DANGEROUS! This should be a bit faster, but freeze some systems (i.e. my Laptop). + SIS630/730 chip only. Description @@ -36,6 +39,12 @@ or like this: 00:00.0 Host bridge: Silicon Integrated Systems [SiS] 730 Host (rev 02) 00:01.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513 +or like this: + +00:00.0 Host bridge: Silicon Integrated Systems [SiS] 760/M760 Host (rev 02) +00:02.0 ISA bridge: Silicon Integrated Systems [SiS] SiS964 [MuTIOL Media IO] + LPC Controller (rev 36) + in your 'lspci' output , then this driver is for your chipset. Thank You diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 87df863ba110..74e18bc0bf62 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -197,11 +197,11 @@ config I2C_SIS5595 will be called i2c-sis5595. config I2C_SIS630 - tristate "SiS 630/730" + tristate "SiS 630/730/964" depends on PCI help If you say yes to this option, support will be included for the - SiS630 and SiS730 SMBus (a subset of I2C) interface. + SiS630, SiS730 and SiS964 SMBus (a subset of I2C) interface. This driver can also be built as a module. If so, the module will be called i2c-sis630. diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index de6dddb9f865..df8e20ab3a09 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -41,6 +41,20 @@ Supports: SIS 630 SIS 730 + SIS 964 + + Notable differences between chips: + +------------------------+--------------------+-------------------+ + | | SIS630/730 | SIS964 | + +------------------------+--------------------+-------------------+ + | Clock | 14kHz/56kHz | 55.56kHz/27.78kHz | + | SMBus registers offset | 0x80 | 0xE0 | + | SMB_CNT | Bit 1 = Slave Busy | Bit 1 = Bus probe | + | (not used yet) | Bit 3 is reserved | Bit 3 = Last byte | + | SMB_PCOUNT | Offset + 0x06 | Offset + 0x14 | + | SMB_COUNT | 4:0 bits | 5:0 bits | + +------------------------+--------------------+-------------------+ + (Other differences don't affect the functions provided by the driver) Note: we assume there can only be one device, with one SMBus interface. */ @@ -55,22 +69,21 @@ #include #include -/* SIS630 SMBus registers */ -#define SMB_STS 0x80 /* status */ -#define SMB_EN 0x81 /* status enable */ -#define SMB_CNT 0x82 -#define SMBHOST_CNT 0x83 -#define SMB_ADDR 0x84 -#define SMB_CMD 0x85 -#define SMB_PCOUNT 0x86 /* processed count */ -#define SMB_COUNT 0x87 -#define SMB_BYTE 0x88 /* ~0x8F data byte field */ -#define SMBDEV_ADDR 0x90 -#define SMB_DB0 0x91 -#define SMB_DB1 0x92 -#define SMB_SAA 0x93 +/* SIS964 id is defined here as we are the only file using it */ +#define PCI_DEVICE_ID_SI_964 0x0964 -/* register count for request_region */ +/* SIS630/730/964 SMBus registers */ +#define SMB_STS 0x00 /* status */ +#define SMB_CNT 0x02 /* control */ +#define SMBHOST_CNT 0x03 /* host control */ +#define SMB_ADDR 0x04 /* address */ +#define SMB_CMD 0x05 /* command */ +#define SMB_COUNT 0x07 /* byte count */ +#define SMB_BYTE 0x08 /* ~0x8F data byte field */ + +/* register count for request_region + * As we don't use SMB_PCOUNT, 20 is ok for SiS630 and SiS964 + */ #define SIS630_SMB_IOREGION 20 /* PCI address constants */ @@ -96,28 +109,30 @@ static struct pci_driver sis630_driver; static bool high_clock; static bool force; module_param(high_clock, bool, 0); -MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz)."); +MODULE_PARM_DESC(high_clock, + "Set Host Master Clock to 56KHz (default 14KHz) (SIS630/730 only)."); module_param(force, bool, 0); MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!"); -/* acpi base address */ -static unsigned short acpi_base; +/* SMBus base adress */ +static unsigned short smbus_base; /* supported chips */ static int supported[] = { PCI_DEVICE_ID_SI_630, PCI_DEVICE_ID_SI_730, + PCI_DEVICE_ID_SI_760, 0 /* terminates the list */ }; static inline u8 sis630_read(u8 reg) { - return inb(acpi_base + reg); + return inb(smbus_base + reg); } static inline void sis630_write(u8 reg, u8 data) { - outb(data, acpi_base + reg); + outb(data, smbus_base + reg); } static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock) @@ -394,6 +409,8 @@ static int sis630_setup(struct pci_dev *sis630_dev) unsigned char b; struct pci_dev *dummy = NULL; int retval, i; + /* acpi base address */ + unsigned short acpi_base; /* check for supported SiS devices */ for (i=0; supported[i] > 0 ; i++) { @@ -438,16 +455,25 @@ static int sis630_setup(struct pci_dev *sis630_dev) dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base); - retval = acpi_check_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, + if (supported[i] == PCI_DEVICE_ID_SI_760) + smbus_base = acpi_base + 0xE0; + else + smbus_base = acpi_base + 0x80; + + dev_dbg(&sis630_dev->dev, "SMBus base at 0x%04hx\n", smbus_base); + + retval = acpi_check_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION, sis630_driver.name); if (retval) goto exit; /* Everything is happy, let's grab the memory and set things up. */ - if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, + if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION, sis630_driver.name)) { - dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already " - "in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA); + dev_err(&sis630_dev->dev, + "I/O Region 0x%04hx-0x%04hx for SMBus already in use.\n", + smbus_base + SMB_STS, + smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1); retval = -EBUSY; goto exit; } @@ -456,7 +482,7 @@ static int sis630_setup(struct pci_dev *sis630_dev) exit: if (retval) - acpi_base = 0; + smbus_base = 0; return retval; } @@ -470,11 +496,13 @@ static struct i2c_adapter sis630_adapter = { .owner = THIS_MODULE, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, + .retries = 3 }; static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) }, + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_964) }, { 0, } }; @@ -491,17 +519,17 @@ static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id) sis630_adapter.dev.parent = &dev->dev; snprintf(sis630_adapter.name, sizeof(sis630_adapter.name), - "SMBus SIS630 adapter at %04x", acpi_base + SMB_STS); + "SMBus SIS630 adapter at %04hx", smbus_base + SMB_STS); return i2c_add_adapter(&sis630_adapter); } static void sis630_remove(struct pci_dev *dev) { - if (acpi_base) { + if (smbus_base) { i2c_del_adapter(&sis630_adapter); - release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION); - acpi_base = 0; + release_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION); + smbus_base = 0; } } From aa9e7a39c5a5a77ff02670ef915f4c6712bc7658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Decr=C3=AAme?= Date: Mon, 28 Jan 2013 22:21:06 +0100 Subject: [PATCH 2279/4607] i2c: sis630: clear sticky bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sticky bits must be cleared at the end of the transaction by writing a 1 to all fields. Datasheet: SMBus Status (SMB_STS) The following registers are all sticky bits and only can be cleared by writing a one to their corresponding fields. Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sis630.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index df8e20ab3a09..3124d807c05a 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -213,10 +213,8 @@ static int sis630_transaction_wait(struct i2c_adapter *adap, int size) static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock) { - int temp = 0; - /* clear all status "sticky" bits */ - sis630_write(SMB_STS, temp); + sis630_write(SMB_STS, 0xFF); dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT)); From 499b9194ad7b7b6d7c06b01005508e5bcf3c8980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Decr=C3=AAme?= Date: Mon, 28 Jan 2013 22:21:07 +0100 Subject: [PATCH 2280/4607] i2c: sis630: fix behavior after collision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Datasheet on collision: SMBus Collision (SMBCOL_STS) This bit is set when a SMBus Collision condition occurs and SMBus Host loses in the bus arbitration. The software should clear this bit and re-start SMBus operation. As the status will be cleared in transaction_end, we can remove the sis630_write and prepare to return -EAGAIN to retry. Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sis630.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index 3124d807c05a..e152d36b94ac 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -200,12 +200,7 @@ static int sis630_transaction_wait(struct i2c_adapter *adap, int size) if (temp & 0x04) { dev_err(&adap->dev, "Bus collision!\n"); - result = -EIO; - /* - TBD: Datasheet say: - the software should clear this bit and restart SMBUS operation. - Should we do it or user start request again? - */ + result = -EAGAIN; } return result; From a6e7d0efe0e21091682f62c4a72707a1adeef8ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Decr=C3=AAme?= Date: Mon, 28 Jan 2013 22:21:08 +0100 Subject: [PATCH 2281/4607] i2c: sis630: use hex to constants for SMBus commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch replaces hexadecimal values by constants for SMBus commands. Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sis630.c | 45 +++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index e152d36b94ac..b2fa7415f7e4 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -81,6 +81,21 @@ #define SMB_COUNT 0x07 /* byte count */ #define SMB_BYTE 0x08 /* ~0x8F data byte field */ +/* SMB_STS register */ +#define BYTE_DONE_STS 0x10 /* Byte Done Status / Block Array */ +#define SMBCOL_STS 0x04 /* Collision */ +#define SMBERR_STS 0x02 /* Device error */ + +/* SMB_CNT register */ +#define MSTO_EN 0x40 /* Host Master Timeout Enable */ +#define SMBCLK_SEL 0x20 /* Host master clock selection */ +#define SMB_PROBE 0x02 /* Bus Probe/Slave busy */ +#define SMB_HOSTBUSY 0x01 /* Host Busy */ + +/* SMBHOST_CNT register */ +#define SMB_KILL 0x20 /* Kill */ +#define SMB_START 0x10 /* Start */ + /* register count for request_region * As we don't use SMB_PCOUNT, 20 is ok for SiS630 and SiS964 */ @@ -140,12 +155,14 @@ static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldc int temp; /* Make sure the SMBus host is ready to start transmitting. */ - if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) { - dev_dbg(&adap->dev, "SMBus busy (%02x).Resetting...\n",temp); + temp = sis630_read(SMB_CNT); + if ((temp & (SMB_PROBE | SMB_HOSTBUSY)) != 0x00) { + dev_dbg(&adap->dev, "SMBus busy (%02x). Resetting...\n", temp); /* kill smbus transaction */ - sis630_write(SMBHOST_CNT, 0x20); + sis630_write(SMBHOST_CNT, SMB_KILL); - if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) { + temp = sis630_read(SMB_CNT); + if (temp & (SMB_PROBE | SMB_HOSTBUSY)) { dev_dbg(&adap->dev, "Failed! (%02x)\n", temp); return -EBUSY; } else { @@ -160,16 +177,16 @@ static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldc /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */ if (high_clock) - sis630_write(SMB_CNT, 0x20); + sis630_write(SMB_CNT, SMBCLK_SEL); else - sis630_write(SMB_CNT, (*oldclock & ~0x40)); + sis630_write(SMB_CNT, (*oldclock & ~MSTO_EN)); /* clear all sticky bits */ temp = sis630_read(SMB_STS); sis630_write(SMB_STS, temp & 0x1e); /* start the transaction by setting bit 4 and size */ - sis630_write(SMBHOST_CNT,0x10 | (size & 0x07)); + sis630_write(SMBHOST_CNT, SMB_START | (size & 0x07)); return 0; } @@ -183,7 +200,7 @@ static int sis630_transaction_wait(struct i2c_adapter *adap, int size) msleep(1); temp = sis630_read(SMB_STS); /* check if block transmitted */ - if (size == SIS630_BLOCK_DATA && (temp & 0x10)) + if (size == SIS630_BLOCK_DATA && (temp & BYTE_DONE_STS)) break; } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); @@ -193,12 +210,12 @@ static int sis630_transaction_wait(struct i2c_adapter *adap, int size) result = -ETIMEDOUT; } - if (temp & 0x02) { + if (temp & SMBERR_STS) { dev_dbg(&adap->dev, "Error: Failed bus transaction\n"); result = -ENXIO; } - if (temp & 0x04) { + if (temp & SMBCOL_STS) { dev_err(&adap->dev, "Bus collision!\n"); result = -EAGAIN; } @@ -217,8 +234,8 @@ static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock) * restore old Host Master Clock if high_clock is set * and oldclock was not 56KHz */ - if (high_clock && !(oldclock & 0x20)) - sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20)); + if (high_clock && !(oldclock & SMBCLK_SEL)) + sis630_write(SMB_CNT, sis630_read(SMB_CNT) & ~SMBCLK_SEL); dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT)); } @@ -270,7 +287,7 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat we must clear sticky bit. clear SMBARY_STS */ - sis630_write(SMB_STS,0x10); + sis630_write(SMB_STS, BYTE_DONE_STS); } rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA); @@ -312,7 +329,7 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i); /* clear SMBARY_STS */ - sis630_write(SMB_STS,0x10); + sis630_write(SMB_STS, BYTE_DONE_STS); } while(len < data->block[0]); } From 97da42dcb48079860b31dc83cb6bc5af15f9fc17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Decr=C3=AAme?= Date: Mon, 28 Jan 2013 22:21:09 +0100 Subject: [PATCH 2282/4607] i2c: sis630: display unsigned hex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch corrects the display of the acpi_base unsigned hex value. Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sis630.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index b2fa7415f7e4..424545b50970 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -463,7 +463,7 @@ static int sis630_setup(struct pci_dev *sis630_dev) goto exit; } - dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base); + dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04hx\n", acpi_base); if (supported[i] == PCI_DEVICE_ID_SI_760) smbus_base = acpi_base + 0xE0; From 91991f34d71d4dd1d6333710d1e159db2f95e6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Decr=C3=AAme?= Date: Tue, 29 Jan 2013 21:22:26 +0100 Subject: [PATCH 2283/4607] i2c: sis630: checkpatch cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch corrects checkpatch errors. The changes has also been removed as it has less meaning with version control tools. Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-sis630.c | 214 ++++++++++++++++---------------- 1 file changed, 109 insertions(+), 105 deletions(-) diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index 424545b50970..36a9556d7cfa 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -16,25 +16,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* - Changes: - 24.08.2002 - Fixed the typo in sis630_access (Thanks to Mark M. Hoffman) - Changed sis630_transaction.(Thanks to Mark M. Hoffman) - 18.09.2002 - Added SIS730 as supported. - 21.09.2002 - Added high_clock module option.If this option is set - used Host Master Clock 56KHz (default 14KHz).For now we save old Host - Master Clock and after transaction completed restore (otherwise - it's confuse BIOS and hung Machine). - 24.09.2002 - Fixed typo in sis630_access - Fixed logical error by restoring of Host Master Clock - 31.07.2003 - Added block data read/write support. -*/ - /* Status: beta @@ -150,9 +131,10 @@ static inline void sis630_write(u8 reg, u8 data) outb(data, smbus_base + reg); } -static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock) +static int sis630_transaction_start(struct i2c_adapter *adap, int size, + u8 *oldclock) { - int temp; + int temp; /* Make sure the SMBus host is ready to start transmitting. */ temp = sis630_read(SMB_CNT); @@ -165,17 +147,18 @@ static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldc if (temp & (SMB_PROBE | SMB_HOSTBUSY)) { dev_dbg(&adap->dev, "Failed! (%02x)\n", temp); return -EBUSY; - } else { + } else { dev_dbg(&adap->dev, "Successful!\n"); } - } + } /* save old clock, so we can prevent machine for hung */ *oldclock = sis630_read(SMB_CNT); dev_dbg(&adap->dev, "saved clock 0x%02x\n", *oldclock); - /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */ + /* disable timeout interrupt, + * set Host Master Clock to 56KHz if requested */ if (high_clock) sis630_write(SMB_CNT, SMBCLK_SEL); else @@ -228,7 +211,8 @@ static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock) /* clear all status "sticky" bits */ sis630_write(SMB_STS, 0xFF); - dev_dbg(&adap->dev, "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT)); + dev_dbg(&adap->dev, + "SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT)); /* * restore old Host Master Clock if high_clock is set @@ -237,7 +221,8 @@ static void sis630_transaction_end(struct i2c_adapter *adap, u8 oldclock) if (high_clock && !(oldclock & SMBCLK_SEL)) sis630_write(SMB_CNT, sis630_read(SMB_CNT) & ~SMBCLK_SEL); - dev_dbg(&adap->dev, "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT)); + dev_dbg(&adap->dev, + "SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT)); } static int sis630_transaction(struct i2c_adapter *adap, int size) @@ -254,7 +239,8 @@ static int sis630_transaction(struct i2c_adapter *adap, int size) return result; } -static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *data, int read_write) +static int sis630_block_data(struct i2c_adapter *adap, + union i2c_smbus_data *data, int read_write) { int i, len = 0, rc = 0; u8 oldclock = 0; @@ -266,22 +252,26 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat else if (len > 32) len = 32; sis630_write(SMB_COUNT, len); - for (i=1; i <= len; i++) { - dev_dbg(&adap->dev, "set data 0x%02x\n", data->block[i]); + for (i = 1; i <= len; i++) { + dev_dbg(&adap->dev, + "set data 0x%02x\n", data->block[i]); /* set data */ - sis630_write(SMB_BYTE+(i-1)%8, data->block[i]); - if (i==8 || (len<8 && i==len)) { - dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i); + sis630_write(SMB_BYTE + (i - 1) % 8, data->block[i]); + if (i == 8 || (len < 8 && i == len)) { + dev_dbg(&adap->dev, + "start trans len=%d i=%d\n", len, i); /* first transaction */ rc = sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock); if (rc) return rc; - } - else if ((i-1)%8 == 7 || i==len) { - dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i); - if (i>8) { - dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i); + } else if ((i - 1) % 8 == 7 || i == len) { + dev_dbg(&adap->dev, + "trans_wait len=%d i=%d\n", len, i); + if (i > 8) { + dev_dbg(&adap->dev, + "clear smbary_sts" + " len=%d i=%d\n", len, i); /* If this is not first transaction, we must clear sticky bit. @@ -292,13 +282,13 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA); if (rc) { - dev_dbg(&adap->dev, "trans_wait failed\n"); + dev_dbg(&adap->dev, + "trans_wait failed\n"); break; } } } - } - else { + } else { /* read request */ data->block[0] = len = 0; rc = sis630_transaction_start(adap, @@ -319,18 +309,22 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat if (data->block[0] > 32) data->block[0] = 32; - dev_dbg(&adap->dev, "block data read len=0x%x\n", data->block[0]); + dev_dbg(&adap->dev, + "block data read len=0x%x\n", data->block[0]); - for (i=0; i < 8 && len < data->block[0]; i++,len++) { - dev_dbg(&adap->dev, "read i=%d len=%d\n", i, len); - data->block[len+1] = sis630_read(SMB_BYTE+i); + for (i = 0; i < 8 && len < data->block[0]; i++, len++) { + dev_dbg(&adap->dev, + "read i=%d len=%d\n", i, len); + data->block[len + 1] = sis630_read(SMB_BYTE + + i); } - dev_dbg(&adap->dev, "clear smbary_sts len=%d i=%d\n",len,i); + dev_dbg(&adap->dev, + "clear smbary_sts len=%d i=%d\n", len, i); /* clear SMBARY_STS */ sis630_write(SMB_STS, BYTE_DONE_STS); - } while(len < data->block[0]); + } while (len < data->block[0]); } sis630_transaction_end(adap, oldclock); @@ -346,42 +340,47 @@ static s32 sis630_access(struct i2c_adapter *adap, u16 addr, int status; switch (size) { - case I2C_SMBUS_QUICK: - sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); - size = SIS630_QUICK; - break; - case I2C_SMBUS_BYTE: - sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); - if (read_write == I2C_SMBUS_WRITE) - sis630_write(SMB_CMD, command); - size = SIS630_BYTE; - break; - case I2C_SMBUS_BYTE_DATA: - sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + case I2C_SMBUS_QUICK: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS630_QUICK; + break; + case I2C_SMBUS_BYTE: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) sis630_write(SMB_CMD, command); - if (read_write == I2C_SMBUS_WRITE) - sis630_write(SMB_BYTE, data->byte); - size = SIS630_BYTE_DATA; - break; - case I2C_SMBUS_PROC_CALL: - case I2C_SMBUS_WORD_DATA: - sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); - sis630_write(SMB_CMD, command); - if (read_write == I2C_SMBUS_WRITE) { - sis630_write(SMB_BYTE, data->word & 0xff); - sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8); - } - size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA); - break; - case I2C_SMBUS_BLOCK_DATA: - sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); - sis630_write(SMB_CMD, command); - size = SIS630_BLOCK_DATA; - return sis630_block_data(adap, data, read_write); - default: - dev_warn(&adap->dev, "Unsupported transaction %d\n", - size); - return -EOPNOTSUPP; + size = SIS630_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis630_write(SMB_BYTE, data->byte); + size = SIS630_BYTE_DATA; + break; + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis630_write(SMB_BYTE, data->word & 0xff); + sis630_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8); + } + size = (size == I2C_SMBUS_PROC_CALL ? + SIS630_PCALL : SIS630_WORD_DATA); + break; + case I2C_SMBUS_BLOCK_DATA: + sis630_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + size = SIS630_BLOCK_DATA; + return sis630_block_data(adap, data, read_write); + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; } status = sis630_transaction(adap, size); @@ -393,15 +392,16 @@ static s32 sis630_access(struct i2c_adapter *adap, u16 addr, return 0; } - switch(size) { - case SIS630_BYTE: - case SIS630_BYTE_DATA: - data->byte = sis630_read(SMB_BYTE); - break; - case SIS630_PCALL: - case SIS630_WORD_DATA: - data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8); - break; + switch (size) { + case SIS630_BYTE: + case SIS630_BYTE_DATA: + data->byte = sis630_read(SMB_BYTE); + break; + case SIS630_PCALL: + case SIS630_WORD_DATA: + data->word = sis630_read(SMB_BYTE) + + (sis630_read(SMB_BYTE + 1) << 8); + break; } return 0; @@ -409,9 +409,9 @@ static s32 sis630_access(struct i2c_adapter *adap, u16 addr, static u32 sis630_func(struct i2c_adapter *adapter) { - return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL | - I2C_FUNC_SMBUS_BLOCK_DATA; + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_DATA; } static int sis630_setup(struct pci_dev *sis630_dev) @@ -423,19 +423,19 @@ static int sis630_setup(struct pci_dev *sis630_dev) unsigned short acpi_base; /* check for supported SiS devices */ - for (i=0; supported[i] > 0 ; i++) { - if ((dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy))) + for (i = 0; supported[i] > 0; i++) { + dummy = pci_get_device(PCI_VENDOR_ID_SI, supported[i], dummy); + if (dummy) break; /* found */ } if (dummy) { pci_dev_put(dummy); - } - else if (force) { - dev_err(&sis630_dev->dev, "WARNING: Can't detect SIS630 compatible device, but " + } else if (force) { + dev_err(&sis630_dev->dev, + "WARNING: Can't detect SIS630 compatible device, but " "loading because of force option enabled\n"); - } - else { + } else { return -ENODEV; } @@ -443,7 +443,7 @@ static int sis630_setup(struct pci_dev *sis630_dev) Enable ACPI first , so we can accsess reg 74-75 in acpi io space and read acpi base addr */ - if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) { + if (pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG, &b)) { dev_err(&sis630_dev->dev, "Error: Can't read bios ctl reg\n"); retval = -ENODEV; goto exit; @@ -457,8 +457,10 @@ static int sis630_setup(struct pci_dev *sis630_dev) } /* Determine the ACPI base address */ - if (pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) { - dev_err(&sis630_dev->dev, "Error: Can't determine ACPI base address\n"); + if (pci_read_config_word(sis630_dev, + SIS630_ACPI_BASE_REG, &acpi_base)) { + dev_err(&sis630_dev->dev, + "Error: Can't determine ACPI base address\n"); retval = -ENODEV; goto exit; } @@ -516,12 +518,14 @@ static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = { { 0, } }; -MODULE_DEVICE_TABLE (pci, sis630_ids); +MODULE_DEVICE_TABLE(pci, sis630_ids); static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id) { if (sis630_setup(dev)) { - dev_err(&dev->dev, "SIS630 comp. bus not detected, module not inserted.\n"); + dev_err(&dev->dev, + "SIS630 compatible bus not detected, " + "module not inserted.\n"); return -ENODEV; } From cb7f07a4c58659d971a60ebbb8547f6611278622 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Sun, 10 Feb 2013 11:59:03 -0500 Subject: [PATCH 2284/4607] i2c: ismt: Add Seth and Myself as maintainers Adding Seth Heasley and Myself as maintainers for the i2c-ismt drvier Signed-off-by: Neil Horman Signed-off-by: "Heasley, Seth" Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8ae709e34523..5a5816c68808 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3750,6 +3750,13 @@ F: drivers/i2c/busses/i2c-sis96x.c F: drivers/i2c/busses/i2c-via.c F: drivers/i2c/busses/i2c-viapro.c +I2C/SMBUS ISMT DRIVER +M: Seth Heasley +M: Neil Horman +L: linux-i2c@vger.kernel.org +F: drivers/i2c/busses/i2c-ismt.c +F: Documentation/i2c/busses/i2c-ismt + I2C/SMBUS STUB DRIVER M: "Mark M. Hoffman" L: linux-i2c@vger.kernel.org From 51aa66a58494f869f491eedda86c409c50536c14 Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Tue, 4 Dec 2012 17:06:18 +0530 Subject: [PATCH 2285/4607] mmc: sdio: fix resume failure due to lack of CMD52 reset If SDIO keep power flag (MMC_PM_KEEP_POWER) is not set, card would be reinitialized during resume but as we are not resetting (CMD52 reset) the SDIO card during this reinitialization, card may fail to respond back to subsequent commands (CMD5 etc...). This change resets the card before the reinitialization of card during resume. Signed-off-by: Subhash Jadavani Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/sdio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 2273ce6b6c1a..34ad4c877c1f 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -937,10 +937,12 @@ static int mmc_sdio_resume(struct mmc_host *host) mmc_claim_host(host); /* No need to reinitialize powered-resumed nonremovable cards */ - if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) + if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) { + sdio_reset(host); + mmc_go_idle(host); err = mmc_sdio_init_card(host, host->ocr, host->card, mmc_card_keep_power(host)); - else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { + } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ err = sdio_enable_4bit_bus(host->card); if (err > 0) { From 41875e388401ad97c33252d5fa39d52e0b70ee9b Mon Sep 17 00:00:00 2001 From: Sujit Reddy Thumma Date: Tue, 4 Dec 2012 17:06:19 +0530 Subject: [PATCH 2286/4607] mmc: sdio: Fix SDIO 3.0 UHS-I initialization sequence According to UHS-I initialization sequence for SDIO 3.0 cards, the host must set bit[24] (S18R) of OCR register during OCR handshake to know whether the SDIO card is capable of doing 1.8V I/O. Signed-off-by: Sujit Reddy Thumma Signed-off-by: Subhash Jadavani Reviewed-by: Johan Rudholm Reviewed-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/sdio.c | 22 +++++++++++----------- include/linux/mmc/host.h | 8 ++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 34ad4c877c1f..9565d38d91a4 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -157,10 +157,7 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr) if (ret) goto out; - if (card->host->caps & - (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | - MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | - MMC_CAP_UHS_DDR50)) { + if (mmc_host_uhs(card->host)) { if (data & SDIO_UHS_DDR50) card->sw_caps.sd3_bus_mode |= SD_MODE_UHS_DDR50; @@ -478,8 +475,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) * If the host doesn't support any of the UHS-I modes, fallback on * default speed. */ - if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | - MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))) + if (!mmc_host_uhs(card->host)) return 0; bus_speed = SDIO_SPEED_SDR12; @@ -645,11 +641,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, * systems that claim 1.8v signalling in fact do not support * it. */ - if ((ocr & R4_18V_PRESENT) && - (host->caps & - (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | - MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | - MMC_CAP_UHS_DDR50))) { + if ((ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) { err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, true); if (err) { @@ -1022,6 +1014,10 @@ static int mmc_sdio_power_restore(struct mmc_host *host) goto out; } + if (mmc_host_uhs(host)) + /* to query card if 1.8V signalling is supported */ + host->ocr |= R4_18V_PRESENT; + ret = mmc_sdio_init_card(host, host->ocr, host->card, mmc_card_keep_power(host)); if (!ret && host->sdio_irqs) @@ -1087,6 +1083,10 @@ int mmc_attach_sdio(struct mmc_host *host) /* * Detect and init the card. */ + if (mmc_host_uhs(host)) + /* to query card if 1.8V signalling is supported */ + host->ocr |= R4_18V_PRESENT; + err = mmc_sdio_init_card(host, host->ocr, NULL, 0); if (err) { if (err == -EAGAIN) { diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 61a10c17aabd..c89a1bb87fa5 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -434,6 +434,14 @@ static inline int mmc_boot_partition_access(struct mmc_host *host) return !(host->caps2 & MMC_CAP2_BOOTPART_NOACC); } +static inline int mmc_host_uhs(struct mmc_host *host) +{ + return host->caps & + (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | + MMC_CAP_UHS_DDR50); +} + #ifdef CONFIG_MMC_CLKGATE void mmc_host_clk_hold(struct mmc_host *host); void mmc_host_clk_release(struct mmc_host *host); From 77e2ff08925c7ec7267dc87c27eda2e62d585b57 Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Tue, 4 Dec 2012 17:06:20 +0530 Subject: [PATCH 2287/4607] mmc: sdio: print correct UHS mode during card detection When SDIO3.0 card is detected, incorrect bus speed mode is printed as part of card detection print in kernel logs. This change fixes it so that user won't be confused by looking at incorrect card detection message in logs. Signed-off-by: Subhash Jadavani Tested-by: Jackey Shen Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/sdio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 9565d38d91a4..3a64933466b8 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -485,23 +485,27 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) bus_speed = SDIO_SPEED_SDR104; timing = MMC_TIMING_UHS_SDR104; card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR; + card->sd_bus_speed = UHS_SDR104_BUS_SPEED; } else if ((card->host->caps & MMC_CAP_UHS_DDR50) && (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) { bus_speed = SDIO_SPEED_DDR50; timing = MMC_TIMING_UHS_DDR50; card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR; + card->sd_bus_speed = UHS_DDR50_BUS_SPEED; } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50)) { bus_speed = SDIO_SPEED_SDR50; timing = MMC_TIMING_UHS_SDR50; card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR; + card->sd_bus_speed = UHS_SDR50_BUS_SPEED; } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) && (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) { bus_speed = SDIO_SPEED_SDR25; timing = MMC_TIMING_UHS_SDR25; card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR; + card->sd_bus_speed = UHS_SDR25_BUS_SPEED; } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode & @@ -509,6 +513,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) bus_speed = SDIO_SPEED_SDR12; timing = MMC_TIMING_UHS_SDR12; card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR; + card->sd_bus_speed = UHS_SDR12_BUS_SPEED; } err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed); From 505a8680b78f580245cfb83f37971c7b62bcf991 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 11 Dec 2012 15:23:42 +0800 Subject: [PATCH 2288/4607] mmc: sdhci: query card presence from cd-gpio before asking SDHCI Call mmc_gpio_get_cd() to query card presence from cd-gpio before asking SDHCI. The rationale behind this change is that flag SDHCI_QUIRK_BROKEN_CARD_DETECTION is designed for SDHCI controller to tell that SDHCI_PRESENT_STATE is broken, and it should be used for this case only. So when cd-gpio is being used, the controller should set the flag to tell that SDHCI_PRESENT_STATE is not available. However, the existing code will skip checking cd-gpio as long as flag SDHCI_QUIRK_BROKEN_CARD_DETECTION is set. Change the querying order between cd-gpio and SDHCI to support the rationale above. Signed-off-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6f0bfc0c8c9c..1b97fe2d70ab 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1258,7 +1258,7 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power) static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct sdhci_host *host; - bool present; + int present; unsigned long flags; u32 tuning_opcode; @@ -1287,18 +1287,21 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq = mrq; - /* If polling, assume that the card is always present. */ - if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) - present = true; - else - present = sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT; - - /* If we're using a cd-gpio, testing the presence bit might fail. */ - if (!present) { - int ret = mmc_gpio_get_cd(host->mmc); - if (ret > 0) - present = true; + /* + * Firstly check card presence from cd-gpio. The return could + * be one of the following possibilities: + * negative: cd-gpio is not available + * zero: cd-gpio is used, and card is removed + * one: cd-gpio is used, and card is present + */ + present = mmc_gpio_get_cd(host->mmc); + if (present < 0) { + /* If polling, assume that the card is always present. */ + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + present = 1; + else + present = sdhci_readl(host, SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT; } if (!present || host->flags & SDHCI_DEVICE_DEAD) { From d65b5ae8dabf48d8e7811a5319ec581e41b04d62 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 11 Dec 2012 22:32:18 +0800 Subject: [PATCH 2289/4607] mmc: slot-gpio: use devm_* managed functions to ease users Use devm_* managed functions, so that slot-gpio users do not have to call mmc_gpio_free_ro/cd to free up resources requested in mmc_gpio_request_ro/cd. Signed-off-by: Shawn Guo Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/core/slot-gpio.c | 57 ++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 16a1c0b6f264..324235105519 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -92,6 +92,20 @@ int mmc_gpio_get_cd(struct mmc_host *host) } EXPORT_SYMBOL(mmc_gpio_get_cd); +/** + * mmc_gpio_request_ro - request a gpio for write-protection + * @host: mmc host + * @gpio: gpio number requested + * + * As devm_* managed functions are used in mmc_gpio_request_ro(), client + * drivers do not need to explicitly call mmc_gpio_free_ro() for freeing up, + * if the requesting and freeing are only needed at probing and unbinding time + * for once. However, if client drivers do something special like runtime + * switching for write-protection, they are responsible for calling + * mmc_gpio_request_ro() and mmc_gpio_free_ro() as a pair on their own. + * + * Returns zero on success, else an error. + */ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) { struct mmc_gpio *ctx; @@ -106,7 +120,8 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) ctx = host->slot.handler_priv; - ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->ro_label); + ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN, + ctx->ro_label); if (ret < 0) return ret; @@ -116,6 +131,20 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) } EXPORT_SYMBOL(mmc_gpio_request_ro); +/** + * mmc_gpio_request_cd - request a gpio for card-detection + * @host: mmc host + * @gpio: gpio number requested + * + * As devm_* managed functions are used in mmc_gpio_request_cd(), client + * drivers do not need to explicitly call mmc_gpio_free_cd() for freeing up, + * if the requesting and freeing are only needed at probing and unbinding time + * for once. However, if client drivers do something special like runtime + * switching for card-detection, they are responsible for calling + * mmc_gpio_request_cd() and mmc_gpio_free_cd() as a pair on their own. + * + * Returns zero on success, else an error. + */ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) { struct mmc_gpio *ctx; @@ -128,7 +157,8 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) ctx = host->slot.handler_priv; - ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->cd_label); + ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN, + ctx->cd_label); if (ret < 0) /* * don't bother freeing memory. It might still get used by other @@ -146,7 +176,8 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) irq = -EINVAL; if (irq >= 0) { - ret = request_threaded_irq(irq, NULL, mmc_gpio_cd_irqt, + ret = devm_request_threaded_irq(&host->class_dev, irq, + NULL, mmc_gpio_cd_irqt, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ctx->cd_label, host); if (ret < 0) @@ -164,6 +195,13 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) } EXPORT_SYMBOL(mmc_gpio_request_cd); +/** + * mmc_gpio_free_ro - free the write-protection gpio + * @host: mmc host + * + * It's provided only for cases that client drivers need to manually free + * up the write-protection gpio requested by mmc_gpio_request_ro(). + */ void mmc_gpio_free_ro(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; @@ -175,10 +213,17 @@ void mmc_gpio_free_ro(struct mmc_host *host) gpio = ctx->ro_gpio; ctx->ro_gpio = -EINVAL; - gpio_free(gpio); + devm_gpio_free(&host->class_dev, gpio); } EXPORT_SYMBOL(mmc_gpio_free_ro); +/** + * mmc_gpio_free_cd - free the card-detection gpio + * @host: mmc host + * + * It's provided only for cases that client drivers need to manually free + * up the card-detection gpio requested by mmc_gpio_request_cd(). + */ void mmc_gpio_free_cd(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; @@ -188,13 +233,13 @@ void mmc_gpio_free_cd(struct mmc_host *host) return; if (host->slot.cd_irq >= 0) { - free_irq(host->slot.cd_irq, host); + devm_free_irq(&host->class_dev, host->slot.cd_irq, host); host->slot.cd_irq = -EINVAL; } gpio = ctx->cd_gpio; ctx->cd_gpio = -EINVAL; - gpio_free(gpio); + devm_gpio_free(&host->class_dev, gpio); } EXPORT_SYMBOL(mmc_gpio_free_cd); From 164cda5236acf833f22a3edafdf8b95a7de7b402 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 11 Dec 2012 22:32:19 +0800 Subject: [PATCH 2290/4607] mmc: remove unncessary mmc_gpio_free_cd() call from slot-gpio users Since slot-gpio uses devm_* managed functions in mmc_gpio_request_cd() now, we can remove those mmc_gpio_free_cd() call from host drivers' .probe() error path and .remove(). Signed-off-by: Shawn Guo Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pxav3.c | 5 ----- drivers/mmc/host/sh_mmcif.c | 6 ------ drivers/mmc/host/tmio_mmc_pio.c | 8 -------- 3 files changed, 19 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index fad0966427fd..b7ee7761bc26 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -316,7 +316,6 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) err_add_host: clk_disable_unprepare(clk); clk_put(clk); - mmc_gpio_free_cd(host->mmc); err_cd_req: err_clk_get: sdhci_pltfm_free(pdev); @@ -329,16 +328,12 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = pltfm_host->priv; - struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; sdhci_remove_host(host, 1); clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); - if (gpio_is_valid(pdata->ext_cd_gpio)) - mmc_gpio_free_cd(host->mmc); - sdhci_pltfm_free(pdev); kfree(pxa); diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 9a4c151067dd..741aeb95c7a4 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1404,8 +1404,6 @@ static int sh_mmcif_probe(struct platform_device *pdev) return ret; emmcaddh: - if (pd && pd->use_cd_gpio) - mmc_gpio_free_cd(mmc); erqcd: free_irq(irq[1], host); ereqirq1: @@ -1427,7 +1425,6 @@ ealloch: static int sh_mmcif_remove(struct platform_device *pdev) { struct sh_mmcif_host *host = platform_get_drvdata(pdev); - struct sh_mmcif_plat_data *pd = pdev->dev.platform_data; int irq[2]; host->dying = true; @@ -1436,9 +1433,6 @@ static int sh_mmcif_remove(struct platform_device *pdev) dev_pm_qos_hide_latency_limit(&pdev->dev); - if (pd && pd->use_cd_gpio) - mmc_gpio_free_cd(host->mmc); - mmc_remove_host(host->mmc); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 50bf495a988b..0f992e9ffc73 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1060,16 +1060,8 @@ EXPORT_SYMBOL(tmio_mmc_host_probe); void tmio_mmc_host_remove(struct tmio_mmc_host *host) { struct platform_device *pdev = host->pdev; - struct tmio_mmc_data *pdata = host->pdata; struct mmc_host *mmc = host->mmc; - if (pdata->flags & TMIO_MMC_USE_GPIO_CD) - /* - * This means we can miss a card-eject, but this is anyway - * possible, because of delayed processing of hotplug events. - */ - mmc_gpio_free_cd(mmc); - if (!host->native_hotplug) pm_runtime_get_sync(&pdev->dev); From fbe5fdd12c4cdae61a8c3d371e564371177da86e Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 11 Dec 2012 22:32:20 +0800 Subject: [PATCH 2291/4607] mmc: sdhci-esdhc-imx: use slot-gpio helpers for CD and WP Use slot-gpio helpers to save some code in the driver. Signed-off-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 56 +++++++++--------------------- 1 file changed, 16 insertions(+), 40 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index e07df812ff1e..dd7fcc137644 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -147,17 +148,16 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) struct pltfm_imx_data *imx_data = pltfm_host->priv; struct esdhc_platform_data *boarddata = &imx_data->boarddata; - /* fake CARD_PRESENT flag */ u32 val = readl(host->ioaddr + reg); - if (unlikely((reg == SDHCI_PRESENT_STATE) - && gpio_is_valid(boarddata->cd_gpio))) { - if (gpio_get_value(boarddata->cd_gpio)) - /* no card, if a valid gpio says so... */ + if (unlikely(reg == SDHCI_PRESENT_STATE)) { + /* + * After SDHCI core gets improved to never query + * SDHCI_CARD_PRESENT state in GPIO case, we can + * remove this check. + */ + if (boarddata->cd_type == ESDHC_CD_GPIO) val &= ~SDHCI_CARD_PRESENT; - else - /* ... in all other cases assume card is present */ - val |= SDHCI_CARD_PRESENT; } if (unlikely(reg == SDHCI_CAPABILITIES)) { @@ -362,8 +362,7 @@ static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) switch (boarddata->wp_type) { case ESDHC_WP_GPIO: - if (gpio_is_valid(boarddata->wp_gpio)) - return gpio_get_value(boarddata->wp_gpio); + return mmc_gpio_get_ro(host->mmc); case ESDHC_WP_CONTROLLER: return !(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT); @@ -394,14 +393,6 @@ static struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { .ops = &sdhci_esdhc_ops, }; -static irqreturn_t cd_irq(int irq, void *data) -{ - struct sdhci_host *sdhost = (struct sdhci_host *)data; - - tasklet_schedule(&sdhost->card_tasklet); - return IRQ_HANDLED; -}; - #ifdef CONFIG_OF static int sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, @@ -527,37 +518,22 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) /* write_protect */ if (boarddata->wp_type == ESDHC_WP_GPIO) { - err = devm_gpio_request_one(&pdev->dev, boarddata->wp_gpio, - GPIOF_IN, "ESDHC_WP"); + err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio); if (err) { - dev_warn(mmc_dev(host->mmc), - "no write-protect pin available!\n"); - boarddata->wp_gpio = -EINVAL; + dev_err(mmc_dev(host->mmc), + "failed to request write-protect gpio!\n"); + goto disable_clk; } - } else { - boarddata->wp_gpio = -EINVAL; + host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; } /* card_detect */ - if (boarddata->cd_type != ESDHC_CD_GPIO) - boarddata->cd_gpio = -EINVAL; - switch (boarddata->cd_type) { case ESDHC_CD_GPIO: - err = devm_gpio_request_one(&pdev->dev, boarddata->cd_gpio, - GPIOF_IN, "ESDHC_CD"); + err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio); if (err) { dev_err(mmc_dev(host->mmc), - "no card-detect pin available!\n"); - goto disable_clk; - } - - err = devm_request_irq(&pdev->dev, - gpio_to_irq(boarddata->cd_gpio), cd_irq, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - mmc_hostname(host->mmc), host); - if (err) { - dev_err(mmc_dev(host->mmc), "request irq error\n"); + "failed to request card-detect gpio!\n"); goto disable_clk; } /* fall through */ From 92ff0c5bc4000d6b2b1bfc8cd40bd1397a03c8ec Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:05 +0100 Subject: [PATCH 2292/4607] mmc: sh_mmcif: force to fail CMD52 immediately mmc_rescan() sends CMD52 (SD_IO_RW_DIRECT) to reset SDIO card during card detection. CMD52 should be ignored by SD/eMMC cards, but we can also abort it in the driver immediately, since MMCIF doesn't support SDIO cards anyway. Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 741aeb95c7a4..8b4e98e2b130 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -911,6 +911,7 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) if ((mrq->cmd->flags & MMC_CMD_MASK) != MMC_CMD_BCR) break; case MMC_APP_CMD: + case SD_IO_RW_DIRECT: host->state = STATE_IDLE; mrq->cmd->error = -ETIMEDOUT; mmc_request_done(mmc, mrq); From f8a8ced7f9e79916311c0ef08d1f6de7bf954807 Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:06 +0100 Subject: [PATCH 2293/4607] mmc: sh_mmcif: ensure run-time suspend call is processed before suspend With this post-v2.6.35 change applied: commit a0a1a5fd4fb15ec61117c759fe9f5c16c53d9e9c Author: Tejun Heo Date: Tue Jun 29 10:07:12 2010 +0200 workqueue: reimplement workqueue freeze using max_active freeze_workqueues_begin() was introduced and workqueue now gets frozen before device drivers suspend operations. We have to ensure that run-time PM suspend operation completes before system-wide suspend is started. Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 8b4e98e2b130..2ff3e4774b67 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -982,7 +982,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } if (host->power) { - pm_runtime_put(&host->pd->dev); + pm_runtime_put_sync(&host->pd->dev); clk_disable(host->hclk); host->power = false; if (ios->power_mode == MMC_POWER_OFF) From 2cd5b3e061e3742de6b3a50f45f7d3b96aa50964 Mon Sep 17 00:00:00 2001 From: Shinya Kuribayashi Date: Mon, 14 Jan 2013 14:12:36 -0500 Subject: [PATCH 2294/4607] mmc: sh_mmcif: add support for bundled MMCIF IRQs On newer SoCs like R-Mobile U2, MMCIF interrupts are bundled. Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 2ff3e4774b67..c7984bad8efc 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1307,10 +1307,11 @@ static int sh_mmcif_probe(struct platform_device *pdev) struct sh_mmcif_plat_data *pd = pdev->dev.platform_data; struct resource *res; void __iomem *reg; + const char *name; irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq(pdev, 1); - if (irq[0] < 0 || irq[1] < 0) { + if (irq[0] < 0) { dev_err(&pdev->dev, "Get irq error\n"); return -ENXIO; } @@ -1375,15 +1376,19 @@ static int sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_sync_reset(host); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); - ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:error", host); + name = irq[1] < 0 ? dev_name(&pdev->dev) : "sh_mmc:error"; + ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, name, host); if (ret) { - dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); + dev_err(&pdev->dev, "request_irq error (%s)\n", name); goto ereqirq0; } - ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host); - if (ret) { - dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); - goto ereqirq1; + if (irq[1] >= 0) { + ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, + 0, "sh_mmc:int", host); + if (ret) { + dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); + goto ereqirq1; + } } if (pd && pd->use_cd_gpio) { @@ -1406,7 +1411,8 @@ static int sh_mmcif_probe(struct platform_device *pdev) emmcaddh: erqcd: - free_irq(irq[1], host); + if (irq[1] >= 0) + free_irq(irq[1], host); ereqirq1: free_irq(irq[0], host); ereqirq0: @@ -1451,7 +1457,8 @@ static int sh_mmcif_remove(struct platform_device *pdev) irq[1] = platform_get_irq(pdev, 1); free_irq(irq[0], host); - free_irq(irq[1], host); + if (irq[1] >= 0) + free_irq(irq[1], host); platform_set_drvdata(pdev, NULL); From 555061f987787a966b01e62517b9befbd35e2f89 Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:08 +0100 Subject: [PATCH 2295/4607] mmc: sh_mmcif: Add support for eMMC Dual Data Rate Some MMCIF implementations support the Dual Data Rate. With this patch, platforms can set the MMC_CAP_UHS_DDR50 capability flag in MMCIF platform data. This will let the MMC core to actually use the DDR mode. Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index c7984bad8efc..6d4328dce320 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -88,6 +88,7 @@ #define CMD_SET_TBIT (1 << 7) /* 1: tran mission bit "Low" */ #define CMD_SET_OPDM (1 << 6) /* 1: open/drain */ #define CMD_SET_CCSH (1 << 5) +#define CMD_SET_DARS (1 << 2) /* Dual Data Rate */ #define CMD_SET_DATW_1 ((0 << 1) | (0 << 0)) /* 1bit */ #define CMD_SET_DATW_4 ((0 << 1) | (1 << 0)) /* 4bit */ #define CMD_SET_DATW_8 ((1 << 1) | (0 << 0)) /* 8bit */ @@ -216,6 +217,7 @@ struct sh_mmcif_host { struct clk *hclk; unsigned int clk; int bus_width; + unsigned char timing; bool sd_error; bool dying; long timeout; @@ -781,6 +783,17 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, dev_err(&host->pd->dev, "Unsupported bus width.\n"); break; } + switch (host->timing) { + case MMC_TIMING_UHS_DDR50: + /* + * MMC core will only set this timing, if the host + * advertises the MMC_CAP_UHS_DDR50 capability. MMCIF + * implementations with this capability, e.g. sh73a0, + * will have to set it in their platform data. + */ + tmp |= CMD_SET_DARS; + break; + } } /* DWEN */ if (opc == MMC_WRITE_BLOCK || opc == MMC_WRITE_MULTIPLE_BLOCK) @@ -1002,6 +1015,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sh_mmcif_clock_control(host, ios->clock); } + host->timing = ios->timing; host->bus_width = ios->bus_width; host->state = STATE_IDLE; } From f9fd54f22e79046208ff1656afff7b5f9b7433cb Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:09 +0100 Subject: [PATCH 2296/4607] mmc: sh_mmcif: Use msecs_to_jiffies() for host->timeout Timeout period should be properly normalized using msecs_to_jiffies(). Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 6d4328dce320..6d3644430877 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1348,7 +1348,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) host = mmc_priv(mmc); host->mmc = mmc; host->addr = reg; - host->timeout = 1000; + host->timeout = msecs_to_jiffies(1000); host->pd = pdev; From a812ba0fd031c14c7680c94a1a22d5c60c7b6e2b Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:10 +0100 Subject: [PATCH 2297/4607] mmc: sh_mmcif: Avoid unnecessary mmc_delay() at mmc_card_sleepawake() SH/R-Mobile MMCIF host controller can wait while the card signals busy. Set MMC_CAP_WAIT_WHILE_BUSY to inform an upper layer (core/mmc_ops.c) not to insert unnecessary mmc_delay(). Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 6d3644430877..663b92b364bb 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -758,6 +758,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, } switch (opc) { /* RBSY */ + case MMC_SLEEP_AWAKE: case MMC_SWITCH: case MMC_STOP_TRANSMISSION: case MMC_SET_WRITE_PROT: @@ -851,6 +852,7 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, switch (opc) { /* response busy check */ + case MMC_SLEEP_AWAKE: case MMC_SWITCH: case MMC_STOP_TRANSMISSION: case MMC_SET_WRITE_PROT: @@ -1357,7 +1359,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) mmc->ops = &sh_mmcif_ops; sh_mmcif_init_ocr(host); - mmc->caps = MMC_CAP_MMC_HIGHSPEED; + mmc->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_WAIT_WHILE_BUSY; if (pd && pd->caps) mmc->caps |= pd->caps; mmc->max_segs = 32; From 5df460b15e10ffcf2c9a05d0c55b309568d330ea Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:11 +0100 Subject: [PATCH 2298/4607] mmc: sh_mmcif: fix missing and consolidate IO completion timeouts Read block and write block operations are currently missing completion timeouts. Add missing timeouts and consolidate them at one location. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 45 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 663b92b364bb..f4b10c8f6384 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -564,7 +564,6 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host, BLOCK_SIZE_MASK) + 3; host->wait_for = MMCIF_WAIT_FOR_READ; - schedule_delayed_work(&host->timeout_work, host->timeout); /* buf read enable */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); @@ -606,7 +605,7 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host, host->sg_idx = 0; host->sg_blkidx = 0; host->pio_ptr = sg_virt(data->sg); - schedule_delayed_work(&host->timeout_work, host->timeout); + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); } @@ -629,7 +628,6 @@ static bool sh_mmcif_mread_block(struct sh_mmcif_host *host) if (!sh_mmcif_next_block(host, p)) return false; - schedule_delayed_work(&host->timeout_work, host->timeout); sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); return true; @@ -642,7 +640,6 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host, BLOCK_SIZE_MASK) + 3; host->wait_for = MMCIF_WAIT_FOR_WRITE; - schedule_delayed_work(&host->timeout_work, host->timeout); /* buf write enable */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); @@ -684,7 +681,7 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host, host->sg_idx = 0; host->sg_blkidx = 0; host->pio_ptr = sg_virt(data->sg); - schedule_delayed_work(&host->timeout_work, host->timeout); + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); } @@ -707,7 +704,6 @@ static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) if (!sh_mmcif_next_block(host, p)) return false; - schedule_delayed_work(&host->timeout_work, host->timeout); sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); return true; @@ -900,7 +896,6 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, } host->wait_for = MMCIF_WAIT_FOR_STOP; - schedule_delayed_work(&host->timeout_work, host->timeout); } static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) @@ -1121,6 +1116,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; struct mmc_request *mrq = host->mrq; + bool wait = false; cancel_delayed_work_sync(&host->timeout_work); @@ -1133,29 +1129,24 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) /* We're too late, the timeout has already kicked in */ return IRQ_HANDLED; case MMCIF_WAIT_FOR_CMD: - if (sh_mmcif_end_cmd(host)) - /* Wait for data */ - return IRQ_HANDLED; + /* Wait for data? */ + wait = sh_mmcif_end_cmd(host); break; case MMCIF_WAIT_FOR_MREAD: - if (sh_mmcif_mread_block(host)) - /* Wait for more data */ - return IRQ_HANDLED; + /* Wait for more data? */ + wait = sh_mmcif_mread_block(host); break; case MMCIF_WAIT_FOR_READ: - if (sh_mmcif_read_block(host)) - /* Wait for data end */ - return IRQ_HANDLED; + /* Wait for data end? */ + wait = sh_mmcif_read_block(host); break; case MMCIF_WAIT_FOR_MWRITE: - if (sh_mmcif_mwrite_block(host)) - /* Wait data to write */ - return IRQ_HANDLED; + /* Wait data to write? */ + wait = sh_mmcif_mwrite_block(host); break; case MMCIF_WAIT_FOR_WRITE: - if (sh_mmcif_write_block(host)) - /* Wait for data end */ - return IRQ_HANDLED; + /* Wait for data end? */ + wait = sh_mmcif_write_block(host); break; case MMCIF_WAIT_FOR_STOP: if (host->sd_error) { @@ -1174,6 +1165,12 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) BUG(); } + if (wait) { + schedule_delayed_work(&host->timeout_work, host->timeout); + /* Wait for more data */ + return IRQ_HANDLED; + } + if (host->wait_for != MMCIF_WAIT_FOR_STOP) { struct mmc_data *data = mrq->data; if (!mrq->cmd->error && data && !data->error) @@ -1182,8 +1179,10 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) if (mrq->stop && !mrq->cmd->error && (!data || !data->error)) { sh_mmcif_stop_cmd(host, mrq); - if (!mrq->stop->error) + if (!mrq->stop->error) { + schedule_delayed_work(&host->timeout_work, host->timeout); return IRQ_HANDLED; + } } } From eae309836509496c981ceaebdef57041de86ecd4 Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:12 +0100 Subject: [PATCH 2299/4607] mmc: sh_mmcif: Terminate DMA transactions when detecting timeout or error If a DMA transaction fails, terminate all outstanding DMA transfers and unmap buffers. Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi [g.liakhovetski@gmx.de: forward-port, add dma_unmap_sg() in error cases] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index f4b10c8f6384..8aa7b0e6dec2 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -263,15 +263,6 @@ static void mmcif_dma_complete(void *arg) dev_name(&host->pd->dev))) return; - if (data->flags & MMC_DATA_READ) - dma_unmap_sg(host->chan_rx->device->dev, - data->sg, data->sg_len, - DMA_FROM_DEVICE); - else - dma_unmap_sg(host->chan_tx->device->dev, - data->sg, data->sg_len, - DMA_TO_DEVICE); - complete(&host->dma_complete); } @@ -1088,14 +1079,20 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) /* Running in the IRQ thread, can sleep */ time = wait_for_completion_interruptible_timeout(&host->dma_complete, host->timeout); + + if (data->flags & MMC_DATA_READ) + dma_unmap_sg(host->chan_rx->device->dev, + data->sg, data->sg_len, + DMA_FROM_DEVICE); + else + dma_unmap_sg(host->chan_tx->device->dev, + data->sg, data->sg_len, + DMA_TO_DEVICE); + if (host->sd_error) { dev_err(host->mmc->parent, "Error IRQ while waiting for DMA completion!\n"); /* Woken up by an error IRQ: abort DMA */ - if (data->flags & MMC_DATA_READ) - dmaengine_terminate_all(host->chan_rx); - else - dmaengine_terminate_all(host->chan_tx); data->error = sh_mmcif_error_manage(host); } else if (!time) { data->error = -ETIMEDOUT; @@ -1106,8 +1103,14 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) BUF_ACC_DMAREN | BUF_ACC_DMAWEN); host->dma_active = false; - if (data->error) + if (data->error) { data->bytes_xfered = 0; + /* Abort DMA */ + if (data->flags & MMC_DATA_READ) + dmaengine_terminate_all(host->chan_rx); + else + dmaengine_terminate_all(host->chan_tx); + } return false; } From 99eb9d8df996fc1695d4de687873875fbd8f6719 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:13 +0100 Subject: [PATCH 2300/4607] mmc: sh_mmcif: (cosmetic) simplify boolean return blocks Use "return condition" instead of "if (condition) return true; return false" in functions, returning bool. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 8aa7b0e6dec2..de4b6d073599 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -542,10 +542,7 @@ static bool sh_mmcif_next_block(struct sh_mmcif_host *host, u32 *p) host->pio_ptr = p; } - if (host->sg_idx == data->sg_len) - return false; - - return true; + return host->sg_idx != data->sg_len; } static void sh_mmcif_single_read(struct sh_mmcif_host *host, @@ -1071,9 +1068,7 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) if (!host->dma_active) { data->error = sh_mmcif_data_trans(host, host->mrq, cmd->opcode); - if (!data->error) - return true; - return false; + return !data->error; } /* Running in the IRQ thread, can sleep */ From 8047310ee984b3efe932ee5b561f2523396466dd Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:14 +0100 Subject: [PATCH 2301/4607] mmc: sh_mmcif: fix a race, causing an Oops on SMP Oopses have been observed on SMP in the sh-mmcif IRQ thread, when the two IRQ threads run simultaneously on two CPUs. Also take care to guard the timeout work and the DMA completion callback from possible NULL-pointer dereferences and races. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 39 ++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index de4b6d073599..3cfe383dc22b 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -196,6 +197,7 @@ enum mmcif_state { STATE_IDLE, STATE_REQUEST, STATE_IOS, + STATE_TIMEOUT, }; enum mmcif_wait_for { @@ -232,6 +234,7 @@ struct sh_mmcif_host { int sg_blkidx; bool power; bool card_present; + struct mutex thread_lock; /* DMA support */ struct dma_chan *chan_rx; @@ -255,11 +258,11 @@ static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host, static void mmcif_dma_complete(void *arg) { struct sh_mmcif_host *host = arg; - struct mmc_data *data = host->mrq->data; + struct mmc_request *mrq = host->mrq; dev_dbg(&host->pd->dev, "Command completed\n"); - if (WARN(!data, "%s: NULL data in DMA completion!\n", + if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion!\n", dev_name(&host->pd->dev))) return; @@ -1113,11 +1116,21 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; - struct mmc_request *mrq = host->mrq; + struct mmc_request *mrq; bool wait = false; cancel_delayed_work_sync(&host->timeout_work); + mutex_lock(&host->thread_lock); + + mrq = host->mrq; + if (!mrq) { + dev_dbg(&host->pd->dev, "IRQ thread state %u, wait %u: NULL mrq!\n", + host->state, host->wait_for); + mutex_unlock(&host->thread_lock); + return IRQ_HANDLED; + } + /* * All handlers return true, if processing continues, and false, if the * request has to be completed - successfully or not @@ -1125,6 +1138,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) switch (host->wait_for) { case MMCIF_WAIT_FOR_REQUEST: /* We're too late, the timeout has already kicked in */ + mutex_unlock(&host->thread_lock); return IRQ_HANDLED; case MMCIF_WAIT_FOR_CMD: /* Wait for data? */ @@ -1166,6 +1180,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) if (wait) { schedule_delayed_work(&host->timeout_work, host->timeout); /* Wait for more data */ + mutex_unlock(&host->thread_lock); return IRQ_HANDLED; } @@ -1179,6 +1194,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) sh_mmcif_stop_cmd(host, mrq); if (!mrq->stop->error) { schedule_delayed_work(&host->timeout_work, host->timeout); + mutex_unlock(&host->thread_lock); return IRQ_HANDLED; } } @@ -1189,6 +1205,8 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) host->mrq = NULL; mmc_request_done(host->mmc, mrq); + mutex_unlock(&host->thread_lock); + return IRQ_HANDLED; } @@ -1262,11 +1280,24 @@ static void mmcif_timeout_work(struct work_struct *work) struct delayed_work *d = container_of(work, struct delayed_work, work); struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work); struct mmc_request *mrq = host->mrq; + unsigned long flags; if (host->dying) /* Don't run after mmc_remove_host() */ return; + dev_dbg(&host->pd->dev, "Timeout waiting for %u, opcode %u\n", + host->wait_for, mrq->cmd->opcode); + + spin_lock_irqsave(&host->lock, flags); + if (host->state == STATE_IDLE) { + spin_unlock_irqrestore(&host->lock, flags); + return; + } + + host->state = STATE_TIMEOUT; + spin_unlock_irqrestore(&host->lock, flags); + /* * Handle races with cancel_delayed_work(), unless * cancel_delayed_work_sync() is used @@ -1410,6 +1441,8 @@ static int sh_mmcif_probe(struct platform_device *pdev) goto erqcd; } + mutex_init(&host->thread_lock); + clk_disable(host->hclk); ret = mmc_add_host(mmc); if (ret < 0) From aba9d646785c8b1decb1a15ee157b0179a15bef9 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:15 +0100 Subject: [PATCH 2302/4607] mmc: sh_mmcif: reset error code for any opcode If a command execution has produced an error, it has to be reset as a part of the error handling. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 3cfe383dc22b..14fafafc12d3 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1041,7 +1041,6 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) case MMC_SELECT_CARD: case MMC_APP_CMD: cmd->error = -ETIMEDOUT; - host->sd_error = false; break; default: cmd->error = sh_mmcif_error_manage(host); @@ -1049,6 +1048,7 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) cmd->opcode, cmd->error); break; } + host->sd_error = false; return false; } if (!(cmd->flags & MMC_RSP_PRESENT)) { From 90f1cb438e33bb88036649665c2165155561b54f Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:16 +0100 Subject: [PATCH 2303/4607] mmc: sh_mmcif: reset DMA completion immediately before starting DMA DMA completion can be signalled from the DMA callback and from the error handler. If both are called, the completion struct can enter an inconsistent state. To prevent this move completion initialisation immediately before activating DMA. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 14fafafc12d3..1c37854c0f33 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -419,8 +419,6 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host, if (ret < 0) goto ecfgrx; - init_completion(&host->dma_complete); - return; ecfgrx: @@ -1061,6 +1059,12 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) if (!data) return false; + /* + * Completion can be signalled from DMA callback and error, so, have to + * reset here, before setting .dma_active + */ + init_completion(&host->dma_complete); + if (data->flags & MMC_DATA_READ) { if (host->chan_rx) sh_mmcif_start_dma_rx(host); From 276bc96b2abfa2a6bb219aa4eda13c4c331c53eb Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:38:17 +0100 Subject: [PATCH 2304/4607] mmc: sh_mmcif: fix I/O errors The INT_BUFWEN IRQ often arrives with other bits set too. If they are not cleared, an additional IRQ can be triggered, sometimes also after the MMC request has already been completed. This leads to block I/O errors. Earlier Teppei Kamijou also observed these additional interrupts and proposed to explicitly wait for them. This patch chooses an alternative approach of clearing all active bits immediately, when processing the main interrupt. Reported-by: Teppei Kamijou Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 1c37854c0f33..e6a6d2363a4d 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1238,7 +1238,9 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFREN); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); } else if (state & INT_BUFWEN) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFWEN); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, + ~(INT_BUFWEN | INT_DTRANE | INT_CMD12DRE | + INT_CMD12RBE | INT_CMD12CRE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); } else if (state & INT_CMD12DRE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, From e475b2702f612901772d12110614ad8c3fb5d89b Mon Sep 17 00:00:00 2001 From: Teppei Kamijou Date: Wed, 12 Dec 2012 15:38:18 +0100 Subject: [PATCH 2305/4607] mmc: sh_mmcif: report all errors Make error reporting in the driver more verbose. This patch is based on an earlier work by Teppei Kamijou, but we try to not add any new error messages to the log in the normal case to avoid confusing the user, and also add a few more dev_dbg() calls. Signed-off-by: Teppei Kamijou Signed-off-by: Shinya Kuribayashi [g.liakhovetski@gmx.de: avoid producing new errors in normal case] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index e6a6d2363a4d..b4180c7bffc4 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -514,13 +514,16 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host) } if (state2 & STS2_CRC_ERR) { - dev_dbg(&host->pd->dev, ": CRC error\n"); + dev_err(&host->pd->dev, " CRC error: state %u, wait %u\n", + host->state, host->wait_for); ret = -EIO; } else if (state2 & STS2_TIMEOUT_ERR) { - dev_dbg(&host->pd->dev, ": Timeout\n"); + dev_err(&host->pd->dev, " Timeout: state %u, wait %u\n", + host->state, host->wait_for); ret = -ETIMEDOUT; } else { - dev_dbg(&host->pd->dev, ": End/Index error\n"); + dev_dbg(&host->pd->dev, " End/Index error: state %u, wait %u\n", + host->state, host->wait_for); ret = -EIO; } return ret; @@ -566,6 +569,7 @@ static bool sh_mmcif_read_block(struct sh_mmcif_host *host) if (host->sd_error) { data->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); return false; } @@ -606,6 +610,7 @@ static bool sh_mmcif_mread_block(struct sh_mmcif_host *host) if (host->sd_error) { data->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); return false; } @@ -642,6 +647,7 @@ static bool sh_mmcif_write_block(struct sh_mmcif_host *host) if (host->sd_error) { data->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); return false; } @@ -682,6 +688,7 @@ static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) if (host->sd_error) { data->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); return false; } @@ -823,7 +830,7 @@ static int sh_mmcif_data_trans(struct sh_mmcif_host *host, sh_mmcif_single_read(host, mrq); return 0; default: - dev_err(&host->pd->dev, "UNSUPPORTED CMD = d'%08d\n", opc); + dev_err(&host->pd->dev, "Unsupported CMD%d\n", opc); return -EINVAL; } } @@ -894,6 +901,7 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_lock_irqsave(&host->lock, flags); if (host->state != STATE_IDLE) { + dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state); spin_unlock_irqrestore(&host->lock, flags); mrq->cmd->error = -EAGAIN; mmc_request_done(mmc, mrq); @@ -957,6 +965,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) spin_lock_irqsave(&host->lock, flags); if (host->state != STATE_IDLE) { + dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state); spin_unlock_irqrestore(&host->lock, flags); return; } @@ -1042,10 +1051,10 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) break; default: cmd->error = sh_mmcif_error_manage(host); - dev_dbg(&host->pd->dev, "Cmd(d'%d) error %d\n", - cmd->opcode, cmd->error); break; } + dev_dbg(&host->pd->dev, "CMD%d error %d\n", + cmd->opcode, cmd->error); host->sd_error = false; return false; } @@ -1097,8 +1106,11 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) /* Woken up by an error IRQ: abort DMA */ data->error = sh_mmcif_error_manage(host); } else if (!time) { + dev_err(host->mmc->parent, "DMA timeout!\n"); data->error = -ETIMEDOUT; } else if (time < 0) { + dev_err(host->mmc->parent, + "wait_for_completion_...() error %ld!\n", time); data->error = time; } sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, @@ -1167,6 +1179,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) case MMCIF_WAIT_FOR_STOP: if (host->sd_error) { mrq->stop->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->stop->error); break; } sh_mmcif_get_cmd12response(host, mrq->stop); @@ -1174,8 +1187,10 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) break; case MMCIF_WAIT_FOR_READ_END: case MMCIF_WAIT_FOR_WRITE_END: - if (host->sd_error) + if (host->sd_error) { mrq->data->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->data->error); + } break; default: BUG(); @@ -1292,7 +1307,7 @@ static void mmcif_timeout_work(struct work_struct *work) /* Don't run after mmc_remove_host() */ return; - dev_dbg(&host->pd->dev, "Timeout waiting for %u, opcode %u\n", + dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n", host->wait_for, mrq->cmd->opcode); spin_lock_irqsave(&host->lock, flags); From 8af5075088a0aaa64caed5ed212b485b5760bf0b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 12 Dec 2012 15:45:14 +0100 Subject: [PATCH 2306/4607] mmc: sh_mmcif: simplify IRQ processing The classical way to process IRQs is read out the status, ack all triggered IRQs, possibly mask them, then process them. Follow this simple procesure instead of the current complex custom algorithm. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 65 +++++++++++-------------------------- 1 file changed, 19 insertions(+), 46 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index b4180c7bffc4..0189efcb9e12 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -129,6 +129,10 @@ INT_CCSTO | INT_CRCSTO | INT_WDATTO | \ INT_RDATTO | INT_RBSYTO | INT_RSPTO) +#define INT_ALL (INT_RBSYE | INT_CRSPE | INT_BUFREN | \ + INT_BUFWEN | INT_CMD12DRE | INT_BUFRE | \ + INT_DTRANE | INT_CMD12RBE | INT_CMD12CRE) + /* CE_INT_MASK */ #define MASK_ALL 0x00000000 #define MASK_MCCSDE (1 << 29) @@ -160,6 +164,11 @@ MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | \ MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO) +#define MASK_CLEAN (INT_ERR_STS | MASK_MRBSYE | MASK_MCRSPE | \ + MASK_MBUFREN | MASK_MBUFWEN | \ + MASK_MCMD12DRE | MASK_MBUFRE | MASK_MDTRANE | \ + MASK_MCMD12RBE | MASK_MCMD12CRE) + /* CE_HOST_STS1 */ #define STS1_CMDSEQ (1 << 31) @@ -1233,58 +1242,22 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; u32 state; - int err = 0; state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN); - if (state & INT_ERR_STS) { - /* error interrupts - process first */ - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); - err = 1; - } else if (state & INT_RBSYE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(INT_RBSYE | INT_CRSPE)); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MRBSYE); - } else if (state & INT_CRSPE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_CRSPE); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCRSPE); - } else if (state & INT_BUFREN) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFREN); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); - } else if (state & INT_BUFWEN) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(INT_BUFWEN | INT_DTRANE | INT_CMD12DRE | - INT_CMD12RBE | INT_CMD12CRE)); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); - } else if (state & INT_CMD12DRE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(INT_CMD12DRE | INT_CMD12RBE | - INT_CMD12CRE | INT_BUFRE)); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); - } else if (state & INT_BUFRE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~INT_BUFRE); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); - } else if (state & INT_DTRANE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(INT_CMD12DRE | INT_CMD12RBE | - INT_CMD12CRE | INT_DTRANE)); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); - } else if (state & INT_CMD12RBE) { - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(INT_CMD12RBE | INT_CMD12CRE)); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); - } else { - dev_dbg(&host->pd->dev, "Unsupported interrupt: 0x%x\n", state); - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); - err = 1; - } - if (err) { + if (state & ~MASK_CLEAN) + dev_dbg(&host->pd->dev, "IRQ state = 0x%08x incompletely cleared\n", + state); + + if (state & INT_ERR_STS || state & ~INT_ALL) { host->sd_error = true; - dev_dbg(&host->pd->dev, "int err state = %08x\n", state); + dev_dbg(&host->pd->dev, "int err state = 0x%08x\n", state); } if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) { + if (!host->mrq) + dev_dbg(&host->pd->dev, "NULL IRQ state = 0x%08x\n", state); if (!host->dma_active) return IRQ_WAKE_THREAD; else if (host->sd_error) From 369d321ed1baa7748e770aaaae4d8effad699633 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 26 Dec 2012 10:40:17 +0900 Subject: [PATCH 2307/4607] mmc: queue: exclude asynchronous transfer for special request Unlike normal r/w request, special requests(discard, flush) is finished with a one-time issue_fn. Request change to mqrq_prev makes unnecessary call. Signed-off-by: Seungwon Jeon Reviewed-by: Konstantin Dorfman Signed-off-by: Chris Ball --- drivers/mmc/card/queue.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index fadf52eb5d70..d630d9861e7b 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -24,6 +24,8 @@ #define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_REQ_SPECIAL_MASK (REQ_DISCARD | REQ_FLUSH) + /* * Prepare a MMC request. This just filters out odd stuff. */ @@ -58,6 +60,7 @@ static int mmc_queue_thread(void *d) do { struct request *req = NULL; struct mmc_queue_req *tmp; + unsigned int cmd_flags = 0; spin_lock_irq(q->queue_lock); set_current_state(TASK_INTERRUPTIBLE); @@ -67,12 +70,19 @@ static int mmc_queue_thread(void *d) if (req || mq->mqrq_prev->req) { set_current_state(TASK_RUNNING); + cmd_flags = req ? req->cmd_flags : 0; mq->issue_fn(mq, req); /* * Current request becomes previous request * and vice versa. + * In case of special requests, current request + * has been finished. Do not assign it to previous + * request. */ + if (cmd_flags & MMC_REQ_SPECIAL_MASK) + mq->mqrq_cur->req = NULL; + mq->mqrq_prev->brq.mrq.data = NULL; mq->mqrq_prev->req = NULL; tmp = mq->mqrq_prev; From 2220eedfd7aea69008173a224975e10284fbe854 Mon Sep 17 00:00:00 2001 From: Konstantin Dorfman Date: Mon, 14 Jan 2013 14:28:17 -0500 Subject: [PATCH 2308/4607] mmc: fix async request mechanism for sequential read scenarios When current request is running on the bus and if next request fetched by mmcqd is NULL, mmc context (mmcqd thread) gets blocked until the current request completes. This means that if new request comes in while the mmcqd thread is blocked, this new request can not be prepared in parallel to current ongoing request. This may result in delaying the new request execution and increase it's latency. This change allows to wake up the MMC thread on new request arrival. Now once the MMC thread is woken up, a new request can be fetched and prepared in parallel to the current running request which means this new request can be started immediately after the current running request completes. With this change read throughput is improved by 16%. Signed-off-by: Konstantin Dorfman Reviewed-by: Seungwon Jeon Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 30 +++++----- drivers/mmc/card/queue.c | 22 ++++++- drivers/mmc/card/queue.h | 3 + drivers/mmc/core/bus.c | 1 + drivers/mmc/core/core.c | 121 ++++++++++++++++++++++++++++++++++++++- drivers/mmc/core/core.h | 1 + include/linux/mmc/card.h | 12 ++++ include/linux/mmc/core.h | 3 +- include/linux/mmc/host.h | 17 ++++++ 9 files changed, 191 insertions(+), 19 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 21056b9ef0a0..f79b4688e471 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -113,17 +113,6 @@ struct mmc_blk_data { static DEFINE_MUTEX(open_lock); -enum mmc_blk_status { - MMC_BLK_SUCCESS = 0, - MMC_BLK_PARTIAL, - MMC_BLK_CMD_ERR, - MMC_BLK_RETRY, - MMC_BLK_ABORT, - MMC_BLK_DATA_ERR, - MMC_BLK_ECC_ERR, - MMC_BLK_NOMEDIUM, -}; - module_param(perdev_minors, int, 0444); MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device"); @@ -1364,8 +1353,11 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) } else areq = NULL; areq = mmc_start_req(card->host, areq, (int *) &status); - if (!areq) + if (!areq) { + if (status == MMC_BLK_NEW_REQUEST) + mq->flags |= MMC_QUEUE_NEW_REQUEST; return 0; + } mq_rq = container_of(areq, struct mmc_queue_req, mmc_active); brq = &mq_rq->brq; @@ -1438,6 +1430,10 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) break; case MMC_BLK_NOMEDIUM: goto cmd_abort; + default: + pr_err("%s: Unhandled return value (%d)", + req->rq_disk->disk_name, status); + goto cmd_abort; } if (ret) { @@ -1472,6 +1468,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) int ret; struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; + struct mmc_host *host = card->host; + unsigned long flags; if (req && !mq->mqrq_prev->req) /* claim host only for the first request */ @@ -1486,6 +1484,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) goto out; } + mq->flags &= ~MMC_QUEUE_NEW_REQUEST; if (req && req->cmd_flags & REQ_DISCARD) { /* complete ongoing async transfer before issuing discard */ if (card->host->areq) @@ -1501,11 +1500,16 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) mmc_blk_issue_rw_rq(mq, NULL); ret = mmc_blk_issue_flush(mq, req); } else { + if (!req && host->areq) { + spin_lock_irqsave(&host->context_info.lock, flags); + host->context_info.is_waiting_last_req = true; + spin_unlock_irqrestore(&host->context_info.lock, flags); + } ret = mmc_blk_issue_rw_rq(mq, req); } out: - if (!req) + if (!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) /* release host only when there are no more requests */ mmc_release_host(card->host); return ret; diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index d630d9861e7b..5e0971016ac5 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -22,7 +22,6 @@ #define MMC_QUEUE_BOUNCESZ 65536 -#define MMC_QUEUE_SUSPENDED (1 << 0) #define MMC_REQ_SPECIAL_MASK (REQ_DISCARD | REQ_FLUSH) @@ -72,6 +71,10 @@ static int mmc_queue_thread(void *d) set_current_state(TASK_RUNNING); cmd_flags = req ? req->cmd_flags : 0; mq->issue_fn(mq, req); + if (mq->flags & MMC_QUEUE_NEW_REQUEST) { + mq->flags &= ~MMC_QUEUE_NEW_REQUEST; + continue; /* fetch again */ + } /* * Current request becomes previous request @@ -113,6 +116,8 @@ static void mmc_request_fn(struct request_queue *q) { struct mmc_queue *mq = q->queuedata; struct request *req; + unsigned long flags; + struct mmc_context_info *cntx; if (!mq) { while ((req = blk_fetch_request(q)) != NULL) { @@ -122,7 +127,20 @@ static void mmc_request_fn(struct request_queue *q) return; } - if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) + cntx = &mq->card->host->context_info; + if (!mq->mqrq_cur->req && mq->mqrq_prev->req) { + /* + * New MMC request arrived when MMC thread may be + * blocked on the previous request to be complete + * with no current request fetched + */ + spin_lock_irqsave(&cntx->lock, flags); + if (cntx->is_waiting_last_req) { + cntx->is_new_req = true; + wake_up_interruptible(&cntx->wait); + } + spin_unlock_irqrestore(&cntx->lock, flags); + } else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) wake_up_process(mq->thread); } diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h index d2a1eb4b9f9f..e20c27b2b8b4 100644 --- a/drivers/mmc/card/queue.h +++ b/drivers/mmc/card/queue.h @@ -27,6 +27,9 @@ struct mmc_queue { struct task_struct *thread; struct semaphore thread_sem; unsigned int flags; +#define MMC_QUEUE_SUSPENDED (1 << 0) +#define MMC_QUEUE_NEW_REQUEST (1 << 1) + int (*issue_fn)(struct mmc_queue *, struct request *); void *data; struct request_queue *queue; diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 420cb6753c1e..e219c97a02a4 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -321,6 +321,7 @@ int mmc_add_card(struct mmc_card *card) #ifdef CONFIG_DEBUG_FS mmc_add_card_debugfs(card); #endif + mmc_init_context_info(card->host); ret = device_add(&card->dev); if (ret) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index aaed7687cf09..8b3a1222e665 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -319,11 +319,44 @@ out: } EXPORT_SYMBOL(mmc_start_bkops); +/* + * mmc_wait_data_done() - done callback for data request + * @mrq: done data request + * + * Wakes up mmc context, passed as a callback to host controller driver + */ +static void mmc_wait_data_done(struct mmc_request *mrq) +{ + mrq->host->context_info.is_done_rcv = true; + wake_up_interruptible(&mrq->host->context_info.wait); +} + static void mmc_wait_done(struct mmc_request *mrq) { complete(&mrq->completion); } +/* + *__mmc_start_data_req() - starts data request + * @host: MMC host to start the request + * @mrq: data request to start + * + * Sets the done callback to be called when request is completed by the card. + * Starts data mmc request execution + */ +static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq) +{ + mrq->done = mmc_wait_data_done; + mrq->host = host; + if (mmc_card_removed(host->card)) { + mrq->cmd->error = -ENOMEDIUM; + return -ENOMEDIUM; + } + mmc_start_request(host, mrq); + + return 0; +} + static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) { init_completion(&mrq->completion); @@ -337,6 +370,62 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) return 0; } +/* + * mmc_wait_for_data_req_done() - wait for request completed + * @host: MMC host to prepare the command. + * @mrq: MMC request to wait for + * + * Blocks MMC context till host controller will ack end of data request + * execution or new request notification arrives from the block layer. + * Handles command retries. + * + * Returns enum mmc_blk_status after checking errors. + */ +static int mmc_wait_for_data_req_done(struct mmc_host *host, + struct mmc_request *mrq, + struct mmc_async_req *next_req) +{ + struct mmc_command *cmd; + struct mmc_context_info *context_info = &host->context_info; + int err; + unsigned long flags; + + while (1) { + wait_event_interruptible(context_info->wait, + (context_info->is_done_rcv || + context_info->is_new_req)); + spin_lock_irqsave(&context_info->lock, flags); + context_info->is_waiting_last_req = false; + spin_unlock_irqrestore(&context_info->lock, flags); + if (context_info->is_done_rcv) { + context_info->is_done_rcv = false; + context_info->is_new_req = false; + cmd = mrq->cmd; + if (!cmd->error || !cmd->retries || + mmc_card_removed(host->card)) { + err = host->areq->err_check(host->card, + host->areq); + break; /* return err */ + } else { + pr_info("%s: req failed (CMD%u): %d, retrying...\n", + mmc_hostname(host), + cmd->opcode, cmd->error); + cmd->retries--; + cmd->error = 0; + host->ops->request(host, mrq); + continue; /* wait for done/new event again */ + } + } else if (context_info->is_new_req) { + context_info->is_new_req = false; + if (!next_req) { + err = MMC_BLK_NEW_REQUEST; + break; /* return err */ + } + } + } + return err; +} + static void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq) { @@ -426,8 +515,17 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, mmc_pre_req(host, areq->mrq, !host->areq); if (host->areq) { - mmc_wait_for_req_done(host, host->areq->mrq); - err = host->areq->err_check(host->card, host->areq); + err = mmc_wait_for_data_req_done(host, host->areq->mrq, + areq); + if (err == MMC_BLK_NEW_REQUEST) { + if (error) + *error = err; + /* + * The previous request was not completed, + * nothing to return + */ + return NULL; + } /* * Check BKOPS urgency for each R1 response */ @@ -439,7 +537,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, } if (!err && areq) - start_err = __mmc_start_req(host, areq->mrq); + start_err = __mmc_start_data_req(host, areq->mrq); if (host->areq) mmc_post_req(host, host->areq->mrq, 0); @@ -2581,6 +2679,23 @@ int mmc_pm_notify(struct notifier_block *notify_block, } #endif +/** + * mmc_init_context_info() - init synchronization context + * @host: mmc host + * + * Init struct context_info needed to implement asynchronous + * request mechanism, used by mmc core, host driver and mmc requests + * supplier. + */ +void mmc_init_context_info(struct mmc_host *host) +{ + spin_lock_init(&host->context_info.lock); + host->context_info.is_new_req = false; + host->context_info.is_done_rcv = false; + host->context_info.is_waiting_last_req = false; + init_waitqueue_head(&host->context_info.wait); +} + static int __init mmc_init(void) { int ret; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 3bdafbca354f..0272b3284b5e 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -76,5 +76,6 @@ void mmc_remove_host_debugfs(struct mmc_host *host); void mmc_add_card_debugfs(struct mmc_card *card); void mmc_remove_card_debugfs(struct mmc_card *card); +void mmc_init_context_info(struct mmc_host *host); #endif diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 5c69315d60cc..be2500a49925 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -187,6 +187,18 @@ struct sdio_func_tuple; #define SDIO_MAX_FUNCS 7 +enum mmc_blk_status { + MMC_BLK_SUCCESS = 0, + MMC_BLK_PARTIAL, + MMC_BLK_CMD_ERR, + MMC_BLK_RETRY, + MMC_BLK_ABORT, + MMC_BLK_DATA_ERR, + MMC_BLK_ECC_ERR, + MMC_BLK_NOMEDIUM, + MMC_BLK_NEW_REQUEST, +}; + /* The number of MMC physical partitions. These consist of: * boot partitions (2), general purpose partitions (4) in MMC v4.4. */ diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 5bf7c2274fcb..495d1336149c 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -120,6 +120,7 @@ struct mmc_data { s32 host_cookie; /* host private data */ }; +struct mmc_host; struct mmc_request { struct mmc_command *sbc; /* SET_BLOCK_COUNT for multiblock */ struct mmc_command *cmd; @@ -128,9 +129,9 @@ struct mmc_request { struct completion completion; void (*done)(struct mmc_request *);/* completion function */ + struct mmc_host *host; }; -struct mmc_host; struct mmc_card; struct mmc_async_req; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c89a1bb87fa5..523d570f58ad 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -170,6 +170,22 @@ struct mmc_slot { void *handler_priv; }; +/** + * mmc_context_info - synchronization details for mmc context + * @is_done_rcv wake up reason was done request + * @is_new_req wake up reason was new request + * @is_waiting_last_req mmc context waiting for single running request + * @wait wait queue + * @lock lock to protect data fields + */ +struct mmc_context_info { + bool is_done_rcv; + bool is_new_req; + bool is_waiting_last_req; + wait_queue_head_t wait; + spinlock_t lock; +}; + struct regulator; struct mmc_supply { @@ -331,6 +347,7 @@ struct mmc_host { struct dentry *debugfs_root; struct mmc_async_req *areq; /* active async req */ + struct mmc_context_info context_info; /* async synchronization info */ #ifdef CONFIG_FAIL_MMC_REQUEST struct fault_attr fail_mmc_request; From 1a94715d4db7b71f825cd5585bc8d653eae1faf4 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Wed, 2 Jan 2013 22:34:49 -0700 Subject: [PATCH 2309/4607] mmc: add BCM2835 driver Add a very simple driver for the BCM2835 SoC, which is used in the Raspberry Pi board. Signed-off-by: Stephen Warren Signed-off-by: Chris Ball --- .../bindings/mmc/brcm,bcm2835-sdhci.txt | 18 ++ drivers/mmc/host/Kconfig | 11 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-bcm2835.c | 227 ++++++++++++++++++ 4 files changed, 257 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt create mode 100644 drivers/mmc/host/sdhci-bcm2835.c diff --git a/Documentation/devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt b/Documentation/devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt new file mode 100644 index 000000000000..59476fbdbfa1 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/brcm,bcm2835-sdhci.txt @@ -0,0 +1,18 @@ +Broadcom BCM2835 SDHCI controller + +This file documents differences between the core properties described +by mmc.txt and the properties that represent the BCM2835 controller. + +Required properties: +- compatible : Should be "brcm,bcm2835-sdhci". +- clocks : The clock feeding the SDHCI controller. + +Example: + +sdhci: sdhci { + compatible = "brcm,bcm2835-sdhci"; + reg = <0x7e300000 0x100>; + interrupts = <2 30>; + clocks = <&clk_mmc>; + bus-width = <4>; +}; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 8d13c6594520..66a54aa68e25 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -241,6 +241,17 @@ config MMC_SDHCI_S3C_DMA YMMV. +config MMC_SDHCI_BCM2835 + tristate "SDHCI platform support for the BCM2835 SD/MMC Controller" + depends on ARCH_BCM2835 + depends on MMC_SDHCI_PLTFM + select MMC_SDHCI_IO_ACCESSORS + help + This selects the BCM2835 SD/MMC controller. If you have a BCM2835 + platform with SD or MMC devices, say Y or M here. + + If unsure, say N. + config MMC_OMAP tristate "TI OMAP Multimedia Card Interface support" depends on ARCH_OMAP diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index e4e218c930bd..d5ea072207ec 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o +obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o ifeq ($(CONFIG_CB710_DEBUG),y) CFLAGS-cb710-mmc += -DDEBUG diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c new file mode 100644 index 000000000000..453825fcc5cf --- /dev/null +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -0,0 +1,227 @@ +/* + * BCM2835 SDHCI + * Copyright (C) 2012 Stephen Warren + * Based on U-Boot's MMC driver for the BCM2835 by Oleksandr Tymoshenko & me + * Portions of the code there were obviously based on the Linux kernel at: + * git://github.com/raspberrypi/linux.git rpi-3.6.y + * commit f5b930b "Main bcm2708 linux port" signed-off-by Dom Cobley. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +#include +#include +#include +#include "sdhci-pltfm.h" + +/* + * 400KHz is max freq for card ID etc. Use that as min card clock. We need to + * know the min to enable static calculation of max BCM2835_SDHCI_WRITE_DELAY. + */ +#define MIN_FREQ 400000 + +/* + * The Arasan has a bugette whereby it may lose the content of successive + * writes to registers that are within two SD-card clock cycles of each other + * (a clock domain crossing problem). It seems, however, that the data + * register does not have this problem, which is just as well - otherwise we'd + * have to nobble the DMA engine too. + * + * This should probably be dynamically calculated based on the actual card + * frequency. However, this is the longest we'll have to wait, and doesn't + * seem to slow access down too much, so the added complexity doesn't seem + * worth it for now. + * + * 1/MIN_FREQ is (max) time per tick of eMMC clock. + * 2/MIN_FREQ is time for two ticks. + * Multiply by 1000000 to get uS per two ticks. + * *1000000 for uSecs. + * +1 for hack rounding. + */ +#define BCM2835_SDHCI_WRITE_DELAY (((2 * 1000000) / MIN_FREQ) + 1) + +struct bcm2835_sdhci { + struct clk *clk; + u32 shadow; +}; + +static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg) +{ + writel(val, host->ioaddr + reg); + + udelay(BCM2835_SDHCI_WRITE_DELAY); +} + +static inline u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg) +{ + u32 val = readl(host->ioaddr + reg); + + if (reg == SDHCI_CAPABILITIES) + val |= SDHCI_CAN_VDD_330; + + return val; +} + +static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct bcm2835_sdhci *bcm2835_host = pltfm_host->priv; + u32 oldval = (reg == SDHCI_COMMAND) ? bcm2835_host->shadow : + bcm2835_sdhci_readl(host, reg & ~3); + u32 word_num = (reg >> 1) & 1; + u32 word_shift = word_num * 16; + u32 mask = 0xffff << word_shift; + u32 newval = (oldval & ~mask) | (val << word_shift); + + if (reg == SDHCI_TRANSFER_MODE) + bcm2835_host->shadow = newval; + else + bcm2835_sdhci_writel(host, newval, reg & ~3); +} + +static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_readl(host, (reg & ~3)); + u32 word_num = (reg >> 1) & 1; + u32 word_shift = word_num * 16; + u32 word = (val >> word_shift) & 0xffff; + + return word; +} + +static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ + u32 oldval = bcm2835_sdhci_readl(host, reg & ~3); + u32 byte_num = reg & 3; + u32 byte_shift = byte_num * 8; + u32 mask = 0xff << byte_shift; + u32 newval = (oldval & ~mask) | (val << byte_shift); + + bcm2835_sdhci_writel(host, newval, reg & ~3); +} + +static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg) +{ + u32 val = bcm2835_sdhci_readl(host, (reg & ~3)); + u32 byte_num = reg & 3; + u32 byte_shift = byte_num * 8; + u32 byte = (val >> byte_shift) & 0xff; + + return byte; +} + +static unsigned int bcm2835_sdhci_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct bcm2835_sdhci *bcm2835_host = pltfm_host->priv; + + return clk_get_rate(bcm2835_host->clk); +} + +unsigned int bcm2835_sdhci_get_min_clock(struct sdhci_host *host) +{ + return MIN_FREQ; +} + +unsigned int bcm2835_sdhci_get_timeout_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct bcm2835_sdhci *bcm2835_host = pltfm_host->priv; + + return clk_get_rate(bcm2835_host->clk); +} + +static struct sdhci_ops bcm2835_sdhci_ops = { + .write_l = bcm2835_sdhci_writel, + .write_w = bcm2835_sdhci_writew, + .write_b = bcm2835_sdhci_writeb, + .read_l = bcm2835_sdhci_readl, + .read_w = bcm2835_sdhci_readw, + .read_b = bcm2835_sdhci_readb, + .get_max_clock = bcm2835_sdhci_get_max_clock, + .get_min_clock = bcm2835_sdhci_get_min_clock, + .get_timeout_clock = bcm2835_sdhci_get_timeout_clock, +}; + +static struct sdhci_pltfm_data bcm2835_sdhci_pdata = { + .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION, + .ops = &bcm2835_sdhci_ops, +}; + +static int bcm2835_sdhci_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + struct bcm2835_sdhci *bcm2835_host; + struct sdhci_pltfm_host *pltfm_host; + int ret; + + host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata); + if (IS_ERR(host)) + return PTR_ERR(host); + + bcm2835_host = devm_kzalloc(&pdev->dev, sizeof(*bcm2835_host), + GFP_KERNEL); + if (!bcm2835_host) { + dev_err(mmc_dev(host->mmc), + "failed to allocate bcm2835_sdhci\n"); + return -ENOMEM; + } + + pltfm_host = sdhci_priv(host); + pltfm_host->priv = bcm2835_host; + + bcm2835_host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(bcm2835_host->clk)) { + ret = PTR_ERR(bcm2835_host->clk); + goto err; + } + + return sdhci_add_host(host); + +err: + sdhci_pltfm_free(pdev); + return ret; +} + +static int bcm2835_sdhci_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); + + sdhci_remove_host(host, dead); + sdhci_pltfm_free(pdev); + + return 0; +} + +static const struct of_device_id bcm2835_sdhci_of_match[] = { + { .compatible = "brcm,bcm2835-sdhci" }, + { } +}; +MODULE_DEVICE_TABLE(of, bcm2835_sdhci_of_match); + +static struct platform_driver bcm2835_sdhci_driver = { + .driver = { + .name = "sdhci-bcm2835", + .owner = THIS_MODULE, + .of_match_table = bcm2835_sdhci_of_match, + .pm = SDHCI_PLTFM_PMOPS, + }, + .probe = bcm2835_sdhci_probe, + .remove = bcm2835_sdhci_remove, +}; +module_platform_driver(bcm2835_sdhci_driver); + +MODULE_DESCRIPTION("BCM2835 SDHCI driver"); +MODULE_AUTHOR("Stephen Warren"); +MODULE_LICENSE("GPL v2"); From b0a8dece55ebe792b394e0c73fa365ad24c3ec32 Mon Sep 17 00:00:00 2001 From: Kevin Liu Date: Sat, 5 Jan 2013 17:18:28 +0800 Subject: [PATCH 2310/4607] mmc: sdhci: disable interrupt before free_irq Current code missed disabling interrupts before free irq which is shared. Notice below comments for function free_irq (kernel/irq/manage.c): On a shared IRQ the caller must ensure the interrupt is disabled on the card it drives before calling this function. Original code has below issue during suspend/resume when multiple SD hosts share the same IRQ: 1. Assume there are two hosts (host1 for emmc while host2 for sd) share the same mmc irq. 2. When system suspend, host2 will be suspended before host1. So the sequence is below: step1: irq handler for host2 removed -> step2: irq handler for host1 removed and irq disabled -> ... system suspended ... ... system resumed ... step3: irq enabled and the irq handler for host1 restored -> step4: irq handler for host2 restored 3. So there is the buggy time slot that the irq is enabled but the irq handler for host2 is removed. Then host2 interrupt can be triggered but can't be handled at that moment. Signed-off-by: Jialing Fu Signed-off-by: Kevin Liu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1b97fe2d70ab..1165376592b9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2487,6 +2487,7 @@ int sdhci_suspend_host(struct sdhci_host *host) return ret; } + sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); free_irq(host->irq, host); return ret; @@ -3142,6 +3143,7 @@ int sdhci_add_host(struct sdhci_host *host) #ifdef SDHCI_USE_LEDS_CLASS reset: sdhci_reset(host, SDHCI_RESET_ALL); + sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); free_irq(host->irq, host); #endif untasklet: @@ -3184,6 +3186,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) if (!dead) sdhci_reset(host, SDHCI_RESET_ALL); + sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); free_irq(host->irq, host); del_timer_sync(&host->timer); From ad080d7916a4701fda29a442a69be4fd54d19a2e Mon Sep 17 00:00:00 2001 From: Kevin Liu Date: Sat, 5 Jan 2013 17:21:33 +0800 Subject: [PATCH 2311/4607] mmc: sdhci: add IRQ wake up support Don't disable SD Host IRQ during suspend if it is wake up source. Enable wakeup event during suspend. Signed-off-by: Jialing Fu Signed-off-by: Kevin Liu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 60 ++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1165376592b9..b93076764ddd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2458,6 +2458,32 @@ out: \*****************************************************************************/ #ifdef CONFIG_PM +void sdhci_enable_irq_wakeups(struct sdhci_host *host) +{ + u8 val; + u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE + | SDHCI_WAKE_ON_INT; + + val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL); + val |= mask ; + /* Avoid fake wake up */ + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + val &= ~(SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE); + sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL); +} +EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups); + +void sdhci_disable_irq_wakeups(struct sdhci_host *host) +{ + u8 val; + u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE + | SDHCI_WAKE_ON_INT; + + val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL); + val &= ~mask; + sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL); +} +EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups); int sdhci_suspend_host(struct sdhci_host *host) { @@ -2487,9 +2513,13 @@ int sdhci_suspend_host(struct sdhci_host *host) return ret; } - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); - free_irq(host->irq, host); - + if (!device_may_wakeup(mmc_dev(host->mmc))) { + sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); + free_irq(host->irq, host); + } else { + sdhci_enable_irq_wakeups(host); + enable_irq_wake(host->irq); + } return ret; } @@ -2504,10 +2534,15 @@ int sdhci_resume_host(struct sdhci_host *host) host->ops->enable_dma(host); } - ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - mmc_hostname(host->mmc), host); - if (ret) - return ret; + if (!device_may_wakeup(mmc_dev(host->mmc))) { + ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, + mmc_hostname(host->mmc), host); + if (ret) + return ret; + } else { + sdhci_disable_irq_wakeups(host); + disable_irq_wake(host->irq); + } if ((host->mmc->pm_flags & MMC_PM_KEEP_POWER) && (host->quirks2 & SDHCI_QUIRK2_HOST_OFF_CARD_ON)) { @@ -2535,17 +2570,6 @@ int sdhci_resume_host(struct sdhci_host *host) } EXPORT_SYMBOL_GPL(sdhci_resume_host); - -void sdhci_enable_irq_wakeups(struct sdhci_host *host) -{ - u8 val; - val = sdhci_readb(host, SDHCI_WAKE_UP_CONTROL); - val |= SDHCI_WAKE_ON_INT; - sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL); -} - -EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups); - #endif /* CONFIG_PM */ #ifdef CONFIG_PM_RUNTIME From 740b7a44aedbe8d916ce21b636f78467f5da1c59 Mon Sep 17 00:00:00 2001 From: Kevin Liu Date: Mon, 14 Jan 2013 14:38:53 -0500 Subject: [PATCH 2312/4607] mmc: sdhci-pxav3: add IRQ wake up support [cjb: The MMP3 architecture requires a registered interrupt to retire wfi when waking from suspend.] Signed-off-by: Jialing Fu Signed-off-by: Kevin Liu Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pxav3.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index b7ee7761bc26..3d20c10fc571 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -311,6 +311,13 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); + if (pdata->pm_caps & MMC_PM_KEEP_POWER) { + device_init_wakeup(&pdev->dev, 1); + host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ; + } else { + device_init_wakeup(&pdev->dev, 0); + } + return 0; err_add_host: From 8213af3b7a4059f13ad8b5beb3e6d69d9301f27d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jan 2013 16:31:08 +0200 Subject: [PATCH 2313/4607] mmc: sdhci: introduce sdhci_update_clock helper to re-enable clock There are three places where same piece of code is used. Let's split it to a separate function. Signed-off-by: Andy Shevchenko Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b93076764ddd..336ab06aeb2f 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1189,6 +1189,15 @@ out: host->clock = clock; } +static inline void sdhci_update_clock(struct sdhci_host *host) +{ + unsigned int clock; + + clock = host->clock; + host->clock = 0; + sdhci_set_clock(host, clock); +} + static int sdhci_set_power(struct sdhci_host *host, unsigned short power) { u8 pwr = 0; @@ -1418,7 +1427,6 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) if (host->version >= SDHCI_SPEC_300) { u16 clk, ctrl_2; - unsigned int clock; /* In case of UHS-I modes, set High Speed Enable */ if ((ios->timing == MMC_TIMING_MMC_HS200) || @@ -1458,9 +1466,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* Re-enable SD Clock */ - clock = host->clock; - host->clock = 0; - sdhci_set_clock(host, clock); + sdhci_update_clock(host); } @@ -1491,9 +1497,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) } /* Re-enable SD Clock */ - clock = host->clock; - host->clock = 0; - sdhci_set_clock(host, clock); + sdhci_update_clock(host); } else sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); @@ -2083,14 +2087,9 @@ static void sdhci_tasklet_finish(unsigned long param) (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { /* Some controllers need this kick or reset won't work here */ - if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) { - unsigned int clock; - + if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) /* This is to force an update */ - clock = host->clock; - host->clock = 0; - sdhci_set_clock(host, clock); - } + sdhci_update_clock(host); /* Spec says we should do both at the same time, but Ricoh controllers do not like that. */ From a043859043042e8bc6f68b40782e3b8c9d10a28d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 15 Jan 2013 23:19:54 +0800 Subject: [PATCH 2314/4607] mmc: sdhci-esdhc-imx: remove ESDHC_CD_GPIO handling from IO accessory With commit 9444e07 (mmc: remove unncessary mmc_gpio_free_cd() call from slot-gpio users) in place, the ESDHC_CD_GPIO handling in IO accessories becomes unnecessary. Remove it. Signed-off-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index dd7fcc137644..ae68bc965ab5 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -150,16 +150,6 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) u32 val = readl(host->ioaddr + reg); - if (unlikely(reg == SDHCI_PRESENT_STATE)) { - /* - * After SDHCI core gets improved to never query - * SDHCI_CARD_PRESENT state in GPIO case, we can - * remove this check. - */ - if (boarddata->cd_type == ESDHC_CD_GPIO) - val &= ~SDHCI_CARD_PRESENT; - } - if (unlikely(reg == SDHCI_CAPABILITIES)) { /* In FSL esdhc IC module, only bit20 is used to indicate the * ADMA2 capability of esdhc, but this bit is messed up on @@ -192,13 +182,6 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) u32 data; if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) { - if (boarddata->cd_type == ESDHC_CD_GPIO) - /* - * These interrupts won't work with a custom - * card_detect gpio (only applied to mx25/35) - */ - val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT); - if (val & SDHCI_INT_CARD_INT) { /* * Clear and then set D3CD bit to avoid missing the From 3724482d4ce4790d4534bcecebfda2c7133244c7 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 16 Jan 2013 14:13:57 +0100 Subject: [PATCH 2315/4607] mmc: mvsdio: use slot-gpio infrastructure for write protect gpio The MMC core subsystem provides in drivers/mmc/core/slot-gpio.c a nice set of helper functions to simplify the management of the write protect GPIO in MMC host drivers. This patch migrates the mvsdio driver to using those helpers, which will make the ->probe() code simpler, and therefore ease the process of adding a Device Tree binding for this driver. Signed-off-by: Thomas Petazzoni Signed-off-by: Andrew Lunn Tested-by: Stefan Peter Tested-by: Florian Fainelli Signed-off-by: Jason Cooper Signed-off-by: Chris Ball --- drivers/mmc/host/mvsdio.c | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index f8dd36102949..c6dc8fd696ab 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,6 @@ struct mvsd_host { struct device *dev; struct clk *clk; int gpio_card_detect; - int gpio_write_protect; }; #define mvsd_write(offs, val) writel(val, iobase + (offs)) @@ -564,20 +564,6 @@ static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable) spin_unlock_irqrestore(&host->lock, flags); } -static int mvsd_get_ro(struct mmc_host *mmc) -{ - struct mvsd_host *host = mmc_priv(mmc); - - if (host->gpio_write_protect) - return gpio_get_value(host->gpio_write_protect); - - /* - * Board doesn't support read only detection; let the mmc core - * decide what to do. - */ - return -ENOSYS; -} - static void mvsd_power_up(struct mvsd_host *host) { void __iomem *iobase = host->base; @@ -674,7 +660,7 @@ static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static const struct mmc_host_ops mvsd_ops = { .request = mvsd_request, - .get_ro = mvsd_get_ro, + .get_ro = mmc_gpio_get_ro, .set_ios = mvsd_set_ios, .enable_sdio_irq = mvsd_enable_sdio_irq, }; @@ -793,15 +779,7 @@ static int __init mvsd_probe(struct platform_device *pdev) if (!host->gpio_card_detect) mmc->caps |= MMC_CAP_NEEDS_POLL; - if (mvsd_data->gpio_write_protect) { - ret = devm_gpio_request_one(&pdev->dev, - mvsd_data->gpio_write_protect, - GPIOF_IN, DRIVER_NAME " wp"); - if (ret == 0) { - host->gpio_write_protect = - mvsd_data->gpio_write_protect; - } - } + mmc_gpio_request_ro(mmc, mvsd_data->gpio_write_protect); setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host); platform_set_drvdata(pdev, mmc); @@ -820,6 +798,7 @@ static int __init mvsd_probe(struct platform_device *pdev) out: if (mmc) { + mmc_gpio_free_ro(mmc); if (!IS_ERR(host->clk)) clk_disable_unprepare(host->clk); mmc_free_host(mmc); @@ -834,6 +813,7 @@ static int __exit mvsd_remove(struct platform_device *pdev) struct mvsd_host *host = mmc_priv(mmc); + mmc_gpio_free_ro(mmc); mmc_remove_host(mmc); del_timer_sync(&host->timer); mvsd_power_down(host); From 07728b77c03dc0721daaf551976d95e6f714af1a Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 16 Jan 2013 14:13:58 +0100 Subject: [PATCH 2316/4607] mmc: mvsdio: use slot-gpio for card detect gpio The MMC core subsystem provides in drivers/mmc/core/slot-gpio.c a nice set of helper functions to simplify the management of the card detect GPIO in MMC host drivers. This patch migrates the mvsdio driver to using those helpers, which will make the ->probe() code simpler, and therefore ease the process of adding a Device Tree binding for this driver. Signed-off-by: Thomas Petazzoni Signed-off-by: Andrew Lunn Tested-by: Stefan Peter Tested-by: Florian Fainelli Signed-off-by: Jason Cooper Signed-off-by: Chris Ball --- drivers/mmc/host/mvsdio.c | 39 +++++++++------------------------------ 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index c6dc8fd696ab..704b7a3fe873 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -52,7 +52,6 @@ struct mvsd_host { struct mmc_host *mmc; struct device *dev; struct clk *clk; - int gpio_card_detect; }; #define mvsd_write(offs, val) writel(val, iobase + (offs)) @@ -538,13 +537,6 @@ static void mvsd_timeout_timer(unsigned long data) mmc_request_done(host->mmc, mrq); } -static irqreturn_t mvsd_card_detect_irq(int irq, void *dev) -{ - struct mvsd_host *host = dev; - mmc_detect_change(host->mmc, msecs_to_jiffies(100)); - return IRQ_HANDLED; -} - static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct mvsd_host *host = mmc_priv(mmc); @@ -757,26 +749,11 @@ static int __init mvsd_probe(struct platform_device *pdev) if (!IS_ERR(host->clk)) clk_prepare_enable(host->clk); - if (mvsd_data->gpio_card_detect) { - ret = devm_gpio_request_one(&pdev->dev, - mvsd_data->gpio_card_detect, - GPIOF_IN, DRIVER_NAME " cd"); - if (ret == 0) { - irq = gpio_to_irq(mvsd_data->gpio_card_detect); - ret = devm_request_irq(&pdev->dev, irq, - mvsd_card_detect_irq, - IRQ_TYPE_EDGE_RISING | - IRQ_TYPE_EDGE_FALLING, - DRIVER_NAME " cd", host); - if (ret == 0) - host->gpio_card_detect = - mvsd_data->gpio_card_detect; - else - devm_gpio_free(&pdev->dev, - mvsd_data->gpio_card_detect); - } - } - if (!host->gpio_card_detect) + if (gpio_is_valid(mvsd_data->gpio_card_detect)) { + ret = mmc_gpio_request_cd(mmc, mvsd_data->gpio_card_detect); + if (ret) + goto out; + } else mmc->caps |= MMC_CAP_NEEDS_POLL; mmc_gpio_request_ro(mmc, mvsd_data->gpio_write_protect); @@ -789,15 +766,16 @@ static int __init mvsd_probe(struct platform_device *pdev) pr_notice("%s: %s driver initialized, ", mmc_hostname(mmc), DRIVER_NAME); - if (host->gpio_card_detect) + if (!(mmc->caps & MMC_CAP_NEEDS_POLL)) printk("using GPIO %d for card detection\n", - host->gpio_card_detect); + mvsd_data->gpio_card_detect); else printk("lacking card detect (fall back to polling)\n"); return 0; out: if (mmc) { + mmc_gpio_free_cd(mmc); mmc_gpio_free_ro(mmc); if (!IS_ERR(host->clk)) clk_disable_unprepare(host->clk); @@ -813,6 +791,7 @@ static int __exit mvsd_remove(struct platform_device *pdev) struct mvsd_host *host = mmc_priv(mmc); + mmc_gpio_free_cd(mmc); mmc_gpio_free_ro(mmc); mmc_remove_host(mmc); del_timer_sync(&host->timer); From 111936ff3bc33585b475c1033fc98cd6b3370a74 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Wed, 16 Jan 2013 14:13:59 +0100 Subject: [PATCH 2317/4607] mmc: mvsdio: implement a Device Tree binding This patch adds a simple Device Tree binding for the mvsdio driver, as well as the necessary documentation for it. Compatibility with non-DT platforms is preserved, by keeping the platform_data based initialization. We introduce a small difference between non-DT and DT platforms: DT platforms are required to provide a clocks = <...> property, which the driver uses to get the frequency of the clock that goes to the SDIO IP. The behaviour on non-DT platforms is kept unchanged: a clock reference is not mandatory, but the clock frequency must be passed in the "clock" field of the mvsdio_platform_data structure. Signed-off-by: Thomas Petazzoni Signed-off-by: Andrew Lunn Tested-by: Stefan Peter Tested-by: Florian Fainelli Signed-off-by: Jason Cooper Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/orion-sdio.txt | 17 +++++ drivers/mmc/host/mvsdio.c | 64 ++++++++++++++----- 2 files changed, 66 insertions(+), 15 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/orion-sdio.txt diff --git a/Documentation/devicetree/bindings/mmc/orion-sdio.txt b/Documentation/devicetree/bindings/mmc/orion-sdio.txt new file mode 100644 index 000000000000..84f0ebd67a13 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/orion-sdio.txt @@ -0,0 +1,17 @@ +* Marvell orion-sdio controller + +This file documents differences between the core properties in mmc.txt +and the properties used by the orion-sdio driver. + +- compatible: Should be "marvell,orion-sdio" +- clocks: reference to the clock of the SDIO interface + +Example: + + mvsdio@d00d4000 { + compatible = "marvell,orion-sdio"; + reg = <0xd00d4000 0x200>; + interrupts = <54>; + clocks = <&gateclk 17>; + status = "disabled"; + }; diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 704b7a3fe873..78d3abf837c2 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -681,17 +683,17 @@ mv_conf_mbus_windows(struct mvsd_host *host, static int __init mvsd_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct mmc_host *mmc = NULL; struct mvsd_host *host = NULL; - const struct mvsdio_platform_data *mvsd_data; const struct mbus_dram_target_info *dram; struct resource *r; int ret, irq; + int gpio_card_detect, gpio_write_protect; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - mvsd_data = pdev->dev.platform_data; - if (!r || irq < 0 || !mvsd_data) + if (!r || irq < 0) return -ENXIO; mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev); @@ -703,8 +705,39 @@ static int __init mvsd_probe(struct platform_device *pdev) host = mmc_priv(mmc); host->mmc = mmc; host->dev = &pdev->dev; - host->base_clock = mvsd_data->clock / 2; - host->clk = ERR_PTR(-EINVAL); + + /* + * Some non-DT platforms do not pass a clock, and the clock + * frequency is passed through platform_data. On DT platforms, + * a clock must always be passed, even if there is no gatable + * clock associated to the SDIO interface (it can simply be a + * fixed rate clock). + */ + host->clk = devm_clk_get(&pdev->dev, NULL); + if (!IS_ERR(host->clk)) + clk_prepare_enable(host->clk); + + if (np) { + if (IS_ERR(host->clk)) { + dev_err(&pdev->dev, "DT platforms must have a clock associated\n"); + ret = -EINVAL; + goto out; + } + + host->base_clock = clk_get_rate(host->clk) / 2; + gpio_card_detect = of_get_named_gpio(np, "cd-gpios", 0); + gpio_write_protect = of_get_named_gpio(np, "wp-gpios", 0); + } else { + const struct mvsdio_platform_data *mvsd_data; + mvsd_data = pdev->dev.platform_data; + if (!mvsd_data) { + ret = -ENXIO; + goto out; + } + host->base_clock = mvsd_data->clock / 2; + gpio_card_detect = mvsd_data->gpio_card_detect; + gpio_write_protect = mvsd_data->gpio_write_protect; + } mmc->ops = &mvsd_ops; @@ -743,20 +776,14 @@ static int __init mvsd_probe(struct platform_device *pdev) goto out; } - /* Not all platforms can gate the clock, so it is not - an error if the clock does not exists. */ - host->clk = devm_clk_get(&pdev->dev, NULL); - if (!IS_ERR(host->clk)) - clk_prepare_enable(host->clk); - - if (gpio_is_valid(mvsd_data->gpio_card_detect)) { - ret = mmc_gpio_request_cd(mmc, mvsd_data->gpio_card_detect); + if (gpio_is_valid(gpio_card_detect)) { + ret = mmc_gpio_request_cd(mmc, gpio_card_detect); if (ret) goto out; } else mmc->caps |= MMC_CAP_NEEDS_POLL; - mmc_gpio_request_ro(mmc, mvsd_data->gpio_write_protect); + mmc_gpio_request_ro(mmc, gpio_write_protect); setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host); platform_set_drvdata(pdev, mmc); @@ -768,7 +795,7 @@ static int __init mvsd_probe(struct platform_device *pdev) mmc_hostname(mmc), DRIVER_NAME); if (!(mmc->caps & MMC_CAP_NEEDS_POLL)) printk("using GPIO %d for card detection\n", - mvsd_data->gpio_card_detect); + gpio_card_detect); else printk("lacking card detect (fall back to polling)\n"); return 0; @@ -832,12 +859,19 @@ static int mvsd_resume(struct platform_device *dev) #define mvsd_resume NULL #endif +static const struct of_device_id mvsdio_dt_ids[] = { + { .compatible = "marvell,orion-sdio" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mvsdio_dt_ids); + static struct platform_driver mvsd_driver = { .remove = __exit_p(mvsd_remove), .suspend = mvsd_suspend, .resume = mvsd_resume, .driver = { .name = DRIVER_NAME, + .of_match_table = mvsdio_dt_ids, }, }; From 33f6984ecefb9b84f1b4d1d3b9022731bb8b62d0 Mon Sep 17 00:00:00 2001 From: Frank Schaefer Date: Thu, 7 Feb 2013 13:32:46 -0300 Subject: [PATCH 2318/4607] [media] em28xx: fix analog streaming with USB bulk transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the conversion to videobuf2, some unnecessary calls of em28xx_set_alternate() have been removed. It is now called at analog streaming start only. This has unveiled a bug that causes USB bulk transfers to fail with all urbs having status -EVOERFLOW. The reason is, that for bulk transfers usb_set_interface() needs to be called even if the previous alt setting was the same (side note: bulk transfers seem to work only with alt=0). While it seems to be NOT necessary for isoc transfers, it's reasonable to just call usb_set_interface() unconditionally in em28xx_set_alternate(). Also add a comment that explains the issue to prevent regressions in the future. Cc: stable@vger.kernel.org # for 3.8 Signed-off-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-core.c | 43 +++++++++++++------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index ee00f9e23420..aaedd11791f2 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -813,12 +813,12 @@ int em28xx_resolution_set(struct em28xx *dev) /* Set USB alternate setting for analog video */ int em28xx_set_alternate(struct em28xx *dev) { - int errCode, prev_alt = dev->alt; + int errCode; int i; unsigned int min_pkt_size = dev->width * 2 + 4; /* NOTE: for isoc transfers, only alt settings > 0 are allowed - for bulk transfers, use alt=0 as default value */ + bulk transfers seem to work only with alt=0 ! */ dev->alt = 0; if ((alt > 0) && (alt < dev->num_alt)) { em28xx_coredbg("alternate forced to %d\n", dev->alt); @@ -849,25 +849,26 @@ int em28xx_set_alternate(struct em28xx *dev) } set_alt: - if (dev->alt != prev_alt) { - if (dev->analog_xfer_bulk) { - dev->max_pkt_size = 512; /* USB 2.0 spec */ - dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; - } else { /* isoc */ - em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", - min_pkt_size, dev->alt); - dev->max_pkt_size = - dev->alt_max_pkt_size_isoc[dev->alt]; - dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; - } - em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", - dev->alt, dev->max_pkt_size); - errCode = usb_set_interface(dev->udev, 0, dev->alt); - if (errCode < 0) { - em28xx_errdev("cannot change alternate number to %d (error=%i)\n", - dev->alt, errCode); - return errCode; - } + /* NOTE: for bulk transfers, we need to call usb_set_interface() + * even if the previous settings were the same. Otherwise streaming + * fails with all urbs having status = -EOVERFLOW ! */ + if (dev->analog_xfer_bulk) { + dev->max_pkt_size = 512; /* USB 2.0 spec */ + dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; + } else { /* isoc */ + em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); + dev->max_pkt_size = + dev->alt_max_pkt_size_isoc[dev->alt]; + dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; + } + em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", + dev->alt, dev->max_pkt_size); + errCode = usb_set_interface(dev->udev, 0, dev->alt); + if (errCode < 0) { + em28xx_errdev("cannot change alternate number to %d (error=%i)\n", + dev->alt, errCode); + return errCode; } return 0; } From cfb046cb800ba306b211fbbe4ac633486e11055f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 9 Feb 2013 05:40:10 -0300 Subject: [PATCH 2319/4607] [media] cx18/ivtv: fix regression: remove __init from a non-init function Commits 5e6e81b2890db3969527772a8350825a85c22d5c (cx18) and 2aebbf6737212265b917ed27c875c59d3037110a (ivtv) added an __init annotation to the cx18-alsa-load and ivtv-alsa-load functions. However, these functions are called *after* initialization by the main cx18/ivtv driver. By that time the memory containing those functions is already freed and your machine goes BOOM. Cc: stable@vger.kernel.org # for 3.8 Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/pci/cx18/cx18-alsa-main.c | 2 +- drivers/media/pci/cx18/cx18-alsa-pcm.h | 2 +- drivers/media/pci/ivtv/ivtv-alsa-main.c | 2 +- drivers/media/pci/ivtv/ivtv-alsa-pcm.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/pci/cx18/cx18-alsa-main.c b/drivers/media/pci/cx18/cx18-alsa-main.c index 8e971ff60588..b2c8c3439fea 100644 --- a/drivers/media/pci/cx18/cx18-alsa-main.c +++ b/drivers/media/pci/cx18/cx18-alsa-main.c @@ -197,7 +197,7 @@ err_exit: return ret; } -static int __init cx18_alsa_load(struct cx18 *cx) +static int cx18_alsa_load(struct cx18 *cx) { struct v4l2_device *v4l2_dev = &cx->v4l2_dev; struct cx18_stream *s; diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.h b/drivers/media/pci/cx18/cx18-alsa-pcm.h index d26e51f94577..e2b2c5b01215 100644 --- a/drivers/media/pci/cx18/cx18-alsa-pcm.h +++ b/drivers/media/pci/cx18/cx18-alsa-pcm.h @@ -20,7 +20,7 @@ * 02111-1307 USA */ -int __init snd_cx18_pcm_create(struct snd_cx18_card *cxsc); +int snd_cx18_pcm_create(struct snd_cx18_card *cxsc); /* Used by cx18-mailbox to announce the PCM data to the module */ void cx18_alsa_announce_pcm_data(struct snd_cx18_card *card, u8 *pcm_data, diff --git a/drivers/media/pci/ivtv/ivtv-alsa-main.c b/drivers/media/pci/ivtv/ivtv-alsa-main.c index 4a221c693995..e970cface70e 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-main.c +++ b/drivers/media/pci/ivtv/ivtv-alsa-main.c @@ -205,7 +205,7 @@ err_exit: return ret; } -static int __init ivtv_alsa_load(struct ivtv *itv) +static int ivtv_alsa_load(struct ivtv *itv) { struct v4l2_device *v4l2_dev = &itv->v4l2_dev; struct ivtv_stream *s; diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.h b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h index 23dfe0d12400..186814e0b2d4 100644 --- a/drivers/media/pci/ivtv/ivtv-alsa-pcm.h +++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h @@ -20,4 +20,4 @@ * 02111-1307 USA */ -int __init snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc); +int snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc); From e56a316214d0f1e2446fa7a717309f9414564d9d Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Mon, 11 Feb 2013 16:21:42 -0500 Subject: [PATCH 2320/4607] nfsd4: free_stid can be static Reported-by: Fengguang Wu --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 60a2282905e0..c1a6ddf3a84a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -350,7 +350,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv return dp; } -void free_stid(struct nfs4_stid *s, struct kmem_cache *slab) +static void free_stid(struct nfs4_stid *s, struct kmem_cache *slab) { struct idr *stateids = &s->sc_client->cl_stateids; From 4fd0a21353bd40a2f1f726faf0d74cf8cdcbce7f Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 8 Feb 2013 08:02:50 +0000 Subject: [PATCH 2321/4607] powerpc/5200: Add Lite5200 on-board LEDs as devices The Lite5200 evaluation board has a number of debug LEDs that Linux doesn't know about yet. This change adds a gpio-leds stanza to the lite5200 device tree so that the correct driver can get hooked up. Also, make use of the dtc labels feature to reduce the number of source lines required to add the gpio-controller property to the general purpose timer nodes. In addition, the required #gpio-cells properties are added to the common mpc5200b dtsi include file so that each board doesn't need to add them explicitly. This still doesn't enable gpio mode, 'gpio-controller' is required for that, but it means less work needs to be done by board ports. Cc: Anatolij Gustschin Signed-off-by: Grant Likely --- arch/powerpc/boot/dts/lite5200b.dts | 23 +++++++++++++++++++---- arch/powerpc/boot/dts/mpc5200b.dtsi | 25 +++++++++++++++++-------- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/boot/dts/lite5200b.dts b/arch/powerpc/boot/dts/lite5200b.dts index fb288bb882b6..5abb46c5cc95 100644 --- a/arch/powerpc/boot/dts/lite5200b.dts +++ b/arch/powerpc/boot/dts/lite5200b.dts @@ -12,19 +12,34 @@ /include/ "mpc5200b.dtsi" +&gpt0 { fsl,has-wdt; }; +&gpt2 { gpio-controller; }; +&gpt3 { gpio-controller; }; + / { model = "fsl,lite5200b"; compatible = "fsl,lite5200b"; + leds { + compatible = "gpio-leds"; + tmr2 { + gpios = <&gpt2 0 1>; + }; + tmr3 { + gpios = <&gpt3 0 1>; + linux,default-trigger = "heartbeat"; + }; + led1 { gpios = <&gpio_wkup 2 1>; }; + led2 { gpios = <&gpio_simple 3 1>; }; + led3 { gpios = <&gpio_wkup 3 1>; }; + led4 { gpios = <&gpio_simple 2 1>; }; + }; + memory { reg = <0x00000000 0x10000000>; // 256MB }; soc5200@f0000000 { - timer@600 { // General Purpose Timer - fsl,has-wdt; - }; - psc@2000 { // PSC1 compatible = "fsl,mpc5200b-psc-uart","fsl,mpc5200-psc-uart"; cell-index = <0>; diff --git a/arch/powerpc/boot/dts/mpc5200b.dtsi b/arch/powerpc/boot/dts/mpc5200b.dtsi index 39ed65a44c5f..969b2200b2f9 100644 --- a/arch/powerpc/boot/dts/mpc5200b.dtsi +++ b/arch/powerpc/boot/dts/mpc5200b.dtsi @@ -64,50 +64,59 @@ reg = <0x500 0x80>; }; - timer@600 { // General Purpose Timer + gpt0: timer@600 { // General Purpose Timer compatible = "fsl,mpc5200b-gpt","fsl,mpc5200-gpt"; + #gpio-cells = <2>; // Add 'gpio-controller;' to enable gpio mode reg = <0x600 0x10>; interrupts = <1 9 0>; + // add 'fsl,has-wdt' to enable watchdog }; - timer@610 { // General Purpose Timer + gpt1: timer@610 { // General Purpose Timer compatible = "fsl,mpc5200b-gpt","fsl,mpc5200-gpt"; + #gpio-cells = <2>; // Add 'gpio-controller;' to enable gpio mode reg = <0x610 0x10>; interrupts = <1 10 0>; }; - timer@620 { // General Purpose Timer + gpt2: timer@620 { // General Purpose Timer compatible = "fsl,mpc5200b-gpt","fsl,mpc5200-gpt"; + #gpio-cells = <2>; // Add 'gpio-controller;' to enable gpio mode reg = <0x620 0x10>; interrupts = <1 11 0>; }; - timer@630 { // General Purpose Timer + gpt3: timer@630 { // General Purpose Timer compatible = "fsl,mpc5200b-gpt","fsl,mpc5200-gpt"; + #gpio-cells = <2>; // Add 'gpio-controller;' to enable gpio mode reg = <0x630 0x10>; interrupts = <1 12 0>; }; - timer@640 { // General Purpose Timer + gpt4: timer@640 { // General Purpose Timer compatible = "fsl,mpc5200b-gpt","fsl,mpc5200-gpt"; + #gpio-cells = <2>; // Add 'gpio-controller;' to enable gpio mode reg = <0x640 0x10>; interrupts = <1 13 0>; }; - timer@650 { // General Purpose Timer + gpt5: timer@650 { // General Purpose Timer compatible = "fsl,mpc5200b-gpt","fsl,mpc5200-gpt"; + #gpio-cells = <2>; // Add 'gpio-controller;' to enable gpio mode reg = <0x650 0x10>; interrupts = <1 14 0>; }; - timer@660 { // General Purpose Timer + gpt6: timer@660 { // General Purpose Timer compatible = "fsl,mpc5200b-gpt","fsl,mpc5200-gpt"; + #gpio-cells = <2>; // Add 'gpio-controller;' to enable gpio mode reg = <0x660 0x10>; interrupts = <1 15 0>; }; - timer@670 { // General Purpose Timer + gpt7: timer@670 { // General Purpose Timer compatible = "fsl,mpc5200b-gpt","fsl,mpc5200-gpt"; + #gpio-cells = <2>; // Add 'gpio-controller;' to enable gpio mode reg = <0x670 0x10>; interrupts = <1 16 0>; }; From cdc69ae98b3d8ff337c564e16f497c8816d1a51b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 11 Feb 2013 19:38:59 -0200 Subject: [PATCH 2322/4607] Revert "[media] fc0011: Return early, if the frequency is already tuned" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a92591a7112042f92b609be42bc332d989776e9b. From: Michael Büsch To: linux-media@vger.kernel.org Cc: mchehab@redhat.com Subject: Re: [git:v4l-dvb/for_v3.9] [media] fc0011: Return early, if the frequency is already tuned Date: Mon, 11 Feb 2013 21:59:19 +0100 Can you please revert this one again? It might cause issues if the dvb device is reset/reinitialized. Requested-by: Michael Büsch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/tuners/fc0011.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index 18caab11c94b..3932aa81e18c 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -187,9 +187,6 @@ static int fc0011_set_params(struct dvb_frontend *fe) u8 fa, fp, vco_sel, vco_cal; u8 regs[FC11_NR_REGS] = { }; - if (priv->frequency == p->frequency) - return 0; - regs[FC11_REG_7] = 0x0F; regs[FC11_REG_8] = 0x3E; regs[FC11_REG_10] = 0xB8; From fa59f178552f927bd96771ba84e9706655bea705 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 8 Feb 2013 08:02:51 +0000 Subject: [PATCH 2323/4607] powerpc/5200: Use the gpt* labels to simplify mpc5200 dts files The DTC labels feature allows a dts file to reference a node without having to reproduce the entire node hierarchy above it. We can use this to simplify the MPC5200 board dts files by referencing the gpt nodes by label. Cc: Anatolij Gustschin Signed-off-by: Grant Likely [agust: fixed gpt7 phandle in the csi node of o2d.dtsi] Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/a3m071.dts | 6 ++-- arch/powerpc/boot/dts/a4m072.dts | 27 +++------------ arch/powerpc/boot/dts/cm5200.dts | 6 ++-- arch/powerpc/boot/dts/digsy_mtc.dts | 14 ++------ arch/powerpc/boot/dts/media5200.dts | 6 ++-- arch/powerpc/boot/dts/motionpro.dts | 26 ++++++--------- arch/powerpc/boot/dts/mucmc52.dts | 48 +++++++------------------- arch/powerpc/boot/dts/o2d.dtsi | 27 +++++---------- arch/powerpc/boot/dts/pcm030.dts | 48 +++++--------------------- arch/powerpc/boot/dts/pcm032.dts | 45 +++++-------------------- arch/powerpc/boot/dts/uc101.dts | 52 +++++------------------------ 11 files changed, 70 insertions(+), 235 deletions(-) diff --git a/arch/powerpc/boot/dts/a3m071.dts b/arch/powerpc/boot/dts/a3m071.dts index 877a28cb77e4..bf81b8f9704c 100644 --- a/arch/powerpc/boot/dts/a3m071.dts +++ b/arch/powerpc/boot/dts/a3m071.dts @@ -17,6 +17,8 @@ /include/ "mpc5200b.dtsi" +&gpt0 { fsl,has-wdt; }; + / { model = "anonymous,a3m071"; compatible = "anonymous,a3m071"; @@ -30,10 +32,6 @@ bus-frequency = <0>; /* From boot loader */ system-frequency = <0>; /* From boot loader */ - timer@600 { - fsl,has-wdt; - }; - spi@f00 { status = "disabled"; }; diff --git a/arch/powerpc/boot/dts/a4m072.dts b/arch/powerpc/boot/dts/a4m072.dts index fabe7b7d5f13..1f02034c7e99 100644 --- a/arch/powerpc/boot/dts/a4m072.dts +++ b/arch/powerpc/boot/dts/a4m072.dts @@ -15,6 +15,11 @@ /include/ "mpc5200b.dtsi" +&gpt0 { fsl,has-wdt; }; +&gpt3 { gpio-controller; }; +&gpt4 { gpio-controller; }; +&gpt5 { gpio-controller; }; + / { model = "anonymous,a4m072"; compatible = "anonymous,a4m072"; @@ -34,28 +39,6 @@ fsl,init-fd-counters = <0x3333>; }; - timer@600 { - fsl,has-wdt; - }; - - gpt3: timer@630 { /* General Purpose Timer in GPIO mode */ - compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - - gpt4: timer@640 { /* General Purpose Timer in GPIO mode */ - compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - - gpt5: timer@650 { /* General Purpose Timer in GPIO mode */ - compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - spi@f00 { status = "disabled"; }; diff --git a/arch/powerpc/boot/dts/cm5200.dts b/arch/powerpc/boot/dts/cm5200.dts index ad3a4f4a2b04..fb580dd84ddf 100644 --- a/arch/powerpc/boot/dts/cm5200.dts +++ b/arch/powerpc/boot/dts/cm5200.dts @@ -12,15 +12,13 @@ /include/ "mpc5200b.dtsi" +&gpt0 { fsl,has-wdt; }; + / { model = "schindler,cm5200"; compatible = "schindler,cm5200"; soc5200@f0000000 { - timer@600 { // General Purpose Timer - fsl,has-wdt; - }; - can@900 { status = "disabled"; }; diff --git a/arch/powerpc/boot/dts/digsy_mtc.dts b/arch/powerpc/boot/dts/digsy_mtc.dts index a7511f2d844d..955bff629df3 100644 --- a/arch/powerpc/boot/dts/digsy_mtc.dts +++ b/arch/powerpc/boot/dts/digsy_mtc.dts @@ -13,6 +13,9 @@ /include/ "mpc5200b.dtsi" +&gpt0 { gpio-controller; fsl,has-wdt; }; +&gpt1 { gpio-controller; }; + / { model = "intercontrol,digsy-mtc"; compatible = "intercontrol,digsy-mtc"; @@ -22,17 +25,6 @@ }; soc5200@f0000000 { - timer@600 { // General Purpose Timer - #gpio-cells = <2>; - fsl,has-wdt; - gpio-controller; - }; - - timer@610 { - #gpio-cells = <2>; - gpio-controller; - }; - rtc@800 { status = "disabled"; }; diff --git a/arch/powerpc/boot/dts/media5200.dts b/arch/powerpc/boot/dts/media5200.dts index 48d72f38e5ed..b5413cb85f13 100644 --- a/arch/powerpc/boot/dts/media5200.dts +++ b/arch/powerpc/boot/dts/media5200.dts @@ -13,6 +13,8 @@ /include/ "mpc5200b.dtsi" +&gpt0 { fsl,has-wdt; }; + / { model = "fsl,media5200"; compatible = "fsl,media5200"; @@ -41,10 +43,6 @@ soc5200@f0000000 { bus-frequency = <132000000>;// 132 MHz - timer@600 { // General Purpose Timer - fsl,has-wdt; - }; - psc@2000 { // PSC1 status = "disabled"; }; diff --git a/arch/powerpc/boot/dts/motionpro.dts b/arch/powerpc/boot/dts/motionpro.dts index 0b78e89ac69b..bbabd97492ad 100644 --- a/arch/powerpc/boot/dts/motionpro.dts +++ b/arch/powerpc/boot/dts/motionpro.dts @@ -12,26 +12,22 @@ /include/ "mpc5200b.dtsi" +&gpt0 { fsl,has-wdt; }; +&gpt6 { // Motion-PRO status LED + compatible = "promess,motionpro-led"; + label = "motionpro-statusled"; + blink-delay = <100>; // 100 msec +}; +&gpt7 { // Motion-PRO ready LED + compatible = "promess,motionpro-led"; + label = "motionpro-readyled"; +}; + / { model = "promess,motionpro"; compatible = "promess,motionpro"; soc5200@f0000000 { - timer@600 { // General Purpose Timer - fsl,has-wdt; - }; - - timer@660 { // Motion-PRO status LED - compatible = "promess,motionpro-led"; - label = "motionpro-statusled"; - blink-delay = <100>; // 100 msec - }; - - timer@670 { // Motion-PRO ready LED - compatible = "promess,motionpro-led"; - label = "motionpro-readyled"; - }; - can@900 { status = "disabled"; }; diff --git a/arch/powerpc/boot/dts/mucmc52.dts b/arch/powerpc/boot/dts/mucmc52.dts index 21d34720fcc9..d3a792bb5c1a 100644 --- a/arch/powerpc/boot/dts/mucmc52.dts +++ b/arch/powerpc/boot/dts/mucmc52.dts @@ -13,47 +13,23 @@ /include/ "mpc5200b.dtsi" +/* Timer pins that need to be in GPIO mode */ +&gpt0 { gpio-controller; }; +&gpt1 { gpio-controller; }; +&gpt2 { gpio-controller; }; +&gpt3 { gpio-controller; }; + +/* Disabled timers */ +&gpt4 { status = "disabled"; }; +&gpt5 { status = "disabled"; }; +&gpt6 { status = "disabled"; }; +&gpt7 { status = "disabled"; }; + / { model = "manroland,mucmc52"; compatible = "manroland,mucmc52"; soc5200@f0000000 { - gpt0: timer@600 { // GPT 0 in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt1: timer@610 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt2: timer@620 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt3: timer@630 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - timer@640 { - status = "disabled"; - }; - - timer@650 { - status = "disabled"; - }; - - timer@660 { - status = "disabled"; - }; - - timer@670 { - status = "disabled"; - }; - rtc@800 { status = "disabled"; }; diff --git a/arch/powerpc/boot/dts/o2d.dtsi b/arch/powerpc/boot/dts/o2d.dtsi index 24f668039295..cf073e693f24 100644 --- a/arch/powerpc/boot/dts/o2d.dtsi +++ b/arch/powerpc/boot/dts/o2d.dtsi @@ -12,6 +12,13 @@ /include/ "mpc5200b.dtsi" +&gpt0 { + gpio-controller; + fsl,has-wdt; + fsl,wdt-on-boot = <0>; +}; +&gpt1 { gpio-controller; }; + / { model = "ifm,o2d"; compatible = "ifm,o2d"; @@ -22,24 +29,6 @@ soc5200@f0000000 { - gpio_simple: gpio@b00 { - }; - - timer@600 { // General Purpose Timer - #gpio-cells = <2>; - gpio-controller; - fsl,has-wdt; - fsl,wdt-on-boot = <0>; - }; - - timer@610 { - #gpio-cells = <2>; - gpio-controller; - }; - - timer7: timer@670 { - }; - rtc@800 { status = "disabled"; }; @@ -118,7 +107,7 @@ csi@3,0 { compatible = "ifm,o2d-csi"; reg = <3 0 0x00100000>; - ifm,csi-clk-handle = <&timer7>; + ifm,csi-clk-handle = <&gpt7>; gpios = <&gpio_simple 23 0 /* imag_capture */ &gpio_simple 26 0 /* imag_reset */ &gpio_simple 29 0>; /* imag_master_en */ diff --git a/arch/powerpc/boot/dts/pcm030.dts b/arch/powerpc/boot/dts/pcm030.dts index 96512c058033..192e66af0001 100644 --- a/arch/powerpc/boot/dts/pcm030.dts +++ b/arch/powerpc/boot/dts/pcm030.dts @@ -14,51 +14,19 @@ /include/ "mpc5200b.dtsi" +&gpt0 { fsl,has-wdt; }; +&gpt2 { gpio-controller; }; +&gpt3 { gpio-controller; }; +&gpt4 { gpio-controller; }; +&gpt5 { gpio-controller; }; +&gpt6 { gpio-controller; }; +&gpt7 { gpio-controller; }; + / { model = "phytec,pcm030"; compatible = "phytec,pcm030"; soc5200@f0000000 { - timer@600 { // General Purpose Timer - fsl,has-wdt; - }; - - gpt2: timer@620 { // General Purpose Timer in GPIO mode - compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - - gpt3: timer@630 { // General Purpose Timer in GPIO mode - compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - - gpt4: timer@640 { // General Purpose Timer in GPIO mode - compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - - gpt5: timer@650 { // General Purpose Timer in GPIO mode - compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - - gpt6: timer@660 { // General Purpose Timer in GPIO mode - compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - - gpt7: timer@670 { // General Purpose Timer in GPIO mode - compatible = "fsl,mpc5200b-gpt-gpio","fsl,mpc5200-gpt-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; - audioplatform: psc@2000 { /* PSC1 in ac97 mode */ compatible = "mpc5200b-psc-ac97","fsl,mpc5200b-psc-ac97"; cell-index = <0>; diff --git a/arch/powerpc/boot/dts/pcm032.dts b/arch/powerpc/boot/dts/pcm032.dts index 1dd478bfff96..96b139bf50e9 100644 --- a/arch/powerpc/boot/dts/pcm032.dts +++ b/arch/powerpc/boot/dts/pcm032.dts @@ -14,6 +14,14 @@ /include/ "mpc5200b.dtsi" +&gpt0 { fsl,has-wdt; }; +&gpt2 { gpio-controller; }; +&gpt3 { gpio-controller; }; +&gpt4 { gpio-controller; }; +&gpt5 { gpio-controller; }; +&gpt6 { gpio-controller; }; +&gpt7 { gpio-controller; }; + / { model = "phytec,pcm032"; compatible = "phytec,pcm032"; @@ -23,43 +31,6 @@ }; soc5200@f0000000 { - timer@600 { // General Purpose Timer - fsl,has-wdt; - }; - - gpt2: timer@620 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt3: timer@630 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt4: timer@640 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt5: timer@650 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt6: timer@660 { // General Purpose Timer in GPIO mode - compatible = "fsl,mpc5200b-gpt","fsl,mpc5200-gpt"; - reg = <0x660 0x10>; - interrupts = <1 15 0>; - gpio-controller; - #gpio-cells = <2>; - }; - - gpt7: timer@670 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - psc@2000 { /* PSC1 is ac97 */ compatible = "fsl,mpc5200b-psc-ac97","fsl,mpc5200-psc-ac97"; cell-index = <0>; diff --git a/arch/powerpc/boot/dts/uc101.dts b/arch/powerpc/boot/dts/uc101.dts index ba83d5488ec6..5c462194ef06 100644 --- a/arch/powerpc/boot/dts/uc101.dts +++ b/arch/powerpc/boot/dts/uc101.dts @@ -13,54 +13,20 @@ /include/ "mpc5200b.dtsi" +&gpt0 { gpio-controller; }; +&gpt1 { gpio-controller; }; +&gpt2 { gpio-controller; }; +&gpt3 { gpio-controller; }; +&gpt4 { gpio-controller; }; +&gpt5 { gpio-controller; }; +&gpt6 { gpio-controller; }; +&gpt7 { gpio-controller; }; + / { model = "manroland,uc101"; compatible = "manroland,uc101"; soc5200@f0000000 { - gpt0: timer@600 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt1: timer@610 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt2: timer@620 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt3: timer@630 { // General Purpose Timer in GPIO mode - compatible = "fsl,mpc5200b-gpt","fsl,mpc5200-gpt"; - reg = <0x630 0x10>; - interrupts = <1 12 0>; - gpio-controller; - #gpio-cells = <2>; - }; - - gpt4: timer@640 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt5: timer@650 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt6: timer@660 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - - gpt7: timer@670 { // General Purpose Timer in GPIO mode - gpio-controller; - #gpio-cells = <2>; - }; - rtc@800 { status = "disabled"; }; From 7d79e75f6420fc13cfe72554b3ea5afad24c8625 Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Wed, 23 Jan 2013 09:40:23 +0900 Subject: [PATCH 2324/4607] f2fs: save device node number into f2fs_inode This patch stores inode->i_rdev into on-disk inode structure. Alun reported that: aspire tmp # mount -t f2fs /dev/sdb mnt aspire tmp # mknod mnt/sda1 b 8 1 aspire tmp # mknod mnt/null c 1 3 aspire tmp # mknod mnt/console c 5 1 aspire tmp # ls -l mnt total 2 crw-r--r-- 1 root root 5, 1 Jan 22 18:44 console crw-r--r-- 1 root root 1, 3 Jan 22 18:44 null brw-r--r-- 1 root root 8, 1 Jan 22 18:44 sda1 aspire tmp # umount mnt aspire tmp # mount -t f2fs /dev/sdb mnt aspire tmp # ls -l mnt total 2 crw-r--r-- 1 root root 0, 0 Jan 22 18:44 console crw-r--r-- 1 root root 0, 0 Jan 22 18:44 null brw-r--r-- 1 root root 0, 0 Jan 22 18:44 sda1 In this report, f2fs lost the major/minor numbers of device files after umount. The reason was revealed that f2fs does not store the inode->i_rdev to the on-disk inode data structure. So, as the other file systems do, f2fs also stores i_rdev into the i_addr fields in on-disk inode structure without any on-disk layout changes. Note that, this bug is limited to device files made by mknod(). Reported-and-Tested-by: Alun Jones Signed-off-by: Changman Lee Signed-off-by: Jaegeuk Kim --- fs/f2fs/inode.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 794241777322..340a21be5a76 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -100,6 +100,10 @@ static int do_read_inode(struct inode *inode) inode->i_ctime.tv_nsec = le32_to_cpu(ri->i_ctime_nsec); inode->i_mtime.tv_nsec = le32_to_cpu(ri->i_mtime_nsec); inode->i_generation = le32_to_cpu(ri->i_generation); + if (ri->i_addr[0]) + inode->i_rdev = old_decode_dev(le32_to_cpu(ri->i_addr[0])); + else + inode->i_rdev = new_decode_dev(le32_to_cpu(ri->i_addr[1])); fi->i_current_depth = le32_to_cpu(ri->i_current_depth); fi->i_xattr_nid = le32_to_cpu(ri->i_xattr_nid); @@ -203,6 +207,20 @@ void update_inode(struct inode *inode, struct page *node_page) ri->i_flags = cpu_to_le32(F2FS_I(inode)->i_flags); ri->i_pino = cpu_to_le32(F2FS_I(inode)->i_pino); ri->i_generation = cpu_to_le32(inode->i_generation); + + if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { + if (old_valid_dev(inode->i_rdev)) { + ri->i_addr[0] = + cpu_to_le32(old_encode_dev(inode->i_rdev)); + ri->i_addr[1] = 0; + } else { + ri->i_addr[0] = 0; + ri->i_addr[1] = + cpu_to_le32(new_encode_dev(inode->i_rdev)); + ri->i_addr[2] = 0; + } + } + set_cold_node(inode, node_page); set_page_dirty(node_page); } From 577e349514452fa3fcd99fd06e587b02d3d1cf28 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 24 Jan 2013 19:56:11 +0900 Subject: [PATCH 2325/4607] f2fs: prevent checkpoint once any IO failure is detected This patch enhances the checkpoint routine to cope with IO errors. Basically f2fs detects IO errors from end_io_write, and the errors are able to be occurred during one of data, node, and meta page writes. In the previous code, when an IO error is occurred during writes, f2fs sets a flag, CP_ERROR_FLAG, in the raw ckeckpoint buffer which will be written to disk. Afterwards, write_checkpoint() will check the flag and remount f2fs as a read-only (ro) mode. However, even once f2fs is remounted as a ro mode, dirty checkpoint pages are freely able to be written to disk by flusher or kswapd in background. In such a case, after cold reboot, f2fs would restore the checkpoint data having CP_ERROR_FLAG, resulting in disabling write_checkpoint and remounting f2fs as a ro mode again. Therefore, let's prevent any checkpoint page (meta) writes once an IO error is occurred, and remount f2fs as a ro mode right away at that moment. Reported-by: Oliver Winker Signed-off-by: Jaegeuk Kim Reviewed-by: Namjae Jeon --- fs/f2fs/checkpoint.c | 40 +++++++++++++++++++++------------------- fs/f2fs/f2fs.h | 3 +-- fs/f2fs/segment.c | 8 ++------ fs/f2fs/super.c | 12 +++++++++--- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index ff3c8439af87..9c1627165039 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -72,22 +72,22 @@ static int f2fs_write_meta_page(struct page *page, { struct inode *inode = page->mapping->host; struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - int err; + + /* Should not write any meta pages, if any IO error was occurred */ + if (wbc->for_reclaim || + is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ERROR_FLAG)) { + dec_page_count(sbi, F2FS_DIRTY_META); + wbc->pages_skipped++; + set_page_dirty(page); + return AOP_WRITEPAGE_ACTIVATE; + } wait_on_page_writeback(page); - err = write_meta_page(sbi, page, wbc); - if (err) { - wbc->pages_skipped++; - set_page_dirty(page); - } - + write_meta_page(sbi, page); dec_page_count(sbi, F2FS_DIRTY_META); - - /* In this case, we should not unlock this page */ - if (err != AOP_WRITEPAGE_ACTIVATE) - unlock_page(page); - return err; + unlock_page(page); + return 0; } static int f2fs_write_meta_pages(struct address_space *mapping, @@ -138,7 +138,10 @@ long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, BUG_ON(page->mapping != mapping); BUG_ON(!PageDirty(page)); clear_page_dirty_for_io(page); - f2fs_write_meta_page(page, &wbc); + if (f2fs_write_meta_page(page, &wbc)) { + unlock_page(page); + break; + } if (nwritten++ >= nr_to_write) break; } @@ -717,13 +720,12 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) sbi->alloc_valid_block_count = 0; /* Here, we only have one bio having CP pack */ - if (is_set_ckpt_flags(ckpt, CP_ERROR_FLAG)) - sbi->sb->s_flags |= MS_RDONLY; - else - sync_meta_pages(sbi, META_FLUSH, LONG_MAX); + sync_meta_pages(sbi, META_FLUSH, LONG_MAX); - clear_prefree_segments(sbi); - F2FS_RESET_SB_DIRT(sbi); + if (!is_set_ckpt_flags(ckpt, CP_ERROR_FLAG)) { + clear_prefree_segments(sbi); + F2FS_RESET_SB_DIRT(sbi); + } } /* diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index c8e2d751ef9c..f4f509766465 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -929,8 +929,7 @@ void allocate_new_segments(struct f2fs_sb_info *); struct page *get_sum_page(struct f2fs_sb_info *, unsigned int); struct bio *f2fs_bio_alloc(struct block_device *, int); void f2fs_submit_bio(struct f2fs_sb_info *, enum page_type, bool sync); -int write_meta_page(struct f2fs_sb_info *, struct page *, - struct writeback_control *); +void write_meta_page(struct f2fs_sb_info *, struct page *); void write_node_page(struct f2fs_sb_info *, struct page *, unsigned int, block_t, block_t *); void write_data_page(struct inode *, struct page *, struct dnode_of_data*, diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 4b0099066582..7aa270f3538a 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -600,6 +600,7 @@ static void f2fs_end_io_write(struct bio *bio, int err) if (page->mapping) set_bit(AS_EIO, &page->mapping->flags); set_ckpt_flags(p->sbi->ckpt, CP_ERROR_FLAG); + p->sbi->sb->s_flags |= MS_RDONLY; } end_page_writeback(page); dec_page_count(p->sbi, F2FS_WRITEBACK); @@ -815,15 +816,10 @@ static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, mutex_unlock(&curseg->curseg_mutex); } -int write_meta_page(struct f2fs_sb_info *sbi, struct page *page, - struct writeback_control *wbc) +void write_meta_page(struct f2fs_sb_info *sbi, struct page *page) { - if (wbc->for_reclaim) - return AOP_WRITEPAGE_ACTIVATE; - set_page_writeback(page); submit_write_page(sbi, page, page->index, META); - return 0; } void write_node_page(struct f2fs_sb_info *sbi, struct page *page, diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 37fad04c8669..117ca2a46e39 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -387,10 +387,11 @@ static int sanity_check_raw_super(struct super_block *sb, return 0; } -static int sanity_check_ckpt(struct f2fs_super_block *raw_super, - struct f2fs_checkpoint *ckpt) +static int sanity_check_ckpt(struct f2fs_sb_info *sbi) { unsigned int total, fsmeta; + struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); + struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); total = le32_to_cpu(raw_super->segment_count); fsmeta = le32_to_cpu(raw_super->segment_count_ckpt); @@ -401,6 +402,11 @@ static int sanity_check_ckpt(struct f2fs_super_block *raw_super, if (fsmeta >= total) return 1; + + if (is_set_ckpt_flags(ckpt, CP_ERROR_FLAG)) { + f2fs_msg(sbi->sb, KERN_ERR, "A bug case: need to run fsck"); + return 1; + } return 0; } @@ -525,7 +531,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) /* sanity checking of checkpoint */ err = -EINVAL; - if (sanity_check_ckpt(raw_super, sbi->ckpt)) { + if (sanity_check_ckpt(sbi)) { f2fs_msg(sb, KERN_ERR, "Invalid F2FS checkpoint"); goto free_cp; } From bd43df021ac37247f2db58ff376fb4032170f754 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 25 Jan 2013 18:33:41 +0900 Subject: [PATCH 2326/4607] f2fs: cover global locks for reserve_new_block The fill_zero() from fallocate() calls get_new_data_page() in which calls reserve_new_block(). The reserve_new_block() should be covered by *DATA_NEW*, one of global locks. And also, before getting the lock, we should check free sections by calling f2fs_balance_fs(). If we break this rule, f2fs is able to face with out-of-control free space management and fall into infinite loop like the following scenario as well. [f2fs_sync_fs()] [fallocate()] - write_checkpoint() - fill_zero() - block_operations() - get_new_data_page() : grab NODE_NEW - get_dnode_of_data() : get locked dirty node page - sync_node_pages() : try to grab NODE_NEW for data allocation : trylock and skip the dirty node page : call sync_node_pages() repeatedly in order to flush all the dirty node pages! In order to avoid this, we should grab another global lock such as DATA_NEW before calling get_new_data_page() in fill_zero(). Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3191b52aafb0..6cdab2c64fc6 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -387,12 +387,17 @@ const struct inode_operations f2fs_file_inode_operations = { static void fill_zero(struct inode *inode, pgoff_t index, loff_t start, loff_t len) { + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct page *page; if (!len) return; + f2fs_balance_fs(sbi); + + mutex_lock_op(sbi, DATA_NEW); page = get_new_data_page(inode, index, false); + mutex_unlock_op(sbi, DATA_NEW); if (!IS_ERR(page)) { wait_on_page_writeback(page); From aa43507f68e44fbc014d820e47eda5a369b8dc9d Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Fri, 25 Jan 2013 19:08:59 +0100 Subject: [PATCH 2327/4607] f2fs: fix disable_ext_identify option spelling There is a typo in the ->show_options function for disable_ext_identify. Fix it to match the spelling from the documentation. Signed-off-by: Alejandro Martinez Ruiz Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 117ca2a46e39..25656b6869a4 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -198,7 +198,7 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",noacl"); #endif if (test_opt(sbi, DISABLE_EXT_IDENTIFY)) - seq_puts(seq, ",disable_ext_indentify"); + seq_puts(seq, ",disable_ext_identify"); seq_printf(seq, ",active_logs=%u", sbi->active_logs); From a2617dc6863b21a8109c199ab533b3dbfe178f27 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Tue, 29 Jan 2013 16:19:02 +0800 Subject: [PATCH 2328/4607] f2fs: clean up the add_orphan_inode func For the code > prev = list_entry(orphan->list.prev, typeof(*prev), list); if orphan->list.prev == head, it can't get the right prev. And we can use the parameter 'this' to add. Signed-off-by: Jianpeng Ma Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 9c1627165039..d3b34d05211f 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -219,19 +219,11 @@ retry: new->ino = ino; /* add new_oentry into list which is sorted by inode number */ - if (orphan) { - struct orphan_inode_entry *prev; - - /* get previous entry */ - prev = list_entry(orphan->list.prev, typeof(*prev), list); - if (&prev->list != head) - /* insert new orphan inode entry */ - list_add(&new->list, &prev->list); - else - list_add(&new->list, head); - } else { + if (orphan) + list_add(&new->list, this->prev); + else list_add_tail(&new->list, head); - } + sbi->n_orphans++; out: mutex_unlock(&sbi->orphan_inode_mutex); From d6212a5f18c8f9f9cc884070a96e11907711217f Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Tue, 29 Jan 2013 18:30:07 +0900 Subject: [PATCH 2329/4607] f2fs: add un/freeze_fs into super_operations This patch supports ioctl FIFREEZE and FITHAW to snapshot filesystem. Before calling f2fs_freeze, all writers would be suspended and sync_fs would be completed. So no f2fs has to do something. Just background gc operation should be skipped due to generate dirty nodes and data until unfreeze. Signed-off-by: Changman Lee Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 5 +++++ fs/f2fs/inode.c | 2 ++ fs/f2fs/super.c | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index c386910dacc5..9b5d0aad5daf 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -44,6 +44,11 @@ static int gc_thread_func(void *data) if (kthread_should_stop()) break; + if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) { + wait_ms = GC_THREAD_MAX_SLEEP_TIME; + continue; + } + f2fs_balance_fs(sbi); if (!test_opt(sbi, BG_GC)) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 340a21be5a76..62433c63e2db 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -278,6 +278,7 @@ void f2fs_evict_inode(struct inode *inode) if (inode->i_nlink || is_bad_inode(inode)) goto no_delete; + sb_start_intwrite(inode->i_sb); set_inode_flag(F2FS_I(inode), FI_NO_ALLOC); i_size_write(inode, 0); @@ -285,6 +286,7 @@ void f2fs_evict_inode(struct inode *inode) f2fs_truncate(inode); remove_inode_page(inode); + sb_end_intwrite(inode->i_sb); no_delete: clear_inode(inode); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 25656b6869a4..0b18aee2ed25 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -143,6 +143,22 @@ int f2fs_sync_fs(struct super_block *sb, int sync) return 0; } +static int f2fs_freeze(struct super_block *sb) +{ + int err; + + if (sb->s_flags & MS_RDONLY) + return 0; + + err = f2fs_sync_fs(sb, 1); + return err; +} + +static int f2fs_unfreeze(struct super_block *sb) +{ + return 0; +} + static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; @@ -213,6 +229,8 @@ static struct super_operations f2fs_sops = { .evict_inode = f2fs_evict_inode, .put_super = f2fs_put_super, .sync_fs = f2fs_sync_fs, + .freeze_fs = f2fs_freeze, + .unfreeze_fs = f2fs_unfreeze, .statfs = f2fs_statfs, }; From 3786dfdf4f00755ce5797c0e87eac8f898acb52c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jan 2013 22:47:02 +0900 Subject: [PATCH 2330/4607] f2fs: avoid redundant call to has_not_enough_free_secs in f2fs_gc After doing a write_checkpoint from garbage collection path if there is still need to do more garbage collection, gc_more label is used to jump and start the process again. And in that process, first step before getting victim is to check if there are not enough free sections, which is already done before doing a jump to gc_more. We can avoid the redundant call to check free sections, by checking the gc_type flag which will remain FG_GC(value 1) under this condition. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 9b5d0aad5daf..fb03be68cb20 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -672,7 +672,7 @@ gc_more: if (!(sbi->sb->s_flags & MS_ACTIVE)) goto stop; - if (has_not_enough_free_secs(sbi)) + if (gc_type == BG_GC && has_not_enough_free_secs(sbi)) gc_type = FG_GC; if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) From a2b52a598a4df55782462acba3bdced1a4610a60 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jan 2013 22:47:16 +0900 Subject: [PATCH 2331/4607] f2fs: reorganize code for ra_node_page We can remove unneeded label unlock_out, avoid unnecessary jump and reorganize the returning conditions in this function. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 9bda63c9c166..f71dfbbcb2b0 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -874,15 +874,11 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) return; if (read_node_page(apage, READA)) - goto unlock_out; + unlock_page(apage); - page_cache_release(apage); - return; - -unlock_out: - unlock_page(apage); release_out: page_cache_release(apage); + return; } struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) From 324ddc702eb89fb4ec8ed0cb38b4563ba4804f88 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 30 Jan 2013 22:47:29 +0900 Subject: [PATCH 2332/4607] f2fs: fix typo mistake for data_version description In f2fs_inode_info structure, the description for data_version has a typo mistake. It should be latest instead of lastes. So, correcting that. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index f4f509766465..975cb44851ab 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -141,7 +141,7 @@ struct f2fs_inode_info { /* Use below internally in f2fs*/ unsigned long flags; /* use to pass per-file flags */ - unsigned long long data_version;/* lastes version of data for fsync */ + unsigned long long data_version;/* latest version of data for fsync */ atomic_t dirty_dents; /* # of dirty dentry pages */ f2fs_hash_t chash; /* hash value of given file name */ unsigned int clevel; /* maximum level of given file name */ From 369a708c2a6557fabd409cf17a03db271c54931a Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Jan 2013 10:15:35 +0900 Subject: [PATCH 2333/4607] f2fs: remove the use of page_cache_release Let's remove the use of page_cache_release() in f2fs, and instead, use f2fs_put_page(page, 0) which is exactly same but for code readability. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index f71dfbbcb2b0..33fa6d506d94 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -104,7 +104,7 @@ static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid) f2fs_put_page(page, 1); continue; } - page_cache_release(page); + f2fs_put_page(page, 0); } } @@ -877,7 +877,7 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) unlock_page(apage); release_out: - page_cache_release(apage); + f2fs_put_page(apage, 0); return; } From d4686d56ec912d55fd8a9d6d509de50de24e90ab Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 31 Jan 2013 15:36:04 +0900 Subject: [PATCH 2334/4607] f2fs: avoid balanc_fs during evict_inode 1. Background Previously, if f2fs tries to move data blocks of an *evicting* inode during the cleaning process, it stops the process incompletely and then restarts the whole process, since it needs a locked inode to grab victim data pages in its address space. In order to get a locked inode, iget_locked() by f2fs_iget() is normally used, but, it waits if the inode is on freeing. So, here is a deadlock scenario. 1. f2fs_evict_inode() <- inode "A" 2. f2fs_balance_fs() 3. f2fs_gc() 4. gc_data_segment() 5. f2fs_iget() <- inode "A" too! If step #1 and #5 treat a same inode "A", step #5 would fall into deadlock since the inode "A" is on freeing. In order to resolve this, f2fs_iget_nowait() which skips __wait_on_freeing_inode() was introduced in step #5, and stops f2fs_gc() to complete f2fs_evict_inode(). 1. f2fs_evict_inode() <- inode "A" 2. f2fs_balance_fs() 3. f2fs_gc() 4. gc_data_segment() 5. f2fs_iget_nowait() <- inode "A", then stop f2fs_gc() w/ -ENOENT 2. Problem and Solution In the above scenario, however, f2fs cannot finish f2fs_evict_inode() only if: o there are not enough free sections, and o f2fs_gc() tries to move data blocks of the *evicting* inode repeatedly. So, the final solution is to use f2fs_iget() and remove f2fs_balance_fs() in f2fs_evict_inode(). The f2fs_evict_inode() actually truncates all the data and node blocks, which means that it doesn't produce any dirty node pages accordingly. So, we don't need to do f2fs_balance_fs() in practical. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 1 - fs/f2fs/file.c | 3 +-- fs/f2fs/gc.c | 2 +- fs/f2fs/inode.c | 33 --------------------------------- fs/f2fs/recovery.c | 2 +- 5 files changed, 3 insertions(+), 38 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 975cb44851ab..5022a7d7f7ca 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -847,7 +847,6 @@ long f2fs_ioctl(struct file *, unsigned int, unsigned long); * inode.c */ void f2fs_set_inode_flags(struct inode *); -struct inode *f2fs_iget_nowait(struct super_block *, unsigned long); struct inode *f2fs_iget(struct super_block *, unsigned long); void update_inode(struct inode *, struct page *); int f2fs_write_inode(struct inode *, struct writeback_control *); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 6cdab2c64fc6..3efa4739b00c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -298,8 +298,6 @@ void f2fs_truncate(struct inode *inode) inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); } - - f2fs_balance_fs(F2FS_SB(inode->i_sb)); } static int f2fs_getattr(struct vfsmount *mnt, @@ -356,6 +354,7 @@ int f2fs_setattr(struct dentry *dentry, struct iattr *attr) attr->ia_size != i_size_read(inode)) { truncate_setsize(inode, attr->ia_size); f2fs_truncate(inode); + f2fs_balance_fs(F2FS_SB(inode->i_sb)); } __setattr_copy(inode, attr); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index fb03be68cb20..375e69e2c6f1 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -579,7 +579,7 @@ next_step: ofs_in_node = le16_to_cpu(entry->ofs_in_node); if (phase == 2) { - inode = f2fs_iget_nowait(sb, dni.ino); + inode = f2fs_iget(sb, dni.ino); if (IS_ERR(inode)) continue; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 62433c63e2db..ddae412d30c8 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -16,11 +16,6 @@ #include "f2fs.h" #include "node.h" -struct f2fs_iget_args { - u64 ino; - int on_free; -}; - void f2fs_set_inode_flags(struct inode *inode) { unsigned int flags = F2FS_I(inode)->i_flags; @@ -40,34 +35,6 @@ void f2fs_set_inode_flags(struct inode *inode) inode->i_flags |= S_DIRSYNC; } -static int f2fs_iget_test(struct inode *inode, void *data) -{ - struct f2fs_iget_args *args = data; - - if (inode->i_ino != args->ino) - return 0; - if (inode->i_state & (I_FREEING | I_WILL_FREE)) { - args->on_free = 1; - return 0; - } - return 1; -} - -struct inode *f2fs_iget_nowait(struct super_block *sb, unsigned long ino) -{ - struct f2fs_iget_args args = { - .ino = ino, - .on_free = 0 - }; - struct inode *inode = ilookup5(sb, ino, f2fs_iget_test, &args); - - if (inode) - return inode; - if (!args.on_free) - return f2fs_iget(sb, ino); - return ERR_PTR(-ENOENT); -} - static int do_read_inode(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index f42e4060b399..e2a3e1a8eae9 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -226,7 +226,7 @@ static void check_index_in_prev_nodes(struct f2fs_sb_info *sbi, f2fs_put_page(node_page, 1); /* Deallocate previous index in the node page */ - inode = f2fs_iget_nowait(sbi->sb, ino); + inode = f2fs_iget(sbi->sb, ino); if (IS_ERR(inode)) return; From facb0205401a6ce4be410e1f4b9357bbc6caa36a Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Thu, 31 Jan 2013 17:16:56 +0900 Subject: [PATCH 2335/4607] f2fs: stop repeated checking if cp is needed If it is decided that f2fs should do checkpoint, skip next comparison. Signed-off-by: Changman Lee --- fs/f2fs/file.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 3efa4739b00c..33d1736ee5f9 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -157,11 +157,11 @@ int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) need_cp = true; - if (is_inode_flag_set(F2FS_I(inode), FI_NEED_CP)) + else if (is_inode_flag_set(F2FS_I(inode), FI_NEED_CP)) need_cp = true; - if (!space_for_roll_forward(sbi)) + else if (!space_for_roll_forward(sbi)) need_cp = true; - if (need_to_sync_dir(sbi, inode)) + else if (need_to_sync_dir(sbi, inode)) need_cp = true; if (need_cp) { From f83759e28372e593879f4dd20eb6c5ba6c4f393a Mon Sep 17 00:00:00 2001 From: majianpeng Date: Fri, 1 Feb 2013 15:00:30 +0800 Subject: [PATCH 2336/4607] f2fs: add device name in debugfs In file status, it can't distinguish between different devices. So add device name to do this function. Signed-off-by: Jianpeng Ma Signed-off-by: Jaegeuk Kim --- fs/f2fs/debug.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index c8c37307b326..025b9e2f935d 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -183,10 +183,12 @@ static int stat_show(struct seq_file *s, void *v) mutex_lock(&f2fs_stat_mutex); list_for_each_entry_safe(si, next, &f2fs_stat_list, stat_list) { + char devname[BDEVNAME_SIZE]; update_general_status(si->sbi); - seq_printf(s, "\n=====[ partition info. #%d ]=====\n", i++); + seq_printf(s, "\n=====[ partition info(%s). #%d ]=====\n", + bdevname(si->sbi->sb->s_bdev, devname), i++); seq_printf(s, "[SB: 1] [CP: 2] [SIT: %d] [NAT: %d] ", si->sit_area_segs, si->nat_area_segs); seq_printf(s, "[SSA: %d] [MAIN: %d", From 5c9b469295fb6b10d98923eab5e79c4edb80ed20 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Fri, 1 Feb 2013 19:07:57 +0800 Subject: [PATCH 2337/4607] f2fs: use F2FS_BLKSIZE to judge bloksize and page_cache_size In some system PAGE_CACHE_SIZE isn't 4K. So using F2FS_BLKSIZE to judge. By Jaegeuk Kim: o f2fs does not support no other 4KB page cache size. Signed-off-by: Jianpeng Ma Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 0b18aee2ed25..1d7fe11fea30 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -384,14 +384,23 @@ static int sanity_check_raw_super(struct super_block *sb, return 1; } + /* Currently, support only 4KB page cache size */ + if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) { + f2fs_msg(sb, KERN_INFO, + "Invalid page_cache_size (%u), supports only 4KB\n", + PAGE_CACHE_SIZE); + return 1; + } + /* Currently, support only 4KB block size */ blocksize = 1 << le32_to_cpu(raw_super->log_blocksize); - if (blocksize != PAGE_CACHE_SIZE) { + if (blocksize != F2FS_BLKSIZE) { f2fs_msg(sb, KERN_INFO, "Invalid blocksize (%u), supports only 4KB\n", blocksize); return 1; } + if (le32_to_cpu(raw_super->log_sectorsize) != F2FS_LOG_SECTOR_SIZE) { f2fs_msg(sb, KERN_INFO, "Invalid log sectorsize"); From 14d7e9de0500e9110e5820e2e77d096fc1bab724 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Fri, 1 Feb 2013 19:07:03 +0800 Subject: [PATCH 2338/4607] f2fs: when check superblock failed, try to check another superblock In f2fs, there are two superblocks. So when the first superblock was invalidate, it should try to check another. By Jaegeuk Kim: o Remove a white space for coding style o Clean up for code readability o Fix a typo Signed-off-by: Jianpeng Ma Signed-off-by: Jaegeuk Kim --- fs/f2fs/super.c | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 1d7fe11fea30..ddb665f54d17 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -387,7 +387,7 @@ static int sanity_check_raw_super(struct super_block *sb, /* Currently, support only 4KB page cache size */ if (F2FS_BLKSIZE != PAGE_CACHE_SIZE) { f2fs_msg(sb, KERN_INFO, - "Invalid page_cache_size (%u), supports only 4KB\n", + "Invalid page_cache_size (%lu), supports only 4KB\n", PAGE_CACHE_SIZE); return 1; } @@ -462,6 +462,32 @@ static void init_sb_info(struct f2fs_sb_info *sbi) atomic_set(&sbi->nr_pages[i], 0); } +static int validate_superblock(struct super_block *sb, + struct f2fs_super_block **raw_super, + struct buffer_head **raw_super_buf, sector_t block) +{ + const char *super = (block == 0 ? "first" : "second"); + + /* read f2fs raw super block */ + *raw_super_buf = sb_bread(sb, block); + if (!*raw_super_buf) { + f2fs_msg(sb, KERN_ERR, "unable to read %s superblock", + super); + return 1; + } + + *raw_super = (struct f2fs_super_block *) + ((char *)(*raw_super_buf)->b_data + F2FS_SUPER_OFFSET); + + /* sanity checking of raw super */ + if (!sanity_check_raw_super(sb, *raw_super)) + return 0; + + f2fs_msg(sb, KERN_ERR, "Can't find a valid F2FS filesystem " + "in %s superblock", super); + return 1; +} + static int f2fs_fill_super(struct super_block *sb, void *data, int silent) { struct f2fs_sb_info *sbi; @@ -482,16 +508,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) goto free_sbi; } - /* read f2fs raw super block */ - raw_super_buf = sb_bread(sb, 0); - if (!raw_super_buf) { - err = -EIO; - f2fs_msg(sb, KERN_ERR, "unable to read superblock"); - goto free_sbi; + if (validate_superblock(sb, &raw_super, &raw_super_buf, 0)) { + brelse(raw_super_buf); + if (validate_superblock(sb, &raw_super, &raw_super_buf, 1)) + goto free_sb_buf; } - raw_super = (struct f2fs_super_block *) - ((char *)raw_super_buf->b_data + F2FS_SUPER_OFFSET); - /* init some FS parameters */ sbi->active_logs = NR_CURSEG_TYPE; @@ -507,12 +528,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) if (parse_options(sb, sbi, (char *)data)) goto free_sb_buf; - /* sanity checking of raw super */ - if (sanity_check_raw_super(sb, raw_super)) { - f2fs_msg(sb, KERN_ERR, "Can't find a valid F2FS filesystem"); - goto free_sb_buf; - } - sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize)); sb->s_max_links = F2FS_LINK_MAX; get_random_bytes(&sbi->s_next_generation, sizeof(u32)); From 94787d91cba7ba168b028703b50a0224702ace9c Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Fri, 1 Feb 2013 18:05:09 +0900 Subject: [PATCH 2339/4607] f2fs: remove repeated F2FS_SET_SB_DIRT call F2FS_SET_SB_DIRT is called in inc_page_count and it is directly called one more time in the next line. Signed-off-by: Changman Lee Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index d3b34d05211f..2887c196b0a2 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -164,7 +164,6 @@ static int f2fs_set_meta_page_dirty(struct page *page) if (!PageDirty(page)) { __set_page_dirty_nobuffers(page); inc_page_count(sbi, F2FS_DIRTY_META); - F2FS_SET_SB_DIRT(sbi); return 1; } return 0; From 48600e44c18e4eb0f7c02ec8633c4c56aef292f0 Mon Sep 17 00:00:00 2001 From: Changman Lee Date: Mon, 4 Feb 2013 10:05:09 +0900 Subject: [PATCH 2340/4607] f2fs: remove unnecessary gc option check and balance_fs 1. If f2fs is mounted with background_gc_off option, checking BG_GC is not redundant. 2. f2fs_balance_fs is checked in f2fs_gc, so this is also redundant. Signed-off-by: Changman Lee Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 375e69e2c6f1..8d293cb685ba 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -49,11 +49,6 @@ static int gc_thread_func(void *data) continue; } - f2fs_balance_fs(sbi); - - if (!test_opt(sbi, BG_GC)) - continue; - /* * [GC triggering condition] * 0. GC is not conducted currently. @@ -96,6 +91,8 @@ int start_gc_thread(struct f2fs_sb_info *sbi) { struct f2fs_gc_kthread *gc_th; + if (!test_opt(sbi, BG_GC)) + return 0; gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL); if (!gc_th) return -ENOMEM; From ec7b1f2dd180afb1ce23aa44a7451e59bf2f3eb3 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 2 Feb 2013 23:52:28 +0900 Subject: [PATCH 2341/4607] f2fs: name gc task as per the block device Currently GC task is started for each f2fs formatted/mounted device. But, when we check the task list, using 'ps', there is no distinguishing factor between the tasks. So, name the task as per the block device just like the flusher threads. Also, remove the macro GC_THREAD_NAME and instead use the name: f2fs_gc to avoid name length truncation, as the command length is 16 -> TASK_COMM_LEN 16 and example name like: f2fs_gc_task:8:16 -> this exceeds name length Before Patch for 2 F2FS formatted partitions: root 28061 0.0 0.0 0 0 ? S 10:31 0:00 [f2fs_gc_task] root 28087 0.0 0.0 0 0 ? S 10:32 0:00 [f2fs_gc_task] After Patch: root 16756 0.0 0.0 0 0 ? S 14:57 0:00 [f2fs_gc-8:18] root 16765 0.0 0.0 0 0 ? S 14:57 0:00 [f2fs_gc-8:19] Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 3 ++- fs/f2fs/gc.h | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 8d293cb685ba..b1e498503e59 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -90,6 +90,7 @@ static int gc_thread_func(void *data) int start_gc_thread(struct f2fs_sb_info *sbi) { struct f2fs_gc_kthread *gc_th; + dev_t dev = sbi->sb->s_bdev->bd_dev; if (!test_opt(sbi, BG_GC)) return 0; @@ -100,7 +101,7 @@ int start_gc_thread(struct f2fs_sb_info *sbi) sbi->gc_thread = gc_th; init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head); sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi, - GC_THREAD_NAME); + "f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev)); if (IS_ERR(gc_th->f2fs_gc_task)) { kfree(gc_th); return -ENOMEM; diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index b026d9354ccd..3abdf83b5c16 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#define GC_THREAD_NAME "f2fs_gc_task" #define GC_THREAD_MIN_WB_PAGES 1 /* * a threshold to determine * whether IO subsystem is idle From 25718423ea6f7118f9c173adff0fac52414571b8 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 2 Feb 2013 23:52:42 +0900 Subject: [PATCH 2342/4607] f2fs: mark gc_thread as NULL when thread creation is failed When gc thread creation is failed, mark gc_thread as NULL to avoid crash while trying to stop invalid thread in stop_gc_thread->kthread_stop. Instead make it return from: if (!gc_th) return; Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index b1e498503e59..16fdec355201 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -104,6 +104,7 @@ int start_gc_thread(struct f2fs_sb_info *sbi) "f2fs_gc-%u:%u", MAJOR(dev), MINOR(dev)); if (IS_ERR(gc_th->f2fs_gc_task)) { kfree(gc_th); + sbi->gc_thread = NULL; return -ENOMEM; } return 0; From 5ac206cf4f9aad871aa1f1dd653a3404092db56c Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 2 Feb 2013 23:52:59 +0900 Subject: [PATCH 2343/4607] f2fs: make an accessor to get sections for particular block type Introduce accessor to get the sections based upon the block type (node,dents...) and modify the functions : should_do_checkpoint, has_not_enough_free_secs to use this accessor function to get the node sections and dent sections. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 8 ++++++++ fs/f2fs/gc.h | 8 ++------ fs/f2fs/segment.h | 9 ++------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5022a7d7f7ca..87840bccaf21 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -573,6 +573,14 @@ static inline int get_pages(struct f2fs_sb_info *sbi, int count_type) return atomic_read(&sbi->nr_pages[count_type]); } +static inline int get_blocktype_secs(struct f2fs_sb_info *sbi, int block_type) +{ + unsigned int pages_per_sec = sbi->segs_per_sec * + (1 << sbi->log_blocks_per_seg); + return ((get_pages(sbi, block_type) + pages_per_sec - 1) + >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; +} + static inline block_t valid_user_blocks(struct f2fs_sb_info *sbi) { block_t ret; diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index 3abdf83b5c16..c407a75a7daa 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -106,11 +106,7 @@ static inline int is_idle(struct f2fs_sb_info *sbi) static inline bool should_do_checkpoint(struct f2fs_sb_info *sbi) { - unsigned int pages_per_sec = sbi->segs_per_sec * - (1 << sbi->log_blocks_per_seg); - int node_secs = ((get_pages(sbi, F2FS_DIRTY_NODES) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; - int dent_secs = ((get_pages(sbi, F2FS_DIRTY_DENTS) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; + int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); + int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); return free_sections(sbi) <= (node_secs + 2 * dent_secs + 2); } diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 66a288a52fd3..a9417b94d6bc 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -459,13 +459,8 @@ static inline int get_ssr_segment(struct f2fs_sb_info *sbi, int type) static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi) { - unsigned int pages_per_sec = (1 << sbi->log_blocks_per_seg) * - sbi->segs_per_sec; - int node_secs = ((get_pages(sbi, F2FS_DIRTY_NODES) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; - int dent_secs = ((get_pages(sbi, F2FS_DIRTY_DENTS) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; - + int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); + int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); if (sbi->por_doing) return false; From b1f1daf8c72d615b64163e26488d8effeed29b60 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 2 Feb 2013 23:53:15 +0900 Subject: [PATCH 2344/4607] f2fs: optimize the return condition for has_not_enough_free_secs Instead of evaluating the free_sections and then deciding to return true/false from that path. We can directly use the evaluation condition for returning proper value. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index a9417b94d6bc..458bf5c726f7 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -464,10 +464,8 @@ static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi) if (sbi->por_doing) return false; - if (free_sections(sbi) <= (node_secs + 2 * dent_secs + - reserved_sections(sbi))) - return true; - return false; + return (free_sections(sbi) <= (node_secs + 2 * dent_secs + + reserved_sections(sbi))); } static inline int utilization(struct f2fs_sb_info *sbi) From 437275272f9e635673f065300e5d95226a25cb06 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 4 Feb 2013 15:11:17 +0900 Subject: [PATCH 2345/4607] f2fs: clarify and enhance the f2fs_gc flow This patch makes clearer the ambiguous f2fs_gc flow as follows. 1. Remove intermediate checkpoint condition during f2fs_gc (i.e., should_do_checkpoint() and GC_BLOCKED) 2. Remove unnecessary return values of f2fs_gc because of #1. (i.e., GC_NODE, GC_OK, etc) 3. Simplify write_checkpoint() because of #2. 4. Clarify the main f2fs_gc flow. o monitor how many freed sections during one iteration of do_garbage_collect(). o do GC more without checkpoints if we can't get enough free sections. o do checkpoint once we've got enough free sections through forground GCs. 5. Adopt thread-logging (Slack-Space-Recycle) scheme more aggressively on data log types. See. get_ssr_segement() Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 10 ++-- fs/f2fs/f2fs.h | 3 +- fs/f2fs/gc.c | 107 +++++++++++++++++-------------------------- fs/f2fs/gc.h | 16 ------- fs/f2fs/node.c | 2 +- fs/f2fs/recovery.c | 2 +- fs/f2fs/segment.c | 21 ++++++++- fs/f2fs/segment.h | 12 ++--- fs/f2fs/super.c | 4 +- 9 files changed, 72 insertions(+), 105 deletions(-) diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 2887c196b0a2..2b6fc131e2ce 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -539,7 +539,7 @@ retry: /* * Freeze all the FS-operations for checkpoint. */ -void block_operations(struct f2fs_sb_info *sbi) +static void block_operations(struct f2fs_sb_info *sbi) { int t; struct writeback_control wbc = { @@ -722,15 +722,13 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) /* * We guarantee that this checkpoint procedure should not fail. */ -void write_checkpoint(struct f2fs_sb_info *sbi, bool blocked, bool is_umount) +void write_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) { struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); unsigned long long ckpt_ver; - if (!blocked) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - } + mutex_lock(&sbi->cp_mutex); + block_operations(sbi); f2fs_submit_bio(sbi, DATA, true); f2fs_submit_bio(sbi, NODE, true); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 87840bccaf21..e7e7a29767d6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -969,8 +969,7 @@ int get_valid_checkpoint(struct f2fs_sb_info *); void set_dirty_dir_page(struct inode *, struct page *); void remove_dirty_dir_inode(struct inode *); void sync_dirty_dir_inodes(struct f2fs_sb_info *); -void block_operations(struct f2fs_sb_info *); -void write_checkpoint(struct f2fs_sb_info *, bool, bool); +void write_checkpoint(struct f2fs_sb_info *, bool); void init_orphan_info(struct f2fs_sb_info *); int __init create_checkpoint_caches(void); void destroy_checkpoint_caches(void); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 16fdec355201..52d3a391b922 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -78,7 +78,8 @@ static int gc_thread_func(void *data) sbi->bg_gc++; - if (f2fs_gc(sbi) == GC_NONE) + /* if return value is not zero, no victim was selected */ + if (f2fs_gc(sbi)) wait_ms = GC_THREAD_NOGC_SLEEP_TIME; else if (wait_ms == GC_THREAD_NOGC_SLEEP_TIME) wait_ms = GC_THREAD_MAX_SLEEP_TIME; @@ -360,7 +361,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi, sentry = get_seg_entry(sbi, segno); ret = f2fs_test_bit(offset, sentry->cur_valid_map); mutex_unlock(&sit_i->sentry_lock); - return ret ? GC_OK : GC_NEXT; + return ret; } /* @@ -368,7 +369,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi, * On validity, copy that node with cold status, otherwise (invalid node) * ignore that. */ -static int gc_node_segment(struct f2fs_sb_info *sbi, +static void gc_node_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, unsigned int segno, int gc_type) { bool initial = true; @@ -380,21 +381,12 @@ next_step: for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { nid_t nid = le32_to_cpu(entry->nid); struct page *node_page; - int err; - /* - * It makes sure that free segments are able to write - * all the dirty node pages before CP after this CP. - * So let's check the space of dirty node pages. - */ - if (should_do_checkpoint(sbi)) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - return GC_BLOCKED; - } + /* stop BG_GC if there is not enough free sections. */ + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) + return; - err = check_valid_map(sbi, segno, off); - if (err == GC_NEXT) + if (check_valid_map(sbi, segno, off) == 0) continue; if (initial) { @@ -424,7 +416,6 @@ next_step: }; sync_node_pages(sbi, 0, &wbc); } - return GC_DONE; } /* @@ -467,13 +458,13 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, node_page = get_node_page(sbi, nid); if (IS_ERR(node_page)) - return GC_NEXT; + return 0; get_node_info(sbi, nid, dni); if (sum->version != dni->version) { f2fs_put_page(node_page, 1); - return GC_NEXT; + return 0; } *nofs = ofs_of_node(node_page); @@ -481,8 +472,8 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, f2fs_put_page(node_page, 1); if (source_blkaddr != blkaddr) - return GC_NEXT; - return GC_OK; + return 0; + return 1; } static void move_data_page(struct inode *inode, struct page *page, int gc_type) @@ -523,13 +514,13 @@ out: * If the parent node is not valid or the data block address is different, * the victim data block is ignored. */ -static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, +static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, struct list_head *ilist, unsigned int segno, int gc_type) { struct super_block *sb = sbi->sb; struct f2fs_summary *entry; block_t start_addr; - int err, off; + int off; int phase = 0; start_addr = START_BLOCK(sbi, segno); @@ -543,20 +534,11 @@ next_step: unsigned int ofs_in_node, nofs; block_t start_bidx; - /* - * It makes sure that free segments are able to write - * all the dirty node pages before CP after this CP. - * So let's check the space of dirty node pages. - */ - if (should_do_checkpoint(sbi)) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - err = GC_BLOCKED; - goto stop; - } + /* stop BG_GC if there is not enough free sections. */ + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) + return; - err = check_valid_map(sbi, segno, off); - if (err == GC_NEXT) + if (check_valid_map(sbi, segno, off) == 0) continue; if (phase == 0) { @@ -565,8 +547,7 @@ next_step: } /* Get an inode by ino with checking validity */ - err = check_dnode(sbi, entry, &dni, start_addr + off, &nofs); - if (err == GC_NEXT) + if (check_dnode(sbi, entry, &dni, start_addr + off, &nofs) == 0) continue; if (phase == 1) { @@ -606,11 +587,9 @@ next_iput: } if (++phase < 4) goto next_step; - err = GC_DONE; -stop: + if (gc_type == FG_GC) f2fs_submit_bio(sbi, DATA, true); - return err; } static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, @@ -624,17 +603,16 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, return ret; } -static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, +static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, struct list_head *ilist, int gc_type) { struct page *sum_page; struct f2fs_summary_block *sum; - int ret = GC_DONE; /* read segment summary of victim */ sum_page = get_sum_page(sbi, segno); if (IS_ERR(sum_page)) - return GC_ERROR; + return; /* * CP needs to lock sum_page. In this time, we don't need @@ -646,17 +624,16 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, switch (GET_SUM_TYPE((&sum->footer))) { case SUM_TYPE_NODE: - ret = gc_node_segment(sbi, sum->entries, segno, gc_type); + gc_node_segment(sbi, sum->entries, segno, gc_type); break; case SUM_TYPE_DATA: - ret = gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); + gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); break; } stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer))); stat_inc_call_count(sbi->stat_info); f2fs_put_page(sum_page, 0); - return ret; } int f2fs_gc(struct f2fs_sb_info *sbi) @@ -664,40 +641,38 @@ int f2fs_gc(struct f2fs_sb_info *sbi) struct list_head ilist; unsigned int segno, i; int gc_type = BG_GC; - int gc_status = GC_NONE; + int nfree = 0; + int ret = -1; INIT_LIST_HEAD(&ilist); gc_more: if (!(sbi->sb->s_flags & MS_ACTIVE)) goto stop; - if (gc_type == BG_GC && has_not_enough_free_secs(sbi)) + if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) gc_type = FG_GC; if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) goto stop; + ret = 0; - for (i = 0; i < sbi->segs_per_sec; i++) { - /* - * do_garbage_collect will give us three gc_status: - * GC_ERROR, GC_DONE, and GC_BLOCKED. - * If GC is finished uncleanly, we have to return - * the victim to dirty segment list. - */ - gc_status = do_garbage_collect(sbi, segno + i, &ilist, gc_type); - if (gc_status != GC_DONE) - break; - } - if (has_not_enough_free_secs(sbi)) { - write_checkpoint(sbi, (gc_status == GC_BLOCKED), false); - if (has_not_enough_free_secs(sbi)) - goto gc_more; - } + for (i = 0; i < sbi->segs_per_sec; i++) + do_garbage_collect(sbi, segno + i, &ilist, gc_type); + + if (gc_type == FG_GC && + get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0) + nfree++; + + if (has_not_enough_free_secs(sbi, nfree)) + goto gc_more; + + if (gc_type == FG_GC) + write_checkpoint(sbi, false); stop: mutex_unlock(&sbi->gc_mutex); put_gc_inode(&ilist); - return gc_status; + return ret; } void build_gc_manager(struct f2fs_sb_info *sbi) diff --git a/fs/f2fs/gc.h b/fs/f2fs/gc.h index c407a75a7daa..30b2db003acd 100644 --- a/fs/f2fs/gc.h +++ b/fs/f2fs/gc.h @@ -22,15 +22,6 @@ /* Search max. number of dirty segments to select a victim segment */ #define MAX_VICTIM_SEARCH 20 -enum { - GC_NONE = 0, - GC_ERROR, - GC_OK, - GC_NEXT, - GC_BLOCKED, - GC_DONE, -}; - struct f2fs_gc_kthread { struct task_struct *f2fs_gc_task; wait_queue_head_t gc_wait_queue_head; @@ -103,10 +94,3 @@ static inline int is_idle(struct f2fs_sb_info *sbi) struct request_list *rl = &q->root_rl; return !(rl->count[BLK_RW_SYNC]) && !(rl->count[BLK_RW_ASYNC]); } - -static inline bool should_do_checkpoint(struct f2fs_sb_info *sbi) -{ - int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); - int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); - return free_sections(sbi) <= (node_secs + 2 * dent_secs + 2); -} diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 33fa6d506d94..43ce16422b75 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -1135,7 +1135,7 @@ static int f2fs_write_node_pages(struct address_space *mapping, /* First check balancing cached NAT entries */ if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK)) { - write_checkpoint(sbi, false, false); + write_checkpoint(sbi, false); return 0; } diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index e2a3e1a8eae9..01e1a03b54c8 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -373,5 +373,5 @@ void recover_fsync_data(struct f2fs_sb_info *sbi) out: destroy_fsync_dnodes(sbi, &inode_list); kmem_cache_destroy(fsync_entry_slab); - write_checkpoint(sbi, false, false); + write_checkpoint(sbi, false); } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 7aa270f3538a..777f17e496e6 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -29,7 +29,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi) * We should do GC or end up with checkpoint, if there are so many dirty * dir/node pages without enough free segments. */ - if (has_not_enough_free_secs(sbi)) { + if (has_not_enough_free_secs(sbi, 0)) { mutex_lock(&sbi->gc_mutex); f2fs_gc(sbi); } @@ -308,7 +308,7 @@ static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi, * If there is not enough reserved sections, * we should not reuse prefree segments. */ - if (has_not_enough_free_secs(sbi)) + if (has_not_enough_free_secs(sbi, 0)) return NULL_SEGNO; /* @@ -536,6 +536,23 @@ static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse) } } +static int get_ssr_segment(struct f2fs_sb_info *sbi, int type) +{ + struct curseg_info *curseg = CURSEG_I(sbi, type); + const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops; + + if (IS_NODESEG(type) || !has_not_enough_free_secs(sbi, 0)) + return v_ops->get_victim(sbi, + &(curseg)->next_segno, BG_GC, type, SSR); + + /* For data segments, let's do SSR more intensively */ + for (; type >= CURSEG_HOT_DATA; type--) + if (v_ops->get_victim(sbi, &(curseg)->next_segno, + BG_GC, type, SSR)) + return 1; + return 0; +} + /* * flush out current segment and replace it with new segment * This function should be returned with success, otherwise BUG diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 458bf5c726f7..552dadbb2327 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -450,21 +450,15 @@ static inline bool need_SSR(struct f2fs_sb_info *sbi) return (free_sections(sbi) < overprovision_sections(sbi)); } -static inline int get_ssr_segment(struct f2fs_sb_info *sbi, int type) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - return DIRTY_I(sbi)->v_ops->get_victim(sbi, - &(curseg)->next_segno, BG_GC, type, SSR); -} - -static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi) +static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi, int freed) { int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES); int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS); + if (sbi->por_doing) return false; - return (free_sections(sbi) <= (node_secs + 2 * dent_secs + + return ((free_sections(sbi) + freed) <= (node_secs + 2 * dent_secs + reserved_sections(sbi))); } diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index ddb665f54d17..8c117649a035 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -112,7 +112,7 @@ static void f2fs_put_super(struct super_block *sb) f2fs_destroy_stats(sbi); stop_gc_thread(sbi); - write_checkpoint(sbi, false, true); + write_checkpoint(sbi, true); iput(sbi->node_inode); iput(sbi->meta_inode); @@ -136,7 +136,7 @@ int f2fs_sync_fs(struct super_block *sb, int sync) return 0; if (sync) - write_checkpoint(sbi, false, false); + write_checkpoint(sbi, false); else f2fs_balance_fs(sbi); From b7250d2d845822466356f7f22a650bf807090d7e Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 5 Feb 2013 13:19:28 +0900 Subject: [PATCH 2346/4607] f2fs: fix calculation of max. gc cost in the SSR case In the SSR case, the max gc cost should be the number of pages in a segment. Otherwise, f2fs is able to fail getting dirty segments frequently for SSR. In get_victim_by_default() previously, while(1) { ... cost = get_gc_cost(); <- cost is between 0 ~ 512. ... if (cost == get_max_cost(sbi, &p)) <- max cost is UINT_MAX due to GC_CB type continue; if (nsearched++ >= MAX_VICTIM_SEARCH) break; } So, if there are a number of fully valid segments in series, f2fs cannot skip those segments by comparing the cost and max cost of each segment. Note that, the cost is the number of valid blocks at the time of the last checkpoint. Signed-off-by: Jaegeuk Kim --- fs/f2fs/gc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 52d3a391b922..94b8a0c48453 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -146,6 +146,9 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type, static unsigned int get_max_cost(struct f2fs_sb_info *sbi, struct victim_sel_policy *p) { + /* SSR allocates in a segment unit */ + if (p->alloc_mode == SSR) + return 1 << sbi->log_blocks_per_seg; if (p->gc_mode == GC_GREEDY) return (1 << sbi->log_blocks_per_seg) * p->ofs_unit; else if (p->gc_mode == GC_CB) From e9750824114ff939d9da299e73651add6aa65456 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 4 Feb 2013 23:41:41 +0900 Subject: [PATCH 2347/4607] f2fs: add compat_ioctl to provide backward compatability adding compat_ioctl to provide support for backward comptability - 32bit binary execution on 64bit kernel. Signed-off-by: Namjae Jeon Signed-off-by: Amit Sahrawat Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 15 +++++++++++++++ fs/f2fs/file.c | 21 +++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index e7e7a29767d6..c3462b69917e 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -103,6 +103,20 @@ static inline int update_sits_in_cursum(struct f2fs_summary_block *rs, int i) return before; } +/* + * ioctl commands + */ +#define F2FS_IOC_GETFLAGS FS_IOC_GETFLAGS +#define F2FS_IOC_SETFLAGS FS_IOC_SETFLAGS + +#if defined(__KERNEL__) && defined(CONFIG_COMPAT) +/* + * ioctl commands in 32 bit emulation + */ +#define F2FS_IOC32_GETFLAGS FS_IOC32_GETFLAGS +#define F2FS_IOC32_SETFLAGS FS_IOC32_SETFLAGS +#endif + /* * For INODE and NODE manager */ @@ -850,6 +864,7 @@ void f2fs_truncate(struct inode *); int f2fs_setattr(struct dentry *, struct iattr *); int truncate_hole(struct inode *, pgoff_t, pgoff_t); long f2fs_ioctl(struct file *, unsigned int, unsigned long); +long f2fs_compat_ioctl(struct file *, unsigned int, unsigned long); /* * inode.c diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 33d1736ee5f9..b7a053d4c6d3 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -634,6 +635,23 @@ out: } } +#ifdef CONFIG_COMPAT +long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case F2FS_IOC32_GETFLAGS: + cmd = F2FS_IOC_GETFLAGS; + break; + case F2FS_IOC32_SETFLAGS: + cmd = F2FS_IOC_SETFLAGS; + break; + default: + return -ENOIOCTLCMD; + } + return f2fs_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); +} +#endif + const struct file_operations f2fs_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, @@ -645,6 +663,9 @@ const struct file_operations f2fs_file_operations = { .fsync = f2fs_sync_file, .fallocate = f2fs_fallocate, .unlocked_ioctl = f2fs_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = f2fs_compat_ioctl, +#endif .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, }; From 372e722ea4dd4ca11c3d04845e11cbc15f32144c Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:29 +0900 Subject: [PATCH 2348/4607] gpiolib: use descriptors internally Make sure gpiolib works internally with descriptors and (chip, offset) pairs instead of using the global integer namespace. This prepares the ground for the removal of the global gpio_desc[] array and the introduction of the descriptor-based GPIO API. Signed-off-by: Alexandre Courbot Reviewed-by: Linus Walleij [grant.likely: Squash in fix for link error when CONFIG_SYSFS=n] Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 516 +++++++++++++++++++++++++++-------------- 1 file changed, 339 insertions(+), 177 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8ccf68bb8a40..866431f674c3 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -78,6 +78,28 @@ static LIST_HEAD(gpio_chips); static DEFINE_IDR(dirent_idr); #endif +/* + * Internal gpiod_* API using descriptors instead of the integer namespace. + * Most of this should eventually go public. + */ +static int gpiod_request(struct gpio_desc *desc, const char *label); +static void gpiod_free(struct gpio_desc *desc); +static int gpiod_direction_input(struct gpio_desc *desc); +static int gpiod_direction_output(struct gpio_desc *desc, int value); +static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); +static int gpiod_get_value_cansleep(struct gpio_desc *desc); +static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); +static int gpiod_get_value(struct gpio_desc *desc); +static void gpiod_set_value(struct gpio_desc *desc, int value); +static int gpiod_cansleep(struct gpio_desc *desc); +static int gpiod_to_irq(struct gpio_desc *desc); +static int gpiod_export(struct gpio_desc *desc, bool direction_may_change); +static int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc); +static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value); +static void gpiod_unexport(struct gpio_desc *desc); + + static inline void desc_set_label(struct gpio_desc *d, const char *label) { #ifdef CONFIG_DEBUG_FS @@ -85,6 +107,36 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) #endif } +/* + * Return the GPIO number of the passed descriptor relative to its chip + */ +static int gpio_chip_hwgpio(const struct gpio_desc *desc) +{ + return (desc - &gpio_desc[0]) - desc->chip->base; +} + +/** + * Convert a GPIO number to its descriptor + */ +static struct gpio_desc *gpio_to_desc(unsigned gpio) +{ + if (WARN(!gpio_is_valid(gpio), "invalid GPIO %d\n", gpio)) + return NULL; + else + return &gpio_desc[gpio]; +} + +/** + * Convert a GPIO descriptor to the integer namespace. + * This should disappear in the future but is needed since we still + * use GPIO numbers for error messages and sysfs nodes + */ +static int desc_to_gpio(const struct gpio_desc *desc) +{ + return desc - &gpio_desc[0]; +} + + /* Warn when drivers omit gpio_request() calls -- legal but ill-advised * when setting direction, and otherwise illegal. Until board setup code * and drivers use explicit requests everywhere (which won't happen when @@ -96,10 +148,10 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) * only "legal" in the sense that (old) code using it won't break yet, * but instead only triggers a WARN() stack dump. */ -static int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset) +static int gpio_ensure_requested(struct gpio_desc *desc) { const struct gpio_chip *chip = desc->chip; - const int gpio = chip->base + offset; + const int gpio = desc_to_gpio(desc); if (WARN(test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0, "autorequest GPIO-%d\n", gpio)) { @@ -118,9 +170,14 @@ static int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset) } /* caller holds gpio_lock *OR* gpio is marked as requested */ +static struct gpio_chip *gpiod_to_chip(struct gpio_desc *desc) +{ + return desc->chip; +} + struct gpio_chip *gpio_to_chip(unsigned gpio) { - return gpio_desc[gpio].chip; + return gpiod_to_chip(gpio_to_desc(gpio)); } /* dynamic allocation of GPIOs, e.g. on a hotplugged device */ @@ -148,19 +205,19 @@ static int gpiochip_find_base(int ngpio) } /* caller ensures gpio is valid and requested, chip->get_direction may sleep */ -static int gpio_get_direction(unsigned gpio) +static int gpiod_get_direction(struct gpio_desc *desc) { struct gpio_chip *chip; - struct gpio_desc *desc = &gpio_desc[gpio]; + unsigned offset; int status = -EINVAL; - chip = gpio_to_chip(gpio); - gpio -= chip->base; + chip = gpiod_to_chip(desc); + offset = gpio_chip_hwgpio(desc); if (!chip->get_direction) return status; - status = chip->get_direction(chip, gpio); + status = chip->get_direction(chip, offset); if (status > 0) { /* GPIOF_DIR_IN, or other positive */ status = 1; @@ -204,8 +261,7 @@ static DEFINE_MUTEX(sysfs_lock); static ssize_t gpio_direction_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_desc *desc = dev_get_drvdata(dev); - unsigned gpio = desc - gpio_desc; + struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); @@ -213,7 +269,7 @@ static ssize_t gpio_direction_show(struct device *dev, if (!test_bit(FLAG_EXPORT, &desc->flags)) { status = -EIO; } else { - gpio_get_direction(gpio); + gpiod_get_direction(desc); status = sprintf(buf, "%s\n", test_bit(FLAG_IS_OUT, &desc->flags) ? "out" : "in"); @@ -226,8 +282,7 @@ static ssize_t gpio_direction_show(struct device *dev, static ssize_t gpio_direction_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - const struct gpio_desc *desc = dev_get_drvdata(dev); - unsigned gpio = desc - gpio_desc; + struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); @@ -235,11 +290,11 @@ static ssize_t gpio_direction_store(struct device *dev, if (!test_bit(FLAG_EXPORT, &desc->flags)) status = -EIO; else if (sysfs_streq(buf, "high")) - status = gpio_direction_output(gpio, 1); + status = gpiod_direction_output(desc, 1); else if (sysfs_streq(buf, "out") || sysfs_streq(buf, "low")) - status = gpio_direction_output(gpio, 0); + status = gpiod_direction_output(desc, 0); else if (sysfs_streq(buf, "in")) - status = gpio_direction_input(gpio); + status = gpiod_direction_input(desc); else status = -EINVAL; @@ -253,8 +308,7 @@ static /* const */ DEVICE_ATTR(direction, 0644, static ssize_t gpio_value_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_desc *desc = dev_get_drvdata(dev); - unsigned gpio = desc - gpio_desc; + struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); @@ -264,7 +318,7 @@ static ssize_t gpio_value_show(struct device *dev, } else { int value; - value = !!gpio_get_value_cansleep(gpio); + value = !!gpiod_get_value_cansleep(desc); if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; @@ -278,8 +332,7 @@ static ssize_t gpio_value_show(struct device *dev, static ssize_t gpio_value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - const struct gpio_desc *desc = dev_get_drvdata(dev); - unsigned gpio = desc - gpio_desc; + struct gpio_desc *desc = dev_get_drvdata(dev); ssize_t status; mutex_lock(&sysfs_lock); @@ -295,7 +348,7 @@ static ssize_t gpio_value_store(struct device *dev, if (status == 0) { if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) value = !value; - gpio_set_value_cansleep(gpio, value != 0); + gpiod_set_value_cansleep(desc, value != 0); status = size; } } @@ -325,7 +378,7 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, if ((desc->flags & GPIO_TRIGGER_MASK) == gpio_flags) return 0; - irq = gpio_to_irq(desc - gpio_desc); + irq = gpiod_to_irq(desc); if (irq < 0) return -EIO; @@ -595,29 +648,32 @@ static ssize_t export_store(struct class *class, struct class_attribute *attr, const char *buf, size_t len) { - long gpio; - int status; + long gpio; + struct gpio_desc *desc; + int status; status = strict_strtol(buf, 0, &gpio); if (status < 0) goto done; + desc = gpio_to_desc(gpio); + /* No extra locking here; FLAG_SYSFS just signifies that the * request and export were done by on behalf of userspace, so * they may be undone on its behalf too. */ - status = gpio_request(gpio, "sysfs"); + status = gpiod_request(desc, "sysfs"); if (status < 0) { if (status == -EPROBE_DEFER) status = -ENODEV; goto done; } - status = gpio_export(gpio, true); + status = gpiod_export(desc, true); if (status < 0) - gpio_free(gpio); + gpiod_free(desc); else - set_bit(FLAG_SYSFS, &gpio_desc[gpio].flags); + set_bit(FLAG_SYSFS, &desc->flags); done: if (status) @@ -629,8 +685,9 @@ static ssize_t unexport_store(struct class *class, struct class_attribute *attr, const char *buf, size_t len) { - long gpio; - int status; + long gpio; + struct gpio_desc *desc; + int status; status = strict_strtol(buf, 0, &gpio); if (status < 0) @@ -638,17 +695,18 @@ static ssize_t unexport_store(struct class *class, status = -EINVAL; + desc = gpio_to_desc(gpio); /* reject bogus commands (gpio_unexport ignores them) */ - if (!gpio_is_valid(gpio)) + if (!desc) goto done; /* No extra locking here; FLAG_SYSFS just signifies that the * request and export were done by on behalf of userspace, so * they may be undone on its behalf too. */ - if (test_and_clear_bit(FLAG_SYSFS, &gpio_desc[gpio].flags)) { + if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) { status = 0; - gpio_free(gpio); + gpiod_free(desc); } done: if (status) @@ -685,13 +743,13 @@ static struct class gpio_class = { * * Returns zero on success, else an error. */ -int gpio_export(unsigned gpio, bool direction_may_change) +static int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { unsigned long flags; - struct gpio_desc *desc; int status; const char *ioname = NULL; struct device *dev; + int offset; /* can't export until sysfs is available ... */ if (!gpio_class.p) { @@ -699,20 +757,19 @@ int gpio_export(unsigned gpio, bool direction_may_change) return -ENOENT; } - if (!gpio_is_valid(gpio)) { - pr_debug("%s: gpio %d is not valid\n", __func__, gpio); + if (!desc) { + pr_debug("%s: invalid gpio descriptor\n", __func__); return -EINVAL; } mutex_lock(&sysfs_lock); spin_lock_irqsave(&gpio_lock, flags); - desc = &gpio_desc[gpio]; if (!test_bit(FLAG_REQUESTED, &desc->flags) || test_bit(FLAG_EXPORT, &desc->flags)) { spin_unlock_irqrestore(&gpio_lock, flags); pr_debug("%s: gpio %d unavailable (requested=%d, exported=%d)\n", - __func__, gpio, + __func__, desc_to_gpio(desc), test_bit(FLAG_REQUESTED, &desc->flags), test_bit(FLAG_EXPORT, &desc->flags)); status = -EPERM; @@ -723,11 +780,13 @@ int gpio_export(unsigned gpio, bool direction_may_change) direction_may_change = false; spin_unlock_irqrestore(&gpio_lock, flags); - if (desc->chip->names && desc->chip->names[gpio - desc->chip->base]) - ioname = desc->chip->names[gpio - desc->chip->base]; + offset = gpio_chip_hwgpio(desc); + if (desc->chip->names && desc->chip->names[offset]) + ioname = desc->chip->names[offset]; dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), - desc, ioname ? ioname : "gpio%u", gpio); + desc, ioname ? ioname : "gpio%u", + desc_to_gpio(desc)); if (IS_ERR(dev)) { status = PTR_ERR(dev); goto fail_unlock; @@ -743,7 +802,7 @@ int gpio_export(unsigned gpio, bool direction_may_change) goto fail_unregister_device; } - if (gpio_to_irq(gpio) >= 0 && (direction_may_change || + if (gpiod_to_irq(desc) >= 0 && (direction_may_change || !test_bit(FLAG_IS_OUT, &desc->flags))) { status = device_create_file(dev, &dev_attr_edge); if (status) @@ -758,9 +817,15 @@ fail_unregister_device: device_unregister(dev); fail_unlock: mutex_unlock(&sysfs_lock); - pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc), + status); return status; } + +int gpio_export(unsigned gpio, bool direction_may_change) +{ + return gpiod_export(gpio_to_desc(gpio), direction_may_change); +} EXPORT_SYMBOL_GPL(gpio_export); static int match_export(struct device *dev, void *data) @@ -779,18 +844,16 @@ static int match_export(struct device *dev, void *data) * * Returns zero on success, else an error. */ -int gpio_export_link(struct device *dev, const char *name, unsigned gpio) +static int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc) { - struct gpio_desc *desc; int status = -EINVAL; - if (!gpio_is_valid(gpio)) + if (!desc) goto done; mutex_lock(&sysfs_lock); - desc = &gpio_desc[gpio]; - if (test_bit(FLAG_EXPORT, &desc->flags)) { struct device *tdev; @@ -807,12 +870,17 @@ int gpio_export_link(struct device *dev, const char *name, unsigned gpio) done: if (status) - pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc), + status); return status; } -EXPORT_SYMBOL_GPL(gpio_export_link); +int gpio_export_link(struct device *dev, const char *name, unsigned gpio) +{ + return gpiod_export_link(dev, name, gpio_to_desc(gpio)); +} +EXPORT_SYMBOL_GPL(gpio_export_link); /** * gpio_sysfs_set_active_low - set the polarity of gpio sysfs value @@ -826,19 +894,16 @@ EXPORT_SYMBOL_GPL(gpio_export_link); * * Returns zero on success, else an error. */ -int gpio_sysfs_set_active_low(unsigned gpio, int value) +static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) { - struct gpio_desc *desc; struct device *dev = NULL; int status = -EINVAL; - if (!gpio_is_valid(gpio)) + if (!desc) goto done; mutex_lock(&sysfs_lock); - desc = &gpio_desc[gpio]; - if (test_bit(FLAG_EXPORT, &desc->flags)) { dev = class_find_device(&gpio_class, NULL, desc, match_export); if (dev == NULL) { @@ -854,10 +919,16 @@ unlock: done: if (status) - pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc), + status); return status; } + +int gpio_sysfs_set_active_low(unsigned gpio, int value) +{ + return gpiod_sysfs_set_active_low(gpio_to_desc(gpio), value); +} EXPORT_SYMBOL_GPL(gpio_sysfs_set_active_low); /** @@ -866,21 +937,18 @@ EXPORT_SYMBOL_GPL(gpio_sysfs_set_active_low); * * This is implicit on gpio_free(). */ -void gpio_unexport(unsigned gpio) +static void gpiod_unexport(struct gpio_desc *desc) { - struct gpio_desc *desc; int status = 0; struct device *dev = NULL; - if (!gpio_is_valid(gpio)) { + if (!desc) { status = -EINVAL; goto done; } mutex_lock(&sysfs_lock); - desc = &gpio_desc[gpio]; - if (test_bit(FLAG_EXPORT, &desc->flags)) { dev = class_find_device(&gpio_class, NULL, desc, match_export); @@ -892,13 +960,20 @@ void gpio_unexport(unsigned gpio) } mutex_unlock(&sysfs_lock); + if (dev) { device_unregister(dev); put_device(dev); } done: if (status) - pr_debug("%s: gpio%d status %d\n", __func__, gpio, status); + pr_debug("%s: gpio%d status %d\n", __func__, desc_to_gpio(desc), + status); +} + +void gpio_unexport(unsigned gpio) +{ + gpiod_unexport(gpio_to_desc(gpio)); } EXPORT_SYMBOL_GPL(gpio_unexport); @@ -1007,6 +1082,27 @@ static inline void gpiochip_unexport(struct gpio_chip *chip) { } +static inline int gpiod_export(struct gpio_desc *desc, + bool direction_may_change) +{ + return -ENOSYS; +} + +static inline int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc) +{ + return -ENOSYS; +} + +static inline int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) +{ + return -ENOSYS; +} + +static inline void gpiod_unexport(struct gpio_desc *desc) +{ +} + #endif /* CONFIG_GPIO_SYSFS */ /* @@ -1282,20 +1378,18 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); * on each other, and help provide better diagnostics in debugfs. * They're called even less than the "set direction" calls. */ -int gpio_request(unsigned gpio, const char *label) +static int gpiod_request(struct gpio_desc *desc, const char *label) { - struct gpio_desc *desc; struct gpio_chip *chip; int status = -EPROBE_DEFER; unsigned long flags; spin_lock_irqsave(&gpio_lock, flags); - if (!gpio_is_valid(gpio)) { + if (!desc) { status = -EINVAL; goto done; } - desc = &gpio_desc[gpio]; chip = desc->chip; if (chip == NULL) goto done; @@ -1319,7 +1413,7 @@ int gpio_request(unsigned gpio, const char *label) if (chip->request) { /* chip->request may sleep */ spin_unlock_irqrestore(&gpio_lock, flags); - status = chip->request(chip, gpio - chip->base); + status = chip->request(chip, gpio_chip_hwgpio(desc)); spin_lock_irqsave(&gpio_lock, flags); if (status < 0) { @@ -1332,42 +1426,46 @@ int gpio_request(unsigned gpio, const char *label) if (chip->get_direction) { /* chip->get_direction may sleep */ spin_unlock_irqrestore(&gpio_lock, flags); - gpio_get_direction(gpio); + gpiod_get_direction(desc); spin_lock_irqsave(&gpio_lock, flags); } done: if (status) - pr_debug("gpio_request: gpio-%d (%s) status %d\n", - gpio, label ? : "?", status); + pr_debug("_gpio_request: gpio-%d (%s) status %d\n", + desc ? desc_to_gpio(desc) : -1, + label ? : "?", status); spin_unlock_irqrestore(&gpio_lock, flags); return status; } + +int gpio_request(unsigned gpio, const char *label) +{ + return gpiod_request(gpio_to_desc(gpio), label); +} EXPORT_SYMBOL_GPL(gpio_request); -void gpio_free(unsigned gpio) +static void gpiod_free(struct gpio_desc *desc) { unsigned long flags; - struct gpio_desc *desc; struct gpio_chip *chip; might_sleep(); - if (!gpio_is_valid(gpio)) { + if (!desc) { WARN_ON(extra_checks); return; } - gpio_unexport(gpio); + gpiod_unexport(desc); spin_lock_irqsave(&gpio_lock, flags); - desc = &gpio_desc[gpio]; chip = desc->chip; if (chip && test_bit(FLAG_REQUESTED, &desc->flags)) { if (chip->free) { spin_unlock_irqrestore(&gpio_lock, flags); might_sleep_if(chip->can_sleep); - chip->free(chip, gpio - chip->base); + chip->free(chip, gpio_chip_hwgpio(desc)); spin_lock_irqsave(&gpio_lock, flags); } desc_set_label(desc, NULL); @@ -1381,6 +1479,11 @@ void gpio_free(unsigned gpio) spin_unlock_irqrestore(&gpio_lock, flags); } + +void gpio_free(unsigned gpio) +{ + gpiod_free(gpio_to_desc(gpio)); +} EXPORT_SYMBOL_GPL(gpio_free); /** @@ -1391,29 +1494,32 @@ EXPORT_SYMBOL_GPL(gpio_free); */ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) { + struct gpio_desc *desc; int err; - err = gpio_request(gpio, label); + desc = gpio_to_desc(gpio); + + err = gpiod_request(desc, label); if (err) return err; if (flags & GPIOF_OPEN_DRAIN) - set_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags); + set_bit(FLAG_OPEN_DRAIN, &desc->flags); if (flags & GPIOF_OPEN_SOURCE) - set_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags); + set_bit(FLAG_OPEN_SOURCE, &desc->flags); if (flags & GPIOF_DIR_IN) - err = gpio_direction_input(gpio); + err = gpiod_direction_input(desc); else - err = gpio_direction_output(gpio, + err = gpiod_direction_output(desc, (flags & GPIOF_INIT_HIGH) ? 1 : 0); if (err) goto free_gpio; if (flags & GPIOF_EXPORT) { - err = gpio_export(gpio, flags & GPIOF_EXPORT_CHANGEABLE); + err = gpiod_export(desc, flags & GPIOF_EXPORT_CHANGEABLE); if (err) goto free_gpio; } @@ -1421,7 +1527,7 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) return 0; free_gpio: - gpio_free(gpio); + gpiod_free(desc); return err; } EXPORT_SYMBOL_GPL(gpio_request_one); @@ -1477,13 +1583,14 @@ EXPORT_SYMBOL_GPL(gpio_free_array); const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset) { unsigned gpio = chip->base + offset; + struct gpio_desc *desc = &gpio_desc[gpio]; - if (!gpio_is_valid(gpio) || gpio_desc[gpio].chip != chip) + if (!gpio_is_valid(gpio) || desc->chip != chip) return NULL; - if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0) + if (test_bit(FLAG_REQUESTED, &desc->flags) == 0) return NULL; #ifdef CONFIG_DEBUG_FS - return gpio_desc[gpio].label; + return desc->label; #else return "?"; #endif @@ -1500,24 +1607,21 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested); * rely on gpio_request() having been called beforehand. */ -int gpio_direction_input(unsigned gpio) +static int gpiod_direction_input(struct gpio_desc *desc) { unsigned long flags; struct gpio_chip *chip; - struct gpio_desc *desc = &gpio_desc[gpio]; int status = -EINVAL; + int offset; spin_lock_irqsave(&gpio_lock, flags); - if (!gpio_is_valid(gpio)) + if (!desc) goto fail; chip = desc->chip; if (!chip || !chip->get || !chip->direction_input) goto fail; - gpio -= chip->base; - if (gpio >= chip->ngpio) - goto fail; - status = gpio_ensure_requested(desc, gpio); + status = gpio_ensure_requested(desc); if (status < 0) goto fail; @@ -1527,11 +1631,12 @@ int gpio_direction_input(unsigned gpio) might_sleep_if(chip->can_sleep); + offset = gpio_chip_hwgpio(desc); if (status) { - status = chip->request(chip, gpio); + status = chip->request(chip, offset); if (status < 0) { pr_debug("GPIO-%d: chip request fail, %d\n", - chip->base + gpio, status); + desc_to_gpio(desc), status); /* and it's not available to anyone else ... * gpio_request() is the fully clean solution. */ @@ -1539,48 +1644,54 @@ int gpio_direction_input(unsigned gpio) } } - status = chip->direction_input(chip, gpio); + status = chip->direction_input(chip, offset); if (status == 0) clear_bit(FLAG_IS_OUT, &desc->flags); - trace_gpio_direction(chip->base + gpio, 1, status); + trace_gpio_direction(desc_to_gpio(desc), 1, status); lose: return status; fail: spin_unlock_irqrestore(&gpio_lock, flags); - if (status) + if (status) { + int gpio = -1; + if (desc) + gpio = desc_to_gpio(desc); pr_debug("%s: gpio-%d status %d\n", __func__, gpio, status); + } return status; } + +int gpio_direction_input(unsigned gpio) +{ + return gpiod_direction_input(gpio_to_desc(gpio)); +} EXPORT_SYMBOL_GPL(gpio_direction_input); -int gpio_direction_output(unsigned gpio, int value) +static int gpiod_direction_output(struct gpio_desc *desc, int value) { unsigned long flags; struct gpio_chip *chip; - struct gpio_desc *desc = &gpio_desc[gpio]; int status = -EINVAL; + int offset; /* Open drain pin should not be driven to 1 */ if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - return gpio_direction_input(gpio); + return gpiod_direction_input(desc); /* Open source pin should not be driven to 0 */ if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - return gpio_direction_input(gpio); + return gpiod_direction_input(desc); spin_lock_irqsave(&gpio_lock, flags); - if (!gpio_is_valid(gpio)) + if (!desc) goto fail; chip = desc->chip; if (!chip || !chip->set || !chip->direction_output) goto fail; - gpio -= chip->base; - if (gpio >= chip->ngpio) - goto fail; - status = gpio_ensure_requested(desc, gpio); + status = gpio_ensure_requested(desc); if (status < 0) goto fail; @@ -1590,11 +1701,12 @@ int gpio_direction_output(unsigned gpio, int value) might_sleep_if(chip->can_sleep); + offset = gpio_chip_hwgpio(desc); if (status) { - status = chip->request(chip, gpio); + status = chip->request(chip, offset); if (status < 0) { pr_debug("GPIO-%d: chip request fail, %d\n", - chip->base + gpio, status); + desc_to_gpio(desc), status); /* and it's not available to anyone else ... * gpio_request() is the fully clean solution. */ @@ -1602,20 +1714,29 @@ int gpio_direction_output(unsigned gpio, int value) } } - status = chip->direction_output(chip, gpio, value); + status = chip->direction_output(chip, offset, value); if (status == 0) set_bit(FLAG_IS_OUT, &desc->flags); - trace_gpio_value(chip->base + gpio, 0, value); - trace_gpio_direction(chip->base + gpio, 0, status); + trace_gpio_value(desc_to_gpio(desc), 0, value); + trace_gpio_direction(desc_to_gpio(desc), 0, status); lose: return status; fail: spin_unlock_irqrestore(&gpio_lock, flags); - if (status) + if (status) { + int gpio = -1; + if (desc) + gpio = desc_to_gpio(desc); pr_debug("%s: gpio-%d status %d\n", __func__, gpio, status); + } return status; } + +int gpio_direction_output(unsigned gpio, int value) +{ + return gpiod_direction_output(gpio_to_desc(gpio), value); +} EXPORT_SYMBOL_GPL(gpio_direction_output); /** @@ -1623,24 +1744,22 @@ EXPORT_SYMBOL_GPL(gpio_direction_output); * @gpio: the gpio to set debounce time * @debounce: debounce time is microseconds */ -int gpio_set_debounce(unsigned gpio, unsigned debounce) +static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) { unsigned long flags; struct gpio_chip *chip; - struct gpio_desc *desc = &gpio_desc[gpio]; int status = -EINVAL; + int offset; spin_lock_irqsave(&gpio_lock, flags); - if (!gpio_is_valid(gpio)) + if (!desc) goto fail; chip = desc->chip; if (!chip || !chip->set || !chip->set_debounce) goto fail; - gpio -= chip->base; - if (gpio >= chip->ngpio) - goto fail; - status = gpio_ensure_requested(desc, gpio); + + status = gpio_ensure_requested(desc); if (status < 0) goto fail; @@ -1650,16 +1769,26 @@ int gpio_set_debounce(unsigned gpio, unsigned debounce) might_sleep_if(chip->can_sleep); - return chip->set_debounce(chip, gpio, debounce); + offset = gpio_chip_hwgpio(desc); + return chip->set_debounce(chip, offset, debounce); fail: spin_unlock_irqrestore(&gpio_lock, flags); - if (status) + if (status) { + int gpio = -1; + if (desc) + gpio = desc_to_gpio(desc); pr_debug("%s: gpio-%d status %d\n", __func__, gpio, status); + } return status; } + +int gpio_set_debounce(unsigned gpio, unsigned debounce) +{ + return gpiod_set_debounce(gpio_to_desc(gpio), debounce); +} EXPORT_SYMBOL_GPL(gpio_set_debounce); /* I/O calls are only valid after configuration completed; the relevant @@ -1693,18 +1822,25 @@ EXPORT_SYMBOL_GPL(gpio_set_debounce); * It returns the zero or nonzero value provided by the associated * gpio_chip.get() method; or zero if no such method is provided. */ -int __gpio_get_value(unsigned gpio) +static int gpiod_get_value(struct gpio_desc *desc) { struct gpio_chip *chip; int value; + int offset; - chip = gpio_to_chip(gpio); + chip = desc->chip; + offset = gpio_chip_hwgpio(desc); /* Should be using gpio_get_value_cansleep() */ WARN_ON(chip->can_sleep); - value = chip->get ? chip->get(chip, gpio - chip->base) : 0; - trace_gpio_value(gpio, 1, value); + value = chip->get ? chip->get(chip, offset) : 0; + trace_gpio_value(desc_to_gpio(desc), 1, value); return value; } + +int __gpio_get_value(unsigned gpio) +{ + return gpiod_get_value(gpio_to_desc(gpio)); +} EXPORT_SYMBOL_GPL(__gpio_get_value); /* @@ -1713,23 +1849,25 @@ EXPORT_SYMBOL_GPL(__gpio_get_value); * @chip: Gpio chip. * @value: Non-zero for setting it HIGH otherise it will set to LOW. */ -static void _gpio_set_open_drain_value(unsigned gpio, - struct gpio_chip *chip, int value) +static void _gpio_set_open_drain_value(struct gpio_desc *desc, int value) { int err = 0; + struct gpio_chip *chip = desc->chip; + int offset = gpio_chip_hwgpio(desc); + if (value) { - err = chip->direction_input(chip, gpio - chip->base); + err = chip->direction_input(chip, offset); if (!err) - clear_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags); + clear_bit(FLAG_IS_OUT, &desc->flags); } else { - err = chip->direction_output(chip, gpio - chip->base, 0); + err = chip->direction_output(chip, offset, 0); if (!err) - set_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags); + set_bit(FLAG_IS_OUT, &desc->flags); } - trace_gpio_direction(gpio, value, err); + trace_gpio_direction(desc_to_gpio(desc), value, err); if (err < 0) pr_err("%s: Error in set_value for open drain gpio%d err %d\n", - __func__, gpio, err); + __func__, desc_to_gpio(desc), err); } /* @@ -1738,26 +1876,27 @@ static void _gpio_set_open_drain_value(unsigned gpio, * @chip: Gpio chip. * @value: Non-zero for setting it HIGH otherise it will set to LOW. */ -static void _gpio_set_open_source_value(unsigned gpio, - struct gpio_chip *chip, int value) +static void _gpio_set_open_source_value(struct gpio_desc *desc, int value) { int err = 0; + struct gpio_chip *chip = desc->chip; + int offset = gpio_chip_hwgpio(desc); + if (value) { - err = chip->direction_output(chip, gpio - chip->base, 1); + err = chip->direction_output(chip, offset, 1); if (!err) - set_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags); + set_bit(FLAG_IS_OUT, &desc->flags); } else { - err = chip->direction_input(chip, gpio - chip->base); + err = chip->direction_input(chip, offset); if (!err) - clear_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags); + clear_bit(FLAG_IS_OUT, &desc->flags); } - trace_gpio_direction(gpio, !value, err); + trace_gpio_direction(desc_to_gpio(desc), !value, err); if (err < 0) pr_err("%s: Error in set_value for open source gpio%d err %d\n", - __func__, gpio, err); + __func__, desc_to_gpio(desc), err); } - /** * __gpio_set_value() - assign a gpio's value * @gpio: gpio whose value will be assigned @@ -1767,20 +1906,25 @@ static void _gpio_set_open_source_value(unsigned gpio, * This is used directly or indirectly to implement gpio_set_value(). * It invokes the associated gpio_chip.set() method. */ -void __gpio_set_value(unsigned gpio, int value) +static void gpiod_set_value(struct gpio_desc *desc, int value) { struct gpio_chip *chip; - chip = gpio_to_chip(gpio); + chip = desc->chip; /* Should be using gpio_set_value_cansleep() */ WARN_ON(chip->can_sleep); - trace_gpio_value(gpio, 0, value); - if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags)) - _gpio_set_open_drain_value(gpio, chip, value); - else if (test_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags)) - _gpio_set_open_source_value(gpio, chip, value); + trace_gpio_value(desc_to_gpio(desc), 0, value); + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) + _gpio_set_open_drain_value(desc, value); + else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) + _gpio_set_open_source_value(desc, value); else - chip->set(chip, gpio - chip->base, value); + chip->set(chip, gpio_chip_hwgpio(desc), value); +} + +void __gpio_set_value(unsigned gpio, int value) +{ + return gpiod_set_value(gpio_to_desc(gpio), value); } EXPORT_SYMBOL_GPL(__gpio_set_value); @@ -1792,14 +1936,15 @@ EXPORT_SYMBOL_GPL(__gpio_set_value); * This is used directly or indirectly to implement gpio_cansleep(). It * returns nonzero if access reading or writing the GPIO value can sleep. */ +static int gpiod_cansleep(struct gpio_desc *desc) +{ + /* only call this on GPIOs that are valid! */ + return desc->chip->can_sleep; +} + int __gpio_cansleep(unsigned gpio) { - struct gpio_chip *chip; - - /* only call this on GPIOs that are valid! */ - chip = gpio_to_chip(gpio); - - return chip->can_sleep; + return gpiod_cansleep(gpio_to_desc(gpio)); } EXPORT_SYMBOL_GPL(__gpio_cansleep); @@ -1812,51 +1957,68 @@ EXPORT_SYMBOL_GPL(__gpio_cansleep); * It returns the number of the IRQ signaled by this (input) GPIO, * or a negative errno. */ -int __gpio_to_irq(unsigned gpio) +static int gpiod_to_irq(struct gpio_desc *desc) { struct gpio_chip *chip; + int offset; - chip = gpio_to_chip(gpio); - return chip->to_irq ? chip->to_irq(chip, gpio - chip->base) : -ENXIO; + chip = desc->chip; + offset = gpio_chip_hwgpio(desc); + return chip->to_irq ? chip->to_irq(chip, offset) : -ENXIO; +} + +int __gpio_to_irq(unsigned gpio) +{ + return gpiod_to_irq(gpio_to_desc(gpio)); } EXPORT_SYMBOL_GPL(__gpio_to_irq); - /* There's no value in making it easy to inline GPIO calls that may sleep. * Common examples include ones connected to I2C or SPI chips. */ -int gpio_get_value_cansleep(unsigned gpio) +static int gpiod_get_value_cansleep(struct gpio_desc *desc) { struct gpio_chip *chip; int value; + int offset; might_sleep_if(extra_checks); - chip = gpio_to_chip(gpio); - value = chip->get ? chip->get(chip, gpio - chip->base) : 0; - trace_gpio_value(gpio, 1, value); + chip = desc->chip; + offset = gpio_chip_hwgpio(desc); + value = chip->get ? chip->get(chip, offset) : 0; + trace_gpio_value(desc_to_gpio(desc), 1, value); return value; } + +int gpio_get_value_cansleep(unsigned gpio) +{ + return gpiod_get_value_cansleep(gpio_to_desc(gpio)); +} EXPORT_SYMBOL_GPL(gpio_get_value_cansleep); -void gpio_set_value_cansleep(unsigned gpio, int value) +static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { struct gpio_chip *chip; might_sleep_if(extra_checks); - chip = gpio_to_chip(gpio); - trace_gpio_value(gpio, 0, value); - if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags)) - _gpio_set_open_drain_value(gpio, chip, value); - else if (test_bit(FLAG_OPEN_SOURCE, &gpio_desc[gpio].flags)) - _gpio_set_open_source_value(gpio, chip, value); + chip = desc->chip; + trace_gpio_value(desc_to_gpio(desc), 0, value); + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) + _gpio_set_open_drain_value(desc, value); + else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) + _gpio_set_open_source_value(desc, value); else - chip->set(chip, gpio - chip->base, value); + chip->set(chip, gpio_chip_hwgpio(desc), value); +} + +void gpio_set_value_cansleep(unsigned gpio, int value) +{ + return gpiod_set_value_cansleep(gpio_to_desc(gpio), value); } EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); - #ifdef CONFIG_DEBUG_FS static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) @@ -1870,7 +2032,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) continue; - gpio_get_direction(gpio); + gpiod_get_direction(gdesc); is_out = test_bit(FLAG_IS_OUT, &gdesc->flags); seq_printf(s, " gpio-%-3d (%-20.20s) %s %s", gpio, gdesc->label, From 6c0b4e6c85d085bd92966bc2b8da73e2d7f35929 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 3 Feb 2013 01:29:30 +0900 Subject: [PATCH 2349/4607] gpiolib: let gpio_chip reference its descriptors Add a pointer to the gpio_chip structure that references the array of GPIO descriptors belonging to the chip, and update gpiolib code to use this pointer instead of the global gpio_desc[] array. This is another step towards the removal of the gpio_desc[] global array. Signed-off-by: Alexandre Courbot Reviewed-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 39 ++++++++++++++++++++++---------------- include/asm-generic/gpio.h | 3 +++ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 866431f674c3..5050693dcc35 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -72,6 +72,8 @@ struct gpio_desc { }; static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; +#define GPIO_OFFSET_VALID(chip, offset) (offset >= 0 && offset < chip->ngpio) + static LIST_HEAD(gpio_chips); #ifdef CONFIG_GPIO_SYSFS @@ -112,7 +114,7 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) */ static int gpio_chip_hwgpio(const struct gpio_desc *desc) { - return (desc - &gpio_desc[0]) - desc->chip->base; + return desc - &desc->chip->desc[0]; } /** @@ -133,7 +135,7 @@ static struct gpio_desc *gpio_to_desc(unsigned gpio) */ static int desc_to_gpio(const struct gpio_desc *desc) { - return desc - &gpio_desc[0]; + return desc->chip->base + gpio_chip_hwgpio(desc); } @@ -1007,9 +1009,9 @@ static int gpiochip_export(struct gpio_chip *chip) unsigned gpio; spin_lock_irqsave(&gpio_lock, flags); - gpio = chip->base; - while (gpio_desc[gpio].chip == chip) - gpio_desc[gpio++].chip = NULL; + gpio = 0; + while (gpio < chip->ngpio) + chip->desc[gpio++].chip = NULL; spin_unlock_irqrestore(&gpio_lock, flags); pr_debug("%s: chip %s status %d\n", __func__, @@ -1186,8 +1188,11 @@ int gpiochip_add(struct gpio_chip *chip) status = gpiochip_add_to_list(chip); if (status == 0) { - for (id = base; id < base + chip->ngpio; id++) { - gpio_desc[id].chip = chip; + chip->desc = &gpio_desc[chip->base]; + + for (id = 0; id < chip->ngpio; id++) { + struct gpio_desc *desc = &chip->desc[id]; + desc->chip = chip; /* REVISIT: most hardware initializes GPIOs as * inputs (often with pullups enabled) so power @@ -1196,7 +1201,7 @@ int gpiochip_add(struct gpio_chip *chip) * and in case chip->get_direction is not set, * we may expose the wrong direction in sysfs. */ - gpio_desc[id].flags = !chip->direction_input + desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0; } @@ -1249,15 +1254,15 @@ int gpiochip_remove(struct gpio_chip *chip) gpiochip_remove_pin_ranges(chip); of_gpiochip_remove(chip); - for (id = chip->base; id < chip->base + chip->ngpio; id++) { - if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) { + for (id = 0; id < chip->ngpio; id++) { + if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) { status = -EBUSY; break; } } if (status == 0) { - for (id = chip->base; id < chip->base + chip->ngpio; id++) - gpio_desc[id].chip = NULL; + for (id = 0; id < chip->ngpio; id++) + chip->desc[id].chip = NULL; list_del(&chip->list); } @@ -1582,11 +1587,13 @@ EXPORT_SYMBOL_GPL(gpio_free_array); */ const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset) { - unsigned gpio = chip->base + offset; - struct gpio_desc *desc = &gpio_desc[gpio]; + struct gpio_desc *desc; - if (!gpio_is_valid(gpio) || desc->chip != chip) + if (!GPIO_OFFSET_VALID(chip, offset)) return NULL; + + desc = &chip->desc[offset]; + if (test_bit(FLAG_REQUESTED, &desc->flags) == 0) return NULL; #ifdef CONFIG_DEBUG_FS @@ -2025,7 +2032,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) { unsigned i; unsigned gpio = chip->base; - struct gpio_desc *gdesc = &gpio_desc[gpio]; + struct gpio_desc *gdesc = &chip->desc[0]; int is_out; for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) { diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index b562f95cdc2f..bde646995d10 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -47,6 +47,7 @@ struct gpio; struct seq_file; struct module; struct device_node; +struct gpio_desc; /** * struct gpio_chip - abstract a GPIO controller @@ -76,6 +77,7 @@ struct device_node; * negative during registration, requests dynamic ID allocation. * @ngpio: the number of GPIOs handled by this controller; the last GPIO * handled is (base + ngpio - 1). + * @desc: array of ngpio descriptors. Private. * @can_sleep: flag must be set iff get()/set() methods sleep, as they * must while accessing GPIO expander chips over I2C or SPI * @names: if set, must be an array of strings to use as alternative @@ -126,6 +128,7 @@ struct gpio_chip { struct gpio_chip *chip); int base; u16 ngpio; + struct gpio_desc *desc; const char *const *names; unsigned can_sleep:1; unsigned exported:1; From 362432aed5e5b497a8cf7b20c268ba71df93c045 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 9 Feb 2013 09:41:49 +0000 Subject: [PATCH 2350/4607] gpiolib: Fix locking on gpio debugfs files The debugfs files really need to hold the gpiolib spinlock before accessing the list. Otherwise chip addition/removal will cause an oops. Cc: Alexandre Courbot Cc: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpiolib.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5050693dcc35..4b3f388bcc6d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2053,29 +2053,35 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) { + unsigned long flags; struct gpio_chip *chip = NULL; loff_t index = *pos; - /* REVISIT this isn't locked against gpio_chip removal ... */ - s->private = ""; + spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(chip, &gpio_chips, list) - if (index-- == 0) + if (index-- == 0) { + spin_unlock_irqrestore(&gpio_lock, flags); return chip; + } + spin_unlock_irqrestore(&gpio_lock, flags); return NULL; } static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) { + unsigned long flags; struct gpio_chip *chip = v; void *ret = NULL; + spin_lock_irqsave(&gpio_lock, flags); if (list_is_last(&chip->list, &gpio_chips)) ret = NULL; else ret = list_entry(chip->list.next, struct gpio_chip, list); + spin_unlock_irqrestore(&gpio_lock, flags); s->private = "\n"; ++*pos; From 9f01d30ee191a74f1e7a2ac0710f064f472d000a Mon Sep 17 00:00:00 2001 From: Tony Prisk Date: Fri, 18 Jan 2013 17:58:22 +1300 Subject: [PATCH 2351/4607] gpio/vt8500: memory cleanup missing This driver is missing a .remove callback, and the fail path on probe is incomplete. If an error occurs in vt8500_add_chips, gpio_base is not unmapped. The driver is also ignoring the return value from this function so if a chip fails to register it completes as successful. Replaced pr_err with dev_err in vt8500_add_chips since the device is available. There is also no .remove callback defined so the function is added. Signed-off-by: Tony Prisk Signed-off-by: Grant Likely --- drivers/gpio/gpio-vt8500.c | 65 ++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c index 9a7c434d2947..81683ca35ac1 100644 --- a/drivers/gpio/gpio-vt8500.c +++ b/drivers/gpio/gpio-vt8500.c @@ -127,6 +127,12 @@ struct vt8500_gpio_chip { void __iomem *base; }; +struct vt8500_data { + struct vt8500_gpio_chip *chip; + void __iomem *iobase; + int num_banks; +}; + #define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip) @@ -224,19 +230,32 @@ static int vt8500_of_xlate(struct gpio_chip *gc, static int vt8500_add_chips(struct platform_device *pdev, void __iomem *base, const struct vt8500_gpio_data *data) { + struct vt8500_data *priv; struct vt8500_gpio_chip *vtchip; struct gpio_chip *chip; int i; int pin_cnt = 0; - vtchip = devm_kzalloc(&pdev->dev, - sizeof(struct vt8500_gpio_chip) * data->num_banks, - GFP_KERNEL); - if (!vtchip) { - pr_err("%s: failed to allocate chip memory\n", __func__); + priv = devm_kzalloc(&pdev->dev, sizeof(struct vt8500_data), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "failed to allocate memory\n"); return -ENOMEM; } + priv->chip = devm_kzalloc(&pdev->dev, + sizeof(struct vt8500_gpio_chip) * data->num_banks, + GFP_KERNEL); + if (!priv->chip) { + dev_err(&pdev->dev, "failed to allocate chip memory\n"); + return -ENOMEM; + } + + priv->iobase = base; + priv->num_banks = data->num_banks; + platform_set_drvdata(pdev, priv); + + vtchip = priv->chip; + for (i = 0; i < data->num_banks; i++) { vtchip[i].base = base; vtchip[i].regs = &data->banks[i]; @@ -273,36 +292,54 @@ static struct of_device_id vt8500_gpio_dt_ids[] = { static int vt8500_gpio_probe(struct platform_device *pdev) { + int ret; void __iomem *gpio_base; - struct device_node *np; + struct resource *res; const struct of_device_id *of_id = of_match_device(vt8500_gpio_dt_ids, &pdev->dev); if (!of_id) { - dev_err(&pdev->dev, "Failed to find gpio controller\n"); + dev_err(&pdev->dev, "No matching driver data\n"); return -ENODEV; } - np = pdev->dev.of_node; - if (!np) { - dev_err(&pdev->dev, "Missing GPIO description in devicetree\n"); - return -EFAULT; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Unable to get IO resource\n"); + return -ENODEV; } - gpio_base = of_iomap(np, 0); + gpio_base = devm_request_and_ioremap(&pdev->dev, res); if (!gpio_base) { dev_err(&pdev->dev, "Unable to map GPIO registers\n"); - of_node_put(np); return -ENOMEM; } - vt8500_add_chips(pdev, gpio_base, of_id->data); + ret = vt8500_add_chips(pdev, gpio_base, of_id->data); + + return ret; +} + +static int vt8500_gpio_remove(struct platform_device *pdev) +{ + int i; + int ret; + struct vt8500_data *priv = platform_get_drvdata(pdev); + struct vt8500_gpio_chip *vtchip = priv->chip; + + for (i = 0; i < priv->num_banks; i++) { + ret = gpiochip_remove(&vtchip[i].chip); + if (ret) + dev_warn(&pdev->dev, "gpiochip_remove returned %d\n", + ret); + } return 0; } static struct platform_driver vt8500_gpio_driver = { .probe = vt8500_gpio_probe, + .remove = vt8500_gpio_remove, .driver = { .name = "vt8500-gpio", .owner = THIS_MODULE, From e9a65bb63e5fe45b9d0ecd1983749786858da14d Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Wed, 6 Feb 2013 18:44:32 +0800 Subject: [PATCH 2352/4607] gpio: using common order: let 'static const' instead of 'const static' 'const static ' is not a common order, better to use 'static const' instead. building: make EXTRA_CFLAGS=-W ARCH=arm drivers/gpio/gpio-omap.c:1479: warning: 'static' is not at beginning of declaration drivers/gpio/gpio-omap.c:1485: warning: 'static' is not at beginning of declaration drivers/gpio/gpio-omap.c:1491: warning: 'static' is not at beginning of declaration Signed-off-by: Chen Gang Acked-by: Santosh Shilimkar Signed-off-by: Grant Likely --- drivers/gpio/gpio-omap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index f1fbedb2a6f9..159f5c57eb45 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1476,19 +1476,19 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = { .fallingdetect = OMAP4_GPIO_FALLINGDETECT, }; -const static struct omap_gpio_platform_data omap2_pdata = { +static const struct omap_gpio_platform_data omap2_pdata = { .regs = &omap2_gpio_regs, .bank_width = 32, .dbck_flag = false, }; -const static struct omap_gpio_platform_data omap3_pdata = { +static const struct omap_gpio_platform_data omap3_pdata = { .regs = &omap2_gpio_regs, .bank_width = 32, .dbck_flag = true, }; -const static struct omap_gpio_platform_data omap4_pdata = { +static const struct omap_gpio_platform_data omap4_pdata = { .regs = &omap4_gpio_regs, .bank_width = 32, .dbck_flag = true, From 7dd690c82029ed34aafdb58ce7463cdead69abb5 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Tue, 12 Feb 2013 07:28:55 +0900 Subject: [PATCH 2353/4607] f2fs: avoid build warning This patch removes the following build warning: fs/f2fs/node.c: warning: 'nofs' may be used uninitialized in this function [-Wuninitialized]: => 738:8 Note that this is a false alarm. Signed-off-by: Jaegeuk Kim --- fs/f2fs/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 0da252c78af8..e275218904ed 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -660,7 +660,7 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from) struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); int err = 0, cont = 1; int level, offset[4], noffset[4]; - unsigned int nofs; + unsigned int nofs = 0; struct f2fs_node *rn; struct dnode_of_data dn; struct page *page; From 7897e6022761ace7377f0f784fca059da55f5d71 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Mon, 4 Feb 2013 15:55:58 +0400 Subject: [PATCH 2354/4607] PCI: Disable Bus Master unconditionally in pci_device_shutdown() Commit b566a22c23 ("PCI: disable Bus Master on PCI device shutdown") used pci_disable_device(), but that doesn't disable Bus Mastering unconditionally; we allow nested enable/disable calls, and only the last disable call actually does anything. This uses pci_clear_master() to unconditionally clear the Bus Master bit. Matthew Garrett and Alan Cox said (see LKML link below) that clearing Bus Master for all PCI devices may lead to unpredictable consequences: some devices ignores this bit and continue DMA, some of them hang after that or crash the whole system. But we're already trying to clear Bus Master in general because of b566a22c23; this merely deals with the cases where drivers haven't shut down the device correctly. [bhelgaas: changelog] Link: https://lkml.org/lkml/2012/6/6/278 Signed-off-by: Konstantin Khlebnikov Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/pci/pci-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index f79cbcd3944b..dc5bdce63f12 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -392,7 +392,7 @@ static void pci_device_shutdown(struct device *dev) * Turn off Bus Master bit on the device to tell it to not * continue to do DMA */ - pci_disable_device(pci_dev); + pci_clear_master(pci_dev); } #ifdef CONFIG_PM From fd6dceab017e6be6c158dc56ec6dacf817f21a5b Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Mon, 4 Feb 2013 15:56:01 +0400 Subject: [PATCH 2355/4607] PCI: Catch attempts to disable already-disabled devices Warn when disabling a device that has already been disabled. [bhelgaas: message wording] Signed-off-by: Konstantin Khlebnikov Signed-off-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki --- drivers/pci/pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5cb5820fae40..29a09b705f04 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1401,6 +1401,9 @@ pci_disable_device(struct pci_dev *dev) if (dr) dr->enabled = 0; + dev_WARN_ONCE(&dev->dev, atomic_read(&dev->enable_cnt) <= 0, + "disabling already-disabled device"); + if (atomic_sub_return(1, &dev->enable_cnt) != 0) return; From cc7ba39bab126339d6d525ada07dea5633d71521 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 11 Feb 2013 16:47:01 -0700 Subject: [PATCH 2356/4607] PCI: Use atomic_inc_return() rather than atomic_add_return() No functional change; just use atomic_inc_return() rather than the general-purpose atomic_add_return(). Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 29a09b705f04..c746b04afd6a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1174,7 +1174,7 @@ static int __pci_enable_device_flags(struct pci_dev *dev, dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } - if (atomic_add_return(1, &dev->enable_cnt) > 1) + if (atomic_inc_return(&dev->enable_cnt) > 1) return 0; /* already enabled */ /* only skip sriov related */ @@ -1404,7 +1404,7 @@ pci_disable_device(struct pci_dev *dev) dev_WARN_ONCE(&dev->dev, atomic_read(&dev->enable_cnt) <= 0, "disabling already-disabled device"); - if (atomic_sub_return(1, &dev->enable_cnt) != 0) + if (atomic_dec_return(&dev->enable_cnt) != 0) return; do_pci_disable_device(dev); From 82fee4d67ab86d6fe5eb0f9a9e988ca9d654d765 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 4 Feb 2013 15:56:05 +0400 Subject: [PATCH 2357/4607] PCI/PM: Clear state_saved during suspend This patch clears pci_dev->state_saved at the beginning of suspending. PCI config state may be saved long before that. Some drivers call pci_save_state() from the ->probe() callback to get snapshot of sane configuration space to use in the ->slot_reset() callback. Signed-off-by: Konstantin Khlebnikov # add comment Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index dc5bdce63f12..f9aa311b9f34 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -628,6 +628,7 @@ static int pci_pm_suspend(struct device *dev) goto Fixup; } + pci_dev->state_saved = false; if (pm->suspend) { pci_power_t prev = pci_dev->current_state; int error; @@ -774,6 +775,7 @@ static int pci_pm_freeze(struct device *dev) return 0; } + pci_dev->state_saved = false; if (pm->freeze) { int error; @@ -862,6 +864,7 @@ static int pci_pm_poweroff(struct device *dev) goto Fixup; } + pci_dev->state_saved = false; if (pm->poweroff) { int error; @@ -987,6 +990,7 @@ static int pci_pm_runtime_suspend(struct device *dev) if (!pm || !pm->runtime_suspend) return -ENOSYS; + pci_dev->state_saved = false; pci_dev->no_d3cold = false; error = pm->runtime_suspend(dev); suspend_report_result(pm->runtime_suspend, error); From b390784dc1649f6e6c5e66e5f53c21e715ccf39b Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 11 Feb 2013 16:27:28 -0800 Subject: [PATCH 2358/4607] x86, mm: Use a bitfield to mask nuisance get_user() warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even though it is never executed, gcc wants to warn for casting from a large integer to a pointer. Furthermore, using a variable with __typeof__() doesn't work because __typeof__ retains storage specifiers (const, restrict, volatile). However, we can declare a bitfield using sizeof(), which is legal because sizeof() is a constant expression. This quiets the warning, although the code generated isn't 100% identical from the baseline before 96477b4 x86-32: Add support for 64bit get_user(): [x86-mb is baseline, x86-mm is this commit] text data bss filename 113716147 15858380 35037184 tip.x86-mb/o.i386-allconfig/vmlinux 113716145 15858380 35037184 tip.x86-mm/o.i386-allconfig/vmlinux 12989837 3597944 12255232 tip.x86-mb/o.i386-modconfig/vmlinux 12989831 3597944 12255232 tip.x86-mm/o.i386-modconfig/vmlinux 1462784 237608 1401988 tip.x86-mb/o.i386-noconfig/vmlinux 1462837 237608 1401964 tip.x86-mm/o.i386-noconfig/vmlinux 7938994 553688 7639040 tip.x86-mb/o.i386-pae/vmlinux 7943136 557784 7639040 tip.x86-mm/o.i386-pae/vmlinux 7186126 510572 6574080 tip.x86-mb/o.i386/vmlinux 7186124 510572 6574080 tip.x86-mm/o.i386/vmlinux 103747269 33578856 65888256 tip.x86-mb/o.x86_64-allconfig/vmlinux 103746949 33578856 65888256 tip.x86-mm/o.x86_64-allconfig/vmlinux 12116695 11035832 20160512 tip.x86-mb/o.x86_64-modconfig/vmlinux 12116567 11035832 20160512 tip.x86-mm/o.x86_64-modconfig/vmlinux 1700790 380524 511808 tip.x86-mb/o.x86_64-noconfig/vmlinux 1700790 380524 511808 tip.x86-mm/o.x86_64-noconfig/vmlinux 12413612 1133376 1101824 tip.x86-mb/o.x86_64/vmlinux 12413484 1133376 1101824 tip.x86-mm/o.x86_64/vmlinux Cc: Jamie Lokier Cc: Ville Syrjälä Cc: Borislav Petkov Cc: Russell King Cc: Linus Torvalds Link: http://lkml.kernel.org/r/20130209110031.GA17833@n2100.arm.linux.org.uk Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/uaccess.h | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 1e963267d44e..a8d12653f304 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -168,31 +168,29 @@ do { \ #define get_user(x, ptr) \ ({ \ int __ret_gu; \ - unsigned long __val_gu; \ - unsigned long long __val_gu8; \ + struct { \ + unsigned long long __val_n : 8*sizeof(*(ptr)); \ + } __val_gu; \ __chk_user_ptr(ptr); \ might_fault(); \ switch (sizeof(*(ptr))) { \ case 1: \ - __get_user_x(1, __ret_gu, __val_gu, ptr); \ + __get_user_x(1, __ret_gu, __val_gu.__val_n, ptr); \ break; \ case 2: \ - __get_user_x(2, __ret_gu, __val_gu, ptr); \ + __get_user_x(2, __ret_gu, __val_gu.__val_n, ptr); \ break; \ case 4: \ - __get_user_x(4, __ret_gu, __val_gu, ptr); \ + __get_user_x(4, __ret_gu, __val_gu.__val_n, ptr); \ break; \ case 8: \ - __get_user_8(__ret_gu, __val_gu8, ptr); \ + __get_user_8(__ret_gu, __val_gu.__val_n, ptr); \ break; \ default: \ - __get_user_x(X, __ret_gu, __val_gu, ptr); \ + __get_user_x(X, __ret_gu, __val_gu.__val_n, ptr); \ break; \ } \ - if (sizeof(*(ptr)) == 8) \ - (x) = (__typeof__(*(ptr)))__val_gu8; \ - else \ - (x) = (__typeof__(*(ptr)))__val_gu; \ + (x) = (__typeof__(*(ptr)))__val_gu.__val_n; \ __ret_gu; \ }) From b2593cbe18c4f50c9acacd88860b051f567654d7 Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Mon, 4 Feb 2013 23:00:20 -0800 Subject: [PATCH 2359/4607] libfcoe: Handle CVL while waiting to select an FCF When a CVL is received while we wait to select best FCF, we drop it without handling it. This causes initiator and the switch to go out-of-sync. Initiator proceeds selecting one of the FCFs and tries to send FIP FLOGI. However the switch may reject the FLOGI, as it has cleared its internal state, and expects the initiator to start FIP discovery protocol. Fix this condition by resetting the fcoe controller. Signed-off-by: Bhanu Prakash Gollapudi Reviewed-by: Yi Zou Signed-off-by: Robert Love --- drivers/scsi/fcoe/fcoe_ctlr.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index 75834255bf07..aff3c44a1cdc 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -1291,8 +1291,16 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n"); - if (!fcf || !lport->port_id) + if (!fcf || !lport->port_id) { + /* + * We are yet to select best FCF, but we got CVL in the + * meantime. reset the ctlr and let it rediscover the FCF + */ + mutex_lock(&fip->ctlr_mutex); + fcoe_ctlr_reset(fip); + mutex_unlock(&fip->ctlr_mutex); return; + } /* * mask of required descriptors. Validating each one clears its bit. From dc18f0800f5f16460030a9623d4fcc165d607edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Tue, 12 Feb 2013 16:24:59 +1030 Subject: [PATCH 2360/4607] virtio_console: Use virtio device index to generate port name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use virtio device index for creating unique device port names. Current index allocation in virtio is based on a monotonically increasing variable "index". A better handling of this is to use device index which is allocated by ida. Signed-off-by: Sjur Brændeland Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 95cae778bd73..2cfd5a0575ab 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -61,9 +61,6 @@ struct ports_driver_data { /* List of all the devices we're handling */ struct list_head portdevs; - /* Number of devices this driver is handling */ - unsigned int index; - /* * This is used to keep track of the number of hvc consoles * spawned by this driver. This number is given as the first @@ -169,9 +166,6 @@ struct ports_device { /* Array of per-port IO virtqueues */ struct virtqueue **in_vqs, **out_vqs; - /* Used for numbering devices for sysfs and debugfs */ - unsigned int drv_index; - /* Major number for this device. Ports will be created as minors. */ int chr_major; }; @@ -1415,7 +1409,7 @@ static int add_port(struct ports_device *portdev, u32 id) } port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, devt, port, "vport%up%u", - port->portdev->drv_index, id); + port->portdev->vdev->index, id); if (IS_ERR(port->dev)) { err = PTR_ERR(port->dev); dev_err(&port->portdev->vdev->dev, @@ -1470,7 +1464,7 @@ static int add_port(struct ports_device *portdev, u32 id) * inspect a port's state at any time */ sprintf(debugfs_name, "vport%up%u", - port->portdev->drv_index, id); + port->portdev->vdev->index, id); port->debugfs_file = debugfs_create_file(debugfs_name, 0444, pdrvdata.debugfs_dir, port, @@ -1961,16 +1955,12 @@ static int virtcons_probe(struct virtio_device *vdev) portdev->vdev = vdev; vdev->priv = portdev; - spin_lock_irq(&pdrvdata_lock); - portdev->drv_index = pdrvdata.index++; - spin_unlock_irq(&pdrvdata_lock); - portdev->chr_major = register_chrdev(0, "virtio-portsdev", &portdev_fops); if (portdev->chr_major < 0) { dev_err(&vdev->dev, "Error %d registering chrdev for device %u\n", - portdev->chr_major, portdev->drv_index); + portdev->chr_major, vdev->index); err = portdev->chr_major; goto free; } From 16640165c9079e2cf36fdcfca093f29663a716f7 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 11 Feb 2013 23:14:48 -0800 Subject: [PATCH 2361/4607] x86: Be consistent with data size in getuser.S Consistently use the data register by name and use a sized assembly instruction in getuser.S. There is never any reason to macroize it, and being inconsistent in the same file is just annoying. No actual code change. Signed-off-by: H. Peter Anvin --- arch/x86/lib/getuser.S | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/x86/lib/getuser.S b/arch/x86/lib/getuser.S index d3bf9f99ca77..a4512359656a 100644 --- a/arch/x86/lib/getuser.S +++ b/arch/x86/lib/getuser.S @@ -41,7 +41,7 @@ ENTRY(__get_user_1) cmp TI_addr_limit(%_ASM_DX),%_ASM_AX jae bad_get_user ASM_STAC -1: movzb (%_ASM_AX),%edx +1: movzbl (%_ASM_AX),%edx xor %eax,%eax ASM_CLAC ret @@ -71,7 +71,7 @@ ENTRY(__get_user_4) cmp TI_addr_limit(%_ASM_DX),%_ASM_AX jae bad_get_user ASM_STAC -3: mov -3(%_ASM_AX),%edx +3: movl -3(%_ASM_AX),%edx xor %eax,%eax ASM_CLAC ret @@ -87,7 +87,7 @@ ENTRY(__get_user_8) cmp TI_addr_limit(%_ASM_DX),%_ASM_AX jae bad_get_user ASM_STAC -4: movq -7(%_ASM_AX),%_ASM_DX +4: movq -7(%_ASM_AX),%rdx xor %eax,%eax ASM_CLAC ret @@ -98,8 +98,8 @@ ENTRY(__get_user_8) cmp TI_addr_limit(%_ASM_DX),%_ASM_AX jae bad_get_user_8 ASM_STAC -4: mov -7(%_ASM_AX),%edx -5: mov -3(%_ASM_AX),%ecx +4: movl -7(%_ASM_AX),%edx +5: movl -3(%_ASM_AX),%ecx xor %eax,%eax ASM_CLAC ret From 1de9e46c21298f85820e004bce103858bc9c9dcb Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 16 Jan 2013 18:53:22 +0100 Subject: [PATCH 2362/4607] microblaze: Fix strncpy_from_user macro Problem happens when len in strncpy_from_user is setup and passing string has len-1 chars + \0 terminated character. In this case was returned incorrect length of the string. It should always retunrs the length of the string (not including the trailing NULL). Signed-off-by: Michal Simek --- arch/microblaze/lib/uaccess_old.S | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/arch/microblaze/lib/uaccess_old.S b/arch/microblaze/lib/uaccess_old.S index f085995ee848..0e8cc2710c27 100644 --- a/arch/microblaze/lib/uaccess_old.S +++ b/arch/microblaze/lib/uaccess_old.S @@ -38,15 +38,14 @@ __strncpy_user: addik r3,r7,0 /* temp_count = len */ 1: lbu r4,r6,r0 + beqid r4,2f sb r4,r5,r0 - addik r3,r3,-1 - beqi r3,2f /* break on len */ - addik r5,r5,1 - bneid r4,1b addik r6,r6,1 /* delay slot */ - addik r3,r3,1 /* undo "temp_count--" */ + + addik r3,r3,-1 + bnei r3,1b /* break on len */ 2: rsubk r3,r3,r7 /* temp_count = len - temp_count */ 3: From f6bfc62b9757fda22599dc609d2150b94ebe949d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 15 Jan 2013 11:33:41 +0100 Subject: [PATCH 2363/4607] microblaze: Add .gitignore entries for auto-generated files Add .gitignore entries for files which are generated during the build process. Signed-off-by: Lars-Peter Clausen --- arch/microblaze/boot/.gitignore | 3 +++ arch/microblaze/kernel/.gitignore | 1 + 2 files changed, 4 insertions(+) create mode 100644 arch/microblaze/boot/.gitignore create mode 100644 arch/microblaze/kernel/.gitignore diff --git a/arch/microblaze/boot/.gitignore b/arch/microblaze/boot/.gitignore new file mode 100644 index 000000000000..bf0459186027 --- /dev/null +++ b/arch/microblaze/boot/.gitignore @@ -0,0 +1,3 @@ +*.dtb +linux.bin* +simpleImage.* diff --git a/arch/microblaze/kernel/.gitignore b/arch/microblaze/kernel/.gitignore new file mode 100644 index 000000000000..c5f676c3c224 --- /dev/null +++ b/arch/microblaze/kernel/.gitignore @@ -0,0 +1 @@ +vmlinux.lds From 5f5e3236954af2ce855bc70b253e665392e93d1b Mon Sep 17 00:00:00 2001 From: Jason Wu Date: Thu, 31 Jan 2013 13:59:09 +1000 Subject: [PATCH 2364/4607] microblaze: Makefile clean Remove unnecessary variables Signed-off-by: Jason Wu Acked-by: Michal Simek --- arch/microblaze/Makefile | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/microblaze/Makefile b/arch/microblaze/Makefile index d26fb905ee0a..0a603d3ecf24 100644 --- a/arch/microblaze/Makefile +++ b/arch/microblaze/Makefile @@ -69,16 +69,13 @@ export MMU DTB all: linux.bin -# With make 3.82 we cannot mix normal and wildcard targets -BOOT_TARGETS1 = linux.bin linux.bin.gz -BOOT_TARGETS2 = simpleImage.% - archclean: $(Q)$(MAKE) $(clean)=$(boot) -$(BOOT_TARGETS1): vmlinux +linux.bin linux.bin.gz: vmlinux $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ -$(BOOT_TARGETS2): vmlinux + +simpleImage.%: vmlinux $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ define archhelp From 5b3084b5823339ac7bebca0701b9b89defe11c91 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 1 Feb 2013 15:11:21 +0100 Subject: [PATCH 2365/4607] microblaze: Add missing return from debugfs_tlb Function must return any value. Signed-off-by: Michal Simek --- arch/microblaze/kernel/setup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index 954348f83505..686b6ba09d92 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -216,6 +216,8 @@ static int __init debugfs_tlb(void) d = debugfs_create_u32("tlb_skip", S_IRUGO, of_debugfs_root, &tlb_skip); if (!d) return -ENOMEM; + + return 0; } device_initcall(debugfs_tlb); # endif From 6bd55f0bbaebb79b39e147aa864401fd0c94db82 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 27 Dec 2012 10:40:38 +0100 Subject: [PATCH 2366/4607] microblaze: Fix coding style issues Fix coding style issues reported by checkpatch.pl. Signed-off-by: Michal Simek --- arch/microblaze/include/asm/io.h | 2 +- arch/microblaze/kernel/cpu/cache.c | 148 ++++++++---------- arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c | 21 ++- arch/microblaze/kernel/cpu/cpuinfo.c | 13 +- arch/microblaze/kernel/cpu/pvr.c | 2 +- arch/microblaze/kernel/dma.c | 6 +- arch/microblaze/kernel/early_printk.c | 17 +- arch/microblaze/kernel/exceptions.c | 25 ++- arch/microblaze/kernel/ftrace.c | 44 +++--- arch/microblaze/kernel/heartbeat.c | 2 +- arch/microblaze/kernel/intc.c | 4 +- arch/microblaze/kernel/kgdb.c | 2 +- arch/microblaze/kernel/module.c | 3 +- arch/microblaze/kernel/process.c | 22 +-- arch/microblaze/kernel/ptrace.c | 2 +- arch/microblaze/kernel/setup.c | 28 ++-- arch/microblaze/kernel/signal.c | 6 +- arch/microblaze/kernel/sys_microblaze.c | 1 - arch/microblaze/kernel/traps.c | 6 +- arch/microblaze/lib/ashldi3.c | 1 - arch/microblaze/lib/ashrdi3.c | 1 - arch/microblaze/lib/lshrdi3.c | 1 - arch/microblaze/lib/memcpy.c | 10 +- arch/microblaze/lib/memmove.c | 9 +- arch/microblaze/mm/consistent.c | 5 +- arch/microblaze/mm/fault.c | 10 +- arch/microblaze/mm/init.c | 32 ++-- arch/microblaze/mm/pgtable.c | 15 +- arch/microblaze/pci/indirect_pci.c | 2 +- arch/microblaze/pci/iomap.c | 2 +- arch/microblaze/pci/pci-common.c | 95 +++++------ arch/microblaze/pci/xilinx_pci.c | 16 +- 32 files changed, 259 insertions(+), 294 deletions(-) diff --git a/arch/microblaze/include/asm/io.h b/arch/microblaze/include/asm/io.h index 4fbfdc1ac7f8..8cb8a8566ede 100644 --- a/arch/microblaze/include/asm/io.h +++ b/arch/microblaze/include/asm/io.h @@ -150,7 +150,7 @@ static inline void writel(unsigned int v, volatile void __iomem *addr) #define page_to_bus(page) (page_to_phys(page)) #define bus_to_virt(addr) (phys_to_virt(addr)) -extern void iounmap(void *addr); +extern void iounmap(void __iomem *addr); /*extern void *__ioremap(phys_addr_t address, unsigned long size, unsigned long flags);*/ extern void __iomem *ioremap(phys_addr_t address, unsigned long size); diff --git a/arch/microblaze/kernel/cpu/cache.c b/arch/microblaze/kernel/cpu/cache.c index 4b7d8a3f4aef..4254514b4c8c 100644 --- a/arch/microblaze/kernel/cpu/cache.c +++ b/arch/microblaze/kernel/cpu/cache.c @@ -17,82 +17,70 @@ static inline void __enable_icache_msr(void) { - __asm__ __volatile__ (" msrset r0, %0; \ - nop; " \ + __asm__ __volatile__ (" msrset r0, %0;" \ + "nop;" \ : : "i" (MSR_ICE) : "memory"); } static inline void __disable_icache_msr(void) { - __asm__ __volatile__ (" msrclr r0, %0; \ - nop; " \ + __asm__ __volatile__ (" msrclr r0, %0;" \ + "nop;" \ : : "i" (MSR_ICE) : "memory"); } static inline void __enable_dcache_msr(void) { - __asm__ __volatile__ (" msrset r0, %0; \ - nop; " \ - : \ - : "i" (MSR_DCE) \ - : "memory"); + __asm__ __volatile__ (" msrset r0, %0;" \ + "nop;" \ + : : "i" (MSR_DCE) : "memory"); } static inline void __disable_dcache_msr(void) { - __asm__ __volatile__ (" msrclr r0, %0; \ - nop; " \ - : \ - : "i" (MSR_DCE) \ - : "memory"); + __asm__ __volatile__ (" msrclr r0, %0;" \ + "nop; " \ + : : "i" (MSR_DCE) : "memory"); } static inline void __enable_icache_nomsr(void) { - __asm__ __volatile__ (" mfs r12, rmsr; \ - nop; \ - ori r12, r12, %0; \ - mts rmsr, r12; \ - nop; " \ - : \ - : "i" (MSR_ICE) \ - : "memory", "r12"); + __asm__ __volatile__ (" mfs r12, rmsr;" \ + "nop;" \ + "ori r12, r12, %0;" \ + "mts rmsr, r12;" \ + "nop;" \ + : : "i" (MSR_ICE) : "memory", "r12"); } static inline void __disable_icache_nomsr(void) { - __asm__ __volatile__ (" mfs r12, rmsr; \ - nop; \ - andi r12, r12, ~%0; \ - mts rmsr, r12; \ - nop; " \ - : \ - : "i" (MSR_ICE) \ - : "memory", "r12"); + __asm__ __volatile__ (" mfs r12, rmsr;" \ + "nop;" \ + "andi r12, r12, ~%0;" \ + "mts rmsr, r12;" \ + "nop;" \ + : : "i" (MSR_ICE) : "memory", "r12"); } static inline void __enable_dcache_nomsr(void) { - __asm__ __volatile__ (" mfs r12, rmsr; \ - nop; \ - ori r12, r12, %0; \ - mts rmsr, r12; \ - nop; " \ - : \ - : "i" (MSR_DCE) \ - : "memory", "r12"); + __asm__ __volatile__ (" mfs r12, rmsr;" \ + "nop;" \ + "ori r12, r12, %0;" \ + "mts rmsr, r12;" \ + "nop;" \ + : : "i" (MSR_DCE) : "memory", "r12"); } static inline void __disable_dcache_nomsr(void) { - __asm__ __volatile__ (" mfs r12, rmsr; \ - nop; \ - andi r12, r12, ~%0; \ - mts rmsr, r12; \ - nop; " \ - : \ - : "i" (MSR_DCE) \ - : "memory", "r12"); + __asm__ __volatile__ (" mfs r12, rmsr;" \ + "nop;" \ + "andi r12, r12, ~%0;" \ + "mts rmsr, r12;" \ + "nop;" \ + : : "i" (MSR_DCE) : "memory", "r12"); } @@ -106,7 +94,7 @@ do { \ int align = ~(cache_line_length - 1); \ end = min(start + cache_size, end); \ start &= align; \ -} while (0); +} while (0) /* * Helper macro to loop over the specified cache_size/line_length and @@ -118,12 +106,12 @@ do { \ int step = -line_length; \ WARN_ON(step >= 0); \ \ - __asm__ __volatile__ (" 1: " #op " %0, r0; \ - bgtid %0, 1b; \ - addk %0, %0, %1; \ - " : : "r" (len), "r" (step) \ + __asm__ __volatile__ (" 1: " #op " %0, r0;" \ + "bgtid %0, 1b;" \ + "addk %0, %0, %1;" \ + : : "r" (len), "r" (step) \ : "memory"); \ -} while (0); +} while (0) /* Used for wdc.flush/clear which can use rB for offset which is not possible * to use for simple wdc or wic. @@ -142,12 +130,12 @@ do { \ count = end - start; \ WARN_ON(count < 0); \ \ - __asm__ __volatile__ (" 1: " #op " %0, %1; \ - bgtid %1, 1b; \ - addk %1, %1, %2; \ - " : : "r" (start), "r" (count), \ + __asm__ __volatile__ (" 1: " #op " %0, %1;" \ + "bgtid %1, 1b;" \ + "addk %1, %1, %2;" \ + : : "r" (start), "r" (count), \ "r" (step) : "memory"); \ -} while (0); +} while (0) /* It is used only first parameter for OP - for wic, wdc */ #define CACHE_RANGE_LOOP_1(start, end, line_length, op) \ @@ -157,13 +145,13 @@ do { \ end = ((end & align) == end) ? end - line_length : end & align; \ WARN_ON(end - start < 0); \ \ - __asm__ __volatile__ (" 1: " #op " %1, r0; \ - cmpu %0, %1, %2; \ - bgtid %0, 1b; \ - addk %1, %1, %3; \ - " : : "r" (temp), "r" (start), "r" (end),\ + __asm__ __volatile__ (" 1: " #op " %1, r0;" \ + "cmpu %0, %1, %2;" \ + "bgtid %0, 1b;" \ + "addk %1, %1, %3;" \ + : : "r" (temp), "r" (start), "r" (end), \ "r" (line_length) : "memory"); \ -} while (0); +} while (0) #define ASM_LOOP @@ -352,7 +340,7 @@ static void __invalidate_dcache_all_noirq_wt(void) #endif pr_debug("%s\n", __func__); #ifdef ASM_LOOP - CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc) + CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc); #else for (i = 0; i < cpuinfo.dcache_size; i += cpuinfo.dcache_line_length) @@ -361,7 +349,8 @@ static void __invalidate_dcache_all_noirq_wt(void) #endif } -/* FIXME It is blindly invalidation as is expected +/* + * FIXME It is blindly invalidation as is expected * but can't be called on noMMU in microblaze_cache_init below * * MS: noMMU kernel won't boot if simple wdc is used @@ -375,7 +364,7 @@ static void __invalidate_dcache_all_wb(void) pr_debug("%s\n", __func__); #ifdef ASM_LOOP CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, - wdc) + wdc); #else for (i = 0; i < cpuinfo.dcache_size; i += cpuinfo.dcache_line_length) @@ -616,49 +605,48 @@ static const struct scache wt_nomsr_noirq = { #define CPUVER_7_20_A 0x0c #define CPUVER_7_20_D 0x0f -#define INFO(s) printk(KERN_INFO "cache: " s "\n"); - void microblaze_cache_init(void) { if (cpuinfo.use_instr & PVR2_USE_MSR_INSTR) { if (cpuinfo.dcache_wb) { - INFO("wb_msr"); + pr_info("wb_msr\n"); mbc = (struct scache *)&wb_msr; if (cpuinfo.ver_code <= CPUVER_7_20_D) { /* MS: problem with signal handling - hw bug */ - INFO("WB won't work properly"); + pr_info("WB won't work properly\n"); } } else { if (cpuinfo.ver_code >= CPUVER_7_20_A) { - INFO("wt_msr_noirq"); + pr_info("wt_msr_noirq\n"); mbc = (struct scache *)&wt_msr_noirq; } else { - INFO("wt_msr"); + pr_info("wt_msr\n"); mbc = (struct scache *)&wt_msr; } } } else { if (cpuinfo.dcache_wb) { - INFO("wb_nomsr"); + pr_info("wb_nomsr\n"); mbc = (struct scache *)&wb_nomsr; if (cpuinfo.ver_code <= CPUVER_7_20_D) { /* MS: problem with signal handling - hw bug */ - INFO("WB won't work properly"); + pr_info("WB won't work properly\n"); } } else { if (cpuinfo.ver_code >= CPUVER_7_20_A) { - INFO("wt_nomsr_noirq"); + pr_info("wt_nomsr_noirq\n"); mbc = (struct scache *)&wt_nomsr_noirq; } else { - INFO("wt_nomsr"); + pr_info("wt_nomsr\n"); mbc = (struct scache *)&wt_nomsr; } } } -/* FIXME Invalidation is done in U-BOOT - * WT cache: Data is already written to main memory - * WB cache: Discard data on noMMU which caused that kernel doesn't boot - */ + /* + * FIXME Invalidation is done in U-BOOT + * WT cache: Data is already written to main memory + * WB cache: Discard data on noMMU which caused that kernel doesn't boot + */ /* invalidate_dcache(); */ enable_dcache(); diff --git a/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c b/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c index 916aaedf1945..ee4689415410 100644 --- a/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c +++ b/arch/microblaze/kernel/cpu/cpuinfo-pvr-full.c @@ -27,7 +27,7 @@ early_printk("ERROR: Microblaze " x "-different for PVR and DTS\n"); #else #define err_printk(x) \ - printk(KERN_INFO "ERROR: Microblaze " x "-different for PVR and DTS\n"); + pr_info("ERROR: Microblaze " x "-different for PVR and DTS\n"); #endif void set_cpuinfo_pvr_full(struct cpuinfo *ci, struct device_node *cpu) @@ -38,12 +38,11 @@ void set_cpuinfo_pvr_full(struct cpuinfo *ci, struct device_node *cpu) CI(ver_code, VERSION); if (!ci->ver_code) { - printk(KERN_ERR "ERROR: MB has broken PVR regs " - "-> use DTS setting\n"); + pr_err("ERROR: MB has broken PVR regs -> use DTS setting\n"); return; } - temp = PVR_USE_BARREL(pvr) | PVR_USE_MSR_INSTR(pvr) |\ + temp = PVR_USE_BARREL(pvr) | PVR_USE_MSR_INSTR(pvr) | PVR_USE_PCMP_INSTR(pvr) | PVR_USE_DIV(pvr); if (ci->use_instr != temp) err_printk("BARREL, MSR, PCMP or DIV"); @@ -59,13 +58,13 @@ void set_cpuinfo_pvr_full(struct cpuinfo *ci, struct device_node *cpu) err_printk("HW_FPU"); ci->use_fpu = temp; - ci->use_exc = PVR_OPCODE_0x0_ILLEGAL(pvr) |\ - PVR_UNALIGNED_EXCEPTION(pvr) |\ - PVR_ILL_OPCODE_EXCEPTION(pvr) |\ - PVR_IOPB_BUS_EXCEPTION(pvr) |\ - PVR_DOPB_BUS_EXCEPTION(pvr) |\ - PVR_DIV_ZERO_EXCEPTION(pvr) |\ - PVR_FPU_EXCEPTION(pvr) |\ + ci->use_exc = PVR_OPCODE_0x0_ILLEGAL(pvr) | + PVR_UNALIGNED_EXCEPTION(pvr) | + PVR_ILL_OPCODE_EXCEPTION(pvr) | + PVR_IOPB_BUS_EXCEPTION(pvr) | + PVR_DOPB_BUS_EXCEPTION(pvr) | + PVR_DIV_ZERO_EXCEPTION(pvr) | + PVR_FPU_EXCEPTION(pvr) | PVR_FSL_EXCEPTION(pvr); CI(pvr_user1, USER1); diff --git a/arch/microblaze/kernel/cpu/cpuinfo.c b/arch/microblaze/kernel/cpu/cpuinfo.c index eab6abf5652e..0b2299bcb948 100644 --- a/arch/microblaze/kernel/cpu/cpuinfo.c +++ b/arch/microblaze/kernel/cpu/cpuinfo.c @@ -68,31 +68,30 @@ void __init setup_cpuinfo(void) cpu = (struct device_node *) of_find_node_by_type(NULL, "cpu"); if (!cpu) - printk(KERN_ERR "You don't have cpu!!!\n"); + pr_err("You don't have cpu!!!\n"); - printk(KERN_INFO "%s: initialising\n", __func__); + pr_info("%s: initialising\n", __func__); switch (cpu_has_pvr()) { case 0: - printk(KERN_WARNING - "%s: No PVR support. Using static CPU info from FDT\n", + pr_warn("%s: No PVR support. Using static CPU info from FDT\n", __func__); set_cpuinfo_static(&cpuinfo, cpu); break; /* FIXME I found weird behavior with MB 7.00.a/b 7.10.a * please do not use FULL PVR with MMU */ case 1: - printk(KERN_INFO "%s: Using full CPU PVR support\n", + pr_info("%s: Using full CPU PVR support\n", __func__); set_cpuinfo_static(&cpuinfo, cpu); set_cpuinfo_pvr_full(&cpuinfo, cpu); break; default: - printk(KERN_WARNING "%s: Unsupported PVR setting\n", __func__); + pr_warn("%s: Unsupported PVR setting\n", __func__); set_cpuinfo_static(&cpuinfo, cpu); } if (cpuinfo.mmu_privins) - printk(KERN_WARNING "%s: Stream instructions enabled" + pr_warn("%s: Stream instructions enabled" " - USERSPACE CAN LOCK THIS KERNEL!\n", __func__); } diff --git a/arch/microblaze/kernel/cpu/pvr.c b/arch/microblaze/kernel/cpu/pvr.c index 3a749d5e71fd..8d0dc6db48cf 100644 --- a/arch/microblaze/kernel/cpu/pvr.c +++ b/arch/microblaze/kernel/cpu/pvr.c @@ -27,7 +27,7 @@ tmp = 0x0; /* Prevent warning about unused */ \ __asm__ __volatile__ ( \ "mfs %0, rpvr" #pvrid ";" \ - : "=r" (tmp) : : "memory"); \ + : "=r" (tmp) : : "memory"); \ val = tmp; \ } diff --git a/arch/microblaze/kernel/dma.c b/arch/microblaze/kernel/dma.c index a2bfa2ca5730..da68d00fd087 100644 --- a/arch/microblaze/kernel/dma.c +++ b/arch/microblaze/kernel/dma.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include /* * Generic direct DMA implementation @@ -197,8 +197,8 @@ EXPORT_SYMBOL(dma_direct_ops); static int __init dma_init(void) { - dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES); + dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES); - return 0; + return 0; } fs_initcall(dma_init); diff --git a/arch/microblaze/kernel/early_printk.c b/arch/microblaze/kernel/early_printk.c index aba1f9a97d5d..60dcacc68038 100644 --- a/arch/microblaze/kernel/early_printk.c +++ b/arch/microblaze/kernel/early_printk.c @@ -140,20 +140,20 @@ int __init setup_early_printk(char *opt) switch (version) { #ifdef CONFIG_SERIAL_UARTLITE_CONSOLE case UARTLITE: - printk(KERN_INFO "Early console on uartlite " - "at 0x%08x\n", base_addr); + pr_info("Early console on uartlite at 0x%08x\n", + base_addr); early_console = &early_serial_uartlite_console; break; #endif #ifdef CONFIG_SERIAL_8250_CONSOLE case UART16550: - printk(KERN_INFO "Early console on uart16650 " - "at 0x%08x\n", base_addr); + pr_info("Early console on uart16650 at 0x%08x\n", + base_addr); early_console = &early_serial_uart16550_console; break; #endif default: - printk(KERN_INFO "Unsupported early console %d\n", + pr_info("Unsupported early console %d\n", version); return 1; } @@ -171,10 +171,9 @@ void __init remap_early_printk(void) { if (!early_console_initialized || !early_console) return; - printk(KERN_INFO "early_printk_console remapping from 0x%x to ", - base_addr); + pr_info("early_printk_console remapping from 0x%x to ", base_addr); base_addr = (u32) ioremap(base_addr, PAGE_SIZE); - printk(KERN_CONT "0x%x\n", base_addr); + pr_cont("0x%x\n", base_addr); #ifdef CONFIG_MMU /* @@ -197,7 +196,7 @@ void __init disable_early_printk(void) { if (!early_console_initialized || !early_console) return; - printk(KERN_WARNING "disabling early console\n"); + pr_warn("disabling early console\n"); unregister_console(early_console); early_console_initialized = 0; } diff --git a/arch/microblaze/kernel/exceptions.c b/arch/microblaze/kernel/exceptions.c index 6348dc82f428..d6edf5d39cfa 100644 --- a/arch/microblaze/kernel/exceptions.c +++ b/arch/microblaze/kernel/exceptions.c @@ -40,7 +40,7 @@ void die(const char *str, struct pt_regs *fp, long err) { console_verbose(); spin_lock_irq(&die_lock); - printk(KERN_WARNING "Oops: %s, sig: %ld\n", str, err); + pr_warn("Oops: %s, sig: %ld\n", str, err); show_regs(fp); spin_unlock_irq(&die_lock); /* do_exit() should take care of panic'ing from an interrupt @@ -61,9 +61,9 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr) { siginfo_t info; - if (kernel_mode(regs)) { + if (kernel_mode(regs)) die("Exception in kernel mode", regs, signr); - } + info.si_signo = signr; info.si_errno = 0; info.si_code = code; @@ -79,8 +79,7 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, #endif #if 0 - printk(KERN_WARNING "Exception %02x in %s mode, FSR=%08x PC=%08x " \ - "ESR=%08x\n", + pr_warn("Exception %02x in %s mode, FSR=%08x PC=%08x ESR=%08x\n", type, user_mode(regs) ? "user" : "kernel", fsr, (unsigned int) regs->pc, (unsigned int) regs->esr); #endif @@ -92,8 +91,7 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, _exception(SIGILL, regs, ILL_ILLOPC, addr); return; } - printk(KERN_WARNING "Illegal opcode exception " \ - "in kernel mode.\n"); + pr_warn("Illegal opcode exception in kernel mode.\n"); die("opcode exception", regs, SIGBUS); break; case MICROBLAZE_IBUS_EXCEPTION: @@ -102,8 +100,7 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, _exception(SIGBUS, regs, BUS_ADRERR, addr); return; } - printk(KERN_WARNING "Instruction bus error exception " \ - "in kernel mode.\n"); + pr_warn("Instruction bus error exception in kernel mode.\n"); die("bus exception", regs, SIGBUS); break; case MICROBLAZE_DBUS_EXCEPTION: @@ -112,8 +109,7 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, _exception(SIGBUS, regs, BUS_ADRERR, addr); return; } - printk(KERN_WARNING "Data bus error exception " \ - "in kernel mode.\n"); + pr_warn("Data bus error exception in kernel mode.\n"); die("bus exception", regs, SIGBUS); break; case MICROBLAZE_DIV_ZERO_EXCEPTION: @@ -122,8 +118,7 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, _exception(SIGFPE, regs, FPE_INTDIV, addr); return; } - printk(KERN_WARNING "Divide by zero exception " \ - "in kernel mode.\n"); + pr_warn("Divide by zero exception in kernel mode.\n"); die("Divide by zero exception", regs, SIGBUS); break; case MICROBLAZE_FPU_EXCEPTION: @@ -151,8 +146,8 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, #endif default: /* FIXME what to do in unexpected exception */ - printk(KERN_WARNING "Unexpected exception %02x " - "PC=%08x in %s mode\n", type, (unsigned int) addr, + pr_warn("Unexpected exception %02x PC=%08x in %s mode\n", + type, (unsigned int) addr, kernel_mode(regs) ? "kernel" : "user"); } return; diff --git a/arch/microblaze/kernel/ftrace.c b/arch/microblaze/kernel/ftrace.c index 357d56abe24a..e8a5e9cf4ed1 100644 --- a/arch/microblaze/kernel/ftrace.c +++ b/arch/microblaze/kernel/ftrace.c @@ -35,18 +35,18 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) * happen. This tool is too much intrusive to * ignore such a protection. */ - asm volatile(" 1: lwi %0, %2, 0; \ - 2: swi %3, %2, 0; \ - addik %1, r0, 0; \ - 3: \ - .section .fixup, \"ax\"; \ - 4: brid 3b; \ - addik %1, r0, 1; \ - .previous; \ - .section __ex_table,\"a\"; \ - .word 1b,4b; \ - .word 2b,4b; \ - .previous;" \ + asm volatile(" 1: lwi %0, %2, 0;" \ + "2: swi %3, %2, 0;" \ + " addik %1, r0, 0;" \ + "3:" \ + " .section .fixup, \"ax\";" \ + "4: brid 3b;" \ + " addik %1, r0, 1;" \ + " .previous;" \ + " .section __ex_table,\"a\";" \ + " .word 1b,4b;" \ + " .word 2b,4b;" \ + " .previous;" \ : "=&r" (old), "=r" (faulted) : "r" (parent), "r" (return_hooker) ); @@ -81,16 +81,16 @@ static int ftrace_modify_code(unsigned long addr, unsigned int value) { int faulted = 0; - __asm__ __volatile__(" 1: swi %2, %1, 0; \ - addik %0, r0, 0; \ - 2: \ - .section .fixup, \"ax\"; \ - 3: brid 2b; \ - addik %0, r0, 1; \ - .previous; \ - .section __ex_table,\"a\"; \ - .word 1b,3b; \ - .previous;" \ + __asm__ __volatile__(" 1: swi %2, %1, 0;" \ + " addik %0, r0, 0;" \ + "2:" \ + " .section .fixup, \"ax\";" \ + "3: brid 2b;" \ + " addik %0, r0, 1;" \ + " .previous;" \ + " .section __ex_table,\"a\";" \ + " .word 1b,3b;" \ + " .previous;" \ : "=r" (faulted) : "r" (addr), "r" (value) ); diff --git a/arch/microblaze/kernel/heartbeat.c b/arch/microblaze/kernel/heartbeat.c index 154756f3c694..1879a0527776 100644 --- a/arch/microblaze/kernel/heartbeat.c +++ b/arch/microblaze/kernel/heartbeat.c @@ -61,7 +61,7 @@ void setup_heartbeat(void) if (gpio) { base_addr = be32_to_cpup(of_get_property(gpio, "reg", NULL)); base_addr = (unsigned long) ioremap(base_addr, PAGE_SIZE); - printk(KERN_NOTICE "Heartbeat GPIO at 0x%x\n", base_addr); + pr_notice("Heartbeat GPIO at 0x%x\n", base_addr); /* GPIO is configured as output */ prop = (int *) of_get_property(gpio, "xlnx,is-bidir", NULL); diff --git a/arch/microblaze/kernel/intc.c b/arch/microblaze/kernel/intc.c index 7a1a8d4354fe..8778adf72bd3 100644 --- a/arch/microblaze/kernel/intc.c +++ b/arch/microblaze/kernel/intc.c @@ -147,12 +147,12 @@ void __init init_IRQ(void) intr_mask = be32_to_cpup(of_get_property(intc, "xlnx,kind-of-intr", NULL)); if (intr_mask > (u32)((1ULL << nr_irq) - 1)) - printk(KERN_INFO " ERROR: Mismatch in kind-of-intr param\n"); + pr_info(" ERROR: Mismatch in kind-of-intr param\n"); #ifdef CONFIG_SELFMOD_INTC selfmod_function((int *) arr_func, intc_baseaddr); #endif - printk(KERN_INFO "%s #0 at 0x%08x, num_irq=%d, edge=0x%x\n", + pr_info("%s #0 at 0x%08x, num_irq=%d, edge=0x%x\n", intc->name, intc_baseaddr, nr_irq, intr_mask); /* diff --git a/arch/microblaze/kernel/kgdb.c b/arch/microblaze/kernel/kgdb.c index 09a5e8286137..8adc92443100 100644 --- a/arch/microblaze/kernel/kgdb.c +++ b/arch/microblaze/kernel/kgdb.c @@ -141,7 +141,7 @@ void kgdb_arch_exit(void) /* * Global data */ -struct kgdb_arch arch_kgdb_ops = { +const struct kgdb_arch arch_kgdb_ops = { #ifdef __MICROBLAZEEL__ .gdb_bpt_instr = {0x18, 0x00, 0x0c, 0xba}, /* brki r16, 0x18 */ #else diff --git a/arch/microblaze/kernel/module.c b/arch/microblaze/kernel/module.c index f39257a5abcf..f0f43688a0c6 100644 --- a/arch/microblaze/kernel/module.c +++ b/arch/microblaze/kernel/module.c @@ -108,8 +108,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, break; default: - printk(KERN_ERR "module %s: " - "Unknown relocation: %u\n", + pr_err("module %s: Unknown relocation: %u\n", module->name, ELF32_R_TYPE(rela[i].r_info)); return -ENOEXEC; diff --git a/arch/microblaze/kernel/process.c b/arch/microblaze/kernel/process.c index a5b74f729e5b..282dfb8f1fe7 100644 --- a/arch/microblaze/kernel/process.c +++ b/arch/microblaze/kernel/process.c @@ -15,29 +15,29 @@ #include #include #include -#include /* for USER_DS macros */ +#include /* for USER_DS macros */ #include void show_regs(struct pt_regs *regs) { - printk(KERN_INFO " Registers dump: mode=%X\r\n", regs->pt_mode); - printk(KERN_INFO " r1=%08lX, r2=%08lX, r3=%08lX, r4=%08lX\n", + pr_info(" Registers dump: mode=%X\r\n", regs->pt_mode); + pr_info(" r1=%08lX, r2=%08lX, r3=%08lX, r4=%08lX\n", regs->r1, regs->r2, regs->r3, regs->r4); - printk(KERN_INFO " r5=%08lX, r6=%08lX, r7=%08lX, r8=%08lX\n", + pr_info(" r5=%08lX, r6=%08lX, r7=%08lX, r8=%08lX\n", regs->r5, regs->r6, regs->r7, regs->r8); - printk(KERN_INFO " r9=%08lX, r10=%08lX, r11=%08lX, r12=%08lX\n", + pr_info(" r9=%08lX, r10=%08lX, r11=%08lX, r12=%08lX\n", regs->r9, regs->r10, regs->r11, regs->r12); - printk(KERN_INFO " r13=%08lX, r14=%08lX, r15=%08lX, r16=%08lX\n", + pr_info(" r13=%08lX, r14=%08lX, r15=%08lX, r16=%08lX\n", regs->r13, regs->r14, regs->r15, regs->r16); - printk(KERN_INFO " r17=%08lX, r18=%08lX, r19=%08lX, r20=%08lX\n", + pr_info(" r17=%08lX, r18=%08lX, r19=%08lX, r20=%08lX\n", regs->r17, regs->r18, regs->r19, regs->r20); - printk(KERN_INFO " r21=%08lX, r22=%08lX, r23=%08lX, r24=%08lX\n", + pr_info(" r21=%08lX, r22=%08lX, r23=%08lX, r24=%08lX\n", regs->r21, regs->r22, regs->r23, regs->r24); - printk(KERN_INFO " r25=%08lX, r26=%08lX, r27=%08lX, r28=%08lX\n", + pr_info(" r25=%08lX, r26=%08lX, r27=%08lX, r28=%08lX\n", regs->r25, regs->r26, regs->r27, regs->r28); - printk(KERN_INFO " r29=%08lX, r30=%08lX, r31=%08lX, rPC=%08lX\n", + pr_info(" r29=%08lX, r30=%08lX, r31=%08lX, rPC=%08lX\n", regs->r29, regs->r30, regs->r31, regs->pc); - printk(KERN_INFO " msr=%08lX, ear=%08lX, esr=%08lX, fsr=%08lX\n", + pr_info(" msr=%08lX, ear=%08lX, esr=%08lX, fsr=%08lX\n", regs->msr, regs->ear, regs->esr, regs->fsr); } diff --git a/arch/microblaze/kernel/ptrace.c b/arch/microblaze/kernel/ptrace.c index ab1b9db661f3..c554980eca8b 100644 --- a/arch/microblaze/kernel/ptrace.c +++ b/arch/microblaze/kernel/ptrace.c @@ -40,7 +40,7 @@ #include #include #include -#include +#include /* Returns the address where the register at REG_OFFS in P is stashed away. */ static microblaze_reg_t *reg_save_addr(unsigned reg_offs, diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c index 686b6ba09d92..0263da7b83dd 100644 --- a/arch/microblaze/kernel/setup.c +++ b/arch/microblaze/kernel/setup.c @@ -150,33 +150,35 @@ void __init machine_early_init(const char *cmdline, unsigned int ram, /* printk("TLB1 0x%08x, TLB0 0x%08x, tlb 0x%x\n", tlb0, tlb1, kernel_tlb); */ - printk("Ramdisk addr 0x%08x, ", ram); + pr_info("Ramdisk addr 0x%08x, ", ram); if (fdt) - printk("FDT at 0x%08x\n", fdt); + pr_info("FDT at 0x%08x\n", fdt); else - printk("Compiled-in FDT at 0x%08x\n", + pr_info("Compiled-in FDT at 0x%08x\n", (unsigned int)_fdt_start); #ifdef CONFIG_MTD_UCLINUX - printk("Found romfs @ 0x%08x (0x%08x)\n", + pr_info("Found romfs @ 0x%08x (0x%08x)\n", romfs_base, romfs_size); - printk("#### klimit %p ####\n", old_klimit); + pr_info("#### klimit %p ####\n", old_klimit); BUG_ON(romfs_size < 0); /* What else can we do? */ - printk("Moved 0x%08x bytes from 0x%08x to 0x%08x\n", + pr_info("Moved 0x%08x bytes from 0x%08x to 0x%08x\n", romfs_size, romfs_base, (unsigned)&__bss_stop); - printk("New klimit: 0x%08x\n", (unsigned)klimit); + pr_info("New klimit: 0x%08x\n", (unsigned)klimit); #endif #if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR - if (msr) - printk("!!!Your kernel has setup MSR instruction but " - "CPU don't have it %x\n", msr); + if (msr) { + pr_info("!!!Your kernel has setup MSR instruction but "); + pr_cont("CPU don't have it %x\n", msr); + } #else - if (!msr) - printk("!!!Your kernel not setup MSR instruction but " - "CPU have it %x\n", msr); + if (!msr) { + pr_info("!!!Your kernel not setup MSR instruction but "); + pr_cont"CPU have it %x\n", msr); + } #endif /* Do not copy reset vectors. offset = 0x2 means skip the first diff --git a/arch/microblaze/kernel/signal.c b/arch/microblaze/kernel/signal.c index ac3d0a0f4814..4fcbad0015f9 100644 --- a/arch/microblaze/kernel/signal.c +++ b/arch/microblaze/kernel/signal.c @@ -255,7 +255,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, set_fs(USER_DS); #ifdef DEBUG_SIG - printk(KERN_INFO "SIG deliver (%s:%d): sp=%p pc=%08lx\n", + pr_info("SIG deliver (%s:%d): sp=%p pc=%08lx\n", current->comm, current->pid, frame, regs->pc); #endif @@ -330,8 +330,8 @@ static void do_signal(struct pt_regs *regs, int in_syscall) int signr; struct k_sigaction ka; #ifdef DEBUG_SIG - printk(KERN_INFO "do signal: %p %d\n", regs, in_syscall); - printk(KERN_INFO "do signal2: %lx %lx %ld [%lx]\n", regs->pc, regs->r1, + pr_info("do signal: %p %d\n", regs, in_syscall); + pr_info("do signal2: %lx %lx %ld [%lx]\n", regs->pc, regs->r1, regs->r12, current_thread_info()->flags); #endif diff --git a/arch/microblaze/kernel/sys_microblaze.c b/arch/microblaze/kernel/sys_microblaze.c index 63647c586b43..3845677e93ea 100644 --- a/arch/microblaze/kernel/sys_microblaze.c +++ b/arch/microblaze/kernel/sys_microblaze.c @@ -31,7 +31,6 @@ #include #include #include - #include asmlinkage long sys_mmap(unsigned long addr, unsigned long len, diff --git a/arch/microblaze/kernel/traps.c b/arch/microblaze/kernel/traps.c index 5541ac559593..a37b2b111d86 100644 --- a/arch/microblaze/kernel/traps.c +++ b/arch/microblaze/kernel/traps.c @@ -26,7 +26,7 @@ static unsigned long kstack_depth_to_print; /* 0 == entire stack */ static int __init kstack_setup(char *s) { - return !strict_strtoul(s, 0, &kstack_depth_to_print); + return !kstrtoul(s, 0, &kstack_depth_to_print); } __setup("kstack=", kstack_setup); @@ -66,9 +66,7 @@ void show_stack(struct task_struct *task, unsigned long *sp) } print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4, (void *)fp, words_to_show << 2, 0); - printk(KERN_INFO "\n\n"); - - pr_info("Call Trace:\n"); + pr_info("\n\nCall Trace:\n"); microblaze_unwind(task, NULL); pr_info("\n"); diff --git a/arch/microblaze/lib/ashldi3.c b/arch/microblaze/lib/ashldi3.c index beb80f316095..dd80fbcdc57c 100644 --- a/arch/microblaze/lib/ashldi3.c +++ b/arch/microblaze/lib/ashldi3.c @@ -25,5 +25,4 @@ long long __ashldi3(long long u, word_type b) return w.ll; } - EXPORT_SYMBOL(__ashldi3); diff --git a/arch/microblaze/lib/ashrdi3.c b/arch/microblaze/lib/ashrdi3.c index c884a912b660..1f21fc6a576b 100644 --- a/arch/microblaze/lib/ashrdi3.c +++ b/arch/microblaze/lib/ashrdi3.c @@ -27,5 +27,4 @@ long long __ashrdi3(long long u, word_type b) return w.ll; } - EXPORT_SYMBOL(__ashrdi3); diff --git a/arch/microblaze/lib/lshrdi3.c b/arch/microblaze/lib/lshrdi3.c index dcf8d6810b7c..c10090bedc33 100644 --- a/arch/microblaze/lib/lshrdi3.c +++ b/arch/microblaze/lib/lshrdi3.c @@ -25,5 +25,4 @@ long long __lshrdi3(long long u, word_type b) return w.ll; } - EXPORT_SYMBOL(__lshrdi3); diff --git a/arch/microblaze/lib/memcpy.c b/arch/microblaze/lib/memcpy.c index fe9c53fafdea..15a6759ff9fa 100644 --- a/arch/microblaze/lib/memcpy.c +++ b/arch/microblaze/lib/memcpy.c @@ -103,12 +103,12 @@ void *memcpy(void *v_dst, const void *v_src, __kernel_size_t c) } #else /* Load the holding buffer */ - buf_hold = (*i_src++ & 0xFFFFFF00) >>8; + buf_hold = (*i_src++ & 0xFFFFFF00) >> 8; for (; c >= 4; c -= 4) { value = *i_src++; *i_dst++ = buf_hold | ((value & 0xFF) << 24); - buf_hold = (value & 0xFFFFFF00) >>8; + buf_hold = (value & 0xFFFFFF00) >> 8; } #endif /* Realign the source */ @@ -129,12 +129,12 @@ void *memcpy(void *v_dst, const void *v_src, __kernel_size_t c) } #else /* Load the holding buffer */ - buf_hold = (*i_src++ & 0xFFFF0000 )>>16; + buf_hold = (*i_src++ & 0xFFFF0000) >> 16; for (; c >= 4; c -= 4) { value = *i_src++; - *i_dst++ = buf_hold | ((value & 0xFFFF)<<16); - buf_hold = (value & 0xFFFF0000) >>16; + *i_dst++ = buf_hold | ((value & 0xFFFF) << 16); + buf_hold = (value & 0xFFFF0000) >> 16; } #endif /* Realign the source */ diff --git a/arch/microblaze/lib/memmove.c b/arch/microblaze/lib/memmove.c index 2146c3752a80..9843c6b0f0f8 100644 --- a/arch/microblaze/lib/memmove.c +++ b/arch/microblaze/lib/memmove.c @@ -129,7 +129,8 @@ void *memmove(void *v_dst, const void *v_src, __kernel_size_t c) for (; c >= 4; c -= 4) { value = *--i_src; - *--i_dst = buf_hold | ((value & 0xFFFFFF00)>>8); + *--i_dst = buf_hold | + ((value & 0xFFFFFF00) >> 8); buf_hold = (value & 0xFF) << 24; } #endif @@ -155,7 +156,8 @@ void *memmove(void *v_dst, const void *v_src, __kernel_size_t c) for (; c >= 4; c -= 4) { value = *--i_src; - *--i_dst = buf_hold | ((value & 0xFFFF0000)>>16); + *--i_dst = buf_hold | + ((value & 0xFFFF0000) >> 16); buf_hold = (value & 0xFFFF) << 16; } #endif @@ -181,7 +183,8 @@ void *memmove(void *v_dst, const void *v_src, __kernel_size_t c) for (; c >= 4; c -= 4) { value = *--i_src; - *--i_dst = buf_hold | ((value & 0xFF000000)>> 24); + *--i_dst = buf_hold | + ((value & 0xFF000000) >> 24); buf_hold = (value & 0xFFFFFF) << 8; } #endif diff --git a/arch/microblaze/mm/consistent.c b/arch/microblaze/mm/consistent.c index a1e2e18e0961..fe5fcdb5df5e 100644 --- a/arch/microblaze/mm/consistent.c +++ b/arch/microblaze/mm/consistent.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include #include @@ -102,8 +102,7 @@ void *consistent_alloc(gfp_t gfp, size_t size, dma_addr_t *dma_handle) # endif if ((unsigned int)ret > cpuinfo.dcache_base && (unsigned int)ret < cpuinfo.dcache_high) - printk(KERN_WARNING - "ERROR: Your cache coherent area is CACHED!!!\n"); + pr_warn("ERROR: Your cache coherent area is CACHED!!!\n"); /* dma_handle is same as physical (shadowed) address */ *dma_handle = (dma_addr_t)ret; diff --git a/arch/microblaze/mm/fault.c b/arch/microblaze/mm/fault.c index 714b35a9c4f7..731f739d17a1 100644 --- a/arch/microblaze/mm/fault.c +++ b/arch/microblaze/mm/fault.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include @@ -100,7 +100,7 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, /* On a kernel SLB miss we can only check for a valid exception entry */ if (unlikely(kernel_mode(regs) && (address >= TASK_SIZE))) { - printk(KERN_WARNING "kernel task_size exceed"); + pr_warn("kernel task_size exceed"); _exception(SIGSEGV, regs, code, address); } @@ -114,9 +114,9 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, /* in_atomic() in user mode is really bad, as is current->mm == NULL. */ - printk(KERN_EMERG "Page fault in user mode with " - "in_atomic(), mm = %p\n", mm); - printk(KERN_EMERG "r15 = %lx MSR = %lx\n", + pr_emerg("Page fault in user mode with in_atomic(), mm = %p\n", + mm); + pr_emerg("r15 = %lx MSR = %lx\n", regs->r15, regs->msr); die("Weird page fault", regs, SIGSEGV); } diff --git a/arch/microblaze/mm/init.c b/arch/microblaze/mm/init.c index ce80823051ba..8f8b367c079e 100644 --- a/arch/microblaze/mm/init.c +++ b/arch/microblaze/mm/init.c @@ -89,7 +89,7 @@ static unsigned long highmem_setup(void) reservedpages++; } totalram_pages += totalhigh_pages; - printk(KERN_INFO "High memory: %luk\n", + pr_info("High memory: %luk\n", totalhigh_pages << (PAGE_SHIFT-10)); return reservedpages; @@ -142,8 +142,8 @@ void __init setup_memory(void) ((u32)_text <= (memory_start + lowmem_size - 1))) { memory_size = lowmem_size; PAGE_OFFSET = memory_start; - printk(KERN_INFO "%s: Main mem: 0x%x, " - "size 0x%08x\n", __func__, (u32) memory_start, + pr_info("%s: Main mem: 0x%x, size 0x%08x\n", + __func__, (u32) memory_start, (u32) memory_size); break; } @@ -158,7 +158,7 @@ void __init setup_memory(void) kernel_align_start = PAGE_DOWN((u32)_text); /* ALIGN can be remove because _end in vmlinux.lds.S is align */ kernel_align_size = PAGE_UP((u32)klimit) - kernel_align_start; - printk(KERN_INFO "%s: kernel addr:0x%08x-0x%08x size=0x%08x\n", + pr_info("%s: kernel addr:0x%08x-0x%08x size=0x%08x\n", __func__, kernel_align_start, kernel_align_start + kernel_align_size, kernel_align_size); memblock_reserve(kernel_align_start, kernel_align_size); @@ -181,10 +181,10 @@ void __init setup_memory(void) max_low_pfn = ((u64)memory_start + (u64)lowmem_size) >> PAGE_SHIFT; max_pfn = ((u64)memory_start + (u64)memory_size) >> PAGE_SHIFT; - printk(KERN_INFO "%s: max_mapnr: %#lx\n", __func__, max_mapnr); - printk(KERN_INFO "%s: min_low_pfn: %#lx\n", __func__, min_low_pfn); - printk(KERN_INFO "%s: max_low_pfn: %#lx\n", __func__, max_low_pfn); - printk(KERN_INFO "%s: max_pfn: %#lx\n", __func__, max_pfn); + pr_info("%s: max_mapnr: %#lx\n", __func__, max_mapnr); + pr_info("%s: min_low_pfn: %#lx\n", __func__, min_low_pfn); + pr_info("%s: max_low_pfn: %#lx\n", __func__, max_low_pfn); + pr_info("%s: max_pfn: %#lx\n", __func__, max_pfn); /* * Find an area to use for the bootmem bitmap. @@ -246,7 +246,7 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end) free_page(addr); totalram_pages++; } - printk(KERN_INFO "Freeing %s: %ldk freed\n", what, (end - begin) >> 10); + pr_info("Freeing %s: %ldk freed\n", what, (end - begin) >> 10); } #ifdef CONFIG_BLK_DEV_INITRD @@ -260,7 +260,7 @@ void free_initrd_mem(unsigned long start, unsigned long end) totalram_pages++; pages++; } - printk(KERN_NOTICE "Freeing initrd memory: %dk freed\n", + pr_notice("Freeing initrd memory: %dk freed\n", (int)(pages * (PAGE_SIZE / 1024))); } #endif @@ -304,11 +304,11 @@ void __init mem_init(void) initsize = (unsigned long)&__init_end - (unsigned long)&__init_begin; bsssize = (unsigned long)&__bss_stop - (unsigned long)&__bss_start; - pr_info("Memory: %luk/%luk available (%luk kernel code, " - "%luk reserved, %luk data, %luk bss, %luk init)\n", + pr_info("Memory: %luk/%luk available (%luk kernel code, ", nr_free_pages() << (PAGE_SHIFT-10), num_physpages << (PAGE_SHIFT-10), - codesize >> 10, + codesize >> 10); + pr_cont("%luk reserved, %luk data, %luk bss, %luk init)\n", reservedpages << (PAGE_SHIFT-10), datasize >> 10, bsssize >> 10, @@ -394,17 +394,17 @@ asmlinkage void __init mmu_init(void) unsigned int kstart, ksize; if (!memblock.reserved.cnt) { - printk(KERN_EMERG "Error memory count\n"); + pr_emerg("Error memory count\n"); machine_restart(NULL); } if ((u32) memblock.memory.regions[0].size < 0x400000) { - printk(KERN_EMERG "Memory must be greater than 4MB\n"); + pr_emerg("Memory must be greater than 4MB\n"); machine_restart(NULL); } if ((u32) memblock.memory.regions[0].size < kernel_tlb) { - printk(KERN_EMERG "Kernel size is greater than memory node\n"); + pr_emerg("Kernel size is greater than memory node\n"); machine_restart(NULL); } diff --git a/arch/microblaze/mm/pgtable.c b/arch/microblaze/mm/pgtable.c index d1c06d07fed8..1888b0f50568 100644 --- a/arch/microblaze/mm/pgtable.c +++ b/arch/microblaze/mm/pgtable.c @@ -39,8 +39,6 @@ #include #include -#define flush_HPTE(X, va, pg) _tlbie(va) - unsigned long ioremap_base; unsigned long ioremap_bot; EXPORT_SYMBOL(ioremap_bot); @@ -75,9 +73,8 @@ static void __iomem *__ioremap(phys_addr_t addr, unsigned long size, p >= memory_start && p < virt_to_phys(high_memory) && !(p >= virt_to_phys((unsigned long)&__bss_stop) && p < virt_to_phys((unsigned long)__bss_stop))) { - printk(KERN_WARNING "__ioremap(): phys addr "PTE_FMT - " is RAM lr %pf\n", (unsigned long)p, - __builtin_return_address(0)); + pr_warn("__ioremap(): phys addr "PTE_FMT" is RAM lr %pf\n", + (unsigned long)p, __builtin_return_address(0)); return NULL; } @@ -128,9 +125,10 @@ void __iomem *ioremap(phys_addr_t addr, unsigned long size) } EXPORT_SYMBOL(ioremap); -void iounmap(void *addr) +void iounmap(void __iomem *addr) { - if (addr > high_memory && (unsigned long) addr < ioremap_bot) + if ((__force void *)addr > high_memory && + (unsigned long) addr < ioremap_bot) vfree((void *) (PAGE_MASK & (unsigned long) addr)); } EXPORT_SYMBOL(iounmap); @@ -152,8 +150,7 @@ int map_page(unsigned long va, phys_addr_t pa, int flags) set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT, __pgprot(flags))); if (unlikely(mem_init_done)) - flush_HPTE(0, va, pmd_val(*pd)); - /* flush_HPTE(0, va, pg); */ + _tlbie(va); } return err; } diff --git a/arch/microblaze/pci/indirect_pci.c b/arch/microblaze/pci/indirect_pci.c index 4196eb6bd764..ae4fca46c9f6 100644 --- a/arch/microblaze/pci/indirect_pci.c +++ b/arch/microblaze/pci/indirect_pci.c @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include diff --git a/arch/microblaze/pci/iomap.c b/arch/microblaze/pci/iomap.c index b07abbac0319..94149f5e6ebe 100644 --- a/arch/microblaze/pci/iomap.c +++ b/arch/microblaze/pci/iomap.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include void pci_iounmap(struct pci_dev *dev, void __iomem *addr) diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index 96416553cb36..bdb8ea100e73 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -33,7 +33,7 @@ #include #include -#include +#include #include #include @@ -552,11 +552,10 @@ int pci_mmap_legacy_page_range(struct pci_bus *bus, */ if ((offset + size) > hose->isa_mem_size) { #ifdef CONFIG_MMU - printk(KERN_DEBUG - "Process %s (pid:%d) mapped non-existing PCI" - "legacy memory for 0%04x:%02x\n", - current->comm, current->pid, pci_domain_nr(bus), - bus->number); + pr_debug("Process %s (pid:%d) mapped non-existing PCI", + current->comm, current->pid); + pr_debug("legacy memory for 0%04x:%02x\n", + pci_domain_nr(bus), bus->number); #endif if (vma->vm_flags & VM_SHARED) return shmem_zero_setup(vma); @@ -564,7 +563,7 @@ int pci_mmap_legacy_page_range(struct pci_bus *bus, } offset += hose->isa_mem_phys; } else { - unsigned long io_offset = (unsigned long)hose->io_base_virt - \ + unsigned long io_offset = (unsigned long)hose->io_base_virt - _IO_BASE; unsigned long roffset = offset + io_offset; rp = &hose->io_resource; @@ -668,7 +667,7 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose, unsigned long long isa_mb = 0; struct resource *res; - printk(KERN_INFO "PCI host bridge %s %s ranges:\n", + pr_info("PCI host bridge %s %s ranges:\n", dev->full_name, primary ? "(primary)" : ""); /* Get ranges property */ @@ -685,9 +684,10 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose, cpu_addr = of_translate_address(dev, ranges + 3); size = of_read_number(ranges + pna + 3, 2); - pr_debug("pci_space: 0x%08x pci_addr:0x%016llx " - "cpu_addr:0x%016llx size:0x%016llx\n", - pci_space, pci_addr, cpu_addr, size); + pr_debug("pci_space: 0x%08x pci_addr:0x%016llx ", + pci_space, pci_addr); + pr_debug("cpu_addr:0x%016llx size:0x%016llx\n", + cpu_addr, size); ranges += np; @@ -716,14 +716,12 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose, res = NULL; switch ((pci_space >> 24) & 0x3) { case 1: /* PCI IO space */ - printk(KERN_INFO - " IO 0x%016llx..0x%016llx -> 0x%016llx\n", + pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n", cpu_addr, cpu_addr + size - 1, pci_addr); /* We support only one IO range */ if (hose->pci_io_size) { - printk(KERN_INFO - " \\--> Skipped (too many) !\n"); + pr_info(" \\--> Skipped (too many) !\n"); continue; } /* On 32 bits, limit I/O space to 16MB */ @@ -750,15 +748,13 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose, break; case 2: /* PCI Memory space */ case 3: /* PCI 64 bits Memory space */ - printk(KERN_INFO - " MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", + pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx %s\n", cpu_addr, cpu_addr + size - 1, pci_addr, (pci_space & 0x40000000) ? "Prefetch" : ""); /* We support only 3 memory ranges */ if (memno >= 3) { - printk(KERN_INFO - " \\--> Skipped (too many) !\n"); + pr_info(" \\--> Skipped (too many) !\n"); continue; } /* Handles ISA memory hole space here */ @@ -781,8 +777,7 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose, hose->pci_mem_offset = cpu_addr - pci_addr; else if (pci_addr != 0 && hose->pci_mem_offset != cpu_addr - pci_addr) { - printk(KERN_INFO - " \\--> Skipped (offset mismatch) !\n"); + pr_info(" \\--> Skipped (offset mismatch) !\n"); continue; } @@ -809,7 +804,7 @@ void pci_process_bridge_OF_ranges(struct pci_controller *hose, */ if (isa_hole >= 0 && hose->pci_mem_offset != isa_mb) { unsigned int next = isa_hole + 1; - printk(KERN_INFO " Removing ISA hole at 0x%016llx\n", isa_mb); + pr_info(" Removing ISA hole at 0x%016llx\n", isa_mb); if (next < memno) memmove(&hose->mem_resources[isa_hole], &hose->mem_resources[next], @@ -833,7 +828,7 @@ static void pcibios_fixup_resources(struct pci_dev *dev) int i; if (!hose) { - printk(KERN_ERR "No host bridge for PCI dev %s !\n", + pr_err("No host bridge for PCI dev %s !\n", pci_name(dev)); return; } @@ -842,12 +837,12 @@ static void pcibios_fixup_resources(struct pci_dev *dev) if (!res->flags) continue; if (res->start == 0) { - pr_debug("PCI:%s Resource %d %016llx-%016llx [%x]" \ - "is unassigned\n", + pr_debug("PCI:%s Resource %d %016llx-%016llx [%x]", pci_name(dev), i, (unsigned long long)res->start, (unsigned long long)res->end, (unsigned int)res->flags); + pr_debug("is unassigned\n"); res->end -= res->start; res->start = 0; res->flags |= IORESOURCE_UNSET; @@ -856,7 +851,7 @@ static void pcibios_fixup_resources(struct pci_dev *dev) pr_debug("PCI:%s Resource %d %016llx-%016llx [%x]\n", pci_name(dev), i, - (unsigned long long)res->start,\ + (unsigned long long)res->start, (unsigned long long)res->end, (unsigned int)res->flags); } @@ -947,7 +942,7 @@ static void pcibios_fixup_bridge(struct pci_bus *bus) pr_debug("PCI:%s Bus rsrc %d %016llx-%016llx [%x] fixup...\n", pci_name(dev), i, - (unsigned long long)res->start,\ + (unsigned long long)res->start, (unsigned long long)res->end, (unsigned int)res->flags); @@ -1154,12 +1149,12 @@ static void pcibios_allocate_bus_resources(struct pci_bus *bus) } } - pr_debug("PCI: %s (bus %d) bridge rsrc %d: %016llx-%016llx " - "[0x%x], parent %p (%s)\n", + pr_debug("PCI: %s (bus %d) bridge rsrc %d: %016llx-%016llx ", bus->self ? pci_name(bus->self) : "PHB", bus->number, i, (unsigned long long)res->start, - (unsigned long long)res->end, + (unsigned long long)res->end); + pr_debug("[0x%x], parent %p (%s)\n", (unsigned int)res->flags, pr, (pr && pr->name) ? pr->name : "nil"); @@ -1174,9 +1169,8 @@ static void pcibios_allocate_bus_resources(struct pci_bus *bus) if (reparent_resources(pr, res) == 0) continue; } - printk(KERN_WARNING "PCI: Cannot allocate resource region " - "%d of PCI bridge %d, will remap\n", i, bus->number); - + pr_warn("PCI: Cannot allocate resource region "); + pr_cont("%d of PCI bridge %d, will remap\n", i, bus->number); res->start = res->end = 0; res->flags = 0; } @@ -1198,8 +1192,8 @@ static inline void alloc_resource(struct pci_dev *dev, int idx) pr = pci_find_parent_resource(dev, r); if (!pr || (pr->flags & IORESOURCE_UNSET) || request_resource(pr, r) < 0) { - printk(KERN_WARNING "PCI: Cannot allocate resource region %d" - " of device %s, will remap\n", idx, pci_name(dev)); + pr_warn("PCI: Cannot allocate resource region %d ", idx); + pr_cont("of device %s, will remap\n", pci_name(dev)); if (pr) pr_debug("PCI: parent is %p: %016llx-%016llx [%x]\n", pr, @@ -1282,8 +1276,7 @@ static void __init pcibios_reserve_legacy_regions(struct pci_bus *bus) res->end = (offset + 0xfff) & 0xfffffffful; pr_debug("Candidate legacy IO: %pR\n", res); if (request_resource(&hose->io_resource, res)) { - printk(KERN_DEBUG - "PCI %04x:%02x Cannot reserve Legacy IO %pR\n", + pr_debug("PCI %04x:%02x Cannot reserve Legacy IO %pR\n", pci_domain_nr(bus), bus->number, res); kfree(res); } @@ -1311,8 +1304,7 @@ static void __init pcibios_reserve_legacy_regions(struct pci_bus *bus) res->end = 0xbffff + offset; pr_debug("Candidate VGA memory: %pR\n", res); if (request_resource(pres, res)) { - printk(KERN_DEBUG - "PCI %04x:%02x Cannot reserve VGA memory %pR\n", + pr_debug("PCI %04x:%02x Cannot reserve VGA memory %pR\n", pci_domain_nr(bus), bus->number, res); kfree(res); } @@ -1362,10 +1354,9 @@ void pcibios_claim_one_bus(struct pci_bus *bus) if (r->parent || !r->start || !r->flags) continue; - pr_debug("PCI: Claiming %s: " - "Resource %d: %016llx..%016llx [%x]\n", - pci_name(dev), i, - (unsigned long long)r->start, + pr_debug("PCI: Claiming %s: ", pci_name(dev)); + pr_debug("Resource %d: %016llx..%016llx [%x]\n", + i, (unsigned long long)r->start, (unsigned long long)r->end, (unsigned int)r->flags); @@ -1423,9 +1414,9 @@ static void pcibios_setup_phb_resources(struct pci_controller *hose, res->end = (res->end + io_offset) & 0xffffffffu; if (!res->flags) { - printk(KERN_WARNING "PCI: I/O resource not set for host" - " bridge %s (domain %d)\n", - hose->dn->full_name, hose->global_number); + pr_warn("PCI: I/O resource not set for host "); + pr_cont("bridge %s (domain %d)\n", + hose->dn->full_name, hose->global_number); /* Workaround for lack of IO resource only on 32-bit */ res->start = (unsigned long)hose->io_base_virt - isa_io_base; res->end = res->start + IO_SPACE_LIMIT; @@ -1445,9 +1436,9 @@ static void pcibios_setup_phb_resources(struct pci_controller *hose, if (!res->flags) { if (i > 0) continue; - printk(KERN_ERR "PCI: Memory resource 0 not set for " - "host bridge %s (domain %d)\n", - hose->dn->full_name, hose->global_number); + pr_err("PCI: Memory resource 0 not set for "); + pr_cont("host bridge %s (domain %d)\n", + hose->dn->full_name, hose->global_number); /* Workaround for lack of MEM resource only on 32-bit */ res->start = hose->pci_mem_offset; @@ -1489,7 +1480,7 @@ static void pcibios_scan_phb(struct pci_controller *hose) bus = pci_scan_root_bus(hose->parent, hose->first_busno, hose->ops, hose, &resources); if (bus == NULL) { - printk(KERN_ERR "Failed to create bus for PCI domain %04x\n", + pr_err("Failed to create bus for PCI domain %04x\n", hose->global_number); pci_free_resource_list(&resources); return; @@ -1505,7 +1496,7 @@ static int __init pcibios_init(void) struct pci_controller *hose, *tmp; int next_busno = 0; - printk(KERN_INFO "PCI: Probing PCI hardware\n"); + pr_info("PCI: Probing PCI hardware\n"); /* Scan all of the recorded PCI controllers. */ list_for_each_entry_safe(hose, tmp, &hose_list, list_node) { @@ -1605,7 +1596,7 @@ fake_pci_bus(struct pci_controller *hose, int busnr) static struct pci_bus bus; if (!hose) - printk(KERN_ERR "Can't find hose for PCI bus %d!\n", busnr); + pr_err("Can't find hose for PCI bus %d!\n", busnr); bus.number = busnr; bus.sysdata = hose; diff --git a/arch/microblaze/pci/xilinx_pci.c b/arch/microblaze/pci/xilinx_pci.c index 0687a42a5bd4..14c7da5fd039 100644 --- a/arch/microblaze/pci/xilinx_pci.c +++ b/arch/microblaze/pci/xilinx_pci.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #define XPLB_PCI_ADDR 0x10c #define XPLB_PCI_DATA 0x110 @@ -82,7 +82,7 @@ xilinx_pci_exclude_device(struct pci_controller *hose, u_char bus, u8 devfn) * * List pci devices in very early phase. */ -void __init xilinx_early_pci_scan(struct pci_controller *hose) +static void __init xilinx_early_pci_scan(struct pci_controller *hose) { u32 bus = 0; u32 val, dev, func, offset; @@ -91,27 +91,27 @@ void __init xilinx_early_pci_scan(struct pci_controller *hose) for (dev = 0; dev < 2; dev++) { /* List only first function number - up-to 8 functions */ for (func = 0; func < 1; func++) { - printk(KERN_INFO "%02x:%02x:%02x", bus, dev, func); + pr_info("%02x:%02x:%02x", bus, dev, func); /* read the first 64 standardized bytes */ /* Up-to 192 bytes can be list of capabilities */ for (offset = 0; offset < 64; offset += 4) { early_read_config_dword(hose, bus, PCI_DEVFN(dev, func), offset, &val); if (offset == 0 && val == 0xFFFFFFFF) { - printk(KERN_CONT "\nABSENT"); + pr_cont("\nABSENT"); break; } if (!(offset % 0x10)) - printk(KERN_CONT "\n%04x: ", offset); + pr_cont("\n%04x: ", offset); - printk(KERN_CONT "%08x ", val); + pr_cont("%08x ", val); } - printk(KERN_INFO "\n"); + pr_info("\n"); } } } #else -void __init xilinx_early_pci_scan(struct pci_controller *hose) +static void __init xilinx_early_pci_scan(struct pci_controller *hose) { } #endif From d64af918feb6cb81c396d6d2dabb738bc51dda3f Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 1 Feb 2013 13:10:35 +0100 Subject: [PATCH 2367/4607] microblaze: Do not use module.h in files which are not modules Based on the patch: "lib: reduce the use of module.h wherever possible" (sha1: 8bc3bcc93a2b4e47d5d410146f6546bca6171663) fix all microblaze files which are not modules. Signed-off-by: Michal Simek --- arch/microblaze/kernel/exceptions.c | 2 +- arch/microblaze/kernel/microblaze_ksyms.c | 2 +- arch/microblaze/kernel/module.c | 2 +- arch/microblaze/kernel/process.c | 2 +- arch/microblaze/kernel/prom.c | 2 +- arch/microblaze/kernel/prom_parse.c | 2 +- arch/microblaze/kernel/stacktrace.c | 2 +- arch/microblaze/kernel/sys_microblaze.c | 2 +- arch/microblaze/kernel/traps.c | 2 +- arch/microblaze/kernel/unwind.c | 2 +- arch/microblaze/lib/ashldi3.c | 2 +- arch/microblaze/lib/ashrdi3.c | 2 +- arch/microblaze/lib/cmpdi2.c | 2 +- arch/microblaze/lib/lshrdi3.c | 2 +- arch/microblaze/lib/memcpy.c | 2 +- arch/microblaze/lib/memmove.c | 2 +- arch/microblaze/lib/memset.c | 2 +- arch/microblaze/lib/muldi3.c | 2 +- arch/microblaze/lib/ucmpdi2.c | 2 +- arch/microblaze/mm/consistent.c | 2 +- arch/microblaze/mm/highmem.c | 2 +- arch/microblaze/mm/pgtable.c | 2 +- arch/microblaze/pci/pci-common.c | 1 + 23 files changed, 23 insertions(+), 22 deletions(-) diff --git a/arch/microblaze/kernel/exceptions.c b/arch/microblaze/kernel/exceptions.c index d6edf5d39cfa..42dd12a62ff5 100644 --- a/arch/microblaze/kernel/exceptions.c +++ b/arch/microblaze/kernel/exceptions.c @@ -13,11 +13,11 @@ * This file handles the architecture-dependent parts of hardware exceptions */ +#include #include #include #include #include -#include #include #include /* For KM CPU var */ diff --git a/arch/microblaze/kernel/microblaze_ksyms.c b/arch/microblaze/kernel/microblaze_ksyms.c index 2b25bcf05c00..9f1d02c4c5cc 100644 --- a/arch/microblaze/kernel/microblaze_ksyms.c +++ b/arch/microblaze/kernel/microblaze_ksyms.c @@ -7,7 +7,7 @@ * published by the Free Software Foundation. */ -#include +#include #include #include #include diff --git a/arch/microblaze/kernel/module.c b/arch/microblaze/kernel/module.c index f0f43688a0c6..182e6be856cd 100644 --- a/arch/microblaze/kernel/module.c +++ b/arch/microblaze/kernel/module.c @@ -7,7 +7,7 @@ * published by the Free Software Foundation. */ -#include +#include #include #include #include diff --git a/arch/microblaze/kernel/process.c b/arch/microblaze/kernel/process.c index 282dfb8f1fe7..246d4c1a9d53 100644 --- a/arch/microblaze/kernel/process.c +++ b/arch/microblaze/kernel/process.c @@ -8,7 +8,7 @@ * for more details. */ -#include +#include #include #include #include diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c index a744e3f18883..0a2c68f9f9b0 100644 --- a/arch/microblaze/kernel/prom.c +++ b/arch/microblaze/kernel/prom.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -25,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/microblaze/kernel/prom_parse.c b/arch/microblaze/kernel/prom_parse.c index 47187cc2cf00..068762f55fd6 100644 --- a/arch/microblaze/kernel/prom_parse.c +++ b/arch/microblaze/kernel/prom_parse.c @@ -1,8 +1,8 @@ #undef DEBUG +#include #include #include -#include #include #include #include diff --git a/arch/microblaze/kernel/stacktrace.c b/arch/microblaze/kernel/stacktrace.c index 84bc6686102c..b4debe283a79 100644 --- a/arch/microblaze/kernel/stacktrace.c +++ b/arch/microblaze/kernel/stacktrace.c @@ -9,11 +9,11 @@ * for more details. */ +#include #include #include #include #include -#include #include void save_stack_trace(struct stack_trace *trace) diff --git a/arch/microblaze/kernel/sys_microblaze.c b/arch/microblaze/kernel/sys_microblaze.c index 3845677e93ea..f905b3ae68c7 100644 --- a/arch/microblaze/kernel/sys_microblaze.c +++ b/arch/microblaze/kernel/sys_microblaze.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -24,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/microblaze/kernel/traps.c b/arch/microblaze/kernel/traps.c index a37b2b111d86..30e6b5004a6a 100644 --- a/arch/microblaze/kernel/traps.c +++ b/arch/microblaze/kernel/traps.c @@ -8,9 +8,9 @@ * for more details. */ +#include #include #include -#include #include #include diff --git a/arch/microblaze/kernel/unwind.c b/arch/microblaze/kernel/unwind.c index 6be4ae3c3351..1f7b8d449668 100644 --- a/arch/microblaze/kernel/unwind.c +++ b/arch/microblaze/kernel/unwind.c @@ -13,13 +13,13 @@ */ /* #define DEBUG 1 */ +#include #include #include #include #include #include #include -#include #include #include #include diff --git a/arch/microblaze/lib/ashldi3.c b/arch/microblaze/lib/ashldi3.c index dd80fbcdc57c..1af904cd972d 100644 --- a/arch/microblaze/lib/ashldi3.c +++ b/arch/microblaze/lib/ashldi3.c @@ -1,4 +1,4 @@ -#include +#include #include "libgcc.h" diff --git a/arch/microblaze/lib/ashrdi3.c b/arch/microblaze/lib/ashrdi3.c index 1f21fc6a576b..32c334c05d04 100644 --- a/arch/microblaze/lib/ashrdi3.c +++ b/arch/microblaze/lib/ashrdi3.c @@ -1,4 +1,4 @@ -#include +#include #include "libgcc.h" diff --git a/arch/microblaze/lib/cmpdi2.c b/arch/microblaze/lib/cmpdi2.c index a708400ea7b7..67abc9ac1bd4 100644 --- a/arch/microblaze/lib/cmpdi2.c +++ b/arch/microblaze/lib/cmpdi2.c @@ -1,4 +1,4 @@ -#include +#include #include "libgcc.h" diff --git a/arch/microblaze/lib/lshrdi3.c b/arch/microblaze/lib/lshrdi3.c index c10090bedc33..adcb253f11c8 100644 --- a/arch/microblaze/lib/lshrdi3.c +++ b/arch/microblaze/lib/lshrdi3.c @@ -1,4 +1,4 @@ -#include +#include #include "libgcc.h" diff --git a/arch/microblaze/lib/memcpy.c b/arch/microblaze/lib/memcpy.c index 15a6759ff9fa..f536e81b8168 100644 --- a/arch/microblaze/lib/memcpy.c +++ b/arch/microblaze/lib/memcpy.c @@ -24,10 +24,10 @@ * not any responsibility to update it. */ +#include #include #include #include -#include #include diff --git a/arch/microblaze/lib/memmove.c b/arch/microblaze/lib/memmove.c index 9843c6b0f0f8..3611ce70415b 100644 --- a/arch/microblaze/lib/memmove.c +++ b/arch/microblaze/lib/memmove.c @@ -24,10 +24,10 @@ * not any responsibility to update it. */ +#include #include #include #include -#include #include #ifdef __HAVE_ARCH_MEMMOVE diff --git a/arch/microblaze/lib/memset.c b/arch/microblaze/lib/memset.c index ddf67939576d..04ea72c8a81d 100644 --- a/arch/microblaze/lib/memset.c +++ b/arch/microblaze/lib/memset.c @@ -24,10 +24,10 @@ * not any responsibility to update it. */ +#include #include #include #include -#include #include #ifdef __HAVE_ARCH_MEMSET diff --git a/arch/microblaze/lib/muldi3.c b/arch/microblaze/lib/muldi3.c index d3659244ab6f..a3f9a03acdcd 100644 --- a/arch/microblaze/lib/muldi3.c +++ b/arch/microblaze/lib/muldi3.c @@ -1,4 +1,4 @@ -#include +#include #include "libgcc.h" diff --git a/arch/microblaze/lib/ucmpdi2.c b/arch/microblaze/lib/ucmpdi2.c index 63ca105b6713..d05f1585121c 100644 --- a/arch/microblaze/lib/ucmpdi2.c +++ b/arch/microblaze/lib/ucmpdi2.c @@ -1,4 +1,4 @@ -#include +#include #include "libgcc.h" diff --git a/arch/microblaze/mm/consistent.c b/arch/microblaze/mm/consistent.c index fe5fcdb5df5e..5226b09cbbb2 100644 --- a/arch/microblaze/mm/consistent.c +++ b/arch/microblaze/mm/consistent.c @@ -13,7 +13,7 @@ * published by the Free Software Foundation. */ -#include +#include #include #include #include diff --git a/arch/microblaze/mm/highmem.c b/arch/microblaze/mm/highmem.c index 7d78838e8bfa..5a92576fad92 100644 --- a/arch/microblaze/mm/highmem.c +++ b/arch/microblaze/mm/highmem.c @@ -20,8 +20,8 @@ * highmem.h by Benjamin Herrenschmidt (c) 2009 IBM Corp. */ +#include #include -#include /* * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap diff --git a/arch/microblaze/mm/pgtable.c b/arch/microblaze/mm/pgtable.c index 1888b0f50568..10b3bd0a980d 100644 --- a/arch/microblaze/mm/pgtable.c +++ b/arch/microblaze/mm/pgtable.c @@ -26,8 +26,8 @@ * */ +#include #include -#include #include #include #include diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index bdb8ea100e73..9ea521e4959e 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include From 711e5b4520986380700e6f095608021cf087170e Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 7 Feb 2013 14:58:35 +0100 Subject: [PATCH 2368/4607] asm-generic: io: Fix ioread16/32be and iowrite16/32be Fix ioreadXXbe and iowriteXXbe functions which did additional little endian conversion on native big endian systems. Using be_to_cpu (cpu_to_be) conversions with __raw_read/write functions have resolved it. Signed-off-by: Michal Simek Acked-by: Arnd Bergmann Acked-by: Geert Uytterhoeven CC: Benjamin Herrenschmidt CC: Will Deacon CC: linux-arch@vger.kernel.org --- include/asm-generic/io.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index 33bbbae4ddc6..8823581cf1cd 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h @@ -225,15 +225,15 @@ static inline void outsl(unsigned long addr, const void *buffer, int count) #ifndef CONFIG_GENERIC_IOMAP #define ioread8(addr) readb(addr) #define ioread16(addr) readw(addr) -#define ioread16be(addr) be16_to_cpu(ioread16(addr)) +#define ioread16be(addr) __be16_to_cpu(__raw_readw(addr)) #define ioread32(addr) readl(addr) -#define ioread32be(addr) be32_to_cpu(ioread32(addr)) +#define ioread32be(addr) __be32_to_cpu(__raw_readl(addr)) #define iowrite8(v, addr) writeb((v), (addr)) #define iowrite16(v, addr) writew((v), (addr)) -#define iowrite16be(v, addr) iowrite16(be16_to_cpu(v), (addr)) +#define iowrite16be(v, addr) __raw_writew(__cpu_to_be16(v), addr) #define iowrite32(v, addr) writel((v), (addr)) -#define iowrite32be(v, addr) iowrite32(be32_to_cpu(v), (addr)) +#define iowrite32be(v, addr) __raw_writel(__cpu_to_be32(v), addr) #define ioread8_rep(p, dst, count) \ insb((unsigned long) (p), (dst), (count)) From f3b54b9a066edeac5c06e1cdcd82e1cb1224aaef Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 11 Feb 2013 19:47:56 -0700 Subject: [PATCH 2369/4607] i2c: add bcm2835 driver This implements a very basic I2C host driver for the BCM2835 SoC. Missing features so far are: * 10-bit addressing. * DMA. Reviewed-by: Grant Likely Signed-off-by: Stephen Warren Signed-off-by: Wolfram Sang --- .../bindings/i2c/brcm,bcm2835-i2c.txt | 20 + drivers/i2c/busses/Kconfig | 12 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm2835.c | 342 ++++++++++++++++++ 4 files changed, 375 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm2835.c diff --git a/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt new file mode 100644 index 000000000000..e9de3756752b --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt @@ -0,0 +1,20 @@ +Broadcom BCM2835 I2C controller + +Required properties: +- compatible : Should be "brcm,bcm2835-i2c". +- reg: Should contain register location and length. +- interrupts: Should contain interrupt. +- clocks : The clock feeding the I2C controller. + +Recommended properties: +- clock-frequency : desired I2C bus clock frequency in Hz. + +Example: + +i2c@20205000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e205000 0x1000>; + interrupts = <2 21>; + clocks = <&clk_i2c>; + clock-frequency = <100000>; +}; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 74e18bc0bf62..8cbd9cd1e0fa 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -330,6 +330,18 @@ config I2C_AU1550 This driver can also be built as a module. If so, the module will be called i2c-au1550. +config I2C_BCM2835 + tristate "Broadcom BCM2835 I2C controller" + depends on ARCH_BCM2835 + help + If you say yes to this option, support will be included for the + BCM2835 I2C controller. + + If you don't know what to do here, say N. + + This support is also available as a module. If so, the module + will be called i2c-bcm2835. + config I2C_BLACKFIN_TWI tristate "Blackfin TWI I2C support" depends on BLACKFIN diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 23c9d55bc880..8f4fc23b85b1 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o # Embedded system I2C/SMBus host controller drivers obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o +obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c new file mode 100644 index 000000000000..ea4b08fc3353 --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -0,0 +1,342 @@ +/* + * BCM2835 master mode driver + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BCM2835_I2C_C 0x0 +#define BCM2835_I2C_S 0x4 +#define BCM2835_I2C_DLEN 0x8 +#define BCM2835_I2C_A 0xc +#define BCM2835_I2C_FIFO 0x10 +#define BCM2835_I2C_DIV 0x14 +#define BCM2835_I2C_DEL 0x18 +#define BCM2835_I2C_CLKT 0x1c + +#define BCM2835_I2C_C_READ BIT(0) +#define BCM2835_I2C_C_CLEAR BIT(4) /* bits 4 and 5 both clear */ +#define BCM2835_I2C_C_ST BIT(7) +#define BCM2835_I2C_C_INTD BIT(8) +#define BCM2835_I2C_C_INTT BIT(9) +#define BCM2835_I2C_C_INTR BIT(10) +#define BCM2835_I2C_C_I2CEN BIT(15) + +#define BCM2835_I2C_S_TA BIT(0) +#define BCM2835_I2C_S_DONE BIT(1) +#define BCM2835_I2C_S_TXW BIT(2) +#define BCM2835_I2C_S_RXR BIT(3) +#define BCM2835_I2C_S_TXD BIT(4) +#define BCM2835_I2C_S_RXD BIT(5) +#define BCM2835_I2C_S_TXE BIT(6) +#define BCM2835_I2C_S_RXF BIT(7) +#define BCM2835_I2C_S_ERR BIT(8) +#define BCM2835_I2C_S_CLKT BIT(9) +#define BCM2835_I2C_S_LEN BIT(10) /* Fake bit for SW error reporting */ + +#define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000)) + +struct bcm2835_i2c_dev { + struct device *dev; + void __iomem *regs; + struct clk *clk; + int irq; + struct i2c_adapter adapter; + struct completion completion; + u32 msg_err; + u8 *msg_buf; + size_t msg_buf_remaining; +}; + +static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev, + u32 reg, u32 val) +{ + writel(val, i2c_dev->regs + reg); +} + +static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg) +{ + return readl(i2c_dev->regs + reg); +} + +static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev) +{ + u32 val; + + while (i2c_dev->msg_buf_remaining) { + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + if (!(val & BCM2835_I2C_S_TXD)) + break; + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_FIFO, + *i2c_dev->msg_buf); + i2c_dev->msg_buf++; + i2c_dev->msg_buf_remaining--; + } +} + +static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev) +{ + u32 val; + + while (i2c_dev->msg_buf_remaining) { + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + if (!(val & BCM2835_I2C_S_RXD)) + break; + *i2c_dev->msg_buf = bcm2835_i2c_readl(i2c_dev, + BCM2835_I2C_FIFO); + i2c_dev->msg_buf++; + i2c_dev->msg_buf_remaining--; + } +} + +static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data) +{ + struct bcm2835_i2c_dev *i2c_dev = data; + u32 val, err; + + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val); + + err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR); + if (err) { + i2c_dev->msg_err = err; + complete(&i2c_dev->completion); + return IRQ_HANDLED; + } + + if (val & BCM2835_I2C_S_RXD) { + bcm2835_drain_rxfifo(i2c_dev); + if (!(val & BCM2835_I2C_S_DONE)) + return IRQ_HANDLED; + } + + if (val & BCM2835_I2C_S_DONE) { + if (i2c_dev->msg_buf_remaining) + i2c_dev->msg_err = BCM2835_I2C_S_LEN; + else + i2c_dev->msg_err = 0; + complete(&i2c_dev->completion); + return IRQ_HANDLED; + } + + if (val & BCM2835_I2C_S_TXD) { + bcm2835_fill_txfifo(i2c_dev); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev, + struct i2c_msg *msg) +{ + u32 c; + int time_left; + + i2c_dev->msg_buf = msg->buf; + i2c_dev->msg_buf_remaining = msg->len; + INIT_COMPLETION(i2c_dev->completion); + + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR); + + if (msg->flags & I2C_M_RD) { + c = BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR; + } else { + c = BCM2835_I2C_C_INTT; + bcm2835_fill_txfifo(i2c_dev); + } + c |= BCM2835_I2C_C_ST | BCM2835_I2C_C_INTD | BCM2835_I2C_C_I2CEN; + + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c); + + time_left = wait_for_completion_timeout(&i2c_dev->completion, + BCM2835_I2C_TIMEOUT); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR); + if (!time_left) { + dev_err(i2c_dev->dev, "i2c transfer timed out\n"); + return -ETIMEDOUT; + } + + if (likely(!i2c_dev->msg_err)) + return 0; + + if ((i2c_dev->msg_err & BCM2835_I2C_S_ERR) && + (msg->flags & I2C_M_IGNORE_NAK)) + return 0; + + dev_err(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err); + + if (i2c_dev->msg_err & BCM2835_I2C_S_ERR) + return -EREMOTEIO; + else + return -EIO; +} + +static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + int i; + int ret = 0; + + for (i = 0; i < num; i++) { + ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i]); + if (ret) + break; + } + + return ret ?: i; +} + +static u32 bcm2835_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm2835_i2c_algo = { + .master_xfer = bcm2835_i2c_xfer, + .functionality = bcm2835_i2c_func, +}; + +static int bcm2835_i2c_probe(struct platform_device *pdev) +{ + struct bcm2835_i2c_dev *i2c_dev; + struct resource *mem, *requested, *irq; + u32 bus_clk_rate, divider; + int ret; + struct i2c_adapter *adap; + + i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) { + dev_err(&pdev->dev, "Cannot allocate i2c_dev\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, i2c_dev); + i2c_dev->dev = &pdev->dev; + init_completion(&i2c_dev->completion); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No mem resource\n"); + return -ENODEV; + } + + requested = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), + dev_name(&pdev->dev)); + if (!requested) { + dev_err(&pdev->dev, "Could not claim register region\n"); + return -EBUSY; + } + + i2c_dev->regs = devm_ioremap(&pdev->dev, mem->start, + resource_size(mem)); + if (!i2c_dev->regs) { + dev_err(&pdev->dev, "Could not map registers\n"); + return -ENOMEM; + } + + i2c_dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c_dev->clk)) { + dev_err(&pdev->dev, "Could not get clock\n"); + return PTR_ERR(i2c_dev->clk); + } + + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &bus_clk_rate); + if (ret < 0) { + dev_warn(&pdev->dev, + "Could not read clock-frequency property\n"); + bus_clk_rate = 100000; + } + + divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), bus_clk_rate); + /* + * Per the datasheet, the register is always interpreted as an even + * number, by rounding down. In other words, the LSB is ignored. So, + * if the LSB is set, increment the divider to avoid any issue. + */ + if (divider & 1) + divider++; + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider); + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "No IRQ resource\n"); + return -ENODEV; + } + i2c_dev->irq = irq->start; + + ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED, + dev_name(&pdev->dev), i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return -ENODEV; + } + + adap = &i2c_dev->adapter; + i2c_set_adapdata(adap, i2c_dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name)); + adap->algo = &bcm2835_i2c_algo; + adap->dev.parent = &pdev->dev; + + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0); + + ret = i2c_add_adapter(adap); + if (ret) + free_irq(i2c_dev->irq, i2c_dev); + + return ret; +} + +static int bcm2835_i2c_remove(struct platform_device *pdev) +{ + struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + free_irq(i2c_dev->irq, i2c_dev); + i2c_del_adapter(&i2c_dev->adapter); + + return 0; +} + +static const struct of_device_id bcm2835_i2c_of_match[] = { + { .compatible = "brcm,bcm2835-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2835_i2c_of_match); + +static struct platform_driver bcm2835_i2c_driver = { + .probe = bcm2835_i2c_probe, + .remove = bcm2835_i2c_remove, + .driver = { + .name = "i2c-bcm2835", + .owner = THIS_MODULE, + .of_match_table = bcm2835_i2c_of_match, + }, +}; +module_platform_driver(bcm2835_i2c_driver); + +MODULE_AUTHOR("Stephen Warren "); +MODULE_DESCRIPTION("BCM2835 I2C bus adapter"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:i2c-bcm2835"); From eea553c21fbfa486978c82525ee8256239d4f921 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 02:09:50 -0800 Subject: [PATCH 2370/4607] ceph: Only allow mounts in the initial network namespace Today ceph opens tcp sockets from a delayed work callback. Delayed work happens from kernel threads which are always in the initial network namespace. Therefore fail early if someone attempts to mount a ceph filesystem from something other than the initial network namespace. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- net/ceph/ceph_common.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index ee71ea26777a..1deb29af82fd 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include @@ -292,6 +294,9 @@ ceph_parse_options(char *options, const char *dev_name, int err = -ENOMEM; substring_t argstr[MAX_OPT_ARGS]; + if (current->nsproxy->net_ns != &init_net) + return ERR_PTR(-EINVAL); + opt = kzalloc(sizeof(*opt), GFP_KERNEL); if (!opt) return ERR_PTR(-ENOMEM); From 05cb11c17e892f0e131b6c2ba25d63221aafbd11 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 02:56:19 -0800 Subject: [PATCH 2371/4607] ceph: Translate between uid and gids in cap messages and kuids and kgids - Make the uid and gid arguments of send_cap_msg() used to compose ceph_mds_caps messages of type kuid_t and kgid_t. - Pass inode->i_uid and inode->i_gid in __send_cap to send_cap_msg() through variables of type kuid_t and kgid_t. - Modify struct ceph_cap_snap to store uids and gids in types kuid_t and kgid_t. This allows capturing inode->i_uid and inode->i_gid in ceph_queue_cap_snap() without loss and pssing them to __ceph_flush_snaps() where they are removed from struct ceph_cap_snap and passed to send_cap_msg(). - In handle_cap_grant translate uid and gids in the initial user namespace stored in struct ceph_mds_cap into kuids and kgids before setting inode->i_uid and inode->i_gid. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- fs/ceph/caps.c | 14 +++++++------- fs/ceph/super.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index a1d9bb30c1bf..39eb4665b122 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -930,7 +930,7 @@ static int send_cap_msg(struct ceph_mds_session *session, u64 size, u64 max_size, struct timespec *mtime, struct timespec *atime, u64 time_warp_seq, - uid_t uid, gid_t gid, umode_t mode, + kuid_t uid, kgid_t gid, umode_t mode, u64 xattr_version, struct ceph_buffer *xattrs_buf, u64 follows) @@ -974,8 +974,8 @@ static int send_cap_msg(struct ceph_mds_session *session, ceph_encode_timespec(&fc->atime, atime); fc->time_warp_seq = cpu_to_le32(time_warp_seq); - fc->uid = cpu_to_le32(uid); - fc->gid = cpu_to_le32(gid); + fc->uid = cpu_to_le32(from_kuid(&init_user_ns, uid)); + fc->gid = cpu_to_le32(from_kgid(&init_user_ns, gid)); fc->mode = cpu_to_le32(mode); fc->xattr_version = cpu_to_le64(xattr_version); @@ -1081,8 +1081,8 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, struct timespec mtime, atime; int wake = 0; umode_t mode; - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; struct ceph_mds_session *session; u64 xattr_version = 0; struct ceph_buffer *xattr_blob = NULL; @@ -2359,8 +2359,8 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, if ((issued & CEPH_CAP_AUTH_EXCL) == 0) { inode->i_mode = le32_to_cpu(grant->mode); - inode->i_uid = le32_to_cpu(grant->uid); - inode->i_gid = le32_to_cpu(grant->gid); + inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(grant->uid)); + inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(grant->gid)); dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode, inode->i_uid, inode->i_gid); } diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 66ebe720e40d..f053bbd1886f 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -138,8 +138,8 @@ struct ceph_cap_snap { struct ceph_snap_context *context; umode_t mode; - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; struct ceph_buffer *xattr_blob; u64 xattr_version; From ab871b903e9095772c219b512d9eae96c4663a5d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 03:40:12 -0800 Subject: [PATCH 2372/4607] ceph: Translate inode uid and gid attributes to/from kuids and kgids. - In fill_inode() transate uids and gids in the initial user namespace into kuids and kgids stored in inode->i_uid and inode->i_gid. - In ceph_setattr() if they have changed convert inode->i_uid and inode->i_gid into initial user namespace uids and gids for transmission. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- fs/ceph/inode.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 2971eaa65cdc..40e0787022e9 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -612,8 +612,8 @@ static int fill_inode(struct inode *inode, if ((issued & CEPH_CAP_AUTH_EXCL) == 0) { inode->i_mode = le32_to_cpu(info->mode); - inode->i_uid = le32_to_cpu(info->uid); - inode->i_gid = le32_to_cpu(info->gid); + inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(info->uid)); + inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(info->gid)); dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode, inode->i_uid, inode->i_gid); } @@ -1570,8 +1570,9 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) inode->i_uid = attr->ia_uid; dirtied |= CEPH_CAP_AUTH_EXCL; } else if ((issued & CEPH_CAP_AUTH_SHARED) == 0 || - attr->ia_uid != inode->i_uid) { - req->r_args.setattr.uid = cpu_to_le32(attr->ia_uid); + !uid_eq(attr->ia_uid, inode->i_uid)) { + req->r_args.setattr.uid = cpu_to_le32( + from_kuid(&init_user_ns, attr->ia_uid)); mask |= CEPH_SETATTR_UID; release |= CEPH_CAP_AUTH_SHARED; } @@ -1583,8 +1584,9 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) inode->i_gid = attr->ia_gid; dirtied |= CEPH_CAP_AUTH_EXCL; } else if ((issued & CEPH_CAP_AUTH_SHARED) == 0 || - attr->ia_gid != inode->i_gid) { - req->r_args.setattr.gid = cpu_to_le32(attr->ia_gid); + !gid_eq(attr->ia_gid, inode->i_gid)) { + req->r_args.setattr.gid = cpu_to_le32( + from_kgid(&init_user_ns, attr->ia_gid)); mask |= CEPH_SETATTR_GID; release |= CEPH_CAP_AUTH_SHARED; } From ff3d0046625c1b37df37beb8477135d44dae2823 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 04:01:53 -0800 Subject: [PATCH 2373/4607] ceph: Convert struct ceph_mds_request to use kuid_t and kgid_t Hold the uid and gid for a pending ceph mds request using the types kuid_t and kgid_t. When a request message is finally created convert the kuid_t and kgid_t values into uids and gids in the initial user namespace. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- fs/ceph/mds_client.c | 4 ++-- fs/ceph/mds_client.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 9165eb8309eb..7a3dfe0a9a80 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1658,8 +1658,8 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc, head->mdsmap_epoch = cpu_to_le32(mdsc->mdsmap->m_epoch); head->op = cpu_to_le32(req->r_op); - head->caller_uid = cpu_to_le32(req->r_uid); - head->caller_gid = cpu_to_le32(req->r_gid); + head->caller_uid = cpu_to_le32(from_kuid(&init_user_ns, req->r_uid)); + head->caller_gid = cpu_to_le32(from_kgid(&init_user_ns, req->r_gid)); head->args = req->r_args; ceph_encode_filepath(&p, end, ino1, path1); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index dd26846dd71d..ff4188bf6199 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -184,8 +184,8 @@ struct ceph_mds_request { union ceph_mds_request_args r_args; int r_fmode; /* file mode, if expecting cap */ - uid_t r_uid; - gid_t r_gid; + kuid_t r_uid; + kgid_t r_gid; /* for choosing which mds to send this request to */ int r_direct_mode; From bd2bae6a66df9261a39e47291b0a6b00cd0831e0 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 04:05:39 -0800 Subject: [PATCH 2374/4607] ceph: Convert kuids and kgids before printing them. Before printing kuid and kgids values convert them into the initial user namespace. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- fs/ceph/caps.c | 3 ++- fs/ceph/inode.c | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 39eb4665b122..ae2be696eb5b 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2362,7 +2362,8 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(grant->uid)); inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(grant->gid)); dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode, - inode->i_uid, inode->i_gid); + from_kuid(&init_user_ns, inode->i_uid), + from_kgid(&init_user_ns, inode->i_gid)); } if ((issued & CEPH_CAP_LINK_EXCL) == 0) diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 40e0787022e9..d45895f4a04d 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -615,7 +615,8 @@ static int fill_inode(struct inode *inode, inode->i_uid = make_kuid(&init_user_ns, le32_to_cpu(info->uid)); inode->i_gid = make_kgid(&init_user_ns, le32_to_cpu(info->gid)); dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode, - inode->i_uid, inode->i_gid); + from_kuid(&init_user_ns, inode->i_uid), + from_kgid(&init_user_ns, inode->i_gid)); } if ((issued & CEPH_CAP_LINK_EXCL) == 0) @@ -1565,7 +1566,8 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) if (ia_valid & ATTR_UID) { dout("setattr %p uid %d -> %d\n", inode, - inode->i_uid, attr->ia_uid); + from_kuid(&init_user_ns, inode->i_uid), + from_kuid(&init_user_ns, attr->ia_uid)); if (issued & CEPH_CAP_AUTH_EXCL) { inode->i_uid = attr->ia_uid; dirtied |= CEPH_CAP_AUTH_EXCL; @@ -1579,7 +1581,8 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr) } if (ia_valid & ATTR_GID) { dout("setattr %p gid %d -> %d\n", inode, - inode->i_gid, attr->ia_gid); + from_kgid(&init_user_ns, inode->i_gid), + from_kgid(&init_user_ns, attr->ia_gid)); if (issued & CEPH_CAP_AUTH_EXCL) { inode->i_gid = attr->ia_gid; dirtied |= CEPH_CAP_AUTH_EXCL; From d5ea055f1cc0ff6d4170c7f60f3cb5eb09d927bc Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 04:07:20 -0800 Subject: [PATCH 2375/4607] ceph: Enable building when user namespaces are enabled. Now that conversions happen from kuids and kgids when generating ceph messages and conversion happen to kuids and kgids after receiving celph messages, and all intermediate data structures store uids and gids as type kuid_t and kgid_t it is safe to enable ceph with user namespace support enabled. Cc: Sage Weil Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index c8c58bddfed3..7170d549159a 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1076,7 +1076,6 @@ config UIDGID_CONVERTED # Filesystems depends on 9P_FS = n depends on AFS_FS = n - depends on CEPH_FS = n depends on CIFS = n depends on CODA_FS = n depends on GFS2_FS = n From 97fc8b1ebf6a0fe4bb9c71a8e91a822c22c09bc5 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 29 Jan 2013 17:07:42 -0800 Subject: [PATCH 2376/4607] 9p: Add 'u' and 'g' format specifies for kuids and kgids This allows concentrating all of the conversion to and from kuids and kgids into the format needed by the 9p protocol into one location. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: "Eric W. Biederman" --- net/9p/protocol.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 3d33ecf13327..c289c6cee98e 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -85,6 +85,8 @@ pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size) d - int32_t q - int64_t s - string + u - numeric uid + g - numeric gid S - stat Q - qid D - data blob (int32_t size followed by void *, results are not freed) @@ -163,6 +165,26 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, (*sptr)[len] = 0; } break; + case 'u': { + kuid_t *uid = va_arg(ap, kuid_t *); + __le32 le_val; + if (pdu_read(pdu, &le_val, sizeof(le_val))) { + errcode = -EFAULT; + break; + } + *uid = make_kuid(&init_user_ns, + le32_to_cpu(le_val)); + } break; + case 'g': { + kgid_t *gid = va_arg(ap, kgid_t *); + __le32 le_val; + if (pdu_read(pdu, &le_val, sizeof(le_val))) { + errcode = -EFAULT; + break; + } + *gid = make_kgid(&init_user_ns, + le32_to_cpu(le_val)); + } break; case 'Q':{ struct p9_qid *qid = va_arg(ap, struct p9_qid *); @@ -377,6 +399,20 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, errcode = -EFAULT; } break; + case 'u': { + kuid_t uid = va_arg(ap, kuid_t); + __le32 val = cpu_to_le32( + from_kuid(&init_user_ns, uid)); + if (pdu_write(pdu, &val, sizeof(val))) + errcode = -EFAULT; + } break; + case 'g': { + kgid_t gid = va_arg(ap, kgid_t); + __le32 val = cpu_to_le32( + from_kgid(&init_user_ns, gid)); + if (pdu_write(pdu, &val, sizeof(val))) + errcode = -EFAULT; + } break; case 'Q':{ const struct p9_qid *qid = va_arg(ap, const struct p9_qid *); From f791f7c5e354870eaa5e31c4038c6723683283f1 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 29 Jan 2013 16:09:41 -0800 Subject: [PATCH 2377/4607] 9p: Transmit kuid and kgid values Modify the p9_client_rpc format specifiers of every function that directly transmits a uid or a gid from 'd' to 'u' or 'g' as appropriate. Modify those same functions to take kuid_t and kgid_t parameters instead of uid_t and gid_t parameters. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: Eric W. Biederman --- fs/9p/v9fs.c | 2 +- include/net/9p/client.h | 10 +++++----- net/9p/client.c | 25 +++++++++++++------------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index d934f04e7736..4e0bd9d62ed6 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -375,7 +375,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, v9ses->flags &= ~V9FS_ACL_MASK; } - fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, ~0, + fid = p9_client_attach(v9ses->clnt, NULL, v9ses->uname, INVALID_UID, v9ses->aname); if (IS_ERR(fid)) { retval = PTR_ERR(fid); diff --git a/include/net/9p/client.h b/include/net/9p/client.h index fc9b90b0c052..6b031ab6e4db 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -220,17 +220,17 @@ void p9_client_destroy(struct p9_client *clnt); void p9_client_disconnect(struct p9_client *clnt); void p9_client_begin_disconnect(struct p9_client *clnt); struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, - char *uname, u32 n_uname, char *aname); + char *uname, kuid_t n_uname, char *aname); struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwname, char **wnames, int clone); int p9_client_open(struct p9_fid *fid, int mode); int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode, char *extension); int p9_client_link(struct p9_fid *fid, struct p9_fid *oldfid, char *newname); -int p9_client_symlink(struct p9_fid *fid, char *name, char *symname, gid_t gid, +int p9_client_symlink(struct p9_fid *fid, char *name, char *symname, kgid_t gid, struct p9_qid *qid); int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode, - gid_t gid, struct p9_qid *qid); + kgid_t gid, struct p9_qid *qid); int p9_client_clunk(struct p9_fid *fid); int p9_client_fsync(struct p9_fid *fid, int datasync); int p9_client_remove(struct p9_fid *fid); @@ -250,9 +250,9 @@ struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid, u64 request_mask); int p9_client_mknod_dotl(struct p9_fid *oldfid, char *name, int mode, - dev_t rdev, gid_t gid, struct p9_qid *); + dev_t rdev, kgid_t gid, struct p9_qid *); int p9_client_mkdir_dotl(struct p9_fid *fid, char *name, int mode, - gid_t gid, struct p9_qid *); + kgid_t gid, struct p9_qid *); int p9_client_lock_dotl(struct p9_fid *fid, struct p9_flock *flock, u8 *status); int p9_client_getlock_dotl(struct p9_fid *fid, struct p9_getlock *fl); struct p9_req_t *p9_tag_lookup(struct p9_client *, u16); diff --git a/net/9p/client.c b/net/9p/client.c index 34d417670935..17855f080acd 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1100,7 +1100,7 @@ void p9_client_begin_disconnect(struct p9_client *clnt) EXPORT_SYMBOL(p9_client_begin_disconnect); struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, - char *uname, u32 n_uname, char *aname) + char *uname, kuid_t n_uname, char *aname) { int err = 0; struct p9_req_t *req; @@ -1117,7 +1117,7 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, goto error; } - req = p9_client_rpc(clnt, P9_TATTACH, "ddss?d", fid->fid, + req = p9_client_rpc(clnt, P9_TATTACH, "ddss?u", fid->fid, afid ? afid->fid : P9_NOFID, uname, aname, n_uname); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -1270,7 +1270,7 @@ error: EXPORT_SYMBOL(p9_client_open); int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode, - gid_t gid, struct p9_qid *qid) + kgid_t gid, struct p9_qid *qid) { int err = 0; struct p9_client *clnt; @@ -1279,13 +1279,14 @@ int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode, p9_debug(P9_DEBUG_9P, ">>> TLCREATE fid %d name %s flags %d mode %d gid %d\n", - ofid->fid, name, flags, mode, gid); + ofid->fid, name, flags, mode, + from_kgid(&init_user_ns, gid)); clnt = ofid->clnt; if (ofid->mode != -1) return -EINVAL; - req = p9_client_rpc(clnt, P9_TLCREATE, "dsddd", ofid->fid, name, flags, + req = p9_client_rpc(clnt, P9_TLCREATE, "dsddg", ofid->fid, name, flags, mode, gid); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -1358,7 +1359,7 @@ error: } EXPORT_SYMBOL(p9_client_fcreate); -int p9_client_symlink(struct p9_fid *dfid, char *name, char *symtgt, gid_t gid, +int p9_client_symlink(struct p9_fid *dfid, char *name, char *symtgt, kgid_t gid, struct p9_qid *qid) { int err = 0; @@ -1369,7 +1370,7 @@ int p9_client_symlink(struct p9_fid *dfid, char *name, char *symtgt, gid_t gid, dfid->fid, name, symtgt); clnt = dfid->clnt; - req = p9_client_rpc(clnt, P9_TSYMLINK, "dssd", dfid->fid, name, symtgt, + req = p9_client_rpc(clnt, P9_TSYMLINK, "dssg", dfid->fid, name, symtgt, gid); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -2106,7 +2107,7 @@ error: EXPORT_SYMBOL(p9_client_readdir); int p9_client_mknod_dotl(struct p9_fid *fid, char *name, int mode, - dev_t rdev, gid_t gid, struct p9_qid *qid) + dev_t rdev, kgid_t gid, struct p9_qid *qid) { int err; struct p9_client *clnt; @@ -2116,7 +2117,7 @@ int p9_client_mknod_dotl(struct p9_fid *fid, char *name, int mode, clnt = fid->clnt; p9_debug(P9_DEBUG_9P, ">>> TMKNOD fid %d name %s mode %d major %d " "minor %d\n", fid->fid, name, mode, MAJOR(rdev), MINOR(rdev)); - req = p9_client_rpc(clnt, P9_TMKNOD, "dsdddd", fid->fid, name, mode, + req = p9_client_rpc(clnt, P9_TMKNOD, "dsdddg", fid->fid, name, mode, MAJOR(rdev), MINOR(rdev), gid); if (IS_ERR(req)) return PTR_ERR(req); @@ -2137,7 +2138,7 @@ error: EXPORT_SYMBOL(p9_client_mknod_dotl); int p9_client_mkdir_dotl(struct p9_fid *fid, char *name, int mode, - gid_t gid, struct p9_qid *qid) + kgid_t gid, struct p9_qid *qid) { int err; struct p9_client *clnt; @@ -2146,8 +2147,8 @@ int p9_client_mkdir_dotl(struct p9_fid *fid, char *name, int mode, err = 0; clnt = fid->clnt; p9_debug(P9_DEBUG_9P, ">>> TMKDIR fid %d name %s mode %d gid %d\n", - fid->fid, name, mode, gid); - req = p9_client_rpc(clnt, P9_TMKDIR, "dsdd", fid->fid, name, mode, + fid->fid, name, mode, from_kgid(&init_user_ns, gid)); + req = p9_client_rpc(clnt, P9_TMKDIR, "dsdg", fid->fid, name, mode, gid); if (IS_ERR(req)) return PTR_ERR(req); From 447c50943fd008755122c7a62bac068e73c1cf2c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 29 Jan 2013 16:18:50 -0800 Subject: [PATCH 2378/4607] 9p: Modify the stat structures to use kuid_t and kgid_t 9p has thre strucrtures that can encode inode stat information. Modify all of those structures to contain kuid_t and kgid_t values. Modify he wire encoders and decoders of those structures to use 'u' and 'g' instead of 'd' in the format string where uids and gids are present. This results in all kuid and kgid conversion to and from on the wire values being performed by the same code in protocol.c where the client is known at the time of the conversion. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: Eric W. Biederman --- fs/9p/vfs_inode.c | 6 +++--- include/net/9p/9p.h | 14 +++++++------- net/9p/client.c | 18 +++++++++++++----- net/9p/protocol.c | 13 +++++++------ 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 890bed538f9b..1581fe218934 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -228,9 +228,9 @@ v9fs_blank_wstat(struct p9_wstat *wstat) wstat->uid = NULL; wstat->gid = NULL; wstat->muid = NULL; - wstat->n_uid = ~0; - wstat->n_gid = ~0; - wstat->n_muid = ~0; + wstat->n_uid = INVALID_UID; + wstat->n_gid = INVALID_GID; + wstat->n_muid = INVALID_UID; wstat->extension = NULL; } diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 7184853ca360..27dfe85772b1 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -407,17 +407,17 @@ struct p9_wstat { char *gid; char *muid; char *extension; /* 9p2000.u extensions */ - u32 n_uid; /* 9p2000.u extensions */ - u32 n_gid; /* 9p2000.u extensions */ - u32 n_muid; /* 9p2000.u extensions */ + kuid_t n_uid; /* 9p2000.u extensions */ + kgid_t n_gid; /* 9p2000.u extensions */ + kuid_t n_muid; /* 9p2000.u extensions */ }; struct p9_stat_dotl { u64 st_result_mask; struct p9_qid qid; u32 st_mode; - u32 st_uid; - u32 st_gid; + kuid_t st_uid; + kgid_t st_gid; u64 st_nlink; u64 st_rdev; u64 st_size; @@ -471,8 +471,8 @@ struct p9_stat_dotl { struct p9_iattr_dotl { u32 valid; u32 mode; - u32 uid; - u32 gid; + kuid_t uid; + kgid_t gid; u64 size; u64 atime_sec; u64 atime_nsec; diff --git a/net/9p/client.c b/net/9p/client.c index 17855f080acd..8eb75425e6e6 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1711,7 +1711,9 @@ struct p9_wstat *p9_client_stat(struct p9_fid *fid) (unsigned long long)ret->qid.path, ret->qid.version, ret->mode, ret->atime, ret->mtime, (unsigned long long)ret->length, ret->name, ret->uid, ret->gid, ret->muid, ret->extension, - ret->n_uid, ret->n_gid, ret->n_muid); + from_kuid(&init_user_ns, ret->n_uid), + from_kgid(&init_user_ns, ret->n_gid), + from_kuid(&init_user_ns, ret->n_muid)); p9_free_req(clnt, req); return ret; @@ -1765,8 +1767,10 @@ struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid, "<<< st_btime_sec=%lld st_btime_nsec=%lld\n" "<<< st_gen=%lld st_data_version=%lld", ret->st_result_mask, ret->qid.type, ret->qid.path, - ret->qid.version, ret->st_mode, ret->st_nlink, ret->st_uid, - ret->st_gid, ret->st_rdev, ret->st_size, ret->st_blksize, + ret->qid.version, ret->st_mode, ret->st_nlink, + from_kuid(&init_user_ns, ret->st_uid), + from_kgid(&init_user_ns, ret->st_gid), + ret->st_rdev, ret->st_size, ret->st_blksize, ret->st_blocks, ret->st_atime_sec, ret->st_atime_nsec, ret->st_mtime_sec, ret->st_mtime_nsec, ret->st_ctime_sec, ret->st_ctime_nsec, ret->st_btime_sec, ret->st_btime_nsec, @@ -1829,7 +1833,9 @@ int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst) (unsigned long long)wst->qid.path, wst->qid.version, wst->mode, wst->atime, wst->mtime, (unsigned long long)wst->length, wst->name, wst->uid, wst->gid, wst->muid, wst->extension, - wst->n_uid, wst->n_gid, wst->n_muid); + from_kuid(&init_user_ns, wst->n_uid), + from_kgid(&init_user_ns, wst->n_gid), + from_kuid(&init_user_ns, wst->n_muid)); req = p9_client_rpc(clnt, P9_TWSTAT, "dwS", fid->fid, wst->size+2, wst); if (IS_ERR(req)) { @@ -1858,7 +1864,9 @@ int p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *p9attr) " valid=%x mode=%x uid=%d gid=%d size=%lld\n" " atime_sec=%lld atime_nsec=%lld\n" " mtime_sec=%lld mtime_nsec=%lld\n", - p9attr->valid, p9attr->mode, p9attr->uid, p9attr->gid, + p9attr->valid, p9attr->mode, + from_kuid(&init_user_ns, p9attr->uid), + from_kgid(&init_user_ns, p9attr->gid), p9attr->size, p9attr->atime_sec, p9attr->atime_nsec, p9attr->mtime_sec, p9attr->mtime_nsec); diff --git a/net/9p/protocol.c b/net/9p/protocol.c index c289c6cee98e..ab9127ec5b7a 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -199,11 +199,12 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, va_arg(ap, struct p9_wstat *); memset(stbuf, 0, sizeof(struct p9_wstat)); - stbuf->n_uid = stbuf->n_gid = stbuf->n_muid = - -1; + stbuf->n_uid = stbuf->n_muid = INVALID_UID; + stbuf->n_gid = INVALID_GID; + errcode = p9pdu_readf(pdu, proto_version, - "wwdQdddqssss?sddd", + "wwdQdddqssss?sugu", &stbuf->size, &stbuf->type, &stbuf->dev, &stbuf->qid, &stbuf->mode, &stbuf->atime, @@ -316,7 +317,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, memset(stbuf, 0, sizeof(struct p9_stat_dotl)); errcode = p9pdu_readf(pdu, proto_version, - "qQdddqqqqqqqqqqqqqqq", + "qQdugqqqqqqqqqqqqqqq", &stbuf->st_result_mask, &stbuf->qid, &stbuf->st_mode, @@ -426,7 +427,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, va_arg(ap, const struct p9_wstat *); errcode = p9pdu_writef(pdu, proto_version, - "wwdQdddqssss?sddd", + "wwdQdddqssss?sugu", stbuf->size, stbuf->type, stbuf->dev, &stbuf->qid, stbuf->mode, stbuf->atime, @@ -504,7 +505,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, struct p9_iattr_dotl *); errcode = p9pdu_writef(pdu, proto_version, - "ddddqqqqq", + "ddugqqqqq", p9attr->valid, p9attr->mode, p9attr->uid, From b464255699077c6b33ea58ee01db80f5729511ad Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 11:48:53 -0800 Subject: [PATCH 2379/4607] 9p: Modify struct 9p_fid to use a kuid_t not a uid_t Change struct 9p_fid and it's associated functions to use kuid_t's instead of uid_t. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: "Eric W. Biederman" --- fs/9p/fid.c | 17 +++++++++-------- fs/9p/v9fs.c | 2 +- include/net/9p/client.h | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/fs/9p/fid.c b/fs/9p/fid.c index da8eefbe830d..afd4724b2d92 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -74,19 +74,20 @@ int v9fs_fid_add(struct dentry *dentry, struct p9_fid *fid) * */ -static struct p9_fid *v9fs_fid_find(struct dentry *dentry, u32 uid, int any) +static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any) { struct v9fs_dentry *dent; struct p9_fid *fid, *ret; p9_debug(P9_DEBUG_VFS, " dentry: %s (%p) uid %d any %d\n", - dentry->d_name.name, dentry, uid, any); + dentry->d_name.name, dentry, from_kuid(&init_user_ns, uid), + any); dent = (struct v9fs_dentry *) dentry->d_fsdata; ret = NULL; if (dent) { spin_lock(&dent->lock); list_for_each_entry(fid, &dent->fidlist, dlist) { - if (any || fid->uid == uid) { + if (any || uid_eq(fid->uid, uid)) { ret = fid; break; } @@ -126,7 +127,7 @@ err_out: } static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry, - uid_t uid, int any) + kuid_t uid, int any) { struct dentry *ds; char **wnames, *uname; @@ -233,7 +234,7 @@ err_out: struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) { - uid_t uid; + kuid_t uid; int any, access; struct v9fs_session_info *v9ses; @@ -253,7 +254,7 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) break; default: - uid = ~0; + uid = INVALID_UID; any = 0; break; } @@ -272,7 +273,7 @@ struct p9_fid *v9fs_fid_clone(struct dentry *dentry) return ret; } -static struct p9_fid *v9fs_fid_clone_with_uid(struct dentry *dentry, uid_t uid) +static struct p9_fid *v9fs_fid_clone_with_uid(struct dentry *dentry, kuid_t uid) { struct p9_fid *fid, *ret; @@ -289,7 +290,7 @@ struct p9_fid *v9fs_writeback_fid(struct dentry *dentry) int err; struct p9_fid *fid; - fid = v9fs_fid_clone_with_uid(dentry, 0); + fid = v9fs_fid_clone_with_uid(dentry, GLOBAL_ROOT_UID); if (IS_ERR(fid)) goto error_out; /* diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 4e0bd9d62ed6..d64967cbd73e 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -387,7 +387,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, if ((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_SINGLE) fid->uid = v9ses->uid; else - fid->uid = ~0; + fid->uid = INVALID_UID; #ifdef CONFIG_9P_FSCACHE /* register the session for caching */ diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 6b031ab6e4db..5ff70f433e87 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -187,7 +187,7 @@ struct p9_fid { int mode; struct p9_qid qid; u32 iounit; - uid_t uid; + kuid_t uid; void *rdir; From 76ed23a5d703d94ede1ef6c12c14a75add691202 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 11:57:40 -0800 Subject: [PATCH 2380/4607] 9p: Modify struct v9fs_session_info to use a kuids and kgids Change struct v9fs_session_info and the code that popluates it to use kuids and kgids. When parsing the 9p mount options convert the dfltuid, dflutgid, and the session uid from the current user namespace into kuids and kgids. Modify V9FS_DEFUID and V9FS_DEFGUID to be kuid and kgid values. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: "Eric W. Biederman" --- fs/9p/v9fs.c | 30 +++++++++++++++++++++++++----- fs/9p/v9fs.h | 10 +++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index d64967cbd73e..58e6cbce4156 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -161,7 +161,13 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) ret = r; continue; } - v9ses->dfltuid = option; + v9ses->dfltuid = make_kuid(current_user_ns(), option); + if (!uid_valid(v9ses->dfltuid)) { + p9_debug(P9_DEBUG_ERROR, + "uid field, but not a uid?\n"); + ret = -EINVAL; + continue; + } break; case Opt_dfltgid: r = match_int(&args[0], &option); @@ -171,7 +177,13 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) ret = r; continue; } - v9ses->dfltgid = option; + v9ses->dfltgid = make_kgid(current_user_ns(), option); + if (!gid_valid(v9ses->dfltgid)) { + p9_debug(P9_DEBUG_ERROR, + "gid field, but not a gid?\n"); + ret = -EINVAL; + continue; + } break; case Opt_afid: r = match_int(&args[0], &option); @@ -248,8 +260,9 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) else if (strcmp(s, "client") == 0) { v9ses->flags |= V9FS_ACCESS_CLIENT; } else { + uid_t uid; v9ses->flags |= V9FS_ACCESS_SINGLE; - v9ses->uid = simple_strtoul(s, &e, 10); + uid = simple_strtoul(s, &e, 10); if (*e != '\0') { ret = -EINVAL; pr_info("Unknown access argument %s\n", @@ -257,6 +270,13 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts) kfree(s); goto free_and_return; } + v9ses->uid = make_kuid(current_user_ns(), uid); + if (!uid_valid(v9ses->uid)) { + ret = -EINVAL; + pr_info("Uknown uid %s\n", s); + kfree(s); + goto free_and_return; + } } kfree(s); @@ -319,7 +339,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, list_add(&v9ses->slist, &v9fs_sessionlist); spin_unlock(&v9fs_sessionlist_lock); - v9ses->uid = ~0; + v9ses->uid = INVALID_UID; v9ses->dfltuid = V9FS_DEFUID; v9ses->dfltgid = V9FS_DEFGID; @@ -364,7 +384,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, v9ses->flags &= ~V9FS_ACCESS_MASK; v9ses->flags |= V9FS_ACCESS_ANY; - v9ses->uid = ~0; + v9ses->uid = INVALID_UID; } if (!v9fs_proto_dotl(v9ses) || !((v9ses->flags & V9FS_ACCESS_MASK) == V9FS_ACCESS_CLIENT)) { diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index 34c59f14a1c9..a8e127c89627 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -109,9 +109,9 @@ struct v9fs_session_info { char *uname; /* user name to mount as */ char *aname; /* name of remote hierarchy being mounted */ unsigned int maxdata; /* max data for client interface */ - unsigned int dfltuid; /* default uid/muid for legacy support */ - unsigned int dfltgid; /* default gid for legacy support */ - u32 uid; /* if ACCESS_SINGLE, the uid that has access */ + kuid_t dfltuid; /* default uid/muid for legacy support */ + kgid_t dfltgid; /* default gid for legacy support */ + kuid_t uid; /* if ACCESS_SINGLE, the uid that has access */ struct p9_client *clnt; /* 9p client */ struct list_head slist; /* list of sessions registered with v9fs */ struct backing_dev_info bdi; @@ -165,8 +165,8 @@ extern struct inode *v9fs_inode_from_fid_dotl(struct v9fs_session_info *v9ses, #define V9FS_PORT 564 #define V9FS_DEFUSER "nobody" #define V9FS_DEFANAME "" -#define V9FS_DEFUID (-2) -#define V9FS_DEFGID (-2) +#define V9FS_DEFUID KUIDT_INIT(-2) +#define V9FS_DEFGID KGIDT_INIT(-2) static inline struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode) { From d4ef4e3581044890e4a04607a4e8f42d3ebcf704 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 12:08:21 -0800 Subject: [PATCH 2381/4607] 9p: Modify v9fs_get_fsgid_for_create to return a kgid Modify v9fs_get_fsgid_for_create to return a kgid and modify all of the variables that hold the result of v9fs_get_fsgid_for_create to be of type kgid_t. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: "Eric W. Biederman" --- fs/9p/vfs_inode_dotl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 40895546e103..e27886573e7d 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -57,7 +57,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode, * group of the new file system object. */ -static gid_t v9fs_get_fsgid_for_create(struct inode *dir_inode) +static kgid_t v9fs_get_fsgid_for_create(struct inode *dir_inode) { BUG_ON(dir_inode == NULL); @@ -246,7 +246,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry, int *opened) { int err = 0; - gid_t gid; + kgid_t gid; umode_t mode; char *name = NULL; struct p9_qid qid; @@ -391,7 +391,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir, int err; struct v9fs_session_info *v9ses; struct p9_fid *fid = NULL, *dfid = NULL; - gid_t gid; + kgid_t gid; char *name; umode_t mode; struct inode *inode; @@ -692,7 +692,7 @@ v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry, const char *symname) { int err; - gid_t gid; + kgid_t gid; char *name; struct p9_qid qid; struct inode *inode; @@ -832,7 +832,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode, dev_t rdev) { int err; - gid_t gid; + kgid_t gid; char *name; umode_t mode; struct v9fs_session_info *v9ses; From 4fa814be258169caef51e320b8b06cb3b139d4a0 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 12:11:45 -0800 Subject: [PATCH 2382/4607] 9p: Allow building 9p with user namespaces enabled. Now that the uid_t -> kuid_t, gid_t -> kgid_t conversion has been completed in 9p allow 9p to be built when user namespaces are enabled. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/init/Kconfig b/init/Kconfig index 7170d549159a..394d24f99efe 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1070,11 +1070,7 @@ config UIDGID_CONVERTED bool default y - # Networking - depends on NET_9P = n - # Filesystems - depends on 9P_FS = n depends on AFS_FS = n depends on CIFS = n depends on CODA_FS = n From 66fdb93f882d21612a5287cd1303c9b1391ebf5d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 13:04:05 -0800 Subject: [PATCH 2383/4607] afs: Remove unused structure afs_store_status While looking for kuid_t and kgid_t conversions I found this structure that has never been used since it was added to the kernel in 2007. The obvious for this structure to be used is in xdr_encode_AFS_StoreStatus and that function uses a small handful of local variables instead. So remove the unnecessary structure to prevent confusion. Cc: David Howells Signed-off-by: "Eric W. Biederman" --- fs/afs/afs.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/fs/afs/afs.h b/fs/afs/afs.h index c548aa346f0d..3d8fd35c0dd0 100644 --- a/fs/afs/afs.h +++ b/fs/afs/afs.h @@ -133,13 +133,6 @@ struct afs_file_status { /* * AFS file status change request */ -struct afs_store_status { - u32 mask; /* which bits of the struct are set */ - u32 mtime_client; /* last time client changed data */ - u32 owner; /* owner ID */ - u32 group; /* group ID */ - umode_t mode; /* UNIX mode */ -}; #define AFS_SET_MTIME 0x01 /* set the mtime */ #define AFS_SET_OWNER 0x02 /* set the owner ID */ From 47f531e8ba3bc3901a0c493f4252826c41dea1a1 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 31 Jan 2013 19:02:03 +0000 Subject: [PATCH 2384/4607] efivarfs: Validate filenames much more aggressively The only thing that efivarfs does to enforce a valid filename is ensure that the name isn't too short. We need to strongly sanitise any filenames, not least because variable creation is delayed until efivarfs_file_write(), which means we can't rely on the firmware to inform us of an invalid name, because if the file is never written to we'll never know it's invalid. Perform a couple of steps before agreeing to create a new file, * hex_to_bin() returns a value indicating whether or not it was able to convert its arguments to a binary representation - we should check it. * Ensure that the GUID portion of the filename is the correct length and format. * The variable name portion of the filename needs to be at least one character in size. Reported-by: Lingzhu Xiang Cc: Matthew Garrett Cc: Jeremy Kerr Cc: Al Viro Signed-off-by: Matt Fleming --- drivers/firmware/efivars.c | 49 ++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 371c44129525..868cea5cd4b8 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -79,6 +79,7 @@ #include #include #include +#include #include #include @@ -900,6 +901,48 @@ static struct inode *efivarfs_get_inode(struct super_block *sb, return inode; } +/* + * Return true if 'str' is a valid efivarfs filename of the form, + * + * VariableName-12345678-1234-1234-1234-1234567891bc + */ +static bool efivarfs_valid_name(const char *str, int len) +{ + static const char dashes[GUID_LEN] = { + [8] = 1, [13] = 1, [18] = 1, [23] = 1 + }; + const char *s = str + len - GUID_LEN; + int i; + + /* + * We need a GUID, plus at least one letter for the variable name, + * plus the '-' separator + */ + if (len < GUID_LEN + 2) + return false; + + /* GUID should be right after the first '-' */ + if (s - 1 != strchr(str, '-')) + return false; + + /* + * Validate that 's' is of the correct format, e.g. + * + * 12345678-1234-1234-1234-123456789abc + */ + for (i = 0; i < GUID_LEN; i++) { + if (dashes[i]) { + if (*s++ != '-') + return false; + } else { + if (!isxdigit(*s++)) + return false; + } + } + + return true; +} + static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid) { guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]); @@ -928,11 +971,7 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, struct efivar_entry *var; int namelen, i = 0, err = 0; - /* - * We need a GUID, plus at least one letter for the variable name, - * plus the '-' separator - */ - if (dentry->d_name.len < GUID_LEN + 2) + if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len)) return -EINVAL; inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0); From da27a24383b2b10bf6ebd0db29b325548aafecb4 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 1 Feb 2013 11:02:28 +0000 Subject: [PATCH 2385/4607] efivarfs: guid part of filenames are case-insensitive It makes no sense to treat the following filenames as unique, VarName-abcdefab-abcd-abcd-abcd-abcdefabcdef VarName-ABCDEFAB-ABCD-ABCD-ABCD-ABCDEFABCDEF VarName-ABcDEfAB-ABcD-ABcD-ABcD-ABcDEfABcDEf VarName-aBcDEfAB-aBcD-aBcD-aBcD-aBcDEfaBcDEf ... etc ... since the guid will be converted into a binary representation, which has no case. Roll our own dentry operations so that we can treat the variable name part of filenames ("VarName" in the above example) as case-sensitive, but the guid portion as case-insensitive. That way, efivarfs will refuse to create the above files if any one already exists. Reported-by: Lingzhu Xiang Cc: Matthew Garrett Cc: Jeremy Kerr Cc: Al Viro Signed-off-by: Matt Fleming --- drivers/firmware/efivars.c | 95 +++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 868cea5cd4b8..8bcb5958f21a 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -1043,6 +1043,84 @@ static int efivarfs_unlink(struct inode *dir, struct dentry *dentry) return -EINVAL; }; +/* + * Compare two efivarfs file names. + * + * An efivarfs filename is composed of two parts, + * + * 1. A case-sensitive variable name + * 2. A case-insensitive GUID + * + * So we need to perform a case-sensitive match on part 1 and a + * case-insensitive match on part 2. + */ +static int efivarfs_d_compare(const struct dentry *parent, const struct inode *pinode, + const struct dentry *dentry, const struct inode *inode, + unsigned int len, const char *str, + const struct qstr *name) +{ + int guid = len - GUID_LEN; + + if (name->len != len) + return 1; + + /* Case-sensitive compare for the variable name */ + if (memcmp(str, name->name, guid)) + return 1; + + /* Case-insensitive compare for the GUID */ + return strncasecmp(name->name + guid, str + guid, GUID_LEN); +} + +static int efivarfs_d_hash(const struct dentry *dentry, + const struct inode *inode, struct qstr *qstr) +{ + unsigned long hash = init_name_hash(); + const unsigned char *s = qstr->name; + unsigned int len = qstr->len; + + if (!efivarfs_valid_name(s, len)) + return -EINVAL; + + while (len-- > GUID_LEN) + hash = partial_name_hash(*s++, hash); + + /* GUID is case-insensitive. */ + while (len--) + hash = partial_name_hash(tolower(*s++), hash); + + qstr->hash = end_name_hash(hash); + return 0; +} + +/* + * Retaining negative dentries for an in-memory filesystem just wastes + * memory and lookup time: arrange for them to be deleted immediately. + */ +static int efivarfs_delete_dentry(const struct dentry *dentry) +{ + return 1; +} + +static struct dentry_operations efivarfs_d_ops = { + .d_compare = efivarfs_d_compare, + .d_hash = efivarfs_d_hash, + .d_delete = efivarfs_delete_dentry, +}; + +static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) +{ + struct qstr q; + + q.name = name; + q.len = strlen(name); + + if (efivarfs_d_hash(NULL, NULL, &q)) + return NULL; + + return d_alloc(parent, &q); +} + static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode = NULL; @@ -1058,6 +1136,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = EFIVARFS_MAGIC; sb->s_op = &efivarfs_ops; + sb->s_d_op = &efivarfs_d_ops; sb->s_time_gran = 1; inode = efivarfs_get_inode(sb, NULL, S_IFDIR | 0755, 0); @@ -1098,7 +1177,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) if (!inode) goto fail_name; - dentry = d_alloc_name(root, name); + dentry = efivarfs_alloc_dentry(root, name); if (!dentry) goto fail_inode; @@ -1148,8 +1227,20 @@ static struct file_system_type efivarfs_type = { .kill_sb = efivarfs_kill_sb, }; +/* + * Handle negative dentry. + */ +static struct dentry *efivarfs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + if (dentry->d_name.len > NAME_MAX) + return ERR_PTR(-ENAMETOOLONG); + d_add(dentry, NULL); + return NULL; +} + static const struct inode_operations efivarfs_dir_inode_operations = { - .lookup = simple_lookup, + .lookup = efivarfs_lookup, .unlink = efivarfs_unlink, .create = efivarfs_create, }; From c0cf787f10c6ffb050e85c8ac9c0a8161cb06cb9 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Wed, 23 Jan 2013 20:47:08 +0800 Subject: [PATCH 2386/4607] mtd: atmel_nand: avoid to report an error when lookup table offset is 0. Before this patch, we assume the whole ROM code are mapping to memory. So it is wrong if the lookup table offset is 0. After this patch, we can map only the lookup table of ROM code to memory intead of the whole ROM code (about 1M). In this case, one lookup table offset can be 0. Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy --- drivers/mtd/nand/atmel_nand.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index c516a9408087..1d989dba4ce5 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1215,7 +1215,7 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) static int atmel_of_init_port(struct atmel_nand_host *host, struct device_node *np) { - u32 val, table_offset; + u32 val; u32 offset[2]; int ecc_mode; struct atmel_nand_data *board = &host->board; @@ -1288,13 +1288,12 @@ static int atmel_of_init_port(struct atmel_nand_host *host, dev_err(host->dev, "Cannot get PMECC lookup table offset\n"); return -EINVAL; } - table_offset = host->pmecc_sector_size == 512 ? offset[0] : offset[1]; - - if (!table_offset) { + if (!offset[0] && !offset[1]) { dev_err(host->dev, "Invalid PMECC lookup table offset\n"); return -EINVAL; } - host->pmecc_lookup_table_offset = table_offset; + host->pmecc_lookup_table_offset = + (host->pmecc_sector_size == 512) ? offset[0] : offset[1]; return 0; } From e66b4318f8fc0e12421de6287c557cdd92789010 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Wed, 23 Jan 2013 20:47:11 +0800 Subject: [PATCH 2387/4607] mtd: atmel_nand: make pmecc-cap, pmecc-sector-size in dts is optional. If those two are not specified in dts file, driver will report an error. TODO: in this case, driver will find ecc requirement in NAND ONFI parameters. Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy --- drivers/mtd/nand/atmel_nand.c | 52 +++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 1d989dba4ce5..f186a371e437 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -101,6 +101,8 @@ struct atmel_nand_host { u8 pmecc_corr_cap; u16 pmecc_sector_size; u32 pmecc_lookup_table_offset; + u32 pmecc_lookup_table_offset_512; + u32 pmecc_lookup_table_offset_1024; int pmecc_bytes_per_sector; int pmecc_sector_number; @@ -916,8 +918,16 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, struct resource *regs, *regs_pmerr, *regs_rom; int cap, sector_size, err_no; + if (host->pmecc_corr_cap == 0 || host->pmecc_sector_size == 0) + /* TODO: Should use ONFI ecc parameters. */ + return -EINVAL; + cap = host->pmecc_corr_cap; sector_size = host->pmecc_sector_size; + host->pmecc_lookup_table_offset = (sector_size == 512) ? + host->pmecc_lookup_table_offset_512 : + host->pmecc_lookup_table_offset_1024; + dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n", cap, sector_size); @@ -1259,29 +1269,29 @@ static int atmel_of_init_port(struct atmel_nand_host *host, /* use PMECC, get correction capability, sector size and lookup * table offset. + * If correction bits and sector size are not specified, then find + * them from NAND ONFI parameters. */ - if (of_property_read_u32(np, "atmel,pmecc-cap", &val) != 0) { - dev_err(host->dev, "Cannot decide PMECC Capability\n"); - return -EINVAL; - } else if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && - (val != 24)) { - dev_err(host->dev, - "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", - val); - return -EINVAL; + if (of_property_read_u32(np, "atmel,pmecc-cap", &val) == 0) { + if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && + (val != 24)) { + dev_err(host->dev, + "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", + val); + return -EINVAL; + } + host->pmecc_corr_cap = (u8)val; } - host->pmecc_corr_cap = (u8)val; - if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) != 0) { - dev_err(host->dev, "Cannot decide PMECC Sector Size\n"); - return -EINVAL; - } else if ((val != 512) && (val != 1024)) { - dev_err(host->dev, - "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", - val); - return -EINVAL; + if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) { + if ((val != 512) && (val != 1024)) { + dev_err(host->dev, + "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", + val); + return -EINVAL; + } + host->pmecc_sector_size = (u16)val; } - host->pmecc_sector_size = (u16)val; if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset", offset, 2) != 0) { @@ -1292,8 +1302,8 @@ static int atmel_of_init_port(struct atmel_nand_host *host, dev_err(host->dev, "Invalid PMECC lookup table offset\n"); return -EINVAL; } - host->pmecc_lookup_table_offset = - (host->pmecc_sector_size == 512) ? offset[0] : offset[1]; + host->pmecc_lookup_table_offset_512 = offset[0]; + host->pmecc_lookup_table_offset_1024 = offset[1]; return 0; } From 84cfbbb85a98c8c89407f108da9d102e5dfe2ae5 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Wed, 23 Jan 2013 20:47:12 +0800 Subject: [PATCH 2388/4607] mtd: at91: atmel_nand: for PMECC, add code to check the ONFI parameter ECC requirement. This patch will check NAND flash's ecc minimum requirement in ONFI parameter. 1. if pmecc-cap, pmecc-sector-size is set in dts. then use it. Driver will print out a WARNING if the values are different from ONFI parameters. 2. if pmecc-cap, pmecc-sector-size is not set in dts, then use the value from ONFI parameters. * If ONFI ECC parameters are in ONFI extended parameter page, since we are not support it, so assume the minimum ecc requirement is 2 bits in 512 bytes. * For non-ONFI support nand flash, also assume the minimum ecc requirement is 2 bits in 512 bytes. Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy --- drivers/mtd/nand/atmel_nand.c | 90 +++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index f186a371e437..ffcbcca2fd2d 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -910,6 +910,84 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd) pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); } +/* + * Get ECC requirement in ONFI parameters, returns -1 if ONFI + * parameters is not supported. + * return 0 if success to get the ECC requirement. + */ +static int get_onfi_ecc_param(struct nand_chip *chip, + int *ecc_bits, int *sector_size) +{ + *ecc_bits = *sector_size = 0; + + if (chip->onfi_params.ecc_bits == 0xff) + /* TODO: the sector_size and ecc_bits need to be find in + * extended ecc parameter, currently we don't support it. + */ + return -1; + + *ecc_bits = chip->onfi_params.ecc_bits; + + /* The default sector size (ecc codeword size) is 512 */ + *sector_size = 512; + + return 0; +} + +/* + * Get ecc requirement from ONFI parameters ecc requirement. + * If pmecc-cap, pmecc-sector-size in DTS are not specified, this function + * will set them according to ONFI ecc requirement. Otherwise, use the + * value in DTS file. + * return 0 if success. otherwise return error code. + */ +static int pmecc_choose_ecc(struct atmel_nand_host *host, + int *cap, int *sector_size) +{ + /* Get ECC requirement from ONFI parameters */ + *cap = *sector_size = 0; + if (host->nand_chip.onfi_version) { + if (!get_onfi_ecc_param(&host->nand_chip, cap, sector_size)) + dev_info(host->dev, "ONFI params, minimum required ECC: %d bits in %d bytes\n", + *cap, *sector_size); + else + dev_info(host->dev, "NAND chip ECC reqirement is in Extended ONFI parameter, we don't support yet.\n"); + } else { + dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes"); + } + if (*cap == 0 && *sector_size == 0) { + *cap = 2; + *sector_size = 512; + } + + /* If dts file doesn't specify then use the one in ONFI parameters */ + if (host->pmecc_corr_cap == 0) { + /* use the most fitable ecc bits (the near bigger one ) */ + if (*cap <= 2) + host->pmecc_corr_cap = 2; + else if (*cap <= 4) + host->pmecc_corr_cap = 4; + else if (*cap < 8) + host->pmecc_corr_cap = 8; + else if (*cap < 12) + host->pmecc_corr_cap = 12; + else if (*cap < 24) + host->pmecc_corr_cap = 24; + else + return -EINVAL; + } + if (host->pmecc_sector_size == 0) { + /* use the most fitable sector size (the near smaller one ) */ + if (*sector_size >= 1024) + host->pmecc_sector_size = 1024; + else if (*sector_size >= 512) + host->pmecc_sector_size = 512; + else + return -EINVAL; + } + return 0; +} + static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, struct atmel_nand_host *host) { @@ -918,9 +996,15 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, struct resource *regs, *regs_pmerr, *regs_rom; int cap, sector_size, err_no; - if (host->pmecc_corr_cap == 0 || host->pmecc_sector_size == 0) - /* TODO: Should use ONFI ecc parameters. */ - return -EINVAL; + err_no = pmecc_choose_ecc(host, &cap, §or_size); + if (err_no) { + dev_err(host->dev, "The NAND flash's ECC requirement are not support!"); + return err_no; + } + + if (cap != host->pmecc_corr_cap || + sector_size != host->pmecc_sector_size) + dev_info(host->dev, "WARNING: Be Caution! Using different PMECC parameters from Nand ONFI ECC reqirement.\n"); cap = host->pmecc_corr_cap; sector_size = host->pmecc_sector_size; From eb82038f97f93c5f0ff274fb98a9fff741dc2f5e Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 25 Jan 2013 11:50:27 -0300 Subject: [PATCH 2389/4607] mtd: physmap_of: Convert device allocation to managed devm_kzalloc() Signed-off-by: Ezequiel Garcia Signed-off-by: Artem Bityutskiy --- drivers/mtd/maps/physmap_of.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 263d9e1b8001..500d7f4f8028 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -68,9 +68,6 @@ static int of_flash_remove(struct platform_device *dev) kfree(info->list[i].res); } } - - kfree(info); - return 0; } @@ -199,8 +196,9 @@ static int of_flash_probe(struct platform_device *dev) map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access"); err = -ENOMEM; - info = kzalloc(sizeof(struct of_flash) + - sizeof(struct of_flash_list) * count, GFP_KERNEL); + info = devm_kzalloc(&dev->dev, + sizeof(struct of_flash) + + sizeof(struct of_flash_list) * count, GFP_KERNEL); if (!info) goto err_flash_remove; From cfdf5b6cc5985014a7ce891093f4fd0ae2d27ca6 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 7 Feb 2013 17:36:28 +0200 Subject: [PATCH 2390/4607] dw_dmac: add support for Lynxpoint DMA controllers Intel Lynxpoint PCH Low Power Subsystem has DMA controller to support general purpose serial buses like SPI, I2C, and HSUART. This controller is enumerated from ACPI namespace with ACPI ID INTL9C60. Signed-off-by: Mika Westerberg Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 35c8dd5f1013..6694676c9b82 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1899,6 +1899,11 @@ static const struct of_device_id dw_dma_id_table[] = { MODULE_DEVICE_TABLE(of, dw_dma_id_table); #endif +static const struct platform_device_id dw_dma_ids[] = { + { "INTL9C60", 0 }, + { } +}; + static struct platform_driver dw_driver = { .probe = dw_probe, .remove = dw_remove, @@ -1908,6 +1913,7 @@ static struct platform_driver dw_driver = { .pm = &dw_dev_pm_ops, .of_match_table = of_match_ptr(dw_dma_id_table), }, + .id_table = dw_dma_ids, }; static int __init dw_init(void) From 4dec23d7718e6f1f5e1773698d112025169e7d49 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 7 Feb 2013 14:38:32 -0700 Subject: [PATCH 2391/4607] ioatdma: fix race between updating ioat->head and IOAT_COMPLETION_PENDING There is a race that can hit during __cleanup() when the ioat->head pointer is incremented during descriptor submission. The __cleanup() can clear the PENDING flag when it does not see any active descriptors. This causes new submitted descriptors to be ignored because the COMPLETION_PENDING flag is cleared. This was introduced when code was adapted from ioatdma v1 to ioatdma v2. For v2 and v3, IOAT_COMPLETION_PENDING flag will be abandoned and a new flag IOAT_CHAN_ACTIVE will be utilized. This flag will also be protected under the prep_lock when being modified in order to avoid the race. Signed-off-by: Dave Jiang Reviewed-by: Dan Williams Signed-off-by: Vinod Koul --- drivers/dma/ioat/dma.h | 1 + drivers/dma/ioat/dma_v2.c | 113 +++++++++++++++++++++----------------- drivers/dma/ioat/dma_v3.c | 111 +++++++++++++++++++++---------------- 3 files changed, 128 insertions(+), 97 deletions(-) diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index 5e8fe01ba69d..1020598b80d3 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -97,6 +97,7 @@ struct ioat_chan_common { #define IOAT_KOBJ_INIT_FAIL 3 #define IOAT_RESHAPE_PENDING 4 #define IOAT_RUN 5 + #define IOAT_CHAN_ACTIVE 6 struct timer_list timer; #define COMPLETION_TIMEOUT msecs_to_jiffies(100) #define IDLE_TIMEOUT msecs_to_jiffies(2000) diff --git a/drivers/dma/ioat/dma_v2.c b/drivers/dma/ioat/dma_v2.c index b9d667851445..e2b2c70f03db 100644 --- a/drivers/dma/ioat/dma_v2.c +++ b/drivers/dma/ioat/dma_v2.c @@ -269,61 +269,22 @@ static void ioat2_restart_channel(struct ioat2_dma_chan *ioat) __ioat2_restart_chan(ioat); } -void ioat2_timer_event(unsigned long data) +static void check_active(struct ioat2_dma_chan *ioat) { - struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); struct ioat_chan_common *chan = &ioat->base; - if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - dma_addr_t phys_complete; - u64 status; - - status = ioat_chansts(chan); - - /* when halted due to errors check for channel - * programming errors before advancing the completion state - */ - if (is_ioat_halted(status)) { - u32 chanerr; - - chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); - dev_err(to_dev(chan), "%s: Channel halted (%x)\n", - __func__, chanerr); - if (test_bit(IOAT_RUN, &chan->state)) - BUG_ON(is_ioat_bug(chanerr)); - else /* we never got off the ground */ - return; - } - - /* if we haven't made progress and we have already - * acknowledged a pending completion once, then be more - * forceful with a restart - */ - spin_lock_bh(&chan->cleanup_lock); - if (ioat_cleanup_preamble(chan, &phys_complete)) { - __cleanup(ioat, phys_complete); - } else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { - spin_lock_bh(&ioat->prep_lock); - ioat2_restart_channel(ioat); - spin_unlock_bh(&ioat->prep_lock); - } else { - set_bit(IOAT_COMPLETION_ACK, &chan->state); - mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); - } - spin_unlock_bh(&chan->cleanup_lock); - } else { - u16 active; + if (ioat2_ring_active(ioat)) { + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + return; + } + if (test_and_clear_bit(IOAT_CHAN_ACTIVE, &chan->state)) + mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); + else if (ioat->alloc_order > ioat_get_alloc_order()) { /* if the ring is idle, empty, and oversized try to step * down the size */ - spin_lock_bh(&chan->cleanup_lock); - spin_lock_bh(&ioat->prep_lock); - active = ioat2_ring_active(ioat); - if (active == 0 && ioat->alloc_order > ioat_get_alloc_order()) - reshape_ring(ioat, ioat->alloc_order-1); - spin_unlock_bh(&ioat->prep_lock); - spin_unlock_bh(&chan->cleanup_lock); + reshape_ring(ioat, ioat->alloc_order - 1); /* keep shrinking until we get back to our minimum * default size @@ -331,6 +292,60 @@ void ioat2_timer_event(unsigned long data) if (ioat->alloc_order > ioat_get_alloc_order()) mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); } + +} + +void ioat2_timer_event(unsigned long data) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); + struct ioat_chan_common *chan = &ioat->base; + dma_addr_t phys_complete; + u64 status; + + status = ioat_chansts(chan); + + /* when halted due to errors check for channel + * programming errors before advancing the completion state + */ + if (is_ioat_halted(status)) { + u32 chanerr; + + chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); + dev_err(to_dev(chan), "%s: Channel halted (%x)\n", + __func__, chanerr); + if (test_bit(IOAT_RUN, &chan->state)) + BUG_ON(is_ioat_bug(chanerr)); + else /* we never got off the ground */ + return; + } + + /* if we haven't made progress and we have already + * acknowledged a pending completion once, then be more + * forceful with a restart + */ + spin_lock_bh(&chan->cleanup_lock); + if (ioat_cleanup_preamble(chan, &phys_complete)) + __cleanup(ioat, phys_complete); + else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { + spin_lock_bh(&ioat->prep_lock); + ioat2_restart_channel(ioat); + spin_unlock_bh(&ioat->prep_lock); + spin_unlock_bh(&chan->cleanup_lock); + return; + } else { + set_bit(IOAT_COMPLETION_ACK, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + } + + + if (ioat2_ring_active(ioat)) + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + else { + spin_lock_bh(&ioat->prep_lock); + check_active(ioat); + spin_unlock_bh(&ioat->prep_lock); + } + spin_unlock_bh(&chan->cleanup_lock); } static int ioat2_reset_hw(struct ioat_chan_common *chan) @@ -404,7 +419,7 @@ static dma_cookie_t ioat2_tx_submit_unlock(struct dma_async_tx_descriptor *tx) cookie = dma_cookie_assign(tx); dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie); - if (!test_and_set_bit(IOAT_COMPLETION_PENDING, &chan->state)) + if (!test_and_set_bit(IOAT_CHAN_ACTIVE, &chan->state)) mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); /* make descriptor updates visible before advancing ioat->head, diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index e52cf1eb6839..570dd6320c32 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -342,61 +342,22 @@ static void ioat3_restart_channel(struct ioat2_dma_chan *ioat) __ioat2_restart_chan(ioat); } -static void ioat3_timer_event(unsigned long data) +static void check_active(struct ioat2_dma_chan *ioat) { - struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); struct ioat_chan_common *chan = &ioat->base; - if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - dma_addr_t phys_complete; - u64 status; - - status = ioat_chansts(chan); - - /* when halted due to errors check for channel - * programming errors before advancing the completion state - */ - if (is_ioat_halted(status)) { - u32 chanerr; - - chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); - dev_err(to_dev(chan), "%s: Channel halted (%x)\n", - __func__, chanerr); - if (test_bit(IOAT_RUN, &chan->state)) - BUG_ON(is_ioat_bug(chanerr)); - else /* we never got off the ground */ - return; - } - - /* if we haven't made progress and we have already - * acknowledged a pending completion once, then be more - * forceful with a restart - */ - spin_lock_bh(&chan->cleanup_lock); - if (ioat_cleanup_preamble(chan, &phys_complete)) - __cleanup(ioat, phys_complete); - else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { - spin_lock_bh(&ioat->prep_lock); - ioat3_restart_channel(ioat); - spin_unlock_bh(&ioat->prep_lock); - } else { - set_bit(IOAT_COMPLETION_ACK, &chan->state); - mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); - } - spin_unlock_bh(&chan->cleanup_lock); - } else { - u16 active; + if (ioat2_ring_active(ioat)) { + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + return; + } + if (test_and_clear_bit(IOAT_CHAN_ACTIVE, &chan->state)) + mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); + else if (ioat->alloc_order > ioat_get_alloc_order()) { /* if the ring is idle, empty, and oversized try to step * down the size */ - spin_lock_bh(&chan->cleanup_lock); - spin_lock_bh(&ioat->prep_lock); - active = ioat2_ring_active(ioat); - if (active == 0 && ioat->alloc_order > ioat_get_alloc_order()) - reshape_ring(ioat, ioat->alloc_order-1); - spin_unlock_bh(&ioat->prep_lock); - spin_unlock_bh(&chan->cleanup_lock); + reshape_ring(ioat, ioat->alloc_order - 1); /* keep shrinking until we get back to our minimum * default size @@ -404,6 +365,60 @@ static void ioat3_timer_event(unsigned long data) if (ioat->alloc_order > ioat_get_alloc_order()) mod_timer(&chan->timer, jiffies + IDLE_TIMEOUT); } + +} + +void ioat3_timer_event(unsigned long data) +{ + struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); + struct ioat_chan_common *chan = &ioat->base; + dma_addr_t phys_complete; + u64 status; + + status = ioat_chansts(chan); + + /* when halted due to errors check for channel + * programming errors before advancing the completion state + */ + if (is_ioat_halted(status)) { + u32 chanerr; + + chanerr = readl(chan->reg_base + IOAT_CHANERR_OFFSET); + dev_err(to_dev(chan), "%s: Channel halted (%x)\n", + __func__, chanerr); + if (test_bit(IOAT_RUN, &chan->state)) + BUG_ON(is_ioat_bug(chanerr)); + else /* we never got off the ground */ + return; + } + + /* if we haven't made progress and we have already + * acknowledged a pending completion once, then be more + * forceful with a restart + */ + spin_lock_bh(&chan->cleanup_lock); + if (ioat_cleanup_preamble(chan, &phys_complete)) + __cleanup(ioat, phys_complete); + else if (test_bit(IOAT_COMPLETION_ACK, &chan->state)) { + spin_lock_bh(&ioat->prep_lock); + ioat3_restart_channel(ioat); + spin_unlock_bh(&ioat->prep_lock); + spin_unlock_bh(&chan->cleanup_lock); + return; + } else { + set_bit(IOAT_COMPLETION_ACK, &chan->state); + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + } + + + if (ioat2_ring_active(ioat)) + mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); + else { + spin_lock_bh(&ioat->prep_lock); + check_active(ioat); + spin_unlock_bh(&ioat->prep_lock); + } + spin_unlock_bh(&chan->cleanup_lock); } static enum dma_status From 6efc3fe0f4058d6764e5c58c7377037f9da24d22 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Tue, 12 Feb 2013 03:58:35 +0000 Subject: [PATCH 2392/4607] ARM: OMAP2+: fix some omap_device_build() calls that aren't compiled by default Commit c1d1cd597fc77af3086470f8627d77f52f7f8b6c ("ARM: OMAP2+: omap_device: remove obsolete pm_lats and early_device code") missed a few omap_device_build() calls that aren't included as part of the default OMAP2+ Kconfig, omap2plus_defconfig. Ideally, all devices that are present on the SoC should be created by default, and only the corresponding device driver should be configured or deconfigured in Kconfig. This allows drivers to be built as modules and loaded later, even if they weren't part of the original kernel build. Unfortunately, we're not quite there yet. Thanks to Tony Lindgren for reporting this, found during his randconfig tests. Signed-off-by: Paul Walmsley Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/am35xx-emac.c | 3 +-- arch/arm/mach-omap2/devices.c | 2 +- arch/arm/mach-omap2/sr_device.c | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-omap2/am35xx-emac.c b/arch/arm/mach-omap2/am35xx-emac.c index a00d39107a21..25b79a297365 100644 --- a/arch/arm/mach-omap2/am35xx-emac.c +++ b/arch/arm/mach-omap2/am35xx-emac.c @@ -62,8 +62,7 @@ static int __init omap_davinci_emac_dev_init(struct omap_hwmod *oh, { struct platform_device *pdev; - pdev = omap_device_build(oh->class->name, 0, oh, pdata, pdata_len, - false); + pdev = omap_device_build(oh->class->name, 0, oh, pdata, pdata_len); if (IS_ERR(pdev)) { WARN(1, "Can't build omap_device for %s:%s.\n", oh->class->name, oh->name); diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index d8a0cc3b9d2c..6ecc89adda87 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -382,7 +382,7 @@ static void __init omap_init_hdmi_audio(void) return; } - pdev = omap_device_build("omap-hdmi-audio-dai", -1, oh, NULL, 0, 0); + pdev = omap_device_build("omap-hdmi-audio-dai", -1, oh, NULL, 0); WARN(IS_ERR(pdev), "Can't build omap_device for omap-hdmi-audio-dai.\n"); diff --git a/arch/arm/mach-omap2/sr_device.c b/arch/arm/mach-omap2/sr_device.c index bb829e065400..d7bc33f15344 100644 --- a/arch/arm/mach-omap2/sr_device.c +++ b/arch/arm/mach-omap2/sr_device.c @@ -152,7 +152,7 @@ static int __init sr_dev_init(struct omap_hwmod *oh, void *user) sr_data->enable_on_init = sr_enable_on_init; - pdev = omap_device_build(name, i, oh, sr_data, sizeof(*sr_data), 0); + pdev = omap_device_build(name, i, oh, sr_data, sizeof(*sr_data)); if (IS_ERR(pdev)) pr_warning("%s: Could not build omap_device for %s: %s.\n\n", __func__, name, oh->name); From 01673a135eca8c6e984488ba9c29aa0b09601f77 Mon Sep 17 00:00:00 2001 From: Kim Phillips Date: Fri, 30 Nov 2012 17:35:00 -0600 Subject: [PATCH 2393/4607] powerpc/fsl: lbc: sparse fixes arch/powerpc/sysdev/fsl_lbc.c:77:36: warning: incorrect type in initializer (different base types) arch/powerpc/sysdev/fsl_lbc.c:77:36: expected restricted __be32 [usertype] br arch/powerpc/sysdev/fsl_lbc.c:77:36: got unsigned int arch/powerpc/sysdev/fsl_lbc.c:78:36: warning: incorrect type in initializer (different base types) arch/powerpc/sysdev/fsl_lbc.c:78:36: expected restricted __be32 [usertype] or arch/powerpc/sysdev/fsl_lbc.c:78:36: got unsigned int arch/powerpc/sysdev/fsl_lbc.c:80:21: warning: restricted __be32 degrades to integer arch/powerpc/sysdev/fsl_lbc.c:80:38: warning: restricted __be32 degrades to integer arch/powerpc/sysdev/fsl_lbc.c:111:12: warning: incorrect type in assignment (different base types) arch/powerpc/sysdev/fsl_lbc.c:111:12: expected restricted __be32 [usertype] br arch/powerpc/sysdev/fsl_lbc.c:111:12: got unsigned int arch/powerpc/sysdev/fsl_lbc.c:113:17: warning: restricted __be32 degrades to integer arch/powerpc/sysdev/fsl_lbc.c:127:17: warning: restricted __be32 degrades to integer Signed-off-by: Kim Phillips Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_lbc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/sysdev/fsl_lbc.c b/arch/powerpc/sysdev/fsl_lbc.c index 300be2d06a26..6bc5a546d49f 100644 --- a/arch/powerpc/sysdev/fsl_lbc.c +++ b/arch/powerpc/sysdev/fsl_lbc.c @@ -74,8 +74,8 @@ int fsl_lbc_find(phys_addr_t addr_base) lbc = fsl_lbc_ctrl_dev->regs; for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) { - __be32 br = in_be32(&lbc->bank[i].br); - __be32 or = in_be32(&lbc->bank[i].or); + u32 br = in_be32(&lbc->bank[i].br); + u32 or = in_be32(&lbc->bank[i].or); if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base)) return i; @@ -97,7 +97,7 @@ EXPORT_SYMBOL(fsl_lbc_find); int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm) { int bank; - __be32 br; + u32 br; struct fsl_lbc_regs __iomem *lbc; bank = fsl_lbc_find(addr_base); From 8443cc142d81b722740a98f42cd4deae826311d0 Mon Sep 17 00:00:00 2001 From: Kim Phillips Date: Fri, 30 Nov 2012 17:35:02 -0600 Subject: [PATCH 2394/4607] powerpc/fsl: fsl_soc: sparse fixes arch/powerpc/sysdev/fsl_soc.c:70:67: warning: incorrect type in argument 2 (different base types) arch/powerpc/sysdev/fsl_soc.c:70:67: expected restricted __be32 const [usertype] *addr arch/powerpc/sysdev/fsl_soc.c:70:67: got unsigned int const [usertype] * Signed-off-by: Kim Phillips Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_soc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index 97118dc3d285..228cf91b91c1 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -58,10 +58,10 @@ phys_addr_t get_immrbase(void) if (soc) { int size; u32 naddr; - const u32 *prop = of_get_property(soc, "#address-cells", &size); + const __be32 *prop = of_get_property(soc, "#address-cells", &size); if (prop && size == 4) - naddr = *prop; + naddr = be32_to_cpup(prop); else naddr = 2; From 8998a030098db908a2b20c864d7ec7246db516f8 Mon Sep 17 00:00:00 2001 From: Kim Phillips Date: Fri, 30 Nov 2012 17:35:01 -0600 Subject: [PATCH 2395/4607] powerpc/fsl: ifc: sparse fixes arch/powerpc/sysdev/fsl_ifc.c:66:38: warning: incorrect type in initializer (different base types) arch/powerpc/sysdev/fsl_ifc.c:66:38: expected restricted __be32 [usertype] cspr arch/powerpc/sysdev/fsl_ifc.c:66:38: got unsigned int arch/powerpc/sysdev/fsl_ifc.c:67:21: warning: restricted __be32 degrades to integer arch/powerpc/sysdev/fsl_ifc.c:67:39: warning: restricted __be32 degrades to integer Signed-off-by: Kim Phillips Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_ifc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/sysdev/fsl_ifc.c b/arch/powerpc/sysdev/fsl_ifc.c index 2a36fd6a9583..d7fc72239144 100644 --- a/arch/powerpc/sysdev/fsl_ifc.c +++ b/arch/powerpc/sysdev/fsl_ifc.c @@ -63,7 +63,7 @@ int fsl_ifc_find(phys_addr_t addr_base) return -ENODEV; for (i = 0; i < ARRAY_SIZE(fsl_ifc_ctrl_dev->regs->cspr_cs); i++) { - __be32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); + u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); if (cspr & CSPR_V && (cspr & CSPR_BA) == convert_ifc_address(addr_base)) return i; From 6cce76dc9ed9efc0a43fca233ddd205e7ded55eb Mon Sep 17 00:00:00 2001 From: Kim Phillips Date: Fri, 30 Nov 2012 17:34:59 -0600 Subject: [PATCH 2396/4607] powerpc/fsl: msi: sparse fixes arch/powerpc/sysdev/fsl_msi.c:31:1: warning: symbol 'msi_head' was not declared. Should it be static? arch/powerpc/sysdev/fsl_msi.c:138:40: warning: incorrect type in argument 1 (different base types) arch/powerpc/sysdev/fsl_msi.c:138:40: expected restricted __be64 const [usertype] *p arch/powerpc/sysdev/fsl_msi.c:138:40: got unsigned long long const [usertype] *[assigned] reg Signed-off-by: Kim Phillips Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_msi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index 6e53d97abd3f..178c99427b1c 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c @@ -28,7 +28,7 @@ #include "fsl_msi.h" #include "fsl_pci.h" -LIST_HEAD(msi_head); +static LIST_HEAD(msi_head); struct fsl_msi_feature { u32 fsl_pic_ip; @@ -130,7 +130,7 @@ static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, struct pci_controller *hose = pci_bus_to_host(pdev->bus); u64 address; /* Physical address of the MSIIR */ int len; - const u64 *reg; + const __be64 *reg; /* If the msi-address-64 property exists, then use it */ reg = of_get_property(hose->dn, "msi-address-64", &len); From 3a46741804a4226cd837d1246eed95d4161f7159 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Tue, 12 Feb 2013 10:56:54 -0700 Subject: [PATCH 2397/4607] eCryptfs: decrypt_pki_encrypted_session_key(): remove kfree() redundant null check smatch analysis: fs/ecryptfs/keystore.c:1206 decrypt_pki_encrypted_session_key() info: redundant null check on msg calling kfree() Cc: Dustin Kirkland Cc: ecryptfs@vger.kernel.org Signed-off-by: Tim Gardner Signed-off-by: Tyler Hicks --- fs/ecryptfs/keystore.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 6154cde3a052..5aceff202dc0 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -1202,8 +1202,7 @@ decrypt_pki_encrypted_session_key(struct ecryptfs_auth_tok *auth_tok, crypt_stat->key_size); } out: - if (msg) - kfree(msg); + kfree(msg); return rc; } From 1101d58669a92ed9c9f4c7281404fb1e067a1e28 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Tue, 12 Feb 2013 11:03:49 -0700 Subject: [PATCH 2398/4607] ecryptfs: ecryptfs_msg_ctx_alloc_to_free(): remove kfree() redundant null check smatch analysis: fs/ecryptfs/messaging.c:101 ecryptfs_msg_ctx_alloc_to_free() info: redundant null check on msg_ctx->msg calling kfree() Cc: Dustin Kirkland Cc: ecryptfs@vger.kernel.org Signed-off-by: Tim Gardner Signed-off-by: Tyler Hicks --- fs/ecryptfs/messaging.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c index 5fa2471796c2..d5c7297c5816 100644 --- a/fs/ecryptfs/messaging.c +++ b/fs/ecryptfs/messaging.c @@ -97,8 +97,7 @@ static void ecryptfs_msg_ctx_free_to_alloc(struct ecryptfs_msg_ctx *msg_ctx) void ecryptfs_msg_ctx_alloc_to_free(struct ecryptfs_msg_ctx *msg_ctx) { list_move(&(msg_ctx->node), &ecryptfs_msg_ctx_free_list); - if (msg_ctx->msg) - kfree(msg_ctx->msg); + kfree(msg_ctx->msg); msg_ctx->msg = NULL; msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_FREE; } From 3578baaed4613a9fc09bab9f79f6ce2ac682e8a3 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 12 Feb 2013 11:47:31 -0800 Subject: [PATCH 2399/4607] x86, mm: Redesign get_user with a __builtin_choose_expr hack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using a bitfield, use an odd little trick using typeof, __builtin_choose_expr, and sizeof. __builtin_choose_expr is explicitly defined to not convert its type (its argument is required to be a constant expression) so this should be well-defined. The code is still not 100% preturbation-free versus the baseline before 64-bit get_user(), but the differences seem to be very small, mostly related to padding and to gcc deciding when to spill registers. Cc: Jamie Lokier Cc: Ville Syrjälä Cc: Borislav Petkov Cc: Russell King Cc: Linus Torvalds Cc: H. J. Lu Link: http://lkml.kernel.org/r/511A8922.6050908@zytor.com Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/uaccess.h | 57 +++++++++------------------------- 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index a8d12653f304..d710a2555fd6 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -125,13 +125,12 @@ extern int __get_user_4(void); extern int __get_user_8(void); extern int __get_user_bad(void); -#define __get_user_x(size, ret, x, ptr) \ - asm volatile("call __get_user_" #size \ - : "=a" (ret), "=d" (x) \ - : "0" (ptr)) \ - -/* Careful: we have to cast the result to the type of the pointer - * for sign reasons */ +/* + * This is a type: either unsigned long, if the argument fits into + * that type, or otherwise unsigned long long. + */ +#define __inttype(x) \ +__typeof__(__builtin_choose_expr(sizeof(x) > sizeof(0UL), 0ULL, 0UL)) /** * get_user: - Get a simple variable from user space. @@ -149,48 +148,20 @@ extern int __get_user_bad(void); * * Returns zero on success, or -EFAULT on error. * On error, the variable @x is set to zero. + * + * Careful: we have to cast the result to the type of the pointer + * for sign reasons. */ -#ifdef CONFIG_X86_32 -#define __get_user_8(ret, x, ptr) \ -do { \ - register unsigned long long __xx asm("%edx"); \ - asm volatile("call __get_user_8" \ - : "=a" (ret), "=r" (__xx) \ - : "0" (ptr)); \ - (x) = __xx; \ -} while (0) - -#else -#define __get_user_8(__ret_gu, __val_gu, ptr) \ - __get_user_x(8, __ret_gu, __val_gu, ptr) -#endif - #define get_user(x, ptr) \ ({ \ int __ret_gu; \ - struct { \ - unsigned long long __val_n : 8*sizeof(*(ptr)); \ - } __val_gu; \ + register __inttype(*(ptr)) __val_gu asm("%edx"); \ __chk_user_ptr(ptr); \ might_fault(); \ - switch (sizeof(*(ptr))) { \ - case 1: \ - __get_user_x(1, __ret_gu, __val_gu.__val_n, ptr); \ - break; \ - case 2: \ - __get_user_x(2, __ret_gu, __val_gu.__val_n, ptr); \ - break; \ - case 4: \ - __get_user_x(4, __ret_gu, __val_gu.__val_n, ptr); \ - break; \ - case 8: \ - __get_user_8(__ret_gu, __val_gu.__val_n, ptr); \ - break; \ - default: \ - __get_user_x(X, __ret_gu, __val_gu.__val_n, ptr); \ - break; \ - } \ - (x) = (__typeof__(*(ptr)))__val_gu.__val_n; \ + asm volatile("call __get_user_%P3" \ + : "=a" (__ret_gu), "=r" (__val_gu) \ + : "0" (ptr), "i" (sizeof(*(ptr)))); \ + (x) = (__typeof__(*(ptr))) __val_gu; \ __ret_gu; \ }) From f431b634f24d099872e78acc356c7fd35913b36b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 12 Feb 2013 16:18:59 -0500 Subject: [PATCH 2400/4607] tracing/syscalls: Allow archs to ignore tracing compat syscalls The tracing of ia32 compat system calls has been a bit of a pain as they use different system call numbers than the 64bit equivalents. I wrote a simple 'lls' program that lists files. I compiled it as a i686 ELF binary and ran it under a x86_64 box. This is the result: echo 0 > /debug/tracing/tracing_on echo 1 > /debug/tracing/events/syscalls/enable echo 1 > /debug/tracing/tracing_on ; ./lls ; echo 0 > /debug/tracing/tracing_on grep lls /debug/tracing/trace [.. skipping calls before TS_COMPAT is set ...] lls-1127 [005] d... 936.409188: sys_recvfrom(fd: 0, ubuf: 4d560fc4, size: 0, flags: 8048034, addr: 8, addr_len: f7700420) lls-1127 [005] d... 936.409190: sys_recvfrom -> 0x8a77000 lls-1127 [005] d... 936.409211: sys_lgetxattr(pathname: 0, name: 1000, value: 3, size: 22) lls-1127 [005] d... 936.409215: sys_lgetxattr -> 0xf76ff000 lls-1127 [005] d... 936.409223: sys_dup2(oldfd: 4d55ae9b, newfd: 4) lls-1127 [005] d... 936.409228: sys_dup2 -> 0xfffffffffffffffe lls-1127 [005] d... 936.409236: sys_newfstat(fd: 4d55b085, statbuf: 80000) lls-1127 [005] d... 936.409242: sys_newfstat -> 0x3 lls-1127 [005] d... 936.409243: sys_removexattr(pathname: 3, name: ffcd0060) lls-1127 [005] d... 936.409244: sys_removexattr -> 0x0 lls-1127 [005] d... 936.409245: sys_lgetxattr(pathname: 0, name: 19614, value: 1, size: 2) lls-1127 [005] d... 936.409248: sys_lgetxattr -> 0xf76e5000 lls-1127 [005] d... 936.409248: sys_newlstat(filename: 3, statbuf: 19614) lls-1127 [005] d... 936.409249: sys_newlstat -> 0x0 lls-1127 [005] d... 936.409262: sys_newfstat(fd: f76fb588, statbuf: 80000) lls-1127 [005] d... 936.409279: sys_newfstat -> 0x3 lls-1127 [005] d... 936.409279: sys_close(fd: 3) lls-1127 [005] d... 936.421550: sys_close -> 0x200 lls-1127 [005] d... 936.421558: sys_removexattr(pathname: 3, name: ffcd00d0) lls-1127 [005] d... 936.421560: sys_removexattr -> 0x0 lls-1127 [005] d... 936.421569: sys_lgetxattr(pathname: 4d564000, name: 1b1abc, value: 5, size: 802) lls-1127 [005] d... 936.421574: sys_lgetxattr -> 0x4d564000 lls-1127 [005] d... 936.421575: sys_capget(header: 4d70f000, dataptr: 1000) lls-1127 [005] d... 936.421580: sys_capget -> 0x0 lls-1127 [005] d... 936.421580: sys_lgetxattr(pathname: 4d710000, name: 3000, value: 3, size: 812) lls-1127 [005] d... 936.421589: sys_lgetxattr -> 0x4d710000 lls-1127 [005] d... 936.426130: sys_lgetxattr(pathname: 4d713000, name: 2abc, value: 3, size: 32) lls-1127 [005] d... 936.426141: sys_lgetxattr -> 0x4d713000 lls-1127 [005] d... 936.426145: sys_newlstat(filename: 3, statbuf: f76ff3f0) lls-1127 [005] d... 936.426146: sys_newlstat -> 0x0 lls-1127 [005] d... 936.431748: sys_lgetxattr(pathname: 0, name: 1000, value: 3, size: 22) Obviously I'm not calling newfstat with a fd of 4d55b085. The calls are obviously incorrect, and confusing. Other efforts have been made to fix this: https://lkml.org/lkml/2012/3/26/367 But the real solution is to rewrite the syscall internals and come up with a fixed solution. One that doesn't require all the kluge that the current solution has. Thus for now, instead of outputting incorrect data, simply ignore them. With this patch the changes now have: #> grep lls /debug/tracing/trace #> Compat system calls simply are not traced. If users need compat syscalls, then they should just use the raw syscall tracepoints. For an architecture to make their compat syscalls ignored, it must define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS (done in asm/ftrace.h) and also define an arch_trace_is_compat_syscall() function that will return true if the current task should ignore tracing the syscall. I want to stress that this change does not affect actual syscalls in any way, shape or form. It is only used within the tracing system and doesn't interfere with the syscall logic at all. The changes are consolidated nicely into trace_syscalls.c and asm/ftrace.h. I had to make one small modification to asm/thread_info.h and that was to remove the include of asm/ftrace.h. As asm/ftrace.h required the current_thread_info() it was causing include hell. That include was added back in 2008 when the function graph tracer was added: commit caf4b323 "tracing, x86: add low level support for ftrace return tracing" It does not need to be included there. Link: http://lkml.kernel.org/r/1360703939.21867.99.camel@gandalf.local.home Acked-by: H. Peter Anvin Signed-off-by: Steven Rostedt --- arch/x86/include/asm/ftrace.h | 24 +++++++++++++++++ arch/x86/include/asm/thread_info.h | 1 - kernel/trace/trace_syscalls.c | 43 ++++++++++++++++++++++++++---- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h index 86cb51e1ca96..0525a8bdf65d 100644 --- a/arch/x86/include/asm/ftrace.h +++ b/arch/x86/include/asm/ftrace.h @@ -72,4 +72,28 @@ int ftrace_int3_handler(struct pt_regs *regs); #endif /* __ASSEMBLY__ */ #endif /* CONFIG_FUNCTION_TRACER */ + +#if !defined(__ASSEMBLY__) && !defined(COMPILE_OFFSETS) + +#if defined(CONFIG_FTRACE_SYSCALLS) && defined(CONFIG_IA32_EMULATION) +#include + +/* + * Because ia32 syscalls do not map to x86_64 syscall numbers + * this screws up the trace output when tracing a ia32 task. + * Instead of reporting bogus syscalls, just do not trace them. + * + * If the user realy wants these, then they should use the + * raw syscall tracepoints with filtering. + */ +#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS 1 +static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs) +{ + if (is_compat_task()) + return true; + return false; +} +#endif /* CONFIG_FTRACE_SYSCALLS && CONFIG_IA32_EMULATION */ +#endif /* !__ASSEMBLY__ && !COMPILE_OFFSETS */ + #endif /* _ASM_X86_FTRACE_H */ diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index 2d946e63ee82..2cd056e3ada3 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -20,7 +20,6 @@ struct task_struct; struct exec_domain; #include -#include #include struct thread_info { diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 5329e13e74a1..7a809e321058 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include /* for MODULE_NAME_LEN via KSYM_SYMBOL_LEN */ @@ -47,6 +48,38 @@ static inline bool arch_syscall_match_sym_name(const char *sym, const char *name } #endif +#ifdef ARCH_TRACE_IGNORE_COMPAT_SYSCALLS +/* + * Some architectures that allow for 32bit applications + * to run on a 64bit kernel, do not map the syscalls for + * the 32bit tasks the same as they do for 64bit tasks. + * + * *cough*x86*cough* + * + * In such a case, instead of reporting the wrong syscalls, + * simply ignore them. + * + * For an arch to ignore the compat syscalls it needs to + * define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS as well as + * define the function arch_trace_is_compat_syscall() to let + * the tracing system know that it should ignore it. + */ +static int +trace_get_syscall_nr(struct task_struct *task, struct pt_regs *regs) +{ + if (unlikely(arch_trace_is_compat_syscall(regs))) + return -1; + + return syscall_get_nr(task, regs); +} +#else +static inline int +trace_get_syscall_nr(struct task_struct *task, struct pt_regs *regs) +{ + return syscall_get_nr(task, regs); +} +#endif /* ARCH_TRACE_IGNORE_COMPAT_SYSCALLS */ + static __init struct syscall_metadata * find_syscall_meta(unsigned long syscall) { @@ -276,10 +309,10 @@ static void ftrace_syscall_enter(void *ignore, struct pt_regs *regs, long id) struct syscall_metadata *sys_data; struct ring_buffer_event *event; struct ring_buffer *buffer; - int size; int syscall_nr; + int size; - syscall_nr = syscall_get_nr(current, regs); + syscall_nr = trace_get_syscall_nr(current, regs); if (syscall_nr < 0) return; if (!test_bit(syscall_nr, enabled_enter_syscalls)) @@ -313,7 +346,7 @@ static void ftrace_syscall_exit(void *ignore, struct pt_regs *regs, long ret) struct ring_buffer *buffer; int syscall_nr; - syscall_nr = syscall_get_nr(current, regs); + syscall_nr = trace_get_syscall_nr(current, regs); if (syscall_nr < 0) return; if (!test_bit(syscall_nr, enabled_exit_syscalls)) @@ -502,7 +535,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) int rctx; int size; - syscall_nr = syscall_get_nr(current, regs); + syscall_nr = trace_get_syscall_nr(current, regs); if (syscall_nr < 0) return; if (!test_bit(syscall_nr, enabled_perf_enter_syscalls)) @@ -578,7 +611,7 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) int rctx; int size; - syscall_nr = syscall_get_nr(current, regs); + syscall_nr = trace_get_syscall_nr(current, regs); if (syscall_nr < 0) return; if (!test_bit(syscall_nr, enabled_perf_exit_syscalls)) From ff52c3b02b3f73178bfe0c219cd22abdcb0e46c3 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 12 Feb 2013 15:37:02 -0800 Subject: [PATCH 2401/4607] x86, doc: Clarify the use of asm("%edx") in uaccess.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Put in a comment that explains that the use of asm("%edx") in uaccess.h doesn't actually necessarily mean %edx alone. Cc: Jamie Lokier Cc: Ville Syrjälä Cc: Borislav Petkov Cc: Russell King Cc: Linus Torvalds Cc: H. J. Lu Link: http://lkml.kernel.org/r/511ACDFB.1050707@zytor.com Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/uaccess.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index d710a2555fd6..5ee26875baea 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -148,9 +148,16 @@ __typeof__(__builtin_choose_expr(sizeof(x) > sizeof(0UL), 0ULL, 0UL)) * * Returns zero on success, or -EFAULT on error. * On error, the variable @x is set to zero. - * + */ +/* * Careful: we have to cast the result to the type of the pointer * for sign reasons. + * + * The use of %edx as the register specifier is a bit of a + * simplification, as gcc only cares about it as the starting point + * and not size: for a 64-bit value it will use %ecx:%edx on 32 bits + * (%ecx being the next register in gcc's x86 register sequence), and + * %rdx on 64 bits. */ #define get_user(x, ptr) \ ({ \ From 6e105e0593055dc4aae21df7bca3868045754148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Wed, 13 Feb 2013 15:52:36 +1030 Subject: [PATCH 2402/4607] virtio: Add module driver macro for virtio drivers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helper macro for drivers that don't do anything special in module init/exit. Signed-off-by: Sjur Brændeland Signed-off-by: Rusty Russell --- include/linux/virtio.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/virtio.h b/include/linux/virtio.h index eb34cf774473..ff6714e6d0f5 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -126,4 +126,13 @@ static inline struct virtio_driver *drv_to_virtio(struct device_driver *drv) int register_virtio_driver(struct virtio_driver *drv); void unregister_virtio_driver(struct virtio_driver *drv); + +/* module_virtio_driver() - Helper macro for drivers that don't do + * anything special in module init/exit. This eliminates a lot of + * boilerplate. Each module may only use this macro once, and + * calling it replaces module_init() and module_exit() + */ +#define module_virtio_driver(__virtio_driver) \ + module_driver(__virtio_driver, register_virtio_driver, \ + unregister_virtio_driver) #endif /* _LINUX_VIRTIO_H */ From b2a17029c29bb6f7dec580feffa715b9fcf51e42 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 13 Feb 2013 16:59:28 +1030 Subject: [PATCH 2403/4607] virtio: use module_virtio_driver. Signed-off-by: Rusty Russell --- drivers/char/hw_random/virtio-rng.c | 13 +------------ drivers/net/virtio_net.c | 12 +----------- drivers/virtio/virtio_balloon.c | 13 +------------ 3 files changed, 3 insertions(+), 35 deletions(-) diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index b65c10395959..10fd71ccf587 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -154,18 +154,7 @@ static struct virtio_driver virtio_rng_driver = { #endif }; -static int __init init(void) -{ - return register_virtio_driver(&virtio_rng_driver); -} - -static void __exit fini(void) -{ - unregister_virtio_driver(&virtio_rng_driver); -} -module_init(init); -module_exit(fini); - +module_virtio_driver(virtio_rng_driver); MODULE_DEVICE_TABLE(virtio, id_table); MODULE_DESCRIPTION("Virtio random number driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index a6fcf15adc4f..80fb683d9d3a 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1645,17 +1645,7 @@ static struct virtio_driver virtio_net_driver = { #endif }; -static int __init init(void) -{ - return register_virtio_driver(&virtio_net_driver); -} - -static void __exit fini(void) -{ - unregister_virtio_driver(&virtio_net_driver); -} -module_init(init); -module_exit(fini); +module_virtio_driver(virtio_net_driver); MODULE_DEVICE_TABLE(virtio, id_table); MODULE_DESCRIPTION("Virtio network driver"); diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 797e1c79a104..8dab163c5ef0 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -560,18 +560,7 @@ static struct virtio_driver virtio_balloon_driver = { #endif }; -static int __init init(void) -{ - return register_virtio_driver(&virtio_balloon_driver); -} - -static void __exit fini(void) -{ - unregister_virtio_driver(&virtio_balloon_driver); -} -module_init(init); -module_exit(fini); - +module_virtio_driver(virtio_balloon_driver); MODULE_DEVICE_TABLE(virtio, id_table); MODULE_DESCRIPTION("Virtio balloon driver"); MODULE_LICENSE("GPL"); From 8078db789a92b10ff6e2d713231b5367e014c53b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Wed, 13 Feb 2013 20:57:21 +1030 Subject: [PATCH 2404/4607] virtio_console: Initialize guest_connected=true for rproc_serial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When rproc_serial is initialized, guest_connected should be set to true. We can then revert the extra checks introduced in commit: "virtio_console: Let unconnected rproc device receive data." Signed-off-by: Sjur Brændeland Signed-off-by: Rusty Russell --- drivers/char/virtio_console.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 2cfd5a0575ab..5afc8f614b1c 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1436,7 +1436,7 @@ static int add_port(struct ports_device *portdev, u32 id) * rproc_serial does not want the console port, only * the generic port implementation. */ - port->host_connected = true; + port->host_connected = port->guest_connected = true; else if (!use_multiport(port->portdev)) { /* * If we're not using multiport support, @@ -1757,11 +1757,8 @@ static void in_intr(struct virtqueue *vq) * tty is spawned) and the host sends out data to console * ports. For generic serial ports, the host won't * (shouldn't) send data till the guest is connected. - * However a remote device might send data before the port is - * connected. So don't remove data from a rproc_serial device. */ - - if (!port->guest_connected && !is_rproc_serial(port->portdev->vdev)) + if (!port->guest_connected) discard_port_data(port); spin_unlock_irqrestore(&port->inbuf_lock, flags); From db3985e5ca8f50fc17606855ba394783d11683a5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 13 Feb 2013 20:37:48 +1000 Subject: [PATCH 2405/4607] Revert "drm: Add EDID_QUIRK_FORCE_REDUCED_BLANKING for ASUS VW222S" This reverts commit 6f33814bd4d9cfe76033a31b1c0c76c960cd8e4b. The quirk cause a regression, and it looks like the original bug was simply a lack of FIFO bandwidth on the i915G of the reporter. Which should eventually be fixed as soon as we get around to implemented DSPARB FIFO reassignment on gen 3. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=52281 Cc: stable@vger.kernel.org Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a3a3b61059ff..51324256a657 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -87,9 +87,6 @@ static struct edid_quirk { int product_id; u32 quirks; } edid_quirk_list[] = { - /* ASUS VW222S */ - { "ACI", 0x22a2, EDID_QUIRK_FORCE_REDUCED_BLANKING }, - /* Acer AL1706 */ { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, /* Acer F51 */ From 91457df7733564015f846c501424610318a5b85f Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Tue, 12 Feb 2013 05:01:50 +0100 Subject: [PATCH 2406/4607] iommu/amd: Remove redundant NULL check before dma_ops_domain_free(). dma_ops_domain_free on a NULL pointer is a no-op, so the NULL check in amd_iommu_init_dma_ops() can be removed. Signed-off-by: Cyril Roelandt Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index c1c74e030a58..f6f4a62ddf09 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3187,8 +3187,7 @@ int __init amd_iommu_init_dma_ops(void) free_domains: for_each_iommu(iommu) { - if (iommu->default_dom) - dma_ops_domain_free(iommu->default_dom); + dma_ops_domain_free(iommu->default_dom); } return ret; From 9d9c6ae79c5e3452721c5eaebdd793edde9d93df Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 13 Feb 2013 13:16:25 +0200 Subject: [PATCH 2407/4607] mfd: omap-usb-host: Consolidate OMAP USB-HS platform data (part 2/3) Let's have a single platform data structure for the OMAP's High-Speed USB host subsystem instead of having 3 separate ones i.e. one for board data, one for USB Host (UHH) module and one for USB-TLL module. This makes the code much simpler and avoids creating multiple copies of platform data. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi For the ehci-omap.c part: Acked-by: Alan Stern --- drivers/mfd/omap-usb-host.c | 63 +++++++++++++++--------------------- drivers/mfd/omap-usb-tll.c | 11 +++---- drivers/usb/host/ehci-omap.c | 6 ++-- 3 files changed, 34 insertions(+), 46 deletions(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 05164d7f054b..d6e6b8ca854c 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -102,7 +102,7 @@ struct usbhs_hcd_omap { void __iomem *uhh_base; - struct usbhs_omap_platform_data platdata; + struct usbhs_omap_platform_data *pdata; u32 usbhs_rev; spinlock_t lock; @@ -184,19 +184,13 @@ err_end: static int omap_usbhs_alloc_children(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct usbhs_hcd_omap *omap; - struct ehci_hcd_omap_platform_data *ehci_data; - struct ohci_hcd_omap_platform_data *ohci_data; + struct usbhs_omap_platform_data *pdata = dev->platform_data; struct platform_device *ehci; struct platform_device *ohci; struct resource *res; struct resource resources[2]; int ret; - omap = platform_get_drvdata(pdev); - ehci_data = omap->platdata.ehci_data; - ohci_data = omap->platdata.ohci_data; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci"); if (!res) { dev_err(dev, "EHCI get resource IORESOURCE_MEM failed\n"); @@ -213,8 +207,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev) } resources[1] = *res; - ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, ehci_data, - sizeof(*ehci_data), dev); + ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, pdata, + sizeof(*pdata), dev); if (!ehci) { dev_err(dev, "omap_usbhs_alloc_child failed\n"); @@ -238,8 +232,8 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev) } resources[1] = *res; - ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, ohci_data, - sizeof(*ohci_data), dev); + ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, pdata, + sizeof(*pdata), dev); if (!ohci) { dev_err(dev, "omap_usbhs_alloc_child failed\n"); ret = -ENOMEM; @@ -278,7 +272,7 @@ static bool is_ohci_port(enum usbhs_omap_port_mode pmode) static int usbhs_runtime_resume(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = &omap->platdata; + struct usbhs_omap_platform_data *pdata = omap->pdata; unsigned long flags; dev_dbg(dev, "usbhs_runtime_resume\n"); @@ -310,7 +304,7 @@ static int usbhs_runtime_resume(struct device *dev) static int usbhs_runtime_suspend(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = &omap->platdata; + struct usbhs_omap_platform_data *pdata = omap->pdata; unsigned long flags; dev_dbg(dev, "usbhs_runtime_suspend\n"); @@ -342,19 +336,19 @@ static int usbhs_runtime_suspend(struct device *dev) static void omap_usbhs_init(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = &omap->platdata; + struct usbhs_omap_platform_data *pdata = omap->pdata; unsigned long flags; unsigned reg; dev_dbg(dev, "starting TI HSUSB Controller\n"); - if (pdata->ehci_data->phy_reset) { - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) - gpio_request_one(pdata->ehci_data->reset_gpio_port[0], + if (pdata->phy_reset) { + if (gpio_is_valid(pdata->reset_gpio_port[0])) + gpio_request_one(pdata->reset_gpio_port[0], GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) - gpio_request_one(pdata->ehci_data->reset_gpio_port[1], + if (gpio_is_valid(pdata->reset_gpio_port[1])) + gpio_request_one(pdata->reset_gpio_port[1], GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); /* Hold the PHY in RESET for enough time till DIR is high */ @@ -430,33 +424,33 @@ static void omap_usbhs_init(struct device *dev) spin_unlock_irqrestore(&omap->lock, flags); pm_runtime_put_sync(dev); - if (pdata->ehci_data->phy_reset) { + if (pdata->phy_reset) { /* Hold the PHY in RESET for enough time till * PHY is settled and ready */ udelay(10); - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) + if (gpio_is_valid(pdata->reset_gpio_port[0])) gpio_set_value_cansleep - (pdata->ehci_data->reset_gpio_port[0], 1); + (pdata->reset_gpio_port[0], 1); - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) + if (gpio_is_valid(pdata->reset_gpio_port[1])) gpio_set_value_cansleep - (pdata->ehci_data->reset_gpio_port[1], 1); + (pdata->reset_gpio_port[1], 1); } } static void omap_usbhs_deinit(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = &omap->platdata; + struct usbhs_omap_platform_data *pdata = omap->pdata; - if (pdata->ehci_data->phy_reset) { - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) - gpio_free(pdata->ehci_data->reset_gpio_port[0]); + if (pdata->phy_reset) { + if (gpio_is_valid(pdata->reset_gpio_port[0])) + gpio_free(pdata->reset_gpio_port[0]); - if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) - gpio_free(pdata->ehci_data->reset_gpio_port[1]); + if (gpio_is_valid(pdata->reset_gpio_port[1])) + gpio_free(pdata->reset_gpio_port[1]); } } @@ -490,15 +484,10 @@ static int usbhs_omap_probe(struct platform_device *pdev) spin_lock_init(&omap->lock); - for (i = 0; i < OMAP3_HS_USB_PORTS; i++) - omap->platdata.port_mode[i] = pdata->port_mode[i]; - - omap->platdata.ehci_data = pdata->ehci_data; - omap->platdata.ohci_data = pdata->ohci_data; + omap->pdata = pdata; pm_runtime_enable(dev); - for (i = 0; i < OMAP3_HS_USB_PORTS; i++) if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) || is_ehci_hsic_mode(i)) { diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index eb869153206d..e45948969907 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -98,7 +98,7 @@ struct usbtll_omap { struct clk *usbtll_p1_fck; struct clk *usbtll_p2_fck; - struct usbtll_omap_platform_data platdata; + struct usbhs_omap_platform_data *pdata; /* secure the register updates */ spinlock_t lock; }; @@ -203,7 +203,7 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode) static int usbtll_omap_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct usbtll_omap_platform_data *pdata = dev->platform_data; + struct usbhs_omap_platform_data *pdata = dev->platform_data; void __iomem *base; struct resource *res; struct usbtll_omap *tll; @@ -223,8 +223,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) spin_lock_init(&tll->lock); - for (i = 0; i < OMAP3_HS_USB_PORTS; i++) - tll->platdata.port_mode[i] = pdata->port_mode[i]; + tll->pdata = pdata; tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk"); if (IS_ERR(tll->usbtll_p1_fck)) { @@ -362,7 +361,7 @@ static int usbtll_omap_remove(struct platform_device *pdev) static int usbtll_runtime_resume(struct device *dev) { struct usbtll_omap *tll = dev_get_drvdata(dev); - struct usbtll_omap_platform_data *pdata = &tll->platdata; + struct usbhs_omap_platform_data *pdata = tll->pdata; unsigned long flags; dev_dbg(dev, "usbtll_runtime_resume\n"); @@ -388,7 +387,7 @@ static int usbtll_runtime_resume(struct device *dev) static int usbtll_runtime_suspend(struct device *dev) { struct usbtll_omap *tll = dev_get_drvdata(dev); - struct usbtll_omap_platform_data *pdata = &tll->platdata; + struct usbhs_omap_platform_data *pdata = tll->pdata; unsigned long flags; dev_dbg(dev, "usbtll_runtime_suspend\n"); diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index ac17a7c3a0cd..5d954d7b290d 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -107,7 +107,7 @@ static int omap_ehci_init(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int rc; - struct ehci_hcd_omap_platform_data *pdata; + struct usbhs_omap_platform_data *pdata; pdata = hcd->self.controller->platform_data; @@ -151,7 +151,7 @@ static int omap_ehci_init(struct usb_hcd *hcd) } static void disable_put_regulator( - struct ehci_hcd_omap_platform_data *pdata) + struct usbhs_omap_platform_data *pdata) { int i; @@ -176,7 +176,7 @@ static void disable_put_regulator( static int ehci_hcd_omap_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct ehci_hcd_omap_platform_data *pdata = dev->platform_data; + struct usbhs_omap_platform_data *pdata = dev->platform_data; struct resource *res; struct usb_hcd *hcd; void __iomem *regs; From 7e0ff1035cb7efe9c3621c6173a123603ca33857 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 12:28:44 +0200 Subject: [PATCH 2408/4607] mfd: omap-usb-tll: Fix channel count detection Fix channel count detecion for REV2. Also, don't give up if we don't recognize the IP Revision. We assume the default number of channels (i.e. 3) for unrecognized IPs. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index e45948969907..9658e1843334 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -98,6 +98,7 @@ struct usbtll_omap { struct clk *usbtll_p1_fck; struct clk *usbtll_p2_fck; + int nch; /* num. of channels */ struct usbhs_omap_platform_data *pdata; /* secure the register updates */ spinlock_t lock; @@ -210,7 +211,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) unsigned reg; unsigned long flags; int ret = 0; - int i, ver, count; + int i, ver; dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); @@ -262,16 +263,18 @@ static int usbtll_omap_probe(struct platform_device *pdev) ver = usbtll_read(base, OMAP_USBTLL_REVISION); switch (ver) { case OMAP_USBTLL_REV1: - case OMAP_USBTLL_REV2: - count = OMAP_TLL_CHANNEL_COUNT; + tll->nch = OMAP_TLL_CHANNEL_COUNT; break; + case OMAP_USBTLL_REV2: case OMAP_USBTLL_REV3: - count = OMAP_REV2_TLL_CHANNEL_COUNT; + tll->nch = OMAP_REV2_TLL_CHANNEL_COUNT; break; default: - dev_err(dev, "TLL version failed\n"); - ret = -ENODEV; - goto err_ioremap; + tll->nch = OMAP_TLL_CHANNEL_COUNT; + dev_dbg(dev, + "USB TLL Rev : 0x%x not recognized, assuming %d channels\n", + ver, tll->nch); + break; } if (is_ehci_tll_mode(pdata->port_mode[0]) || @@ -291,7 +294,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) usbtll_write(base, OMAP_TLL_SHARED_CONF, reg); /* Enable channels now */ - for (i = 0; i < count; i++) { + for (i = 0; i < tll->nch; i++) { reg = usbtll_read(base, OMAP_TLL_CHANNEL_CONF(i)); if (is_ohci_port(pdata->port_mode[i])) { @@ -319,7 +322,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) } } -err_ioremap: spin_unlock_irqrestore(&tll->lock, flags); iounmap(base); pm_runtime_put_sync(dev); From 1a7a8d70cddcf4f6847e42b414da1c3113675873 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 12:56:37 +0200 Subject: [PATCH 2409/4607] mfd: omap-usb-tll: Use devm_kzalloc/ioremap and clean up error path Use devm_ variants of kzalloc() and ioremap(). Simplify the error path. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 9658e1843334..9a19cc722c76 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -215,11 +215,10 @@ static int usbtll_omap_probe(struct platform_device *pdev) dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); - tll = kzalloc(sizeof(struct usbtll_omap), GFP_KERNEL); + tll = devm_kzalloc(dev, sizeof(struct usbtll_omap), GFP_KERNEL); if (!tll) { dev_err(dev, "Memory allocation failed\n"); - ret = -ENOMEM; - goto end; + return -ENOMEM; } spin_lock_init(&tll->lock); @@ -230,28 +229,22 @@ static int usbtll_omap_probe(struct platform_device *pdev) if (IS_ERR(tll->usbtll_p1_fck)) { ret = PTR_ERR(tll->usbtll_p1_fck); dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret); - goto err_tll; + return ret; } tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk"); if (IS_ERR(tll->usbtll_p2_fck)) { ret = PTR_ERR(tll->usbtll_p2_fck); dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret); - goto err_usbtll_p1_fck; + goto err_p2_fck; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "usb tll get resource failed\n"); - ret = -ENODEV; - goto err_usbtll_p2_fck; - } - - base = ioremap(res->start, resource_size(res)); + base = devm_request_and_ioremap(dev, res); if (!base) { - dev_err(dev, "TLL ioremap failed\n"); - ret = -ENOMEM; - goto err_usbtll_p2_fck; + ret = -EADDRNOTAVAIL; + dev_err(dev, "Resource request/ioremap failed:%d\n", ret); + goto err_res; } platform_set_drvdata(pdev, tll); @@ -323,23 +316,17 @@ static int usbtll_omap_probe(struct platform_device *pdev) } spin_unlock_irqrestore(&tll->lock, flags); - iounmap(base); pm_runtime_put_sync(dev); tll_pdev = pdev; - if (!ret) - goto end; - pm_runtime_disable(dev); -err_usbtll_p2_fck: + return 0; + +err_res: clk_put(tll->usbtll_p2_fck); -err_usbtll_p1_fck: +err_p2_fck: clk_put(tll->usbtll_p1_fck); -err_tll: - kfree(tll); - -end: return ret; } @@ -356,7 +343,6 @@ static int usbtll_omap_remove(struct platform_device *pdev) clk_put(tll->usbtll_p2_fck); clk_put(tll->usbtll_p1_fck); pm_runtime_disable(&pdev->dev); - kfree(tll); return 0; } From 0bde3e9fee74b668857b1664a70998d02807fb5b Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 13:07:09 +0200 Subject: [PATCH 2410/4607] mfd: omap-usb-tll: Clean up clock handling Every channel has a functional clock that is similarly named. It makes sense to use a for loop to manage these clocks as OMAPs can come with up to 3 channels. Dynamically allocate and get channel clocks depending on the number of clocks avaiable on the platform. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi Acked-by: Russell King --- drivers/mfd/omap-usb-tll.c | 87 +++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 9a19cc722c76..bf7355e1126d 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -96,10 +96,9 @@ #define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL) struct usbtll_omap { - struct clk *usbtll_p1_fck; - struct clk *usbtll_p2_fck; int nch; /* num. of channels */ struct usbhs_omap_platform_data *pdata; + struct clk **ch_clk; /* secure the register updates */ spinlock_t lock; }; @@ -225,26 +224,12 @@ static int usbtll_omap_probe(struct platform_device *pdev) tll->pdata = pdata; - tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk"); - if (IS_ERR(tll->usbtll_p1_fck)) { - ret = PTR_ERR(tll->usbtll_p1_fck); - dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret); - return ret; - } - - tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk"); - if (IS_ERR(tll->usbtll_p2_fck)) { - ret = PTR_ERR(tll->usbtll_p2_fck); - dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret); - goto err_p2_fck; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_request_and_ioremap(dev, res); if (!base) { ret = -EADDRNOTAVAIL; dev_err(dev, "Resource request/ioremap failed:%d\n", ret); - goto err_res; + return ret; } platform_set_drvdata(pdev, tll); @@ -270,6 +255,29 @@ static int usbtll_omap_probe(struct platform_device *pdev) break; } + spin_unlock_irqrestore(&tll->lock, flags); + + tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]), + GFP_KERNEL); + if (!tll->ch_clk) { + ret = -ENOMEM; + dev_err(dev, "Couldn't allocate memory for channel clocks\n"); + goto err_clk_alloc; + } + + for (i = 0; i < tll->nch; i++) { + char clkname[] = "usb_tll_hs_usb_chx_clk"; + + snprintf(clkname, sizeof(clkname), + "usb_tll_hs_usb_ch%d_clk", i); + tll->ch_clk[i] = clk_get(dev, clkname); + + if (IS_ERR(tll->ch_clk[i])) + dev_dbg(dev, "can't get clock : %s\n", clkname); + } + + spin_lock_irqsave(&tll->lock, flags); + if (is_ehci_tll_mode(pdata->port_mode[0]) || is_ehci_tll_mode(pdata->port_mode[1]) || is_ehci_tll_mode(pdata->port_mode[2]) || @@ -321,11 +329,9 @@ static int usbtll_omap_probe(struct platform_device *pdev) return 0; -err_res: - clk_put(tll->usbtll_p2_fck); - -err_p2_fck: - clk_put(tll->usbtll_p1_fck); +err_clk_alloc: + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); return ret; } @@ -339,9 +345,12 @@ err_p2_fck: static int usbtll_omap_remove(struct platform_device *pdev) { struct usbtll_omap *tll = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < tll->nch; i++) + if (!IS_ERR(tll->ch_clk[i])) + clk_put(tll->ch_clk[i]); - clk_put(tll->usbtll_p2_fck); - clk_put(tll->usbtll_p1_fck); pm_runtime_disable(&pdev->dev); return 0; } @@ -351,6 +360,7 @@ static int usbtll_runtime_resume(struct device *dev) struct usbtll_omap *tll = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = tll->pdata; unsigned long flags; + int i; dev_dbg(dev, "usbtll_runtime_resume\n"); @@ -361,11 +371,20 @@ static int usbtll_runtime_resume(struct device *dev) spin_lock_irqsave(&tll->lock, flags); - if (is_ehci_tll_mode(pdata->port_mode[0])) - clk_enable(tll->usbtll_p1_fck); + for (i = 0; i < tll->nch; i++) { + if (is_ehci_tll_mode(pdata->port_mode[i])) { + int r; - if (is_ehci_tll_mode(pdata->port_mode[1])) - clk_enable(tll->usbtll_p2_fck); + if (IS_ERR(tll->ch_clk[i])) + continue; + + r = clk_enable(tll->ch_clk[i]); + if (r) { + dev_err(dev, + "Error enabling ch %d clock: %d\n", i, r); + } + } + } spin_unlock_irqrestore(&tll->lock, flags); @@ -377,6 +396,7 @@ static int usbtll_runtime_suspend(struct device *dev) struct usbtll_omap *tll = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = tll->pdata; unsigned long flags; + int i; dev_dbg(dev, "usbtll_runtime_suspend\n"); @@ -387,11 +407,12 @@ static int usbtll_runtime_suspend(struct device *dev) spin_lock_irqsave(&tll->lock, flags); - if (is_ehci_tll_mode(pdata->port_mode[0])) - clk_disable(tll->usbtll_p1_fck); - - if (is_ehci_tll_mode(pdata->port_mode[1])) - clk_disable(tll->usbtll_p2_fck); + for (i = 0; i < tll->nch; i++) { + if (is_ehci_tll_mode(pdata->port_mode[i])) { + if (!IS_ERR(tll->ch_clk[i])) + clk_disable(tll->ch_clk[i]); + } + } spin_unlock_irqrestore(&tll->lock, flags); From 32a51f2a536fd045c472779c13ac09e9e80ac3cc Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 14:40:45 +0200 Subject: [PATCH 2411/4607] mfd: omap-usb-tll: introduce and use mode_needs_tll() This is a handy macro to check if the port requires the USB TLL module or not. Use it to Enable the TLL module and manage the clocks. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index bf7355e1126d..af67b96ded0f 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -95,6 +95,10 @@ #define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL) +/* only PHY and UNUSED modes don't need TLL */ +#define omap_usb_mode_needs_tll(x) ((x) != OMAP_USBHS_PORT_MODE_UNUSED &&\ + (x) != OMAP_EHCI_PORT_MODE_PHY) + struct usbtll_omap { int nch; /* num. of channels */ struct usbhs_omap_platform_data *pdata; @@ -211,6 +215,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) unsigned long flags; int ret = 0; int i, ver; + bool needs_tll; dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); @@ -278,12 +283,11 @@ static int usbtll_omap_probe(struct platform_device *pdev) spin_lock_irqsave(&tll->lock, flags); - if (is_ehci_tll_mode(pdata->port_mode[0]) || - is_ehci_tll_mode(pdata->port_mode[1]) || - is_ehci_tll_mode(pdata->port_mode[2]) || - is_ohci_port(pdata->port_mode[0]) || - is_ohci_port(pdata->port_mode[1]) || - is_ohci_port(pdata->port_mode[2])) { + needs_tll = false; + for (i = 0; i < tll->nch; i++) + needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]); + + if (needs_tll) { /* Program Common TLL register */ reg = usbtll_read(base, OMAP_TLL_SHARED_CONF); @@ -372,7 +376,7 @@ static int usbtll_runtime_resume(struct device *dev) spin_lock_irqsave(&tll->lock, flags); for (i = 0; i < tll->nch; i++) { - if (is_ehci_tll_mode(pdata->port_mode[i])) { + if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { int r; if (IS_ERR(tll->ch_clk[i])) @@ -408,7 +412,7 @@ static int usbtll_runtime_suspend(struct device *dev) spin_lock_irqsave(&tll->lock, flags); for (i = 0; i < tll->nch; i++) { - if (is_ehci_tll_mode(pdata->port_mode[i])) { + if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { if (!IS_ERR(tll->ch_clk[i])) clk_disable(tll->ch_clk[i]); } From 8bdbd1549479840293e46bf0f84d7174e77b7b5e Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 15:17:32 +0200 Subject: [PATCH 2412/4607] mfd: omap-usb-tll: Check for missing platform data in probe No need to check for missing platform data in runtime_suspend/resume as it makes more sense to do it in the probe function. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index af67b96ded0f..3dbbfea515ad 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -225,6 +225,11 @@ static int usbtll_omap_probe(struct platform_device *pdev) return -ENOMEM; } + if (!pdata) { + dev_err(dev, "Platform data missing\n"); + return -ENODEV; + } + spin_lock_init(&tll->lock); tll->pdata = pdata; @@ -368,11 +373,6 @@ static int usbtll_runtime_resume(struct device *dev) dev_dbg(dev, "usbtll_runtime_resume\n"); - if (!pdata) { - dev_dbg(dev, "missing platform_data\n"); - return -ENODEV; - } - spin_lock_irqsave(&tll->lock, flags); for (i = 0; i < tll->nch; i++) { @@ -404,11 +404,6 @@ static int usbtll_runtime_suspend(struct device *dev) dev_dbg(dev, "usbtll_runtime_suspend\n"); - if (!pdata) { - dev_dbg(dev, "missing platform_data\n"); - return -ENODEV; - } - spin_lock_irqsave(&tll->lock, flags); for (i = 0; i < tll->nch; i++) { From 7ed8619141198191d09f63fa6f172af361ad280d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 15:26:28 +0200 Subject: [PATCH 2413/4607] mfd: omap-usb-tll: Fix error message omap_enable/disable_tll() can fail if TLL device is not initialized. It could be due to multiple reasons and not only due to missing platform data. Also make local variables static and use 'struct device *' instead of 'struct platform_device *' for global reference. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 3dbbfea515ad..eccc65e97202 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -109,8 +109,8 @@ struct usbtll_omap { /*-------------------------------------------------------------------------*/ -const char usbtll_driver_name[] = USBTLL_DRIVER_NAME; -struct platform_device *tll_pdev; +static const char usbtll_driver_name[] = USBTLL_DRIVER_NAME; +static struct device *tll_dev; /*-------------------------------------------------------------------------*/ @@ -334,7 +334,8 @@ static int usbtll_omap_probe(struct platform_device *pdev) spin_unlock_irqrestore(&tll->lock, flags); pm_runtime_put_sync(dev); - tll_pdev = pdev; + /* only after this can omap_tll_enable/disable work */ + tll_dev = dev; return 0; @@ -356,6 +357,8 @@ static int usbtll_omap_remove(struct platform_device *pdev) struct usbtll_omap *tll = platform_get_drvdata(pdev); int i; + tll_dev = NULL; + for (i = 0; i < tll->nch; i++) if (!IS_ERR(tll->ch_clk[i])) clk_put(tll->ch_clk[i]); @@ -436,21 +439,21 @@ static struct platform_driver usbtll_omap_driver = { int omap_tll_enable(void) { - if (!tll_pdev) { - pr_err("missing omap usbhs tll platform_data\n"); + if (!tll_dev) { + pr_err("%s: OMAP USB TLL not initialized\n", __func__); return -ENODEV; } - return pm_runtime_get_sync(&tll_pdev->dev); + return pm_runtime_get_sync(tll_dev); } EXPORT_SYMBOL_GPL(omap_tll_enable); int omap_tll_disable(void) { - if (!tll_pdev) { - pr_err("missing omap usbhs tll platform_data\n"); + if (!tll_dev) { + pr_err("%s: OMAP USB TLL not initialized\n", __func__); return -ENODEV; } - return pm_runtime_put_sync(&tll_pdev->dev); + return pm_runtime_put_sync(tll_dev); } EXPORT_SYMBOL_GPL(omap_tll_disable); From 6675144668883a506f0c11db668b582bd5a3161d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 16:56:26 +0200 Subject: [PATCH 2414/4607] mfd: omap-usb-tll: serialize access to TLL device Get rid of the unnecessary spin_lock_irqsave/restore() as there is no interrupt handler for this driver. Instead we serialize access to tll_dev using a global spinlock. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 53 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index eccc65e97202..55c85c78ba44 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -103,14 +103,13 @@ struct usbtll_omap { int nch; /* num. of channels */ struct usbhs_omap_platform_data *pdata; struct clk **ch_clk; - /* secure the register updates */ - spinlock_t lock; }; /*-------------------------------------------------------------------------*/ static const char usbtll_driver_name[] = USBTLL_DRIVER_NAME; static struct device *tll_dev; +static DEFINE_SPINLOCK(tll_lock); /* serialize access to tll_dev */ /*-------------------------------------------------------------------------*/ @@ -212,7 +211,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) struct resource *res; struct usbtll_omap *tll; unsigned reg; - unsigned long flags; int ret = 0; int i, ver; bool needs_tll; @@ -230,8 +228,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) return -ENODEV; } - spin_lock_init(&tll->lock); - tll->pdata = pdata; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -246,8 +242,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_sync(dev); - spin_lock_irqsave(&tll->lock, flags); - ver = usbtll_read(base, OMAP_USBTLL_REVISION); switch (ver) { case OMAP_USBTLL_REV1: @@ -265,8 +259,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) break; } - spin_unlock_irqrestore(&tll->lock, flags); - tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk * [tll->nch]), GFP_KERNEL); if (!tll->ch_clk) { @@ -286,8 +278,6 @@ static int usbtll_omap_probe(struct platform_device *pdev) dev_dbg(dev, "can't get clock : %s\n", clkname); } - spin_lock_irqsave(&tll->lock, flags); - needs_tll = false; for (i = 0; i < tll->nch; i++) needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]); @@ -332,10 +322,11 @@ static int usbtll_omap_probe(struct platform_device *pdev) } } - spin_unlock_irqrestore(&tll->lock, flags); pm_runtime_put_sync(dev); /* only after this can omap_tll_enable/disable work */ + spin_lock(&tll_lock); tll_dev = dev; + spin_unlock(&tll_lock); return 0; @@ -357,7 +348,9 @@ static int usbtll_omap_remove(struct platform_device *pdev) struct usbtll_omap *tll = platform_get_drvdata(pdev); int i; + spin_lock(&tll_lock); tll_dev = NULL; + spin_unlock(&tll_lock); for (i = 0; i < tll->nch; i++) if (!IS_ERR(tll->ch_clk[i])) @@ -371,13 +364,10 @@ static int usbtll_runtime_resume(struct device *dev) { struct usbtll_omap *tll = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = tll->pdata; - unsigned long flags; int i; dev_dbg(dev, "usbtll_runtime_resume\n"); - spin_lock_irqsave(&tll->lock, flags); - for (i = 0; i < tll->nch; i++) { if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { int r; @@ -393,8 +383,6 @@ static int usbtll_runtime_resume(struct device *dev) } } - spin_unlock_irqrestore(&tll->lock, flags); - return 0; } @@ -402,13 +390,10 @@ static int usbtll_runtime_suspend(struct device *dev) { struct usbtll_omap *tll = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = tll->pdata; - unsigned long flags; int i; dev_dbg(dev, "usbtll_runtime_suspend\n"); - spin_lock_irqsave(&tll->lock, flags); - for (i = 0; i < tll->nch; i++) { if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { if (!IS_ERR(tll->ch_clk[i])) @@ -416,8 +401,6 @@ static int usbtll_runtime_suspend(struct device *dev) } } - spin_unlock_irqrestore(&tll->lock, flags); - return 0; } @@ -439,21 +422,39 @@ static struct platform_driver usbtll_omap_driver = { int omap_tll_enable(void) { + int ret; + + spin_lock(&tll_lock); + if (!tll_dev) { pr_err("%s: OMAP USB TLL not initialized\n", __func__); - return -ENODEV; + ret = -ENODEV; + } else { + ret = pm_runtime_get_sync(tll_dev); } - return pm_runtime_get_sync(tll_dev); + + spin_unlock(&tll_lock); + + return ret; } EXPORT_SYMBOL_GPL(omap_tll_enable); int omap_tll_disable(void) { + int ret; + + spin_lock(&tll_lock); + if (!tll_dev) { pr_err("%s: OMAP USB TLL not initialized\n", __func__); - return -ENODEV; + ret = -ENODEV; + } else { + ret = pm_runtime_put_sync(tll_dev); } - return pm_runtime_put_sync(tll_dev); + + spin_unlock(&tll_lock); + + return ret; } EXPORT_SYMBOL_GPL(omap_tll_disable); From 300c2f8ff4745f65d99cf105e9afef0f01b70d09 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 16:10:41 +0200 Subject: [PATCH 2415/4607] mfd: omap-usb-tll: Add OMAP5 revision and HSIC support The TLL module on OMAP5 has 3 channels. HSIC mode requires the TLL channel to be in Transparent UTMI mode. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-tll.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 55c85c78ba44..0aef1a768880 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -54,10 +54,13 @@ #define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num) #define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT 24 +#define OMAP_TLL_CHANNEL_CONF_DRVVBUS (1 << 16) +#define OMAP_TLL_CHANNEL_CONF_CHRGVBUS (1 << 15) #define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11) #define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10) #define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9) #define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8) +#define OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI (2 << 1) #define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS (1 << 1) #define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0) @@ -92,6 +95,7 @@ #define OMAP_USBTLL_REV1 0x00000015 /* OMAP3 */ #define OMAP_USBTLL_REV2 0x00000018 /* OMAP 3630 */ #define OMAP_USBTLL_REV3 0x00000004 /* OMAP4 */ +#define OMAP_USBTLL_REV4 0x00000006 /* OMAP5 */ #define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL) @@ -245,6 +249,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) ver = usbtll_read(base, OMAP_USBTLL_REVISION); switch (ver) { case OMAP_USBTLL_REV1: + case OMAP_USBTLL_REV4: tll->nch = OMAP_TLL_CHANNEL_COUNT; break; case OMAP_USBTLL_REV2: @@ -310,6 +315,15 @@ static int usbtll_omap_probe(struct platform_device *pdev) reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); + } else if (pdata->port_mode[i] == + OMAP_EHCI_PORT_MODE_HSIC) { + /* + * HSIC Mode requires UTMI port configurations + */ + reg |= OMAP_TLL_CHANNEL_CONF_DRVVBUS + | OMAP_TLL_CHANNEL_CONF_CHRGVBUS + | OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI + | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF; } else { continue; } From a1f0d7a1f8cede5585c13c2f1c79e86a05d425ec Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 18:41:56 +0200 Subject: [PATCH 2416/4607] mfd: omap_usb_host: Avoid missing platform data checks in suspend/resume Get rid of the unnecessary missing platform data checks in runtime_suspend/resume. We are already checking for missing platform data in probe. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index d6e6b8ca854c..061366dce8f6 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -277,11 +277,6 @@ static int usbhs_runtime_resume(struct device *dev) dev_dbg(dev, "usbhs_runtime_resume\n"); - if (!pdata) { - dev_dbg(dev, "missing platform_data\n"); - return -ENODEV; - } - omap_tll_enable(); spin_lock_irqsave(&omap->lock, flags); @@ -309,11 +304,6 @@ static int usbhs_runtime_suspend(struct device *dev) dev_dbg(dev, "usbhs_runtime_suspend\n"); - if (!pdata) { - dev_dbg(dev, "missing platform_data\n"); - return -ENODEV; - } - spin_lock_irqsave(&omap->lock, flags); if (is_ehci_tll_mode(pdata->port_mode[0])) From 27d4f2c654e39bed13374af9537633de414057b6 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 26 Nov 2012 17:59:22 +0200 Subject: [PATCH 2417/4607] mfd: omap-usb-host: Use devm_kzalloc() and devm_request_and_ioremap() Use devm_ variants of kzalloc and ioremap. Also clean up error path. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 38 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 061366dce8f6..310aaa9731ee 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -461,15 +461,20 @@ static int usbhs_omap_probe(struct platform_device *pdev) if (!pdata) { dev_err(dev, "Missing platform data\n"); - ret = -ENOMEM; - goto end_probe; + return -ENODEV; } - omap = kzalloc(sizeof(*omap), GFP_KERNEL); + omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); if (!omap) { dev_err(dev, "Memory allocation failed\n"); - ret = -ENOMEM; - goto end_probe; + return -ENOMEM; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh"); + omap->uhh_base = devm_request_and_ioremap(dev, res); + if (!omap->uhh_base) { + dev_err(dev, "Resource request/ioremap failed\n"); + return -EADDRNOTAVAIL; } spin_lock_init(&omap->lock); @@ -568,20 +573,6 @@ static int usbhs_omap_probe(struct platform_device *pdev) "failed error:%d\n", ret); } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh"); - if (!res) { - dev_err(dev, "UHH EHCI get resource failed\n"); - ret = -ENODEV; - goto err_init_60m_fclk; - } - - omap->uhh_base = ioremap(res->start, resource_size(res)); - if (!omap->uhh_base) { - dev_err(dev, "UHH ioremap failed\n"); - ret = -ENOMEM; - goto err_init_60m_fclk; - } - platform_set_drvdata(pdev, omap); omap_usbhs_init(dev); @@ -591,13 +582,10 @@ static int usbhs_omap_probe(struct platform_device *pdev) goto err_alloc; } - goto end_probe; + return 0; err_alloc: omap_usbhs_deinit(&pdev->dev); - iounmap(omap->uhh_base); - -err_init_60m_fclk: clk_put(omap->init_60m_fclk); err_usbhost_p2_fck: @@ -621,9 +609,7 @@ err_utmi_p1_fck: err_end: clk_put(omap->ehci_logic_fck); pm_runtime_disable(dev); - kfree(omap); -end_probe: return ret; } @@ -638,7 +624,6 @@ static int usbhs_omap_remove(struct platform_device *pdev) struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); omap_usbhs_deinit(&pdev->dev); - iounmap(omap->uhh_base); clk_put(omap->init_60m_fclk); clk_put(omap->usbhost_p2_fck); clk_put(omap->usbhost_p1_fck); @@ -648,7 +633,6 @@ static int usbhs_omap_remove(struct platform_device *pdev) clk_put(omap->utmi_p1_fck); clk_put(omap->ehci_logic_fck); pm_runtime_disable(&pdev->dev); - kfree(omap); return 0; } From d7eaf866104757d66ccb6627dccf7fb9d07aae7e Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 18:04:26 +0200 Subject: [PATCH 2418/4607] mfd: omap-usb-host: know about number of ports from revision register The revision register should tell us how many ports are present. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 310aaa9731ee..26319ca72e38 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -91,6 +91,8 @@ struct usbhs_hcd_omap { + int nports; + struct clk *xclk60mhsp1_ck; struct clk *xclk60mhsp2_ck; struct clk *utmi_p1_fck; @@ -347,8 +349,6 @@ static void omap_usbhs_init(struct device *dev) pm_runtime_get_sync(dev); spin_lock_irqsave(&omap->lock, flags); - omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); - dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev); reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); /* setup ULPI bypass and burst configurations */ @@ -483,7 +483,32 @@ static int usbhs_omap_probe(struct platform_device *pdev) pm_runtime_enable(dev); - for (i = 0; i < OMAP3_HS_USB_PORTS; i++) + platform_set_drvdata(pdev, omap); + pm_runtime_get_sync(dev); + + omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); + + /* we need to call runtime suspend before we update omap->nports + * to prevent unbalanced clk_disable() + */ + pm_runtime_put_sync(dev); + + switch (omap->usbhs_rev) { + case OMAP_USBHS_REV1: + omap->nports = 3; + break; + case OMAP_USBHS_REV2: + omap->nports = 2; + break; + default: + omap->nports = OMAP3_HS_USB_PORTS; + dev_dbg(dev, + "USB HOST Rev : 0x%d not recognized, assuming %d ports\n", + omap->usbhs_rev, omap->nports); + break; + } + + for (i = 0; i < omap->nports; i++) if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) || is_ehci_hsic_mode(i)) { omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck"); @@ -573,8 +598,6 @@ static int usbhs_omap_probe(struct platform_device *pdev) "failed error:%d\n", ret); } - platform_set_drvdata(pdev, omap); - omap_usbhs_init(dev); ret = omap_usbhs_alloc_children(pdev); if (ret) { From ccac71a7f063ad31eb99fac37e95b70ff57f1354 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 19:18:08 +0200 Subject: [PATCH 2419/4607] mfd: omap-usb-host: override number of ports from platform data Both OMAP4 and 5 exhibit the same revision ID in the REVISION register but they have different number of ports i.e. 2 and 3 respectively. So we can't rely on REVISION register for number of ports on OMAP5 and depend on platform data (or device tree) instead. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 34 ++++++++++++++++---------- include/linux/platform_data/usb-omap.h | 1 + 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 26319ca72e38..779588be8ab2 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -493,19 +493,27 @@ static int usbhs_omap_probe(struct platform_device *pdev) */ pm_runtime_put_sync(dev); - switch (omap->usbhs_rev) { - case OMAP_USBHS_REV1: - omap->nports = 3; - break; - case OMAP_USBHS_REV2: - omap->nports = 2; - break; - default: - omap->nports = OMAP3_HS_USB_PORTS; - dev_dbg(dev, - "USB HOST Rev : 0x%d not recognized, assuming %d ports\n", - omap->usbhs_rev, omap->nports); - break; + /* + * If platform data contains nports then use that + * else make out number of ports from USBHS revision + */ + if (pdata->nports) { + omap->nports = pdata->nports; + } else { + switch (omap->usbhs_rev) { + case OMAP_USBHS_REV1: + omap->nports = 3; + break; + case OMAP_USBHS_REV2: + omap->nports = 2; + break; + default: + omap->nports = OMAP3_HS_USB_PORTS; + dev_dbg(dev, + "USB HOST Rev:0x%d not recognized, assuming %d ports\n", + omap->usbhs_rev, omap->nports); + break; + } } for (i = 0; i < omap->nports; i++) diff --git a/include/linux/platform_data/usb-omap.h b/include/linux/platform_data/usb-omap.h index e697c85ad3bc..fa579b4c666b 100644 --- a/include/linux/platform_data/usb-omap.h +++ b/include/linux/platform_data/usb-omap.h @@ -55,6 +55,7 @@ struct ohci_hcd_omap_platform_data { }; struct usbhs_omap_platform_data { + int nports; enum usbhs_omap_port_mode port_mode[OMAP3_HS_USB_PORTS]; int reset_gpio_port[OMAP3_HS_USB_PORTS]; struct regulator *regulator[OMAP3_HS_USB_PORTS]; From 06ba7dc75fbf1ac731dd3ca08d5f95f552780cb2 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 8 Nov 2012 17:40:25 +0200 Subject: [PATCH 2420/4607] mfd: omap-usb-host: cleanup clock management code All ports have similarly named port clocks so we can bunch them into a port data structure and use for loop to enable/disable the clocks. Dynamically allocate and get clocks based on number of ports available on the platform Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 192 ++++++++++++++++++++---------------- 1 file changed, 109 insertions(+), 83 deletions(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 779588be8ab2..9fa0215e3df4 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -92,13 +92,12 @@ struct usbhs_hcd_omap { int nports; + struct clk **utmi_clk; struct clk *xclk60mhsp1_ck; struct clk *xclk60mhsp2_ck; - struct clk *utmi_p1_fck; - struct clk *usbhost_p1_fck; - struct clk *utmi_p2_fck; - struct clk *usbhost_p2_fck; + struct clk *utmi_p1_gfclk; + struct clk *utmi_p2_gfclk; struct clk *init_60m_fclk; struct clk *ehci_logic_fck; @@ -276,22 +275,25 @@ static int usbhs_runtime_resume(struct device *dev) struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = omap->pdata; unsigned long flags; + int i, r; dev_dbg(dev, "usbhs_runtime_resume\n"); omap_tll_enable(); spin_lock_irqsave(&omap->lock, flags); - if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck)) + if (!IS_ERR(omap->ehci_logic_fck)) clk_enable(omap->ehci_logic_fck); - if (is_ehci_tll_mode(pdata->port_mode[0])) - clk_enable(omap->usbhost_p1_fck); - if (is_ehci_tll_mode(pdata->port_mode[1])) - clk_enable(omap->usbhost_p2_fck); + for (i = 0; i < omap->nports; i++) { + if (!is_ehci_tll_mode(pdata->port_mode[i]) || + IS_ERR(omap->utmi_clk[i])) + continue; - clk_enable(omap->utmi_p1_fck); - clk_enable(omap->utmi_p2_fck); + r = clk_enable(omap->utmi_clk[i]); + if (r) + dev_err(dev, "Can't enable port %d clk : %d\n", i, r); + } spin_unlock_irqrestore(&omap->lock, flags); @@ -303,20 +305,19 @@ static int usbhs_runtime_suspend(struct device *dev) struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = omap->pdata; unsigned long flags; + int i; dev_dbg(dev, "usbhs_runtime_suspend\n"); spin_lock_irqsave(&omap->lock, flags); - if (is_ehci_tll_mode(pdata->port_mode[0])) - clk_disable(omap->usbhost_p1_fck); - if (is_ehci_tll_mode(pdata->port_mode[1])) - clk_disable(omap->usbhost_p2_fck); + for (i = 0; i < omap->nports; i++) { + if (is_ehci_tll_mode(pdata->port_mode[i]) && + !IS_ERR(omap->utmi_clk[i])) + clk_disable(omap->utmi_clk[i]); + } - clk_disable(omap->utmi_p2_fck); - clk_disable(omap->utmi_p1_fck); - - if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck)) + if (!IS_ERR(omap->ehci_logic_fck)) clk_disable(omap->ehci_logic_fck); spin_unlock_irqrestore(&omap->lock, flags); @@ -458,6 +459,7 @@ static int usbhs_omap_probe(struct platform_device *pdev) struct resource *res; int ret = 0; int i; + bool need_logic_fck; if (!pdata) { dev_err(dev, "Missing platform data\n"); @@ -516,76 +518,91 @@ static int usbhs_omap_probe(struct platform_device *pdev) } } - for (i = 0; i < omap->nports; i++) - if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) || - is_ehci_hsic_mode(i)) { - omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck"); - if (IS_ERR(omap->ehci_logic_fck)) { - ret = PTR_ERR(omap->ehci_logic_fck); - dev_warn(dev, "ehci_logic_fck failed:%d\n", - ret); - } - break; - } + i = sizeof(struct clk *) * omap->nports; + omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL); + if (!omap->utmi_clk) { + dev_err(dev, "Memory allocation failed\n"); + ret = -ENOMEM; + goto err_mem; + } - omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk"); - if (IS_ERR(omap->utmi_p1_fck)) { - ret = PTR_ERR(omap->utmi_p1_fck); - dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); - goto err_end; + need_logic_fck = false; + for (i = 0; i < omap->nports; i++) { + if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) || + is_ehci_hsic_mode(i)) + need_logic_fck |= true; + } + + omap->ehci_logic_fck = ERR_PTR(-EINVAL); + if (need_logic_fck) { + omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck"); + if (IS_ERR(omap->ehci_logic_fck)) { + ret = PTR_ERR(omap->ehci_logic_fck); + dev_dbg(dev, "ehci_logic_fck failed:%d\n", ret); + } + } + + omap->utmi_p1_gfclk = clk_get(dev, "utmi_p1_gfclk"); + if (IS_ERR(omap->utmi_p1_gfclk)) { + ret = PTR_ERR(omap->utmi_p1_gfclk); + dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); + goto err_p1_gfclk; + } + + omap->utmi_p2_gfclk = clk_get(dev, "utmi_p2_gfclk"); + if (IS_ERR(omap->utmi_p2_gfclk)) { + ret = PTR_ERR(omap->utmi_p2_gfclk); + dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); + goto err_p2_gfclk; } omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); if (IS_ERR(omap->xclk60mhsp1_ck)) { ret = PTR_ERR(omap->xclk60mhsp1_ck); dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); - goto err_utmi_p1_fck; - } - - omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk"); - if (IS_ERR(omap->utmi_p2_fck)) { - ret = PTR_ERR(omap->utmi_p2_fck); - dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); - goto err_xclk60mhsp1_ck; + goto err_xclk60mhsp1; } omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck"); if (IS_ERR(omap->xclk60mhsp2_ck)) { ret = PTR_ERR(omap->xclk60mhsp2_ck); dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); - goto err_utmi_p2_fck; - } - - omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk"); - if (IS_ERR(omap->usbhost_p1_fck)) { - ret = PTR_ERR(omap->usbhost_p1_fck); - dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret); - goto err_xclk60mhsp2_ck; - } - - omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk"); - if (IS_ERR(omap->usbhost_p2_fck)) { - ret = PTR_ERR(omap->usbhost_p2_fck); - dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret); - goto err_usbhost_p1_fck; + goto err_xclk60mhsp2; } omap->init_60m_fclk = clk_get(dev, "init_60m_fclk"); if (IS_ERR(omap->init_60m_fclk)) { ret = PTR_ERR(omap->init_60m_fclk); dev_err(dev, "init_60m_fclk failed error:%d\n", ret); - goto err_usbhost_p2_fck; + goto err_init60m; + } + + for (i = 0; i < omap->nports; i++) { + char clkname[] = "usb_host_hs_utmi_px_clk"; + + /* clock names are indexed from 1*/ + snprintf(clkname, sizeof(clkname), + "usb_host_hs_utmi_p%d_clk", i + 1); + + /* If a clock is not found we won't bail out as not all + * platforms have all clocks and we can function without + * them + */ + omap->utmi_clk[i] = clk_get(dev, clkname); + if (IS_ERR(omap->utmi_clk[i])) + dev_dbg(dev, "Failed to get clock : %s : %ld\n", + clkname, PTR_ERR(omap->utmi_clk[i])); } if (is_ehci_phy_mode(pdata->port_mode[0])) { /* for OMAP3 , the clk set paretn fails */ - ret = clk_set_parent(omap->utmi_p1_fck, + ret = clk_set_parent(omap->utmi_p1_gfclk, omap->xclk60mhsp1_ck); if (ret != 0) dev_err(dev, "xclk60mhsp1_ck set parent" "failed error:%d\n", ret); } else if (is_ehci_tll_mode(pdata->port_mode[0])) { - ret = clk_set_parent(omap->utmi_p1_fck, + ret = clk_set_parent(omap->utmi_p1_gfclk, omap->init_60m_fclk); if (ret != 0) dev_err(dev, "init_60m_fclk set parent" @@ -593,13 +610,13 @@ static int usbhs_omap_probe(struct platform_device *pdev) } if (is_ehci_phy_mode(pdata->port_mode[1])) { - ret = clk_set_parent(omap->utmi_p2_fck, + ret = clk_set_parent(omap->utmi_p2_gfclk, omap->xclk60mhsp2_ck); if (ret != 0) dev_err(dev, "xclk60mhsp2_ck set parent" "failed error:%d\n", ret); } else if (is_ehci_tll_mode(pdata->port_mode[1])) { - ret = clk_set_parent(omap->utmi_p2_fck, + ret = clk_set_parent(omap->utmi_p2_gfclk, omap->init_60m_fclk); if (ret != 0) dev_err(dev, "init_60m_fclk set parent" @@ -617,28 +634,30 @@ static int usbhs_omap_probe(struct platform_device *pdev) err_alloc: omap_usbhs_deinit(&pdev->dev); + + for (i = 0; i < omap->nports; i++) + if (!IS_ERR(omap->utmi_clk[i])) + clk_put(omap->utmi_clk[i]); + clk_put(omap->init_60m_fclk); -err_usbhost_p2_fck: - clk_put(omap->usbhost_p2_fck); - -err_usbhost_p1_fck: - clk_put(omap->usbhost_p1_fck); - -err_xclk60mhsp2_ck: +err_init60m: clk_put(omap->xclk60mhsp2_ck); -err_utmi_p2_fck: - clk_put(omap->utmi_p2_fck); - -err_xclk60mhsp1_ck: +err_xclk60mhsp2: clk_put(omap->xclk60mhsp1_ck); -err_utmi_p1_fck: - clk_put(omap->utmi_p1_fck); +err_xclk60mhsp1: + clk_put(omap->utmi_p2_gfclk); -err_end: - clk_put(omap->ehci_logic_fck); +err_p2_gfclk: + clk_put(omap->utmi_p1_gfclk); + +err_p1_gfclk: + if (!IS_ERR(omap->ehci_logic_fck)) + clk_put(omap->ehci_logic_fck); + +err_mem: pm_runtime_disable(dev); return ret; @@ -653,16 +672,23 @@ err_end: static int usbhs_omap_remove(struct platform_device *pdev) { struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); + int i; omap_usbhs_deinit(&pdev->dev); + + for (i = 0; i < omap->nports; i++) + if (!IS_ERR(omap->utmi_clk[i])) + clk_put(omap->utmi_clk[i]); + clk_put(omap->init_60m_fclk); - clk_put(omap->usbhost_p2_fck); - clk_put(omap->usbhost_p1_fck); + clk_put(omap->utmi_p1_gfclk); + clk_put(omap->utmi_p2_gfclk); clk_put(omap->xclk60mhsp2_ck); - clk_put(omap->utmi_p2_fck); clk_put(omap->xclk60mhsp1_ck); - clk_put(omap->utmi_p1_fck); - clk_put(omap->ehci_logic_fck); + + if (!IS_ERR(omap->ehci_logic_fck)) + clk_put(omap->ehci_logic_fck); + pm_runtime_disable(&pdev->dev); return 0; From 340c64eabacb2a4331b4357c0e8944027ce37216 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 12 Nov 2012 16:53:16 +0200 Subject: [PATCH 2421/4607] mfd: omap-usb-host: Manage HSIC clocks for HSIC mode Enable the optional HSIC clocks (60MHz and 480MHz) for the ports that are configured in HSIC mode. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 95 ++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 13 deletions(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 9fa0215e3df4..bdfc8b7b5aff 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -93,6 +93,8 @@ struct usbhs_hcd_omap { int nports; struct clk **utmi_clk; + struct clk **hsic60m_clk; + struct clk **hsic480m_clk; struct clk *xclk60mhsp1_ck; struct clk *xclk60mhsp2_ck; @@ -286,13 +288,40 @@ static int usbhs_runtime_resume(struct device *dev) clk_enable(omap->ehci_logic_fck); for (i = 0; i < omap->nports; i++) { - if (!is_ehci_tll_mode(pdata->port_mode[i]) || - IS_ERR(omap->utmi_clk[i])) - continue; + switch (pdata->port_mode[i]) { + case OMAP_EHCI_PORT_MODE_HSIC: + if (!IS_ERR(omap->hsic60m_clk[i])) { + r = clk_enable(omap->hsic60m_clk[i]); + if (r) { + dev_err(dev, + "Can't enable port %d hsic60m clk:%d\n", + i, r); + } + } - r = clk_enable(omap->utmi_clk[i]); - if (r) - dev_err(dev, "Can't enable port %d clk : %d\n", i, r); + if (!IS_ERR(omap->hsic480m_clk[i])) { + r = clk_enable(omap->hsic480m_clk[i]); + if (r) { + dev_err(dev, + "Can't enable port %d hsic480m clk:%d\n", + i, r); + } + } + /* Fall through as HSIC mode needs utmi_clk */ + + case OMAP_EHCI_PORT_MODE_TLL: + if (!IS_ERR(omap->utmi_clk[i])) { + r = clk_enable(omap->utmi_clk[i]); + if (r) { + dev_err(dev, + "Can't enable port %d clk : %d\n", + i, r); + } + } + break; + default: + break; + } } spin_unlock_irqrestore(&omap->lock, flags); @@ -312,9 +341,22 @@ static int usbhs_runtime_suspend(struct device *dev) spin_lock_irqsave(&omap->lock, flags); for (i = 0; i < omap->nports; i++) { - if (is_ehci_tll_mode(pdata->port_mode[i]) && - !IS_ERR(omap->utmi_clk[i])) - clk_disable(omap->utmi_clk[i]); + switch (pdata->port_mode[i]) { + case OMAP_EHCI_PORT_MODE_HSIC: + if (!IS_ERR(omap->hsic60m_clk[i])) + clk_disable(omap->hsic60m_clk[i]); + + if (!IS_ERR(omap->hsic480m_clk[i])) + clk_disable(omap->hsic480m_clk[i]); + /* Fall through as utmi_clks were used in HSIC mode */ + + case OMAP_EHCI_PORT_MODE_TLL: + if (!IS_ERR(omap->utmi_clk[i])) + clk_disable(omap->utmi_clk[i]); + break; + default: + break; + } } if (!IS_ERR(omap->ehci_logic_fck)) @@ -520,7 +562,10 @@ static int usbhs_omap_probe(struct platform_device *pdev) i = sizeof(struct clk *) * omap->nports; omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL); - if (!omap->utmi_clk) { + omap->hsic480m_clk = devm_kzalloc(dev, i, GFP_KERNEL); + omap->hsic60m_clk = devm_kzalloc(dev, i, GFP_KERNEL); + + if (!omap->utmi_clk || !omap->hsic480m_clk || !omap->hsic60m_clk) { dev_err(dev, "Memory allocation failed\n"); ret = -ENOMEM; goto err_mem; @@ -578,7 +623,7 @@ static int usbhs_omap_probe(struct platform_device *pdev) } for (i = 0; i < omap->nports; i++) { - char clkname[] = "usb_host_hs_utmi_px_clk"; + char clkname[30]; /* clock names are indexed from 1*/ snprintf(clkname, sizeof(clkname), @@ -592,6 +637,20 @@ static int usbhs_omap_probe(struct platform_device *pdev) if (IS_ERR(omap->utmi_clk[i])) dev_dbg(dev, "Failed to get clock : %s : %ld\n", clkname, PTR_ERR(omap->utmi_clk[i])); + + snprintf(clkname, sizeof(clkname), + "usb_host_hs_hsic480m_p%d_clk", i + 1); + omap->hsic480m_clk[i] = clk_get(dev, clkname); + if (IS_ERR(omap->hsic480m_clk[i])) + dev_dbg(dev, "Failed to get clock : %s : %ld\n", + clkname, PTR_ERR(omap->hsic480m_clk[i])); + + snprintf(clkname, sizeof(clkname), + "usb_host_hs_hsic60m_p%d_clk", i + 1); + omap->hsic60m_clk[i] = clk_get(dev, clkname); + if (IS_ERR(omap->hsic60m_clk[i])) + dev_dbg(dev, "Failed to get clock : %s : %ld\n", + clkname, PTR_ERR(omap->hsic60m_clk[i])); } if (is_ehci_phy_mode(pdata->port_mode[0])) { @@ -635,9 +694,14 @@ static int usbhs_omap_probe(struct platform_device *pdev) err_alloc: omap_usbhs_deinit(&pdev->dev); - for (i = 0; i < omap->nports; i++) + for (i = 0; i < omap->nports; i++) { if (!IS_ERR(omap->utmi_clk[i])) clk_put(omap->utmi_clk[i]); + if (!IS_ERR(omap->hsic60m_clk[i])) + clk_put(omap->hsic60m_clk[i]); + if (!IS_ERR(omap->hsic480m_clk[i])) + clk_put(omap->hsic480m_clk[i]); + } clk_put(omap->init_60m_fclk); @@ -676,9 +740,14 @@ static int usbhs_omap_remove(struct platform_device *pdev) omap_usbhs_deinit(&pdev->dev); - for (i = 0; i < omap->nports; i++) + for (i = 0; i < omap->nports; i++) { if (!IS_ERR(omap->utmi_clk[i])) clk_put(omap->utmi_clk[i]); + if (!IS_ERR(omap->hsic60m_clk[i])) + clk_put(omap->hsic60m_clk[i]); + if (!IS_ERR(omap->hsic480m_clk[i])) + clk_put(omap->hsic480m_clk[i]); + } clk_put(omap->init_60m_fclk); clk_put(omap->utmi_p1_gfclk); From c6cd087ed058f8a7dfe1997cc51fdf8005e25a03 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 15 Nov 2012 11:48:51 +0200 Subject: [PATCH 2422/4607] mfd: omap-usb-host: Get rid of unnecessary spinlock The driver does not have an interrupt handler and we don't really need a spinlock, so get rid of it. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index bdfc8b7b5aff..0740c6856b51 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -108,7 +107,6 @@ struct usbhs_hcd_omap { struct usbhs_omap_platform_data *pdata; u32 usbhs_rev; - spinlock_t lock; }; /*-------------------------------------------------------------------------*/ @@ -276,13 +274,11 @@ static int usbhs_runtime_resume(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = omap->pdata; - unsigned long flags; int i, r; dev_dbg(dev, "usbhs_runtime_resume\n"); omap_tll_enable(); - spin_lock_irqsave(&omap->lock, flags); if (!IS_ERR(omap->ehci_logic_fck)) clk_enable(omap->ehci_logic_fck); @@ -324,8 +320,6 @@ static int usbhs_runtime_resume(struct device *dev) } } - spin_unlock_irqrestore(&omap->lock, flags); - return 0; } @@ -333,13 +327,10 @@ static int usbhs_runtime_suspend(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = omap->pdata; - unsigned long flags; int i; dev_dbg(dev, "usbhs_runtime_suspend\n"); - spin_lock_irqsave(&omap->lock, flags); - for (i = 0; i < omap->nports; i++) { switch (pdata->port_mode[i]) { case OMAP_EHCI_PORT_MODE_HSIC: @@ -362,7 +353,6 @@ static int usbhs_runtime_suspend(struct device *dev) if (!IS_ERR(omap->ehci_logic_fck)) clk_disable(omap->ehci_logic_fck); - spin_unlock_irqrestore(&omap->lock, flags); omap_tll_disable(); return 0; @@ -372,7 +362,6 @@ static void omap_usbhs_init(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_omap_platform_data *pdata = omap->pdata; - unsigned long flags; unsigned reg; dev_dbg(dev, "starting TI HSUSB Controller\n"); @@ -391,7 +380,6 @@ static void omap_usbhs_init(struct device *dev) } pm_runtime_get_sync(dev); - spin_lock_irqsave(&omap->lock, flags); reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); /* setup ULPI bypass and burst configurations */ @@ -454,8 +442,6 @@ static void omap_usbhs_init(struct device *dev) usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg); - spin_unlock_irqrestore(&omap->lock, flags); - pm_runtime_put_sync(dev); if (pdata->phy_reset) { /* Hold the PHY in RESET for enough time till @@ -521,8 +507,6 @@ static int usbhs_omap_probe(struct platform_device *pdev) return -EADDRNOTAVAIL; } - spin_lock_init(&omap->lock); - omap->pdata = pdata; pm_runtime_enable(dev); From c4df00aed9e2e6e3ab094b4bb8b9ecb64cf8c70e Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 12 Nov 2012 16:32:01 +0200 Subject: [PATCH 2423/4607] mfd: omap-usb-host: clean up omap_usbhs_init() We split initializing revision 1 and revision 2 into different functions. Initialization is now done dynamically so that only the number of ports available on the system are initialized. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 125 +++++++++++++++++++++++------------- 1 file changed, 79 insertions(+), 46 deletions(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 0740c6856b51..7f9c38675d91 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -358,6 +358,75 @@ static int usbhs_runtime_suspend(struct device *dev) return 0; } +static unsigned omap_usbhs_rev1_hostconfig(struct usbhs_hcd_omap *omap, + unsigned reg) +{ + struct usbhs_omap_platform_data *pdata = omap->pdata; + int i; + + for (i = 0; i < omap->nports; i++) { + switch (pdata->port_mode[i]) { + case OMAP_USBHS_PORT_MODE_UNUSED: + reg &= ~(OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS << i); + break; + case OMAP_EHCI_PORT_MODE_PHY: + if (pdata->single_ulpi_bypass) + break; + + if (i == 0) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + else + reg &= ~(OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS + << (i-1)); + break; + default: + if (pdata->single_ulpi_bypass) + break; + + if (i == 0) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + else + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS + << (i-1); + break; + } + } + + if (pdata->single_ulpi_bypass) { + /* bypass ULPI only if none of the ports use PHY mode */ + reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + + for (i = 0; i < omap->nports; i++) { + if (is_ehci_phy_mode(pdata->port_mode[i])) { + reg &= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + break; + } + } + } + + return reg; +} + +static unsigned omap_usbhs_rev2_hostconfig(struct usbhs_hcd_omap *omap, + unsigned reg) +{ + struct usbhs_omap_platform_data *pdata = omap->pdata; + int i; + + for (i = 0; i < omap->nports; i++) { + /* Clear port mode fields for PHY mode */ + reg &= ~(OMAP4_P1_MODE_CLEAR << 2 * i); + + if (is_ehci_tll_mode(pdata->port_mode[i]) || + (is_ohci_port(pdata->port_mode[i]))) + reg |= OMAP4_P1_MODE_TLL << 2 * i; + else if (is_ehci_hsic_mode(pdata->port_mode[i])) + reg |= OMAP4_P1_MODE_HSIC << 2 * i; + } + + return reg; +} + static void omap_usbhs_init(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); @@ -389,54 +458,18 @@ static void omap_usbhs_init(struct device *dev) reg |= OMAP4_UHH_HOSTCONFIG_APP_START_CLK; reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN; - if (is_omap_usbhs_rev1(omap)) { - if (pdata->port_mode[0] == OMAP_USBHS_PORT_MODE_UNUSED) - reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS; - if (pdata->port_mode[1] == OMAP_USBHS_PORT_MODE_UNUSED) - reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS; - if (pdata->port_mode[2] == OMAP_USBHS_PORT_MODE_UNUSED) - reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS; + switch (omap->usbhs_rev) { + case OMAP_USBHS_REV1: + omap_usbhs_rev1_hostconfig(omap, reg); + break; - /* Bypass the TLL module for PHY mode operation */ - if (pdata->single_ulpi_bypass) { - dev_dbg(dev, "OMAP3 ES version <= ES2.1\n"); - if (is_ehci_phy_mode(pdata->port_mode[0]) || - is_ehci_phy_mode(pdata->port_mode[1]) || - is_ehci_phy_mode(pdata->port_mode[2])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; - else - reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; - } else { - dev_dbg(dev, "OMAP3 ES version > ES2.1\n"); - if (is_ehci_phy_mode(pdata->port_mode[0])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; - else - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; - if (is_ehci_phy_mode(pdata->port_mode[1])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; - else - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; - if (is_ehci_phy_mode(pdata->port_mode[2])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; - else - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; - } - } else if (is_omap_usbhs_rev2(omap)) { - /* Clear port mode fields for PHY mode*/ - reg &= ~OMAP4_P1_MODE_CLEAR; - reg &= ~OMAP4_P2_MODE_CLEAR; + case OMAP_USBHS_REV2: + omap_usbhs_rev2_hostconfig(omap, reg); + break; - if (is_ehci_tll_mode(pdata->port_mode[0]) || - (is_ohci_port(pdata->port_mode[0]))) - reg |= OMAP4_P1_MODE_TLL; - else if (is_ehci_hsic_mode(pdata->port_mode[0])) - reg |= OMAP4_P1_MODE_HSIC; - - if (is_ehci_tll_mode(pdata->port_mode[1]) || - (is_ohci_port(pdata->port_mode[1]))) - reg |= OMAP4_P2_MODE_TLL; - else if (is_ehci_hsic_mode(pdata->port_mode[1])) - reg |= OMAP4_P2_MODE_HSIC; + default: /* newer revisions */ + omap_usbhs_rev2_hostconfig(omap, reg); + break; } usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); From a8c4e9e1118f0a12e3a9524d8d597487d7e3476d Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 28 Nov 2012 16:31:29 +0200 Subject: [PATCH 2424/4607] mfd: omap-usb-host: Don't spam console on clk_set_parent failure clk_set_parent is expected to fail on OMAP3 platforms. We don't consider that as fatal so don't spam console. Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 7f9c38675d91..b21ca760b1bd 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -671,32 +671,32 @@ static int usbhs_omap_probe(struct platform_device *pdev) } if (is_ehci_phy_mode(pdata->port_mode[0])) { - /* for OMAP3 , the clk set paretn fails */ + /* for OMAP3, clk_set_parent fails */ ret = clk_set_parent(omap->utmi_p1_gfclk, omap->xclk60mhsp1_ck); if (ret != 0) - dev_err(dev, "xclk60mhsp1_ck set parent" - "failed error:%d\n", ret); + dev_dbg(dev, "xclk60mhsp1_ck set parent failed: %d\n", + ret); } else if (is_ehci_tll_mode(pdata->port_mode[0])) { ret = clk_set_parent(omap->utmi_p1_gfclk, omap->init_60m_fclk); if (ret != 0) - dev_err(dev, "init_60m_fclk set parent" - "failed error:%d\n", ret); + dev_dbg(dev, "P0 init_60m_fclk set parent failed: %d\n", + ret); } if (is_ehci_phy_mode(pdata->port_mode[1])) { ret = clk_set_parent(omap->utmi_p2_gfclk, omap->xclk60mhsp2_ck); if (ret != 0) - dev_err(dev, "xclk60mhsp2_ck set parent" - "failed error:%d\n", ret); + dev_dbg(dev, "xclk60mhsp2_ck set parent failed: %d\n", + ret); } else if (is_ehci_tll_mode(pdata->port_mode[1])) { ret = clk_set_parent(omap->utmi_p2_gfclk, omap->init_60m_fclk); if (ret != 0) - dev_err(dev, "init_60m_fclk set parent" - "failed error:%d\n", ret); + dev_dbg(dev, "P1 init_60m_fclk set parent failed: %d\n", + ret); } omap_usbhs_init(dev); From ab3f2a86d17c5c2c1127871d28d1f64baebc5d04 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 2 Jan 2013 15:59:28 +0200 Subject: [PATCH 2425/4607] mfd: omap-usb-host: get rid of build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the below build warning when driver is built-in. drivers/mfd/omap-usb-host.c:750:12: warning: ‘usbhs_omap_remove’ defined but not used [-Wunused-function] Signed-off-by: Roger Quadros Reviewed-by: Felipe Balbi --- drivers/mfd/omap-usb-host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index b21ca760b1bd..6b5edf64de2b 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -791,7 +791,7 @@ static struct platform_driver usbhs_omap_driver = { .owner = THIS_MODULE, .pm = &usbhsomap_dev_pm_ops, }, - .remove = __exit_p(usbhs_omap_remove), + .remove = usbhs_omap_remove, }; MODULE_AUTHOR("Keshava Munegowda "); From ffe129ecd79779221fdb03305049ec8b5a8beb0f Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Tue, 15 Jan 2013 22:20:42 +0000 Subject: [PATCH 2426/4607] KVM: PPC: booke: use vcpu reference from thread_struct Like other places, use thread_struct to get vcpu reference. Signed-off-by: Bharat Bhushan Signed-off-by: Alexander Graf --- arch/powerpc/include/asm/reg.h | 2 -- arch/powerpc/kernel/asm-offsets.c | 2 +- arch/powerpc/kvm/booke_interrupts.S | 6 ++---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 97d37278ea2d..11ae3d8ba3a2 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -919,8 +919,6 @@ #define SPRN_SPRG_RSCRATCH_DBG SPRN_SPRG9 #define SPRN_SPRG_WSCRATCH_DBG SPRN_SPRG9 #endif -#define SPRN_SPRG_RVCPU SPRN_SPRG1 -#define SPRN_SPRG_WVCPU SPRN_SPRG1 #endif #ifdef CONFIG_8xx diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 4e23ba2f3ca7..46f6afd2172a 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -117,7 +117,7 @@ int main(void) #ifdef CONFIG_KVM_BOOK3S_32_HANDLER DEFINE(THREAD_KVM_SVCPU, offsetof(struct thread_struct, kvm_shadow_vcpu)); #endif -#ifdef CONFIG_KVM_BOOKE_HV +#if defined(CONFIG_KVM) && defined(CONFIG_BOOKE) DEFINE(THREAD_KVM_VCPU, offsetof(struct thread_struct, kvm_vcpu)); #endif diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S index bb46b32f9813..ca16d57f7686 100644 --- a/arch/powerpc/kvm/booke_interrupts.S +++ b/arch/powerpc/kvm/booke_interrupts.S @@ -56,7 +56,8 @@ _GLOBAL(kvmppc_handler_\ivor_nr) /* Get pointer to vcpu and record exit number. */ mtspr \scratch , r4 - mfspr r4, SPRN_SPRG_RVCPU + mfspr r4, SPRN_SPRG_THREAD + lwz r4, THREAD_KVM_VCPU(r4) stw r3, VCPU_GPR(R3)(r4) stw r5, VCPU_GPR(R5)(r4) stw r6, VCPU_GPR(R6)(r4) @@ -402,9 +403,6 @@ lightweight_exit: lwz r8, kvmppc_booke_handlers@l(r8) mtspr SPRN_IVPR, r8 - /* Save vcpu pointer for the exception handlers. */ - mtspr SPRN_SPRG_WVCPU, r4 - lwz r5, VCPU_SHARED(r4) /* Can't switch the stack pointer until after IVPR is switched, From 1d542d9c2bbca9b99835fef6a938b9ae9dd7ca2a Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Tue, 15 Jan 2013 22:24:39 +0000 Subject: [PATCH 2427/4607] KVM: PPC: booke: Allow multiple exception types Current kvmppc_booke_handlers uses the same macro (KVM_HANDLER) and all handlers are considered to be the same size. This will not be the case if we want to use different macros for different handlers. This patch improves the kvmppc_booke_handler so that it can support different macros for different handlers. Signed-off-by: Liu Yu [bharat.bhushan@freescale.com: Substantial changes] Signed-off-by: Bharat Bhushan Signed-off-by: Alexander Graf --- arch/powerpc/include/asm/kvm_ppc.h | 2 -- arch/powerpc/kvm/booke.c | 14 +++++++---- arch/powerpc/kvm/booke.h | 1 + arch/powerpc/kvm/booke_interrupts.S | 37 ++++++++++++++++++++++++++--- arch/powerpc/kvm/e500.c | 16 ++++++++----- 5 files changed, 54 insertions(+), 16 deletions(-) diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 493630e209c8..44a657adf416 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -49,8 +49,6 @@ enum emulation_result { extern int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu); extern int __kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu); -extern char kvmppc_handlers_start[]; -extern unsigned long kvmppc_handler_len; extern void kvmppc_handler_highmem(void); extern void kvmppc_dump_vcpu(struct kvm_vcpu *vcpu); diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 8779cd4c52d9..d2f502d209ff 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -1594,7 +1594,9 @@ int __init kvmppc_booke_init(void) { #ifndef CONFIG_KVM_BOOKE_HV unsigned long ivor[16]; + unsigned long *handler = kvmppc_booke_handler_addr; unsigned long max_ivor = 0; + unsigned long handler_len; int i; /* We install our own exception handlers by hijacking IVPR. IVPR must @@ -1627,14 +1629,16 @@ int __init kvmppc_booke_init(void) for (i = 0; i < 16; i++) { if (ivor[i] > max_ivor) - max_ivor = ivor[i]; + max_ivor = i; + handler_len = handler[i + 1] - handler[i]; memcpy((void *)kvmppc_booke_handlers + ivor[i], - kvmppc_handlers_start + i * kvmppc_handler_len, - kvmppc_handler_len); + (void *)handler[i], handler_len); } - flush_icache_range(kvmppc_booke_handlers, - kvmppc_booke_handlers + max_ivor + kvmppc_handler_len); + + handler_len = handler[max_ivor + 1] - handler[max_ivor]; + flush_icache_range(kvmppc_booke_handlers, kvmppc_booke_handlers + + ivor[max_ivor] + handler_len); #endif /* !BOOKE_HV */ return 0; } diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h index e9b88e433f64..5fd1ba693579 100644 --- a/arch/powerpc/kvm/booke.h +++ b/arch/powerpc/kvm/booke.h @@ -65,6 +65,7 @@ (1 << BOOKE_IRQPRIO_CRITICAL)) extern unsigned long kvmppc_booke_handlers; +extern unsigned long kvmppc_booke_handler_addr[]; void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr); void kvmppc_mmu_msr_notify(struct kvm_vcpu *vcpu, u32 old_msr); diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S index ca16d57f7686..eae848376440 100644 --- a/arch/powerpc/kvm/booke_interrupts.S +++ b/arch/powerpc/kvm/booke_interrupts.S @@ -74,6 +74,14 @@ _GLOBAL(kvmppc_handler_\ivor_nr) bctr .endm +.macro KVM_HANDLER_ADDR ivor_nr + .long kvmppc_handler_\ivor_nr +.endm + +.macro KVM_HANDLER_END + .long kvmppc_handlers_end +.endm + _GLOBAL(kvmppc_handlers_start) KVM_HANDLER BOOKE_INTERRUPT_CRITICAL SPRN_SPRG_RSCRATCH_CRIT SPRN_CSRR0 KVM_HANDLER BOOKE_INTERRUPT_MACHINE_CHECK SPRN_SPRG_RSCRATCH_MC SPRN_MCSRR0 @@ -94,9 +102,7 @@ KVM_HANDLER BOOKE_INTERRUPT_DEBUG SPRN_SPRG_RSCRATCH_CRIT SPRN_CSRR0 KVM_HANDLER BOOKE_INTERRUPT_SPE_UNAVAIL SPRN_SPRG_RSCRATCH0 SPRN_SRR0 KVM_HANDLER BOOKE_INTERRUPT_SPE_FP_DATA SPRN_SPRG_RSCRATCH0 SPRN_SRR0 KVM_HANDLER BOOKE_INTERRUPT_SPE_FP_ROUND SPRN_SPRG_RSCRATCH0 SPRN_SRR0 - -_GLOBAL(kvmppc_handler_len) - .long kvmppc_handler_1 - kvmppc_handler_0 +_GLOBAL(kvmppc_handlers_end) /* Registers: * SPRG_SCRATCH0: guest r4 @@ -461,6 +467,31 @@ lightweight_exit: lwz r4, VCPU_GPR(R4)(r4) rfi + .data + .align 4 + .globl kvmppc_booke_handler_addr +kvmppc_booke_handler_addr: +KVM_HANDLER_ADDR BOOKE_INTERRUPT_CRITICAL +KVM_HANDLER_ADDR BOOKE_INTERRUPT_MACHINE_CHECK +KVM_HANDLER_ADDR BOOKE_INTERRUPT_DATA_STORAGE +KVM_HANDLER_ADDR BOOKE_INTERRUPT_INST_STORAGE +KVM_HANDLER_ADDR BOOKE_INTERRUPT_EXTERNAL +KVM_HANDLER_ADDR BOOKE_INTERRUPT_ALIGNMENT +KVM_HANDLER_ADDR BOOKE_INTERRUPT_PROGRAM +KVM_HANDLER_ADDR BOOKE_INTERRUPT_FP_UNAVAIL +KVM_HANDLER_ADDR BOOKE_INTERRUPT_SYSCALL +KVM_HANDLER_ADDR BOOKE_INTERRUPT_AP_UNAVAIL +KVM_HANDLER_ADDR BOOKE_INTERRUPT_DECREMENTER +KVM_HANDLER_ADDR BOOKE_INTERRUPT_FIT +KVM_HANDLER_ADDR BOOKE_INTERRUPT_WATCHDOG +KVM_HANDLER_ADDR BOOKE_INTERRUPT_DTLB_MISS +KVM_HANDLER_ADDR BOOKE_INTERRUPT_ITLB_MISS +KVM_HANDLER_ADDR BOOKE_INTERRUPT_DEBUG +KVM_HANDLER_ADDR BOOKE_INTERRUPT_SPE_UNAVAIL +KVM_HANDLER_ADDR BOOKE_INTERRUPT_SPE_FP_DATA +KVM_HANDLER_ADDR BOOKE_INTERRUPT_SPE_FP_ROUND +KVM_HANDLER_END /*Always keep this in end*/ + #ifdef CONFIG_SPE _GLOBAL(kvmppc_save_guest_spe) cmpi 0,r3,0 diff --git a/arch/powerpc/kvm/e500.c b/arch/powerpc/kvm/e500.c index b479ed77c515..6dd4de7802bf 100644 --- a/arch/powerpc/kvm/e500.c +++ b/arch/powerpc/kvm/e500.c @@ -491,6 +491,9 @@ static int __init kvmppc_e500_init(void) { int r, i; unsigned long ivor[3]; + /* Process remaining handlers above the generic first 16 */ + unsigned long *handler = &kvmppc_booke_handler_addr[16]; + unsigned long handler_len; unsigned long max_ivor = 0; r = kvmppc_core_check_processor_compat(); @@ -506,15 +509,16 @@ static int __init kvmppc_e500_init(void) ivor[1] = mfspr(SPRN_IVOR33); ivor[2] = mfspr(SPRN_IVOR34); for (i = 0; i < 3; i++) { - if (ivor[i] > max_ivor) - max_ivor = ivor[i]; + if (ivor[i] > ivor[max_ivor]) + max_ivor = i; + handler_len = handler[i + 1] - handler[i]; memcpy((void *)kvmppc_booke_handlers + ivor[i], - kvmppc_handlers_start + (i + 16) * kvmppc_handler_len, - kvmppc_handler_len); + (void *)handler[i], handler_len); } - flush_icache_range(kvmppc_booke_handlers, - kvmppc_booke_handlers + max_ivor + kvmppc_handler_len); + handler_len = handler[max_ivor + 1] - handler[max_ivor]; + flush_icache_range(kvmppc_booke_handlers, kvmppc_booke_handlers + + ivor[max_ivor] + handler_len); return kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), 0, THIS_MODULE); } From ee53e560a8f52cbc1fba877fdf4acffb0c163f29 Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Tue, 15 Jan 2013 22:24:43 +0000 Subject: [PATCH 2428/4607] booke: Added DBCR4 SPR number Signed-off-by: Bharat Bhushan Signed-off-by: Alexander Graf --- arch/powerpc/include/asm/reg_booke.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/include/asm/reg_booke.h b/arch/powerpc/include/asm/reg_booke.h index e07e6af5e1ff..b417de3cc2c4 100644 --- a/arch/powerpc/include/asm/reg_booke.h +++ b/arch/powerpc/include/asm/reg_booke.h @@ -56,6 +56,7 @@ #define SPRN_SPRG7W 0x117 /* Special Purpose Register General 7 Write */ #define SPRN_EPCR 0x133 /* Embedded Processor Control Register */ #define SPRN_DBCR2 0x136 /* Debug Control Register 2 */ +#define SPRN_DBCR4 0x233 /* Debug Control Register 4 */ #define SPRN_MSRP 0x137 /* MSR Protect Register */ #define SPRN_IAC3 0x13A /* Instruction Address Compare 3 */ #define SPRN_IAC4 0x13B /* Instruction Address Compare 4 */ From 011da8996263f799a469a761ee15c998d7ef1acb Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 31 Jan 2013 14:17:38 +0100 Subject: [PATCH 2429/4607] KVM: PPC: BookE: Handle alignment interrupts When the guest triggers an alignment interrupt, we don't handle it properly today and instead BUG_ON(). This really shouldn't happen. Instead, we should just pass the interrupt back into the guest so it can deal with it. Reported-by: Gao Guanhua-B22826 Tested-by: Gao Guanhua-B22826 Signed-off-by: Alexander Graf --- arch/powerpc/kvm/booke.c | 16 +++++++++++++++- arch/powerpc/kvm/booke_interrupts.S | 6 ++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index d2f502d209ff..020923e43134 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -182,6 +182,14 @@ static void kvmppc_core_queue_inst_storage(struct kvm_vcpu *vcpu, kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_INST_STORAGE); } +static void kvmppc_core_queue_alignment(struct kvm_vcpu *vcpu, ulong dear_flags, + ulong esr_flags) +{ + vcpu->arch.queued_dear = dear_flags; + vcpu->arch.queued_esr = esr_flags; + kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_ALIGNMENT); +} + void kvmppc_core_queue_program(struct kvm_vcpu *vcpu, ulong esr_flags) { vcpu->arch.queued_esr = esr_flags; @@ -345,6 +353,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, switch (priority) { case BOOKE_IRQPRIO_DTLB_MISS: case BOOKE_IRQPRIO_DATA_STORAGE: + case BOOKE_IRQPRIO_ALIGNMENT: update_dear = true; /* fall through */ case BOOKE_IRQPRIO_INST_STORAGE: @@ -358,7 +367,6 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, case BOOKE_IRQPRIO_SPE_FP_DATA: case BOOKE_IRQPRIO_SPE_FP_ROUND: case BOOKE_IRQPRIO_AP_UNAVAIL: - case BOOKE_IRQPRIO_ALIGNMENT: allowed = 1; msr_mask = MSR_CE | MSR_ME | MSR_DE; int_class = INT_CLASS_NONCRIT; @@ -971,6 +979,12 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, r = RESUME_GUEST; break; + case BOOKE_INTERRUPT_ALIGNMENT: + kvmppc_core_queue_alignment(vcpu, vcpu->arch.fault_dear, + vcpu->arch.fault_esr); + r = RESUME_GUEST; + break; + #ifdef CONFIG_KVM_BOOKE_HV case BOOKE_INTERRUPT_HV_SYSCALL: if (!(vcpu->arch.shared->msr & MSR_PR)) { diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S index eae848376440..f4bb55c96517 100644 --- a/arch/powerpc/kvm/booke_interrupts.S +++ b/arch/powerpc/kvm/booke_interrupts.S @@ -45,12 +45,14 @@ (1< Date: Tue, 5 Feb 2013 09:08:10 -0500 Subject: [PATCH 2430/4607] mtd: mtd_torturetest can cause stack overflows mtd_torturetest uses the module parm "ebcnt" to control the size of a stack based array of int's. When "ebcnt" is large, Ex: 1000, it causes stack overflows on systems with small kernel stacks. The fix is to move the array from the stack to kmalloc memory. Signed-off-by: Al Cooper Signed-off-by: Artem Bityutskiy --- drivers/mtd/tests/mtd_torturetest.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/mtd_torturetest.c index c4cde1e9eddb..3a9f6a6a79f9 100644 --- a/drivers/mtd/tests/mtd_torturetest.c +++ b/drivers/mtd/tests/mtd_torturetest.c @@ -208,7 +208,7 @@ static inline int write_pattern(int ebnum, void *buf) static int __init tort_init(void) { int err = 0, i, infinite = !cycles_count; - int bad_ebs[ebcnt]; + int *bad_ebs; printk(KERN_INFO "\n"); printk(KERN_INFO "=================================================\n"); @@ -250,28 +250,24 @@ static int __init tort_init(void) err = -ENOMEM; patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!patt_5A5) { - pr_err("error: cannot allocate memory\n"); + if (!patt_5A5) goto out_mtd; - } patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!patt_A5A) { - pr_err("error: cannot allocate memory\n"); + if (!patt_A5A) goto out_patt_5A5; - } patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!patt_FF) { - pr_err("error: cannot allocate memory\n"); + if (!patt_FF) goto out_patt_A5A; - } check_buf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!check_buf) { - pr_err("error: cannot allocate memory\n"); + if (!check_buf) goto out_patt_FF; - } + + bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL); + if (!bad_ebs) + goto out_check_buf; err = 0; @@ -290,7 +286,6 @@ static int __init tort_init(void) /* * Check if there is a bad eraseblock among those we are going to test. */ - memset(&bad_ebs[0], 0, sizeof(int) * ebcnt); if (mtd_can_have_bb(mtd)) { for (i = eb; i < eb + ebcnt; i++) { err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize); @@ -394,6 +389,8 @@ out: pr_info("finished after %u erase cycles\n", erase_cycles); + kfree(bad_ebs); +out_check_buf: kfree(check_buf); out_patt_FF: kfree(patt_FF); From ef4e0c2145e928b1c165ac88a9aa20038a981488 Mon Sep 17 00:00:00 2001 From: Mrugesh Katepallewar Date: Thu, 7 Feb 2013 16:03:15 +0530 Subject: [PATCH 2431/4607] mtd: davinci_nand: Use managed resources davinci_nand driver currently uses normal kzalloc, ioremap and get_clk routines. This patch replaces these routines with devm_kzalloc, devm_request_and_ioremap and devm_clk_get resp. Signed-off-by: Mrugesh Katepallewar Signed-off-by: Artem Bityutskiy --- drivers/mtd/nand/davinci_nand.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index feae55c7b880..94e17af8e450 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -606,7 +606,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) if (pdev->id < 0 || pdev->id > 3) return -ENODEV; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) { dev_err(&pdev->dev, "unable to allocate memory\n"); ret = -ENOMEM; @@ -623,11 +623,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev) goto err_nomem; } - vaddr = ioremap(res1->start, resource_size(res1)); - base = ioremap(res2->start, resource_size(res2)); + vaddr = devm_request_and_ioremap(&pdev->dev, res1); + base = devm_request_and_ioremap(&pdev->dev, res2); if (!vaddr || !base) { dev_err(&pdev->dev, "ioremap failed\n"); - ret = -EINVAL; + ret = -EADDRNOTAVAIL; goto err_ioremap; } @@ -717,7 +717,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) } info->chip.ecc.mode = ecc_mode; - info->clk = clk_get(&pdev->dev, "aemif"); + info->clk = devm_clk_get(&pdev->dev, "aemif"); if (IS_ERR(info->clk)) { ret = PTR_ERR(info->clk); dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); @@ -845,8 +845,6 @@ err_timing: clk_disable_unprepare(info->clk); err_clk_enable: - clk_put(info->clk); - spin_lock_irq(&davinci_nand_lock); if (ecc_mode == NAND_ECC_HW_SYNDROME) ecc4_busy = false; @@ -855,13 +853,7 @@ err_clk_enable: err_ecc: err_clk: err_ioremap: - if (base) - iounmap(base); - if (vaddr) - iounmap(vaddr); - err_nomem: - kfree(info); return ret; } @@ -874,15 +866,9 @@ static int __exit nand_davinci_remove(struct platform_device *pdev) ecc4_busy = false; spin_unlock_irq(&davinci_nand_lock); - iounmap(info->base); - iounmap(info->vaddr); - nand_release(&info->mtd); clk_disable_unprepare(info->clk); - clk_put(info->clk); - - kfree(info); return 0; } From be0638d9e4795254e28e39eff61e38f47d240fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Thu, 7 Feb 2013 11:56:04 +0100 Subject: [PATCH 2432/4607] mtd: bcm47xxnflash: use pr_fmt for module prefix in messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: Artem Bityutskiy --- drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h | 4 ++++ drivers/mtd/nand/bcm47xxnflash/main.c | 4 ++-- drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h index 0bdb2ce4da75..c005a62330b1 100644 --- a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h +++ b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h @@ -1,6 +1,10 @@ #ifndef __BCM47XXNFLASH_H #define __BCM47XXNFLASH_H +#ifndef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#endif + #include #include diff --git a/drivers/mtd/nand/bcm47xxnflash/main.c b/drivers/mtd/nand/bcm47xxnflash/main.c index a52acdccd0c4..7bae569fdc79 100644 --- a/drivers/mtd/nand/bcm47xxnflash/main.c +++ b/drivers/mtd/nand/bcm47xxnflash/main.c @@ -9,14 +9,14 @@ * */ +#include "bcm47xxnflash.h" + #include #include #include #include #include -#include "bcm47xxnflash.h" - MODULE_DESCRIPTION("NAND flash driver for BCMA bus"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rafał Miłecki"); diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c index 86c9a79b89b3..37d5b8956694 100644 --- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c +++ b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c @@ -9,13 +9,13 @@ * */ +#include "bcm47xxnflash.h" + #include #include #include #include -#include "bcm47xxnflash.h" - /* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has * shown 164 retries as maxiumum. */ #define NFLASH_READY_RETRIES 1000 From 5444d639ec360679977758d5e896386c6b1babff Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 3 Dec 2012 08:36:03 -0500 Subject: [PATCH 2433/4607] powerpc/85xx: use for_each_compatible_node() macro Use for_each_compatible_node() macro instead of open coding it. Signed-off-by: Wei Yongjun Acked-by: Grant Likely Signed-off-by: Kumar Gala --- arch/powerpc/platforms/85xx/mpc85xx_mds.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index bd12588fa252..a7b3621a8df5 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -206,9 +206,7 @@ static void __init mpc85xx_mds_reset_ucc_phys(void) setbits8(&bcsr_regs[7], BCSR7_UCC12_GETHnRST); clrbits8(&bcsr_regs[8], BCSR8_UEM_MARVELL_RST); - for (np = NULL; (np = of_find_compatible_node(np, - "network", - "ucc_geth")) != NULL;) { + for_each_compatible_node(np, "network", "ucc_geth") { const unsigned int *prop; int ucc_num; From d1cf1c7db3460972fa93eb6816d9cae71a10e8c7 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 23 Jan 2013 15:13:30 -0500 Subject: [PATCH 2434/4607] powerpc/85xx: split sbc8548 dts file into pre and post chunks Updates to u-boot allow this board to boot off of either the 8MB soldered on flash, or the 64MB SODIMM flash. This is achieved by changing JP12 and SW2.8 which in turn swaps which flash device appears on /CS0 and /CS6 respectively. Since the flash devices are not the same size, this also changes the MTD memory map layout on the local bus. Here we split the common chunks out into a pre and post include, so they can be reused by an upcoming "alternative boot" dts file; leaving only the local bus chunk behind. No content changes are made at this point - it is just purely the move to using include files. Signed-off-by: Paul Gortmaker Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/sbc8548-post.dtsi | 295 ++++++++++++++++++++++ arch/powerpc/boot/dts/sbc8548-pre.dtsi | 52 ++++ arch/powerpc/boot/dts/sbc8548.dts | 322 +----------------------- 3 files changed, 351 insertions(+), 318 deletions(-) create mode 100644 arch/powerpc/boot/dts/sbc8548-post.dtsi create mode 100644 arch/powerpc/boot/dts/sbc8548-pre.dtsi diff --git a/arch/powerpc/boot/dts/sbc8548-post.dtsi b/arch/powerpc/boot/dts/sbc8548-post.dtsi new file mode 100644 index 000000000000..33a47e27a11e --- /dev/null +++ b/arch/powerpc/boot/dts/sbc8548-post.dtsi @@ -0,0 +1,295 @@ +/* + * SBC8548 Device Tree Source + * + * Copyright 2007 Wind River Systems Inc. + * + * Paul Gortmaker (see MAINTAINERS for contact information) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/{ + soc8548@e0000000 { + #address-cells = <1>; + #size-cells = <1>; + device_type = "soc"; + ranges = <0x00000000 0xe0000000 0x00100000>; + bus-frequency = <0>; + compatible = "simple-bus"; + + ecm-law@0 { + compatible = "fsl,ecm-law"; + reg = <0x0 0x1000>; + fsl,num-laws = <10>; + }; + + ecm@1000 { + compatible = "fsl,mpc8548-ecm", "fsl,ecm"; + reg = <0x1000 0x1000>; + interrupts = <17 2>; + interrupt-parent = <&mpic>; + }; + + memory-controller@2000 { + compatible = "fsl,mpc8548-memory-controller"; + reg = <0x2000 0x1000>; + interrupt-parent = <&mpic>; + interrupts = <0x12 0x2>; + }; + + L2: l2-cache-controller@20000 { + compatible = "fsl,mpc8548-l2-cache-controller"; + reg = <0x20000 0x1000>; + cache-line-size = <0x20>; // 32 bytes + cache-size = <0x80000>; // L2, 512K + interrupt-parent = <&mpic>; + interrupts = <0x10 0x2>; + }; + + i2c@3000 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <0>; + compatible = "fsl-i2c"; + reg = <0x3000 0x100>; + interrupts = <0x2b 0x2>; + interrupt-parent = <&mpic>; + dfsrr; + }; + + i2c@3100 { + #address-cells = <1>; + #size-cells = <0>; + cell-index = <1>; + compatible = "fsl-i2c"; + reg = <0x3100 0x100>; + interrupts = <0x2b 0x2>; + interrupt-parent = <&mpic>; + dfsrr; + }; + + dma@21300 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,mpc8548-dma", "fsl,eloplus-dma"; + reg = <0x21300 0x4>; + ranges = <0x0 0x21100 0x200>; + cell-index = <0>; + dma-channel@0 { + compatible = "fsl,mpc8548-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x0 0x80>; + cell-index = <0>; + interrupt-parent = <&mpic>; + interrupts = <20 2>; + }; + dma-channel@80 { + compatible = "fsl,mpc8548-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x80 0x80>; + cell-index = <1>; + interrupt-parent = <&mpic>; + interrupts = <21 2>; + }; + dma-channel@100 { + compatible = "fsl,mpc8548-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x100 0x80>; + cell-index = <2>; + interrupt-parent = <&mpic>; + interrupts = <22 2>; + }; + dma-channel@180 { + compatible = "fsl,mpc8548-dma-channel", + "fsl,eloplus-dma-channel"; + reg = <0x180 0x80>; + cell-index = <3>; + interrupt-parent = <&mpic>; + interrupts = <23 2>; + }; + }; + + enet0: ethernet@24000 { + #address-cells = <1>; + #size-cells = <1>; + cell-index = <0>; + device_type = "network"; + model = "eTSEC"; + compatible = "gianfar"; + reg = <0x24000 0x1000>; + ranges = <0x0 0x24000 0x1000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <0x1d 0x2 0x1e 0x2 0x22 0x2>; + interrupt-parent = <&mpic>; + tbi-handle = <&tbi0>; + phy-handle = <&phy0>; + + mdio@520 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,gianfar-mdio"; + reg = <0x520 0x20>; + + phy0: ethernet-phy@19 { + interrupt-parent = <&mpic>; + interrupts = <0x6 0x1>; + reg = <0x19>; + device_type = "ethernet-phy"; + }; + phy1: ethernet-phy@1a { + interrupt-parent = <&mpic>; + interrupts = <0x7 0x1>; + reg = <0x1a>; + device_type = "ethernet-phy"; + }; + tbi0: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + }; + + enet1: ethernet@25000 { + #address-cells = <1>; + #size-cells = <1>; + cell-index = <1>; + device_type = "network"; + model = "eTSEC"; + compatible = "gianfar"; + reg = <0x25000 0x1000>; + ranges = <0x0 0x25000 0x1000>; + local-mac-address = [ 00 00 00 00 00 00 ]; + interrupts = <0x23 0x2 0x24 0x2 0x28 0x2>; + interrupt-parent = <&mpic>; + tbi-handle = <&tbi1>; + phy-handle = <&phy1>; + + mdio@520 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,gianfar-tbi"; + reg = <0x520 0x20>; + + tbi1: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + }; + + serial0: serial@4500 { + cell-index = <0>; + device_type = "serial"; + compatible = "fsl,ns16550", "ns16550"; + reg = <0x4500 0x100>; // reg base, size + clock-frequency = <0>; // should we fill in in uboot? + interrupts = <0x2a 0x2>; + interrupt-parent = <&mpic>; + }; + + serial1: serial@4600 { + cell-index = <1>; + device_type = "serial"; + compatible = "fsl,ns16550", "ns16550"; + reg = <0x4600 0x100>; // reg base, size + clock-frequency = <0>; // should we fill in in uboot? + interrupts = <0x2a 0x2>; + interrupt-parent = <&mpic>; + }; + + global-utilities@e0000 { //global utilities reg + compatible = "fsl,mpc8548-guts"; + reg = <0xe0000 0x1000>; + fsl,has-rstcr; + }; + + crypto@30000 { + compatible = "fsl,sec2.1", "fsl,sec2.0"; + reg = <0x30000 0x10000>; + interrupts = <45 2>; + interrupt-parent = <&mpic>; + fsl,num-channels = <4>; + fsl,channel-fifo-len = <24>; + fsl,exec-units-mask = <0xfe>; + fsl,descriptor-types-mask = <0x12b0ebf>; + }; + + mpic: pic@40000 { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + reg = <0x40000 0x40000>; + compatible = "chrp,open-pic"; + device_type = "open-pic"; + }; + }; + + pci0: pci@e0008000 { + interrupt-map-mask = <0xf800 0x0 0x0 0x7>; + interrupt-map = < + /* IDSEL 0x01 (PCI-X slot) @66MHz */ + 0x0800 0x0 0x0 0x1 &mpic 0x2 0x1 + 0x0800 0x0 0x0 0x2 &mpic 0x3 0x1 + 0x0800 0x0 0x0 0x3 &mpic 0x4 0x1 + 0x0800 0x0 0x0 0x4 &mpic 0x1 0x1 + + /* IDSEL 0x11 (PCI, 3.3V 32bit) @33MHz */ + 0x8800 0x0 0x0 0x1 &mpic 0x2 0x1 + 0x8800 0x0 0x0 0x2 &mpic 0x3 0x1 + 0x8800 0x0 0x0 0x3 &mpic 0x4 0x1 + 0x8800 0x0 0x0 0x4 &mpic 0x1 0x1>; + + interrupt-parent = <&mpic>; + interrupts = <0x18 0x2>; + bus-range = <0 0>; + ranges = <0x02000000 0x0 0x80000000 0x80000000 0x0 0x10000000 + 0x01000000 0x0 0x00000000 0xe2000000 0x0 0x00800000>; + clock-frequency = <66000000>; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <0xe0008000 0x1000>; + compatible = "fsl,mpc8540-pcix", "fsl,mpc8540-pci"; + device_type = "pci"; + }; + + pci1: pcie@e000a000 { + interrupt-map-mask = <0xf800 0x0 0x0 0x7>; + interrupt-map = < + + /* IDSEL 0x0 (PEX) */ + 0x0000 0x0 0x0 0x1 &mpic 0x0 0x1 + 0x0000 0x0 0x0 0x2 &mpic 0x1 0x1 + 0x0000 0x0 0x0 0x3 &mpic 0x2 0x1 + 0x0000 0x0 0x0 0x4 &mpic 0x3 0x1>; + + interrupt-parent = <&mpic>; + interrupts = <0x1a 0x2>; + bus-range = <0x0 0xff>; + ranges = <0x02000000 0x0 0xa0000000 0xa0000000 0x0 0x10000000 + 0x01000000 0x0 0x00000000 0xe2800000 0x0 0x08000000>; + clock-frequency = <33000000>; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + reg = <0xe000a000 0x1000>; + compatible = "fsl,mpc8548-pcie"; + device_type = "pci"; + pcie@0 { + reg = <0x0 0x0 0x0 0x0 0x0>; + #size-cells = <2>; + #address-cells = <3>; + device_type = "pci"; + ranges = <0x02000000 0x0 0xa0000000 + 0x02000000 0x0 0xa0000000 + 0x0 0x10000000 + + 0x01000000 0x0 0x00000000 + 0x01000000 0x0 0x00000000 + 0x0 0x00800000>; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/sbc8548-pre.dtsi b/arch/powerpc/boot/dts/sbc8548-pre.dtsi new file mode 100644 index 000000000000..d8c66290c5b4 --- /dev/null +++ b/arch/powerpc/boot/dts/sbc8548-pre.dtsi @@ -0,0 +1,52 @@ +/* + * SBC8548 Device Tree Source + * + * Copyright 2007 Wind River Systems Inc. + * + * Paul Gortmaker (see MAINTAINERS for contact information) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/{ + model = "SBC8548"; + compatible = "SBC8548"; + #address-cells = <1>; + #size-cells = <1>; + + aliases { + ethernet0 = &enet0; + ethernet1 = &enet1; + serial0 = &serial0; + serial1 = &serial1; + pci0 = &pci0; + pci1 = &pci1; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,8548@0 { + device_type = "cpu"; + reg = <0>; + d-cache-line-size = <0x20>; // 32 bytes + i-cache-line-size = <0x20>; // 32 bytes + d-cache-size = <0x8000>; // L1, 32K + i-cache-size = <0x8000>; // L1, 32K + timebase-frequency = <0>; // From uboot + bus-frequency = <0>; + clock-frequency = <0>; + next-level-cache = <&L2>; + }; + }; + + memory { + device_type = "memory"; + reg = <0x00000000 0x10000000>; + }; + +}; diff --git a/arch/powerpc/boot/dts/sbc8548.dts b/arch/powerpc/boot/dts/sbc8548.dts index 77be77116c2e..ad8dc6808d2e 100644 --- a/arch/powerpc/boot/dts/sbc8548.dts +++ b/arch/powerpc/boot/dts/sbc8548.dts @@ -14,44 +14,9 @@ /dts-v1/; -/ { - model = "SBC8548"; - compatible = "SBC8548"; - #address-cells = <1>; - #size-cells = <1>; - - aliases { - ethernet0 = &enet0; - ethernet1 = &enet1; - serial0 = &serial0; - serial1 = &serial1; - pci0 = &pci0; - pci1 = &pci1; - }; - - cpus { - #address-cells = <1>; - #size-cells = <0>; - - PowerPC,8548@0 { - device_type = "cpu"; - reg = <0>; - d-cache-line-size = <0x20>; // 32 bytes - i-cache-line-size = <0x20>; // 32 bytes - d-cache-size = <0x8000>; // L1, 32K - i-cache-size = <0x8000>; // L1, 32K - timebase-frequency = <0>; // From uboot - bus-frequency = <0>; - clock-frequency = <0>; - next-level-cache = <&L2>; - }; - }; - - memory { - device_type = "memory"; - reg = <0x00000000 0x10000000>; - }; +/include/ "sbc8548-pre.dtsi" +/{ localbus@e0000000 { #address-cells = <2>; #size-cells = <1>; @@ -144,285 +109,6 @@ }; }; }; - - soc8548@e0000000 { - #address-cells = <1>; - #size-cells = <1>; - device_type = "soc"; - ranges = <0x00000000 0xe0000000 0x00100000>; - bus-frequency = <0>; - compatible = "simple-bus"; - - ecm-law@0 { - compatible = "fsl,ecm-law"; - reg = <0x0 0x1000>; - fsl,num-laws = <10>; - }; - - ecm@1000 { - compatible = "fsl,mpc8548-ecm", "fsl,ecm"; - reg = <0x1000 0x1000>; - interrupts = <17 2>; - interrupt-parent = <&mpic>; - }; - - memory-controller@2000 { - compatible = "fsl,mpc8548-memory-controller"; - reg = <0x2000 0x1000>; - interrupt-parent = <&mpic>; - interrupts = <0x12 0x2>; - }; - - L2: l2-cache-controller@20000 { - compatible = "fsl,mpc8548-l2-cache-controller"; - reg = <0x20000 0x1000>; - cache-line-size = <0x20>; // 32 bytes - cache-size = <0x80000>; // L2, 512K - interrupt-parent = <&mpic>; - interrupts = <0x10 0x2>; - }; - - i2c@3000 { - #address-cells = <1>; - #size-cells = <0>; - cell-index = <0>; - compatible = "fsl-i2c"; - reg = <0x3000 0x100>; - interrupts = <0x2b 0x2>; - interrupt-parent = <&mpic>; - dfsrr; - }; - - i2c@3100 { - #address-cells = <1>; - #size-cells = <0>; - cell-index = <1>; - compatible = "fsl-i2c"; - reg = <0x3100 0x100>; - interrupts = <0x2b 0x2>; - interrupt-parent = <&mpic>; - dfsrr; - }; - - dma@21300 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "fsl,mpc8548-dma", "fsl,eloplus-dma"; - reg = <0x21300 0x4>; - ranges = <0x0 0x21100 0x200>; - cell-index = <0>; - dma-channel@0 { - compatible = "fsl,mpc8548-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x0 0x80>; - cell-index = <0>; - interrupt-parent = <&mpic>; - interrupts = <20 2>; - }; - dma-channel@80 { - compatible = "fsl,mpc8548-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x80 0x80>; - cell-index = <1>; - interrupt-parent = <&mpic>; - interrupts = <21 2>; - }; - dma-channel@100 { - compatible = "fsl,mpc8548-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x100 0x80>; - cell-index = <2>; - interrupt-parent = <&mpic>; - interrupts = <22 2>; - }; - dma-channel@180 { - compatible = "fsl,mpc8548-dma-channel", - "fsl,eloplus-dma-channel"; - reg = <0x180 0x80>; - cell-index = <3>; - interrupt-parent = <&mpic>; - interrupts = <23 2>; - }; - }; - - enet0: ethernet@24000 { - #address-cells = <1>; - #size-cells = <1>; - cell-index = <0>; - device_type = "network"; - model = "eTSEC"; - compatible = "gianfar"; - reg = <0x24000 0x1000>; - ranges = <0x0 0x24000 0x1000>; - local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <0x1d 0x2 0x1e 0x2 0x22 0x2>; - interrupt-parent = <&mpic>; - tbi-handle = <&tbi0>; - phy-handle = <&phy0>; - - mdio@520 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,gianfar-mdio"; - reg = <0x520 0x20>; - - phy0: ethernet-phy@19 { - interrupt-parent = <&mpic>; - interrupts = <0x6 0x1>; - reg = <0x19>; - device_type = "ethernet-phy"; - }; - phy1: ethernet-phy@1a { - interrupt-parent = <&mpic>; - interrupts = <0x7 0x1>; - reg = <0x1a>; - device_type = "ethernet-phy"; - }; - tbi0: tbi-phy@11 { - reg = <0x11>; - device_type = "tbi-phy"; - }; - }; - }; - - enet1: ethernet@25000 { - #address-cells = <1>; - #size-cells = <1>; - cell-index = <1>; - device_type = "network"; - model = "eTSEC"; - compatible = "gianfar"; - reg = <0x25000 0x1000>; - ranges = <0x0 0x25000 0x1000>; - local-mac-address = [ 00 00 00 00 00 00 ]; - interrupts = <0x23 0x2 0x24 0x2 0x28 0x2>; - interrupt-parent = <&mpic>; - tbi-handle = <&tbi1>; - phy-handle = <&phy1>; - - mdio@520 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "fsl,gianfar-tbi"; - reg = <0x520 0x20>; - - tbi1: tbi-phy@11 { - reg = <0x11>; - device_type = "tbi-phy"; - }; - }; - }; - - serial0: serial@4500 { - cell-index = <0>; - device_type = "serial"; - compatible = "fsl,ns16550", "ns16550"; - reg = <0x4500 0x100>; // reg base, size - clock-frequency = <0>; // should we fill in in uboot? - interrupts = <0x2a 0x2>; - interrupt-parent = <&mpic>; - }; - - serial1: serial@4600 { - cell-index = <1>; - device_type = "serial"; - compatible = "fsl,ns16550", "ns16550"; - reg = <0x4600 0x100>; // reg base, size - clock-frequency = <0>; // should we fill in in uboot? - interrupts = <0x2a 0x2>; - interrupt-parent = <&mpic>; - }; - - global-utilities@e0000 { //global utilities reg - compatible = "fsl,mpc8548-guts"; - reg = <0xe0000 0x1000>; - fsl,has-rstcr; - }; - - crypto@30000 { - compatible = "fsl,sec2.1", "fsl,sec2.0"; - reg = <0x30000 0x10000>; - interrupts = <45 2>; - interrupt-parent = <&mpic>; - fsl,num-channels = <4>; - fsl,channel-fifo-len = <24>; - fsl,exec-units-mask = <0xfe>; - fsl,descriptor-types-mask = <0x12b0ebf>; - }; - - mpic: pic@40000 { - interrupt-controller; - #address-cells = <0>; - #interrupt-cells = <2>; - reg = <0x40000 0x40000>; - compatible = "chrp,open-pic"; - device_type = "open-pic"; - }; - }; - - pci0: pci@e0008000 { - interrupt-map-mask = <0xf800 0x0 0x0 0x7>; - interrupt-map = < - /* IDSEL 0x01 (PCI-X slot) @66MHz */ - 0x0800 0x0 0x0 0x1 &mpic 0x2 0x1 - 0x0800 0x0 0x0 0x2 &mpic 0x3 0x1 - 0x0800 0x0 0x0 0x3 &mpic 0x4 0x1 - 0x0800 0x0 0x0 0x4 &mpic 0x1 0x1 - - /* IDSEL 0x11 (PCI, 3.3V 32bit) @33MHz */ - 0x8800 0x0 0x0 0x1 &mpic 0x2 0x1 - 0x8800 0x0 0x0 0x2 &mpic 0x3 0x1 - 0x8800 0x0 0x0 0x3 &mpic 0x4 0x1 - 0x8800 0x0 0x0 0x4 &mpic 0x1 0x1>; - - interrupt-parent = <&mpic>; - interrupts = <0x18 0x2>; - bus-range = <0 0>; - ranges = <0x02000000 0x0 0x80000000 0x80000000 0x0 0x10000000 - 0x01000000 0x0 0x00000000 0xe2000000 0x0 0x00800000>; - clock-frequency = <66000000>; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - reg = <0xe0008000 0x1000>; - compatible = "fsl,mpc8540-pcix", "fsl,mpc8540-pci"; - device_type = "pci"; - }; - - pci1: pcie@e000a000 { - interrupt-map-mask = <0xf800 0x0 0x0 0x7>; - interrupt-map = < - - /* IDSEL 0x0 (PEX) */ - 0x0000 0x0 0x0 0x1 &mpic 0x0 0x1 - 0x0000 0x0 0x0 0x2 &mpic 0x1 0x1 - 0x0000 0x0 0x0 0x3 &mpic 0x2 0x1 - 0x0000 0x0 0x0 0x4 &mpic 0x3 0x1>; - - interrupt-parent = <&mpic>; - interrupts = <0x1a 0x2>; - bus-range = <0x0 0xff>; - ranges = <0x02000000 0x0 0xa0000000 0xa0000000 0x0 0x10000000 - 0x01000000 0x0 0x00000000 0xe2800000 0x0 0x08000000>; - clock-frequency = <33000000>; - #interrupt-cells = <1>; - #size-cells = <2>; - #address-cells = <3>; - reg = <0xe000a000 0x1000>; - compatible = "fsl,mpc8548-pcie"; - device_type = "pci"; - pcie@0 { - reg = <0x0 0x0 0x0 0x0 0x0>; - #size-cells = <2>; - #address-cells = <3>; - device_type = "pci"; - ranges = <0x02000000 0x0 0xa0000000 - 0x02000000 0x0 0xa0000000 - 0x0 0x10000000 - - 0x01000000 0x0 0x00000000 - 0x01000000 0x0 0x00000000 - 0x0 0x00800000>; - }; - }; }; + +/include/ "sbc8548-post.dtsi" From 7e83f2ad3eeda5ebe701413918957b7d222bcdfd Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 23 Jan 2013 15:13:31 -0500 Subject: [PATCH 2435/4607] powerpc/85xx: update sbc8548 flash information to match recent u-boot The original memory map for the sbc8548 had the 64MB SODIMM flash device misaligned by 8MB to allow a window of address space for the soldered on 8MB device -- i.e. start end CS width Desc. ---------------------------------------------------------- fb80_0000 ff7f_ffff CS6 32 SODIMM flash (64MB) ff80_0000 ffff_ffff CS0 8 Boot flash (8MB) However, if we want to change the configuration so that it boots off the 64MB flash, it is in turn then aligned with a 64MB boundary, starting at fc00_0000 (and the 8MB @ fb80_0000 -> fbff_ffff). This makes for complicated updates, since what is the beginning of the physical device is 8MB into its address space in the default configuration shown above. This issue was fixed as of u-boot commit 3fd673cf363bc86ed42eff713d4 ("sbc8548: relocate 64MB user flash to sane boundary") -- in which the SODIMM was mapped to ec00_0000 (natively aligned under efff_ffff) and so when JP12/SW2.8 are switched, it will be a a simple 0xec --> 0xfc mapping between the two instances. Here we make the associated changes in the localbus flash memory map in the dts file: indicating the 64MB device starts at ec00_0000 and that the tail end of the 64MB device (last 2 sectors) can contain a bootloader image. The partitions for both flash devices get a clean-up; there were non-meaningful assignments in there that probably originated from the MPC8548CDS on which the file was based on. Now there is just the categorization of free space and bootloader images. Signed-off-by: Paul Gortmaker Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/sbc8548.dts | 36 ++++++++++++++----------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/arch/powerpc/boot/dts/sbc8548.dts b/arch/powerpc/boot/dts/sbc8548.dts index ad8dc6808d2e..1df2a0955668 100644 --- a/arch/powerpc/boot/dts/sbc8548.dts +++ b/arch/powerpc/boot/dts/sbc8548.dts @@ -28,23 +28,25 @@ 0x3 0x0 0xf0000000 0x04000000 /*64MB SDRAM*/ 0x4 0x0 0xf4000000 0x04000000 /*64MB SDRAM*/ 0x5 0x0 0xf8000000 0x00b10000 /* EPLD */ - 0x6 0x0 0xfb800000 0x04000000>; /*64MB Flash*/ + 0x6 0x0 0xec000000 0x04000000>; /*64MB Flash*/ flash@0,0 { #address-cells = <1>; #size-cells = <1>; - compatible = "cfi-flash"; + compatible = "intel,JS28F640", "cfi-flash"; reg = <0x0 0x0 0x800000>; bank-width = <1>; device-width = <1>; partition@0x0 { label = "space"; - reg = <0x00000000 0x00100000>; + /* FF800000 -> FFF9FFFF */ + reg = <0x00000000 0x007a0000>; }; - partition@0x100000 { + partition@0x7a0000 { label = "bootloader"; - reg = <0x00100000 0x00700000>; + /* FFFA0000 -> FFFFFFFF */ + reg = <0x007a0000 0x00060000>; read-only; }; }; @@ -87,25 +89,19 @@ #address-cells = <1>; #size-cells = <1>; reg = <0x6 0x0 0x04000000>; - compatible = "cfi-flash"; + compatible = "intel,JS28F128", "cfi-flash"; bank-width = <4>; device-width = <1>; partition@0x0 { - label = "bootloader"; - reg = <0x00000000 0x00100000>; - read-only; - }; - partition@0x00100000 { - label = "file-system"; - reg = <0x00100000 0x01f00000>; - }; - partition@0x02000000 { - label = "boot-config"; - reg = <0x02000000 0x00100000>; - }; - partition@0x02100000 { label = "space"; - reg = <0x02100000 0x01f00000>; + /* EC000000 -> EFEFFFFF */ + reg = <0x00000000 0x03f00000>; + }; + partition@0x03f00000 { + label = "bootloader"; + /* EFF00000 -> EFFFFFFF */ + reg = <0x03f00000 0x00100000>; + read-only; }; }; }; From dcc8722a643d700b402c0477a5259f9cabb60e33 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 23 Jan 2013 15:13:32 -0500 Subject: [PATCH 2436/4607] powerpc/85xx: add alternate dts file for sbc8548 boot via SODIMM By moving the two JP12 jumpers 90 degrees, and switching the setting of SW2.8, the sbc8548 can be configured to boot off the alternate 64MB SODIMM, which when populated with u-boot can be a handy recovery option, in case the u-boot in the 8MB soldered on flash gets corrupted. Here we add an alternate dts file to match that configuration. To better highlight the differences, the output from the u-boot "fli" command is shown for the normal configuration and then the alternate configuration. Normal: ----------------------- Bank # 1: CFI conformant flash (8 x 8) Size: 8 MB in 64 Sectors Intel Extended command set, Manufacturer ID: 0x89, Device ID: 0x17 Erase timeout: 4096 ms, write timeout: 1 ms Buffer write timeout: 2 ms, buffer size: 32 bytes Sector Start Addresses: FF800000 E FF820000 E FF840000 E FF860000 E FF880000 E [...] FFEE0000 E FFF00000 E FFF20000 E FFF40000 E FFF60000 E FFF80000 FFFA0000 RO FFFC0000 RO FFFE0000 RO Bank # 2: CFI conformant flash (32 x 8) Size: 64 MB in 128 Sectors Intel Extended command set, Manufacturer ID: 0x89, Device ID: 0x18 Erase timeout: 4096 ms, write timeout: 1 ms Buffer write timeout: 2 ms, buffer size: 32 bytes Sector Start Addresses: EC000000 E EC080000 E EC100000 E EC180000 E EC200000 E [...] EFC00000 E EFC80000 E EFD00000 E EFD80000 E EFE00000 E EFE80000 E EFF00000 EFF80000 ----------------------- Alternate: ----------------------- Bank # 1: CFI conformant flash (32 x 8) Size: 64 MB in 128 Sectors Intel Extended command set, Manufacturer ID: 0x89, Device ID: 0x18 Erase timeout: 4096 ms, write timeout: 1 ms Buffer write timeout: 2 ms, buffer size: 32 bytes Sector Start Addresses: FC000000 E FC080000 E FC100000 E FC180000 E FC200000 E [...] FFC00000 E FFC80000 E FFD00000 E FFD80000 E FFE00000 E FFE80000 E FFF00000 RO FFF80000 RO Bank # 2: CFI conformant flash (8 x 8) Size: 8 MB in 64 Sectors Intel Extended command set, Manufacturer ID: 0x89, Device ID: 0x17 Erase timeout: 4096 ms, write timeout: 1 ms Buffer write timeout: 2 ms, buffer size: 32 bytes Sector Start Addresses: EF800000 E EF820000 E EF840000 E EF860000 E EF880000 E [...] EFEE0000 E EFF00000 E EFF20000 E EFF40000 E EFF60000 E EFF80000 E EFFA0000 EFFC0000 EFFE0000 ----------------------- Signed-off-by: Paul Gortmaker Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/sbc8548-altflash.dts | 115 +++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 arch/powerpc/boot/dts/sbc8548-altflash.dts diff --git a/arch/powerpc/boot/dts/sbc8548-altflash.dts b/arch/powerpc/boot/dts/sbc8548-altflash.dts new file mode 100644 index 000000000000..0b38a0defd2c --- /dev/null +++ b/arch/powerpc/boot/dts/sbc8548-altflash.dts @@ -0,0 +1,115 @@ +/* + * SBC8548 Device Tree Source + * + * Configured for booting off the alternate (64MB SODIMM) flash. + * Requires switching JP12 jumpers and changing SW2.8 setting. + * + * Copyright 2013 Wind River Systems Inc. + * + * Paul Gortmaker (see MAINTAINERS for contact information) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + + +/dts-v1/; + +/include/ "sbc8548-pre.dtsi" + +/{ + localbus@e0000000 { + #address-cells = <2>; + #size-cells = <1>; + compatible = "simple-bus"; + reg = <0xe0000000 0x5000>; + interrupt-parent = <&mpic>; + + ranges = <0x0 0x0 0xfc000000 0x04000000 /*64MB Flash*/ + 0x3 0x0 0xf0000000 0x04000000 /*64MB SDRAM*/ + 0x4 0x0 0xf4000000 0x04000000 /*64MB SDRAM*/ + 0x5 0x0 0xf8000000 0x00b10000 /* EPLD */ + 0x6 0x0 0xef800000 0x00800000>; /*8MB Flash*/ + + flash@0,0 { + #address-cells = <1>; + #size-cells = <1>; + reg = <0x0 0x0 0x04000000>; + compatible = "intel,JS28F128", "cfi-flash"; + bank-width = <4>; + device-width = <1>; + partition@0x0 { + label = "space"; + /* FC000000 -> FFEFFFFF */ + reg = <0x00000000 0x03f00000>; + }; + partition@0x03f00000 { + label = "bootloader"; + /* FFF00000 -> FFFFFFFF */ + reg = <0x03f00000 0x00100000>; + read-only; + }; + }; + + + epld@5,0 { + compatible = "wrs,epld-localbus"; + #address-cells = <2>; + #size-cells = <1>; + reg = <0x5 0x0 0x00b10000>; + ranges = < + 0x0 0x0 0x5 0x000000 0x1fff /* LED */ + 0x1 0x0 0x5 0x100000 0x1fff /* Switches */ + 0x3 0x0 0x5 0x300000 0x1fff /* HW Rev. */ + 0xb 0x0 0x5 0xb00000 0x1fff /* EEPROM */ + >; + + led@0,0 { + compatible = "led"; + reg = <0x0 0x0 0x1fff>; + }; + + switches@1,0 { + compatible = "switches"; + reg = <0x1 0x0 0x1fff>; + }; + + hw-rev@3,0 { + compatible = "hw-rev"; + reg = <0x3 0x0 0x1fff>; + }; + + eeprom@b,0 { + compatible = "eeprom"; + reg = <0xb 0 0x1fff>; + }; + + }; + + alt-flash@6,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "intel,JS28F640", "cfi-flash"; + reg = <0x6 0x0 0x800000>; + bank-width = <1>; + device-width = <1>; + partition@0x0 { + label = "space"; + /* EF800000 -> EFF9FFFF */ + reg = <0x00000000 0x007a0000>; + }; + partition@0x7a0000 { + label = "bootloader"; + /* EFFA0000 -> EFFFFFFF */ + reg = <0x007a0000 0x00060000>; + read-only; + }; + }; + + + }; +}; + +/include/ "sbc8548-post.dtsi" From d5bc813f407b640830c29b8e45fae56e9ce11e7a Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 23 Jan 2013 15:13:33 -0500 Subject: [PATCH 2437/4607] powerpc/85xx: enable MTD options in sbc8548 defconfig This board has soldered on flash, and a SODIMM flash module. Both can be used for booting, via switching JP12 and SW2.8 and using the sbc8548-altflash.dts when booting from SODIMM. Here we enable MTD in kernel so that we can see the bootloader (and other flash sectors) from linux. Normal configuration: root@sbc8548:~# cat /proc/mtd dev: size erasesize name mtd0: 007a0000 00020000 "space" mtd1: 00060000 00020000 "bootloader" mtd2: 03f00000 00080000 "space" mtd3: 00100000 00080000 "bootloader" root@sbc8548:~# dd if=/dev/mtd1 count=1 bs=48|hexdump -C 1+0 records in 1+0 records out 00000000 27 05 19 56 55 2d 42 6f 6f 74 20 32 30 31 32 2e |'..VU-Boot 2012.| 00000010 31 30 2d 64 69 72 74 79 20 28 4a 61 6e 20 31 39 |10-dirty (Jan 19| 00000020 20 32 30 31 33 20 2d 20 31 39 3a 34 30 3a 31 31 | 2013 - 19:40:11| 00000030 root@sbc8548:~# dd if=/dev/mtd3 count=1 bs=48|hexdump -C 1+0 records in 1+0 records out 00000000 27 05 19 56 55 2d 42 6f 6f 74 20 32 30 31 32 2e |'..VU-Boot 2012.| 00000010 31 30 2d 64 69 72 74 79 20 28 44 65 63 20 31 33 |10-dirty (Dec 13| 00000020 20 32 30 31 32 20 2d 20 31 35 3a 30 30 3a 30 37 | 2012 - 15:00:07| 00000030 root@sbc8548:~# Alternate configuration, with sbc8548-altflash.dts: root@sbc8548:~# cat /proc/mtd dev: size erasesize name mtd0: 03f00000 00080000 "space" mtd1: 00100000 00080000 "bootloader" mtd2: 007a0000 00020000 "space" mtd3: 00060000 00020000 "bootloader" root@sbc8548:~# dd if=/dev/mtd1 count=1 bs=48|hexdump -C 1+0 records in 1+0 records out 00000000 27 05 19 56 55 2d 42 6f 6f 74 20 32 30 31 32 2e |'..VU-Boot 2012.| 00000010 31 30 2d 64 69 72 74 79 20 28 44 65 63 20 31 33 |10-dirty (Dec 13| 00000020 20 32 30 31 32 20 2d 20 31 35 3a 30 30 3a 30 37 | 2012 - 15:00:07| 00000030 root@sbc8548:~# dd if=/dev/mtd3 count=1 bs=48|hexdump -C 1+0 records in 1+0 records out 00000000 27 05 19 56 55 2d 42 6f 6f 74 20 32 30 31 32 2e |'..VU-Boot 2012.| 00000010 31 30 2d 64 69 72 74 79 20 28 4a 61 6e 20 31 39 |10-dirty (Jan 19| 00000020 20 32 30 31 33 20 2d 20 31 39 3a 34 30 3a 31 31 | 2013 - 19:40:11| 00000030 root@sbc8548:~# Note that in the latter, the larger SODIMM device appears 1st, as mtd0 and mtd1, as indicated in the sizes, and in the date of the u-boot image. The kernel configuration is the same in both cases; only the dtb needs to be changed in accordance with the JP12/SW2.8 settings. Signed-off-by: Paul Gortmaker Signed-off-by: Kumar Gala --- arch/powerpc/configs/85xx/sbc8548_defconfig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/arch/powerpc/configs/85xx/sbc8548_defconfig b/arch/powerpc/configs/85xx/sbc8548_defconfig index 5b2b651dfb98..008a7a47b89b 100644 --- a/arch/powerpc/configs/85xx/sbc8548_defconfig +++ b/arch/powerpc/configs/85xx/sbc8548_defconfig @@ -55,3 +55,22 @@ CONFIG_ROOT_NFS=y # CONFIG_RCU_CPU_STALL_DETECTOR is not set CONFIG_SYSCTL_SYSCALL_CHECK=y # CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_MTD=y +CONFIG_MTD_OF_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_GEN_PROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_NOSWAP=y +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +CONFIG_MTD_CFI_I4=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_UTIL=y +CONFIG_MTD_PHYSMAP_OF=y From 2d1efdb232bbb4b5253147ead0c8ad8dc7f6c6c3 Mon Sep 17 00:00:00 2001 From: Vakul Garg Date: Wed, 23 Jan 2013 12:51:08 +0530 Subject: [PATCH 2438/4607] crypto: caam - Added property fsl, sec-era in SEC4.0 device tree binding. This new property defines the era of the particular SEC version. The compatible property in device tree "crypto" node has been updated not to contain SEC era numbers. Signed-off-by: Vakul Garg Signed-off-by: Kumar Gala --- .../devicetree/bindings/crypto/fsl-sec4.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt index fc9ce6f1688c..6d21c0288e9e 100644 --- a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt +++ b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt @@ -54,8 +54,13 @@ PROPERTIES - compatible Usage: required Value type: - Definition: Must include "fsl,sec-v4.0". Also includes SEC - ERA versions (optional) with which the device is compatible. + Definition: Must include "fsl,sec-v4.0" + + - fsl,sec-era + Usage: optional + Value type: + Definition: A standard property. Define the 'ERA' of the SEC + device. - #address-cells Usage: required @@ -107,7 +112,8 @@ PROPERTIES EXAMPLE crypto@300000 { - compatible = "fsl,sec-v4.0", "fsl,sec-era-v2.0"; + compatible = "fsl,sec-v4.0"; + fsl,sec-era = <0x2>; #address-cells = <1>; #size-cells = <1>; reg = <0x300000 0x10000>; From 0408753faeb53b37628c71c92ee6a7a422042607 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 17 Jan 2013 16:34:33 -0600 Subject: [PATCH 2439/4607] powerpc/85xx: describe the PAMU topology in the device tree The PAMU caches use the LIODNs to determine which cache lines hold the entries for the corresponding LIODs. The LIODNs must therefore be carefully assigned to avoid cache thrashing -- two active LIODs with LIODNs that put them in the same cache line. Currently, LIODNs are statically assigned by U-Boot, but this has limitations. LIODNs are assigned even for devices that may be disabled or unused by the kernel. Static assignments also do not allow for device drivers which may know which LIODs can be used simultaneously. In other words, we really should assign LIODNs dynamically in Linux. To do that, we need to describe the PAMU device and cache topologies in the device trees. Signed-off-by: Timur Tabi Acked-by: Stuart Yoder Acked-by: Scott Wood Signed-off-by: Kumar Gala --- .../devicetree/bindings/powerpc/fsl/guts.txt | 13 +- .../devicetree/bindings/powerpc/fsl/pamu.txt | 140 ++++++++++++++++++ arch/powerpc/boot/dts/fsl/p2041si-post.dtsi | 87 +++++++++-- arch/powerpc/boot/dts/fsl/p3041si-post.dtsi | 87 +++++++++-- arch/powerpc/boot/dts/fsl/p4080si-post.dtsi | 68 ++++++++- arch/powerpc/boot/dts/fsl/p5020si-post.dtsi | 92 ++++++++++-- arch/powerpc/boot/dts/fsl/p5040si-post.dtsi | 92 ++++++++++-- 7 files changed, 530 insertions(+), 49 deletions(-) create mode 100644 Documentation/devicetree/bindings/powerpc/fsl/pamu.txt diff --git a/Documentation/devicetree/bindings/powerpc/fsl/guts.txt b/Documentation/devicetree/bindings/powerpc/fsl/guts.txt index 9e7a2417dac5..7f150b5012cc 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/guts.txt +++ b/Documentation/devicetree/bindings/powerpc/fsl/guts.txt @@ -17,9 +17,20 @@ Recommended properties: contains a functioning "reset control register" (i.e. the board is wired to reset upon setting the HRESET_REQ bit in this register). -Example: + - fsl,liodn-bits : Indicates the number of defined bits in the LIODN + registers, for those SOCs that have a PAMU device. + +Examples: global-utilities@e0000 { /* global utilities block */ compatible = "fsl,mpc8548-guts"; reg = ; fsl,has-rstcr; }; + + guts: global-utilities@e0000 { + compatible = "fsl,qoriq-device-config-1.0"; + reg = <0xe0000 0xe00>; + fsl,has-rstcr; + #sleep-cells = <1>; + fsl,liodn-bits = <12>; + }; diff --git a/Documentation/devicetree/bindings/powerpc/fsl/pamu.txt b/Documentation/devicetree/bindings/powerpc/fsl/pamu.txt new file mode 100644 index 000000000000..1f5e329f756c --- /dev/null +++ b/Documentation/devicetree/bindings/powerpc/fsl/pamu.txt @@ -0,0 +1,140 @@ +Freescale Peripheral Management Access Unit (PAMU) Device Tree Binding + +DESCRIPTION + +The PAMU is an I/O MMU that provides device-to-memory access control and +address translation capabilities. + +Required properties: + +- compatible : + First entry is a version-specific string, such as + "fsl,pamu-v1.0". The second is "fsl,pamu". +- ranges : + A standard property. Utilized to describe the memory mapped + I/O space utilized by the controller. The size should + be set to the total size of the register space of all + physically present PAMU controllers. For example, for + PAMU v1.0, on an SOC that has five PAMU devices, the size + is 0x5000. +- interrupts : + Interrupt mappings. The first tuple is the normal PAMU + interrupt, used for reporting access violations. The second + is for PAMU hardware errors, such as PAMU operation errors + and ECC errors. +- #address-cells: + A standard property. +- #size-cells : + A standard property. + +Optional properties: +- reg : + A standard property. It represents the CCSR registers of + all child PAMUs combined. Include it to provide support + for legacy drivers. +- interrupt-parent : + Phandle to interrupt controller + +Child nodes: + +Each child node represents one PAMU controller. Each SOC device that is +connected to a specific PAMU device should have a "fsl,pamu-phandle" property +that links to the corresponding specific child PAMU controller. + +- reg : + A standard property. Specifies the physical address and + length (relative to the parent 'ranges' property) of this + PAMU controller's configuration registers. The size should + be set to the size of this PAMU controllers's register space. + For PAMU v1.0, this size is 0x1000. +- fsl,primary-cache-geometry + : + Two cells that specify the geometry of the primary PAMU + cache. The first is the number of cache lines, and the + second is the number of "ways". For direct-mapped caches, + specify a value of 1. +- fsl,secondary-cache-geometry + : + Two cells that specify the geometry of the secondary PAMU + cache. The first is the number of cache lines, and the + second is the number of "ways". For direct-mapped caches, + specify a value of 1. + +Device nodes: + +Devices that have LIODNs need to specify links to the parent PAMU controller +(the actual PAMU controller that this device is connected to) and a pointer to +the LIODN register, if applicable. + +- fsl,iommu-parent + : + Phandle to the single, specific PAMU controller node to which + this device is connect. The PAMU topology is represented in + the device tree to assist code that dynamically determines the + best LIODN values to minimize PAMU cache thrashing. + +- fsl,liodn-reg : + Two cells that specify the location of the LIODN register + for this device. Required for devices that have a single + LIODN. The first cell is a phandle to a node that contains + the registers where the LIODN is to be set. The second is + the offset from the first "reg" resource of the node where + the specific LIODN register is located. + + +Example: + + iommu@20000 { + compatible = "fsl,pamu-v1.0", "fsl,pamu"; + reg = <0x20000 0x5000>; + ranges = <0 0x20000 0x5000>; + #address-cells = <1>; + #size-cells = <1>; + interrupts = < + 24 2 0 0 + 16 2 1 30>; + + pamu0: pamu@0 { + reg = <0 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu1: pamu@1000 { + reg = <0x1000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu2: pamu@2000 { + reg = <0x2000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu3: pamu@3000 { + reg = <0x3000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu4: pamu@4000 { + reg = <0x4000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + }; + + guts: global-utilities@e0000 { + compatible = "fsl,qoriq-device-config-1.0"; + reg = <0xe0000 0xe00>; + fsl,has-rstcr; + #sleep-cells = <1>; + fsl,liodn-bits = <12>; + }; + +/include/ "qoriq-dma-0.dtsi" + dma@100300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x584>; /* DMA2LIODNR */ + }; diff --git a/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi b/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi index 531eab82c6c9..69ac1acd4349 100644 --- a/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi @@ -48,6 +48,8 @@ bus-range = <0x0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 15>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x500>; /* PEX1LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -75,6 +77,8 @@ bus-range = <0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 14>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x504>; /* PEX2LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -102,6 +106,8 @@ bus-range = <0x0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 13>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x508>; /* PEX3LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -125,18 +131,21 @@ interrupts = <16 2 1 11>; #address-cells = <2>; #size-cells = <2>; + fsl,iommu-parent = <&pamu0>; ranges; port1 { #address-cells = <2>; #size-cells = <2>; cell-index = <1>; + fsl,liodn-reg = <&guts 0x510>; /* RIO1LIODNR */ }; port2 { #address-cells = <2>; #size-cells = <2>; cell-index = <2>; + fsl,liodn-reg = <&guts 0x514>; /* RIO2LIODNR */ }; }; @@ -246,10 +255,37 @@ iommu@20000 { compatible = "fsl,pamu-v1.0", "fsl,pamu"; - reg = <0x20000 0x4000>; + reg = <0x20000 0x4000>; /* for compatibility with older PAMU drivers */ + ranges = <0 0x20000 0x4000>; + #address-cells = <1>; + #size-cells = <1>; interrupts = < 24 2 0 0 16 2 1 30>; + + pamu0: pamu@0 { + reg = <0 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu1: pamu@1000 { + reg = <0x1000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu2: pamu@2000 { + reg = <0x2000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu3: pamu@3000 { + reg = <0x3000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; }; /include/ "qoriq-mpic.dtsi" @@ -291,7 +327,17 @@ }; /include/ "qoriq-dma-0.dtsi" + dma@100300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x580>; /* DMA1LIODNR */ + }; + /include/ "qoriq-dma-1.dtsi" + dma@101300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x584>; /* DMA2LIODNR */ + }; + /include/ "qoriq-espi-0.dtsi" spi@110000 { fsl,espi-num-chipselects = <4>; @@ -299,6 +345,8 @@ /include/ "qoriq-esdhc-0.dtsi" sdhc@114000 { + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */ sdhci,auto-cmd12; }; @@ -308,20 +356,37 @@ /include/ "qoriq-duart-1.dtsi" /include/ "qoriq-gpio-0.dtsi" /include/ "qoriq-usb2-mph-0.dtsi" - usb0: usb@210000 { - compatible = "fsl-usb2-mph-v1.6", "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; - phy_type = "utmi"; - port0; - }; + usb0: usb@210000 { + compatible = "fsl-usb2-mph-v1.6", "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; + phy_type = "utmi"; + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x520>; /* USB1LIODNR */ + port0; + }; /include/ "qoriq-usb2-dr-0.dtsi" - usb1: usb@211000 { - compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; - dr_mode = "host"; - phy_type = "utmi"; - }; + usb1: usb@211000 { + compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x524>; /* USB2LIODNR */ + dr_mode = "host"; + phy_type = "utmi"; + }; /include/ "qoriq-sata2-0.dtsi" + sata@220000 { + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x550>; /* SATA1LIODNR */ + }; + /include/ "qoriq-sata2-1.dtsi" + sata@221000 { + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x554>; /* SATA2LIODNR */ + }; + /include/ "qoriq-sec4.2-0.dtsi" +crypto: crypto@300000 { + fsl,iommu-parent = <&pamu1>; + }; }; diff --git a/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi b/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi index af4ebc8009e3..9b5a81a4529c 100644 --- a/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi @@ -48,6 +48,8 @@ bus-range = <0x0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 15>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x500>; /* PEX1LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -75,6 +77,8 @@ bus-range = <0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 14>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x504>; /* PEX2LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -102,6 +106,8 @@ bus-range = <0x0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 13>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x508>; /* PEX3LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -152,18 +158,21 @@ interrupts = <16 2 1 11>; #address-cells = <2>; #size-cells = <2>; + fsl,iommu-parent = <&pamu0>; ranges; port1 { #address-cells = <2>; #size-cells = <2>; cell-index = <1>; + fsl,liodn-reg = <&guts 0x510>; /* RIO1LIODNR */ }; port2 { #address-cells = <2>; #size-cells = <2>; cell-index = <2>; + fsl,liodn-reg = <&guts 0x514>; /* RIO2LIODNR */ }; }; @@ -273,10 +282,37 @@ iommu@20000 { compatible = "fsl,pamu-v1.0", "fsl,pamu"; - reg = <0x20000 0x4000>; + reg = <0x20000 0x4000>; /* for compatibility with older PAMU drivers */ + ranges = <0 0x20000 0x4000>; + #address-cells = <1>; + #size-cells = <1>; interrupts = < 24 2 0 0 16 2 1 30>; + + pamu0: pamu@0 { + reg = <0 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu1: pamu@1000 { + reg = <0x1000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu2: pamu@2000 { + reg = <0x2000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu3: pamu@3000 { + reg = <0x3000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; }; /include/ "qoriq-mpic.dtsi" @@ -318,7 +354,17 @@ }; /include/ "qoriq-dma-0.dtsi" + dma@100300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x580>; /* DMA1LIODNR */ + }; + /include/ "qoriq-dma-1.dtsi" + dma@101300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x584>; /* DMA2LIODNR */ + }; + /include/ "qoriq-espi-0.dtsi" spi@110000 { fsl,espi-num-chipselects = <4>; @@ -326,6 +372,8 @@ /include/ "qoriq-esdhc-0.dtsi" sdhc@114000 { + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */ sdhci,auto-cmd12; }; @@ -335,20 +383,37 @@ /include/ "qoriq-duart-1.dtsi" /include/ "qoriq-gpio-0.dtsi" /include/ "qoriq-usb2-mph-0.dtsi" - usb0: usb@210000 { - compatible = "fsl-usb2-mph-v1.6", "fsl-usb2-mph"; - phy_type = "utmi"; - port0; - }; + usb0: usb@210000 { + compatible = "fsl-usb2-mph-v1.6", "fsl-usb2-mph"; + phy_type = "utmi"; + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x520>; /* USB1LIODNR */ + port0; + }; /include/ "qoriq-usb2-dr-0.dtsi" - usb1: usb@211000 { - compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; - dr_mode = "host"; - phy_type = "utmi"; - }; + usb1: usb@211000 { + compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x524>; /* USB2LIODNR */ + dr_mode = "host"; + phy_type = "utmi"; + }; /include/ "qoriq-sata2-0.dtsi" + sata@220000 { + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x550>; /* SATA1LIODNR */ + }; + /include/ "qoriq-sata2-1.dtsi" + sata@221000 { + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x554>; /* SATA2LIODNR */ + }; + /include/ "qoriq-sec4.2-0.dtsi" +crypto: crypto@300000 { + fsl,iommu-parent = <&pamu1>; + }; }; diff --git a/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi b/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi index 4f9c9f682ecf..dc82bcfa2f61 100644 --- a/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi @@ -48,6 +48,8 @@ bus-range = <0x0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 15>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x500>; /* PEX1LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -75,6 +77,8 @@ bus-range = <0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 14>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x504>; /* PEX2LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -102,6 +106,8 @@ bus-range = <0x0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 13>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x508>; /* PEX3LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -126,18 +132,21 @@ #address-cells = <2>; #size-cells = <2>; fsl,srio-rmu-handle = <&rmu>; + fsl,iommu-parent = <&pamu0>; ranges; port1 { #address-cells = <2>; #size-cells = <2>; cell-index = <1>; + fsl,liodn-reg = <&guts 0x510>; /* RIO1LIODNR */ }; port2 { #address-cells = <2>; #size-cells = <2>; cell-index = <2>; + fsl,liodn-reg = <&guts 0x514>; /* RIO2LIODNR */ }; }; @@ -281,13 +290,51 @@ iommu@20000 { compatible = "fsl,pamu-v1.0", "fsl,pamu"; - reg = <0x20000 0x5000>; + reg = <0x20000 0x5000>; /* for compatibility with older PAMU drivers */ + ranges = <0 0x20000 0x5000>; + #address-cells = <1>; + #size-cells = <1>; interrupts = < 24 2 0 0 16 2 1 30>; + + pamu0: pamu@0 { + reg = <0 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu1: pamu@1000 { + reg = <0x1000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu2: pamu@2000 { + reg = <0x2000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu3: pamu@3000 { + reg = <0x3000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu4: pamu@4000 { + reg = <0x4000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; }; /include/ "qoriq-rmu-0.dtsi" + rmu@d3000 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x540>; /* RMULIODNR */ + }; + /include/ "qoriq-mpic.dtsi" guts: global-utilities@e0000 { @@ -327,7 +374,17 @@ }; /include/ "qoriq-dma-0.dtsi" + dma@100300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x580>; /* DMA1LIODNR */ + }; + /include/ "qoriq-dma-1.dtsi" + dma@101300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x584>; /* DMA2LIODNR */ + }; + /include/ "qoriq-espi-0.dtsi" spi@110000 { fsl,espi-num-chipselects = <4>; @@ -335,6 +392,8 @@ /include/ "qoriq-esdhc-0.dtsi" sdhc@114000 { + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */ voltage-ranges = <3300 3300>; sdhci,auto-cmd12; }; @@ -347,11 +406,18 @@ /include/ "qoriq-usb2-mph-0.dtsi" usb@210000 { compatible = "fsl-usb2-mph-v1.6", "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x520>; /* USB1LIODNR */ port0; }; /include/ "qoriq-usb2-dr-0.dtsi" usb@211000 { compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x524>; /* USB2LIODNR */ }; /include/ "qoriq-sec4.0-0.dtsi" +crypto: crypto@300000 { + fsl,iommu-parent = <&pamu1>; + }; }; diff --git a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi index 5d7205b7bb05..9ea77c3513f6 100644 --- a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi @@ -48,6 +48,8 @@ bus-range = <0x0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 15>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x500>; /* PEX1LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -75,6 +77,8 @@ bus-range = <0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 14>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x504>; /* PEX2LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -102,6 +106,8 @@ bus-range = <0x0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 13>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x508>; /* PEX3LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -129,6 +135,8 @@ bus-range = <0x0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 12>; + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x50c>; /* PEX4LIODNR */ pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -152,18 +160,21 @@ interrupts = <16 2 1 11>; #address-cells = <2>; #size-cells = <2>; + fsl,iommu-parent = <&pamu0>; ranges; port1 { #address-cells = <2>; #size-cells = <2>; cell-index = <1>; + fsl,liodn-reg = <&guts 0x510>; /* RIO1LIODNR */ }; port2 { #address-cells = <2>; #size-cells = <2>; cell-index = <2>; + fsl,liodn-reg = <&guts 0x514>; /* RIO2LIODNR */ }; }; @@ -276,10 +287,37 @@ iommu@20000 { compatible = "fsl,pamu-v1.0", "fsl,pamu"; - reg = <0x20000 0x4000>; + reg = <0x20000 0x4000>; /* for compatibility with older PAMU drivers */ + ranges = <0 0x20000 0x4000>; + #address-cells = <1>; + #size-cells = <1>; interrupts = < 24 2 0 0 16 2 1 30>; + + pamu0: pamu@0 { + reg = <0 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu1: pamu@1000 { + reg = <0x1000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu2: pamu@2000 { + reg = <0x2000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu3: pamu@3000 { + reg = <0x3000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; }; /include/ "qoriq-mpic.dtsi" @@ -321,7 +359,17 @@ }; /include/ "qoriq-dma-0.dtsi" + dma@100300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x580>; /* DMA1LIODNR */ + }; + /include/ "qoriq-dma-1.dtsi" + dma@101300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x584>; /* DMA2LIODNR */ + }; + /include/ "qoriq-espi-0.dtsi" spi@110000 { fsl,espi-num-chipselects = <4>; @@ -329,6 +377,8 @@ /include/ "qoriq-esdhc-0.dtsi" sdhc@114000 { + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */ sdhci,auto-cmd12; }; @@ -338,21 +388,41 @@ /include/ "qoriq-duart-1.dtsi" /include/ "qoriq-gpio-0.dtsi" /include/ "qoriq-usb2-mph-0.dtsi" - usb0: usb@210000 { - compatible = "fsl-usb2-mph-v1.6", "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; - phy_type = "utmi"; - port0; - }; + usb0: usb@210000 { + compatible = "fsl-usb2-mph-v1.6", "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x520>; /* USB1LIODNR */ + phy_type = "utmi"; + port0; + }; /include/ "qoriq-usb2-dr-0.dtsi" - usb1: usb@211000 { - compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; - dr_mode = "host"; - phy_type = "utmi"; - }; + usb1: usb@211000 { + compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x524>; /* USB2LIODNR */ + dr_mode = "host"; + phy_type = "utmi"; + }; /include/ "qoriq-sata2-0.dtsi" + sata@220000 { + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x550>; /* SATA1LIODNR */ + }; + /include/ "qoriq-sata2-1.dtsi" + sata@221000 { + fsl,iommu-parent = <&pamu1>; + fsl,liodn-reg = <&guts 0x554>; /* SATA2LIODNR */ + }; /include/ "qoriq-sec4.2-0.dtsi" + crypto@300000 { + fsl,iommu-parent = <&pamu1>; + }; + /include/ "qoriq-raid1.0-0.dtsi" + raideng@320000 { + fsl,iommu-parent = <&pamu1>; + }; }; diff --git a/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi b/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi index db2c9a7b3a0e..97f8c26f9709 100644 --- a/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi @@ -48,6 +48,7 @@ bus-range = <0x0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 15>; + fsl,iommu-parent = <&pamu0>; pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -75,6 +76,7 @@ bus-range = <0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 14>; + fsl,iommu-parent = <&pamu0>; pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -102,6 +104,7 @@ bus-range = <0x0 0xff>; clock-frequency = <33333333>; interrupts = <16 2 1 13>; + fsl,iommu-parent = <&pamu0>; pcie@0 { reg = <0 0 0 0 0>; #interrupt-cells = <1>; @@ -239,10 +242,42 @@ iommu@20000 { compatible = "fsl,pamu-v1.0", "fsl,pamu"; - reg = <0x20000 0x5000>; - interrupts = < - 24 2 0 0 - 16 2 1 30>; + reg = <0x20000 0x5000>; /* for compatibility with older PAMU drivers */ + ranges = <0 0x20000 0x5000>; + #address-cells = <1>; + #size-cells = <1>; + interrupts = <24 2 0 0 + 16 2 1 30>; + + pamu0: pamu@0 { + reg = <0 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu1: pamu@1000 { + reg = <0x1000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu2: pamu@2000 { + reg = <0x2000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu3: pamu@3000 { + reg = <0x3000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; + + pamu4: pamu@4000 { + reg = <0x4000 0x1000>; + fsl,primary-cache-geometry = <32 1>; + fsl,secondary-cache-geometry = <128 2>; + }; }; /include/ "qoriq-mpic.dtsi" @@ -284,7 +319,17 @@ }; /include/ "qoriq-dma-0.dtsi" + dma@100300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x580>; /* DMA1LIODNR */ + }; + /include/ "qoriq-dma-1.dtsi" + dma@101300 { + fsl,iommu-parent = <&pamu0>; + fsl,liodn-reg = <&guts 0x584>; /* DMA2LIODNR */ + }; + /include/ "qoriq-espi-0.dtsi" spi@110000 { fsl,espi-num-chipselects = <4>; @@ -292,6 +337,8 @@ /include/ "qoriq-esdhc-0.dtsi" sdhc@114000 { + fsl,iommu-parent = <&pamu2>; + fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */ sdhci,auto-cmd12; }; @@ -301,20 +348,37 @@ /include/ "qoriq-duart-1.dtsi" /include/ "qoriq-gpio-0.dtsi" /include/ "qoriq-usb2-mph-0.dtsi" - usb0: usb@210000 { - compatible = "fsl-usb2-mph-v1.6", "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; - phy_type = "utmi"; - port0; - }; + usb0: usb@210000 { + compatible = "fsl-usb2-mph-v1.6", "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph"; + fsl,iommu-parent = <&pamu4>; + fsl,liodn-reg = <&guts 0x520>; /* USB1LIODNR */ + phy_type = "utmi"; + port0; + }; /include/ "qoriq-usb2-dr-0.dtsi" - usb1: usb@211000 { - compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; - dr_mode = "host"; - phy_type = "utmi"; - }; + usb1: usb@211000 { + compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr"; + fsl,iommu-parent = <&pamu4>; + fsl,liodn-reg = <&guts 0x524>; /* USB2LIODNR */ + dr_mode = "host"; + phy_type = "utmi"; + }; /include/ "qoriq-sata2-0.dtsi" + sata@220000 { + fsl,iommu-parent = <&pamu4>; + fsl,liodn-reg = <&guts 0x550>; /* SATA1LIODNR */ + }; + /include/ "qoriq-sata2-1.dtsi" + sata@221000 { + fsl,iommu-parent = <&pamu4>; + fsl,liodn-reg = <&guts 0x554>; /* SATA2LIODNR */ + }; + /include/ "qoriq-sec5.2-0.dtsi" + crypto@300000 { + fsl,iommu-parent = <&pamu4>; + }; }; From 14bdc9132eca0c77cb19e26f4d73328434170de7 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 17 Jan 2013 16:34:32 -0600 Subject: [PATCH 2440/4607] powerpc/85xx: fix various PCI node compatible strings Fix and/or improve the compatible strings of the PCI device tree nodes for some Freescale SOCs. This fixes some issues and improves consistency among the SOCs. Specifically: 1) The P1022 has a v1 PCIe controller, so the compatible property should just say "fsl,mpc8548-pcie". U-Boot does not look for "fsl,p1022-pcie", so it wasn't fixing up the node. 2) The P4080 has a v2.1 PCIe controller, so add that version-specific string to the device tree. Update the kernel to also look for that string. Currently, the kernel looks for "fsl,p4080-pcie" specifically, but eventually that check should be deleted. 3) The P1010 device tree claims compatibility with v2.2 and v2.3, but that's redundant. No other device tree does this. Remove the v2.2 string. 4) The kernel looks for both "fsl,p1023-pcie" and "fsl,qoriq-pcie-v2.2", even though the P1023 device trees has always included both strings. Remove the search for "fsl,p1023-pcie". Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/fsl/p1010si-post.dtsi | 4 ++-- arch/powerpc/boot/dts/fsl/p1022si-post.dtsi | 6 +++--- arch/powerpc/boot/dts/fsl/p4080si-post.dtsi | 6 +++--- arch/powerpc/sysdev/fsl_pci.c | 17 +++++++++++------ 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi index 0bde9ee8afaf..af12ead88c5f 100644 --- a/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1010si-post.dtsi @@ -41,7 +41,7 @@ /* controller at 0x9000 */ &pci0 { - compatible = "fsl,p1010-pcie", "fsl,qoriq-pcie-v2.3", "fsl,qoriq-pcie-v2.2"; + compatible = "fsl,p1010-pcie", "fsl,qoriq-pcie-v2.3"; device_type = "pci"; #size-cells = <2>; #address-cells = <3>; @@ -69,7 +69,7 @@ /* controller at 0xa000 */ &pci1 { - compatible = "fsl,p1010-pcie", "fsl,qoriq-pcie-v2.3", "fsl,qoriq-pcie-v2.2"; + compatible = "fsl,p1010-pcie", "fsl,qoriq-pcie-v2.3"; device_type = "pci"; #size-cells = <2>; #address-cells = <3>; diff --git a/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi b/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi index 06216b8c0af5..e179803a81ef 100644 --- a/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p1022si-post.dtsi @@ -45,7 +45,7 @@ /* controller at 0x9000 */ &pci0 { - compatible = "fsl,p1022-pcie"; + compatible = "fsl,mpc8548-pcie"; device_type = "pci"; #size-cells = <2>; #address-cells = <3>; @@ -73,7 +73,7 @@ /* controller at 0xa000 */ &pci1 { - compatible = "fsl,p1022-pcie"; + compatible = "fsl,mpc8548-pcie"; device_type = "pci"; #size-cells = <2>; #address-cells = <3>; @@ -102,7 +102,7 @@ /* controller at 0xb000 */ &pci2 { - compatible = "fsl,p1022-pcie"; + compatible = "fsl,mpc8548-pcie"; device_type = "pci"; #size-cells = <2>; #address-cells = <3>; diff --git a/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi b/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi index dc82bcfa2f61..19859ad851eb 100644 --- a/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi @@ -41,7 +41,7 @@ /* controller at 0x200000 */ &pci0 { - compatible = "fsl,p4080-pcie"; + compatible = "fsl,p4080-pcie", "fsl,qoriq-pcie-v2.1"; device_type = "pci"; #size-cells = <2>; #address-cells = <3>; @@ -70,7 +70,7 @@ /* controller at 0x201000 */ &pci1 { - compatible = "fsl,p4080-pcie"; + compatible = "fsl,p4080-pcie", "fsl,qoriq-pcie-v2.1"; device_type = "pci"; #size-cells = <2>; #address-cells = <3>; @@ -99,7 +99,7 @@ /* controller at 0x202000 */ &pci2 { - compatible = "fsl,p4080-pcie"; + compatible = "fsl,p4080-pcie", "fsl,qoriq-pcie-v2.1"; device_type = "pci"; #size-cells = <2>; #address-cells = <3>; diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index 92a5915b1827..f213fb6dabfc 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -827,13 +827,18 @@ static const struct of_device_id pci_ids[] = { { .compatible = "fsl,mpc8548-pcie", }, { .compatible = "fsl,mpc8610-pci", }, { .compatible = "fsl,mpc8641-pcie", }, - { .compatible = "fsl,p1022-pcie", }, - { .compatible = "fsl,p1010-pcie", }, - { .compatible = "fsl,p1023-pcie", }, - { .compatible = "fsl,p4080-pcie", }, - { .compatible = "fsl,qoriq-pcie-v2.4", }, - { .compatible = "fsl,qoriq-pcie-v2.3", }, + { .compatible = "fsl,qoriq-pcie-v2.1", }, { .compatible = "fsl,qoriq-pcie-v2.2", }, + { .compatible = "fsl,qoriq-pcie-v2.3", }, + { .compatible = "fsl,qoriq-pcie-v2.4", }, + + /* + * The following entries are for compatibility with older device + * trees. + */ + { .compatible = "fsl,p1022-pcie", }, + { .compatible = "fsl,p4080-pcie", }, + {}, }; From 3c00433b7637fdffb0e3fabdf2f08e55b2de594d Mon Sep 17 00:00:00 2001 From: Holger Brunck Date: Fri, 7 Dec 2012 16:09:12 +0100 Subject: [PATCH 2441/4607] powerpc/82xx: fix checkpatch warnings for km82xx.c Signed-off-by: Holger Brunck Signed-off-by: Kumar Gala --- arch/powerpc/platforms/82xx/km82xx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/82xx/km82xx.c b/arch/powerpc/platforms/82xx/km82xx.c index cf964e19573a..058cc1895c88 100644 --- a/arch/powerpc/platforms/82xx/km82xx.c +++ b/arch/powerpc/platforms/82xx/km82xx.c @@ -18,11 +18,11 @@ #include #include -#include +#include #include #include #include -#include +#include #include #include @@ -36,7 +36,7 @@ static void __init km82xx_pic_init(void) struct device_node *np = of_find_compatible_node(NULL, NULL, "fsl,pq2-pic"); if (!np) { - printk(KERN_ERR "PIC init: can not find cpm-pic node\n"); + pr_err("PIC init: can not find cpm-pic node\n"); return; } From 89491d83b27c8a713826787e40d87a4024a2cc24 Mon Sep 17 00:00:00 2001 From: Holger Brunck Date: Fri, 7 Dec 2012 16:09:13 +0100 Subject: [PATCH 2442/4607] powerpc/83xx: fix checkpatch warnings for km83xx.c Signed-off-by: Holger Brunck Signed-off-by: Kumar Gala --- arch/powerpc/platforms/83xx/km83xx.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/platforms/83xx/km83xx.c b/arch/powerpc/platforms/83xx/km83xx.c index 89923d723349..cb6460cd96e9 100644 --- a/arch/powerpc/platforms/83xx/km83xx.c +++ b/arch/powerpc/platforms/83xx/km83xx.c @@ -28,8 +28,8 @@ #include #include -#include -#include +#include +#include #include #include #include @@ -88,15 +88,13 @@ static void __init mpc83xx_km_setup_arch(void) np_par = of_find_node_by_name(NULL, "par_io"); if (np_par == NULL) { - printk(KERN_WARNING "%s couldn;t find par_io node\n", - __func__); + pr_warn("%s couldn;t find par_io node\n", __func__); return; } /* Map Parallel I/O ports registers */ ret = of_address_to_resource(np_par, 0, &res); if (ret) { - printk(KERN_WARNING "%s couldn;t map par_io registers\n", - __func__); + pr_warn("%s couldn;t map par_io registers\n", __func__); return; } From 14f40f31e8ffd639c2e3e0cd90ec6dd8b536144b Mon Sep 17 00:00:00 2001 From: Gerlando Falauto Date: Fri, 7 Dec 2012 16:09:14 +0100 Subject: [PATCH 2443/4607] powerpc/83xx: refactor mpc8360e quirk for kmeter1 Move the code for this quirk to a dedicated function. Signed-off-by: Gerlando Falauto Signed-off-by: Holger Brunck Signed-off-by: Kumar Gala --- arch/powerpc/platforms/83xx/km83xx.c | 149 ++++++++++++++------------- 1 file changed, 77 insertions(+), 72 deletions(-) diff --git a/arch/powerpc/platforms/83xx/km83xx.c b/arch/powerpc/platforms/83xx/km83xx.c index cb6460cd96e9..88b1af04661b 100644 --- a/arch/powerpc/platforms/83xx/km83xx.c +++ b/arch/powerpc/platforms/83xx/km83xx.c @@ -43,6 +43,82 @@ #include "mpc83xx.h" #define SVR_REV(svr) (((svr) >> 0) & 0xFFFF) /* Revision field */ + +static void quirk_mpc8360e_qe_enet10(void) +{ + /* + * handle mpc8360E Erratum QE_ENET10: + * RGMII AC values do not meet the specification + */ + uint svid = mfspr(SPRN_SVR); + struct device_node *np_par; + struct resource res; + void __iomem *base; + int ret; + + np_par = of_find_node_by_name(NULL, "par_io"); + if (np_par == NULL) { + pr_warn("%s couldn;t find par_io node\n", __func__); + return; + } + /* Map Parallel I/O ports registers */ + ret = of_address_to_resource(np_par, 0, &res); + if (ret) { + pr_warn("%s couldn;t map par_io registers\n", __func__); + return; + } + + base = ioremap(res.start, res.end - res.start + 1); + + /* + * set output delay adjustments to default values according + * table 5 in Errata Rev. 5, 9/2011: + * + * write 0b01 to UCC1 bits 18:19 + * write 0b01 to UCC2 option 1 bits 4:5 + * write 0b01 to UCC2 option 2 bits 16:17 + */ + clrsetbits_be32((base + 0xa8), 0x0c00f000, 0x04005000); + + /* + * set output delay adjustments to default values according + * table 3-13 in Reference Manual Rev.3 05/2010: + * + * write 0b01 to UCC2 option 2 bits 16:17 + * write 0b0101 to UCC1 bits 20:23 + * write 0b0101 to UCC2 option 1 bits 24:27 + */ + clrsetbits_be32((base + 0xac), 0x0000cff0, 0x00004550); + + if (SVR_REV(svid) == 0x0021) { + /* + * UCC2 option 1: write 0b1010 to bits 24:27 + * at address IMMRBAR+0x14AC + */ + clrsetbits_be32((base + 0xac), 0x000000f0, 0x000000a0); + } else if (SVR_REV(svid) == 0x0020) { + /* + * UCC1: write 0b11 to bits 18:19 + * at address IMMRBAR+0x14A8 + */ + setbits32((base + 0xa8), 0x00003000); + + /* + * UCC2 option 1: write 0b11 to bits 4:5 + * at address IMMRBAR+0x14A8 + */ + setbits32((base + 0xa8), 0x0c000000); + + /* + * UCC2 option 2: write 0b11 to bits 16:17 + * at address IMMRBAR+0x14AC + */ + setbits32((base + 0xac), 0x0000c000); + } + iounmap(base); + of_node_put(np_par); +} + /* ************************************************************************ * * Setup the architecture @@ -73,80 +149,9 @@ static void __init mpc83xx_km_setup_arch(void) for_each_node_by_name(np, "ucc") par_io_of_config(np); } - np = of_find_compatible_node(NULL, "network", "ucc_geth"); if (np != NULL) { - /* - * handle mpc8360E Erratum QE_ENET10: - * RGMII AC values do not meet the specification - */ - uint svid = mfspr(SPRN_SVR); - struct device_node *np_par; - struct resource res; - void __iomem *base; - int ret; - - np_par = of_find_node_by_name(NULL, "par_io"); - if (np_par == NULL) { - pr_warn("%s couldn;t find par_io node\n", __func__); - return; - } - /* Map Parallel I/O ports registers */ - ret = of_address_to_resource(np_par, 0, &res); - if (ret) { - pr_warn("%s couldn;t map par_io registers\n", __func__); - return; - } - - base = ioremap(res.start, res.end - res.start + 1); - - /* - * set output delay adjustments to default values according - * table 5 in Errata Rev. 5, 9/2011: - * - * write 0b01 to UCC1 bits 18:19 - * write 0b01 to UCC2 option 1 bits 4:5 - * write 0b01 to UCC2 option 2 bits 16:17 - */ - clrsetbits_be32((base + 0xa8), 0x0c00f000, 0x04005000); - - /* - * set output delay adjustments to default values according - * table 3-13 in Reference Manual Rev.3 05/2010: - * - * write 0b01 to UCC2 option 2 bits 16:17 - * write 0b0101 to UCC1 bits 20:23 - * write 0b0101 to UCC2 option 1 bits 24:27 - */ - clrsetbits_be32((base + 0xac), 0x0000cff0, 0x00004550); - - if (SVR_REV(svid) == 0x0021) { - /* - * UCC2 option 1: write 0b1010 to bits 24:27 - * at address IMMRBAR+0x14AC - */ - clrsetbits_be32((base + 0xac), 0x000000f0, 0x000000a0); - } else if (SVR_REV(svid) == 0x0020) { - /* - * UCC1: write 0b11 to bits 18:19 - * at address IMMRBAR+0x14A8 - */ - setbits32((base + 0xa8), 0x00003000); - - /* - * UCC2 option 1: write 0b11 to bits 4:5 - * at address IMMRBAR+0x14A8 - */ - setbits32((base + 0xa8), 0x0c000000); - - /* - * UCC2 option 2: write 0b11 to bits 16:17 - * at address IMMRBAR+0x14AC - */ - setbits32((base + 0xac), 0x0000c000); - } - iounmap(base); - of_node_put(np_par); + quirk_mpc8360e_qe_enet10(); of_node_put(np); } #endif /* CONFIG_QUICC_ENGINE */ From 9c2f451e0db90d257c745df1d967c8010f14f0dd Mon Sep 17 00:00:00 2001 From: Gerlando Falauto Date: Fri, 7 Dec 2012 16:09:15 +0100 Subject: [PATCH 2444/4607] powerpc/83xx: apply mpc8360e quirk for kmeter1 only when par_io is present There is no point in applying this quirk when par_io is not present. Signed-off-by: Gerlando Falauto Signed-off-by: Holger Brunck Signed-off-by: Kumar Gala --- arch/powerpc/platforms/83xx/km83xx.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/platforms/83xx/km83xx.c b/arch/powerpc/platforms/83xx/km83xx.c index 88b1af04661b..bf4c4473abb9 100644 --- a/arch/powerpc/platforms/83xx/km83xx.c +++ b/arch/powerpc/platforms/83xx/km83xx.c @@ -148,11 +148,13 @@ static void __init mpc83xx_km_setup_arch(void) for_each_node_by_name(np, "ucc") par_io_of_config(np); - } - np = of_find_compatible_node(NULL, "network", "ucc_geth"); - if (np != NULL) { - quirk_mpc8360e_qe_enet10(); - of_node_put(np); + + /* Only apply this quirk when par_io is available */ + np = of_find_compatible_node(NULL, "network", "ucc_geth"); + if (np != NULL) { + quirk_mpc8360e_qe_enet10(); + of_node_put(np); + } } #endif /* CONFIG_QUICC_ENGINE */ } From ed59b3eca05a1afe1605238494020007a1cdaa4f Mon Sep 17 00:00:00 2001 From: Holger Brunck Date: Fri, 7 Dec 2012 16:09:16 +0100 Subject: [PATCH 2445/4607] powerpc/83xx: update kmeter1_defconfig Synchronize this defconfig with latest kernel version. Signed-off-by: Holger Brunck Signed-off-by: Kumar Gala --- arch/powerpc/configs/83xx/kmeter1_defconfig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/configs/83xx/kmeter1_defconfig b/arch/powerpc/configs/83xx/kmeter1_defconfig index a0dfef1fcdb7..e12e60c3b9a2 100644 --- a/arch/powerpc/configs/83xx/kmeter1_defconfig +++ b/arch/powerpc/configs/83xx/kmeter1_defconfig @@ -2,6 +2,8 @@ CONFIG_EXPERIMENTAL=y # CONFIG_SWAP is not set CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_EXPERT=y CONFIG_SLAB=y @@ -16,8 +18,6 @@ CONFIG_PARTITION_ADVANCED=y # CONFIG_PPC_PMAC is not set CONFIG_PPC_83xx=y CONFIG_KMETER1=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y CONFIG_PREEMPT=y # CONFIG_SECCOMP is not set CONFIG_NET=y @@ -45,7 +45,6 @@ CONFIG_MTD_PHYSMAP_OF=y CONFIG_MTD_PHRAM=y CONFIG_MTD_UBI=y CONFIG_MTD_UBI_GLUEBI=y -CONFIG_MTD_UBI_DEBUG=y CONFIG_PROC_DEVICETREE=y CONFIG_NETDEVICES=y CONFIG_DUMMY=y @@ -76,5 +75,4 @@ CONFIG_TMPFS=y CONFIG_JFFS2_FS=y CONFIG_UBIFS_FS=y CONFIG_NFS_FS=y -CONFIG_NFS_V3=y CONFIG_ROOT_NFS=y From db29cd3c4497e7edf9176284ba7cf3cec1814c7a Mon Sep 17 00:00:00 2001 From: Po Liu Date: Fri, 18 Jan 2013 17:16:13 +0800 Subject: [PATCH 2446/4607] powerpc/85xx: dts - add ranges property for SEC This facilitates getting the physical address of the SEC node. Signed-off-by: Liu po Reviewed-by: Kim Phillips Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/fsl/pq3-sec4.4-0.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/boot/dts/fsl/pq3-sec4.4-0.dtsi b/arch/powerpc/boot/dts/fsl/pq3-sec4.4-0.dtsi index d4c9d5daab21..ffadcb563ada 100644 --- a/arch/powerpc/boot/dts/fsl/pq3-sec4.4-0.dtsi +++ b/arch/powerpc/boot/dts/fsl/pq3-sec4.4-0.dtsi @@ -36,6 +36,7 @@ crypto@30000 { compatible = "fsl,sec-v4.4", "fsl,sec-v4.0"; #address-cells = <1>; #size-cells = <1>; + ranges = <0x0 0x30000 0x10000>; reg = <0x30000 0x10000>; interrupts = <58 2 0 0>; From f74f70f8b10b435f5f20247e70d1d86b53a59685 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 04:23:54 -0800 Subject: [PATCH 2447/4607] afs: Only allow mounting afs in the intial network namespace rxrpc sockets only work in the initial network namespace so it isn't possible to support afs in any other network namespace. Cc: David Howells Signed-off-by: "Eric W. Biederman" --- fs/afs/super.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/afs/super.c b/fs/afs/super.c index 43165009428d..7c31ec399575 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "internal.h" #define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */ @@ -363,6 +365,10 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, memset(¶ms, 0, sizeof(params)); + ret = -EINVAL; + if (current->nsproxy->net_ns != &init_net) + goto error; + /* parse the options and device name */ if (options) { ret = afs_parse_options(¶ms, options, &dev_name); From a0a5386ac6400493cc2eb8b58583e56af0708730 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Feb 2012 16:20:48 -0800 Subject: [PATCH 2448/4607] afs: Support interacting with multiple user namespaces Modify struct afs_file_status to store owner as a kuid_t and group as a kgid_t. In xdr_decode_AFSFetchStatus as owner is now a kuid_t and group is now a kgid_t don't use the EXTRACT macro. Instead perform the work of the extract macro explicitly. Read the value with ntohl and convert it to the appropriate type with make_kuid or make_kgid. Test if the value is different from what is stored in status and update changed. Update the value in status. In xdr_encode_AFS_StoreStatus call from_kuid or from_kgid as we are computing the on the wire encoding. Initialize uids with GLOBAL_ROOT_UID instead of 0. Initialize gids with GLOBAL_ROOT_GID instead of 0. Cc: David Howells Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- fs/afs/afs.h | 4 ++-- fs/afs/fsclient.c | 14 ++++++++++---- fs/afs/inode.c | 6 +++--- init/Kconfig | 1 - 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/fs/afs/afs.h b/fs/afs/afs.h index 3d8fd35c0dd0..3c462ff6db63 100644 --- a/fs/afs/afs.h +++ b/fs/afs/afs.h @@ -119,8 +119,8 @@ struct afs_file_status { u64 size; /* file size */ afs_dataversion_t data_version; /* current data version */ u32 author; /* author ID */ - u32 owner; /* owner ID */ - u32 group; /* group ID */ + kuid_t owner; /* owner ID */ + kgid_t group; /* group ID */ afs_access_t caller_access; /* access rights for authenticated caller */ afs_access_t anon_access; /* access rights for unauthenticated caller */ umode_t mode; /* UNIX mode */ diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index b960ff05ea0b..c2e930ec2888 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -42,6 +42,8 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, umode_t mode; u64 data_version, size; u32 changed = 0; /* becomes non-zero if ctime-type changes seen */ + kuid_t owner; + kgid_t group; #define EXTRACT(DST) \ do { \ @@ -56,7 +58,9 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, size = ntohl(*bp++); data_version = ntohl(*bp++); EXTRACT(status->author); - EXTRACT(status->owner); + owner = make_kuid(&init_user_ns, ntohl(*bp++)); + changed |= !uid_eq(owner, status->owner); + status->owner = owner; EXTRACT(status->caller_access); /* call ticket dependent */ EXTRACT(status->anon_access); EXTRACT(status->mode); @@ -65,7 +69,9 @@ static void xdr_decode_AFSFetchStatus(const __be32 **_bp, bp++; /* seg size */ status->mtime_client = ntohl(*bp++); status->mtime_server = ntohl(*bp++); - EXTRACT(status->group); + group = make_kgid(&init_user_ns, ntohl(*bp++)); + changed |= !gid_eq(group, status->group); + status->group = group; bp++; /* sync counter */ data_version |= (u64) ntohl(*bp++) << 32; EXTRACT(status->lock_count); @@ -181,12 +187,12 @@ static void xdr_encode_AFS_StoreStatus(__be32 **_bp, struct iattr *attr) if (attr->ia_valid & ATTR_UID) { mask |= AFS_SET_OWNER; - owner = attr->ia_uid; + owner = from_kuid(&init_user_ns, attr->ia_uid); } if (attr->ia_valid & ATTR_GID) { mask |= AFS_SET_GROUP; - group = attr->ia_gid; + group = from_kgid(&init_user_ns, attr->ia_gid); } if (attr->ia_valid & ATTR_MODE) { diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 95cffd38239f..789bc253b5f6 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -69,7 +69,7 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) set_nlink(inode, vnode->status.nlink); inode->i_uid = vnode->status.owner; - inode->i_gid = 0; + inode->i_gid = GLOBAL_ROOT_GID; inode->i_size = vnode->status.size; inode->i_ctime.tv_sec = vnode->status.mtime_server; inode->i_ctime.tv_nsec = 0; @@ -175,8 +175,8 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; inode->i_op = &afs_autocell_inode_operations; set_nlink(inode, 2); - inode->i_uid = 0; - inode->i_gid = 0; + inode->i_uid = GLOBAL_ROOT_UID; + inode->i_gid = GLOBAL_ROOT_GID; inode->i_ctime.tv_sec = get_seconds(); inode->i_ctime.tv_nsec = 0; inode->i_atime = inode->i_mtime = inode->i_ctime; diff --git a/init/Kconfig b/init/Kconfig index 394d24f99efe..4570b02abcc5 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1071,7 +1071,6 @@ config UIDGID_CONVERTED default y # Filesystems - depends on AFS_FS = n depends on CIFS = n depends on CODA_FS = n depends on GFS2_FS = n From 9fd973e085f7759f710603422b2e11ad5f2e000d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 18:50:54 -0800 Subject: [PATCH 2449/4607] coda: Restrict coda messages to the initial pid namespace Remove the slight chance that pids in coda messages will be interpreted in the wrong pid namespace. - Explicitly send all pids in coda messages in the initial pid namespace. - Only allow mounts from processes in the initial pid namespace. - Only allow processes in the initial pid namespace to open the coda character device to communicate with coda. Cc: Jan Harkes Signed-off-by: "Eric W. Biederman" --- fs/coda/inode.c | 4 ++++ fs/coda/psdev.c | 4 ++++ fs/coda/upcall.c | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/coda/inode.c b/fs/coda/inode.c index be2aa4909487..77bbaf4666a7 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -157,6 +158,9 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent) int error; int idx; + if (task_active_pid_ns(current) != &init_pid_ns) + return -EINVAL; + idx = get_device_index((struct coda_mount_data *) data); /* Ignore errors in data, for backward compatibility */ diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 761d5b31b18d..dd60f905d4fe 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -266,6 +267,9 @@ static int coda_psdev_open(struct inode * inode, struct file * file) struct venus_comm *vcp; int idx, err; + if (task_active_pid_ns(current) != &init_pid_ns) + return -EINVAL; + idx = iminor(inode); if (idx < 0 || idx >= MAX_CODADEVS) return -ENODEV; diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 0c68fd31fbf2..5c6d2cd6ee86 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -50,8 +50,8 @@ static void *alloc_upcall(int opcode, int size) return ERR_PTR(-ENOMEM); inp->ih.opcode = opcode; - inp->ih.pid = current->pid; - inp->ih.pgid = task_pgrp_nr(current); + inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns); + inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns); inp->ih.uid = current_fsuid(); return (void*)inp; From d83f5901bc0cd7131a3b8534169ee889efc4c257 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 19:21:14 -0800 Subject: [PATCH 2450/4607] coda: Restrict coda messages to the initial user namespace Remove the slight chance that uids and gids in coda messages will be interpreted in the wrong user namespace. - Only allow processes in the initial user namespace to open the coda character device to communicate with coda filesystems. - Explicitly convert the uids in the coda header into the initial user namespace. - In coda_vattr_to_attr make kuids and kgids from the initial user namespace uids and gids in struct coda_vattr that just came from userspace. - In coda_iattr_to_vattr convert kuids and kgids into uids and gids in the intial user namespace and store them in struct coda_vattr for sending to coda userspace programs. Nothing needs to be changed with mounts as coda does not support being mounted in anything other than the initial user namespace. Cc: Jan Harkes Signed-off-by: "Eric W. Biederman" --- fs/coda/coda_linux.c | 8 ++++---- fs/coda/psdev.c | 3 +++ fs/coda/upcall.c | 6 +++--- include/linux/coda_psdev.h | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c index 854ace712685..2849f41e72a2 100644 --- a/fs/coda/coda_linux.c +++ b/fs/coda/coda_linux.c @@ -100,9 +100,9 @@ void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr) if (attr->va_mode != (u_short) -1) inode->i_mode = attr->va_mode | inode_type; if (attr->va_uid != -1) - inode->i_uid = (uid_t) attr->va_uid; + inode->i_uid = make_kuid(&init_user_ns, (uid_t) attr->va_uid); if (attr->va_gid != -1) - inode->i_gid = (gid_t) attr->va_gid; + inode->i_gid = make_kgid(&init_user_ns, (gid_t) attr->va_gid); if (attr->va_nlink != -1) set_nlink(inode, attr->va_nlink); if (attr->va_size != -1) @@ -171,10 +171,10 @@ void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr) vattr->va_mode = iattr->ia_mode; } if ( valid & ATTR_UID ) { - vattr->va_uid = (vuid_t) iattr->ia_uid; + vattr->va_uid = (vuid_t) from_kuid(&init_user_ns, iattr->ia_uid); } if ( valid & ATTR_GID ) { - vattr->va_gid = (vgid_t) iattr->ia_gid; + vattr->va_gid = (vgid_t) from_kgid(&init_user_ns, iattr->ia_gid); } if ( valid & ATTR_SIZE ) { vattr->va_size = iattr->ia_size; diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index dd60f905d4fe..ebc2bae6c289 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -270,6 +270,9 @@ static int coda_psdev_open(struct inode * inode, struct file * file) if (task_active_pid_ns(current) != &init_pid_ns) return -EINVAL; + if (current_user_ns() != &init_user_ns) + return -EINVAL; + idx = iminor(inode); if (idx < 0 || idx >= MAX_CODADEVS) return -ENODEV; diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index 5c6d2cd6ee86..3a731976dc5e 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -52,7 +52,7 @@ static void *alloc_upcall(int opcode, int size) inp->ih.opcode = opcode; inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns); inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns); - inp->ih.uid = current_fsuid(); + inp->ih.uid = from_kuid(&init_user_ns, current_fsuid()); return (void*)inp; } @@ -157,7 +157,7 @@ int venus_lookup(struct super_block *sb, struct CodaFid *fid, } int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, - vuid_t uid) + kuid_t uid) { union inputArgs *inp; union outputArgs *outp; @@ -166,7 +166,7 @@ int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, insize = SIZE(release); UPARG(CODA_CLOSE); - inp->ih.uid = uid; + inp->ih.uid = from_kuid(&init_user_ns, uid); inp->coda_close.VFid = *fid; inp->coda_close.flags = flags; diff --git a/include/linux/coda_psdev.h b/include/linux/coda_psdev.h index 8031d6eef102..5b8721efa948 100644 --- a/include/linux/coda_psdev.h +++ b/include/linux/coda_psdev.h @@ -34,7 +34,7 @@ int venus_lookup(struct super_block *sb, struct CodaFid *fid, const char *name, int length, int *type, struct CodaFid *resfid); int venus_close(struct super_block *sb, struct CodaFid *fid, int flags, - vuid_t uid); + kuid_t uid); int venus_open(struct super_block *sb, struct CodaFid *fid, int flags, struct file **f); int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, From 17499e332962e495b3ee2e5507e09b853ed6f607 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 19:36:06 -0800 Subject: [PATCH 2451/4607] coda: Cache permisions in struct coda_inode_info in a kuid_t. - Change c_uid in struct coda_indoe_info from a vuid_t to a kuid_t. - Initialize c_uid to GLOBAL_ROOT_UID instead of 0. - Use uid_eq to compare cached kuids. Cc: Jan Harkes Signed-off-by: "Eric W. Biederman" --- fs/coda/cache.c | 4 ++-- fs/coda/coda_fs_i.h | 2 +- fs/coda/inode.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/coda/cache.c b/fs/coda/cache.c index 958ae0e0ff8c..1da168c61d35 100644 --- a/fs/coda/cache.c +++ b/fs/coda/cache.c @@ -33,7 +33,7 @@ void coda_cache_enter(struct inode *inode, int mask) spin_lock(&cii->c_lock); cii->c_cached_epoch = atomic_read(&permission_epoch); - if (cii->c_uid != current_fsuid()) { + if (!uid_eq(cii->c_uid, current_fsuid())) { cii->c_uid = current_fsuid(); cii->c_cached_perm = mask; } else @@ -65,7 +65,7 @@ int coda_cache_check(struct inode *inode, int mask) spin_lock(&cii->c_lock); hit = (mask & cii->c_cached_perm) == mask && - cii->c_uid == current_fsuid() && + uid_eq(cii->c_uid, current_fsuid()) && cii->c_cached_epoch == atomic_read(&permission_epoch); spin_unlock(&cii->c_lock); diff --git a/fs/coda/coda_fs_i.h b/fs/coda/coda_fs_i.h index b24fdfd8a3f0..c64075213218 100644 --- a/fs/coda/coda_fs_i.h +++ b/fs/coda/coda_fs_i.h @@ -25,7 +25,7 @@ struct coda_inode_info { u_short c_flags; /* flags (see below) */ unsigned int c_mapcount; /* nr of times this inode is mapped */ unsigned int c_cached_epoch; /* epoch for cached permissions */ - vuid_t c_uid; /* fsuid for cached permissions */ + kuid_t c_uid; /* fsuid for cached permissions */ unsigned int c_cached_perm; /* cached access permissions */ spinlock_t c_lock; struct inode vfs_inode; diff --git a/fs/coda/inode.c b/fs/coda/inode.c index 77bbaf4666a7..cf674e9179a3 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -49,7 +49,7 @@ static struct inode *coda_alloc_inode(struct super_block *sb) return NULL; memset(&ei->c_fid, 0, sizeof(struct CodaFid)); ei->c_flags = 0; - ei->c_uid = 0; + ei->c_uid = GLOBAL_ROOT_UID; ei->c_cached_perm = 0; spin_lock_init(&ei->c_lock); return &ei->vfs_inode; From 515ee7bd9758208dea081dbc933400d6be81028a Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 30 Jan 2013 19:43:05 -0800 Subject: [PATCH 2452/4607] coda: Allow coda to be built when user namespace support is enabled Now that the coda kernel to userspace has been modified to convert between kuids and kgids and uids and gids, and all internal coda structures have be modified to store uids and gids as kuids and kgids it is safe to allow code to be built with user namespace support enabled. Cc: Jan Harkes Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index 4570b02abcc5..f516d52f93c8 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1072,7 +1072,6 @@ config UIDGID_CONVERTED # Filesystems depends on CIFS = n - depends on CODA_FS = n depends on GFS2_FS = n depends on NCP_FS = n depends on NFSD = n From 9522751cded17c231acd1cf92bc21b3da1b07f38 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 04:49:40 -0800 Subject: [PATCH 2453/4607] ocfs2: Handle kuids and kgids in acl/xattr conversions. Explicitly deal with the different kinds of acls because they need different conversions. Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- fs/ocfs2/acl.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c index 260b16281fc3..8a404576fb26 100644 --- a/fs/ocfs2/acl.c +++ b/fs/ocfs2/acl.c @@ -65,7 +65,20 @@ static struct posix_acl *ocfs2_acl_from_xattr(const void *value, size_t size) acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); - acl->a_entries[n].e_id = le32_to_cpu(entry->e_id); + switch(acl->a_entries[n].e_tag) { + case ACL_USER: + acl->a_entries[n].e_uid = + make_kuid(&init_user_ns, + le32_to_cpu(entry->e_id)); + break; + case ACL_GROUP: + acl->a_entries[n].e_gid = + make_kgid(&init_user_ns, + le32_to_cpu(entry->e_id)); + break; + default: + break; + } value += sizeof(struct posix_acl_entry); } @@ -91,7 +104,21 @@ static void *ocfs2_acl_to_xattr(const struct posix_acl *acl, size_t *size) for (n = 0; n < acl->a_count; n++, entry++) { entry->e_tag = cpu_to_le16(acl->a_entries[n].e_tag); entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm); - entry->e_id = cpu_to_le32(acl->a_entries[n].e_id); + switch(acl->a_entries[n].e_tag) { + case ACL_USER: + entry->e_id = cpu_to_le32( + from_kuid(&init_user_ns, + acl->a_entries[n].e_uid)); + break; + case ACL_GROUP: + entry->e_id = cpu_to_le32( + from_kgid(&init_user_ns, + acl->a_entries[n].e_gid)); + break; + default: + entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID); + break; + } } return ocfs2_acl; } From 03ab30f73dbf2f4f719d2c0c2acef81bf0445eb7 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 16:59:23 -0800 Subject: [PATCH 2454/4607] ocfs2: convert between kuids and kgids and DLM locks Convert between uid and gids stored in the on the wire format of dlm locks aka struct ocfs2_meta_lvb and kuids and kgids stored in inode->i_uid and inode->i_gid. Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- fs/ocfs2/dlmglue.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 4f7795fb5fc0..f99af1cb849c 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -2045,8 +2045,8 @@ static void __ocfs2_stuff_meta_lvb(struct inode *inode) lvb->lvb_version = OCFS2_LVB_VERSION; lvb->lvb_isize = cpu_to_be64(i_size_read(inode)); lvb->lvb_iclusters = cpu_to_be32(oi->ip_clusters); - lvb->lvb_iuid = cpu_to_be32(inode->i_uid); - lvb->lvb_igid = cpu_to_be32(inode->i_gid); + lvb->lvb_iuid = cpu_to_be32(i_uid_read(inode)); + lvb->lvb_igid = cpu_to_be32(i_gid_read(inode)); lvb->lvb_imode = cpu_to_be16(inode->i_mode); lvb->lvb_inlink = cpu_to_be16(inode->i_nlink); lvb->lvb_iatime_packed = @@ -2095,8 +2095,8 @@ static void ocfs2_refresh_inode_from_lvb(struct inode *inode) else inode->i_blocks = ocfs2_inode_sector_count(inode); - inode->i_uid = be32_to_cpu(lvb->lvb_iuid); - inode->i_gid = be32_to_cpu(lvb->lvb_igid); + i_uid_write(inode, be32_to_cpu(lvb->lvb_iuid)); + i_gid_write(inode, be32_to_cpu(lvb->lvb_igid)); inode->i_mode = be16_to_cpu(lvb->lvb_imode); set_nlink(inode, be16_to_cpu(lvb->lvb_inlink)); ocfs2_unpack_timespec(&inode->i_atime, From 2c03417627c5edaeeae1e8c5da6e8fd6f8c720d3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 17:27:11 -0800 Subject: [PATCH 2455/4607] ocfs2: Convert uid and gids between in core and on disk inodes Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- fs/ocfs2/inode.c | 12 ++++++------ fs/ocfs2/namei.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index d89e08a81eda..f87f9bd1edff 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -269,8 +269,8 @@ void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, inode->i_generation = le32_to_cpu(fe->i_generation); inode->i_rdev = huge_decode_dev(le64_to_cpu(fe->id1.dev1.i_rdev)); inode->i_mode = le16_to_cpu(fe->i_mode); - inode->i_uid = le32_to_cpu(fe->i_uid); - inode->i_gid = le32_to_cpu(fe->i_gid); + i_uid_write(inode, le32_to_cpu(fe->i_uid)); + i_gid_write(inode, le32_to_cpu(fe->i_gid)); /* Fast symlinks will have i_size but no allocated clusters. */ if (S_ISLNK(inode->i_mode) && !fe->i_clusters) { @@ -1259,8 +1259,8 @@ int ocfs2_mark_inode_dirty(handle_t *handle, fe->i_size = cpu_to_le64(i_size_read(inode)); ocfs2_set_links_count(fe, inode->i_nlink); - fe->i_uid = cpu_to_le32(inode->i_uid); - fe->i_gid = cpu_to_le32(inode->i_gid); + fe->i_uid = cpu_to_le32(i_uid_read(inode)); + fe->i_gid = cpu_to_le32(i_gid_read(inode)); fe->i_mode = cpu_to_le16(inode->i_mode); fe->i_atime = cpu_to_le64(inode->i_atime.tv_sec); fe->i_atime_nsec = cpu_to_le32(inode->i_atime.tv_nsec); @@ -1290,8 +1290,8 @@ void ocfs2_refresh_inode(struct inode *inode, ocfs2_set_inode_flags(inode); i_size_write(inode, le64_to_cpu(fe->i_size)); set_nlink(inode, ocfs2_read_links_count(fe)); - inode->i_uid = le32_to_cpu(fe->i_uid); - inode->i_gid = le32_to_cpu(fe->i_gid); + i_uid_write(inode, le32_to_cpu(fe->i_uid)); + i_gid_write(inode, le32_to_cpu(fe->i_gid)); inode->i_mode = le16_to_cpu(fe->i_mode); if (S_ISLNK(inode->i_mode) && le32_to_cpu(fe->i_clusters) == 0) inode->i_blocks = 0; diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index f1fd0741162b..04ee1b57c243 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -512,8 +512,8 @@ static int __ocfs2_mknod_locked(struct inode *dir, fe->i_suballoc_loc = cpu_to_le64(suballoc_loc); fe->i_suballoc_bit = cpu_to_le16(suballoc_bit); fe->i_suballoc_slot = cpu_to_le16(inode_ac->ac_alloc_slot); - fe->i_uid = cpu_to_le32(inode->i_uid); - fe->i_gid = cpu_to_le32(inode->i_gid); + fe->i_uid = cpu_to_le32(i_uid_read(inode)); + fe->i_gid = cpu_to_le32(i_gid_read(inode)); fe->i_mode = cpu_to_le16(inode->i_mode); if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) fe->id1.dev1.i_rdev = cpu_to_le64(huge_encode_dev(dev)); From ba6135609c2b56851e37e1d89ddbdbae4e774a71 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 17:33:53 -0800 Subject: [PATCH 2456/4607] ocfs2: For tracing report the uid and gid values in the initial user namespace Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- fs/ocfs2/file.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 37d313ede159..8ee93322db2b 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1116,7 +1116,8 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) (unsigned long long)OCFS2_I(inode)->ip_blkno, dentry->d_name.len, dentry->d_name.name, attr->ia_valid, attr->ia_mode, - attr->ia_uid, attr->ia_gid); + from_kuid(&init_user_ns, attr->ia_uid), + from_kgid(&init_user_ns, attr->ia_gid)); /* ensuring we don't even attempt to truncate a symlink */ if (S_ISLNK(inode->i_mode)) From 488c8ef033c6409cd8d23bcd04eed2f56301836d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 17:35:29 -0800 Subject: [PATCH 2457/4607] ocfs2: Compare kuids and kgids using uid_eq and gid_eq Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- fs/ocfs2/file.c | 8 ++++---- fs/ocfs2/refcounttree.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 8ee93322db2b..0a2924a2d9e6 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1175,14 +1175,14 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) } } - if ((attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || - (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { + if ((attr->ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) || + (attr->ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid))) { /* * Gather pointers to quota structures so that allocation / * freeing of quota structures happens here and not inside * dquot_transfer() where we have problems with lock ordering */ - if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid + if (attr->ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid) && OCFS2_HAS_RO_COMPAT_FEATURE(sb, OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { transfer_to[USRQUOTA] = dqget(sb, make_kqid_uid(attr->ia_uid)); @@ -1191,7 +1191,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) goto bail_unlock; } } - if (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid + if (attr->ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid) && OCFS2_HAS_RO_COMPAT_FEATURE(sb, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { transfer_to[GRPQUOTA] = dqget(sb, make_kqid_gid(attr->ia_gid)); diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 30a055049e16..934a4ac3e7fc 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -4407,7 +4407,7 @@ static int ocfs2_vfs_reflink(struct dentry *old_dentry, struct inode *dir, * rights to do so. */ if (preserve) { - if ((current_fsuid() != inode->i_uid) && !capable(CAP_CHOWN)) + if (!uid_eq(current_fsuid(), inode->i_uid) && !capable(CAP_CHOWN)) return -EPERM; if (!in_group_p(inode->i_gid) && !capable(CAP_CHOWN)) return -EPERM; From ecb528e3ea208750693731538411f86f78a4d965 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 17:36:56 -0800 Subject: [PATCH 2458/4607] ocfs2: Enable building with user namespaces enabled Now that ocfs2 has been converted to store uids and gids in kuid_t and kgid_t and all of the conversions have been added to the appropriate places it is safe to allow building and using ocfs2 with user namespace support enabled. Cc: Mark Fasheh Cc: Joel Becker Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index f516d52f93c8..9cb63c94cab8 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1076,7 +1076,6 @@ config UIDGID_CONVERTED depends on NCP_FS = n depends on NFSD = n depends on NFS_FS = n - depends on OCFS2_FS = n depends on XFS_FS = n config UIDGID_STRICT_TYPE_CHECKS From 393551e9898136513007b1e88a25bd4dcdb0d759 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 02:09:03 -0800 Subject: [PATCH 2459/4607] gfs2: Remove improper checks in gfs2_set_dqblk. In set_dqblk it is an error to look at fdq->d_id or fdq->d_flags. Userspace quota applications do not set these fields when calling quotactl(Q_XSETQLIM,...), and the kernel does not set those fields when quota_setquota calls set_dqblk. gfs2 never looks at fdq->d_id or fdq->d_flags after checking to see if they match the id and type supplied to set_dqblk. No other linux filesystem in set_dqblk looks at either fdq->d_id or fdq->d_flags. Therefore remove these bogus checks from gfs2 and allow normal quota setting applications to work. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index ae55e248c3b7..e4f6ccf3da64 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1543,13 +1543,9 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, switch(qid.type) { case USRQUOTA: type = QUOTA_USER; - if (fdq->d_flags != FS_USER_QUOTA) - return -EINVAL; break; case GRPQUOTA: type = QUOTA_GROUP; - if (fdq->d_flags != FS_GROUP_QUOTA) - return -EINVAL; break; default: return -EINVAL; @@ -1557,8 +1553,6 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, if (fdq->d_fieldmask & ~GFS2_FIELDMASK) return -EINVAL; - if (fdq->d_id != from_kqid(&init_user_ns, qid)) - return -EINVAL; error = qd_get(sdp, type, from_kqid(&init_user_ns, qid), &qd); if (error) From f4108a607f75b073423eed229ee4f95e5fc10631 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 17:49:26 -0800 Subject: [PATCH 2460/4607] gfs2: Split NO_QUOTA_CHANGE inot NO_UID_QUTOA_CHANGE and NO_GID_QUTOA_CHANGE Split NO_QUOTA_CHANGE into NO_UID_QUTOA_CHANGE and NO_GID_QUTOA_CHANGE so the constants may be well typed. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/bmap.c | 2 +- fs/gfs2/dir.c | 2 +- fs/gfs2/inode.c | 10 +++++----- fs/gfs2/quota.c | 4 ++-- fs/gfs2/quota.h | 5 +++-- fs/gfs2/super.c | 2 +- fs/gfs2/xattr.c | 4 ++-- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index a68e91bcef3d..10c54e3c2e72 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1098,7 +1098,7 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 size) if (error) return error; - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_hold(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) return error; diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 9a35670fdc38..6d3e3e2cabf8 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -1849,7 +1849,7 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len, if (!ht) return -ENOMEM; - error = gfs2_quota_hold(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_hold(dip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) goto out; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 2b6f5698ef18..aa1050287f3c 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -548,7 +548,7 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, if (error) return error; - error = gfs2_quota_lock(dip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_lock(dip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) goto fail; @@ -1589,15 +1589,15 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) ngid = attr->ia_gid; if (!(attr->ia_valid & ATTR_UID) || ouid == nuid) - ouid = nuid = NO_QUOTA_CHANGE; + ouid = nuid = NO_UID_QUOTA_CHANGE; if (!(attr->ia_valid & ATTR_GID) || ogid == ngid) - ogid = ngid = NO_QUOTA_CHANGE; + ogid = ngid = NO_GID_QUOTA_CHANGE; error = gfs2_quota_lock(ip, nuid, ngid); if (error) return error; - if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) { + if (ouid != NO_UID_QUOTA_CHANGE || ogid != NO_GID_QUOTA_CHANGE) { error = gfs2_quota_check(ip, nuid, ngid); if (error) goto out_gunlock_q; @@ -1611,7 +1611,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) if (error) goto out_end_trans; - if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) { + if (ouid != NO_UID_QUOTA_CHANGE || ogid != NO_GID_QUOTA_CHANGE) { u64 blocks = gfs2_get_inode_blocks(&ip->i_inode); gfs2_quota_change(ip, -blocks, ouid, ogid); gfs2_quota_change(ip, blocks, nuid, ngid); diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index e4f6ccf3da64..dbfacaa8b6fb 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -524,7 +524,7 @@ int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid) ip->i_res->rs_qa_qd_num++; qd++; - if (uid != NO_QUOTA_CHANGE && uid != ip->i_inode.i_uid) { + if (uid != NO_UID_QUOTA_CHANGE && uid != ip->i_inode.i_uid) { error = qdsb_get(sdp, QUOTA_USER, uid, qd); if (error) goto out; @@ -532,7 +532,7 @@ int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid) qd++; } - if (gid != NO_QUOTA_CHANGE && gid != ip->i_inode.i_gid) { + if (gid != NO_GID_QUOTA_CHANGE && gid != ip->i_inode.i_gid) { error = qdsb_get(sdp, QUOTA_GROUP, gid, qd); if (error) goto out; diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index f25d98b87904..7f67323a6a72 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h @@ -14,7 +14,8 @@ struct gfs2_inode; struct gfs2_sbd; struct shrink_control; -#define NO_QUOTA_CHANGE ((u32)-1) +#define NO_UID_QUOTA_CHANGE INVALID_UID +#define NO_GID_QUOTA_CHANGE INVALID_GID extern int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid); extern void gfs2_quota_unhold(struct gfs2_inode *ip); @@ -41,7 +42,7 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip) int ret; if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF) return 0; - ret = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + ret = gfs2_quota_lock(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (ret) return ret; if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index d6488674d916..7cc5a6054547 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1429,7 +1429,7 @@ static int gfs2_dinode_dealloc(struct gfs2_inode *ip) if (error) return error; - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_hold(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) return error; diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c index 76c144b3c9bb..80c25ae79048 100644 --- a/fs/gfs2/xattr.c +++ b/fs/gfs2/xattr.c @@ -331,7 +331,7 @@ static int ea_remove_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh, if (error) return error; - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_hold(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) goto out_alloc; @@ -1461,7 +1461,7 @@ int gfs2_ea_dealloc(struct gfs2_inode *ip) if (error) return error; - error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); + error = gfs2_quota_hold(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (error) return error; From 558e85289fca3d3397882442d1a695936c4f2662 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 18:15:33 -0800 Subject: [PATCH 2461/4607] gfs2: Report quotas in the caller's user namespace. When a quota is queried return the uid or the gid in the mapped into the caller's user namespace. In addition perform the munged version of the mapping so that instead of -1 a value that does not map is reported as the overflowuid or the overflowgid. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index dbfacaa8b6fb..f8279eed56e1 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1509,7 +1509,7 @@ static int gfs2_get_dqblk(struct super_block *sb, struct kqid qid, qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lksb.sb_lvbptr; fdq->d_version = FS_DQUOT_VERSION; fdq->d_flags = (type == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA; - fdq->d_id = from_kqid(&init_user_ns, qid); + fdq->d_id = from_kqid_munged(current_user_ns(), qid); fdq->d_blk_hardlimit = be64_to_cpu(qlvb->qb_limit) << sdp->sd_fsb2bb_shift; fdq->d_blk_softlimit = be64_to_cpu(qlvb->qb_warn) << sdp->sd_fsb2bb_shift; fdq->d_bcount = be64_to_cpu(qlvb->qb_value) << sdp->sd_fsb2bb_shift; From 2f6c9896f71e6b6c1c565ea76dd9f5e89579c120 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 18:33:38 -0800 Subject: [PATCH 2462/4607] gfs2: Introduce qd2index Both qd_alloc and qd2offset perform the exact same computation to get an index from a gfs2_quota_data. Make life a little simpler and factor out this index computation. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index f8279eed56e1..0e7c982377a1 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -120,11 +120,17 @@ out: return (atomic_read(&qd_lru_count) * sysctl_vfs_cache_pressure) / 100; } +static u64 qd2index(struct gfs2_quota_data *qd) +{ + return (2 * (u64)qd->qd_id) + + test_bit(QDF_USER, &qd->qd_flags) ? 0 : 1; +} + static u64 qd2offset(struct gfs2_quota_data *qd) { u64 offset; - offset = 2 * (u64)qd->qd_id + !test_bit(QDF_USER, &qd->qd_flags); + offset = qd2index(qd); offset *= sizeof(struct gfs2_quota); return offset; @@ -147,7 +153,7 @@ static int qd_alloc(struct gfs2_sbd *sdp, int user, u32 id, qd->qd_slot = -1; INIT_LIST_HEAD(&qd->qd_reclaim); - error = gfs2_glock_get(sdp, 2 * (u64)id + !user, + error = gfs2_glock_get(sdp, qd2index(qd), &gfs2_quota_glops, CREATE, &qd->qd_gl); if (error) goto fail; From e08d8d7f201dc1e64f5d9d5aa2cd4f37aecaaab4 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 19:25:50 -0800 Subject: [PATCH 2463/4607] gfs2: Modify struct gfs2_quota_change_host to use struct kqid Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 0e7c982377a1..02913e95491c 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -71,7 +71,7 @@ struct gfs2_quota_change_host { u64 qc_change; u32 qc_flags; /* GFS2_QCF_... */ - u32 qc_id; + struct kqid qc_id; }; static LIST_HEAD(qd_lru_list); @@ -1200,7 +1200,9 @@ static void gfs2_quota_change_in(struct gfs2_quota_change_host *qc, const void * qc->qc_change = be64_to_cpu(str->qc_change); qc->qc_flags = be32_to_cpu(str->qc_flags); - qc->qc_id = be32_to_cpu(str->qc_id); + qc->qc_id = make_kqid(&init_user_ns, + (qc->qc_flags & GFS2_QCF_USER)?USRQUOTA:GRPQUOTA, + be32_to_cpu(str->qc_id)); } int gfs2_quota_init(struct gfs2_sbd *sdp) @@ -1264,7 +1266,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) continue; error = qd_alloc(sdp, (qc.qc_flags & GFS2_QCF_USER), - qc.qc_id, &qd); + from_kqid(&init_user_ns, qc.qc_id), &qd); if (error) { brelse(bh); goto fail; From b59c8b6f9d1b1220e5ed72152f42a658bf739d90 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 19:35:56 -0800 Subject: [PATCH 2464/4607] gfs2: Modify qdsb_get to take a struct kqid Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 02913e95491c..20762ae2a9c4 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -464,12 +464,13 @@ static void qd_unlock(struct gfs2_quota_data *qd) qd_put(qd); } -static int qdsb_get(struct gfs2_sbd *sdp, int user, u32 id, +static int qdsb_get(struct gfs2_sbd *sdp, struct kqid qid, struct gfs2_quota_data **qdp) { int error; - error = qd_get(sdp, user, id, qdp); + error = qd_get(sdp, qid.type == USRQUOTA ? QUOTA_USER : QUOTA_GROUP, + from_kqid(&init_user_ns, qid), qdp); if (error) return error; @@ -518,20 +519,20 @@ int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid) if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF) return 0; - error = qdsb_get(sdp, QUOTA_USER, ip->i_inode.i_uid, qd); + error = qdsb_get(sdp, make_kqid_uid(ip->i_inode.i_uid), qd); if (error) goto out; ip->i_res->rs_qa_qd_num++; qd++; - error = qdsb_get(sdp, QUOTA_GROUP, ip->i_inode.i_gid, qd); + error = qdsb_get(sdp, make_kqid_gid(ip->i_inode.i_gid), qd); if (error) goto out; ip->i_res->rs_qa_qd_num++; qd++; if (uid != NO_UID_QUOTA_CHANGE && uid != ip->i_inode.i_uid) { - error = qdsb_get(sdp, QUOTA_USER, uid, qd); + error = qdsb_get(sdp, make_kqid_uid(uid), qd); if (error) goto out; ip->i_res->rs_qa_qd_num++; @@ -539,7 +540,7 @@ int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid) } if (gid != NO_GID_QUOTA_CHANGE && gid != ip->i_inode.i_gid) { - error = qdsb_get(sdp, QUOTA_GROUP, gid, qd); + error = qdsb_get(sdp, make_kqid_gid(gid), qd); if (error) goto out; ip->i_res->rs_qa_qd_num++; From ed87dabcc3fc0a5040f95dd3f7206cffebca5c79 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 19:42:40 -0800 Subject: [PATCH 2465/4607] gfs2: Convert gfs2_quota_refresh to take a kqid - In quota_refresh_user_store convert the user supplied uid into a kqid and pass it to gfs2_quota_refresh. - In quota_refresh_group_store convert the user supplied gid into a kqid and pass it to gfs2_quota_refresh. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 5 +++-- fs/gfs2/quota.h | 2 +- fs/gfs2/sys.c | 14 ++++++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 20762ae2a9c4..47315c091a09 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1177,13 +1177,14 @@ static int gfs2_quota_sync_timeo(struct super_block *sb, int type) return gfs2_quota_sync(sb, type); } -int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id) +int gfs2_quota_refresh(struct gfs2_sbd *sdp, struct kqid qid) { struct gfs2_quota_data *qd; struct gfs2_holder q_gh; int error; - error = qd_get(sdp, user, id, &qd); + error = qd_get(sdp, qid.type == USRQUOTA ? QUOTA_USER : QUOTA_GROUP, + from_kqid(&init_user_ns, qid), &qd); if (error) return error; diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index 7f67323a6a72..bef805de8491 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h @@ -28,7 +28,7 @@ extern void gfs2_quota_change(struct gfs2_inode *ip, s64 change, u32 uid, u32 gid); extern int gfs2_quota_sync(struct super_block *sb, int type); -extern int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, u32 id); +extern int gfs2_quota_refresh(struct gfs2_sbd *sdp, struct kqid qid); extern int gfs2_quota_init(struct gfs2_sbd *sdp); extern void gfs2_quota_cleanup(struct gfs2_sbd *sdp); diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index 8056b7b7238e..e6d8d482422f 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -175,6 +175,7 @@ static ssize_t quota_sync_store(struct gfs2_sbd *sdp, const char *buf, static ssize_t quota_refresh_user_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { + struct kqid qid; int error; u32 id; @@ -183,13 +184,18 @@ static ssize_t quota_refresh_user_store(struct gfs2_sbd *sdp, const char *buf, id = simple_strtoul(buf, NULL, 0); - error = gfs2_quota_refresh(sdp, 1, id); + qid = make_kqid(current_user_ns(), USRQUOTA, id); + if (!qid_valid(qid)) + return -EINVAL; + + error = gfs2_quota_refresh(sdp, qid); return error ? error : len; } static ssize_t quota_refresh_group_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { + struct kqid qid; int error; u32 id; @@ -198,7 +204,11 @@ static ssize_t quota_refresh_group_store(struct gfs2_sbd *sdp, const char *buf, id = simple_strtoul(buf, NULL, 0); - error = gfs2_quota_refresh(sdp, 0, id); + qid = make_kqid(current_user_ns(), GRPQUOTA, id); + if (!qid_valid(qid)) + return -EINVAL; + + error = gfs2_quota_refresh(sdp, qid); return error ? error : len; } From 05e0a60d8025e280e56b3fa36ea8facc7c1c65c2 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 19:52:08 -0800 Subject: [PATCH 2466/4607] gfs2: Store qd_id in struct gfs2_quota_data as a struct kqid - Change qd_id in struct gfs2_qutoa_data to struct kqid. - Remove the now unnecessary QDF_USER bit field in qd_flags. - Propopoage this change through the code generally making things simpler along the way. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/incore.h | 3 +-- fs/gfs2/quota.c | 69 ++++++++++++++++++------------------------------ 2 files changed, 26 insertions(+), 46 deletions(-) diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index c373a24fedd9..5b298bdab90c 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -391,7 +391,6 @@ struct gfs2_revoke_replay { }; enum { - QDF_USER = 0, QDF_CHANGE = 1, QDF_LOCKED = 2, QDF_REFRESH = 3, @@ -403,7 +402,7 @@ struct gfs2_quota_data { atomic_t qd_count; - u32 qd_id; + struct kqid qd_id; unsigned long qd_flags; /* QDF_... */ s64 qd_change; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 47315c091a09..8cb4d10cb16a 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -122,8 +122,9 @@ out: static u64 qd2index(struct gfs2_quota_data *qd) { - return (2 * (u64)qd->qd_id) + - test_bit(QDF_USER, &qd->qd_flags) ? 0 : 1; + struct kqid qid = qd->qd_id; + return (2 * (u64)from_kqid(&init_user_ns, qid)) + + (qid.type == USRQUOTA) ? 0 : 1; } static u64 qd2offset(struct gfs2_quota_data *qd) @@ -136,7 +137,7 @@ static u64 qd2offset(struct gfs2_quota_data *qd) return offset; } -static int qd_alloc(struct gfs2_sbd *sdp, int user, u32 id, +static int qd_alloc(struct gfs2_sbd *sdp, struct kqid qid, struct gfs2_quota_data **qdp) { struct gfs2_quota_data *qd; @@ -147,9 +148,7 @@ static int qd_alloc(struct gfs2_sbd *sdp, int user, u32 id, return -ENOMEM; atomic_set(&qd->qd_count, 1); - qd->qd_id = id; - if (user) - set_bit(QDF_USER, &qd->qd_flags); + qd->qd_id = qid; qd->qd_slot = -1; INIT_LIST_HEAD(&qd->qd_reclaim); @@ -167,7 +166,7 @@ fail: return error; } -static int qd_get(struct gfs2_sbd *sdp, int user, u32 id, +static int qd_get(struct gfs2_sbd *sdp, struct kqid qid, struct gfs2_quota_data **qdp) { struct gfs2_quota_data *qd = NULL, *new_qd = NULL; @@ -179,8 +178,7 @@ static int qd_get(struct gfs2_sbd *sdp, int user, u32 id, found = 0; spin_lock(&qd_lru_lock); list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { - if (qd->qd_id == id && - !test_bit(QDF_USER, &qd->qd_flags) == !user) { + if (qid_eq(qd->qd_id, qid)) { if (!atomic_read(&qd->qd_count) && !list_empty(&qd->qd_reclaim)) { /* Remove it from reclaim list */ @@ -214,7 +212,7 @@ static int qd_get(struct gfs2_sbd *sdp, int user, u32 id, return 0; } - error = qd_alloc(sdp, user, id, &new_qd); + error = qd_alloc(sdp, qid, &new_qd); if (error) return error; } @@ -469,8 +467,7 @@ static int qdsb_get(struct gfs2_sbd *sdp, struct kqid qid, { int error; - error = qd_get(sdp, qid.type == USRQUOTA ? QUOTA_USER : QUOTA_GROUP, - from_kqid(&init_user_ns, qid), qdp); + error = qd_get(sdp, qid, qdp); if (error) return error; @@ -574,18 +571,10 @@ static int sort_qd(const void *a, const void *b) const struct gfs2_quota_data *qd_a = *(const struct gfs2_quota_data **)a; const struct gfs2_quota_data *qd_b = *(const struct gfs2_quota_data **)b; - if (!test_bit(QDF_USER, &qd_a->qd_flags) != - !test_bit(QDF_USER, &qd_b->qd_flags)) { - if (test_bit(QDF_USER, &qd_a->qd_flags)) - return -1; - else - return 1; - } - if (qd_a->qd_id < qd_b->qd_id) + if (qid_lt(qd_a->qd_id, qd_b->qd_id)) return -1; - if (qd_a->qd_id > qd_b->qd_id) + if (qid_lt(qd_b->qd_id, qd_a->qd_id)) return 1; - return 0; } @@ -602,9 +591,9 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change) if (!test_bit(QDF_CHANGE, &qd->qd_flags)) { qc->qc_change = 0; qc->qc_flags = 0; - if (test_bit(QDF_USER, &qd->qd_flags)) + if (qd->qd_id.type == USRQUOTA) qc->qc_flags = cpu_to_be32(GFS2_QCF_USER); - qc->qc_id = cpu_to_be32(qd->qd_id); + qc->qc_id = cpu_to_be32(from_kqid(&init_user_ns, qd->qd_id)); } x = be64_to_cpu(qc->qc_change) + change; @@ -1047,8 +1036,8 @@ static int print_message(struct gfs2_quota_data *qd, char *type) printk(KERN_INFO "GFS2: fsid=%s: quota %s for %s %u\n", sdp->sd_fsname, type, - (test_bit(QDF_USER, &qd->qd_flags)) ? "user" : "group", - qd->qd_id); + (qd->qd_id.type == USRQUOTA) ? "user" : "group", + from_kqid(&init_user_ns, qd->qd_id)); return 0; } @@ -1070,8 +1059,8 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid) for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) { qd = ip->i_res->rs_qa_qd[x]; - if (!((qd->qd_id == uid && test_bit(QDF_USER, &qd->qd_flags)) || - (qd->qd_id == gid && !test_bit(QDF_USER, &qd->qd_flags)))) + if (!(qid_eq(qd->qd_id, make_kqid_uid(uid)) || + qid_eq(qd->qd_id, make_kqid_gid(gid)))) continue; value = (s64)be64_to_cpu(qd->qd_qb.qb_value); @@ -1081,10 +1070,7 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid) if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) { print_message(qd, "exceeded"); - quota_send_warning(make_kqid(&init_user_ns, - test_bit(QDF_USER, &qd->qd_flags) ? - USRQUOTA : GRPQUOTA, - qd->qd_id), + quota_send_warning(qd->qd_id, sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN); error = -EDQUOT; @@ -1094,10 +1080,7 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid) time_after_eq(jiffies, qd->qd_last_warn + gfs2_tune_get(sdp, gt_quota_warn_period) * HZ)) { - quota_send_warning(make_kqid(&init_user_ns, - test_bit(QDF_USER, &qd->qd_flags) ? - USRQUOTA : GRPQUOTA, - qd->qd_id), + quota_send_warning(qd->qd_id, sdp->sd_vfs->s_dev, QUOTA_NL_BSOFTWARN); error = print_message(qd, "warning"); qd->qd_last_warn = jiffies; @@ -1121,8 +1104,8 @@ void gfs2_quota_change(struct gfs2_inode *ip, s64 change, for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) { qd = ip->i_res->rs_qa_qd[x]; - if ((qd->qd_id == uid && test_bit(QDF_USER, &qd->qd_flags)) || - (qd->qd_id == gid && !test_bit(QDF_USER, &qd->qd_flags))) { + if (qid_eq(qd->qd_id, make_kqid_uid(uid)) || + qid_eq(qd->qd_id, make_kqid_gid(gid))) { do_qc(qd, change); } } @@ -1183,8 +1166,7 @@ int gfs2_quota_refresh(struct gfs2_sbd *sdp, struct kqid qid) struct gfs2_holder q_gh; int error; - error = qd_get(sdp, qid.type == USRQUOTA ? QUOTA_USER : QUOTA_GROUP, - from_kqid(&init_user_ns, qid), &qd); + error = qd_get(sdp, qid, &qd); if (error) return error; @@ -1267,8 +1249,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) if (!qc.qc_change) continue; - error = qd_alloc(sdp, (qc.qc_flags & GFS2_QCF_USER), - from_kqid(&init_user_ns, qc.qc_id), &qd); + error = qd_alloc(sdp, qc.qc_id, &qd); if (error) { brelse(bh); goto fail; @@ -1509,7 +1490,7 @@ static int gfs2_get_dqblk(struct super_block *sb, struct kqid qid, else return -EINVAL; - error = qd_get(sdp, type, from_kqid(&init_user_ns, qid), &qd); + error = qd_get(sdp, qid, &qd); if (error) return error; error = do_glock(qd, FORCE, &q_gh); @@ -1564,7 +1545,7 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, if (fdq->d_fieldmask & ~GFS2_FIELDMASK) return -EINVAL; - error = qd_get(sdp, type, from_kqid(&init_user_ns, qid), &qd); + error = qd_get(sdp, qid, &qd); if (error) return error; From 236c64e4b79b78059ec3e17362d8f02f6dc06f26 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 20:09:30 -0800 Subject: [PATCH 2467/4607] gfs2: Remove the QUOTA_USER and QUOTA_GROUP defines Remove the QUOTA_USER and QUOTA_GRUP defines. Remove the last vestigal users of QUOTA_USER and QUOTA_GROUP. Now that struct kqid is used throughout the gfs2 quota code the need there is to use QUOTA_USER and QUOTA_GROUP and the defines are just extraneous and confusing. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/quota.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 8cb4d10cb16a..0bbb0407fd96 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -65,9 +65,6 @@ #include "inode.h" #include "util.h" -#define QUOTA_USER 1 -#define QUOTA_GROUP 0 - struct gfs2_quota_change_host { u64 qc_change; u32 qc_flags; /* GFS2_QCF_... */ @@ -1476,18 +1473,14 @@ static int gfs2_get_dqblk(struct super_block *sb, struct kqid qid, struct gfs2_quota_data *qd; struct gfs2_holder q_gh; int error; - int type; memset(fdq, 0, sizeof(struct fs_disk_quota)); if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF) return -ESRCH; /* Crazy XFS error code */ - if (qid.type == USRQUOTA) - type = QUOTA_USER; - else if (qid.type == GRPQUOTA) - type = QUOTA_GROUP; - else + if ((qid.type != USRQUOTA) && + (qid.type != GRPQUOTA)) return -EINVAL; error = qd_get(sdp, qid, &qd); @@ -1499,7 +1492,7 @@ static int gfs2_get_dqblk(struct super_block *sb, struct kqid qid, qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lksb.sb_lvbptr; fdq->d_version = FS_DQUOT_VERSION; - fdq->d_flags = (type == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA; + fdq->d_flags = (qid.type == USRQUOTA) ? FS_USER_QUOTA : FS_GROUP_QUOTA; fdq->d_id = from_kqid_munged(current_user_ns(), qid); fdq->d_blk_hardlimit = be64_to_cpu(qlvb->qb_limit) << sdp->sd_fsb2bb_shift; fdq->d_blk_softlimit = be64_to_cpu(qlvb->qb_warn) << sdp->sd_fsb2bb_shift; @@ -1526,21 +1519,13 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, int alloc_required; loff_t offset; int error; - int type; if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF) return -ESRCH; /* Crazy XFS error code */ - switch(qid.type) { - case USRQUOTA: - type = QUOTA_USER; - break; - case GRPQUOTA: - type = QUOTA_GROUP; - break; - default: + if ((qid.type != USRQUOTA) && + (qid.type != GRPQUOTA)) return -EINVAL; - } if (fdq->d_fieldmask & ~GFS2_FIELDMASK) return -EINVAL; From 7c06b5d67225dc99ca81a33db3e055e08da857c3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 20:27:54 -0800 Subject: [PATCH 2468/4607] gfs2: Use kuid_t and kgid_t types where appropriate. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/inode.c | 3 ++- fs/gfs2/quota.c | 8 ++++---- fs/gfs2/quota.h | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index aa1050287f3c..ce07ce4734f4 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1580,7 +1580,8 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); - u32 ouid, ogid, nuid, ngid; + kuid_t ouid, nuid; + kgid_t ogid, ngid; int error; ouid = inode->i_uid; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 0bbb0407fd96..87f274039c4b 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -492,7 +492,7 @@ static void qdsb_put(struct gfs2_quota_data *qd) qd_put(qd); } -int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid) +int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_quota_data **qd; @@ -918,7 +918,7 @@ fail: return error; } -int gfs2_quota_lock(struct gfs2_inode *ip, u32 uid, u32 gid) +int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_quota_data *qd; @@ -1039,7 +1039,7 @@ static int print_message(struct gfs2_quota_data *qd, char *type) return 0; } -int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid) +int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_quota_data *qd; @@ -1088,7 +1088,7 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid) } void gfs2_quota_change(struct gfs2_inode *ip, s64 change, - u32 uid, u32 gid) + kuid_t uid, kgid_t gid) { struct gfs2_quota_data *qd; unsigned int x; diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index bef805de8491..4f5e6e44ed83 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h @@ -17,15 +17,15 @@ struct shrink_control; #define NO_UID_QUOTA_CHANGE INVALID_UID #define NO_GID_QUOTA_CHANGE INVALID_GID -extern int gfs2_quota_hold(struct gfs2_inode *ip, u32 uid, u32 gid); +extern int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid); extern void gfs2_quota_unhold(struct gfs2_inode *ip); -extern int gfs2_quota_lock(struct gfs2_inode *ip, u32 uid, u32 gid); +extern int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid); extern void gfs2_quota_unlock(struct gfs2_inode *ip); -extern int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid); +extern int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid); extern void gfs2_quota_change(struct gfs2_inode *ip, s64 change, - u32 uid, u32 gid); + kuid_t uid, kgid_t gid); extern int gfs2_quota_sync(struct super_block *sb, int type); extern int gfs2_quota_refresh(struct gfs2_sbd *sdp, struct kqid qid); From 6b24c0d279eacfb499854d09ea7f2b69d1721a29 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 21:56:13 -0800 Subject: [PATCH 2469/4607] gfs2: Use uid_eq and gid_eq where appropriate Where kuid_t values are compared use uid_eq and where kgid_t values are compared use gid_eq. This is unfortunately necessary because of the type safety that keeps someone from accidentally mixing kuids and kgids with other types. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/acl.c | 2 +- fs/gfs2/inode.c | 19 +++++++++++-------- fs/gfs2/quota.c | 6 ++++-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c index f850020ad906..f69ac0af5496 100644 --- a/fs/gfs2/acl.c +++ b/fs/gfs2/acl.c @@ -237,7 +237,7 @@ static int gfs2_xattr_system_set(struct dentry *dentry, const char *name, return -EINVAL; if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode)) return value ? -EACCES : 0; - if ((current_fsuid() != inode->i_uid) && !capable(CAP_FOWNER)) + if (!uid_eq(current_fsuid(), inode->i_uid) && !capable(CAP_FOWNER)) return -EPERM; if (S_ISLNK(inode->i_mode)) return -EOPNOTSUPP; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index ce07ce4734f4..bb7c754f46a2 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -368,10 +368,11 @@ static void munge_mode_uid_gid(const struct gfs2_inode *dip, struct inode *inode) { if (GFS2_SB(&dip->i_inode)->sd_args.ar_suiddir && - (dip->i_inode.i_mode & S_ISUID) && dip->i_inode.i_uid) { + (dip->i_inode.i_mode & S_ISUID) && + !uid_eq(dip->i_inode.i_uid, GLOBAL_ROOT_UID)) { if (S_ISDIR(inode->i_mode)) inode->i_mode |= S_ISUID; - else if (dip->i_inode.i_uid != current_fsuid()) + else if (!uid_eq(dip->i_inode.i_uid, current_fsuid())) inode->i_mode &= ~07111; inode->i_uid = dip->i_inode.i_uid; } else @@ -978,8 +979,8 @@ static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name, return -EPERM; if ((dip->i_inode.i_mode & S_ISVTX) && - dip->i_inode.i_uid != current_fsuid() && - ip->i_inode.i_uid != current_fsuid() && !capable(CAP_FOWNER)) + !uid_eq(dip->i_inode.i_uid, current_fsuid()) && + !uid_eq(ip->i_inode.i_uid, current_fsuid()) && !capable(CAP_FOWNER)) return -EPERM; if (IS_APPEND(&dip->i_inode)) @@ -1589,16 +1590,17 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) nuid = attr->ia_uid; ngid = attr->ia_gid; - if (!(attr->ia_valid & ATTR_UID) || ouid == nuid) + if (!(attr->ia_valid & ATTR_UID) || uid_eq(ouid, nuid)) ouid = nuid = NO_UID_QUOTA_CHANGE; - if (!(attr->ia_valid & ATTR_GID) || ogid == ngid) + if (!(attr->ia_valid & ATTR_GID) || gid_eq(ogid, ngid)) ogid = ngid = NO_GID_QUOTA_CHANGE; error = gfs2_quota_lock(ip, nuid, ngid); if (error) return error; - if (ouid != NO_UID_QUOTA_CHANGE || ogid != NO_GID_QUOTA_CHANGE) { + if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || + !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { error = gfs2_quota_check(ip, nuid, ngid); if (error) goto out_gunlock_q; @@ -1612,7 +1614,8 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) if (error) goto out_end_trans; - if (ouid != NO_UID_QUOTA_CHANGE || ogid != NO_GID_QUOTA_CHANGE) { + if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || + !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { u64 blocks = gfs2_get_inode_blocks(&ip->i_inode); gfs2_quota_change(ip, -blocks, ouid, ogid); gfs2_quota_change(ip, blocks, nuid, ngid); diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 87f274039c4b..afd2e5d38e5a 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -525,7 +525,8 @@ int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) ip->i_res->rs_qa_qd_num++; qd++; - if (uid != NO_UID_QUOTA_CHANGE && uid != ip->i_inode.i_uid) { + if (!uid_eq(uid, NO_UID_QUOTA_CHANGE) && + !uid_eq(uid, ip->i_inode.i_uid)) { error = qdsb_get(sdp, make_kqid_uid(uid), qd); if (error) goto out; @@ -533,7 +534,8 @@ int gfs2_quota_hold(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) qd++; } - if (gid != NO_GID_QUOTA_CHANGE && gid != ip->i_inode.i_gid) { + if (!gid_eq(gid, NO_GID_QUOTA_CHANGE) && + !gid_eq(gid, ip->i_inode.i_gid)) { error = qdsb_get(sdp, make_kqid_gid(gid), qd); if (error) goto out; From d0546426426ca96f0b815581c0a9ed0db28319b6 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 22:08:10 -0800 Subject: [PATCH 2470/4607] gfs2: Convert uids and gids between dinodes and vfs inodes. When reading dinodes from the disk convert uids and gids into kuids and kgids to store in vfs data structures. When writing to dinodes to the disk convert kuids and kgids in the in memory structures into plain uids and gids. For now all on disk data structures are assumed to be stored in the initial user namespace. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- fs/gfs2/glops.c | 4 ++-- fs/gfs2/inode.c | 4 ++-- fs/gfs2/super.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 78d4184ffc7d..444b6503ebc4 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -322,8 +322,8 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) break; }; - ip->i_inode.i_uid = be32_to_cpu(str->di_uid); - ip->i_inode.i_gid = be32_to_cpu(str->di_gid); + i_uid_write(&ip->i_inode, be32_to_cpu(str->di_uid)); + i_gid_write(&ip->i_inode, be32_to_cpu(str->di_gid)); gfs2_set_nlink(&ip->i_inode, be32_to_cpu(str->di_nlink)); i_size_write(&ip->i_inode, be64_to_cpu(str->di_size)); gfs2_set_inode_blocks(&ip->i_inode, be64_to_cpu(str->di_blocks)); diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index bb7c754f46a2..b862d114e155 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -456,8 +456,8 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_inode *ip, di->di_num.no_formal_ino = cpu_to_be64(ip->i_no_formal_ino); di->di_num.no_addr = cpu_to_be64(ip->i_no_addr); di->di_mode = cpu_to_be32(ip->i_inode.i_mode); - di->di_uid = cpu_to_be32(ip->i_inode.i_uid); - di->di_gid = cpu_to_be32(ip->i_inode.i_gid); + di->di_uid = cpu_to_be32(i_uid_read(&ip->i_inode)); + di->di_gid = cpu_to_be32(i_gid_read(&ip->i_inode)); di->di_nlink = 0; di->di_size = cpu_to_be64(ip->i_inode.i_size); di->di_blocks = cpu_to_be64(1); diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 7cc5a6054547..a03425376500 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -721,8 +721,8 @@ void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf) str->di_num.no_addr = cpu_to_be64(ip->i_no_addr); str->di_num.no_formal_ino = cpu_to_be64(ip->i_no_formal_ino); str->di_mode = cpu_to_be32(ip->i_inode.i_mode); - str->di_uid = cpu_to_be32(ip->i_inode.i_uid); - str->di_gid = cpu_to_be32(ip->i_inode.i_gid); + str->di_uid = cpu_to_be32(i_uid_read(&ip->i_inode)); + str->di_gid = cpu_to_be32(i_gid_read(&ip->i_inode)); str->di_nlink = cpu_to_be32(ip->i_inode.i_nlink); str->di_size = cpu_to_be64(i_size_read(&ip->i_inode)); str->di_blocks = cpu_to_be64(gfs2_get_inode_blocks(&ip->i_inode)); From 0f07bd3753e25c80fe24428273c791f350b3a1eb Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 31 Jan 2013 22:17:00 -0800 Subject: [PATCH 2471/4607] gfs2: Enable building with user namespaces enabled Now that all of the necessary work has been done to push kuids and kgids throughout gfs2 and to convert between kuids and kgids when reading and writing the on disk structures it is safe to enable gfs2 when multiple user namespaces are enabled. Cc: Steven Whitehouse Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index 9cb63c94cab8..591fc75710b3 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1072,7 +1072,6 @@ config UIDGID_CONVERTED # Filesystems depends on CIFS = n - depends on GFS2_FS = n depends on NCP_FS = n depends on NFSD = n depends on NFS_FS = n From 1ac7fd8190b79c822631ed537186fb8b2d9e9b74 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Feb 2012 16:28:28 -0800 Subject: [PATCH 2472/4607] ncpfs: Support interacting with multiple user namespaces ncpfs does not natively support uids and gids so this conversion was simply a matter of updating the the type of the mounteduid, the uid and the gid on the superblock. Fixing the ioctls that read them, updating the mount option parser and the mount option printer. Cc: Petr Vandrovec Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- fs/ncpfs/inode.c | 55 ++++++++++++++++++++++++++------------------ fs/ncpfs/ioctl.c | 25 +++++++++++--------- fs/ncpfs/ncp_fs_sb.h | 6 ++--- init/Kconfig | 1 - 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 1acdad7fcec7..e2be336d1c22 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -331,12 +331,15 @@ static int ncp_show_options(struct seq_file *seq, struct dentry *root) struct ncp_server *server = NCP_SBP(root->d_sb); unsigned int tmp; - if (server->m.uid != 0) - seq_printf(seq, ",uid=%u", server->m.uid); - if (server->m.gid != 0) - seq_printf(seq, ",gid=%u", server->m.gid); - if (server->m.mounted_uid != 0) - seq_printf(seq, ",owner=%u", server->m.mounted_uid); + if (!uid_eq(server->m.uid, GLOBAL_ROOT_UID)) + seq_printf(seq, ",uid=%u", + from_kuid_munged(&init_user_ns, server->m.uid)); + if (!gid_eq(server->m.gid, GLOBAL_ROOT_GID)) + seq_printf(seq, ",gid=%u", + from_kgid_munged(&init_user_ns, server->m.gid)); + if (!uid_eq(server->m.mounted_uid, GLOBAL_ROOT_UID)) + seq_printf(seq, ",owner=%u", + from_kuid_munged(&init_user_ns, server->m.mounted_uid)); tmp = server->m.file_mode & S_IALLUGO; if (tmp != NCP_DEFAULT_FILE_MODE) seq_printf(seq, ",mode=0%o", tmp); @@ -381,13 +384,13 @@ static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) data->flags = 0; data->int_flags = 0; - data->mounted_uid = 0; + data->mounted_uid = GLOBAL_ROOT_UID; data->wdog_pid = NULL; data->ncp_fd = ~0; data->time_out = NCP_DEFAULT_TIME_OUT; data->retry_count = NCP_DEFAULT_RETRY_COUNT; - data->uid = 0; - data->gid = 0; + data->uid = GLOBAL_ROOT_UID; + data->gid = GLOBAL_ROOT_GID; data->file_mode = NCP_DEFAULT_FILE_MODE; data->dir_mode = NCP_DEFAULT_DIR_MODE; data->info_fd = -1; @@ -399,13 +402,19 @@ static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) goto err; switch (optval) { case 'u': - data->uid = optint; + data->uid = make_kuid(current_user_ns(), optint); + if (!uid_valid(data->uid)) + goto err; break; case 'g': - data->gid = optint; + data->gid = make_kgid(current_user_ns(), optint); + if (!gid_valid(data->gid)) + goto err; break; case 'o': - data->mounted_uid = optint; + data->mounted_uid = make_kuid(current_user_ns(), optint); + if (!uid_valid(data->mounted_uid)) + goto err; break; case 'm': data->file_mode = optint; @@ -480,13 +489,13 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) data.flags = md->flags; data.int_flags = NCP_IMOUNT_LOGGEDIN_POSSIBLE; - data.mounted_uid = md->mounted_uid; + data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid); data.wdog_pid = find_get_pid(md->wdog_pid); data.ncp_fd = md->ncp_fd; data.time_out = md->time_out; data.retry_count = md->retry_count; - data.uid = md->uid; - data.gid = md->gid; + data.uid = make_kuid(current_user_ns(), md->uid); + data.gid = make_kgid(current_user_ns(), md->gid); data.file_mode = md->file_mode; data.dir_mode = md->dir_mode; data.info_fd = -1; @@ -499,13 +508,13 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) struct ncp_mount_data_v4* md = (struct ncp_mount_data_v4*)raw_data; data.flags = md->flags; - data.mounted_uid = md->mounted_uid; + data.mounted_uid = make_kuid(current_user_ns(), md->mounted_uid); data.wdog_pid = find_get_pid(md->wdog_pid); data.ncp_fd = md->ncp_fd; data.time_out = md->time_out; data.retry_count = md->retry_count; - data.uid = md->uid; - data.gid = md->gid; + data.uid = make_kuid(current_user_ns(), md->uid); + data.gid = make_kgid(current_user_ns(), md->gid); data.file_mode = md->file_mode; data.dir_mode = md->dir_mode; data.info_fd = -1; @@ -520,6 +529,10 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) goto out; break; } + error = -EINVAL; + if (!uid_valid(data.mounted_uid) || !uid_valid(data.uid) || + !gid_valid(data.gid)) + goto out; error = -EBADF; ncp_filp = fget(data.ncp_fd); if (!ncp_filp) @@ -886,12 +899,10 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) goto out; result = -EPERM; - if (((attr->ia_valid & ATTR_UID) && - (attr->ia_uid != server->m.uid))) + if ((attr->ia_valid & ATTR_UID) && !uid_eq(attr->ia_uid, server->m.uid)) goto out; - if (((attr->ia_valid & ATTR_GID) && - (attr->ia_gid != server->m.gid))) + if ((attr->ia_valid & ATTR_GID) && !gid_eq(attr->ia_gid, server->m.gid)) goto out; if (((attr->ia_valid & ATTR_MODE) && diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 6958adfaff08..d44318d27504 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -45,7 +45,7 @@ ncp_get_fs_info(struct ncp_server * server, struct inode *inode, return -EINVAL; } /* TODO: info.addr = server->m.serv_addr; */ - SET_UID(info.mounted_uid, server->m.mounted_uid); + SET_UID(info.mounted_uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid)); info.connection = server->connection; info.buffer_size = server->buffer_size; info.volume_number = NCP_FINFO(inode)->volNumber; @@ -69,7 +69,7 @@ ncp_get_fs_info_v2(struct ncp_server * server, struct inode *inode, DPRINTK("info.version invalid: %d\n", info2.version); return -EINVAL; } - info2.mounted_uid = server->m.mounted_uid; + info2.mounted_uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); info2.connection = server->connection; info2.buffer_size = server->buffer_size; info2.volume_number = NCP_FINFO(inode)->volNumber; @@ -135,7 +135,7 @@ ncp_get_compat_fs_info_v2(struct ncp_server * server, struct inode *inode, DPRINTK("info.version invalid: %d\n", info2.version); return -EINVAL; } - info2.mounted_uid = server->m.mounted_uid; + info2.mounted_uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); info2.connection = server->connection; info2.buffer_size = server->buffer_size; info2.volume_number = NCP_FINFO(inode)->volNumber; @@ -348,22 +348,25 @@ static long __ncp_ioctl(struct inode *inode, unsigned int cmd, unsigned long arg { u16 uid; - SET_UID(uid, server->m.mounted_uid); + SET_UID(uid, from_kuid_munged(current_user_ns(), server->m.mounted_uid)); if (put_user(uid, (u16 __user *)argp)) return -EFAULT; return 0; } case NCP_IOC_GETMOUNTUID32: - if (put_user(server->m.mounted_uid, - (u32 __user *)argp)) + { + uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); + if (put_user(uid, (u32 __user *)argp)) return -EFAULT; return 0; + } case NCP_IOC_GETMOUNTUID64: - if (put_user(server->m.mounted_uid, - (u64 __user *)argp)) + { + uid_t uid = from_kuid_munged(current_user_ns(), server->m.mounted_uid); + if (put_user(uid, (u64 __user *)argp)) return -EFAULT; return 0; - + } case NCP_IOC_GETROOT: { struct ncp_setroot_ioctl sr; @@ -810,7 +813,7 @@ long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; struct ncp_server *server = NCP_SERVER(inode); - uid_t uid = current_uid(); + kuid_t uid = current_uid(); int need_drop_write = 0; long ret; @@ -824,7 +827,7 @@ long ncp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; } - if (server->m.mounted_uid != uid) { + if (!uid_eq(server->m.mounted_uid, uid)) { switch (cmd) { /* * Only mount owner can issue these ioctls. Information diff --git a/fs/ncpfs/ncp_fs_sb.h b/fs/ncpfs/ncp_fs_sb.h index 54cc0cdb3dcb..c51b2c543539 100644 --- a/fs/ncpfs/ncp_fs_sb.h +++ b/fs/ncpfs/ncp_fs_sb.h @@ -23,15 +23,15 @@ struct ncp_mount_data_kernel { unsigned long flags; /* NCP_MOUNT_* flags */ unsigned int int_flags; /* internal flags */ #define NCP_IMOUNT_LOGGEDIN_POSSIBLE 0x0001 - uid_t mounted_uid; /* Who may umount() this filesystem? */ + kuid_t mounted_uid; /* Who may umount() this filesystem? */ struct pid *wdog_pid; /* Who cares for our watchdog packets? */ unsigned int ncp_fd; /* The socket to the ncp port */ unsigned int time_out; /* How long should I wait after sending a NCP request? */ unsigned int retry_count; /* And how often should I retry? */ unsigned char mounted_vol[NCP_VOLNAME_LEN + 1]; - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; umode_t file_mode; umode_t dir_mode; int info_fd; diff --git a/init/Kconfig b/init/Kconfig index 591fc75710b3..b526f4c35b95 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1072,7 +1072,6 @@ config UIDGID_CONVERTED # Filesystems depends on CIFS = n - depends on NCP_FS = n depends on NFSD = n depends on NFS_FS = n depends on XFS_FS = n From ddca4e1730cf1b72044ae76ddf17b29d790b4dbc Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 14:50:52 -0800 Subject: [PATCH 2473/4607] nfs_common: Update the translation between nfsv3 acls linux posix acls - Use kuid_t and kgit in struct nfsacl_encode_desc. - Convert from kuids and kgids when generating on the wire values. - Convert on the wire values to kuids and kgids when read. - Modify cmp_acl_entry to be type safe comparison on posix acls. Only acls with type ACL_USER and ACL_GROUP can appear more than once and as such need to compare more than their tag. - The e_id field is being removed from posix acls so don't initialize it. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs_common/nfsacl.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/fs/nfs_common/nfsacl.c b/fs/nfs_common/nfsacl.c index 6940439bd609..ed628f71274c 100644 --- a/fs/nfs_common/nfsacl.c +++ b/fs/nfs_common/nfsacl.c @@ -38,8 +38,8 @@ struct nfsacl_encode_desc { unsigned int count; struct posix_acl *acl; int typeflag; - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; }; struct nfsacl_simple_acl { @@ -60,14 +60,16 @@ xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem) *p++ = htonl(entry->e_tag | nfsacl_desc->typeflag); switch(entry->e_tag) { case ACL_USER_OBJ: - *p++ = htonl(nfsacl_desc->uid); + *p++ = htonl(from_kuid(&init_user_ns, nfsacl_desc->uid)); break; case ACL_GROUP_OBJ: - *p++ = htonl(nfsacl_desc->gid); + *p++ = htonl(from_kgid(&init_user_ns, nfsacl_desc->gid)); break; case ACL_USER: + *p++ = htonl(from_kuid(&init_user_ns, entry->e_uid)); + break; case ACL_GROUP: - *p++ = htonl(entry->e_id); + *p++ = htonl(from_kgid(&init_user_ns, entry->e_gid)); break; default: /* Solaris depends on that! */ *p++ = 0; @@ -148,6 +150,7 @@ xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem) (struct nfsacl_decode_desc *) desc; __be32 *p = elem; struct posix_acl_entry *entry; + unsigned int id; if (!nfsacl_desc->acl) { if (desc->array_len > NFS_ACL_MAX_ENTRIES) @@ -160,14 +163,22 @@ xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem) entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++]; entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT; - entry->e_id = ntohl(*p++); + id = ntohl(*p++); entry->e_perm = ntohl(*p++); switch(entry->e_tag) { - case ACL_USER_OBJ: case ACL_USER: - case ACL_GROUP_OBJ: + entry->e_uid = make_kuid(&init_user_ns, id); + if (!uid_valid(entry->e_uid)) + return -EINVAL; + break; case ACL_GROUP: + entry->e_gid = make_kgid(&init_user_ns, id); + if (!gid_valid(entry->e_gid)) + return -EINVAL; + break; + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: case ACL_OTHER: if (entry->e_perm & ~S_IRWXO) return -EINVAL; @@ -190,9 +201,13 @@ cmp_acl_entry(const void *x, const void *y) if (a->e_tag != b->e_tag) return a->e_tag - b->e_tag; - else if (a->e_id > b->e_id) + else if ((a->e_tag == ACL_USER) && uid_gt(a->e_uid, b->e_uid)) return 1; - else if (a->e_id < b->e_id) + else if ((a->e_tag == ACL_USER) && uid_lt(a->e_uid, b->e_uid)) + return -1; + else if ((a->e_tag == ACL_GROUP) && gid_gt(a->e_gid, b->e_gid)) + return 1; + else if ((a->e_tag == ACL_GROUP) && gid_lt(a->e_gid, b->e_gid)) return -1; else return 0; @@ -213,22 +228,18 @@ posix_acl_from_nfsacl(struct posix_acl *acl) sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry), cmp_acl_entry, NULL); - /* Clear undefined identifier fields and find the ACL_GROUP_OBJ - and ACL_MASK entries. */ + /* Find the ACL_GROUP_OBJ and ACL_MASK entries. */ FOREACH_ACL_ENTRY(pa, acl, pe) { switch(pa->e_tag) { case ACL_USER_OBJ: - pa->e_id = ACL_UNDEFINED_ID; break; case ACL_GROUP_OBJ: - pa->e_id = ACL_UNDEFINED_ID; group_obj = pa; break; case ACL_MASK: mask = pa; /* fall through */ case ACL_OTHER: - pa->e_id = ACL_UNDEFINED_ID; break; } } From bf37f794372d5b8fda66702e1f3e70d4f07b6533 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 15:55:38 -0800 Subject: [PATCH 2474/4607] sunrpc: Use userns friendly constants. Instead of (uid_t)0 use GLOBAL_ROOT_UID. Instead of (gid_t)0 use GLOBAL_ROOT_GID. Instead of (uid_t)-1 use INVALID_UID Instead of (gid_t)-1 use INVALID_GID. Instead of NOGROUP use INVALID_GID. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth.c | 4 ++-- net/sunrpc/auth_generic.c | 4 ++-- net/sunrpc/auth_unix.c | 6 +++--- net/sunrpc/svcauth_unix.c | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index b5c067bccc45..4cd0ecfe9837 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -519,8 +519,8 @@ rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags) { struct rpc_auth *auth = task->tk_client->cl_auth; struct auth_cred acred = { - .uid = 0, - .gid = 0, + .uid = GLOBAL_ROOT_UID, + .gid = GLOBAL_ROOT_GID, }; dprintk("RPC: %5u looking up %s cred\n", diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index 6ed6f201b022..9d2e0b045596 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c @@ -18,8 +18,8 @@ # define RPCDBG_FACILITY RPCDBG_AUTH #endif -#define RPC_MACHINE_CRED_USERID ((uid_t)0) -#define RPC_MACHINE_CRED_GROUPID ((gid_t)0) +#define RPC_MACHINE_CRED_USERID GLOBAL_ROOT_UID +#define RPC_MACHINE_CRED_GROUPID GLOBAL_ROOT_GID struct generic_cred { struct rpc_cred gc_base; diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 52c5abdee211..2f8627082fa7 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -85,7 +85,7 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) cred->uc_gids[i] = gid; } if (i < NFS_NGROUPS) - cred->uc_gids[i] = NOGROUP; + cred->uc_gids[i] = INVALID_GID; return &cred->uc_base; } @@ -137,7 +137,7 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) return 0; } if (groups < NFS_NGROUPS && - cred->uc_gids[groups] != NOGROUP) + cred->uc_gids[groups] != INVALID_GID) return 0; return 1; } @@ -166,7 +166,7 @@ unx_marshal(struct rpc_task *task, __be32 *p) *p++ = htonl((u32) cred->uc_uid); *p++ = htonl((u32) cred->uc_gid); hold = p++; - for (i = 0; i < 16 && cred->uc_gids[i] != (gid_t) NOGROUP; i++) + for (i = 0; i < 16 && cred->uc_gids[i] != INVALID_GID; i++) *p++ = htonl((u32) cred->uc_gids[i]); *hold = htonl(p - hold - 1); /* gid array length */ *base = htonl((p - base - 1) << 2); /* cred length */ diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 4d0129203733..c8cb5dc8d871 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -750,8 +750,8 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp) } /* Signal that mapping to nobody uid/gid is required */ - cred->cr_uid = (uid_t) -1; - cred->cr_gid = (gid_t) -1; + cred->cr_uid = INVALID_UID; + cred->cr_gid = INVALID_GID; cred->cr_group_info = groups_alloc(0); if (cred->cr_group_info == NULL) return SVC_CLOSE; /* kmalloc failure - client must retry */ From 7eaf040b720bc8c0ce5cd49151ca194ca2d56842 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 16:31:17 -0800 Subject: [PATCH 2475/4607] sunrpc: Use kuid_t and kgid_t where appropriate Convert variables that store uids and gids to be of type kuid_t and kgid_t instead of type uid_t and gid_t. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- include/linux/sunrpc/auth.h | 7 ++++--- include/linux/sunrpc/svcauth.h | 4 ++-- net/sunrpc/auth_gss/auth_gss.c | 8 ++++---- net/sunrpc/auth_unix.c | 4 ++-- net/sunrpc/svcauth_unix.c | 8 ++++---- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index f25ba922baaf..58fda1c3c783 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -17,14 +17,15 @@ #include #include +#include /* size of the nodename buffer */ #define UNX_MAXNODENAME 32 /* Work around the lack of a VFS credential */ struct auth_cred { - uid_t uid; - gid_t gid; + kuid_t uid; + kgid_t gid; struct group_info *group_info; const char *principal; unsigned char machine_cred : 1; @@ -48,7 +49,7 @@ struct rpc_cred { unsigned long cr_flags; /* various flags */ atomic_t cr_count; /* ref count */ - uid_t cr_uid; + kuid_t cr_uid; /* per-flavor data */ }; diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index dd74084a9799..ff374ab30839 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -18,8 +18,8 @@ #include struct svc_cred { - uid_t cr_uid; - gid_t cr_gid; + kuid_t cr_uid; + kgid_t cr_gid; struct group_info *cr_group_info; u32 cr_flavor; /* pseudoflavor */ char *cr_principal; /* for gss */ diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 6e5c824b040b..4daab81ca337 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -256,7 +256,7 @@ err: struct gss_upcall_msg { atomic_t count; - uid_t uid; + kuid_t uid; struct rpc_pipe_msg msg; struct list_head list; struct gss_auth *auth; @@ -303,7 +303,7 @@ gss_release_msg(struct gss_upcall_msg *gss_msg) } static struct gss_upcall_msg * -__gss_find_upcall(struct rpc_pipe *pipe, uid_t uid) +__gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid) { struct gss_upcall_msg *pos; list_for_each_entry(pos, &pipe->in_downcall, list) { @@ -445,7 +445,7 @@ static void gss_encode_msg(struct gss_upcall_msg *gss_msg, static struct gss_upcall_msg * gss_alloc_msg(struct gss_auth *gss_auth, struct rpc_clnt *clnt, - uid_t uid, const char *service_name) + kuid_t uid, const char *service_name) { struct gss_upcall_msg *gss_msg; int vers; @@ -475,7 +475,7 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_upcall_msg *gss_new, *gss_msg; - uid_t uid = cred->cr_uid; + kuid_t uid = cred->cr_uid; gss_new = gss_alloc_msg(gss_auth, clnt, uid, gss_cred->gc_principal); if (IS_ERR(gss_new)) diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 2f8627082fa7..372d9156f6e3 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -18,8 +18,8 @@ struct unx_cred { struct rpc_cred uc_base; - gid_t uc_gid; - gid_t uc_gids[NFS_NGROUPS]; + kgid_t uc_gid; + kgid_t uc_gids[NFS_NGROUPS]; }; #define uc_uid uc_base.cr_uid diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index c8cb5dc8d871..caae662f9fa3 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -415,7 +415,7 @@ svcauth_unix_info_release(struct svc_xprt *xpt) struct unix_gid { struct cache_head h; - uid_t uid; + kuid_t uid; struct group_info *gi; }; @@ -475,7 +475,7 @@ static int unix_gid_upcall(struct cache_detail *cd, struct cache_head *h) return sunrpc_cache_pipe_upcall(cd, h, unix_gid_request); } -static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, uid_t uid); +static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid); static int unix_gid_parse(struct cache_detail *cd, char *mesg, int mlen) @@ -615,7 +615,7 @@ void unix_gid_cache_destroy(struct net *net) cache_destroy_net(cd, net); } -static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, uid_t uid) +static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid) { struct unix_gid ug; struct cache_head *ch; @@ -628,7 +628,7 @@ static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, uid_t uid) return NULL; } -static struct group_info *unix_gid_find(uid_t uid, struct svc_rqst *rqstp) +static struct group_info *unix_gid_find(kuid_t uid, struct svc_rqst *rqstp) { struct unix_gid *ug; struct group_info *gi; From 0b4d51b02a2e941beec6f02a6c7a32c5a28c5b43 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 16:39:32 -0800 Subject: [PATCH 2476/4607] sunrpc: Use uid_eq and gid_eq where appropriate When comparing uids use uid_eq instead of ==. When comparing gids use gid_eq instead of ==. And unfortunate cost of type safety. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_generic.c | 8 ++++---- net/sunrpc/auth_gss/auth_gss.c | 4 ++-- net/sunrpc/auth_unix.c | 2 +- net/sunrpc/svcauth_unix.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index 9d2e0b045596..bff3e4730f6b 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c @@ -129,8 +129,8 @@ machine_cred_match(struct auth_cred *acred, struct generic_cred *gcred, int flag { if (!gcred->acred.machine_cred || gcred->acred.principal != acred->principal || - gcred->acred.uid != acred->uid || - gcred->acred.gid != acred->gid) + !uid_eq(gcred->acred.uid, acred->uid) || + !gid_eq(gcred->acred.gid, acred->gid)) return 0; return 1; } @@ -147,8 +147,8 @@ generic_match(struct auth_cred *acred, struct rpc_cred *cred, int flags) if (acred->machine_cred) return machine_cred_match(acred, gcred, flags); - if (gcred->acred.uid != acred->uid || - gcred->acred.gid != acred->gid || + if (!uid_eq(gcred->acred.uid, acred->uid) || + !gid_eq(gcred->acred.gid, acred->gid) || gcred->acred.machine_cred != 0) goto out_nomatch; diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 4daab81ca337..1b8b3e4fad46 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -307,7 +307,7 @@ __gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid) { struct gss_upcall_msg *pos; list_for_each_entry(pos, &pipe->in_downcall, list) { - if (pos->uid != uid) + if (!uid_eq(pos->uid, uid)) continue; atomic_inc(&pos->count); dprintk("RPC: %s found msg %p\n", __func__, pos); @@ -1115,7 +1115,7 @@ out: } if (gss_cred->gc_principal != NULL) return 0; - return rc->cr_uid == acred->uid; + return uid_eq(rc->cr_uid, acred->uid); } /* diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 372d9156f6e3..8365a9cade98 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -123,7 +123,7 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) unsigned int i; - if (cred->uc_uid != acred->uid || cred->uc_gid != acred->gid) + if (!uid_eq(cred->uc_uid, acred->uid) || !gid_eq(cred->uc_gid, acred->gid)) return 0; if (acred->group_info != NULL) diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index caae662f9fa3..92166b57ec7a 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -433,7 +433,7 @@ static int unix_gid_match(struct cache_head *corig, struct cache_head *cnew) { struct unix_gid *orig = container_of(corig, struct unix_gid, h); struct unix_gid *new = container_of(cnew, struct unix_gid, h); - return orig->uid == new->uid; + return uid_eq(orig->uid, new->uid); } static void unix_gid_init(struct cache_head *cnew, struct cache_head *citem) { From 9132adb0212c7ddf37d1c2a26c12f8fe7706827d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 16:46:42 -0800 Subject: [PATCH 2477/4607] sunrpc: Simplify auth_unix now that everything is a kgid_t In unx_create_cred directly assign gids from acred->group_info to cred->uc_gids. In unx_match directly compare uc_gids with group_info. Now that both group_info and unx_cred gids are stored as kgids this is valid and the extra layer of translation can be removed. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_unix.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 8365a9cade98..9f3885745fb3 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -79,11 +79,8 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) groups = NFS_NGROUPS; cred->uc_gid = acred->gid; - for (i = 0; i < groups; i++) { - gid_t gid; - gid = from_kgid(&init_user_ns, GROUP_AT(acred->group_info, i)); - cred->uc_gids[i] = gid; - } + for (i = 0; i < groups; i++) + cred->uc_gids[i] = GROUP_AT(acred->group_info, i); if (i < NFS_NGROUPS) cred->uc_gids[i] = INVALID_GID; @@ -130,12 +127,9 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) groups = acred->group_info->ngroups; if (groups > NFS_NGROUPS) groups = NFS_NGROUPS; - for (i = 0; i < groups ; i++) { - gid_t gid; - gid = from_kgid(&init_user_ns, GROUP_AT(acred->group_info, i)); - if (cred->uc_gids[i] != gid) + for (i = 0; i < groups ; i++) + if (!gid_eq(cred->uc_gids[i], GROUP_AT(acred->group_info, i))) return 0; - } if (groups < NFS_NGROUPS && cred->uc_gids[groups] != INVALID_GID) return 0; From cdba321e291f0fbf5abda4d88340292b858e3d4d Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 17:10:52 -0800 Subject: [PATCH 2478/4607] sunrpc: Convert kuids and kgids to uids and gids for printing When printing kuids and kgids for debugging purpropses convert them to ordinary integers so their values can be fed to the oridnary print functions. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_generic.c | 4 +++- net/sunrpc/auth_gss/auth_gss.c | 13 ++++++++----- net/sunrpc/auth_unix.c | 3 ++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index bff3e4730f6b..b6badafc6494 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c @@ -96,7 +96,9 @@ generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) dprintk("RPC: allocated %s cred %p for uid %d gid %d\n", gcred->acred.machine_cred ? "machine" : "generic", - gcred, acred->uid, acred->gid); + gcred, + from_kuid(&init_user_ns, acred->uid), + from_kgid(&init_user_ns, acred->gid)); return &gcred->gc_base; } diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 1b8b3e4fad46..afbbcfb1078b 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -517,7 +517,7 @@ gss_refresh_upcall(struct rpc_task *task) int err = 0; dprintk("RPC: %5u %s for uid %u\n", - task->tk_pid, __func__, cred->cr_uid); + task->tk_pid, __func__, from_kuid(&init_user_ns, cred->cr_uid)); gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred); if (PTR_ERR(gss_msg) == -EAGAIN) { /* XXX: warning on the first, under the assumption we @@ -549,7 +549,8 @@ gss_refresh_upcall(struct rpc_task *task) gss_release_msg(gss_msg); out: dprintk("RPC: %5u %s for uid %u result %d\n", - task->tk_pid, __func__, cred->cr_uid, err); + task->tk_pid, __func__, + from_kuid(&init_user_ns, cred->cr_uid), err); return err; } @@ -562,7 +563,8 @@ gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred) DEFINE_WAIT(wait); int err = 0; - dprintk("RPC: %s for uid %u\n", __func__, cred->cr_uid); + dprintk("RPC: %s for uid %u\n", + __func__, from_kuid(&init_user_ns, cred->cr_uid)); retry: gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred); if (PTR_ERR(gss_msg) == -EAGAIN) { @@ -604,7 +606,7 @@ out_intr: gss_release_msg(gss_msg); out: dprintk("RPC: %s for uid %u result %d\n", - __func__, cred->cr_uid, err); + __func__, from_kuid(&init_user_ns, cred->cr_uid), err); return err; } @@ -1059,7 +1061,8 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) int err = -ENOMEM; dprintk("RPC: %s for uid %d, flavor %d\n", - __func__, acred->uid, auth->au_flavor); + __func__, from_kuid(&init_user_ns, acred->uid), + auth->au_flavor); if (!(cred = kzalloc(sizeof(*cred), GFP_NOFS))) goto out_err; diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 9f3885745fb3..55b6ca6fbbd3 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -65,7 +65,8 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) unsigned int i; dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", - acred->uid, acred->gid); + from_kuid(&init_user_ns, acred->uid), + from_kgid(&init_user_ns, acred->gid)); if (!(cred = kmalloc(sizeof(*cred), GFP_NOFS))) return ERR_PTR(-ENOMEM); From e572fc739822ad779493b8a72bd27f2101fc3373 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 17:18:04 -0800 Subject: [PATCH 2479/4607] sunrpc: Use gid_valid to test for gid != INVALID_GID In auth unix there are a couple of places INVALID_GID is used a sentinel to mark the end of uc_gids array. Use gid_valid as a type safe way to verify we have not hit the end of valid data in the array. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_unix.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 55b6ca6fbbd3..c434fde2079b 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -131,8 +131,7 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags) for (i = 0; i < groups ; i++) if (!gid_eq(cred->uc_gids[i], GROUP_AT(acred->group_info, i))) return 0; - if (groups < NFS_NGROUPS && - cred->uc_gids[groups] != INVALID_GID) + if (groups < NFS_NGROUPS && gid_valid(cred->uc_gids[groups])) return 0; return 1; } @@ -161,7 +160,7 @@ unx_marshal(struct rpc_task *task, __be32 *p) *p++ = htonl((u32) cred->uc_uid); *p++ = htonl((u32) cred->uc_gid); hold = p++; - for (i = 0; i < 16 && cred->uc_gids[i] != INVALID_GID; i++) + for (i = 0; i < 16 && gid_valid(cred->uc_gids[i]); i++) *p++ = htonl((u32) cred->uc_gids[i]); *hold = htonl(p - hold - 1); /* gid array length */ *base = htonl((p - base - 1) << 2); /* cred length */ From 90602c7b192fdd3e6b7c7623479f4bc86ed7ee34 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 00:25:43 -0800 Subject: [PATCH 2480/4607] sunrpc: Update gss uid to security context mapping. - Use from_kuid when generating the on the wire uid values. - Use make_kuid when reading on the wire values. In gss_encode_v0_msg, since the uid in gss_upcall_msg is now a kuid_t generate the necessary uid_t value on the stack copy it into gss_msg->databuf where it can safely live until the message is no longer needed. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_gss/auth_gss.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index afbbcfb1078b..a3600671989a 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -395,8 +395,11 @@ gss_upcall_callback(struct rpc_task *task) static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg) { - gss_msg->msg.data = &gss_msg->uid; - gss_msg->msg.len = sizeof(gss_msg->uid); + uid_t uid = from_kuid(&init_user_ns, gss_msg->uid); + memcpy(gss_msg->databuf, &uid, sizeof(uid)); + gss_msg->msg.data = gss_msg->databuf; + gss_msg->msg.len = sizeof(uid); + BUG_ON(sizeof(uid) > UPCALL_BUF_LEN); } static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, @@ -409,7 +412,7 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, gss_msg->msg.len = sprintf(gss_msg->databuf, "mech=%s uid=%d ", mech->gm_name, - gss_msg->uid); + from_kuid(&init_user_ns, gss_msg->uid)); p += gss_msg->msg.len; if (clnt->cl_principal) { len = sprintf(p, "target=%s ", clnt->cl_principal); @@ -620,7 +623,8 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) struct gss_upcall_msg *gss_msg; struct rpc_pipe *pipe = RPC_I(filp->f_dentry->d_inode)->pipe; struct gss_cl_ctx *ctx; - uid_t uid; + uid_t id; + kuid_t uid; ssize_t err = -EFBIG; if (mlen > MSG_BUF_MAXSIZE) @@ -635,12 +639,18 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) goto err; end = (const void *)((char *)buf + mlen); - p = simple_get_bytes(buf, end, &uid, sizeof(uid)); + p = simple_get_bytes(buf, end, &id, sizeof(id)); if (IS_ERR(p)) { err = PTR_ERR(p); goto err; } + uid = make_kuid(&init_user_ns, id); + if (!uid_valid(uid)) { + err = -EINVAL; + goto err; + } + err = -ENOMEM; ctx = gss_alloc_context(); if (ctx == NULL) From 683428fae8c73d7d7da0fa2e0b6beb4d8df4e808 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 01:40:53 -0800 Subject: [PATCH 2481/4607] sunrpc: Update svcgss xdr handle to rpsec_contect cache For each received uid call make_kuid and validate the result. For each received gid call make_kgid and validate the result. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_gss/svcauth_gss.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 73e957386600..ecd1d58bf611 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -418,6 +418,7 @@ static int rsc_parse(struct cache_detail *cd, { /* contexthandle expiry [ uid gid N mechname ...mechdata... ] */ char *buf = mesg; + int id; int len, rv; struct rsc rsci, *rscp = NULL; time_t expiry; @@ -444,7 +445,7 @@ static int rsc_parse(struct cache_detail *cd, goto out; /* uid, or NEGATIVE */ - rv = get_int(&mesg, &rsci.cred.cr_uid); + rv = get_int(&mesg, &id); if (rv == -EINVAL) goto out; if (rv == -ENOENT) @@ -452,8 +453,16 @@ static int rsc_parse(struct cache_detail *cd, else { int N, i; + /* uid */ + rsci.cred.cr_uid = make_kuid(&init_user_ns, id); + if (!uid_valid(rsci.cred.cr_uid)) + goto out; + /* gid */ - if (get_int(&mesg, &rsci.cred.cr_gid)) + if (get_int(&mesg, &id)) + goto out; + rsci.cred.cr_gid = make_kgid(&init_user_ns, id); + if (!gid_valid(rsci.cred.cr_gid)) goto out; /* number of additional gid's */ @@ -467,11 +476,10 @@ static int rsc_parse(struct cache_detail *cd, /* gid's */ status = -EINVAL; for (i=0; i Date: Sat, 2 Feb 2013 01:57:30 -0800 Subject: [PATCH 2482/4607] sunrpc: Hash uids by first computing their value in the initial userns In svcauth_unix introduce a helper unix_gid_hash as otherwise the expresion to generate the hash value is just too long. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth.c | 2 +- net/sunrpc/svcauth_unix.c | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 4cd0ecfe9837..392adc41e2e5 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -412,7 +412,7 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, *entry, *new; unsigned int nr; - nr = hash_long(acred->uid, cache->hashbits); + nr = hash_long(from_kuid(&init_user_ns, acred->uid), cache->hashbits); rcu_read_lock(); hlist_for_each_entry_rcu(entry, pos, &cache->hashtable[nr], cr_hash) { diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 92166b57ec7a..faf17195a9fd 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -419,6 +419,11 @@ struct unix_gid { struct group_info *gi; }; +static int unix_gid_hash(kuid_t uid) +{ + return hash_long(from_kuid(&init_user_ns, uid), GID_HASHBITS); +} + static void unix_gid_put(struct kref *kref) { struct cache_head *item = container_of(kref, struct cache_head, ref); @@ -530,7 +535,7 @@ static int unix_gid_parse(struct cache_detail *cd, ug.h.expiry_time = expiry; ch = sunrpc_cache_update(cd, &ug.h, &ugp->h, - hash_long(uid, GID_HASHBITS)); + unix_gid_hash(uid)); if (!ch) err = -ENOMEM; else { @@ -621,7 +626,7 @@ static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid) struct cache_head *ch; ug.uid = uid; - ch = sunrpc_cache_lookup(cd, &ug.h, hash_long(uid, GID_HASHBITS)); + ch = sunrpc_cache_lookup(cd, &ug.h, unix_gid_hash(uid)); if (ch) return container_of(ch, struct unix_gid, h); else From a570abbb966ee7de6c4357a58be11a558fa7099b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 02:45:08 -0800 Subject: [PATCH 2483/4607] sunrpc: Properly encode kuids and kgids in RPC_AUTH_UNIX credentials When writing kuids onto the wire first map them into the initial user namespace. When writing kgids onto the wire first map them into the initial user namespace. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/auth_unix.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index c434fde2079b..dc37021fc3e5 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -157,11 +157,11 @@ unx_marshal(struct rpc_task *task, __be32 *p) */ p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen); - *p++ = htonl((u32) cred->uc_uid); - *p++ = htonl((u32) cred->uc_gid); + *p++ = htonl((u32) from_kuid(&init_user_ns, cred->uc_uid)); + *p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gid)); hold = p++; for (i = 0; i < 16 && gid_valid(cred->uc_gids[i]); i++) - *p++ = htonl((u32) cred->uc_gids[i]); + *p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gids[i])); *hold = htonl(p - hold - 1); /* gid array length */ *base = htonl((p - base - 1) << 2); /* cred length */ From 25da9263710ec94c964259c79fa9a3a635cd3a50 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 02:49:44 -0800 Subject: [PATCH 2484/4607] sunrpc: Properly encode kuids and kgids in auth.unix.gid rpc pipe upcalls. When a new rpc connection is established with an in-kernel server, the traffic passes through svc_process_common, and svc_set_client and down into svcauth_unix_set_client if it is of type RPC_AUTH_NULL or RPC_AUTH_UNIX. svcauth_unix_set_client then looks at the uid of the credential we have assigned to the incomming client and if we don't have the groups already cached makes an upcall to get a list of groups that the client can use. The upcall encodes send a rpc message to user space encoding the uid of the user whose groups we want to know. Encode the kuid of the user in the initial user namespace as nfs mounts can only happen today in the initial user namespace. When a reply to an upcall comes in convert interpret the uid and gid values from the rpc pipe as uids and gids in the initial user namespace and convert them into kuids and kgids before processing them further. When reading proc files listing the uid to gid list cache convert the kuids and kgids from into uids and gids the initial user namespace. As we are displaying server internal details it makes sense to display these values from the servers perspective. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/svcauth_unix.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index faf17195a9fd..bdea0a1b6d1d 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -470,7 +470,7 @@ static void unix_gid_request(struct cache_detail *cd, char tuid[20]; struct unix_gid *ug = container_of(h, struct unix_gid, h); - snprintf(tuid, 20, "%u", ug->uid); + snprintf(tuid, 20, "%u", from_kuid(&init_user_ns, ug->uid)); qword_add(bpp, blen, tuid); (*bpp)[-1] = '\n'; } @@ -486,7 +486,8 @@ static int unix_gid_parse(struct cache_detail *cd, char *mesg, int mlen) { /* uid expiry Ngid gid0 gid1 ... gidN-1 */ - int uid; + int id; + kuid_t uid; int gids; int rv; int i; @@ -498,9 +499,12 @@ static int unix_gid_parse(struct cache_detail *cd, return -EINVAL; mesg[mlen-1] = 0; - rv = get_int(&mesg, &uid); + rv = get_int(&mesg, &id); if (rv) return -EINVAL; + uid = make_kuid(&init_user_ns, id); + if (!uid_valid(uid)) + return -EINVAL; ug.uid = uid; expiry = get_expiry(&mesg); @@ -554,7 +558,7 @@ static int unix_gid_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h) { - struct user_namespace *user_ns = current_user_ns(); + struct user_namespace *user_ns = &init_user_ns; struct unix_gid *ug; int i; int glen; @@ -570,7 +574,7 @@ static int unix_gid_show(struct seq_file *m, else glen = 0; - seq_printf(m, "%u %d:", ug->uid, glen); + seq_printf(m, "%u %d:", from_kuid_munged(user_ns, ug->uid), glen); for (i = 0; i < glen; i++) seq_printf(m, " %d", from_kgid_munged(user_ns, GROUP_AT(ug->gi, i))); seq_printf(m, "\n"); From f025adf191924e3a75ce80e130afcd2485b53bb8 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 03:03:04 -0800 Subject: [PATCH 2485/4607] sunrpc: Properly decode kuids and kgids in RPC_AUTH_UNIX credentials When reading kuids from the wire map them into the initial user namespace, and validate the mapping succeded. When reading kgids from the wire map them into the initial user namespace, and validate the mapping succeded. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- net/sunrpc/svcauth_unix.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index bdea0a1b6d1d..a1852e19ed0c 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -821,8 +821,10 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp) argv->iov_base = (void*)((__be32*)argv->iov_base + slen); /* skip machname */ argv->iov_len -= slen*4; - cred->cr_uid = svc_getnl(argv); /* uid */ - cred->cr_gid = svc_getnl(argv); /* gid */ + cred->cr_uid = make_kuid(&init_user_ns, svc_getnl(argv)); /* uid */ + cred->cr_gid = make_kgid(&init_user_ns, svc_getnl(argv)); /* gid */ + if (!uid_valid(cred->cr_uid) || !gid_valid(cred->cr_gid)) + goto badcred; slen = svc_getnl(argv); /* gids length */ if (slen > 16 || (len -= (slen + 2)*4) < 0) goto badcred; From 4e963d4f3ed1b756ff20f84960fcb4db42509146 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 03:03:16 -0800 Subject: [PATCH 2486/4607] nfs: Pass GLOBAL_ROOT_UID and GLOBAL_ROOT_GID to keyring alloc Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/idmap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index bc3968fa81e5..f77017c4e28b 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -193,7 +193,8 @@ static int nfs_idmap_init_keyring(void) if (!cred) return -ENOMEM; - keyring = keyring_alloc(".id_resolver", 0, 0, cred, + keyring = keyring_alloc(".id_resolver", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, KEY_ALLOC_NOT_IN_QUOTA, NULL); From 54f834cd5501fb5fc801e4719a3ad0c894a3af2c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 14:16:29 -0800 Subject: [PATCH 2487/4607] nfs: Convert struct nfs_fattr to Use kuid_t and kgid_t Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- include/linux/nfs_xdr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 29adb12c7ecf..13441ddac33d 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -48,8 +48,8 @@ struct nfs_fattr { unsigned int valid; /* which fields are valid */ umode_t mode; __u32 nlink; - __u32 uid; - __u32 gid; + kuid_t uid; + kgid_t gid; dev_t rdev; __u64 size; union { From 9f309c86cf343c59c79d25d9bde5d4a895d2e81f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 03:21:47 -0800 Subject: [PATCH 2488/4607] nfs: Convert idmap to use kuids and kgids Convert nfs_map_name_to_uid to return a kuid_t value. Convert nfs_map_name_to_gid to return a kgid_t value. Convert nfs_map_uid_to_name to take a kuid_t paramater. Convert nfs_map_gid_to_name to take a kgid_t paramater. Tweak nfs_fattr_map_owner_to_name to use a kuid_t intermediate value. Tweak nfs_fattr_map_group_to_name to use a kgid_t intermediate value. Which makes these functions properly handle kuids and kgids, including erroring of the generated kuid or kgid is invalid. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/idmap.c | 50 ++++++++++++++++++++++++++------------- include/linux/nfs_idmap.h | 9 +++---- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index f77017c4e28b..b9623d19d599 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -97,7 +97,7 @@ static void nfs_fattr_free_group_name(struct nfs_fattr *fattr) static bool nfs_fattr_map_owner_name(struct nfs_server *server, struct nfs_fattr *fattr) { struct nfs4_string *owner = fattr->owner_name; - __u32 uid; + kuid_t uid; if (!(fattr->valid & NFS_ATTR_FATTR_OWNER_NAME)) return false; @@ -111,7 +111,7 @@ static bool nfs_fattr_map_owner_name(struct nfs_server *server, struct nfs_fattr static bool nfs_fattr_map_group_name(struct nfs_server *server, struct nfs_fattr *fattr) { struct nfs4_string *group = fattr->group_name; - __u32 gid; + kgid_t gid; if (!(fattr->valid & NFS_ATTR_FATTR_GROUP_NAME)) return false; @@ -837,43 +837,61 @@ idmap_release_pipe(struct inode *inode) nfs_idmap_abort_pipe_upcall(idmap, -EPIPE); } -int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid) +int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, kuid_t *uid) { struct idmap *idmap = server->nfs_client->cl_idmap; + __u32 id = -1; + int ret = 0; - if (nfs_map_string_to_numeric(name, namelen, uid)) - return 0; - return nfs_idmap_lookup_id(name, namelen, "uid", uid, idmap); + if (!nfs_map_string_to_numeric(name, namelen, &id)) + ret = nfs_idmap_lookup_id(name, namelen, "uid", &id, idmap); + if (ret == 0) { + *uid = make_kuid(&init_user_ns, id); + if (!uid_valid(*uid)) + ret = -ERANGE; + } + return ret; } -int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *gid) +int nfs_map_group_to_gid(const struct nfs_server *server, const char *name, size_t namelen, kgid_t *gid) { struct idmap *idmap = server->nfs_client->cl_idmap; + __u32 id = -1; + int ret = 0; - if (nfs_map_string_to_numeric(name, namelen, gid)) - return 0; - return nfs_idmap_lookup_id(name, namelen, "gid", gid, idmap); + if (!nfs_map_string_to_numeric(name, namelen, &id)) + ret = nfs_idmap_lookup_id(name, namelen, "gid", &id, idmap); + if (ret == 0) { + *gid = make_kgid(&init_user_ns, id); + if (!gid_valid(*gid)) + ret = -ERANGE; + } + return ret; } -int nfs_map_uid_to_name(const struct nfs_server *server, __u32 uid, char *buf, size_t buflen) +int nfs_map_uid_to_name(const struct nfs_server *server, kuid_t uid, char *buf, size_t buflen) { struct idmap *idmap = server->nfs_client->cl_idmap; int ret = -EINVAL; + __u32 id; + id = from_kuid(&init_user_ns, uid); if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) - ret = nfs_idmap_lookup_name(uid, "user", buf, buflen, idmap); + ret = nfs_idmap_lookup_name(id, "user", buf, buflen, idmap); if (ret < 0) - ret = nfs_map_numeric_to_string(uid, buf, buflen); + ret = nfs_map_numeric_to_string(id, buf, buflen); return ret; } -int nfs_map_gid_to_group(const struct nfs_server *server, __u32 gid, char *buf, size_t buflen) +int nfs_map_gid_to_group(const struct nfs_server *server, kgid_t gid, char *buf, size_t buflen) { struct idmap *idmap = server->nfs_client->cl_idmap; int ret = -EINVAL; + __u32 id; + id = from_kgid(&init_user_ns, gid); if (!(server->caps & NFS_CAP_UIDGID_NOMAP)) - ret = nfs_idmap_lookup_name(gid, "group", buf, buflen, idmap); + ret = nfs_idmap_lookup_name(id, "group", buf, buflen, idmap); if (ret < 0) - ret = nfs_map_numeric_to_string(gid, buf, buflen); + ret = nfs_map_numeric_to_string(id, buf, buflen); return ret; } diff --git a/include/linux/nfs_idmap.h b/include/linux/nfs_idmap.h index 2dcef3ab58b6..0f4b79da6584 100644 --- a/include/linux/nfs_idmap.h +++ b/include/linux/nfs_idmap.h @@ -36,6 +36,7 @@ #ifndef NFS_IDMAP_H #define NFS_IDMAP_H +#include #include @@ -67,10 +68,10 @@ void nfs_fattr_init_names(struct nfs_fattr *fattr, void nfs_fattr_free_names(struct nfs_fattr *); void nfs_fattr_map_and_free_names(struct nfs_server *, struct nfs_fattr *); -int nfs_map_name_to_uid(const struct nfs_server *, const char *, size_t, __u32 *); -int nfs_map_group_to_gid(const struct nfs_server *, const char *, size_t, __u32 *); -int nfs_map_uid_to_name(const struct nfs_server *, __u32, char *, size_t); -int nfs_map_gid_to_group(const struct nfs_server *, __u32, char *, size_t); +int nfs_map_name_to_uid(const struct nfs_server *, const char *, size_t, kuid_t *); +int nfs_map_group_to_gid(const struct nfs_server *, const char *, size_t, kgid_t *); +int nfs_map_uid_to_name(const struct nfs_server *, kuid_t, char *, size_t); +int nfs_map_gid_to_group(const struct nfs_server *, kgid_t, char *, size_t); extern unsigned int nfs_idmap_cache_timeout; #endif /* NFS_IDMAP_H */ From cfa0898d4ff1874b86d21768adb49d2c033fa90b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 03:45:54 -0800 Subject: [PATCH 2489/4607] nfs: Convert nfs2xdr to use kuids and kgids When reading uids and gids off the wire convert them to kuids and kgids. When putting kuids and kgids onto the wire first convert them to uids and gids the other side will understand. Add an additional failure mode for incoming uid or gids that are invalid. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/nfs2xdr.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 06b9df49f7f7..62db136339ea 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -290,8 +290,13 @@ static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) fattr->mode = be32_to_cpup(p++); fattr->nlink = be32_to_cpup(p++); - fattr->uid = be32_to_cpup(p++); - fattr->gid = be32_to_cpup(p++); + fattr->uid = make_kuid(&init_user_ns, be32_to_cpup(p++)); + if (!uid_valid(fattr->uid)) + goto out_uid; + fattr->gid = make_kgid(&init_user_ns, be32_to_cpup(p++)); + if (!gid_valid(fattr->gid)) + goto out_gid; + fattr->size = be32_to_cpup(p++); fattr->du.nfs2.blocksize = be32_to_cpup(p++); @@ -313,6 +318,12 @@ static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime); return 0; +out_uid: + dprintk("NFS: returned invalid uid\n"); + return -EINVAL; +out_gid: + dprintk("NFS: returned invalid gid\n"); + return -EINVAL; out_overflow: print_overflow_msg(__func__, xdr); return -EIO; @@ -351,11 +362,11 @@ static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr) else *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); if (attr->ia_valid & ATTR_UID) - *p++ = cpu_to_be32(attr->ia_uid); + *p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid)); else *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); if (attr->ia_valid & ATTR_GID) - *p++ = cpu_to_be32(attr->ia_gid); + *p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid)); else *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); if (attr->ia_valid & ATTR_SIZE) From 57a38dae2ac3f3d618bf197314e2805840220d0f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 03:50:00 -0800 Subject: [PATCH 2490/4607] nfs: Convert nfs3xdr to use kuids and kgids When reading uids and gids off the wire convert them to kuids and kgids. When putting kuids and kgids onto the wire first convert them to uids and gids the other side will understand. Add an additional failure mode incoming for uids or gids that are invalid. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/nfs3xdr.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index bffc32406fbf..fa6d72131c19 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -592,13 +592,13 @@ static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr) if (attr->ia_valid & ATTR_UID) { *p++ = xdr_one; - *p++ = cpu_to_be32(attr->ia_uid); + *p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid)); } else *p++ = xdr_zero; if (attr->ia_valid & ATTR_GID) { *p++ = xdr_one; - *p++ = cpu_to_be32(attr->ia_gid); + *p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid)); } else *p++ = xdr_zero; @@ -657,8 +657,12 @@ static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr) fattr->mode = (be32_to_cpup(p++) & ~S_IFMT) | fmode; fattr->nlink = be32_to_cpup(p++); - fattr->uid = be32_to_cpup(p++); - fattr->gid = be32_to_cpup(p++); + fattr->uid = make_kuid(&init_user_ns, be32_to_cpup(p++)); + if (!uid_valid(fattr->uid)) + goto out_uid; + fattr->gid = make_kgid(&init_user_ns, be32_to_cpup(p++)); + if (!gid_valid(fattr->gid)) + goto out_gid; p = xdr_decode_size3(p, &fattr->size); p = xdr_decode_size3(p, &fattr->du.nfs3.used); @@ -675,6 +679,12 @@ static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr) fattr->valid |= NFS_ATTR_FATTR_V3; return 0; +out_uid: + dprintk("NFS: returned invalid uid\n"); + return -EINVAL; +out_gid: + dprintk("NFS: returned invalid gid\n"); + return -EINVAL; out_overflow: print_overflow_msg(__func__, xdr); return -EIO; From e5782076e72be2a39bf261f7db03e1769ec05198 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 14:22:02 -0800 Subject: [PATCH 2491/4607] nfs: Convert nfs4xdr to use kuids and kgids When reading uids and gids off the wire convert them to kuids and kgids. When putting kuids and kgids onto the wire first convert them to uids and gids the other side will understand. When printing kuids and kgids convert them to values in the initial user namespace then use normal printf formats. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/nfs4xdr.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 26b143920433..e3edda554ac7 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1002,7 +1002,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name, IDMAP_NAMESZ); if (owner_namelen < 0) { dprintk("nfs: couldn't resolve uid %d to string\n", - iap->ia_uid); + from_kuid(&init_user_ns, iap->ia_uid)); /* XXX */ strcpy(owner_name, "nobody"); owner_namelen = sizeof("nobody") - 1; @@ -1014,7 +1014,7 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const owner_grouplen = nfs_map_gid_to_group(server, iap->ia_gid, owner_group, IDMAP_NAMESZ); if (owner_grouplen < 0) { dprintk("nfs: couldn't resolve gid %d to string\n", - iap->ia_gid); + from_kgid(&init_user_ns, iap->ia_gid)); strcpy(owner_group, "nobody"); owner_grouplen = sizeof("nobody") - 1; /* goto out; */ @@ -3778,14 +3778,14 @@ out_overflow: } static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, - const struct nfs_server *server, uint32_t *uid, + const struct nfs_server *server, kuid_t *uid, struct nfs4_string *owner_name) { uint32_t len; __be32 *p; int ret = 0; - *uid = -2; + *uid = make_kuid(&init_user_ns, -2); if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER - 1U))) return -EIO; if (likely(bitmap[1] & FATTR4_WORD1_OWNER)) { @@ -3813,7 +3813,7 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, __func__, len); bitmap[1] &= ~FATTR4_WORD1_OWNER; } - dprintk("%s: uid=%d\n", __func__, (int)*uid); + dprintk("%s: uid=%d\n", __func__, (int)from_kuid(&init_user_ns, *uid)); return ret; out_overflow: print_overflow_msg(__func__, xdr); @@ -3821,14 +3821,14 @@ out_overflow: } static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, - const struct nfs_server *server, uint32_t *gid, + const struct nfs_server *server, kgid_t *gid, struct nfs4_string *group_name) { uint32_t len; __be32 *p; int ret = 0; - *gid = -2; + *gid = make_kgid(&init_user_ns, -2); if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER_GROUP - 1U))) return -EIO; if (likely(bitmap[1] & FATTR4_WORD1_OWNER_GROUP)) { @@ -3856,7 +3856,7 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, __func__, len); bitmap[1] &= ~FATTR4_WORD1_OWNER_GROUP; } - dprintk("%s: gid=%d\n", __func__, (int)*gid); + dprintk("%s: gid=%d\n", __func__, (int)from_kgid(&init_user_ns, *gid)); return ret; out_overflow: print_overflow_msg(__func__, xdr); From 9ff593c4739c76c31ffdf7f2a1d67489ece728e6 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 14:26:23 -0800 Subject: [PATCH 2492/4607] nfs: kuid and kgid conversions for nfs/inode.c - Use uid_eq and gid_eq when comparing kuids and kgids. - Use make_kuid(&init_user_ns, -2) and make_kgid(&init_user_ns, -2) as the initial uid and gid on nfs inodes, instead of using the typeunsafe value of -2. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfs/inode.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index ebeb94ce1b0b..ce2b0394c4d6 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -332,8 +332,8 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) inode->i_version = 0; inode->i_size = 0; clear_nlink(inode); - inode->i_uid = -2; - inode->i_gid = -2; + inode->i_uid = make_kuid(&init_user_ns, -2); + inode->i_gid = make_kgid(&init_user_ns, -2); inode->i_blocks = 0; memset(nfsi->cookieverf, 0, sizeof(nfsi->cookieverf)); nfsi->write_io = 0; @@ -1009,9 +1009,9 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat /* Have any file permissions changed? */ if ((fattr->valid & NFS_ATTR_FATTR_MODE) && (inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; - if ((fattr->valid & NFS_ATTR_FATTR_OWNER) && inode->i_uid != fattr->uid) + if ((fattr->valid & NFS_ATTR_FATTR_OWNER) && !uid_eq(inode->i_uid, fattr->uid)) invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; - if ((fattr->valid & NFS_ATTR_FATTR_GROUP) && inode->i_gid != fattr->gid) + if ((fattr->valid & NFS_ATTR_FATTR_GROUP) && !gid_eq(inode->i_gid, fattr->gid)) invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL; /* Has the link count changed? */ @@ -1440,7 +1440,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) | NFS_INO_REVAL_FORCED); if (fattr->valid & NFS_ATTR_FATTR_OWNER) { - if (inode->i_uid != fattr->uid) { + if (!uid_eq(inode->i_uid, fattr->uid)) { invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; inode->i_uid = fattr->uid; } @@ -1451,7 +1451,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) | NFS_INO_REVAL_FORCED); if (fattr->valid & NFS_ATTR_FATTR_GROUP) { - if (inode->i_gid != fattr->gid) { + if (!gid_eq(inode->i_gid, fattr->gid)) { invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; inode->i_gid = fattr->gid; } From 4277bbf750d95a4c86925fa8f8956568b4912d43 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 06:59:11 -0800 Subject: [PATCH 2493/4607] nfs: Enable building with user namespaces enabled. Now that the kuids and kgids conversion have propogated through net/sunrpc/ and the fs/nfs/ it is safe to enable building nfs when user namespaces are enabled. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index b526f4c35b95..d7b926abe91f 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1073,7 +1073,6 @@ config UIDGID_CONVERTED # Filesystems depends on CIFS = n depends on NFSD = n - depends on NFS_FS = n depends on XFS_FS = n config UIDGID_STRICT_TYPE_CHECKS From 6c1810e040d87fcd8fc95aedfd2ef6979d71e517 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 1 Feb 2013 03:38:35 -0800 Subject: [PATCH 2494/4607] nfsd: Remove declaration of nonexistent nfs4_acl_permisison Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/acl.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h index 34e5c40af5ef..8b186a4955cc 100644 --- a/fs/nfsd/acl.h +++ b/fs/nfsd/acl.h @@ -44,8 +44,6 @@ struct nfs4_acl *nfs4_acl_new(int); int nfs4_acl_get_whotype(char *, u32); int nfs4_acl_write_who(int who, char *p); -int nfs4_acl_permission(struct nfs4_acl *acl, uid_t owner, gid_t group, - uid_t who, u32 mask); #define NFS4_ACL_TYPE_DEFAULT 0x01 #define NFS4_ACL_DIR 0x02 From b5663898ec3fa7f1a58a9def9592be345bb173c2 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 03:54:35 -0800 Subject: [PATCH 2495/4607] nfsd: idmap use u32 not uid_t as the intermediate type u32 and uid_t have the same size and semantics so this change should have no operational effect. This just removes the WTF factor when looking at variables that hold both uids and gids whos type is uid_t. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/nfs4idmap.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index a1f10c0a6255..7e84dfa23d83 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -65,7 +65,7 @@ MODULE_PARM_DESC(nfs4_disable_idmapping, struct ent { struct cache_head h; int type; /* User / Group */ - uid_t id; + u32 id; char name[IDMAP_NAMESZ]; char authname[IDMAP_NAMESZ]; }; @@ -540,7 +540,7 @@ rqst_authname(struct svc_rqst *rqstp) static __be32 idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, - uid_t *id) + u32 *id) { struct ent *item, key = { .type = type, @@ -564,7 +564,7 @@ idmap_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen } static int -idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) +idmap_id_to_name(struct svc_rqst *rqstp, int type, u32 id, char *name) { struct ent *item, key = { .id = id, @@ -587,7 +587,7 @@ idmap_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) } static bool -numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, uid_t *id) +numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id) { int ret; char buf[11]; @@ -603,7 +603,7 @@ numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namel } static __be32 -do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, uid_t *id) +do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u32 *id) { if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS) if (numeric_name_to_id(rqstp, type, name, namelen, id)) @@ -616,7 +616,7 @@ do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u } static int -do_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name) +do_id_to_name(struct svc_rqst *rqstp, int type, u32 id, char *name) { if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS) return sprintf(name, "%u", id); From 65e10f6d0ab09ba95c2eb07cac43208692cf670e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 03:24:21 -0800 Subject: [PATCH 2496/4607] nfsd: Convert idmap to use kuids and kgids Convert nfsd_map_name_to_uid to return a kuid_t value. Convert nfsd_map_name_to_gid to return a kgid_t value. Convert nfsd_map_uid_to_name to take a kuid_t parameter. Convert nfsd_map_gid_to_name to take a kgid_t paramater. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/idmap.h | 8 ++++---- fs/nfsd/nfs4idmap.c | 26 ++++++++++++++++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/fs/nfsd/idmap.h b/fs/nfsd/idmap.h index 9d513efc01ba..bf95f6b817a4 100644 --- a/fs/nfsd/idmap.h +++ b/fs/nfsd/idmap.h @@ -54,9 +54,9 @@ static inline void nfsd_idmap_shutdown(struct net *net) } #endif -__be32 nfsd_map_name_to_uid(struct svc_rqst *, const char *, size_t, __u32 *); -__be32 nfsd_map_name_to_gid(struct svc_rqst *, const char *, size_t, __u32 *); -int nfsd_map_uid_to_name(struct svc_rqst *, __u32, char *); -int nfsd_map_gid_to_name(struct svc_rqst *, __u32, char *); +__be32 nfsd_map_name_to_uid(struct svc_rqst *, const char *, size_t, kuid_t *); +__be32 nfsd_map_name_to_gid(struct svc_rqst *, const char *, size_t, kgid_t *); +int nfsd_map_uid_to_name(struct svc_rqst *, kuid_t, char *); +int nfsd_map_gid_to_name(struct svc_rqst *, kgid_t, char *); #endif /* LINUX_NFSD_IDMAP_H */ diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 7e84dfa23d83..0ce12346df9c 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -625,26 +625,40 @@ do_id_to_name(struct svc_rqst *rqstp, int type, u32 id, char *name) __be32 nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, - __u32 *id) + kuid_t *uid) { - return do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, id); + __be32 status; + u32 id = -1; + status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, &id); + *uid = make_kuid(&init_user_ns, id); + if (!uid_valid(*uid)) + status = nfserr_badowner; + return status; } __be32 nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, - __u32 *id) + kgid_t *gid) { - return do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, id); + __be32 status; + u32 id = -1; + status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, &id); + *gid = make_kgid(&init_user_ns, id); + if (!gid_valid(*gid)) + status = nfserr_badowner; + return status; } int -nfsd_map_uid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) +nfsd_map_uid_to_name(struct svc_rqst *rqstp, kuid_t uid, char *name) { + u32 id = from_kuid(&init_user_ns, uid); return do_id_to_name(rqstp, IDMAP_TYPE_USER, id, name); } int -nfsd_map_gid_to_name(struct svc_rqst *rqstp, __u32 id, char *name) +nfsd_map_gid_to_name(struct svc_rqst *rqstp, kgid_t gid, char *name) { + u32 id = from_kgid(&init_user_ns, gid); return do_id_to_name(rqstp, IDMAP_TYPE_GROUP, id, name); } From e097258f2eb4a91e7389ae69a3c87df111637a3f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 04:04:22 -0800 Subject: [PATCH 2497/4607] nfsd: Remove nfsd_luid, nfsd_lgid, nfsd_ruid and nfsd_rgid These trivial macros that don't currently do anything are the last vestiages of an old attempt at uid mapping that was removed from the kernel in September of 2002. Remove them to make it clear what the code is currently doing. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/auth.h | 6 ------ fs/nfsd/nfs3xdr.c | 4 ++-- fs/nfsd/nfsxdr.c | 4 ++-- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/fs/nfsd/auth.h b/fs/nfsd/auth.h index 78b3c0e93822..53325a12ba62 100644 --- a/fs/nfsd/auth.h +++ b/fs/nfsd/auth.h @@ -1,6 +1,5 @@ /* * nfsd-specific authentication stuff. - * uid/gid mapping not yet implemented. * * Copyright (C) 1995, 1996 Olaf Kirch */ @@ -8,11 +7,6 @@ #ifndef LINUX_NFSD_AUTH_H #define LINUX_NFSD_AUTH_H -#define nfsd_luid(rq, uid) ((u32)(uid)) -#define nfsd_lgid(rq, gid) ((u32)(gid)) -#define nfsd_ruid(rq, uid) ((u32)(uid)) -#define nfsd_rgid(rq, gid) ((u32)(gid)) - /* * Set the current process's fsuid/fsgid etc to those of the NFS * client user diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 324c0baf7cda..1884a3fbb584 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -167,8 +167,8 @@ encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]); *p++ = htonl((u32) stat->mode); *p++ = htonl((u32) stat->nlink); - *p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid)); - *p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid)); + *p++ = htonl((u32) stat->uid); + *p++ = htonl((u32) stat->gid); if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) { p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN); } else { diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 979b42106979..1e51e7034a89 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -151,8 +151,8 @@ encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, *p++ = htonl(nfs_ftypes[type >> 12]); *p++ = htonl((u32) stat->mode); *p++ = htonl((u32) stat->nlink); - *p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid)); - *p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid)); + *p++ = htonl((u32) stat->uid); + *p++ = htonl((u32) stat->gid); if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) { *p++ = htonl(NFS_MAXPATHLEN); From 458878a705c822a6be267977e435b16576bde59b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 04:16:08 -0800 Subject: [PATCH 2498/4607] nfsd: Convert nfs3xdr to use kuids and kgids When reading uids and gids off the wire convert them to kuids and kgids. When putting kuids and kgids onto the wire first convert them to uids and gids the other side will understand. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/nfs3xdr.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 1884a3fbb584..925c944bc0bc 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c @@ -105,12 +105,14 @@ decode_sattr3(__be32 *p, struct iattr *iap) iap->ia_mode = ntohl(*p++); } if (*p++) { - iap->ia_valid |= ATTR_UID; - iap->ia_uid = ntohl(*p++); + iap->ia_uid = make_kuid(&init_user_ns, ntohl(*p++)); + if (uid_valid(iap->ia_uid)) + iap->ia_valid |= ATTR_UID; } if (*p++) { - iap->ia_valid |= ATTR_GID; - iap->ia_gid = ntohl(*p++); + iap->ia_gid = make_kgid(&init_user_ns, ntohl(*p++)); + if (gid_valid(iap->ia_gid)) + iap->ia_valid |= ATTR_GID; } if (*p++) { u64 newsize; @@ -167,8 +169,8 @@ encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]); *p++ = htonl((u32) stat->mode); *p++ = htonl((u32) stat->nlink); - *p++ = htonl((u32) stat->uid); - *p++ = htonl((u32) stat->gid); + *p++ = htonl((u32) from_kuid(&init_user_ns, stat->uid)); + *p++ = htonl((u32) from_kgid(&init_user_ns, stat->gid)); if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) { p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN); } else { From 7c19723e997a3990951c0db0500009fb90c0c5b9 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 04:32:35 -0800 Subject: [PATCH 2499/4607] nfsd: Convert nfsxdr to use kuids and kgids When reading uids and gids off the wire convert them to kuids and kgids. If the conversion results in an invalid result don't set the ATTR_UID or ATTR_GID. When putting kuids and kgids onto the wire first convert them to uids and gids the other side will understand. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/nfsxdr.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 1e51e7034a89..4201ede0ec91 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -100,12 +100,14 @@ decode_sattr(__be32 *p, struct iattr *iap) iap->ia_mode = tmp; } if ((tmp = ntohl(*p++)) != (u32)-1) { - iap->ia_valid |= ATTR_UID; - iap->ia_uid = tmp; + iap->ia_uid = make_kuid(&init_user_ns, tmp); + if (uid_valid(iap->ia_uid)) + iap->ia_valid |= ATTR_UID; } if ((tmp = ntohl(*p++)) != (u32)-1) { - iap->ia_valid |= ATTR_GID; - iap->ia_gid = tmp; + iap->ia_gid = make_kgid(&init_user_ns, tmp); + if (gid_valid(iap->ia_gid)) + iap->ia_valid |= ATTR_GID; } if ((tmp = ntohl(*p++)) != (u32)-1) { iap->ia_valid |= ATTR_SIZE; @@ -151,8 +153,8 @@ encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, *p++ = htonl(nfs_ftypes[type >> 12]); *p++ = htonl((u32) stat->mode); *p++ = htonl((u32) stat->nlink); - *p++ = htonl((u32) stat->uid); - *p++ = htonl((u32) stat->gid); + *p++ = htonl((u32) from_kuid(&init_user_ns, stat->uid)); + *p++ = htonl((u32) from_kgid(&init_user_ns, stat->gid)); if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) { *p++ = htonl(NFS_MAXPATHLEN); From ab8e4aee0a3f73d1b12e6d63b42075f0586ad4fd Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 05:18:08 -0800 Subject: [PATCH 2500/4607] nfsd: Handle kuids and kgids in the nfs4acl to posix_acl conversion In struct nfs4_ace remove the member who and replace it with an anonymous union holding who_uid and who_gid. Allowing typesafe storage uids and gids. Add a helper pace_gt for sorting posix_acl_entries. In struct posix_user_ace_state to replace uid with a union of kuid_t uid and kgid_t gid. Remove all initializations of the deprecated posic_acl_entry e_id field. Which is not present when user namespaces are enabled. Split find_uid into two functions find_uid and find_gid that work in a typesafe manner. In nfs4xdr update nfsd4_encode_fattr to deal with the changes in struct nfs4_ace. Rewrite nfsd4_encode_name to take a kuid_t and a kgid_t instead of a generic id and flag if it is a group or a uid. Replace the group flag with a test for a valid gid. Modify nfsd4_encode_user to take a kuid_t and call the modifed nfsd4_encode_name. Modify nfsd4_encode_group to take a kgid_t and call the modified nfsd4_encode_name. Modify nfsd4_encode_aclname to take an ace instead of taking the fields of an ace broken out. This allows it to detect if the ace is for a user or a group and to pass the appropriate value while still being typesafe. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/nfs4acl.c | 63 ++++++++++++++++++++++++++++++++------------ fs/nfsd/nfs4xdr.c | 41 +++++++++++++++++----------- include/linux/nfs4.h | 6 ++++- 3 files changed, 76 insertions(+), 34 deletions(-) diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 9c51aff02ae2..8a50b3c18093 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -264,7 +264,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, ace->flag = eflag; ace->access_mask = deny_mask_from_posix(deny, flags); ace->whotype = NFS4_ACL_WHO_NAMED; - ace->who = pa->e_id; + ace->who_uid = pa->e_uid; ace++; acl->naces++; } @@ -273,7 +273,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, ace->access_mask = mask_from_posix(pa->e_perm & pas.mask, flags); ace->whotype = NFS4_ACL_WHO_NAMED; - ace->who = pa->e_id; + ace->who_uid = pa->e_uid; ace++; acl->naces++; pa++; @@ -300,7 +300,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, ace->access_mask = mask_from_posix(pa->e_perm & pas.mask, flags); ace->whotype = NFS4_ACL_WHO_NAMED; - ace->who = pa->e_id; + ace->who_gid = pa->e_gid; ace++; acl->naces++; pa++; @@ -329,7 +329,7 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP; ace->access_mask = deny_mask_from_posix(deny, flags); ace->whotype = NFS4_ACL_WHO_NAMED; - ace->who = pa->e_id; + ace->who_gid = pa->e_gid; ace++; acl->naces++; } @@ -345,6 +345,18 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl, acl->naces++; } +static bool +pace_gt(struct posix_acl_entry *pace1, struct posix_acl_entry *pace2) +{ + if (pace1->e_tag != pace2->e_tag) + return pace1->e_tag > pace2->e_tag; + if (pace1->e_tag == ACL_USER) + return uid_gt(pace1->e_uid, pace2->e_uid); + if (pace1->e_tag == ACL_GROUP) + return gid_gt(pace1->e_gid, pace2->e_gid); + return false; +} + static void sort_pacl_range(struct posix_acl *pacl, int start, int end) { int sorted = 0, i; @@ -355,8 +367,8 @@ sort_pacl_range(struct posix_acl *pacl, int start, int end) { while (!sorted) { sorted = 1; for (i = start; i < end; i++) { - if (pacl->a_entries[i].e_id - > pacl->a_entries[i+1].e_id) { + if (pace_gt(&pacl->a_entries[i], + &pacl->a_entries[i+1])) { sorted = 0; tmp = pacl->a_entries[i]; pacl->a_entries[i] = pacl->a_entries[i+1]; @@ -398,7 +410,10 @@ struct posix_ace_state { }; struct posix_user_ace_state { - uid_t uid; + union { + kuid_t uid; + kgid_t gid; + }; struct posix_ace_state perms; }; @@ -521,7 +536,6 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) if (error) goto out_err; low_mode_from_nfs4(state->owner.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; for (i=0; i < state->users->n; i++) { pace++; @@ -531,7 +545,7 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) goto out_err; low_mode_from_nfs4(state->users->aces[i].perms.allow, &pace->e_perm, flags); - pace->e_id = state->users->aces[i].uid; + pace->e_uid = state->users->aces[i].uid; add_to_mask(state, &state->users->aces[i].perms); } @@ -541,7 +555,6 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) if (error) goto out_err; low_mode_from_nfs4(state->group.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; add_to_mask(state, &state->group); for (i=0; i < state->groups->n; i++) { @@ -552,14 +565,13 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) goto out_err; low_mode_from_nfs4(state->groups->aces[i].perms.allow, &pace->e_perm, flags); - pace->e_id = state->groups->aces[i].uid; + pace->e_gid = state->groups->aces[i].gid; add_to_mask(state, &state->groups->aces[i].perms); } pace++; pace->e_tag = ACL_MASK; low_mode_from_nfs4(state->mask.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; pace++; pace->e_tag = ACL_OTHER; @@ -567,7 +579,6 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags) if (error) goto out_err; low_mode_from_nfs4(state->other.allow, &pace->e_perm, flags); - pace->e_id = ACL_UNDEFINED_ID; return pacl; out_err: @@ -587,12 +598,13 @@ static inline void deny_bits(struct posix_ace_state *astate, u32 mask) astate->deny |= mask & ~astate->allow; } -static int find_uid(struct posix_acl_state *state, struct posix_ace_state_array *a, uid_t uid) +static int find_uid(struct posix_acl_state *state, kuid_t uid) { + struct posix_ace_state_array *a = state->users; int i; for (i = 0; i < a->n; i++) - if (a->aces[i].uid == uid) + if (uid_eq(a->aces[i].uid, uid)) return i; /* Not found: */ a->n++; @@ -603,6 +615,23 @@ static int find_uid(struct posix_acl_state *state, struct posix_ace_state_array return i; } +static int find_gid(struct posix_acl_state *state, kgid_t gid) +{ + struct posix_ace_state_array *a = state->groups; + int i; + + for (i = 0; i < a->n; i++) + if (gid_eq(a->aces[i].gid, gid)) + return i; + /* Not found: */ + a->n++; + a->aces[i].gid = gid; + a->aces[i].perms.allow = state->everyone.allow; + a->aces[i].perms.deny = state->everyone.deny; + + return i; +} + static void deny_bits_array(struct posix_ace_state_array *a, u32 mask) { int i; @@ -636,7 +665,7 @@ static void process_one_v4_ace(struct posix_acl_state *state, } break; case ACL_USER: - i = find_uid(state, state->users, ace->who); + i = find_uid(state, ace->who_uid); if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { allow_bits(&state->users->aces[i].perms, mask); } else { @@ -658,7 +687,7 @@ static void process_one_v4_ace(struct posix_acl_state *state, } break; case ACL_GROUP: - i = find_uid(state, state->groups, ace->who); + i = find_gid(state, ace->who_gid); if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) { allow_bits(&state->groups->aces[i].perms, mask); } else { diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 0dc11586682f..3812b06d24b1 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -293,13 +293,13 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, ace->whotype = nfs4_acl_get_whotype(buf, dummy32); status = nfs_ok; if (ace->whotype != NFS4_ACL_WHO_NAMED) - ace->who = 0; + ; else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) status = nfsd_map_name_to_gid(argp->rqstp, - buf, dummy32, &ace->who); + buf, dummy32, &ace->who_gid); else status = nfsd_map_name_to_uid(argp->rqstp, - buf, dummy32, &ace->who); + buf, dummy32, &ace->who_uid); if (status) return status; } @@ -1926,7 +1926,7 @@ static u32 nfs4_file_type(umode_t mode) } static __be32 -nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group, +nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, kuid_t uid, kgid_t gid, __be32 **p, int *buflen) { int status; @@ -1935,10 +1935,10 @@ nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group, return nfserr_resource; if (whotype != NFS4_ACL_WHO_NAMED) status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1)); - else if (group) - status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1)); + else if (gid_valid(gid)) + status = nfsd_map_gid_to_name(rqstp, gid, (u8 *)(*p + 1)); else - status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1)); + status = nfsd_map_uid_to_name(rqstp, uid, (u8 *)(*p + 1)); if (status < 0) return nfserrno(status); *p = xdr_encode_opaque(*p, NULL, status); @@ -1948,22 +1948,33 @@ nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group, } static inline __be32 -nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, __be32 **p, int *buflen) +nfsd4_encode_user(struct svc_rqst *rqstp, kuid_t user, __be32 **p, int *buflen) { - return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen); + return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, user, INVALID_GID, + p, buflen); } static inline __be32 -nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, __be32 **p, int *buflen) +nfsd4_encode_group(struct svc_rqst *rqstp, kgid_t group, __be32 **p, int *buflen) { - return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen); + return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, INVALID_UID, group, + p, buflen); } static inline __be32 -nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group, +nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace, __be32 **p, int *buflen) { - return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen); + kuid_t uid = INVALID_UID; + kgid_t gid = INVALID_GID; + + if (ace->whotype == NFS4_ACL_WHO_NAMED) { + if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP) + gid = ace->who_gid; + else + uid = ace->who_uid; + } + return nfsd4_encode_name(rqstp, ace->whotype, uid, gid, p, buflen); } #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \ @@ -2224,9 +2235,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, WRITE32(ace->type); WRITE32(ace->flag); WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL); - status = nfsd4_encode_aclname(rqstp, ace->whotype, - ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP, - &p, &buflen); + status = nfsd4_encode_aclname(rqstp, ace, &p, &buflen); if (status == nfserr_resource) goto out_resource; if (status) diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index e111fa419a4e..7b8fc73810ad 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -13,6 +13,7 @@ #define _LINUX_NFS4_H #include +#include #include struct nfs4_ace { @@ -20,7 +21,10 @@ struct nfs4_ace { uint32_t flag; uint32_t access_mask; int whotype; - uid_t who; + union { + kuid_t who_uid; + kgid_t who_gid; + }; }; struct nfs4_acl { From 03bc6d1cc1759e6b5959cacc02a19ef36e95e741 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 06:24:49 -0800 Subject: [PATCH 2501/4607] nfsd: Modify nfsd4_cb_sec to use kuids and kgids Change uid and gid in struct nfsd4_cb_sec to be of type kuid_t and kgid_t. In nfsd4_decode_cb_sec when reading uids and gids off the wire convert them to kuids and kgids, and if they don't convert to valid kuids or valid kuids ignore RPC_AUTH_UNIX and don't fill in any of the fields. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/nfs4xdr.c | 13 ++++++++++--- fs/nfsd/state.h | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 3812b06d24b1..2d1d06bae3a7 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -464,9 +464,16 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_ READ32(dummy); READ_BUF(dummy * 4); if (cbs->flavor == (u32)(-1)) { - cbs->uid = uid; - cbs->gid = gid; - cbs->flavor = RPC_AUTH_UNIX; + kuid_t kuid = make_kuid(&init_user_ns, uid); + kgid_t kgid = make_kgid(&init_user_ns, gid); + if (uid_valid(kuid) && gid_valid(kgid)) { + cbs->uid = kuid; + cbs->gid = kgid; + cbs->flavor = RPC_AUTH_UNIX; + } else { + dprintk("RPC_AUTH_UNIX with invalid" + "uid or gid ignoring!\n"); + } } break; case RPC_AUTH_GSS: diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index d1c229feed52..1a8c7391f7ae 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -152,8 +152,8 @@ struct nfsd4_channel_attrs { struct nfsd4_cb_sec { u32 flavor; /* (u32)(-1) used to mean "no valid flavor" */ - u32 uid; - u32 gid; + kuid_t uid; + kgid_t gid; }; struct nfsd4_create_session { From 4c1e1b34d5c800ad3ac9a7e2805b0bea70ad2278 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 06:42:53 -0800 Subject: [PATCH 2502/4607] nfsd: Store ex_anon_uid and ex_anon_gid as kuids and kgids Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/auth.c | 2 +- fs/nfsd/export.c | 22 ++++++++++++++-------- include/linux/nfsd/export.h | 4 ++-- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index 34a10d78b839..4d6642b38eae 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -58,7 +58,7 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) for (i = 0; i < rqgi->ngroups; i++) { if (gid_eq(GLOBAL_ROOT_GID, GROUP_AT(rqgi, i))) - GROUP_AT(gi, i) = make_kgid(&init_user_ns, exp->ex_anon_gid); + GROUP_AT(gi, i) = exp->ex_anon_gid; else GROUP_AT(gi, i) = GROUP_AT(rqgi, i); } diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index a3946cf13fc8..5681c5906f08 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -544,13 +544,17 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) err = get_int(&mesg, &an_int); if (err) goto out3; - exp.ex_anon_uid= an_int; + exp.ex_anon_uid= make_kuid(&init_user_ns, an_int); + if (!uid_valid(exp.ex_anon_uid)) + goto out3; /* anon gid */ err = get_int(&mesg, &an_int); if (err) goto out3; - exp.ex_anon_gid= an_int; + exp.ex_anon_gid= make_kgid(&init_user_ns, an_int); + if (!gid_valid(exp.ex_anon_gid)) + goto out3; /* fsid */ err = get_int(&mesg, &an_int); @@ -613,7 +617,7 @@ out: } static void exp_flags(struct seq_file *m, int flag, int fsid, - uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fslocs); + kuid_t anonu, kgid_t anong, struct nfsd4_fs_locations *fslocs); static void show_secinfo(struct seq_file *m, struct svc_export *exp); static int svc_export_show(struct seq_file *m, @@ -1179,15 +1183,17 @@ static void show_secinfo(struct seq_file *m, struct svc_export *exp) } static void exp_flags(struct seq_file *m, int flag, int fsid, - uid_t anonu, uid_t anong, struct nfsd4_fs_locations *fsloc) + kuid_t anonu, kgid_t anong, struct nfsd4_fs_locations *fsloc) { show_expflags(m, flag, NFSEXP_ALLFLAGS); if (flag & NFSEXP_FSID) seq_printf(m, ",fsid=%d", fsid); - if (anonu != (uid_t)-2 && anonu != (0x10000-2)) - seq_printf(m, ",anonuid=%u", anonu); - if (anong != (gid_t)-2 && anong != (0x10000-2)) - seq_printf(m, ",anongid=%u", anong); + if (!uid_eq(anonu, make_kuid(&init_user_ns, (uid_t)-2)) && + !uid_eq(anonu, make_kuid(&init_user_ns, 0x10000-2))) + seq_printf(m, ",anonuid=%u", from_kuid(&init_user_ns, anonu)); + if (!gid_eq(anong, make_kgid(&init_user_ns, (gid_t)-2)) && + !gid_eq(anong, make_kgid(&init_user_ns, 0x10000-2))) + seq_printf(m, ",anongid=%u", from_kgid(&init_user_ns, anong)); if (fsloc && fsloc->locations_count > 0) { char *loctype = (fsloc->migrated) ? "refer" : "replicas"; int i; diff --git a/include/linux/nfsd/export.h b/include/linux/nfsd/export.h index 24c139288db4..7898c997dfea 100644 --- a/include/linux/nfsd/export.h +++ b/include/linux/nfsd/export.h @@ -49,8 +49,8 @@ struct svc_export { struct auth_domain * ex_client; int ex_flags; struct path ex_path; - uid_t ex_anon_uid; - gid_t ex_anon_gid; + kuid_t ex_anon_uid; + kgid_t ex_anon_gid; int ex_fsid; unsigned char * ex_uuid; /* 16 byte fsid */ struct nfsd4_fs_locations ex_fslocs; From 6fab877900030ba3ae11928efb6087589f1e514c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 06:53:11 -0800 Subject: [PATCH 2503/4607] nfsd: Properly compare and initialize kuids and kgids Use uid_eq(uid, GLOBAL_ROOT_UID) instead of !uid. Use gid_eq(gid, GLOBAL_ROOT_GID) instead of !gid. Use uid_eq(uid, INVALID_UID) instead of uid == -1 Use gid_eq(uid, INVALID_GID) instead of gid == -1 Use uid = GLOBAL_ROOT_UID instead of uid = 0; Use gid = GLOBAL_ROOT_GID instead of gid = 0; Use !uid_eq(uid1, uid2) instead of uid1 != uid2. Use !gid_eq(gid1, gid2) instead of gid1 != gid2. Use uid_eq(uid1, uid2) instead of uid1 == uid2. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- fs/nfsd/auth.c | 10 +++++----- fs/nfsd/nfs4recover.c | 4 ++-- fs/nfsd/nfs4state.c | 6 +++--- fs/nfsd/vfs.c | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index 4d6642b38eae..06cddd572264 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -47,9 +47,9 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) if (!gi) goto oom; } else if (flags & NFSEXP_ROOTSQUASH) { - if (!new->fsuid) + if (uid_eq(new->fsuid, GLOBAL_ROOT_UID)) new->fsuid = exp->ex_anon_uid; - if (!new->fsgid) + if (gid_eq(new->fsgid, GLOBAL_ROOT_GID)) new->fsgid = exp->ex_anon_gid; gi = groups_alloc(rqgi->ngroups); @@ -66,9 +66,9 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) gi = get_group_info(rqgi); } - if (new->fsuid == (uid_t) -1) + if (uid_eq(new->fsuid, INVALID_UID)) new->fsuid = exp->ex_anon_uid; - if (new->fsgid == (gid_t) -1) + if (gid_eq(new->fsgid, INVALID_GID)) new->fsgid = exp->ex_anon_gid; ret = set_groups(new, gi); @@ -76,7 +76,7 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) if (ret < 0) goto error; - if (new->fsuid) + if (!uid_eq(new->fsuid, GLOBAL_ROOT_UID)) new->cap_effective = cap_drop_nfsd_set(new->cap_effective); else new->cap_effective = cap_raise_nfsd_set(new->cap_effective, diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index ba6fdd4a0455..4914af4a817e 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -73,8 +73,8 @@ nfs4_save_creds(const struct cred **original_creds) if (!new) return -ENOMEM; - new->fsuid = 0; - new->fsgid = 0; + new->fsuid = GLOBAL_ROOT_UID; + new->fsgid = GLOBAL_ROOT_GID; *original_creds = override_creds(new); put_cred(new); return 0; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index ac8ed96c4199..0af6d3c114ed 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1202,7 +1202,7 @@ static bool groups_equal(struct group_info *g1, struct group_info *g2) if (g1->ngroups != g2->ngroups) return false; for (i=0; ingroups; i++) - if (GROUP_AT(g1, i) != GROUP_AT(g2, i)) + if (!gid_eq(GROUP_AT(g1, i), GROUP_AT(g2, i))) return false; return true; } @@ -1227,8 +1227,8 @@ static bool same_creds(struct svc_cred *cr1, struct svc_cred *cr2) { if ((is_gss_cred(cr1) != is_gss_cred(cr2)) - || (cr1->cr_uid != cr2->cr_uid) - || (cr1->cr_gid != cr2->cr_gid) + || (!uid_eq(cr1->cr_uid, cr2->cr_uid)) + || (!gid_eq(cr1->cr_gid, cr2->cr_gid)) || !groups_equal(cr1->cr_group_info, cr2->cr_group_info)) return false; if (cr1->cr_principal == cr2->cr_principal) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index d586117fa94a..31ff1d642e31 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -401,8 +401,8 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, /* Revoke setuid/setgid on chown */ if (!S_ISDIR(inode->i_mode) && - (((iap->ia_valid & ATTR_UID) && iap->ia_uid != inode->i_uid) || - ((iap->ia_valid & ATTR_GID) && iap->ia_gid != inode->i_gid))) { + (((iap->ia_valid & ATTR_UID) && !uid_eq(iap->ia_uid, inode->i_uid)) || + ((iap->ia_valid & ATTR_GID) && !gid_eq(iap->ia_gid, inode->i_gid)))) { iap->ia_valid |= ATTR_KILL_PRIV; if (iap->ia_valid & ATTR_MODE) { /* we're setting mode too, just clear the s*id bits */ @@ -1205,7 +1205,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *resfhp, * send along the gid on create when it tries to implement * setgid directories via NFS: */ - if (current_fsuid() != 0) + if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) iap->ia_valid &= ~(ATTR_UID|ATTR_GID); if (iap->ia_valid) return nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0); @@ -2150,7 +2150,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, * with NFSv3. */ if ((acc & NFSD_MAY_OWNER_OVERRIDE) && - inode->i_uid == current_fsuid()) + uid_eq(inode->i_uid, current_fsuid())) return 0; /* This assumes NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */ From c9617a44b39d75b2098283d56355d02d4d5bee8b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 2 Feb 2013 07:00:40 -0800 Subject: [PATCH 2504/4607] nfsd: Enable building with user namespaces enabled. Now that the kuids and kgids conversion have propogated through net/sunrpc/ and the fs/nfsd/ it is safe to enable building nfsd when user namespaces are enabled. Cc: "J. Bruce Fields" Cc: Trond Myklebust Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index d7b926abe91f..69c5ccce55e7 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1072,7 +1072,6 @@ config UIDGID_CONVERTED # Filesystems depends on CIFS = n - depends on NFSD = n depends on XFS_FS = n config UIDGID_STRICT_TYPE_CHECKS From 46bbc25f9ff1242cd1c042580aec11ddb5e41369 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 5 Feb 2013 23:55:44 -0800 Subject: [PATCH 2505/4607] cifs: Override unmappable incoming uids and gids The cifs protocol has a 64bit space for uids and gids, while linux only supports a 32bit space today. Instead of silently truncating 64bit cifs ids, replace cifs ids that do not fit in the 32bit linux id space with the default uid and gids for the cifs mount. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/inode.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index ed6208ff85a7..d7ea2a6eaead 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -244,15 +244,19 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info, break; } - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) - fattr->cf_uid = cifs_sb->mnt_uid; - else - fattr->cf_uid = le64_to_cpu(info->Uid); - - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) - fattr->cf_gid = cifs_sb->mnt_gid; - else - fattr->cf_gid = le64_to_cpu(info->Gid); + fattr->cf_uid = cifs_sb->mnt_uid; + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) { + u64 id = le64_to_cpu(info->Uid); + if (id < ((uid_t)-1)) + fattr->cf_uid = id; + } + + fattr->cf_gid = cifs_sb->mnt_gid; + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) { + u64 id = le64_to_cpu(info->Gid); + if (id < ((gid_t)-1)) + fattr->cf_gid = id; + } fattr->cf_nlink = le64_to_cpu(info->Nlinks); } From 355958f2893d97375da17696d2566dd4ab7b54a8 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 00:10:23 -0800 Subject: [PATCH 2506/4607] cifs: Use BUILD_BUG_ON to validate uids and gids are the same size The assumption that sizeof(uid_t) is the same as sizeof(gid_t) is completely reasonable but since we can verify the condition at compile time. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsacl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 5cbd00e74067..2e811f45b413 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -297,6 +297,7 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, * probably a safe assumption but might be better to check based on * sidtype. */ + BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t)); if (sidkey->datalen != sizeof(uid_t)) { rc = -EIO; cFYI(1, "%s: Downcall contained malformed key " From 8e3028b908a048ac0b3102504b7daad7b5544846 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 00:21:22 -0800 Subject: [PATCH 2507/4607] cifs: Pass GLOBAL_ROOT_UID and GLOBAL_ROOT_GID to keyring_alloc keyring_alloc has been updated to take a kuid_t and kgid_t so pass GLOBAL_ROOT_UID instead of 0 for the uid and GLOBAL_ROOT_GID instead of 0 for the gid. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsacl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 2e811f45b413..aaaf5ce142b8 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -347,7 +347,8 @@ init_cifs_idmap(void) if (!cred) return -ENOMEM; - keyring = keyring_alloc(".cifs_idmap", 0, 0, cred, + keyring = keyring_alloc(".cifs_idmap", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, KEY_ALLOC_NOT_IN_QUOTA, NULL); From 8abf2775dd425ec3c767ea7c5a51b45fc8be76c2 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 00:33:17 -0800 Subject: [PATCH 2508/4607] cifs: Use kuids and kgids SID to uid/gid mapping Update id_mode_to_cifs_acl to take a kuid_t and a kgid_t. Replace NO_CHANGE_32 with INVALID_UID and INVALID_GID, and tests for NO_CHANGE_32 with uid_valid and gid_valid. Carefully unpack the value returned from request_key. memcpy the value into the expected type. The convert the uid/gid into a kuid/kgid. And then only if the result is a valid kuid or kgid update fuid/fgid. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsacl.c | 43 +++++++++++++++++++++++++++++-------------- fs/cifs/cifspdu.h | 1 - fs/cifs/cifsproto.h | 2 +- fs/cifs/inode.c | 8 ++++---- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index aaaf5ce142b8..f1e3f25fe004 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -266,8 +266,8 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, struct key *sidkey; char *sidstr; const struct cred *saved_cred; - uid_t fuid = cifs_sb->mnt_uid; - gid_t fgid = cifs_sb->mnt_gid; + kuid_t fuid = cifs_sb->mnt_uid; + kgid_t fgid = cifs_sb->mnt_gid; /* * If we have too many subauthorities, then something is really wrong. @@ -306,10 +306,21 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, goto out_key_put; } - if (sidtype == SIDOWNER) - memcpy(&fuid, &sidkey->payload.value, sizeof(uid_t)); - else - memcpy(&fgid, &sidkey->payload.value, sizeof(gid_t)); + if (sidtype == SIDOWNER) { + kuid_t uid; + uid_t id; + memcpy(&id, &sidkey->payload.value, sizeof(uid_t)); + uid = make_kuid(&init_user_ns, id); + if (uid_valid(uid)) + fuid = uid; + } else { + kgid_t gid; + gid_t id; + memcpy(&id, &sidkey->payload.value, sizeof(gid_t)); + gid = make_kgid(&init_user_ns, id); + if (gid_valid(gid)) + fgid = gid; + } out_key_put: key_put(sidkey); @@ -776,7 +787,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb, /* Convert permission bits from mode to equivalent CIFS ACL */ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, - __u32 secdesclen, __u64 nmode, uid_t uid, gid_t gid, int *aclflag) + __u32 secdesclen, __u64 nmode, kuid_t uid, kgid_t gid, int *aclflag) { int rc = 0; __u32 dacloffset; @@ -808,17 +819,19 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, *aclflag = CIFS_ACL_DACL; } else { memcpy(pnntsd, pntsd, secdesclen); - if (uid != NO_CHANGE_32) { /* chown */ + if (uid_valid(uid)) { /* chown */ + uid_t id; owner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + le32_to_cpu(pnntsd->osidoffset)); nowner_sid_ptr = kmalloc(sizeof(struct cifs_sid), GFP_KERNEL); if (!nowner_sid_ptr) return -ENOMEM; - rc = id_to_sid(uid, SIDOWNER, nowner_sid_ptr); + id = from_kuid(&init_user_ns, uid); + rc = id_to_sid(id, SIDOWNER, nowner_sid_ptr); if (rc) { cFYI(1, "%s: Mapping error %d for owner id %d", - __func__, rc, uid); + __func__, rc, id); kfree(nowner_sid_ptr); return rc; } @@ -826,17 +839,19 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, kfree(nowner_sid_ptr); *aclflag = CIFS_ACL_OWNER; } - if (gid != NO_CHANGE_32) { /* chgrp */ + if (gid_valid(gid)) { /* chgrp */ + gid_t id; group_sid_ptr = (struct cifs_sid *)((char *)pnntsd + le32_to_cpu(pnntsd->gsidoffset)); ngroup_sid_ptr = kmalloc(sizeof(struct cifs_sid), GFP_KERNEL); if (!ngroup_sid_ptr) return -ENOMEM; - rc = id_to_sid(gid, SIDGROUP, ngroup_sid_ptr); + id = from_kgid(&init_user_ns, gid); + rc = id_to_sid(id, SIDGROUP, ngroup_sid_ptr); if (rc) { cFYI(1, "%s: Mapping error %d for group id %d", - __func__, rc, gid); + __func__, rc, id); kfree(ngroup_sid_ptr); return rc; } @@ -1004,7 +1019,7 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, /* Convert mode bits to an ACL so we can update the ACL on the server */ int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode, - uid_t uid, gid_t gid) + kuid_t uid, kgid_t gid) { int rc = 0; int aclflag = CIFS_ACL_DACL; /* default flag to set */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index b9d59a948a2c..e996ff6b26d1 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -277,7 +277,6 @@ #define CIFS_NO_HANDLE 0xFFFF #define NO_CHANGE_64 0xFFFFFFFFFFFFFFFFULL -#define NO_CHANGE_32 0xFFFFFFFFUL /* IPC$ in ASCII */ #define CIFS_IPC_RESOURCE "\x49\x50\x43\x24" diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 1988c1baa224..0060ec2124bf 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -161,7 +161,7 @@ extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, struct inode *inode, const char *path, const __u16 *pfid); extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64, - uid_t, gid_t); + kuid_t, kgid_t); extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *, const char *, u32 *); extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *, diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index d7ea2a6eaead..d4cf7509c106 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -2090,8 +2090,8 @@ static int cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) { unsigned int xid; - uid_t uid = NO_CHANGE_32; - gid_t gid = NO_CHANGE_32; + kuid_t uid = INVALID_UID; + kgid_t gid = INVALID_GID; struct inode *inode = direntry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifsInodeInfo *cifsInode = CIFS_I(inode); @@ -2150,7 +2150,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) #ifdef CONFIG_CIFS_ACL if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { - if (uid != NO_CHANGE_32 || gid != NO_CHANGE_32) { + if (uid_valid(uid) || gid_valid(gid)) { rc = id_mode_to_cifs_acl(inode, full_path, NO_CHANGE_64, uid, gid); if (rc) { @@ -2174,7 +2174,7 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) #ifdef CONFIG_CIFS_ACL if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { rc = id_mode_to_cifs_acl(inode, full_path, mode, - NO_CHANGE_32, NO_CHANGE_32); + INVALID_UID, INVALID_GID); if (rc) { cFYI(1, "%s: Setting ACL failed with error: %d", __func__, rc); From dbfb98af18194cff87d4c1dea8d43faf14eae2e7 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 00:44:13 -0800 Subject: [PATCH 2509/4607] cifs: Convert from a kuid before printing current_fsuid Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsproto.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 0060ec2124bf..feb9b6e2ab5a 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -46,7 +46,8 @@ extern void _free_xid(unsigned int); ({ \ unsigned int __xid = _get_xid(); \ cFYI(1, "CIFS VFS: in %s as Xid: %u with uid: %d", \ - __func__, __xid, current_fsuid()); \ + __func__, __xid, \ + from_kuid(&init_user_ns, current_fsuid())); \ __xid; \ }) From 49418b2c28c901294f8b36ff14c766c9458c3623 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 00:57:56 -0800 Subject: [PATCH 2510/4607] cifs: Modify struct cifs_unix_set_info_args to hold a kuid_t and a kgid_t Use INVALID_UID and INVALID_GID instead of NO_CHANGE_64 to indicate the value should not be changed. In cifs_fill_unix_set_info convert from kuids and kgids into uids and gids that will fit in FILE_UNIX_BASIC_INFO. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsproto.h | 4 ++-- fs/cifs/cifssmb.c | 10 ++++++++-- fs/cifs/dir.c | 18 +++++++++--------- fs/cifs/file.c | 4 ++-- fs/cifs/inode.c | 14 +++++++------- 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index feb9b6e2ab5a..f450f0683ddd 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -305,8 +305,8 @@ struct cifs_unix_set_info_args { __u64 atime; __u64 mtime; __u64 mode; - __u64 uid; - __u64 gid; + kuid_t uid; + kgid_t gid; dev_t device; }; diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 76d0d2998850..00e12f2d626b 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -5819,8 +5819,14 @@ static void cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset, const struct cifs_unix_set_info_args *args) { + u64 uid = NO_CHANGE_64, gid = NO_CHANGE_64; u64 mode = args->mode; + if (uid_valid(args->uid)) + uid = from_kuid(&init_user_ns, args->uid); + if (gid_valid(args->gid)) + gid = from_kgid(&init_user_ns, args->gid); + /* * Samba server ignores set of file size to zero due to bugs in some * older clients, but we should be precise - we use SetFileSize to @@ -5833,8 +5839,8 @@ cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset, data_offset->LastStatusChange = cpu_to_le64(args->ctime); data_offset->LastAccessTime = cpu_to_le64(args->atime); data_offset->LastModificationTime = cpu_to_le64(args->mtime); - data_offset->Uid = cpu_to_le64(args->uid); - data_offset->Gid = cpu_to_le64(args->gid); + data_offset->Uid = cpu_to_le64(uid); + data_offset->Gid = cpu_to_le64(gid); /* better to leave device as zero when it is */ data_offset->DevMajor = cpu_to_le64(MAJOR(args->device)); data_offset->DevMinor = cpu_to_le64(MINOR(args->device)); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 8719bbe0dcc3..1cd016217448 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -342,14 +342,14 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, *created |= FILE_CREATED; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = (__u64) current_fsuid(); + args.uid = current_fsuid(); if (inode->i_mode & S_ISGID) - args.gid = (__u64) inode->i_gid; + args.gid = inode->i_gid; else - args.gid = (__u64) current_fsgid(); + args.gid = current_fsgid(); } else { - args.uid = NO_CHANGE_64; - args.gid = NO_CHANGE_64; + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ } CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid, current->tgid); @@ -588,11 +588,11 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode, .device = device_number, }; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = (__u64) current_fsuid(); - args.gid = (__u64) current_fsgid(); + args.uid = current_fsuid(); + args.gid = current_fsgid(); } else { - args.uid = NO_CHANGE_64; - args.gid = NO_CHANGE_64; + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ } rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args, cifs_sb->local_nls, diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 0a6677ba212b..b9baf5f66349 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -487,8 +487,8 @@ int cifs_open(struct inode *inode, struct file *file) */ struct cifs_unix_set_info_args args = { .mode = inode->i_mode, - .uid = NO_CHANGE_64, - .gid = NO_CHANGE_64, + .uid = INVALID_UID, /* no change */ + .gid = INVALID_GID, /* no change */ .ctime = NO_CHANGE_64, .atime = NO_CHANGE_64, .mtime = NO_CHANGE_64, diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index d4cf7509c106..737643940540 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1249,14 +1249,14 @@ cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode, .device = 0, }; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { - args.uid = (__u64)current_fsuid(); + args.uid = current_fsuid(); if (parent->i_mode & S_ISGID) - args.gid = (__u64)parent->i_gid; + args.gid = parent->i_gid; else - args.gid = (__u64)current_fsgid(); + args.gid = current_fsgid(); } else { - args.uid = NO_CHANGE_64; - args.gid = NO_CHANGE_64; + args.uid = INVALID_UID; /* no change */ + args.gid = INVALID_GID; /* no change */ } CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, cifs_sb->local_nls, @@ -2017,12 +2017,12 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) if (attrs->ia_valid & ATTR_UID) args->uid = attrs->ia_uid; else - args->uid = NO_CHANGE_64; + args->uid = INVALID_UID; /* no change */ if (attrs->ia_valid & ATTR_GID) args->gid = attrs->ia_gid; else - args->gid = NO_CHANGE_64; + args->gid = INVALID_GID; /* no change */ if (attrs->ia_valid & ATTR_ATIME) args->atime = cifs_UnixTimeToNT(attrs->ia_atime); From 6d4a083205c27bc2e2a1c03dff4acf2fe457d1c4 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 01:48:56 -0800 Subject: [PATCH 2511/4607] cifs: Convert struct tcon_link to use a kuid. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsglob.h | 2 +- fs/cifs/connect.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index aea1eec64911..7993a7a07ae2 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -837,7 +837,7 @@ struct cifs_tcon { */ struct tcon_link { struct rb_node tl_rbnode; - uid_t tl_uid; + kuid_t tl_uid; unsigned long tl_flags; #define TCON_LINK_MASTER 0 #define TCON_LINK_PENDING 1 diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 17c3643e5950..541169459223 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3919,7 +3919,7 @@ cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses) } static struct cifs_tcon * -cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) +cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid) { int rc; struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); @@ -3989,7 +3989,7 @@ cifs_sb_tcon_pending_wait(void *unused) /* find and return a tlink with given uid */ static struct tcon_link * -tlink_rb_search(struct rb_root *root, uid_t uid) +tlink_rb_search(struct rb_root *root, kuid_t uid) { struct rb_node *node = root->rb_node; struct tcon_link *tlink; @@ -3997,9 +3997,9 @@ tlink_rb_search(struct rb_root *root, uid_t uid) while (node) { tlink = rb_entry(node, struct tcon_link, tl_rbnode); - if (tlink->tl_uid > uid) + if (uid_gt(tlink->tl_uid, uid)) node = node->rb_left; - else if (tlink->tl_uid < uid) + else if (uid_lt(tlink->tl_uid, uid)) node = node->rb_right; else return tlink; @@ -4018,7 +4018,7 @@ tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink) tlink = rb_entry(*new, struct tcon_link, tl_rbnode); parent = *new; - if (tlink->tl_uid > new_tlink->tl_uid) + if (uid_gt(tlink->tl_uid, new_tlink->tl_uid)) new = &((*new)->rb_left); else new = &((*new)->rb_right); @@ -4048,7 +4048,7 @@ struct tcon_link * cifs_sb_tlink(struct cifs_sb_info *cifs_sb) { int ret; - uid_t fsuid = current_fsuid(); + kuid_t fsuid = current_fsuid(); struct tcon_link *tlink, *newtlink; if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)) From 4a2c8cf56953a6ebe3c8671433607b7f96f200d5 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 01:53:25 -0800 Subject: [PATCH 2512/4607] cifs: Convert struct cifs_fattr to use kuid and kgids In cifs_unix_to_basic_fattr only update the cifs_fattr with an id if it is valid after conversion. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsglob.h | 4 ++-- fs/cifs/inode.c | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 7993a7a07ae2..98312fda0d35 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1245,8 +1245,8 @@ struct cifs_fattr { u64 cf_eof; u64 cf_bytes; u64 cf_createtime; - uid_t cf_uid; - gid_t cf_gid; + kuid_t cf_uid; + kgid_t cf_gid; umode_t cf_mode; dev_t cf_rdev; unsigned int cf_nlink; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 737643940540..9638233964fc 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -247,15 +247,21 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info, fattr->cf_uid = cifs_sb->mnt_uid; if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) { u64 id = le64_to_cpu(info->Uid); - if (id < ((uid_t)-1)) - fattr->cf_uid = id; + if (id < ((uid_t)-1)) { + kuid_t uid = make_kuid(&init_user_ns, id); + if (uid_valid(uid)) + fattr->cf_uid = uid; + } } fattr->cf_gid = cifs_sb->mnt_gid; if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) { u64 id = le64_to_cpu(info->Gid); - if (id < ((gid_t)-1)) - fattr->cf_gid = id; + if (id < ((gid_t)-1)) { + kgid_t gid = make_kgid(&init_user_ns, id); + if (gid_valid(gid)) + fattr->cf_gid = gid; + } } fattr->cf_nlink = le64_to_cpu(info->Nlinks); From fef59fd728366aa9bf125b8859aff84fa0bd1a36 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 02:23:02 -0800 Subject: [PATCH 2513/4607] cifs: Convert struct cifsFileInfo to use a kuid Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsglob.h | 2 +- fs/cifs/file.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 98312fda0d35..04aa74e5b348 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -930,7 +930,7 @@ struct cifsFileInfo { struct list_head tlist; /* pointer to next fid owned by tcon */ struct list_head flist; /* next fid (file instance) for this inode */ struct cifs_fid_locks *llist; /* brlocks held by this fid */ - unsigned int uid; /* allows finding which FileInfo structure */ + kuid_t uid; /* allows finding which FileInfo structure */ __u32 pid; /* process id who opened file */ struct cifs_fid fid; /* file id from remote */ /* BB add lock scope info here if needed */ ; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index b9baf5f66349..c23fbd81fe1a 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1649,7 +1649,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, are always at the end of the list but since the first entry might have a close pending, we go through the whole list */ list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { - if (fsuid_only && open_file->uid != current_fsuid()) + if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) continue; if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) { if (!open_file->invalidHandle) { @@ -1702,7 +1702,7 @@ refind_writable: list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (!any_available && open_file->pid != current->tgid) continue; - if (fsuid_only && open_file->uid != current_fsuid()) + if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) continue; if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { if (!open_file->invalidHandle) { From 3da46565043a3d7b9fd5c924e7de2d3e65e9d2a9 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 01:37:39 -0800 Subject: [PATCH 2514/4607] cifs: Modify struct smb_vol to use kuids and kgids Add two helper functions get_option_uid and get_option_gid to handle the work of parsing uid and gids paramaters from the command line and making kuids and kgids out of them. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifsglob.h | 10 +++++----- fs/cifs/connect.c | 50 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 04aa74e5b348..e308e8bd0772 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -399,11 +399,11 @@ struct smb_vol { char *iocharset; /* local code page for mapping to and from Unicode */ char source_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* clnt nb name */ char target_rfc1001_name[RFC1001_NAME_LEN_WITH_NULL]; /* srvr nb name */ - uid_t cred_uid; - uid_t linux_uid; - gid_t linux_gid; - uid_t backupuid; - gid_t backupgid; + kuid_t cred_uid; + kuid_t linux_uid; + kgid_t linux_gid; + kuid_t backupuid; + kgid_t backupgid; umode_t file_mode; umode_t dir_mode; unsigned secFlg; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 541169459223..9bd13a75602d 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -987,6 +987,41 @@ static int get_option_ul(substring_t args[], unsigned long *option) return rc; } +static int get_option_uid(substring_t args[], kuid_t *result) +{ + unsigned long value; + kuid_t uid; + int rc; + + rc = get_option_ul(args, &value); + if (rc) + return rc; + + uid = make_kuid(current_user_ns(), value); + if (!uid_valid(uid)) + return -EINVAL; + + *result = uid; + return 0; +} + +static int get_option_gid(substring_t args[], kgid_t *result) +{ + unsigned long value; + kgid_t gid; + int rc; + + rc = get_option_ul(args, &value); + if (rc) + return rc; + + gid = make_kgid(current_user_ns(), value); + if (!gid_valid(gid)) + return -EINVAL; + + *result = gid; + return 0; +} static int cifs_parse_security_flavors(char *value, struct smb_vol *vol) @@ -1424,47 +1459,42 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, /* Numeric Values */ case Opt_backupuid: - if (get_option_ul(args, &option)) { + if (get_option_uid(args, &vol->backupuid)) { cERROR(1, "%s: Invalid backupuid value", __func__); goto cifs_parse_mount_err; } - vol->backupuid = option; vol->backupuid_specified = true; break; case Opt_backupgid: - if (get_option_ul(args, &option)) { + if (get_option_gid(args, &vol->backupgid)) { cERROR(1, "%s: Invalid backupgid value", __func__); goto cifs_parse_mount_err; } - vol->backupgid = option; vol->backupgid_specified = true; break; case Opt_uid: - if (get_option_ul(args, &option)) { + if (get_option_uid(args, &vol->linux_uid)) { cERROR(1, "%s: Invalid uid value", __func__); goto cifs_parse_mount_err; } - vol->linux_uid = option; uid_specified = true; break; case Opt_cruid: - if (get_option_ul(args, &option)) { + if (get_option_uid(args, &vol->cred_uid)) { cERROR(1, "%s: Invalid cruid value", __func__); goto cifs_parse_mount_err; } - vol->cred_uid = option; break; case Opt_gid: - if (get_option_ul(args, &option)) { + if (get_option_gid(args, &vol->linux_gid)) { cERROR(1, "%s: Invalid gid value", __func__); goto cifs_parse_mount_err; } - vol->linux_gid = option; gid_specified = true; break; case Opt_file_mode: From 1f68233c52e9f2bb53130a0063bc1e6864f6d204 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 01:20:20 -0800 Subject: [PATCH 2515/4607] cifs: Convert struct cifs_sb_info to use kuids and kgids Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifs_fs_sb.h | 8 ++++---- fs/cifs/cifsfs.c | 14 ++++++++++---- fs/cifs/connect.c | 2 +- fs/cifs/misc.c | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index c865bfdfe819..37e4a72a7d1c 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -55,10 +55,10 @@ struct cifs_sb_info { unsigned int wsize; unsigned long actimeo; /* attribute cache timeout (jiffies) */ atomic_t active; - uid_t mnt_uid; - gid_t mnt_gid; - uid_t mnt_backupuid; - gid_t mnt_backupgid; + kuid_t mnt_uid; + kgid_t mnt_gid; + kuid_t mnt_backupuid; + kgid_t mnt_backupgid; umode_t mnt_file_mode; umode_t mnt_dir_mode; unsigned int mnt_cifs_flags; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index f653835d067b..17590731786d 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -376,13 +376,15 @@ cifs_show_options(struct seq_file *s, struct dentry *root) (int)(srcaddr->sa_family)); } - seq_printf(s, ",uid=%u", cifs_sb->mnt_uid); + seq_printf(s, ",uid=%u", + from_kuid_munged(&init_user_ns, cifs_sb->mnt_uid)); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) seq_printf(s, ",forceuid"); else seq_printf(s, ",noforceuid"); - seq_printf(s, ",gid=%u", cifs_sb->mnt_gid); + seq_printf(s, ",gid=%u", + from_kgid_munged(&init_user_ns, cifs_sb->mnt_gid)); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) seq_printf(s, ",forcegid"); else @@ -437,9 +439,13 @@ cifs_show_options(struct seq_file *s, struct dentry *root) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) seq_printf(s, ",noperm"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) - seq_printf(s, ",backupuid=%u", cifs_sb->mnt_backupuid); + seq_printf(s, ",backupuid=%u", + from_kuid_munged(&init_user_ns, + cifs_sb->mnt_backupuid)); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) - seq_printf(s, ",backupgid=%u", cifs_sb->mnt_backupgid); + seq_printf(s, ",backupgid=%u", + from_kgid_munged(&init_user_ns, + cifs_sb->mnt_backupgid)); seq_printf(s, ",rsize=%u", cifs_sb->rsize); seq_printf(s, ",wsize=%u", cifs_sb->wsize); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 9bd13a75602d..01279b8f5d1f 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2743,7 +2743,7 @@ compare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data) if (new->rsize && new->rsize < old->rsize) return 0; - if (old->mnt_uid != new->mnt_uid || old->mnt_gid != new->mnt_gid) + if (!uid_eq(old->mnt_uid, new->mnt_uid) || !gid_eq(old->mnt_gid, new->mnt_gid)) return 0; if (old->mnt_file_mode != new->mnt_file_mode || diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 3a00c0d0cead..1b15bf839f37 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -569,7 +569,7 @@ bool backup_cred(struct cifs_sb_info *cifs_sb) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) { - if (cifs_sb->mnt_backupuid == current_fsuid()) + if (uid_eq(cifs_sb->mnt_backupuid, current_fsuid())) return true; } if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) { From 64ed39dd1ef284c0338799a6167b77a6d6e01982 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 02:30:39 -0800 Subject: [PATCH 2516/4607] cifs: Convert struct cifs_ses to use a kuid_t and a kgid_t Cc: Steve French Signed-off-by: "Eric W. Biederman" --- fs/cifs/cifs_spnego.c | 6 ++++-- fs/cifs/cifsglob.h | 4 ++-- fs/cifs/connect.c | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index 086f381d6489..10e774761299 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -149,10 +149,12 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) goto out; dp = description + strlen(description); - sprintf(dp, ";uid=0x%x", sesInfo->linux_uid); + sprintf(dp, ";uid=0x%x", + from_kuid_munged(&init_user_ns, sesInfo->linux_uid)); dp = description + strlen(description); - sprintf(dp, ";creduid=0x%x", sesInfo->cred_uid); + sprintf(dp, ";creduid=0x%x", + from_kuid_munged(&init_user_ns, sesInfo->cred_uid)); if (sesInfo->user_name) { dp = description + strlen(description); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index e308e8bd0772..aaef57beba0e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -702,8 +702,8 @@ struct cifs_ses { char *serverNOS; /* name of network operating system of server */ char *serverDomain; /* security realm of server */ __u64 Suid; /* remote smb uid */ - uid_t linux_uid; /* overriding owner of files on the mount */ - uid_t cred_uid; /* owner of credentials */ + kuid_t linux_uid; /* overriding owner of files on the mount */ + kuid_t cred_uid; /* owner of credentials */ unsigned int capabilities; char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for TCP names - will ipv6 and sctp addresses fit? */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 01279b8f5d1f..067b0e169fe4 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2271,7 +2271,7 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol) { switch (ses->server->secType) { case Kerberos: - if (vol->cred_uid != ses->cred_uid) + if (!uid_eq(vol->cred_uid, ses->cred_uid)) return 0; break; default: From 139321c65c0584cd65c4c87a5eb3fdb4fdbd0e19 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 6 Feb 2013 02:31:39 -0800 Subject: [PATCH 2517/4607] cifs: Enable building with user namespaces enabled. Cc: Steve French Signed-off-by: "Eric W. Biederman" --- init/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/init/Kconfig b/init/Kconfig index 69c5ccce55e7..b2de5edee0ce 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1071,7 +1071,6 @@ config UIDGID_CONVERTED default y # Filesystems - depends on CIFS = n depends on XFS_FS = n config UIDGID_STRICT_TYPE_CHECKS From f583c29b7913fa32b0b1b7f43038d6a7d9f71b6f Mon Sep 17 00:00:00 2001 From: Gleb Natapov Date: Wed, 13 Feb 2013 17:50:39 +0200 Subject: [PATCH 2518/4607] x86 emulator: fix parity calculation for AAD instruction Reported-by: Paolo Bonzini Suggested-by: Paolo Bonzini Reviewed-by: Paolo Bonzini Signed-off-by: Gleb Natapov --- arch/x86/kvm/emulate.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 2b11318151a4..a335cc6cde72 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -2995,14 +2995,11 @@ static int em_aad(struct x86_emulate_ctxt *ctxt) ctxt->dst.val = (ctxt->dst.val & 0xffff0000) | al; - ctxt->eflags &= ~(X86_EFLAGS_PF | X86_EFLAGS_SF | X86_EFLAGS_ZF); - - if (!al) - ctxt->eflags |= X86_EFLAGS_ZF; - if (!(al & 1)) - ctxt->eflags |= X86_EFLAGS_PF; - if (al & 0x80) - ctxt->eflags |= X86_EFLAGS_SF; + /* Set PF, ZF, SF */ + ctxt->src.type = OP_IMM; + ctxt->src.val = 0; + ctxt->src.bytes = 1; + fastop(ctxt, em_or); return X86EMUL_CONTINUE; } From 5fa422c922c2599dbfd960faf6dfca2411cc3f99 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 12 Feb 2013 09:15:02 -0800 Subject: [PATCH 2519/4607] dmaengine: move drivers/of/dma.c -> drivers/dma/of-dma.c as requested by Rob Suggested-by: Rob Herring Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 4 ++++ drivers/dma/Makefile | 2 ++ drivers/{of/dma.c => dma/of-dma.c} | 0 drivers/of/Makefile | 2 +- 4 files changed, 7 insertions(+), 1 deletion(-) rename drivers/{of/dma.c => dma/of-dma.c} (100%) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 0b408bbb6a17..e92b5f0f8a5f 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -325,6 +325,10 @@ config DMA_ENGINE config DMA_VIRTUAL_CHANNELS tristate +config DMA_OF + def_bool y + depends on OF + comment "DMA Clients" depends on DMA_ENGINE diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 7428feaa8705..c1ed644be9e2 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -3,6 +3,8 @@ ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG obj-$(CONFIG_DMA_ENGINE) += dmaengine.o obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o +obj-$(CONFIG_DMA_OF) += of-dma.o + obj-$(CONFIG_NET_DMA) += iovlock.o obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o obj-$(CONFIG_DMATEST) += dmatest.o diff --git a/drivers/of/dma.c b/drivers/dma/of-dma.c similarity index 100% rename from drivers/of/dma.c rename to drivers/dma/of-dma.c diff --git a/drivers/of/Makefile b/drivers/of/Makefile index eafa107aed40..e027f444d10c 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,4 +1,4 @@ -obj-y = base.o dma.o +obj-y = base.o obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o From a20702b8d7c2b54a618e203d94b37b4f1d21bbd4 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Wed, 13 Feb 2013 09:40:03 +0800 Subject: [PATCH 2520/4607] dmaengine: ioat - fix spare sparse complain >> drivers/dma/ioat/dma_v3.c:371:6: sparse: symbol 'ioat3_timer_event' was not declared. Reported-by: Fengguang Wu Signed-off-by: Fengguang Wu Acked-by: Dave Jiang Signed-off-by: Vinod Koul --- drivers/dma/ioat/dma_v3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/ioat/dma_v3.c b/drivers/dma/ioat/dma_v3.c index 570dd6320c32..2acd1ad57fd6 100644 --- a/drivers/dma/ioat/dma_v3.c +++ b/drivers/dma/ioat/dma_v3.c @@ -368,7 +368,7 @@ static void check_active(struct ioat2_dma_chan *ioat) } -void ioat3_timer_event(unsigned long data) +static void ioat3_timer_event(unsigned long data) { struct ioat2_dma_chan *ioat = to_ioat2_chan((void *) data); struct ioat_chan_common *chan = &ioat->base; From d6646b8075f8110ac167f335d660b8a544dd324e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 30 Jan 2013 05:19:56 -0300 Subject: [PATCH 2521/4607] [media] sh-mobile-ceu-camera: fix SHARPNESS control default The V4L2_CID_SHARPNESS control in the sh-mobile-ceu-camera driver, if off, turns the CEU low-pass filter on. This is the opposite to the hardware default and can degrade image quality. Switch default to on to restore the default unfiltered mode. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 602584dc9330..bb08a46432f4 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1064,7 +1064,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int /* Add our control */ v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops, - V4L2_CID_SHARPNESS, 0, 1, 1, 0); + V4L2_CID_SHARPNESS, 0, 1, 1, 1); if (icd->ctrl_handler.error) return icd->ctrl_handler.error; From ec34e1d579819789ecde888c1d54310824957893 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 30 Jan 2013 08:04:47 -0300 Subject: [PATCH 2522/4607] [media] mt9t112: mt9t111 format set up differs from mt9t112 The original commit, adding the mt9t112 driver said, that mt9t111 and mt9t112 had identical register layouts. This however doesn't seem to be the case. At least pixel format selection in the mt9t111 datasheet is different from the driver implementation. So far only the default YUYV format has been verified to work with mt9t111. Limit the driver to only report one supported format with mt9t111 until more formats are implemented. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/soc_camera/mt9t112.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index c75d83168700..188e29b03273 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -92,6 +92,7 @@ struct mt9t112_priv { struct v4l2_rect frame; const struct mt9t112_format *format; int model; + int num_formats; u32 flags; /* for flags */ #define INIT_DONE (1 << 0) @@ -859,11 +860,11 @@ static int mt9t112_set_params(struct mt9t112_priv *priv, /* * get color format */ - for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++) + for (i = 0; i < priv->num_formats; i++) if (mt9t112_cfmts[i].code == code) break; - if (i == ARRAY_SIZE(mt9t112_cfmts)) + if (i == priv->num_formats) return -EINVAL; priv->frame = *rect; @@ -955,14 +956,16 @@ static int mt9t112_s_fmt(struct v4l2_subdev *sd, static int mt9t112_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); unsigned int top, left; int i; - for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++) + for (i = 0; i < priv->num_formats; i++) if (mt9t112_cfmts[i].code == mf->code) break; - if (i == ARRAY_SIZE(mt9t112_cfmts)) { + if (i == priv->num_formats) { mf->code = V4L2_MBUS_FMT_UYVY8_2X8; mf->colorspace = V4L2_COLORSPACE_JPEG; } else { @@ -979,7 +982,10 @@ static int mt9t112_try_fmt(struct v4l2_subdev *sd, static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code) { - if (index >= ARRAY_SIZE(mt9t112_cfmts)) + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct mt9t112_priv *priv = to_mt9t112(client); + + if (index >= priv->num_formats) return -EINVAL; *code = mt9t112_cfmts[index].code; @@ -1056,10 +1062,12 @@ static int mt9t112_camera_probe(struct i2c_client *client) case 0x2680: devname = "mt9t111"; priv->model = V4L2_IDENT_MT9T111; + priv->num_formats = 1; break; case 0x2682: devname = "mt9t112"; priv->model = V4L2_IDENT_MT9T112; + priv->num_formats = ARRAY_SIZE(mt9t112_cfmts); break; default: dev_err(&client->dev, "Product ID error %04x\n", chipid); From 0dfa1c5da3e4b6849d40f4c3fc43212b6359a09d Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 17 Dec 2012 09:53:35 +0100 Subject: [PATCH 2523/4607] target: Export SPC inquiry emulation Some target drivers might need to access the inquiry data directly, without sending out the actual command. So export these functions. Signed-off-by: Hannes Reinecke Cc: Nicholas Bellinger Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_spc.c | 8 +++++--- include/target/target_core_backend.h | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 2d88f087d961..fa2447004006 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -66,8 +66,8 @@ static void spc_fill_alua_data(struct se_port *port, unsigned char *buf) spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock); } -static sense_reason_t -spc_emulate_inquiry_std(struct se_cmd *cmd, char *buf) +sense_reason_t +spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char *buf) { struct se_lun *lun = cmd->se_lun; struct se_device *dev = cmd->se_dev; @@ -104,6 +104,7 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, char *buf) return 0; } +EXPORT_SYMBOL(spc_emulate_inquiry_std); /* unit serial number */ static sense_reason_t @@ -160,7 +161,7 @@ static void spc_parse_naa_6h_vendor_specific(struct se_device *dev, * Device identification VPD, for a complete list of * DESIGNATOR TYPEs see spc4r17 Table 459. */ -static sense_reason_t +sense_reason_t spc_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf) { struct se_device *dev = cmd->se_dev; @@ -404,6 +405,7 @@ check_scsi_name: buf[3] = (len & 0xff); /* Page Length for VPD 0x83 */ return 0; } +EXPORT_SYMBOL(spc_emulate_evpd_83); /* Extended INQUIRY Data VPD Page */ static sense_reason_t diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index 507910992c59..819b0fc45215 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h @@ -53,6 +53,8 @@ void target_complete_cmd(struct se_cmd *, u8); sense_reason_t spc_parse_cdb(struct se_cmd *cmd, unsigned int *size); sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd); sector_t spc_get_write_same_sectors(struct se_cmd *cmd); +sense_reason_t spc_emulate_inquiry_std(struct se_cmd *, unsigned char *); +sense_reason_t spc_emulate_evpd_83(struct se_cmd *, unsigned char *); sense_reason_t sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops); u32 sbc_get_device_rev(struct se_device *dev); From 6f15667e21e40ef14005699610723a13cfb26155 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 2 Jan 2013 12:48:00 -0800 Subject: [PATCH 2524/4607] target: Remove useless if statement We do the same thing no matter which way the test goes, so just remove the test and do what we're going to do. The debug messages printed the wrong value of CMD_T_ACTIVE and don't seem particularly useful, remove them too. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_tmr.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index c6e0293ffdb0..d0b4dd95b91e 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -331,18 +331,6 @@ static void core_tmr_drain_state_list( fe_count = atomic_read(&cmd->t_fe_count); - if (!(cmd->transport_state & CMD_T_ACTIVE)) { - pr_debug("LUN_RESET: got CMD_T_ACTIVE for" - " cdb: %p, t_fe_count: %d dev: %p\n", cmd, - fe_count, dev); - cmd->transport_state |= CMD_T_ABORTED; - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - - core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count); - continue; - } - pr_debug("LUN_RESET: Got !CMD_T_ACTIVE for cdb: %p," - " t_fe_count: %d dev: %p\n", cmd, fe_count, dev); cmd->transport_state |= CMD_T_ABORTED; spin_unlock_irqrestore(&cmd->t_state_lock, flags); From d09816ae8fc05322b4e37a589537b4ecdca28a0d Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Wed, 2 Jan 2013 12:48:01 -0800 Subject: [PATCH 2525/4607] target: Remove never-used TMR_FABRIC_TMR enum value Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- include/target/target_core_base.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 663e34a5383f..4fa0f1038360 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -211,7 +211,6 @@ enum tcm_tmreq_table { TMR_LUN_RESET = 5, TMR_TARGET_WARM_RESET = 6, TMR_TARGET_COLD_RESET = 7, - TMR_FABRIC_TMR = 255, }; /* fabric independent task management response values */ From 8f67835f1e389978bb0809d5e528961986aa2a69 Mon Sep 17 00:00:00 2001 From: Martin Svec Date: Tue, 15 Jan 2013 12:43:35 -0800 Subject: [PATCH 2526/4607] target/rd: improve sg_table lookup scalability Sequential scan of rd_dev->sg_table_array in rd_get_sg_table is a serious I/O performance bottleneck for large rd LUNs. Fix this by computing the sg_table index directly from page offset because all sg_tables (except the last one) have the same number of pages. Tested with 90 GiB rd_mcp LUN, where the patch improved maximal random R/W IOPS by more than 100-150%, depending on actual hardware and SAN setup. Signed-off-by: Martin Svec Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_rd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index 0457de362e68..b0fff52c990e 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -256,10 +256,12 @@ static void rd_free_device(struct se_device *dev) static struct rd_dev_sg_table *rd_get_sg_table(struct rd_dev *rd_dev, u32 page) { - u32 i; struct rd_dev_sg_table *sg_table; + u32 i, sg_per_table = (RD_MAX_ALLOCATION_SIZE / + sizeof(struct scatterlist)); - for (i = 0; i < rd_dev->sg_table_count; i++) { + i = page / sg_per_table; + if (i < rd_dev->sg_table_count) { sg_table = &rd_dev->sg_table_array[i]; if ((sg_table->page_start_offset <= page) && (sg_table->page_end_offset >= page)) From 703d641d87034629f8b0da94334034ed5d805b36 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 18 Jan 2013 16:05:12 +0300 Subject: [PATCH 2527/4607] target: change sprintf to snprintf in transport_dump_vpd_ident "buf" is 128 characters and "vpd->device_identifier" is 256. It makes the static checkers complain. Also bump VPD_TMP_BUF_SIZE to match INQUIRY_VPD_DEVICE_IDENTIFIER_LEN. Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_transport.c | 9 ++++++--- include/target/target_core_base.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index bd587b70661a..96b64d57ebbb 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -907,15 +907,18 @@ int transport_dump_vpd_ident( switch (vpd->device_identifier_code_set) { case 0x01: /* Binary */ - sprintf(buf, "T10 VPD Binary Device Identifier: %s\n", + snprintf(buf, sizeof(buf), + "T10 VPD Binary Device Identifier: %s\n", &vpd->device_identifier[0]); break; case 0x02: /* ASCII */ - sprintf(buf, "T10 VPD ASCII Device Identifier: %s\n", + snprintf(buf, sizeof(buf), + "T10 VPD ASCII Device Identifier: %s\n", &vpd->device_identifier[0]); break; case 0x03: /* UTF-8 */ - sprintf(buf, "T10 VPD UTF-8 Device Identifier: %s\n", + snprintf(buf, sizeof(buf), + "T10 VPD UTF-8 Device Identifier: %s\n", &vpd->device_identifier[0]); break; default: diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 4fa0f1038360..199b0ad1a55e 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -44,7 +44,7 @@ /* Used by core_alua_store_tg_pt_gp_info() and target_core_alua_tg_pt_gp_show_attr_members() */ #define TG_PT_GROUP_NAME_BUF 256 /* Used to parse VPD into struct t10_vpd */ -#define VPD_TMP_BUF_SIZE 128 +#define VPD_TMP_BUF_SIZE 254 /* Used by transport_generic_cmd_sequencer() */ #define READ_BLOCK_LEN 6 #define READ_CAP_LEN 8 From 9d6064a347a8e15451cbdf6286542d402c9da24c Mon Sep 17 00:00:00 2001 From: Asias He Date: Sun, 6 Jan 2013 14:36:13 +0800 Subject: [PATCH 2528/4607] tcm_vhost: Use llist for cmd completion list This drops the cmd completion list spin lock and makes the cmd completion queue lock-less. Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 46 +++++++++++---------------------------- drivers/vhost/tcm_vhost.h | 2 +- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 22321cf84fbe..77d760bb2e44 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -47,6 +47,7 @@ #include #include /* TODO vhost.h currently depends on this */ #include +#include #include "vhost.c" #include "vhost.h" @@ -64,8 +65,7 @@ struct vhost_scsi { struct vhost_virtqueue vqs[3]; struct vhost_work vs_completion_work; /* cmd completion work item */ - struct list_head vs_completion_list; /* cmd completion queue */ - spinlock_t vs_completion_lock; /* protects s_completion_list */ + struct llist_head vs_completion_list; /* cmd completion queue */ }; /* Local pointer to allocated TCM configfs fabric module */ @@ -301,9 +301,7 @@ static void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *tv_cmd) { struct vhost_scsi *vs = tv_cmd->tvc_vhost; - spin_lock_bh(&vs->vs_completion_lock); - list_add_tail(&tv_cmd->tvc_completion_list, &vs->vs_completion_list); - spin_unlock_bh(&vs->vs_completion_lock); + llist_add(&tv_cmd->tvc_completion_list, &vs->vs_completion_list); vhost_work_queue(&vs->dev, &vs->vs_completion_work); } @@ -347,27 +345,6 @@ static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *tv_cmd) kfree(tv_cmd); } -/* Dequeue a command from the completion list */ -static struct tcm_vhost_cmd *vhost_scsi_get_cmd_from_completion( - struct vhost_scsi *vs) -{ - struct tcm_vhost_cmd *tv_cmd = NULL; - - spin_lock_bh(&vs->vs_completion_lock); - if (list_empty(&vs->vs_completion_list)) { - spin_unlock_bh(&vs->vs_completion_lock); - return NULL; - } - - list_for_each_entry(tv_cmd, &vs->vs_completion_list, - tvc_completion_list) { - list_del(&tv_cmd->tvc_completion_list); - break; - } - spin_unlock_bh(&vs->vs_completion_lock); - return tv_cmd; -} - /* Fill in status and signal that we are done processing this command * * This is scheduled in the vhost work queue so we are called with the owner @@ -377,12 +354,18 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) { struct vhost_scsi *vs = container_of(work, struct vhost_scsi, vs_completion_work); + struct virtio_scsi_cmd_resp v_rsp; struct tcm_vhost_cmd *tv_cmd; + struct llist_node *llnode; + struct se_cmd *se_cmd; + int ret; - while ((tv_cmd = vhost_scsi_get_cmd_from_completion(vs))) { - struct virtio_scsi_cmd_resp v_rsp; - struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd; - int ret; + llnode = llist_del_all(&vs->vs_completion_list); + while (llnode) { + tv_cmd = llist_entry(llnode, struct tcm_vhost_cmd, + tvc_completion_list); + llnode = llist_next(llnode); + se_cmd = &tv_cmd->tvc_se_cmd; pr_debug("%s tv_cmd %p resid %u status %#02x\n", __func__, tv_cmd, se_cmd->residual_count, se_cmd->scsi_status); @@ -426,7 +409,6 @@ static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( pr_err("Unable to allocate struct tcm_vhost_cmd\n"); return ERR_PTR(-ENOMEM); } - INIT_LIST_HEAD(&tv_cmd->tvc_completion_list); tv_cmd->tvc_tag = v_req->tag; tv_cmd->tvc_task_attr = v_req->task_attr; tv_cmd->tvc_exp_data_len = exp_data_len; @@ -857,8 +839,6 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) return -ENOMEM; vhost_work_init(&s->vs_completion_work, vhost_scsi_complete_cmd_work); - INIT_LIST_HEAD(&s->vs_completion_list); - spin_lock_init(&s->vs_completion_lock); s->vqs[VHOST_SCSI_VQ_CTL].handle_kick = vhost_scsi_ctl_handle_kick; s->vqs[VHOST_SCSI_VQ_EVT].handle_kick = vhost_scsi_evt_handle_kick; diff --git a/drivers/vhost/tcm_vhost.h b/drivers/vhost/tcm_vhost.h index 7e87c63ecbcd..47ee80b3adee 100644 --- a/drivers/vhost/tcm_vhost.h +++ b/drivers/vhost/tcm_vhost.h @@ -34,7 +34,7 @@ struct tcm_vhost_cmd { /* Sense buffer that will be mapped into outgoing status */ unsigned char tvc_sense_buf[TRANSPORT_SENSE_BUFFER]; /* Completed commands list, serviced from vhost worker thread */ - struct list_head tvc_completion_list; + struct llist_node tvc_completion_list; }; struct tcm_vhost_nexus { From 765b34fdc1507add8fd6f1007410e7a375d0f89c Mon Sep 17 00:00:00 2001 From: Asias He Date: Tue, 22 Jan 2013 11:20:25 +0800 Subject: [PATCH 2529/4607] tcm_vhost: Introduce iov_num_pages Add a helper to calculate the number of pages needed for a iov entry. (nab: Drop unnecessary inline) Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 77d760bb2e44..defc28595656 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -77,6 +77,12 @@ static struct workqueue_struct *tcm_vhost_workqueue; static DEFINE_MUTEX(tcm_vhost_mutex); static LIST_HEAD(tcm_vhost_list); +static int iov_num_pages(struct iovec *iov) +{ + return (PAGE_ALIGN((unsigned long)iov->iov_base + iov->iov_len) - + ((unsigned long)iov->iov_base & PAGE_MASK)) >> PAGE_SHIFT; +} + static int tcm_vhost_check_true(struct se_portal_group *se_tpg) { return 1; From f3158f362ca8b77b24c5de86cbec347ab2bb6430 Mon Sep 17 00:00:00 2001 From: Asias He Date: Tue, 22 Jan 2013 11:20:26 +0800 Subject: [PATCH 2530/4607] tcm_vhost: Use iov_num_pages to calculate sgl_count Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index defc28595656..62058d7a562f 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -479,11 +479,9 @@ static int vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd, * Find out how long sglist needs to be */ sgl_count = 0; - for (i = 0; i < niov; i++) { - sgl_count += (((uintptr_t)iov[i].iov_base + iov[i].iov_len + - PAGE_SIZE - 1) >> PAGE_SHIFT) - - ((uintptr_t)iov[i].iov_base >> PAGE_SHIFT); - } + for (i = 0; i < niov; i++) + sgl_count += iov_num_pages(&iov[i]); + /* TODO overflow checking */ sg = kmalloc(sizeof(tv_cmd->tvc_sgl[0]) * sgl_count, GFP_ATOMIC); From 1810053e8db3fd65f039229bde9e420278994300 Mon Sep 17 00:00:00 2001 From: Asias He Date: Tue, 22 Jan 2013 11:20:27 +0800 Subject: [PATCH 2531/4607] tcm_vhost: Optimize gup in vhost_scsi_map_to_sgl We can get all the pages in one time instead of calling gup N times. Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 58 +++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 62058d7a562f..704e4f674776 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -430,40 +430,47 @@ static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( * Returns the number of scatterlist entries used or -errno on error. */ static int vhost_scsi_map_to_sgl(struct scatterlist *sgl, - unsigned int sgl_count, void __user *ptr, size_t len, int write) + unsigned int sgl_count, struct iovec *iov, int write) { + unsigned int npages = 0, pages_nr, offset, nbytes; struct scatterlist *sg = sgl; - unsigned int npages = 0; - int ret; + void __user *ptr = iov->iov_base; + size_t len = iov->iov_len; + struct page **pages; + int ret, i; + + pages_nr = iov_num_pages(iov); + if (pages_nr > sgl_count) + return -ENOBUFS; + + pages = kmalloc(pages_nr * sizeof(struct page *), GFP_KERNEL); + if (!pages) + return -ENOMEM; + + ret = get_user_pages_fast((unsigned long)ptr, pages_nr, write, pages); + /* No pages were pinned */ + if (ret < 0) + goto out; + /* Less pages pinned than wanted */ + if (ret != pages_nr) { + for (i = 0; i < ret; i++) + put_page(pages[i]); + ret = -EFAULT; + goto out; + } while (len > 0) { - struct page *page; - unsigned int offset = (uintptr_t)ptr & ~PAGE_MASK; - unsigned int nbytes = min_t(unsigned int, - PAGE_SIZE - offset, len); - - if (npages == sgl_count) { - ret = -ENOBUFS; - goto err; - } - - ret = get_user_pages_fast((unsigned long)ptr, 1, write, &page); - BUG_ON(ret == 0); /* we should either get our page or fail */ - if (ret < 0) - goto err; - - sg_set_page(sg, page, nbytes, offset); + offset = (uintptr_t)ptr & ~PAGE_MASK; + nbytes = min_t(unsigned int, PAGE_SIZE - offset, len); + sg_set_page(sg, pages[npages], nbytes, offset); ptr += nbytes; len -= nbytes; sg++; npages++; } - return npages; -err: - /* Put pages that we hold */ - for (sg = sgl; sg != &sgl[npages]; sg++) - put_page(sg_page(sg)); +out: + kfree(pages); return ret; } @@ -496,8 +503,7 @@ static int vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd, pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count); for (i = 0; i < niov; i++) { - ret = vhost_scsi_map_to_sgl(sg, sgl_count, iov[i].iov_base, - iov[i].iov_len, write); + ret = vhost_scsi_map_to_sgl(sg, sgl_count, &iov[i], write); if (ret < 0) { for (i = 0; i < tv_cmd->tvc_sgl_count; i++) put_page(sg_page(&tv_cmd->tvc_sgl[i])); From 47de201c73fbe435e7b635fa0eb812c7ce68be43 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 7 Jan 2013 09:51:21 -0300 Subject: [PATCH 2532/4607] [media] drivers/media/platform/soc_camera/pxa_camera.c: use devm_ functions This patch uses various devm_ functions for data that is allocated in the probe function of a platform driver and is only freed in the remove function. This also fixes a checkpatch warning, removing a space before a \n in a string. Signed-off-by: Julia Lawall Signed-off-by: Mauro Carvalho Chehab --- .../media/platform/soc_camera/pxa_camera.c | 65 +++++-------------- 1 file changed, 15 insertions(+), 50 deletions(-) diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 9c97f7e985e1..395e2e043615 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -1661,23 +1661,18 @@ static int pxa_camera_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (!res || irq < 0) { - err = -ENODEV; - goto exit; - } + if (!res || irq < 0) + return -ENODEV; - pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); if (!pcdev) { dev_err(&pdev->dev, "Could not allocate pcdev\n"); - err = -ENOMEM; - goto exit; + return -ENOMEM; } - pcdev->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(pcdev->clk)) { - err = PTR_ERR(pcdev->clk); - goto exit_kfree; - } + pcdev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pcdev->clk)) + return PTR_ERR(pcdev->clk); pcdev->res = res; @@ -1715,17 +1710,9 @@ static int pxa_camera_probe(struct platform_device *pdev) /* * Request the regions. */ - if (!request_mem_region(res->start, resource_size(res), - PXA_CAM_DRV_NAME)) { - err = -EBUSY; - goto exit_clk; - } - - base = ioremap(res->start, resource_size(res)); - if (!base) { - err = -ENOMEM; - goto exit_release; - } + base = devm_request_and_ioremap(&pdev->dev, res); + if (!base) + return -ENOMEM; pcdev->irq = irq; pcdev->base = base; @@ -1734,7 +1721,7 @@ static int pxa_camera_probe(struct platform_device *pdev) pxa_camera_dma_irq_y, pcdev); if (err < 0) { dev_err(&pdev->dev, "Can't request DMA for Y\n"); - goto exit_iounmap; + return err; } pcdev->dma_chans[0] = err; dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]); @@ -1762,10 +1749,10 @@ static int pxa_camera_probe(struct platform_device *pdev) DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD; /* request irq */ - err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME, - pcdev); + err = devm_request_irq(&pdev->dev, pcdev->irq, pxa_camera_irq, 0, + PXA_CAM_DRV_NAME, pcdev); if (err) { - dev_err(&pdev->dev, "Camera interrupt register failed \n"); + dev_err(&pdev->dev, "Camera interrupt register failed\n"); goto exit_free_dma; } @@ -1777,27 +1764,16 @@ static int pxa_camera_probe(struct platform_device *pdev) err = soc_camera_host_register(&pcdev->soc_host); if (err) - goto exit_free_irq; + goto exit_free_dma; return 0; -exit_free_irq: - free_irq(pcdev->irq, pcdev); exit_free_dma: pxa_free_dma(pcdev->dma_chans[2]); exit_free_dma_u: pxa_free_dma(pcdev->dma_chans[1]); exit_free_dma_y: pxa_free_dma(pcdev->dma_chans[0]); -exit_iounmap: - iounmap(base); -exit_release: - release_mem_region(res->start, resource_size(res)); -exit_clk: - clk_put(pcdev->clk); -exit_kfree: - kfree(pcdev); -exit: return err; } @@ -1806,24 +1782,13 @@ static int pxa_camera_remove(struct platform_device *pdev) struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); struct pxa_camera_dev *pcdev = container_of(soc_host, struct pxa_camera_dev, soc_host); - struct resource *res; - - clk_put(pcdev->clk); pxa_free_dma(pcdev->dma_chans[0]); pxa_free_dma(pcdev->dma_chans[1]); pxa_free_dma(pcdev->dma_chans[2]); - free_irq(pcdev->irq, pcdev); soc_camera_host_unregister(soc_host); - iounmap(pcdev->base); - - res = pcdev->res; - release_mem_region(res->start, resource_size(res)); - - kfree(pcdev); - dev_info(&pdev->dev, "PXA Camera driver unloaded\n"); return 0; From 1be2956d30b2b6200ebb26ffb758ed3c8071303c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 24 Jan 2013 10:06:37 +0300 Subject: [PATCH 2533/4607] iscsi-target: make some temporary buffers larger My static checker complains because we use sprintf() to print some unsigned ints into 10 byte buffers. In theory unsigned ints can take 10 characters and we need another for the terminator. Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_parameters.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index d89164287d00..ca2be406f141 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -1095,11 +1095,11 @@ static int iscsi_check_acceptor_state(struct iscsi_param *param, char *value, SET_PSTATE_REPLY_OPTIONAL(param); } } else if (IS_TYPE_NUMBER(param)) { - char *tmpptr, buf[10]; + char *tmpptr, buf[11]; u32 acceptor_value = simple_strtoul(param->value, &tmpptr, 0); u32 proposer_value = simple_strtoul(value, &tmpptr, 0); - memset(buf, 0, 10); + memset(buf, 0, sizeof(buf)); if (!strcmp(param->name, MAXCONNECTIONS) || !strcmp(param->name, MAXBURSTLENGTH) || @@ -1503,8 +1503,8 @@ static int iscsi_enforce_integrity_rules( FirstBurstLength = simple_strtoul(param->value, &tmpptr, 0); if (FirstBurstLength > MaxBurstLength) { - char tmpbuf[10]; - memset(tmpbuf, 0, 10); + char tmpbuf[11]; + memset(tmpbuf, 0, sizeof(tmpbuf)); sprintf(tmpbuf, "%u", MaxBurstLength); if (iscsi_update_param_value(param, tmpbuf)) return -1; From d0c8b259f8970d39354c1966853363345d401330 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Tue, 29 Jan 2013 22:10:06 -0800 Subject: [PATCH 2534/4607] target/iblock: Use backend REQ_FLUSH hint for WriteCacheEnabled status This patch allows IBLOCK to check block hints in request_queue->flush_flags when reporting current backend device WriteCacheEnabled status to a remote SCSI initiator port. This is done via a se_subsystem_api->get_write_cache() call instead of a backend se_device creation time flag, as we expect REQ_FLUSH bits to possibly change from an underlying blk_queue_flush() by the SCSI disk driver, or internal raw struct block_device driver usage. Also go ahead and update iblock_execute_rw() bio I/O path code to use REQ_FLUSH + REQ_FUA hints when determining WRITE_FUA usage, and make SPC emulation code use a spc_check_dev_wce() helper to handle both types of cases for virtual backend subsystem drivers. (asias: Drop unnecessary comparsion operators) Reported-by: majianpeng Cc: majianpeng Cc: Christoph Hellwig Cc: Jens Axboe Cc: James Bottomley Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_device.c | 6 ++++++ drivers/target/target_core_iblock.c | 31 +++++++++++++++++++++------- drivers/target/target_core_spc.c | 21 ++++++++++++++++--- include/target/target_core_backend.h | 1 + 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index f2aa7543d20a..d10cbed23472 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -772,6 +772,12 @@ int se_dev_set_emulate_write_cache(struct se_device *dev, int flag) pr_err("emulate_write_cache not supported for pSCSI\n"); return -EINVAL; } + if (dev->transport->get_write_cache) { + pr_warn("emulate_write_cache cannot be changed when underlying" + " HW reports WriteCacheEnabled, ignoring request\n"); + return 0; + } + dev->dev_attrib.emulate_write_cache = flag; pr_debug("dev[%p]: SE Device WRITE_CACHE_EMULATION flag: %d\n", dev, dev->dev_attrib.emulate_write_cache); diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index b526d23dcd4f..2f74e17ad3e6 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -154,6 +154,7 @@ static int iblock_configure_device(struct se_device *dev) if (blk_queue_nonrot(q)) dev->dev_attrib.is_nonrot = 1; + return 0; out_free_bioset: @@ -654,20 +655,24 @@ iblock_execute_rw(struct se_cmd *cmd) u32 sg_num = sgl_nents; sector_t block_lba; unsigned bio_cnt; - int rw; + int rw = 0; int i; if (data_direction == DMA_TO_DEVICE) { + struct iblock_dev *ib_dev = IBLOCK_DEV(dev); + struct request_queue *q = bdev_get_queue(ib_dev->ibd_bd); /* - * Force data to disk if we pretend to not have a volatile - * write cache, or the initiator set the Force Unit Access bit. + * Force writethrough using WRITE_FUA if a volatile write cache + * is not enabled, or if initiator set the Force Unit Access bit. */ - if (dev->dev_attrib.emulate_write_cache == 0 || - (dev->dev_attrib.emulate_fua_write > 0 && - (cmd->se_cmd_flags & SCF_FUA))) - rw = WRITE_FUA; - else + if (q->flush_flags & REQ_FUA) { + if (cmd->se_cmd_flags & SCF_FUA) + rw = WRITE_FUA; + else if (!(q->flush_flags & REQ_FLUSH)) + rw = WRITE_FUA; + } else { rw = WRITE; + } } else { rw = READ; } @@ -774,6 +779,15 @@ iblock_parse_cdb(struct se_cmd *cmd) return sbc_parse_cdb(cmd, &iblock_sbc_ops); } +bool iblock_get_write_cache(struct se_device *dev) +{ + struct iblock_dev *ib_dev = IBLOCK_DEV(dev); + struct block_device *bd = ib_dev->ibd_bd; + struct request_queue *q = bdev_get_queue(bd); + + return q->flush_flags & REQ_FLUSH; +} + static struct se_subsystem_api iblock_template = { .name = "iblock", .inquiry_prod = "IBLOCK", @@ -790,6 +804,7 @@ static struct se_subsystem_api iblock_template = { .show_configfs_dev_params = iblock_show_configfs_dev_params, .get_device_type = sbc_get_device_type, .get_blocks = iblock_get_blocks, + .get_write_cache = iblock_get_write_cache, }; static int __init iblock_module_init(void) diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index fa2447004006..803339516fb9 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -407,16 +407,31 @@ check_scsi_name: } EXPORT_SYMBOL(spc_emulate_evpd_83); +static bool +spc_check_dev_wce(struct se_device *dev) +{ + bool wce = false; + + if (dev->transport->get_write_cache) + wce = dev->transport->get_write_cache(dev); + else if (dev->dev_attrib.emulate_write_cache > 0) + wce = true; + + return wce; +} + /* Extended INQUIRY Data VPD Page */ static sense_reason_t spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf) { + struct se_device *dev = cmd->se_dev; + buf[3] = 0x3c; /* Set HEADSUP, ORDSUP, SIMPSUP */ buf[5] = 0x07; /* If WriteCache emulation is enabled, set V_SUP */ - if (cmd->se_dev->dev_attrib.emulate_write_cache > 0) + if (spc_check_dev_wce(dev)) buf[6] = 0x01; return 0; } @@ -766,7 +781,7 @@ static int spc_modesense_caching(struct se_device *dev, u8 pc, u8 *p) if (pc == 1) goto out; - if (dev->dev_attrib.emulate_write_cache > 0) + if (spc_check_dev_wce(dev)) p[2] = 0x04; /* Write Cache Enable */ p[12] = 0x20; /* Disabled Read Ahead */ @@ -878,7 +893,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY))) spc_modesense_write_protect(&buf[length], type); - if ((dev->dev_attrib.emulate_write_cache > 0) && + if ((spc_check_dev_wce(dev)) && (dev->dev_attrib.emulate_fua_write > 0)) spc_modesense_dpofua(&buf[length], type); diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index 819b0fc45215..f9a80169775d 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h @@ -35,6 +35,7 @@ struct se_subsystem_api { u32 (*get_device_type)(struct se_device *); sector_t (*get_blocks)(struct se_device *); unsigned char *(*get_sense_buffer)(struct se_cmd *); + bool (*get_write_cache)(struct se_device *); }; struct sbc_ops { From 07ea81b6f7d6345e146245c4964640a5a27b5cc5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 31 Jan 2013 11:17:54 +0300 Subject: [PATCH 2535/4607] target: don't always say "ipv6" as address type "lstat->last_intr_fail_ip_addr" is an array inside the "lstat" struct. It's never NULL so we always print "ipv6\n" here. The test should be "if (lstat->last_intr_fail_ip_family == AF_INET6)". We don't need the temporary buffer either. We could print directly into "page". Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_stat.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_stat.c b/drivers/target/iscsi/iscsi_target_stat.c index 421d6947dc64..54a6f3834cf0 100644 --- a/drivers/target/iscsi/iscsi_target_stat.c +++ b/drivers/target/iscsi/iscsi_target_stat.c @@ -410,14 +410,16 @@ static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr_type( struct iscsi_tiqn *tiqn = container_of(igrps, struct iscsi_tiqn, tiqn_stat_grps); struct iscsi_login_stats *lstat = &tiqn->login_stats; - unsigned char buf[8]; + int ret; spin_lock(&lstat->lock); - snprintf(buf, 8, "%s", (lstat->last_intr_fail_ip_addr != NULL) ? - "ipv6" : "ipv4"); + if (lstat->last_intr_fail_ip_family == AF_INET6) + ret = snprintf(page, PAGE_SIZE, "ipv6\n"); + else + ret = snprintf(page, PAGE_SIZE, "ipv4\n"); spin_unlock(&lstat->lock); - return snprintf(page, PAGE_SIZE, "%s\n", buf); + return ret; } ISCSI_STAT_TGT_ATTR_RO(fail_intr_addr_type); From 0e48e7a5a345a727d5fd7a06bd6a9e6a67eae2bd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 31 Jan 2013 11:19:09 +0300 Subject: [PATCH 2536/4607] target: don't truncate the fail intr address The temporary buffer was only 32 characters but ->last_intr_fail_ip_addr is a 48 character buffer. We don't need to use a temporary buffer at all, we can just print directly to "page". Signed-off-by: Dan Carpenter Signed-off-by: Nicholas Bellinger --- drivers/target/iscsi/iscsi_target_stat.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_stat.c b/drivers/target/iscsi/iscsi_target_stat.c index 54a6f3834cf0..464b4206a51e 100644 --- a/drivers/target/iscsi/iscsi_target_stat.c +++ b/drivers/target/iscsi/iscsi_target_stat.c @@ -429,16 +429,19 @@ static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr( struct iscsi_tiqn *tiqn = container_of(igrps, struct iscsi_tiqn, tiqn_stat_grps); struct iscsi_login_stats *lstat = &tiqn->login_stats; - unsigned char buf[32]; + int ret; spin_lock(&lstat->lock); - if (lstat->last_intr_fail_ip_family == AF_INET6) - snprintf(buf, 32, "[%s]", lstat->last_intr_fail_ip_addr); - else - snprintf(buf, 32, "%s", lstat->last_intr_fail_ip_addr); + if (lstat->last_intr_fail_ip_family == AF_INET6) { + ret = snprintf(page, PAGE_SIZE, "[%s]\n", + lstat->last_intr_fail_ip_addr); + } else { + ret = snprintf(page, PAGE_SIZE, "%s\n", + lstat->last_intr_fail_ip_addr); + } spin_unlock(&lstat->lock); - return snprintf(page, PAGE_SIZE, "%s\n", buf); + return ret; } ISCSI_STAT_TGT_ATTR_RO(fail_intr_addr); From adfa9570a56c3dbfc2a28baab77ff6f0b8f480d3 Mon Sep 17 00:00:00 2001 From: Tregaron Bayly Date: Thu, 31 Jan 2013 15:30:24 -0700 Subject: [PATCH 2537/4607] target: Add device attribute to expose config_item_name for INQUIRY model This patch changes LIO to use the configfs backend device name as the model if you echo '1' to an individual device's emulate_model_alias attribute. This is a valid operation only on devices with an export count of 0. Signed-off-by: Tregaron Bayly Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_configfs.c | 4 +++ drivers/target/target_core_device.c | 39 +++++++++++++++++++++++++++ drivers/target/target_core_internal.h | 1 + include/target/target_core_base.h | 3 +++ 4 files changed, 47 insertions(+) diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 4efb61b8d001..43b7ac6c5b1c 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -609,6 +609,9 @@ static struct target_core_dev_attrib_attribute \ __CONFIGFS_EATTR_RO(_name, \ target_core_dev_show_attr_##_name); +DEF_DEV_ATTRIB(emulate_model_alias); +SE_DEV_ATTR(emulate_model_alias, S_IRUGO | S_IWUSR); + DEF_DEV_ATTRIB(emulate_dpo); SE_DEV_ATTR(emulate_dpo, S_IRUGO | S_IWUSR); @@ -681,6 +684,7 @@ SE_DEV_ATTR(max_write_same_len, S_IRUGO | S_IWUSR); CONFIGFS_EATTR_OPS(target_core_dev_attrib, se_dev_attrib, da_group); static struct configfs_attribute *target_core_dev_attrib_attrs[] = { + &target_core_dev_attrib_emulate_model_alias.attr, &target_core_dev_attrib_emulate_dpo.attr, &target_core_dev_attrib_emulate_fua_write.attr, &target_core_dev_attrib_emulate_fua_read.attr, diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index d10cbed23472..6fb82f1abe5c 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -713,6 +713,44 @@ int se_dev_set_max_write_same_len( return 0; } +static void dev_set_t10_wwn_model_alias(struct se_device *dev) +{ + const char *configname; + + configname = config_item_name(&dev->dev_group.cg_item); + if (strlen(configname) >= 16) { + pr_warn("dev[%p]: Backstore name '%s' is too long for " + "INQUIRY_MODEL, truncating to 16 bytes\n", dev, + configname); + } + snprintf(&dev->t10_wwn.model[0], 16, "%s", configname); +} + +int se_dev_set_emulate_model_alias(struct se_device *dev, int flag) +{ + if (dev->export_count) { + pr_err("dev[%p]: Unable to change model alias" + " while export_count is %d\n", + dev, dev->export_count); + return -EINVAL; + } + + if (flag != 0 && flag != 1) { + pr_err("Illegal value %d\n", flag); + return -EINVAL; + } + + if (flag) { + dev_set_t10_wwn_model_alias(dev); + } else { + strncpy(&dev->t10_wwn.model[0], + dev->transport->inquiry_prod, 16); + } + dev->dev_attrib.emulate_model_alias = flag; + + return 0; +} + int se_dev_set_emulate_dpo(struct se_device *dev, int flag) { if (flag != 0 && flag != 1) { @@ -1396,6 +1434,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) dev->t10_alua.t10_dev = dev; dev->dev_attrib.da_dev = dev; + dev->dev_attrib.emulate_model_alias = DA_EMULATE_MODEL_ALIAS; dev->dev_attrib.emulate_dpo = DA_EMULATE_DPO; dev->dev_attrib.emulate_fua_write = DA_EMULATE_FUA_WRITE; dev->dev_attrib.emulate_fua_read = DA_EMULATE_FUA_READ; diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 93e9c1f580b0..fdc51f8301a1 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -25,6 +25,7 @@ int se_dev_set_max_unmap_block_desc_count(struct se_device *, u32); int se_dev_set_unmap_granularity(struct se_device *, u32); int se_dev_set_unmap_granularity_alignment(struct se_device *, u32); int se_dev_set_max_write_same_len(struct se_device *, u32); +int se_dev_set_emulate_model_alias(struct se_device *, int); int se_dev_set_emulate_dpo(struct se_device *, int); int se_dev_set_emulate_fua_write(struct se_device *, int); int se_dev_set_emulate_fua_read(struct se_device *, int); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 199b0ad1a55e..df14dce59191 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -75,6 +75,8 @@ #define DA_MAX_WRITE_SAME_LEN 0 /* Default max transfer length */ #define DA_FABRIC_MAX_SECTORS 8192 +/* Use a model alias based on the configfs backend device name */ +#define DA_EMULATE_MODEL_ALIAS 0 /* Emulation for Direct Page Out */ #define DA_EMULATE_DPO 0 /* Emulation for Forced Unit Access WRITEs */ @@ -591,6 +593,7 @@ struct se_dev_entry { }; struct se_dev_attrib { + int emulate_model_alias; int emulate_dpo; int emulate_fua_write; int emulate_fua_read; From fd51625d6331c80cd249bd201b012ed5fe4f0476 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 3 Jan 2013 15:35:56 -0300 Subject: [PATCH 2538/4607] [media] sh_vou: Use video_drvdata() Replace video_devdata() followed by video_get_drvdata() calls with video_drvdata(). Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 57 ++++++++++++--------------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index ab5bca40a5b3..e969fea3c122 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -420,8 +420,7 @@ static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv, static int sh_vou_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *fmt) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); @@ -672,8 +671,7 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std) static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *fmt) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct v4l2_pix_format *pix = &fmt->fmt.pix; unsigned int img_height_max; int pix_idx; @@ -830,8 +828,7 @@ static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) static int sh_vou_streamon(struct file *file, void *priv, enum v4l2_buf_type buftype) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; int ret; @@ -849,8 +846,7 @@ static int sh_vou_streamon(struct file *file, void *priv, static int sh_vou_streamoff(struct file *file, void *priv, enum v4l2_buf_type buftype) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); @@ -882,13 +878,12 @@ static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt) static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); int ret; dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, *std_id); - if (*std_id & ~vdev->tvnorms) + if (*std_id & ~vou_dev->vdev->tvnorms) return -EINVAL; ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, @@ -910,8 +905,7 @@ static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id) static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); @@ -922,8 +916,7 @@ static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std) static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); @@ -937,8 +930,7 @@ static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a) static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) { struct v4l2_crop a_writable = *a; - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct v4l2_rect *rect = &a_writable.c; struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT}; struct v4l2_pix_format *pix = &vou_dev->pix; @@ -1161,8 +1153,7 @@ static int sh_vou_hw_init(struct sh_vou_device *vou_dev) /* File operations */ static int sh_vou_open(struct file *file) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file), GFP_KERNEL); @@ -1179,11 +1170,11 @@ static int sh_vou_open(struct file *file) int ret; /* First open */ vou_dev->status = SH_VOU_INITIALISING; - pm_runtime_get_sync(vdev->v4l2_dev->dev); + pm_runtime_get_sync(vou_dev->v4l2_dev.dev); ret = sh_vou_hw_init(vou_dev); if (ret < 0) { atomic_dec(&vou_dev->use_count); - pm_runtime_put(vdev->v4l2_dev->dev); + pm_runtime_put(vou_dev->v4l2_dev.dev); vou_dev->status = SH_VOU_IDLE; mutex_unlock(&vou_dev->fop_lock); return ret; @@ -1194,8 +1185,8 @@ static int sh_vou_open(struct file *file) vou_dev->v4l2_dev.dev, &vou_dev->lock, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_FIELD_NONE, - sizeof(struct videobuf_buffer), vdev, - &vou_dev->fop_lock); + sizeof(struct videobuf_buffer), + vou_dev->vdev, &vou_dev->fop_lock); mutex_unlock(&vou_dev->fop_lock); return 0; @@ -1203,8 +1194,7 @@ static int sh_vou_open(struct file *file) static int sh_vou_release(struct file *file) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = file->private_data; dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); @@ -1214,7 +1204,7 @@ static int sh_vou_release(struct file *file) /* Last close */ vou_dev->status = SH_VOU_IDLE; sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101); - pm_runtime_put(vdev->v4l2_dev->dev); + pm_runtime_put(vou_dev->v4l2_dev.dev); mutex_unlock(&vou_dev->fop_lock); } @@ -1226,8 +1216,7 @@ static int sh_vou_release(struct file *file) static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = file->private_data; int ret; @@ -1242,8 +1231,7 @@ static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma) static unsigned int sh_vou_poll(struct file *file, poll_table *wait) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = file->private_data; unsigned int res; @@ -1258,8 +1246,7 @@ static unsigned int sh_vou_poll(struct file *file, poll_table *wait) static int sh_vou_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *id) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_chip_ident, id); } @@ -1268,8 +1255,7 @@ static int sh_vou_g_chip_ident(struct file *file, void *fh, static int sh_vou_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_register, reg); } @@ -1277,8 +1263,7 @@ static int sh_vou_g_register(struct file *file, void *fh, static int sh_vou_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) { - struct video_device *vdev = video_devdata(file); - struct sh_vou_device *vou_dev = video_get_drvdata(vdev); + struct sh_vou_device *vou_dev = video_drvdata(file); return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, s_register, reg); } From d899eddde548b9a6d3a59d0600feaab377efcd3f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 3 Jan 2013 15:35:57 -0300 Subject: [PATCH 2539/4607] [media] sh_vou: Use vou_dev instead of vou_file wherever possible This prepares for the removal of vou_file. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/sh_vou.c | 57 ++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index e969fea3c122..66c8da18df84 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -254,7 +254,8 @@ static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count, if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024) *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size); - dev_dbg(vq->dev, "%s(): count=%d, size=%d\n", __func__, *count, *size); + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): count=%d, size=%d\n", __func__, + *count, *size); return 0; } @@ -270,7 +271,7 @@ static int sh_vou_buf_prepare(struct videobuf_queue *vq, int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8; int ret; - dev_dbg(vq->dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); if (vb->width != pix->width || vb->height != pix->height || @@ -300,7 +301,7 @@ static int sh_vou_buf_prepare(struct videobuf_queue *vq, vb->state = VIDEOBUF_PREPARED; } - dev_dbg(vq->dev, + dev_dbg(vou_dev->v4l2_dev.dev, "%s(): fmt #%d, %u bytes per line, phys 0x%x, type %d, state %d\n", __func__, vou_dev->pix_idx, bytes_per_line, videobuf_to_dma_contig(vb), vb->memory, vb->state); @@ -315,7 +316,7 @@ static void sh_vou_buf_queue(struct videobuf_queue *vq, struct video_device *vdev = vq->priv_data; struct sh_vou_device *vou_dev = video_get_drvdata(vdev); - dev_dbg(vq->dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); vb->state = VIDEOBUF_QUEUED; list_add_tail(&vb->queue, &vou_dev->queue); @@ -326,8 +327,8 @@ static void sh_vou_buf_queue(struct videobuf_queue *vq, vou_dev->active = vb; /* Start from side A: we use mirror addresses, so, set B */ sh_vou_reg_a_write(vou_dev, VOURPR, 1); - dev_dbg(vq->dev, "%s: first buffer status 0x%x\n", __func__, - sh_vou_reg_a_read(vou_dev, VOUSTR)); + dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n", + __func__, sh_vou_reg_a_read(vou_dev, VOUSTR)); sh_vou_schedule_next(vou_dev, vb); /* Only activate VOU after the second buffer */ } else if (vou_dev->active->queue.next == &vb->queue) { @@ -337,8 +338,8 @@ static void sh_vou_buf_queue(struct videobuf_queue *vq, /* Register side switching with frame VSYNC */ sh_vou_reg_a_write(vou_dev, VOURCR, 5); - dev_dbg(vq->dev, "%s: second buffer status 0x%x\n", __func__, - sh_vou_reg_a_read(vou_dev, VOUSTR)); + dev_dbg(vou_dev->v4l2_dev.dev, "%s: second buffer status 0x%x\n", + __func__, sh_vou_reg_a_read(vou_dev, VOUSTR)); /* Enable End-of-Frame (VSYNC) interrupts */ sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004); @@ -356,7 +357,7 @@ static void sh_vou_buf_release(struct videobuf_queue *vq, struct sh_vou_device *vou_dev = video_get_drvdata(vdev); unsigned long flags; - dev_dbg(vq->dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); spin_lock_irqsave(&vou_dev->lock, flags); @@ -389,9 +390,9 @@ static struct videobuf_queue_ops sh_vou_video_qops = { static int sh_vou_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct sh_vou_file *vou_file = priv; + struct sh_vou_device *vou_dev = video_drvdata(file); - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); strlcpy(cap->card, "SuperH VOU", sizeof(cap->card)); cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; @@ -402,12 +403,12 @@ static int sh_vou_querycap(struct file *file, void *priv, static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *fmt) { - struct sh_vou_file *vou_file = priv; + struct sh_vou_device *vou_dev = video_drvdata(file); if (fmt->index >= ARRAY_SIZE(vou_fmt)) return -EINVAL; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; strlcpy(fmt->description, vou_fmt[fmt->index].desc, @@ -763,11 +764,11 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv, static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *fmt) { - struct sh_vou_file *vou_file = priv; + struct sh_vou_device *vou_dev = video_drvdata(file); struct v4l2_pix_format *pix = &fmt->fmt.pix; int i; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; pix->field = V4L2_FIELD_NONE; @@ -787,9 +788,10 @@ static int sh_vou_try_fmt_vid_out(struct file *file, void *priv, static int sh_vou_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *req) { + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; @@ -800,27 +802,30 @@ static int sh_vou_reqbufs(struct file *file, void *priv, static int sh_vou_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) { + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); return videobuf_querybuf(&vou_file->vbq, b); } static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) { + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); return videobuf_qbuf(&vou_file->vbq, b); } static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) { + struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK); } @@ -832,7 +837,7 @@ static int sh_vou_streamon(struct file *file, void *priv, struct sh_vou_file *vou_file = priv; int ret; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 1); @@ -849,7 +854,7 @@ static int sh_vou_streamoff(struct file *file, void *priv, struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = priv; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); /* * This calls buf_release from host driver's videobuf_queue_ops for all @@ -1021,9 +1026,9 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) static int sh_vou_cropcap(struct file *file, void *priv, struct v4l2_cropcap *a) { - struct sh_vou_file *vou_file = priv; + struct sh_vou_device *vou_dev = video_drvdata(file); - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; a->bounds.left = 0; @@ -1197,7 +1202,7 @@ static int sh_vou_release(struct file *file) struct sh_vou_device *vou_dev = video_drvdata(file); struct sh_vou_file *vou_file = file->private_data; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); if (!atomic_dec_return(&vou_dev->use_count)) { mutex_lock(&vou_dev->fop_lock); @@ -1220,7 +1225,7 @@ static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma) struct sh_vou_file *vou_file = file->private_data; int ret; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); if (mutex_lock_interruptible(&vou_dev->fop_lock)) return -ERESTARTSYS; @@ -1235,7 +1240,7 @@ static unsigned int sh_vou_poll(struct file *file, poll_table *wait) struct sh_vou_file *vou_file = file->private_data; unsigned int res; - dev_dbg(vou_file->vbq.dev, "%s()\n", __func__); + dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__); mutex_lock(&vou_dev->fop_lock); res = videobuf_poll_stream(file, &vou_file->vbq, wait); From 67e18cf9ab21648a477e91e0d3cb6dbdb1330262 Mon Sep 17 00:00:00 2001 From: Asias He Date: Tue, 5 Feb 2013 12:31:57 +0800 Subject: [PATCH 2540/4607] tcm_vhost: Multi-target support In order to take advantages of Paolo's multi-queue virito-scsi, we need multi-target support in tcm_vhost first. Otherwise all the requests go to one queue and other queues are idle. This patch makes: 1. All the targets under the wwpn is seen and can be used by guest. 2. No need to pass the tpgt number in struct vhost_scsi_target to tcm_vhost.ko. Only wwpn is needed. 3. We can always pass max_target = 255 to guest now, since we abort the request who's target id does not exist. Changes in v2: - Handle non-contiguous tpgt Changes in v3: - Simplfy lock in vhost_scsi_set_endpoint - Return -EEXIST when does not match Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 129 ++++++++++++++++++++++++-------------- drivers/vhost/tcm_vhost.h | 4 +- 2 files changed, 84 insertions(+), 49 deletions(-) diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 704e4f674776..81ecda54b923 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -59,8 +59,14 @@ enum { VHOST_SCSI_VQ_IO = 2, }; +#define VHOST_SCSI_MAX_TARGET 256 + struct vhost_scsi { - struct tcm_vhost_tpg *vs_tpg; /* Protected by vhost_scsi->dev.mutex */ + /* Protected by vhost_scsi->dev.mutex */ + struct tcm_vhost_tpg *vs_tpg[VHOST_SCSI_MAX_TARGET]; + char vs_vhost_wwpn[TRANSPORT_IQN_LEN]; + bool vs_endpoint; + struct vhost_dev dev; struct vhost_virtqueue vqs[3]; @@ -564,10 +570,10 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) u32 exp_data_len, data_first, data_num, data_direction; unsigned out, in, i; int head, ret; + u8 target; /* Must use ioctl VHOST_SCSI_SET_ENDPOINT */ - tv_tpg = vs->vs_tpg; - if (unlikely(!tv_tpg)) + if (unlikely(!vs->vs_endpoint)) return; mutex_lock(&vq->mutex); @@ -635,6 +641,28 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) break; } + /* Extract the tpgt */ + target = v_req.lun[1]; + tv_tpg = vs->vs_tpg[target]; + + /* Target does not exist, fail the request */ + if (unlikely(!tv_tpg)) { + struct virtio_scsi_cmd_resp __user *resp; + struct virtio_scsi_cmd_resp rsp; + + memset(&rsp, 0, sizeof(rsp)); + rsp.response = VIRTIO_SCSI_S_BAD_TARGET; + resp = vq->iov[out].iov_base; + ret = __copy_to_user(resp, &rsp, sizeof(rsp)); + if (!ret) + vhost_add_used_and_signal(&vs->dev, + &vs->vqs[2], head, 0); + else + pr_err("Faulted on virtio_scsi_cmd_resp\n"); + + continue; + } + exp_data_len = 0; for (i = 0; i < data_num; i++) exp_data_len += vq->iov[data_first + i].iov_len; @@ -743,7 +771,8 @@ static int vhost_scsi_set_endpoint( { struct tcm_vhost_tport *tv_tport; struct tcm_vhost_tpg *tv_tpg; - int index; + bool match = false; + int index, ret; mutex_lock(&vs->dev.mutex); /* Verify that ring has been setup correctly. */ @@ -754,7 +783,6 @@ static int vhost_scsi_set_endpoint( return -EFAULT; } } - mutex_unlock(&vs->dev.mutex); mutex_lock(&tcm_vhost_mutex); list_for_each_entry(tv_tpg, &tcm_vhost_list, tv_tpg_list) { @@ -769,30 +797,33 @@ static int vhost_scsi_set_endpoint( } tv_tport = tv_tpg->tport; - if (!strcmp(tv_tport->tport_name, t->vhost_wwpn) && - (tv_tpg->tport_tpgt == t->vhost_tpgt)) { - tv_tpg->tv_tpg_vhost_count++; - mutex_unlock(&tv_tpg->tv_tpg_mutex); - mutex_unlock(&tcm_vhost_mutex); - - mutex_lock(&vs->dev.mutex); - if (vs->vs_tpg) { - mutex_unlock(&vs->dev.mutex); - mutex_lock(&tv_tpg->tv_tpg_mutex); - tv_tpg->tv_tpg_vhost_count--; + if (!strcmp(tv_tport->tport_name, t->vhost_wwpn)) { + if (vs->vs_tpg[tv_tpg->tport_tpgt]) { mutex_unlock(&tv_tpg->tv_tpg_mutex); + mutex_unlock(&tcm_vhost_mutex); + mutex_unlock(&vs->dev.mutex); return -EEXIST; } - - vs->vs_tpg = tv_tpg; + tv_tpg->tv_tpg_vhost_count++; + vs->vs_tpg[tv_tpg->tport_tpgt] = tv_tpg; smp_mb__after_atomic_inc(); - mutex_unlock(&vs->dev.mutex); - return 0; + match = true; } mutex_unlock(&tv_tpg->tv_tpg_mutex); } mutex_unlock(&tcm_vhost_mutex); - return -EINVAL; + + if (match) { + memcpy(vs->vs_vhost_wwpn, t->vhost_wwpn, + sizeof(vs->vs_vhost_wwpn)); + vs->vs_endpoint = true; + ret = 0; + } else { + ret = -EEXIST; + } + + mutex_unlock(&vs->dev.mutex); + return ret; } static int vhost_scsi_clear_endpoint( @@ -801,7 +832,8 @@ static int vhost_scsi_clear_endpoint( { struct tcm_vhost_tport *tv_tport; struct tcm_vhost_tpg *tv_tpg; - int index, ret; + int index, ret, i; + u8 target; mutex_lock(&vs->dev.mutex); /* Verify that ring has been setup correctly. */ @@ -811,27 +843,32 @@ static int vhost_scsi_clear_endpoint( goto err; } } + for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) { + target = i; - if (!vs->vs_tpg) { - ret = -ENODEV; - goto err; - } - tv_tpg = vs->vs_tpg; - tv_tport = tv_tpg->tport; + tv_tpg = vs->vs_tpg[target]; + if (!tv_tpg) + continue; - if (strcmp(tv_tport->tport_name, t->vhost_wwpn) || - (tv_tpg->tport_tpgt != t->vhost_tpgt)) { - pr_warn("tv_tport->tport_name: %s, tv_tpg->tport_tpgt: %hu" - " does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n", - tv_tport->tport_name, tv_tpg->tport_tpgt, - t->vhost_wwpn, t->vhost_tpgt); - ret = -EINVAL; - goto err; + tv_tport = tv_tpg->tport; + if (!tv_tport) { + ret = -ENODEV; + goto err; + } + + if (strcmp(tv_tport->tport_name, t->vhost_wwpn)) { + pr_warn("tv_tport->tport_name: %s, tv_tpg->tport_tpgt: %hu" + " does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n", + tv_tport->tport_name, tv_tpg->tport_tpgt, + t->vhost_wwpn, t->vhost_tpgt); + ret = -EINVAL; + goto err; + } + tv_tpg->tv_tpg_vhost_count--; + vs->vs_tpg[target] = NULL; + vs->vs_endpoint = false; } - tv_tpg->tv_tpg_vhost_count--; - vs->vs_tpg = NULL; mutex_unlock(&vs->dev.mutex); - return 0; err: @@ -866,16 +903,12 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) static int vhost_scsi_release(struct inode *inode, struct file *f) { struct vhost_scsi *s = f->private_data; + struct vhost_scsi_target t; - if (s->vs_tpg && s->vs_tpg->tport) { - struct vhost_scsi_target backend; - - memcpy(backend.vhost_wwpn, s->vs_tpg->tport->tport_name, - sizeof(backend.vhost_wwpn)); - backend.vhost_tpgt = s->vs_tpg->tport_tpgt; - vhost_scsi_clear_endpoint(s, &backend); - } - + mutex_lock(&s->dev.mutex); + memcpy(t.vhost_wwpn, s->vs_vhost_wwpn, sizeof(t.vhost_wwpn)); + mutex_unlock(&s->dev.mutex); + vhost_scsi_clear_endpoint(s, &t); vhost_dev_stop(&s->dev); vhost_dev_cleanup(&s->dev, false); kfree(s); diff --git a/drivers/vhost/tcm_vhost.h b/drivers/vhost/tcm_vhost.h index 47ee80b3adee..519a5504d347 100644 --- a/drivers/vhost/tcm_vhost.h +++ b/drivers/vhost/tcm_vhost.h @@ -93,9 +93,11 @@ struct tcm_vhost_tport { * * ABI Rev 0: July 2012 version starting point for v3.6-rc merge candidate + * RFC-v2 vhost-scsi userspace. Add GET_ABI_VERSION ioctl usage + * ABI Rev 1: January 2013. Ignore vhost_tpgt filed in struct vhost_scsi_target. + * All the targets under vhost_wwpn can be seen and used by guset. */ -#define VHOST_SCSI_ABI_VERSION 0 +#define VHOST_SCSI_ABI_VERSION 1 struct vhost_scsi_target { int abi_version; From 1b7f390eb3bfc197c979c5478eadbc2a90f07667 Mon Sep 17 00:00:00 2001 From: Asias He Date: Wed, 6 Feb 2013 13:20:59 +0800 Subject: [PATCH 2541/4607] tcm_vhost: Multi-queue support This adds virtio-scsi multi-queue support to tcm_vhost. In order to use multi-queue, guest side multi-queue support is need. It can be found here: https://lkml.org/lkml/2012/12/18/166 Currently, only one thread is created by vhost core code for each vhost_scsi instance. Even if there are multi-queues, all the handling of guest kick (vhost_scsi_handle_kick) are processed in one thread. This is not optimal. Luckily, most of the work is offloaded to the tcm_vhost workqueue. Some initial perf numbers: 1 queue, 4 targets, 1 lun per target 4K request size, 50% randread + 50% randwrite: 127K/127k IOPS 4 queues, 4 targets, 1 lun per target 4K request size, 50% randread + 50% randwrite: 181K/181k IOPS Signed-off-by: Asias He Signed-off-by: Nicholas Bellinger --- drivers/vhost/tcm_vhost.c | 46 ++++++++++++++++++++++++--------------- drivers/vhost/tcm_vhost.h | 2 ++ 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 81ecda54b923..9951297b2427 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -48,6 +48,7 @@ #include /* TODO vhost.h currently depends on this */ #include #include +#include #include "vhost.c" #include "vhost.h" @@ -59,7 +60,8 @@ enum { VHOST_SCSI_VQ_IO = 2, }; -#define VHOST_SCSI_MAX_TARGET 256 +#define VHOST_SCSI_MAX_TARGET 256 +#define VHOST_SCSI_MAX_VQ 128 struct vhost_scsi { /* Protected by vhost_scsi->dev.mutex */ @@ -68,7 +70,7 @@ struct vhost_scsi { bool vs_endpoint; struct vhost_dev dev; - struct vhost_virtqueue vqs[3]; + struct vhost_virtqueue vqs[VHOST_SCSI_MAX_VQ]; struct vhost_work vs_completion_work; /* cmd completion work item */ struct llist_head vs_completion_list; /* cmd completion queue */ @@ -366,12 +368,14 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) { struct vhost_scsi *vs = container_of(work, struct vhost_scsi, vs_completion_work); + DECLARE_BITMAP(signal, VHOST_SCSI_MAX_VQ); struct virtio_scsi_cmd_resp v_rsp; struct tcm_vhost_cmd *tv_cmd; struct llist_node *llnode; struct se_cmd *se_cmd; - int ret; + int ret, vq; + bitmap_zero(signal, VHOST_SCSI_MAX_VQ); llnode = llist_del_all(&vs->vs_completion_list); while (llnode) { tv_cmd = llist_entry(llnode, struct tcm_vhost_cmd, @@ -390,15 +394,20 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work) memcpy(v_rsp.sense, tv_cmd->tvc_sense_buf, v_rsp.sense_len); ret = copy_to_user(tv_cmd->tvc_resp, &v_rsp, sizeof(v_rsp)); - if (likely(ret == 0)) - vhost_add_used(&vs->vqs[2], tv_cmd->tvc_vq_desc, 0); - else + if (likely(ret == 0)) { + vhost_add_used(tv_cmd->tvc_vq, tv_cmd->tvc_vq_desc, 0); + vq = tv_cmd->tvc_vq - vs->vqs; + __set_bit(vq, signal); + } else pr_err("Faulted on virtio_scsi_cmd_resp\n"); vhost_scsi_free_cmd(tv_cmd); } - vhost_signal(&vs->dev, &vs->vqs[2]); + vq = -1; + while ((vq = find_next_bit(signal, VHOST_SCSI_MAX_VQ, vq + 1)) + < VHOST_SCSI_MAX_VQ) + vhost_signal(&vs->dev, &vs->vqs[vq]); } static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( @@ -561,9 +570,9 @@ static void tcm_vhost_submission_work(struct work_struct *work) } } -static void vhost_scsi_handle_vq(struct vhost_scsi *vs) +static void vhost_scsi_handle_vq(struct vhost_scsi *vs, + struct vhost_virtqueue *vq) { - struct vhost_virtqueue *vq = &vs->vqs[2]; struct virtio_scsi_cmd_req v_req; struct tcm_vhost_tpg *tv_tpg; struct tcm_vhost_cmd *tv_cmd; @@ -656,7 +665,7 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) ret = __copy_to_user(resp, &rsp, sizeof(rsp)); if (!ret) vhost_add_used_and_signal(&vs->dev, - &vs->vqs[2], head, 0); + vq, head, 0); else pr_err("Faulted on virtio_scsi_cmd_resp\n"); @@ -678,6 +687,7 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) ": %d\n", tv_cmd, exp_data_len, data_direction); tv_cmd->tvc_vhost = vs; + tv_cmd->tvc_vq = vq; if (unlikely(vq->iov[out].iov_len != sizeof(struct virtio_scsi_cmd_resp))) { @@ -758,7 +768,7 @@ static void vhost_scsi_handle_kick(struct vhost_work *work) poll.work); struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev); - vhost_scsi_handle_vq(vs); + vhost_scsi_handle_vq(vs, vq); } /* @@ -879,7 +889,7 @@ err: static int vhost_scsi_open(struct inode *inode, struct file *f) { struct vhost_scsi *s; - int r; + int r, i; s = kzalloc(sizeof(*s), GFP_KERNEL); if (!s) @@ -889,8 +899,9 @@ static int vhost_scsi_open(struct inode *inode, struct file *f) s->vqs[VHOST_SCSI_VQ_CTL].handle_kick = vhost_scsi_ctl_handle_kick; s->vqs[VHOST_SCSI_VQ_EVT].handle_kick = vhost_scsi_evt_handle_kick; - s->vqs[VHOST_SCSI_VQ_IO].handle_kick = vhost_scsi_handle_kick; - r = vhost_dev_init(&s->dev, s->vqs, 3); + for (i = VHOST_SCSI_VQ_IO; i < VHOST_SCSI_MAX_VQ; i++) + s->vqs[i].handle_kick = vhost_scsi_handle_kick; + r = vhost_dev_init(&s->dev, s->vqs, VHOST_SCSI_MAX_VQ); if (r < 0) { kfree(s); return r; @@ -922,9 +933,10 @@ static void vhost_scsi_flush_vq(struct vhost_scsi *vs, int index) static void vhost_scsi_flush(struct vhost_scsi *vs) { - vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_CTL); - vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_EVT); - vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_IO); + int i; + + for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) + vhost_scsi_flush_vq(vs, i); } static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) diff --git a/drivers/vhost/tcm_vhost.h b/drivers/vhost/tcm_vhost.h index 519a5504d347..1d2ae7a60e11 100644 --- a/drivers/vhost/tcm_vhost.h +++ b/drivers/vhost/tcm_vhost.h @@ -23,6 +23,8 @@ struct tcm_vhost_cmd { struct virtio_scsi_cmd_resp __user *tvc_resp; /* Pointer to vhost_scsi for our device */ struct vhost_scsi *tvc_vhost; + /* Pointer to vhost_virtqueue for the cmd */ + struct vhost_virtqueue *tvc_vq; /* Pointer to vhost nexus memory */ struct tcm_vhost_nexus *tvc_nexus; /* The TCM I/O descriptor that is accessed via container_of() */ From 535ec049e82e391dbe8be55ecd9f392acd71d0d7 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 11 Feb 2013 14:38:05 -0300 Subject: [PATCH 2542/4607] [media] staging: media: Remove unnecessary OOM messages alloc failures already get standardized OOM messages and a dump_stack. Signed-off-by: Joe Perches Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/media/as102/as102_usb_drv.c | 4 +--- drivers/staging/media/go7007/go7007-fw.c | 24 ++++++--------------- drivers/staging/media/go7007/s2250-loader.c | 5 ++--- drivers/staging/media/lirc/lirc_imon.c | 3 --- drivers/staging/media/lirc/lirc_sasem.c | 6 ------ 5 files changed, 10 insertions(+), 32 deletions(-) diff --git a/drivers/staging/media/as102/as102_usb_drv.c b/drivers/staging/media/as102/as102_usb_drv.c index aaf1bc2ad1b2..9f275f020150 100644 --- a/drivers/staging/media/as102/as102_usb_drv.c +++ b/drivers/staging/media/as102/as102_usb_drv.c @@ -374,10 +374,8 @@ static int as102_usb_probe(struct usb_interface *intf, } as102_dev = kzalloc(sizeof(struct as102_dev_t), GFP_KERNEL); - if (as102_dev == NULL) { - dev_err(&intf->dev, "%s: kzalloc failed\n", __func__); + if (as102_dev == NULL) return -ENOMEM; - } /* Assign the user-friendly device name */ for (i = 0; i < ARRAY_SIZE(as102_usb_id_table); i++) { diff --git a/drivers/staging/media/go7007/go7007-fw.c b/drivers/staging/media/go7007/go7007-fw.c index f99c05b454b0..a5ede1c109d0 100644 --- a/drivers/staging/media/go7007/go7007-fw.c +++ b/drivers/staging/media/go7007/go7007-fw.c @@ -381,11 +381,8 @@ static int gen_mjpeghdr_to_package(struct go7007 *go, __le16 *code, int space) int size = 0, i, off = 0, chunk; buf = kzalloc(4096, GFP_KERNEL); - if (buf == NULL) { - dev_err(go->dev, - "unable to allocate 4096 bytes for firmware construction\n"); + if (buf == NULL) return -1; - } for (i = 1; i < 32; ++i) { mjpeg_frame_header(go, buf + size, i); @@ -651,11 +648,9 @@ static int gen_mpeg1hdr_to_package(struct go7007 *go, int i, off = 0, chunk; buf = kzalloc(5120, GFP_KERNEL); - if (buf == NULL) { - dev_err(go->dev, - "unable to allocate 5120 bytes for firmware construction\n"); + if (buf == NULL) return -1; - } + framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME); if (go->interlace_coding) framelen[0] += mpeg1_frame_header(go, buf + framelen[0] / 8, @@ -838,11 +833,9 @@ static int gen_mpeg4hdr_to_package(struct go7007 *go, int i, off = 0, chunk; buf = kzalloc(5120, GFP_KERNEL); - if (buf == NULL) { - dev_err(go->dev, - "unable to allocate 5120 bytes for firmware construction\n"); + if (buf == NULL) return -1; - } + framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME); i = 368; framelen[1] = mpeg4_frame_header(go, buf + i, 0, BFRAME_PRE); @@ -1582,12 +1575,9 @@ int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen) return -1; } code = kzalloc(codespace * 2, GFP_KERNEL); - if (code == NULL) { - dev_err(go->dev, - "unable to allocate %d bytes for firmware construction\n", - codespace * 2); + if (code == NULL) goto fw_failed; - } + src = (__le16 *)fw_entry->data; srclen = fw_entry->size / 2; while (srclen >= 2) { diff --git a/drivers/staging/media/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c index f57eb3beb341..72e5175fe7e3 100644 --- a/drivers/staging/media/go7007/s2250-loader.c +++ b/drivers/staging/media/go7007/s2250-loader.c @@ -81,10 +81,9 @@ static int s2250loader_probe(struct usb_interface *interface, /* Allocate dev data structure */ s = kmalloc(sizeof(device_extension_t), GFP_KERNEL); - if (s == NULL) { - dev_err(&interface->dev, "Out of memory\n"); + if (s == NULL) goto failed; - } + s2250_dev_table[minor] = s; dev_info(&interface->dev, diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index 343c622f9387..0a2c45dd4475 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -744,7 +744,6 @@ static int imon_probe(struct usb_interface *interface, context = kzalloc(sizeof(struct imon_context), GFP_KERNEL); if (!context) { - dev_err(dev, "%s: kzalloc failed for context\n", __func__); alloc_status = 1; goto alloc_status_switch; } @@ -826,13 +825,11 @@ static int imon_probe(struct usb_interface *interface, driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); if (!driver) { - dev_err(dev, "%s: kzalloc failed for lirc_driver\n", __func__); alloc_status = 2; goto alloc_status_switch; } rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); if (!rbuf) { - dev_err(dev, "%s: kmalloc failed for lirc_buffer\n", __func__); alloc_status = 3; goto alloc_status_switch; } diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index b3fe21e7a90d..68acca74ddb1 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -759,22 +759,16 @@ static int sasem_probe(struct usb_interface *interface, context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL); if (!context) { - dev_err(&interface->dev, - "%s: kzalloc failed for context\n", __func__); alloc_status = 1; goto alloc_status_switch; } driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); if (!driver) { - dev_err(&interface->dev, - "%s: kzalloc failed for lirc_driver\n", __func__); alloc_status = 2; goto alloc_status_switch; } rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); if (!rbuf) { - dev_err(&interface->dev, - "%s: kmalloc failed for lirc_buffer\n", __func__); alloc_status = 3; goto alloc_status_switch; } From 4db45af5ecdd5a989b5da2e05e37746dffaec0be Mon Sep 17 00:00:00 2001 From: Alexey Klimov Date: Mon, 11 Feb 2013 19:04:51 -0300 Subject: [PATCH 2543/4607] [media] radio-si470x doc: add info about v4l2-ctl and sox+alsa Patch adds information about using v4l2-ctl utility to tune the si470x radio and example how to use sox+alsa to redirect sound from radio sound device to another sound device. Signed-off-by: Alexey Klimov Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/si470x.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/video4linux/si470x.txt b/Documentation/video4linux/si470x.txt index 3a7823e01b4d..98c32925eb39 100644 --- a/Documentation/video4linux/si470x.txt +++ b/Documentation/video4linux/si470x.txt @@ -53,6 +53,9 @@ Testing is usually done with most application under Debian/testing: - kradio - Comfortable Radio Application for KDE - radio - ncurses-based radio application - mplayer - The Ultimate Movie Player For Linux +- v4l2-ctl - Collection of command line video4linux utilities +For example, you can use: +v4l2-ctl -d /dev/radio0 --set-ctrl=volume=10,mute=0 --set-freq=95.21 --all There is also a library libv4l, which can be used. It's going to have a function for frequency seeking, either by using hardware functionality as in radio-si470x @@ -75,8 +78,10 @@ commands. Please adjust the audio devices to your needs (/dev/dsp* and hw:x,x). If you just want to test audio (very poor quality): cat /dev/dsp1 > /dev/dsp -If you use OSS try: +If you use sox + OSS try: sox -2 --endian little -r 96000 -t oss /dev/dsp1 -t oss /dev/dsp +or using sox + alsa: +sox --endian little -c 2 -S -r 96000 -t alsa hw:1 -t alsa -r 96000 hw:0 If you use arts try: arecord -D hw:1,0 -r96000 -c2 -f S16_LE | artsdsp aplay -B - From b940a2219c9d59171339cc4510462154934fcb49 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 Feb 2013 08:22:08 -0300 Subject: [PATCH 2544/4607] [media] mceusb: move check earlier to make smatch happy Smatch complains that "cmdbuf[cmdcount - length]" might go past the end of the array. It's an easy warning to silence by moving the limit check earlier. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index bdd1ed8e406e..5b5b6e6f79e8 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -828,16 +828,16 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count) (txbuf[i] -= MCE_MAX_PULSE_LENGTH)); } - /* Fix packet length in last header */ - length = cmdcount % MCE_CODE_LENGTH; - cmdbuf[cmdcount - length] -= MCE_CODE_LENGTH - length; - /* Check if we have room for the empty packet at the end */ if (cmdcount >= MCE_CMDBUF_SIZE) { ret = -EINVAL; goto out; } + /* Fix packet length in last header */ + length = cmdcount % MCE_CODE_LENGTH; + cmdbuf[cmdcount - length] -= MCE_CODE_LENGTH - length; + /* All mce commands end with an empty packet (0x80) */ cmdbuf[cmdcount++] = MCE_IRDATA_TRAILER; From 2fd7f398d32a0d490d28de75b613263502918e51 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Fri, 8 Feb 2013 18:47:30 -0300 Subject: [PATCH 2545/4607] [media] media: rc: gpio-ir-recv: add support for device tree parsing This patch adds device tree parsing for gpio_ir_recv platform_data and the mandatory binding documentation. It basically follows what we already have for e.g. gpio_keys. All required device tree properties are OS independent but an optional property allows linux specific support for rc maps. Signed-off-by: Sebastian Hesselbarth Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- .../bindings/media/gpio-ir-receiver.txt | 16 ++++++ drivers/media/rc/gpio-ir-recv.c | 52 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/gpio-ir-receiver.txt diff --git a/Documentation/devicetree/bindings/media/gpio-ir-receiver.txt b/Documentation/devicetree/bindings/media/gpio-ir-receiver.txt new file mode 100644 index 000000000000..56e726ef4bf2 --- /dev/null +++ b/Documentation/devicetree/bindings/media/gpio-ir-receiver.txt @@ -0,0 +1,16 @@ +Device-Tree bindings for GPIO IR receiver + +Required properties: + - compatible: should be "gpio-ir-receiver". + - gpios: specifies GPIO used for IR signal reception. + +Optional properties: + - linux,rc-map-name: Linux specific remote control map name. + +Example node: + + ir: ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio0 19 1>; + linux,rc-map-name = "rc-rc6-mce"; + }; diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index 382f362b69af..8b82ae9bd686 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,45 @@ struct gpio_rc_dev { bool active_low; }; +#ifdef CONFIG_OF +/* + * Translate OpenFirmware node properties into platform_data + */ +static int gpio_ir_recv_get_devtree_pdata(struct device *dev, + struct gpio_ir_recv_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + enum of_gpio_flags flags; + int gpio; + + gpio = of_get_gpio_flags(np, 0, &flags); + if (gpio < 0) { + if (gpio != -EPROBE_DEFER) + dev_err(dev, "Failed to get gpio flags (%d)\n", gpio); + return gpio; + } + + pdata->gpio_nr = gpio; + pdata->active_low = (flags & OF_GPIO_ACTIVE_LOW); + /* probe() takes care of map_name == NULL or allowed_protos == 0 */ + pdata->map_name = of_get_property(np, "linux,rc-map-name", NULL); + pdata->allowed_protos = 0; + + return 0; +} + +static struct of_device_id gpio_ir_recv_of_match[] = { + { .compatible = "gpio-ir-receiver", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_ir_recv_of_match); + +#else /* !CONFIG_OF */ + +#define gpio_ir_recv_get_devtree_pdata(dev, pdata) (-ENOSYS) + +#endif + static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id) { struct gpio_rc_dev *gpio_dev = dev_id; @@ -66,6 +106,17 @@ static int gpio_ir_recv_probe(struct platform_device *pdev) pdev->dev.platform_data; int rc; + if (pdev->dev.of_node) { + struct gpio_ir_recv_platform_data *dtpdata = + devm_kzalloc(&pdev->dev, sizeof(*dtpdata), GFP_KERNEL); + if (!dtpdata) + return -ENOMEM; + rc = gpio_ir_recv_get_devtree_pdata(&pdev->dev, dtpdata); + if (rc) + return rc; + pdata = dtpdata; + } + if (!pdata) return -EINVAL; @@ -191,6 +242,7 @@ static struct platform_driver gpio_ir_recv_driver = { .driver = { .name = GPIO_IR_DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(gpio_ir_recv_of_match), #ifdef CONFIG_PM .pm = &gpio_ir_recv_pm_ops, #endif From 676fa7d4c9fd141a31cba2870e592a597c0bb07f Mon Sep 17 00:00:00 2001 From: Roland Scheidegger Date: Fri, 8 Feb 2013 21:08:55 -0300 Subject: [PATCH 2546/4607] [media] em28xx: add usb id for terratec h5 rev. 3 Seems to work just the same as older revisions. Signed-off-by: Roland Scheidegger Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 0a5aa6223adf..54a03b20de6e 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2080,6 +2080,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2884_BOARD_TERRATEC_H5 }, { USB_DEVICE(0x0ccd, 0x10ad), /* H5 Rev. 2 */ .driver_info = EM2884_BOARD_TERRATEC_H5 }, + { USB_DEVICE(0x0ccd, 0x10b6), /* H5 Rev. 3 */ + .driver_info = EM2884_BOARD_TERRATEC_H5 }, { USB_DEVICE(0x0ccd, 0x0084), .driver_info = EM2860_BOARD_TERRATEC_AV350 }, { USB_DEVICE(0x0ccd, 0x0096), From bbf344e54ed9a76e344d08feedc70ab2c5a8a64c Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Wed, 6 Feb 2013 14:42:28 +0100 Subject: [PATCH 2547/4607] target_core_rd: break out unterminated loop during copy The loop in rd_execute_rw() will never terminate if the sg element has a zero size. Or it'll spill over into outer space if the sg element is larger than the available space. So we need to add some safety catches here. Cc: Nic Bellinger Signed-off-by: Hannes Reinecke Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_rd.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index b0fff52c990e..e0b3c379aa14 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -316,7 +316,19 @@ rd_execute_rw(struct se_cmd *cmd) void *rd_addr; sg_miter_next(&m); + if (!(u32)m.length) { + pr_debug("RD[%u]: invalid sgl %p len %zu\n", + dev->rd_dev_id, m.addr, m.length); + sg_miter_stop(&m); + return TCM_INCORRECT_AMOUNT_OF_DATA; + } len = min((u32)m.length, src_len); + if (len > rd_size) { + pr_debug("RD[%u]: size underrun page %d offset %d " + "size %d\n", dev->rd_dev_id, + rd_page, rd_offset, rd_size); + len = rd_size; + } m.consumed = len; rd_addr = sg_virt(rd_sg) + rd_offset; From 33633676df0d16d0685f2fbc571143801bc16e3b Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 8 Feb 2013 15:18:38 -0800 Subject: [PATCH 2548/4607] target: Fix sense data for out-of-bounds IO operations We're supposed to return LOGICAL BLOCK ADDRESS OUT OF RANGE, not INVALID FIELD IN CDB. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_sbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index a664c664a31a..170f1f75d2d8 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -486,7 +486,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) */ if (cmd->t_task_lba || sectors) { if (sbc_check_valid_sectors(cmd) < 0) - return TCM_INVALID_CDB_FIELD; + return TCM_ADDRESS_OUT_OF_RANGE; } cmd->execute_cmd = ops->execute_sync_cache; break; From b9e2afff1e6b36d05a0e12b6114eb0aaf8949c09 Mon Sep 17 00:00:00 2001 From: Alistair Buxton Date: Tue, 12 Feb 2013 21:58:47 -0300 Subject: [PATCH 2549/4607] [media] rtl28xxu: Add USB IDs for Compro VideoMate U620F Signed-off-by: Alistair Buxton Acked-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/dvb-usb-v2/rtl28xxu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index e127bd134be0..d98387a3c95e 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1370,6 +1370,8 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "GIGABYTE U7300", NULL) }, { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1104, &rtl2832u_props, "Digivox Micro Hd", NULL) }, + { DVB_USB_DEVICE(USB_VID_COMPRO, 0x0620, + &rtl2832u_props, "Compro VideoMate U620F", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); From ed72d37a33fdf43dc47787fe220532cdec9da528 Mon Sep 17 00:00:00 2001 From: Christoph Nuscheler Date: Sat, 9 Feb 2013 15:56:23 -0300 Subject: [PATCH 2550/4607] [media] media: Add 0x3009 USB PID to ttusb2 driver (fixed diff) The "Technisat SkyStar USB plus" is a TT-connect S-2400 clone, which the V4L-DVB drivers already support. However, some of these devices (like mine) come with a different USB PID 0x3009 instead of 0x3006. There have already been patches simply overwriting the USB PID in dvb-usb-ids.h. Of course these patches were rejected because they would have disabled the 0x3006 PID. This new patch adds the 0x3009 PID to dvb-usb-ids.h, and adds references to it within the ttusb2.c driver. PID 0x3006 devices will continue to work. The only difference between the two hardware models seems to be the EEPROM chip. In fact, Windows BDA driver names the 0x3009 device with a "(8 kB EEPROM)" suffix. In spite of that, the 0x3009 device works absolutely flawlessly using the existing ttusb2 driver. Signed-off-by: Christoph Nuscheler Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/usb/dvb-usb/ttusb2.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index 7e1597dad568..399e1042d351 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -242,6 +242,7 @@ #define USB_PID_AVERMEDIA_A867 0xa867 #define USB_PID_AVERMEDIA_TWINSTAR 0x0825 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 +#define USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM 0x3009 #define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081 diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c index bcdac225ebe1..2ce3d19c58ef 100644 --- a/drivers/media/usb/dvb-usb/ttusb2.c +++ b/drivers/media/usb/dvb-usb/ttusb2.c @@ -620,6 +620,8 @@ static struct usb_device_id ttusb2_table [] = { USB_PID_TECHNOTREND_CONNECT_S2400) }, { USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_CT3650) }, + { USB_DEVICE(USB_VID_TECHNOTREND, + USB_PID_TECHNOTREND_CONNECT_S2400_8KEEPROM) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, ttusb2_table); @@ -721,12 +723,16 @@ static struct dvb_usb_device_properties ttusb2_properties_s2400 = { .generic_bulk_ctrl_endpoint = 0x01, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { "Technotrend TT-connect S-2400", { &ttusb2_table[2], NULL }, { NULL }, }, + { "Technotrend TT-connect S-2400 (8kB EEPROM)", + { &ttusb2_table[4], NULL }, + { NULL }, + }, } }; From bb992e72f9b751fceb04afeb7736b6a3e50effcf Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 8 Feb 2013 15:18:39 -0800 Subject: [PATCH 2551/4607] target: Fix error checking for UNMAP commands SBC-3 (revision 35) says: The PARAMETER LIST LENGTH field specifies the length in bytes of the UNMAP parameter list that is available to be transferred from the Data-Out Buffer. If the parameter list length is greater than zero and less than 0008h (i.e., eight), then the device server shall terminate the command with CHECK CONDITION status with the sense key set to ILLEGAL REQUEST and the additional sense code set to PARAMETER LIST LENGTH ERROR. A PARAMETER LIST LENGTH set to zero specifies that no data shall be sent. so our sense code for too-short descriptors was wrong, and we were incorrectly failing commands that didn't transfer any descriptors. While we're at it, also handle the UNMAP check: If the ANCHOR bit is set to one, and the ANC_SUP bit in the Logical Block Provisioning VPD page (see 6.6.4) is set to zero, then the device server shall terminate the command with CHECK CONDITION status with the sense key set to ILLEGAL REQUEST and the additional sense code set to INVALID FIELD IN CDB. (chris boot: Fix wrong cut+paste comment in transport_send_check_condition_and_sense) Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_iblock.c | 11 ++++++++++- drivers/target/target_core_transport.c | 10 ++++++++++ include/target/target_core_base.h | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 2f74e17ad3e6..facee5f74fa4 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -391,10 +391,19 @@ iblock_execute_unmap(struct se_cmd *cmd) sense_reason_t ret = 0; int dl, bd_dl, err; + /* We never set ANC_SUP */ + if (cmd->t_task_cdb[1]) + return TCM_INVALID_CDB_FIELD; + + if (cmd->data_length == 0) { + target_complete_cmd(cmd, SAM_STAT_GOOD); + return 0; + } + if (cmd->data_length < 8) { pr_warn("UNMAP parameter list length %u too small\n", cmd->data_length); - return TCM_INVALID_PARAMETER_LIST; + return TCM_PARAMETER_LIST_LENGTH_ERROR; } buf = transport_kmap_data_sg(cmd); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 96b64d57ebbb..2030b608136d 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1517,6 +1517,7 @@ void transport_generic_request_failure(struct se_cmd *cmd, case TCM_UNSUPPORTED_SCSI_OPCODE: case TCM_INVALID_CDB_FIELD: case TCM_INVALID_PARAMETER_LIST: + case TCM_PARAMETER_LIST_LENGTH_ERROR: case TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE: case TCM_UNKNOWN_MODE_PAGE: case TCM_WRITE_PROTECTED: @@ -2677,6 +2678,15 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, /* INVALID FIELD IN PARAMETER LIST */ buffer[SPC_ASC_KEY_OFFSET] = 0x26; break; + case TCM_PARAMETER_LIST_LENGTH_ERROR: + /* CURRENT ERROR */ + buffer[0] = 0x70; + buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; + /* ILLEGAL REQUEST */ + buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST; + /* PARAMETER LIST LENGTH ERROR */ + buffer[SPC_ASC_KEY_OFFSET] = 0x1a; + break; case TCM_UNEXPECTED_UNSOLICITED_DATA: /* CURRENT ERROR */ buffer[0] = 0x70; diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index df14dce59191..c4af592f7057 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -195,6 +195,7 @@ enum tcm_sense_reason_table { TCM_RESERVATION_CONFLICT = R(0x10), TCM_ADDRESS_OUT_OF_RANGE = R(0x11), TCM_OUT_OF_RESOURCES = R(0x12), + TCM_PARAMETER_LIST_LENGTH_ERROR = R(0x13), #undef R }; From 71f41fe1fafae2e407ef19d8174207f7ff80b387 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 8 Feb 2013 15:18:40 -0800 Subject: [PATCH 2552/4607] target: Fix parameter list length checking in MODE SELECT An empty parameter list (length == 0) is not an error, so succeed MODE SELECT in this case. If the parameter list length is too small, return the correct sense code of PARAMETER LIST LENGTH ERROR. Signed-off-by: Roland Dreier Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_spc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 803339516fb9..4cb667d720a7 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -1000,6 +1000,14 @@ static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd) int ret = 0; int i; + if (!cmd->data_length) { + target_complete_cmd(cmd, GOOD); + return 0; + } + + if (cmd->data_length < off + 2) + return TCM_PARAMETER_LIST_LENGTH_ERROR; + buf = transport_kmap_data_sg(cmd); if (!buf) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; @@ -1024,6 +1032,11 @@ static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd) goto out; check_contents: + if (cmd->data_length < off + length) { + ret = TCM_PARAMETER_LIST_LENGTH_ERROR; + goto out; + } + if (memcmp(buf + off, tbuf, length)) ret = TCM_INVALID_PARAMETER_LIST; From 6aed8ec3f76a22217c9ae183d32b1aa990bed069 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 17:32:21 +0100 Subject: [PATCH 2553/4607] drm: review locking for drm_fb_helper_restore_fbdev_mode ... it's required. Fix up exynos and the cma helper, and add a corresponding WARN_ON to drm_fb_helper_restore_fbdev_mode. Note that tegra calls the fbdev cma helper restore function also from it's driver-load callback. Which is a bit against current practice, since usually the call is only from ->lastclose, and initial setup is done by drm_fb_helper_initial_config. Also add the relevant drm DocBook entry. v2: Add promised WARN to restore_fbdev_mode. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 16 +++++++++++++++- drivers/gpu/drm/drm_fb_cma_helper.c | 2 ++ drivers/gpu/drm/drm_fb_helper.c | 11 +++++++++++ drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 2 ++ include/drm/drm_crtc.h | 1 + 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9c797f6fea75..f17077307c65 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -68,9 +68,23 @@ void drm_modeset_unlock_all(struct drm_device *dev) mutex_unlock(&dev->mode_config.mutex); } - EXPORT_SYMBOL(drm_modeset_unlock_all); +/** + * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked + * @dev: device + */ +void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + WARN_ON(!mutex_is_locked(&crtc->mutex)); + + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); +} +EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); + /* Avoid boilerplate. I'm tired of typing. */ #define DRM_ENUM_NAME_FN(fnname, list) \ char *fnname(int val) \ diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 3742bc96421e..1b6ba2d4d60c 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -389,8 +389,10 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); */ void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) { + drm_modeset_lock_all(dev); if (fbdev_cma) drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper); + drm_modeset_unlock_all(dev); } EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 0c6e25e979dd..77378a1e907e 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -239,10 +239,21 @@ int drm_fb_helper_debug_leave(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_debug_leave); +/** + * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration + * @fb_helper: fbcon to restore + * + * This should be called from driver's drm->lastclose callback when implementing + * an fbcon on top of kms using this helper. This ensures that the user isn't + * greeted with a black screen when e.g. X dies. + */ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) { bool error = false; int i, ret; + + drm_warn_on_modeset_not_all_locked(fb_helper->dev); + for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; ret = drm_mode_set_config_internal(mode_set); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 90d335cfb8c0..086d0f79785f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -376,5 +376,7 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev) if (!private || !private->fb_helper) return; + drm_modeset_lock_all(dev); drm_fb_helper_restore_fbdev_mode(private->fb_helper); + drm_modeset_unlock_all(dev); } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e90c8dcc028d..8b7762728639 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -867,6 +867,7 @@ struct drm_prop_enum_list { extern void drm_modeset_lock_all(struct drm_device *dev); extern void drm_modeset_unlock_all(struct drm_device *dev); +extern void drm_warn_on_modeset_not_all_locked(struct drm_device *dev); extern int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, From d21bf469d5301d025cd82997bb1529bcdc7086af Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 18:09:52 +0100 Subject: [PATCH 2554/4607] drm/fb-helper: kill drm_fb_helper_restore It's only used internally for the sysrq and panic handlers provided by the drm fb helper implementation. Hence just inline it, kill the export and remove the confusing kerneldoc. Driver's are supposed to call drm_fb_helper_restore_fbdev_mode on lastclose. Note that locking is totally fubar - the sysrq case doesn't take any locks at all. The panic handler probably shouldn't take any locks since it'll only make things worse. Otoh it's probably better to switch things over to the atomic modeset callbacks (and disable the panic handler for those drivers which don't implement it). But that's both better done in separate patches. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 23 ++++++++--------------- include/drm/drm_fb_helper.h | 1 - 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 77378a1e907e..14a81fc3e01a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -264,6 +264,10 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) } EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode); +/* + * restore fbcon display for all kms driver's using this helper, used for sysrq + * and panic handling. + */ static bool drm_fb_helper_force_kernel_mode(void) { bool ret, error = false; @@ -302,20 +306,6 @@ static struct notifier_block paniced = { .notifier_call = drm_fb_helper_panic, }; -/** - * drm_fb_helper_restore - restore the framebuffer console (kernel) config - * - * Restore's the kernel's fbcon mode, used for lastclose & panic paths. - */ -void drm_fb_helper_restore(void) -{ - bool ret; - ret = drm_fb_helper_force_kernel_mode(); - if (ret == true) - DRM_ERROR("Failed to restore crtc configuration\n"); -} -EXPORT_SYMBOL(drm_fb_helper_restore); - static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; @@ -337,7 +327,10 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) #ifdef CONFIG_MAGIC_SYSRQ static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) { - drm_fb_helper_restore(); + bool ret; + ret = drm_fb_helper_force_kernel_mode(); + if (ret == true) + DRM_ERROR("Failed to restore crtc configuration\n"); } static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 5120b01c2eeb..ba32505efb02 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -103,7 +103,6 @@ int drm_fb_helper_setcolreg(unsigned regno, struct fb_info *info); bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper); -void drm_fb_helper_restore(void); void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, uint32_t fb_width, uint32_t fb_height); void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, From 43c8a849a1e92fba6ea4493ed950d2a2e31ac87c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 18:18:07 +0100 Subject: [PATCH 2555/4607] drm/fb-helper: unexport drm_fb_helper_panic It doesn't even show up in any header files and only used iternally. Originally it was (ab)used to restore the fbcon on lastclose, but that died with commit e8e7a2b8ccfdae0d4cb6bd25824bbedcd42da316 Author: Dave Airlie Date: Thu Apr 21 22:18:32 2011 +0100 drm/i915: restore only the mode of this driver on lastclose (v2) Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 14a81fc3e01a..eaab092b995d 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -287,7 +287,7 @@ static bool drm_fb_helper_force_kernel_mode(void) return error; } -int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, +static int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, void *panic_str) { /* @@ -300,7 +300,6 @@ int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, pr_err("panic occurred, switching back to text console\n"); return drm_fb_helper_force_kernel_mode(); } -EXPORT_SYMBOL(drm_fb_helper_panic); static struct notifier_block paniced = { .notifier_call = drm_fb_helper_panic, From de1ace5b56d11db26abe69fd19754f91c326763f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 21:50:49 +0100 Subject: [PATCH 2556/4607] drm/fb-helper: unexport drm_fb_helper_single_fb_probe Not called by anyone, and really, shouldn't be. Drivers are supposed either drm_fb_helper_initial_config or drm_fb_helper_hotplug_event. Originally this was done differently, but is now consolidated in the helper functions and no longer done by drivers directly. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 5 ++--- include/drm/drm_fb_helper.h | 3 --- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index eaab092b995d..f5d362680f2b 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -754,8 +754,8 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, } EXPORT_SYMBOL(drm_fb_helper_pan_display); -int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, - int preferred_bpp) +static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, + int preferred_bpp) { int new_fb = 0; int crtc_count = 0; @@ -870,7 +870,6 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, return 0; } -EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, uint32_t depth) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index ba32505efb02..973402d6effe 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -82,9 +82,6 @@ struct drm_fb_helper { bool delayed_hotplug; }; -int drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper, - int preferred_bpp); - int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *helper, int crtc_count, int max_conn); From 203cb50143029b2bc95736ce10f7defcf59aca44 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 21:56:20 +0100 Subject: [PATCH 2557/4607] drm/tegra: don't set up initial fbcon config twice drm_fbdev_cma_init does the inital fbcon setup by calling down into drm_fb_helper_initial_config, so no need at all to restore the just set up configuration right away ... Signed-off-by: Daniel Vetter --- drivers/gpu/drm/tegra/fb.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index 97993c6835fd..03914953cb1c 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -39,10 +39,6 @@ int tegra_drm_fb_init(struct drm_device *drm) if (IS_ERR(fbdev)) return PTR_ERR(fbdev); -#ifndef CONFIG_FRAMEBUFFER_CONSOLE - drm_fbdev_cma_restore_mode(fbdev); -#endif - host1x->fbdev = fbdev; return 0; From 76a39dbfb2d1bc45219839e5a95d4ceaf6ca114f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 23:12:54 +0100 Subject: [PATCH 2558/4607] drm/fb-helper: don't disable everything in initial_config This should be done in the drivers for two reasons: - it gets in the way of fastboot efforts - it links the fb helpers with the crtc helpers instead of going through the real interface vfuncs, forcing i915 to fake all the ->disable callbacks used by the crtc helper to avoid ugly Oopsen v2: Resolve conflicts since drivers still call drm_fb_helper_single_add_all_connectors. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_fb.c | 5 +++++ drivers/gpu/drm/cirrus/cirrus_fbdev.c | 4 ++++ drivers/gpu/drm/drm_fb_cma_helper.c | 3 +++ drivers/gpu/drm/drm_fb_helper.c | 3 --- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 3 +++ drivers/gpu/drm/gma500/framebuffer.c | 4 ++++ drivers/gpu/drm/i915/intel_fb.c | 3 +++ drivers/gpu/drm/mgag200/mgag200_fb.c | 5 +++++ drivers/gpu/drm/nouveau/nouveau_fbcon.c | 3 +++ drivers/gpu/drm/radeon/radeon_fb.c | 4 ++++ drivers/gpu/drm/udl/udl_fb.c | 4 ++++ drivers/staging/omapdrm/omap_fbdev.c | 4 ++++ 12 files changed, 42 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index 3e6584b940dc..81763cad9940 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "ast_drv.h" static void ast_dirty_update(struct ast_fbdev *afbdev, @@ -314,6 +315,10 @@ int ast_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&afbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&afbdev->helper, 32); return 0; } diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 3daea0f638c3..b96605c6e1b6 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -291,6 +292,9 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) return ret; } drm_fb_helper_single_add_all_connectors(&gfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(cdev->dev); drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel); return 0; diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 1b6ba2d4d60c..ef3d33a8a7e2 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -333,6 +333,9 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, } + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + ret = drm_fb_helper_initial_config(helper, preferred_bpp); if (ret < 0) { dev_err(dev->dev, "Failed to set inital hw configuration.\n"); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index f5d362680f2b..d841b68aaa3e 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1360,9 +1360,6 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) struct drm_device *dev = fb_helper->dev; int count = 0; - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(fb_helper->dev); - drm_fb_helper_parse_command_line(fb_helper); count = drm_fb_helper_probe_connector_modes(fb_helper, diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 086d0f79785f..fe2a0f068af7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -295,6 +295,9 @@ int exynos_drm_fbdev_init(struct drm_device *dev) } + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); if (ret < 0) { DRM_ERROR("failed to set up hw configuration.\n"); diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index c1ef37e2efdf..fee3bf85af4a 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -616,6 +616,10 @@ int psb_fbdev_init(struct drm_device *dev) INTELFB_CONN_LIMIT); drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32); return 0; } diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 1c510da04d16..e67061249934 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -258,6 +258,9 @@ void intel_fbdev_initial_config(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + /* Due to peculiar init order wrt to hpd handling this is separate. */ drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32); } diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 5c69b432f99a..5bded5b74eaf 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -278,6 +279,10 @@ int mgag200_fbdev_init(struct mga_device *mdev) return ret; } drm_fb_helper_single_add_all_connectors(&mfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(mdev->dev); + drm_fb_helper_initial_config(&mfbdev->helper, 32); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index d4ecb4deb484..b1ebfe30f912 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -491,6 +491,9 @@ nouveau_fbcon_init(struct drm_device *dev) else preferred_bpp = 32; + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp); return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 515e5ee1f9ee..b48d1c8cf9eb 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -379,6 +379,10 @@ int radeon_fbdev_init(struct radeon_device *rdev) } drm_fb_helper_single_add_all_connectors(&rfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(rdev->ddev); + drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); return 0; } diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index b9feec9d08d3..cf5d05a0d955 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -619,6 +619,10 @@ int udl_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&ufbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&ufbdev->helper, bpp_sel); return 0; } diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c index 2728e37e02be..7e66eb138315 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/staging/omapdrm/omap_fbdev.c @@ -369,6 +369,10 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(helper, 32); priv->fbdev = helper; From af5676f1f91585cabe811b8f697e32015e2be826 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 23:28:14 +0100 Subject: [PATCH 2559/4607] drm/i915: rip out helper->disable noop functions Now that the driver is in control of whether it needs to disable everything at take-over or not, we can rip this all out. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_crt.c | 1 - drivers/gpu/drm/i915/intel_ddi.c | 1 - drivers/gpu/drm/i915/intel_display.c | 9 --------- drivers/gpu/drm/i915/intel_dp.c | 1 - drivers/gpu/drm/i915/intel_drv.h | 1 - drivers/gpu/drm/i915/intel_dvo.c | 1 - drivers/gpu/drm/i915/intel_fb.c | 3 --- drivers/gpu/drm/i915/intel_hdmi.c | 1 - drivers/gpu/drm/i915/intel_lvds.c | 1 - drivers/gpu/drm/i915/intel_sdvo.c | 1 - drivers/gpu/drm/i915/intel_tv.c | 1 - 11 files changed, 21 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 68e79f32e100..729dd1a3fe72 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -685,7 +685,6 @@ static void intel_crt_reset(struct drm_connector *connector) static const struct drm_encoder_helper_funcs crt_encoder_funcs = { .mode_fixup = intel_crt_mode_fixup, .mode_set = intel_crt_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_crt_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index cedf4ab5ff16..a259e09eb6a8 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1481,7 +1481,6 @@ static const struct drm_encoder_funcs intel_ddi_funcs = { static const struct drm_encoder_helper_funcs intel_ddi_helper_funcs = { .mode_fixup = intel_ddi_mode_fixup, .mode_set = intel_ddi_mode_set, - .disable = intel_encoder_noop, }; void intel_ddi_init(struct drm_device *dev, enum port port) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0dfecaf599ff..24f2654338d2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3718,10 +3718,6 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc) intel_crtc_update_sarea(crtc, enable); } -static void intel_crtc_noop(struct drm_crtc *crtc) -{ -} - static void intel_crtc_disable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3770,10 +3766,6 @@ void intel_modeset_disable(struct drm_device *dev) } } -void intel_encoder_noop(struct drm_encoder *encoder) -{ -} - void intel_encoder_destroy(struct drm_encoder *encoder) { struct intel_encoder *intel_encoder = to_intel_encoder(encoder); @@ -7277,7 +7269,6 @@ free_work: static struct drm_crtc_helper_funcs intel_helper_funcs = { .mode_set_base_atomic = intel_pipe_set_base_atomic, .load_lut = intel_crtc_load_lut, - .disable = intel_crtc_noop, }; bool intel_encoder_check_is_cloned(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 56e408b2e3c8..13c1536a8bb2 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2561,7 +2561,6 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = { .mode_fixup = intel_dp_mode_fixup, .mode_set = intel_dp_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_dp_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 13afb37d8dec..a2ec01c49f40 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -521,7 +521,6 @@ extern void intel_modeset_disable(struct drm_device *dev); extern void intel_crtc_restore_mode(struct drm_crtc *crtc); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_update_dpms(struct drm_crtc *crtc); -extern void intel_encoder_noop(struct drm_encoder *encoder); extern void intel_encoder_destroy(struct drm_encoder *encoder); extern void intel_encoder_dpms(struct intel_encoder *encoder, int mode); extern bool intel_encoder_check_is_cloned(struct intel_encoder *encoder); diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 15da99533e5b..00e70dbe82da 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -345,7 +345,6 @@ static void intel_dvo_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = { .mode_fixup = intel_dvo_mode_fixup, .mode_set = intel_dvo_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_dvo_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index e67061249934..1c510da04d16 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -258,9 +258,6 @@ void intel_fbdev_initial_config(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); - /* Due to peculiar init order wrt to hpd handling this is separate. */ drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32); } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 5b4efd64c2f9..3883bed80faa 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -972,7 +972,6 @@ static void intel_hdmi_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { .mode_fixup = intel_hdmi_mode_fixup, .mode_set = intel_hdmi_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_hdmi_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 5e3f08e3fd8b..feb43fd7debf 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -656,7 +656,6 @@ static int intel_lvds_set_property(struct drm_connector *connector, static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { .mode_fixup = intel_lvds_mode_fixup, .mode_set = intel_lvds_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index f01063a2323a..33b46d9694ea 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2043,7 +2043,6 @@ done: static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { .mode_fixup = intel_sdvo_mode_fixup, .mode_set = intel_sdvo_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_sdvo_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 984a113c5d13..d808421c1c80 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1487,7 +1487,6 @@ out: static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { .mode_fixup = intel_tv_mode_fixup, .mode_set = intel_tv_mode_set, - .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_tv_connector_funcs = { From 7e53f3a423146745a4e4bb93362d488dfad502a8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 21 Jan 2013 10:52:17 +0100 Subject: [PATCH 2560/4607] drm/fb-helper: fixup set_config semantics While doing the modeset rework for drm/i915 I've noticed that the fb helper is very liberal with the semantics of the ->set_config interface: - It doesn't bother clearing stale modes (e.g. when unplugging a screen). - It unconditionally sets the fb, even if no mode will be set on a given crtc. - The initial setup is a bit fun since we need to pick crtcs to decide the desired fb size, but also should set the modeset->fb pointer. Explain what's going on in the fixup code after the fb is allocated. The crtc helper didn't really care, but the new i915 modeset infrastructure did, so I've had to add a bunch of special-cases to catch this. Fix this all up and enforce the interface by converting the checks in drm/i915/intel_display.c to BUG_ONs. v2: Fix commit message spell fail spotted by Rob Clark. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 27 +++++++++++++++++++++++---- drivers/gpu/drm/i915/intel_display.c | 11 +++-------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d841b68aaa3e..809ef99f910e 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -689,7 +689,6 @@ int drm_fb_helper_set_par(struct fb_info *info) struct drm_fb_helper *fb_helper = info->par; struct drm_device *dev = fb_helper->dev; struct fb_var_screeninfo *var = &info->var; - struct drm_crtc *crtc; int ret; int i; @@ -700,7 +699,6 @@ int drm_fb_helper_set_par(struct fb_info *info) drm_modeset_lock_all(dev); for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set); if (ret) { drm_modeset_unlock_all(dev); @@ -841,9 +839,17 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, info = fb_helper->fbdev; - /* set the fb pointer */ + /* + * Set the fb pointer - usually drm_setup_crtcs does this for hotplug + * events, but at init time drm_setup_crtcs needs to be called before + * the fb is allocated (since we need to figure out the desired size of + * the fb before we can allocate it ...). Hence we need to fix things up + * here again. + */ for (i = 0; i < fb_helper->crtc_count; i++) - fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; + if (fb_helper->crtc_info[i].mode_set.num_connectors) + fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; + if (new_fb) { info->var.pixclock = 0; @@ -1314,6 +1320,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) for (i = 0; i < fb_helper->crtc_count; i++) { modeset = &fb_helper->crtc_info[i].mode_set; modeset->num_connectors = 0; + modeset->fb = NULL; } for (i = 0; i < fb_helper->connector_count; i++) { @@ -1330,9 +1337,21 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) modeset->mode = drm_mode_duplicate(dev, fb_crtc->desired_mode); modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; + modeset->fb = fb_helper->fb; } } + /* Clear out any old modes if there are no more connected outputs. */ + for (i = 0; i < fb_helper->crtc_count; i++) { + modeset = &fb_helper->crtc_info[i].mode_set; + if (modeset->num_connectors == 0) { + BUG_ON(modeset->fb); + BUG_ON(modeset->num_connectors); + if (modeset->mode) + drm_mode_destroy(dev, modeset->mode); + modeset->mode = NULL; + } + } out: kfree(crtcs); kfree(modes); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 24f2654338d2..ca8d5929063e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7978,14 +7978,9 @@ static int intel_crtc_set_config(struct drm_mode_set *set) BUG_ON(!set->crtc); BUG_ON(!set->crtc->helper_private); - if (!set->mode) - set->fb = NULL; - - /* The fb helper likes to play gross jokes with ->mode_set_config. - * Unfortunately the crtc helper doesn't do much at all for this case, - * so we have to cope with this madness until the fb helper is fixed up. */ - if (set->fb && set->num_connectors == 0) - return 0; + /* Enforce sane interface api - has been abused by the fb helper. */ + BUG_ON(!set->mode && set->fb); + BUG_ON(set->fb && set->num_connectors == 0); if (set->fb) { DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", From 2180c3c7e76536ce8ff0fd957a11dbfaa1e93403 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 21 Jan 2013 23:12:36 +0100 Subject: [PATCH 2561/4607] drm/fb-helper: directly call set_par from the hotplug handler The idea behind calling down into the driver's ->fb_probe function on each hotplug seems to be able to reallocate the backing storage (if e.g. a screen with higher resolution gets added). But that requires quite a bit of work in the fb helper itself, since currently we limit new screens to the currently allocated fb. An no kms driver supports fbdev fb resizing. So don't bother and start to simplify the code by calling drm_fb_helper_set_par directly from the fbdev hotplug function, since that's the only thing left in drm_fb_helper_single_fb_probe which does not concern itself with fb allocation and initial setup. Follow-on patches will streamline the initial setup code. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 809ef99f910e..535c2cadf4d1 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -859,8 +859,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", info->node, info->fix.id); - } else { - drm_fb_helper_set_par(info); } /* Switch back to kernel console on panic */ @@ -1436,7 +1434,9 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) drm_setup_crtcs(fb_helper); drm_modeset_unlock_all(dev); - return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); + drm_fb_helper_set_par(fb_helper->fbdev); + + return 0; } EXPORT_SYMBOL(drm_fb_helper_hotplug_event); From 8acf658ad6246be7913647e6e76da6a539bff425 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 21 Jan 2013 23:38:37 +0100 Subject: [PATCH 2562/4607] drm/fb-helper: streamline drm_fb_helper_single_fb_probe No need to check whether we've allocated a new fb since we're not always doing that. Also, we always need to register the fbdev and add it to the panic notifier. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 535c2cadf4d1..e0d4b04c0eb5 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -752,10 +752,14 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, } EXPORT_SYMBOL(drm_fb_helper_pan_display); +/* + * Allocates the backing storage through the ->fb_probe callback and then + * registers the fbdev and sets up the panic notifier. + */ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, int preferred_bpp) { - int new_fb = 0; + int ret = 0; int crtc_count = 0; int i; struct fb_info *info; @@ -833,9 +837,9 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, } /* push down into drivers */ - new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); - if (new_fb < 0) - return new_fb; + ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); + if (ret < 0) + return ret; info = fb_helper->fbdev; @@ -851,15 +855,12 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; - if (new_fb) { - info->var.pixclock = 0; - if (register_framebuffer(info) < 0) - return -EINVAL; + info->var.pixclock = 0; + if (register_framebuffer(info) < 0) + return -EINVAL; - dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", - info->node, info->fix.id); - - } + dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", + info->node, info->fix.id); /* Switch back to kernel console on panic */ /* multi card linked list maybe */ @@ -869,8 +870,8 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, &paniced); register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); } - if (new_fb) - list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); + + list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); return 0; } From cd5428a5447cc6ca77ec6547d6f86834b205eac7 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 21 Jan 2013 23:42:49 +0100 Subject: [PATCH 2563/4607] drm/: simplify ->fb_probe callback The fb helper lost its support for reallocating an fb completely, so no need to return special success values any more. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_fb.c | 21 +++------------- drivers/gpu/drm/cirrus/cirrus_fbdev.c | 22 +++-------------- drivers/gpu/drm/drm_fb_cma_helper.c | 17 +------------ drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 30 +---------------------- drivers/gpu/drm/gma500/framebuffer.c | 10 +------- drivers/gpu/drm/i915/intel_fb.c | 21 +++------------- drivers/gpu/drm/mgag200/mgag200_fb.c | 22 +++-------------- drivers/gpu/drm/nouveau/nouveau_fbcon.c | 22 +++-------------- drivers/gpu/drm/radeon/radeon_fb.c | 21 +++------------- drivers/gpu/drm/udl/udl_fb.c | 22 +++-------------- drivers/staging/omapdrm/omap_fbdev.c | 17 +------------ 11 files changed, 25 insertions(+), 200 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index 81763cad9940..34931fe7d2c5 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -146,9 +146,10 @@ static int astfb_create_object(struct ast_fbdev *afbdev, return ret; } -static int astfb_create(struct ast_fbdev *afbdev, +static int astfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct ast_fbdev *afbdev = (struct ast_fbdev *)helper; struct drm_device *dev = afbdev->helper.dev; struct drm_mode_fb_cmd2 mode_cmd; struct drm_framebuffer *fb; @@ -249,26 +250,10 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, *blue = ast_crtc->lut_b[regno] << 8; } -static int ast_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct ast_fbdev *afbdev = (struct ast_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = astfb_create(afbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs ast_fb_helper_funcs = { .gamma_set = ast_fb_gamma_set, .gamma_get = ast_fb_gamma_get, - .fb_probe = ast_find_or_create_single, + .fb_probe = astfb_create, }; static void ast_fbdev_destroy(struct drm_device *dev, diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index b96605c6e1b6..e25afccaf85b 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -121,9 +121,10 @@ static int cirrusfb_create_object(struct cirrus_fbdev *afbdev, return ret; } -static int cirrusfb_create(struct cirrus_fbdev *gfbdev, +static int cirrusfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper; struct drm_device *dev = gfbdev->helper.dev; struct cirrus_device *cdev = gfbdev->helper.dev->dev_private; struct fb_info *info; @@ -220,23 +221,6 @@ out_iounmap: return ret; } -static int cirrus_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size - *sizes) -{ - struct cirrus_fbdev *gfbdev = (struct cirrus_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = cirrusfb_create(gfbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static int cirrus_fbdev_destroy(struct drm_device *dev, struct cirrus_fbdev *gfbdev) { @@ -268,7 +252,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev, static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { .gamma_set = cirrus_crtc_fb_gamma_set, .gamma_get = cirrus_crtc_fb_gamma_get, - .fb_probe = cirrus_fb_find_or_create_single, + .fb_probe = cirrusfb_create, }; int cirrus_fbdev_init(struct cirrus_device *cdev) diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index ef3d33a8a7e2..e851658f87d5 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -275,23 +275,8 @@ err_drm_gem_cma_free_object: return ret; } -static int drm_fbdev_cma_probe(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - int ret = 0; - - if (!helper->fb) { - ret = drm_fbdev_cma_create(helper, sizes); - if (ret < 0) - return ret; - ret = 1; - } - - return ret; -} - static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { - .fb_probe = drm_fbdev_cma_probe, + .fb_probe = drm_fbdev_cma_create, }; /** diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index fe2a0f068af7..68f0045f86b8 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -226,36 +226,8 @@ out: return ret; } -static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - int ret = 0; - - DRM_DEBUG_KMS("%s\n", __FILE__); - - /* - * with !helper->fb, it means that this funcion is called first time - * and after that, the helper->fb would be used as clone mode. - */ - if (!helper->fb) { - ret = exynos_drm_fbdev_create(helper, sizes); - if (ret < 0) { - DRM_ERROR("failed to create fbdev.\n"); - return ret; - } - - /* - * fb_helper expects a value more than 1 if succeed - * because register_framebuffer() should be called. - */ - ret = 1; - } - - return ret; -} - static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { - .fb_probe = exynos_drm_fbdev_probe, + .fb_probe = exynos_drm_fbdev_create, }; int exynos_drm_fbdev_init(struct drm_device *dev) diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index fee3bf85af4a..2590cac84257 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -545,9 +545,7 @@ static int psbfb_probe(struct drm_fb_helper *helper, struct psb_fbdev *psb_fbdev = (struct psb_fbdev *)helper; struct drm_device *dev = psb_fbdev->psb_fb_helper.dev; struct drm_psb_private *dev_priv = dev->dev_private; - int new_fb = 0; int bytespp; - int ret; bytespp = sizes->surface_bpp / 8; if (bytespp == 3) /* no 24bit packed */ @@ -562,13 +560,7 @@ static int psbfb_probe(struct drm_fb_helper *helper, sizes->surface_depth = 16; } - if (!helper->fb) { - ret = psbfb_create(psb_fbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; + return psbfb_create(psb_fbdev, sizes); } static struct drm_fb_helper_funcs psb_fb_helper_funcs = { diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 1c510da04d16..981bdce3634e 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -57,9 +57,10 @@ static struct fb_ops intelfb_ops = { .fb_debug_leave = drm_fb_helper_debug_leave, }; -static int intelfb_create(struct intel_fbdev *ifbdev, +static int intelfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper; struct drm_device *dev = ifbdev->helper.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct fb_info *info; @@ -181,26 +182,10 @@ out: return ret; } -static int intel_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = intelfb_create(ifbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs intel_fb_helper_funcs = { .gamma_set = intel_crtc_fb_gamma_set, .gamma_get = intel_crtc_fb_gamma_get, - .fb_probe = intel_fb_find_or_create_single, + .fb_probe = intelfb_create, }; static void intel_fbdev_destroy(struct drm_device *dev, diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 5bded5b74eaf..d2253f639481 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -121,9 +121,10 @@ static int mgag200fb_create_object(struct mga_fbdev *afbdev, return ret; } -static int mgag200fb_create(struct mga_fbdev *mfbdev, +static int mgag200fb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper; struct drm_device *dev = mfbdev->helper.dev; struct drm_mode_fb_cmd2 mode_cmd; struct mga_device *mdev = dev->dev_private; @@ -210,23 +211,6 @@ out: return ret; } -static int mga_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size - *sizes) -{ - struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = mgag200fb_create(mfbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static int mga_fbdev_destroy(struct drm_device *dev, struct mga_fbdev *mfbdev) { @@ -257,7 +241,7 @@ static int mga_fbdev_destroy(struct drm_device *dev, static struct drm_fb_helper_funcs mga_fb_helper_funcs = { .gamma_set = mga_crtc_fb_gamma_set, .gamma_get = mga_crtc_fb_gamma_get, - .fb_probe = mga_fb_find_or_create_single, + .fb_probe = mgag200fb_create, }; int mgag200_fbdev_init(struct mga_device *mdev) diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index b1ebfe30f912..b03531781580 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -251,9 +251,10 @@ nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon) } static int -nouveau_fbcon_create(struct nouveau_fbdev *fbcon, +nouveau_fbcon_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct nouveau_fbdev *fbcon = (struct nouveau_fbdev *)helper; struct drm_device *dev = fbcon->dev; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_device *device = nv_device(drm->device); @@ -388,23 +389,6 @@ out: return ret; } -static int -nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct nouveau_fbdev *fbcon = (struct nouveau_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = nouveau_fbcon_create(fbcon, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - void nouveau_fbcon_output_poll_changed(struct drm_device *dev) { @@ -450,7 +434,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info) static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { .gamma_set = nouveau_fbcon_gamma_set, .gamma_get = nouveau_fbcon_gamma_get, - .fb_probe = nouveau_fbcon_find_or_create_single, + .fb_probe = nouveau_fbcon_create, }; diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index b48d1c8cf9eb..b1746741bc59 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -187,9 +187,10 @@ out_unref: return ret; } -static int radeonfb_create(struct radeon_fbdev *rfbdev, +static int radeonfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper; struct radeon_device *rdev = rfbdev->rdev; struct fb_info *info; struct drm_framebuffer *fb = NULL; @@ -300,22 +301,6 @@ out_unref: return ret; } -static int radeon_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = radeonfb_create(rfbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - void radeon_fb_output_poll_changed(struct radeon_device *rdev) { drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper); @@ -349,7 +334,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { .gamma_set = radeon_crtc_fb_gamma_set, .gamma_get = radeon_crtc_fb_gamma_get, - .fb_probe = radeon_fb_find_or_create_single, + .fb_probe = radeonfb_create, }; int radeon_fbdev_init(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index cf5d05a0d955..9f4be3d4a02e 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -476,9 +476,10 @@ udl_framebuffer_init(struct drm_device *dev, } -static int udlfb_create(struct udl_fbdev *ufbdev, +static int udlfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { + struct udl_fbdev *ufbdev = (struct udl_fbdev *)helper; struct drm_device *dev = ufbdev->helper.dev; struct fb_info *info; struct device *device = &dev->usbdev->dev; @@ -556,27 +557,10 @@ out: return ret; } -static int udl_fb_find_or_create_single(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct udl_fbdev *ufbdev = (struct udl_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = udlfb_create(ufbdev, sizes); - if (ret) - return ret; - - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs udl_fb_helper_funcs = { .gamma_set = udl_crtc_fb_gamma_set, .gamma_get = udl_crtc_fb_gamma_get, - .fb_probe = udl_fb_find_or_create_single, + .fb_probe = udlfb_create, }; static void udl_fbdev_destroy(struct drm_device *dev, diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c index 7e66eb138315..caefdf9430f8 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/staging/omapdrm/omap_fbdev.c @@ -296,25 +296,10 @@ static void omap_crtc_fb_gamma_get(struct drm_crtc *crtc, DBG("fbdev: get gamma"); } -static int omap_fbdev_probe(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = omap_fbdev_create(helper, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; -} - static struct drm_fb_helper_funcs omap_fb_helper_funcs = { .gamma_set = omap_crtc_fb_gamma_set, .gamma_get = omap_crtc_fb_gamma_get, - .fb_probe = omap_fbdev_probe, + .fb_probe = omap_fbdev_create, }; static struct drm_fb_helper *get_fb(struct fb_info *fbi) From 207fd32970b1def91b11ae28f6bebffc792db714 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 22:13:14 +0100 Subject: [PATCH 2564/4607] drm/fb-helper: improve kerneldoc Now that the fbdev helper interface for drivers is trimmed down, update the kerneldoc for all the remaining exported functions. I've tried to beat the DocBook a bit by reordering the function references a bit into a more sensible ordering. But that didn't work out at all. Hence just extend the in-code DOC: section a bit. Also remove the LOCKING: sections - especially for the setup functions they're totally bogus. But that's not a documentation problem, but simply an artifact of the current rather hazardous locking around drm init and even more so around fbdev setup ... v2: Some further improvements: - Also add documentation for drm_fb_helper_single_add_all_connectors, Dave Airlie didn't want me to kill this one from the fb helper interface. - Update docs for drm_fb_helper_fill_var/fix - they should be used from the driver's ->fb_probe callback to setup the fbdev info structure. - Clarify what the ->fb_probe callback should all do - it needs to setup both the fbdev info and allocate the drm framebuffer used as backing storage. - Add basic documentaation for the drm_fb_helper_funcs driver callback vfunc. v3: Implement clarifications Laurent Pinchart suggested in his review. v4: Fix another mispelling Laurent spotted. Cc: Laurent Pinchart Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 1 + drivers/gpu/drm/drm_fb_helper.c | 148 ++++++++++++++++++++++++++++---- include/drm/drm_fb_helper.h | 12 +++ 3 files changed, 146 insertions(+), 15 deletions(-) diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index b26de523ab70..51e1904ac4c7 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2143,6 +2143,7 @@ void intel_crt_init(struct drm_device *dev) fbdev Helper Functions Reference !Pdrivers/gpu/drm/drm_fb_helper.c fbdev helpers !Edrivers/gpu/drm/drm_fb_helper.c +!Iinclude/drm/drm_fb_helper.h Display Port Helper Functions Reference diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index e0d4b04c0eb5..94f304d8b795 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -52,9 +52,36 @@ static LIST_HEAD(kernel_fb_helper_list); * mode setting driver. They can be used mostly independantely from the crtc * helper functions used by many drivers to implement the kernel mode setting * interfaces. + * + * Initialization is done as a three-step process with drm_fb_helper_init(), + * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config(). + * Drivers with fancier requirements than the default beheviour can override the + * second step with their own code. Teardown is done with drm_fb_helper_fini(). + * + * At runtime drivers should restore the fbdev console by calling + * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They + * should also notify the fb helper code from updates to the output + * configuration by calling drm_fb_helper_hotplug_event(). For easier + * integration with the output polling code in drm_crtc_helper.c the modeset + * code proves a ->output_poll_changed callback. + * + * All other functions exported by the fb helper library can be used to + * implement the fbdev driver interface by the driver. */ -/* simple single crtc case helper function */ +/** + * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev + * emulation helper + * @fb_helper: fbdev initialized with drm_fb_helper_init + * + * This functions adds all the available connectors for use with the given + * fb_helper. This is a separate step to allow drivers to freely assign + * connectors to the fbdev, e.g. if some are reserved for special purposes or + * not adequate to be used for the fbcon. + * + * Since this is part of the initial setup before the fbdev is published, no + * locking is required. + */ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; @@ -163,6 +190,10 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); } +/** + * drm_fb_helper_debug_enter - implementation for ->fb_debug_enter + * @info: fbdev registered by the helper + */ int drm_fb_helper_debug_enter(struct fb_info *info) { struct drm_fb_helper *helper = info->par; @@ -208,6 +239,10 @@ static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) return NULL; } +/** + * drm_fb_helper_debug_leave - implementation for ->fb_debug_leave + * @info: fbdev registered by the helper + */ int drm_fb_helper_debug_leave(struct fb_info *info) { struct drm_fb_helper *helper = info->par; @@ -243,9 +278,9 @@ EXPORT_SYMBOL(drm_fb_helper_debug_leave); * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration * @fb_helper: fbcon to restore * - * This should be called from driver's drm->lastclose callback when implementing - * an fbcon on top of kms using this helper. This ensures that the user isn't - * greeted with a black screen when e.g. X dies. + * This should be called from driver's drm ->lastclose callback + * when implementing an fbcon on top of kms using this helper. This ensures that + * the user isn't greeted with a black screen when e.g. X dies. */ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) { @@ -381,6 +416,11 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) drm_modeset_unlock_all(dev); } +/** + * drm_fb_helper_blank - implementation for ->fb_blank + * @blank: desired blanking state + * @info: fbdev registered by the helper + */ int drm_fb_helper_blank(int blank, struct fb_info *info) { switch (blank) { @@ -424,6 +464,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) kfree(helper->crtc_info); } +/** + * drm_fb_helper_init - initialize a drm_fb_helper structure + * @dev: drm device + * @fb_helper: driver-allocated fbdev helper structure to initialize + * @crtc_count: maximum number of crtcs to support in this fbdev emulation + * @max_conn_count: max connector count + * + * This allocates the structures for the fbdev helper with the given limits. + * Note that this won't yet touch the hardware (through the driver interfaces) + * nor register the fbdev. This is only done in drm_fb_helper_initial_config() + * to allow driver writes more control over the exact init sequence. + * + * Drivers must set fb_helper->funcs before calling + * drm_fb_helper_initial_config(). + * + * RETURNS: + * Zero if everything went ok, nonzero otherwise. + */ int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *fb_helper, int crtc_count, int max_conn_count) @@ -552,6 +610,11 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, return 0; } +/** + * drm_fb_helper_setcmap - implementation for ->fb_setcmap + * @cmap: cmap to set + * @info: fbdev registered by the helper + */ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; @@ -591,6 +654,11 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_setcmap); +/** + * drm_fb_helper_check_var - implementation for ->fb_check_var + * @var: screeninfo to check + * @info: fbdev registered by the helper + */ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -683,7 +751,14 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, } EXPORT_SYMBOL(drm_fb_helper_check_var); -/* this will let fbcon do the mode init */ +/** + * drm_fb_helper_set_par - implementation for ->fb_set_par + * @info: fbdev registered by the helper + * + * This will let fbcon do the mode init and is called at initialization time by + * the fbdev core when registering the driver, and later on through the hotplug + * callback. + */ int drm_fb_helper_set_par(struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; @@ -715,6 +790,11 @@ int drm_fb_helper_set_par(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_set_par); +/** + * drm_fb_helper_pan_display - implementation for ->fb_pan_display + * @var: updated screen information + * @info: fbdev registered by the helper + */ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -753,8 +833,9 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, EXPORT_SYMBOL(drm_fb_helper_pan_display); /* - * Allocates the backing storage through the ->fb_probe callback and then - * registers the fbdev and sets up the panic notifier. + * Allocates the backing storage and sets up the fbdev info structure through + * the ->fb_probe callback and then registers the fbdev and sets up the panic + * notifier. */ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, int preferred_bpp) @@ -876,6 +957,19 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, return 0; } +/** + * drm_fb_helper_fill_fix - initializes fixed fbdev information + * @info: fbdev registered by the helper + * @pitch: desired pitch + * @depth: desired depth + * + * Helper to fill in the fixed fbdev information useful for a non-accelerated + * fbdev emulations. Drivers which support acceleration methods which impose + * additional constraints need to set up their own limits. + * + * Drivers should call this (or their equivalent setup code) from their + * ->fb_probe callback. + */ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, uint32_t depth) { @@ -896,6 +990,20 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, } EXPORT_SYMBOL(drm_fb_helper_fill_fix); +/** + * drm_fb_helper_fill_var - initalizes variable fbdev information + * @info: fbdev instance to set up + * @fb_helper: fb helper instance to use as template + * @fb_width: desired fb width + * @fb_height: desired fb height + * + * Sets up the variable fbdev metainformation from the given fb helper instance + * and the drm framebuffer allocated in fb_helper->fb. + * + * Drivers should call this (or their equivalent setup code) from their + * ->fb_probe callback after having allocated the fbdev backing + * storage framebuffer. + */ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, uint32_t fb_width, uint32_t fb_height) { @@ -1358,18 +1466,23 @@ out: } /** - * drm_helper_initial_config - setup a sane initial connector configuration + * drm_fb_helper_initial_config - setup a sane initial connector configuration * @fb_helper: fb_helper device struct * @bpp_sel: bpp value to use for the framebuffer configuration * - * LOCKING: - * Called at init time by the driver to set up the @fb_helper initial - * configuration, must take the mode config lock. - * * Scans the CRTCs and connectors and tries to put together an initial setup. * At the moment, this is a cloned configuration across all heads with * a new framebuffer object as the backing store. * + * Note that this also registers the fbdev and so allows userspace to call into + * the driver through the fbdev interfaces. + * + * This function will call down into the ->fb_probe callback to let + * the driver allocate and initialize the fbdev info structure and the drm + * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and + * drm_fb_helper_fill_fix() are provided as helpers to setup simple default + * values for the fbdev info structure. + * * RETURNS: * Zero if everything went ok, nonzero otherwise. */ @@ -1400,12 +1513,17 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); * probing all the outputs attached to the fb * @fb_helper: the drm_fb_helper * - * LOCKING: - * Called at runtime, must take mode config lock. - * * Scan the connectors attached to the fb_helper and try to put together a * setup after *notification of a change in output configuration. * + * Called at runtime, takes the mode config locks to be able to check/change the + * modeset configuration. Must be run from process context (which usually means + * either the output polling work or a work item launched from the driver's + * hotplug interrupt). + * + * Note that the driver must ensure that this is only called _after_ the fb has + * been fully set up, i.e. after the call to drm_fb_helper_initial_config. + * * RETURNS: * 0 on success and a non-zero error code otherwise. */ diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 973402d6effe..31e5b97c2e89 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -48,6 +48,18 @@ struct drm_fb_helper_surface_size { u32 surface_depth; }; +/** + * struct drm_fb_helper_funcs - driver callbacks for the fbdev emulation library + * @gamma_set: - Set the given gamma lut register on the given crtc. + * @gamma_get: - Read the given gamma lut register on the given crtc, used to + * save the current lut when force-restoring the fbdev for e.g. + * kdbg. + * @fb_probe: - Driver callback to allocate and initialize the fbdev info + * structure. Futhermore it also needs to allocate the drm + * framebuffer used to back the fbdev. + * + * Driver callbacks used by the fbdev emulation helper library. + */ struct drm_fb_helper_funcs { void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, u16 blue, int regno); From 1b1d5397058f06bc5bd87d43ed93f34b28546ea4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 24 Jan 2013 16:42:07 +0100 Subject: [PATCH 2565/4607] drm/fb-helper: don't sleep for screen unblank when an oopps is in progress Otherwise the system will burn even brighter and worse, leave the user wondering what's going on exactly. Since we already have a panic handler which will (try) to restore the entire fbdev console mode, we can just bail out. Inspired by a patch from Konstantin Khlebnikov. The callchain leading to this, cut&pasted from Konstantin's original patch: callstack: panic() bust_spinlocks(1) unblank_screen() vc->vc_sw->con_blank() fbcon_blank() fb_blank() info->fbops->fb_blank() drm_fb_helper_blank() drm_fb_helper_dpms() drm_modeset_lock_all() mutex_lock(&dev->mode_config.mutex) Note that the entire locking in the fb helper around panic/sysrq and kdbg is ... non-existant. So we have a decent change of blowing up everything. But since reworking this ties in with funny concepts like the fbdev notifier chain or the impressive things which happen around console_lock while oopsing, I'll leave that as an exercise for braver souls than me. v2: Drop the -EBUSY return value I've copied, we don't need it since the we'll take care of things ourselves anyway. Cc: Konstantin Khlebnikov Cc: Andrew Morton References: https://patchwork.kernel.org/patch/1878181/ Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 94f304d8b795..59d6b9bf204b 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -390,6 +390,14 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) struct drm_connector *connector; int i, j; + /* + * fbdev->blank can be called from irq context in case of a panic. + * Since we already have our own special panic handler which will + * restore the fbdev console mode completely, just bail out early. + */ + if (oops_in_progress) + return; + /* * For each CRTC in this fb, turn the connectors on/off. */ From a065b46a01b25d7d364e01e75f7ec2bd9ed5d9cb Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 12 Feb 2013 00:17:09 +0100 Subject: [PATCH 2566/4607] drm/fb-helper: remove unused members of struct drm_fb_helper Spotted by Rob Clark. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- include/drm/drm_fb_helper.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 31e5b97c2e89..c09511625a11 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -77,9 +77,7 @@ struct drm_fb_helper_connector { struct drm_fb_helper { struct drm_framebuffer *fb; - struct drm_framebuffer *saved_fb; struct drm_device *dev; - struct drm_display_mode *mode; int crtc_count; struct drm_fb_helper_crtc *crtc_info; int connector_count; From a36516b016f52f0f6e5284025e3487f63b4be33b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 3 Jan 2013 16:16:57 +0530 Subject: [PATCH 2567/4607] mfd: palmas: Add rtc irq number as irq resource for palmas-rtc Palma RTC is capable of generating alarm interrupt. Pass the alarm interrupt as IRQ_RESOURCE for palmas-rtc sub device driver so that rtc driver can get irq as platform_get_irq(). Also pass the irq domain in mfd_add_devices() to properly offset the irqs for sub devices. This is needed when adding device through DT. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/palmas.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index 6ffd7a2affdc..bbdbc50a3cca 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -39,6 +39,14 @@ enum palmas_ids { PALMAS_USB_ID, }; +static struct resource palmas_rtc_resources[] = { + { + .start = PALMAS_RTC_ALARM_IRQ, + .end = PALMAS_RTC_ALARM_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + static const struct mfd_cell palmas_children[] = { { .name = "palmas-pmic", @@ -59,6 +67,8 @@ static const struct mfd_cell palmas_children[] = { { .name = "palmas-rtc", .id = PALMAS_RTC_ID, + .resources = &palmas_rtc_resources[0], + .num_resources = ARRAY_SIZE(palmas_rtc_resources), }, { .name = "palmas-pwrbutton", @@ -456,8 +466,8 @@ static int palmas_i2c_probe(struct i2c_client *i2c, ret = mfd_add_devices(palmas->dev, -1, children, ARRAY_SIZE(palmas_children), - NULL, regmap_irq_chip_get_base(palmas->irq_data), - NULL); + NULL, 0, + regmap_irq_get_domain(palmas->irq_data)); kfree(children); if (ret < 0) From 60c185f059c88ad4b9b170b1f9322e3adcccca07 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 3 Jan 2013 16:16:58 +0530 Subject: [PATCH 2568/4607] mfd: palmas: Add APIs to access the Palmas' registers Palmas register set is divided into different blocks (base and offset) and hence different i2c addresses. The i2c address offsets are derived from base address of block of registers. Add inline APIs to access the Palma's registers which takes the base of register block and register offset. The i2c address offset is derived from the base address of register blocks. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- include/linux/mfd/palmas.h | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 29f6616e12f0..a4d13d7cd001 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -2789,4 +2789,56 @@ enum usb_irq_events { #define PALMAS_GPADC_TRIM15 0xE #define PALMAS_GPADC_TRIM16 0xF +static inline int palmas_read(struct palmas *palmas, unsigned int base, + unsigned int reg, unsigned int *val) +{ + unsigned int addr = PALMAS_BASE_TO_REG(base, reg); + int slave_id = PALMAS_BASE_TO_SLAVE(base); + + return regmap_read(palmas->regmap[slave_id], addr, val); +} + +static inline int palmas_write(struct palmas *palmas, unsigned int base, + unsigned int reg, unsigned int value) +{ + unsigned int addr = PALMAS_BASE_TO_REG(base, reg); + int slave_id = PALMAS_BASE_TO_SLAVE(base); + + return regmap_write(palmas->regmap[slave_id], addr, value); +} + +static inline int palmas_bulk_write(struct palmas *palmas, unsigned int base, + unsigned int reg, const void *val, size_t val_count) +{ + unsigned int addr = PALMAS_BASE_TO_REG(base, reg); + int slave_id = PALMAS_BASE_TO_SLAVE(base); + + return regmap_bulk_write(palmas->regmap[slave_id], addr, + val, val_count); +} + +static inline int palmas_bulk_read(struct palmas *palmas, unsigned int base, + unsigned int reg, void *val, size_t val_count) +{ + unsigned int addr = PALMAS_BASE_TO_REG(base, reg); + int slave_id = PALMAS_BASE_TO_SLAVE(base); + + return regmap_bulk_read(palmas->regmap[slave_id], addr, + val, val_count); +} + +static inline int palmas_update_bits(struct palmas *palmas, unsigned int base, + unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int addr = PALMAS_BASE_TO_REG(base, reg); + int slave_id = PALMAS_BASE_TO_SLAVE(base); + + return regmap_update_bits(palmas->regmap[slave_id], addr, mask, val); +} + +static inline int palmas_irq_get_virq(struct palmas *palmas, int irq) +{ + return regmap_irq_get_virq(palmas->irq_data, irq); +} + #endif /* __LINUX_MFD_PALMAS_H */ From 3d50a278527154974f4240584da691db7424956d Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 3 Jan 2013 16:16:59 +0530 Subject: [PATCH 2569/4607] gpio: palmas: Add support for Palmas GPIO Add gpio driver for TI Palmas series PMIC. This has 8 gpio which can work as input/output. Signed-off-by: Laxman Dewangan Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/gpio/Kconfig | 7 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-palmas.c | 184 +++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 drivers/gpio/gpio-palmas.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 682de754d63f..40a0ec3b7c40 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -663,6 +663,13 @@ config GPIO_AB8500 help Select this to enable the AB8500 IC GPIO driver +config GPIO_PALMAS + bool "TI PALMAS series PMICs GPIO" + depends on MFD_PALMAS + help + Select this option to enable GPIO driver for the TI PALMAS + series chip family. + config GPIO_TPS6586X bool "TPS6586X GPIO" depends on MFD_TPS6586X diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c5aebd008dde..8962c5f7b026 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o +obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c new file mode 100644 index 000000000000..e3a4e56f5a42 --- /dev/null +++ b/drivers/gpio/gpio-palmas.c @@ -0,0 +1,184 @@ +/* + * TI Palma series PMIC's GPIO driver. + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +struct palmas_gpio { + struct gpio_chip gpio_chip; + struct palmas *palmas; +}; + +static inline struct palmas_gpio *to_palmas_gpio(struct gpio_chip *chip) +{ + return container_of(chip, struct palmas_gpio, gpio_chip); +} + +static int palmas_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct palmas_gpio *pg = to_palmas_gpio(gc); + struct palmas *palmas = pg->palmas; + unsigned int val; + int ret; + + ret = palmas_read(palmas, PALMAS_GPIO_BASE, PALMAS_GPIO_DATA_IN, &val); + if (ret < 0) { + dev_err(gc->dev, "GPIO_DATA_IN read failed, err = %d\n", ret); + return ret; + } + return !!(val & BIT(offset)); +} + +static void palmas_gpio_set(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct palmas_gpio *pg = to_palmas_gpio(gc); + struct palmas *palmas = pg->palmas; + int ret; + + if (value) + ret = palmas_write(palmas, PALMAS_GPIO_BASE, + PALMAS_GPIO_SET_DATA_OUT, BIT(offset)); + else + ret = palmas_write(palmas, PALMAS_GPIO_BASE, + PALMAS_GPIO_CLEAR_DATA_OUT, BIT(offset)); + if (ret < 0) + dev_err(gc->dev, "%s write failed, err = %d\n", + (value) ? "GPIO_SET_DATA_OUT" : "GPIO_CLEAR_DATA_OUT", + ret); +} + +static int palmas_gpio_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct palmas_gpio *pg = to_palmas_gpio(gc); + struct palmas *palmas = pg->palmas; + int ret; + + /* Set the initial value */ + palmas_gpio_set(gc, offset, value); + + ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, + PALMAS_GPIO_DATA_DIR, BIT(offset), BIT(offset)); + if (ret < 0) + dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret); + return ret; +} + +static int palmas_gpio_input(struct gpio_chip *gc, unsigned offset) +{ + struct palmas_gpio *pg = to_palmas_gpio(gc); + struct palmas *palmas = pg->palmas; + int ret; + + ret = palmas_update_bits(palmas, PALMAS_GPIO_BASE, + PALMAS_GPIO_DATA_DIR, BIT(offset), 0); + if (ret < 0) + dev_err(gc->dev, "GPIO_DATA_DIR write failed, err = %d\n", ret); + return ret; +} + +static int palmas_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct palmas_gpio *pg = to_palmas_gpio(gc); + struct palmas *palmas = pg->palmas; + + return palmas_irq_get_virq(palmas, PALMAS_GPIO_0_IRQ + offset); +} + +static int palmas_gpio_probe(struct platform_device *pdev) +{ + struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); + struct palmas_platform_data *palmas_pdata; + struct palmas_gpio *palmas_gpio; + int ret; + + palmas_gpio = devm_kzalloc(&pdev->dev, + sizeof(*palmas_gpio), GFP_KERNEL); + if (!palmas_gpio) { + dev_err(&pdev->dev, "Could not allocate palmas_gpio\n"); + return -ENOMEM; + } + + palmas_gpio->palmas = palmas; + palmas_gpio->gpio_chip.owner = THIS_MODULE; + palmas_gpio->gpio_chip.label = dev_name(&pdev->dev); + palmas_gpio->gpio_chip.ngpio = 8; + palmas_gpio->gpio_chip.can_sleep = 1; + palmas_gpio->gpio_chip.direction_input = palmas_gpio_input; + palmas_gpio->gpio_chip.direction_output = palmas_gpio_output; + palmas_gpio->gpio_chip.to_irq = palmas_gpio_to_irq; + palmas_gpio->gpio_chip.set = palmas_gpio_set; + palmas_gpio->gpio_chip.get = palmas_gpio_get; + palmas_gpio->gpio_chip.dev = &pdev->dev; +#ifdef CONFIG_OF_GPIO + palmas_gpio->gpio_chip.of_node = palmas->dev->of_node; +#endif + palmas_pdata = dev_get_platdata(palmas->dev); + if (palmas_pdata && palmas_pdata->gpio_base) + palmas_gpio->gpio_chip.base = palmas_pdata->gpio_base; + else + palmas_gpio->gpio_chip.base = -1; + + ret = gpiochip_add(&palmas_gpio->gpio_chip); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, palmas_gpio); + return ret; +} + +static int palmas_gpio_remove(struct platform_device *pdev) +{ + struct palmas_gpio *palmas_gpio = platform_get_drvdata(pdev); + + return gpiochip_remove(&palmas_gpio->gpio_chip); +} + +static struct platform_driver palmas_gpio_driver = { + .driver.name = "palmas-gpio", + .driver.owner = THIS_MODULE, + .probe = palmas_gpio_probe, + .remove = palmas_gpio_remove, +}; + +static int __init palmas_gpio_init(void) +{ + return platform_driver_register(&palmas_gpio_driver); +} +subsys_initcall(palmas_gpio_init); + +static void __exit palmas_gpio_exit(void) +{ + platform_driver_unregister(&palmas_gpio_driver); +} +module_exit(palmas_gpio_exit); + +MODULE_ALIAS("platform:palmas-gpio"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_DESCRIPTION("GPIO driver for TI Palmas series PMICs"); +MODULE_LICENSE("GPL v2"); From 0101e53cbb1c653738de8dfddc80c0faa5a4fd39 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 3 Jan 2013 16:17:00 +0530 Subject: [PATCH 2570/4607] rtc: palmas: Add RTC driver Palmas series PMIC TI Palmas series PMIC support the RTC and alarm functionality. Add RTC driver with alarm support for this device. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-palmas.c | 339 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 350 insertions(+) create mode 100644 drivers/rtc/rtc-palmas.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 923a9da9c829..f11f746f7a35 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -269,6 +269,16 @@ config RTC_DRV_X1205 This driver can also be built as a module. If so, the module will be called rtc-x1205. +config RTC_DRV_PALMAS + tristate "TI Palmas RTC driver" + depends on MFD_PALMAS + help + If you say yes here you get support for the RTC of TI PALMA series PMIC + chips. + + This driver can also be built as a module. If so, the module + will be called rtc-palma. + config RTC_DRV_PCF8523 tristate "NXP PCF8523" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 4418ef3f9ecc..3b66c75f6c76 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o +obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c new file mode 100644 index 000000000000..59c42986254e --- /dev/null +++ b/drivers/rtc/rtc-palmas.c @@ -0,0 +1,339 @@ +/* + * rtc-palmas.c -- Palmas Real Time Clock driver. + + * RTC driver for TI Palma series devices like TPS65913, + * TPS65914 power management IC. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; 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., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct palmas_rtc { + struct rtc_device *rtc; + struct device *dev; + unsigned int irq; +}; + +/* Total number of RTC registers needed to set time*/ +#define PALMAS_NUM_TIME_REGS (PALMAS_YEARS_REG - PALMAS_SECONDS_REG + 1) + +static int palmas_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + /* Copy RTC counting registers to static registers or latches */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_GET_TIME, PALMAS_RTC_CTRL_REG_GET_TIME); + if (ret < 0) { + dev_err(dev, "RTC CTRL reg update failed, err: %d\n", ret); + return ret; + } + + ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG, + rtc_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_SECONDS reg read failed, err = %d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(rtc_data[0]); + tm->tm_min = bcd2bin(rtc_data[1]); + tm->tm_hour = bcd2bin(rtc_data[2]); + tm->tm_mday = bcd2bin(rtc_data[3]); + tm->tm_mon = bcd2bin(rtc_data[4]) - 1; + tm->tm_year = bcd2bin(rtc_data[5]) + 100; + + return ret; +} + +static int palmas_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char rtc_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + rtc_data[0] = bin2bcd(tm->tm_sec); + rtc_data[1] = bin2bcd(tm->tm_min); + rtc_data[2] = bin2bcd(tm->tm_hour); + rtc_data[3] = bin2bcd(tm->tm_mday); + rtc_data[4] = bin2bcd(tm->tm_mon + 1); + rtc_data[5] = bin2bcd(tm->tm_year - 100); + + /* Stop RTC while updating the RTC time registers */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_STOP_RTC, 0); + if (ret < 0) { + dev_err(dev, "RTC stop failed, err = %d\n", ret); + return ret; + } + + ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG, + rtc_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_SECONDS reg write failed, err = %d\n", ret); + return ret; + } + + /* Start back RTC */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_STOP_RTC, PALMAS_RTC_CTRL_REG_STOP_RTC); + if (ret < 0) + dev_err(dev, "RTC start failed, err = %d\n", ret); + return ret; +} + +static int palmas_rtc_alarm_irq_enable(struct device *dev, unsigned enabled) +{ + struct palmas *palmas = dev_get_drvdata(dev->parent); + u8 val; + + val = enabled ? PALMAS_RTC_INTERRUPTS_REG_IT_ALARM : 0; + return palmas_write(palmas, PALMAS_RTC_BASE, + PALMAS_RTC_INTERRUPTS_REG, val); +} + +static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[PALMAS_NUM_TIME_REGS]; + u32 int_val; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, + PALMAS_ALARM_SECONDS_REG, + alarm_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "RTC_ALARM_SECONDS read failed, err = %d\n", ret); + return ret; + } + + alm->time.tm_sec = bcd2bin(alarm_data[0]); + alm->time.tm_min = bcd2bin(alarm_data[1]); + alm->time.tm_hour = bcd2bin(alarm_data[2]); + alm->time.tm_mday = bcd2bin(alarm_data[3]); + alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1; + alm->time.tm_year = bcd2bin(alarm_data[5]) + 100; + + ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG, + &int_val); + if (ret < 0) { + dev_err(dev, "RTC_INTERRUPTS reg read failed, err = %d\n", ret); + return ret; + } + + if (int_val & PALMAS_RTC_INTERRUPTS_REG_IT_ALARM) + alm->enabled = 1; + return ret; +} + +static int palmas_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + unsigned char alarm_data[PALMAS_NUM_TIME_REGS]; + struct palmas *palmas = dev_get_drvdata(dev->parent); + int ret; + + ret = palmas_rtc_alarm_irq_enable(dev, 0); + if (ret < 0) { + dev_err(dev, "Disable RTC alarm failed\n"); + return ret; + } + + alarm_data[0] = bin2bcd(alm->time.tm_sec); + alarm_data[1] = bin2bcd(alm->time.tm_min); + alarm_data[2] = bin2bcd(alm->time.tm_hour); + alarm_data[3] = bin2bcd(alm->time.tm_mday); + alarm_data[4] = bin2bcd(alm->time.tm_mon + 1); + alarm_data[5] = bin2bcd(alm->time.tm_year - 100); + + ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, + PALMAS_ALARM_SECONDS_REG, alarm_data, PALMAS_NUM_TIME_REGS); + if (ret < 0) { + dev_err(dev, "ALARM_SECONDS_REG write failed, err = %d\n", ret); + return ret; + } + + if (alm->enabled) + ret = palmas_rtc_alarm_irq_enable(dev, 1); + return ret; +} + +static int palmas_clear_interrupts(struct device *dev) +{ + struct palmas *palmas = dev_get_drvdata(dev->parent); + unsigned int rtc_reg; + int ret; + + ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, + &rtc_reg); + if (ret < 0) { + dev_err(dev, "RTC_STATUS read failed, err = %d\n", ret); + return ret; + } + + ret = palmas_write(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG, + rtc_reg); + if (ret < 0) { + dev_err(dev, "RTC_STATUS write failed, err = %d\n", ret); + return ret; + } + return 0; +} + +static irqreturn_t palmas_rtc_interrupt(int irq, void *context) +{ + struct palmas_rtc *palmas_rtc = context; + struct device *dev = palmas_rtc->dev; + int ret; + + ret = palmas_clear_interrupts(dev); + if (ret < 0) { + dev_err(dev, "RTC interrupt clear failed, err = %d\n", ret); + return IRQ_NONE; + } + + rtc_update_irq(palmas_rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static struct rtc_class_ops palmas_rtc_ops = { + .read_time = palmas_rtc_read_time, + .set_time = palmas_rtc_set_time, + .read_alarm = palmas_rtc_read_alarm, + .set_alarm = palmas_rtc_set_alarm, + .alarm_irq_enable = palmas_rtc_alarm_irq_enable, +}; + +static int palmas_rtc_probe(struct platform_device *pdev) +{ + struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); + struct palmas_rtc *palmas_rtc = NULL; + int ret; + + palmas_rtc = devm_kzalloc(&pdev->dev, sizeof(struct palmas_rtc), + GFP_KERNEL); + if (!palmas_rtc) + return -ENOMEM; + + /* Clear pending interrupts */ + ret = palmas_clear_interrupts(&pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "clear RTC int failed, err = %d\n", ret); + return ret; + } + + palmas_rtc->dev = &pdev->dev; + platform_set_drvdata(pdev, palmas_rtc); + + /* Start RTC */ + ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG, + PALMAS_RTC_CTRL_REG_STOP_RTC, + PALMAS_RTC_CTRL_REG_STOP_RTC); + if (ret < 0) { + dev_err(&pdev->dev, "RTC_CTRL write failed, err = %d\n", ret); + return ret; + } + + palmas_rtc->irq = platform_get_irq(pdev, 0); + + palmas_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, + &palmas_rtc_ops, THIS_MODULE); + if (IS_ERR(palmas_rtc->rtc)) { + ret = PTR_ERR(palmas_rtc->rtc); + dev_err(&pdev->dev, "RTC register failed, err = %d\n", ret); + return ret; + } + + ret = request_threaded_irq(palmas_rtc->irq, NULL, + palmas_rtc_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT | + IRQF_EARLY_RESUME, + dev_name(&pdev->dev), palmas_rtc); + if (ret < 0) { + dev_err(&pdev->dev, "IRQ request failed, err = %d\n", ret); + rtc_device_unregister(palmas_rtc->rtc); + return ret; + } + + device_set_wakeup_capable(&pdev->dev, 1); + return 0; +} + +static int palmas_rtc_remove(struct platform_device *pdev) +{ + struct palmas_rtc *palmas_rtc = platform_get_drvdata(pdev); + + palmas_rtc_alarm_irq_enable(&pdev->dev, 0); + free_irq(palmas_rtc->irq, palmas_rtc); + rtc_device_unregister(palmas_rtc->rtc); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int palmas_rtc_suspend(struct device *dev) +{ + struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(palmas_rtc->irq); + return 0; +} + +static int palmas_rtc_resume(struct device *dev) +{ + struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(palmas_rtc->irq); + return 0; +} +#endif + +static const struct dev_pm_ops palmas_rtc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(palmas_rtc_suspend, palmas_rtc_resume) +}; + +static struct platform_driver palmas_rtc_driver = { + .probe = palmas_rtc_probe, + .remove = palmas_rtc_remove, + .driver = { + .owner = THIS_MODULE, + .name = "palmas-rtc", + .pm = &palmas_rtc_pm_ops, + }, +}; + +module_platform_driver(palmas_rtc_driver); + +MODULE_ALIAS("platform:palmas_rtc"); +MODULE_DESCRIPTION("TI PALMAS series RTC driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); From 025d982079502b1d6c7c428504eb64c5059a2e80 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 11 Dec 2012 16:51:39 +0900 Subject: [PATCH 2571/4607] mfd: wm5102: Mark only extant DSP registers volatile Since regmap sometimes uses volatile as a proxy for readable simply having a blanket condition can mark too many registers as readable. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 1133a64c2dc9..ee0a109f5ce9 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1831,9 +1831,6 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) static bool wm5102_volatile_register(struct device *dev, unsigned int reg) { - if (reg > 0xffff) - return true; - switch (reg) { case ARIZONA_SOFTWARE_RESET: case ARIZONA_DEVICE_REVISION: @@ -1878,7 +1875,13 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_MIC_DETECT_3: return true; default: - return false; + if ((reg >= 0x100000 && reg < 0x106000) || + (reg >= 0x180000 && reg < 0x180800) || + (reg >= 0x190000 && reg < 0x194800) || + (reg >= 0x1a8000 && reg < 0x1a9800)) + return true; + else + return false; } } From d7768111a98bcf865e1e14d8c663789f6e21393d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 20 Dec 2012 15:38:03 +0000 Subject: [PATCH 2572/4607] mfd: arizona: Register MICVDD supply first to ensure no retries Not strictly required as probe deferral will take care of everything but it makes boot a little smoother. Reported-by: Ryo Tsutsui Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 222c03a5ddc0..1ab02a7d2b05 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -275,19 +275,19 @@ static struct mfd_cell early_devs[] = { }; static struct mfd_cell wm5102_devs[] = { + { .name = "arizona-micsupp" }, { .name = "arizona-extcon" }, { .name = "arizona-gpio" }, { .name = "arizona-haptics" }, - { .name = "arizona-micsupp" }, { .name = "arizona-pwm" }, { .name = "wm5102-codec" }, }; static struct mfd_cell wm5110_devs[] = { + { .name = "arizona-micsupp" }, { .name = "arizona-extcon" }, { .name = "arizona-gpio" }, { .name = "arizona-haptics" }, - { .name = "arizona-micsupp" }, { .name = "arizona-pwm" }, { .name = "wm5110-codec" }, }; From 71f39e5c087418fb63a57f74478c3e32899592cd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 2 Jan 2013 14:30:18 +0000 Subject: [PATCH 2573/4607] mfd: wm5102: Update rev B patch for latest evaluation The latest evaluation of the revision B silicon suggests some changes to the tuning applied for optimal performance. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index ee0a109f5ce9..74f9853b6dc4 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -59,12 +59,13 @@ static const struct reg_default wm5102_reva_patch[] = { static const struct reg_default wm5102_revb_patch[] = { { 0x80, 0x0003 }, { 0x081, 0xE022 }, - { 0x410, 0x6080 }, - { 0x418, 0x6080 }, - { 0x420, 0x6080 }, + { 0x410, 0x4080 }, + { 0x418, 0x4080 }, + { 0x420, 0x4080 }, { 0x428, 0xC000 }, - { 0x441, 0x8014 }, + { 0x4B0, 0x0066 }, { 0x458, 0x000b }, + { 0x212, 0x0000 }, { 0x80, 0x0000 }, }; From f1c68e4dd455eeaf30647a52595a7daf62fc5492 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 8 Jan 2013 14:01:22 +0530 Subject: [PATCH 2574/4607] mfd: wm8994: Use devm_regulator_bulk_get API devm_regulator_bulk_get is device managed and saves some cleanup and exit code. Signed-off-by: Sachin Kamat Acked-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 57c488d42d3e..803e93fae56a 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -467,7 +467,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) goto err; } - ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies, + ret = devm_regulator_bulk_get(wm8994->dev, wm8994->num_supplies, wm8994->supplies); if (ret != 0) { dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret); @@ -478,7 +478,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) wm8994->supplies); if (ret != 0) { dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret); - goto err_get; + goto err; } ret = wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET); @@ -658,8 +658,6 @@ err_irq: err_enable: regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); -err_get: - regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); err: mfd_remove_devices(wm8994->dev); return ret; @@ -672,7 +670,6 @@ static void wm8994_device_exit(struct wm8994 *wm8994) wm8994_irq_exit(wm8994); regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); - regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); } static const struct of_device_id wm8994_of_match[] = { From 0cc9d08d3dca174ee712044e299732f26fdab22d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 27 Jan 2013 12:07:23 +0800 Subject: [PATCH 2575/4607] mfd: wm5102: Mark DSP memory regions as volatile and readable We can cache some of them but this is simpler for now. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 74f9853b6dc4..6655c9e89e92 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1826,7 +1826,13 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_3: return true; default: - return false; + if ((reg >= 0x100000 && reg < 0x106000) || + (reg >= 0x180000 && reg < 0x180800) || + (reg >= 0x190000 && reg < 0x194800) || + (reg >= 0x1a8000 && reg < 0x1a9800)) + return true; + else + return false; } } From 595e5bf75cea0664eeb63d8df20716f66f4aa459 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 27 Jan 2013 12:07:30 +0800 Subject: [PATCH 2576/4607] mfd: wm5102: Refresh register defaults The WM5102 register defaults are not up to date with the current register map, synchronise them with those for current devices. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 95 +++++++++++++------------------------ 1 file changed, 33 insertions(+), 62 deletions(-) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 6655c9e89e92..ff8f0804dde3 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -225,11 +225,9 @@ const struct regmap_irq_chip wm5102_irq = { static const struct reg_default wm5102_reg_default[] = { { 0x00000008, 0x0019 }, /* R8 - Ctrl IF SPI CFG 1 */ { 0x00000009, 0x0001 }, /* R9 - Ctrl IF I2C1 CFG 1 */ - { 0x0000000D, 0x0000 }, /* R13 - Ctrl IF Status 1 */ { 0x00000016, 0x0000 }, /* R22 - Write Sequencer Ctrl 0 */ { 0x00000017, 0x0000 }, /* R23 - Write Sequencer Ctrl 1 */ { 0x00000018, 0x0000 }, /* R24 - Write Sequencer Ctrl 2 */ - { 0x0000001A, 0x0000 }, /* R26 - Write Sequencer PROM */ { 0x00000020, 0x0000 }, /* R32 - Tone Generator 1 */ { 0x00000021, 0x1000 }, /* R33 - Tone Generator 2 */ { 0x00000022, 0x0000 }, /* R34 - Tone Generator 3 */ @@ -244,12 +242,14 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000062, 0x01FF }, /* R98 - Sample Rate Sequence Select 2 */ { 0x00000063, 0x01FF }, /* R99 - Sample Rate Sequence Select 3 */ { 0x00000064, 0x01FF }, /* R100 - Sample Rate Sequence Select 4 */ - { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 1 */ - { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 2 */ - { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 3 */ - { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 4 */ - { 0x0000006C, 0x01FF }, /* R108 - Always On Triggers Sequence Select 5 */ - { 0x0000006D, 0x01FF }, /* R109 - Always On Triggers Sequence Select 6 */ + { 0x00000066, 0x01FF }, /* R102 - Always On Triggers Sequence Select 1 */ + { 0x00000067, 0x01FF }, /* R103 - Always On Triggers Sequence Select 2 */ + { 0x00000068, 0x01FF }, /* R104 - Always On Triggers Sequence Select 3 */ + { 0x00000069, 0x01FF }, /* R105 - Always On Triggers Sequence Select 4 */ + { 0x0000006A, 0x01FF }, /* R106 - Always On Triggers Sequence Select 5 */ + { 0x0000006B, 0x01FF }, /* R107 - Always On Triggers Sequence Select 6 */ + { 0x0000006E, 0x01FF }, /* R110 - Trigger Sequence Select 32 */ + { 0x0000006F, 0x01FF }, /* R111 - Trigger Sequence Select 33 */ { 0x00000070, 0x0000 }, /* R112 - Comfort Noise Generator */ { 0x00000090, 0x0000 }, /* R144 - Haptics Control 1 */ { 0x00000091, 0x7FFF }, /* R145 - Haptics Control 2 */ @@ -259,13 +259,14 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000095, 0x0000 }, /* R149 - Haptics phase 2 duration */ { 0x00000096, 0x0000 }, /* R150 - Haptics phase 3 intensity */ { 0x00000097, 0x0000 }, /* R151 - Haptics phase 3 duration */ - { 0x00000100, 0x0001 }, /* R256 - Clock 32k 1 */ + { 0x00000100, 0x0002 }, /* R256 - Clock 32k 1 */ { 0x00000101, 0x0304 }, /* R257 - System Clock 1 */ { 0x00000102, 0x0011 }, /* R258 - Sample rate 1 */ { 0x00000103, 0x0011 }, /* R259 - Sample rate 2 */ { 0x00000104, 0x0011 }, /* R260 - Sample rate 3 */ { 0x00000112, 0x0305 }, /* R274 - Async clock 1 */ { 0x00000113, 0x0011 }, /* R275 - Async sample rate 1 */ + { 0x00000114, 0x0011 }, /* R276 - Async sample rate 2 */ { 0x00000149, 0x0000 }, /* R329 - Output system clock */ { 0x0000014A, 0x0000 }, /* R330 - Output async clock */ { 0x00000152, 0x0000 }, /* R338 - Rate Estimator 1 */ @@ -274,13 +275,14 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */ { 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */ { 0x00000161, 0x0000 }, /* R353 - Dynamic Frequency Scaling 1 */ - { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */ + { 0x00000171, 0x0002 }, /* R369 - FLL1 Control 1 */ { 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */ { 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */ { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */ { 0x00000175, 0x0004 }, /* R373 - FLL1 Control 5 */ { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */ { 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */ + { 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */ { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */ { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */ { 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */ @@ -296,6 +298,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000195, 0x0004 }, /* R405 - FLL2 Control 5 */ { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */ { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */ + { 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */ { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */ { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */ { 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */ @@ -311,8 +314,13 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */ { 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */ { 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */ + { 0x00000225, 0x0400 }, /* R549 - HP Ctrl 1L */ + { 0x00000226, 0x0400 }, /* R550 - HP Ctrl 1R */ { 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */ { 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */ + { 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */ + { 0x0000029F, 0x0000 }, /* R671 - Headphone Detect Test */ + { 0x000002A2, 0x0000 }, /* R674 - Micd Clamp control */ { 0x000002A3, 0x1102 }, /* R675 - Mic Detect 1 */ { 0x000002A4, 0x009F }, /* R676 - Mic Detect 2 */ { 0x000002A5, 0x0000 }, /* R677 - Mic Detect 3 */ @@ -343,53 +351,44 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */ { 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */ { 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */ - { 0x00000410, 0x0080 }, /* R1040 - Output Path Config 1L */ + { 0x00000410, 0x4080 }, /* R1040 - Output Path Config 1L */ { 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */ - { 0x00000412, 0x0080 }, /* R1042 - DAC Volume Limit 1L */ + { 0x00000412, 0x0081 }, /* R1042 - DAC Volume Limit 1L */ { 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */ { 0x00000414, 0x0080 }, /* R1044 - Output Path Config 1R */ { 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */ - { 0x00000416, 0x0080 }, /* R1046 - DAC Volume Limit 1R */ + { 0x00000416, 0x0081 }, /* R1046 - DAC Volume Limit 1R */ { 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */ - { 0x00000418, 0x0080 }, /* R1048 - Output Path Config 2L */ + { 0x00000418, 0x4080 }, /* R1048 - Output Path Config 2L */ { 0x00000419, 0x0180 }, /* R1049 - DAC Digital Volume 2L */ - { 0x0000041A, 0x0080 }, /* R1050 - DAC Volume Limit 2L */ + { 0x0000041A, 0x0081 }, /* R1050 - DAC Volume Limit 2L */ { 0x0000041B, 0x0004 }, /* R1051 - Noise Gate Select 2L */ { 0x0000041C, 0x0080 }, /* R1052 - Output Path Config 2R */ { 0x0000041D, 0x0180 }, /* R1053 - DAC Digital Volume 2R */ - { 0x0000041E, 0x0080 }, /* R1054 - DAC Volume Limit 2R */ + { 0x0000041E, 0x0081 }, /* R1054 - DAC Volume Limit 2R */ { 0x0000041F, 0x0008 }, /* R1055 - Noise Gate Select 2R */ - { 0x00000420, 0x0080 }, /* R1056 - Output Path Config 3L */ + { 0x00000420, 0x4080 }, /* R1056 - Output Path Config 3L */ { 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */ - { 0x00000422, 0x0080 }, /* R1058 - DAC Volume Limit 3L */ + { 0x00000422, 0x0081 }, /* R1058 - DAC Volume Limit 3L */ { 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */ - { 0x00000424, 0x0080 }, /* R1060 - Output Path Config 3R */ - { 0x00000425, 0x0180 }, /* R1061 - DAC Digital Volume 3R */ - { 0x00000426, 0x0080 }, /* R1062 - DAC Volume Limit 3R */ - { 0x00000428, 0x0000 }, /* R1064 - Output Path Config 4L */ + { 0x00000428, 0xC000 }, /* R1064 - Output Path Config 4L */ { 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */ - { 0x0000042A, 0x0080 }, /* R1066 - Out Volume 4L */ + { 0x0000042A, 0x0081 }, /* R1066 - Out Volume 4L */ { 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */ - { 0x0000042C, 0x0000 }, /* R1068 - Output Path Config 4R */ { 0x0000042D, 0x0180 }, /* R1069 - DAC Digital Volume 4R */ - { 0x0000042E, 0x0080 }, /* R1070 - Out Volume 4R */ + { 0x0000042E, 0x0081 }, /* R1070 - Out Volume 4R */ { 0x0000042F, 0x0080 }, /* R1071 - Noise Gate Select 4R */ { 0x00000430, 0x0000 }, /* R1072 - Output Path Config 5L */ { 0x00000431, 0x0180 }, /* R1073 - DAC Digital Volume 5L */ - { 0x00000432, 0x0080 }, /* R1074 - DAC Volume Limit 5L */ + { 0x00000432, 0x0081 }, /* R1074 - DAC Volume Limit 5L */ { 0x00000433, 0x0100 }, /* R1075 - Noise Gate Select 5L */ - { 0x00000434, 0x0000 }, /* R1076 - Output Path Config 5R */ { 0x00000435, 0x0180 }, /* R1077 - DAC Digital Volume 5R */ - { 0x00000436, 0x0080 }, /* R1078 - DAC Volume Limit 5R */ - { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */ + { 0x00000436, 0x0081 }, /* R1078 - DAC Volume Limit 5R */ + { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */ { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */ { 0x00000458, 0x0001 }, /* R1112 - Noise Gate Control */ { 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */ { 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */ - { 0x000004DC, 0x0000 }, /* R1244 - DAC comp 1 */ - { 0x000004DD, 0x0000 }, /* R1245 - DAC comp 2 */ - { 0x000004DE, 0x0000 }, /* R1246 - DAC comp 3 */ - { 0x000004DF, 0x0000 }, /* R1247 - DAC comp 4 */ { 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */ { 0x00000501, 0x0008 }, /* R1281 - AIF1 Tx Pin Ctrl */ { 0x00000502, 0x0000 }, /* R1282 - AIF1 Rx Pin Ctrl */ @@ -417,7 +416,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000518, 0x0007 }, /* R1304 - AIF1 Frame Ctrl 18 */ { 0x00000519, 0x0000 }, /* R1305 - AIF1 Tx Enables */ { 0x0000051A, 0x0000 }, /* R1306 - AIF1 Rx Enables */ - { 0x0000051B, 0x0000 }, /* R1307 - AIF1 Force Write */ { 0x00000540, 0x000C }, /* R1344 - AIF2 BCLK Ctrl */ { 0x00000541, 0x0008 }, /* R1345 - AIF2 Tx Pin Ctrl */ { 0x00000542, 0x0000 }, /* R1346 - AIF2 Rx Pin Ctrl */ @@ -433,7 +431,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */ { 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */ { 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */ - { 0x0000055B, 0x0000 }, /* R1371 - AIF2 Force Write */ { 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */ { 0x00000581, 0x0008 }, /* R1409 - AIF3 Tx Pin Ctrl */ { 0x00000582, 0x0000 }, /* R1410 - AIF3 Rx Pin Ctrl */ @@ -449,7 +446,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000592, 0x0001 }, /* R1426 - AIF3 Frame Ctrl 12 */ { 0x00000599, 0x0000 }, /* R1433 - AIF3 Tx Enables */ { 0x0000059A, 0x0000 }, /* R1434 - AIF3 Rx Enables */ - { 0x0000059B, 0x0000 }, /* R1435 - AIF3 Force Write */ { 0x000005E3, 0x0004 }, /* R1507 - SLIMbus Framer Ref Gear */ { 0x000005E5, 0x0000 }, /* R1509 - SLIMbus Rates 1 */ { 0x000005E6, 0x0000 }, /* R1510 - SLIMbus Rates 2 */ @@ -773,22 +769,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x000008CD, 0x0080 }, /* R2253 - DRC1RMIX Input 3 Volume */ { 0x000008CE, 0x0000 }, /* R2254 - DRC1RMIX Input 4 Source */ { 0x000008CF, 0x0080 }, /* R2255 - DRC1RMIX Input 4 Volume */ - { 0x000008D0, 0x0000 }, /* R2256 - DRC2LMIX Input 1 Source */ - { 0x000008D1, 0x0080 }, /* R2257 - DRC2LMIX Input 1 Volume */ - { 0x000008D2, 0x0000 }, /* R2258 - DRC2LMIX Input 2 Source */ - { 0x000008D3, 0x0080 }, /* R2259 - DRC2LMIX Input 2 Volume */ - { 0x000008D4, 0x0000 }, /* R2260 - DRC2LMIX Input 3 Source */ - { 0x000008D5, 0x0080 }, /* R2261 - DRC2LMIX Input 3 Volume */ - { 0x000008D6, 0x0000 }, /* R2262 - DRC2LMIX Input 4 Source */ - { 0x000008D7, 0x0080 }, /* R2263 - DRC2LMIX Input 4 Volume */ - { 0x000008D8, 0x0000 }, /* R2264 - DRC2RMIX Input 1 Source */ - { 0x000008D9, 0x0080 }, /* R2265 - DRC2RMIX Input 1 Volume */ - { 0x000008DA, 0x0000 }, /* R2266 - DRC2RMIX Input 2 Source */ - { 0x000008DB, 0x0080 }, /* R2267 - DRC2RMIX Input 2 Volume */ - { 0x000008DC, 0x0000 }, /* R2268 - DRC2RMIX Input 3 Source */ - { 0x000008DD, 0x0080 }, /* R2269 - DRC2RMIX Input 3 Volume */ - { 0x000008DE, 0x0000 }, /* R2270 - DRC2RMIX Input 4 Source */ - { 0x000008DF, 0x0080 }, /* R2271 - DRC2RMIX Input 4 Volume */ { 0x00000900, 0x0000 }, /* R2304 - HPLP1MIX Input 1 Source */ { 0x00000901, 0x0080 }, /* R2305 - HPLP1MIX Input 1 Volume */ { 0x00000902, 0x0000 }, /* R2306 - HPLP1MIX Input 2 Source */ @@ -880,7 +860,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000D1B, 0xFFFF }, /* R3355 - IRQ2 Status 4 Mask */ { 0x00000D1C, 0xFFFF }, /* R3356 - IRQ2 Status 5 Mask */ { 0x00000D1F, 0x0000 }, /* R3359 - IRQ2 Control */ - { 0x00000D41, 0x0000 }, /* R3393 - ADSP2 IRQ0 */ + { 0x00000D50, 0x0000 }, /* R3408 - AOD wkup and trig */ { 0x00000D53, 0xFFFF }, /* R3411 - AOD IRQ Mask IRQ1 */ { 0x00000D54, 0xFFFF }, /* R3412 - AOD IRQ Mask IRQ2 */ { 0x00000D56, 0x0000 }, /* R3414 - Jack detect debounce */ @@ -975,11 +955,6 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000E82, 0x0018 }, /* R3714 - DRC1 ctrl3 */ { 0x00000E83, 0x0000 }, /* R3715 - DRC1 ctrl4 */ { 0x00000E84, 0x0000 }, /* R3716 - DRC1 ctrl5 */ - { 0x00000E89, 0x0018 }, /* R3721 - DRC2 ctrl1 */ - { 0x00000E8A, 0x0933 }, /* R3722 - DRC2 ctrl2 */ - { 0x00000E8B, 0x0018 }, /* R3723 - DRC2 ctrl3 */ - { 0x00000E8C, 0x0000 }, /* R3724 - DRC2 ctrl4 */ - { 0x00000E8D, 0x0000 }, /* R3725 - DRC2 ctrl5 */ { 0x00000EC0, 0x0000 }, /* R3776 - HPLPF1_1 */ { 0x00000EC1, 0x0000 }, /* R3777 - HPLPF1_2 */ { 0x00000EC4, 0x0000 }, /* R3780 - HPLPF2_1 */ @@ -990,16 +965,12 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000ECD, 0x0000 }, /* R3789 - HPLPF4_2 */ { 0x00000EE0, 0x0000 }, /* R3808 - ASRC_ENABLE */ { 0x00000EE2, 0x0000 }, /* R3810 - ASRC_RATE1 */ - { 0x00000EE3, 0x4000 }, /* R3811 - ASRC_RATE2 */ { 0x00000EF0, 0x0000 }, /* R3824 - ISRC 1 CTRL 1 */ { 0x00000EF1, 0x0000 }, /* R3825 - ISRC 1 CTRL 2 */ { 0x00000EF2, 0x0000 }, /* R3826 - ISRC 1 CTRL 3 */ { 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */ { 0x00000EF4, 0x0000 }, /* R3828 - ISRC 2 CTRL 2 */ { 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */ - { 0x00000EF6, 0x0000 }, /* R3830 - ISRC 3 CTRL 1 */ - { 0x00000EF7, 0x0000 }, /* R3831 - ISRC 3 CTRL 2 */ - { 0x00000EF8, 0x0000 }, /* R3832 - ISRC 3 CTRL 3 */ { 0x00001100, 0x0010 }, /* R4352 - DSP1 Control 1 */ { 0x00001101, 0x0000 }, /* R4353 - DSP1 Clocking 1 */ }; From dc781d0e10fca29123ddde45429d777725c0fde5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 27 Jan 2013 12:07:32 +0800 Subject: [PATCH 2577/4607] mfd: arizona: Disable interrupts during resume Runtime power management does not function during system suspend but the Arizona devices need to use runtime power management to power up the device in order to handle interrupts. Try to avoid interrupts firing during resume by disabling the primary IRQ before interrupts are reenabled on resume and only reenabling it again during main resume. The goal is to avoid issues in the situation where an interrupt is asserted during resume (eg, due to it being the wake source) and the interrupt handling gets scheduled prior to the device being able to handle runtime PM. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 1ab02a7d2b05..9c994b69b063 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -263,10 +263,36 @@ static int arizona_runtime_suspend(struct device *dev) } #endif +#ifdef CONFIG_PM_SLEEP +static int arizona_resume_noirq(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + + dev_dbg(arizona->dev, "Early resume, disabling IRQ\n"); + disable_irq(arizona->irq); + + return 0; +} + +static int arizona_resume(struct device *dev) +{ + struct arizona *arizona = dev_get_drvdata(dev); + + dev_dbg(arizona->dev, "Late resume, reenabling IRQ\n"); + enable_irq(arizona->irq); + + return 0; +} +#endif + const struct dev_pm_ops arizona_pm_ops = { SET_RUNTIME_PM_OPS(arizona_runtime_suspend, arizona_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(NULL, arizona_resume) +#ifdef CONFIG_PM_SLEEP + .resume_noirq = arizona_resume_noirq, +#endif }; EXPORT_SYMBOL_GPL(arizona_pm_ops); From 648a98808c6319dde03b64550dc64a61aaccc2b4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 28 Jan 2013 00:32:53 +0800 Subject: [PATCH 2578/4607] mfd: arizona: Clarify mixer underclocking error If the mixer is underclocked it will drop a sample so log that error more directly. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 9c994b69b063..317ba0a4ea25 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -115,7 +115,7 @@ static irqreturn_t arizona_underclocked(int irq, void *data) if (val & ARIZONA_ADC_UNDERCLOCKED_STS) dev_err(arizona->dev, "ADC underclocked\n"); if (val & ARIZONA_MIXER_UNDERCLOCKED_STS) - dev_err(arizona->dev, "Mixer underclocked\n"); + dev_err(arizona->dev, "Mixer dropped sample\n"); return IRQ_HANDLED; } From 3d91f8282c66d9edafa3980385324ce6a48edcda Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 29 Jan 2013 00:47:37 +0800 Subject: [PATCH 2579/4607] mfd: arizona: Provide platform data for MICBIAS configuration Allow the MICBIAS voltages and other attributes to be configured by the platform. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/arizona-core.c | 23 +++++++++++++++++++++++ include/linux/mfd/arizona/pdata.h | 12 ++++++++++++ 2 files changed, 35 insertions(+) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 317ba0a4ea25..b562c7bf8a46 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -510,6 +510,29 @@ int arizona_dev_init(struct arizona *arizona) goto err_reset; } + for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) { + if (!arizona->pdata.micbias[i].mV) + continue; + + val = (arizona->pdata.micbias[i].mV - 1500) / 100; + val <<= ARIZONA_MICB1_LVL_SHIFT; + + if (arizona->pdata.micbias[i].ext_cap) + val |= ARIZONA_MICB1_EXT_CAP; + + if (arizona->pdata.micbias[i].discharge) + val |= ARIZONA_MICB1_DISCH; + + if (arizona->pdata.micbias[i].fast_start) + val |= ARIZONA_MICB1_RATE; + + regmap_update_bits(arizona->regmap, + ARIZONA_MIC_BIAS_CTRL_1 + i, + ARIZONA_MICB1_LVL_MASK | + ARIZONA_MICB1_DISCH | + ARIZONA_MICB1_RATE, val); + } + for (i = 0; i < ARIZONA_MAX_INPUT; i++) { /* Default for both is 0 so noop with defaults */ val = arizona->pdata.dmic_ref[i] diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 8b1d1daaae16..37894d6a4f6f 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -56,6 +56,8 @@ #define ARIZONA_DMIC_MICBIAS2 2 #define ARIZONA_DMIC_MICBIAS3 3 +#define ARIZONA_MAX_MICBIAS 3 + #define ARIZONA_INMODE_DIFF 0 #define ARIZONA_INMODE_SE 1 #define ARIZONA_INMODE_DMIC 2 @@ -69,6 +71,13 @@ struct regulator_init_data; +struct arizona_micbias { + int mV; /** Regulated voltage */ + unsigned int ext_cap:1; /** External capacitor fitted */ + unsigned int discharge:1; /** Actively discharge */ + unsigned int fast_start:1; /** Enable aggressive startup ramp rate */ +}; + struct arizona_micd_config { unsigned int src; unsigned int bias; @@ -106,6 +115,9 @@ struct arizona_pdata { /** Reference voltage for DMIC inputs */ int dmic_ref[ARIZONA_MAX_INPUT]; + /** MICBIAS configurations */ + struct arizona_micbias micbias[ARIZONA_MAX_MICBIAS]; + /** Mode of input structures */ int inmode[ARIZONA_MAX_INPUT]; From c6dc96467ad94e3fe848d883d3a5a7e18a387abd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:49 +0100 Subject: [PATCH 2580/4607] ARM: OMAP: zoom-display: Remove the use of TWL4030_MODULE_PWM1 Use the future proof TWL_MODULE_PWM module id instead to aim the twl-core cleanup planed for 3.9 kernel cycle. Signed-off-by: Peter Ujfalusi Acked-by: Tony Lindgren Signed-off-by: Samuel Ortiz --- arch/arm/mach-omap2/board-zoom-display.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-omap2/board-zoom-display.c b/arch/arm/mach-omap2/board-zoom-display.c index 1c7c834a5b5f..8cef477d6b00 100644 --- a/arch/arm/mach-omap2/board-zoom-display.c +++ b/arch/arm/mach-omap2/board-zoom-display.c @@ -49,13 +49,13 @@ static void zoom_panel_disable_lcd(struct omap_dss_device *dssdev) { } -/* - * PWMA/B register offsets (TWL4030_MODULE_PWMA) - */ +/* Register offsets in TWL4030_MODULE_INTBR */ #define TWL_INTBR_PMBR1 0xD #define TWL_INTBR_GPBR1 0xC -#define TWL_LED_PWMON 0x0 -#define TWL_LED_PWMOFF 0x1 + +/* Register offsets in TWL_MODULE_PWM */ +#define TWL_LED_PWMON 0x3 +#define TWL_LED_PWMOFF 0x4 static int zoom_set_bl_intensity(struct omap_dss_device *dssdev, int level) { @@ -93,8 +93,8 @@ static int zoom_set_bl_intensity(struct omap_dss_device *dssdev, int level) } c = ((50 * (100 - level)) / 100) + 1; - twl_i2c_write_u8(TWL4030_MODULE_PWM1, 0x7F, TWL_LED_PWMOFF); - twl_i2c_write_u8(TWL4030_MODULE_PWM1, c, TWL_LED_PWMON); + twl_i2c_write_u8(TWL_MODULE_PWM, 0x7F, TWL_LED_PWMOFF); + twl_i2c_write_u8(TWL_MODULE_PWM, c, TWL_LED_PWMON); #else pr_warn("Backlight not enabled\n"); #endif From 5d4e9bd79a5ab5bd4695d3becaa71da447a76a94 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:50 +0100 Subject: [PATCH 2581/4607] mfd: twl-core: Clean up module id lookup and definitions Use enums for all module definitions: twl_module_ids for common functionality among twl4030/twl6030 twl4030_module_ids for twl4030 specific ids twl6030_module_ids for twl6030 specific ids In this way the list can be managed easier when new functionality going to be implemented. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 99 ++++++++++++++++++++--------------------- include/linux/i2c/twl.h | 68 +++++++++++++++------------- 2 files changed, 84 insertions(+), 83 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 4f3baadd0038..b781cdd0629d 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -74,8 +74,6 @@ #define SUB_CHIP_ID3 3 #define SUB_CHIP_ID_INVAL 0xff -#define TWL_MODULE_LAST TWL4030_MODULE_LAST - /* Base Address defns for twl4030_map[] */ /* subchip/slave 0 - USB ID */ @@ -94,10 +92,7 @@ #define TWL4030_BASEADD_MADC 0x0000 #define TWL4030_BASEADD_MAIN_CHARGE 0x0074 #define TWL4030_BASEADD_PRECHARGE 0x00AA -#define TWL4030_BASEADD_PWM0 0x00F8 -#define TWL4030_BASEADD_PWM1 0x00FB -#define TWL4030_BASEADD_PWMA 0x00EF -#define TWL4030_BASEADD_PWMB 0x00F1 +#define TWL4030_BASEADD_PWM 0x00F8 #define TWL4030_BASEADD_KEYPAD 0x00D2 #define TWL5031_BASEADD_ACCESSORY 0x0074 /* Replaces Main Charge */ @@ -117,7 +112,7 @@ /* subchip/slave 0 0x48 - POWER */ #define TWL6030_BASEADD_RTC 0x0000 -#define TWL6030_BASEADD_MEM 0x0017 +#define TWL6030_BASEADD_SECURED_REG 0x0017 #define TWL6030_BASEADD_PM_MASTER 0x001F #define TWL6030_BASEADD_PM_SLAVE_MISC 0x0030 /* PM_RECEIVER */ #define TWL6030_BASEADD_PM_MISC 0x00E2 @@ -132,6 +127,7 @@ #define TWL6030_BASEADD_PIH 0x00D0 #define TWL6030_BASEADD_CHARGER 0x00E0 #define TWL6025_BASEADD_CHARGER 0x00DA +#define TWL6030_BASEADD_LED 0x00F4 /* subchip/slave 2 0x4A - DFT */ #define TWL6030_BASEADD_DIEID 0x00C0 @@ -188,34 +184,33 @@ static struct twl_mapping twl4030_map[] = { * so they continue to match the order in this table. */ + /* Common IPs */ { 0, TWL4030_BASEADD_USB }, + { 1, TWL4030_BASEADD_PIH }, + { 2, TWL4030_BASEADD_MAIN_CHARGE }, + { 3, TWL4030_BASEADD_PM_MASTER }, + { 3, TWL4030_BASEADD_PM_RECEIVER }, + + { 3, TWL4030_BASEADD_RTC }, + { 2, TWL4030_BASEADD_PWM }, + { 2, TWL4030_BASEADD_LED }, + { 3, TWL4030_BASEADD_SECURED_REG }, + + /* TWL4030 specific IPs */ { 1, TWL4030_BASEADD_AUDIO_VOICE }, { 1, TWL4030_BASEADD_GPIO }, { 1, TWL4030_BASEADD_INTBR }, - { 1, TWL4030_BASEADD_PIH }, - { 1, TWL4030_BASEADD_TEST }, { 2, TWL4030_BASEADD_KEYPAD }, + { 2, TWL4030_BASEADD_MADC }, { 2, TWL4030_BASEADD_INTERRUPTS }, - { 2, TWL4030_BASEADD_LED }, - - { 2, TWL4030_BASEADD_MAIN_CHARGE }, { 2, TWL4030_BASEADD_PRECHARGE }, - { 2, TWL4030_BASEADD_PWM0 }, - { 2, TWL4030_BASEADD_PWM1 }, - { 2, TWL4030_BASEADD_PWMA }, - - { 2, TWL4030_BASEADD_PWMB }, - { 2, TWL5031_BASEADD_ACCESSORY }, - { 2, TWL5031_BASEADD_INTERRUPTS }, { 3, TWL4030_BASEADD_BACKUP }, { 3, TWL4030_BASEADD_INT }, - { 3, TWL4030_BASEADD_PM_MASTER }, - { 3, TWL4030_BASEADD_PM_RECEIVER }, - { 3, TWL4030_BASEADD_RTC }, - { 3, TWL4030_BASEADD_SECURED_REG }, + { 2, TWL5031_BASEADD_ACCESSORY }, + { 2, TWL5031_BASEADD_INTERRUPTS }, }; static struct regmap_config twl4030_regmap_config[4] = { @@ -251,35 +246,25 @@ static struct twl_mapping twl6030_map[] = { * defines for TWL4030_MODULE_* * so they continue to match the order in this table. */ - { SUB_CHIP_ID1, TWL6030_BASEADD_USB }, - { SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO }, - { SUB_CHIP_ID2, TWL6030_BASEADD_DIEID }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID1, TWL6030_BASEADD_PIH }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID1, TWL6030_BASEADD_GPADC_CTRL }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, + /* Common IPs */ + { 1, TWL6030_BASEADD_USB }, + { 1, TWL6030_BASEADD_PIH }, + { 1, TWL6030_BASEADD_CHARGER }, + { 0, TWL6030_BASEADD_PM_MASTER }, + { 0, TWL6030_BASEADD_PM_SLAVE_MISC }, - { SUB_CHIP_ID1, TWL6030_BASEADD_CHARGER }, - { SUB_CHIP_ID1, TWL6030_BASEADD_GASGAUGE }, - { SUB_CHIP_ID1, TWL6030_BASEADD_PWM }, - { SUB_CHIP_ID0, TWL6030_BASEADD_ZERO }, - { SUB_CHIP_ID1, TWL6030_BASEADD_ZERO }, + { 0, TWL6030_BASEADD_RTC }, + { 1, TWL6030_BASEADD_PWM }, + { 1, TWL6030_BASEADD_LED }, + { 0, TWL6030_BASEADD_SECURED_REG }, - { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO }, - { SUB_CHIP_ID2, TWL6030_BASEADD_ZERO }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - { SUB_CHIP_ID2, TWL6030_BASEADD_RSV }, - - { SUB_CHIP_ID0, TWL6030_BASEADD_PM_MASTER }, - { SUB_CHIP_ID0, TWL6030_BASEADD_PM_SLAVE_MISC }, - { SUB_CHIP_ID0, TWL6030_BASEADD_RTC }, - { SUB_CHIP_ID0, TWL6030_BASEADD_MEM }, - { SUB_CHIP_ID1, TWL6025_BASEADD_CHARGER }, + /* TWL6030 specific IPs */ + { 0, TWL6030_BASEADD_ZERO }, + { 1, TWL6030_BASEADD_ZERO }, + { 2, TWL6030_BASEADD_ZERO }, + { 1, TWL6030_BASEADD_GPADC_CTRL }, + { 1, TWL6030_BASEADD_GASGAUGE }, }; static struct regmap_config twl6030_regmap_config[3] = { @@ -305,6 +290,14 @@ static struct regmap_config twl6030_regmap_config[3] = { /*----------------------------------------------------------------------*/ +static inline int twl_get_last_module(void) +{ + if (twl_class_is_4030()) + return TWL4030_MODULE_LAST; + else + return TWL6030_MODULE_LAST; +} + /* Exported Functions */ /** @@ -325,7 +318,7 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no >= TWL_MODULE_LAST)) { + if (unlikely(mod_no >= twl_get_last_module())) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } @@ -367,7 +360,7 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no >= TWL_MODULE_LAST)) { + if (unlikely(mod_no >= twl_get_last_module())) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } @@ -1228,6 +1221,10 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) if ((id->driver_data) & TWL6030_CLASS) { twl_id = TWL6030_CLASS_ID; twl_map = &twl6030_map[0]; + /* The charger base address is different in twl6025 */ + if ((id->driver_data) & TWL6025_SUBCLASS) + twl_map[TWL_MODULE_MAIN_CHARGE].base = + TWL6025_BASEADD_CHARGER; twl_regmap_config = twl6030_regmap_config; num_slaves = TWL_NUM_SLAVES - 1; } else { diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 1ff54b114efc..72adc8807912 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -39,51 +39,55 @@ * address each module uses within a given i2c slave. */ +/* Module IDs for similar functionalities found in twl4030/twl6030 */ +enum twl_module_ids { + TWL_MODULE_USB, + TWL_MODULE_PIH, + TWL_MODULE_MAIN_CHARGE, + TWL_MODULE_PM_MASTER, + TWL_MODULE_PM_RECEIVER, + + TWL_MODULE_RTC, + TWL_MODULE_PWM, + TWL_MODULE_LED, + TWL_MODULE_SECURED_REG, + + TWL_MODULE_LAST, +}; + +/* Modules only available in twl4030 series */ enum twl4030_module_ids { - TWL4030_MODULE_USB = 0, /* Slave 0 (i2c address 0x48) */ - TWL4030_MODULE_AUDIO_VOICE, /* Slave 1 (i2c address 0x49) */ + TWL4030_MODULE_AUDIO_VOICE = TWL_MODULE_LAST, TWL4030_MODULE_GPIO, TWL4030_MODULE_INTBR, - TWL4030_MODULE_PIH, - TWL4030_MODULE_TEST, - TWL4030_MODULE_KEYPAD, /* Slave 2 (i2c address 0x4a) */ + TWL4030_MODULE_KEYPAD, + TWL4030_MODULE_MADC, TWL4030_MODULE_INTERRUPTS, - TWL4030_MODULE_LED, - - TWL4030_MODULE_MAIN_CHARGE, TWL4030_MODULE_PRECHARGE, - TWL4030_MODULE_PWM0, - TWL4030_MODULE_PWM1, - TWL4030_MODULE_PWMA, - - TWL4030_MODULE_PWMB, - TWL5031_MODULE_ACCESSORY, - TWL5031_MODULE_INTERRUPTS, - TWL4030_MODULE_BACKUP, /* Slave 3 (i2c address 0x4b) */ + TWL4030_MODULE_BACKUP, TWL4030_MODULE_INT, - TWL4030_MODULE_PM_MASTER, - TWL4030_MODULE_PM_RECEIVER, - TWL4030_MODULE_RTC, - TWL4030_MODULE_SECURED_REG, + TWL5031_MODULE_ACCESSORY, + TWL5031_MODULE_INTERRUPTS, + TWL4030_MODULE_LAST, }; -/* Similar functionalities implemented in TWL4030/6030 */ -#define TWL_MODULE_USB TWL4030_MODULE_USB -#define TWL_MODULE_PIH TWL4030_MODULE_PIH -#define TWL_MODULE_MAIN_CHARGE TWL4030_MODULE_MAIN_CHARGE -#define TWL_MODULE_PM_MASTER TWL4030_MODULE_PM_MASTER -#define TWL_MODULE_PM_RECEIVER TWL4030_MODULE_PM_RECEIVER -#define TWL_MODULE_RTC TWL4030_MODULE_RTC -#define TWL_MODULE_PWM TWL4030_MODULE_PWM0 -#define TWL_MODULE_LED TWL4030_MODULE_LED +/* Modules only available in twl6030 series */ +enum twl6030_module_ids { + TWL6030_MODULE_ID0 = TWL_MODULE_LAST, + TWL6030_MODULE_ID1, + TWL6030_MODULE_ID2, + TWL6030_MODULE_GPADC, + TWL6030_MODULE_GASGAUGE, -#define TWL6030_MODULE_ID0 13 -#define TWL6030_MODULE_ID1 14 -#define TWL6030_MODULE_ID2 15 + TWL6030_MODULE_LAST, +}; + +/* Until the clients has been converted to use TWL_MODULE_LED */ +#define TWL4030_MODULE_LED TWL_MODULE_LED #define GPIO_INTR_OFFSET 0 #define KEYPAD_INTR_OFFSET 1 From 050cde1363a3fc59f2a2f1cac6ef050393a00c99 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:51 +0100 Subject: [PATCH 2582/4607] mfd: twl-core: No need to check for invalid subchip ID The module id table no longer can have invalid/unused entries. No need for checking the ID for validity. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index b781cdd0629d..fa1a5a0fa759 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -72,7 +72,6 @@ #define SUB_CHIP_ID1 1 #define SUB_CHIP_ID2 2 #define SUB_CHIP_ID3 3 -#define SUB_CHIP_ID_INVAL 0xff /* Base Address defns for twl4030_map[] */ @@ -326,12 +325,8 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } + sid = twl_map[mod_no].sid; - if (unlikely(sid == SUB_CHIP_ID_INVAL)) { - pr_err("%s: module %d is not part of the pmic\n", - DRIVER_NAME, mod_no); - return -EINVAL; - } twl = &twl_modules[sid]; ret = regmap_bulk_write(twl->regmap, twl_map[mod_no].base + reg, @@ -368,12 +363,8 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } + sid = twl_map[mod_no].sid; - if (unlikely(sid == SUB_CHIP_ID_INVAL)) { - pr_err("%s: module %d is not part of the pmic\n", - DRIVER_NAME, mod_no); - return -EINVAL; - } twl = &twl_modules[sid]; ret = regmap_bulk_read(twl->regmap, twl_map[mod_no].base + reg, From 3c3302794cc79b363779a762051ebe8670812791 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:52 +0100 Subject: [PATCH 2583/4607] mfd: twl-core: Use the lookup table to find the correct subchip for the modules Instead of using SUB_CHIP_ID* or magic numbers use the twl_mapping table to look for the subchip ID. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 56 ++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index fa1a5a0fa759..f07317b35e4a 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -68,11 +68,6 @@ #define TWL_NUM_SLAVES 4 -#define SUB_CHIP_ID0 0 -#define SUB_CHIP_ID1 1 -#define SUB_CHIP_ID2 2 -#define SUB_CHIP_ID3 3 - /* Base Address defns for twl4030_map[] */ /* subchip/slave 0 - USB ID */ @@ -493,13 +488,20 @@ int twl_get_hfclk_rate(void) EXPORT_SYMBOL_GPL(twl_get_hfclk_rate); static struct device * -add_numbered_child(unsigned chip, const char *name, int num, +add_numbered_child(unsigned mod_no, const char *name, int num, void *pdata, unsigned pdata_len, bool can_wakeup, int irq0, int irq1) { struct platform_device *pdev; - struct twl_client *twl = &twl_modules[chip]; - int status; + struct twl_client *twl; + int status, sid; + + if (unlikely(mod_no >= twl_get_last_module())) { + pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); + return ERR_PTR(-EPERM); + } + sid = twl_map[mod_no].sid; + twl = &twl_modules[sid]; pdev = platform_device_alloc(name, num); if (!pdev) { @@ -544,11 +546,11 @@ err: return &pdev->dev; } -static inline struct device *add_child(unsigned chip, const char *name, +static inline struct device *add_child(unsigned mod_no, const char *name, void *pdata, unsigned pdata_len, bool can_wakeup, int irq0, int irq1) { - return add_numbered_child(chip, name, -1, pdata, pdata_len, + return add_numbered_child(mod_no, name, -1, pdata, pdata_len, can_wakeup, irq0, irq1); } @@ -557,7 +559,6 @@ add_regulator_linked(int num, struct regulator_init_data *pdata, struct regulator_consumer_supply *consumers, unsigned num_consumers, unsigned long features) { - unsigned sub_chip_id; struct twl_regulator_driver_data drv_data; /* regulator framework demands init_data ... */ @@ -584,8 +585,7 @@ add_regulator_linked(int num, struct regulator_init_data *pdata, } /* NOTE: we currently ignore regulator IRQs, e.g. for short circuits */ - sub_chip_id = twl_map[TWL_MODULE_PM_MASTER].sid; - return add_numbered_child(sub_chip_id, "twl_reg", num, + return add_numbered_child(TWL_MODULE_PM_MASTER, "twl_reg", num, pdata, sizeof(*pdata), false, 0, 0); } @@ -607,10 +607,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, unsigned long features) { struct device *child; - unsigned sub_chip_id; if (IS_ENABLED(CONFIG_GPIO_TWL4030) && pdata->gpio) { - child = add_child(SUB_CHIP_ID1, "twl4030_gpio", + child = add_child(TWL4030_MODULE_GPIO, "twl4030_gpio", pdata->gpio, sizeof(*pdata->gpio), false, irq_base + GPIO_INTR_OFFSET, 0); if (IS_ERR(child)) @@ -618,7 +617,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, } if (IS_ENABLED(CONFIG_KEYBOARD_TWL4030) && pdata->keypad) { - child = add_child(SUB_CHIP_ID2, "twl4030_keypad", + child = add_child(TWL4030_MODULE_KEYPAD, "twl4030_keypad", pdata->keypad, sizeof(*pdata->keypad), true, irq_base + KEYPAD_INTR_OFFSET, 0); if (IS_ERR(child)) @@ -627,7 +626,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, if (IS_ENABLED(CONFIG_TWL4030_MADC) && pdata->madc && twl_class_is_4030()) { - child = add_child(SUB_CHIP_ID2, "twl4030_madc", + child = add_child(TWL4030_MODULE_MADC, "twl4030_madc", pdata->madc, sizeof(*pdata->madc), true, irq_base + MADC_INTR_OFFSET, 0); if (IS_ERR(child)) @@ -642,22 +641,21 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, * Eventually, Linux might become more aware of such * HW security concerns, and "least privilege". */ - sub_chip_id = twl_map[TWL_MODULE_RTC].sid; - child = add_child(sub_chip_id, "twl_rtc", NULL, 0, + child = add_child(TWL_MODULE_RTC, "twl_rtc", NULL, 0, true, irq_base + RTC_INTR_OFFSET, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_PWM_TWL)) { - child = add_child(SUB_CHIP_ID1, "twl-pwm", NULL, 0, + child = add_child(TWL_MODULE_PWM, "twl-pwm", NULL, 0, false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_PWM_TWL_LED)) { - child = add_child(SUB_CHIP_ID1, "twl-pwmled", NULL, 0, + child = add_child(TWL_MODULE_LED, "twl-pwmled", NULL, 0, false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); @@ -709,7 +707,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, } - child = add_child(SUB_CHIP_ID0, "twl4030_usb", + child = add_child(TWL_MODULE_USB, "twl4030_usb", pdata->usb, sizeof(*pdata->usb), true, /* irq0 = USB_PRES, irq1 = USB */ irq_base + USB_PRES_INTR_OFFSET, @@ -758,7 +756,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, pdata->usb->features = features; - child = add_child(SUB_CHIP_ID0, "twl6030_usb", + child = add_child(TWL_MODULE_USB, "twl6030_usb", pdata->usb, sizeof(*pdata->usb), true, /* irq1 = VBUS_PRES, irq0 = USB ID */ irq_base + USBOTG_INTR_OFFSET, @@ -783,22 +781,22 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, } if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) { - child = add_child(SUB_CHIP_ID3, "twl4030_wdt", NULL, 0, - false, 0, 0); + child = add_child(TWL_MODULE_PM_RECEIVER, "twl4030_wdt", NULL, + 0, false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_INPUT_TWL4030_PWRBUTTON) && twl_class_is_4030()) { - child = add_child(SUB_CHIP_ID3, "twl4030_pwrbutton", NULL, 0, - true, irq_base + 8 + 0, 0); + child = add_child(TWL_MODULE_PM_MASTER, "twl4030_pwrbutton", + NULL, 0, true, irq_base + 8 + 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } if (IS_ENABLED(CONFIG_MFD_TWL4030_AUDIO) && pdata->audio && twl_class_is_4030()) { - child = add_child(SUB_CHIP_ID1, "twl4030-audio", + child = add_child(TWL4030_MODULE_AUDIO_VOICE, "twl4030-audio", pdata->audio, sizeof(*pdata->audio), false, 0, 0); if (IS_ERR(child)) @@ -1038,7 +1036,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci && !(features & (TPS_SUBSET | TWL5031))) { - child = add_child(SUB_CHIP_ID3, "twl4030_bci", + child = add_child(TWL_MODULE_MAIN_CHARGE, "twl4030_bci", pdata->bci, sizeof(*pdata->bci), false, /* irq0 = CHG_PRES, irq1 = BCI */ irq_base + BCI_PRES_INTR_OFFSET, From 6dd810b5e6fa688010dcb6d386c61589e850aaaa Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:53 +0100 Subject: [PATCH 2584/4607] mfd: twl-core: Allocate twl_modules dynamically At boot time we can allocate the twl_modules array dynamically based on the twl class we are using with devm_kzalloc() instead of the static twl_modules[] array. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index f07317b35e4a..fbff8301dfca 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -66,8 +66,6 @@ /* Triton Core internal information (BEGIN) */ -#define TWL_NUM_SLAVES 4 - /* Base Address defns for twl4030_map[] */ /* subchip/slave 0 - USB ID */ @@ -162,7 +160,7 @@ struct twl_client { struct regmap *regmap; }; -static struct twl_client twl_modules[TWL_NUM_SLAVES]; +static struct twl_client *twl_modules; /* mapping the module id to slave id and base address */ struct twl_mapping { @@ -284,6 +282,14 @@ static struct regmap_config twl6030_regmap_config[3] = { /*----------------------------------------------------------------------*/ +static inline int twl_get_num_slaves(void) +{ + if (twl_class_is_4030()) + return 4; /* TWL4030 class have four slave address */ + else + return 3; /* TWL6030 class have three slave address */ +} + static inline int twl_get_last_module(void) { if (twl_class_is_4030()) @@ -1127,17 +1133,15 @@ static int twl_remove(struct i2c_client *client) unsigned i, num_slaves; int status; - if (twl_class_is_4030()) { + if (twl_class_is_4030()) status = twl4030_exit_irq(); - num_slaves = TWL_NUM_SLAVES; - } else { + else status = twl6030_exit_irq(); - num_slaves = TWL_NUM_SLAVES - 1; - } if (status < 0) return status; + num_slaves = twl_get_num_slaves(); for (i = 0; i < num_slaves; i++) { struct twl_client *twl = &twl_modules[i]; @@ -1215,12 +1219,19 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) twl_map[TWL_MODULE_MAIN_CHARGE].base = TWL6025_BASEADD_CHARGER; twl_regmap_config = twl6030_regmap_config; - num_slaves = TWL_NUM_SLAVES - 1; } else { twl_id = TWL4030_CLASS_ID; twl_map = &twl4030_map[0]; twl_regmap_config = twl4030_regmap_config; - num_slaves = TWL_NUM_SLAVES; + } + + num_slaves = twl_get_num_slaves(); + twl_modules = devm_kzalloc(&client->dev, + sizeof(struct twl_client) * num_slaves, + GFP_KERNEL); + if (!twl_modules) { + status = -ENOMEM; + goto free; } for (i = 0; i < num_slaves; i++) { From e581238f2817d8ca8948340a2bc26dd9504bb812 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:54 +0100 Subject: [PATCH 2585/4607] mfd: twl-core: Do not try to call legacy mfd add_children() when booted with DT There is really no point to retry to add children devices in case the of_platform_populate() fails. We do not have any information provided via pdata in this case anyways. Depending on the boot type (legacy or DT) only execute either one. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index fbff8301dfca..86cca9e09380 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -1305,10 +1305,9 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1); } - status = -ENODEV; if (node) status = of_platform_populate(node, NULL, NULL, &client->dev); - if (status) + else status = add_children(pdata, irq_base, id->driver_data); fail: From 7e2e6c5758de94ec22686b30e7b906a3ddcd9896 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:55 +0100 Subject: [PATCH 2586/4607] mfd: twl-core: Do not create dummy pdata when booted with DT When booted with DT we can manage without the dummy pdata. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 86cca9e09380..547fed540ef7 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -1165,6 +1165,11 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) int status; unsigned i, num_slaves; + if (!node && !pdata) { + dev_err(&client->dev, "no platform data\n"); + return -EINVAL; + } + pdev = platform_device_alloc(DRIVER_NAME, -1); if (!pdev) { dev_err(&client->dev, "can't alloc pdev\n"); @@ -1177,28 +1182,6 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) return status; } - if (node && !pdata) { - /* - * XXX: Temporary pdata until the information is correctly - * retrieved by every TWL modules from DT. - */ - pdata = devm_kzalloc(&client->dev, - sizeof(struct twl4030_platform_data), - GFP_KERNEL); - if (!pdata) { - status = -ENOMEM; - goto free; - } - } - - if (!pdata) { - dev_dbg(&client->dev, "no platform data?\n"); - status = -EINVAL; - goto free; - } - - platform_set_drvdata(pdev, pdata); - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) { dev_dbg(&client->dev, "can't talk I2C?\n"); status = -EIO; @@ -1264,7 +1247,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) inuse = true; /* setup clock framework */ - clocks_init(&pdev->dev, pdata->clock); + clocks_init(&pdev->dev, pdata ? pdata->clock : NULL); /* read TWL IDCODE Register */ if (twl_id == TWL4030_CLASS_ID) { @@ -1273,7 +1256,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* load power event scripts */ - if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata->power) + if (IS_ENABLED(CONFIG_TWL4030_POWER) && pdata && pdata->power) twl4030_power_init(pdata->power); /* Maybe init the T2 Interrupt subsystem */ From 6382a0614144901af1cbbfdf9b9a618f5dfb8548 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:56 +0100 Subject: [PATCH 2587/4607] mfd: twl-core: Move 'inuse' check early at probe time We can fail earlier in case multiple instance of the twl-core is tried to be loaded. The twl-core by design only supports one instance. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 547fed540ef7..182708811065 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -1170,6 +1170,12 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) return -EINVAL; } + if (inuse) { + dev_dbg(&client->dev, "only one instance of %s allowed\n", + DRIVER_NAME); + return -EBUSY; + } + pdev = platform_device_alloc(DRIVER_NAME, -1); if (!pdev) { dev_err(&client->dev, "can't alloc pdev\n"); @@ -1188,12 +1194,6 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) goto free; } - if (inuse) { - dev_dbg(&client->dev, "driver is already in use\n"); - status = -EBUSY; - goto free; - } - if ((id->driver_data) & TWL6030_CLASS) { twl_id = TWL6030_CLASS_ID; twl_map = &twl6030_map[0]; From 80a97ccd33be9c79018d3d4983eefa3e6d6bfb6d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:57 +0100 Subject: [PATCH 2588/4607] mfd: twl-core: Collect global variables behind one private structure (global) Gather the global variables under a single structure and allocate it with devm_kzalloc(). It is easier to see them and if in the future we try to add support for multiple instance of twl in the system it is going to be much simpler. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 104 ++++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 182708811065..e2895a4026a1 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -141,33 +141,28 @@ /*----------------------------------------------------------------------*/ -/* is driver active, bound to a chip? */ -static bool inuse; - -/* TWL IDCODE Register value */ -static u32 twl_idcode; - -static unsigned int twl_id; -unsigned int twl_rev(void) -{ - return twl_id; -} -EXPORT_SYMBOL(twl_rev); - /* Structure for each TWL4030/TWL6030 Slave */ struct twl_client { struct i2c_client *client; struct regmap *regmap; }; -static struct twl_client *twl_modules; - /* mapping the module id to slave id and base address */ struct twl_mapping { unsigned char sid; /* Slave ID */ unsigned char base; /* base address */ }; -static struct twl_mapping *twl_map; + +struct twl_private { + bool ready; /* The core driver is ready to be used */ + u32 twl_idcode; /* TWL IDCODE Register value */ + unsigned int twl_id; + + struct twl_mapping *twl_map; + struct twl_client *twl_modules; +}; + +static struct twl_private *twl_priv; static struct twl_mapping twl4030_map[] = { /* @@ -300,6 +295,12 @@ static inline int twl_get_last_module(void) /* Exported Functions */ +unsigned int twl_rev(void) +{ + return twl_priv ? twl_priv->twl_id : 0; +} +EXPORT_SYMBOL(twl_rev); + /** * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0 * @mod_no: module number @@ -322,16 +323,17 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } - if (unlikely(!inuse)) { + if (unlikely(!twl_priv->ready)) { pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } - sid = twl_map[mod_no].sid; - twl = &twl_modules[sid]; + sid = twl_priv->twl_map[mod_no].sid; + twl = &twl_priv->twl_modules[sid]; - ret = regmap_bulk_write(twl->regmap, twl_map[mod_no].base + reg, - value, num_bytes); + ret = regmap_bulk_write(twl->regmap, + twl_priv->twl_map[mod_no].base + reg, value, + num_bytes); if (ret) pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n", @@ -360,16 +362,17 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } - if (unlikely(!inuse)) { + if (unlikely(!twl_priv->ready)) { pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } - sid = twl_map[mod_no].sid; - twl = &twl_modules[sid]; + sid = twl_priv->twl_map[mod_no].sid; + twl = &twl_priv->twl_modules[sid]; - ret = regmap_bulk_read(twl->regmap, twl_map[mod_no].base + reg, - value, num_bytes); + ret = regmap_bulk_read(twl->regmap, + twl_priv->twl_map[mod_no].base + reg, value, + num_bytes); if (ret) pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n", @@ -425,7 +428,7 @@ static int twl_read_idcode_register(void) goto fail; } - err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_idcode), + err = twl_i2c_read(TWL4030_MODULE_INTBR, (u8 *)(&twl_priv->twl_idcode), REG_IDCODE_7_0, 4); if (err) { pr_err("TWL4030: unable to read IDCODE -%d\n", err); @@ -446,7 +449,7 @@ fail: */ int twl_get_type(void) { - return TWL_SIL_TYPE(twl_idcode); + return TWL_SIL_TYPE(twl_priv->twl_idcode); } EXPORT_SYMBOL_GPL(twl_get_type); @@ -457,7 +460,7 @@ EXPORT_SYMBOL_GPL(twl_get_type); */ int twl_get_version(void) { - return TWL_SIL_REV(twl_idcode); + return TWL_SIL_REV(twl_priv->twl_idcode); } EXPORT_SYMBOL_GPL(twl_get_version); @@ -506,8 +509,8 @@ add_numbered_child(unsigned mod_no, const char *name, int num, pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return ERR_PTR(-EPERM); } - sid = twl_map[mod_no].sid; - twl = &twl_modules[sid]; + sid = twl_priv->twl_map[mod_no].sid; + twl = &twl_priv->twl_modules[sid]; pdev = platform_device_alloc(name, num); if (!pdev) { @@ -1143,13 +1146,13 @@ static int twl_remove(struct i2c_client *client) num_slaves = twl_get_num_slaves(); for (i = 0; i < num_slaves; i++) { - struct twl_client *twl = &twl_modules[i]; + struct twl_client *twl = &twl_priv->twl_modules[i]; if (twl->client && twl->client != client) i2c_unregister_device(twl->client); - twl_modules[i].client = NULL; + twl->client = NULL; } - inuse = false; + twl_priv->ready = false; return 0; } @@ -1170,7 +1173,7 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) return -EINVAL; } - if (inuse) { + if (twl_priv) { dev_dbg(&client->dev, "only one instance of %s allowed\n", DRIVER_NAME); return -EBUSY; @@ -1194,31 +1197,38 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) goto free; } + twl_priv = devm_kzalloc(&client->dev, sizeof(struct twl_private), + GFP_KERNEL); + if (!twl_priv) { + status = -ENOMEM; + goto free; + } + if ((id->driver_data) & TWL6030_CLASS) { - twl_id = TWL6030_CLASS_ID; - twl_map = &twl6030_map[0]; + twl_priv->twl_id = TWL6030_CLASS_ID; + twl_priv->twl_map = &twl6030_map[0]; /* The charger base address is different in twl6025 */ if ((id->driver_data) & TWL6025_SUBCLASS) - twl_map[TWL_MODULE_MAIN_CHARGE].base = + twl_priv->twl_map[TWL_MODULE_MAIN_CHARGE].base = TWL6025_BASEADD_CHARGER; twl_regmap_config = twl6030_regmap_config; } else { - twl_id = TWL4030_CLASS_ID; - twl_map = &twl4030_map[0]; + twl_priv->twl_id = TWL4030_CLASS_ID; + twl_priv->twl_map = &twl4030_map[0]; twl_regmap_config = twl4030_regmap_config; } num_slaves = twl_get_num_slaves(); - twl_modules = devm_kzalloc(&client->dev, - sizeof(struct twl_client) * num_slaves, - GFP_KERNEL); - if (!twl_modules) { + twl_priv->twl_modules = devm_kzalloc(&client->dev, + sizeof(struct twl_client) * num_slaves, + GFP_KERNEL); + if (!twl_priv->twl_modules) { status = -ENOMEM; goto free; } for (i = 0; i < num_slaves; i++) { - struct twl_client *twl = &twl_modules[i]; + struct twl_client *twl = &twl_priv->twl_modules[i]; if (i == 0) { twl->client = client; @@ -1244,13 +1254,13 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id) } } - inuse = true; + twl_priv->ready = true; /* setup clock framework */ clocks_init(&pdev->dev, pdata ? pdata->clock : NULL); /* read TWL IDCODE Register */ - if (twl_id == TWL4030_CLASS_ID) { + if (twl_class_is_4030()) { status = twl_read_idcode_register(); WARN(status < 0, "Error: reading twl_idcode register value\n"); } From ac7bc5a953c38c32513d1825decf336c4909ce3b Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:58 +0100 Subject: [PATCH 2589/4607] mfd: twl-core: Remove no longer valid comment regarding to write buffer size With the regmap conversion there is no longeer a need to allocate bigger buffer for writes Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 3 --- include/linux/i2c/twl.h | 3 --- 2 files changed, 6 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index e2895a4026a1..9661204fb159 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -308,9 +308,6 @@ EXPORT_SYMBOL(twl_rev); * @reg: register address (just offset will do) * @num_bytes: number of bytes to transfer * - * IMPORTANT: for 'value' parameter: Allocate value num_bytes+1 and - * valid data starts at Offset 1. - * * Returns the result of operation - 0 is success */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index 72adc8807912..e441fd8e7c86 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -182,9 +182,6 @@ int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg); /* * Read and write several 8-bit registers at once. - * - * IMPORTANT: For twl_i2c_write(), allocate num_bytes + 1 - * for the value, and populate your data starting at offset 1. */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); From fbc6ae363e5e589a28135c051a2ff835e6236d5f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 16 Jan 2013 14:53:59 +0100 Subject: [PATCH 2590/4607] mfd: twl-core: Move twl_i2c_read/write_u8 to header as inline function twl_i2c_read/write_u8 become as a simple wrapper over the twl_i2c_read/write. Signed-off-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 28 ---------------------------- include/linux/i2c/twl.h | 17 +++++++++++------ 2 files changed, 11 insertions(+), 34 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 9661204fb159..557f9ee5ec18 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -379,34 +379,6 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) } EXPORT_SYMBOL(twl_i2c_read); -/** - * twl_i2c_write_u8 - Writes a 8 bit register in TWL4030/TWL5030/TWL60X0 - * @mod_no: module number - * @value: the value to be written 8 bit - * @reg: register address (just offset will do) - * - * Returns result of operation - 0 is success - */ -int twl_i2c_write_u8(u8 mod_no, u8 value, u8 reg) -{ - return twl_i2c_write(mod_no, &value, reg, 1); -} -EXPORT_SYMBOL(twl_i2c_write_u8); - -/** - * twl_i2c_read_u8 - Reads a 8 bit register from TWL4030/TWL5030/TWL60X0 - * @mod_no: module number - * @value: the value read 8 bit - * @reg: register address (just offset will do) - * - * Returns result of operation - 0 is success - */ -int twl_i2c_read_u8(u8 mod_no, u8 *value, u8 reg) -{ - return twl_i2c_read(mod_no, value, reg, 1); -} -EXPORT_SYMBOL(twl_i2c_read_u8); - /*----------------------------------------------------------------------*/ /** diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h index e441fd8e7c86..488debbef895 100644 --- a/include/linux/i2c/twl.h +++ b/include/linux/i2c/twl.h @@ -174,18 +174,23 @@ static inline int twl_class_is_ ##class(void) \ TWL_CLASS_IS(4030, TWL4030_CLASS_ID) TWL_CLASS_IS(6030, TWL6030_CLASS_ID) -/* - * Read and write single 8-bit registers - */ -int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg); -int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg); - /* * Read and write several 8-bit registers at once. */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); +/* + * Read and write single 8-bit registers + */ +static inline int twl_i2c_write_u8(u8 mod_no, u8 val, u8 reg) { + return twl_i2c_write(mod_no, &val, reg, 1); +} + +static inline int twl_i2c_read_u8(u8 mod_no, u8 *val, u8 reg) { + return twl_i2c_read(mod_no, val, reg, 1); +} + int twl_get_type(void); int twl_get_version(void); int twl_get_hfclk_rate(void); From 01560f6bb958b821ceec98590a7147d610a62625 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Thu, 24 Jan 2013 14:52:39 -0600 Subject: [PATCH 2591/4607] mfd: lpc_ich: Fix gpio base and control offsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In ICH5 and earlier the GPIOBASE and GPIOCTRL registers are found at offsets 0x58 and 0x5C, respectively. This patch allows GPIO access to properly be enabled (and disabled) for these chipsets. Signed-off-by: Agócs Pál Signed-off-by: Aaron Sierra Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/lpc_ich.c | 109 +++++++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 33 deletions(-) diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index d9d930302e98..a0cfdf980748 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -75,8 +75,10 @@ #define ACPIBASE_GCS_OFF 0x3410 #define ACPIBASE_GCS_END 0x3414 -#define GPIOBASE 0x48 -#define GPIOCTRL 0x4C +#define GPIOBASE_ICH0 0x58 +#define GPIOCTRL_ICH0 0x5C +#define GPIOBASE_ICH6 0x48 +#define GPIOCTRL_ICH6 0x4C #define RCBABASE 0xf0 @@ -84,8 +86,17 @@ #define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i) #define wdt_res(b, i) (&wdt_ich_res[(b) + (i)]) -static int lpc_ich_acpi_save = -1; -static int lpc_ich_gpio_save = -1; +struct lpc_ich_cfg { + int base; + int ctrl; + int save; +}; + +struct lpc_ich_priv { + int chipset; + struct lpc_ich_cfg acpi; + struct lpc_ich_cfg gpio; +}; static struct resource wdt_ich_res[] = { /* ACPI - TCO */ @@ -661,39 +672,44 @@ MODULE_DEVICE_TABLE(pci, lpc_ich_ids); static void lpc_ich_restore_config_space(struct pci_dev *dev) { - if (lpc_ich_acpi_save >= 0) { - pci_write_config_byte(dev, ACPICTRL, lpc_ich_acpi_save); - lpc_ich_acpi_save = -1; + struct lpc_ich_priv *priv = pci_get_drvdata(dev); + + if (priv->acpi.save >= 0) { + pci_write_config_byte(dev, priv->acpi.ctrl, priv->acpi.save); + priv->acpi.save = -1; } - if (lpc_ich_gpio_save >= 0) { - pci_write_config_byte(dev, GPIOCTRL, lpc_ich_gpio_save); - lpc_ich_gpio_save = -1; + if (priv->gpio.save >= 0) { + pci_write_config_byte(dev, priv->gpio.ctrl, priv->gpio.save); + priv->gpio.save = -1; } } static void lpc_ich_enable_acpi_space(struct pci_dev *dev) { + struct lpc_ich_priv *priv = pci_get_drvdata(dev); u8 reg_save; - pci_read_config_byte(dev, ACPICTRL, ®_save); - pci_write_config_byte(dev, ACPICTRL, reg_save | 0x10); - lpc_ich_acpi_save = reg_save; + pci_read_config_byte(dev, priv->acpi.ctrl, ®_save); + pci_write_config_byte(dev, priv->acpi.ctrl, reg_save | 0x10); + priv->acpi.save = reg_save; } static void lpc_ich_enable_gpio_space(struct pci_dev *dev) { + struct lpc_ich_priv *priv = pci_get_drvdata(dev); u8 reg_save; - pci_read_config_byte(dev, GPIOCTRL, ®_save); - pci_write_config_byte(dev, GPIOCTRL, reg_save | 0x10); - lpc_ich_gpio_save = reg_save; + pci_read_config_byte(dev, priv->gpio.ctrl, ®_save); + pci_write_config_byte(dev, priv->gpio.ctrl, reg_save | 0x10); + priv->gpio.save = reg_save; } -static void lpc_ich_finalize_cell(struct mfd_cell *cell, - const struct pci_device_id *id) +static void lpc_ich_finalize_cell(struct pci_dev *dev, struct mfd_cell *cell) { - cell->platform_data = &lpc_chipset_info[id->driver_data]; + struct lpc_ich_priv *priv = pci_get_drvdata(dev); + + cell->platform_data = &lpc_chipset_info[priv->chipset]; cell->pdata_size = sizeof(struct lpc_ich_info); } @@ -721,9 +737,9 @@ static int lpc_ich_check_conflict_gpio(struct resource *res) return use_gpio ? use_gpio : ret; } -static int lpc_ich_init_gpio(struct pci_dev *dev, - const struct pci_device_id *id) +static int lpc_ich_init_gpio(struct pci_dev *dev) { + struct lpc_ich_priv *priv = pci_get_drvdata(dev); u32 base_addr_cfg; u32 base_addr; int ret; @@ -731,7 +747,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev, struct resource *res; /* Setup power management base register */ - pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg); + pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); @@ -757,7 +773,7 @@ static int lpc_ich_init_gpio(struct pci_dev *dev, gpe0_done: /* Setup GPIO base register */ - pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg); + pci_read_config_dword(dev, priv->gpio.base, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { dev_notice(&dev->dev, "I/O space for GPIO uninitialized\n"); @@ -768,7 +784,7 @@ gpe0_done: /* Older devices provide fewer GPIO and have a smaller resource size. */ res = &gpio_ich_res[ICH_RES_GPIO]; res->start = base_addr; - switch (lpc_chipset_info[id->driver_data].gpio_version) { + switch (lpc_chipset_info[priv->chipset].gpio_version) { case ICH_V5_GPIO: case ICH_V10CORP_GPIO: res->end = res->start + 128 - 1; @@ -784,10 +800,10 @@ gpe0_done: acpi_conflict = true; goto gpio_done; } - lpc_chipset_info[id->driver_data].use_gpio = ret; + lpc_chipset_info[priv->chipset].use_gpio = ret; lpc_ich_enable_gpio_space(dev); - lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id); + lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_GPIO]); ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO], 1, NULL, 0, NULL); @@ -798,16 +814,16 @@ gpio_done: return ret; } -static int lpc_ich_init_wdt(struct pci_dev *dev, - const struct pci_device_id *id) +static int lpc_ich_init_wdt(struct pci_dev *dev) { + struct lpc_ich_priv *priv = pci_get_drvdata(dev); u32 base_addr_cfg; u32 base_addr; int ret; struct resource *res; /* Setup power management base register */ - pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg); + pci_read_config_dword(dev, priv->acpi.base, &base_addr_cfg); base_addr = base_addr_cfg & 0x0000ff80; if (!base_addr) { dev_notice(&dev->dev, "I/O space for ACPI uninitialized\n"); @@ -830,7 +846,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev, * we have to read RCBA from PCI Config space 0xf0 and use * it as base. GCS = RCBA + ICH6_GCS(0x3410). */ - if (lpc_chipset_info[id->driver_data].iTCO_version == 1) { + if (lpc_chipset_info[priv->chipset].iTCO_version == 1) { /* Don't register iomem for TCO ver 1 */ lpc_ich_cells[LPC_WDT].num_resources--; } else { @@ -847,7 +863,7 @@ static int lpc_ich_init_wdt(struct pci_dev *dev, res->end = base_addr + ACPIBASE_GCS_END; } - lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id); + lpc_ich_finalize_cell(dev, &lpc_ich_cells[LPC_WDT]); ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT], 1, NULL, 0, NULL); @@ -858,14 +874,35 @@ wdt_done: static int lpc_ich_probe(struct pci_dev *dev, const struct pci_device_id *id) { + struct lpc_ich_priv *priv; int ret; bool cell_added = false; - ret = lpc_ich_init_wdt(dev, id); + priv = kmalloc(GFP_KERNEL, sizeof(struct lpc_ich_priv)); + if (!priv) + return -ENOMEM; + + priv->chipset = id->driver_data; + priv->acpi.save = -1; + priv->acpi.base = ACPIBASE; + priv->acpi.ctrl = ACPICTRL; + + priv->gpio.save = -1; + if (priv->chipset <= LPC_ICH5) { + priv->gpio.base = GPIOBASE_ICH0; + priv->gpio.ctrl = GPIOCTRL_ICH0; + } else { + priv->gpio.base = GPIOBASE_ICH6; + priv->gpio.ctrl = GPIOCTRL_ICH6; + } + + pci_set_drvdata(dev, priv); + + ret = lpc_ich_init_wdt(dev); if (!ret) cell_added = true; - ret = lpc_ich_init_gpio(dev, id); + ret = lpc_ich_init_gpio(dev); if (!ret) cell_added = true; @@ -876,6 +913,8 @@ static int lpc_ich_probe(struct pci_dev *dev, if (!cell_added) { dev_warn(&dev->dev, "No MFD cells added\n"); lpc_ich_restore_config_space(dev); + pci_set_drvdata(dev, NULL); + kfree(priv); return -ENODEV; } @@ -884,8 +923,12 @@ static int lpc_ich_probe(struct pci_dev *dev, static void lpc_ich_remove(struct pci_dev *dev) { + void *priv = pci_get_drvdata(dev); + mfd_remove_devices(&dev->dev); lpc_ich_restore_config_space(dev); + pci_set_drvdata(dev, NULL); + kfree(priv); } static struct pci_driver lpc_ich_driver = { From 98c60a0d3afc8f68e6e4b85b93df14e238fec3cb Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 18 Jan 2013 12:40:11 +0100 Subject: [PATCH 2592/4607] mfd: dbx500-prcmu: Add watchdog ID definitions Add definition of watchdog IDs to be used by ux500_wdt driver. Acked-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Samuel Ortiz --- include/linux/mfd/dbx500-prcmu.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index c202d6c4d879..c6e0608a21b5 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -146,6 +146,18 @@ enum prcmu_clock { PRCMU_DSI2ESCCLK, }; +/** + * enum prcmu_wdog_id - PRCMU watchdog IDs + * @PRCMU_WDOG_ALL: use all timers + * @PRCMU_WDOG_CPU1: use first CPU timer only + * @PRCMU_WDOG_CPU2: use second CPU timer conly + */ +enum prcmu_wdog_id { + PRCMU_WDOG_ALL = 0x00, + PRCMU_WDOG_CPU1 = 0x01, + PRCMU_WDOG_CPU2 = 0x02, +}; + /** * enum ape_opp - APE OPP states definition * @APE_OPP_INIT: From 6f8cfa99845f12ab98990baef739e7e93565de87 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 18 Jan 2013 12:40:12 +0100 Subject: [PATCH 2593/4607] mfd: dbx500-prcmu: Export a9wdog functions Add EXPORT_SYMBOL to db500_prcmu_*_a9wdog functions to allow usage from module. Acked-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 268f45d42394..7a63fa86bf01 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2212,21 +2212,25 @@ int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off) sleep_auto_off ? A9WDOG_AUTO_OFF_EN : A9WDOG_AUTO_OFF_DIS); } +EXPORT_SYMBOL(db8500_prcmu_config_a9wdog); int db8500_prcmu_enable_a9wdog(u8 id) { return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0); } +EXPORT_SYMBOL(db8500_prcmu_enable_a9wdog); int db8500_prcmu_disable_a9wdog(u8 id) { return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0); } +EXPORT_SYMBOL(db8500_prcmu_disable_a9wdog); int db8500_prcmu_kick_a9wdog(u8 id) { return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0); } +EXPORT_SYMBOL(db8500_prcmu_kick_a9wdog); /* * timeout is 28 bit, in ms. @@ -2244,6 +2248,7 @@ int db8500_prcmu_load_a9wdog(u8 id, u32 timeout) (u8)((timeout >> 12) & 0xff), (u8)((timeout >> 20) & 0xff)); } +EXPORT_SYMBOL(db8500_prcmu_load_a9wdog); /** * prcmu_abb_read() - Read register value(s) from the ABB. From f0e5bd412fde30de3839c8dfa93a3e19e71ee462 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 29 Jan 2013 09:57:19 +0100 Subject: [PATCH 2594/4607] watchdog: Add support for ux500_wdt watchdog This patch adds support for the ux500_wdt watchdog that is found in ST-Ericsson Ux500 platform. The driver is based on PRCMU APIs. Acked-by: Linus Walleij Acked-by: Lee Jones Acked-by: Wim Van Sebroeck Signed-off-by: Fabio Baltieri Signed-off-by: Samuel Ortiz --- drivers/watchdog/Kconfig | 12 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/ux500_wdt.c | 171 ++++++++++++++++++++++++ include/linux/platform_data/ux500_wdt.h | 19 +++ 4 files changed, 203 insertions(+) create mode 100644 drivers/watchdog/ux500_wdt.c create mode 100644 include/linux/platform_data/ux500_wdt.h diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 7f809fd4a57f..26e1fdbddf69 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -364,6 +364,18 @@ config IMX2_WDT To compile this driver as a module, choose M here: the module will be called imx2_wdt. +config UX500_WATCHDOG + tristate "ST-Ericsson Ux500 watchdog" + depends on MFD_DB8500_PRCMU + select WATCHDOG_CORE + default y + help + Say Y here to include Watchdog timer support for the watchdog + existing in the prcmu of ST-Ericsson Ux500 series platforms. + + To compile this driver as a module, choose M here: the + module will be called ux500_wdt. + # AVR32 Architecture config AT32AP700X_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 97bbdb3a4648..bec86ee6e9e3 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o +obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c new file mode 100644 index 000000000000..a614d84121c3 --- /dev/null +++ b/drivers/watchdog/ux500_wdt.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) ST-Ericsson SA 2011-2013 + * + * License Terms: GNU General Public License v2 + * + * Author: Mathieu Poirier for ST-Ericsson + * Author: Jonas Aaberg for ST-Ericsson + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define WATCHDOG_TIMEOUT 600 /* 10 minutes */ + +#define WATCHDOG_MIN 0 +#define WATCHDOG_MAX28 268435 /* 28 bit resolution in ms == 268435.455 s */ +#define WATCHDOG_MAX32 4294967 /* 32 bit resolution in ms == 4294967.295 s */ + +static unsigned int timeout = WATCHDOG_TIMEOUT; +module_param(timeout, uint, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. default=" + __MODULE_STRING(WATCHDOG_TIMEOUT) "."); + +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int ux500_wdt_start(struct watchdog_device *wdd) +{ + return prcmu_enable_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_stop(struct watchdog_device *wdd) +{ + return prcmu_disable_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_keepalive(struct watchdog_device *wdd) +{ + return prcmu_kick_a9wdog(PRCMU_WDOG_ALL); +} + +static int ux500_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + ux500_wdt_stop(wdd); + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(wdd); + + return 0; +} + +static const struct watchdog_info ux500_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "Ux500 WDT", + .firmware_version = 1, +}; + +static const struct watchdog_ops ux500_wdt_ops = { + .owner = THIS_MODULE, + .start = ux500_wdt_start, + .stop = ux500_wdt_stop, + .ping = ux500_wdt_keepalive, + .set_timeout = ux500_wdt_set_timeout, +}; + +static struct watchdog_device ux500_wdt = { + .info = &ux500_wdt_info, + .ops = &ux500_wdt_ops, + .min_timeout = WATCHDOG_MIN, + .max_timeout = WATCHDOG_MAX32, +}; + +static int ux500_wdt_probe(struct platform_device *pdev) +{ + int ret; + struct ux500_wdt_data *pdata = pdev->dev.platform_data; + + if (pdata) { + if (pdata->timeout > 0) + timeout = pdata->timeout; + if (pdata->has_28_bits_resolution) + ux500_wdt.max_timeout = WATCHDOG_MAX28; + } + + watchdog_set_nowayout(&ux500_wdt, nowayout); + + /* disable auto off on sleep */ + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); + + /* set HW initial value */ + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + + ret = watchdog_register_device(&ux500_wdt); + if (ret) + return ret; + + dev_info(&pdev->dev, "initialized\n"); + + return 0; +} + +static int ux500_wdt_remove(struct platform_device *dev) +{ + watchdog_unregister_device(&ux500_wdt); + + return 0; +} + +#ifdef CONFIG_PM +static int ux500_wdt_suspend(struct platform_device *pdev, + pm_message_t state) +{ + if (watchdog_active(&ux500_wdt)) { + ux500_wdt_stop(&ux500_wdt); + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, true); + + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(&ux500_wdt); + } + return 0; +} + +static int ux500_wdt_resume(struct platform_device *pdev) +{ + if (watchdog_active(&ux500_wdt)) { + ux500_wdt_stop(&ux500_wdt); + prcmu_config_a9wdog(PRCMU_WDOG_CPU1, false); + + prcmu_load_a9wdog(PRCMU_WDOG_ALL, timeout * 1000); + ux500_wdt_start(&ux500_wdt); + } + return 0; +} +#else +#define ux500_wdt_suspend NULL +#define ux500_wdt_resume NULL +#endif + +static struct platform_driver ux500_wdt_driver = { + .probe = ux500_wdt_probe, + .remove = ux500_wdt_remove, + .suspend = ux500_wdt_suspend, + .resume = ux500_wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "ux500_wdt", + }, +}; + +module_platform_driver(ux500_wdt_driver); + +MODULE_AUTHOR("Jonas Aaberg "); +MODULE_DESCRIPTION("Ux500 Watchdog Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS("platform:ux500_wdt"); diff --git a/include/linux/platform_data/ux500_wdt.h b/include/linux/platform_data/ux500_wdt.h new file mode 100644 index 000000000000..1689ff4c3bfd --- /dev/null +++ b/include/linux/platform_data/ux500_wdt.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) ST Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * + * STE Ux500 Watchdog platform data + */ +#ifndef __UX500_WDT_H +#define __UX500_WDT_H + +/** + * struct ux500_wdt_data + */ +struct ux500_wdt_data { + unsigned int timeout; + bool has_28_bits_resolution; +}; + +#endif /* __UX500_WDT_H */ From b3aac62bbb1c3f8e71c88e6e477836def3058fe8 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Fri, 18 Jan 2013 12:40:14 +0100 Subject: [PATCH 2595/4607] mfd: db8500-prcmu: Add ux500_wdt mfd_cell This patch adds the necessary structures to use the watchdog functionality of PRCMU. The watchdog driver is named ux500_wdt. Acked-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Samuel Ortiz --- drivers/mfd/db8500-prcmu.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 7a63fa86bf01..e42a417adc5f 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -3074,6 +3075,11 @@ static struct resource ab8500_resources[] = { } }; +static struct ux500_wdt_data db8500_wdt_pdata = { + .timeout = 600, /* 10 minutes */ + .has_28_bits_resolution = true, +}; + static struct mfd_cell db8500_prcmu_devs[] = { { .name = "db8500-prcmu-regulators", @@ -3087,6 +3093,12 @@ static struct mfd_cell db8500_prcmu_devs[] = { .platform_data = &db8500_cpufreq_table, .pdata_size = sizeof(db8500_cpufreq_table), }, + { + .name = "ux500_wdt", + .platform_data = &db8500_wdt_pdata, + .pdata_size = sizeof(db8500_wdt_pdata), + .id = -1, + }, { .name = "ab8500-core", .of_compatible = "stericsson,ab8500", From 306df798507d8e009a7d4a5e8ce238a3b107de20 Mon Sep 17 00:00:00 2001 From: Yi Zhang Date: Tue, 22 Jan 2013 10:43:45 +0800 Subject: [PATCH 2596/4607] mfd: 88pm80x: Remove redundant devm_* calls devm_* functions are device managed and make error handling and code simpler; it also fix error exit paths Signed-off-by: Yi Zhang Signed-off-by: Samuel Ortiz --- drivers/mfd/88pm800.c | 10 +++------- drivers/mfd/88pm805.c | 4 ++-- drivers/mfd/88pm80x.c | 22 ++++------------------ include/linux/mfd/88pm80x.h | 2 +- 4 files changed, 10 insertions(+), 28 deletions(-) diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c index 391e23e6a647..582bda543520 100644 --- a/drivers/mfd/88pm800.c +++ b/drivers/mfd/88pm800.c @@ -531,7 +531,7 @@ static int pm800_probe(struct i2c_client *client, ret = device_800_init(chip, pdata); if (ret) { dev_err(chip->dev, "%s id 0x%x failed!\n", __func__, chip->id); - goto err_800_init; + goto err_subchip_alloc; } ret = pm800_pages_init(chip); @@ -546,10 +546,8 @@ static int pm800_probe(struct i2c_client *client, err_page_init: mfd_remove_devices(chip->dev); device_irq_exit_800(chip); -err_800_init: - devm_kfree(&client->dev, subchip); err_subchip_alloc: - pm80x_deinit(client); + pm80x_deinit(); out_init: return ret; } @@ -562,9 +560,7 @@ static int pm800_remove(struct i2c_client *client) device_irq_exit_800(chip); pm800_pages_exit(chip); - devm_kfree(&client->dev, chip->subchip); - - pm80x_deinit(client); + pm80x_deinit(); return 0; } diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c index e671230be2b1..65d7ac099b20 100644 --- a/drivers/mfd/88pm805.c +++ b/drivers/mfd/88pm805.c @@ -257,7 +257,7 @@ static int pm805_probe(struct i2c_client *client, pdata->plat_config(chip, pdata); err_805_init: - pm80x_deinit(client); + pm80x_deinit(); out_init: return ret; } @@ -269,7 +269,7 @@ static int pm805_remove(struct i2c_client *client) mfd_remove_devices(chip->dev); device_irq_exit_805(chip); - pm80x_deinit(client); + pm80x_deinit(); return 0; } diff --git a/drivers/mfd/88pm80x.c b/drivers/mfd/88pm80x.c index 1adb355d86d1..f736a46eb8c0 100644 --- a/drivers/mfd/88pm80x.c +++ b/drivers/mfd/88pm80x.c @@ -48,14 +48,12 @@ int pm80x_init(struct i2c_client *client, ret = PTR_ERR(map); dev_err(&client->dev, "Failed to allocate register map: %d\n", ret); - goto err_regmap_init; + return ret; } chip->id = id->driver_data; - if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) { - ret = -EINVAL; - goto err_chip_id; - } + if (chip->id < CHIP_PM800 || chip->id > CHIP_PM805) + return -EINVAL; chip->client = client; chip->regmap = map; @@ -82,19 +80,11 @@ int pm80x_init(struct i2c_client *client, } return 0; - -err_chip_id: - regmap_exit(map); -err_regmap_init: - devm_kfree(&client->dev, chip); - return ret; } EXPORT_SYMBOL_GPL(pm80x_init); -int pm80x_deinit(struct i2c_client *client) +int pm80x_deinit(void) { - struct pm80x_chip *chip = i2c_get_clientdata(client); - /* * workaround: clear the dependency between pm800 and pm805. * would remove it after HW chip fixes the issue. @@ -103,10 +93,6 @@ int pm80x_deinit(struct i2c_client *client) g_pm80x_chip->companion = NULL; else g_pm80x_chip = NULL; - - regmap_exit(chip->regmap); - devm_kfree(&client->dev, chip); - return 0; } EXPORT_SYMBOL_GPL(pm80x_deinit); diff --git a/include/linux/mfd/88pm80x.h b/include/linux/mfd/88pm80x.h index 478672ed0c3d..e94537befabd 100644 --- a/include/linux/mfd/88pm80x.h +++ b/include/linux/mfd/88pm80x.h @@ -365,5 +365,5 @@ static inline int pm80x_dev_resume(struct device *dev) extern int pm80x_init(struct i2c_client *client, const struct i2c_device_id *id); -extern int pm80x_deinit(struct i2c_client *client); +extern int pm80x_deinit(void); #endif /* __LINUX_MFD_88PM80X_H */ From 154c4c7b613049168e2923d096b7c2d100ba8a6a Mon Sep 17 00:00:00 2001 From: "Vishwanathrao Badarkhe, Manish" Date: Thu, 24 Jan 2013 16:25:17 +0530 Subject: [PATCH 2597/4607] mfd: tps6507x: Add DT support Add device tree based initialization support for TI's tps6507x mfd device. Signed-off-by: Vishwanathrao Badarkhe, Manish Signed-off-by: Samuel Ortiz --- drivers/mfd/tps6507x.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c index 409afa23d5dc..5ad4b772b097 100644 --- a/drivers/mfd/tps6507x.c +++ b/drivers/mfd/tps6507x.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -116,11 +117,19 @@ static const struct i2c_device_id tps6507x_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, tps6507x_i2c_id); +#ifdef CONFIG_OF +static struct of_device_id tps6507x_of_match[] = { + {.compatible = "ti,tps6507x", }, + {}, +}; +MODULE_DEVICE_TABLE(of, tps6507x_of_match); +#endif static struct i2c_driver tps6507x_i2c_driver = { .driver = { .name = "tps6507x", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps6507x_of_match), }, .probe = tps6507x_i2c_probe, .remove = tps6507x_i2c_remove, From 5f384c1f8be19487f904731d7232120dcfeca8e1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 25 Jan 2013 17:09:23 +0800 Subject: [PATCH 2598/4607] mfd: wm5102: Make DSP scratch registers readable Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm5102-tables.c | 8 ++++++++ include/linux/mfd/arizona/registers.h | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index ff8f0804dde3..d754596eb942 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1795,6 +1795,10 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: + case ARIZONA_DSP1_SCRATCH_0: + case ARIZONA_DSP1_SCRATCH_1: + case ARIZONA_DSP1_SCRATCH_2: + case ARIZONA_DSP1_SCRATCH_3: return true; default: if ((reg >= 0x100000 && reg < 0x106000) || @@ -1849,6 +1853,10 @@ static bool wm5102_volatile_register(struct device *dev, unsigned int reg) case ARIZONA_DSP1_STATUS_1: case ARIZONA_DSP1_STATUS_2: case ARIZONA_DSP1_STATUS_3: + case ARIZONA_DSP1_SCRATCH_0: + case ARIZONA_DSP1_SCRATCH_1: + case ARIZONA_DSP1_SCRATCH_2: + case ARIZONA_DSP1_SCRATCH_3: case ARIZONA_HEADPHONE_DETECT_2: case ARIZONA_MIC_DETECT_3: return true; diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 1f6fe31a4d5c..718126084ad1 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -982,18 +982,34 @@ #define ARIZONA_DSP1_STATUS_1 0x1104 #define ARIZONA_DSP1_STATUS_2 0x1105 #define ARIZONA_DSP1_STATUS_3 0x1106 +#define ARIZONA_DSP1_SCRATCH_0 0x1140 +#define ARIZONA_DSP1_SCRATCH_1 0x1141 +#define ARIZONA_DSP1_SCRATCH_2 0x1142 +#define ARIZONA_DSP1_SCRATCH_3 0x1143 #define ARIZONA_DSP2_CONTROL_1 0x1200 #define ARIZONA_DSP2_CLOCKING_1 0x1201 #define ARIZONA_DSP2_STATUS_1 0x1204 #define ARIZONA_DSP2_STATUS_2 0x1205 +#define ARIZONA_DSP2_SCRATCH_0 0x1240 +#define ARIZONA_DSP2_SCRATCH_1 0x1241 +#define ARIZONA_DSP2_SCRATCH_2 0x1242 +#define ARIZONA_DSP2_SCRATCH_3 0x1243 #define ARIZONA_DSP3_CONTROL_1 0x1300 #define ARIZONA_DSP3_CLOCKING_1 0x1301 #define ARIZONA_DSP3_STATUS_1 0x1304 #define ARIZONA_DSP3_STATUS_2 0x1305 +#define ARIZONA_DSP3_SCRATCH_0 0x1340 +#define ARIZONA_DSP3_SCRATCH_1 0x1341 +#define ARIZONA_DSP3_SCRATCH_2 0x1342 +#define ARIZONA_DSP3_SCRATCH_3 0x1343 #define ARIZONA_DSP4_CONTROL_1 0x1400 #define ARIZONA_DSP4_CLOCKING_1 0x1401 #define ARIZONA_DSP4_STATUS_1 0x1404 #define ARIZONA_DSP4_STATUS_2 0x1405 +#define ARIZONA_DSP4_SCRATCH_0 0x1440 +#define ARIZONA_DSP4_SCRATCH_1 0x1441 +#define ARIZONA_DSP4_SCRATCH_2 0x1442 +#define ARIZONA_DSP4_SCRATCH_3 0x1443 /* * Field Definitions. From 40719314f259ab9409ca3d48551c17aa23bc2b4d Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 29 Jan 2013 14:35:15 +0530 Subject: [PATCH 2599/4607] mfd: tps65090: add DT support for tps65090 Add device tree support for the TI PMIC TPS65090. The device can be registered through platform or DT. Add device tree binding document for this device. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- .../bindings/regulator/tps65090.txt | 122 ++++++++++++++++++ drivers/mfd/tps65090.c | 22 +++- 2 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/tps65090.txt diff --git a/Documentation/devicetree/bindings/regulator/tps65090.txt b/Documentation/devicetree/bindings/regulator/tps65090.txt new file mode 100644 index 000000000000..313a60ba61d8 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/tps65090.txt @@ -0,0 +1,122 @@ +TPS65090 regulators + +Required properties: +- compatible: "ti,tps65090" +- reg: I2C slave address +- interrupts: the interrupt outputs of the controller +- regulators: A node that houses a sub-node for each regulator within the + device. Each sub-node is identified using the node's name, with valid + values listed below. The content of each sub-node is defined by the + standard binding for regulators; see regulator.txt. + dcdc[1-3], fet[1-7] and ldo[1-2] respectively. +- vsys[1-3]-supply: The input supply for DCDC[1-3] respectively. +- infet[1-7]-supply: The input supply for FET[1-7] respectively. +- vsys-l[1-2]-supply: The input supply for LDO[1-2] respectively. + +Optional properties: +- ti,enable-ext-control: This is applicable for DCDC1, DCDC2 and DCDC3. + If DCDCs are externally controlled then this property should be there. +- "dcdc-ext-control-gpios: This is applicable for DCDC1, DCDC2 and DCDC3. + If DCDCs are externally controlled and if it is from GPIO then GPIO + number should be provided. If it is externally controlled and no GPIO + entry then driver will just configure this rails as external control + and will not provide any enable/disable APIs. + +Each regulator is defined using the standard binding for regulators. + +Example: + + tps65090@48 { + compatible = "ti,tps65090"; + reg = <0x48>; + interrupts = <0 88 0x4>; + + vsys1-supply = <&some_reg>; + vsys2-supply = <&some_reg>; + vsys3-supply = <&some_reg>; + infet1-supply = <&some_reg>; + infet2-supply = <&some_reg>; + infet3-supply = <&some_reg>; + infet4-supply = <&some_reg>; + infet5-supply = <&some_reg>; + infet6-supply = <&some_reg>; + infet7-supply = <&some_reg>; + vsys_l1-supply = <&some_reg>; + vsys_l2-supply = <&some_reg>; + + regulators { + dcdc1 { + regulator-name = "dcdc1"; + regulator-boot-on; + regulator-always-on; + ti,enable-ext-control; + dcdc-ext-control-gpios = <&gpio 10 0>; + }; + + dcdc2 { + regulator-name = "dcdc2"; + regulator-boot-on; + regulator-always-on; + }; + + dcdc3 { + regulator-name = "dcdc3"; + regulator-boot-on; + regulator-always-on; + }; + + fet1 { + regulator-name = "fet1"; + regulator-boot-on; + regulator-always-on; + }; + + fet2 { + regulator-name = "fet2"; + regulator-boot-on; + regulator-always-on; + }; + + fet3 { + regulator-name = "fet3"; + regulator-boot-on; + regulator-always-on; + }; + + fet4 { + regulator-name = "fet4"; + regulator-boot-on; + regulator-always-on; + }; + + fet5 { + regulator-name = "fet5"; + regulator-boot-on; + regulator-always-on; + }; + + fet6 { + regulator-name = "fet6"; + regulator-boot-on; + regulator-always-on; + }; + + fet7 { + regulator-name = "fet7"; + regulator-boot-on; + regulator-always-on; + }; + + ldo1 { + regulator-name = "ldo1"; + regulator-boot-on; + regulator-always-on; + }; + + ldo2 { + regulator-name = "ldo2"; + regulator-boot-on; + regulator-always-on; + }; + }; + }; diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 8d12a8e00d9c..b49654587ce5 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #define NUM_INT_REG 2 @@ -148,18 +150,31 @@ static const struct regmap_config tps65090_regmap_config = { .volatile_reg = is_volatile_reg, }; +#ifdef CONFIG_OF +static const struct of_device_id tps65090_of_match[] = { + { .compatible = "ti,tps65090",}, + {}, +}; +MODULE_DEVICE_TABLE(of, tps65090_of_match); +#endif + static int tps65090_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tps65090_platform_data *pdata = client->dev.platform_data; + int irq_base = 0; struct tps65090 *tps65090; int ret; - if (!pdata) { - dev_err(&client->dev, "tps65090 requires platform data\n"); + if (!pdata && !client->dev.of_node) { + dev_err(&client->dev, + "tps65090 requires platform data or of_node\n"); return -EINVAL; } + if (pdata) + irq_base = pdata->irq_base; + tps65090 = devm_kzalloc(&client->dev, sizeof(*tps65090), GFP_KERNEL); if (!tps65090) { dev_err(&client->dev, "mem alloc for tps65090 failed\n"); @@ -178,7 +193,7 @@ static int tps65090_i2c_probe(struct i2c_client *client, if (client->irq) { ret = regmap_add_irq_chip(tps65090->rmap, client->irq, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, pdata->irq_base, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base, &tps65090_irq_chip, &tps65090->irq_data); if (ret) { dev_err(&client->dev, @@ -247,6 +262,7 @@ static struct i2c_driver tps65090_driver = { .driver = { .name = "tps65090", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps65090_of_match), .pm = &tps65090_pm_ops, }, .probe = tps65090_i2c_probe, From 4f979ed5e2656570f433101bfc5bc116a919316b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 29 Jan 2013 14:35:17 +0530 Subject: [PATCH 2600/4607] mfd: tps65090: Pass irq domain when adding mfd sub devices When device is get added through DT then irq_base is 0 (zero) and in this case regmap_irq_chip_get_base() generates warning. The interrupt of this device get added through irq_domain_add_linear() when irq_base is 0. Hence pass the irq domain in place of base_irq when calling mfd_add_devices(). Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65090.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index b49654587ce5..2ad0a1528650 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -204,7 +204,7 @@ static int tps65090_i2c_probe(struct i2c_client *client, ret = mfd_add_devices(tps65090->dev, -1, tps65090s, ARRAY_SIZE(tps65090s), NULL, - regmap_irq_chip_get_base(tps65090->irq_data), NULL); + 0, regmap_irq_get_domain(tps65090->irq_data)); if (ret) { dev_err(&client->dev, "add mfd devices failed with err: %d\n", ret); From 5fd86d34c1fad19a570e7f787d5e4ea94c6f38f7 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 29 Jan 2013 14:35:18 +0530 Subject: [PATCH 2601/4607] mfd: tps65090: remove suspend/resume callbacks The tps65090 mfd driver implement the suspend/resume callbacks which just disable and enable irqs in suspend/resume respectively. This operation is already done in irq suspend and irq_resume and hence it is not require to implement the same in the driver. Remove this non-require code. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz --- drivers/mfd/tps65090.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 2ad0a1528650..98edb5be85c6 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -230,28 +230,6 @@ static int tps65090_i2c_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int tps65090_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - if (client->irq) - disable_irq(client->irq); - return 0; -} - -static int tps65090_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - if (client->irq) - enable_irq(client->irq); - return 0; -} -#endif - -static const struct dev_pm_ops tps65090_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tps65090_suspend, tps65090_resume) -}; - static const struct i2c_device_id tps65090_id_table[] = { { "tps65090", 0 }, { }, @@ -263,7 +241,6 @@ static struct i2c_driver tps65090_driver = { .name = "tps65090", .owner = THIS_MODULE, .of_match_table = of_match_ptr(tps65090_of_match), - .pm = &tps65090_pm_ops, }, .probe = tps65090_i2c_probe, .remove = tps65090_i2c_remove, From 3730bb8b65f9ee8b7097021f8073b80511af770d Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:32 +0800 Subject: [PATCH 2602/4607] mfd: rtsx: Fix typo in comment Fix a misspelling word in comment Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- include/linux/mfd/rtsx_pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 4b117a3f54d4..3f2bf26ca0d1 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -465,7 +465,7 @@ #define SD_RSP_TYPE_R6 0x01 #define SD_RSP_TYPE_R7 0x01 -/* SD_CONFIURE3 */ +/* SD_CONFIGURE3 */ #define SD_RSP_80CLK_TIMEOUT_EN 0x01 /* Card Transfer Reset Register */ From 9871c799d15dfb1609582bf8b2868217802a2100 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:33 +0800 Subject: [PATCH 2603/4607] mfd: rtsx: Remove redundant code In function rtsx_pci_add_sg_tbl, the statement "ptr++" is useless. Signed-off-by: Wei WANG Acked-by: Borislav Petkov Signed-off-by: Samuel Ortiz --- drivers/mfd/rtsx_pcr.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 9fc57009e228..c021ce3df955 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -325,7 +325,6 @@ static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr, val = ((u64)addr << 32) | ((u64)len << 12) | option; put_unaligned_le64(val, ptr); - ptr++; pcr->sgi++; } From f84ef04227d8983c8f76ac0f5cf8b0a15e0c67af Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:34 +0800 Subject: [PATCH 2604/4607] mfd: rtsx: Declare that the DMA address limitation is 32bit explicitly Realtek PCIe card reader only supports 32bit DMA. This declaration can improve the readability. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtsx_pcr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index c021ce3df955..b2cfd2ed616c 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -1029,6 +1029,10 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device, (int)pcidev->revision); + ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32)); + if (ret < 0) + return ret; + ret = pci_enable_device(pcidev); if (ret) return ret; From 678cacdfda800b0cdbfdd01350dcf5e3b767f6ed Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:35 +0800 Subject: [PATCH 2605/4607] mfd: rtsx: Fix checkpatch warning WARNING: Avoid CamelCase: + u8 N, min_N, max_N, clk_divider; WARNING: Avoid CamelCase: + u8 N, min_N, max_N, clk_divider; Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtsx_pcr.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index b2cfd2ed616c..4897c39cfb7b 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -590,7 +590,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) { int err, clk; - u8 N, min_N, max_N, clk_divider; + u8 n, min_n, max_n, clk_divider; u8 mcu_cnt, div, max_div; u8 depth[] = { [RTSX_SSC_DEPTH_4M] = SSC_DEPTH_4M, @@ -615,8 +615,8 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, card_clock /= 1000000; dev_dbg(&(pcr->pci->dev), "Switch card clock to %dMHz\n", card_clock); - min_N = 80; - max_N = 208; + min_n = 80; + max_n = 208; max_div = CLK_DIV_8; clk = card_clock; @@ -630,30 +630,30 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, return 0; if (pcr->ops->conv_clk_and_div_n) - N = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N); + n = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N); else - N = (u8)(clk - 2); - if ((clk <= 2) || (N > max_N)) + n = (u8)(clk - 2); + if ((clk <= 2) || (n > max_n)) return -EINVAL; mcu_cnt = (u8)(125/clk + 3); if (mcu_cnt > 15) mcu_cnt = 15; - /* Make sure that the SSC clock div_n is equal or greater than min_N */ + /* Make sure that the SSC clock div_n is equal or greater than min_n */ div = CLK_DIV_1; - while ((N < min_N) && (div < max_div)) { + while ((n < min_n) && (div < max_div)) { if (pcr->ops->conv_clk_and_div_n) { - int dbl_clk = pcr->ops->conv_clk_and_div_n(N, + int dbl_clk = pcr->ops->conv_clk_and_div_n(n, DIV_N_TO_CLK) * 2; - N = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk, + n = (u8)pcr->ops->conv_clk_and_div_n(dbl_clk, CLK_TO_DIV_N); } else { - N = (N + 2) * 2 - 2; + n = (n + 2) * 2 - 2; } div++; } - dev_dbg(&(pcr->pci->dev), "N = %d, div = %d\n", N, div); + dev_dbg(&(pcr->pci->dev), "n = %d, div = %d\n", n, div); ssc_depth = depth[ssc_depth]; if (double_clk) @@ -670,7 +670,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, 0); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL2, SSC_DEPTH_MASK, ssc_depth); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, N); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_DIV_N_0, 0xFF, n); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, SSC_RSTB, SSC_RSTB); if (vpclk) { rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, From eebbe2541684da99bf0b179d5182dc8025f5f5b6 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:36 +0800 Subject: [PATCH 2606/4607] mfd: rtsx: Use macros to replace some variables In function rtsx_pci_switch_clock, some variables, such as min_n, max_n, and max_div, are not necessary. And those assigned values look very obscure for others. It's more proper to use macro definitions here to replace these variables. Signed-off-by: Wei WANG Acked-by: Borislav Petkov Signed-off-by: Samuel Ortiz --- drivers/mfd/rtsx_pcr.c | 13 ++++--------- drivers/mfd/rtsx_pcr.h | 3 +++ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 4897c39cfb7b..67bb34ef0ebd 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -590,8 +590,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk) { int err, clk; - u8 n, min_n, max_n, clk_divider; - u8 mcu_cnt, div, max_div; + u8 n, clk_divider, mcu_cnt, div; u8 depth[] = { [RTSX_SSC_DEPTH_4M] = SSC_DEPTH_4M, [RTSX_SSC_DEPTH_2M] = SSC_DEPTH_2M, @@ -615,10 +614,6 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, card_clock /= 1000000; dev_dbg(&(pcr->pci->dev), "Switch card clock to %dMHz\n", card_clock); - min_n = 80; - max_n = 208; - max_div = CLK_DIV_8; - clk = card_clock; if (!initial_mode && double_clk) clk = card_clock * 2; @@ -633,16 +628,16 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, n = (u8)pcr->ops->conv_clk_and_div_n(clk, CLK_TO_DIV_N); else n = (u8)(clk - 2); - if ((clk <= 2) || (n > max_n)) + if ((clk <= 2) || (n > MAX_DIV_N_PCR)) return -EINVAL; mcu_cnt = (u8)(125/clk + 3); if (mcu_cnt > 15) mcu_cnt = 15; - /* Make sure that the SSC clock div_n is equal or greater than min_n */ + /* Make sure that the SSC clock div_n is not less than MIN_DIV_N_PCR */ div = CLK_DIV_1; - while ((n < min_n) && (div < max_div)) { + while ((n < MIN_DIV_N_PCR) && (div < CLK_DIV_8)) { if (pcr->ops->conv_clk_and_div_n) { int dbl_clk = pcr->ops->conv_clk_and_div_n(n, DIV_N_TO_CLK) * 2; diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 12462c1df1a9..33c210be1daa 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -25,6 +25,9 @@ #include +#define MIN_DIV_N_PCR 80 +#define MAX_DIV_N_PCR 208 + void rts5209_init_params(struct rtsx_pcr *pcr); void rts5229_init_params(struct rtsx_pcr *pcr); void rtl8411_init_params(struct rtsx_pcr *pcr); From 504decc0a063e6a09a1e5b203ca68bc21dfffde9 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 29 Jan 2013 15:21:37 +0800 Subject: [PATCH 2607/4607] mfd: rtsx: Optimize card detect flow 1. Schedule card detect work at the end of the ISR 2. Callback function ops->cd_deglitch may delay for a period of time. It is not proper to call this callback when local irq disabled. 3. Card detect flow can't be executed in parallel with other card reader operations, so it's better to be protected by mutex. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtsx_pcr.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 67bb34ef0ebd..9016932f0267 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -752,7 +752,7 @@ static void rtsx_pci_card_detect(struct work_struct *work) struct delayed_work *dwork; struct rtsx_pcr *pcr; unsigned long flags; - unsigned int card_detect = 0; + unsigned int card_detect = 0, card_inserted, card_removed; u32 irq_status; dwork = to_delayed_work(work); @@ -760,26 +760,33 @@ static void rtsx_pci_card_detect(struct work_struct *work) dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__); + mutex_lock(&pcr->pcr_mutex); spin_lock_irqsave(&pcr->lock, flags); irq_status = rtsx_pci_readl(pcr, RTSX_BIPR); dev_dbg(&(pcr->pci->dev), "irq_status: 0x%08x\n", irq_status); - if (pcr->card_inserted || pcr->card_removed) { - dev_dbg(&(pcr->pci->dev), - "card_inserted: 0x%x, card_removed: 0x%x\n", - pcr->card_inserted, pcr->card_removed); - - if (pcr->ops->cd_deglitch) - pcr->card_inserted = pcr->ops->cd_deglitch(pcr); - - card_detect = pcr->card_inserted | pcr->card_removed; - pcr->card_inserted = 0; - pcr->card_removed = 0; - } + irq_status &= CARD_EXIST; + card_inserted = pcr->card_inserted & irq_status; + card_removed = pcr->card_removed; + pcr->card_inserted = 0; + pcr->card_removed = 0; spin_unlock_irqrestore(&pcr->lock, flags); + if (card_inserted || card_removed) { + dev_dbg(&(pcr->pci->dev), + "card_inserted: 0x%x, card_removed: 0x%x\n", + card_inserted, card_removed); + + if (pcr->ops->cd_deglitch) + card_inserted = pcr->ops->cd_deglitch(pcr); + + card_detect = card_inserted | card_removed; + } + + mutex_unlock(&pcr->pcr_mutex); + if ((card_detect & SD_EXIST) && pcr->slots[RTSX_SD_CARD].card_event) pcr->slots[RTSX_SD_CARD].card_event( pcr->slots[RTSX_SD_CARD].p_dev); @@ -830,10 +837,6 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) } } - if (pcr->card_inserted || pcr->card_removed) - schedule_delayed_work(&pcr->carddet_work, - msecs_to_jiffies(200)); - if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) { if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) { pcr->trans_result = TRANS_RESULT_FAIL; @@ -846,6 +849,10 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id) } } + if (pcr->card_inserted || pcr->card_removed) + schedule_delayed_work(&pcr->carddet_work, + msecs_to_jiffies(200)); + spin_unlock(&pcr->lock); return IRQ_HANDLED; } From 151621a704fc7b8eaa1d6905bec0c6388b0a57af Mon Sep 17 00:00:00 2001 From: Hongbo Zhang Date: Wed, 30 Jan 2013 18:21:27 +0800 Subject: [PATCH 2608/4607] mfd: ab8500: Rename ab8500 to abx500 for hwmon driver We are using a generic abx500 hwmon layer, so rename specific ab8500 to generic abx500 for hwmon device and driver matching. Signed-off-by: Hongbo Zhang Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 4778bb124efe..cdcaa5b78c4d 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -923,7 +923,7 @@ static struct resource ab8505_iddet_resources[] = { static struct resource ab8500_temp_resources[] = { { - .name = "AB8500_TEMP_WARM", + .name = "ABX500_TEMP_WARM", .start = AB8500_INT_TEMP_WARM, .end = AB8500_INT_TEMP_WARM, .flags = IORESOURCE_IRQ, @@ -999,8 +999,8 @@ static struct mfd_cell abx500_common_devs[] = { .of_compatible = "stericsson,ab8500-denc", }, { - .name = "ab8500-temp", - .of_compatible = "stericsson,ab8500-temp", + .name = "abx500-temp", + .of_compatible = "stericsson,abx500-temp", .num_resources = ARRAY_SIZE(ab8500_temp_resources), .resources = ab8500_temp_resources, }, From 8ea402f5646e6e36c8cd0a62053ba8939204dceb Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Wed, 30 Jan 2013 10:33:16 +0000 Subject: [PATCH 2609/4607] mfd: vexpress: Add pseudo-GPIO based LEDs The LEDs on the Versatile Express motherboard are controlled through simple memory-mapped register. This patch extends the pseudo-GPIO controller definition for these lines and creates generic "leds-gpio" device using them Signed-off-by: Pawel Moll Signed-off-by: Samuel Ortiz --- drivers/mfd/vexpress-sysreg.c | 73 ++++++++++++++++++++++++++--------- include/linux/vexpress.h | 8 ++++ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 77048b18439e..51c3ca263bf5 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -49,6 +49,8 @@ #define SYS_ID_HBI_SHIFT 16 #define SYS_PROCIDx_HBI_SHIFT 0 +#define SYS_LED_LED(n) (1 << (n)) + #define SYS_MCI_CARDIN (1 << 0) #define SYS_MCI_WPROT (1 << 1) @@ -348,22 +350,27 @@ void __init vexpress_sysreg_of_early_init(void) } +#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \ + [VEXPRESS_GPIO_##_name] = { \ + .reg = _reg, \ + .value = _reg##_##_value, \ + } + static struct vexpress_sysreg_gpio { unsigned long reg; u32 value; } vexpress_sysreg_gpios[] = { - [VEXPRESS_GPIO_MMC_CARDIN] = { - .reg = SYS_MCI, - .value = SYS_MCI_CARDIN, - }, - [VEXPRESS_GPIO_MMC_WPROT] = { - .reg = SYS_MCI, - .value = SYS_MCI_WPROT, - }, - [VEXPRESS_GPIO_FLASH_WPn] = { - .reg = SYS_FLASH, - .value = SYS_FLASH_WPn, - }, + VEXPRESS_SYSREG_GPIO(MMC_CARDIN, SYS_MCI, CARDIN), + VEXPRESS_SYSREG_GPIO(MMC_WPROT, SYS_MCI, WPROT), + VEXPRESS_SYSREG_GPIO(FLASH_WPn, SYS_FLASH, WPn), + VEXPRESS_SYSREG_GPIO(LED0, SYS_LED, LED(0)), + VEXPRESS_SYSREG_GPIO(LED1, SYS_LED, LED(1)), + VEXPRESS_SYSREG_GPIO(LED2, SYS_LED, LED(2)), + VEXPRESS_SYSREG_GPIO(LED3, SYS_LED, LED(3)), + VEXPRESS_SYSREG_GPIO(LED4, SYS_LED, LED(4)), + VEXPRESS_SYSREG_GPIO(LED5, SYS_LED, LED(5)), + VEXPRESS_SYSREG_GPIO(LED6, SYS_LED, LED(6)), + VEXPRESS_SYSREG_GPIO(LED7, SYS_LED, LED(7)), }; static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, @@ -372,12 +379,6 @@ static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip, return 0; } -static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - return 0; -} - static int vexpress_sysreg_gpio_get(struct gpio_chip *chip, unsigned offset) { @@ -401,6 +402,14 @@ static void vexpress_sysreg_gpio_set(struct gpio_chip *chip, writel(reg_value, vexpress_sysreg_base + gpio->reg); } +static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + vexpress_sysreg_gpio_set(chip, offset, value); + + return 0; +} + static struct gpio_chip vexpress_sysreg_gpio_chip = { .label = "vexpress-sysreg", .direction_input = vexpress_sysreg_gpio_direction_input, @@ -412,6 +421,30 @@ static struct gpio_chip vexpress_sysreg_gpio_chip = { }; +#define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \ + { \ + .name = "v2m:green:"_name, \ + .default_trigger = _default_trigger, \ + .gpio = VEXPRESS_GPIO_##_gpio, \ + } + +struct gpio_led vexpress_sysreg_leds[] = { + VEXPRESS_SYSREG_GREEN_LED("user1", "heartbeat", LED0), + VEXPRESS_SYSREG_GREEN_LED("user2", "mmc0", LED1), + VEXPRESS_SYSREG_GREEN_LED("user3", "cpu0", LED2), + VEXPRESS_SYSREG_GREEN_LED("user4", "cpu1", LED3), + VEXPRESS_SYSREG_GREEN_LED("user5", "cpu2", LED4), + VEXPRESS_SYSREG_GREEN_LED("user6", "cpu3", LED5), + VEXPRESS_SYSREG_GREEN_LED("user7", "cpu4", LED6), + VEXPRESS_SYSREG_GREEN_LED("user8", "cpu5", LED7), +}; + +struct gpio_led_platform_data vexpress_sysreg_leds_pdata = { + .num_leds = ARRAY_SIZE(vexpress_sysreg_leds), + .leds = vexpress_sysreg_leds, +}; + + static ssize_t vexpress_sysreg_sys_id_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -456,6 +489,10 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) return err; } + platform_device_register_data(vexpress_sysreg_dev, "leds-gpio", + PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata, + sizeof(vexpress_sysreg_leds_pdata)); + vexpress_sysreg_dev = &pdev->dev; device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h index c52215ff4245..75818744ab59 100644 --- a/include/linux/vexpress.h +++ b/include/linux/vexpress.h @@ -27,6 +27,14 @@ #define VEXPRESS_GPIO_MMC_CARDIN 0 #define VEXPRESS_GPIO_MMC_WPROT 1 #define VEXPRESS_GPIO_FLASH_WPn 2 +#define VEXPRESS_GPIO_LED0 3 +#define VEXPRESS_GPIO_LED1 4 +#define VEXPRESS_GPIO_LED2 5 +#define VEXPRESS_GPIO_LED3 6 +#define VEXPRESS_GPIO_LED4 7 +#define VEXPRESS_GPIO_LED5 8 +#define VEXPRESS_GPIO_LED6 9 +#define VEXPRESS_GPIO_LED7 10 #define VEXPRESS_RES_FUNC(_site, _func) \ { \ From 88a7ee37f3c5c73b000f7ba2000b27c5002a5286 Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Mon, 4 Feb 2013 15:45:58 +0800 Subject: [PATCH 2610/4607] mfd: rtsx: Implement driving adjustment to device-dependent callbacks Implement different ways of selecting driving capability(a necessary adjustment along with voltage change). It was origionally in device-independent mmc/host/rtsx_pci_sdmmc.c. Moving it here to support devices which may have a different way of adjustment. Signed-off-by: Roger Tseng Reviewed-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtl8411.c | 16 +++++++++++++--- drivers/mfd/rts5209.c | 8 ++++++++ drivers/mfd/rts5229.c | 8 ++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c index 3d3b4addf81a..2a2d31687b72 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/mfd/rtl8411.c @@ -115,14 +115,24 @@ static int rtl8411_card_power_off(struct rtsx_pcr *pcr, int card) static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) { u8 mask, val; + int err; mask = (BPP_REG_TUNED18 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_MASK; - if (voltage == OUTPUT_3V3) + if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D); + if (err < 0) + return err; val = (BPP_ASIC_3V3 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_3V3; - else if (voltage == OUTPUT_1V8) + } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); + if (err < 0) + return err; val = (BPP_ASIC_1V8 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_1V8; - else + } else { return -EINVAL; + } return rtsx_pci_write_register(pcr, LDO_CTL, mask, val); } diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c index 98fe0f39463e..ec78d9fb0879 100644 --- a/drivers/mfd/rts5209.c +++ b/drivers/mfd/rts5209.c @@ -149,10 +149,18 @@ static int rts5209_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) int err; if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D); + if (err < 0) + return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); if (err < 0) return err; } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); + if (err < 0) + return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); if (err < 0) return err; diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c index 29d889cbb9c5..58af4dbe3586 100644 --- a/drivers/mfd/rts5229.c +++ b/drivers/mfd/rts5229.c @@ -119,10 +119,18 @@ static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) int err; if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D); + if (err < 0) + return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); if (err < 0) return err; } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); + if (err < 0) + return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); if (err < 0) return err; From e12379320b2e1ceffc4211ad174989bc042149d9 Mon Sep 17 00:00:00 2001 From: Roger Tseng Date: Mon, 4 Feb 2013 15:45:59 +0800 Subject: [PATCH 2611/4607] mfd: rtsx: Support RTS5227 Support new model RTS5227. Signed-off-by: Roger Tseng Reviewed-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/Makefile | 2 +- drivers/mfd/rts5227.c | 234 +++++++++++++++++++++++++++++++++++ drivers/mfd/rtsx_pcr.c | 5 + drivers/mfd/rtsx_pcr.h | 1 + include/linux/mfd/rtsx_pci.h | 5 + 5 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/rts5227.c diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8b977f8045ae..b90409c23664 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c new file mode 100644 index 000000000000..fc831dcb1480 --- /dev/null +++ b/drivers/mfd/rts5227.c @@ -0,0 +1,234 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009 Realtek Semiconductor Corp. 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 as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * 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, see . + * + * Author: + * Wei WANG + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + * + * Roger Tseng + * No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan + */ + +#include +#include +#include + +#include "rtsx_pcr.h" + +static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) +{ + u16 cap; + + rtsx_pci_init_cmd(pcr); + + /* Configure GPIO as output */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); + /* Switch LDO3318 source from DV33 to card_3v3 */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); + /* LED shine disabled, set initial shine cycle period */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); + /* Configure LTR */ + pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &cap); + if (cap & PCI_EXP_LTR_EN) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LTR_CTL, 0xFF, 0xA3); + /* Configure OBFF */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OBFF_CFG, 0x03, 0x03); + /* Configure force_clock_req + * Maybe We should define 0xFF03 as some name + */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, 0xFF03, 0x08, 0x08); + /* Correct driving */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_CLK_DRIVE_SEL, 0xFF, 0x96); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_CMD_DRIVE_SEL, 0xFF, 0x96); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_DAT_DRIVE_SEL, 0xFF, 0x96); + + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5227_optimize_phy(struct rtsx_pcr *pcr) +{ + /* Optimize RX sensitivity */ + return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42); +} + +static int rts5227_turn_on_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); +} + +static int rts5227_turn_off_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); +} + +static int rts5227_enable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); +} + +static int rts5227_disable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); +} + +static int rts5227_card_power_on(struct rtsx_pcr *pcr, int card) +{ + int err; + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK, SD_PARTIAL_POWER_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x02); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + /* To avoid too large in-rush current */ + udelay(150); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK, SD_POWER_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x06); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + return 0; +} + +static int rts5227_card_power_off(struct rtsx_pcr *pcr, int card) +{ + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK | PMOS_STRG_MASK, + SD_POWER_OFF | PMOS_STRG_400mA); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0X00); + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5227_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + int err; + u8 drive_sel; + + if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); + if (err < 0) + return err; + drive_sel = 0x96; + } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_phy_register(pcr, 0x11, 0x3C02); + if (err < 0) + return err; + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C80 | 0x24); + if (err < 0) + return err; + drive_sel = 0xB3; + } else { + return -EINVAL; + } + + /* set pad drive */ + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, + 0xFF, drive_sel); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, + 0xFF, drive_sel); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, + 0xFF, drive_sel); + return rtsx_pci_send_cmd(pcr, 100); +} + +static const struct pcr_ops rts5227_pcr_ops = { + .extra_init_hw = rts5227_extra_init_hw, + .optimize_phy = rts5227_optimize_phy, + .turn_on_led = rts5227_turn_on_led, + .turn_off_led = rts5227_turn_off_led, + .enable_auto_blink = rts5227_enable_auto_blink, + .disable_auto_blink = rts5227_disable_auto_blink, + .card_power_on = rts5227_card_power_on, + .card_power_off = rts5227_card_power_off, + .switch_output_voltage = rts5227_switch_output_voltage, + .cd_deglitch = NULL, + .conv_clk_and_div_n = NULL, +}; + +/* SD Pull Control Enable: + * SD_DAT[3:0] ==> pull up + * SD_CD ==> pull up + * SD_WP ==> pull up + * SD_CMD ==> pull up + * SD_CLK ==> pull down + */ +static const u32 rts5227_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), + 0, +}; + +/* SD Pull Control Disable: + * SD_DAT[3:0] ==> pull down + * SD_CD ==> pull up + * SD_WP ==> pull down + * SD_CMD ==> pull down + * SD_CLK ==> pull down + */ +static const u32 rts5227_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), + 0, +}; + +/* MS Pull Control Enable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5227_ms_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +/* MS Pull Control Disable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5227_ms_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +void rts5227_init_params(struct rtsx_pcr *pcr) +{ + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; + pcr->num_slots = 2; + pcr->ops = &rts5227_pcr_ops; + + pcr->sd_pull_ctl_enable_tbl = rts5227_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = rts5227_sd_pull_ctl_disable_tbl; + pcr->ms_pull_ctl_enable_tbl = rts5227_ms_pull_ctl_enable_tbl; + pcr->ms_pull_ctl_disable_tbl = rts5227_ms_pull_ctl_disable_tbl; +} diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 9016932f0267..822237e322ba 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -55,6 +55,7 @@ static DEFINE_PCI_DEVICE_TABLE(rtsx_pci_ids) = { { PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; @@ -998,6 +999,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) case 0x5289: rtl8411_init_params(pcr); break; + + case 0x5227: + rts5227_init_params(pcr); + break; } dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n", diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 33c210be1daa..2b3ab8a04823 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -31,5 +31,6 @@ void rts5209_init_params(struct rtsx_pcr *pcr); void rts5229_init_params(struct rtsx_pcr *pcr); void rtl8411_init_params(struct rtsx_pcr *pcr); +void rts5227_init_params(struct rtsx_pcr *pcr); #endif diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 3f2bf26ca0d1..5d9b81e8aff4 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -581,8 +581,11 @@ #define CARD_GPIO_DIR 0xFD57 #define CARD_GPIO 0xFD58 #define CARD_DATA_SOURCE 0xFD5B +#define SD30_CLK_DRIVE_SEL 0xFD5A #define CARD_SELECT 0xFD5C #define SD30_DRIVE_SEL 0xFD5E +#define SD30_CMD_DRIVE_SEL 0xFD5E +#define SD30_DAT_DRIVE_SEL 0xFD5F #define CARD_CLK_EN 0xFD69 #define SDIO_CTRL 0xFD6B #define CD_PAD_CTL 0xFD73 @@ -655,6 +658,8 @@ #define MSGTXDATA3 0xFE47 #define MSGTXCTL 0xFE48 #define PETXCFG 0xFE49 +#define LTR_CTL 0xFE4A +#define OBFF_CFG 0xFE4C #define CDRESUMECTL 0xFE52 #define WAKE_SEL_CTL 0xFE54 From dcd560c8587171bb22c75c41ac2a70986bbbde7f Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Mon, 4 Feb 2013 18:08:02 +0000 Subject: [PATCH 2612/4607] mfd: vexpress: Allow vexpress-sysreg to self-initialise The vexpress_sysreg_init() is a core_initcall() already and it can trigger the early initialisation if a matching node is found. This patch allows the SoC code to avoid calling vexpress_sysreg_of_early_init() explicitly. Signed-off-by: Catalin Marinas Acked-by: Arnd Bergmann Acked-by: Pawel Moll Signed-off-by: Samuel Ortiz --- drivers/mfd/vexpress-sysreg.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 51c3ca263bf5..a4a43230abcd 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -338,14 +338,15 @@ void __init vexpress_sysreg_early_init(void __iomem *base) void __init vexpress_sysreg_of_early_init(void) { - struct device_node *node = of_find_compatible_node(NULL, NULL, - "arm,vexpress-sysreg"); + struct device_node *node; + if (vexpress_sysreg_base) + return; + + node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); if (node) { vexpress_sysreg_base = of_iomap(node, 0); vexpress_sysreg_setup(node); - } else { - pr_info("vexpress-sysreg: No Device Tree node found."); } } @@ -515,6 +516,7 @@ static struct platform_driver vexpress_sysreg_driver = { static int __init vexpress_sysreg_init(void) { + vexpress_sysreg_of_early_init(); return platform_driver_register(&vexpress_sysreg_driver); } core_initcall(vexpress_sysreg_init); From 4e405ae256b7e04f7c1213136f3bfd9fb76e2023 Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 4 Feb 2013 23:40:42 +0800 Subject: [PATCH 2613/4607] mfd: max8925: Add irqdomain for dt Add irqdomains for max8925's main irq, wrap irq register operations into irqdomain's map func. it is necessary for dt support. Also, add dt support for max8925 driver. Signed-off-by: Qing Xu Signed-off-by: Haojian Zhuang Signed-off-by: Samuel Ortiz --- drivers/mfd/max8925-core.c | 75 ++++++++++++++++++++++--------------- drivers/mfd/max8925-i2c.c | 34 ++++++++++++++++- include/linux/mfd/max8925.h | 3 +- 3 files changed, 78 insertions(+), 34 deletions(-) diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index e32466e865b9..0ad8d9a7c15a 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -14,10 +14,13 @@ #include #include #include +#include #include #include #include #include +#include +#include static struct resource bk_resources[] = { { 0x84, 0x84, "mode control", IORESOURCE_REG, }, @@ -639,17 +642,33 @@ static struct irq_chip max8925_irq_chip = { .irq_disable = max8925_irq_disable, }; +static int max8925_irq_domain_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_data(virq, d->host_data); + irq_set_chip_and_handler(virq, &max8925_irq_chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); +#ifdef CONFIG_ARM + set_irq_flags(virq, IRQF_VALID); +#else + irq_set_noprobe(virq); +#endif + return 0; +} + +static struct irq_domain_ops max8925_irq_domain_ops = { + .map = max8925_irq_domain_map, + .xlate = irq_domain_xlate_onetwocell, +}; + + static int max8925_irq_init(struct max8925_chip *chip, int irq, struct max8925_platform_data *pdata) { unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; - int i, ret; - int __irq; + int ret; + struct device_node *node = chip->dev->of_node; - if (!pdata || !pdata->irq_base) { - dev_warn(chip->dev, "No interrupt support on IRQ base\n"); - return -EINVAL; - } /* clear all interrupts */ max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ1); max8925_reg_read(chip->i2c, MAX8925_CHG_IRQ2); @@ -667,35 +686,30 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq, max8925_reg_write(chip->rtc, MAX8925_RTC_IRQ_MASK, 0xff); mutex_init(&chip->irq_lock); + chip->irq_base = irq_alloc_descs(-1, 0, MAX8925_NR_IRQS, 0); + if (chip->irq_base < 0) { + dev_err(chip->dev, "Failed to allocate interrupts, ret:%d\n", + chip->irq_base); + return -EBUSY; + } + + irq_domain_add_legacy(node, MAX8925_NR_IRQS, chip->irq_base, 0, + &max8925_irq_domain_ops, chip); + + /* request irq handler for pmic main irq*/ chip->core_irq = irq; - chip->irq_base = pdata->irq_base; - - /* register with genirq */ - for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) { - __irq = i + chip->irq_base; - irq_set_chip_data(__irq, chip); - irq_set_chip_and_handler(__irq, &max8925_irq_chip, - handle_edge_irq); - irq_set_nested_thread(__irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(__irq, IRQF_VALID); -#else - irq_set_noprobe(__irq); -#endif - } - if (!irq) { - dev_warn(chip->dev, "No interrupt support on core IRQ\n"); - goto tsc_irq; - } - + if (!chip->core_irq) + return -EBUSY; ret = request_threaded_irq(irq, NULL, max8925_irq, flags | IRQF_ONESHOT, "max8925", chip); if (ret) { dev_err(chip->dev, "Failed to request core IRQ: %d\n", ret); chip->core_irq = 0; + return -EBUSY; } -tsc_irq: + /* request irq handler for pmic tsc irq*/ + /* mask TSC interrupt */ max8925_reg_write(chip->adc, MAX8925_TSC_IRQ_MASK, 0x0f); @@ -704,7 +718,6 @@ tsc_irq: return 0; } chip->tsc_irq = pdata->tsc_irq; - ret = request_threaded_irq(chip->tsc_irq, NULL, max8925_tsc_irq, flags | IRQF_ONESHOT, "max8925-tsc", chip); if (ret) { @@ -875,11 +888,11 @@ int max8925_device_init(struct max8925_chip *chip, if (pdata && pdata->power) { ret = mfd_add_devices(chip->dev, 0, &power_devs[0], - ARRAY_SIZE(power_devs), + ARRAY_SIZE(power_devs), &power_supply_resources[0], 0, NULL); if (ret < 0) { - dev_err(chip->dev, "Failed to add power supply " - "subdev\n"); + dev_err(chip->dev, + "Failed to add power supply subdev\n"); goto out_dev; } } diff --git a/drivers/mfd/max8925-i2c.c b/drivers/mfd/max8925-i2c.c index 00b5b456063d..92bbebd31598 100644 --- a/drivers/mfd/max8925-i2c.c +++ b/drivers/mfd/max8925-i2c.c @@ -135,13 +135,37 @@ static const struct i2c_device_id max8925_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, max8925_id_table); +static int max8925_dt_init(struct device_node *np, struct device *dev, + struct max8925_platform_data *pdata) +{ + int ret; + + ret = of_property_read_u32(np, "maxim,tsc-irq", &pdata->tsc_irq); + if (ret) { + dev_err(dev, "Not found maxim,tsc-irq property\n"); + return -EINVAL; + } + return 0; +} + static int max8925_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct max8925_platform_data *pdata = client->dev.platform_data; static struct max8925_chip *chip; + struct device_node *node = client->dev.of_node; - if (!pdata) { + if (node && !pdata) { + /* parse DT to get platform data */ + pdata = devm_kzalloc(&client->dev, + sizeof(struct max8925_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (max8925_dt_init(node, &client->dev, pdata)) + return -EINVAL; + } else if (!pdata) { pr_info("%s: platform data is missing\n", __func__); return -EINVAL; } @@ -203,11 +227,18 @@ static int max8925_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(max8925_pm_ops, max8925_suspend, max8925_resume); +static const struct of_device_id max8925_dt_ids[] = { + { .compatible = "maxim,max8925", }, + {}, +}; +MODULE_DEVICE_TABLE(of, max8925_dt_ids); + static struct i2c_driver max8925_driver = { .driver = { .name = "max8925", .owner = THIS_MODULE, .pm = &max8925_pm_ops, + .of_match_table = of_match_ptr(max8925_dt_ids), }, .probe = max8925_probe, .remove = max8925_remove, @@ -217,7 +248,6 @@ static struct i2c_driver max8925_driver = { static int __init max8925_i2c_init(void) { int ret; - ret = i2c_add_driver(&max8925_driver); if (ret != 0) pr_err("Failed to register MAX8925 I2C driver: %d\n", ret); diff --git a/include/linux/mfd/max8925.h b/include/linux/mfd/max8925.h index 74d8e2969630..ce8502e9e7dc 100644 --- a/include/linux/mfd/max8925.h +++ b/include/linux/mfd/max8925.h @@ -190,6 +190,8 @@ enum { MAX8925_NR_IRQS, }; + + struct max8925_chip { struct device *dev; struct i2c_client *i2c; @@ -201,7 +203,6 @@ struct max8925_chip { int irq_base; int core_irq; int tsc_irq; - unsigned int wakeup_flag; }; From f9ed143180ffe94a617671aa4ed4106ae183407c Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 4 Feb 2013 23:40:43 +0800 Subject: [PATCH 2614/4607] mfd: max8925: Fix mfd device register failure we encounter rtc/power/touch driver registry failure, root cause it is resources confilict in insert_resouce, solved by changing mfd_add_devices 5th parameter to NULL Signed-off-by: Qing Xu Signed-off-by: Samuel Ortiz --- drivers/mfd/max8925-core.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index 0ad8d9a7c15a..d7abbb354acd 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -859,7 +859,7 @@ int max8925_device_init(struct max8925_chip *chip, ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0], ARRAY_SIZE(rtc_devs), - &rtc_resources[0], chip->irq_base, NULL); + NULL, chip->irq_base, NULL); if (ret < 0) { dev_err(chip->dev, "Failed to add rtc subdev\n"); goto out; @@ -867,7 +867,7 @@ int max8925_device_init(struct max8925_chip *chip, ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0], ARRAY_SIZE(onkey_devs), - &onkey_resources[0], 0, NULL); + NULL, 0, NULL); if (ret < 0) { dev_err(chip->dev, "Failed to add onkey subdev\n"); goto out_dev; @@ -886,21 +886,19 @@ int max8925_device_init(struct max8925_chip *chip, goto out_dev; } - if (pdata && pdata->power) { - ret = mfd_add_devices(chip->dev, 0, &power_devs[0], - ARRAY_SIZE(power_devs), - &power_supply_resources[0], 0, NULL); - if (ret < 0) { - dev_err(chip->dev, - "Failed to add power supply subdev\n"); - goto out_dev; - } + ret = mfd_add_devices(chip->dev, 0, &power_devs[0], + ARRAY_SIZE(power_devs), + NULL, 0, NULL); + if (ret < 0) { + dev_err(chip->dev, + "Failed to add power supply subdev, err = %d\n", ret); + goto out_dev; } if (pdata && pdata->touch) { ret = mfd_add_devices(chip->dev, 0, &touch_devs[0], ARRAY_SIZE(touch_devs), - &touch_resources[0], 0, NULL); + NULL, chip->tsc_irq, NULL); if (ret < 0) { dev_err(chip->dev, "Failed to add touch subdev\n"); goto out_dev; From 678e8cb50647a7580b8122937e8e6e50d949267d Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 4 Feb 2013 23:40:44 +0800 Subject: [PATCH 2615/4607] mfd: max8925: Fix onkey driver irq base update onkey driver's irq base, it should get from max8925, but not save in a private value Signed-off-by: Qing Xu Acked-by: Dmitry Torokhov Signed-off-by: Samuel Ortiz --- drivers/input/misc/max8925_onkey.c | 3 --- drivers/mfd/max8925-core.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c index 369a39de4ff3..f9179b2585a9 100644 --- a/drivers/input/misc/max8925_onkey.c +++ b/drivers/input/misc/max8925_onkey.c @@ -100,9 +100,6 @@ static int max8925_onkey_probe(struct platform_device *pdev) input->dev.parent = &pdev->dev; input_set_capability(input, EV_KEY, KEY_POWER); - irq[0] += chip->irq_base; - irq[1] += chip->irq_base; - error = request_threaded_irq(irq[0], NULL, max8925_onkey_handler, IRQF_ONESHOT, "onkey-down", info); if (error < 0) { diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index d7abbb354acd..f0cc40296d8c 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -867,7 +867,7 @@ int max8925_device_init(struct max8925_chip *chip, ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0], ARRAY_SIZE(onkey_devs), - NULL, 0, NULL); + NULL, chip->irq_base, NULL); if (ret < 0) { dev_err(chip->dev, "Failed to add onkey subdev\n"); goto out_dev; From 47ec340cb8e232671e7c4a4689ff32c3bdf329da Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 4 Feb 2013 23:40:45 +0800 Subject: [PATCH 2616/4607] mfd: max8925: Support dt for backlight Add device tree support in max8925 backlight. Signed-off-by: Qing Xu Signed-off-by: Haojian Zhuang Signed-off-by: Samuel Ortiz --- drivers/video/backlight/max8925_bl.c | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c index 2c9bce050aa9..5ca11b066b7e 100644 --- a/drivers/video/backlight/max8925_bl.c +++ b/drivers/video/backlight/max8925_bl.c @@ -101,6 +101,29 @@ static const struct backlight_ops max8925_backlight_ops = { .get_brightness = max8925_backlight_get_brightness, }; +#ifdef CONFIG_OF +static int max8925_backlight_dt_init(struct platform_device *pdev, + struct max8925_backlight_pdata *pdata) +{ + struct device_node *nproot = pdev->dev.parent->of_node, *np; + int dual_string; + + if (!nproot) + return -ENODEV; + np = of_find_node_by_name(nproot, "backlight"); + if (!np) { + dev_err(&pdev->dev, "failed to find backlight node\n"); + return -ENODEV; + } + + of_property_read_u32(np, "maxim,max8925-dual-string", &dual_string); + pdata->dual_string = dual_string; + return 0; +} +#else +#define max8925_backlight_dt_init(x, y) (-1) +#endif + static int max8925_backlight_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -147,6 +170,13 @@ static int max8925_backlight_probe(struct platform_device *pdev) platform_set_drvdata(pdev, bl); value = 0; + if (pdev->dev.parent->of_node && !pdata) { + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct max8925_backlight_pdata), + GFP_KERNEL); + max8925_backlight_dt_init(pdev, pdata); + } + if (pdata) { if (pdata->lxw_scl) value |= (1 << 7); @@ -158,7 +188,6 @@ static int max8925_backlight_probe(struct platform_device *pdev) ret = max8925_set_bits(chip->i2c, data->reg_mode_cntl, 0xfe, value); if (ret < 0) goto out_brt; - backlight_update_status(bl); return 0; out_brt: From 58f1193e6210b986ce662b19aa7b96cf569c2eb3 Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 4 Feb 2013 23:40:46 +0800 Subject: [PATCH 2617/4607] mfd: max8925: Add dts Add max8925 dts support into mmp2 brownstone platform Signed-off-by: Qing Xu Signed-off-by: Haojian Zhuang Signed-off-by: Samuel Ortiz --- arch/arm/boot/dts/mmp2-brownstone.dts | 158 ++++++++++++++++++++++++++ arch/arm/boot/dts/mmp2.dtsi | 4 +- 2 files changed, 161 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/mmp2-brownstone.dts b/arch/arm/boot/dts/mmp2-brownstone.dts index c9b4f27d191e..7f70a39459f6 100644 --- a/arch/arm/boot/dts/mmp2-brownstone.dts +++ b/arch/arm/boot/dts/mmp2-brownstone.dts @@ -29,6 +29,164 @@ }; twsi1: i2c@d4011000 { status = "okay"; + pmic: max8925@3c { + compatible = "maxium,max8925"; + reg = <0x3c>; + interrupts = <1>; + interrupt-parent = <&intcmux4>; + interrupt-controller; + #interrupt-cells = <1>; + maxim,tsc-irq = <0>; + + regulators { + SDV1 { + regulator-min-microvolt = <637500>; + regulator-max-microvolt = <1425000>; + regulator-boot-on; + regulator-always-on; + }; + SDV2 { + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <2225000>; + regulator-boot-on; + regulator-always-on; + }; + SDV3 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO1 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO2 { + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <2250000>; + regulator-boot-on; + regulator-always-on; + }; + LDO3 { + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <2250000>; + regulator-boot-on; + regulator-always-on; + }; + LDO4 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO5 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO6 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO7 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO8 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO9 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO10 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + }; + LDO11 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO12 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO13 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO14 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO15 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO16 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO17 { + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <2250000>; + regulator-boot-on; + regulator-always-on; + }; + LDO18 { + regulator-min-microvolt = <650000>; + regulator-max-microvolt = <2250000>; + regulator-boot-on; + regulator-always-on; + }; + LDO19 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + LDO20 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + }; + backlight { + maxim,max8925-dual-string = <0>; + }; + charger { + batt-detect = <0>; + topoff-threshold = <1>; + fast-charge = <7>; + no-temp-support = <0>; + no-insert-detect = <0>; + }; + }; }; rtc: rtc@d4010000 { status = "okay"; diff --git a/arch/arm/boot/dts/mmp2.dtsi b/arch/arm/boot/dts/mmp2.dtsi index 0514fb41627e..1429ac05b36d 100644 --- a/arch/arm/boot/dts/mmp2.dtsi +++ b/arch/arm/boot/dts/mmp2.dtsi @@ -46,7 +46,7 @@ mrvl,intc-nr-irqs = <64>; }; - intcmux4@d4282150 { + intcmux4: interrupt-controller@d4282150 { compatible = "mrvl,mmp2-mux-intc"; interrupts = <4>; interrupt-controller; @@ -201,6 +201,8 @@ compatible = "mrvl,mmp-twsi"; reg = <0xd4011000 0x1000>; interrupts = <7>; + #address-cells = <1>; + #size-cells = <0>; mrvl,i2c-fast-mode; status = "disabled"; }; From ba3980df4fc145f36a577307d6387c42841438fd Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Mon, 4 Feb 2013 23:40:47 +0800 Subject: [PATCH 2618/4607] Documentation: Add docs for max8925 dt add docs for dt of max8925-mfd, max8925-backlight, and max8925-battery Signed-off-by: Qing Xu Signed-off-by: Haojian Zhuang Signed-off-by: Samuel Ortiz --- .../devicetree/bindings/mfd/max8925.txt | 64 +++++++++++++++++++ .../bindings/power_supply/max8925_batter.txt | 18 ++++++ .../video/backlight/max8925-backlight.txt | 10 +++ 3 files changed, 92 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/max8925.txt create mode 100644 Documentation/devicetree/bindings/power_supply/max8925_batter.txt create mode 100644 Documentation/devicetree/bindings/video/backlight/max8925-backlight.txt diff --git a/Documentation/devicetree/bindings/mfd/max8925.txt b/Documentation/devicetree/bindings/mfd/max8925.txt new file mode 100644 index 000000000000..4f0dc6638e5e --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/max8925.txt @@ -0,0 +1,64 @@ +* Maxim max8925 Power Management IC + +Required parent device properties: +- compatible : "maxim,max8925" +- reg : the I2C slave address for the max8925 chip +- interrupts : IRQ line for the max8925 chip +- interrupt-controller: describes the max8925 as an interrupt + controller (has its own domain) +- #interrupt-cells : should be 1. + - The cell is the max8925 local IRQ number + +Optional parent device properties: +- maxim,tsc-irq: there are 2 IRQ lines for max8925, one is indicated in + interrupts property, the other is indicated here. + +max8925 consists of a large and varied group of sub-devices: + +Device Supply Names Description +------ ------------ ----------- +max8925-onkey : : On key +max8925-rtc : : RTC +max8925-regulator : : Regulators +max8925-backlight : : Backlight +max8925-touch : : Touchscreen +max8925-power : : Charger + +Example: + + pmic: max8925@3c { + compatible = "maxim,max8925"; + reg = <0x3c>; + interrupts = <1>; + interrupt-parent = <&intcmux4>; + interrupt-controller; + #interrupt-cells = <1>; + maxim,tsc-irq = <0>; + + regulators { + SDV1 { + regulator-min-microvolt = <637500>; + regulator-max-microvolt = <1425000>; + regulator-boot-on; + regulator-always-on; + }; + + LDO1 { + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <3900000>; + regulator-boot-on; + regulator-always-on; + }; + + }; + backlight { + maxim,max8925-dual-string = <0>; + }; + charger { + batt-detect = <0>; + topoff-threshold = <1>; + fast-charge = <7>; + no-temp-support = <0>; + no-insert-detect = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/power_supply/max8925_batter.txt b/Documentation/devicetree/bindings/power_supply/max8925_batter.txt new file mode 100644 index 000000000000..d7e3e0c0f71d --- /dev/null +++ b/Documentation/devicetree/bindings/power_supply/max8925_batter.txt @@ -0,0 +1,18 @@ +max8925-battery bindings +~~~~~~~~~~~~~~~~ + +Optional properties : + - batt-detect: whether support battery detect + - topoff-threshold: set charging current in topoff mode + - fast-charge: set charging current in fast mode + - no-temp-support: whether support temperature protection detect + - no-insert-detect: whether support insert detect + +Example: + charger { + batt-detect = <0>; + topoff-threshold = <1>; + fast-charge = <7>; + no-temp-support = <0>; + no-insert-detect = <0>; + }; diff --git a/Documentation/devicetree/bindings/video/backlight/max8925-backlight.txt b/Documentation/devicetree/bindings/video/backlight/max8925-backlight.txt new file mode 100644 index 000000000000..b4cffdaa4137 --- /dev/null +++ b/Documentation/devicetree/bindings/video/backlight/max8925-backlight.txt @@ -0,0 +1,10 @@ +88pm860x-backlight bindings + +Optional properties: + - maxim,max8925-dual-string: whether support dual string + +Example: + + backlights { + maxim,max8925-dual-string = <0>; + }; From 21f792cde17f71091252540b79e9312a8d1a7ac2 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 6 Feb 2013 17:23:51 +0100 Subject: [PATCH 2619/4607] mfd: Add missing GENERIC_HARDIRQS dependecies A lot of mfd drivers select MFD_CORE which however depends on GENERIC_HARDIRQS support. So add the missing dependency to all drivers to get rid of this link error: ERROR: "irq_create_mapping" [drivers/mfd/mfd-core.ko] undefined! Signed-off-by: Heiko Carstens Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 72 ++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ff553babf455..671f5b171c73 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -65,7 +65,7 @@ config MFD_SM501_GPIO config MFD_RTSX_PCI tristate "Support for Realtek PCI-E card reader" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE help This supports for Realtek PCI-Express card reader including rts5209, @@ -95,7 +95,7 @@ config MFD_DM355EVM_MSP config MFD_TI_SSP tristate "TI Sequencer Serial Port support" - depends on ARCH_DAVINCI_TNETV107X + depends on ARCH_DAVINCI_TNETV107X && GENERIC_HARDIRQS select MFD_CORE ---help--- Say Y here if you want support for the Sequencer Serial Port @@ -109,6 +109,7 @@ config MFD_TI_AM335X_TSCADC select MFD_CORE select REGMAP select REGMAP_MMIO + depends on GENERIC_HARDIRQS help If you say yes here you get support for Texas Instruments series of Touch Screen /ADC chips. @@ -126,6 +127,7 @@ config HTC_EGPIO config HTC_PASIC3 tristate "HTC PASIC3 LED/DS1WM chip support" select MFD_CORE + depends on GENERIC_HARDIRQS help This core driver provides register access for the LED/DS1WM chips labeled "AIC2" and "AIC3", found on HTC Blueangel and @@ -157,6 +159,7 @@ config MFD_LM3533 depends on I2C select MFD_CORE select REGMAP_I2C + depends on GENERIC_HARDIRQS help Say yes here to enable support for National Semiconductor / TI LM3533 Lighting Power chips. @@ -171,6 +174,7 @@ config TPS6105X select REGULATOR select MFD_CORE select REGULATOR_FIXED_VOLTAGE + depends on GENERIC_HARDIRQS help This option enables a driver for the TP61050/TPS61052 high-power "white LED driver". This boost converter is @@ -193,7 +197,7 @@ config TPS65010 config TPS6507X tristate "TPS6507x Power Management / Touch Screen chips" select MFD_CORE - depends on I2C + depends on I2C && GENERIC_HARDIRQS help If you say yes here you get support for the TPS6507x series of Power Management / Touch Screen chips. These include voltage @@ -204,7 +208,7 @@ config TPS6507X config MFD_TPS65217 tristate "TPS65217 Power Management / White LED chips" - depends on I2C + depends on I2C && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C help @@ -234,7 +238,7 @@ config MFD_TPS6586X config MFD_TPS65910 bool "TPS65910 Power Management chip" - depends on I2C=y && GPIOLIB + depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C select REGMAP_IRQ @@ -251,7 +255,7 @@ config MFD_TPS65912_I2C bool "TPS65912 Power Management chip with I2C" select MFD_CORE select MFD_TPS65912 - depends on I2C=y && GPIOLIB + depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS help If you say yes here you get support for the TPS65912 series of PM chips with I2C interface. @@ -260,7 +264,7 @@ config MFD_TPS65912_SPI bool "TPS65912 Power Management chip with SPI" select MFD_CORE select MFD_TPS65912 - depends on SPI_MASTER && GPIOLIB + depends on SPI_MASTER && GPIOLIB && GENERIC_HARDIRQS help If you say yes here you get support for the TPS65912 series of PM chips with SPI interface. @@ -330,13 +334,13 @@ config TWL4030_POWER config MFD_TWL4030_AUDIO bool - depends on TWL4030_CORE + depends on TWL4030_CORE && GENERIC_HARDIRQS select MFD_CORE default n config TWL6040_CORE bool "Support for TWL6040 audio codec" - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C select REGMAP_IRQ @@ -405,7 +409,7 @@ config MFD_TMIO config MFD_T7L66XB bool "Support Toshiba T7L66XB" - depends on ARM && HAVE_CLK + depends on ARM && HAVE_CLK && GENERIC_HARDIRQS select MFD_CORE select MFD_TMIO help @@ -413,7 +417,7 @@ config MFD_T7L66XB config MFD_SMSC bool "Support for the SMSC ECE1099 series chips" - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C help @@ -460,7 +464,7 @@ config MFD_DA9052_SPI select REGMAP_SPI select REGMAP_IRQ select PMIC_DA9052 - depends on SPI_MASTER=y + depends on SPI_MASTER=y && GENERIC_HARDIRQS help Support for the Dialog Semiconductor DA9052 PMIC when controlled using SPI. This driver provides common support @@ -472,7 +476,7 @@ config MFD_DA9052_I2C select REGMAP_I2C select REGMAP_IRQ select PMIC_DA9052 - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS help Support for the Dialog Semiconductor DA9052 PMIC when controlled using I2C. This driver provides common support @@ -485,7 +489,7 @@ config MFD_DA9055 select REGMAP_IRQ select PMIC_DA9055 select MFD_CORE - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS help Say yes here for support of Dialog Semiconductor DA9055. This is a Power Management IC. This driver provides common support for @@ -508,7 +512,7 @@ config PMIC_ADP5520 config MFD_LP8788 bool "Texas Instruments LP8788 Power Management Unit Driver" - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C select IRQ_DOMAIN @@ -611,7 +615,7 @@ config MFD_ARIZONA_I2C select MFD_ARIZONA select MFD_CORE select REGMAP_I2C - depends on I2C + depends on I2C && GENERIC_HARDIRQS help Support for the Wolfson Microelectronics Arizona platform audio SoC core functionality controlled via I2C. @@ -621,7 +625,7 @@ config MFD_ARIZONA_SPI select MFD_ARIZONA select MFD_CORE select REGMAP_SPI - depends on SPI_MASTER + depends on SPI_MASTER && GENERIC_HARDIRQS help Support for the Wolfson Microelectronics Arizona platform audio SoC core functionality controlled via I2C. @@ -641,7 +645,7 @@ config MFD_WM5110 config MFD_WM8400 bool "Support Wolfson Microelectronics WM8400" select MFD_CORE - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS select REGMAP_I2C help Support for the Wolfson Microelecronics WM8400 PMIC and audio @@ -785,7 +789,7 @@ config MFD_MC13783 config MFD_MC13XXX tristate - depends on SPI_MASTER || I2C + depends on (SPI_MASTER || I2C) && GENERIC_HARDIRQS select MFD_CORE select MFD_MC13783 help @@ -796,7 +800,7 @@ config MFD_MC13XXX config MFD_MC13XXX_SPI tristate "Freescale MC13783 and MC13892 SPI interface" - depends on SPI_MASTER + depends on SPI_MASTER && GENERIC_HARDIRQS select REGMAP_SPI select MFD_MC13XXX help @@ -804,7 +808,7 @@ config MFD_MC13XXX_SPI config MFD_MC13XXX_I2C tristate "Freescale MC13892 I2C interface" - depends on I2C + depends on I2C && GENERIC_HARDIRQS select REGMAP_I2C select MFD_MC13XXX help @@ -822,7 +826,7 @@ config ABX500_CORE config AB3100_CORE bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions" - depends on I2C=y && ABX500_CORE + depends on I2C=y && ABX500_CORE && GENERIC_HARDIRQS select MFD_CORE default y if ARCH_U300 help @@ -909,7 +913,7 @@ config MFD_TIMBERDALE config LPC_SCH tristate "Intel SCH LPC" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE help LPC bridge function of the Intel SCH provides support for @@ -917,7 +921,7 @@ config LPC_SCH config LPC_ICH tristate "Intel ICH LPC" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE help The LPC bridge function of the Intel ICH provides support for @@ -928,7 +932,7 @@ config LPC_ICH config MFD_RDC321X tristate "Support for RDC-R321x southbridge" select MFD_CORE - depends on PCI + depends on PCI && GENERIC_HARDIRQS help Say yes here if you want to have support for the RDC R-321x SoC southbridge which provides access to GPIOs and Watchdog using the @@ -937,7 +941,7 @@ config MFD_RDC321X config MFD_JANZ_CMODIO tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board" select MFD_CORE - depends on PCI + depends on PCI && GENERIC_HARDIRQS help This is the core driver for the Janz CMOD-IO PCI MODULbus carrier board. This device is a PCI to MODULbus bridge which may @@ -955,7 +959,7 @@ config MFD_JZ4740_ADC config MFD_VX855 tristate "Support for VIA VX855/VX875 integrated south bridge" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE help Say yes here to enable support for various functions of the @@ -964,7 +968,7 @@ config MFD_VX855 config MFD_WL1273_CORE tristate "Support for TI WL1273 FM radio." - depends on I2C + depends on I2C && GENERIC_HARDIRQS select MFD_CORE default n help @@ -1028,7 +1032,7 @@ config MFD_TPS65090 config MFD_AAT2870_CORE bool "Support for the AnalogicTech AAT2870" select MFD_CORE - depends on I2C=y && GPIOLIB + depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS help If you say yes here you get support for the AAT2870. This driver provides common support for accessing the device, @@ -1060,7 +1064,7 @@ config MFD_RC5T583 config MFD_STA2X11 bool "STA2X11 multi function device support" - depends on STA2X11 + depends on STA2X11 && GENERIC_HARDIRQS select MFD_CORE select REGMAP_MMIO @@ -1077,7 +1081,7 @@ config MFD_PALMAS select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS help If you say yes here you get support for the Palmas series of PMIC chips from Texas Instruments. @@ -1085,7 +1089,7 @@ config MFD_PALMAS config MFD_VIPERBOARD tristate "Support for Nano River Technologies Viperboard" select MFD_CORE - depends on USB + depends on USB && GENERIC_HARDIRQS default n help Say yes here if you want support for Nano River Technologies @@ -1099,7 +1103,7 @@ config MFD_VIPERBOARD config MFD_RETU tristate "Support for Retu multi-function device" select MFD_CORE - depends on I2C + depends on I2C && GENERIC_HARDIRQS select REGMAP_IRQ help Retu is a multi-function device found on Nokia Internet Tablets @@ -1110,7 +1114,7 @@ config MFD_AS3711 select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - depends on I2C=y + depends on I2C=y && GENERIC_HARDIRQS help Support for the AS3711 PMIC from AMS From 0cd5b6d08c7bf8b2d81eb1413ea1463cc72487b1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 6 Feb 2013 23:23:01 +0100 Subject: [PATCH 2620/4607] mfd: ab8500: Fix compile error When compiling the AB8500 core driver in the latest MFD tree the following happens: CC drivers/mfd/ab8500-debugfs.o /home/elinwal/linux-next/drivers/mfd/ab8500-debugfs.c:157:3: error: 'AB8500_SYS_CTRL1_BLOCK' undeclared here (not in a function) /home/elinwal/linux-next/drivers/mfd/ab8500-debugfs.c:157:2: error: array index in initializer not of integer type /home/elinwal/linux-next/drivers/mfd/ab8500-debugfs.c:157:2: error: (near initialization for 'debug_ranges') (...) This is due to a missing include statement, so fix it up. Cc: Lee Jones Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-debugfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index ba25f95e1677..45fe3c50eb03 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -82,6 +82,7 @@ #include #include +#include #include #ifdef CONFIG_DEBUG_FS From c3481955f6c78c8dd99921759306d7469c999ec2 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Fri, 8 Feb 2013 15:24:27 +0800 Subject: [PATCH 2621/4607] mfd: rtsx: Fix issue that booting OS with SD card inserted Realtek card reader supports both SD and MS card. According to the settings of rtsx MFD driver, SD host will be probed before MS host. If we boot/reboot Linux with SD card inserted, the resetting flow of SD card will succeed, and the following resetting flow of MS is sure to fail. Then MS upper-level driver will ask rtsx driver to turn power off. This request leads to the result that the following SD commands fail and SD card can't be accessed again. In this commit, Realtek's SD and MS host driver will check whether the card that upper driver requesting is the one existing in the slot. If not, Realtek's host driver will refuse the operation to make sure the exlusive accessing at the same time. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/memstick/host/rtsx_pci_ms.c | 7 +++++++ drivers/mfd/rtsx_pcr.c | 30 +++++++++++++++++++++++++++++ drivers/mmc/host/rtsx_pci_sdmmc.c | 18 +++++++++++++++++ include/linux/mfd/rtsx_pci.h | 2 ++ 4 files changed, 57 insertions(+) diff --git a/drivers/memstick/host/rtsx_pci_ms.c b/drivers/memstick/host/rtsx_pci_ms.c index f5ddb82dadb7..64a779c58a74 100644 --- a/drivers/memstick/host/rtsx_pci_ms.c +++ b/drivers/memstick/host/rtsx_pci_ms.c @@ -426,6 +426,9 @@ static void rtsx_pci_ms_request(struct memstick_host *msh) dev_dbg(ms_dev(host), "--> %s\n", __func__); + if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD)) + return; + schedule_work(&host->handle_req); } @@ -441,6 +444,10 @@ static int rtsx_pci_ms_set_param(struct memstick_host *msh, dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n", __func__, param, value); + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD); + if (err) + return err; + switch (param) { case MEMSTICK_POWER: if (value == MEMSTICK_POWER_ON) diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 822237e322ba..481a98a10ecd 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -708,6 +708,25 @@ int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card) } EXPORT_SYMBOL_GPL(rtsx_pci_card_power_off); +int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card) +{ + unsigned int cd_mask[] = { + [RTSX_SD_CARD] = SD_EXIST, + [RTSX_MS_CARD] = MS_EXIST + }; + + if (!pcr->ms_pmos) { + /* When using single PMOS, accessing card is not permitted + * if the existing card is not the designated one. + */ + if (pcr->card_exist & (~cd_mask[card])) + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rtsx_pci_card_exclusive_check); + int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) { if (pcr->ops->switch_output_voltage) @@ -784,6 +803,9 @@ static void rtsx_pci_card_detect(struct work_struct *work) card_inserted = pcr->ops->cd_deglitch(pcr); card_detect = card_inserted | card_removed; + + pcr->card_exist |= card_inserted; + pcr->card_exist &= ~card_removed; } mutex_unlock(&pcr->pcr_mutex); @@ -976,6 +998,14 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) return err; } + /* No CD interrupt if probing driver with card inserted. + * So we need to initialize pcr->card_exist here. + */ + if (pcr->ops->cd_deglitch) + pcr->card_exist = pcr->ops->cd_deglitch(pcr); + else + pcr->card_exist = rtsx_pci_readl(pcr, RTSX_BIPR) & CARD_EXIST; + return 0; } diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index f74b5adca642..468c92303167 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -678,12 +678,19 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_command *cmd = mrq->cmd; struct mmc_data *data = mrq->data; unsigned int data_size = 0; + int err; if (host->eject) { cmd->error = -ENOMEDIUM; goto finish; } + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD); + if (err) { + cmd->error = err; + goto finish; + } + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); @@ -901,6 +908,9 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (host->eject) return; + if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD)) + return; + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); @@ -1073,6 +1083,10 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) if (host->eject) return -ENOMEDIUM; + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD); + if (err) + return err; + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); @@ -1122,6 +1136,10 @@ static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) if (host->eject) return -ENOMEDIUM; + err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_SD_CARD); + if (err) + return err; + mutex_lock(&pcr->pcr_mutex); rtsx_pci_start_run(pcr); diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 5d9b81e8aff4..26ea7f1b7caf 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -740,6 +740,7 @@ struct rtsx_pcr { unsigned int card_inserted; unsigned int card_removed; + unsigned int card_exist; struct delayed_work carddet_work; struct delayed_work idle_work; @@ -804,6 +805,7 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, u8 ssc_depth, bool initial_mode, bool double_clk, bool vpclk); int rtsx_pci_card_power_on(struct rtsx_pcr *pcr, int card); int rtsx_pci_card_power_off(struct rtsx_pcr *pcr, int card); +int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card); int rtsx_pci_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage); unsigned int rtsx_pci_card_exist(struct rtsx_pcr *pcr); void rtsx_pci_complete_unfinished_transfer(struct rtsx_pcr *pcr); From 1765dbccaa5aa2db7b53dc765f0e636591876c03 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 11 Feb 2013 14:26:19 -0600 Subject: [PATCH 2622/4607] mfd: twl-core: Fix kernel panic on boot Commit 8a6aaa3 (mfd: twl-core: Collect global variables behind one private structure (global)) removed the variable "inuse" that is used to determine if the device has been initialised and now use the twl_priv structure instead. This is causing the kernel to panic on OMAP3+ devices using the twl driver, because we try to access the twl_priv->ready member before checking if twl_priv is initialised. Fix this and move this test to the beginning of the twl_i2c_read/write function because twl_get_last_module() also uses the twl_priv structure. Signed-off-by: Jon Hunter Acked-by: Peter Ujfalusi Signed-off-by: Samuel Ortiz --- drivers/mfd/twl-core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 557f9ee5ec18..89ab4d970643 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -316,12 +316,12 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no >= twl_get_last_module())) { - pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); + if (unlikely(!twl_priv || !twl_priv->ready)) { + pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } - if (unlikely(!twl_priv->ready)) { - pr_err("%s: not initialized\n", DRIVER_NAME); + if (unlikely(mod_no >= twl_get_last_module())) { + pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } @@ -355,12 +355,12 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int sid; struct twl_client *twl; - if (unlikely(mod_no >= twl_get_last_module())) { - pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); + if (unlikely(!twl_priv || !twl_priv->ready)) { + pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } - if (unlikely(!twl_priv->ready)) { - pr_err("%s: not initialized\n", DRIVER_NAME); + if (unlikely(mod_no >= twl_get_last_module())) { + pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; } From 75177deee91c17b374b827a832c0d2061ce0f07e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 11 Feb 2013 18:48:00 -0200 Subject: [PATCH 2623/4607] mfd: syscon: Fix sparse warning Fix the following sparse warnings: drivers/mfd/syscon.c:40:15: warning: symbol 'syscon_node_to_regmap' was not declared. Should it be static? drivers/mfd/syscon.c:56:15: warning: symbol 'syscon_regmap_lookup_by_compatible' was not declared. Should it be static? drivers/mfd/syscon.c:72:15: warning: symbol 'syscon_regmap_lookup_by_phandle' was not declared. Should it be static? Cc: Dong Aisheng Signed-off-by: Fabio Estevam Signed-off-by: Samuel Ortiz --- drivers/mfd/syscon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 3f10591ea94e..61aea6381cdf 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -20,6 +20,7 @@ #include #include #include +#include static struct platform_driver syscon_driver; From c3e9e6b67db23ae678550f861b5679880481cfa3 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 11 Feb 2013 18:48:01 -0200 Subject: [PATCH 2624/4607] mfd: da9052-i2c: Staticize da9052_i2c_fix() da9052_i2c_fix() is only used locally, so let it be static. Fix the following sparse warning: drivers/mfd/da9052-i2c.c:63:5: warning: symbol 'da9052_i2c_fix' was not declared. Should it be static? Cc: Ashish Jangam Signed-off-by: Fabio Estevam Signed-off-by: Samuel Ortiz --- drivers/mfd/da9052-i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index 885e56780358..6a9fec40d018 100644 --- a/drivers/mfd/da9052-i2c.c +++ b/drivers/mfd/da9052-i2c.c @@ -60,7 +60,7 @@ static inline bool i2c_safe_reg(unsigned char reg) * This fix is to follow any read or write with a dummy read to a safe * register. */ -int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg) +static int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg) { int val; @@ -85,7 +85,6 @@ int da9052_i2c_fix(struct da9052 *da9052, unsigned char reg) return 0; } -EXPORT_SYMBOL(da9052_i2c_fix); static int da9052_i2c_enable_multiwrite(struct da9052 *da9052) { From 5829e9b64e657560e840dc0ecfee177cb002cd69 Mon Sep 17 00:00:00 2001 From: Darren Hart Date: Fri, 8 Feb 2013 15:20:36 -0800 Subject: [PATCH 2625/4607] mfd: lpc_sch: Accomodate partial population of the MFD devices The current probe aborts if any of the 3 base address registers are disabled. On a TunnelCreek system I am working on, this resulted in the SMBIOS and GPIO devices being removed when it couldn't read the base address for the watchdog timer. This patch accommodates partial population of the lpc_sch_cells array and only aborts if all the base address registers are disabled. A max size array is allocated and the individual device cells are added to it after their base addresses are successfully determined. This simplifies the code a bit by removing the need for the separate tunnelcreek cells array and combining some of the add/remove logic. Cc: Grant Likely , Cc: Denis Turischev , Cc: Greg Kroah-Hartman , Cc: Linus Walleij Signed-off-by: Darren Hart Signed-off-by: Samuel Ortiz --- drivers/mfd/lpc_sch.c | 153 ++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 79 deletions(-) diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index 5624fcbba69b..8cc6aac27cb2 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -45,34 +45,32 @@ static struct resource smbus_sch_resource = { .flags = IORESOURCE_IO, }; - static struct resource gpio_sch_resource = { .flags = IORESOURCE_IO, }; -static struct mfd_cell lpc_sch_cells[] = { - { - .name = "isch_smbus", - .num_resources = 1, - .resources = &smbus_sch_resource, - }, - { - .name = "sch_gpio", - .num_resources = 1, - .resources = &gpio_sch_resource, - }, -}; - static struct resource wdt_sch_resource = { .flags = IORESOURCE_IO, }; -static struct mfd_cell tunnelcreek_cells[] = { - { - .name = "ie6xx_wdt", - .num_resources = 1, - .resources = &wdt_sch_resource, - }, +static struct mfd_cell lpc_sch_cells[3]; + +static struct mfd_cell isch_smbus_cell = { + .name = "isch_smbus", + .num_resources = 1, + .resources = &smbus_sch_resource, +}; + +static struct mfd_cell sch_gpio_cell = { + .name = "sch_gpio", + .num_resources = 1, + .resources = &gpio_sch_resource, +}; + +static struct mfd_cell wdt_sch_cell = { + .name = "ie6xx_wdt", + .num_resources = 1, + .resources = &wdt_sch_resource, }; static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = { @@ -88,79 +86,76 @@ static int lpc_sch_probe(struct pci_dev *dev, { unsigned int base_addr_cfg; unsigned short base_addr; - int i; + int i, cells = 0; int ret; pci_read_config_dword(dev, SMBASE, &base_addr_cfg); - if (!(base_addr_cfg & (1 << 31))) { - dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n"); - return -ENODEV; - } - base_addr = (unsigned short)base_addr_cfg; - if (base_addr == 0) { - dev_err(&dev->dev, "I/O space for SMBus uninitialized\n"); - return -ENODEV; - } + base_addr = 0; + if (!(base_addr_cfg & (1 << 31))) + dev_warn(&dev->dev, "Decode of the SMBus I/O range disabled\n"); + else + base_addr = (unsigned short)base_addr_cfg; - smbus_sch_resource.start = base_addr; - smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1; + if (base_addr == 0) { + dev_warn(&dev->dev, "I/O space for SMBus uninitialized\n"); + } else { + lpc_sch_cells[cells++] = isch_smbus_cell; + smbus_sch_resource.start = base_addr; + smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1; + } pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg); - if (!(base_addr_cfg & (1 << 31))) { - dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n"); - return -ENODEV; - } - base_addr = (unsigned short)base_addr_cfg; - if (base_addr == 0) { - dev_err(&dev->dev, "I/O space for GPIO uninitialized\n"); - return -ENODEV; - } - - gpio_sch_resource.start = base_addr; - - if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) - gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1; + base_addr = 0; + if (!(base_addr_cfg & (1 << 31))) + dev_warn(&dev->dev, "Decode of the GPIO I/O range disabled\n"); else - gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1; + base_addr = (unsigned short)base_addr_cfg; - for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++) - lpc_sch_cells[i].id = id->device; - - ret = mfd_add_devices(&dev->dev, 0, - lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, - 0, NULL); - if (ret) - goto out_dev; + if (base_addr == 0) { + dev_warn(&dev->dev, "I/O space for GPIO uninitialized\n"); + } else { + lpc_sch_cells[cells++] = sch_gpio_cell; + gpio_sch_resource.start = base_addr; + if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) + gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1; + else + gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1; + } if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC - || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) { + || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) { pci_read_config_dword(dev, WDTBASE, &base_addr_cfg); - if (!(base_addr_cfg & (1 << 31))) { - dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n"); - ret = -ENODEV; - goto out_dev; + base_addr = 0; + if (!(base_addr_cfg & (1 << 31))) + dev_warn(&dev->dev, "Decode of the WDT I/O range disabled\n"); + else + base_addr = (unsigned short)base_addr_cfg; + if (base_addr == 0) + dev_warn(&dev->dev, "I/O space for WDT uninitialized\n"); + else { + lpc_sch_cells[cells++] = wdt_sch_cell; + wdt_sch_resource.start = base_addr; + wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1; } - base_addr = (unsigned short)base_addr_cfg; - if (base_addr == 0) { - dev_err(&dev->dev, "I/O space for WDT uninitialized\n"); - ret = -ENODEV; - goto out_dev; - } - - wdt_sch_resource.start = base_addr; - wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1; - - for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++) - tunnelcreek_cells[i].id = id->device; - - ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells, - ARRAY_SIZE(tunnelcreek_cells), NULL, - 0, NULL); } - return ret; -out_dev: - mfd_remove_devices(&dev->dev); + if (WARN_ON(cells > ARRAY_SIZE(lpc_sch_cells))) { + dev_err(&dev->dev, "Cell count exceeds array size"); + return -ENODEV; + } + + if (cells == 0) { + dev_err(&dev->dev, "All decode registers disabled.\n"); + return -ENODEV; + } + + for (i = 0; i < cells; i++) + lpc_sch_cells[i].id = id->device; + + ret = mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL); + if (ret) + mfd_remove_devices(&dev->dev); + return ret; } From 6e6680e3effbf7cff444405a990dbc355dc3a96f Mon Sep 17 00:00:00 2001 From: James Ralston Date: Fri, 8 Feb 2013 17:33:38 -0800 Subject: [PATCH 2626/4607] mfd: lpc_ich: Add Device IDs for Intel Wellsburg PCH This patch adds the Watchdog Timer Device IDs for the Intel Wellsburg PCH Signed-off-by: James Ralston Acked-by: Peter Tyser Signed-off-by: Samuel Ortiz --- drivers/mfd/lpc_ich.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index a0cfdf980748..5c2ef41fa24c 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -50,6 +50,7 @@ * document number TBD : Panther Point * document number TBD : Lynx Point * document number TBD : Lynx Point-LP + * document number TBD : Wellsburg */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -205,6 +206,7 @@ enum lpc_chipsets { LPC_PPT, /* Panther Point */ LPC_LPT, /* Lynx Point */ LPC_LPT_LP, /* Lynx Point-LP */ + LPC_WBG, /* Wellsburg */ }; struct lpc_ich_info lpc_chipset_info[] = { @@ -485,6 +487,10 @@ struct lpc_ich_info lpc_chipset_info[] = { .name = "Lynx Point_LP", .iTCO_version = 2, }, + [LPC_WBG] = { + .name = "Wellsburg", + .iTCO_version = 2, + }, }; /* @@ -666,6 +672,38 @@ static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = { { PCI_VDEVICE(INTEL, 0x9c45), LPC_LPT_LP}, { PCI_VDEVICE(INTEL, 0x9c46), LPC_LPT_LP}, { PCI_VDEVICE(INTEL, 0x9c47), LPC_LPT_LP}, + { PCI_VDEVICE(INTEL, 0x8d40), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d41), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d42), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d43), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d44), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d45), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d46), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d47), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d48), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d49), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4a), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4b), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4c), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4d), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4e), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d4f), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d50), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d51), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d52), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d53), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d54), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d55), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d56), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d57), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d58), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d59), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5a), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5b), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5c), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5d), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5e), LPC_WBG}, + { PCI_VDEVICE(INTEL, 0x8d5f), LPC_WBG}, { 0, }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, lpc_ich_ids); From 9664ffe6a16676fa4d6a238ad3d9bb6cc24825a1 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Tue, 12 Feb 2013 18:22:14 +0100 Subject: [PATCH 2627/4607] ARM: 7651/1: remove unused smp_timer_broadcast #define The assignment of clock_event_device::broadcast can be done by timer core as of 12ad100046: "clockevents: Add generic timer broadcast function", and the arm code moved over to this as of 3d06770eef: "arm: Add generic timer broadcast support", but left a dangling #define when !CONFIG_GENERIC_TIMER_BROADCAST. This patch removes the now unused #define. Signed-off-by: Mark Rutland Signed-off-by: Russell King --- arch/arm/kernel/smp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index b7e3b506219b..ab9458d62cbb 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -480,8 +480,6 @@ void tick_broadcast(const struct cpumask *mask) { smp_cross_call(mask, IPI_TIMER); } -#else -#define smp_timer_broadcast NULL #endif static void broadcast_timer_set_mode(enum clock_event_mode mode, From 7083e05072b88d503d257b6f012ce56367f3ac97 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 1 Feb 2013 16:41:14 -0800 Subject: [PATCH 2628/4607] drm/i915: Fix RC6VIDS encode/decode The RC6 VIDS has a linear ramp starting at 250mv, which means any values below 250 are invalid. The old buggy macros tried to adjust for this to be more flexible, but there is no need. As Dan pointed out the ENCODE only ever has one value. The only invalid value for decode is an input of 0 which means something is really wonky, and the cases where DECODE are used either don't matter (debug values), or would be implicitly correct (the check for less than 450). This patch makes simpler, easier to read macros which are actually correct. Maybe this patch can actually fix some bugs now. Thanks to Dan for catching this. /me hides Cc: stable@kernel.org Reported-by: Dan Carpenter Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 15f5e7f9cded..8754f9160aef 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4272,8 +4272,8 @@ #define GEN6_PCODE_READ_MIN_FREQ_TABLE 0x9 #define GEN6_PCODE_WRITE_RC6VIDS 0x4 #define GEN6_PCODE_READ_RC6VIDS 0x5 -#define GEN6_ENCODE_RC6_VID(mv) (((mv) / 5) - 245) < 0 ?: 0 -#define GEN6_DECODE_RC6_VID(vids) (((vids) * 5) > 0 ? ((vids) * 5) + 245 : 0) +#define GEN6_ENCODE_RC6_VID(mv) (((mv) - 245) / 5) +#define GEN6_DECODE_RC6_VID(vids) (((vids) * 5) + 245) #define GEN6_PCODE_DATA 0x138128 #define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 From b8efb17b3d687695b81485f606fc4e6c35a50f9a Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Tue, 5 Feb 2013 15:41:53 +0800 Subject: [PATCH 2629/4607] i915: ignore lid open event when resuming i915 driver needs to do modeset when 1. system resumes from sleep 2. lid is opened In PM_SUSPEND_MEM state, all the GPEs are cleared when system resumes, thus it is the i915_resume code does the modeset rather than intel_lid_notify(). But in PM_SUSPEND_FREEZE state, this will be broken because system is still responsive to the lid events. 1. When we close the lid in Freeze state, intel_lid_notify() sets modeset_on_lid. 2. When we reopen the lid, intel_lid_notify() will do a modeset, before the system is resumed. here is the error log, [92146.548074] WARNING: at drivers/gpu/drm/i915/intel_display.c:1028 intel_wait_for_pipe_off+0x184/0x190 [i915]() [92146.548076] Hardware name: VGN-Z540N [92146.548078] pipe_off wait timed out [92146.548167] Modules linked in: hid_generic usbhid hid snd_hda_codec_realtek snd_hda_intel snd_hda_codec parport_pc snd_hwdep ppdev snd_pcm_oss i915 snd_mixer_oss snd_pcm arc4 iwldvm snd_seq_dummy mac80211 snd_seq_oss snd_seq_midi fbcon tileblit font bitblit softcursor drm_kms_helper snd_rawmidi snd_seq_midi_event coretemp drm snd_seq kvm btusb bluetooth snd_timer iwlwifi pcmcia tpm_infineon i2c_algo_bit joydev snd_seq_device intel_agp cfg80211 snd intel_gtt yenta_socket pcmcia_rsrc sony_laptop agpgart microcode psmouse tpm_tis serio_raw mxm_wmi soundcore snd_page_alloc tpm acpi_cpufreq lpc_ich pcmcia_core tpm_bios mperf processor lp parport firewire_ohci firewire_core crc_itu_t sdhci_pci sdhci thermal e1000e [92146.548173] Pid: 4304, comm: kworker/0:0 Tainted: G W 3.8.0-rc3-s0i3-v3-test+ #9 [92146.548175] Call Trace: [92146.548189] [] warn_slowpath_common+0x72/0xa0 [92146.548227] [] ? intel_wait_for_pipe_off+0x184/0x190 [i915] [92146.548263] [] ? intel_wait_for_pipe_off+0x184/0x190 [i915] [92146.548270] [] warn_slowpath_fmt+0x33/0x40 [92146.548307] [] intel_wait_for_pipe_off+0x184/0x190 [i915] [92146.548344] [] intel_disable_pipe+0x102/0x190 [i915] [92146.548380] [] ? intel_disable_plane+0x64/0x80 [i915] [92146.548417] [] i9xx_crtc_disable+0xbc/0x150 [i915] [92146.548456] [] intel_crtc_update_dpms+0x5e/0x90 [i915] [92146.548493] [] intel_modeset_setup_hw_state+0x42f/0x8f0 [i915] [92146.548535] [] intel_lid_notify+0x9b/0xc0 [i915] [92146.548543] [] notifier_call_chain+0x43/0x60 [92146.548550] [] __blocking_notifier_call_chain+0x41/0x80 [92146.548556] [] blocking_notifier_call_chain+0x1f/0x30 [92146.548563] [] acpi_lid_send_state+0x78/0xa4 [92146.548569] [] acpi_button_notify+0x3b/0xf1 [92146.548577] [] ? acpi_os_execute+0x17/0x19 [92146.548582] [] ? acpi_ec_sync_query+0xa5/0xbc [92146.548589] [] acpi_device_notify+0x16/0x18 [92146.548595] [] acpi_ev_notify_dispatch+0x38/0x4f [92146.548600] [] acpi_os_execute_deferred+0x20/0x2b [92146.548607] [] process_one_work+0x128/0x3f0 [92146.548613] [] ? common_interrupt+0x33/0x38 [92146.548618] [] ? wake_up_worker+0x30/0x30 [92146.548624] [] ? acpi_os_wait_events_complete+0x1e/0x1e [92146.548629] [] worker_thread+0x119/0x3b0 [92146.548634] [] ? manage_workers+0x240/0x240 [92146.548640] [] kthread+0x94/0xa0 [92146.548647] [] ? ftrace_raw_output_sched_stat_runtime+0x70/0xf0 [92146.548652] [] ret_from_kernel_thread+0x1b/0x28 [92146.548658] [] ? kthread_create_on_node+0xc0/0xc0 three different modeset flags are introduced in this patch MODESET_ON_LID_OPEN: do modeset on next lid open event MODESET_DONE: modeset already done MODESET_SUSPENDED: suspended, only do modeset when system is resumed In this way, 1. when lid is closed, MODESET_ON_LID_OPEN is set so that we'll do modeset on next lid open event. 2. when lid is opened, MODESET_DONE is set so that duplicate lid open events will be ignored. 3. when system suspends, MODESET_SUSPENDED is set. In this case, we will not do modeset on any lid events. Plus, locking mechanism is also introduced to avoid racing. Signed-off-by: Zhang Rui Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 1 + drivers/gpu/drm/i915/i915_drv.c | 13 +++++++----- drivers/gpu/drm/i915/i915_drv.h | 10 ++++++++-- drivers/gpu/drm/i915/intel_lvds.c | 33 +++++++++++++++++++------------ 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index cf0610330135..4fa6beb14c77 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1610,6 +1610,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) mutex_init(&dev_priv->dpio_lock); mutex_init(&dev_priv->rps.hw_lock); + mutex_init(&dev_priv->modeset_restore_lock); if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) dev_priv->num_pipe = 3; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index d159d7a402e9..c5b8c81b9440 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -470,6 +470,11 @@ static int i915_drm_freeze(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + /* ignore lid events during suspend */ + mutex_lock(&dev_priv->modeset_restore_lock); + dev_priv->modeset_restore = MODESET_SUSPENDED; + mutex_unlock(&dev_priv->modeset_restore_lock); + intel_set_power_well(dev, true); drm_kms_helper_poll_disable(dev); @@ -496,9 +501,6 @@ static int i915_drm_freeze(struct drm_device *dev) intel_opregion_fini(dev); - /* Modeset on resume, not lid events */ - dev_priv->modeset_on_lid = 0; - console_lock(); intel_fbdev_set_suspend(dev, 1); console_unlock(); @@ -574,8 +576,6 @@ static int __i915_drm_thaw(struct drm_device *dev) intel_opregion_init(dev); - dev_priv->modeset_on_lid = 0; - /* * The console lock can be pretty contented on resume due * to all the printk activity. Try to keep it out of the hot @@ -588,6 +588,9 @@ static int __i915_drm_thaw(struct drm_device *dev) schedule_work(&dev_priv->console_resume_work); } + mutex_lock(&dev_priv->modeset_restore_lock); + dev_priv->modeset_restore = MODESET_DONE; + mutex_unlock(&dev_priv->modeset_restore_lock); return error; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c338b4443fd9..4e5a3776a3ea 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -846,6 +846,12 @@ struct i915_gpu_error { unsigned int stop_rings; }; +enum modeset_restore { + MODESET_ON_LID_OPEN, + MODESET_DONE, + MODESET_SUSPENDED, +}; + typedef struct drm_i915_private { struct drm_device *dev; struct kmem_cache *slab; @@ -967,8 +973,8 @@ typedef struct drm_i915_private { unsigned long quirks; - /* Register state */ - bool modeset_on_lid; + enum modeset_restore modeset_restore; + struct mutex modeset_restore_lock; struct i915_gtt gtt; diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 5e3f08e3fd8b..bff78a3ec37b 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -547,13 +547,14 @@ static const struct dmi_system_id intel_no_modeset_on_lid[] = { }; /* - * Lid events. Note the use of 'modeset_on_lid': - * - we set it on lid close, and reset it on open + * Lid events. Note the use of 'modeset': + * - we set it to MODESET_ON_LID_OPEN on lid close, + * and set it to MODESET_DONE on open * - we use it as a "only once" bit (ie we ignore - * duplicate events where it was already properly - * set/reset) - * - the suspend/resume paths will also set it to - * zero, since they restore the mode ("lid open"). + * duplicate events where it was already properly set) + * - the suspend/resume paths will set it to + * MODESET_SUSPENDED and ignore the lid open event, + * because they restore the mode ("lid open"). */ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, void *unused) @@ -567,6 +568,9 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, if (dev->switch_power_state != DRM_SWITCH_POWER_ON) return NOTIFY_OK; + mutex_lock(&dev_priv->modeset_restore_lock); + if (dev_priv->modeset_restore == MODESET_SUSPENDED) + goto exit; /* * check and update the status of LVDS connector after receiving * the LID nofication event. @@ -575,21 +579,24 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, /* Don't force modeset on machines where it causes a GPU lockup */ if (dmi_check_system(intel_no_modeset_on_lid)) - return NOTIFY_OK; + goto exit; if (!acpi_lid_open()) { - dev_priv->modeset_on_lid = 1; - return NOTIFY_OK; + /* do modeset on next lid open event */ + dev_priv->modeset_restore = MODESET_ON_LID_OPEN; + goto exit; } - if (!dev_priv->modeset_on_lid) - return NOTIFY_OK; - - dev_priv->modeset_on_lid = 0; + if (dev_priv->modeset_restore == MODESET_DONE) + goto exit; drm_modeset_lock_all(dev); intel_modeset_setup_hw_state(dev, true); drm_modeset_unlock_all(dev); + dev_priv->modeset_restore = MODESET_DONE; + +exit: + mutex_unlock(&dev_priv->modeset_restore_lock); return NOTIFY_OK; } From 39b648d9ec7d4ab0b4362872c6284a12c582afa6 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 31 Jan 2013 11:53:05 -0800 Subject: [PATCH 2630/4607] ceph: remove 'ceph.layout' virtual xattr This has been deprecated since v3.3, 114fc474. Kill it. Signed-off-by: Sage Weil Reviewed-by: Sam Lang --- fs/ceph/xattr.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 2c2ae5be9902..c2048b1a5395 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -123,13 +123,6 @@ static size_t ceph_vxattrcb_file_layout(struct ceph_inode_info *ci, char *val, static struct ceph_vxattr ceph_file_vxattrs[] = { XATTR_NAME_CEPH(file, layout), - /* The following extended attribute name is deprecated */ - { - .name = XATTR_CEPH_PREFIX "layout", - .name_size = sizeof (XATTR_CEPH_PREFIX "layout"), - .getxattr_cb = ceph_vxattrcb_file_layout, - .readonly = true, - }, { 0 } /* Required table terminator */ }; static size_t ceph_file_vxattrs_name_size; /* total size of all names */ From 8860147a01c4243f64f7d602dbf8342ca616ed45 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 31 Jan 2013 11:53:27 -0800 Subject: [PATCH 2631/4607] ceph: support hidden vxattrs Add ability to flag virtual xattrs as hidden, such that you can getxattr them but they do not appear in listxattr. Signed-off-by: Sage Weil Reviewed-by: Sam Lang --- fs/ceph/xattr.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index c2048b1a5395..43063d0dee8f 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -29,7 +29,7 @@ struct ceph_vxattr { size_t name_size; /* strlen(name) + 1 (for '\0') */ size_t (*getxattr_cb)(struct ceph_inode_info *ci, char *val, size_t size); - bool readonly; + bool readonly, hidden; }; /* directories */ @@ -85,13 +85,14 @@ static size_t ceph_vxattrcb_dir_rctime(struct ceph_inode_info *ci, char *val, #define CEPH_XATTR_NAME(_type, _name) XATTR_CEPH_PREFIX #_type "." #_name -#define XATTR_NAME_CEPH(_type, _name) \ - { \ - .name = CEPH_XATTR_NAME(_type, _name), \ - .name_size = sizeof (CEPH_XATTR_NAME(_type, _name)), \ - .getxattr_cb = ceph_vxattrcb_ ## _type ## _ ## _name, \ - .readonly = true, \ - } +#define XATTR_NAME_CEPH(_type, _name) \ + { \ + .name = CEPH_XATTR_NAME(_type, _name), \ + .name_size = sizeof (CEPH_XATTR_NAME(_type, _name)), \ + .getxattr_cb = ceph_vxattrcb_ ## _type ## _ ## _name, \ + .readonly = true, \ + .hidden = false, \ + } static struct ceph_vxattr ceph_dir_vxattrs[] = { XATTR_NAME_CEPH(dir, entries), @@ -157,7 +158,8 @@ static size_t __init vxattrs_name_size(struct ceph_vxattr *vxattrs) size_t size = 0; for (vxattr = vxattrs; vxattr->name; vxattr++) - size += vxattr->name_size; + if (!vxattr->hidden) + size += vxattr->name_size; return size; } From 3adf654ddbc355c23d75c6684128d4b067a7b792 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 31 Jan 2013 11:53:41 -0800 Subject: [PATCH 2632/4607] ceph: pass unhandled ceph.* setxattrs through to MDS If we do not specifically understand a setxattr on a ceph.* virtual xattr, send it through to the MDS. This allows us to implement new functionality via the MDS without direct support on the client side. Signed-off-by: Sage Weil Reviewed-by: Sam Lang --- fs/ceph/xattr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 43063d0dee8f..edc47de77fed 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -777,6 +777,10 @@ int ceph_setxattr(struct dentry *dentry, const char *name, if (vxattr && vxattr->readonly) return -EOPNOTSUPP; + /* pass any unhandled ceph.* xattrs through to the MDS */ + if (!strncmp(name, XATTR_CEPH_PREFIX, XATTR_CEPH_PREFIX_LEN)) + goto do_sync_unlocked; + /* preallocate memory for xattr name, value, index node */ err = -ENOMEM; newname = kmemdup(name, name_len + 1, GFP_NOFS); @@ -833,6 +837,7 @@ retry: do_sync: spin_unlock(&ci->i_ceph_lock); +do_sync_unlocked: err = ceph_sync_setxattr(dentry, name, value, size, flags); out: kfree(newname); From d421acb1ad7dfa31b7463b67f1593714b0b727c3 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 20 Jan 2013 21:55:30 -0800 Subject: [PATCH 2633/4607] ceph: pass ceph.* removexattrs through to MDS If we do not explicitly recognized a vxattr (e.g., as readonly), pass the request through to the MDS and deal with it there. Signed-off-by: Sage Weil Reviewed-by: Sam Lang --- fs/ceph/xattr.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index edc47de77fed..234270f00c2a 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -892,6 +892,10 @@ int ceph_removexattr(struct dentry *dentry, const char *name) if (vxattr && vxattr->readonly) return -EOPNOTSUPP; + /* pass any unhandled ceph.* xattrs through to the MDS */ + if (!strncmp(name, XATTR_CEPH_PREFIX, XATTR_CEPH_PREFIX_LEN)) + goto do_sync_unlocked; + err = -ENOMEM; spin_lock(&ci->i_ceph_lock); retry: @@ -931,6 +935,7 @@ retry: return err; do_sync: spin_unlock(&ci->i_ceph_lock); +do_sync_unlocked: err = ceph_send_removexattr(dentry, name); out: return err; From f36e4472969a78ae65e514b553e9a0feacb40a28 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 20 Jan 2013 21:59:29 -0800 Subject: [PATCH 2634/4607] ceph: add exists_cb to vxattr struct Allow for a callback to dynamically determine if a vxattr exists for the given inode. Signed-off-by: Sage Weil Reviewed-by: Sam Lang --- fs/ceph/xattr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 234270f00c2a..06344da4e968 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -30,6 +30,7 @@ struct ceph_vxattr { size_t (*getxattr_cb)(struct ceph_inode_info *ci, char *val, size_t size); bool readonly, hidden; + bool (*exists_cb)(struct ceph_inode_info *ci); }; /* directories */ @@ -92,6 +93,7 @@ static size_t ceph_vxattrcb_dir_rctime(struct ceph_inode_info *ci, char *val, .getxattr_cb = ceph_vxattrcb_ ## _type ## _ ## _name, \ .readonly = true, \ .hidden = false, \ + .exists_cb = NULL, \ } static struct ceph_vxattr ceph_dir_vxattrs[] = { From 0bee82fb4b8d49541fe474ed460d2b917f329568 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 20 Jan 2013 22:00:58 -0800 Subject: [PATCH 2635/4607] ceph: fix getxattr vxattr handling Change the vxattr handling for getxattr so that vxattrs are checked prior to any xattr content, and never after. Enforce vxattr existence via the exists_cb callback. Signed-off-by: Sage Weil Reviewed-by: Sam Lang --- fs/ceph/xattr.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 06344da4e968..87b85f3403d4 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -569,13 +569,17 @@ ssize_t ceph_getxattr(struct dentry *dentry, const char *name, void *value, if (!ceph_is_valid_xattr(name)) return -ENODATA; - /* let's see if a virtual xattr was requested */ - vxattr = ceph_match_vxattr(inode, name); - spin_lock(&ci->i_ceph_lock); dout("getxattr %p ver=%lld index_ver=%lld\n", inode, ci->i_xattrs.version, ci->i_xattrs.index_version); + /* let's see if a virtual xattr was requested */ + vxattr = ceph_match_vxattr(inode, name); + if (vxattr && !(vxattr->exists_cb && !vxattr->exists_cb(ci))) { + err = vxattr->getxattr_cb(ci, value, size); + goto out; + } + if (__ceph_caps_issued_mask(ci, CEPH_CAP_XATTR_SHARED, 1) && (ci->i_xattrs.index_version >= ci->i_xattrs.version)) { goto get_xattr; @@ -589,11 +593,6 @@ ssize_t ceph_getxattr(struct dentry *dentry, const char *name, void *value, spin_lock(&ci->i_ceph_lock); - if (vxattr && vxattr->readonly) { - err = vxattr->getxattr_cb(ci, value, size); - goto out; - } - err = __build_xattrs(inode); if (err < 0) goto out; @@ -601,11 +600,8 @@ ssize_t ceph_getxattr(struct dentry *dentry, const char *name, void *value, get_xattr: err = -ENODATA; /* == ENOATTR */ xattr = __get_xattr(ci, name); - if (!xattr) { - if (vxattr) - err = vxattr->getxattr_cb(ci, value, size); + if (!xattr) goto out; - } err = -ERANGE; if (size && size < xattr->val_len) From b65917dd2700b7d12e25e2e0bbfd58eb3c932158 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 20 Jan 2013 22:02:39 -0800 Subject: [PATCH 2636/4607] ceph: fix listxattr handling for vxattrs Only include vxattrs in the result if they are not hidden and exist (as determined by the exists_cb callback). Note that the buffer size we return when 0 is passed in always includes vxattrs that *might* exist, forming an upper bound. Signed-off-by: Sage Weil Reviewed-by: Sam Lang --- fs/ceph/xattr.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 87b85f3403d4..ec09ea5c4f07 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -657,23 +657,30 @@ list_xattr: vir_namelen = ceph_vxattrs_name_size(vxattrs); /* adding 1 byte per each variable due to the null termination */ - namelen = vir_namelen + ci->i_xattrs.names_size + ci->i_xattrs.count; + namelen = ci->i_xattrs.names_size + ci->i_xattrs.count; err = -ERANGE; - if (size && namelen > size) + if (size && vir_namelen + namelen > size) goto out; - err = namelen; + err = namelen + vir_namelen; if (size == 0) goto out; names = __copy_xattr_names(ci, names); /* virtual xattr names, too */ - if (vxattrs) + err = namelen; + if (vxattrs) { for (i = 0; vxattrs[i].name; i++) { - len = sprintf(names, "%s", vxattrs[i].name); - names += len + 1; + if (!vxattrs[i].hidden && + !(vxattrs[i].exists_cb && + !vxattrs[i].exists_cb(ci))) { + len = sprintf(names, "%s", vxattrs[i].name); + names += len + 1; + err += len + 1; + } } + } out: spin_unlock(&ci->i_ceph_lock); From 32ab0bd78d7d9235efb38ad5cba6a3a6b39a1da6 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sat, 19 Jan 2013 16:46:32 -0800 Subject: [PATCH 2637/4607] ceph: change ceph.file.layout.* implementation, content Implement a new method to generate the ceph.file.layout vxattr using the new framework. Use 'stripe_unit' instead of 'chunk_size'. Include pool name, either as a string or as an integer. Signed-off-by: Sage Weil Reviewed-by: Sam Lang --- fs/ceph/xattr.c | 67 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index ec09ea5c4f07..532c95a6c9fa 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -33,6 +33,50 @@ struct ceph_vxattr { bool (*exists_cb)(struct ceph_inode_info *ci); }; +/* layouts */ + +static bool ceph_vxattrcb_layout_exists(struct ceph_inode_info *ci) +{ + size_t s; + char *p = (char *)&ci->i_layout; + + for (s = 0; s < sizeof(ci->i_layout); s++, p++) + if (*p) + return true; + return false; +} + +static size_t ceph_vxattrcb_layout(struct ceph_inode_info *ci, char *val, + size_t size) +{ + int ret; + struct ceph_fs_client *fsc = ceph_sb_to_client(ci->vfs_inode.i_sb); + struct ceph_osd_client *osdc = &fsc->client->osdc; + s64 pool = ceph_file_layout_pg_pool(ci->i_layout); + const char *pool_name; + + dout("ceph_vxattrcb_layout %p\n", &ci->vfs_inode); + down_read(&osdc->map_sem); + pool_name = ceph_pg_pool_name_by_id(osdc->osdmap, pool); + if (pool_name) + ret = snprintf(val, size, + "stripe_unit=%lld stripe_count=%lld object_size=%lld pool=%s", + (unsigned long long)ceph_file_layout_su(ci->i_layout), + (unsigned long long)ceph_file_layout_stripe_count(ci->i_layout), + (unsigned long long)ceph_file_layout_object_size(ci->i_layout), + pool_name); + else + ret = snprintf(val, size, + "stripe_unit=%lld stripe_count=%lld object_size=%lld pool=%lld", + (unsigned long long)ceph_file_layout_su(ci->i_layout), + (unsigned long long)ceph_file_layout_stripe_count(ci->i_layout), + (unsigned long long)ceph_file_layout_object_size(ci->i_layout), + (unsigned long long)pool); + + up_read(&osdc->map_sem); + return ret; +} + /* directories */ static size_t ceph_vxattrcb_dir_entries(struct ceph_inode_info *ci, char *val, @@ -84,6 +128,7 @@ static size_t ceph_vxattrcb_dir_rctime(struct ceph_inode_info *ci, char *val, (long)ci->i_rctime.tv_nsec); } + #define CEPH_XATTR_NAME(_type, _name) XATTR_CEPH_PREFIX #_type "." #_name #define XATTR_NAME_CEPH(_type, _name) \ @@ -111,21 +156,15 @@ static size_t ceph_dir_vxattrs_name_size; /* total size of all names */ /* files */ -static size_t ceph_vxattrcb_file_layout(struct ceph_inode_info *ci, char *val, - size_t size) -{ - int ret; - - ret = snprintf(val, size, - "chunk_bytes=%lld\nstripe_count=%lld\nobject_size=%lld\n", - (unsigned long long)ceph_file_layout_su(ci->i_layout), - (unsigned long long)ceph_file_layout_stripe_count(ci->i_layout), - (unsigned long long)ceph_file_layout_object_size(ci->i_layout)); - return ret; -} - static struct ceph_vxattr ceph_file_vxattrs[] = { - XATTR_NAME_CEPH(file, layout), + { + .name = "ceph.file.layout", + .name_size = sizeof("ceph.file.layout"), + .getxattr_cb = ceph_vxattrcb_layout, + .readonly = false, + .hidden = false, + .exists_cb = ceph_vxattrcb_layout_exists, + }, { 0 } /* Required table terminator */ }; static size_t ceph_file_vxattrs_name_size; /* total size of all names */ From 1f08f2b056ea6c2e12f4e95e88949a882a996208 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 20 Jan 2013 22:07:12 -0800 Subject: [PATCH 2638/4607] ceph: add ceph.dir.layout vxattr This virtual xattr will only appear when there is a dir layout policy set on the directory. It can be set via setxattr and removed via removexattr (implemented by the MDS). Signed-off-by: Sage Weil Reviewed-by: Sam Lang --- fs/ceph/xattr.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 532c95a6c9fa..f3c4fe7202c7 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -142,6 +142,14 @@ static size_t ceph_vxattrcb_dir_rctime(struct ceph_inode_info *ci, char *val, } static struct ceph_vxattr ceph_dir_vxattrs[] = { + { + .name = "ceph.dir.layout", + .name_size = sizeof("ceph.dir.layout"), + .getxattr_cb = ceph_vxattrcb_layout, + .readonly = false, + .hidden = false, + .exists_cb = ceph_vxattrcb_layout_exists, + }, XATTR_NAME_CEPH(dir, entries), XATTR_NAME_CEPH(dir, files), XATTR_NAME_CEPH(dir, subdirs), From 695b711933689ea51af782760f4b1e2c6a42a631 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 20 Jan 2013 22:08:33 -0800 Subject: [PATCH 2639/4607] ceph: implement hidden per-field ceph.*.layout.* vxattrs Allow individual fields of the layout to be fetched via getxattr. The ceph.dir.layout.* vxattr with "disappear" if the exists_cb indicates there no dir layout set. Signed-off-by: Sage Weil Reviewed-by: Sam Lang --- fs/ceph/xattr.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index f3c4fe7202c7..2135817e708d 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -77,6 +77,46 @@ static size_t ceph_vxattrcb_layout(struct ceph_inode_info *ci, char *val, return ret; } +static size_t ceph_vxattrcb_layout_stripe_unit(struct ceph_inode_info *ci, + char *val, size_t size) +{ + return snprintf(val, size, "%lld", + (unsigned long long)ceph_file_layout_su(ci->i_layout)); +} + +static size_t ceph_vxattrcb_layout_stripe_count(struct ceph_inode_info *ci, + char *val, size_t size) +{ + return snprintf(val, size, "%lld", + (unsigned long long)ceph_file_layout_stripe_count(ci->i_layout)); +} + +static size_t ceph_vxattrcb_layout_object_size(struct ceph_inode_info *ci, + char *val, size_t size) +{ + return snprintf(val, size, "%lld", + (unsigned long long)ceph_file_layout_object_size(ci->i_layout)); +} + +static size_t ceph_vxattrcb_layout_pool(struct ceph_inode_info *ci, + char *val, size_t size) +{ + int ret; + struct ceph_fs_client *fsc = ceph_sb_to_client(ci->vfs_inode.i_sb); + struct ceph_osd_client *osdc = &fsc->client->osdc; + s64 pool = ceph_file_layout_pg_pool(ci->i_layout); + const char *pool_name; + + down_read(&osdc->map_sem); + pool_name = ceph_pg_pool_name_by_id(osdc->osdmap, pool); + if (pool_name) + ret = snprintf(val, size, "%s", pool_name); + else + ret = snprintf(val, size, "%lld", (unsigned long long)pool); + up_read(&osdc->map_sem); + return ret; +} + /* directories */ static size_t ceph_vxattrcb_dir_entries(struct ceph_inode_info *ci, char *val, @@ -130,6 +170,8 @@ static size_t ceph_vxattrcb_dir_rctime(struct ceph_inode_info *ci, char *val, #define CEPH_XATTR_NAME(_type, _name) XATTR_CEPH_PREFIX #_type "." #_name +#define CEPH_XATTR_NAME2(_type, _name, _name2) \ + XATTR_CEPH_PREFIX #_type "." #_name "." #_name2 #define XATTR_NAME_CEPH(_type, _name) \ { \ @@ -140,6 +182,15 @@ static size_t ceph_vxattrcb_dir_rctime(struct ceph_inode_info *ci, char *val, .hidden = false, \ .exists_cb = NULL, \ } +#define XATTR_LAYOUT_FIELD(_type, _name, _field) \ + { \ + .name = CEPH_XATTR_NAME2(_type, _name, _field), \ + .name_size = sizeof (CEPH_XATTR_NAME2(_type, _name, _field)), \ + .getxattr_cb = ceph_vxattrcb_ ## _name ## _ ## _field, \ + .readonly = false, \ + .hidden = true, \ + .exists_cb = ceph_vxattrcb_layout_exists, \ + } static struct ceph_vxattr ceph_dir_vxattrs[] = { { @@ -150,6 +201,10 @@ static struct ceph_vxattr ceph_dir_vxattrs[] = { .hidden = false, .exists_cb = ceph_vxattrcb_layout_exists, }, + XATTR_LAYOUT_FIELD(dir, layout, stripe_unit), + XATTR_LAYOUT_FIELD(dir, layout, stripe_count), + XATTR_LAYOUT_FIELD(dir, layout, object_size), + XATTR_LAYOUT_FIELD(dir, layout, pool), XATTR_NAME_CEPH(dir, entries), XATTR_NAME_CEPH(dir, files), XATTR_NAME_CEPH(dir, subdirs), @@ -173,6 +228,10 @@ static struct ceph_vxattr ceph_file_vxattrs[] = { .hidden = false, .exists_cb = ceph_vxattrcb_layout_exists, }, + XATTR_LAYOUT_FIELD(file, layout, stripe_unit), + XATTR_LAYOUT_FIELD(file, layout, stripe_count), + XATTR_LAYOUT_FIELD(file, layout, object_size), + XATTR_LAYOUT_FIELD(file, layout, pool), { 0 } /* Required table terminator */ }; static size_t ceph_file_vxattrs_name_size; /* total size of all names */ From 3ebc21f7bc2f9c0145bbbf0f12430b766a200f9f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 31 Jan 2013 16:02:01 -0600 Subject: [PATCH 2640/4607] libceph: fix messenger CONFIG_BLOCK dependencies The ceph messenger has a few spots that are only used when bio messages are supported, and that's only when CONFIG_BLOCK is defined. This surrounds a couple of spots with #ifdef's that would cause a problem if CONFIG_BLOCK were not present in the kernel configuration. This resolves: http://tracker.ceph.com/issues/3976 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- include/linux/ceph/messenger.h | 2 ++ net/ceph/messenger.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 14ba5ee738a9..60903e0f665c 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -83,9 +83,11 @@ struct ceph_msg { struct list_head list_head; struct kref kref; +#ifdef CONFIG_BLOCK struct bio *bio; /* instead of pages/pagelist */ struct bio *bio_iter; /* bio iterator */ int bio_seg; /* current bio segment */ +#endif /* CONFIG_BLOCK */ struct ceph_pagelist *trail; /* the trailing part of the data */ bool front_is_vmalloc; bool more_to_follow; diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 5ccf87ed8d68..8a62a559a2aa 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -9,8 +9,9 @@ #include #include #include +#ifdef CONFIG_BLOCK #include -#include +#endif /* CONFIG_BLOCK */ #include #include @@ -2651,9 +2652,11 @@ struct ceph_msg *ceph_msg_new(int type, int front_len, gfp_t flags, m->page_alignment = 0; m->pages = NULL; m->pagelist = NULL; +#ifdef CONFIG_BLOCK m->bio = NULL; m->bio_iter = NULL; m->bio_seg = 0; +#endif /* CONFIG_BLOCK */ m->trail = NULL; /* front */ From bf0d5f503dc11d6314c0503591d258d60ee9c944 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 22 Nov 2012 00:00:08 -0600 Subject: [PATCH 2641/4607] rbd: new request tracking code This patch fully implements the new request tracking code for rbd I/O requests. Each I/O request to an rbd image will get an rbd_image_request structure allocated to track it. This provides access to all information about the original request, as well as access to the set of one or more object requests that are initiated as a result of the image request. An rbd_obj_request structure defines a request sent to a single osd object (possibly) as part of an rbd image request. An rbd object request refers to a ceph_osd_request structure built up to represent the request; for now it will contain a single osd operation. It also provides space to hold the result status and the version of the object when the osd request completes. An rbd_obj_request structure can also stand on its own. This will be used for reading the version 1 header object, for issuing acknowledgements to event notifications, and for making object method calls. All rbd object requests now complete asynchronously with respect to the osd client--they supply a common callback routine. This resolves: http://tracker.newdream.net/issues/3741 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 621 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 619 insertions(+), 2 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 668936381ab0..daa0f18f7089 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -181,6 +181,67 @@ struct rbd_req_coll { struct rbd_req_status status[0]; }; +struct rbd_img_request; +typedef void (*rbd_img_callback_t)(struct rbd_img_request *); + +#define BAD_WHICH U32_MAX /* Good which or bad which, which? */ + +struct rbd_obj_request; +typedef void (*rbd_obj_callback_t)(struct rbd_obj_request *); + +enum obj_request_type { OBJ_REQUEST_BIO }; /* More types to come */ + +struct rbd_obj_request { + const char *object_name; + u64 offset; /* object start byte */ + u64 length; /* bytes from offset */ + + struct rbd_img_request *img_request; + struct list_head links; /* img_request->obj_requests */ + u32 which; /* posn image request list */ + + enum obj_request_type type; + struct bio *bio_list; + + struct ceph_osd_request *osd_req; + + u64 xferred; /* bytes transferred */ + u64 version; + s32 result; + atomic_t done; + + rbd_obj_callback_t callback; + + struct kref kref; +}; + +struct rbd_img_request { + struct request *rq; + struct rbd_device *rbd_dev; + u64 offset; /* starting image byte offset */ + u64 length; /* byte count from offset */ + bool write_request; /* false for read */ + union { + struct ceph_snap_context *snapc; /* for writes */ + u64 snap_id; /* for reads */ + }; + spinlock_t completion_lock;/* protects next_completion */ + u32 next_completion; + rbd_img_callback_t callback; + + u32 obj_request_count; + struct list_head obj_requests; /* rbd_obj_request structs */ + + struct kref kref; +}; + +#define for_each_obj_request(ireq, oreq) \ + list_for_each_entry(oreq, &ireq->obj_requests, links) +#define for_each_obj_request_from(ireq, oreq) \ + list_for_each_entry_from(oreq, &ireq->obj_requests, links) +#define for_each_obj_request_safe(ireq, oreq, n) \ + list_for_each_entry_safe_reverse(oreq, n, &ireq->obj_requests, links) + /* * a single io request */ @@ -1031,6 +1092,62 @@ out_err: return NULL; } +static void rbd_obj_request_get(struct rbd_obj_request *obj_request) +{ + kref_get(&obj_request->kref); +} + +static void rbd_obj_request_destroy(struct kref *kref); +static void rbd_obj_request_put(struct rbd_obj_request *obj_request) +{ + rbd_assert(obj_request != NULL); + kref_put(&obj_request->kref, rbd_obj_request_destroy); +} + +static void rbd_img_request_get(struct rbd_img_request *img_request) +{ + kref_get(&img_request->kref); +} + +static void rbd_img_request_destroy(struct kref *kref); +static void rbd_img_request_put(struct rbd_img_request *img_request) +{ + rbd_assert(img_request != NULL); + kref_put(&img_request->kref, rbd_img_request_destroy); +} + +static inline void rbd_img_obj_request_add(struct rbd_img_request *img_request, + struct rbd_obj_request *obj_request) +{ + rbd_obj_request_get(obj_request); + obj_request->img_request = img_request; + list_add_tail(&obj_request->links, &img_request->obj_requests); + obj_request->which = img_request->obj_request_count++; + rbd_assert(obj_request->which != BAD_WHICH); +} + +static inline void rbd_img_obj_request_del(struct rbd_img_request *img_request, + struct rbd_obj_request *obj_request) +{ + rbd_assert(obj_request->which != BAD_WHICH); + obj_request->which = BAD_WHICH; + list_del(&obj_request->links); + rbd_assert(obj_request->img_request == img_request); + obj_request->callback = NULL; + obj_request->img_request = NULL; + rbd_obj_request_put(obj_request); +} + +static bool obj_request_type_valid(enum obj_request_type type) +{ + switch (type) { + case OBJ_REQUEST_BIO: + return true; + default: + return false; + } +} + struct ceph_osd_req_op *rbd_osd_req_op_create(u16 opcode, ...) { struct ceph_osd_req_op *op; @@ -1395,6 +1512,26 @@ done: return ret; } +static int rbd_obj_request_submit(struct ceph_osd_client *osdc, + struct rbd_obj_request *obj_request) +{ + return ceph_osdc_start_request(osdc, obj_request->osd_req, false); +} + +static void rbd_img_request_complete(struct rbd_img_request *img_request) +{ + if (img_request->callback) + img_request->callback(img_request); + else + rbd_img_request_put(img_request); +} + +static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) +{ + if (obj_request->callback) + obj_request->callback(obj_request); +} + /* * Request sync osd read */ @@ -1618,6 +1755,486 @@ static int rbd_dev_do_request(struct request *rq, return 0; } +static void rbd_osd_read_callback(struct rbd_obj_request *obj_request, + struct ceph_osd_op *op) +{ + u64 xferred; + + /* + * We support a 64-bit length, but ultimately it has to be + * passed to blk_end_request(), which takes an unsigned int. + */ + xferred = le64_to_cpu(op->extent.length); + rbd_assert(xferred < (u64) UINT_MAX); + if (obj_request->result == (s32) -ENOENT) { + zero_bio_chain(obj_request->bio_list, 0); + obj_request->result = 0; + } else if (xferred < obj_request->length && !obj_request->result) { + zero_bio_chain(obj_request->bio_list, xferred); + xferred = obj_request->length; + } + obj_request->xferred = xferred; + atomic_set(&obj_request->done, 1); +} + +static void rbd_osd_write_callback(struct rbd_obj_request *obj_request, + struct ceph_osd_op *op) +{ + obj_request->xferred = le64_to_cpu(op->extent.length); + atomic_set(&obj_request->done, 1); +} + +static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, + struct ceph_msg *msg) +{ + struct rbd_obj_request *obj_request = osd_req->r_priv; + struct ceph_osd_reply_head *reply_head; + struct ceph_osd_op *op; + u32 num_ops; + u16 opcode; + + rbd_assert(osd_req == obj_request->osd_req); + rbd_assert(!!obj_request->img_request ^ + (obj_request->which == BAD_WHICH)); + + obj_request->xferred = le32_to_cpu(msg->hdr.data_len); + reply_head = msg->front.iov_base; + obj_request->result = (s32) le32_to_cpu(reply_head->result); + obj_request->version = le64_to_cpu(osd_req->r_reassert_version.version); + + num_ops = le32_to_cpu(reply_head->num_ops); + WARN_ON(num_ops != 1); /* For now */ + + op = &reply_head->ops[0]; + opcode = le16_to_cpu(op->op); + switch (opcode) { + case CEPH_OSD_OP_READ: + rbd_osd_read_callback(obj_request, op); + break; + case CEPH_OSD_OP_WRITE: + rbd_osd_write_callback(obj_request, op); + break; + default: + rbd_warn(NULL, "%s: unsupported op %hu\n", + obj_request->object_name, (unsigned short) opcode); + break; + } + + if (atomic_read(&obj_request->done)) + rbd_obj_request_complete(obj_request); +} + +static struct ceph_osd_request *rbd_osd_req_create( + struct rbd_device *rbd_dev, + bool write_request, + struct rbd_obj_request *obj_request, + struct ceph_osd_req_op *op) +{ + struct rbd_img_request *img_request = obj_request->img_request; + struct ceph_snap_context *snapc = NULL; + struct ceph_osd_client *osdc; + struct ceph_osd_request *osd_req; + struct timespec now; + struct timespec *mtime; + u64 snap_id = CEPH_NOSNAP; + u64 offset = obj_request->offset; + u64 length = obj_request->length; + + if (img_request) { + rbd_assert(img_request->write_request == write_request); + if (img_request->write_request) + snapc = img_request->snapc; + else + snap_id = img_request->snap_id; + } + + /* Allocate and initialize the request, for the single op */ + + osdc = &rbd_dev->rbd_client->client->osdc; + osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_ATOMIC); + if (!osd_req) + return NULL; /* ENOMEM */ + + rbd_assert(obj_request_type_valid(obj_request->type)); + switch (obj_request->type) { + case OBJ_REQUEST_BIO: + rbd_assert(obj_request->bio_list != NULL); + osd_req->r_bio = obj_request->bio_list; + bio_get(osd_req->r_bio); + /* osd client requires "num pages" even for bio */ + osd_req->r_num_pages = calc_pages_for(offset, length); + break; + } + + if (write_request) { + osd_req->r_flags = CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK; + now = CURRENT_TIME; + mtime = &now; + } else { + osd_req->r_flags = CEPH_OSD_FLAG_READ; + mtime = NULL; /* not needed for reads */ + offset = 0; /* These are not used... */ + length = 0; /* ...for osd read requests */ + } + + osd_req->r_callback = rbd_osd_req_callback; + osd_req->r_priv = obj_request; + + osd_req->r_oid_len = strlen(obj_request->object_name); + rbd_assert(osd_req->r_oid_len < sizeof (osd_req->r_oid)); + memcpy(osd_req->r_oid, obj_request->object_name, osd_req->r_oid_len); + + osd_req->r_file_layout = rbd_dev->layout; /* struct */ + + /* osd_req will get its own reference to snapc (if non-null) */ + + ceph_osdc_build_request(osd_req, offset, length, 1, op, + snapc, snap_id, mtime); + + return osd_req; +} + +static void rbd_osd_req_destroy(struct ceph_osd_request *osd_req) +{ + ceph_osdc_put_request(osd_req); +} + +/* object_name is assumed to be a non-null pointer and NUL-terminated */ + +static struct rbd_obj_request *rbd_obj_request_create(const char *object_name, + u64 offset, u64 length, + enum obj_request_type type) +{ + struct rbd_obj_request *obj_request; + size_t size; + char *name; + + rbd_assert(obj_request_type_valid(type)); + + size = strlen(object_name) + 1; + obj_request = kzalloc(sizeof (*obj_request) + size, GFP_KERNEL); + if (!obj_request) + return NULL; + + name = (char *)(obj_request + 1); + obj_request->object_name = memcpy(name, object_name, size); + obj_request->offset = offset; + obj_request->length = length; + obj_request->which = BAD_WHICH; + obj_request->type = type; + INIT_LIST_HEAD(&obj_request->links); + atomic_set(&obj_request->done, 0); + kref_init(&obj_request->kref); + + return obj_request; +} + +static void rbd_obj_request_destroy(struct kref *kref) +{ + struct rbd_obj_request *obj_request; + + obj_request = container_of(kref, struct rbd_obj_request, kref); + + rbd_assert(obj_request->img_request == NULL); + rbd_assert(obj_request->which == BAD_WHICH); + + if (obj_request->osd_req) + rbd_osd_req_destroy(obj_request->osd_req); + + rbd_assert(obj_request_type_valid(obj_request->type)); + switch (obj_request->type) { + case OBJ_REQUEST_BIO: + if (obj_request->bio_list) + bio_chain_put(obj_request->bio_list); + break; + } + + kfree(obj_request); +} + +/* + * Caller is responsible for filling in the list of object requests + * that comprises the image request, and the Linux request pointer + * (if there is one). + */ +struct rbd_img_request *rbd_img_request_create(struct rbd_device *rbd_dev, + u64 offset, u64 length, + bool write_request) +{ + struct rbd_img_request *img_request; + struct ceph_snap_context *snapc = NULL; + + img_request = kmalloc(sizeof (*img_request), GFP_ATOMIC); + if (!img_request) + return NULL; + + if (write_request) { + down_read(&rbd_dev->header_rwsem); + snapc = ceph_get_snap_context(rbd_dev->header.snapc); + up_read(&rbd_dev->header_rwsem); + if (WARN_ON(!snapc)) { + kfree(img_request); + return NULL; /* Shouldn't happen */ + } + } + + img_request->rq = NULL; + img_request->rbd_dev = rbd_dev; + img_request->offset = offset; + img_request->length = length; + img_request->write_request = write_request; + if (write_request) + img_request->snapc = snapc; + else + img_request->snap_id = rbd_dev->spec->snap_id; + spin_lock_init(&img_request->completion_lock); + img_request->next_completion = 0; + img_request->callback = NULL; + img_request->obj_request_count = 0; + INIT_LIST_HEAD(&img_request->obj_requests); + kref_init(&img_request->kref); + + rbd_img_request_get(img_request); /* Avoid a warning */ + rbd_img_request_put(img_request); /* TEMPORARY */ + + return img_request; +} + +static void rbd_img_request_destroy(struct kref *kref) +{ + struct rbd_img_request *img_request; + struct rbd_obj_request *obj_request; + struct rbd_obj_request *next_obj_request; + + img_request = container_of(kref, struct rbd_img_request, kref); + + for_each_obj_request_safe(img_request, obj_request, next_obj_request) + rbd_img_obj_request_del(img_request, obj_request); + + if (img_request->write_request) + ceph_put_snap_context(img_request->snapc); + + kfree(img_request); +} + +static int rbd_img_request_fill_bio(struct rbd_img_request *img_request, + struct bio *bio_list) +{ + struct rbd_device *rbd_dev = img_request->rbd_dev; + struct rbd_obj_request *obj_request = NULL; + struct rbd_obj_request *next_obj_request; + unsigned int bio_offset; + u64 image_offset; + u64 resid; + u16 opcode; + + opcode = img_request->write_request ? CEPH_OSD_OP_WRITE + : CEPH_OSD_OP_READ; + bio_offset = 0; + image_offset = img_request->offset; + rbd_assert(image_offset == bio_list->bi_sector << SECTOR_SHIFT); + resid = img_request->length; + while (resid) { + const char *object_name; + unsigned int clone_size; + struct ceph_osd_req_op *op; + u64 offset; + u64 length; + + object_name = rbd_segment_name(rbd_dev, image_offset); + if (!object_name) + goto out_unwind; + offset = rbd_segment_offset(rbd_dev, image_offset); + length = rbd_segment_length(rbd_dev, image_offset, resid); + obj_request = rbd_obj_request_create(object_name, + offset, length, + OBJ_REQUEST_BIO); + kfree(object_name); /* object request has its own copy */ + if (!obj_request) + goto out_unwind; + + rbd_assert(length <= (u64) UINT_MAX); + clone_size = (unsigned int) length; + obj_request->bio_list = bio_chain_clone_range(&bio_list, + &bio_offset, clone_size, + GFP_ATOMIC); + if (!obj_request->bio_list) + goto out_partial; + + /* + * Build up the op to use in building the osd + * request. Note that the contents of the op are + * copied by rbd_osd_req_create(). + */ + op = rbd_osd_req_op_create(opcode, offset, length); + if (!op) + goto out_partial; + obj_request->osd_req = rbd_osd_req_create(rbd_dev, + img_request->write_request, + obj_request, op); + rbd_osd_req_op_destroy(op); + if (!obj_request->osd_req) + goto out_partial; + /* status and version are initially zero-filled */ + + rbd_img_obj_request_add(img_request, obj_request); + + image_offset += length; + resid -= length; + } + + return 0; + +out_partial: + rbd_obj_request_put(obj_request); +out_unwind: + for_each_obj_request_safe(img_request, obj_request, next_obj_request) + rbd_obj_request_put(obj_request); + + return -ENOMEM; +} + +static void rbd_img_obj_callback(struct rbd_obj_request *obj_request) +{ + struct rbd_img_request *img_request; + u32 which = obj_request->which; + bool more = true; + + img_request = obj_request->img_request; + rbd_assert(img_request != NULL); + rbd_assert(img_request->rq != NULL); + rbd_assert(which != BAD_WHICH); + rbd_assert(which < img_request->obj_request_count); + rbd_assert(which >= img_request->next_completion); + + spin_lock_irq(&img_request->completion_lock); + if (which != img_request->next_completion) + goto out; + + for_each_obj_request_from(img_request, obj_request) { + unsigned int xferred; + int result; + + rbd_assert(more); + rbd_assert(which < img_request->obj_request_count); + + if (!atomic_read(&obj_request->done)) + break; + + rbd_assert(obj_request->xferred <= (u64) UINT_MAX); + xferred = (unsigned int) obj_request->xferred; + result = (int) obj_request->result; + if (result) + rbd_warn(NULL, "obj_request %s result %d xferred %u\n", + img_request->write_request ? "write" : "read", + result, xferred); + + more = blk_end_request(img_request->rq, result, xferred); + which++; + } + rbd_assert(more ^ (which == img_request->obj_request_count)); + img_request->next_completion = which; +out: + spin_unlock_irq(&img_request->completion_lock); + + if (!more) + rbd_img_request_complete(img_request); +} + +static int rbd_img_request_submit(struct rbd_img_request *img_request) +{ + struct rbd_device *rbd_dev = img_request->rbd_dev; + struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; + struct rbd_obj_request *obj_request; + + for_each_obj_request(img_request, obj_request) { + int ret; + + obj_request->callback = rbd_img_obj_callback; + ret = rbd_obj_request_submit(osdc, obj_request); + if (ret) + return ret; + /* + * The image request has its own reference to each + * of its object requests, so we can safely drop the + * initial one here. + */ + rbd_obj_request_put(obj_request); + } + + return 0; +} + +static void rbd_request_fn(struct request_queue *q) +{ + struct rbd_device *rbd_dev = q->queuedata; + bool read_only = rbd_dev->mapping.read_only; + struct request *rq; + int result; + + while ((rq = blk_fetch_request(q))) { + bool write_request = rq_data_dir(rq) == WRITE; + struct rbd_img_request *img_request; + u64 offset; + u64 length; + + /* Ignore any non-FS requests that filter through. */ + + if (rq->cmd_type != REQ_TYPE_FS) { + __blk_end_request_all(rq, 0); + continue; + } + + spin_unlock_irq(q->queue_lock); + + /* Disallow writes to a read-only device */ + + if (write_request) { + result = -EROFS; + if (read_only) + goto end_request; + rbd_assert(rbd_dev->spec->snap_id == CEPH_NOSNAP); + } + + /* Quit early if the snapshot has disappeared */ + + if (!atomic_read(&rbd_dev->exists)) { + dout("request for non-existent snapshot"); + rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); + result = -ENXIO; + goto end_request; + } + + offset = (u64) blk_rq_pos(rq) << SECTOR_SHIFT; + length = (u64) blk_rq_bytes(rq); + + result = -EINVAL; + if (WARN_ON(offset && length > U64_MAX - offset + 1)) + goto end_request; /* Shouldn't happen */ + + result = -ENOMEM; + img_request = rbd_img_request_create(rbd_dev, offset, length, + write_request); + if (!img_request) + goto end_request; + + img_request->rq = rq; + + result = rbd_img_request_fill_bio(img_request, rq->bio); + if (!result) + result = rbd_img_request_submit(img_request); + if (result) + rbd_img_request_put(img_request); +end_request: + spin_lock_irq(q->queue_lock); + if (result < 0) { + rbd_warn(rbd_dev, "obj_request %s result %d\n", + write_request ? "write" : "read", result); + __blk_end_request_all(rq, result); + } + } +} + /* * block device queue callback */ @@ -1929,8 +2546,8 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) disk->fops = &rbd_bd_ops; disk->private_data = rbd_dev; - /* init rq */ - q = blk_init_queue(rbd_rq_fn, &rbd_dev->lock); + (void) rbd_rq_fn; /* avoid a warning */ + q = blk_init_queue(rbd_request_fn, &rbd_dev->lock); if (!q) goto out_disk; From 2250a71b591728092db9adcc51629401deb2f9f8 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 30 Nov 2012 17:53:04 -0600 Subject: [PATCH 2642/4607] rbd: kill rbd_rq_fn() and all other related code Now that the request function has been replaced by one using the new request management data structures the old one can go away. Deleting it makes rbd_dev_do_request() no longer needed, and deleting that makes other functions unneeded, and so on. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 319 -------------------------------------------- 1 file changed, 319 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index daa0f18f7089..4c175a7d3f7e 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -621,18 +621,6 @@ static void rbd_put_client(struct rbd_client *rbdc) kref_put(&rbdc->kref, rbd_client_release); } -/* - * Destroy requests collection - */ -static void rbd_coll_release(struct kref *kref) -{ - struct rbd_req_coll *coll = - container_of(kref, struct rbd_req_coll, kref); - - dout("rbd_coll_release %p\n", coll); - kfree(coll); -} - static bool rbd_image_format_valid(u32 image_format) { return image_format == 1 || image_format == 2; @@ -876,28 +864,6 @@ static u64 rbd_segment_length(struct rbd_device *rbd_dev, return length; } -static int rbd_get_num_segments(struct rbd_image_header *header, - u64 ofs, u64 len) -{ - u64 start_seg; - u64 end_seg; - u64 result; - - if (!len) - return 0; - if (len - 1 > U64_MAX - ofs) - return -ERANGE; - - start_seg = ofs >> header->obj_order; - end_seg = (ofs + len - 1) >> header->obj_order; - - result = end_seg - start_seg + 1; - if (result > (u64) INT_MAX) - return -ERANGE; - - return (int) result; -} - /* * returns the size of an object in the image */ @@ -1216,52 +1182,6 @@ static void rbd_osd_req_op_destroy(struct ceph_osd_req_op *op) kfree(op); } -static void rbd_coll_end_req_index(struct request *rq, - struct rbd_req_coll *coll, - int index, - s32 ret, u64 len) -{ - struct request_queue *q; - int min, max, i; - - dout("rbd_coll_end_req_index %p index %d ret %d len %llu\n", - coll, index, (int)ret, (unsigned long long)len); - - if (!rq) - return; - - if (!coll) { - blk_end_request(rq, ret, len); - return; - } - - q = rq->q; - - spin_lock_irq(q->queue_lock); - coll->status[index].done = 1; - coll->status[index].rc = ret; - coll->status[index].bytes = len; - max = min = coll->num_done; - while (max < coll->total && coll->status[max].done) - max++; - - for (i = min; istatus[i].rc, - coll->status[i].bytes); - coll->num_done++; - kref_put(&coll->kref, rbd_coll_release); - } - spin_unlock_irq(q->queue_lock); -} - -static void rbd_coll_end_req(struct rbd_request *rbd_req, - s32 ret, u64 len) -{ - rbd_coll_end_req_index(rbd_req->rq, - rbd_req->coll, rbd_req->coll_index, - ret, len); -} - /* * Send ceph osd request */ @@ -1361,46 +1281,6 @@ done_osd_req: return ret; } -/* - * Ceph osd op callback - */ -static void rbd_req_cb(struct ceph_osd_request *osd_req, struct ceph_msg *msg) -{ - struct rbd_request *rbd_req = osd_req->r_priv; - struct ceph_osd_reply_head *replyhead; - struct ceph_osd_op *op; - s32 rc; - u64 bytes; - int read_op; - - /* parse reply */ - replyhead = msg->front.iov_base; - WARN_ON(le32_to_cpu(replyhead->num_ops) == 0); - op = (void *)(replyhead + 1); - rc = (s32)le32_to_cpu(replyhead->result); - bytes = le64_to_cpu(op->extent.length); - read_op = (le16_to_cpu(op->op) == CEPH_OSD_OP_READ); - - dout("rbd_req_cb bytes=%llu readop=%d rc=%d\n", - (unsigned long long) bytes, read_op, (int) rc); - - if (rc == (s32)-ENOENT && read_op) { - zero_bio_chain(rbd_req->bio, 0); - rc = 0; - } else if (rc == 0 && read_op && bytes < rbd_req->len) { - zero_bio_chain(rbd_req->bio, bytes); - bytes = rbd_req->len; - } - - rbd_coll_end_req(rbd_req, rc, bytes); - - if (rbd_req->bio) - bio_chain_put(rbd_req->bio); - - ceph_osdc_put_request(osd_req); - kfree(rbd_req); -} - static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, struct ceph_msg *msg) { @@ -1448,70 +1328,6 @@ done: return ret; } -/* - * Do an asynchronous ceph osd operation - */ -static int rbd_do_op(struct request *rq, - struct rbd_device *rbd_dev, - struct ceph_snap_context *snapc, - u64 ofs, u64 len, - struct bio *bio, - struct rbd_req_coll *coll, - int coll_index) -{ - const char *seg_name; - u64 seg_ofs; - u64 seg_len; - int ret; - struct ceph_osd_req_op *op; - int opcode; - int flags; - u64 snapid; - - seg_name = rbd_segment_name(rbd_dev, ofs); - if (!seg_name) - return -ENOMEM; - seg_len = rbd_segment_length(rbd_dev, ofs, len); - seg_ofs = rbd_segment_offset(rbd_dev, ofs); - - if (rq_data_dir(rq) == WRITE) { - opcode = CEPH_OSD_OP_WRITE; - flags = CEPH_OSD_FLAG_WRITE|CEPH_OSD_FLAG_ONDISK; - snapid = CEPH_NOSNAP; - } else { - opcode = CEPH_OSD_OP_READ; - flags = CEPH_OSD_FLAG_READ; - rbd_assert(!snapc); - snapid = rbd_dev->spec->snap_id; - } - - ret = -ENOMEM; - op = rbd_osd_req_op_create(opcode, seg_ofs, seg_len); - if (!op) - goto done; - - /* we've taken care of segment sizes earlier when we - cloned the bios. We should never have a segment - truncated at this point */ - rbd_assert(seg_len == len); - - ret = rbd_do_request(rq, rbd_dev, snapc, snapid, - seg_name, seg_ofs, seg_len, - bio, - NULL, 0, - flags, - op, - coll, coll_index, - rbd_req_cb, NULL); - if (ret < 0) - rbd_coll_end_req_index(rq, coll, coll_index, - (s32)ret, seg_len); - rbd_osd_req_op_destroy(op); -done: - kfree(seg_name); - return ret; -} - static int rbd_obj_request_submit(struct ceph_osd_client *osdc, struct rbd_obj_request *obj_request) { @@ -1683,78 +1499,6 @@ static int rbd_req_sync_exec(struct rbd_device *rbd_dev, return ret; } -static struct rbd_req_coll *rbd_alloc_coll(int num_reqs) -{ - struct rbd_req_coll *coll = - kzalloc(sizeof(struct rbd_req_coll) + - sizeof(struct rbd_req_status) * num_reqs, - GFP_ATOMIC); - - if (!coll) - return NULL; - coll->total = num_reqs; - kref_init(&coll->kref); - return coll; -} - -static int rbd_dev_do_request(struct request *rq, - struct rbd_device *rbd_dev, - struct ceph_snap_context *snapc, - u64 ofs, unsigned int size, - struct bio *bio_chain) -{ - int num_segs; - struct rbd_req_coll *coll; - unsigned int bio_offset; - int cur_seg = 0; - - dout("%s 0x%x bytes at 0x%llx\n", - rq_data_dir(rq) == WRITE ? "write" : "read", - size, (unsigned long long) blk_rq_pos(rq) * SECTOR_SIZE); - - num_segs = rbd_get_num_segments(&rbd_dev->header, ofs, size); - if (num_segs <= 0) - return num_segs; - - coll = rbd_alloc_coll(num_segs); - if (!coll) - return -ENOMEM; - - bio_offset = 0; - do { - u64 limit = rbd_segment_length(rbd_dev, ofs, size); - unsigned int clone_size; - struct bio *bio_clone; - - BUG_ON(limit > (u64)UINT_MAX); - clone_size = (unsigned int)limit; - dout("bio_chain->bi_vcnt=%hu\n", bio_chain->bi_vcnt); - - kref_get(&coll->kref); - - /* Pass a cloned bio chain via an osd request */ - - bio_clone = bio_chain_clone_range(&bio_chain, - &bio_offset, clone_size, - GFP_ATOMIC); - if (bio_clone) - (void)rbd_do_op(rq, rbd_dev, snapc, - ofs, clone_size, - bio_clone, coll, cur_seg); - else - rbd_coll_end_req_index(rq, coll, cur_seg, - (s32)-ENOMEM, - clone_size); - size -= clone_size; - ofs += clone_size; - - cur_seg++; - } while (size > 0); - kref_put(&coll->kref, rbd_coll_release); - - return 0; -} - static void rbd_osd_read_callback(struct rbd_obj_request *obj_request, struct ceph_osd_op *op) { @@ -2235,68 +1979,6 @@ end_request: } } -/* - * block device queue callback - */ -static void rbd_rq_fn(struct request_queue *q) -{ - struct rbd_device *rbd_dev = q->queuedata; - bool read_only = rbd_dev->mapping.read_only; - struct request *rq; - - while ((rq = blk_fetch_request(q))) { - struct ceph_snap_context *snapc = NULL; - unsigned int size = 0; - int result; - - dout("fetched request\n"); - - /* Filter out block requests we don't understand */ - - if ((rq->cmd_type != REQ_TYPE_FS)) { - __blk_end_request_all(rq, 0); - continue; - } - spin_unlock_irq(q->queue_lock); - - /* Write requests need a reference to the snapshot context */ - - if (rq_data_dir(rq) == WRITE) { - result = -EROFS; - if (read_only) /* Can't write to a read-only device */ - goto out_end_request; - - /* - * Note that each osd request will take its - * own reference to the snapshot context - * supplied. The reference we take here - * just guarantees the one we provide stays - * valid. - */ - down_read(&rbd_dev->header_rwsem); - snapc = ceph_get_snap_context(rbd_dev->header.snapc); - up_read(&rbd_dev->header_rwsem); - rbd_assert(snapc != NULL); - } else if (!atomic_read(&rbd_dev->exists)) { - rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); - dout("request for non-existent snapshot"); - result = -ENXIO; - goto out_end_request; - } - - size = blk_rq_bytes(rq); - result = rbd_dev_do_request(rq, rbd_dev, snapc, - blk_rq_pos(rq) * SECTOR_SIZE, - size, rq->bio); -out_end_request: - if (snapc) - ceph_put_snap_context(snapc); - spin_lock_irq(q->queue_lock); - if (!size || result < 0) - __blk_end_request_all(rq, result); - } -} - /* * a queue callback. Makes sure that we don't create a bio that spans across * multiple osd objects. One exception would be with a single page bios, @@ -2546,7 +2228,6 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) disk->fops = &rbd_bd_ops; disk->private_data = rbd_dev; - (void) rbd_rq_fn; /* avoid a warning */ q = blk_init_queue(rbd_request_fn, &rbd_dev->lock); if (!q) goto out_disk; From 7d250b949a33c8a658a2ad4ab390d8394b842224 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 30 Nov 2012 17:53:04 -0600 Subject: [PATCH 2643/4607] rbd: kill rbd_req_coll and rbd_request The two remaining callers of rbd_do_request() always pass a null collection pointer, so the "coll" and "coll_index" parameters are not needed. There is no other use of that data structure, so it can be eliminated. Deleting them means there is no need to allocate a rbd_request structure for the callback function. And since that's the only use of *that* structure, it too can be eliminated. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 58 +++------------------------------------------ 1 file changed, 3 insertions(+), 55 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 4c175a7d3f7e..c1bb649b4ad1 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -162,25 +162,6 @@ struct rbd_client { struct list_head node; }; -/* - * a request completion status - */ -struct rbd_req_status { - int done; - s32 rc; - u64 bytes; -}; - -/* - * a collection of requests - */ -struct rbd_req_coll { - int total; - int num_done; - struct kref kref; - struct rbd_req_status status[0]; -}; - struct rbd_img_request; typedef void (*rbd_img_callback_t)(struct rbd_img_request *); @@ -242,18 +223,6 @@ struct rbd_img_request { #define for_each_obj_request_safe(ireq, oreq, n) \ list_for_each_entry_safe_reverse(oreq, n, &ireq->obj_requests, links) -/* - * a single io request - */ -struct rbd_request { - struct request *rq; /* blk layer request */ - struct bio *bio; /* cloned bio */ - struct page **pages; /* list of used pages */ - u64 len; - int coll_index; - struct rbd_req_coll *coll; -}; - struct rbd_snap { struct device dev; const char *name; @@ -1195,21 +1164,18 @@ static int rbd_do_request(struct request *rq, int num_pages, int flags, struct ceph_osd_req_op *op, - struct rbd_req_coll *coll, - int coll_index, void (*rbd_cb)(struct ceph_osd_request *, struct ceph_msg *), u64 *ver) { struct ceph_osd_client *osdc; struct ceph_osd_request *osd_req; - struct rbd_request *rbd_req = NULL; struct timespec mtime = CURRENT_TIME; int ret; - dout("rbd_do_request object_name=%s ofs=%llu len=%llu coll=%p[%d]\n", + dout("rbd_do_request object_name=%s ofs=%llu len=%llu\n", object_name, (unsigned long long) ofs, - (unsigned long long) len, coll, coll_index); + (unsigned long long) len); osdc = &rbd_dev->rbd_client->client->osdc; osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_NOIO); @@ -1223,22 +1189,8 @@ static int rbd_do_request(struct request *rq, bio_get(osd_req->r_bio); } - if (coll) { - ret = -ENOMEM; - rbd_req = kmalloc(sizeof(*rbd_req), GFP_NOIO); - if (!rbd_req) - goto done_osd_req; - - rbd_req->rq = rq; - rbd_req->bio = bio; - rbd_req->pages = pages; - rbd_req->len = len; - rbd_req->coll = coll; - rbd_req->coll_index = coll_index; - } - osd_req->r_callback = rbd_cb; - osd_req->r_priv = rbd_req; + osd_req->r_priv = NULL; strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); osd_req->r_oid_len = strlen(osd_req->r_oid); @@ -1274,8 +1226,6 @@ static int rbd_do_request(struct request *rq, done_err: if (bio) bio_chain_put(osd_req->r_bio); - kfree(rbd_req); -done_osd_req: ceph_osdc_put_request(osd_req); return ret; @@ -1314,7 +1264,6 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, pages, num_pages, flags, op, - NULL, 0, NULL, ver); if (ret < 0) @@ -1390,7 +1339,6 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, NULL, 0, CEPH_OSD_FLAG_READ, op, - NULL, 0, rbd_simple_req_cb, NULL); rbd_osd_req_op_destroy(op); From 788e2df3b92e30f1fff74139bb53e68ec13fe2a5 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 17 Jan 2013 12:25:27 -0600 Subject: [PATCH 2644/4607] rbd: implement sync object read with new code Reimplement the synchronous read operation used for reading a version 1 header using the new request tracking code. Name the resulting function rbd_obj_read_sync() to better reflect that it's a full object operation, not an object request. To do this, implement a new OBJ_REQUEST_PAGES object request type. This implements a new mechanism to allow the caller to wait for completion for an rbd_obj_request by calling rbd_obj_request_wait(). This partially resolves: http://tracker.newdream.net/issues/3755 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 96 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index c1bb649b4ad1..3f5eaea444a0 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -170,7 +170,7 @@ typedef void (*rbd_img_callback_t)(struct rbd_img_request *); struct rbd_obj_request; typedef void (*rbd_obj_callback_t)(struct rbd_obj_request *); -enum obj_request_type { OBJ_REQUEST_BIO }; /* More types to come */ +enum obj_request_type { OBJ_REQUEST_BIO, OBJ_REQUEST_PAGES }; struct rbd_obj_request { const char *object_name; @@ -182,7 +182,13 @@ struct rbd_obj_request { u32 which; /* posn image request list */ enum obj_request_type type; - struct bio *bio_list; + union { + struct bio *bio_list; + struct { + struct page **pages; + u32 page_count; + }; + }; struct ceph_osd_request *osd_req; @@ -192,6 +198,7 @@ struct rbd_obj_request { atomic_t done; rbd_obj_callback_t callback; + struct completion completion; struct kref kref; }; @@ -1077,6 +1084,7 @@ static bool obj_request_type_valid(enum obj_request_type type) { switch (type) { case OBJ_REQUEST_BIO: + case OBJ_REQUEST_PAGES: return true; default: return false; @@ -1291,14 +1299,23 @@ static void rbd_img_request_complete(struct rbd_img_request *img_request) rbd_img_request_put(img_request); } +/* Caller is responsible for rbd_obj_request_destroy(obj_request) */ + +static int rbd_obj_request_wait(struct rbd_obj_request *obj_request) +{ + return wait_for_completion_interruptible(&obj_request->completion); +} + static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) { if (obj_request->callback) obj_request->callback(obj_request); + else + complete_all(&obj_request->completion); } /* - * Request sync osd read + * Synchronously read a range from an object into a provided buffer */ static int rbd_req_sync_read(struct rbd_device *rbd_dev, const char *object_name, @@ -1556,6 +1573,11 @@ static struct ceph_osd_request *rbd_osd_req_create( /* osd client requires "num pages" even for bio */ osd_req->r_num_pages = calc_pages_for(offset, length); break; + case OBJ_REQUEST_PAGES: + osd_req->r_pages = obj_request->pages; + osd_req->r_num_pages = obj_request->page_count; + osd_req->r_page_alignment = offset & ~PAGE_MASK; + break; } if (write_request) { @@ -1616,6 +1638,7 @@ static struct rbd_obj_request *rbd_obj_request_create(const char *object_name, obj_request->type = type; INIT_LIST_HEAD(&obj_request->links); atomic_set(&obj_request->done, 0); + init_completion(&obj_request->completion); kref_init(&obj_request->kref); return obj_request; @@ -1639,6 +1662,11 @@ static void rbd_obj_request_destroy(struct kref *kref) if (obj_request->bio_list) bio_chain_put(obj_request->bio_list); break; + case OBJ_REQUEST_PAGES: + if (obj_request->pages) + ceph_release_page_vector(obj_request->pages, + obj_request->page_count); + break; } kfree(obj_request); @@ -1987,6 +2015,65 @@ static void rbd_free_disk(struct rbd_device *rbd_dev) put_disk(disk); } +static int rbd_obj_read_sync(struct rbd_device *rbd_dev, + const char *object_name, + u64 offset, u64 length, + char *buf, u64 *version) + +{ + struct ceph_osd_req_op *op; + struct rbd_obj_request *obj_request; + struct ceph_osd_client *osdc; + struct page **pages = NULL; + u32 page_count; + int ret; + + page_count = (u32) calc_pages_for(offset, length); + pages = ceph_alloc_page_vector(page_count, GFP_KERNEL); + if (IS_ERR(pages)) + ret = PTR_ERR(pages); + + ret = -ENOMEM; + obj_request = rbd_obj_request_create(object_name, offset, length, + OBJ_REQUEST_PAGES); + if (!obj_request) + goto out; + + obj_request->pages = pages; + obj_request->page_count = page_count; + + op = rbd_osd_req_op_create(CEPH_OSD_OP_READ, offset, length); + if (!op) + goto out; + obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, + obj_request, op); + rbd_osd_req_op_destroy(op); + if (!obj_request->osd_req) + goto out; + + osdc = &rbd_dev->rbd_client->client->osdc; + ret = rbd_obj_request_submit(osdc, obj_request); + if (ret) + goto out; + ret = rbd_obj_request_wait(obj_request); + if (ret) + goto out; + + ret = obj_request->result; + if (ret < 0) + goto out; + ret = ceph_copy_from_page_vector(pages, buf, 0, obj_request->xferred); + if (version) + *version = obj_request->version; +out: + if (obj_request) + rbd_obj_request_put(obj_request); + else + ceph_release_page_vector(pages, page_count); + + return ret; +} + /* * Read the complete header for the given rbd device. * @@ -2025,7 +2112,8 @@ rbd_dev_v1_header_read(struct rbd_device *rbd_dev, u64 *version) if (!ondisk) return ERR_PTR(-ENOMEM); - ret = rbd_req_sync_read(rbd_dev, rbd_dev->header_name, + (void) rbd_req_sync_read; /* avoid a warning */ + ret = rbd_obj_read_sync(rbd_dev, rbd_dev->header_name, 0, size, (char *) ondisk, version); From 86ea43bfcbeae61858b0fee4971e5b1e894d7174 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sun, 20 Jan 2013 14:44:42 -0600 Subject: [PATCH 2645/4607] rbd: get rid of rbd_req_sync_read() Delete rbd_req_sync_read() is no longer used, so get rid of it. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 3f5eaea444a0..5e7110a50513 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1314,29 +1314,6 @@ static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) complete_all(&obj_request->completion); } -/* - * Synchronously read a range from an object into a provided buffer - */ -static int rbd_req_sync_read(struct rbd_device *rbd_dev, - const char *object_name, - u64 ofs, u64 len, - char *buf, - u64 *ver) -{ - struct ceph_osd_req_op *op; - int ret; - - op = rbd_osd_req_op_create(CEPH_OSD_OP_READ, ofs, len); - if (!op) - return -ENOMEM; - - ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, - op, object_name, ofs, len, buf, ver); - rbd_osd_req_op_destroy(op); - - return ret; -} - /* * Request sync osd watch */ @@ -2112,7 +2089,6 @@ rbd_dev_v1_header_read(struct rbd_device *rbd_dev, u64 *version) if (!ondisk) return ERR_PTR(-ENOMEM); - (void) rbd_req_sync_read; /* avoid a warning */ ret = rbd_obj_read_sync(rbd_dev, rbd_dev->header_name, 0, size, (char *) ondisk, version); From 9969ebc5af93028c21f2614621737f0d6ff6fc06 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 18 Jan 2013 12:31:10 -0600 Subject: [PATCH 2646/4607] rbd: implement watch/unwatch with new code Implement a new function to set up or tear down a watch event for an mapped rbd image header using the new request code. Create a new object request type "nodata" to handle this. And define rbd_osd_trivial_callback() which simply marks a request done. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 89 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 5e7110a50513..60b68512fa93 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -170,7 +170,9 @@ typedef void (*rbd_img_callback_t)(struct rbd_img_request *); struct rbd_obj_request; typedef void (*rbd_obj_callback_t)(struct rbd_obj_request *); -enum obj_request_type { OBJ_REQUEST_BIO, OBJ_REQUEST_PAGES }; +enum obj_request_type { + OBJ_REQUEST_NODATA, OBJ_REQUEST_BIO, OBJ_REQUEST_PAGES +}; struct rbd_obj_request { const char *object_name; @@ -1083,6 +1085,7 @@ static inline void rbd_img_obj_request_del(struct rbd_img_request *img_request, static bool obj_request_type_valid(enum obj_request_type type) { switch (type) { + case OBJ_REQUEST_NODATA: case OBJ_REQUEST_BIO: case OBJ_REQUEST_PAGES: return true; @@ -1306,6 +1309,12 @@ static int rbd_obj_request_wait(struct rbd_obj_request *obj_request) return wait_for_completion_interruptible(&obj_request->completion); } +static void rbd_osd_trivial_callback(struct rbd_obj_request *obj_request, + struct ceph_osd_op *op) +{ + atomic_set(&obj_request->done, 1); +} + static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) { if (obj_request->callback) @@ -1500,6 +1509,9 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, case CEPH_OSD_OP_WRITE: rbd_osd_write_callback(obj_request, op); break; + case CEPH_OSD_OP_WATCH: + rbd_osd_trivial_callback(obj_request, op); + break; default: rbd_warn(NULL, "%s: unsupported op %hu\n", obj_request->object_name, (unsigned short) opcode); @@ -1543,6 +1555,8 @@ static struct ceph_osd_request *rbd_osd_req_create( rbd_assert(obj_request_type_valid(obj_request->type)); switch (obj_request->type) { + case OBJ_REQUEST_NODATA: + break; /* Nothing to do */ case OBJ_REQUEST_BIO: rbd_assert(obj_request->bio_list != NULL); osd_req->r_bio = obj_request->bio_list; @@ -1635,6 +1649,8 @@ static void rbd_obj_request_destroy(struct kref *kref) rbd_assert(obj_request_type_valid(obj_request->type)); switch (obj_request->type) { + case OBJ_REQUEST_NODATA: + break; /* Nothing to do */ case OBJ_REQUEST_BIO: if (obj_request->bio_list) bio_chain_put(obj_request->bio_list); @@ -1862,6 +1878,72 @@ static int rbd_img_request_submit(struct rbd_img_request *img_request) return 0; } +/* + * Request sync osd watch/unwatch. The value of "start" determines + * whether a watch request is being initiated or torn down. + */ +static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, int start) +{ + struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc; + struct rbd_obj_request *obj_request; + struct ceph_osd_req_op *op; + int ret; + + rbd_assert(start ^ !!rbd_dev->watch_event); + rbd_assert(start ^ !!rbd_dev->watch_request); + + if (start) { + ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, rbd_dev, + &rbd_dev->watch_event); + if (ret < 0) + return ret; + } + + ret = -ENOMEM; + obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0, + OBJ_REQUEST_NODATA); + if (!obj_request) + goto out_cancel; + + op = rbd_osd_req_op_create(CEPH_OSD_OP_WATCH, + rbd_dev->watch_event->cookie, + rbd_dev->header.obj_version, start); + if (!op) + goto out_cancel; + obj_request->osd_req = rbd_osd_req_create(rbd_dev, true, + obj_request, op); + rbd_osd_req_op_destroy(op); + if (!obj_request->osd_req) + goto out_cancel; + + if (start) { + rbd_dev->watch_request = obj_request->osd_req; + ceph_osdc_set_request_linger(osdc, rbd_dev->watch_request); + } + ret = rbd_obj_request_submit(osdc, obj_request); + if (ret) + goto out_cancel; + ret = rbd_obj_request_wait(obj_request); + if (ret) + goto out_cancel; + + ret = obj_request->result; + if (ret) + goto out_cancel; + + if (start) + goto done; /* Done if setting up the watch request */ +out_cancel: + /* Cancel the event if we're tearing down, or on error */ + ceph_osdc_cancel_event(rbd_dev->watch_event); + rbd_dev->watch_event = NULL; +done: + if (obj_request) + rbd_obj_request_put(obj_request); + + return ret; +} + static void rbd_request_fn(struct request_queue *q) { struct rbd_device *rbd_dev = q->queuedata; @@ -3879,7 +3961,8 @@ static int rbd_dev_probe_finish(struct rbd_device *rbd_dev) if (ret) goto err_out_bus; - ret = rbd_req_sync_watch(rbd_dev, 1); + (void) rbd_req_sync_watch; /* avoid a warning */ + ret = rbd_dev_header_watch_sync(rbd_dev, 1); if (ret) goto err_out_bus; @@ -4042,7 +4125,7 @@ static void rbd_dev_release(struct device *dev) rbd_dev->watch_request); } if (rbd_dev->watch_event) - rbd_req_sync_watch(rbd_dev, 0); + rbd_dev_header_watch_sync(rbd_dev, 0); /* clean up and free blkdev */ rbd_free_disk(rbd_dev); From ecf7a0318b1b026fb147623c36324fa8c73447a9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sun, 20 Jan 2013 14:44:42 -0600 Subject: [PATCH 2647/4607] rbd: get rid of rbd_req_sync_watch() Get rid of rbd_req_sync_watch(), because it is no longer used. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 60b68512fa93..a6a85271380a 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1369,47 +1369,6 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) rbd_req_sync_notify_ack(rbd_dev, hver, notify_id); } -/* - * Request sync osd watch/unwatch. The value of "start" determines - * whether a watch request is being initiated or torn down. - */ -static int rbd_req_sync_watch(struct rbd_device *rbd_dev, int start) -{ - struct ceph_osd_req_op *op; - int ret = 0; - - rbd_assert(start ^ !!rbd_dev->watch_event); - rbd_assert(start ^ !!rbd_dev->watch_request); - - if (start) { - struct ceph_osd_client *osdc; - - osdc = &rbd_dev->rbd_client->client->osdc; - ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, rbd_dev, - &rbd_dev->watch_event); - if (ret < 0) - return ret; - } - - op = rbd_osd_req_op_create(CEPH_OSD_OP_WATCH, - rbd_dev->watch_event->cookie, - rbd_dev->header.obj_version, start); - if (op) - ret = rbd_req_sync_op(rbd_dev, - CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, - op, rbd_dev->header_name, - 0, 0, NULL, NULL); - - /* Cancel the event if we're tearing down, or on error */ - - if (!start || !op || ret < 0) { - ceph_osdc_cancel_event(rbd_dev->watch_event); - rbd_dev->watch_event = NULL; - } - rbd_osd_req_op_destroy(op); - - return ret; -} /* * Synchronous osd object method call @@ -3961,7 +3920,6 @@ static int rbd_dev_probe_finish(struct rbd_device *rbd_dev) if (ret) goto err_out_bus; - (void) rbd_req_sync_watch; /* avoid a warning */ ret = rbd_dev_header_watch_sync(rbd_dev, 1); if (ret) goto err_out_bus; From b8d70035b35dc12135d5835b659b229bcd6d4f94 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 30 Nov 2012 17:53:04 -0600 Subject: [PATCH 2648/4607] rbd: use new code for notify ack Use the new object request tracking mechanism for handling a notify_ack request. Move the callback function below the definition of this so we don't have to do a pre-declaration. This resolves: http://tracker.newdream.net/issues/3754 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 76 ++++++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index a6a85271380a..1428795571c9 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1349,27 +1349,6 @@ static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, return ret; } -static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) -{ - struct rbd_device *rbd_dev = (struct rbd_device *)data; - u64 hver; - int rc; - - if (!rbd_dev) - return; - - dout("rbd_watch_cb %s notify_id=%llu opcode=%u\n", - rbd_dev->header_name, (unsigned long long) notify_id, - (unsigned int) opcode); - rc = rbd_dev_refresh(rbd_dev, &hver); - if (rc) - rbd_warn(rbd_dev, "got notification but failed to " - " update snaps: %d\n", rc); - - rbd_req_sync_notify_ack(rbd_dev, hver, notify_id); -} - - /* * Synchronous osd object method call */ @@ -1468,6 +1447,7 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, case CEPH_OSD_OP_WRITE: rbd_osd_write_callback(obj_request, op); break; + case CEPH_OSD_OP_NOTIFY_ACK: case CEPH_OSD_OP_WATCH: rbd_osd_trivial_callback(obj_request, op); break; @@ -1837,6 +1817,60 @@ static int rbd_img_request_submit(struct rbd_img_request *img_request) return 0; } +static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, + u64 ver, u64 notify_id) +{ + struct rbd_obj_request *obj_request; + struct ceph_osd_req_op *op; + struct ceph_osd_client *osdc; + int ret; + + obj_request = rbd_obj_request_create(rbd_dev->header_name, 0, 0, + OBJ_REQUEST_NODATA); + if (!obj_request) + return -ENOMEM; + + ret = -ENOMEM; + op = rbd_osd_req_op_create(CEPH_OSD_OP_NOTIFY_ACK, notify_id, ver); + if (!op) + goto out; + obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, + obj_request, op); + rbd_osd_req_op_destroy(op); + if (!obj_request->osd_req) + goto out; + + osdc = &rbd_dev->rbd_client->client->osdc; + ret = rbd_obj_request_submit(osdc, obj_request); + if (!ret) + ret = rbd_obj_request_wait(obj_request); +out: + rbd_obj_request_put(obj_request); + + return ret; +} + +static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) +{ + struct rbd_device *rbd_dev = (struct rbd_device *)data; + u64 hver; + int rc; + + if (!rbd_dev) + return; + + dout("rbd_watch_cb %s notify_id=%llu opcode=%u\n", + rbd_dev->header_name, (unsigned long long) notify_id, + (unsigned int) opcode); + rc = rbd_dev_refresh(rbd_dev, &hver); + if (rc) + rbd_warn(rbd_dev, "got notification but failed to " + " update snaps: %d\n", rc); + + (void) rbd_req_sync_notify_ack; /* avoid a warning */ + rbd_obj_notify_ack_sync(rbd_dev, hver, notify_id); +} + /* * Request sync osd watch/unwatch. The value of "start" determines * whether a watch request is being initiated or torn down. From 5ae9db81b45c2d95554c665043afffd5e9a7d5ac Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sun, 20 Jan 2013 14:44:42 -0600 Subject: [PATCH 2649/4607] rbd: get rid of rbd_req_sync_notify_ack() Get rid rbd_req_sync_notify_ack() because it is no longer used. As a result rbd_simple_req_cb() becomes unreferenced, so get rid of that too. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 1428795571c9..7a6694d08874 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1242,12 +1242,6 @@ done_err: return ret; } -static void rbd_simple_req_cb(struct ceph_osd_request *osd_req, - struct ceph_msg *msg) -{ - ceph_osdc_put_request(osd_req); -} - /* * Do a synchronous ceph osd operation */ @@ -1323,32 +1317,6 @@ static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) complete_all(&obj_request->completion); } -/* - * Request sync osd watch - */ -static int rbd_req_sync_notify_ack(struct rbd_device *rbd_dev, - u64 ver, - u64 notify_id) -{ - struct ceph_osd_req_op *op; - int ret; - - op = rbd_osd_req_op_create(CEPH_OSD_OP_NOTIFY_ACK, notify_id, ver); - if (!op) - return -ENOMEM; - - ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, - rbd_dev->header_name, 0, 0, NULL, - NULL, 0, - CEPH_OSD_FLAG_READ, - op, - rbd_simple_req_cb, NULL); - - rbd_osd_req_op_destroy(op); - - return ret; -} - /* * Synchronous osd object method call */ @@ -1867,7 +1835,6 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) rbd_warn(rbd_dev, "got notification but failed to " " update snaps: %d\n", rc); - (void) rbd_req_sync_notify_ack; /* avoid a warning */ rbd_obj_notify_ack_sync(rbd_dev, hver, notify_id); } From cf81b60e4bbd4a1281fe2640f9c0c40fe3a85fdf Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 17 Jan 2013 12:18:46 -0600 Subject: [PATCH 2650/4607] rbd: send notify ack asynchronously When we receive notification of a change to an rbd image's header object we need to refresh our information about the image (its size and snapshot context). Once we have refreshed our rbd image we need to acknowledge the notification. This acknowledgement was previously done synchronously, but there's really no need to wait for it to complete. Change it so the caller doesn't wait for the notify acknowledgement request to complete. And change the name to reflect it's no longer synchronous. This resolves: http://tracker.newdream.net/issues/3877 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 7a6694d08874..76917cc3e5a1 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1785,7 +1785,7 @@ static int rbd_img_request_submit(struct rbd_img_request *img_request) return 0; } -static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, +static int rbd_obj_notify_ack(struct rbd_device *rbd_dev, u64 ver, u64 notify_id) { struct rbd_obj_request *obj_request; @@ -1809,11 +1809,11 @@ static int rbd_obj_notify_ack_sync(struct rbd_device *rbd_dev, goto out; osdc = &rbd_dev->rbd_client->client->osdc; + obj_request->callback = rbd_obj_request_put; ret = rbd_obj_request_submit(osdc, obj_request); - if (!ret) - ret = rbd_obj_request_wait(obj_request); out: - rbd_obj_request_put(obj_request); + if (ret) + rbd_obj_request_put(obj_request); return ret; } @@ -1835,7 +1835,7 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) rbd_warn(rbd_dev, "got notification but failed to " " update snaps: %d\n", rc); - rbd_obj_notify_ack_sync(rbd_dev, hver, notify_id); + rbd_obj_notify_ack(rbd_dev, hver, notify_id); } /* From 36be9a761844e186f629f463b665945df4f67766 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sat, 19 Jan 2013 00:30:28 -0600 Subject: [PATCH 2651/4607] rbd: implement sync method with new code Reimplement synchronous object method calls using the new request tracking code. Use the name rbd_obj_method_sync() Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 114 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 96 insertions(+), 18 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 76917cc3e5a1..d10649f2e346 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1415,6 +1415,7 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, case CEPH_OSD_OP_WRITE: rbd_osd_write_callback(obj_request, op); break; + case CEPH_OSD_OP_CALL: case CEPH_OSD_OP_NOTIFY_ACK: case CEPH_OSD_OP_WATCH: rbd_osd_trivial_callback(obj_request, op); @@ -1904,6 +1905,82 @@ done: return ret; } +/* + * Synchronous osd object method call + */ +static int rbd_obj_method_sync(struct rbd_device *rbd_dev, + const char *object_name, + const char *class_name, + const char *method_name, + const char *outbound, + size_t outbound_size, + char *inbound, + size_t inbound_size, + u64 *version) +{ + struct rbd_obj_request *obj_request; + struct ceph_osd_client *osdc; + struct ceph_osd_req_op *op; + struct page **pages; + u32 page_count; + int ret; + + /* + * Method calls are ultimately read operations but they + * don't involve object data (so no offset or length). + * The result should placed into the inbound buffer + * provided. They also supply outbound data--parameters for + * the object method. Currently if this is present it will + * be a snapshot id. + */ + page_count = (u32) calc_pages_for(0, inbound_size); + pages = ceph_alloc_page_vector(page_count, GFP_KERNEL); + if (IS_ERR(pages)) + return PTR_ERR(pages); + + ret = -ENOMEM; + obj_request = rbd_obj_request_create(object_name, 0, 0, + OBJ_REQUEST_PAGES); + if (!obj_request) + goto out; + + obj_request->pages = pages; + obj_request->page_count = page_count; + + op = rbd_osd_req_op_create(CEPH_OSD_OP_CALL, class_name, + method_name, outbound, outbound_size); + if (!op) + goto out; + obj_request->osd_req = rbd_osd_req_create(rbd_dev, false, + obj_request, op); + rbd_osd_req_op_destroy(op); + if (!obj_request->osd_req) + goto out; + + osdc = &rbd_dev->rbd_client->client->osdc; + ret = rbd_obj_request_submit(osdc, obj_request); + if (ret) + goto out; + ret = rbd_obj_request_wait(obj_request); + if (ret) + goto out; + + ret = obj_request->result; + if (ret < 0) + goto out; + ret = ceph_copy_from_page_vector(pages, inbound, 0, + obj_request->xferred); + if (version) + *version = obj_request->version; +out: + if (obj_request) + rbd_obj_request_put(obj_request); + else + ceph_release_page_vector(pages, page_count); + + return ret; +} + static void rbd_request_fn(struct request_queue *q) { struct rbd_device *rbd_dev = q->queuedata; @@ -2054,7 +2131,7 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev, ret = -ENOMEM; obj_request = rbd_obj_request_create(object_name, offset, length, - OBJ_REQUEST_PAGES); + OBJ_REQUEST_PAGES); if (!obj_request) goto out; @@ -2754,11 +2831,12 @@ static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id, __le64 size; } __attribute__ ((packed)) size_buf = { 0 }; - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + (void) rbd_req_sync_exec; /* Avoid a warning */ + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_size", (char *) &snapid, sizeof (snapid), (char *) &size_buf, sizeof (size_buf), NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) return ret; @@ -2789,14 +2867,14 @@ static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev) if (!reply_buf) return -ENOMEM; - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_object_prefix", NULL, 0, reply_buf, RBD_OBJ_PREFIX_LEN_MAX, NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out; - ret = 0; /* rbd_req_sync_exec() can return positive */ + ret = 0; /* rbd_obj_method_sync() can return positive */ p = reply_buf; rbd_dev->header.object_prefix = ceph_extract_encoded_string(&p, @@ -2827,12 +2905,12 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id, u64 incompat; int ret; - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_features", (char *) &snapid, sizeof (snapid), (char *) &features_buf, sizeof (features_buf), NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) return ret; @@ -2883,11 +2961,11 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) } snapid = cpu_to_le64(CEPH_NOSNAP); - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_parent", (char *) &snapid, sizeof (snapid), (char *) reply_buf, size, NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out_err; @@ -2954,7 +3032,7 @@ static char *rbd_dev_image_name(struct rbd_device *rbd_dev) if (!reply_buf) goto out; - ret = rbd_req_sync_exec(rbd_dev, RBD_DIRECTORY, + ret = rbd_obj_method_sync(rbd_dev, RBD_DIRECTORY, "rbd", "dir_get_name", image_id, image_id_size, (char *) reply_buf, size, NULL); @@ -3060,11 +3138,11 @@ static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev, u64 *ver) if (!reply_buf) return -ENOMEM; - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_snapcontext", NULL, 0, reply_buf, size, ver); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -3129,11 +3207,11 @@ static char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev, u32 which) return ERR_PTR(-ENOMEM); snap_id = cpu_to_le64(rbd_dev->header.snapc->snaps[which]); - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, + ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_snapshot_name", (char *) &snap_id, sizeof (snap_id), reply_buf, size, NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out; @@ -3721,14 +3799,14 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) goto out; } - ret = rbd_req_sync_exec(rbd_dev, object_name, + ret = rbd_obj_method_sync(rbd_dev, object_name, "rbd", "get_id", NULL, 0, response, RBD_IMAGE_ID_LEN_MAX, NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) goto out; - ret = 0; /* rbd_req_sync_exec() can return positive */ + ret = 0; /* rbd_obj_method_sync() can return positive */ p = response; rbd_dev->spec->image_id = ceph_extract_encoded_string(&p, From 9f20e02a53b944a54a35b9f0db1243cd64872f7d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Sun, 20 Jan 2013 14:44:42 -0600 Subject: [PATCH 2652/4607] rbd: get rid of rbd_req_sync_exec() Get rid rbd_req_sync_exec() because it is no longer used. That eliminates the last use of rbd_req_sync_op(), so get rid of that too. And finally, that leaves rbd_do_request() unreferenced, so get rid of that. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 160 -------------------------------------------- 1 file changed, 160 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d10649f2e346..4ea89917bb92 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1162,126 +1162,6 @@ static void rbd_osd_req_op_destroy(struct ceph_osd_req_op *op) kfree(op); } -/* - * Send ceph osd request - */ -static int rbd_do_request(struct request *rq, - struct rbd_device *rbd_dev, - struct ceph_snap_context *snapc, - u64 snapid, - const char *object_name, u64 ofs, u64 len, - struct bio *bio, - struct page **pages, - int num_pages, - int flags, - struct ceph_osd_req_op *op, - void (*rbd_cb)(struct ceph_osd_request *, - struct ceph_msg *), - u64 *ver) -{ - struct ceph_osd_client *osdc; - struct ceph_osd_request *osd_req; - struct timespec mtime = CURRENT_TIME; - int ret; - - dout("rbd_do_request object_name=%s ofs=%llu len=%llu\n", - object_name, (unsigned long long) ofs, - (unsigned long long) len); - - osdc = &rbd_dev->rbd_client->client->osdc; - osd_req = ceph_osdc_alloc_request(osdc, snapc, 1, false, GFP_NOIO); - if (!osd_req) - return -ENOMEM; - - osd_req->r_flags = flags; - osd_req->r_pages = pages; - if (bio) { - osd_req->r_bio = bio; - bio_get(osd_req->r_bio); - } - - osd_req->r_callback = rbd_cb; - osd_req->r_priv = NULL; - - strncpy(osd_req->r_oid, object_name, sizeof(osd_req->r_oid)); - osd_req->r_oid_len = strlen(osd_req->r_oid); - - osd_req->r_file_layout = rbd_dev->layout; /* struct */ - osd_req->r_num_pages = calc_pages_for(ofs, len); - osd_req->r_page_alignment = ofs & ~PAGE_MASK; - - ceph_osdc_build_request(osd_req, ofs, len, 1, op, - snapc, snapid, &mtime); - - if (op->op == CEPH_OSD_OP_WATCH && op->watch.flag) { - ceph_osdc_set_request_linger(osdc, osd_req); - rbd_dev->watch_request = osd_req; - } - - ret = ceph_osdc_start_request(osdc, osd_req, false); - if (ret < 0) - goto done_err; - - if (!rbd_cb) { - u64 version; - - ret = ceph_osdc_wait_request(osdc, osd_req); - version = le64_to_cpu(osd_req->r_reassert_version.version); - if (ver) - *ver = version; - dout("reassert_ver=%llu\n", (unsigned long long) version); - ceph_osdc_put_request(osd_req); - } - return ret; - -done_err: - if (bio) - bio_chain_put(osd_req->r_bio); - ceph_osdc_put_request(osd_req); - - return ret; -} - -/* - * Do a synchronous ceph osd operation - */ -static int rbd_req_sync_op(struct rbd_device *rbd_dev, - int flags, - struct ceph_osd_req_op *op, - const char *object_name, - u64 ofs, u64 inbound_size, - char *inbound, - u64 *ver) -{ - int ret; - struct page **pages; - int num_pages; - - rbd_assert(op != NULL); - - num_pages = calc_pages_for(ofs, inbound_size); - pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL); - if (IS_ERR(pages)) - return PTR_ERR(pages); - - ret = rbd_do_request(NULL, rbd_dev, NULL, CEPH_NOSNAP, - object_name, ofs, inbound_size, NULL, - pages, num_pages, - flags, - op, - NULL, - ver); - if (ret < 0) - goto done; - - if ((flags & CEPH_OSD_FLAG_READ) && inbound) - ret = ceph_copy_from_page_vector(pages, inbound, ofs, ret); - -done: - ceph_release_page_vector(pages, num_pages); - return ret; -} - static int rbd_obj_request_submit(struct ceph_osd_client *osdc, struct rbd_obj_request *obj_request) { @@ -1317,45 +1197,6 @@ static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) complete_all(&obj_request->completion); } -/* - * Synchronous osd object method call - */ -static int rbd_req_sync_exec(struct rbd_device *rbd_dev, - const char *object_name, - const char *class_name, - const char *method_name, - const char *outbound, - size_t outbound_size, - char *inbound, - size_t inbound_size, - u64 *ver) -{ - struct ceph_osd_req_op *op; - int ret; - - /* - * Any input parameters required by the method we're calling - * will be sent along with the class and method names as - * part of the message payload. That data and its size are - * supplied via the indata and indata_len fields (named from - * the perspective of the server side) in the OSD request - * operation. - */ - op = rbd_osd_req_op_create(CEPH_OSD_OP_CALL, class_name, - method_name, outbound, outbound_size); - if (!op) - return -ENOMEM; - - ret = rbd_req_sync_op(rbd_dev, CEPH_OSD_FLAG_READ, op, - object_name, 0, inbound_size, inbound, - ver); - - rbd_osd_req_op_destroy(op); - - dout("cls_exec returned %d\n", ret); - return ret; -} - static void rbd_osd_read_callback(struct rbd_obj_request *obj_request, struct ceph_osd_op *op) { @@ -2831,7 +2672,6 @@ static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id, __le64 size; } __attribute__ ((packed)) size_buf = { 0 }; - (void) rbd_req_sync_exec; /* Avoid a warning */ ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name, "rbd", "get_size", (char *) &snapid, sizeof (snapid), From 6977c3f983b0d2b481a65b1fa3e85683fd1318af Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 25 Jan 2013 17:08:55 -0600 Subject: [PATCH 2653/4607] rbd: unregister linger in watch sync routine Move the code that unregisters an rbd device's lingering header object watch request into rbd_dev_header_watch_sync(), so it occurs in the same function that originally sets up that request. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 4ea89917bb92..5593def33ce5 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1721,6 +1721,10 @@ static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, int start) if (start) { rbd_dev->watch_request = obj_request->osd_req; ceph_osdc_set_request_linger(osdc, rbd_dev->watch_request); + } else { + ceph_osdc_unregister_linger_request(osdc, + rbd_dev->watch_request); + rbd_dev->watch_request = NULL; } ret = rbd_obj_request_submit(osdc, obj_request); if (ret) @@ -3995,12 +3999,6 @@ static void rbd_dev_release(struct device *dev) { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - if (rbd_dev->watch_request) { - struct ceph_client *client = rbd_dev->rbd_client->client; - - ceph_osdc_unregister_linger_request(&client->osdc, - rbd_dev->watch_request); - } if (rbd_dev->watch_event) rbd_dev_header_watch_sync(rbd_dev, 0); From 975241afcbba82ab1ddc6ebf8a02246d1e9314fd Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 25 Jan 2013 17:08:55 -0600 Subject: [PATCH 2654/4607] rbd: track object rather than osd request for watch Switch to keeping track of the object request pointer rather than the osd request used to watch the rbd image header object. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 5593def33ce5..fc1a045cee4d 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -272,7 +272,7 @@ struct rbd_device { struct ceph_file_layout layout; struct ceph_osd_event *watch_event; - struct ceph_osd_request *watch_request; + struct rbd_obj_request *watch_request; struct rbd_spec *parent_spec; u64 parent_overlap; @@ -1719,11 +1719,11 @@ static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, int start) goto out_cancel; if (start) { - rbd_dev->watch_request = obj_request->osd_req; - ceph_osdc_set_request_linger(osdc, rbd_dev->watch_request); + ceph_osdc_set_request_linger(osdc, obj_request->osd_req); + rbd_dev->watch_request = obj_request; } else { ceph_osdc_unregister_linger_request(osdc, - rbd_dev->watch_request); + rbd_dev->watch_request->osd_req); rbd_dev->watch_request = NULL; } ret = rbd_obj_request_submit(osdc, obj_request); From 25dcf954c3230946b5f3e18db9f91d7640eff76e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 25 Jan 2013 17:08:55 -0600 Subject: [PATCH 2655/4607] rbd: decrement obj request count when deleting Decrement the obj_request_count value when deleting an object request from its image request's list. Rearrange a few lines in the surrounding code. This resolves: http://tracker.ceph.com/issues/3940 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index fc1a045cee4d..d3d15d06abc0 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1063,22 +1063,29 @@ static void rbd_img_request_put(struct rbd_img_request *img_request) static inline void rbd_img_obj_request_add(struct rbd_img_request *img_request, struct rbd_obj_request *obj_request) { + rbd_assert(obj_request->img_request == NULL); + rbd_obj_request_get(obj_request); obj_request->img_request = img_request; - list_add_tail(&obj_request->links, &img_request->obj_requests); - obj_request->which = img_request->obj_request_count++; + obj_request->which = img_request->obj_request_count; rbd_assert(obj_request->which != BAD_WHICH); + img_request->obj_request_count++; + list_add_tail(&obj_request->links, &img_request->obj_requests); } static inline void rbd_img_obj_request_del(struct rbd_img_request *img_request, struct rbd_obj_request *obj_request) { rbd_assert(obj_request->which != BAD_WHICH); - obj_request->which = BAD_WHICH; + list_del(&obj_request->links); + rbd_assert(img_request->obj_request_count > 0); + img_request->obj_request_count--; + rbd_assert(obj_request->which == img_request->obj_request_count); + obj_request->which = BAD_WHICH; rbd_assert(obj_request->img_request == img_request); - obj_request->callback = NULL; obj_request->img_request = NULL; + obj_request->callback = NULL; rbd_obj_request_put(obj_request); } @@ -1472,6 +1479,7 @@ static void rbd_img_request_destroy(struct kref *kref) for_each_obj_request_safe(img_request, obj_request, next_obj_request) rbd_img_obj_request_del(img_request, obj_request); + rbd_assert(img_request->obj_request_count == 0); if (img_request->write_request) ceph_put_snap_context(img_request->snapc); From 8eb87565306cf40a32f5d0883d008675cd2dd510 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 25 Jan 2013 17:08:55 -0600 Subject: [PATCH 2656/4607] rbd: don't drop watch requests on completion When we register an osd request to linger, it means that request will stay around (under control of the osd client) until we've unregistered it. We do that for an rbd image's header object, and we keep a pointer to the object request associated with it. Keep a reference to the watch object request for as long as it is registered to linger. Drop it again after we've removed the linger registration. This resolves: http://tracker.ceph.com/issues/3937 (Note: this originally came about because the osd client was issuing a callback more than once. But that behavior will be changing soon, documented in tracker issue 3967.) Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index d3d15d06abc0..fd9656b5fdb9 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1707,6 +1707,7 @@ static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, int start) &rbd_dev->watch_event); if (ret < 0) return ret; + rbd_assert(rbd_dev->watch_event != NULL); } ret = -ENOMEM; @@ -1726,32 +1727,43 @@ static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, int start) if (!obj_request->osd_req) goto out_cancel; - if (start) { + if (start) ceph_osdc_set_request_linger(osdc, obj_request->osd_req); - rbd_dev->watch_request = obj_request; - } else { + else ceph_osdc_unregister_linger_request(osdc, rbd_dev->watch_request->osd_req); - rbd_dev->watch_request = NULL; - } ret = rbd_obj_request_submit(osdc, obj_request); if (ret) goto out_cancel; ret = rbd_obj_request_wait(obj_request); if (ret) goto out_cancel; - ret = obj_request->result; if (ret) goto out_cancel; - if (start) - goto done; /* Done if setting up the watch request */ + /* + * A watch request is set to linger, so the underlying osd + * request won't go away until we unregister it. We retain + * a pointer to the object request during that time (in + * rbd_dev->watch_request), so we'll keep a reference to + * it. We'll drop that reference (below) after we've + * unregistered it. + */ + if (start) { + rbd_dev->watch_request = obj_request; + + return 0; + } + + /* We have successfully torn down the watch request */ + + rbd_obj_request_put(rbd_dev->watch_request); + rbd_dev->watch_request = NULL; out_cancel: /* Cancel the event if we're tearing down, or on error */ ceph_osdc_cancel_event(rbd_dev->watch_event); rbd_dev->watch_event = NULL; -done: if (obj_request) rbd_obj_request_put(obj_request); From 6d292906f80170f4647079dd503df18b737750af Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 14 Jan 2013 12:43:31 -0600 Subject: [PATCH 2657/4607] rbd: define flags field, use it for exists flag Define a new rbd device flags field, manipulated using bit operations. Replace the use of the current "exists" flag with a bit in this new "flags" field. Add a little commentary about the "exists" flag, which does not need to be manipulated atomically. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index fd9656b5fdb9..8c90a39c2a91 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -264,7 +264,7 @@ struct rbd_device { spinlock_t lock; /* queue lock */ struct rbd_image_header header; - atomic_t exists; + unsigned long flags; struct rbd_spec *spec; char *header_name; @@ -292,6 +292,12 @@ struct rbd_device { unsigned long open_count; }; +/* Flag bits for rbd_dev->flags */ + +enum rbd_dev_flags { + RBD_DEV_FLAG_EXISTS, /* mapped snapshot has not been deleted */ +}; + static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */ static LIST_HEAD(rbd_dev_list); /* devices */ @@ -782,7 +788,8 @@ static int rbd_dev_set_mapping(struct rbd_device *rbd_dev) goto done; rbd_dev->mapping.read_only = true; } - atomic_set(&rbd_dev->exists, 1); + set_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags); + done: return ret; } @@ -1877,9 +1884,14 @@ static void rbd_request_fn(struct request_queue *q) rbd_assert(rbd_dev->spec->snap_id == CEPH_NOSNAP); } - /* Quit early if the snapshot has disappeared */ - - if (!atomic_read(&rbd_dev->exists)) { + /* + * Quit early if the mapped snapshot no longer + * exists. It's still possible the snapshot will + * have disappeared by the time our request arrives + * at the osd, but there's no sense in sending it if + * we already know. + */ + if (!test_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags)) { dout("request for non-existent snapshot"); rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); result = -ENXIO; @@ -2571,7 +2583,7 @@ struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, return NULL; spin_lock_init(&rbd_dev->lock); - atomic_set(&rbd_dev->exists, 0); + rbd_dev->flags = 0; INIT_LIST_HEAD(&rbd_dev->node); INIT_LIST_HEAD(&rbd_dev->snaps); init_rwsem(&rbd_dev->header_rwsem); @@ -3200,10 +3212,17 @@ static int rbd_dev_snaps_update(struct rbd_device *rbd_dev) if (snap_id == CEPH_NOSNAP || (snap && snap->id > snap_id)) { struct list_head *next = links->next; - /* Existing snapshot not in the new snap context */ - + /* + * A previously-existing snapshot is not in + * the new snap context. + * + * If the now missing snapshot is the one the + * image is mapped to, clear its exists flag + * so we can avoid sending any more requests + * to it. + */ if (rbd_dev->spec->snap_id == snap->id) - atomic_set(&rbd_dev->exists, 0); + clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags); rbd_remove_snap_dev(snap); dout("%ssnap id %llu has been removed\n", rbd_dev->spec->snap_id == snap->id ? From b82d167be64b3e88d9434d8a98ce83c83a07aa48 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Mon, 14 Jan 2013 12:43:31 -0600 Subject: [PATCH 2658/4607] rbd: prevent open for image being removed An open request for a mapped rbd image can arrive while removal of that mapping is underway. We need to prevent such an open request from succeeding. (It appears that Maciej Galkiewicz ran into this problem.) Define and use a "removing" flag to indicate a mapping is getting removed. Set it in the remove path after verifying nothing holds the device open. And check it in the open path before allowing the open to proceed. Acquire the rbd device's lock around each of these spots to avoid any races accessing the flags and open_count fields. This addresses: http://tracker.newdream.net/issues/3427 Reported-by: Maciej Galkiewicz Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 8c90a39c2a91..ed0c91d81063 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -261,10 +261,10 @@ struct rbd_device { char name[DEV_NAME_LEN]; /* blkdev name, e.g. rbd3 */ - spinlock_t lock; /* queue lock */ + spinlock_t lock; /* queue, flags, open_count */ struct rbd_image_header header; - unsigned long flags; + unsigned long flags; /* possibly lock protected */ struct rbd_spec *spec; char *header_name; @@ -289,13 +289,19 @@ struct rbd_device { /* sysfs related */ struct device dev; - unsigned long open_count; + unsigned long open_count; /* protected by lock */ }; -/* Flag bits for rbd_dev->flags */ - +/* + * Flag bits for rbd_dev->flags. If atomicity is required, + * rbd_dev->lock is used to protect access. + * + * Currently, only the "removing" flag (which is coupled with the + * "open_count" field) requires atomic access. + */ enum rbd_dev_flags { RBD_DEV_FLAG_EXISTS, /* mapped snapshot has not been deleted */ + RBD_DEV_FLAG_REMOVING, /* this mapping is being removed */ }; static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */ @@ -383,14 +389,23 @@ static int rbd_dev_v2_refresh(struct rbd_device *rbd_dev, u64 *hver); static int rbd_open(struct block_device *bdev, fmode_t mode) { struct rbd_device *rbd_dev = bdev->bd_disk->private_data; + bool removing = false; if ((mode & FMODE_WRITE) && rbd_dev->mapping.read_only) return -EROFS; + spin_lock(&rbd_dev->lock); + if (test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags)) + removing = true; + else + rbd_dev->open_count++; + spin_unlock(&rbd_dev->lock); + if (removing) + return -ENOENT; + mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); (void) get_device(&rbd_dev->dev); set_device_ro(bdev, rbd_dev->mapping.read_only); - rbd_dev->open_count++; mutex_unlock(&ctl_mutex); return 0; @@ -399,10 +414,14 @@ static int rbd_open(struct block_device *bdev, fmode_t mode) static int rbd_release(struct gendisk *disk, fmode_t mode) { struct rbd_device *rbd_dev = disk->private_data; + unsigned long open_count_before; + + spin_lock(&rbd_dev->lock); + open_count_before = rbd_dev->open_count--; + spin_unlock(&rbd_dev->lock); + rbd_assert(open_count_before > 0); mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - rbd_assert(rbd_dev->open_count > 0); - rbd_dev->open_count--; put_device(&rbd_dev->dev); mutex_unlock(&ctl_mutex); @@ -4083,10 +4102,14 @@ static ssize_t rbd_remove(struct bus_type *bus, goto done; } - if (rbd_dev->open_count) { + spin_lock(&rbd_dev->lock); + if (rbd_dev->open_count) ret = -EBUSY; + else + set_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags); + spin_unlock(&rbd_dev->lock); + if (ret < 0) goto done; - } rbd_remove_all_snaps(rbd_dev); rbd_bus_del_dev(rbd_dev); From 72fe25e3460c8673984370208e0e6261101372d6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 30 Jan 2013 11:13:33 -0600 Subject: [PATCH 2659/4607] libceph: add a compatibility check interface An upcoming change implements semantic change that could lead to a crash if an old version of the libceph kernel module is used with a new version of the rbd kernel module. In order to preclude that possibility, this adds a compatibilty check interface. If this interface doesn't exist, the modules are obviously not compatible. But if it does exist, this provides a way of letting the caller know whether it will operate properly with this libceph module. Perhaps confusingly, it returns false right now. The semantic change mentioned above will make it return true. This resolves: http://tracker.ceph.com/issues/3800 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- include/linux/ceph/libceph.h | 2 ++ net/ceph/ceph_common.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h index 084d3c622b12..c44275ab375c 100644 --- a/include/linux/ceph/libceph.h +++ b/include/linux/ceph/libceph.h @@ -193,6 +193,8 @@ static inline int calc_pages_for(u64 off, u64 len) } /* ceph_common.c */ +extern bool libceph_compatible(void *data); + extern const char *ceph_msg_type_name(int type); extern int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid); extern struct kmem_cache *ceph_inode_cachep; diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index ee71ea26777a..a98c03ff853f 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -26,6 +26,22 @@ #include "crypto.h" +/* + * Module compatibility interface. For now it doesn't do anything, + * but its existence signals a certain level of functionality. + * + * The data buffer is used to pass information both to and from + * libceph. The return value indicates whether libceph determines + * it is compatible with the caller (from another kernel module), + * given the provided data. + * + * The data pointer can be null. + */ +bool libceph_compatible(void *data) +{ + return false; +} +EXPORT_SYMBOL(libceph_compatible); /* * find filename portion of a path (/foo/bar/baz -> baz) From 1e32d34cfa6759df58b5f4002664241f2a0fef6a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Wed, 30 Jan 2013 11:13:33 -0600 Subject: [PATCH 2660/4607] rbd: don't take extra bio reference for osd client Currently, if the OSD client finds an osd request has had a bio list attached to it, it drops a reference to it (or rather, to the first entry on that list) when the request is released. The code that added that reference (i.e., the rbd client) is therefore required to take an extra reference to that first bio structure. The osd client doesn't really do anything with the bio pointer other than transfer it from the osd request structure to outgoing (for writes) and ingoing (for reads) messages. So it really isn't the right place to be taking or dropping references. Furthermore, the rbd client already holds references to all bio structures it passes to the osd client, and holds them until the request is completed. So there's no need for this extra reference whatsoever. So remove the bio_put() call in ceph_osdc_release_request(), as well as its matching bio_get() call in rbd_osd_req_create(). This change could lead to a crash if old libceph.ko was used with new rbd.ko. Add a compatibility check at rbd initialization time to avoid this possibilty. This resolves: http://tracker.ceph.com/issues/3798 and http://tracker.ceph.com/issues/3799 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 6 +++++- net/ceph/ceph_common.c | 2 +- net/ceph/osd_client.c | 4 ---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index ed0c91d81063..3ba4836f024c 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1342,7 +1342,6 @@ static struct ceph_osd_request *rbd_osd_req_create( case OBJ_REQUEST_BIO: rbd_assert(obj_request->bio_list != NULL); osd_req->r_bio = obj_request->bio_list; - bio_get(osd_req->r_bio); /* osd client requires "num pages" even for bio */ osd_req->r_num_pages = calc_pages_for(offset, length); break; @@ -4149,6 +4148,11 @@ int __init rbd_init(void) { int rc; + if (!libceph_compatible(NULL)) { + rbd_warn(NULL, "libceph incompatibility (quitting)"); + + return -EINVAL; + } rc = rbd_sysfs_init(); if (rc) return rc; diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index a98c03ff853f..c236c235c4a2 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -39,7 +39,7 @@ */ bool libceph_compatible(void *data) { - return false; + return true; } EXPORT_SYMBOL(libceph_compatible); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 500ae8b49321..ba03648533c0 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -147,10 +147,6 @@ void ceph_osdc_release_request(struct kref *kref) if (req->r_own_pages) ceph_release_page_vector(req->r_pages, req->r_num_pages); -#ifdef CONFIG_BLOCK - if (req->r_bio) - bio_put(req->r_bio); -#endif ceph_put_snap_context(req->r_snapc); ceph_pagelist_release(&req->r_trail); if (req->r_mempool) From 9cbb1d7268afa997a7f96d779470cc57d28e1a13 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 31 Jan 2013 16:02:00 -0600 Subject: [PATCH 2661/4607] libceph: don't require r_num_pages for bio requests There is a check in the completion path for osd requests that ensures the number of pages allocated is enough to hold the amount of incoming data expected. For bio requests coming from rbd the "number of pages" is not really meaningful (although total length would be). So stop requiring that nr_pages be supplied for bio requests. This is done by checking whether the pages pointer is null before checking the value of nr_pages. Note that this value is passed on to the messenger, but there it's only used for debugging--it's never used for validation. While here, change another spot that used r_pages in a debug message inappropriately, and also invalidate the r_con_filling_msg pointer after dropping a reference to it. This resolves: http://tracker.ceph.com/issues/3875 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 2 -- net/ceph/osd_client.c | 7 ++++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 3ba4836f024c..14a6967291d3 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1342,8 +1342,6 @@ static struct ceph_osd_request *rbd_osd_req_create( case OBJ_REQUEST_BIO: rbd_assert(obj_request->bio_list != NULL); osd_req->r_bio = obj_request->bio_list; - /* osd client requires "num pages" even for bio */ - osd_req->r_num_pages = calc_pages_for(offset, length); break; case OBJ_REQUEST_PAGES: osd_req->r_pages = obj_request->pages; diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index ba03648533c0..d9d58bbe9f9a 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -137,10 +137,11 @@ void ceph_osdc_release_request(struct kref *kref) if (req->r_request) ceph_msg_put(req->r_request); if (req->r_con_filling_msg) { - dout("%s revoking pages %p from con %p\n", __func__, - req->r_pages, req->r_con_filling_msg); + dout("%s revoking msg %p from con %p\n", __func__, + req->r_reply, req->r_con_filling_msg); ceph_msg_revoke_incoming(req->r_reply); req->r_con_filling_msg->ops->put(req->r_con_filling_msg); + req->r_con_filling_msg = NULL; } if (req->r_reply) ceph_msg_put(req->r_reply); @@ -1981,7 +1982,7 @@ static struct ceph_msg *get_reply(struct ceph_connection *con, if (data_len > 0) { int want = calc_pages_for(req->r_page_alignment, data_len); - if (unlikely(req->r_num_pages < want)) { + if (req->r_pages && unlikely(req->r_num_pages < want)) { pr_warning("tid %lld reply has %d bytes %d pages, we" " had only %d pages ready\n", tid, data_len, want, req->r_num_pages); From a14ea269dd6b5e48a2941ba73b202cd7cd5d716d Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 5 Feb 2013 13:23:12 -0600 Subject: [PATCH 2662/4607] rbd: turn off interrupts for open/remove locking This commit: bc7a62ee5 rbd: prevent open for image being removed added checking for removing rbd before allowing an open, and used the same request spinlock for protecting that and updating the open count as is used for the request queue. However it used the non-irq protected version of the spinlocks. Fix that. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 14a6967291d3..91983a60487b 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -394,12 +394,12 @@ static int rbd_open(struct block_device *bdev, fmode_t mode) if ((mode & FMODE_WRITE) && rbd_dev->mapping.read_only) return -EROFS; - spin_lock(&rbd_dev->lock); + spin_lock_irq(&rbd_dev->lock); if (test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags)) removing = true; else rbd_dev->open_count++; - spin_unlock(&rbd_dev->lock); + spin_unlock_irq(&rbd_dev->lock); if (removing) return -ENOENT; @@ -416,9 +416,9 @@ static int rbd_release(struct gendisk *disk, fmode_t mode) struct rbd_device *rbd_dev = disk->private_data; unsigned long open_count_before; - spin_lock(&rbd_dev->lock); + spin_lock_irq(&rbd_dev->lock); open_count_before = rbd_dev->open_count--; - spin_unlock(&rbd_dev->lock); + spin_unlock_irq(&rbd_dev->lock); rbd_assert(open_count_before > 0); mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); @@ -4099,12 +4099,12 @@ static ssize_t rbd_remove(struct bus_type *bus, goto done; } - spin_lock(&rbd_dev->lock); + spin_lock_irq(&rbd_dev->lock); if (rbd_dev->open_count) ret = -EBUSY; else set_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags); - spin_unlock(&rbd_dev->lock); + spin_unlock_irq(&rbd_dev->lock); if (ret < 0) goto done; From 077413082f9ade9ca4d9774dbdc81ee7256d8089 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 5 Feb 2013 23:41:50 -0600 Subject: [PATCH 2663/4607] rbd: add barriers near done flag operations Somehow, I missed this little item in Documentation/atomic_ops.txt: *** WARNING: atomic_read() and atomic_set() DO NOT IMPLY BARRIERS! *** Create and use some helper functions that include the proper memory barriers for manipulating the done field. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 91983a60487b..982963ec2607 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1216,10 +1216,28 @@ static int rbd_obj_request_wait(struct rbd_obj_request *obj_request) return wait_for_completion_interruptible(&obj_request->completion); } +static void obj_request_done_init(struct rbd_obj_request *obj_request) +{ + atomic_set(&obj_request->done, 0); + smp_wmb(); +} + +static void obj_request_done_set(struct rbd_obj_request *obj_request) +{ + atomic_set(&obj_request->done, 1); + smp_wmb(); +} + +static bool obj_request_done_test(struct rbd_obj_request *obj_request) +{ + smp_rmb(); + return atomic_read(&obj_request->done) != 0; +} + static void rbd_osd_trivial_callback(struct rbd_obj_request *obj_request, struct ceph_osd_op *op) { - atomic_set(&obj_request->done, 1); + obj_request_done_set(obj_request); } static void rbd_obj_request_complete(struct rbd_obj_request *obj_request) @@ -1249,14 +1267,14 @@ static void rbd_osd_read_callback(struct rbd_obj_request *obj_request, xferred = obj_request->length; } obj_request->xferred = xferred; - atomic_set(&obj_request->done, 1); + obj_request_done_set(obj_request); } static void rbd_osd_write_callback(struct rbd_obj_request *obj_request, struct ceph_osd_op *op) { obj_request->xferred = le64_to_cpu(op->extent.length); - atomic_set(&obj_request->done, 1); + obj_request_done_set(obj_request); } static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, @@ -1300,7 +1318,7 @@ static void rbd_osd_req_callback(struct ceph_osd_request *osd_req, break; } - if (atomic_read(&obj_request->done)) + if (obj_request_done_test(obj_request)) rbd_obj_request_complete(obj_request); } @@ -1407,7 +1425,7 @@ static struct rbd_obj_request *rbd_obj_request_create(const char *object_name, obj_request->which = BAD_WHICH; obj_request->type = type; INIT_LIST_HEAD(&obj_request->links); - atomic_set(&obj_request->done, 0); + obj_request_done_init(obj_request); init_completion(&obj_request->completion); kref_init(&obj_request->kref); @@ -1611,7 +1629,7 @@ static void rbd_img_obj_callback(struct rbd_obj_request *obj_request) rbd_assert(more); rbd_assert(which < img_request->obj_request_count); - if (!atomic_read(&obj_request->done)) + if (!obj_request_done_test(obj_request)) break; rbd_assert(obj_request->xferred <= (u64) UINT_MAX); From cbd29cb6e38af6119df2cdac0c58acf0e85c177e Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 11 Feb 2013 12:19:28 +0100 Subject: [PATCH 2664/4607] KVM: nVMX: Remove redundant get_vmcs12 from nested_vmx_exit_handled_msr We already pass vmcs12 as argument. Signed-off-by: Jan Kiszka Signed-off-by: Gleb Natapov --- arch/x86/kvm/vmx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index c7944782ecf6..6667042714cc 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -5920,7 +5920,7 @@ static bool nested_vmx_exit_handled_msr(struct kvm_vcpu *vcpu, u32 msr_index = vcpu->arch.regs[VCPU_REGS_RCX]; gpa_t bitmap; - if (!nested_cpu_has(get_vmcs12(vcpu), CPU_BASED_USE_MSR_BITMAPS)) + if (!nested_cpu_has(vmcs12, CPU_BASED_USE_MSR_BITMAPS)) return 1; /* From 877e86f28385407f05c5aa4e397d4ccb3233f01a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 10:41:09 +0200 Subject: [PATCH 2665/4607] dw_dmac: apply default dma_mask if needed In some cases we got the device without dma_mask configured. We have to apply the default value to avoid crashes during memory mapping. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 6694676c9b82..4c83f18803f1 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1661,6 +1661,12 @@ static int dw_probe(struct platform_device *pdev) if (!regs) return -EBUSY; + /* Apply default dma_mask if needed */ + if (!pdev->dev.dma_mask) { + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + } + dw_params = dma_read_byaddr(regs, DW_PARAMS); autocfg = dw_params >> DW_PARAMS_EN & 0x1; From 6b59e366e074d3962e04f01efb8acc10a33c0e1e Mon Sep 17 00:00:00 2001 From: Satoru Takeuchi Date: Thu, 14 Feb 2013 09:07:35 +0900 Subject: [PATCH 2666/4607] x86, efi: remove duplicate code in setup_arch() by using, efi_is_native() The check, "IS_ENABLED(CONFIG_X86_64) != efi_enabled(EFI_64BIT)", in setup_arch() can be replaced by efi_is_enabled(). This change remove duplicate code and improve readability. Signed-off-by: Satoru Takeuchi Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Olof Johansson Signed-off-by: Matt Fleming --- arch/x86/include/asm/efi.h | 9 ++++++++- arch/x86/kernel/setup.c | 3 +-- arch/x86/platform/efi/efi.c | 5 ----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 28677c55113f..60c89f30c727 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -102,7 +102,14 @@ extern void efi_call_phys_epilog(void); extern void efi_unmap_memmap(void); extern void efi_memory_uc(u64 addr, unsigned long size); -#ifndef CONFIG_EFI +#ifdef CONFIG_EFI + +static inline bool efi_is_native(void) +{ + return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT); +} + +#else /* * IF EFI is not configured, have the EFI calls return -ENOSYS. */ diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 8b24289cc10c..1abb7969173a 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1135,8 +1135,7 @@ void __init setup_arch(char **cmdline_p) * mismatched firmware/kernel archtectures since there is no * support for runtime services. */ - if (efi_enabled(EFI_BOOT) && - IS_ENABLED(CONFIG_X86_64) != efi_enabled(EFI_64BIT)) { + if (efi_enabled(EFI_BOOT) && !efi_is_native()) { pr_info("efi: Setup done, disabling due to 32/64-bit mismatch\n"); efi_unmap_memmap(); } diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 77cf0090c0a3..e075e474245b 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -69,11 +69,6 @@ struct efi_memory_map memmap; static struct efi efi_phys __initdata; static efi_system_table_t efi_systab __initdata; -static inline bool efi_is_native(void) -{ - return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT); -} - unsigned long x86_efi_facility; /* From e68b1130dfbb834a4a028df01133591aeb59d241 Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Thu, 14 Feb 2013 11:16:10 +0100 Subject: [PATCH 2667/4607] dma: of-dma.c: fix memory leakage The memory allocated to ofdma might be a leakage when error occurs. Signed-off-by: Cong Ding Signed-off-by: Vinod Koul --- drivers/dma/of-dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index 59631b2c4666..583e50e3d47c 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -107,6 +107,7 @@ int of_dma_controller_register(struct device_node *np, if (!nbcells) { pr_err("%s: #dma-cells property is missing or invalid\n", __func__); + kfree(ofdma); return -EINVAL; } From 207bdae452e6d3eeba19cc3912e5dfb088adc376 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 23 Dec 2012 01:56:45 -0500 Subject: [PATCH 2668/4607] arm64: switch to generic sigaltstack Signed-off-by: Al Viro --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/syscalls.h | 2 -- arch/arm64/include/asm/unistd32.h | 2 +- arch/arm64/kernel/entry.S | 5 --- arch/arm64/kernel/signal.c | 17 ++-------- arch/arm64/kernel/signal32.c | 53 ++----------------------------- arch/arm64/kernel/sys.c | 1 - arch/arm64/kernel/sys32.S | 5 --- 8 files changed, 6 insertions(+), 80 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index f8f362aafee9..d9c901dd3fdb 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -10,6 +10,7 @@ config ARM64 select GENERIC_IOMAP select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW + select GENERIC_SIGALTSTACK select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL select HARDIRQS_SW_RESEND diff --git a/arch/arm64/include/asm/syscalls.h b/arch/arm64/include/asm/syscalls.h index 20d63b290665..48fe7c600e98 100644 --- a/arch/arm64/include/asm/syscalls.h +++ b/arch/arm64/include/asm/syscalls.h @@ -24,8 +24,6 @@ * System call wrappers implemented in kernel/entry.S. */ asmlinkage long sys_rt_sigreturn_wrapper(void); -asmlinkage long sys_sigaltstack_wrapper(const stack_t __user *uss, - stack_t __user *uoss); #include diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h index 5ef47ba3ed45..008406aff09f 100644 --- a/arch/arm64/include/asm/unistd32.h +++ b/arch/arm64/include/asm/unistd32.h @@ -207,7 +207,7 @@ __SYSCALL(182, sys_chown16) __SYSCALL(183, sys_getcwd) __SYSCALL(184, sys_capget) __SYSCALL(185, sys_capset) -__SYSCALL(186, compat_sys_sigaltstack_wrapper) +__SYSCALL(186, compat_sys_sigaltstack) __SYSCALL(187, compat_sys_sendfile) __SYSCALL(188, sys_ni_syscall) /* 188 reserved */ __SYSCALL(189, sys_ni_syscall) /* 189 reserved */ diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 9c94f404ded6..514d6098dbee 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -677,10 +677,5 @@ ENTRY(sys_rt_sigreturn_wrapper) b sys_rt_sigreturn ENDPROC(sys_rt_sigreturn_wrapper) -ENTRY(sys_sigaltstack_wrapper) - ldr x2, [sp, #S_SP] - b sys_sigaltstack -ENDPROC(sys_sigaltstack_wrapper) - ENTRY(handle_arch_irq) .quad 0 diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index abd756315cb5..890a591f75dd 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -149,8 +149,7 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) if (restore_sigframe(regs, frame)) goto badframe; - if (do_sigaltstack(&frame->uc.uc_stack, - NULL, regs->sp) == -EFAULT) + if (restore_altstack(&frame->uc.uc_stack)) goto badframe; return regs->regs[0]; @@ -164,12 +163,6 @@ badframe: return 0; } -asmlinkage long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, - unsigned long sp) -{ - return do_sigaltstack(uss, uoss, sp); -} - static int setup_sigframe(struct rt_sigframe __user *sf, struct pt_regs *regs, sigset_t *set) { @@ -250,7 +243,6 @@ static int setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; - stack_t stack; int err = 0; frame = get_sigframe(ka, regs); @@ -260,12 +252,7 @@ static int setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, __put_user_error(0, &frame->uc.uc_flags, err); __put_user_error(NULL, &frame->uc.uc_link, err); - memset(&stack, 0, sizeof(stack)); - stack.ss_sp = (void __user *)current->sas_ss_sp; - stack.ss_flags = sas_ss_flags(regs->sp); - stack.ss_size = current->sas_ss_size; - err |= __copy_to_user(&frame->uc.uc_stack, &stack, sizeof(stack)); - + err |= __save_altstack(&frame->uc.uc_stack, regs->sp); err |= setup_sigframe(frame, regs, set); if (err == 0) { setup_return(regs, ka, frame, usig); diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index a4db3d22aac4..54920c5342b2 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -42,12 +42,6 @@ struct compat_old_sigaction { compat_uptr_t sa_restorer; }; -typedef struct compat_sigaltstack { - compat_uptr_t ss_sp; - int ss_flags; - compat_size_t ss_size; -} compat_stack_t; - struct compat_sigcontext { /* We always set these two fields to 0 */ compat_ulong_t trap_no; @@ -423,43 +417,6 @@ asmlinkage int compat_sys_rt_sigaction(int sig, return ret; } -int compat_do_sigaltstack(compat_uptr_t compat_uss, compat_uptr_t compat_uoss, - compat_ulong_t sp) -{ - compat_stack_t __user *newstack = compat_ptr(compat_uss); - compat_stack_t __user *oldstack = compat_ptr(compat_uoss); - compat_uptr_t ss_sp; - int ret; - mm_segment_t old_fs; - stack_t uss, uoss; - - /* Marshall the compat new stack into a stack_t */ - if (newstack) { - if (get_user(ss_sp, &newstack->ss_sp) || - __get_user(uss.ss_flags, &newstack->ss_flags) || - __get_user(uss.ss_size, &newstack->ss_size)) - return -EFAULT; - uss.ss_sp = compat_ptr(ss_sp); - } - - old_fs = get_fs(); - set_fs(KERNEL_DS); - /* The __user pointer casts are valid because of the set_fs() */ - ret = do_sigaltstack( - newstack ? (stack_t __user *) &uss : NULL, - oldstack ? (stack_t __user *) &uoss : NULL, - (unsigned long)sp); - set_fs(old_fs); - - /* Convert the old stack_t into a compat stack. */ - if (!ret && oldstack && - (put_user(ptr_to_compat(uoss.ss_sp), &oldstack->ss_sp) || - __put_user(uoss.ss_flags, &oldstack->ss_flags) || - __put_user(uoss.ss_size, &oldstack->ss_size))) - return -EFAULT; - return ret; -} - static int compat_restore_sigframe(struct pt_regs *regs, struct compat_sigframe __user *sf) { @@ -562,9 +519,7 @@ asmlinkage int compat_sys_rt_sigreturn(struct pt_regs *regs) if (compat_restore_sigframe(regs, &frame->sig)) goto badframe; - if (compat_do_sigaltstack(ptr_to_compat(&frame->sig.uc.uc_stack), - ptr_to_compat((void __user *)NULL), - regs->compat_sp) == -EFAULT) + if (compat_restore_altstack(&frame->sig.uc.uc_stack)) goto badframe; return regs->regs[0]; @@ -705,11 +660,7 @@ int compat_setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, __put_user_error(0, &frame->sig.uc.uc_flags, err); __put_user_error(NULL, &frame->sig.uc.uc_link, err); - memset(&stack, 0, sizeof(stack)); - stack.ss_sp = (compat_uptr_t)current->sas_ss_sp; - stack.ss_flags = sas_ss_flags(regs->compat_sp); - stack.ss_size = current->sas_ss_size; - err |= __copy_to_user(&frame->sig.uc.uc_stack, &stack, sizeof(stack)); + err |= __compat_save_altstack(&frame->sig.uc.uc_stack, regs->compat_sp); err |= compat_setup_sigframe(&frame->sig, regs, set); diff --git a/arch/arm64/kernel/sys.c b/arch/arm64/kernel/sys.c index 8292a9b090f8..3fa98ff14f0e 100644 --- a/arch/arm64/kernel/sys.c +++ b/arch/arm64/kernel/sys.c @@ -40,7 +40,6 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len, * Wrappers to pass the pt_regs argument. */ #define sys_rt_sigreturn sys_rt_sigreturn_wrapper -#define sys_sigaltstack sys_sigaltstack_wrapper #include diff --git a/arch/arm64/kernel/sys32.S b/arch/arm64/kernel/sys32.S index 7ef59e9245ef..6abb05721614 100644 --- a/arch/arm64/kernel/sys32.S +++ b/arch/arm64/kernel/sys32.S @@ -39,11 +39,6 @@ compat_sys_rt_sigreturn_wrapper: b compat_sys_rt_sigreturn ENDPROC(compat_sys_rt_sigreturn_wrapper) -compat_sys_sigaltstack_wrapper: - ldr x2, [sp, #S_COMPAT_SP] - b compat_do_sigaltstack -ENDPROC(compat_sys_sigaltstack_wrapper) - compat_sys_statfs64_wrapper: mov w3, #84 cmp w1, #88 From 630cfbbbe656fb0377a6847606016d88cef7675a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 25 Dec 2012 13:57:16 -0500 Subject: [PATCH 2669/4607] arm64: switch to generic compat rt_sigprocmask() Signed-off-by: Al Viro --- arch/arm64/Kconfig | 1 + arch/arm64/kernel/signal32.c | 33 --------------------------------- 2 files changed, 1 insertion(+), 33 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index d9c901dd3fdb..4b99d632329e 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -6,6 +6,7 @@ config ARM64 select CLONE_BACKWARDS select COMMON_CLK select GENERIC_CLOCKEVENTS + select GENERIC_COMPAT_RT_SIGPROCMASK select GENERIC_HARDIRQS_NO_DEPRECATED select GENERIC_IOMAP select GENERIC_IRQ_PROBE diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index 54920c5342b2..dcc13bd94fb0 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -693,39 +693,6 @@ int compat_setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, return err; } -/* - * RT signals don't have generic compat wrappers. - * See arch/powerpc/kernel/signal_32.c - */ -asmlinkage int compat_sys_rt_sigprocmask(int how, compat_sigset_t __user *set, - compat_sigset_t __user *oset, - compat_size_t sigsetsize) -{ - sigset_t s; - sigset_t __user *up; - int ret; - mm_segment_t old_fs = get_fs(); - - if (set) { - if (get_sigset_t(&s, set)) - return -EFAULT; - } - - set_fs(KERNEL_DS); - /* This is valid because of the set_fs() */ - up = (sigset_t __user *) &s; - ret = sys_rt_sigprocmask(how, set ? up : NULL, oset ? up : NULL, - sigsetsize); - set_fs(old_fs); - if (ret) - return ret; - if (oset) { - if (put_sigset_t(oset, &s)) - return -EFAULT; - } - return 0; -} - asmlinkage int compat_sys_rt_sigpending(compat_sigset_t __user *set, compat_size_t sigsetsize) { From 67cf48fe2529576bc38ab2b3be929d37d799ac91 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 25 Dec 2012 15:01:55 -0500 Subject: [PATCH 2670/4607] arm64: switch to generic compat rt_sigpending() Signed-off-by: Al Viro --- arch/arm64/Kconfig | 1 + arch/arm64/kernel/signal32.c | 18 ------------------ 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 4b99d632329e..6f056d7d3c62 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -6,6 +6,7 @@ config ARM64 select CLONE_BACKWARDS select COMMON_CLK select GENERIC_CLOCKEVENTS + select GENERIC_COMPAT_RT_SIGPENDING select GENERIC_COMPAT_RT_SIGPROCMASK select GENERIC_HARDIRQS_NO_DEPRECATED select GENERIC_IOMAP diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index dcc13bd94fb0..39240d8e7aab 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -693,24 +693,6 @@ int compat_setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, return err; } -asmlinkage int compat_sys_rt_sigpending(compat_sigset_t __user *set, - compat_size_t sigsetsize) -{ - sigset_t s; - int ret; - mm_segment_t old_fs = get_fs(); - - set_fs(KERNEL_DS); - /* The __user pointer cast is valid because of the set_fs() */ - ret = sys_rt_sigpending((sigset_t __user *) &s, sigsetsize); - set_fs(old_fs); - if (!ret) { - if (put_sigset_t(set, &s)) - return -EFAULT; - } - return ret; -} - asmlinkage int compat_sys_rt_sigqueueinfo(int pid, int sig, compat_siginfo_t __user *uinfo) { From 4cd2b2fa61ef65300cb237febc0e8a77f23b6c44 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 25 Dec 2012 15:50:19 -0500 Subject: [PATCH 2671/4607] arm64: switch to generic compat rt_sigqueueinfo() Signed-off-by: Al Viro --- arch/arm64/Kconfig | 1 + arch/arm64/kernel/signal32.c | 18 ------------------ 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6f056d7d3c62..c23afb12e0da 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -8,6 +8,7 @@ config ARM64 select GENERIC_CLOCKEVENTS select GENERIC_COMPAT_RT_SIGPENDING select GENERIC_COMPAT_RT_SIGPROCMASK + select GENERIC_COMPAT_RT_SIGQUEUEINFO select GENERIC_HARDIRQS_NO_DEPRECATED select GENERIC_IOMAP select GENERIC_IRQ_PROBE diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index 39240d8e7aab..806da6f5e28d 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -693,24 +693,6 @@ int compat_setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, return err; } -asmlinkage int compat_sys_rt_sigqueueinfo(int pid, int sig, - compat_siginfo_t __user *uinfo) -{ - siginfo_t info; - int ret; - mm_segment_t old_fs = get_fs(); - - ret = copy_siginfo_from_user32(&info, uinfo); - if (unlikely(ret)) - return ret; - - set_fs (KERNEL_DS); - /* The __user pointer cast is valid because of the set_fs() */ - ret = sys_rt_sigqueueinfo(pid, sig, (siginfo_t __user *) &info); - set_fs (old_fs); - return ret; -} - void compat_setup_restart_syscall(struct pt_regs *regs) { regs->regs[7] = __NR_compat_restart_syscall; From 84b9e9b402386edf12664e37ee1f2e503472fb5e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 25 Dec 2012 16:29:11 -0500 Subject: [PATCH 2672/4607] arm64: switch compat to generic old sigsuspend Signed-off-by: Al Viro --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/unistd32.h | 2 +- arch/arm64/kernel/signal32.c | 12 ------------ 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index c23afb12e0da..8ae01ea98c16 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -208,6 +208,7 @@ config COMPAT depends on !ARM64_64K_PAGES select COMPAT_BINFMT_ELF select HAVE_UID16 + select OLD_SIGSUSPEND3 help This option enables support for a 32-bit EL0 running under a 64-bit kernel at EL1. AArch32-specific components such as system calls, diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h index 008406aff09f..e60e386178d1 100644 --- a/arch/arm64/include/asm/unistd32.h +++ b/arch/arm64/include/asm/unistd32.h @@ -93,7 +93,7 @@ __SYSCALL(68, sys_ni_syscall) /* 68 was sys_sgetmask */ __SYSCALL(69, sys_ni_syscall) /* 69 was sys_ssetmask */ __SYSCALL(70, sys_setreuid16) __SYSCALL(71, sys_setregid16) -__SYSCALL(72, compat_sys_sigsuspend) +__SYSCALL(72, sys_sigsuspend) __SYSCALL(73, compat_sys_sigpending) __SYSCALL(74, sys_sethostname) __SYSCALL(75, compat_sys_setrlimit) diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index 806da6f5e28d..a1bd4395a62c 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -333,18 +333,6 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame) return err ? -EFAULT : 0; } -/* - * atomically swap in the new signal mask, and wait for a signal. - */ -asmlinkage int compat_sys_sigsuspend(int restart, compat_ulong_t oldmask, - compat_old_sigset_t mask) -{ - sigset_t blocked; - - siginitset(¤t->blocked, mask); - return sigsuspend(&blocked); -} - asmlinkage int compat_sys_sigaction(int sig, const struct compat_old_sigaction __user *act, struct compat_old_sigaction __user *oact) From 02323a9d92a1acc0bbc40239a0a3a91bffa0dad3 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 25 Dec 2012 18:56:13 -0500 Subject: [PATCH 2673/4607] arm64: switch to generic compat rt_sigaction() Signed-off-by: Al Viro --- arch/arm64/Kconfig | 1 + arch/arm64/kernel/signal32.c | 41 ------------------------------------ 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 8ae01ea98c16..24dce472a77e 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -6,6 +6,7 @@ config ARM64 select CLONE_BACKWARDS select COMMON_CLK select GENERIC_CLOCKEVENTS + select GENERIC_COMPAT_RT_SIGACTION select GENERIC_COMPAT_RT_SIGPENDING select GENERIC_COMPAT_RT_SIGPROCMASK select GENERIC_COMPAT_RT_SIGQUEUEINFO diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index a1bd4395a62c..892b9dfb5517 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -28,13 +28,6 @@ #include #include -struct compat_sigaction { - compat_uptr_t sa_handler; - compat_ulong_t sa_flags; - compat_uptr_t sa_restorer; - compat_sigset_t sa_mask; -}; - struct compat_old_sigaction { compat_uptr_t sa_handler; compat_old_sigset_t sa_mask; @@ -371,40 +364,6 @@ asmlinkage int compat_sys_sigaction(int sig, return ret; } -asmlinkage int compat_sys_rt_sigaction(int sig, - const struct compat_sigaction __user *act, - struct compat_sigaction __user *oact, - compat_size_t sigsetsize) -{ - struct k_sigaction new_ka, old_ka; - int ret; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(compat_sigset_t)) - return -EINVAL; - - if (act) { - compat_uptr_t handler, restorer; - - ret = get_user(handler, &act->sa_handler); - new_ka.sa.sa_handler = compat_ptr(handler); - ret |= get_user(restorer, &act->sa_restorer); - new_ka.sa.sa_restorer = compat_ptr(restorer); - ret |= get_sigset_t(&new_ka.sa.sa_mask, &act->sa_mask); - ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); - if (ret) - return -EFAULT; - } - - ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); - if (!ret && oact) { - ret = put_user(ptr_to_compat(old_ka.sa.sa_handler), &oact->sa_handler); - ret |= put_sigset_t(&oact->sa_mask, &old_ka.sa.sa_mask); - ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); - } - return ret; -} - static int compat_restore_sigframe(struct pt_regs *regs, struct compat_sigframe __user *sf) { From 51682036d006b175022d0cd010672d3fff041278 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 25 Dec 2012 19:31:29 -0500 Subject: [PATCH 2674/4607] arm64: switch to generic old sigaction() (compat-only) Signed-off-by: Al Viro --- arch/arm64/Kconfig | 1 + arch/arm64/kernel/signal32.c | 45 ------------------------------------ 2 files changed, 1 insertion(+), 45 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 24dce472a77e..1f27c58f44ad 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -210,6 +210,7 @@ config COMPAT select COMPAT_BINFMT_ELF select HAVE_UID16 select OLD_SIGSUSPEND3 + select COMPAT_OLD_SIGACTION help This option enables support for a 32-bit EL0 running under a 64-bit kernel at EL1. AArch32-specific components such as system calls, diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index 892b9dfb5517..92ada01f4cd8 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -28,13 +28,6 @@ #include #include -struct compat_old_sigaction { - compat_uptr_t sa_handler; - compat_old_sigset_t sa_mask; - compat_ulong_t sa_flags; - compat_uptr_t sa_restorer; -}; - struct compat_sigcontext { /* We always set these two fields to 0 */ compat_ulong_t trap_no; @@ -326,44 +319,6 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame) return err ? -EFAULT : 0; } -asmlinkage int compat_sys_sigaction(int sig, - const struct compat_old_sigaction __user *act, - struct compat_old_sigaction __user *oact) -{ - struct k_sigaction new_ka, old_ka; - int ret; - compat_old_sigset_t mask; - compat_uptr_t handler, restorer; - - if (act) { - if (!access_ok(VERIFY_READ, act, sizeof(*act)) || - __get_user(handler, &act->sa_handler) || - __get_user(restorer, &act->sa_restorer) || - __get_user(new_ka.sa.sa_flags, &act->sa_flags) || - __get_user(mask, &act->sa_mask)) - return -EFAULT; - - new_ka.sa.sa_handler = compat_ptr(handler); - new_ka.sa.sa_restorer = compat_ptr(restorer); - siginitset(&new_ka.sa.sa_mask, mask); - } - - ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); - - if (!ret && oact) { - if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || - __put_user(ptr_to_compat(old_ka.sa.sa_handler), - &oact->sa_handler) || - __put_user(ptr_to_compat(old_ka.sa.sa_restorer), - &oact->sa_restorer) || - __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || - __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) - return -EFAULT; - } - - return ret; -} - static int compat_restore_sigframe(struct pt_regs *regs, struct compat_sigframe __user *sf) { From e9b04b5b67ec628a5e9a312e14b6864f8f73ba12 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 20 Nov 2012 11:14:10 -0500 Subject: [PATCH 2675/4607] make do_sigaltstack() static Signed-off-by: Al Viro --- include/linux/sched.h | 1 - kernel/signal.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h index 8f983293b403..6dd06494997a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2259,7 +2259,6 @@ extern struct sigqueue *sigqueue_alloc(void); extern void sigqueue_free(struct sigqueue *); extern int send_sigqueue(struct sigqueue *, struct task_struct *, int group); extern int do_sigaction(int, struct k_sigaction *, struct k_sigaction *); -extern int do_sigaltstack(const stack_t __user *, stack_t __user *, unsigned long); static inline void restore_saved_sigmask(void) { diff --git a/kernel/signal.c b/kernel/signal.c index 87c09e3061d2..6919050c8156 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -3129,7 +3129,7 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact) return 0; } -int +static int do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long sp) { stack_t oss; From d64008a8f30e0b381b292788ec6f3ee509b3bb40 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 25 Nov 2012 23:12:10 -0500 Subject: [PATCH 2676/4607] burying unused conditionals __ARCH_WANT_SYS_RT_SIGACTION, __ARCH_WANT_SYS_RT_SIGSUSPEND, __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND, __ARCH_WANT_COMPAT_SYS_SCHED_RR_GET_INTERVAL - not used anymore CONFIG_GENERIC_{SIGALTSTACK,COMPAT_RT_SIG{ACTION,QUEUEINFO,PENDING,PROCMASK}} - can be assumed always set. --- arch/Kconfig | 15 --------------- arch/alpha/Kconfig | 1 - arch/alpha/include/asm/unistd.h | 1 - arch/arm/Kconfig | 1 - arch/arm/include/asm/unistd.h | 2 -- arch/arm64/Kconfig | 5 ----- arch/arm64/include/asm/unistd.h | 2 -- arch/avr32/Kconfig | 1 - arch/avr32/include/asm/unistd.h | 2 -- arch/blackfin/Kconfig | 1 - arch/blackfin/include/asm/unistd.h | 2 -- arch/c6x/Kconfig | 1 - arch/cris/Kconfig | 1 - arch/cris/include/asm/unistd.h | 2 -- arch/frv/Kconfig | 1 - arch/frv/include/asm/unistd.h | 2 -- arch/h8300/Kconfig | 1 - arch/h8300/include/asm/unistd.h | 2 -- arch/hexagon/Kconfig | 1 - arch/ia64/Kconfig | 1 - arch/ia64/include/asm/unistd.h | 3 --- arch/m32r/Kconfig | 1 - arch/m32r/include/asm/unistd.h | 2 -- arch/m68k/Kconfig | 1 - arch/m68k/include/asm/unistd.h | 2 -- arch/microblaze/Kconfig | 1 - arch/microblaze/include/asm/unistd.h | 2 -- arch/mips/Kconfig | 5 ----- arch/mips/include/asm/unistd.h | 1 - arch/mn10300/Kconfig | 1 - arch/mn10300/include/asm/unistd.h | 2 -- arch/openrisc/Kconfig | 1 - arch/parisc/Kconfig | 5 ----- arch/parisc/include/asm/unistd.h | 3 --- arch/powerpc/Kconfig | 5 ----- arch/powerpc/include/asm/unistd.h | 4 ---- arch/s390/Kconfig | 5 ----- arch/s390/include/asm/unistd.h | 3 --- arch/score/Kconfig | 1 - arch/sh/Kconfig | 1 - arch/sh/include/asm/unistd.h | 2 -- arch/sparc/Kconfig | 5 ----- arch/sparc/include/asm/unistd.h | 3 --- arch/tile/Kconfig | 5 ----- arch/tile/include/asm/unistd.h | 1 - arch/unicore32/Kconfig | 1 - arch/x86/Kconfig | 4 ---- arch/x86/include/asm/unistd.h | 2 -- arch/x86/um/Kconfig | 1 - arch/xtensa/Kconfig | 1 - arch/xtensa/include/asm/unistd.h | 2 -- include/asm-generic/syscalls.h | 7 ------- include/asm-generic/unistd.h | 3 --- include/linux/compat.h | 14 -------------- include/linux/syscalls.h | 2 -- kernel/signal.c | 12 ------------ 56 files changed, 159 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index e50d3af294d4..956756c27ac6 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -356,21 +356,6 @@ config MODULES_USE_ELF_REL Modules only use ELF REL relocations. Modules with ELF RELA relocations will give an error. -config GENERIC_SIGALTSTACK - bool - -config GENERIC_COMPAT_RT_SIGPROCMASK - bool - -config GENERIC_COMPAT_RT_SIGPENDING - bool - -config GENERIC_COMPAT_RT_SIGQUEUEINFO - bool - -config GENERIC_COMPAT_RT_SIGACTION - bool - # # ABI hall of shame # diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 15740cf29bd4..dd083c403ab3 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -22,7 +22,6 @@ config ALPHA select GENERIC_STRNLEN_USER select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK select ODD_RT_SIGACTION select OLD_SIGSUSPEND help diff --git a/arch/alpha/include/asm/unistd.h b/arch/alpha/include/asm/unistd.h index b3396ee039b7..6d6fe7ab5473 100644 --- a/arch/alpha/include/asm/unistd.h +++ b/arch/alpha/include/asm/unistd.h @@ -14,7 +14,6 @@ #define __ARCH_WANT_SYS_OLD_GETRLIMIT #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index db3152d6dd88..fcb406633328 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -56,7 +56,6 @@ config ARM select HAVE_MOD_ARCH_SPECIFIC if ARM_UNWIND select MODULES_USE_ELF_REL select CLONE_BACKWARDS - select GENERIC_SIGALTSTACK select OLD_SIGSUSPEND3 select OLD_SIGACTION help diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h index 21a2700d2957..e4ddfb39ca34 100644 --- a/arch/arm/include/asm/unistd.h +++ b/arch/arm/include/asm/unistd.h @@ -26,8 +26,6 @@ #define __ARCH_WANT_SYS_NICE #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_OLD_MMAP #define __ARCH_WANT_SYS_OLD_SELECT diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 1f27c58f44ad..626ab20f12ea 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -6,15 +6,10 @@ config ARM64 select CLONE_BACKWARDS select COMMON_CLK select GENERIC_CLOCKEVENTS - select GENERIC_COMPAT_RT_SIGACTION - select GENERIC_COMPAT_RT_SIGPENDING - select GENERIC_COMPAT_RT_SIGPROCMASK - select GENERIC_COMPAT_RT_SIGQUEUEINFO select GENERIC_HARDIRQS_NO_DEPRECATED select GENERIC_IOMAP select GENERIC_IRQ_PROBE select GENERIC_IRQ_SHOW - select GENERIC_SIGALTSTACK select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL select HARDIRQS_SW_RESEND diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 744087fb521c..82ce217e94cf 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -20,10 +20,8 @@ #define __ARCH_WANT_SYS_GETPGRP #define __ARCH_WANT_SYS_LLSEEK #define __ARCH_WANT_SYS_NICE -#define __ARCH_WANT_COMPAT_SYS_SCHED_RR_GET_INTERVAL #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_COMPAT_SYS_SENDFILE #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index e888b72b6e10..2ae6591b3a55 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -17,7 +17,6 @@ config AVR32 select GENERIC_CLOCKEVENTS select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK help AVR32 is a high-performance 32-bit RISC microprocessor core, designed for cost-sensitive embedded applications, with particular diff --git a/arch/avr32/include/asm/unistd.h b/arch/avr32/include/asm/unistd.h index 0bdf6371574e..dc4d5a931112 100644 --- a/arch/avr32/include/asm/unistd.h +++ b/arch/avr32/include/asm/unistd.h @@ -37,8 +37,6 @@ #define __ARCH_WANT_SYS_GETPGRP #define __ARCH_WANT_SYS_LLSEEK #define __ARCH_WANT_SYS_GETPGRP -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index a8a9ca7d40f3..b6f3ad5441c5 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -45,7 +45,6 @@ config BLACKFIN select ARCH_USES_GETTIMEOFFSET if !GENERIC_CLOCKEVENTS select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK config GENERIC_CSUM def_bool y diff --git a/arch/blackfin/include/asm/unistd.h b/arch/blackfin/include/asm/unistd.h index e943cb130048..04e83ea8d5cc 100644 --- a/arch/blackfin/include/asm/unistd.h +++ b/arch/blackfin/include/asm/unistd.h @@ -18,8 +18,6 @@ #define __ARCH_WANT_SYS_GETPGRP #define __ARCH_WANT_SYS_LLSEEK #define __ARCH_WANT_SYS_NICE -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_VFORK /* diff --git a/arch/c6x/Kconfig b/arch/c6x/Kconfig index 12d97b7ef0dc..f6a3648f5ec3 100644 --- a/arch/c6x/Kconfig +++ b/arch/c6x/Kconfig @@ -18,7 +18,6 @@ config C6X select OF_EARLY_FLATTREE select GENERIC_CLOCKEVENTS select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK config MMU def_bool n diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index c2a1d0a8924c..0e5c187ac7d2 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -50,7 +50,6 @@ config CRIS select GENERIC_CMOS_UPDATE select MODULES_USE_ELF_RELA select CLONE_BACKWARDS2 - select GENERIC_SIGALTSTACK select OLD_SIGSUSPEND select OLD_SIGACTION diff --git a/arch/cris/include/asm/unistd.h b/arch/cris/include/asm/unistd.h index 6d062bdf92d4..be57a988bfb9 100644 --- a/arch/cris/include/asm/unistd.h +++ b/arch/cris/include/asm/unistd.h @@ -30,8 +30,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index e3f8ffdd4e7b..b7465cd3dbbb 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -12,7 +12,6 @@ config FRV select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_CPU_DEVICES select ARCH_WANT_IPC_PARSE_VERSION - select GENERIC_SIGALTSTACK select OLD_SIGSUSPEND3 select OLD_SIGACTION diff --git a/arch/frv/include/asm/unistd.h b/arch/frv/include/asm/unistd.h index d685da17f5fb..4cfcc7bba25a 100644 --- a/arch/frv/include/asm/unistd.h +++ b/arch/frv/include/asm/unistd.h @@ -27,8 +27,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT /* #define __ARCH_WANT_SYS_SIGPENDING */ #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 0b0176ce2c35..05b613af223a 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -9,7 +9,6 @@ config H8300 select GENERIC_IRQ_SHOW select GENERIC_CPU_DEVICES select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK select OLD_SIGSUSPEND3 select OLD_SIGACTION diff --git a/arch/h8300/include/asm/unistd.h b/arch/h8300/include/asm/unistd.h index aa38105959fb..6721856d841b 100644 --- a/arch/h8300/include/asm/unistd.h +++ b/arch/h8300/include/asm/unistd.h @@ -29,8 +29,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE diff --git a/arch/hexagon/Kconfig b/arch/hexagon/Kconfig index 3e6e27c11f93..0744f7d7b1fd 100644 --- a/arch/hexagon/Kconfig +++ b/arch/hexagon/Kconfig @@ -31,7 +31,6 @@ config HEXAGON select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS_BROADCAST select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK ---help--- Qualcomm Hexagon is a processor architecture designed for high performance and low power across a wide variety of applications. diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index 98482d1cbc5b..3279646120e3 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -42,7 +42,6 @@ config IA64 select GENERIC_TIME_VSYSCALL_OLD select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK default y help The Itanium Processor Family is Intel's 64-bit successor to diff --git a/arch/ia64/include/asm/unistd.h b/arch/ia64/include/asm/unistd.h index c827049eb62c..096373800f73 100644 --- a/arch/ia64/include/asm/unistd.h +++ b/arch/ia64/include/asm/unistd.h @@ -27,9 +27,6 @@ #define __IGNORE_vfork /* clone() */ #define __IGNORE_umount2 /* umount() */ -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND - #if !defined(__ASSEMBLY__) && !defined(ASSEMBLER) #include diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index 1f550d4dd5d0..f807721e19a5 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -15,7 +15,6 @@ config M32R select GENERIC_ATOMIC64 select ARCH_USES_GETTIMEOFFSET select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK config SBUS bool diff --git a/arch/m32r/include/asm/unistd.h b/arch/m32r/include/asm/unistd.h index 79b063caec85..555629b05267 100644 --- a/arch/m32r/include/asm/unistd.h +++ b/arch/m32r/include/asm/unistd.h @@ -20,8 +20,6 @@ #define __ARCH_WANT_SYS_LLSEEK #define __ARCH_WANT_SYS_OLD_GETRLIMIT /*will be unused*/ #define __ARCH_WANT_SYS_OLDUMOUNT -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_CLONE #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index a358bf63defe..efb1ce1f14a3 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -18,7 +18,6 @@ config M68K select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_REL select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK select OLD_SIGSUSPEND3 select OLD_SIGACTION diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h index 847994ce6804..df19631cc4de 100644 --- a/arch/m68k/include/asm/unistd.h +++ b/arch/m68k/include/asm/unistd.h @@ -29,8 +29,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index 5e30d75c74ed..ba3b7c8c04b8 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -27,7 +27,6 @@ config MICROBLAZE select GENERIC_CLOCKEVENTS select MODULES_USE_ELF_RELA select CLONE_BACKWARDS - select GENERIC_SIGALTSTACK config SWAP def_bool n diff --git a/arch/microblaze/include/asm/unistd.h b/arch/microblaze/include/asm/unistd.h index 10f8ac186855..b3778391d9cc 100644 --- a/arch/microblaze/include/asm/unistd.h +++ b/arch/microblaze/include/asm/unistd.h @@ -33,8 +33,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_CLONE #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_FORK diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 0772b5c5bc72..a3d4646098fe 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -42,11 +42,6 @@ config MIPS select MODULES_USE_ELF_REL if MODULES select MODULES_USE_ELF_RELA if MODULES && 64BIT select CLONE_BACKWARDS - select GENERIC_SIGALTSTACK - select GENERIC_COMPAT_RT_SIGACTION - select GENERIC_COMPAT_RT_SIGQUEUEINFO - select GENERIC_COMPAT_RT_SIGPROCMASK - select GENERIC_COMPAT_RT_SIGPENDING menu "Machine selection" diff --git a/arch/mips/include/asm/unistd.h b/arch/mips/include/asm/unistd.h index 06f6463c24ad..64f661e32879 100644 --- a/arch/mips/include/asm/unistd.h +++ b/arch/mips/include/asm/unistd.h @@ -35,7 +35,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION # ifdef CONFIG_32BIT # define __ARCH_WANT_STAT64 # define __ARCH_WANT_SYS_TIME diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig index 12bf06f9abe5..ad0caea0bfea 100644 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig @@ -10,7 +10,6 @@ config MN10300 select HAVE_NMI_WATCHDOG if MN10300_WD_TIMER select GENERIC_CLOCKEVENTS select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK select OLD_SIGSUSPEND3 select OLD_SIGACTION diff --git a/arch/mn10300/include/asm/unistd.h b/arch/mn10300/include/asm/unistd.h index e6d2ed4ba68f..7f9d9adfa51e 100644 --- a/arch/mn10300/include/asm/unistd.h +++ b/arch/mn10300/include/asm/unistd.h @@ -41,8 +41,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index d3632eb98a1c..0ac66f67521f 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -22,7 +22,6 @@ config OPENRISC select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK config MMU def_bool y diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 2bd407ffaebf..b77feffbadea 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -23,11 +23,6 @@ config PARISC select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA select CLONE_BACKWARDS - select GENERIC_SIGALTSTACK - select GENERIC_COMPAT_RT_SIGACTION - select GENERIC_COMPAT_RT_SIGQUEUEINFO - select GENERIC_COMPAT_RT_SIGPROCMASK - select GENERIC_COMPAT_RT_SIGPENDING help The PA-RISC microprocessor is designed by Hewlett-Packard and used diff --git a/arch/parisc/include/asm/unistd.h b/arch/parisc/include/asm/unistd.h index 3043194547cd..93b1d089864b 100644 --- a/arch/parisc/include/asm/unistd.h +++ b/arch/parisc/include/asm/unistd.h @@ -160,9 +160,6 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND -#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index ec89a7b11f7b..cf90f0526411 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -144,11 +144,6 @@ config PPC select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA select CLONE_BACKWARDS - select GENERIC_SIGALTSTACK - select GENERIC_COMPAT_RT_SIGACTION - select GENERIC_COMPAT_RT_SIGQUEUEINFO - select GENERIC_COMPAT_RT_SIGPROCMASK - select GENERIC_COMPAT_RT_SIGPENDING select OLD_SIGSUSPEND select OLD_SIGACTION if PPC32 diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h index 1d4864a40e35..f25b5c45c435 100644 --- a/arch/powerpc/include/asm/unistd.h +++ b/arch/powerpc/include/asm/unistd.h @@ -44,17 +44,13 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #ifdef CONFIG_PPC32 #define __ARCH_WANT_OLD_STAT #endif #ifdef CONFIG_PPC64 #define __ARCH_WANT_COMPAT_SYS_TIME -#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_NEWFSTATAT #define __ARCH_WANT_COMPAT_SYS_SENDFILE -#define __ARCH_WANT_COMPAT_SYS_SCHED_RR_GET_INTERVAL #endif #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index bcdcf31fa672..ec12a3582ae9 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -140,11 +140,6 @@ config S390 select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA select CLONE_BACKWARDS2 - select GENERIC_SIGALTSTACK - select GENERIC_COMPAT_RT_SIGACTION - select GENERIC_COMPAT_RT_SIGQUEUEINFO - select GENERIC_COMPAT_RT_SIGPROCMASK - select GENERIC_COMPAT_RT_SIGPENDING select OLD_SIGSUSPEND3 select OLD_SIGACTION diff --git a/arch/s390/include/asm/unistd.h b/arch/s390/include/asm/unistd.h index 636530872516..a6667a952969 100644 --- a/arch/s390/include/asm/unistd.h +++ b/arch/s390/include/asm/unistd.h @@ -43,15 +43,12 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND # ifndef CONFIG_64BIT # define __ARCH_WANT_STAT64 # define __ARCH_WANT_SYS_TIME # endif # ifdef CONFIG_COMPAT # define __ARCH_WANT_COMPAT_SYS_TIME -# define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND # endif #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK diff --git a/arch/score/Kconfig b/arch/score/Kconfig index a125d7207bcc..3b1482e7afac 100644 --- a/arch/score/Kconfig +++ b/arch/score/Kconfig @@ -14,7 +14,6 @@ config SCORE select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_REL select CLONE_BACKWARDS - select GENERIC_SIGALTSTACK choice prompt "System type" diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index b5fd9f3c9925..5f9b0a3f5f00 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -40,7 +40,6 @@ config SUPERH select GENERIC_STRNLEN_USER select HAVE_MOD_ARCH_SPECIFIC if DWARF_UNWINDER select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK select OLD_SIGSUSPEND select OLD_SIGACTION help diff --git a/arch/sh/include/asm/unistd.h b/arch/sh/include/asm/unistd.h index 012004ed3330..5e90fa2b7eed 100644 --- a/arch/sh/include/asm/unistd.h +++ b/arch/sh/include/asm/unistd.h @@ -4,7 +4,6 @@ # include # endif -# define __ARCH_WANT_SYS_RT_SIGSUSPEND # define __ARCH_WANT_OLD_READDIR # define __ARCH_WANT_OLD_STAT # define __ARCH_WANT_STAT64 @@ -27,7 +26,6 @@ # define __ARCH_WANT_SYS_OLDUMOUNT # define __ARCH_WANT_SYS_SIGPENDING # define __ARCH_WANT_SYS_SIGPROCMASK -# define __ARCH_WANT_SYS_RT_SIGACTION # define __ARCH_WANT_SYS_FORK # define __ARCH_WANT_SYS_VFORK # define __ARCH_WANT_SYS_CLONE diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index e557b0821540..9821a0ff9864 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -42,11 +42,6 @@ config SPARC select GENERIC_STRNLEN_USER select MODULES_USE_ELF_RELA select ODD_RT_SIGACTION - select GENERIC_SIGALTSTACK - select GENERIC_COMPAT_RT_SIGQUEUEINFO - select GENERIC_COMPAT_RT_SIGPROCMASK - select GENERIC_COMPAT_RT_SIGPENDING - select GENERIC_COMPAT_RT_SIGACTION select OLD_SIGSUSPEND config SPARC32 diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h index 87ce24c5eb95..5356810bd7e7 100644 --- a/arch/sparc/include/asm/unistd.h +++ b/arch/sparc/include/asm/unistd.h @@ -38,14 +38,11 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #ifdef __32bit_syscall_numbers__ #define __ARCH_WANT_SYS_IPC #else #define __ARCH_WANT_COMPAT_SYS_TIME -#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_COMPAT_SYS_SENDFILE -#define __ARCH_WANT_COMPAT_SYS_SCHED_RR_GET_INTERVAL #endif /* diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index 96a717ebb1fa..875d008828b8 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -21,11 +21,6 @@ config TILE select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_CLOCKEVENTS select MODULES_USE_ELF_RELA - select GENERIC_SIGALTSTACK - select GENERIC_COMPAT_RT_SIGACTION - select GENERIC_COMPAT_RT_SIGQUEUEINFO - select GENERIC_COMPAT_RT_SIGPROCMASK - select GENERIC_COMPAT_RT_SIGPENDING # FIXME: investigate whether we need/want these options. # select HAVE_IOREMAP_PROT diff --git a/arch/tile/include/asm/unistd.h b/arch/tile/include/asm/unistd.h index 6ac21034f69a..940831fe9e94 100644 --- a/arch/tile/include/asm/unistd.h +++ b/arch/tile/include/asm/unistd.h @@ -14,7 +14,6 @@ /* In compat mode, we use sys_llseek() for compat_sys_llseek(). */ #ifdef CONFIG_COMPAT #define __ARCH_WANT_SYS_LLSEEK -#define __ARCH_WANT_COMPAT_SYS_SCHED_RR_GET_INTERVAL #endif #define __ARCH_WANT_SYS_NEWFSTATAT #define __ARCH_WANT_SYS_CLONE diff --git a/arch/unicore32/Kconfig b/arch/unicore32/Kconfig index a62786fdcaab..60651df5f952 100644 --- a/arch/unicore32/Kconfig +++ b/arch/unicore32/Kconfig @@ -16,7 +16,6 @@ config UNICORE32 select ARCH_WANT_FRAME_POINTERS select GENERIC_IOMAP select MODULES_USE_ELF_REL - select GENERIC_SIGALTSTACK help UniCore-32 is 32-bit Instruction Set Architecture, including a series of low-power-consumption RISC chip diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 87d09175a0a9..49fb44e95f3c 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -113,10 +113,6 @@ config X86 select MODULES_USE_ELF_REL if X86_32 select MODULES_USE_ELF_RELA if X86_64 select CLONE_BACKWARDS if X86_32 - select GENERIC_SIGALTSTACK - select GENERIC_COMPAT_RT_SIGACTION - select GENERIC_COMPAT_RT_SIGQUEUEINFO - select GENERIC_COMPAT_RT_SIGPENDING select OLD_SIGSUSPEND3 if X86_32 || IA32_EMULATION select OLD_SIGACTION if X86_32 select COMPAT_OLD_SIGACTION if IA32_EMULATION diff --git a/arch/x86/include/asm/unistd.h b/arch/x86/include/asm/unistd.h index a0790e07ba65..3d5df1c4447f 100644 --- a/arch/x86/include/asm/unistd.h +++ b/arch/x86/include/asm/unistd.h @@ -38,8 +38,6 @@ # define __ARCH_WANT_SYS_OLD_GETRLIMIT # define __ARCH_WANT_SYS_OLD_UNAME # define __ARCH_WANT_SYS_PAUSE -# define __ARCH_WANT_SYS_RT_SIGACTION -# define __ARCH_WANT_SYS_RT_SIGSUSPEND # define __ARCH_WANT_SYS_SGETMASK # define __ARCH_WANT_SYS_SIGNAL # define __ARCH_WANT_SYS_SIGPENDING diff --git a/arch/x86/um/Kconfig b/arch/x86/um/Kconfig index cf0f2731484e..fafc94193bc8 100644 --- a/arch/x86/um/Kconfig +++ b/arch/x86/um/Kconfig @@ -13,7 +13,6 @@ endmenu config UML_X86 def_bool y select GENERIC_FIND_FIRST_BIT - select GENERIC_SIGALTSTACK config 64BIT bool "64-bit kernel" if SUBARCH = "x86" diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 23cc6ae35da0..5aab1acabf1c 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -16,7 +16,6 @@ config XTENSA select ARCH_WANT_OPTIONAL_GPIOLIB select CLONE_BACKWARDS select IRQ_DOMAIN - select GENERIC_SIGALTSTACK help Xtensa processors are 32-bit RISC machines designed by Tensilica primarily for embedded systems. These processors are both diff --git a/arch/xtensa/include/asm/unistd.h b/arch/xtensa/include/asm/unistd.h index eb63ea87815c..c38834de9ac7 100644 --- a/arch/xtensa/include/asm/unistd.h +++ b/arch/xtensa/include/asm/unistd.h @@ -15,8 +15,6 @@ #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_UTIME #define __ARCH_WANT_SYS_LLSEEK -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_SYS_GETPGRP /* diff --git a/include/asm-generic/syscalls.h b/include/asm-generic/syscalls.h index 9e25a3179d6c..1f74be5113b2 100644 --- a/include/asm-generic/syscalls.h +++ b/include/asm-generic/syscalls.h @@ -21,13 +21,6 @@ asmlinkage long sys_mmap(unsigned long addr, unsigned long len, unsigned long fd, off_t pgoff); #endif -#ifndef CONFIG_GENERIC_SIGALTSTACK -#ifndef sys_sigaltstack -asmlinkage long sys_sigaltstack(const stack_t __user *, stack_t __user *, - struct pt_regs *); -#endif -#endif - #ifndef sys_rt_sigreturn asmlinkage long sys_rt_sigreturn(struct pt_regs *regs); #endif diff --git a/include/asm-generic/unistd.h b/include/asm-generic/unistd.h index a36991ab334e..257c55ec4f77 100644 --- a/include/asm-generic/unistd.h +++ b/include/asm-generic/unistd.h @@ -9,9 +9,6 @@ #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_LLSEEK #endif -#define __ARCH_WANT_SYS_RT_SIGACTION -#define __ARCH_WANT_SYS_RT_SIGSUSPEND -#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND /* * "Conditional" syscalls diff --git a/include/linux/compat.h b/include/linux/compat.h index 8de903587fb9..de095b0462a7 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -68,7 +68,6 @@ #ifndef compat_user_stack_pointer #define compat_user_stack_pointer() current_user_stack_pointer() #endif -#ifdef CONFIG_GENERIC_SIGALTSTACK #ifndef compat_sigaltstack /* we'll need that for MIPS */ typedef struct compat_sigaltstack { compat_uptr_t ss_sp; @@ -76,7 +75,6 @@ typedef struct compat_sigaltstack { compat_size_t ss_size; } compat_stack_t; #endif -#endif #define compat_jiffies_to_clock_t(x) \ (((unsigned long)(x) * COMPAT_USER_HZ) / HZ) @@ -142,7 +140,6 @@ typedef struct { compat_sigset_word sig[_COMPAT_NSIG_WORDS]; } compat_sigset_t; -#ifdef CONFIG_GENERIC_COMPAT_RT_SIGACTION struct compat_sigaction { #ifndef __ARCH_HAS_ODD_SIGACTION compat_uptr_t sa_handler; @@ -156,7 +153,6 @@ struct compat_sigaction { #endif compat_sigset_t sa_mask __packed; }; -#endif /* * These functions operate strictly on struct compat_time* @@ -623,27 +619,19 @@ asmlinkage long compat_sys_rt_sigtimedwait(compat_sigset_t __user *uthese, struct compat_timespec __user *uts, compat_size_t sigsetsize); asmlinkage long compat_sys_rt_sigsuspend(compat_sigset_t __user *unewset, compat_size_t sigsetsize); -#ifdef CONFIG_GENERIC_COMPAT_RT_SIGPROCMASK asmlinkage long compat_sys_rt_sigprocmask(int how, compat_sigset_t __user *set, compat_sigset_t __user *oset, compat_size_t sigsetsize); -#endif -#ifdef CONFIG_GENERIC_COMPAT_RT_SIGPENDING asmlinkage long compat_sys_rt_sigpending(compat_sigset_t __user *uset, compat_size_t sigsetsize); -#endif #ifndef CONFIG_ODD_RT_SIGACTION -#ifdef CONFIG_GENERIC_COMPAT_RT_SIGACTION asmlinkage long compat_sys_rt_sigaction(int, const struct compat_sigaction __user *, struct compat_sigaction __user *, compat_size_t); #endif -#endif -#ifdef CONFIG_GENERIC_COMPAT_RT_SIGQUEUEINFO asmlinkage long compat_sys_rt_sigqueueinfo(compat_pid_t pid, int sig, struct compat_siginfo __user *uinfo); -#endif asmlinkage long compat_sys_sysinfo(struct compat_sysinfo __user *info); asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); @@ -694,13 +682,11 @@ asmlinkage ssize_t compat_sys_process_vm_writev(compat_pid_t pid, asmlinkage long compat_sys_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, compat_size_t count); -#ifdef CONFIG_GENERIC_SIGALTSTACK asmlinkage long compat_sys_sigaltstack(const compat_stack_t __user *uss_ptr, compat_stack_t __user *uoss_ptr); int compat_restore_altstack(const compat_stack_t __user *uss); int __compat_save_altstack(compat_stack_t __user *, unsigned long); -#endif asmlinkage long compat_sys_sched_rr_get_interval(compat_pid_t pid, struct compat_timespec __user *interval); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 66d298f69f98..313a8e0a6553 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -300,10 +300,8 @@ asmlinkage long sys_personality(unsigned int personality); asmlinkage long sys_sigpending(old_sigset_t __user *set); asmlinkage long sys_sigprocmask(int how, old_sigset_t __user *set, old_sigset_t __user *oset); -#ifdef CONFIG_GENERIC_SIGALTSTACK asmlinkage long sys_sigaltstack(const struct sigaltstack __user *uss, struct sigaltstack __user *uoss); -#endif asmlinkage long sys_getitimer(int which, struct itimerval __user *value); asmlinkage long sys_setitimer(int which, diff --git a/kernel/signal.c b/kernel/signal.c index 6919050c8156..2b8282bb487c 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2623,7 +2623,6 @@ SYSCALL_DEFINE4(rt_sigprocmask, int, how, sigset_t __user *, nset, } #ifdef CONFIG_COMPAT -#ifdef CONFIG_GENERIC_COMPAT_RT_SIGPROCMASK COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset, compat_sigset_t __user *, oset, compat_size_t, sigsetsize) { @@ -2661,7 +2660,6 @@ COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset, #endif } #endif -#endif static int do_sigpending(void *set, unsigned long sigsetsize) { @@ -2694,7 +2692,6 @@ SYSCALL_DEFINE2(rt_sigpending, sigset_t __user *, uset, size_t, sigsetsize) } #ifdef CONFIG_COMPAT -#ifdef CONFIG_GENERIC_COMPAT_RT_SIGPENDING COMPAT_SYSCALL_DEFINE2(rt_sigpending, compat_sigset_t __user *, uset, compat_size_t, sigsetsize) { @@ -2714,7 +2711,6 @@ COMPAT_SYSCALL_DEFINE2(rt_sigpending, compat_sigset_t __user *, uset, #endif } #endif -#endif #ifndef HAVE_ARCH_COPY_SIGINFO_TO_USER @@ -3024,7 +3020,6 @@ SYSCALL_DEFINE3(rt_sigqueueinfo, pid_t, pid, int, sig, } #ifdef CONFIG_COMPAT -#ifdef CONFIG_GENERIC_COMPAT_RT_SIGQUEUEINFO COMPAT_SYSCALL_DEFINE3(rt_sigqueueinfo, compat_pid_t, pid, int, sig, @@ -3037,7 +3032,6 @@ COMPAT_SYSCALL_DEFINE3(rt_sigqueueinfo, return do_rt_sigqueueinfo(pid, sig, &info); } #endif -#endif static int do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t *info) { @@ -3194,12 +3188,10 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s out: return error; } -#ifdef CONFIG_GENERIC_SIGALTSTACK SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss) { return do_sigaltstack(uss, uoss, current_user_stack_pointer()); } -#endif int restore_altstack(const stack_t __user *uss) { @@ -3217,7 +3209,6 @@ int __save_altstack(stack_t __user *uss, unsigned long sp) } #ifdef CONFIG_COMPAT -#ifdef CONFIG_GENERIC_SIGALTSTACK COMPAT_SYSCALL_DEFINE2(sigaltstack, const compat_stack_t __user *, uss_ptr, compat_stack_t __user *, uoss_ptr) @@ -3267,7 +3258,6 @@ int __compat_save_altstack(compat_stack_t __user *uss, unsigned long sp) __put_user(t->sas_ss_size, &uss->ss_size); } #endif -#endif #ifdef __ARCH_WANT_SYS_SIGPENDING @@ -3368,7 +3358,6 @@ out: return ret; } #ifdef CONFIG_COMPAT -#ifdef CONFIG_GENERIC_COMPAT_RT_SIGACTION COMPAT_SYSCALL_DEFINE4(rt_sigaction, int, sig, const struct compat_sigaction __user *, act, struct compat_sigaction __user *, oact, @@ -3415,7 +3404,6 @@ COMPAT_SYSCALL_DEFINE4(rt_sigaction, int, sig, return ret; } #endif -#endif #endif /* !CONFIG_ODD_RT_SIGACTION */ #ifdef CONFIG_OLD_SIGACTION From cfd60c0789f395aa5564985177e3b4dab21974e1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 7 Nov 2012 17:38:51 -0500 Subject: [PATCH 2677/4607] alpha: pass k_sigaction and siginfo_t using ksignal pointer ... and use the new helpers Signed-off-by: Al Viro --- arch/alpha/kernel/signal.c | 111 +++++++++++++++---------------------- 1 file changed, 45 insertions(+), 66 deletions(-) diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c index b5f385055244..6cec2881acbf 100644 --- a/arch/alpha/kernel/signal.c +++ b/arch/alpha/kernel/signal.c @@ -272,12 +272,9 @@ give_sigsegv: */ static inline void __user * -get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) +get_sigframe(struct ksignal *ksig, unsigned long sp, size_t frame_size) { - if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! sas_ss_flags(sp)) - sp = current->sas_ss_sp + current->sas_ss_size; - - return (void __user *)((sp - frame_size) & -32ul); + return (void __user *)((sigsp(sp, ksig) - frame_size) & -32ul); } static long @@ -338,14 +335,13 @@ setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, } static int -setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, - struct pt_regs *regs) +setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { unsigned long oldsp, r26, err = 0; struct sigframe __user *frame; oldsp = rdusp(); - frame = get_sigframe(ka, oldsp, sizeof(*frame)); + frame = get_sigframe(ksig, oldsp, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; @@ -355,9 +351,8 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, /* Set up to return from userspace. If provided, use a stub already in userspace. */ - if (ka->ka_restorer) { - r26 = (unsigned long) ka->ka_restorer; - } else { + r26 = (unsigned long) ksig->ka.ka_restorer; + if (!r26) { err |= __put_user(INSN_MOV_R30_R16, frame->retcode+0); err |= __put_user(INSN_LDI_R0+__NR_sigreturn, frame->retcode+1); err |= __put_user(INSN_CALLSYS, frame->retcode+2); @@ -371,8 +366,8 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, /* "Return" to the handler */ regs->r26 = r26; - regs->r27 = regs->pc = (unsigned long) ka->sa.sa_handler; - regs->r16 = sig; /* a0: signal number */ + regs->r27 = regs->pc = (unsigned long) ksig->ka.sa.sa_handler; + regs->r16 = ksig->sig; /* a0: signal number */ regs->r17 = 0; /* a1: exception code */ regs->r18 = (unsigned long) &frame->sc; /* a2: sigcontext pointer */ wrusp((unsigned long) frame); @@ -385,18 +380,17 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, } static int -setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs) +setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { unsigned long oldsp, r26, err = 0; struct rt_sigframe __user *frame; oldsp = rdusp(); - frame = get_sigframe(ka, oldsp, sizeof(*frame)); + frame = get_sigframe(ksig, oldsp, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - err |= copy_siginfo_to_user(&frame->info, info); + err |= copy_siginfo_to_user(&frame->info, &ksig->info); /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); @@ -411,9 +405,8 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Set up to return from userspace. If provided, use a stub already in userspace. */ - if (ka->ka_restorer) { - r26 = (unsigned long) ka->ka_restorer; - } else { + r26 = (unsigned long) ksig->ka.ka_restorer; + if (!r26) { err |= __put_user(INSN_MOV_R30_R16, frame->retcode+0); err |= __put_user(INSN_LDI_R0+__NR_rt_sigreturn, frame->retcode+1); @@ -427,8 +420,8 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* "Return" to the handler */ regs->r26 = r26; - regs->r27 = regs->pc = (unsigned long) ka->sa.sa_handler; - regs->r16 = sig; /* a0: signal number */ + regs->r27 = regs->pc = (unsigned long) ksig->ka.sa.sa_handler; + regs->r16 = ksig->sig; /* a0: signal number */ regs->r17 = (unsigned long) &frame->info; /* a1: siginfo pointer */ regs->r18 = (unsigned long) &frame->uc; /* a2: ucontext pointer */ wrusp((unsigned long) frame); @@ -446,22 +439,17 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, * OK, we're invoking a handler. */ static inline void -handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, - struct pt_regs * regs) +handle_signal(struct ksignal *ksig, struct pt_regs *regs) { sigset_t *oldset = sigmask_to_save(); int ret; - if (ka->sa.sa_flags & SA_SIGINFO) - ret = setup_rt_frame(sig, ka, info, oldset, regs); + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + ret = setup_rt_frame(ksig, oldset, regs); else - ret = setup_frame(sig, ka, oldset, regs); + ret = setup_frame(ksig, oldset, regs); - if (ret) { - force_sigsegv(sig, current); - return; - } - signal_delivered(sig, info, ka, regs, 0); + signal_setup_done(ret, ksig, 0); } static inline void @@ -504,47 +492,38 @@ syscall_restart(unsigned long r0, unsigned long r19, static void do_signal(struct pt_regs *regs, unsigned long r0, unsigned long r19) { - siginfo_t info; - int signr; unsigned long single_stepping = ptrace_cancel_bpt(current); - struct k_sigaction ka; + struct ksignal ksig; /* This lets the debugger run, ... */ - signr = get_signal_to_deliver(&info, &ka, regs, NULL); - - /* ... so re-check the single stepping. */ - single_stepping |= ptrace_cancel_bpt(current); - - if (signr > 0) { + if (get_signal(&ksig)) { + /* ... so re-check the single stepping. */ + single_stepping |= ptrace_cancel_bpt(current); /* Whee! Actually deliver the signal. */ if (r0) - syscall_restart(r0, r19, regs, &ka); - handle_signal(signr, &ka, &info, regs); - if (single_stepping) - ptrace_set_bpt(current); /* re-set bpt */ - return; - } - - if (r0) { - switch (regs->r0) { - case ERESTARTNOHAND: - case ERESTARTSYS: - case ERESTARTNOINTR: - /* Reset v0 and a3 and replay syscall. */ - regs->r0 = r0; - regs->r19 = r19; - regs->pc -= 4; - break; - case ERESTART_RESTARTBLOCK: - /* Force v0 to the restart syscall and reply. */ - regs->r0 = __NR_restart_syscall; - regs->pc -= 4; - break; + syscall_restart(r0, r19, regs, &ksig.ka); + handle_signal(&ksig, regs); + } else { + single_stepping |= ptrace_cancel_bpt(current); + if (r0) { + switch (regs->r0) { + case ERESTARTNOHAND: + case ERESTARTSYS: + case ERESTARTNOINTR: + /* Reset v0 and a3 and replay syscall. */ + regs->r0 = r0; + regs->r19 = r19; + regs->pc -= 4; + break; + case ERESTART_RESTARTBLOCK: + /* Set v0 to the restart_syscall and replay */ + regs->r0 = __NR_restart_syscall; + regs->pc -= 4; + break; + } } + restore_saved_sigmask(); } - - /* If there's no signal to deliver, we just restore the saved mask. */ - restore_saved_sigmask(); if (single_stepping) ptrace_set_bpt(current); /* re-set breakpoint */ } From 7e243643dffbe216dbcb10933c4cafde0f95f537 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 7 Nov 2012 17:53:13 -0500 Subject: [PATCH 2678/4607] arm: switch to struct ksignal * passing Signed-off-by: Al Viro --- arch/arm/kernel/signal.c | 110 +++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 62 deletions(-) diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 07429a23c720..296786bdbb73 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -318,17 +318,11 @@ setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs, sigset_t *set) } static inline void __user * -get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize) +get_sigframe(struct ksignal *ksig, struct pt_regs *regs, int framesize) { - unsigned long sp = regs->ARM_sp; + unsigned long sp = sigsp(regs->ARM_sp, ksig); void __user *frame; - /* - * This is the X/Open sanctioned signal stack switching. - */ - if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) - sp = current->sas_ss_sp + current->sas_ss_size; - /* * ATPCS B01 mandates 8-byte alignment */ @@ -343,11 +337,22 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize) return frame; } -static int -setup_return(struct pt_regs *regs, struct k_sigaction *ka, - unsigned long __user *rc, void __user *frame, int usig) +/* + * translate the signal + */ +static inline int map_sig(int sig) { - unsigned long handler = (unsigned long)ka->sa.sa_handler; + struct thread_info *thread = current_thread_info(); + if (sig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap) + sig = thread->exec_domain->signal_invmap[sig]; + return sig; +} + +static int +setup_return(struct pt_regs *regs, struct ksignal *ksig, + unsigned long __user *rc, void __user *frame) +{ + unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler; unsigned long retcode; int thumb = 0; unsigned long cpsr = regs->ARM_cpsr & ~(PSR_f | PSR_E_BIT); @@ -357,7 +362,7 @@ setup_return(struct pt_regs *regs, struct k_sigaction *ka, /* * Maybe we need to deliver a 32-bit signal to a 26-bit task. */ - if (ka->sa.sa_flags & SA_THIRTYTWO) + if (ksig->ka.sa.sa_flags & SA_THIRTYTWO) cpsr = (cpsr & ~MODE_MASK) | USR_MODE; #ifdef CONFIG_ARM_THUMB @@ -379,12 +384,12 @@ setup_return(struct pt_regs *regs, struct k_sigaction *ka, } #endif - if (ka->sa.sa_flags & SA_RESTORER) { - retcode = (unsigned long)ka->sa.sa_restorer; + if (ksig->ka.sa.sa_flags & SA_RESTORER) { + retcode = (unsigned long)ksig->ka.sa.sa_restorer; } else { unsigned int idx = thumb << 1; - if (ka->sa.sa_flags & SA_SIGINFO) + if (ksig->ka.sa.sa_flags & SA_SIGINFO) idx += 3; if (__put_user(sigreturn_codes[idx], rc) || @@ -409,7 +414,7 @@ setup_return(struct pt_regs *regs, struct k_sigaction *ka, } } - regs->ARM_r0 = usig; + regs->ARM_r0 = map_sig(ksig->sig); regs->ARM_sp = (unsigned long)frame; regs->ARM_lr = retcode; regs->ARM_pc = handler; @@ -419,9 +424,9 @@ setup_return(struct pt_regs *regs, struct k_sigaction *ka, } static int -setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs) +setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { - struct sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame)); + struct sigframe __user *frame = get_sigframe(ksig, regs, sizeof(*frame)); int err = 0; if (!frame) @@ -434,22 +439,21 @@ setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *reg err |= setup_sigframe(frame, regs, set); if (err == 0) - err = setup_return(regs, ka, frame->retcode, frame, usig); + err = setup_return(regs, ksig, frame->retcode, frame); return err; } static int -setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs) +setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { - struct rt_sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame)); + struct rt_sigframe __user *frame = get_sigframe(ksig, regs, sizeof(*frame)); int err = 0; if (!frame) return 1; - err |= copy_siginfo_to_user(&frame->info, info); + err |= copy_siginfo_to_user(&frame->info, &ksig->info); __put_user_error(0, &frame->sig.uc.uc_flags, err); __put_user_error(NULL, &frame->sig.uc.uc_link, err); @@ -457,7 +461,7 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, err |= __save_altstack(&frame->sig.uc.uc_stack, regs->ARM_sp); err |= setup_sigframe(&frame->sig, regs, set); if (err == 0) - err = setup_return(regs, ka, frame->sig.retcode, frame, usig); + err = setup_return(regs, ksig, frame->sig.retcode, frame); if (err == 0) { /* @@ -475,40 +479,25 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, /* * OK, we're invoking a handler */ -static void -handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, struct pt_regs *regs) +static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) { - struct thread_info *thread = current_thread_info(); - struct task_struct *tsk = current; sigset_t *oldset = sigmask_to_save(); - int usig = sig; int ret; - /* - * translate the signal - */ - if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap) - usig = thread->exec_domain->signal_invmap[usig]; - /* * Set up the stack frame */ - if (ka->sa.sa_flags & SA_SIGINFO) - ret = setup_rt_frame(usig, ka, info, oldset, regs); + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + ret = setup_rt_frame(ksig, oldset, regs); else - ret = setup_frame(usig, ka, oldset, regs); + ret = setup_frame(ksig, oldset, regs); /* * Check that the resulting registers are actually sane. */ ret |= !valid_user_regs(regs); - if (ret != 0) { - force_sigsegv(sig, tsk); - return; - } - signal_delivered(sig, info, ka, regs, 0); + signal_setup_done(ret, ksig, 0); } /* @@ -523,9 +512,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka, static int do_signal(struct pt_regs *regs, int syscall) { unsigned int retval = 0, continue_addr = 0, restart_addr = 0; - struct k_sigaction ka; - siginfo_t info; - int signr; + struct ksignal ksig; int restart = 0; /* @@ -557,33 +544,32 @@ static int do_signal(struct pt_regs *regs, int syscall) * Get the signal to deliver. When running under ptrace, at this * point the debugger may change all our registers ... */ - signr = get_signal_to_deliver(&info, &ka, regs, NULL); /* * Depending on the signal settings we may need to revert the * decision to restart the system call. But skip this if a * debugger has chosen to restart at a different PC. */ - if (regs->ARM_pc != restart_addr) - restart = 0; - if (signr > 0) { - if (unlikely(restart)) { + if (get_signal(&ksig)) { + /* handler */ + if (unlikely(restart) && regs->ARM_pc == restart_addr) { if (retval == -ERESTARTNOHAND || retval == -ERESTART_RESTARTBLOCK || (retval == -ERESTARTSYS - && !(ka.sa.sa_flags & SA_RESTART))) { + && !(ksig.ka.sa.sa_flags & SA_RESTART))) { regs->ARM_r0 = -EINTR; regs->ARM_pc = continue_addr; } } - - handle_signal(signr, &ka, &info, regs); - return 0; + handle_signal(&ksig, regs); + } else { + /* no handler */ + restore_saved_sigmask(); + if (unlikely(restart) && regs->ARM_pc == restart_addr) { + regs->ARM_pc = continue_addr; + return restart; + } } - - restore_saved_sigmask(); - if (unlikely(restart)) - regs->ARM_pc = continue_addr; - return restart; + return 0; } asmlinkage int From 08f739570de697dc06b949ba3be33acdda21498c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 7 Nov 2012 23:48:13 -0500 Subject: [PATCH 2679/4607] sparc: convert to ksignal Signed-off-by: Al Viro --- arch/sparc/kernel/signal32.c | 154 ++++++++++++++-------------------- arch/sparc/kernel/signal_32.c | 146 +++++++++++++------------------- arch/sparc/kernel/signal_64.c | 118 ++++++++++---------------- 3 files changed, 164 insertions(+), 254 deletions(-) diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c index 9d9eb91d0de1..cd5dc4d411d1 100644 --- a/arch/sparc/kernel/signal32.c +++ b/arch/sparc/kernel/signal32.c @@ -323,7 +323,7 @@ static int invalid_frame_pointer(void __user *fp, int fplen) return 0; } -static void __user *get_sigframe(struct sigaction *sa, struct pt_regs *regs, unsigned long framesize) +static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize) { unsigned long sp; @@ -338,12 +338,7 @@ static void __user *get_sigframe(struct sigaction *sa, struct pt_regs *regs, uns return (void __user *) -1L; /* This is the X/Open sanctioned signal stack switching. */ - if (sa->sa_flags & SA_ONSTACK) { - if (sas_ss_flags(sp) == 0) - sp = current->sas_ss_sp + current->sas_ss_size; - } - - sp -= framesize; + sp = sigsp(sp, ksig) - framesize; /* Always align the stack frame. This handles two cases. First, * sigaltstack need not be mindful of platform specific stack @@ -414,8 +409,8 @@ out_irqs_on: } -static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, - int signo, sigset_t *oldset) +static int setup_frame32(struct ksignal *ksig, struct pt_regs *regs, + sigset_t *oldset) { struct signal_frame32 __user *sf; int i, err, wsaved; @@ -437,10 +432,12 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, sigframe_size += sizeof(__siginfo_rwin_t); sf = (struct signal_frame32 __user *) - get_sigframe(&ka->sa, regs, sigframe_size); + get_sigframe(ksig, regs, sigframe_size); - if (invalid_frame_pointer(sf, sigframe_size)) - goto sigill; + if (invalid_frame_pointer(sf, sigframe_size)) { + do_exit(SIGILL); + return -EINVAL; + } tail = (sf + 1); @@ -514,16 +511,16 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, err |= __put_user(rp->ins[7], &sf->ss.callers_pc); } if (err) - goto sigsegv; + return err; /* 3. signal handler back-trampoline and parameters */ regs->u_regs[UREG_FP] = (unsigned long) sf; - regs->u_regs[UREG_I0] = signo; + regs->u_regs[UREG_I0] = ksig->sig; regs->u_regs[UREG_I1] = (unsigned long) &sf->info; regs->u_regs[UREG_I2] = (unsigned long) &sf->info; /* 4. signal handler */ - regs->tpc = (unsigned long) ka->sa.sa_handler; + regs->tpc = (unsigned long) ksig->ka.sa.sa_handler; regs->tnpc = (regs->tpc + 4); if (test_thread_flag(TIF_32BIT)) { regs->tpc &= 0xffffffff; @@ -531,8 +528,8 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, } /* 5. return to kernel instructions */ - if (ka->ka_restorer) { - regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; + if (ksig->ka.ka_restorer) { + regs->u_regs[UREG_I7] = (unsigned long)ksig->ka.ka_restorer; } else { unsigned long address = ((unsigned long)&(sf->insns[0])); @@ -541,23 +538,14 @@ static int setup_frame32(struct k_sigaction *ka, struct pt_regs *regs, err = __put_user(0x821020d8, &sf->insns[0]); /*mov __NR_sigreturn, %g1*/ err |= __put_user(0x91d02010, &sf->insns[1]); /*t 0x10*/ if (err) - goto sigsegv; + return err; flush_signal_insns(address); } return 0; - -sigill: - do_exit(SIGILL); - return -EINVAL; - -sigsegv: - force_sigsegv(signo, current); - return -EFAULT; } -static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, - unsigned long signr, sigset_t *oldset, - siginfo_t *info) +static int setup_rt_frame32(struct ksignal *ksig, struct pt_regs *regs, + sigset_t *oldset) { struct rt_signal_frame32 __user *sf; int i, err, wsaved; @@ -579,10 +567,12 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, sigframe_size += sizeof(__siginfo_rwin_t); sf = (struct rt_signal_frame32 __user *) - get_sigframe(&ka->sa, regs, sigframe_size); + get_sigframe(ksig, regs, sigframe_size); - if (invalid_frame_pointer(sf, sigframe_size)) - goto sigill; + if (invalid_frame_pointer(sf, sigframe_size)) { + do_exit(SIGILL); + return -EINVAL; + } tail = (sf + 1); @@ -627,7 +617,7 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, } /* Update the siginfo structure. */ - err |= copy_siginfo_to_user32(&sf->info, info); + err |= copy_siginfo_to_user32(&sf->info, &ksig->info); /* Setup sigaltstack */ err |= __compat_save_altstack(&sf->stack, regs->u_regs[UREG_FP]); @@ -660,16 +650,16 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, err |= __put_user(rp->ins[7], &sf->ss.callers_pc); } if (err) - goto sigsegv; + return err; /* 3. signal handler back-trampoline and parameters */ regs->u_regs[UREG_FP] = (unsigned long) sf; - regs->u_regs[UREG_I0] = signr; + regs->u_regs[UREG_I0] = ksig->sig; regs->u_regs[UREG_I1] = (unsigned long) &sf->info; regs->u_regs[UREG_I2] = (unsigned long) &sf->regs; /* 4. signal handler */ - regs->tpc = (unsigned long) ka->sa.sa_handler; + regs->tpc = (unsigned long) ksig->ka.sa.sa_handler; regs->tnpc = (regs->tpc + 4); if (test_thread_flag(TIF_32BIT)) { regs->tpc &= 0xffffffff; @@ -677,8 +667,8 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, } /* 5. return to kernel instructions */ - if (ka->ka_restorer) - regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; + if (ksig->ka.ka_restorer) + regs->u_regs[UREG_I7] = (unsigned long)ksig->ka.ka_restorer; else { unsigned long address = ((unsigned long)&(sf->insns[0])); @@ -690,36 +680,25 @@ static int setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, /* t 0x10 */ err |= __put_user(0x91d02010, &sf->insns[1]); if (err) - goto sigsegv; + return err; flush_signal_insns(address); } return 0; - -sigill: - do_exit(SIGILL); - return -EINVAL; - -sigsegv: - force_sigsegv(signr, current); - return -EFAULT; } -static inline void handle_signal32(unsigned long signr, struct k_sigaction *ka, - siginfo_t *info, - sigset_t *oldset, struct pt_regs *regs) +static inline void handle_signal32(struct ksignal *ksig, + struct pt_regs *regs) { + sigset_t *oldset = sigmask_to_save(); int err; - if (ka->sa.sa_flags & SA_SIGINFO) - err = setup_rt_frame32(ka, regs, signr, oldset, info); + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + err = setup_rt_frame32(ksig, regs, oldset); else - err = setup_frame32(ka, regs, signr, oldset); + err = setup_frame32(ksig, regs, oldset); - if (err) - return; - - signal_delivered(signr, info, ka, regs, 0); + signal_setup_done(err, ksig, 0); } static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs, @@ -749,50 +728,41 @@ static inline void syscall_restart32(unsigned long orig_i0, struct pt_regs *regs */ void do_signal32(sigset_t *oldset, struct pt_regs * regs) { - struct k_sigaction ka; - unsigned long orig_i0; - int restart_syscall; - siginfo_t info; - int signr; - - signr = get_signal_to_deliver(&info, &ka, regs, NULL); + struct ksignal ksig; + unsigned long orig_i0 = 0; + int restart_syscall = 0; + bool has_handler = get_signal(&ksig); - restart_syscall = 0; - orig_i0 = 0; if (pt_regs_is_syscall(regs) && (regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) { restart_syscall = 1; orig_i0 = regs->u_regs[UREG_G6]; } - if (signr > 0) { + if (has_handler) { if (restart_syscall) - syscall_restart32(orig_i0, regs, &ka.sa); - handle_signal32(signr, &ka, &info, oldset, regs); - return; + syscall_restart32(orig_i0, regs, &ksig.ka.sa); + handle_signal32(&ksig, regs); + } else { + if (restart_syscall) { + switch (regs->u_regs[UREG_I0]) { + case ERESTARTNOHAND: + case ERESTARTSYS: + case ERESTARTNOINTR: + /* replay the system call when we are done */ + regs->u_regs[UREG_I0] = orig_i0; + regs->tpc -= 4; + regs->tnpc -= 4; + pt_regs_clear_syscall(regs); + case ERESTART_RESTARTBLOCK: + regs->u_regs[UREG_G1] = __NR_restart_syscall; + regs->tpc -= 4; + regs->tnpc -= 4; + pt_regs_clear_syscall(regs); + } + } + restore_saved_sigmask(); } - if (restart_syscall && - (regs->u_regs[UREG_I0] == ERESTARTNOHAND || - regs->u_regs[UREG_I0] == ERESTARTSYS || - regs->u_regs[UREG_I0] == ERESTARTNOINTR)) { - /* replay the system call when we are done */ - regs->u_regs[UREG_I0] = orig_i0; - regs->tpc -= 4; - regs->tnpc -= 4; - pt_regs_clear_syscall(regs); - } - if (restart_syscall && - regs->u_regs[UREG_I0] == ERESTART_RESTARTBLOCK) { - regs->u_regs[UREG_G1] = __NR_restart_syscall; - regs->tpc -= 4; - regs->tnpc -= 4; - pt_regs_clear_syscall(regs); - } - - /* If there's no signal to deliver, we just put the saved sigmask - * back - */ - restore_saved_sigmask(); } struct sigstack32 { diff --git a/arch/sparc/kernel/signal_32.c b/arch/sparc/kernel/signal_32.c index cd1823487759..7d5d8e1f8415 100644 --- a/arch/sparc/kernel/signal_32.c +++ b/arch/sparc/kernel/signal_32.c @@ -186,7 +186,7 @@ static inline int invalid_frame_pointer(void __user *fp, int fplen) return 0; } -static inline void __user *get_sigframe(struct sigaction *sa, struct pt_regs *regs, unsigned long framesize) +static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize) { unsigned long sp = regs->u_regs[UREG_FP]; @@ -198,12 +198,7 @@ static inline void __user *get_sigframe(struct sigaction *sa, struct pt_regs *re return (void __user *) -1L; /* This is the X/Open sanctioned signal stack switching. */ - if (sa->sa_flags & SA_ONSTACK) { - if (sas_ss_flags(sp) == 0) - sp = current->sas_ss_sp + current->sas_ss_size; - } - - sp -= framesize; + sp = sigsp(sp, ksig) - framesize; /* Always align the stack frame. This handles two cases. First, * sigaltstack need not be mindful of platform specific stack @@ -216,8 +211,8 @@ static inline void __user *get_sigframe(struct sigaction *sa, struct pt_regs *re return (void __user *) sp; } -static int setup_frame(struct k_sigaction *ka, struct pt_regs *regs, - int signo, sigset_t *oldset) +static int setup_frame(struct ksignal *ksig, struct pt_regs *regs, + sigset_t *oldset) { struct signal_frame __user *sf; int sigframe_size, err, wsaved; @@ -235,10 +230,12 @@ static int setup_frame(struct k_sigaction *ka, struct pt_regs *regs, sigframe_size += sizeof(__siginfo_rwin_t); sf = (struct signal_frame __user *) - get_sigframe(&ka->sa, regs, sigframe_size); + get_sigframe(ksig, regs, sigframe_size); - if (invalid_frame_pointer(sf, sigframe_size)) - goto sigill_and_return; + if (invalid_frame_pointer(sf, sigframe_size)) { + do_exit(SIGILL); + return -EINVAL; + } tail = sf + 1; @@ -277,21 +274,21 @@ static int setup_frame(struct k_sigaction *ka, struct pt_regs *regs, err |= __copy_to_user(sf, rp, sizeof(struct reg_window32)); } if (err) - goto sigsegv; + return err; /* 3. signal handler back-trampoline and parameters */ regs->u_regs[UREG_FP] = (unsigned long) sf; - regs->u_regs[UREG_I0] = signo; + regs->u_regs[UREG_I0] = ksig->sig; regs->u_regs[UREG_I1] = (unsigned long) &sf->info; regs->u_regs[UREG_I2] = (unsigned long) &sf->info; /* 4. signal handler */ - regs->pc = (unsigned long) ka->sa.sa_handler; + regs->pc = (unsigned long) ksig->ka.sa.sa_handler; regs->npc = (regs->pc + 4); /* 5. return to kernel instructions */ - if (ka->ka_restorer) - regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; + if (ksig->ka.ka_restorer) + regs->u_regs[UREG_I7] = (unsigned long)ksig->ka.ka_restorer; else { regs->u_regs[UREG_I7] = (unsigned long)(&(sf->insns[0]) - 2); @@ -301,24 +298,16 @@ static int setup_frame(struct k_sigaction *ka, struct pt_regs *regs, /* t 0x10 */ err |= __put_user(0x91d02010, &sf->insns[1]); if (err) - goto sigsegv; + return err; /* Flush instruction space. */ flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); } return 0; - -sigill_and_return: - do_exit(SIGILL); - return -EINVAL; - -sigsegv: - force_sigsegv(signo, current); - return -EFAULT; } -static int setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, - int signo, sigset_t *oldset, siginfo_t *info) +static int setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs, + sigset_t *oldset) { struct rt_signal_frame __user *sf; int sigframe_size, wsaved; @@ -334,9 +323,11 @@ static int setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, if (wsaved) sigframe_size += sizeof(__siginfo_rwin_t); sf = (struct rt_signal_frame __user *) - get_sigframe(&ka->sa, regs, sigframe_size); - if (invalid_frame_pointer(sf, sigframe_size)) - goto sigill; + get_sigframe(ksig, regs, sigframe_size); + if (invalid_frame_pointer(sf, sigframe_size)) { + do_exit(SIGILL); + return -EINVAL; + } tail = sf + 1; err = __put_user(regs->pc, &sf->regs.pc); @@ -380,21 +371,21 @@ static int setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, err |= __copy_to_user(sf, rp, sizeof(struct reg_window32)); } - err |= copy_siginfo_to_user(&sf->info, info); + err |= copy_siginfo_to_user(&sf->info, &ksig->info); if (err) - goto sigsegv; + return err; regs->u_regs[UREG_FP] = (unsigned long) sf; - regs->u_regs[UREG_I0] = signo; + regs->u_regs[UREG_I0] = ksig->sig; regs->u_regs[UREG_I1] = (unsigned long) &sf->info; regs->u_regs[UREG_I2] = (unsigned long) &sf->regs; - regs->pc = (unsigned long) ka->sa.sa_handler; + regs->pc = (unsigned long) ksig->ka.sa.sa_handler; regs->npc = (regs->pc + 4); - if (ka->ka_restorer) - regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; + if (ksig->ka.ka_restorer) + regs->u_regs[UREG_I7] = (unsigned long)ksig->ka.ka_restorer; else { regs->u_regs[UREG_I7] = (unsigned long)(&(sf->insns[0]) - 2); @@ -404,38 +395,25 @@ static int setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, /* t 0x10 */ err |= __put_user(0x91d02010, &sf->insns[1]); if (err) - goto sigsegv; + return err; /* Flush instruction space. */ flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); } return 0; - -sigill: - do_exit(SIGILL); - return -EINVAL; - -sigsegv: - force_sigsegv(signo, current); - return -EFAULT; } static inline void -handle_signal(unsigned long signr, struct k_sigaction *ka, - siginfo_t *info, struct pt_regs *regs) +handle_signal(struct ksignal *ksig, struct pt_regs *regs) { sigset_t *oldset = sigmask_to_save(); int err; - if (ka->sa.sa_flags & SA_SIGINFO) - err = setup_rt_frame(ka, regs, signr, oldset, info); + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + err = setup_rt_frame(ksig, regs, oldset); else - err = setup_frame(ka, regs, signr, oldset); - - if (err) - return; - - signal_delivered(signr, info, ka, regs, 0); + err = setup_frame(ksig, regs, oldset); + signal_setup_done(err, ksig, 0); } static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, @@ -465,10 +443,9 @@ static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, */ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) { - struct k_sigaction ka; + struct ksignal ksig; int restart_syscall; - siginfo_t info; - int signr; + bool has_handler; /* It's a lot of work and synchronization to add a new ptrace * register for GDB to save and restore in order to get @@ -491,7 +468,7 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) if (pt_regs_is_syscall(regs) && (regs->psr & PSR_C)) regs->u_regs[UREG_G6] = orig_i0; - signr = get_signal_to_deliver(&info, &ka, regs, NULL); + has_handler = get_signal(&ksig); /* If the debugger messes with the program counter, it clears * the software "in syscall" bit, directing us to not perform @@ -503,35 +480,30 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) orig_i0 = regs->u_regs[UREG_G6]; } - - if (signr > 0) { + if (has_handler) { if (restart_syscall) - syscall_restart(orig_i0, regs, &ka.sa); - handle_signal(signr, &ka, &info, regs); - return; + syscall_restart(orig_i0, regs, &ksig.ka.sa); + handle_signal(&ksig, regs); + } else { + if (restart_syscall) { + switch (regs->u_regs[UREG_I0]) { + case ERESTARTNOHAND: + case ERESTARTSYS: + case ERESTARTNOINTR: + /* replay the system call when we are done */ + regs->u_regs[UREG_I0] = orig_i0; + regs->pc -= 4; + regs->npc -= 4; + pt_regs_clear_syscall(regs); + case ERESTART_RESTARTBLOCK: + regs->u_regs[UREG_G1] = __NR_restart_syscall; + regs->pc -= 4; + regs->npc -= 4; + pt_regs_clear_syscall(regs); + } + } + restore_saved_sigmask(); } - if (restart_syscall && - (regs->u_regs[UREG_I0] == ERESTARTNOHAND || - regs->u_regs[UREG_I0] == ERESTARTSYS || - regs->u_regs[UREG_I0] == ERESTARTNOINTR)) { - /* replay the system call when we are done */ - regs->u_regs[UREG_I0] = orig_i0; - regs->pc -= 4; - regs->npc -= 4; - pt_regs_clear_syscall(regs); - } - if (restart_syscall && - regs->u_regs[UREG_I0] == ERESTART_RESTARTBLOCK) { - regs->u_regs[UREG_G1] = __NR_restart_syscall; - regs->pc -= 4; - regs->npc -= 4; - pt_regs_clear_syscall(regs); - } - - /* if there's no signal to deliver, we just put the saved sigmask - * back - */ - restore_saved_sigmask(); } void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, diff --git a/arch/sparc/kernel/signal_64.c b/arch/sparc/kernel/signal_64.c index 165a897a4133..35923e8abd82 100644 --- a/arch/sparc/kernel/signal_64.c +++ b/arch/sparc/kernel/signal_64.c @@ -308,7 +308,7 @@ static int invalid_frame_pointer(void __user *fp) return 0; } -static inline void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, unsigned long framesize) +static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize) { unsigned long sp = regs->u_regs[UREG_FP] + STACK_BIAS; @@ -320,12 +320,7 @@ static inline void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs * return (void __user *) -1L; /* This is the X/Open sanctioned signal stack switching. */ - if (ka->sa.sa_flags & SA_ONSTACK) { - if (sas_ss_flags(sp) == 0) - sp = current->sas_ss_sp + current->sas_ss_size; - } - - sp -= framesize; + sp = sigsp(sp, ksig) - framesize; /* Always align the stack frame. This handles two cases. First, * sigaltstack need not be mindful of platform specific stack @@ -339,8 +334,7 @@ static inline void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs * } static inline int -setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, - int signo, sigset_t *oldset, siginfo_t *info) +setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs) { struct rt_signal_frame __user *sf; int wsaved, err, sf_size; @@ -358,10 +352,12 @@ setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, if (wsaved) sf_size += sizeof(__siginfo_rwin_t); sf = (struct rt_signal_frame __user *) - get_sigframe(ka, regs, sf_size); + get_sigframe(ksig, regs, sf_size); - if (invalid_frame_pointer (sf)) - goto sigill; + if (invalid_frame_pointer (sf)) { + do_exit(SIGILL); /* won't return, actually */ + return -EINVAL; + } tail = (sf + 1); @@ -389,7 +385,7 @@ setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, /* Setup sigaltstack */ err |= __save_altstack(&sf->stack, regs->u_regs[UREG_FP]); - err |= copy_to_user(&sf->mask, oldset, sizeof(sigset_t)); + err |= copy_to_user(&sf->mask, sigmask_to_save(), sizeof(sigset_t)); if (!wsaved) { err |= copy_in_user((u64 __user *)sf, @@ -402,18 +398,18 @@ setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, rp = ¤t_thread_info()->reg_window[wsaved - 1]; err |= copy_to_user(sf, rp, sizeof(struct reg_window)); } - if (info) - err |= copy_siginfo_to_user(&sf->info, info); + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + err |= copy_siginfo_to_user(&sf->info, &ksig->info); else { - err |= __put_user(signo, &sf->info.si_signo); + err |= __put_user(ksig->sig, &sf->info.si_signo); err |= __put_user(SI_NOINFO, &sf->info.si_code); } if (err) - goto sigsegv; + return err; /* 3. signal handler back-trampoline and parameters */ regs->u_regs[UREG_FP] = ((unsigned long) sf) - STACK_BIAS; - regs->u_regs[UREG_I0] = signo; + regs->u_regs[UREG_I0] = ksig->sig; regs->u_regs[UREG_I1] = (unsigned long) &sf->info; /* The sigcontext is passed in this way because of how it @@ -423,37 +419,15 @@ setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, regs->u_regs[UREG_I2] = (unsigned long) &sf->info; /* 5. signal handler */ - regs->tpc = (unsigned long) ka->sa.sa_handler; + regs->tpc = (unsigned long) ksig->ka.sa.sa_handler; regs->tnpc = (regs->tpc + 4); if (test_thread_flag(TIF_32BIT)) { regs->tpc &= 0xffffffff; regs->tnpc &= 0xffffffff; } /* 4. return to kernel instructions */ - regs->u_regs[UREG_I7] = (unsigned long)ka->ka_restorer; + regs->u_regs[UREG_I7] = (unsigned long)ksig->ka.ka_restorer; return 0; - -sigill: - do_exit(SIGILL); - return -EINVAL; - -sigsegv: - force_sigsegv(signo, current); - return -EFAULT; -} - -static inline void handle_signal(unsigned long signr, struct k_sigaction *ka, - siginfo_t *info, - sigset_t *oldset, struct pt_regs *regs) -{ - int err; - - err = setup_rt_frame(ka, regs, signr, oldset, - (ka->sa.sa_flags & SA_SIGINFO) ? info : NULL); - if (err) - return; - - signal_delivered(signr, info, ka, regs, 0); } static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, @@ -483,11 +457,9 @@ static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, */ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) { - struct k_sigaction ka; + struct ksignal ksig; int restart_syscall; - sigset_t *oldset = sigmask_to_save(); - siginfo_t info; - int signr; + bool has_handler; /* It's a lot of work and synchronization to add a new ptrace * register for GDB to save and restore in order to get @@ -513,13 +485,13 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) #ifdef CONFIG_COMPAT if (test_thread_flag(TIF_32BIT)) { - extern void do_signal32(sigset_t *, struct pt_regs *); - do_signal32(oldset, regs); + extern void do_signal32(struct pt_regs *); + do_signal32(regs); return; } #endif - signr = get_signal_to_deliver(&info, &ka, regs, NULL); + has_handler = get_signal(&ksig); restart_syscall = 0; if (pt_regs_is_syscall(regs) && @@ -528,34 +500,30 @@ static void do_signal(struct pt_regs *regs, unsigned long orig_i0) orig_i0 = regs->u_regs[UREG_G6]; } - if (signr > 0) { + if (has_handler) { if (restart_syscall) - syscall_restart(orig_i0, regs, &ka.sa); - handle_signal(signr, &ka, &info, oldset, regs); - return; + syscall_restart(orig_i0, regs, &ksig.ka.sa); + signal_setup_done(setup_rt_frame(&ksig, regs), &ksig, 0); + } else { + if (restart_syscall) { + switch (regs->u_regs[UREG_I0]) { + case ERESTARTNOHAND: + case ERESTARTSYS: + case ERESTARTNOINTR: + /* replay the system call when we are done */ + regs->u_regs[UREG_I0] = orig_i0; + regs->tpc -= 4; + regs->tnpc -= 4; + pt_regs_clear_syscall(regs); + case ERESTART_RESTARTBLOCK: + regs->u_regs[UREG_G1] = __NR_restart_syscall; + regs->tpc -= 4; + regs->tnpc -= 4; + pt_regs_clear_syscall(regs); + } + } + restore_saved_sigmask(); } - if (restart_syscall && - (regs->u_regs[UREG_I0] == ERESTARTNOHAND || - regs->u_regs[UREG_I0] == ERESTARTSYS || - regs->u_regs[UREG_I0] == ERESTARTNOINTR)) { - /* replay the system call when we are done */ - regs->u_regs[UREG_I0] = orig_i0; - regs->tpc -= 4; - regs->tnpc -= 4; - pt_regs_clear_syscall(regs); - } - if (restart_syscall && - regs->u_regs[UREG_I0] == ERESTART_RESTARTBLOCK) { - regs->u_regs[UREG_G1] = __NR_restart_syscall; - regs->tpc -= 4; - regs->tnpc -= 4; - pt_regs_clear_syscall(regs); - } - - /* If there's no signal to deliver, we just put the saved sigmask - * back - */ - restore_saved_sigmask(); } void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long thread_info_flags) From 235b80226b986dabcbba844968f7807866bd0bfe Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 9 Nov 2012 23:51:47 -0500 Subject: [PATCH 2680/4607] x86: convert to ksignal Signed-off-by: Al Viro --- arch/x86/ia32/ia32_signal.c | 37 ++++----- arch/x86/include/asm/fpu-internal.h | 5 +- arch/x86/kernel/signal.c | 117 +++++++++++++--------------- 3 files changed, 74 insertions(+), 85 deletions(-) diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c index b0460cd7de5a..cf1a471a18a2 100644 --- a/arch/x86/ia32/ia32_signal.c +++ b/arch/x86/ia32/ia32_signal.c @@ -309,7 +309,7 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc, /* * Determine which stack to use.. */ -static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, +static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, size_t frame_size, void __user **fpstate) { @@ -319,16 +319,13 @@ static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, sp = regs->sp; /* This is the X/Open sanctioned signal stack switching. */ - if (ka->sa.sa_flags & SA_ONSTACK) { - if (sas_ss_flags(sp) == 0) - sp = current->sas_ss_sp + current->sas_ss_size; - } - + if (ksig->ka.sa.sa_flags & SA_ONSTACK) + sp = sigsp(sp, ksig); /* This is the legacy signal stack switching. */ else if ((regs->ss & 0xffff) != __USER32_DS && - !(ka->sa.sa_flags & SA_RESTORER) && - ka->sa.sa_restorer) - sp = (unsigned long) ka->sa.sa_restorer; + !(ksig->ka.sa.sa_flags & SA_RESTORER) && + ksig->ka.sa.sa_restorer) + sp = (unsigned long) ksig->ka.sa.sa_restorer; if (used_math()) { unsigned long fx_aligned, math_size; @@ -347,7 +344,7 @@ static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, return (void __user *) sp; } -int ia32_setup_frame(int sig, struct k_sigaction *ka, +int ia32_setup_frame(int sig, struct ksignal *ksig, compat_sigset_t *set, struct pt_regs *regs) { struct sigframe_ia32 __user *frame; @@ -366,7 +363,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, 0x80cd, /* int $0x80 */ }; - frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); + frame = get_sigframe(ksig, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; @@ -383,8 +380,8 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, return -EFAULT; } - if (ka->sa.sa_flags & SA_RESTORER) { - restorer = ka->sa.sa_restorer; + if (ksig->ka.sa.sa_flags & SA_RESTORER) { + restorer = ksig->ka.sa.sa_restorer; } else { /* Return stub is in 32bit vsyscall page */ if (current->mm->context.vdso) @@ -409,7 +406,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, /* Set up registers for signal handler */ regs->sp = (unsigned long) frame; - regs->ip = (unsigned long) ka->sa.sa_handler; + regs->ip = (unsigned long) ksig->ka.sa.sa_handler; /* Make -mregparm=3 work */ regs->ax = sig; @@ -425,7 +422,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, return 0; } -int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, +int ia32_setup_rt_frame(int sig, struct ksignal *ksig, compat_sigset_t *set, struct pt_regs *regs) { struct rt_sigframe_ia32 __user *frame; @@ -446,7 +443,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, 0, }; - frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); + frame = get_sigframe(ksig, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; @@ -464,8 +461,8 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, put_user_ex(0, &frame->uc.uc_link); err |= __compat_save_altstack(&frame->uc.uc_stack, regs->sp); - if (ka->sa.sa_flags & SA_RESTORER) - restorer = ka->sa.sa_restorer; + if (ksig->ka.sa.sa_flags & SA_RESTORER) + restorer = ksig->ka.sa.sa_restorer; else restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn); @@ -478,7 +475,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, put_user_ex(*((u64 *)&code), (u64 __user *)frame->retcode); } put_user_catch(err); - err |= copy_siginfo_to_user32(&frame->info, info); + err |= copy_siginfo_to_user32(&frame->info, &ksig->info); err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate, regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); @@ -488,7 +485,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Set up registers for signal handler */ regs->sp = (unsigned long) frame; - regs->ip = (unsigned long) ka->sa.sa_handler; + regs->ip = (unsigned long) ksig->ka.sa.sa_handler; /* Make -mregparm=3 work */ regs->ax = sig; diff --git a/arch/x86/include/asm/fpu-internal.h b/arch/x86/include/asm/fpu-internal.h index 41ab26ea6564..e25cc33ec54d 100644 --- a/arch/x86/include/asm/fpu-internal.h +++ b/arch/x86/include/asm/fpu-internal.h @@ -26,9 +26,10 @@ #ifdef CONFIG_X86_64 # include # include -int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, +struct ksignal; +int ia32_setup_rt_frame(int sig, struct ksignal *ksig, compat_sigset_t *set, struct pt_regs *regs); -int ia32_setup_frame(int sig, struct k_sigaction *ka, +int ia32_setup_frame(int sig, struct ksignal *ksig, compat_sigset_t *set, struct pt_regs *regs); #else # define user_i387_ia32_struct user_i387_struct diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index d5b1f8a912ff..69562992e457 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c @@ -278,7 +278,7 @@ static const struct { }; static int -__setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, +__setup_frame(int sig, struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { struct sigframe __user *frame; @@ -286,7 +286,7 @@ __setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, int err = 0; void __user *fpstate = NULL; - frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); + frame = get_sigframe(&ksig->ka, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; @@ -307,8 +307,8 @@ __setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, restorer = VDSO32_SYMBOL(current->mm->context.vdso, sigreturn); else restorer = &frame->retcode; - if (ka->sa.sa_flags & SA_RESTORER) - restorer = ka->sa.sa_restorer; + if (ksig->ka.sa.sa_flags & SA_RESTORER) + restorer = ksig->ka.sa.sa_restorer; /* Set up to return from userspace. */ err |= __put_user(restorer, &frame->pretcode); @@ -327,7 +327,7 @@ __setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, /* Set up registers for signal handler */ regs->sp = (unsigned long)frame; - regs->ip = (unsigned long)ka->sa.sa_handler; + regs->ip = (unsigned long)ksig->ka.sa.sa_handler; regs->ax = (unsigned long)sig; regs->dx = 0; regs->cx = 0; @@ -340,7 +340,7 @@ __setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, return 0; } -static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, +static int __setup_rt_frame(int sig, struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; @@ -348,7 +348,7 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, int err = 0; void __user *fpstate = NULL; - frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); + frame = get_sigframe(&ksig->ka, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; @@ -368,8 +368,8 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Set up to return from userspace. */ restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn); - if (ka->sa.sa_flags & SA_RESTORER) - restorer = ka->sa.sa_restorer; + if (ksig->ka.sa.sa_flags & SA_RESTORER) + restorer = ksig->ka.sa.sa_restorer; put_user_ex(restorer, &frame->pretcode); /* @@ -382,7 +382,7 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, put_user_ex(*((u64 *)&rt_retcode), (u64 *)frame->retcode); } put_user_catch(err); - err |= copy_siginfo_to_user(&frame->info, info); + err |= copy_siginfo_to_user(&frame->info, &ksig->info); err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate, regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); @@ -392,7 +392,7 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Set up registers for signal handler */ regs->sp = (unsigned long)frame; - regs->ip = (unsigned long)ka->sa.sa_handler; + regs->ip = (unsigned long)ksig->ka.sa.sa_handler; regs->ax = (unsigned long)sig; regs->dx = (unsigned long)&frame->info; regs->cx = (unsigned long)&frame->uc; @@ -405,20 +405,20 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, return 0; } #else /* !CONFIG_X86_32 */ -static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, +static int __setup_rt_frame(int sig, struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; void __user *fp = NULL; int err = 0; - frame = get_sigframe(ka, regs, sizeof(struct rt_sigframe), &fp); + frame = get_sigframe(&ksig->ka, regs, sizeof(struct rt_sigframe), &fp); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - if (ka->sa.sa_flags & SA_SIGINFO) { - if (copy_siginfo_to_user(&frame->info, info)) + if (ksig->ka.sa.sa_flags & SA_SIGINFO) { + if (copy_siginfo_to_user(&frame->info, &ksig->info)) return -EFAULT; } @@ -434,8 +434,8 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Set up to return from userspace. If provided, use a stub already in userspace. */ /* x86-64 should always use SA_RESTORER. */ - if (ka->sa.sa_flags & SA_RESTORER) { - put_user_ex(ka->sa.sa_restorer, &frame->pretcode); + if (ksig->ka.sa.sa_flags & SA_RESTORER) { + put_user_ex(ksig->ka.sa.sa_restorer, &frame->pretcode); } else { /* could use a vstub here */ err |= -EFAULT; @@ -457,7 +457,7 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, next argument after the signal number on the stack. */ regs->si = (unsigned long)&frame->info; regs->dx = (unsigned long)&frame->uc; - regs->ip = (unsigned long) ka->sa.sa_handler; + regs->ip = (unsigned long) ksig->ka.sa.sa_handler; regs->sp = (unsigned long)frame; @@ -469,8 +469,8 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, } #endif /* CONFIG_X86_32 */ -static int x32_setup_rt_frame(int sig, struct k_sigaction *ka, - siginfo_t *info, compat_sigset_t *set, +static int x32_setup_rt_frame(struct ksignal *ksig, + compat_sigset_t *set, struct pt_regs *regs) { #ifdef CONFIG_X86_X32_ABI @@ -479,13 +479,13 @@ static int x32_setup_rt_frame(int sig, struct k_sigaction *ka, int err = 0; void __user *fpstate = NULL; - frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); + frame = get_sigframe(&ksig->ka, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - if (ka->sa.sa_flags & SA_SIGINFO) { - if (copy_siginfo_to_user32(&frame->info, info)) + if (ksig->ka.sa.sa_flags & SA_SIGINFO) { + if (copy_siginfo_to_user32(&frame->info, &ksig->info)) return -EFAULT; } @@ -499,8 +499,8 @@ static int x32_setup_rt_frame(int sig, struct k_sigaction *ka, err |= __compat_save_altstack(&frame->uc.uc_stack, regs->sp); put_user_ex(0, &frame->uc.uc__pad0); - if (ka->sa.sa_flags & SA_RESTORER) { - restorer = ka->sa.sa_restorer; + if (ksig->ka.sa.sa_flags & SA_RESTORER) { + restorer = ksig->ka.sa.sa_restorer; } else { /* could use a vstub here */ restorer = NULL; @@ -518,10 +518,10 @@ static int x32_setup_rt_frame(int sig, struct k_sigaction *ka, /* Set up registers for signal handler */ regs->sp = (unsigned long) frame; - regs->ip = (unsigned long) ka->sa.sa_handler; + regs->ip = (unsigned long) ksig->ka.sa.sa_handler; /* We use the x32 calling convention here... */ - regs->di = sig; + regs->di = ksig->sig; regs->si = (unsigned long) &frame->info; regs->dx = (unsigned long) &frame->uc; @@ -611,30 +611,29 @@ static int signr_convert(int sig) } static int -setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - struct pt_regs *regs) +setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs) { - int usig = signr_convert(sig); + int usig = signr_convert(ksig->sig); sigset_t *set = sigmask_to_save(); compat_sigset_t *cset = (compat_sigset_t *) set; /* Set up the stack frame */ if (is_ia32_frame()) { - if (ka->sa.sa_flags & SA_SIGINFO) - return ia32_setup_rt_frame(usig, ka, info, cset, regs); + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + return ia32_setup_rt_frame(usig, ksig, cset, regs); else - return ia32_setup_frame(usig, ka, cset, regs); + return ia32_setup_frame(usig, ksig, cset, regs); } else if (is_x32_frame()) { - return x32_setup_rt_frame(usig, ka, info, cset, regs); + return x32_setup_rt_frame(ksig, cset, regs); } else { - return __setup_rt_frame(sig, ka, info, set, regs); + return __setup_rt_frame(ksig->sig, ksig, set, regs); } } static void -handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, - struct pt_regs *regs) +handle_signal(struct ksignal *ksig, struct pt_regs *regs) { + bool failed; /* Are we from a system call? */ if (syscall_get_nr(current, regs) >= 0) { /* If so, check system call restarting.. */ @@ -645,7 +644,7 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, break; case -ERESTARTSYS: - if (!(ka->sa.sa_flags & SA_RESTART)) { + if (!(ksig->ka.sa.sa_flags & SA_RESTART)) { regs->ax = -EINTR; break; } @@ -665,26 +664,21 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, likely(test_and_clear_thread_flag(TIF_FORCED_TF))) regs->flags &= ~X86_EFLAGS_TF; - if (setup_rt_frame(sig, ka, info, regs) < 0) { - force_sigsegv(sig, current); - return; + failed = (setup_rt_frame(ksig, regs) < 0); + if (!failed) { + /* + * Clear the direction flag as per the ABI for function entry. + */ + regs->flags &= ~X86_EFLAGS_DF; + /* + * Clear TF when entering the signal handler, but + * notify any tracer that was single-stepping it. + * The tracer may want to single-step inside the + * handler too. + */ + regs->flags &= ~X86_EFLAGS_TF; } - - /* - * Clear the direction flag as per the ABI for function entry. - */ - regs->flags &= ~X86_EFLAGS_DF; - - /* - * Clear TF when entering the signal handler, but - * notify any tracer that was single-stepping it. - * The tracer may want to single-step inside the - * handler too. - */ - regs->flags &= ~X86_EFLAGS_TF; - - signal_delivered(sig, info, ka, regs, - test_thread_flag(TIF_SINGLESTEP)); + signal_setup_done(failed, ksig, test_thread_flag(TIF_SINGLESTEP)); } #ifdef CONFIG_X86_32 @@ -701,14 +695,11 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, */ static void do_signal(struct pt_regs *regs) { - struct k_sigaction ka; - siginfo_t info; - int signr; + struct ksignal ksig; - signr = get_signal_to_deliver(&info, &ka, regs, NULL); - if (signr > 0) { + if (get_signal(&ksig)) { /* Whee! Actually deliver the signal. */ - handle_signal(signr, &info, &ka, regs); + handle_signal(&ksig, regs); return; } From 7dd145252574e34d92ad574e5168e4115639c0be Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 14 Feb 2013 10:03:10 +0100 Subject: [PATCH 2681/4607] dmaengine: ste_dma40: do not remove descriptors for cyclic transfers Fix dma_tc_handle() to call d40_desc_remove() and d40_desc_done() only for non-cyclic transfers, as this was breaking ux500_pcm since introduced in: d49278e dmaengine: dma40: Add support to split up large elements Reported-by: Shreshtha Kumar Sahu Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index ad860a221c33..1734feec47b1 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1570,10 +1570,10 @@ static void dma_tc_handle(struct d40_chan *d40c) d40c->busy = false; pm_runtime_mark_last_busy(d40c->base->dev); pm_runtime_put_autosuspend(d40c->base->dev); - } - d40_desc_remove(d40d); - d40_desc_done(d40c, d40d); + d40_desc_remove(d40d); + d40_desc_done(d40c, d40d); + } d40c->pending_tx++; tasklet_schedule(&d40c->tasklet); From 88b386c0a7c278581d2b8e2c2761d12f6475938d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:15 +0200 Subject: [PATCH 2682/4607] dma: of-dma: protect list write operation by spin_lock It's possible to have an inconsistency in the list due to unprotected operation on it. The patch adds a proper locking on the list operation. Signed-off-by: Andy Shevchenko Acked-by: Rob Herring Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/of-dma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index 583e50e3d47c..69d04d28b1ef 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -118,7 +118,9 @@ int of_dma_controller_register(struct device_node *np, ofdma->use_count = 0; /* Now queue of_dma controller structure in list */ + spin_lock(&of_dma_lock); list_add_tail(&ofdma->of_dma_controllers, &of_dma_list); + spin_unlock(&of_dma_lock); return 0; } From 978c4172af48f0adc082f8b1d94acb817d947730 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:16 +0200 Subject: [PATCH 2683/4607] dmaengine.h: remove redundant else keyword dmaengine_device_control returns -ENOSYS in case the dma driver doesn't have such functionality. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index bfcdecb5d87a..f5939999cb65 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -610,8 +610,8 @@ static inline int dmaengine_device_control(struct dma_chan *chan, { if (chan->device->device_control) return chan->device->device_control(chan, cmd, arg); - else - return -ENOSYS; + + return -ENOSYS; } static inline int dmaengine_slave_config(struct dma_chan *chan, From 4168d0d9d304f184f786b1f00750557b8e09453c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:17 +0200 Subject: [PATCH 2684/4607] dma: coh901318: avoid unbalanced locking In case the len is 0 we must return without trying to unlock the lock that was not locked. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Cc: Linus Walleij Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Vinod Koul --- drivers/dma/coh901318_lli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/coh901318_lli.c b/drivers/dma/coh901318_lli.c index 780e0429b38c..fe9cc5eeddae 100644 --- a/drivers/dma/coh901318_lli.c +++ b/drivers/dma/coh901318_lli.c @@ -61,7 +61,7 @@ coh901318_lli_alloc(struct coh901318_pool *pool, unsigned int len) dma_addr_t phy; if (len == 0) - goto err; + return NULL; spin_lock(&pool->lock); From 9b562639a1dbef847dfc9daa807bd3e7e02ef24f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:18 +0200 Subject: [PATCH 2685/4607] dma: coh901318: set residue only if dma is in progress When status is DMA_SUCCESS the residue should be zero. Otherwise it's a bug. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Cc: Linus Walleij Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Vinod Koul --- drivers/dma/coh901318.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index aa384e53b7ac..671e75962370 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -1147,7 +1147,9 @@ coh901318_tx_status(struct dma_chan *chan, dma_cookie_t cookie, enum dma_status ret; ret = dma_cookie_status(chan, cookie, txstate); - /* FIXME: should be conditional on ret != DMA_SUCCESS? */ + if (ret == DMA_SUCCESS) + return ret; + dma_set_residue(txstate, coh901318_get_bytes_left(chan)); if (ret == DMA_IN_PROGRESS && cohc->stopped) From 373459eee0cd79f775ef9874395bb37638154777 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:19 +0200 Subject: [PATCH 2686/4607] edma: do not waste memory for dma_mask Accordingly to commentary in the platform_device_register_full the memory allocated for dma_mask will not going to be freed. That's why is better to assign dma_mask afterwards. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 82c8672f26e8..af44cadb8de1 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -620,13 +620,11 @@ static struct platform_device *pdev0, *pdev1; static const struct platform_device_info edma_dev_info0 = { .name = "edma-dma-engine", .id = 0, - .dma_mask = DMA_BIT_MASK(32), }; static const struct platform_device_info edma_dev_info1 = { .name = "edma-dma-engine", .id = 1, - .dma_mask = DMA_BIT_MASK(32), }; static int edma_init(void) @@ -640,6 +638,8 @@ static int edma_init(void) ret = PTR_ERR(pdev0); goto out; } + pdev0->dev.dma_mask = &pdev0->dev.coherent_dma_mask; + pdev0->dev.coherent_dma_mask = DMA_BIT_MASK(32); } if (EDMA_CTLRS == 2) { @@ -649,6 +649,8 @@ static int edma_init(void) platform_device_unregister(pdev0); ret = PTR_ERR(pdev1); } + pdev1->dev.dma_mask = &pdev1->dev.coherent_dma_mask; + pdev1->dev.coherent_dma_mask = DMA_BIT_MASK(32); } out: From a72208733f7866972bcba7995f7e598ad07f1158 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Feb 2013 11:00:20 +0200 Subject: [PATCH 2687/4607] dma: tegra20-apb-dma: remove unnecessary assignment There is no need to assign 0 to residue, because dma_cookie_status() does this for us. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Acked-by: Laxman Dewangan Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 6c144814a896..c71812f8d9c1 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -767,7 +767,6 @@ static enum dma_status tegra_dma_tx_status(struct dma_chan *dc, ret = dma_cookie_status(dc, cookie, txstate); if (ret == DMA_SUCCESS) { - dma_set_residue(txstate, 0); spin_unlock_irqrestore(&tdc->lock, flags); return ret; } From 34d19355b84adde9eebc1d6771231c15dff891e6 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 14 Feb 2013 09:10:05 +0530 Subject: [PATCH 2688/4607] DMA: PL330: Add new pl330 filter for DT case. This patch adds a new pl330_dt_filter for DT case to filter the required channel based on the new filter params and modifies the old filter only for non-DT case as suggested by Arnd Bergmann. Signed-off-by: Padmavathi Venna Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index f7edb6f0ee87..40e97528244b 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -606,6 +606,11 @@ struct dma_pl330_desc { struct dma_pl330_chan *pchan; }; +struct dma_pl330_filter_args { + struct dma_pl330_dmac *pdmac; + unsigned int chan_id; +}; + static inline void _callback(struct pl330_req *r, enum pl330_op_err err) { if (r && r->xfer_cb) @@ -2352,6 +2357,16 @@ static void dma_pl330_rqcb(void *token, enum pl330_op_err err) tasklet_schedule(&pch->task); } +static bool pl330_dt_filter(struct dma_chan *chan, void *param) +{ + struct dma_pl330_filter_args *fargs = param; + + if (chan->device != &fargs->pdmac->ddma) + return false; + + return (chan->chan_id == fargs->chan_id); +} + bool pl330_filter(struct dma_chan *chan, void *param) { u8 *peri_id; @@ -2359,20 +2374,6 @@ bool pl330_filter(struct dma_chan *chan, void *param) if (chan->device->dev->driver != &pl330_driver.drv) return false; -#ifdef CONFIG_OF - if (chan->device->dev->of_node) { - const __be32 *prop_value; - phandle phandle; - struct device_node *node; - - prop_value = ((struct property *)param)->value; - phandle = be32_to_cpup(prop_value++); - node = of_find_node_by_phandle(phandle); - return ((chan->private == node) && - (chan->chan_id == be32_to_cpup(prop_value))); - } -#endif - peri_id = chan->private; return *peri_id == (unsigned)param; } From a80258f9b2ac81e72ff680d273df9544a1307a32 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 14 Feb 2013 09:10:06 +0530 Subject: [PATCH 2689/4607] DMA: PL330: Add xlate function Add xlate to translate the device-tree binding information into the appropriate format. The filter function requires the dma controller device and dma channel number as filter_params. Signed-off-by: Padmavathi Venna Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 40e97528244b..f5d47e617df2 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "dmaengine.h" #define PL330_MAX_CHAN 8 @@ -2379,6 +2380,30 @@ bool pl330_filter(struct dma_chan *chan, void *param) } EXPORT_SYMBOL(pl330_filter); +static struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + int count = dma_spec->args_count; + struct dma_pl330_dmac *pdmac = ofdma->of_dma_data; + struct dma_pl330_filter_args fargs; + dma_cap_mask_t cap; + + if (!pdmac) + return NULL; + + if (count != 1) + return NULL; + + fargs.pdmac = pdmac; + fargs.chan_id = dma_spec->args[0]; + + dma_cap_zero(cap); + dma_cap_set(DMA_SLAVE, cap); + dma_cap_set(DMA_CYCLIC, cap); + + return dma_request_channel(cap, pl330_dt_filter, &fargs); +} + static int pl330_alloc_chan_resources(struct dma_chan *chan) { struct dma_pl330_chan *pch = to_pchan(chan); From 421da89aadd1b24f4a3bc1d60c9de9825ec2debc Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 14 Feb 2013 09:10:07 +0530 Subject: [PATCH 2690/4607] DMA: PL330: Register the DMA controller with the generic DMA helpers This patch registers the pl330 dma controller driver with the generic device tree dma helper functions. Signed-off-by: Padmavathi Venna Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index f5d47e617df2..fc9c80017378 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -2995,6 +2995,14 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pi->pcfg.data_bus_width / 8, pi->pcfg.num_chan, pi->pcfg.num_peri, pi->pcfg.num_events); + ret = of_dma_controller_register(adev->dev.of_node, + of_dma_pl330_xlate, pdmac); + if (ret) { + dev_err(&adev->dev, + "unable to register DMA to the generic DT DMA helpers\n"); + goto probe_err2; + } + return 0; probe_err2: @@ -3015,6 +3023,8 @@ static int __devexit pl330_remove(struct amba_device *adev) if (!pdmac) return 0; + of_dma_controller_free(adev->dev.of_node); + amba_set_drvdata(adev, NULL); /* Idle the DMAC */ From 42cf20980cded7e0ecdd6dba74592d059c6a8bda Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Thu, 14 Feb 2013 09:10:08 +0530 Subject: [PATCH 2691/4607] ARM: dts: pl330: Add #dma-cells for generic dma binding support This patch adds #dma-cells property to PL330 DMA controller nodes for supporting generic dma dt bindings on samsung exynos5250 platform. Signed-off-by: Padmavathi Venna Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- .../devicetree/bindings/dma/arm-pl330.txt | 21 ++++++++++++++----- arch/arm/boot/dts/exynos5250.dtsi | 12 +++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/arm-pl330.txt b/Documentation/devicetree/bindings/dma/arm-pl330.txt index 36e27d54260b..267565894db9 100644 --- a/Documentation/devicetree/bindings/dma/arm-pl330.txt +++ b/Documentation/devicetree/bindings/dma/arm-pl330.txt @@ -10,7 +10,11 @@ Required properties: - interrupts: interrupt number to the cpu. Optional properties: -- dma-coherent : Present if dma operations are coherent + - dma-coherent : Present if dma operations are coherent + - #dma-cells: must be <1>. used to represent the number of integer + cells in the dmas property of client device. + - dma-channels: contains the total number of DMA channels supported by the DMAC + - dma-requests: contains the total number of DMA requests supported by the DMAC Example: @@ -18,16 +22,23 @@ Example: compatible = "arm,pl330", "arm,primecell"; reg = <0x12680000 0x1000>; interrupts = <99>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <32>; }; Client drivers (device nodes requiring dma transfers from dev-to-mem or -mem-to-dev) should specify the DMA channel numbers using a two-value pair +mem-to-dev) should specify the DMA channel numbers and dma channel names as shown below. [property name] = <[phandle of the dma controller] [dma request id]>; + [property name] = <[dma channel name]> where 'dma request id' is the dma request number which is connected - to the client controller. The 'property name' is recommended to be - of the form -dma-channel. + to the client controller. The 'property name' 'dmas' and 'dma-names' + as required by the generic dma device tree binding helpers. The dma + names correspond 1:1 with the dma request ids in the dmas property. - Example: tx-dma-channel = <&pdma0 12>; + Example: dmas = <&pdma0 12 + &pdma1 11>; + dma-names = "tx", "rx"; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 2e3b6efaf1a2..c774aae3f82d 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -280,24 +280,36 @@ compatible = "arm,pl330", "arm,primecell"; reg = <0x121A0000 0x1000>; interrupts = <0 34 0>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <32>; }; pdma1: pdma@121B0000 { compatible = "arm,pl330", "arm,primecell"; reg = <0x121B0000 0x1000>; interrupts = <0 35 0>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <32>; }; mdma0: mdma@10800000 { compatible = "arm,pl330", "arm,primecell"; reg = <0x10800000 0x1000>; interrupts = <0 33 0>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <1>; }; mdma1: mdma@11C10000 { compatible = "arm,pl330", "arm,primecell"; reg = <0x11C10000 0x1000>; interrupts = <0 124 0>; + #dma-cells = <1>; + #dma-channels = <8>; + #dma-requests = <1>; }; }; From 7292e7e01cc98fa04a9a3eb7ca11d1bca99c35e9 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 7 Jan 2013 14:17:23 +0100 Subject: [PATCH 2692/4607] asm-generic/io.h: convert readX defines to functions E.g. readl is defined like this #define readl(addr) __le32_to_cpu(__raw_readl(addr)) If a there is a readl() call that doesn't check the return value this will cause a compile warning on big endian machines due to the __le32_to_cpu macro magic. E.g. code like this: readl(addr); will generate the following compile warning: warning: value computed is not used [-Wunused-value] With this patch we get rid of dozens of compile warnings on s390. Signed-off-by: Heiko Carstens Acked-by: Arnd Bergmann Signed-off-by: Martin Schwidefsky --- include/asm-generic/io.h | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index 33bbbae4ddc6..8e260cf01351 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h @@ -53,8 +53,18 @@ static inline u32 __raw_readl(const volatile void __iomem *addr) #endif #define readb __raw_readb -#define readw(addr) __le16_to_cpu(__raw_readw(addr)) -#define readl(addr) __le32_to_cpu(__raw_readl(addr)) + +#define readw readw +static inline u16 readw(const volatile void __iomem *addr) +{ + return __le16_to_cpu(__raw_readw(addr)); +} + +#define readl readl +static inline u32 readl(const volatile void __iomem *addr) +{ + return __le32_to_cpu(__raw_readl(addr)); +} #ifndef __raw_writeb static inline void __raw_writeb(u8 b, volatile void __iomem *addr) @@ -89,7 +99,11 @@ static inline u64 __raw_readq(const volatile void __iomem *addr) } #endif -#define readq(addr) __le64_to_cpu(__raw_readq(addr)) +#define readq readq +static inline u64 readq(const volatile void __iomem *addr) +{ + return __le64_to_cpu(__raw_readq(addr)); +} #ifndef __raw_writeq static inline void __raw_writeq(u64 b, volatile void __iomem *addr) From 736c9fd2902d919b075cf9cf371d1733c5ff635d Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 8 Jan 2013 15:15:12 +0100 Subject: [PATCH 2693/4607] s390/3270: readd tty3270_open Reintroduce the tty3270_open function which has been removed by git commit 20cda6f2 "TTY: tty3270, add tty install". Without the open function in the tty_operations tty_open will return -ENODEV and the 3270 tty will not work. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/tty3270.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 43ea0593bdb0..964018402b36 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -925,6 +925,20 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) return 0; } +/* + * This routine is called whenever a 3270 tty is opened. + */ +static int +tty3270_open(struct tty_struct *tty, struct file *filp) +{ + struct tty3270 *tp = tty->driver_data; + struct tty_port *port = &tp->port; + + port->count++; + tty_port_tty_set(port, tty); + return 0; +} + /* * This routine is called when the 3270 tty is closed. We wait * for the remaining request to be completed. Then we clean up. @@ -1753,6 +1767,7 @@ static long tty3270_compat_ioctl(struct tty_struct *tty, static const struct tty_operations tty3270_ops = { .install = tty3270_install, .cleanup = tty3270_cleanup, + .open = tty3270_open, .close = tty3270_close, .write = tty3270_write, .put_char = tty3270_put_char, From 57985d7e1e48f16548aa6904264e21bca15af0fc Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 8 Jan 2013 15:20:05 +0100 Subject: [PATCH 2694/4607] s390/3270: fix initialization order in tty3270_alloc_view Corrects the order of tasklet_init vs. the allocation of the read request which has been broken by git commit 9d2ae233 "TTY: tty3270, move initialization to allocation". Signed-off-by: Martin Schwidefsky --- drivers/s390/char/tty3270.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 964018402b36..5e4b6fc49ae3 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -683,12 +683,6 @@ tty3270_alloc_view(void) INIT_LIST_HEAD(&tp->update); INIT_LIST_HEAD(&tp->rcl_lines); tp->rcl_max = 20; - tty_port_init(&tp->port); - setup_timer(&tp->timer, (void (*)(unsigned long)) tty3270_update, - (unsigned long) tp); - tasklet_init(&tp->readlet, - (void (*)(unsigned long)) tty3270_read_tasklet, - (unsigned long) tp->read); for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) { tp->freemem_pages[pages] = (void *) @@ -710,6 +704,14 @@ tty3270_alloc_view(void) tp->kbd = kbd_alloc(); if (!tp->kbd) goto out_reset; + + tty_port_init(&tp->port); + setup_timer(&tp->timer, (void (*)(unsigned long)) tty3270_update, + (unsigned long) tp); + tasklet_init(&tp->readlet, + (void (*)(unsigned long)) tty3270_read_tasklet, + (unsigned long) tp->read); + return tp; out_reset: From c95571e68086d36e8e3369597b03ec29c63abec9 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 8 Jan 2013 15:31:11 +0100 Subject: [PATCH 2695/4607] s390/3270: introduce device notifier Add a notifier to create / destroy the device nodes for the tty view and the fullscreen view. Only device nodes for online devices are created and the device names will follow the convention as outlined in Documentation/devices.txt: 3270/tty for the tty nodes, 3270/tub for hte fullscreen nodes and 3270/tub for the fullscreen control node. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/fs3270.c | 29 ++++++++++++-- drivers/s390/char/raw3270.c | 76 ++++++++----------------------------- drivers/s390/char/raw3270.h | 11 +++++- drivers/s390/char/tty3270.c | 49 ++++++++++++++++-------- 4 files changed, 83 insertions(+), 82 deletions(-) diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index 911704571b9c..230697aac94b 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -443,7 +443,7 @@ fs3270_open(struct inode *inode, struct file *filp) tty_kref_put(tty); return -ENODEV; } - minor = tty->index + RAW3270_FIRSTMINOR; + minor = tty->index; tty_kref_put(tty); } mutex_lock(&fs3270_mutex); @@ -524,6 +524,25 @@ static const struct file_operations fs3270_fops = { .llseek = no_llseek, }; +void fs3270_create_cb(int minor) +{ + __register_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub", &fs3270_fops); + device_create(class3270, NULL, MKDEV(IBM_FS3270_MAJOR, minor), + NULL, "3270/tub%d", minor); +} + +void fs3270_destroy_cb(int minor) +{ + device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, minor)); + __unregister_chrdev(IBM_FS3270_MAJOR, minor, 1, "tub"); +} + +struct raw3270_notifier fs3270_notifier = +{ + .create = fs3270_create_cb, + .destroy = fs3270_destroy_cb, +}; + /* * 3270 fullscreen driver initialization. */ @@ -532,16 +551,20 @@ fs3270_init(void) { int rc; - rc = register_chrdev(IBM_FS3270_MAJOR, "fs3270", &fs3270_fops); + rc = __register_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270", &fs3270_fops); if (rc) return rc; + device_create(class3270, NULL, MKDEV(IBM_FS3270_MAJOR, 0), + NULL, "3270/tub"); + raw3270_register_notifier(&fs3270_notifier); return 0; } static void __exit fs3270_exit(void) { - unregister_chrdev(IBM_FS3270_MAJOR, "fs3270"); + raw3270_unregister_notifier(&fs3270_notifier); + __unregister_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270"); } MODULE_LICENSE("GPL"); diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 9a6c140c5f07..72f69fda9d01 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -28,7 +28,7 @@ #include #include -static struct class *class3270; +struct class *class3270; /* The main 3270 data structure. */ struct raw3270 { @@ -46,8 +46,6 @@ struct raw3270 { struct timer_list timer; /* Device timer. */ unsigned char *ascebc; /* ascii -> ebcdic table */ - struct device *clttydev; /* 3270-class tty device ptr */ - struct device *cltubdev; /* 3270-class tub device ptr */ struct raw3270_request init_request; unsigned char init_data[256]; @@ -1072,10 +1070,6 @@ raw3270_delete_device(struct raw3270 *rp) /* Remove from device chain. */ mutex_lock(&raw3270_mutex); - if (rp->clttydev && !IS_ERR(rp->clttydev)) - device_destroy(class3270, MKDEV(IBM_TTY3270_MAJOR, rp->minor)); - if (rp->cltubdev && !IS_ERR(rp->cltubdev)) - device_destroy(class3270, MKDEV(IBM_FS3270_MAJOR, rp->minor)); list_del_init(&rp->list); mutex_unlock(&raw3270_mutex); @@ -1139,75 +1133,34 @@ static struct attribute_group raw3270_attr_group = { static int raw3270_create_attributes(struct raw3270 *rp) { - int rc; - - rc = sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group); - if (rc) - goto out; - - rp->clttydev = device_create(class3270, &rp->cdev->dev, - MKDEV(IBM_TTY3270_MAJOR, rp->minor), NULL, - "tty%s", dev_name(&rp->cdev->dev)); - if (IS_ERR(rp->clttydev)) { - rc = PTR_ERR(rp->clttydev); - goto out_ttydev; - } - - rp->cltubdev = device_create(class3270, &rp->cdev->dev, - MKDEV(IBM_FS3270_MAJOR, rp->minor), NULL, - "tub%s", dev_name(&rp->cdev->dev)); - if (!IS_ERR(rp->cltubdev)) - goto out; - - rc = PTR_ERR(rp->cltubdev); - device_destroy(class3270, MKDEV(IBM_TTY3270_MAJOR, rp->minor)); - -out_ttydev: - sysfs_remove_group(&rp->cdev->dev.kobj, &raw3270_attr_group); -out: - return rc; + return sysfs_create_group(&rp->cdev->dev.kobj, &raw3270_attr_group); } /* * Notifier for device addition/removal */ -struct raw3270_notifier { - struct list_head list; - void (*notifier)(int, int); -}; - static LIST_HEAD(raw3270_notifier); -int raw3270_register_notifier(void (*notifier)(int, int)) +int raw3270_register_notifier(struct raw3270_notifier *notifier) { - struct raw3270_notifier *np; struct raw3270 *rp; - np = kmalloc(sizeof(struct raw3270_notifier), GFP_KERNEL); - if (!np) - return -ENOMEM; - np->notifier = notifier; mutex_lock(&raw3270_mutex); - list_add_tail(&np->list, &raw3270_notifier); - list_for_each_entry(rp, &raw3270_devices, list) { - get_device(&rp->cdev->dev); - notifier(rp->minor, 1); - } + list_add_tail(¬ifier->list, &raw3270_notifier); + list_for_each_entry(rp, &raw3270_devices, list) + notifier->create(rp->minor); mutex_unlock(&raw3270_mutex); return 0; } -void raw3270_unregister_notifier(void (*notifier)(int, int)) +void raw3270_unregister_notifier(struct raw3270_notifier *notifier) { - struct raw3270_notifier *np; + struct raw3270 *rp; mutex_lock(&raw3270_mutex); - list_for_each_entry(np, &raw3270_notifier, list) - if (np->notifier == notifier) { - list_del(&np->list); - kfree(np); - break; - } + list_for_each_entry(rp, &raw3270_devices, list) + notifier->destroy(rp->minor); + list_del(¬ifier->list); mutex_unlock(&raw3270_mutex); } @@ -1217,8 +1170,8 @@ void raw3270_unregister_notifier(void (*notifier)(int, int)) static int raw3270_set_online (struct ccw_device *cdev) { - struct raw3270 *rp; struct raw3270_notifier *np; + struct raw3270 *rp; int rc; rp = raw3270_create_device(cdev); @@ -1239,7 +1192,7 @@ raw3270_set_online (struct ccw_device *cdev) set_bit(RAW3270_FLAGS_READY, &rp->flags); mutex_lock(&raw3270_mutex); list_for_each_entry(np, &raw3270_notifier, list) - np->notifier(rp->minor, 1); + np->create(rp->minor); mutex_unlock(&raw3270_mutex); return 0; @@ -1290,7 +1243,7 @@ raw3270_remove (struct ccw_device *cdev) mutex_lock(&raw3270_mutex); list_for_each_entry(np, &raw3270_notifier, list) - np->notifier(rp->minor, 0); + np->destroy(rp->minor); mutex_unlock(&raw3270_mutex); /* Reset 3270 device. */ @@ -1434,6 +1387,7 @@ MODULE_LICENSE("GPL"); module_init(raw3270_init); module_exit(raw3270_exit); +EXPORT_SYMBOL(class3270); EXPORT_SYMBOL(raw3270_request_alloc); EXPORT_SYMBOL(raw3270_request_free); EXPORT_SYMBOL(raw3270_request_reset); diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index ed34eb2199cc..a4c79d043cd3 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -91,6 +91,7 @@ struct raw3270_iocb { struct raw3270; struct raw3270_view; +extern struct class *class3270; /* 3270 CCW request */ struct raw3270_request { @@ -192,8 +193,14 @@ struct raw3270 *raw3270_setup_console(struct ccw_device *cdev); void raw3270_wait_cons_dev(struct raw3270 *); /* Notifier for device addition/removal */ -int raw3270_register_notifier(void (*notifier)(int, int)); -void raw3270_unregister_notifier(void (*notifier)(int, int)); +struct raw3270_notifier { + struct list_head list; + void (*create)(int minor); + void (*destroy)(int minor); +}; + +int raw3270_register_notifier(struct raw3270_notifier *); +void raw3270_unregister_notifier(struct raw3270_notifier *); void raw3270_pm_unfreeze(struct raw3270_view *); /* diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 5e4b6fc49ae3..48767e6bab9d 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -829,9 +829,8 @@ tty3270_del_views(void) { int i; - for (i = 0; i < tty3270_max_index; i++) { - struct raw3270_view *view = - raw3270_find_view(&tty3270_fn, i + RAW3270_FIRSTMINOR); + for (i = RAW3270_FIRSTMINOR; i <= tty3270_max_index; i++) { + struct raw3270_view *view = raw3270_find_view(&tty3270_fn, i); if (!IS_ERR(view)) raw3270_del_view(view); } @@ -855,8 +854,7 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) int i, rc; /* Check if the tty3270 is already there. */ - view = raw3270_find_view(&tty3270_fn, - tty->index + RAW3270_FIRSTMINOR); + view = raw3270_find_view(&tty3270_fn, tty->index); if (!IS_ERR(view)) { tp = container_of(view, struct tty3270, view); tty->driver_data = tp; @@ -868,8 +866,8 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) tp->inattr = TF_INPUT; return tty_port_install(&tp->port, driver, tty); } - if (tty3270_max_index < tty->index + 1) - tty3270_max_index = tty->index + 1; + if (tty3270_max_index < tty->index) + tty3270_max_index = tty->index; /* Quick exit if there is no device for tty->index. */ if (PTR_ERR(view) == -ENODEV) @@ -880,8 +878,7 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) if (IS_ERR(tp)) return PTR_ERR(tp); - rc = raw3270_add_view(&tp->view, &tty3270_fn, - tty->index + RAW3270_FIRSTMINOR); + rc = raw3270_add_view(&tp->view, &tty3270_fn, tty->index); if (rc) { tty3270_free_view(tp); return rc; @@ -1788,6 +1785,22 @@ static const struct tty_operations tty3270_ops = { .set_termios = tty3270_set_termios }; +void tty3270_create_cb(int minor) +{ + tty_register_device(tty3270_driver, minor, NULL); +} + +void tty3270_destroy_cb(int minor) +{ + tty_unregister_device(tty3270_driver, minor); +} + +struct raw3270_notifier tty3270_notifier = +{ + .create = tty3270_create_cb, + .destroy = tty3270_destroy_cb, +}; + /* * 3270 tty registration code called from tty_init(). * Most kernel services (incl. kmalloc) are available at this poimt. @@ -1797,23 +1810,25 @@ static int __init tty3270_init(void) struct tty_driver *driver; int ret; - driver = alloc_tty_driver(RAW3270_MAXDEVS); - if (!driver) - return -ENOMEM; + driver = tty_alloc_driver(RAW3270_MAXDEVS, + TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV | + TTY_DRIVER_RESET_TERMIOS); + if (IS_ERR(driver)) + return PTR_ERR(driver); /* * Initialize the tty_driver structure * Entries in tty3270_driver that are NOT initialized: * proc_entry, set_termios, flush_buffer, set_ldisc, write_proc */ - driver->driver_name = "ttyTUB"; - driver->name = "ttyTUB"; + driver->driver_name = "tty3270"; + driver->name = "3270/tty"; driver->major = IBM_TTY3270_MAJOR; - driver->minor_start = RAW3270_FIRSTMINOR; + driver->minor_start = 0; driver->type = TTY_DRIVER_TYPE_SYSTEM; driver->subtype = SYSTEM_TYPE_TTY; driver->init_termios = tty_std_termios; - driver->flags = TTY_DRIVER_RESET_TERMIOS; tty_set_operations(driver, &tty3270_ops); ret = tty_register_driver(driver); if (ret) { @@ -1821,6 +1836,7 @@ static int __init tty3270_init(void) return ret; } tty3270_driver = driver; + raw3270_register_notifier(&tty3270_notifier); return 0; } @@ -1829,6 +1845,7 @@ tty3270_exit(void) { struct tty_driver *driver; + raw3270_unregister_notifier(&tty3270_notifier); driver = tty3270_driver; tty3270_driver = NULL; tty_unregister_driver(driver); From 4d334fd155b53adfe78393e66850ff4bb0aa8406 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 4 Jan 2013 14:55:13 +0100 Subject: [PATCH 2696/4607] s390/3270: asynchronous size sensing Convert the synchronous size sense code to an interrupt driven approach. This allows to set the device online even if the terminal is not connected. With the new code views can be registered without a connected terminal, the tty can be opened as soon as the device is online. After the terminal has been connected and the size has been determined the tty is resized to match the device characteristics.. Signed-off-by: Martin Schwidefsky --- drivers/s390/char/raw3270.c | 535 +++++++++++++++++------------------- drivers/s390/char/raw3270.h | 1 + drivers/s390/char/tty3270.c | 109 ++++++-- drivers/tty/tty_io.c | 1 + 4 files changed, 332 insertions(+), 314 deletions(-) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 72f69fda9d01..4c9030a5b9f2 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -37,6 +37,7 @@ struct raw3270 { int minor; short model, rows, cols; + unsigned int state; unsigned long flags; struct list_head req_queue; /* Request queue. */ @@ -47,17 +48,25 @@ struct raw3270 { unsigned char *ascebc; /* ascii -> ebcdic table */ - struct raw3270_request init_request; + struct raw3270_view init_view; + struct raw3270_request init_reset; + struct raw3270_request init_readpart; + struct raw3270_request init_readmod; unsigned char init_data[256]; }; +/* raw3270->state */ +#define RAW3270_STATE_INIT 0 /* Initial state */ +#define RAW3270_STATE_RESET 1 /* Reset command is pending */ +#define RAW3270_STATE_W4ATTN 2 /* Wait for attention interrupt */ +#define RAW3270_STATE_READMOD 3 /* Read partition is pending */ +#define RAW3270_STATE_READY 4 /* Device is usable by views */ + /* raw3270->flags */ #define RAW3270_FLAGS_14BITADDR 0 /* 14-bit buffer addresses */ #define RAW3270_FLAGS_BUSY 1 /* Device busy, leave it alone */ -#define RAW3270_FLAGS_ATTN 2 /* Device sent an ATTN interrupt */ -#define RAW3270_FLAGS_READY 4 /* Device is useable by views */ -#define RAW3270_FLAGS_CONSOLE 8 /* Device is the console. */ -#define RAW3270_FLAGS_FROZEN 16 /* set if 3270 is frozen for suspend */ +#define RAW3270_FLAGS_CONSOLE 2 /* Device is the console. */ +#define RAW3270_FLAGS_FROZEN 3 /* set if 3270 is frozen for suspend */ /* Semaphore to protect global data of raw3270 (devices, views, etc). */ static DEFINE_MUTEX(raw3270_mutex); @@ -95,6 +104,17 @@ static unsigned char raw3270_ebcgraf[64] = { 0xf8, 0xf9, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f }; +static inline int raw3270_state_ready(struct raw3270 *rp) +{ + return rp->state == RAW3270_STATE_READY; +} + +static inline int raw3270_state_final(struct raw3270 *rp) +{ + return rp->state == RAW3270_STATE_INIT || + rp->state == RAW3270_STATE_READY; +} + void raw3270_buffer_address(struct raw3270 *rp, char *cp, unsigned short addr) { @@ -212,7 +232,7 @@ raw3270_request_set_idal(struct raw3270_request *rq, struct idal_buffer *ib) * Stop running ccw. */ static int -raw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq) +__raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq) { int retries; int rc; @@ -231,18 +251,6 @@ raw3270_halt_io_nolock(struct raw3270 *rp, struct raw3270_request *rq) return rc; } -static int -raw3270_halt_io(struct raw3270 *rp, struct raw3270_request *rq) -{ - unsigned long flags; - int rc; - - spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - rc = raw3270_halt_io_nolock(rp, rq); - spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); - return rc; -} - /* * Add the request to the request queue, try to start it if the * 3270 device is idle. Return without waiting for end of i/o. @@ -279,8 +287,8 @@ raw3270_start(struct raw3270_view *view, struct raw3270_request *rq) if (!rp || rp->view != view || test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) rc = -EACCES; - else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) - rc = -ENODEV; + else if (!raw3270_state_ready(rp)) + rc = -EBUSY; else rc = __raw3270_start(rp, view, rq); spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); @@ -297,8 +305,8 @@ raw3270_start_locked(struct raw3270_view *view, struct raw3270_request *rq) if (!rp || rp->view != view || test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) rc = -EACCES; - else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) - rc = -ENODEV; + else if (!raw3270_state_ready(rp)) + rc = -EBUSY; else rc = __raw3270_start(rp, view, rq); return rc; @@ -376,7 +384,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) case RAW3270_IO_STOP: if (!rq) break; - raw3270_halt_io_nolock(rp, rq); + __raw3270_halt_io(rp, rq); rq->rc = -EIO; break; default: @@ -411,9 +419,14 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) } /* - * Size sensing. + * To determine the size of the 3270 device we need to do: + * 1) send a 'read partition' data stream to the device + * 2) wait for the attn interrupt that precedes the query reply + * 3) do a read modified to get the query reply + * To make things worse we have to cope with intervention + * required (3270 device switched to 'stand-by') and command + * rejects (old devices that can't do 'read partition'). */ - struct raw3270_ua { /* Query Reply structure for Usable Area */ struct { /* Usable Area Query Reply Base */ short l; /* Length of this structured field */ @@ -449,117 +462,21 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */ } __attribute__ ((packed)) aua; } __attribute__ ((packed)); -static struct diag210 raw3270_init_diag210; -static DEFINE_MUTEX(raw3270_init_mutex); - -static int -raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, - struct irb *irb) -{ - /* - * Unit-Check Processing: - * Expect Command Reject or Intervention Required. - */ - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { - /* Request finished abnormally. */ - if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { - set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); - return RAW3270_IO_BUSY; - } - } - if (rq) { - if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { - if (irb->ecw[0] & SNS0_CMD_REJECT) - rq->rc = -EOPNOTSUPP; - else - rq->rc = -EIO; - } else - /* Request finished normally. Copy residual count. */ - rq->rescnt = irb->scsw.cmd.count; - } - if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { - set_bit(RAW3270_FLAGS_ATTN, &view->dev->flags); - wake_up(&raw3270_wait_queue); - } - return RAW3270_IO_DONE; -} - -static struct raw3270_fn raw3270_init_fn = { - .intv = raw3270_init_irq -}; - -static struct raw3270_view raw3270_init_view = { - .fn = &raw3270_init_fn -}; - -/* - * raw3270_wait/raw3270_wait_interruptible/__raw3270_wakeup - * Wait for end of request. The request must have been started - * with raw3270_start, rc = 0. The device lock may NOT have been - * released between calling raw3270_start and raw3270_wait. - */ static void -raw3270_wake_init(struct raw3270_request *rq, void *data) -{ - wake_up((wait_queue_head_t *) data); -} - -/* - * Special wait function that can cope with console initialization. - */ -static int -raw3270_start_init(struct raw3270 *rp, struct raw3270_view *view, - struct raw3270_request *rq) -{ - unsigned long flags; - int rc; - -#ifdef CONFIG_TN3270_CONSOLE - if (raw3270_registered == 0) { - spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); - rq->callback = NULL; - rc = __raw3270_start(rp, view, rq); - if (rc == 0) - while (!raw3270_request_final(rq)) { - wait_cons_dev(); - barrier(); - } - spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); - return rq->rc; - } -#endif - rq->callback = raw3270_wake_init; - rq->callback_data = &raw3270_wait_queue; - spin_lock_irqsave(get_ccwdev_lock(view->dev->cdev), flags); - rc = __raw3270_start(rp, view, rq); - spin_unlock_irqrestore(get_ccwdev_lock(view->dev->cdev), flags); - if (rc) - return rc; - /* Now wait for the completion. */ - rc = wait_event_interruptible(raw3270_wait_queue, - raw3270_request_final(rq)); - if (rc == -ERESTARTSYS) { /* Interrupted by a signal. */ - raw3270_halt_io(view->dev, rq); - /* No wait for the halt to complete. */ - wait_event(raw3270_wait_queue, raw3270_request_final(rq)); - return -ERESTARTSYS; - } - return rq->rc; -} - -static int -__raw3270_size_device_vm(struct raw3270 *rp) +raw3270_size_device_vm(struct raw3270 *rp) { int rc, model; struct ccw_dev_id dev_id; + struct diag210 diag_data; ccw_device_get_id(rp->cdev, &dev_id); - raw3270_init_diag210.vrdcdvno = dev_id.devno; - raw3270_init_diag210.vrdclen = sizeof(struct diag210); - rc = diag210(&raw3270_init_diag210); - if (rc) - return rc; - model = raw3270_init_diag210.vrdccrmd; + diag_data.vrdcdvno = dev_id.devno; + diag_data.vrdclen = sizeof(struct diag210); + rc = diag210(&diag_data); + model = diag_data.vrdccrmd; + /* Use default model 2 if the size could not be detected */ + if (rc || model < 2 || model > 5) + model = 2; switch (model) { case 2: rp->model = model; @@ -581,77 +498,25 @@ __raw3270_size_device_vm(struct raw3270 *rp) rp->rows = 27; rp->cols = 132; break; - default: - rc = -EOPNOTSUPP; - break; } - return rc; } -static int -__raw3270_size_device(struct raw3270 *rp) +static void +raw3270_size_device(struct raw3270 *rp) { - static const unsigned char wbuf[] = - { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; struct raw3270_ua *uap; - int rc; - /* - * To determine the size of the 3270 device we need to do: - * 1) send a 'read partition' data stream to the device - * 2) wait for the attn interrupt that precedes the query reply - * 3) do a read modified to get the query reply - * To make things worse we have to cope with intervention - * required (3270 device switched to 'stand-by') and command - * rejects (old devices that can't do 'read partition'). - */ - memset(&rp->init_request, 0, sizeof(rp->init_request)); - memset(&rp->init_data, 0, 256); - /* Store 'read partition' data stream to init_data */ - memcpy(&rp->init_data, wbuf, sizeof(wbuf)); - INIT_LIST_HEAD(&rp->init_request.list); - rp->init_request.ccw.cmd_code = TC_WRITESF; - rp->init_request.ccw.flags = CCW_FLAG_SLI; - rp->init_request.ccw.count = sizeof(wbuf); - rp->init_request.ccw.cda = (__u32) __pa(&rp->init_data); - - rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); - if (rc) - /* Check error cases: -ERESTARTSYS, -EIO and -EOPNOTSUPP */ - return rc; - - /* Wait for attention interrupt. */ -#ifdef CONFIG_TN3270_CONSOLE - if (raw3270_registered == 0) { - unsigned long flags; - - spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - while (!test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags)) - wait_cons_dev(); - spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); - } else -#endif - rc = wait_event_interruptible(raw3270_wait_queue, - test_and_clear_bit(RAW3270_FLAGS_ATTN, &rp->flags)); - if (rc) - return rc; - - /* - * The device accepted the 'read partition' command. Now - * set up a read ccw and issue it. - */ - rp->init_request.ccw.cmd_code = TC_READMOD; - rp->init_request.ccw.flags = CCW_FLAG_SLI; - rp->init_request.ccw.count = sizeof(rp->init_data); - rp->init_request.ccw.cda = (__u32) __pa(rp->init_data); - rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); - if (rc) - return rc; /* Got a Query Reply */ uap = (struct raw3270_ua *) (rp->init_data + 1); /* Paranoia check. */ - if (rp->init_data[0] != 0x88 || uap->uab.qcode != 0x81) - return -EOPNOTSUPP; + if (rp->init_readmod.rc || rp->init_data[0] != 0x88 || + uap->uab.qcode != 0x81) { + /* Couldn't detect size. Use default model 2. */ + rp->model = 2; + rp->rows = 24; + rp->cols = 80; + return; + } /* Copy rows/columns of default Usable Area */ rp->rows = uap->uab.h; rp->cols = uap->uab.w; @@ -664,66 +529,131 @@ __raw3270_size_device(struct raw3270 *rp) rp->rows = uap->aua.hauai; rp->cols = uap->aua.wauai; } - return 0; + /* Try to find a model. */ + rp->model = 0; + if (rp->rows == 24 && rp->cols == 80) + rp->model = 2; + if (rp->rows == 32 && rp->cols == 80) + rp->model = 3; + if (rp->rows == 43 && rp->cols == 80) + rp->model = 4; + if (rp->rows == 27 && rp->cols == 132) + rp->model = 5; +} + +static void +raw3270_size_device_done(struct raw3270 *rp) +{ + struct raw3270_view *view; + + rp->view = NULL; + rp->state = RAW3270_STATE_READY; + /* Notify views about new size */ + list_for_each_entry(view, &rp->view_list, list) + if (view->fn->resize) + view->fn->resize(view, rp->model, rp->rows, rp->cols); + /* Setup processing done, now activate a view */ + list_for_each_entry(view, &rp->view_list, list) { + rp->view = view; + if (view->fn->activate(view) == 0) + break; + rp->view = NULL; + } +} + +static void +raw3270_read_modified_cb(struct raw3270_request *rq, void *data) +{ + struct raw3270 *rp = rq->view->dev; + + raw3270_size_device(rp); + raw3270_size_device_done(rp); +} + +static void +raw3270_read_modified(struct raw3270 *rp) +{ + if (rp->state != RAW3270_STATE_W4ATTN) + return; + /* Use 'read modified' to get the result of a read partition. */ + memset(&rp->init_readmod, 0, sizeof(rp->init_readmod)); + memset(&rp->init_data, 0, sizeof(rp->init_data)); + rp->init_readmod.ccw.cmd_code = TC_READMOD; + rp->init_readmod.ccw.flags = CCW_FLAG_SLI; + rp->init_readmod.ccw.count = sizeof(rp->init_data); + rp->init_readmod.ccw.cda = (__u32) __pa(rp->init_data); + rp->init_readmod.callback = raw3270_read_modified_cb; + rp->state = RAW3270_STATE_READMOD; + raw3270_start_irq(&rp->init_view, &rp->init_readmod); +} + +static void +raw3270_writesf_readpart(struct raw3270 *rp) +{ + static const unsigned char wbuf[] = + { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; + + /* Store 'read partition' data stream to init_data */ + memset(&rp->init_readpart, 0, sizeof(rp->init_readpart)); + memset(&rp->init_data, 0, sizeof(rp->init_data)); + memcpy(&rp->init_data, wbuf, sizeof(wbuf)); + rp->init_readpart.ccw.cmd_code = TC_WRITESF; + rp->init_readpart.ccw.flags = CCW_FLAG_SLI; + rp->init_readpart.ccw.count = sizeof(wbuf); + rp->init_readpart.ccw.cda = (__u32) __pa(&rp->init_data); + rp->state = RAW3270_STATE_W4ATTN; + raw3270_start_irq(&rp->init_view, &rp->init_readpart); +} + +/* + * Device reset + */ +static void +raw3270_reset_device_cb(struct raw3270_request *rq, void *data) +{ + struct raw3270 *rp = rq->view->dev; + + if (rp->state != RAW3270_STATE_RESET) + return; + if (rq && rq->rc) { + /* Reset command failed. */ + rp->state = RAW3270_STATE_INIT; + } else if (0 && MACHINE_IS_VM) { + raw3270_size_device_vm(rp); + raw3270_size_device_done(rp); + } else + raw3270_writesf_readpart(rp); } static int -raw3270_size_device(struct raw3270 *rp) +__raw3270_reset_device(struct raw3270 *rp) { int rc; - mutex_lock(&raw3270_init_mutex); - rp->view = &raw3270_init_view; - raw3270_init_view.dev = rp; - if (MACHINE_IS_VM) - rc = __raw3270_size_device_vm(rp); - else - rc = __raw3270_size_device(rp); - raw3270_init_view.dev = NULL; - rp->view = NULL; - mutex_unlock(&raw3270_init_mutex); - if (rc == 0) { /* Found something. */ - /* Try to find a model. */ - rp->model = 0; - if (rp->rows == 24 && rp->cols == 80) - rp->model = 2; - if (rp->rows == 32 && rp->cols == 80) - rp->model = 3; - if (rp->rows == 43 && rp->cols == 80) - rp->model = 4; - if (rp->rows == 27 && rp->cols == 132) - rp->model = 5; - } else { - /* Couldn't detect size. Use default model 2. */ - rp->model = 2; - rp->rows = 24; - rp->cols = 80; - return 0; - } + /* Store reset data stream to init_data/init_reset */ + memset(&rp->init_reset, 0, sizeof(rp->init_reset)); + memset(&rp->init_data, 0, sizeof(rp->init_data)); + rp->init_data[0] = TW_KR; + rp->init_reset.ccw.cmd_code = TC_EWRITEA; + rp->init_reset.ccw.flags = CCW_FLAG_SLI; + rp->init_reset.ccw.count = 1; + rp->init_reset.ccw.cda = (__u32) __pa(rp->init_data); + rp->init_reset.callback = raw3270_reset_device_cb; + rc = __raw3270_start(rp, &rp->init_view, &rp->init_reset); + if (rc == 0 && rp->state == RAW3270_STATE_INIT) + rp->state = RAW3270_STATE_RESET; return rc; } static int raw3270_reset_device(struct raw3270 *rp) { + unsigned long flags; int rc; - mutex_lock(&raw3270_init_mutex); - memset(&rp->init_request, 0, sizeof(rp->init_request)); - memset(&rp->init_data, 0, sizeof(rp->init_data)); - /* Store reset data stream to init_data/init_request */ - rp->init_data[0] = TW_KR; - INIT_LIST_HEAD(&rp->init_request.list); - rp->init_request.ccw.cmd_code = TC_EWRITEA; - rp->init_request.ccw.flags = CCW_FLAG_SLI; - rp->init_request.ccw.count = 1; - rp->init_request.ccw.cda = (__u32) __pa(rp->init_data); - rp->view = &raw3270_init_view; - raw3270_init_view.dev = rp; - rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request); - raw3270_init_view.dev = NULL; - rp->view = NULL; - mutex_unlock(&raw3270_init_mutex); + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + rc = __raw3270_reset_device(rp); + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); return rc; } @@ -737,13 +667,50 @@ raw3270_reset(struct raw3270_view *view) if (!rp || rp->view != view || test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) rc = -EACCES; - else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) - rc = -ENODEV; + else if (!raw3270_state_ready(rp)) + rc = -EBUSY; else rc = raw3270_reset_device(view->dev); return rc; } +static int +raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq, + struct irb *irb) +{ + struct raw3270 *rp; + + /* + * Unit-Check Processing: + * Expect Command Reject or Intervention Required. + */ + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { + /* Request finished abnormally. */ + if (irb->ecw[0] & SNS0_INTERVENTION_REQ) { + set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags); + return RAW3270_IO_BUSY; + } + } + if (rq) { + if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) { + if (irb->ecw[0] & SNS0_CMD_REJECT) + rq->rc = -EOPNOTSUPP; + else + rq->rc = -EIO; + } + } + if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) { + /* Queue read modified after attention interrupt */ + rp = view->dev; + raw3270_read_modified(rp); + } + return RAW3270_IO_DONE; +} + +static struct raw3270_fn raw3270_init_fn = { + .intv = raw3270_init_irq +}; + /* * Setup new 3270 device. */ @@ -772,6 +739,10 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) INIT_LIST_HEAD(&rp->req_queue); INIT_LIST_HEAD(&rp->view_list); + rp->init_view.dev = rp; + rp->init_view.fn = &raw3270_init_fn; + rp->view = &rp->init_view; + /* * Add device to list and find the smallest unused minor * number for it. Note: there is no device with minor 0, @@ -810,6 +781,7 @@ raw3270_setup_device(struct ccw_device *cdev, struct raw3270 *rp, char *ascebc) */ struct raw3270 __init *raw3270_setup_console(struct ccw_device *cdev) { + unsigned long flags; struct raw3270 *rp; char *ascebc; int rc; @@ -820,16 +792,15 @@ struct raw3270 __init *raw3270_setup_console(struct ccw_device *cdev) if (rc) return ERR_PTR(rc); set_bit(RAW3270_FLAGS_CONSOLE, &rp->flags); - rc = raw3270_reset_device(rp); - if (rc) - return ERR_PTR(rc); - rc = raw3270_size_device(rp); - if (rc) - return ERR_PTR(rc); - rc = raw3270_reset_device(rp); - if (rc) - return ERR_PTR(rc); - set_bit(RAW3270_FLAGS_READY, &rp->flags); + spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); + do { + __raw3270_reset_device(rp); + while (!raw3270_state_final(rp)) { + wait_cons_dev(); + barrier(); + } + } while (rp->state != RAW3270_STATE_READY); + spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); return rp; } @@ -891,13 +862,13 @@ raw3270_activate_view(struct raw3270_view *view) spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); if (rp->view == view) rc = 0; - else if (!test_bit(RAW3270_FLAGS_READY, &rp->flags)) - rc = -ENODEV; + else if (!raw3270_state_ready(rp)) + rc = -EBUSY; else if (test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) rc = -EACCES; else { oldview = NULL; - if (rp->view) { + if (rp->view && rp->view->fn->deactivate) { oldview = rp->view; oldview->fn->deactivate(oldview); } @@ -942,7 +913,7 @@ raw3270_deactivate_view(struct raw3270_view *view) list_del_init(&view->list); list_add_tail(&view->list, &rp->view_list); /* Try to activate another view. */ - if (test_bit(RAW3270_FLAGS_READY, &rp->flags) && + if (raw3270_state_ready(rp) && !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) { list_for_each_entry(view, &rp->view_list, list) { rp->view = view; @@ -973,18 +944,16 @@ raw3270_add_view(struct raw3270_view *view, struct raw3270_fn *fn, int minor) if (rp->minor != minor) continue; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) { - atomic_set(&view->ref_count, 2); - view->dev = rp; - view->fn = fn; - view->model = rp->model; - view->rows = rp->rows; - view->cols = rp->cols; - view->ascebc = rp->ascebc; - spin_lock_init(&view->lock); - list_add(&view->list, &rp->view_list); - rc = 0; - } + atomic_set(&view->ref_count, 2); + view->dev = rp; + view->fn = fn; + view->model = rp->model; + view->rows = rp->rows; + view->cols = rp->cols; + view->ascebc = rp->ascebc; + spin_lock_init(&view->lock); + list_add(&view->list, &rp->view_list); + rc = 0; spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); break; } @@ -1008,14 +977,11 @@ raw3270_find_view(struct raw3270_fn *fn, int minor) if (rp->minor != minor) continue; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - if (test_bit(RAW3270_FLAGS_READY, &rp->flags)) { - view = ERR_PTR(-ENOENT); - list_for_each_entry(tmp, &rp->view_list, list) { - if (tmp->fn == fn) { - raw3270_get_view(tmp); - view = tmp; - break; - } + list_for_each_entry(tmp, &rp->view_list, list) { + if (tmp->fn == fn) { + raw3270_get_view(tmp); + view = tmp; + break; } } spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); @@ -1042,7 +1008,7 @@ raw3270_del_view(struct raw3270_view *view) rp->view = NULL; } list_del_init(&view->list); - if (!rp->view && test_bit(RAW3270_FLAGS_READY, &rp->flags) && + if (!rp->view && raw3270_state_ready(rp) && !test_bit(RAW3270_FLAGS_FROZEN, &rp->flags)) { /* Try to activate another view. */ list_for_each_entry(nv, &rp->view_list, list) { @@ -1177,19 +1143,10 @@ raw3270_set_online (struct ccw_device *cdev) rp = raw3270_create_device(cdev); if (IS_ERR(rp)) return PTR_ERR(rp); - rc = raw3270_reset_device(rp); - if (rc) - goto failure; - rc = raw3270_size_device(rp); - if (rc) - goto failure; - rc = raw3270_reset_device(rp); - if (rc) - goto failure; rc = raw3270_create_attributes(rp); if (rc) goto failure; - set_bit(RAW3270_FLAGS_READY, &rp->flags); + raw3270_reset_device(rp); mutex_lock(&raw3270_mutex); list_for_each_entry(np, &raw3270_notifier, list) np->create(rp->minor); @@ -1221,14 +1178,14 @@ raw3270_remove (struct ccw_device *cdev) */ if (rp == NULL) return; - clear_bit(RAW3270_FLAGS_READY, &rp->flags); sysfs_remove_group(&cdev->dev.kobj, &raw3270_attr_group); /* Deactivate current view and remove all views. */ spin_lock_irqsave(get_ccwdev_lock(cdev), flags); if (rp->view) { - rp->view->fn->deactivate(rp->view); + if (rp->view->fn->deactivate) + rp->view->fn->deactivate(rp->view); rp->view = NULL; } while (!list_empty(&rp->view_list)) { @@ -1277,7 +1234,7 @@ static int raw3270_pm_stop(struct ccw_device *cdev) if (!rp) return 0; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); - if (rp->view) + if (rp->view && rp->view->fn->deactivate) rp->view->fn->deactivate(rp->view); if (!test_bit(RAW3270_FLAGS_CONSOLE, &rp->flags)) { /* @@ -1304,7 +1261,7 @@ static int raw3270_pm_start(struct ccw_device *cdev) return 0; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); clear_bit(RAW3270_FLAGS_FROZEN, &rp->flags); - if (rp->view) + if (rp->view && rp->view->fn->activate) rp->view->fn->activate(rp->view); spin_unlock_irqrestore(get_ccwdev_lock(rp->cdev), flags); return 0; diff --git a/drivers/s390/char/raw3270.h b/drivers/s390/char/raw3270.h index a4c79d043cd3..7b73ff8c1bd7 100644 --- a/drivers/s390/char/raw3270.h +++ b/drivers/s390/char/raw3270.h @@ -141,6 +141,7 @@ struct raw3270_fn { struct raw3270_request *, struct irb *); void (*release)(struct raw3270_view *); void (*free)(struct raw3270_view *); + void (*resize)(struct raw3270_view *, int, int, int); }; /* diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c index 48767e6bab9d..8f1e02543ada 100644 --- a/drivers/s390/char/tty3270.c +++ b/drivers/s390/char/tty3270.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -80,6 +81,8 @@ struct tty3270 { unsigned int highlight; /* Blink/reverse/underscore */ unsigned int f_color; /* Foreground color */ struct tty3270_line *screen; + unsigned int n_model, n_cols, n_rows; /* New model & size */ + struct work_struct resize_work; /* Input stuff. */ struct string *prompt; /* Output string for input area. */ @@ -115,6 +118,7 @@ struct tty3270 { #define TTY_UPDATE_ALL 16 /* Recreate screen. */ static void tty3270_update(struct tty3270 *); +static void tty3270_resize_work(struct work_struct *work); /* * Setup timeout for a device. On timeout trigger an update. @@ -711,6 +715,7 @@ tty3270_alloc_view(void) tasklet_init(&tp->readlet, (void (*)(unsigned long)) tty3270_read_tasklet, (unsigned long) tp->read); + INIT_WORK(&tp->resize_work, tty3270_resize_work); return tp; @@ -754,42 +759,96 @@ tty3270_free_view(struct tty3270 *tp) /* * Allocate tty3270 screen. */ -static int -tty3270_alloc_screen(struct tty3270 *tp) +static struct tty3270_line * +tty3270_alloc_screen(unsigned int rows, unsigned int cols) { + struct tty3270_line *screen; unsigned long size; int lines; - size = sizeof(struct tty3270_line) * (tp->view.rows - 2); - tp->screen = kzalloc(size, GFP_KERNEL); - if (!tp->screen) + size = sizeof(struct tty3270_line) * (rows - 2); + screen = kzalloc(size, GFP_KERNEL); + if (!screen) goto out_err; - for (lines = 0; lines < tp->view.rows - 2; lines++) { - size = sizeof(struct tty3270_cell) * tp->view.cols; - tp->screen[lines].cells = kzalloc(size, GFP_KERNEL); - if (!tp->screen[lines].cells) + for (lines = 0; lines < rows - 2; lines++) { + size = sizeof(struct tty3270_cell) * cols; + screen[lines].cells = kzalloc(size, GFP_KERNEL); + if (!screen[lines].cells) goto out_screen; } - return 0; + return screen; out_screen: while (lines--) - kfree(tp->screen[lines].cells); - kfree(tp->screen); + kfree(screen[lines].cells); + kfree(screen); out_err: - return -ENOMEM; + return ERR_PTR(-ENOMEM); } /* * Free tty3270 screen. */ static void -tty3270_free_screen(struct tty3270 *tp) +tty3270_free_screen(struct tty3270_line *screen, unsigned int rows) { int lines; - for (lines = 0; lines < tp->view.rows - 2; lines++) - kfree(tp->screen[lines].cells); - kfree(tp->screen); + for (lines = 0; lines < rows - 2; lines++) + kfree(screen[lines].cells); + kfree(screen); +} + +/* + * Resize tty3270 screen + */ +static void tty3270_resize_work(struct work_struct *work) +{ + struct tty3270 *tp = container_of(work, struct tty3270, resize_work); + struct tty3270_line *screen, *oscreen; + struct tty_struct *tty; + unsigned int orows; + struct winsize ws; + + screen = tty3270_alloc_screen(tp->n_rows, tp->n_cols); + if (!screen) + return; + /* Switch to new output size */ + spin_lock_bh(&tp->view.lock); + oscreen = tp->screen; + orows = tp->view.rows; + tp->view.model = tp->n_model; + tp->view.rows = tp->n_rows; + tp->view.cols = tp->n_cols; + tp->screen = screen; + free_string(&tp->freemem, tp->prompt); + free_string(&tp->freemem, tp->status); + tty3270_create_prompt(tp); + tty3270_create_status(tp); + tp->nr_up = 0; + while (tp->nr_lines < tp->view.rows - 2) + tty3270_blank_line(tp); + tp->update_flags = TTY_UPDATE_ALL; + spin_unlock_bh(&tp->view.lock); + tty3270_free_screen(oscreen, orows); + tty3270_set_timer(tp, 1); + /* Informat tty layer about new size */ + tty = tty_port_tty_get(&tp->port); + if (!tty) + return; + ws.ws_row = tp->view.rows - 2; + ws.ws_col = tp->view.cols; + tty_do_resize(tty, &ws); +} + +static void +tty3270_resize(struct raw3270_view *view, int model, int rows, int cols) +{ + struct tty3270 *tp = container_of(view, struct tty3270, view); + + tp->n_model = model; + tp->n_rows = rows; + tp->n_cols = cols; + schedule_work(&tp->resize_work); } /* @@ -817,7 +876,8 @@ static void tty3270_free(struct raw3270_view *view) { struct tty3270 *tp = container_of(view, struct tty3270, view); - tty3270_free_screen(tp); + + tty3270_free_screen(tp->screen, tp->view.rows); tty3270_free_view(tp); } @@ -841,7 +901,8 @@ static struct raw3270_fn tty3270_fn = { .deactivate = tty3270_deactivate, .intv = (void *) tty3270_irq, .release = tty3270_release, - .free = tty3270_free + .free = tty3270_free, + .resize = tty3270_resize }; /* @@ -869,10 +930,6 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) if (tty3270_max_index < tty->index) tty3270_max_index = tty->index; - /* Quick exit if there is no device for tty->index. */ - if (PTR_ERR(view) == -ENODEV) - return -ENODEV; - /* Allocate tty3270 structure on first open. */ tp = tty3270_alloc_view(); if (IS_ERR(tp)) @@ -884,10 +941,12 @@ static int tty3270_install(struct tty_driver *driver, struct tty_struct *tty) return rc; } - rc = tty3270_alloc_screen(tp); - if (rc) { + tp->screen = tty3270_alloc_screen(tp->view.cols, tp->view.rows); + if (IS_ERR(tp->screen)) { + rc = PTR_ERR(tp->screen); raw3270_put_view(&tp->view); raw3270_del_view(&tp->view); + tty3270_free_view(tp); return rc; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index da9fde850754..3f00f9e8daf2 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2203,6 +2203,7 @@ done: mutex_unlock(&tty->termios_mutex); return 0; } +EXPORT_SYMBOL(tty_do_resize); /** * tiocswinsz - implement window size set ioctl From 083e14c09b7ae0247b9944a386fdc32cd0719da1 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Fri, 11 Jan 2013 13:15:35 +0100 Subject: [PATCH 2697/4607] s390/modules: add relocation overflow checking Given enough debug options some modules can grow large enough that the GOT table gets bigger than 4K. On s390 the modules are compiled with -fpic which limits the GOT to 4K. The end result is a module that is loaded but won't work. Add a sanity check to apply_rela and return with an error if a relocation error is detected for a module. Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/module.c | 140 ++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 51 deletions(-) diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c index 4610deafd953..06f17311628a 100644 --- a/arch/s390/kernel/module.c +++ b/arch/s390/kernel/module.c @@ -65,8 +65,7 @@ void module_free(struct module *mod, void *module_region) vfree(module_region); } -static void -check_rela(Elf_Rela *rela, struct module *me) +static void check_rela(Elf_Rela *rela, struct module *me) { struct mod_arch_syminfo *info; @@ -115,9 +114,8 @@ check_rela(Elf_Rela *rela, struct module *me) * Account for GOT and PLT relocations. We can't add sections for * got and plt but we can increase the core module size. */ -int -module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, - char *secstrings, struct module *me) +int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, + char *secstrings, struct module *me) { Elf_Shdr *symtab; Elf_Sym *symbols; @@ -179,13 +177,52 @@ module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, return 0; } -static int -apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, - struct module *me) +static int apply_rela_bits(Elf_Addr loc, Elf_Addr val, + int sign, int bits, int shift) +{ + unsigned long umax; + long min, max; + + if (val & ((1UL << shift) - 1)) + return -ENOEXEC; + if (sign) { + val = (Elf_Addr)(((long) val) >> shift); + min = -(1L << (bits - 1)); + max = (1L << (bits - 1)) - 1; + if ((long) val < min || (long) val > max) + return -ENOEXEC; + } else { + val >>= shift; + umax = ((1UL << (bits - 1)) << 1) - 1; + if ((unsigned long) val > umax) + return -ENOEXEC; + } + + if (bits == 8) + *(unsigned char *) loc = val; + else if (bits == 12) + *(unsigned short *) loc = (val & 0xfff) | + (*(unsigned short *) loc & 0xf000); + else if (bits == 16) + *(unsigned short *) loc = val; + else if (bits == 20) + *(unsigned int *) loc = (val & 0xfff) << 16 | + (val & 0xff000) >> 4 | + (*(unsigned int *) loc & 0xf00000ff); + else if (bits == 32) + *(unsigned int *) loc = val; + else if (bits == 64) + *(unsigned long *) loc = val; + return 0; +} + +static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, + const char *strtab, struct module *me) { struct mod_arch_syminfo *info; Elf_Addr loc, val; int r_type, r_sym; + int rc; /* This is where to make the change */ loc = base + rela->r_offset; @@ -205,20 +242,17 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, case R_390_64: /* Direct 64 bit. */ val += rela->r_addend; if (r_type == R_390_8) - *(unsigned char *) loc = val; + rc = apply_rela_bits(loc, val, 0, 8, 0); else if (r_type == R_390_12) - *(unsigned short *) loc = (val & 0xfff) | - (*(unsigned short *) loc & 0xf000); + rc = apply_rela_bits(loc, val, 0, 12, 0); else if (r_type == R_390_16) - *(unsigned short *) loc = val; + rc = apply_rela_bits(loc, val, 0, 16, 0); else if (r_type == R_390_20) - *(unsigned int *) loc = - (*(unsigned int *) loc & 0xf00000ff) | - (val & 0xfff) << 16 | (val & 0xff000) >> 4; + rc = apply_rela_bits(loc, val, 1, 20, 0); else if (r_type == R_390_32) - *(unsigned int *) loc = val; + rc = apply_rela_bits(loc, val, 0, 32, 0); else if (r_type == R_390_64) - *(unsigned long *) loc = val; + rc = apply_rela_bits(loc, val, 0, 64, 0); break; case R_390_PC16: /* PC relative 16 bit. */ case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */ @@ -227,15 +261,15 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, case R_390_PC64: /* PC relative 64 bit. */ val += rela->r_addend - loc; if (r_type == R_390_PC16) - *(unsigned short *) loc = val; + rc = apply_rela_bits(loc, val, 1, 16, 0); else if (r_type == R_390_PC16DBL) - *(unsigned short *) loc = val >> 1; + rc = apply_rela_bits(loc, val, 1, 16, 1); else if (r_type == R_390_PC32DBL) - *(unsigned int *) loc = val >> 1; + rc = apply_rela_bits(loc, val, 1, 32, 1); else if (r_type == R_390_PC32) - *(unsigned int *) loc = val; + rc = apply_rela_bits(loc, val, 1, 32, 0); else if (r_type == R_390_PC64) - *(unsigned long *) loc = val; + rc = apply_rela_bits(loc, val, 1, 64, 0); break; case R_390_GOT12: /* 12 bit GOT offset. */ case R_390_GOT16: /* 16 bit GOT offset. */ @@ -260,26 +294,24 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, val = info->got_offset + rela->r_addend; if (r_type == R_390_GOT12 || r_type == R_390_GOTPLT12) - *(unsigned short *) loc = (val & 0xfff) | - (*(unsigned short *) loc & 0xf000); + rc = apply_rela_bits(loc, val, 0, 12, 0); else if (r_type == R_390_GOT16 || r_type == R_390_GOTPLT16) - *(unsigned short *) loc = val; + rc = apply_rela_bits(loc, val, 0, 16, 0); else if (r_type == R_390_GOT20 || r_type == R_390_GOTPLT20) - *(unsigned int *) loc = - (*(unsigned int *) loc & 0xf00000ff) | - (val & 0xfff) << 16 | (val & 0xff000) >> 4; + rc = apply_rela_bits(loc, val, 1, 20, 0); else if (r_type == R_390_GOT32 || r_type == R_390_GOTPLT32) - *(unsigned int *) loc = val; - else if (r_type == R_390_GOTENT || - r_type == R_390_GOTPLTENT) - *(unsigned int *) loc = - (val + (Elf_Addr) me->module_core - loc) >> 1; + rc = apply_rela_bits(loc, val, 0, 32, 0); else if (r_type == R_390_GOT64 || r_type == R_390_GOTPLT64) - *(unsigned long *) loc = val; + rc = apply_rela_bits(loc, val, 0, 64, 0); + else if (r_type == R_390_GOTENT || + r_type == R_390_GOTPLTENT) { + val += (Elf_Addr) me->module_core - loc; + rc = apply_rela_bits(loc, val, 1, 32, 1); + } break; case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */ case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */ @@ -321,17 +353,17 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, val += rela->r_addend - loc; } if (r_type == R_390_PLT16DBL) - *(unsigned short *) loc = val >> 1; + rc = apply_rela_bits(loc, val, 1, 16, 1); else if (r_type == R_390_PLTOFF16) - *(unsigned short *) loc = val; + rc = apply_rela_bits(loc, val, 0, 16, 0); else if (r_type == R_390_PLT32DBL) - *(unsigned int *) loc = val >> 1; + rc = apply_rela_bits(loc, val, 1, 32, 1); else if (r_type == R_390_PLT32 || r_type == R_390_PLTOFF32) - *(unsigned int *) loc = val; + rc = apply_rela_bits(loc, val, 0, 32, 0); else if (r_type == R_390_PLT64 || r_type == R_390_PLTOFF64) - *(unsigned long *) loc = val; + rc = apply_rela_bits(loc, val, 0, 64, 0); break; case R_390_GOTOFF16: /* 16 bit offset to GOT. */ case R_390_GOTOFF32: /* 32 bit offset to GOT. */ @@ -339,20 +371,20 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, val = val + rela->r_addend - ((Elf_Addr) me->module_core + me->arch.got_offset); if (r_type == R_390_GOTOFF16) - *(unsigned short *) loc = val; + rc = apply_rela_bits(loc, val, 0, 16, 0); else if (r_type == R_390_GOTOFF32) - *(unsigned int *) loc = val; + rc = apply_rela_bits(loc, val, 0, 32, 0); else if (r_type == R_390_GOTOFF64) - *(unsigned long *) loc = val; + rc = apply_rela_bits(loc, val, 0, 64, 0); break; case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */ case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */ val = (Elf_Addr) me->module_core + me->arch.got_offset + rela->r_addend - loc; if (r_type == R_390_GOTPC) - *(unsigned int *) loc = val; + rc = apply_rela_bits(loc, val, 1, 32, 0); else if (r_type == R_390_GOTPCDBL) - *(unsigned int *) loc = val >> 1; + rc = apply_rela_bits(loc, val, 1, 32, 1); break; case R_390_COPY: case R_390_GLOB_DAT: /* Create GOT entry. */ @@ -360,19 +392,25 @@ apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, case R_390_RELATIVE: /* Adjust by program base. */ /* Only needed if we want to support loading of modules linked with -shared. */ - break; + return -ENOEXEC; default: - printk(KERN_ERR "module %s: Unknown relocation: %u\n", + printk(KERN_ERR "module %s: unknown relocation: %u\n", me->name, r_type); return -ENOEXEC; } + if (rc) { + printk(KERN_ERR "module %s: relocation error for symbol %s " + "(r_type %i, value 0x%lx)\n", + me->name, strtab + symtab[r_sym].st_name, + r_type, (unsigned long) val); + return rc; + } return 0; } -int -apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, - unsigned int symindex, unsigned int relsec, - struct module *me) +int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, + unsigned int symindex, unsigned int relsec, + struct module *me) { Elf_Addr base; Elf_Sym *symtab; @@ -388,7 +426,7 @@ apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, n = sechdrs[relsec].sh_size / sizeof(Elf_Rela); for (i = 0; i < n; i++, rela++) { - rc = apply_rela(rela, base, symtab, me); + rc = apply_rela(rela, base, symtab, strtab, me); if (rc) return rc; } From 5c8d0983fcd1a57acae15f717a67127f97d06780 Mon Sep 17 00:00:00 2001 From: Ingo Tuchscherer Date: Mon, 14 Jan 2013 10:07:05 +0100 Subject: [PATCH 2698/4607] maintainer for s390 zcrypt component changed Signed-off-by: Ingo Tuchscherer Signed-off-by: Martin Schwidefsky --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 35a56bcd5e75..577d0fb0953d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6498,7 +6498,7 @@ S: Supported F: drivers/s390/net/ S390 ZCRYPT DRIVER -M: Holger Dengler +M: Ingo Tuchscherer M: linux390@de.ibm.com L: linux-s390@vger.kernel.org W: http://www.ibm.com/developerworks/linux/linux390/ From 9a17e972529e07d6e2531e6b6712bf29687df8a6 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 15 Jan 2013 19:04:39 +0100 Subject: [PATCH 2699/4607] s390/chsc: cleanup SEI helper functions Cleanup the functions used to call SEI. Also provide !CONFIG_PCI dummys for pci error handling. Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/pci.h | 5 +++ drivers/s390/cio/chsc.c | 68 +++++++++++++++++-------------------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index b1fa93c606ad..23d6a245e8a7 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -160,9 +160,14 @@ void zpci_teardown_msi_irq(struct zpci_dev *, struct msi_desc *); int zpci_msihash_init(void); void zpci_msihash_exit(void); +#ifdef CONFIG_PCI /* Error handling and recovery */ void zpci_event_error(void *); void zpci_event_availability(void *); +#else /* CONFIG_PCI */ +static inline void zpci_event_error(void *e) {} +static inline void zpci_event_availability(void *e) {} +#endif /* CONFIG_PCI */ /* Helpers */ struct zpci_dev *get_zdev(struct pci_dev *); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 10729bbceced..31ceef1beb8b 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -435,7 +435,6 @@ static void chsc_process_sei_scm_change(struct chsc_sei_nt0_area *sei_area) static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area) { -#ifdef CONFIG_PCI switch (sei_area->cc) { case 1: zpci_event_error(sei_area->ccdf); @@ -444,11 +443,10 @@ static void chsc_process_sei_nt2(struct chsc_sei_nt2_area *sei_area) zpci_event_availability(sei_area->ccdf); break; default: - CIO_CRW_EVENT(2, "chsc: unhandled sei content code %d\n", + CIO_CRW_EVENT(2, "chsc: sei nt2 unhandled cc=%d\n", sei_area->cc); break; } -#endif } static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area) @@ -471,13 +469,19 @@ static void chsc_process_sei_nt0(struct chsc_sei_nt0_area *sei_area) chsc_process_sei_scm_change(sei_area); break; default: /* other stuff */ - CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", + CIO_CRW_EVENT(2, "chsc: sei nt0 unhandled cc=%d\n", sei_area->cc); break; } + + /* Check if we might have lost some information. */ + if (sei_area->flags & 0x40) { + CIO_CRW_EVENT(2, "chsc: event overflow\n"); + css_schedule_eval_all(); + } } -static int __chsc_process_crw(struct chsc_sei *sei, u64 ntsm) +static void chsc_process_event_information(struct chsc_sei *sei, u64 ntsm) { do { memset(sei, 0, sizeof(*sei)); @@ -488,40 +492,37 @@ static int __chsc_process_crw(struct chsc_sei *sei, u64 ntsm) if (chsc(sei)) break; - if (sei->response.code == 0x0001) { - CIO_CRW_EVENT(2, "chsc: sei successful\n"); - - /* Check if we might have lost some information. */ - if (sei->u.nt0_area.flags & 0x40) { - CIO_CRW_EVENT(2, "chsc: event overflow\n"); - css_schedule_eval_all(); - } - - switch (sei->nt) { - case 0: - chsc_process_sei_nt0(&sei->u.nt0_area); - break; - case 2: - chsc_process_sei_nt2(&sei->u.nt2_area); - break; - default: - CIO_CRW_EVENT(2, "chsc: unhandled nt=%d\n", - sei->nt); - break; - } - } else { + if (sei->response.code != 0x0001) { CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", sei->response.code); break; } - } while (sei->u.nt0_area.flags & 0x80); - return 0; + CIO_CRW_EVENT(2, "chsc: sei successful (nt=%d)\n", sei->nt); + switch (sei->nt) { + case 0: + chsc_process_sei_nt0(&sei->u.nt0_area); + break; + case 2: + chsc_process_sei_nt2(&sei->u.nt2_area); + break; + default: + CIO_CRW_EVENT(2, "chsc: unhandled nt: %d\n", sei->nt); + break; + } + } while (sei->u.nt0_area.flags & 0x80); } +/* + * Handle channel subsystem related CRWs. + * Use store event information to find out what's going on. + * + * Note: Access to sei_page is serialized through machine check handler + * thread, so no need for locking. + */ static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) { - struct chsc_sei *sei; + struct chsc_sei *sei = sei_page; if (overflow) { css_schedule_eval_all(); @@ -531,14 +532,9 @@ static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow) "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc, crw0->erc, crw0->rsid); - if (!sei_page) - return; - /* Access to sei_page is serialized through machine check handler - * thread, so no need for locking. */ - sei = sei_page; CIO_TRACE_EVENT(2, "prcss"); - __chsc_process_crw(sei, CHSC_SEI_NT0 | CHSC_SEI_NT2); + chsc_process_event_information(sei, CHSC_SEI_NT0 | CHSC_SEI_NT2); } void chsc_chp_online(struct chp_id chpid) From 0894b3ae776a60c6bad994e1d8f809ceb59904da Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Mon, 21 Jan 2013 18:35:15 +0100 Subject: [PATCH 2700/4607] s390/ipl: Implement diag308 loop for zfcpdump When a zfcpdump is triggered and a second dump on the same CEC is already in progress for another LPAR, diagnose 308 returns with an error code until the first dump is finished. Currently the second Linux stops with a disabled wait PSW in that case. This is improved now by by triggering diag 308 in a loop until it works. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/ipl.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 6ffcd3203215..d8a6a385d048 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -1414,6 +1414,16 @@ static struct kobj_attribute dump_type_attr = static struct kset *dump_kset; +static void diag308_dump(void *dump_block) +{ + diag308(DIAG308_SET, dump_block); + while (1) { + if (diag308(DIAG308_DUMP, NULL) != 0x302) + break; + udelay_simple(USEC_PER_SEC); + } +} + static void __dump_run(void *unused) { struct ccw_dev_id devid; @@ -1432,12 +1442,10 @@ static void __dump_run(void *unused) __cpcmd(buf, NULL, 0, NULL); break; case DUMP_METHOD_CCW_DIAG: - diag308(DIAG308_SET, dump_block_ccw); - diag308(DIAG308_DUMP, NULL); + diag308_dump(dump_block_ccw); break; case DUMP_METHOD_FCP_DIAG: - diag308(DIAG308_SET, dump_block_fcp); - diag308(DIAG308_DUMP, NULL); + diag308_dump(dump_block_fcp); break; default: break; From b4b3d128c821d70112ac0096d5c1440f5ed9f718 Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Mon, 21 Jan 2013 18:37:41 +0100 Subject: [PATCH 2701/4607] s390/zcore: Add hsa file Under LPAR the zfcpdump HSA is a shared resource. Up to now the HSA memory is released when the zcore file is closed. Dump programs that know that they do not need the HSA memory any more (e.g. because they already dumped it) could release it earlier. This would allow other LPARs to use it again. To achieve this a new debugfs file "hsa" is added that can be used to read the HSA size and to release the HSA by writing "0" into the file. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- drivers/s390/char/zcore.c | 62 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index e3b9308b0fe3..681749e7f6dd 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -62,6 +62,7 @@ static struct dentry *zcore_dir; static struct dentry *zcore_file; static struct dentry *zcore_memmap_file; static struct dentry *zcore_reipl_file; +static struct dentry *zcore_hsa_file; static struct ipl_parameter_block *ipl_block; /* @@ -77,6 +78,8 @@ static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode) int offs, blk_num; static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); + if (!hsa_available) + return -ENODATA; if (count == 0) return 0; @@ -277,6 +280,15 @@ next: return 0; } +/* + * Release the HSA + */ +static void release_hsa(void) +{ + diag308(DIAG308_REL_HSA, NULL); + hsa_available = 0; +} + /* * Read routine for zcore character device * First 4K are dump header @@ -363,8 +375,8 @@ static int zcore_open(struct inode *inode, struct file *filp) static int zcore_release(struct inode *inode, struct file *filep) { - diag308(DIAG308_REL_HSA, NULL); - hsa_available = 0; + if (hsa_available) + release_hsa(); return 0; } @@ -474,6 +486,41 @@ static const struct file_operations zcore_reipl_fops = { .llseek = no_llseek, }; +static ssize_t zcore_hsa_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + static char str[18]; + + if (hsa_available) + snprintf(str, sizeof(str), "%lx\n", ZFCPDUMP_HSA_SIZE); + else + snprintf(str, sizeof(str), "0\n"); + return simple_read_from_buffer(buf, count, ppos, str, strlen(str)); +} + +static ssize_t zcore_hsa_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + char value; + + if (*ppos != 0) + return -EPIPE; + if (copy_from_user(&value, buf, 1)) + return -EFAULT; + if (value != '0') + return -EINVAL; + release_hsa(); + return count; +} + +static const struct file_operations zcore_hsa_fops = { + .owner = THIS_MODULE, + .write = zcore_hsa_write, + .read = zcore_hsa_read, + .open = nonseekable_open, + .llseek = no_llseek, +}; + #ifdef CONFIG_32BIT static void __init set_lc_mask(struct save_area *map) @@ -658,6 +705,7 @@ static int __init zcore_init(void) rc = check_sdias(); if (rc) goto fail; + hsa_available = 1; rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1); if (rc) @@ -714,9 +762,16 @@ static int __init zcore_init(void) rc = -ENOMEM; goto fail_memmap_file; } - hsa_available = 1; + zcore_hsa_file = debugfs_create_file("hsa", S_IRUSR|S_IWUSR, zcore_dir, + NULL, &zcore_hsa_fops); + if (!zcore_hsa_file) { + rc = -ENOMEM; + goto fail_reipl_file; + } return 0; +fail_reipl_file: + debugfs_remove(zcore_reipl_file); fail_memmap_file: debugfs_remove(zcore_memmap_file); fail_file: @@ -733,6 +788,7 @@ static void __exit zcore_exit(void) debug_unregister(zcore_dbf); sclp_sdias_exit(); free_page((unsigned long) ipl_block); + debugfs_remove(zcore_hsa_file); debugfs_remove(zcore_reipl_file); debugfs_remove(zcore_memmap_file); debugfs_remove(zcore_file); From 69f5576f6c8c9d0f0b3670ee7c807a194b4c40f4 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 28 Jan 2013 19:29:43 +0100 Subject: [PATCH 2702/4607] s390/cio: dont abort verification after missing irq Do not abort path verification when waiting for an interrupt timed out. Use path_noirq_mask to keep track of the paths used for this (also maintain a path_notoper_mask for debugging purposes). If the timeout happend to be during an operation where we query or alter the state of path groups set the pgid_unknown flag. With this change we allow usage of devices which have such ill-behaved paths (if at least one path is operational). Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 48 +++++++++++++++++++++++++++------- drivers/s390/cio/io_sch.h | 5 ++++ 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 908d287f66c1..6f2987d8da99 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -102,10 +102,20 @@ static void nop_callback(struct ccw_device *cdev, void *data, int rc) struct subchannel *sch = to_subchannel(cdev->dev.parent); struct ccw_request *req = &cdev->private->req; - if (rc == 0) + switch (rc) { + case 0: sch->vpm |= req->lpm; - else if (rc != -EACCES) + break; + case -ETIME: + cdev->private->path_noirq_mask |= req->lpm; + break; + case -EACCES: + cdev->private->path_notoper_mask |= req->lpm; + break; + default: goto err; + } + /* Continue on the next path. */ req->lpm >>= 1; nop_do(cdev); return; @@ -174,7 +184,12 @@ static void spid_callback(struct ccw_device *cdev, void *data, int rc) case 0: sch->vpm |= req->lpm & sch->opm; break; + case -ETIME: + cdev->private->flags.pgid_unknown = 1; + cdev->private->path_noirq_mask |= req->lpm; + break; case -EACCES: + cdev->private->path_notoper_mask |= req->lpm; break; case -EOPNOTSUPP: if (cdev->private->flags.mpath) { @@ -404,10 +419,21 @@ static void snid_callback(struct ccw_device *cdev, void *data, int rc) { struct ccw_request *req = &cdev->private->req; - if (rc == 0) + switch (rc) { + case 0: cdev->private->pgid_valid_mask |= req->lpm; - else if (rc != -EACCES) + break; + case -ETIME: + cdev->private->flags.pgid_unknown = 1; + cdev->private->path_noirq_mask |= req->lpm; + break; + case -EACCES: + cdev->private->path_notoper_mask |= req->lpm; + break; + default: goto err; + } + /* Continue on the next path. */ req->lpm >>= 1; snid_do(cdev); return; @@ -427,6 +453,13 @@ static void verify_start(struct ccw_device *cdev) sch->vpm = 0; sch->lpm = sch->schib.pmcw.pam; + + /* Initialize PGID data. */ + memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); + cdev->private->pgid_valid_mask = 0; + cdev->private->pgid_todo_mask = sch->schib.pmcw.pam; + cdev->private->path_notoper_mask = 0; + /* Initialize request data. */ memset(req, 0, sizeof(*req)); req->timeout = PGID_TIMEOUT; @@ -459,14 +492,8 @@ static void verify_start(struct ccw_device *cdev) */ void ccw_device_verify_start(struct ccw_device *cdev) { - struct subchannel *sch = to_subchannel(cdev->dev.parent); - CIO_TRACE_EVENT(4, "vrfy"); CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); - /* Initialize PGID data. */ - memset(cdev->private->pgid, 0, sizeof(cdev->private->pgid)); - cdev->private->pgid_valid_mask = 0; - cdev->private->pgid_todo_mask = sch->schib.pmcw.pam; /* * Initialize pathgroup and multipath state with target values. * They may change in the course of path verification. @@ -474,6 +501,7 @@ void ccw_device_verify_start(struct ccw_device *cdev) cdev->private->flags.pgroup = cdev->private->options.pgroup; cdev->private->flags.mpath = cdev->private->options.mpath; cdev->private->flags.doverify = 0; + cdev->private->path_noirq_mask = 0; verify_start(cdev); } diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 76253dfcc1be..b108f4a5c7dd 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -126,6 +126,10 @@ struct ccw_device_private { u8 pgid_valid_mask; /* mask of valid PGIDs */ u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */ u8 pgid_reset_mask; /* mask of PGIDs which were reset */ + u8 path_noirq_mask; /* mask of paths for which no irq was + received */ + u8 path_notoper_mask; /* mask of paths which were found + not operable */ u8 path_gone_mask; /* mask of paths, that became unavailable */ u8 path_new_mask; /* mask of paths, that became available */ struct { @@ -145,6 +149,7 @@ struct ccw_device_private { unsigned int resuming:1; /* recognition while resume */ unsigned int pgroup:1; /* pathgroup is set up */ unsigned int mpath:1; /* multipathing is set up */ + unsigned int pgid_unknown:1;/* unknown pgid state */ unsigned int initialized:1; /* set if initial reference held */ } __attribute__((packed)) flags; unsigned long intparm; /* user interruption parameter */ From e6a0b7c90f9f2663f470bbfaf83afcf52f8459e8 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 28 Jan 2013 19:31:50 +0100 Subject: [PATCH 2703/4607] s390/cio: skip broken paths Omit known to be broken paths (those set in path_noirq_mask) for the sense/set PGID and nop IO commands. Note: path_noirq_mask will be reset in ccw_device_verify_start (the paths could be healthy again). However if we restart a path verification via verify_start this mask will not be reset (there is no need to let the wait for an interrupt time out again - plus we do not want to loop once we deal with the paths in unknown path group state). Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 6f2987d8da99..f12beb72f263 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -70,8 +70,8 @@ static void nop_do(struct ccw_device *cdev) struct subchannel *sch = to_subchannel(cdev->dev.parent); struct ccw_request *req = &cdev->private->req; - /* Adjust lpm. */ - req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm); + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm & + ~cdev->private->path_noirq_mask); if (!req->lpm) goto out_nopath; nop_build_cp(cdev); @@ -345,8 +345,9 @@ static void snid_done(struct ccw_device *cdev, int rc) else { donepm = pgid_to_donepm(cdev); sch->vpm = donepm & sch->opm; - cdev->private->pgid_todo_mask &= ~donepm; cdev->private->pgid_reset_mask |= reset; + cdev->private->pgid_todo_mask &= + ~(donepm | cdev->private->path_noirq_mask); pgid_fill(cdev, pgid); } out: @@ -400,8 +401,8 @@ static void snid_do(struct ccw_device *cdev) struct subchannel *sch = to_subchannel(cdev->dev.parent); struct ccw_request *req = &cdev->private->req; - /* Adjust lpm if paths are not set in pam. */ - req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam); + req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & + ~cdev->private->path_noirq_mask); if (!req->lpm) goto out_nopath; snid_build_cp(cdev); From 84c57ad5d938ff28d85ac93b79c1f918a767a23e Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 28 Jan 2013 19:32:27 +0100 Subject: [PATCH 2704/4607] s390/cio: export vpm via sysfs Add new attribute "vpm" to the subchannel sysfs directory of I/O subchannels. This attribute contains a path mask indicating which channel paths were successfully verified to be usable for I/O. Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 7cd5c6812ac7..c6767f5a58b2 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -632,6 +632,14 @@ initiate_logging(struct device *dev, struct device_attribute *attr, return count; } +static ssize_t vpm_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct subchannel *sch = to_subchannel(dev); + + return sprintf(buf, "%02x\n", sch->vpm); +} + static DEVICE_ATTR(chpids, 0444, chpids_show, NULL); static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL); static DEVICE_ATTR(devtype, 0444, devtype_show, NULL); @@ -640,11 +648,13 @@ static DEVICE_ATTR(modalias, 0444, modalias_show, NULL); static DEVICE_ATTR(online, 0644, online_show, online_store); static DEVICE_ATTR(availability, 0444, available_show, NULL); static DEVICE_ATTR(logging, 0200, NULL, initiate_logging); +static DEVICE_ATTR(vpm, 0444, vpm_show, NULL); static struct attribute *io_subchannel_attrs[] = { &dev_attr_chpids.attr, &dev_attr_pimpampom.attr, &dev_attr_logging.attr, + &dev_attr_vpm.attr, NULL, }; From 88e7616e407fd60ad96e71349393397cf72f8b8d Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 28 Jan 2013 19:32:56 +0100 Subject: [PATCH 2705/4607] s390/cio: handle unknown pgroup state When an attempt to query or modify the grouping state of a channel path fails due to a timeout, we cannot be sure about its state. To get back to a defined state, disband the whole path group and try again while excluding the offending path. Reviewed-by: Peter Oberparleiter Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device_pgid.c | 64 ++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index f12beb72f263..37ada05e82a5 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -23,6 +23,8 @@ #define PGID_RETRIES 256 #define PGID_TIMEOUT (10 * HZ) +static void verify_start(struct ccw_device *cdev); + /* * Process path verification data and report result. */ @@ -142,6 +144,48 @@ static void spid_build_cp(struct ccw_device *cdev, u8 fn) req->cp = cp; } +static void pgid_wipeout_callback(struct ccw_device *cdev, void *data, int rc) +{ + if (rc) { + /* We don't know the path groups' state. Abort. */ + verify_done(cdev, rc); + return; + } + /* + * Path groups have been reset. Restart path verification but + * leave paths in path_noirq_mask out. + */ + cdev->private->flags.pgid_unknown = 0; + verify_start(cdev); +} + +/* + * Reset pathgroups and restart path verification, leave unusable paths out. + */ +static void pgid_wipeout_start(struct ccw_device *cdev) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct ccw_dev_id *id = &cdev->private->dev_id; + struct ccw_request *req = &cdev->private->req; + u8 fn; + + CIO_MSG_EVENT(2, "wipe: device 0.%x.%04x: pvm=%02x nim=%02x\n", + id->ssid, id->devno, cdev->private->pgid_valid_mask, + cdev->private->path_noirq_mask); + + /* Initialize request data. */ + memset(req, 0, sizeof(*req)); + req->timeout = PGID_TIMEOUT; + req->maxretries = PGID_RETRIES; + req->lpm = sch->schib.pmcw.pam; + req->callback = pgid_wipeout_callback; + fn = SPID_FUNC_DISBAND; + if (cdev->private->flags.mpath) + fn |= SPID_FUNC_MULTI_PATH; + spid_build_cp(cdev, fn); + ccw_request_start(cdev); +} + /* * Perform establish/resign SET PGID on a single path. */ @@ -167,11 +211,14 @@ static void spid_do(struct ccw_device *cdev) return; out_nopath: + if (cdev->private->flags.pgid_unknown) { + /* At least one SPID could be partially done. */ + pgid_wipeout_start(cdev); + return; + } verify_done(cdev, sch->vpm ? 0 : -EACCES); } -static void verify_start(struct ccw_device *cdev); - /* * Process SET PGID request result for a single path. */ @@ -357,6 +404,10 @@ out: cdev->private->pgid_todo_mask, mismatch, reserved, reset); switch (rc) { case 0: + if (cdev->private->flags.pgid_unknown) { + pgid_wipeout_start(cdev); + return; + } /* Anything left to do? */ if (cdev->private->pgid_todo_mask == 0) { verify_done(cdev, sch->vpm == 0 ? -EACCES : 0); @@ -400,6 +451,7 @@ static void snid_do(struct ccw_device *cdev) { struct subchannel *sch = to_subchannel(cdev->dev.parent); struct ccw_request *req = &cdev->private->req; + int ret; req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & ~cdev->private->path_noirq_mask); @@ -410,7 +462,13 @@ static void snid_do(struct ccw_device *cdev) return; out_nopath: - snid_done(cdev, cdev->private->pgid_valid_mask ? 0 : -EACCES); + if (cdev->private->pgid_valid_mask) + ret = 0; + else if (cdev->private->path_noirq_mask) + ret = -ETIME; + else + ret = -EACCES; + snid_done(cdev, ret); } /* From 58fece7827a7cc40e02bc68a7db8229166984893 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 28 Jan 2013 19:34:26 +0100 Subject: [PATCH 2706/4607] s390/scm: use inline dummy functions Convert the defines for the !CONFIG_SCM* stuff to static inline functions. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/block/scm_blk.h | 41 ++++++++++++++++++++++++------------ drivers/s390/cio/chsc.h | 2 +- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/s390/block/scm_blk.h b/drivers/s390/block/scm_blk.h index 7ac6bad919ef..3c1ccf494647 100644 --- a/drivers/s390/block/scm_blk.h +++ b/drivers/s390/block/scm_blk.h @@ -68,19 +68,34 @@ void scm_initiate_cluster_request(struct scm_request *); void scm_cluster_request_irq(struct scm_request *); bool scm_test_cluster_request(struct scm_request *); bool scm_cluster_size_valid(void); -#else -#define __scm_free_rq_cluster(scmrq) {} -#define __scm_alloc_rq_cluster(scmrq) 0 -#define scm_request_cluster_init(scmrq) {} -#define scm_reserve_cluster(scmrq) true -#define scm_release_cluster(scmrq) {} -#define scm_blk_dev_cluster_setup(bdev) {} -#define scm_need_cluster_request(scmrq) false -#define scm_initiate_cluster_request(scmrq) {} -#define scm_cluster_request_irq(scmrq) {} -#define scm_test_cluster_request(scmrq) false -#define scm_cluster_size_valid() true -#endif +#else /* CONFIG_SCM_BLOCK_CLUSTER_WRITE */ +static inline void __scm_free_rq_cluster(struct scm_request *scmrq) {} +static inline int __scm_alloc_rq_cluster(struct scm_request *scmrq) +{ + return 0; +} +static inline void scm_request_cluster_init(struct scm_request *scmrq) {} +static inline bool scm_reserve_cluster(struct scm_request *scmrq) +{ + return true; +} +static inline void scm_release_cluster(struct scm_request *scmrq) {} +static inline void scm_blk_dev_cluster_setup(struct scm_blk_dev *bdev) {} +static inline bool scm_need_cluster_request(struct scm_request *scmrq) +{ + return false; +} +static inline void scm_initiate_cluster_request(struct scm_request *scmrq) {} +static inline void scm_cluster_request_irq(struct scm_request *scmrq) {} +static inline bool scm_test_cluster_request(struct scm_request *scmrq) +{ + return false; +} +static inline bool scm_cluster_size_valid(void) +{ + return true; +} +#endif /* CONFIG_SCM_BLOCK_CLUSTER_WRITE */ extern debug_info_t *scm_debug; diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 662dab4b93e6..227e05f674b3 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -157,7 +157,7 @@ int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token); #ifdef CONFIG_SCM_BUS int scm_update_information(void); #else /* CONFIG_SCM_BUS */ -#define scm_update_information() 0 +static inline int scm_update_information(void) { return 0; } #endif /* CONFIG_SCM_BUS */ From 1aae0560d160ee6ebef927a35e4f405306a079df Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 09:49:40 +0100 Subject: [PATCH 2707/4607] s390/time: rename tod clock access functions Fix name clash with some common code device drivers and add "tod" to all tod clock access function names. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/appldata/appldata_mem.c | 2 +- arch/s390/appldata/appldata_net_sum.c | 2 +- arch/s390/appldata/appldata_os.c | 2 +- arch/s390/hypfs/hypfs_vm.c | 2 +- arch/s390/include/asm/timex.h | 18 ++++++++-------- arch/s390/kernel/debug.c | 2 +- arch/s390/kernel/early.c | 6 +++--- arch/s390/kernel/nmi.c | 2 +- arch/s390/kernel/smp.c | 10 ++++----- arch/s390/kernel/time.c | 26 +++++++++++------------ arch/s390/kernel/vtime.c | 2 +- arch/s390/kvm/interrupt.c | 6 +++--- arch/s390/lib/delay.c | 16 +++++++------- drivers/s390/block/dasd.c | 18 ++++++++-------- drivers/s390/block/dasd_3990_erp.c | 8 +++---- drivers/s390/block/dasd_alias.c | 4 ++-- drivers/s390/block/dasd_diag.c | 10 ++++----- drivers/s390/block/dasd_eckd.c | 30 +++++++++++++-------------- drivers/s390/block/dasd_eer.c | 2 +- drivers/s390/block/dasd_erp.c | 4 ++-- drivers/s390/block/dasd_fba.c | 2 +- drivers/s390/char/sclp.c | 4 ++-- drivers/s390/char/zcore.c | 2 +- drivers/s390/cio/cio.c | 4 ++-- drivers/s390/cio/cmf.c | 6 +++--- drivers/s390/cio/css.c | 2 +- drivers/s390/cio/device_fsm.c | 2 +- drivers/s390/cio/qdio_main.c | 12 +++++------ drivers/s390/net/qeth_core.h | 2 +- drivers/s390/scsi/zfcp_fsf.c | 2 +- drivers/s390/scsi/zfcp_qdio.c | 2 +- 31 files changed, 106 insertions(+), 106 deletions(-) diff --git a/arch/s390/appldata/appldata_mem.c b/arch/s390/appldata/appldata_mem.c index 02d9a1cf5057..7ef60b52d6e0 100644 --- a/arch/s390/appldata/appldata_mem.c +++ b/arch/s390/appldata/appldata_mem.c @@ -108,7 +108,7 @@ static void appldata_get_mem_data(void *data) mem_data->totalswap = P2K(val.totalswap); mem_data->freeswap = P2K(val.freeswap); - mem_data->timestamp = get_clock(); + mem_data->timestamp = get_tod_clock(); mem_data->sync_count_2++; } diff --git a/arch/s390/appldata/appldata_net_sum.c b/arch/s390/appldata/appldata_net_sum.c index 1370e358d49a..2d224b945355 100644 --- a/arch/s390/appldata/appldata_net_sum.c +++ b/arch/s390/appldata/appldata_net_sum.c @@ -111,7 +111,7 @@ static void appldata_get_net_sum_data(void *data) net_data->tx_dropped = tx_dropped; net_data->collisions = collisions; - net_data->timestamp = get_clock(); + net_data->timestamp = get_tod_clock(); net_data->sync_count_2++; } diff --git a/arch/s390/appldata/appldata_os.c b/arch/s390/appldata/appldata_os.c index 87521ba682e5..de8e2b3b0180 100644 --- a/arch/s390/appldata/appldata_os.c +++ b/arch/s390/appldata/appldata_os.c @@ -156,7 +156,7 @@ static void appldata_get_os_data(void *data) } ops.size = new_size; } - os_data->timestamp = get_clock(); + os_data->timestamp = get_tod_clock(); os_data->sync_count_2++; } diff --git a/arch/s390/hypfs/hypfs_vm.c b/arch/s390/hypfs/hypfs_vm.c index 4f6afaa8bd8f..f364dcf77e8e 100644 --- a/arch/s390/hypfs/hypfs_vm.c +++ b/arch/s390/hypfs/hypfs_vm.c @@ -245,7 +245,7 @@ static int dbfs_diag2fc_create(void **data, void **data_free_ptr, size_t *size) d2fc = diag2fc_store(guest_query, &count, sizeof(d2fc->hdr)); if (IS_ERR(d2fc)) return PTR_ERR(d2fc); - get_clock_ext(d2fc->hdr.tod_ext); + get_tod_clock_ext(d2fc->hdr.tod_ext); d2fc->hdr.len = count * sizeof(struct diag2fc_data); d2fc->hdr.version = DBFS_D2FC_HDR_VERSION; d2fc->hdr.count = count; diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h index 4c060bb5b8ea..8ad8af915032 100644 --- a/arch/s390/include/asm/timex.h +++ b/arch/s390/include/asm/timex.h @@ -15,7 +15,7 @@ #define TOD_UNIX_EPOCH 0x7d91048bca000000ULL /* Inline functions for clock register access. */ -static inline int set_clock(__u64 time) +static inline int set_tod_clock(__u64 time) { int cc; @@ -27,7 +27,7 @@ static inline int set_clock(__u64 time) return cc; } -static inline int store_clock(__u64 *time) +static inline int store_tod_clock(__u64 *time) { int cc; @@ -71,7 +71,7 @@ static inline void local_tick_enable(unsigned long long comp) typedef unsigned long long cycles_t; -static inline unsigned long long get_clock(void) +static inline unsigned long long get_tod_clock(void) { unsigned long long clk; @@ -83,21 +83,21 @@ static inline unsigned long long get_clock(void) return clk; } -static inline void get_clock_ext(char *clk) +static inline void get_tod_clock_ext(char *clk) { asm volatile("stcke %0" : "=Q" (*clk) : : "cc"); } -static inline unsigned long long get_clock_xt(void) +static inline unsigned long long get_tod_clock_xt(void) { unsigned char clk[16]; - get_clock_ext(clk); + get_tod_clock_ext(clk); return *((unsigned long long *)&clk[1]); } static inline cycles_t get_cycles(void) { - return (cycles_t) get_clock() >> 2; + return (cycles_t) get_tod_clock() >> 2; } int get_sync_clock(unsigned long long *clock); @@ -123,9 +123,9 @@ extern u64 sched_clock_base_cc; * function, otherwise the returned value is not guaranteed to * be monotonic. */ -static inline unsigned long long get_clock_monotonic(void) +static inline unsigned long long get_tod_clock_monotonic(void) { - return get_clock_xt() - sched_clock_base_cc; + return get_tod_clock_xt() - sched_clock_base_cc; } /** diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index 4e8215e0d4b6..09a94cd9debc 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c @@ -867,7 +867,7 @@ static inline void debug_finish_entry(debug_info_t * id, debug_entry_t* active, int level, int exception) { - active->id.stck = get_clock(); + active->id.stck = get_tod_clock(); active->id.fields.cpuid = smp_processor_id(); active->caller = __builtin_return_address(0); active->id.fields.exception = exception; diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 1f0eee9e7daa..1ee98e56fc6a 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -47,10 +47,10 @@ static void __init reset_tod_clock(void) { u64 time; - if (store_clock(&time) == 0) + if (store_tod_clock(&time) == 0) return; /* TOD clock not running. Set the clock to Unix Epoch. */ - if (set_clock(TOD_UNIX_EPOCH) != 0 || store_clock(&time) != 0) + if (set_tod_clock(TOD_UNIX_EPOCH) != 0 || store_tod_clock(&time) != 0) disabled_wait(0); sched_clock_base_cc = TOD_UNIX_EPOCH; @@ -173,7 +173,7 @@ static noinline __init void create_kernel_nss(void) } /* re-initialize cputime accounting. */ - sched_clock_base_cc = get_clock(); + sched_clock_base_cc = get_tod_clock(); S390_lowcore.last_update_clock = sched_clock_base_cc; S390_lowcore.last_update_timer = 0x7fffffffffffffffULL; S390_lowcore.user_timer = 0; diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 7918fbea36bb..504175ebf8b0 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -293,7 +293,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs) * retry this instruction. */ spin_lock(&ipd_lock); - tmp = get_clock(); + tmp = get_tod_clock(); if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME) ipd_count++; else diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 7433a2f9e5cc..549c9d173c0f 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -365,16 +365,16 @@ void smp_emergency_stop(cpumask_t *cpumask) u64 end; int cpu; - end = get_clock() + (1000000UL << 12); + end = get_tod_clock() + (1000000UL << 12); for_each_cpu(cpu, cpumask) { struct pcpu *pcpu = pcpu_devices + cpu; set_bit(ec_stop_cpu, &pcpu->ec_mask); while (__pcpu_sigp(pcpu->address, SIGP_EMERGENCY_SIGNAL, 0, NULL) == SIGP_CC_BUSY && - get_clock() < end) + get_tod_clock() < end) cpu_relax(); } - while (get_clock() < end) { + while (get_tod_clock() < end) { for_each_cpu(cpu, cpumask) if (pcpu_stopped(pcpu_devices + cpu)) cpumask_clear_cpu(cpu, cpumask); @@ -694,7 +694,7 @@ static void __init smp_detect_cpus(void) */ static void __cpuinit smp_start_secondary(void *cpuvoid) { - S390_lowcore.last_update_clock = get_clock(); + S390_lowcore.last_update_clock = get_tod_clock(); S390_lowcore.restart_stack = (unsigned long) restart_stack; S390_lowcore.restart_fn = (unsigned long) do_restart; S390_lowcore.restart_data = 0; @@ -947,7 +947,7 @@ static ssize_t show_idle_time(struct device *dev, unsigned int sequence; do { - now = get_clock(); + now = get_tod_clock(); sequence = ACCESS_ONCE(idle->sequence); idle_time = ACCESS_ONCE(idle->idle_time); idle_enter = ACCESS_ONCE(idle->clock_idle_enter); diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 0aa98db8a80d..876546b9cfa1 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -63,7 +63,7 @@ static DEFINE_PER_CPU(struct clock_event_device, comparators); */ unsigned long long notrace __kprobes sched_clock(void) { - return tod_to_ns(get_clock_monotonic()); + return tod_to_ns(get_tod_clock_monotonic()); } /* @@ -194,7 +194,7 @@ static void stp_reset(void); void read_persistent_clock(struct timespec *ts) { - tod_to_timeval(get_clock() - TOD_UNIX_EPOCH, ts); + tod_to_timeval(get_tod_clock() - TOD_UNIX_EPOCH, ts); } void read_boot_clock(struct timespec *ts) @@ -204,7 +204,7 @@ void read_boot_clock(struct timespec *ts) static cycle_t read_tod_clock(struct clocksource *cs) { - return get_clock(); + return get_tod_clock(); } static struct clocksource clocksource_tod = { @@ -342,7 +342,7 @@ int get_sync_clock(unsigned long long *clock) sw_ptr = &get_cpu_var(clock_sync_word); sw0 = atomic_read(sw_ptr); - *clock = get_clock(); + *clock = get_tod_clock(); sw1 = atomic_read(sw_ptr); put_cpu_var(clock_sync_word); if (sw0 == sw1 && (sw0 & 0x80000000U)) @@ -486,7 +486,7 @@ static void etr_reset(void) .p0 = 0, .p1 = 0, ._pad1 = 0, .ea = 0, .es = 0, .sl = 0 }; if (etr_setr(&etr_eacr) == 0) { - etr_tolec = get_clock(); + etr_tolec = get_tod_clock(); set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags); if (etr_port0_online && etr_port1_online) set_bit(CLOCK_SYNC_ETR, &clock_sync_flags); @@ -768,8 +768,8 @@ static int etr_sync_clock(void *data) __ctl_set_bit(14, 21); __ctl_set_bit(0, 29); clock = ((unsigned long long) (aib->edf2.etv + 1)) << 32; - old_clock = get_clock(); - if (set_clock(clock) == 0) { + old_clock = get_tod_clock(); + if (set_tod_clock(clock) == 0) { __udelay(1); /* Wait for the clock to start. */ __ctl_clear_bit(0, 29); __ctl_clear_bit(14, 21); @@ -845,7 +845,7 @@ static struct etr_eacr etr_handle_events(struct etr_eacr eacr) * assume that this can have caused an stepping * port switch. */ - etr_tolec = get_clock(); + etr_tolec = get_tod_clock(); eacr.p0 = etr_port0_online; if (!eacr.p0) eacr.e0 = 0; @@ -858,7 +858,7 @@ static struct etr_eacr etr_handle_events(struct etr_eacr eacr) * assume that this can have caused an stepping * port switch. */ - etr_tolec = get_clock(); + etr_tolec = get_tod_clock(); eacr.p1 = etr_port1_online; if (!eacr.p1) eacr.e1 = 0; @@ -974,7 +974,7 @@ static void etr_update_eacr(struct etr_eacr eacr) etr_eacr = eacr; etr_setr(&etr_eacr); if (dp_changed) - etr_tolec = get_clock(); + etr_tolec = get_tod_clock(); } /* @@ -1012,7 +1012,7 @@ static void etr_work_fn(struct work_struct *work) /* Store aib to get the current ETR status word. */ BUG_ON(etr_stetr(&aib) != 0); etr_port0.esw = etr_port1.esw = aib.esw; /* Copy status word. */ - now = get_clock(); + now = get_tod_clock(); /* * Update the port information if the last stepping port change @@ -1537,10 +1537,10 @@ static int stp_sync_clock(void *data) if (stp_info.todoff[0] || stp_info.todoff[1] || stp_info.todoff[2] || stp_info.todoff[3] || stp_info.tmd != 2) { - old_clock = get_clock(); + old_clock = get_tod_clock(); rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0); if (rc == 0) { - delta = adjust_time(old_clock, get_clock(), 0); + delta = adjust_time(old_clock, get_tod_clock(), 0); fixup_clock_comparator(delta); rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi)); diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index e84b8b68444a..8911a169af44 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -191,7 +191,7 @@ cputime64_t s390_get_idle_time(int cpu) unsigned int sequence; do { - now = get_clock(); + now = get_tod_clock(); sequence = ACCESS_ONCE(idle->sequence); idle_enter = ACCESS_ONCE(idle->clock_idle_enter); idle_exit = ACCESS_ONCE(idle->clock_idle_exit); diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 82c481ddef76..87418b50f21c 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -362,7 +362,7 @@ static int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu) } if ((!rc) && (vcpu->arch.sie_block->ckc < - get_clock() + vcpu->arch.sie_block->epoch)) { + get_tod_clock() + vcpu->arch.sie_block->epoch)) { if ((!psw_extint_disabled(vcpu)) && (vcpu->arch.sie_block->gcr[0] & 0x800ul)) rc = 1; @@ -402,7 +402,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) goto no_timer; } - now = get_clock() + vcpu->arch.sie_block->epoch; + now = get_tod_clock() + vcpu->arch.sie_block->epoch; if (vcpu->arch.sie_block->ckc < now) { __unset_cpu_idle(vcpu); return 0; @@ -492,7 +492,7 @@ void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu) } if ((vcpu->arch.sie_block->ckc < - get_clock() + vcpu->arch.sie_block->epoch)) + get_tod_clock() + vcpu->arch.sie_block->epoch)) __try_deliver_ckc_interrupt(vcpu); if (atomic_read(&fi->active)) { diff --git a/arch/s390/lib/delay.c b/arch/s390/lib/delay.c index 42d0cf89121d..c61b9fad43cc 100644 --- a/arch/s390/lib/delay.c +++ b/arch/s390/lib/delay.c @@ -32,7 +32,7 @@ static void __udelay_disabled(unsigned long long usecs) unsigned long cr0, cr6, new; u64 clock_saved, end; - end = get_clock() + (usecs << 12); + end = get_tod_clock() + (usecs << 12); clock_saved = local_tick_disable(); __ctl_store(cr0, 0, 0); __ctl_store(cr6, 6, 6); @@ -45,7 +45,7 @@ static void __udelay_disabled(unsigned long long usecs) set_clock_comparator(end); vtime_stop_cpu(); local_irq_disable(); - } while (get_clock() < end); + } while (get_tod_clock() < end); lockdep_on(); __ctl_load(cr0, 0, 0); __ctl_load(cr6, 6, 6); @@ -56,7 +56,7 @@ static void __udelay_enabled(unsigned long long usecs) { u64 clock_saved, end; - end = get_clock() + (usecs << 12); + end = get_tod_clock() + (usecs << 12); do { clock_saved = 0; if (end < S390_lowcore.clock_comparator) { @@ -67,7 +67,7 @@ static void __udelay_enabled(unsigned long long usecs) local_irq_disable(); if (clock_saved) local_tick_enable(clock_saved); - } while (get_clock() < end); + } while (get_tod_clock() < end); } /* @@ -111,8 +111,8 @@ void udelay_simple(unsigned long long usecs) { u64 end; - end = get_clock() + (usecs << 12); - while (get_clock() < end) + end = get_tod_clock() + (usecs << 12); + while (get_tod_clock() < end) cpu_relax(); } @@ -122,10 +122,10 @@ void __ndelay(unsigned long long nsecs) nsecs <<= 9; do_div(nsecs, 125); - end = get_clock() + nsecs; + end = get_tod_clock() + nsecs; if (nsecs & ~0xfffUL) __udelay(nsecs >> 12); - while (get_clock() < end) + while (get_tod_clock() < end) barrier(); } EXPORT_SYMBOL(__ndelay); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 29225e1c159c..e1d96344d733 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1352,7 +1352,7 @@ int dasd_term_IO(struct dasd_ccw_req *cqr) switch (rc) { case 0: /* termination successful */ cqr->status = DASD_CQR_CLEAR_PENDING; - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); cqr->starttime = 0; DBF_DEV_EVENT(DBF_DEBUG, device, "terminate cqr %p successful", @@ -1420,7 +1420,7 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) cqr->status = DASD_CQR_ERROR; return -EIO; } - cqr->startclk = get_clock(); + cqr->startclk = get_tod_clock(); cqr->starttime = jiffies; cqr->retries--; if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) { @@ -1623,7 +1623,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, return; } - now = get_clock(); + now = get_tod_clock(); cqr = (struct dasd_ccw_req *) intparm; /* check for conditions that should be handled immediately */ if (!cqr || @@ -1963,7 +1963,7 @@ int dasd_flush_device_queue(struct dasd_device *device) } break; case DASD_CQR_QUEUED: - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); cqr->status = DASD_CQR_CLEARED; break; default: /* no need to modify the others */ @@ -2210,7 +2210,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) wait_event(generic_waitq, _wait_for_wakeup(cqr)); } - maincqr->endclk = get_clock(); + maincqr->endclk = get_tod_clock(); if ((maincqr->status != DASD_CQR_DONE) && (maincqr->intrc != -ERESTARTSYS)) dasd_log_sense(maincqr, &maincqr->irb); @@ -2340,7 +2340,7 @@ int dasd_cancel_req(struct dasd_ccw_req *cqr) "Cancelling request %p failed with rc=%d\n", cqr, rc); } else { - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); } break; default: /* already finished or clear pending - do nothing */ @@ -2568,7 +2568,7 @@ restart: } /* Rechain finished requests to final queue */ - cqr->endclk = get_clock(); + cqr->endclk = get_tod_clock(); list_move_tail(&cqr->blocklist, final_queue); } } @@ -2711,7 +2711,7 @@ restart_cb: } /* call the callback function */ spin_lock_irq(&block->request_queue_lock); - cqr->endclk = get_clock(); + cqr->endclk = get_tod_clock(); list_del_init(&cqr->blocklist); __dasd_cleanup_cqr(cqr); spin_unlock_irq(&block->request_queue_lock); @@ -3504,7 +3504,7 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, cqr->memdev = device; cqr->expires = 10*HZ; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index f8212d54013a..d26134713682 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -229,7 +229,7 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) dctl_cqr->expires = 5 * 60 * HZ; dctl_cqr->retries = 2; - dctl_cqr->buildclk = get_clock(); + dctl_cqr->buildclk = get_tod_clock(); dctl_cqr->status = DASD_CQR_FILLED; @@ -1719,7 +1719,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) erp->magic = default_erp->magic; erp->expires = default_erp->expires; erp->retries = 256; - erp->buildclk = get_clock(); + erp->buildclk = get_tod_clock(); erp->status = DASD_CQR_FILLED; /* remove the default erp */ @@ -2322,7 +2322,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) DBF_DEV_EVENT(DBF_ERR, device, "%s", "Unable to allocate ERP request"); cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock (); + cqr->stopclk = get_tod_clock(); } else { DBF_DEV_EVENT(DBF_ERR, device, "Unable to allocate ERP request " @@ -2364,7 +2364,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) erp->magic = cqr->magic; erp->expires = cqr->expires; erp->retries = 256; - erp->buildclk = get_clock(); + erp->buildclk = get_tod_clock(); erp->status = DASD_CQR_FILLED; return erp; diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c index 6b556995bb33..a2597e683e79 100644 --- a/drivers/s390/block/dasd_alias.c +++ b/drivers/s390/block/dasd_alias.c @@ -448,7 +448,7 @@ static int read_unit_address_configuration(struct dasd_device *device, ccw->count = sizeof(*(lcu->uac)); ccw->cda = (__u32)(addr_t) lcu->uac; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; /* need to unset flag here to detect race with summary unit check */ @@ -733,7 +733,7 @@ static int reset_summary_unit_check(struct alias_lcu *lcu, cqr->memdev = device; cqr->block = NULL; cqr->expires = 5 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 704488d0f819..cc0603358522 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -184,14 +184,14 @@ dasd_start_diag(struct dasd_ccw_req * cqr) private->iob.bio_list = dreq->bio; private->iob.flaga = DASD_DIAG_FLAGA_DEFAULT; - cqr->startclk = get_clock(); + cqr->startclk = get_tod_clock(); cqr->starttime = jiffies; cqr->retries--; rc = dia250(&private->iob, RW_BIO); switch (rc) { case 0: /* Synchronous I/O finished successfully */ - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); cqr->status = DASD_CQR_SUCCESS; /* Indicate to calling function that only a dasd_schedule_bh() and no timer is needed */ @@ -222,7 +222,7 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr) mdsk_term_io(device); mdsk_init_io(device, device->block->bp_block, 0, NULL); cqr->status = DASD_CQR_CLEAR_PENDING; - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); dasd_schedule_device_bh(device); return 0; } @@ -276,7 +276,7 @@ static void dasd_ext_handler(struct ext_code ext_code, return; } - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); expires = 0; if ((ext_code.subcode & 0xff) == 0) { @@ -556,7 +556,7 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, } } cqr->retries = DIAG_MAX_RETRIES; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); if (blk_noretry_request(req) || block->base->features & DASD_FEATURE_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index e37bc1620d14..33f26bfa62f2 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -862,7 +862,7 @@ static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device, cqr->expires = 10*HZ; cqr->lpm = lpm; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); } @@ -1449,7 +1449,7 @@ static int dasd_eckd_read_features(struct dasd_device *device) ccw->count = sizeof(struct dasd_rssd_features); ccw->cda = (__u32)(addr_t) features; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on(cqr); if (rc == 0) { @@ -1501,7 +1501,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device, cqr->block = NULL; cqr->retries = 256; cqr->expires = 10*HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } @@ -1841,7 +1841,7 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) cqr->startdev = device; cqr->memdev = device; cqr->retries = 255; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } @@ -2241,7 +2241,7 @@ dasd_eckd_format_device(struct dasd_device * device, fcp->startdev = device; fcp->memdev = device; fcp->retries = 256; - fcp->buildclk = get_clock(); + fcp->buildclk = get_tod_clock(); fcp->status = DASD_CQR_FILLED; return fcp; } @@ -2530,7 +2530,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } @@ -2705,7 +2705,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } @@ -2998,7 +2998,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; out_error: @@ -3201,7 +3201,7 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, cqr->expires = startdev->default_expires * HZ; cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN) @@ -3402,7 +3402,7 @@ dasd_eckd_release(struct dasd_device *device) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ cqr->expires = 2 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); @@ -3457,7 +3457,7 @@ dasd_eckd_reserve(struct dasd_device *device) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ cqr->expires = 2 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); @@ -3511,7 +3511,7 @@ dasd_eckd_steal_lock(struct dasd_device *device) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); cqr->retries = 2; /* set retry counter to enable basic ERP */ cqr->expires = 2 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); @@ -3572,7 +3572,7 @@ static int dasd_eckd_snid(struct dasd_device *device, set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); cqr->retries = 5; cqr->expires = 10 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; cqr->lpm = usrparm.path_mask; @@ -3642,7 +3642,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp) ccw->count = sizeof(struct dasd_rssd_perf_stats_t); ccw->cda = (__u32)(addr_t) stats; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on(cqr); if (rc == 0) { @@ -3768,7 +3768,7 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp) cqr->memdev = device; cqr->retries = 3; cqr->expires = 10 * HZ; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; /* Build the ccws */ diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index ff901b5509c1..21ef63cf0960 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -481,7 +481,7 @@ int dasd_eer_enable(struct dasd_device *device) ccw->flags = 0; ccw->cda = (__u32)(addr_t) cqr->data; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; cqr->callback = dasd_eer_snss_cb; diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index d01ef82f8757..3250cb471f78 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -102,7 +102,7 @@ dasd_default_erp_action(struct dasd_ccw_req *cqr) pr_err("%s: default ERP has run out of retries and failed\n", dev_name(&device->cdev->dev)); cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); } return cqr; } /* end dasd_default_erp_action */ @@ -146,7 +146,7 @@ struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr) cqr->status = DASD_CQR_DONE; else { cqr->status = DASD_CQR_FAILED; - cqr->stopclk = get_clock(); + cqr->stopclk = get_tod_clock(); } return cqr; diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 414698584344..4dd0e2f6047e 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -370,7 +370,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, cqr->block = block; cqr->expires = memdev->default_expires * HZ; /* default 5 minutes */ cqr->retries = 32; - cqr->buildclk = get_clock(); + cqr->buildclk = get_tod_clock(); cqr->status = DASD_CQR_FILLED; return cqr; } diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 12c16a65dd25..bd6871bf545a 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -450,7 +450,7 @@ sclp_sync_wait(void) timeout = 0; if (timer_pending(&sclp_request_timer)) { /* Get timeout TOD value */ - timeout = get_clock() + + timeout = get_tod_clock() + sclp_tod_from_jiffies(sclp_request_timer.expires - jiffies); } @@ -472,7 +472,7 @@ sclp_sync_wait(void) while (sclp_running_state != sclp_running_state_idle) { /* Check for expired request timer */ if (timer_pending(&sclp_request_timer) && - get_clock() > timeout && + get_tod_clock() > timeout && del_timer(&sclp_request_timer)) sclp_request_timer.function(sclp_request_timer.data); cpu_relax(); diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 681749e7f6dd..1d61a01576d2 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -637,7 +637,7 @@ static int __init zcore_header_init(int arch, struct zcore_header *hdr) hdr->rmem_size = memory; hdr->mem_end = sys_info.mem_size; hdr->num_pages = memory / PAGE_SIZE; - hdr->tod = get_clock(); + hdr->tod = get_tod_clock(); get_cpu_id(&hdr->cpu_id); for (i = 0; zfcpdump_save_areas[i]; i++) { prefix = zfcpdump_save_areas[i]->pref_reg; diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index c8faf6230b0f..986ef6a92a41 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -962,9 +962,9 @@ static void css_reset(void) atomic_inc(&chpid_reset_count); } /* Wait for machine check for all channel paths. */ - timeout = get_clock() + (RCHP_TIMEOUT << 12); + timeout = get_tod_clock() + (RCHP_TIMEOUT << 12); while (atomic_read(&chpid_reset_count) != 0) { - if (get_clock() > timeout) + if (get_tod_clock() > timeout) break; cpu_relax(); } diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index c9fc61c0a866..4495e0627a40 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -33,7 +33,7 @@ #include #include #include -#include /* get_clock() */ +#include /* get_tod_clock() */ #include #include @@ -326,7 +326,7 @@ static int cmf_copy_block(struct ccw_device *cdev) memcpy(cmb_data->last_block, hw_block, cmb_data->size); memcpy(reference_buf, hw_block, cmb_data->size); } while (memcmp(cmb_data->last_block, reference_buf, cmb_data->size)); - cmb_data->last_update = get_clock(); + cmb_data->last_update = get_tod_clock(); kfree(reference_buf); return 0; } @@ -428,7 +428,7 @@ static void cmf_generic_reset(struct ccw_device *cdev) memset(cmbops->align(cmb_data->hw_block), 0, cmb_data->size); cmb_data->last_update = 0; } - cdev->private->cmb_start_time = get_clock(); + cdev->private->cmb_start_time = get_tod_clock(); spin_unlock_irq(cdev->ccwlock); } diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index fd00afd8b850..a239237d43f3 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -780,7 +780,7 @@ static int __init setup_css(int nr) css->cssid = nr; dev_set_name(&css->device, "css%x", nr); css->device.release = channel_subsystem_release; - tod_high = (u32) (get_clock() >> 32); + tod_high = (u32) (get_tod_clock() >> 32); css_generate_pgid(css, tod_high); return 0; } diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 1bb1d00095af..c7638c543250 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -47,7 +47,7 @@ static void ccw_timeout_log(struct ccw_device *cdev) cc = stsch_err(sch->schid, &schib); printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, " - "device information:\n", get_clock()); + "device information:\n", get_tod_clock()); printk(KERN_WARNING "cio: orb:\n"); print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1, orb, sizeof(*orb), 0); diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 1671d3461f29..abc550e5dd35 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -338,10 +338,10 @@ again: retries++; if (!start_time) { - start_time = get_clock(); + start_time = get_tod_clock(); goto again; } - if ((get_clock() - start_time) < QDIO_BUSY_BIT_PATIENCE) + if ((get_tod_clock() - start_time) < QDIO_BUSY_BIT_PATIENCE) goto again; } if (retries) { @@ -504,7 +504,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q) int count, stop; unsigned char state = 0; - q->timestamp = get_clock(); + q->timestamp = get_tod_clock(); /* * Don't check 128 buffers, as otherwise qdio_inbound_q_moved @@ -563,7 +563,7 @@ static int qdio_inbound_q_moved(struct qdio_q *q) if (bufnr != q->last_move) { q->last_move = bufnr; if (!is_thinint_irq(q->irq_ptr) && MACHINE_IS_LPAR) - q->u.in.timestamp = get_clock(); + q->u.in.timestamp = get_tod_clock(); return 1; } else return 0; @@ -595,7 +595,7 @@ static inline int qdio_inbound_q_done(struct qdio_q *q) * At this point we know, that inbound first_to_check * has (probably) not moved (see qdio_inbound_processing). */ - if (get_clock() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) { + if (get_tod_clock() > q->u.in.timestamp + QDIO_INPUT_THRESHOLD) { DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%02x", q->first_to_check); return 1; @@ -772,7 +772,7 @@ static int get_outbound_buffer_frontier(struct qdio_q *q) int count, stop; unsigned char state = 0; - q->timestamp = get_clock(); + q->timestamp = get_tod_clock(); if (need_siga_sync(q)) if (((queue_type(q) != QDIO_IQDIO_QFMT) && diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 480fbeab0256..b4796a40b002 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -816,7 +816,7 @@ static inline struct qeth_card *CARD_FROM_CDEV(struct ccw_device *cdev) static inline int qeth_get_micros(void) { - return (int) (get_clock() >> 12); + return (int) (get_tod_clock() >> 12); } static inline int qeth_get_ip_version(struct sk_buff *skb) diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index c96320d79fbc..c7e148f33b2a 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -727,7 +727,7 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *req) zfcp_reqlist_add(adapter->req_list, req); req->qdio_req.qdio_outb_usage = atomic_read(&qdio->req_q_free); - req->issued = get_clock(); + req->issued = get_tod_clock(); if (zfcp_qdio_send(qdio, &req->qdio_req)) { del_timer(&req->timer); /* lookup request again, list might have changed */ diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index 50b5615848f6..665e3cfaaf85 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -68,7 +68,7 @@ static inline void zfcp_qdio_account(struct zfcp_qdio *qdio) unsigned long long now, span; int used; - now = get_clock_monotonic(); + now = get_tod_clock_monotonic(); span = (now - qdio->req_q_time) >> 12; used = QDIO_MAX_BUFFERS_PER_Q - atomic_read(&qdio->req_q_free); qdio->req_q_util += used * span; From e06ef372839c0c33f5f91f892ae632cef38cd259 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 13:56:14 +0100 Subject: [PATCH 2708/4607] s390/barrier: convert mb() to define again Some of the now available common code drivers only compile if mb() is a define. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/barrier.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/s390/include/asm/barrier.h b/arch/s390/include/asm/barrier.h index 10a508802940..16760eeb79b0 100644 --- a/arch/s390/include/asm/barrier.h +++ b/arch/s390/include/asm/barrier.h @@ -13,15 +13,12 @@ * to devices. */ -static inline void mb(void) -{ #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES - /* Fast-BCR without checkpoint synchronization */ - asm volatile("bcr 14,0" : : : "memory"); +/* Fast-BCR without checkpoint synchronization */ +#define mb() do { asm volatile("bcr 14,0" : : : "memory"); } while (0) #else - asm volatile("bcr 15,0" : : : "memory"); +#define mb() do { asm volatile("bcr 15,0" : : : "memory"); } while (0) #endif -} #define rmb() mb() #define wmb() mb() From e978948db125cc3b90cc324c68e7787f0ac7be4a Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 14:16:02 +0100 Subject: [PATCH 2709/4607] s390/dma: provide dma_cache_sync() function Provide empty dma_cache_sync() function. Acked-by: Sebastian Ott Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/dma-mapping.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/s390/include/asm/dma-mapping.h b/arch/s390/include/asm/dma-mapping.h index 8a32f7dfd3af..e74bc7ac72e0 100644 --- a/arch/s390/include/asm/dma-mapping.h +++ b/arch/s390/include/asm/dma-mapping.h @@ -20,8 +20,11 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev) extern int dma_set_mask(struct device *dev, u64 mask); extern int dma_is_consistent(struct device *dev, dma_addr_t dma_handle); -extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction); + +static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, + enum dma_data_direction direction) +{ +} #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) From a50b2eae8b9aed3840d7e045c9417ce5e6c5ce91 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 14:17:42 +0100 Subject: [PATCH 2710/4607] s390/dma: remove dma_is_consistent() declaration There is no such function nor any caller in the whole kernel. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/dma-mapping.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/s390/include/asm/dma-mapping.h b/arch/s390/include/asm/dma-mapping.h index e74bc7ac72e0..9411db653bac 100644 --- a/arch/s390/include/asm/dma-mapping.h +++ b/arch/s390/include/asm/dma-mapping.h @@ -19,7 +19,6 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev) } extern int dma_set_mask(struct device *dev, u64 mask); -extern int dma_is_consistent(struct device *dev, dma_addr_t dma_handle); static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, enum dma_data_direction direction) From 1e5635d10d8112e61776b9513491329f7b0859ce Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 15:52:16 +0100 Subject: [PATCH 2711/4607] s390/pci: rename pci_probe to s390_pci_probe pci_probe is too generic and has a name clash with other common code parts. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/pci.h | 2 +- arch/s390/pci/pci.c | 8 ++++---- drivers/pci/hotplug/s390_pci_hpc.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 23d6a245e8a7..6383c44c6629 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -186,7 +186,7 @@ void zpci_dma_exit(void); extern struct mutex zpci_list_lock; extern struct list_head zpci_list; extern struct pci_hp_callback_ops hotplug_ops; -extern unsigned int pci_probe; +extern unsigned int s390_pci_probe; /* FMB */ int zpci_fmb_enable_device(struct zpci_dev *); diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 60e0372545d2..aa74409db656 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -1072,13 +1072,13 @@ static void zpci_mem_exit(void) kmem_cache_destroy(zdev_fmb_cache); } -unsigned int pci_probe = 1; -EXPORT_SYMBOL_GPL(pci_probe); +unsigned int s390_pci_probe = 1; +EXPORT_SYMBOL_GPL(s390_pci_probe); char * __init pcibios_setup(char *str) { if (!strcmp(str, "off")) { - pci_probe = 0; + s390_pci_probe = 0; return NULL; } return str; @@ -1088,7 +1088,7 @@ static int __init pci_base_init(void) { int rc; - if (!pci_probe) + if (!s390_pci_probe) return 0; if (!test_facility(2) || !test_facility(69) diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index dee68e0698e1..8fe2a8aa89a1 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -231,7 +231,7 @@ static int __init pci_hotplug_s390_init(void) * right now. */ - if (!pci_probe) + if (!s390_pci_probe) return -EOPNOTSUPP; /* register callbacks for slot handling from arch code */ From 0383a68cd2583ef3693cbfffe5abfc4369d70d46 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 16:32:12 +0100 Subject: [PATCH 2712/4607] ata: disable ATA for s390 Add s390 to the list of architectures that don' want ATA. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/ata/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index e08d322d01d7..14c3ac9eea96 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -14,7 +14,7 @@ menuconfig ATA tristate "Serial ATA and Parallel ATA drivers" depends on HAS_IOMEM depends on BLOCK - depends on !(M32R || M68K) || BROKEN + depends on !(M32R || M68K || S390) || BROKEN select SCSI ---help--- If you want to use a ATA hard disk, ATA tape drive, ATA CD-ROM or From 075aa0c6b1e33ff953dfd66bdc7d187011f563c0 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 16:34:39 +0100 Subject: [PATCH 2713/4607] parport: disable PC-style parallel port support for s390 Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/parport/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/parport/Kconfig b/drivers/parport/Kconfig index 0e60438ebe30..24e12d4d1769 100644 --- a/drivers/parport/Kconfig +++ b/drivers/parport/Kconfig @@ -35,7 +35,7 @@ if PARPORT config PARPORT_PC tristate "PC-style hardware" - depends on (!SPARC64 || PCI) && !SPARC32 && !M32R && !FRV && \ + depends on (!SPARC64 || PCI) && !SPARC32 && !M32R && !FRV && !S390 && \ (!M68K || ISA) && !MN10300 && !AVR32 && !BLACKFIN && !XTENSA ---help--- You should say Y here if you have a PC-style parallel port. All From bddb7ae217cea0ef722468fce823f9e19a69c561 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 30 Jan 2013 16:38:55 +0100 Subject: [PATCH 2714/4607] s390/mm: provide PAGE_SHARED define Only needed to make some drivers compile... Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/pgtable.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 098adbb62660..a009d4dd70cb 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -385,6 +385,7 @@ extern unsigned long MODULES_END; #define PAGE_RW __pgprot(_PAGE_TYPE_RW) #define PAGE_KERNEL PAGE_RW +#define PAGE_SHARED PAGE_KERNEL #define PAGE_COPY PAGE_RO /* From 151a0eb6c8e4398f76453c791d8fd8f8167a7517 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Wed, 30 Jan 2013 17:51:56 +0100 Subject: [PATCH 2715/4607] s390/perf: cpum_cf: fallback to software sampling events The CPU-measurement counter facility does not support sampling events and returns -EINVAL in that case. This return code lets the perf tool fail. To fall back to software sampling events, return -ENOENT instead. Signed-off-by: Hendrik Brueckner Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/perf_cpum_cf.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c index 86ec7447e1f5..390d9ae57bb2 100644 --- a/arch/s390/kernel/perf_cpum_cf.c +++ b/arch/s390/kernel/perf_cpum_cf.c @@ -367,13 +367,6 @@ static int __hw_perf_event_init(struct perf_event *event) if (ev >= PERF_CPUM_CF_MAX_CTR) return -EINVAL; - /* The CPU measurement counter facility does not have any interrupts - * to do sampling. Sampling must be provided by external means, - * for example, by timers. - */ - if (hwc->sample_period) - return -EINVAL; - /* Use the hardware perf event structure to store the counter number * in 'config' member and the counter set to which the counter belongs * in the 'config_base'. The counter set (config_base) is then used @@ -418,6 +411,12 @@ static int cpumf_pmu_event_init(struct perf_event *event) case PERF_TYPE_HARDWARE: case PERF_TYPE_HW_CACHE: case PERF_TYPE_RAW: + /* The CPU measurement counter facility does not have overflow + * interrupts to do sampling. Sampling must be provided by + * external means, for example, by timers. + */ + if (is_sampling_event(event)) + return -ENOENT; err = __hw_perf_event_init(event); break; default: From bf4ec24ff8ab54d56c835eb61212a1e87270d7c8 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 31 Jan 2013 19:53:12 +0100 Subject: [PATCH 2716/4607] s390/pci: cleanup clp inline assembly Tell gcc that the memory region pointed to by req will be used (and changed). Also remove the (now) superfluous memory constraint. Acked-by: Gerald Schaefer Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/pci/pci_clp.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/s390/pci/pci_clp.c b/arch/s390/pci/pci_clp.c index 2c847143cbd1..702bd2693689 100644 --- a/arch/s390/pci/pci_clp.c +++ b/arch/s390/pci/pci_clp.c @@ -19,18 +19,19 @@ * Call Logical Processor * Retry logic is handled by the caller. */ -static inline u8 clp_instr(void *req) +static inline u8 clp_instr(void *data) { - u64 ilpm; + struct { u8 _[CLP_BLK_SIZE]; } *req = data; + u64 ignored; u8 cc; asm volatile ( - " .insn rrf,0xb9a00000,%[ilpm],%[req],0x0,0x2\n" + " .insn rrf,0xb9a00000,%[ign],%[req],0x0,0x2\n" " ipm %[cc]\n" " srl %[cc],28\n" - : [cc] "=d" (cc), [ilpm] "=d" (ilpm) + : [cc] "=d" (cc), [ign] "=d" (ignored), "+m" (*req) : [req] "a" (req) - : "cc", "memory"); + : "cc"); return cc; } From add09d61fee72d7a346051332b6d99f18989504c Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 31 Jan 2013 19:54:03 +0100 Subject: [PATCH 2717/4607] s390/pci: cleanup clp page allocation Use the __get_free_pages wrapper in clp_alloc_block. Also change the allocation to use one page only. This page is used as CLP response block e.g. to list available pci functions. Using one page we can list > 250 pci functions at once and we have code to loop around this CLP command (if not all functions fit into to the CLP block) already in place. Acked-by: Gerald Schaefer Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/clp.h | 2 +- arch/s390/pci/pci_clp.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/s390/include/asm/clp.h b/arch/s390/include/asm/clp.h index 6c3aecc245ff..a0e71a501f7c 100644 --- a/arch/s390/include/asm/clp.h +++ b/arch/s390/include/asm/clp.h @@ -2,7 +2,7 @@ #define _ASM_S390_CLP_H /* CLP common request & response block size */ -#define CLP_BLK_SIZE (PAGE_SIZE * 2) +#define CLP_BLK_SIZE PAGE_SIZE struct clp_req_hdr { u16 len; diff --git a/arch/s390/pci/pci_clp.c b/arch/s390/pci/pci_clp.c index 702bd2693689..f339fe2feb15 100644 --- a/arch/s390/pci/pci_clp.c +++ b/arch/s390/pci/pci_clp.c @@ -37,8 +37,7 @@ static inline u8 clp_instr(void *data) static void *clp_alloc_block(void) { - struct page *page = alloc_pages(GFP_KERNEL, get_order(CLP_BLK_SIZE)); - return (page) ? page_address(page) : NULL; + return (void *) __get_free_pages(GFP_KERNEL, get_order(CLP_BLK_SIZE)); } static void clp_free_block(void *ptr) From 53923354d69e4748506bfee932b7c6b309a15c21 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Thu, 31 Jan 2013 19:55:17 +0100 Subject: [PATCH 2718/4607] s390/pci: fix hotplug module init Loading the pci hotplug module when no devices are present will fail but unfortunately some hotplug callbacks stay registered to the pci bus level. Fix this by not letting module loading fail when no pci devices are present and provide proper {de}registration functions for these callbacks. Reviewed-by: Gerald Schaefer Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/pci.h | 4 ++- arch/s390/pci/pci.c | 27 ++++++++++---- drivers/pci/hotplug/s390_pci_hpc.c | 58 +++++++++++++----------------- 3 files changed, 49 insertions(+), 40 deletions(-) diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 6383c44c6629..05333b7f0469 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -185,9 +185,11 @@ void zpci_dma_exit(void); /* Hotplug */ extern struct mutex zpci_list_lock; extern struct list_head zpci_list; -extern struct pci_hp_callback_ops hotplug_ops; extern unsigned int s390_pci_probe; +void zpci_register_hp_ops(struct pci_hp_callback_ops *); +void zpci_deregister_hp_ops(void); + /* FMB */ int zpci_fmb_enable_device(struct zpci_dev *); int zpci_fmb_disable_device(struct zpci_dev *); diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index aa74409db656..27b4c17855b9 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -51,8 +51,7 @@ EXPORT_SYMBOL_GPL(zpci_list); DEFINE_MUTEX(zpci_list_lock); EXPORT_SYMBOL_GPL(zpci_list_lock); -struct pci_hp_callback_ops hotplug_ops; -EXPORT_SYMBOL_GPL(hotplug_ops); +static struct pci_hp_callback_ops *hotplug_ops; static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES); static DEFINE_SPINLOCK(zpci_domain_lock); @@ -974,8 +973,8 @@ int zpci_create_device(struct zpci_dev *zdev) mutex_lock(&zpci_list_lock); list_add_tail(&zdev->entry, &zpci_list); - if (hotplug_ops.create_slot) - hotplug_ops.create_slot(zdev); + if (hotplug_ops) + hotplug_ops->create_slot(zdev); mutex_unlock(&zpci_list_lock); if (zdev->state == ZPCI_FN_STATE_STANDBY) @@ -989,8 +988,8 @@ int zpci_create_device(struct zpci_dev *zdev) out_start: mutex_lock(&zpci_list_lock); list_del(&zdev->entry); - if (hotplug_ops.remove_slot) - hotplug_ops.remove_slot(zdev); + if (hotplug_ops) + hotplug_ops->remove_slot(zdev); mutex_unlock(&zpci_list_lock); out_bus: zpci_free_domain(zdev); @@ -1072,6 +1071,22 @@ static void zpci_mem_exit(void) kmem_cache_destroy(zdev_fmb_cache); } +void zpci_register_hp_ops(struct pci_hp_callback_ops *ops) +{ + mutex_lock(&zpci_list_lock); + hotplug_ops = ops; + mutex_unlock(&zpci_list_lock); +} +EXPORT_SYMBOL_GPL(zpci_register_hp_ops); + +void zpci_deregister_hp_ops(void) +{ + mutex_lock(&zpci_list_lock); + hotplug_ops = NULL; + mutex_unlock(&zpci_list_lock); +} +EXPORT_SYMBOL_GPL(zpci_deregister_hp_ops); + unsigned int s390_pci_probe = 1; EXPORT_SYMBOL_GPL(s390_pci_probe); diff --git a/drivers/pci/hotplug/s390_pci_hpc.c b/drivers/pci/hotplug/s390_pci_hpc.c index 8fe2a8aa89a1..7db249a25016 100644 --- a/drivers/pci/hotplug/s390_pci_hpc.c +++ b/drivers/pci/hotplug/s390_pci_hpc.c @@ -172,25 +172,6 @@ error: return -ENOMEM; } -static int __init init_pci_slots(void) -{ - struct zpci_dev *zdev; - int device = 0; - - /* - * Create a structure for each slot, and register that slot - * with the pci_hotplug subsystem. - */ - mutex_lock(&zpci_list_lock); - list_for_each_entry(zdev, &zpci_list, entry) { - init_pci_slot(zdev); - device++; - } - - mutex_unlock(&zpci_list_lock); - return (device) ? 0 : -ENODEV; -} - static void exit_pci_slot(struct zpci_dev *zdev) { struct list_head *tmp, *n; @@ -205,6 +186,26 @@ static void exit_pci_slot(struct zpci_dev *zdev) } } +static struct pci_hp_callback_ops hp_ops = { + .create_slot = init_pci_slot, + .remove_slot = exit_pci_slot, +}; + +static void __init init_pci_slots(void) +{ + struct zpci_dev *zdev; + + /* + * Create a structure for each slot, and register that slot + * with the pci_hotplug subsystem. + */ + mutex_lock(&zpci_list_lock); + list_for_each_entry(zdev, &zpci_list, entry) { + init_pci_slot(zdev); + } + mutex_unlock(&zpci_list_lock); +} + static void __exit exit_pci_slots(void) { struct list_head *tmp, *n; @@ -224,28 +225,19 @@ static void __exit exit_pci_slots(void) static int __init pci_hotplug_s390_init(void) { - /* - * Do specific initialization stuff for your driver here - * like initializing your controller hardware (if any) and - * determining the number of slots you have in the system - * right now. - */ - if (!s390_pci_probe) return -EOPNOTSUPP; - /* register callbacks for slot handling from arch code */ - mutex_lock(&zpci_list_lock); - hotplug_ops.create_slot = init_pci_slot; - hotplug_ops.remove_slot = exit_pci_slot; - mutex_unlock(&zpci_list_lock); - pr_info("registered hotplug slot callbacks\n"); - return init_pci_slots(); + zpci_register_hp_ops(&hp_ops); + init_pci_slots(); + + return 0; } static void __exit pci_hotplug_s390_exit(void) { exit_pci_slots(); + zpci_deregister_hp_ops(); } module_init(pci_hotplug_s390_init); From be4904e5039a769f84f933bacce85c5e8ddd90a7 Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Mon, 4 Feb 2013 13:10:35 +0100 Subject: [PATCH 2719/4607] dasd: fix sysfs cleanup in dasd_generic_remove When the DASD devices are detached from the driver, then the dasd_generic_remove function is called. One of the things this function should do is to remove the DASD specific sysfs attributes, but this is not done in all cases. This is likely to cause an oops when at a later point sysfs stumbles over the stale pointers. In particular this happens when when the modules are unloaded and loaded again. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index e1d96344d733..f1b7fdc58a5f 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -3042,12 +3042,15 @@ void dasd_generic_remove(struct ccw_device *cdev) cdev->handler = NULL; device = dasd_device_from_cdev(cdev); - if (IS_ERR(device)) + if (IS_ERR(device)) { + dasd_remove_sysfs_files(cdev); return; + } if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags) && !test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { /* Already doing offline processing */ dasd_put_device(device); + dasd_remove_sysfs_files(cdev); return; } /* From c423c8ffa7b2681e19b77801390ed2a7708aae98 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 6 Feb 2013 10:08:40 +0100 Subject: [PATCH 2720/4607] uio: remove !S390 dependency from Kconfig Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/uio/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index f56d185790ea..e92eeaf251fe 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -1,6 +1,5 @@ menuconfig UIO tristate "Userspace I/O drivers" - depends on !S390 help Enable this to allow the userspace driver core code to be built. This code allows userspace programs easy access to From 870a2b5e4fcde08c91c102712ea07d3cbc96f19a Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 6 Feb 2013 10:09:31 +0100 Subject: [PATCH 2721/4607] phylib: remove !S390 dependeny from Kconfig Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/net/phy/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 961f0b293913..450345261bd3 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -4,7 +4,6 @@ menuconfig PHYLIB tristate "PHY Device support and infrastructure" - depends on !S390 depends on NETDEVICES help Ethernet controllers are usually attached to PHY From 0e0d04a8677f33360cfbb5f8c7aa4ee8cbf5a287 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 6 Feb 2013 10:15:55 +0100 Subject: [PATCH 2722/4607] s390/Kconfig: sort list of arch selected config options Just like on other architectures. The intention is that this will reduce merge conflicts if new config options get added in sorted order as well. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/Kconfig | 115 +++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index b5ea38c25647..17775cf15348 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -60,86 +60,87 @@ config PCI_QUIRKS config S390 def_bool y - select USE_GENERIC_SMP_HELPERS if SMP - select GENERIC_CPU_DEVICES if !SMP - select HAVE_SYSCALL_WRAPPERS - select HAVE_FUNCTION_TRACER - select HAVE_FUNCTION_TRACE_MCOUNT_TEST - select HAVE_FTRACE_MCOUNT_RECORD - select HAVE_C_RECORDMCOUNT - select HAVE_SYSCALL_TRACEPOINTS - select SYSCTL_EXCEPTION_TRACE - select HAVE_DYNAMIC_FTRACE - select HAVE_FUNCTION_GRAPH_TRACER - select HAVE_REGS_AND_STACK_ACCESS_API - select HAVE_OPROFILE - select HAVE_KPROBES - select HAVE_KRETPROBES - select HAVE_KVM if 64BIT - select HAVE_ARCH_TRACEHOOK - select INIT_ALL_POSSIBLE - select HAVE_IRQ_WORK - select HAVE_PERF_EVENTS - select ARCH_HAVE_NMI_SAFE_CMPXCHG - select HAVE_DEBUG_KMEMLEAK - select HAVE_KERNEL_GZIP - select HAVE_KERNEL_BZIP2 - select HAVE_KERNEL_LZMA - select HAVE_KERNEL_LZO - select HAVE_KERNEL_XZ - select HAVE_ARCH_MUTEX_CPU_RELAX - select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 - select HAVE_BPF_JIT if 64BIT && PACK_STACK - select ARCH_SAVE_PAGE_KEYS if HIBERNATION - select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE - select HAVE_MEMBLOCK - select HAVE_MEMBLOCK_NODE_MAP - select HAVE_CMPXCHG_LOCAL - select HAVE_CMPXCHG_DOUBLE - select HAVE_ALIGNED_STRUCT_PAGE if SLUB - select HAVE_VIRT_CPU_ACCOUNTING - select VIRT_CPU_ACCOUNTING select ARCH_DISCARD_MEMBLOCK - select BUILDTIME_EXTABLE_SORT - select ARCH_INLINE_SPIN_TRYLOCK - select ARCH_INLINE_SPIN_TRYLOCK_BH - select ARCH_INLINE_SPIN_LOCK - select ARCH_INLINE_SPIN_LOCK_BH - select ARCH_INLINE_SPIN_LOCK_IRQ - select ARCH_INLINE_SPIN_LOCK_IRQSAVE - select ARCH_INLINE_SPIN_UNLOCK - select ARCH_INLINE_SPIN_UNLOCK_BH - select ARCH_INLINE_SPIN_UNLOCK_IRQ - select ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE - select ARCH_INLINE_READ_TRYLOCK + select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE + select ARCH_HAVE_NMI_SAFE_CMPXCHG select ARCH_INLINE_READ_LOCK select ARCH_INLINE_READ_LOCK_BH select ARCH_INLINE_READ_LOCK_IRQ select ARCH_INLINE_READ_LOCK_IRQSAVE + select ARCH_INLINE_READ_TRYLOCK select ARCH_INLINE_READ_UNLOCK select ARCH_INLINE_READ_UNLOCK_BH select ARCH_INLINE_READ_UNLOCK_IRQ select ARCH_INLINE_READ_UNLOCK_IRQRESTORE - select ARCH_INLINE_WRITE_TRYLOCK + select ARCH_INLINE_SPIN_LOCK + select ARCH_INLINE_SPIN_LOCK_BH + select ARCH_INLINE_SPIN_LOCK_IRQ + select ARCH_INLINE_SPIN_LOCK_IRQSAVE + select ARCH_INLINE_SPIN_TRYLOCK + select ARCH_INLINE_SPIN_TRYLOCK_BH + select ARCH_INLINE_SPIN_UNLOCK + select ARCH_INLINE_SPIN_UNLOCK_BH + select ARCH_INLINE_SPIN_UNLOCK_IRQ + select ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE select ARCH_INLINE_WRITE_LOCK select ARCH_INLINE_WRITE_LOCK_BH select ARCH_INLINE_WRITE_LOCK_IRQ select ARCH_INLINE_WRITE_LOCK_IRQSAVE + select ARCH_INLINE_WRITE_TRYLOCK select ARCH_INLINE_WRITE_UNLOCK select ARCH_INLINE_WRITE_UNLOCK_BH select ARCH_INLINE_WRITE_UNLOCK_IRQ select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE - select HAVE_UID16 if 32BIT + select ARCH_SAVE_PAGE_KEYS if HIBERNATION select ARCH_WANT_IPC_PARSE_VERSION - select HAVE_ARCH_TRANSPARENT_HUGEPAGE if 64BIT + select BUILDTIME_EXTABLE_SORT + select CLONE_BACKWARDS2 + select GENERIC_CLOCKEVENTS + select GENERIC_CPU_DEVICES if !SMP + select GENERIC_KERNEL_THREAD select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL_OLD - select GENERIC_CLOCKEVENTS - select KTIME_SCALAR if 32BIT + select HAVE_ALIGNED_STRUCT_PAGE if SLUB + select HAVE_ARCH_JUMP_LABEL if !MARCH_G5 + select HAVE_ARCH_MUTEX_CPU_RELAX select HAVE_ARCH_SECCOMP_FILTER + select HAVE_ARCH_TRACEHOOK + select HAVE_ARCH_TRANSPARENT_HUGEPAGE if 64BIT + select HAVE_BPF_JIT if 64BIT && PACK_STACK + select HAVE_CMPXCHG_DOUBLE + select HAVE_CMPXCHG_LOCAL + select HAVE_C_RECORDMCOUNT + select HAVE_DEBUG_KMEMLEAK + select HAVE_DYNAMIC_FTRACE + select HAVE_FTRACE_MCOUNT_RECORD + select HAVE_FUNCTION_GRAPH_TRACER + select HAVE_FUNCTION_TRACER + select HAVE_FUNCTION_TRACE_MCOUNT_TEST + select HAVE_IRQ_WORK + select HAVE_KERNEL_BZIP2 + select HAVE_KERNEL_GZIP + select HAVE_KERNEL_LZMA + select HAVE_KERNEL_LZO + select HAVE_KERNEL_XZ + select HAVE_KPROBES + select HAVE_KRETPROBES + select HAVE_KVM if 64BIT + select HAVE_MEMBLOCK + select HAVE_MEMBLOCK_NODE_MAP select HAVE_MOD_ARCH_SPECIFIC + select HAVE_OPROFILE + select HAVE_PERF_EVENTS + select HAVE_REGS_AND_STACK_ACCESS_API + select HAVE_SYSCALL_TRACEPOINTS + select HAVE_SYSCALL_WRAPPERS + select HAVE_UID16 if 32BIT + select HAVE_VIRT_CPU_ACCOUNTING + select INIT_ALL_POSSIBLE + select KTIME_SCALAR if 32BIT select MODULES_USE_ELF_RELA - select CLONE_BACKWARDS2 + select SYSCTL_EXCEPTION_TRACE + select USE_GENERIC_SMP_HELPERS if SMP + select VIRT_CPU_ACCOUNTING config SCHED_OMIT_FRAME_POINTER def_bool y From c0048de29d207fe3407360c28a9025891506dd6a Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Wed, 6 Feb 2013 16:12:03 +0100 Subject: [PATCH 2723/4607] iucv: fix kernel panic at reboot The iucv base layer is initialized during the registration of the first iucv handler. If no handler is registered and the iucv_reboot_event() notifier is called, a missing check can cause a kernel panic in iucv_block_cpu(). To solve this issue, check the IRQ masks invoke iucv_block_cpu() for enabled CPUs only. Signed-off-by: Hendrik Brueckner Signed-off-by: Martin Schwidefsky --- net/iucv/iucv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index df082508362d..4fe76ff214c2 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -831,8 +831,11 @@ static int iucv_reboot_event(struct notifier_block *this, { int i; + if (cpumask_empty(&iucv_irq_cpumask)) + return NOTIFY_DONE; + get_online_cpus(); - on_each_cpu(iucv_block_cpu, NULL, 1); + on_each_cpu_mask(&iucv_irq_cpumask, iucv_block_cpu, NULL, 1); preempt_disable(); for (i = 0; i < iucv_max_pathid; i++) { if (iucv_path_table[i]) From a1dd7e4ef784a2c3d997d68b9321b6d115f19b20 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 6 Feb 2013 17:23:58 +0100 Subject: [PATCH 2724/4607] drivers/net,AT91RM9200: add missing GENERIC_HARDIRQS dependency The AT91RM9200 driver call devm_request_irq() and therefore should depend on GENERIC_HARDIRQS to prevent link/compile errors on plaforms without GENERIC_HARDIRQS. Signed-off-by: Heiko Carstens Acked-by: David S. Miller Signed-off-by: Martin Schwidefsky --- drivers/net/ethernet/cadence/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/cadence/Kconfig b/drivers/net/ethernet/cadence/Kconfig index ceb0de0cf62c..1194446f859a 100644 --- a/drivers/net/ethernet/cadence/Kconfig +++ b/drivers/net/ethernet/cadence/Kconfig @@ -22,6 +22,7 @@ if NET_CADENCE config ARM_AT91_ETHER tristate "AT91RM9200 Ethernet support" + depends on GENERIC_HARDIRQS select NET_CORE select MACB ---help--- From 5303a0fe8ce8c7493025a3b60a403439edb4159a Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 9 Feb 2013 14:07:50 +0100 Subject: [PATCH 2725/4607] s390/bpf,jit: add vlan tag support s390 version of 855ddb56 "x86: bpf_jit_comp: add vlan tag support". Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/net/bpf_jit_comp.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index bb284419b0fd..0972e91cced2 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c @@ -7,6 +7,7 @@ */ #include #include +#include #include #include #include @@ -254,6 +255,8 @@ static void bpf_jit_noleaks(struct bpf_jit *jit, struct sock_filter *filter) case BPF_S_ANC_HATYPE: case BPF_S_ANC_RXHASH: case BPF_S_ANC_CPU: + case BPF_S_ANC_VLAN_TAG: + case BPF_S_ANC_VLAN_TAG_PRESENT: case BPF_S_RET_K: /* first instruction sets A register */ break; @@ -699,6 +702,24 @@ call_fn: /* lg %r1,(%r13) */ /* l %r5,(%r2) */ EMIT4_DISP(0x58502000, offsetof(struct sk_buff, rxhash)); break; + case BPF_S_ANC_VLAN_TAG: + case BPF_S_ANC_VLAN_TAG_PRESENT: + BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2); + BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000); + /* lhi %r5,0 */ + EMIT4(0xa7580000); + /* icm %r5,3,(%r2) */ + EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, vlan_tci)); + if (filter->code == BPF_S_ANC_VLAN_TAG) { + /* nill %r5,0xefff */ + EMIT4_IMM(0xa5570000, ~VLAN_TAG_PRESENT); + } else { + /* nill %r5,0x1000 */ + EMIT4_IMM(0xa5570000, VLAN_TAG_PRESENT); + /* srl %r5,12 */ + EMIT4_DISP(0x88500000, 12); + } + break; case BPF_S_ANC_CPU: /* A = smp_processor_id() */ #ifdef CONFIG_SMP /* l %r5, */ From fa364fc41cd3c7fd47fb7be7b4d0722c25ebad4b Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 7 Feb 2013 15:54:50 +0100 Subject: [PATCH 2726/4607] drivers/media: add missing GENERIC_HARDIRQS dependency Texas Instruments WL1273 I2C FM Radio (RADIO_WL1273) selects MFD_CORE, which itself depends on GENERIC_HARDIRQS. So add the dependency to the TI driver as well. Signed-off-by: Heiko Carstens Acked-by: Mauro Carvalho Chehab Signed-off-by: Martin Schwidefsky --- drivers/media/radio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 8090b87b3066..9e580166161a 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -180,7 +180,7 @@ config RADIO_TIMBERDALE config RADIO_WL1273 tristate "Texas Instruments WL1273 I2C FM Radio" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_V4L2 && GENERIC_HARDIRQS select MFD_CORE select MFD_WL1273_CORE select FW_LOADER From a4e69245bd7793a620ed67442c00fa1f2dd56891 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 11 Feb 2013 14:26:24 +0100 Subject: [PATCH 2727/4607] s390/linker skript: discard exit.data at runtime Discard exit.data section at run time, not link time, since exit.text references exit.data and causes this build error: `.exit.data' referenced in section `.exit.text' of drivers/built-in.o: defined in discarded section `.exit.data' of drivers/built-in.o Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/vmlinux.lds.S | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index 79cb51adc741..35b13ed0af5f 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -75,6 +75,10 @@ SECTIONS EXIT_TEXT } + .exit.data : { + EXIT_DATA + } + /* early.c uses stsi, which requires page aligned data. */ . = ALIGN(PAGE_SIZE); INIT_DATA_SECTION(0x100) From 486c0a0bc80d370471b21662bf03f04fbb37cdc6 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Mon, 11 Feb 2013 14:29:49 +0100 Subject: [PATCH 2728/4607] s390/mm: Fix crst upgrade of mmap with MAP_FIXED Right now the page table upgrade does not happen if the end address of a fixed mapping is greater than TASK_SIZE. Enhance s390_mmap_check() to handle MAP_FIXED mappings correctly. Signed-off-by: Hendrik Brueckner Reviewed-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/mman.h | 4 ++-- arch/s390/mm/mmap.c | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/s390/include/asm/mman.h b/arch/s390/include/asm/mman.h index 0e47a576d666..9977e08df5bd 100644 --- a/arch/s390/include/asm/mman.h +++ b/arch/s390/include/asm/mman.h @@ -9,7 +9,7 @@ #include #if !defined(__ASSEMBLY__) && defined(CONFIG_64BIT) -int s390_mmap_check(unsigned long addr, unsigned long len); -#define arch_mmap_check(addr,len,flags) s390_mmap_check(addr,len) +int s390_mmap_check(unsigned long addr, unsigned long len, unsigned long flags); +#define arch_mmap_check(addr, len, flags) s390_mmap_check(addr, len, flags) #endif #endif /* __S390_MMAN_H__ */ diff --git a/arch/s390/mm/mmap.c b/arch/s390/mm/mmap.c index c59a5efa58b1..06bafec00278 100644 --- a/arch/s390/mm/mmap.c +++ b/arch/s390/mm/mmap.c @@ -101,12 +101,15 @@ void arch_pick_mmap_layout(struct mm_struct *mm) #else -int s390_mmap_check(unsigned long addr, unsigned long len) +int s390_mmap_check(unsigned long addr, unsigned long len, unsigned long flags) { int rc; - if (!is_compat_task() && - len >= TASK_SIZE && TASK_SIZE < (1UL << 53)) { + if (is_compat_task() || (TASK_SIZE >= (1UL << 53))) + return 0; + if (!(flags & MAP_FIXED)) + addr = 0; + if ((addr + len) >= TASK_SIZE) { rc = crst_table_upgrade(current->mm, 1UL << 53); if (rc) return rc; From abf09bed3cceadd809f0356065c2ada6cee90d4a Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Wed, 7 Nov 2012 13:17:37 +0100 Subject: [PATCH 2729/4607] s390/mm: implement software dirty bits The s390 architecture is unique in respect to dirty page detection, it uses the change bit in the per-page storage key to track page modifications. All other architectures track dirty bits by means of page table entries. This property of s390 has caused numerous problems in the past, e.g. see git commit ef5d437f71afdf4a "mm: fix XFS oops due to dirty pages without buffers on s390". To avoid future issues in regard to per-page dirty bits convert s390 to a fault based software dirty bit detection mechanism. All user page table entries which are marked as clean will be hardware read-only, even if the pte is supposed to be writable. A write by the user process will trigger a protection fault which will cause the user pte to be marked as dirty and the hardware read-only bit is removed. With this change the dirty bit in the storage key is irrelevant for Linux as a host, but the storage key is still required for KVM guests. The effect is that page_test_and_clear_dirty and the related code can be removed. The referenced bit in the storage key is still used by the page_test_and_clear_young primitive to provide page age information. For page cache pages of mappings with mapping_cap_account_dirty there will not be any change in behavior as the dirty bit tracking already uses read-only ptes to control the amount of dirty pages. Only for swap cache pages and pages of mappings without mapping_cap_account_dirty there can be additional protection faults. To avoid an excessive number of additional faults the mk_pte primitive checks for PageDirty if the pgprot value allows for writes and pre-dirties the pte. That avoids all additional faults for tmpfs and shmem pages until these pages are added to the swap cache. Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/page.h | 22 ------ arch/s390/include/asm/pgtable.h | 131 +++++++++++++++++++++----------- arch/s390/include/asm/sclp.h | 1 - arch/s390/include/asm/setup.h | 16 ++-- arch/s390/kvm/kvm-s390.c | 2 +- arch/s390/lib/uaccess_pt.c | 2 +- arch/s390/mm/pageattr.c | 2 +- arch/s390/mm/vmem.c | 24 +++--- drivers/s390/char/sclp_cmd.c | 10 +-- include/asm-generic/pgtable.h | 10 --- include/linux/page-flags.h | 8 -- mm/rmap.c | 24 ------ 12 files changed, 112 insertions(+), 140 deletions(-) diff --git a/arch/s390/include/asm/page.h b/arch/s390/include/asm/page.h index a86ad4084073..75ce9b065f9f 100644 --- a/arch/s390/include/asm/page.h +++ b/arch/s390/include/asm/page.h @@ -154,28 +154,6 @@ static inline int page_reset_referenced(unsigned long addr) #define _PAGE_FP_BIT 0x08 /* HW fetch protection bit */ #define _PAGE_ACC_BITS 0xf0 /* HW access control bits */ -/* - * Test and clear dirty bit in storage key. - * We can't clear the changed bit atomically. This is a potential - * race against modification of the referenced bit. This function - * should therefore only be called if it is not mapped in any - * address space. - * - * Note that the bit gets set whenever page content is changed. That means - * also when the page is modified by DMA or from inside the kernel. - */ -#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY -static inline int page_test_and_clear_dirty(unsigned long pfn, int mapped) -{ - unsigned char skey; - - skey = page_get_storage_key(pfn << PAGE_SHIFT); - if (!(skey & _PAGE_CHANGED)) - return 0; - page_set_storage_key(pfn << PAGE_SHIFT, skey & ~_PAGE_CHANGED, mapped); - return 1; -} - /* * Test and clear referenced bit in storage key. */ diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index a009d4dd70cb..97de1200c849 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -29,6 +29,7 @@ #ifndef __ASSEMBLY__ #include #include +#include #include #include @@ -221,13 +222,15 @@ extern unsigned long MODULES_END; /* Software bits in the page table entry */ #define _PAGE_SWT 0x001 /* SW pte type bit t */ #define _PAGE_SWX 0x002 /* SW pte type bit x */ -#define _PAGE_SWC 0x004 /* SW pte changed bit (for KVM) */ -#define _PAGE_SWR 0x008 /* SW pte referenced bit (for KVM) */ -#define _PAGE_SPECIAL 0x010 /* SW associated with special page */ +#define _PAGE_SWC 0x004 /* SW pte changed bit */ +#define _PAGE_SWR 0x008 /* SW pte referenced bit */ +#define _PAGE_SWW 0x010 /* SW pte write bit */ +#define _PAGE_SPECIAL 0x020 /* SW associated with special page */ #define __HAVE_ARCH_PTE_SPECIAL /* Set of bits not changed in pte_modify */ -#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_SWC | _PAGE_SWR) +#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_CO | \ + _PAGE_SWC | _PAGE_SWR) /* Six different types of pages. */ #define _PAGE_TYPE_EMPTY 0x400 @@ -321,6 +324,7 @@ extern unsigned long MODULES_END; /* Bits in the region table entry */ #define _REGION_ENTRY_ORIGIN ~0xfffUL/* region/segment table origin */ +#define _REGION_ENTRY_RO 0x200 /* region protection bit */ #define _REGION_ENTRY_INV 0x20 /* invalid region table entry */ #define _REGION_ENTRY_TYPE_MASK 0x0c /* region/segment table type mask */ #define _REGION_ENTRY_TYPE_R1 0x0c /* region first table type */ @@ -382,9 +386,10 @@ extern unsigned long MODULES_END; */ #define PAGE_NONE __pgprot(_PAGE_TYPE_NONE) #define PAGE_RO __pgprot(_PAGE_TYPE_RO) -#define PAGE_RW __pgprot(_PAGE_TYPE_RW) +#define PAGE_RW __pgprot(_PAGE_TYPE_RO | _PAGE_SWW) +#define PAGE_RWC __pgprot(_PAGE_TYPE_RW | _PAGE_SWW | _PAGE_SWC) -#define PAGE_KERNEL PAGE_RW +#define PAGE_KERNEL PAGE_RWC #define PAGE_SHARED PAGE_KERNEL #define PAGE_COPY PAGE_RO @@ -632,23 +637,23 @@ static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste) bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED); /* Clear page changed & referenced bit in the storage key */ if (bits & _PAGE_CHANGED) - page_set_storage_key(address, skey ^ bits, 1); + page_set_storage_key(address, skey ^ bits, 0); else if (bits) page_reset_referenced(address); /* Transfer page changed & referenced bit to guest bits in pgste */ pgste_val(pgste) |= bits << 48; /* RCP_GR_BIT & RCP_GC_BIT */ /* Get host changed & referenced bits from pgste */ bits |= (pgste_val(pgste) & (RCP_HR_BIT | RCP_HC_BIT)) >> 52; - /* Clear host bits in pgste. */ + /* Transfer page changed & referenced bit to kvm user bits */ + pgste_val(pgste) |= bits << 45; /* KVM_UR_BIT & KVM_UC_BIT */ + /* Clear relevant host bits in pgste. */ pgste_val(pgste) &= ~(RCP_HR_BIT | RCP_HC_BIT); pgste_val(pgste) &= ~(RCP_ACC_BITS | RCP_FP_BIT); /* Copy page access key and fetch protection bit to pgste */ pgste_val(pgste) |= (unsigned long) (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56; - /* Transfer changed and referenced to kvm user bits */ - pgste_val(pgste) |= bits << 45; /* KVM_UR_BIT & KVM_UC_BIT */ - /* Transfer changed & referenced to pte sofware bits */ - pte_val(*ptep) |= bits << 1; /* _PAGE_SWR & _PAGE_SWC */ + /* Transfer referenced bit to pte */ + pte_val(*ptep) |= (bits & _PAGE_REFERENCED) << 1; #endif return pgste; @@ -661,20 +666,25 @@ static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste) if (!pte_present(*ptep)) return pgste; + /* Get referenced bit from storage key */ young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK); - /* Transfer page referenced bit to pte software bit (host view) */ - if (young || (pgste_val(pgste) & RCP_HR_BIT)) + if (young) + pgste_val(pgste) |= RCP_GR_BIT; + /* Get host referenced bit from pgste */ + if (pgste_val(pgste) & RCP_HR_BIT) { + pgste_val(pgste) &= ~RCP_HR_BIT; + young = 1; + } + /* Transfer referenced bit to kvm user bits and pte */ + if (young) { + pgste_val(pgste) |= KVM_UR_BIT; pte_val(*ptep) |= _PAGE_SWR; - /* Clear host referenced bit in pgste. */ - pgste_val(pgste) &= ~RCP_HR_BIT; - /* Transfer page referenced bit to guest bit in pgste */ - pgste_val(pgste) |= (unsigned long) young << 50; /* set RCP_GR_BIT */ + } #endif return pgste; - } -static inline void pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry) +static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry) { #ifdef CONFIG_PGSTE unsigned long address; @@ -688,10 +698,23 @@ static inline void pgste_set_pte(pte_t *ptep, pgste_t pgste, pte_t entry) /* Set page access key and fetch protection bit from pgste */ nkey |= (pgste_val(pgste) & (RCP_ACC_BITS | RCP_FP_BIT)) >> 56; if (okey != nkey) - page_set_storage_key(address, nkey, 1); + page_set_storage_key(address, nkey, 0); #endif } +static inline void pgste_set_pte(pte_t *ptep, pte_t entry) +{ + if (!MACHINE_HAS_ESOP && (pte_val(entry) & _PAGE_SWW)) { + /* + * Without enhanced suppression-on-protection force + * the dirty bit on for all writable ptes. + */ + pte_val(entry) |= _PAGE_SWC; + pte_val(entry) &= ~_PAGE_RO; + } + *ptep = entry; +} + /** * struct gmap_struct - guest address space * @mm: pointer to the parent mm_struct @@ -750,11 +773,14 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, if (mm_has_pgste(mm)) { pgste = pgste_get_lock(ptep); - pgste_set_pte(ptep, pgste, entry); - *ptep = entry; + pgste_set_key(ptep, pgste, entry); + pgste_set_pte(ptep, entry); pgste_set_unlock(ptep, pgste); - } else + } else { + if (!(pte_val(entry) & _PAGE_INVALID) && MACHINE_HAS_EDAT1) + pte_val(entry) |= _PAGE_CO; *ptep = entry; + } } /* @@ -763,16 +789,12 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, */ static inline int pte_write(pte_t pte) { - return (pte_val(pte) & _PAGE_RO) == 0; + return (pte_val(pte) & _PAGE_SWW) != 0; } static inline int pte_dirty(pte_t pte) { -#ifdef CONFIG_PGSTE - if (pte_val(pte) & _PAGE_SWC) - return 1; -#endif - return 0; + return (pte_val(pte) & _PAGE_SWC) != 0; } static inline int pte_young(pte_t pte) @@ -822,11 +844,14 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { pte_val(pte) &= _PAGE_CHG_MASK; pte_val(pte) |= pgprot_val(newprot); + if ((pte_val(pte) & _PAGE_SWC) && (pte_val(pte) & _PAGE_SWW)) + pte_val(pte) &= ~_PAGE_RO; return pte; } static inline pte_t pte_wrprotect(pte_t pte) { + pte_val(pte) &= ~_PAGE_SWW; /* Do not clobber _PAGE_TYPE_NONE pages! */ if (!(pte_val(pte) & _PAGE_INVALID)) pte_val(pte) |= _PAGE_RO; @@ -835,20 +860,26 @@ static inline pte_t pte_wrprotect(pte_t pte) static inline pte_t pte_mkwrite(pte_t pte) { - pte_val(pte) &= ~_PAGE_RO; + pte_val(pte) |= _PAGE_SWW; + if (pte_val(pte) & _PAGE_SWC) + pte_val(pte) &= ~_PAGE_RO; return pte; } static inline pte_t pte_mkclean(pte_t pte) { -#ifdef CONFIG_PGSTE pte_val(pte) &= ~_PAGE_SWC; -#endif + /* Do not clobber _PAGE_TYPE_NONE pages! */ + if (!(pte_val(pte) & _PAGE_INVALID)) + pte_val(pte) |= _PAGE_RO; return pte; } static inline pte_t pte_mkdirty(pte_t pte) { + pte_val(pte) |= _PAGE_SWC; + if (pte_val(pte) & _PAGE_SWW) + pte_val(pte) &= ~_PAGE_RO; return pte; } @@ -886,10 +917,10 @@ static inline pte_t pte_mkhuge(pte_t pte) pte_val(pte) |= _SEGMENT_ENTRY_INV; } /* - * Clear SW pte bits SWT and SWX, there are no SW bits in a segment - * table entry. + * Clear SW pte bits, there are no SW bits in a segment table entry. */ - pte_val(pte) &= ~(_PAGE_SWT | _PAGE_SWX); + pte_val(pte) &= ~(_PAGE_SWT | _PAGE_SWX | _PAGE_SWC | + _PAGE_SWR | _PAGE_SWW); /* * Also set the change-override bit because we don't need dirty bit * tracking for hugetlbfs pages. @@ -1041,9 +1072,11 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm, unsigned long address, pte_t *ptep, pte_t pte) { - *ptep = pte; - if (mm_has_pgste(mm)) + if (mm_has_pgste(mm)) { + pgste_set_pte(ptep, pte); pgste_set_unlock(ptep, *(pgste_t *)(ptep + PTRS_PER_PTE)); + } else + *ptep = pte; } #define __HAVE_ARCH_PTEP_CLEAR_FLUSH @@ -1111,10 +1144,13 @@ static inline pte_t ptep_set_wrprotect(struct mm_struct *mm, if (!mm_exclusive(mm)) __ptep_ipte(address, ptep); - *ptep = pte_wrprotect(pte); + pte = pte_wrprotect(pte); - if (mm_has_pgste(mm)) + if (mm_has_pgste(mm)) { + pgste_set_pte(ptep, pte); pgste_set_unlock(ptep, pgste); + } else + *ptep = pte; } return pte; } @@ -1132,10 +1168,12 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma, pgste = pgste_get_lock(ptep); __ptep_ipte(address, ptep); - *ptep = entry; - if (mm_has_pgste(vma->vm_mm)) + if (mm_has_pgste(vma->vm_mm)) { + pgste_set_pte(ptep, entry); pgste_set_unlock(ptep, pgste); + } else + *ptep = entry; return 1; } @@ -1153,8 +1191,13 @@ static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot) static inline pte_t mk_pte(struct page *page, pgprot_t pgprot) { unsigned long physpage = page_to_phys(page); + pte_t __pte = mk_pte_phys(physpage, pgprot); - return mk_pte_phys(physpage, pgprot); + if ((pte_val(__pte) & _PAGE_SWW) && PageDirty(page)) { + pte_val(__pte) |= _PAGE_SWC; + pte_val(__pte) &= ~_PAGE_RO; + } + return __pte; } #define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) @@ -1246,6 +1289,8 @@ static inline int pmd_trans_splitting(pmd_t pmd) static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr, pmd_t *pmdp, pmd_t entry) { + if (!(pmd_val(entry) & _SEGMENT_ENTRY_INV) && MACHINE_HAS_EDAT1) + pmd_val(entry) |= _SEGMENT_ENTRY_CO; *pmdp = entry; } diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h index 833788693f09..06a136136047 100644 --- a/arch/s390/include/asm/sclp.h +++ b/arch/s390/include/asm/sclp.h @@ -46,7 +46,6 @@ int sclp_cpu_deconfigure(u8 cpu); void sclp_facilities_detect(void); unsigned long long sclp_get_rnmax(void); unsigned long long sclp_get_rzm(void); -u8 sclp_get_fac85(void); int sclp_sdias_blk_count(void); int sclp_sdias_copy(void *dest, int blk_num, int nr_blks); int sclp_chp_configure(struct chp_id chpid); diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index f69f76b3447a..f6857516e523 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -64,13 +64,14 @@ extern unsigned int s390_user_mode; #define MACHINE_FLAG_VM (1UL << 0) #define MACHINE_FLAG_IEEE (1UL << 1) -#define MACHINE_FLAG_CSP (1UL << 3) -#define MACHINE_FLAG_MVPG (1UL << 4) -#define MACHINE_FLAG_DIAG44 (1UL << 5) -#define MACHINE_FLAG_IDTE (1UL << 6) -#define MACHINE_FLAG_DIAG9C (1UL << 7) -#define MACHINE_FLAG_MVCOS (1UL << 8) -#define MACHINE_FLAG_KVM (1UL << 9) +#define MACHINE_FLAG_CSP (1UL << 2) +#define MACHINE_FLAG_MVPG (1UL << 3) +#define MACHINE_FLAG_DIAG44 (1UL << 4) +#define MACHINE_FLAG_IDTE (1UL << 5) +#define MACHINE_FLAG_DIAG9C (1UL << 6) +#define MACHINE_FLAG_MVCOS (1UL << 7) +#define MACHINE_FLAG_KVM (1UL << 8) +#define MACHINE_FLAG_ESOP (1UL << 9) #define MACHINE_FLAG_EDAT1 (1UL << 10) #define MACHINE_FLAG_EDAT2 (1UL << 11) #define MACHINE_FLAG_LPAR (1UL << 12) @@ -84,6 +85,7 @@ extern unsigned int s390_user_mode; #define MACHINE_IS_LPAR (S390_lowcore.machine_flags & MACHINE_FLAG_LPAR) #define MACHINE_HAS_DIAG9C (S390_lowcore.machine_flags & MACHINE_FLAG_DIAG9C) +#define MACHINE_HAS_ESOP (S390_lowcore.machine_flags & MACHINE_FLAG_ESOP) #define MACHINE_HAS_PFMF MACHINE_HAS_EDAT1 #define MACHINE_HAS_HPAGE MACHINE_HAS_EDAT1 diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index f090e819bf71..2923781590a6 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -147,7 +147,7 @@ int kvm_dev_ioctl_check_extension(long ext) r = KVM_MAX_VCPUS; break; case KVM_CAP_S390_COW: - r = sclp_get_fac85() & 0x2; + r = MACHINE_HAS_ESOP; break; default: r = 0; diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index 9017a63dda3d..a70ee84c0241 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c @@ -50,7 +50,7 @@ static __always_inline unsigned long follow_table(struct mm_struct *mm, ptep = pte_offset_map(pmd, addr); if (!pte_present(*ptep)) return -0x11UL; - if (write && !pte_write(*ptep)) + if (write && (!pte_write(*ptep) || !pte_dirty(*ptep))) return -0x04UL; return (pte_val(*ptep) & PAGE_MASK) + (addr & ~PAGE_MASK); diff --git a/arch/s390/mm/pageattr.c b/arch/s390/mm/pageattr.c index 29ccee3651f4..d21040ed5e59 100644 --- a/arch/s390/mm/pageattr.c +++ b/arch/s390/mm/pageattr.c @@ -127,7 +127,7 @@ void kernel_map_pages(struct page *page, int numpages, int enable) pte_val(*pte) = _PAGE_TYPE_EMPTY; continue; } - *pte = mk_pte_phys(address, __pgprot(_PAGE_TYPE_RW)); + pte_val(*pte) = __pa(address); } } diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index 6ed1426d27c5..79699f46a443 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -85,11 +85,9 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) pud_t *pu_dir; pmd_t *pm_dir; pte_t *pt_dir; - pte_t pte; int ret = -ENOMEM; while (address < end) { - pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0)); pg_dir = pgd_offset_k(address); if (pgd_none(*pg_dir)) { pu_dir = vmem_pud_alloc(); @@ -101,9 +99,9 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) #if defined(CONFIG_64BIT) && !defined(CONFIG_DEBUG_PAGEALLOC) if (MACHINE_HAS_EDAT2 && pud_none(*pu_dir) && address && !(address & ~PUD_MASK) && (address + PUD_SIZE <= end)) { - pte_val(pte) |= _REGION3_ENTRY_LARGE; - pte_val(pte) |= _REGION_ENTRY_TYPE_R3; - pud_val(*pu_dir) = pte_val(pte); + pud_val(*pu_dir) = __pa(address) | + _REGION_ENTRY_TYPE_R3 | _REGION3_ENTRY_LARGE | + (ro ? _REGION_ENTRY_RO : 0); address += PUD_SIZE; continue; } @@ -118,8 +116,9 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) #if defined(CONFIG_64BIT) && !defined(CONFIG_DEBUG_PAGEALLOC) if (MACHINE_HAS_EDAT1 && pmd_none(*pm_dir) && address && !(address & ~PMD_MASK) && (address + PMD_SIZE <= end)) { - pte_val(pte) |= _SEGMENT_ENTRY_LARGE; - pmd_val(*pm_dir) = pte_val(pte); + pmd_val(*pm_dir) = __pa(address) | + _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE | + (ro ? _SEGMENT_ENTRY_RO : 0); address += PMD_SIZE; continue; } @@ -132,7 +131,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) } pt_dir = pte_offset_kernel(pm_dir, address); - *pt_dir = pte; + pte_val(*pt_dir) = __pa(address) | (ro ? _PAGE_RO : 0); address += PAGE_SIZE; } ret = 0; @@ -199,7 +198,6 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) pud_t *pu_dir; pmd_t *pm_dir; pte_t *pt_dir; - pte_t pte; int ret = -ENOMEM; start_addr = (unsigned long) start; @@ -237,9 +235,8 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) new_page = vmemmap_alloc_block(PMD_SIZE, node); if (!new_page) goto out; - pte = mk_pte_phys(__pa(new_page), PAGE_RW); - pte_val(pte) |= _SEGMENT_ENTRY_LARGE; - pmd_val(*pm_dir) = pte_val(pte); + pmd_val(*pm_dir) = __pa(new_page) | + _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE; address = (address + PMD_SIZE) & PMD_MASK; continue; } @@ -260,8 +257,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) new_page =__pa(vmem_alloc_pages(0)); if (!new_page) goto out; - pte = pfn_pte(new_page >> PAGE_SHIFT, PAGE_KERNEL); - *pt_dir = pte; + pte_val(*pt_dir) = __pa(new_page); } address += PAGE_SIZE; } diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index c44d13f607bc..30a2255389e5 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -56,7 +56,6 @@ static int __initdata early_read_info_sccb_valid; u64 sclp_facilities; static u8 sclp_fac84; -static u8 sclp_fac85; static unsigned long long rzm; static unsigned long long rnmax; @@ -131,7 +130,8 @@ void __init sclp_facilities_detect(void) sccb = &early_read_info_sccb; sclp_facilities = sccb->facilities; sclp_fac84 = sccb->fac84; - sclp_fac85 = sccb->fac85; + if (sccb->fac85 & 0x02) + S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP; rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2; rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2; rzm <<= 20; @@ -171,12 +171,6 @@ unsigned long long sclp_get_rzm(void) return rzm; } -u8 sclp_get_fac85(void) -{ - return sclp_fac85; -} -EXPORT_SYMBOL_GPL(sclp_get_fac85); - /* * This function will be called after sclp_facilities_detect(), which gets * called from early.c code. Therefore the sccb should have valid contents. diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 5cf680a98f9b..bfd87685fc1f 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -197,16 +197,6 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b) #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ #endif -#ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY -#define page_test_and_clear_dirty(pfn, mapped) (0) -#endif - -#ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY -#define pte_maybe_dirty(pte) pte_dirty(pte) -#else -#define pte_maybe_dirty(pte) (1) -#endif - #ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG #define page_test_and_clear_young(pfn) (0) #endif diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index 70473da47b3f..6d53675c2b54 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -303,21 +303,13 @@ static inline void __SetPageUptodate(struct page *page) static inline void SetPageUptodate(struct page *page) { -#ifdef CONFIG_S390 - if (!test_and_set_bit(PG_uptodate, &page->flags)) - page_set_storage_key(page_to_phys(page), PAGE_DEFAULT_KEY, 0); -#else /* * Memory barrier must be issued before setting the PG_uptodate bit, * so that all previous stores issued in order to bring the page * uptodate are actually visible before PageUptodate becomes true. - * - * s390 doesn't need an explicit smp_wmb here because the test and - * set bit already provides full barriers. */ smp_wmb(); set_bit(PG_uptodate, &(page)->flags); -#endif } CLEARPAGEFLAG(Uptodate, uptodate) diff --git a/mm/rmap.c b/mm/rmap.c index 2c78f8cadc95..3d38edffda41 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -1126,7 +1126,6 @@ void page_add_file_rmap(struct page *page) */ void page_remove_rmap(struct page *page) { - struct address_space *mapping = page_mapping(page); bool anon = PageAnon(page); bool locked; unsigned long flags; @@ -1143,29 +1142,6 @@ void page_remove_rmap(struct page *page) if (!atomic_add_negative(-1, &page->_mapcount)) goto out; - /* - * Now that the last pte has gone, s390 must transfer dirty - * flag from storage key to struct page. We can usually skip - * this if the page is anon, so about to be freed; but perhaps - * not if it's in swapcache - there might be another pte slot - * containing the swap entry, but page not yet written to swap. - * - * And we can skip it on file pages, so long as the filesystem - * participates in dirty tracking (note that this is not only an - * optimization but also solves problems caused by dirty flag in - * storage key getting set by a write from inside kernel); but need to - * catch shm and tmpfs and ramfs pages which have been modified since - * creation by read fault. - * - * Note that mapping must be decided above, before decrementing - * mapcount (which luckily provides a barrier): once page is unmapped, - * it could be truncated and page->mapping reset to NULL at any moment. - * Note also that we are relying on page_mapping(page) to set mapping - * to &swapper_space when PageSwapCache(page). - */ - if (mapping && !mapping_cap_account_dirty(mapping) && - page_test_and_clear_dirty(page_to_pfn(page), 1)) - set_page_dirty(page); /* * Hugepages are not counted in NR_ANON_PAGES nor NR_FILE_MAPPED * and not charged by memcg for now. From 23d18e8d9311db748b5b496bc4ba38500e3d408b Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Mon, 11 Feb 2013 18:11:09 +0100 Subject: [PATCH 2730/4607] s390/cleanup: rename SPP to LPP The set-program-parameter (SPP) instruction has been renamed to load-program-parameter (LPP) (see SA23-2260). Reflect this change and rename all macro/instruction references. Also remove the duplicate SPP/LPP entry in the kernel disassembler instruction list. Signed-off-by: Hendrik Brueckner Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/cpu_mf.h | 4 ++-- arch/s390/include/asm/setup.h | 6 +++--- arch/s390/kernel/dis.c | 1 - arch/s390/kernel/early.c | 2 +- arch/s390/kernel/entry64.S | 10 +++++----- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/arch/s390/include/asm/cpu_mf.h b/arch/s390/include/asm/cpu_mf.h index 35f0020b7ba7..f1eddd150dd7 100644 --- a/arch/s390/include/asm/cpu_mf.h +++ b/arch/s390/include/asm/cpu_mf.h @@ -34,12 +34,12 @@ /* CPU measurement facility support */ static inline int cpum_cf_avail(void) { - return MACHINE_HAS_SPP && test_facility(67); + return MACHINE_HAS_LPP && test_facility(67); } static inline int cpum_sf_avail(void) { - return MACHINE_HAS_SPP && test_facility(68); + return MACHINE_HAS_LPP && test_facility(68); } diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index f6857516e523..ff67d730c00c 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -75,7 +75,7 @@ extern unsigned int s390_user_mode; #define MACHINE_FLAG_EDAT1 (1UL << 10) #define MACHINE_FLAG_EDAT2 (1UL << 11) #define MACHINE_FLAG_LPAR (1UL << 12) -#define MACHINE_FLAG_SPP (1UL << 13) +#define MACHINE_FLAG_LPP (1UL << 13) #define MACHINE_FLAG_TOPOLOGY (1UL << 14) #define MACHINE_FLAG_TE (1UL << 15) #define MACHINE_FLAG_RRBM (1UL << 16) @@ -98,7 +98,7 @@ extern unsigned int s390_user_mode; #define MACHINE_HAS_MVCOS (0) #define MACHINE_HAS_EDAT1 (0) #define MACHINE_HAS_EDAT2 (0) -#define MACHINE_HAS_SPP (0) +#define MACHINE_HAS_LPP (0) #define MACHINE_HAS_TOPOLOGY (0) #define MACHINE_HAS_TE (0) #define MACHINE_HAS_RRBM (0) @@ -111,7 +111,7 @@ extern unsigned int s390_user_mode; #define MACHINE_HAS_MVCOS (S390_lowcore.machine_flags & MACHINE_FLAG_MVCOS) #define MACHINE_HAS_EDAT1 (S390_lowcore.machine_flags & MACHINE_FLAG_EDAT1) #define MACHINE_HAS_EDAT2 (S390_lowcore.machine_flags & MACHINE_FLAG_EDAT2) -#define MACHINE_HAS_SPP (S390_lowcore.machine_flags & MACHINE_FLAG_SPP) +#define MACHINE_HAS_LPP (S390_lowcore.machine_flags & MACHINE_FLAG_LPP) #define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY) #define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE) #define MACHINE_HAS_RRBM (S390_lowcore.machine_flags & MACHINE_FLAG_RRBM) diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index a7f9abd98cf2..c50665fe9435 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c @@ -840,7 +840,6 @@ static struct insn opcode_b2[] = { { "stcke", 0x78, INSTR_S_RD }, { "sacf", 0x79, INSTR_S_RD }, { "stsi", 0x7d, INSTR_S_RD }, - { "spp", 0x80, INSTR_S_RD }, { "srnm", 0x99, INSTR_S_RD }, { "stfpc", 0x9c, INSTR_S_RD }, { "lfpc", 0x9d, INSTR_S_RD }, diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 1ee98e56fc6a..bda011e2f8ae 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -381,7 +381,7 @@ static __init void detect_machine_facilities(void) if (test_facility(27)) S390_lowcore.machine_flags |= MACHINE_FLAG_MVCOS; if (test_facility(40)) - S390_lowcore.machine_flags |= MACHINE_FLAG_SPP; + S390_lowcore.machine_flags |= MACHINE_FLAG_LPP; if (test_facility(50) && test_facility(73)) S390_lowcore.machine_flags |= MACHINE_FLAG_TE; if (test_facility(66)) diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 6d34e0c97a39..9c837c101297 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -72,9 +72,9 @@ _TIF_EXIT_SIE = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_MCCK_PENDING) #endif .endm - .macro SPP newpp + .macro LPP newpp #if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) - tm __LC_MACHINE_FLAGS+6,0x20 # MACHINE_FLAG_SPP + tm __LC_MACHINE_FLAGS+6,0x20 # MACHINE_FLAG_LPP jz .+8 .insn s,0xb2800000,\newpp #endif @@ -96,7 +96,7 @@ _TIF_EXIT_SIE = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_MCCK_PENDING) jhe .+22 .endif lg %r9,BASED(.Lsie_loop) - SPP BASED(.Lhost_id) # set host id + LPP BASED(.Lhost_id) # set host id #endif .endm @@ -967,10 +967,10 @@ sie_loop: lctlg %c1,%c1,__GMAP_ASCE(%r14) # load primary asce sie_gmap: lg %r14,__SF_EMPTY(%r15) # get control block pointer - SPP __SF_EMPTY(%r15) # set guest id + LPP __SF_EMPTY(%r15) # set guest id sie 0(%r14) sie_done: - SPP __SF_EMPTY+16(%r15) # set host id + LPP __SF_EMPTY+16(%r15) # set host id lg %r14,__LC_THREAD_INFO # pointer thread_info struct sie_exit: lctlg %c1,%c1,__LC_USER_ASCE # load primary asce From be7b334b2abde95063742fa66425ea9e1298e1c7 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 5 Feb 2013 12:38:30 +0100 Subject: [PATCH 2731/4607] drivers/input: add couple of missing GENERIC_HARDIRQS dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When removing the !S390 dependency from drivers/input/Kconfig a couple of drivers don't compile because they have a dependency on GENERIC_HARDIRQS. So add the missing dependencies. Fixes e.g. this one: drivers/input/keyboard/lm8323.c: In function ‘lm8323_suspend’: drivers/input/keyboard/lm8323.c:801:2: error: implicit declaration of function ‘irq_set_irq_wake’ [-Werror=implicit-function-declaration] Cc: Dmitry Torokhov Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/input/Kconfig | 2 +- drivers/input/keyboard/Kconfig | 4 ++-- drivers/input/serio/Kconfig | 1 + drivers/input/touchscreen/Kconfig | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 55f7e57d4e42..38b523a1ece0 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -3,7 +3,7 @@ # menu "Input device support" - depends on !S390 && !UML + depends on !UML config INPUT tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 5a240c60342d..a6ccbf078463 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -224,7 +224,7 @@ config KEYBOARD_TCA6416 config KEYBOARD_TCA8418 tristate "TCA8418 Keypad Support" - depends on I2C + depends on I2C && GENERIC_HARDIRQS select INPUT_MATRIXKMAP help This driver implements basic keypad functionality @@ -303,7 +303,7 @@ config KEYBOARD_HP7XX config KEYBOARD_LM8323 tristate "LM8323 keypad chip" - depends on I2C + depends on I2C && GENERIC_HARDIRQS depends on LEDS_CLASS help If you say yes here you get support for the National Semiconductor diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 4a4e182c33e7..560c243bfcaf 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -236,6 +236,7 @@ config SERIO_PS2MULT config SERIO_ARC_PS2 tristate "ARC PS/2 support" + depends on GENERIC_HARDIRQS help Say Y here if you have an ARC FPGA platform with a PS/2 controller in it. diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 515cfe790543..f9a5fd89bc02 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -359,7 +359,7 @@ config TOUCHSCREEN_MCS5000 config TOUCHSCREEN_MMS114 tristate "MELFAS MMS114 touchscreen" - depends on I2C + depends on I2C && GENERIC_HARDIRQS help Say Y here if you have the MELFAS MMS114 touchscreen controller chip in your system. From 29bde2d93289d86d5caf24e75c14559292a42756 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 5 Feb 2013 12:48:29 +0100 Subject: [PATCH 2732/4607] drivers/gpio: add missing GENERIC_HARDIRQ dependency The VIA VX855/VX875 GPIO and RDC R-321x GPIO support drivers select MFD_CORE which itself depends on GENERIC_HARDIRQ support. So add this dependency to these two drivers as well to prevent selection of MFD_CORE. Cc: Grant Likely Cc: Samuel Ortiz Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/gpio/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 682de754d63f..40da0ef3a31b 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -277,7 +277,7 @@ config GPIO_ICH config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE select MFD_VX855 help @@ -599,7 +599,7 @@ config GPIO_TIMBERDALE config GPIO_RDC321X tristate "RDC R-321x GPIO support" - depends on PCI + depends on PCI && GENERIC_HARDIRQS select MFD_CORE select MFD_RDC321X help From e80cfc31d872b6b85b8966bce6ba80bee401a7dd Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Wed, 31 Oct 2012 17:26:44 +0100 Subject: [PATCH 2733/4607] s390/module: Add missing R_390_NONE relocation type Allow loading of kernel modules that have relocations of type R_390_NONE. Signed-off-by: Hendrik Brueckner Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/module.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c index 06f17311628a..f750bd7bd2c2 100644 --- a/arch/s390/kernel/module.c +++ b/arch/s390/kernel/module.c @@ -234,6 +234,9 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, val = symtab[r_sym].st_value; switch (r_type) { + case R_390_NONE: /* No relocation. */ + rc = 0; + break; case R_390_8: /* Direct 8 bit. */ case R_390_12: /* Direct 12 bit. */ case R_390_16: /* Direct 16 bit. */ From a81252d75e14cc2cf0ee45078ef143562a0bc279 Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Thu, 14 Feb 2013 16:16:49 +0100 Subject: [PATCH 2734/4607] openrisc: fix up vmalloc page table loading vmalloc'ed pages are faulted into a process' page tables on demand. In order to facilitate this, do_page_fault needs to know whether it was called via a page fault exception or a TLB-miss exception. This patch adds a wrapper around the _x_page_fault_handler entry points that the TLB-miss exceptions can call into in order to have the relevant parameter set to satisfy do_page_fault. This fixes a bug and is "good enough" for now. That said, this whole handling of vmalloc needs to be audited for correctness at some point. Signed-off-by: Jonas Bonn --- arch/openrisc/kernel/entry.S | 14 ++++++++++++-- arch/openrisc/kernel/head.S | 6 ++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S index 5e5b30601bbf..3de971224cfc 100644 --- a/arch/openrisc/kernel/entry.S +++ b/arch/openrisc/kernel/entry.S @@ -201,12 +201,17 @@ EXCEPTION_ENTRY(_bus_fault_handler) l.nop /* ---[ 0x300: Data Page Fault exception ]------------------------------- */ +EXCEPTION_ENTRY(_dtlb_miss_page_fault_handler) + l.and r5,r5,r0 + l.j 1f + l.nop EXCEPTION_ENTRY(_data_page_fault_handler) /* set up parameters for do_page_fault */ + l.ori r5,r0,0x300 // exception vector +1: l.addi r3,r1,0 // pt_regs /* r4 set be EXCEPTION_HANDLE */ // effective address of fault - l.ori r5,r0,0x300 // exception vector /* * __PHX__: TODO @@ -276,12 +281,17 @@ EXCEPTION_ENTRY(_data_page_fault_handler) l.nop /* ---[ 0x400: Insn Page Fault exception ]------------------------------- */ +EXCEPTION_ENTRY(_itlb_miss_page_fault_handler) + l.and r5,r5,r0 + l.j 1f + l.nop EXCEPTION_ENTRY(_insn_page_fault_handler) /* set up parameters for do_page_fault */ + l.ori r5,r0,0x400 // exception vector +1: l.addi r3,r1,0 // pt_regs /* r4 set be EXCEPTION_HANDLE */ // effective address of fault - l.ori r5,r0,0x400 // exception vector l.ori r6,r0,0x0 // !write access /* call fault.c handler in or32/mm/fault.c */ diff --git a/arch/openrisc/kernel/head.S b/arch/openrisc/kernel/head.S index 46aa940ebd20..b357e7f79aca 100644 --- a/arch/openrisc/kernel/head.S +++ b/arch/openrisc/kernel/head.S @@ -1069,8 +1069,7 @@ d_pte_not_present: EXCEPTION_LOAD_GPR4 EXCEPTION_LOAD_GPR5 EXCEPTION_LOAD_GPR6 - l.j _dispatch_do_dpage_fault - l.nop + EXCEPTION_HANDLE(_dtlb_miss_page_fault_handler) /* ==============================================[ ITLB miss handler ]=== */ ENTRY(itlb_miss_handler) @@ -1192,8 +1191,7 @@ i_pte_not_present: EXCEPTION_LOAD_GPR4 EXCEPTION_LOAD_GPR5 EXCEPTION_LOAD_GPR6 - l.j _dispatch_do_ipage_fault - l.nop + EXCEPTION_HANDLE(_itlb_miss_page_fault_handler) /* ==============================================[ boot tlb handlers ]=== */ From c7886b18273b07042e25e8d3ba5c983837b84123 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 13 Feb 2013 00:56:13 +0900 Subject: [PATCH 2735/4607] gpio: em: Use irq_domain_add_simple() to fix runtime error Adjust the gpio-em.c driver to reconsider the pdata->irq_base variable. Non-DT board code like for instance board-kzm9d.c needs to operate of a static IRQ range for platform devices. So this patch is updating the code to make use of the function irq_domain_add_simple() instead of irq_domain_add_linear(). Fixes a EMEV2 / KZM9D runtime error caused by the following commit: 7385500 gpio/em: convert to linear IRQ domain Cc: stable@kernel.org Signed-off-by: Magnus Damm Tested-by: Simon Horman Reported-by: Simon Horman Signed-off-by: Linus Walleij --- drivers/gpio/gpio-em.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index bdc8302e711a..deca78f99316 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -299,8 +299,9 @@ static int em_gio_probe(struct platform_device *pdev) irq_chip->irq_set_type = em_gio_irq_set_type; irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; - p->irq_domain = irq_domain_add_linear(pdev->dev.of_node, + p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, pdata->number_of_pins, + pdata->irq_base, &em_gio_irq_domain_ops, p); if (!p->irq_domain) { ret = -ENXIO; From 5641ade41f7c7d16e614e25ce3315e04f1bacd33 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 10:45:31 -0700 Subject: [PATCH 2736/4607] vfio-pci: Enable PCIe extended capabilities on v1 Even PCIe 1.x had extended config space. Signed-off-by: Alex Williamson --- drivers/vfio/pci/vfio_pci_config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index 8b8f7d11e102..c975d91e1ccc 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -985,12 +985,12 @@ static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos) if (ret) return pcibios_err_to_errno(ret); + vdev->extended_caps = true; + if ((word & PCI_EXP_FLAGS_VERS) == 1) return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1; - else { - vdev->extended_caps = true; + else return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2; - } case PCI_CAP_ID_HT: ret = pci_read_config_byte(pdev, pos + 3, &byte); if (ret) From 8de5c325b4ee7d5b23b95edd28b81c9a2a9f427f Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 14 Feb 2013 15:11:41 -0500 Subject: [PATCH 2737/4607] ext4: use KERN_WARNING for warning messages Some messages printed related to a WARN_ON(1) were printed using KERN_NOTICE. Use KERN_WARNING or ext4_warning() instead so that context related to the WARN_ON() is printed at the same printk warning level (and log files, etc.) Signed-off-by: "Theodore Ts'o" --- fs/ext4/ialloc.c | 12 ++++++------ fs/ext4/inode.c | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 91d8fe3aced3..32fd2b9075dd 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -1028,17 +1028,17 @@ iget_failed: inode = NULL; bad_orphan: ext4_warning(sb, "bad orphan inode %lu! e2fsck was run?", ino); - printk(KERN_NOTICE "ext4_test_bit(bit=%d, block=%llu) = %d\n", + printk(KERN_WARNING "ext4_test_bit(bit=%d, block=%llu) = %d\n", bit, (unsigned long long)bitmap_bh->b_blocknr, ext4_test_bit(bit, bitmap_bh->b_data)); - printk(KERN_NOTICE "inode=%p\n", inode); + printk(KERN_WARNING "inode=%p\n", inode); if (inode) { - printk(KERN_NOTICE "is_bad_inode(inode)=%d\n", + printk(KERN_WARNING "is_bad_inode(inode)=%d\n", is_bad_inode(inode)); - printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n", + printk(KERN_WARNING "NEXT_ORPHAN(inode)=%u\n", NEXT_ORPHAN(inode)); - printk(KERN_NOTICE "max_ino=%lu\n", max_ino); - printk(KERN_NOTICE "i_nlink=%u\n", inode->i_nlink); + printk(KERN_WARNING "max_ino=%lu\n", max_ino); + printk(KERN_WARNING "i_nlink=%u\n", inode->i_nlink); /* Avoid freeing blocks if we got a bad deleted inode */ if (inode->i_nlink == 0) inode->i_blocks = 0; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 2fa18bb0bf3c..b85d5dae726b 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -343,7 +343,7 @@ void ext4_da_update_reserve_space(struct inode *inode, spin_lock(&ei->i_block_reservation_lock); trace_ext4_da_update_reserve_space(inode, used, quota_claim); if (unlikely(used > ei->i_reserved_data_blocks)) { - ext4_msg(inode->i_sb, KERN_NOTICE, "%s: ino %lu, used %d " + ext4_warning(inode->i_sb, "%s: ino %lu, used %d " "with only %d reserved data blocks", __func__, inode->i_ino, used, ei->i_reserved_data_blocks); @@ -352,7 +352,7 @@ void ext4_da_update_reserve_space(struct inode *inode, } if (unlikely(ei->i_allocated_meta_blocks > ei->i_reserved_meta_blocks)) { - ext4_msg(inode->i_sb, KERN_NOTICE, "%s: ino %lu, allocated %d " + ext4_warning(inode->i_sb, "%s: ino %lu, allocated %d " "with only %d reserved metadata blocks\n", __func__, inode->i_ino, ei->i_allocated_meta_blocks, ei->i_reserved_meta_blocks); @@ -1263,7 +1263,7 @@ static void ext4_da_release_space(struct inode *inode, int to_free) * function is called from invalidate page, it's * harmless to return without any action. */ - ext4_msg(inode->i_sb, KERN_NOTICE, "ext4_da_release_space: " + ext4_warning(inode->i_sb, "ext4_da_release_space: " "ino %lu, to_free %d with only %d reserved " "data blocks", inode->i_ino, to_free, ei->i_reserved_data_blocks); From 01a523eb51cb505a4bc1eaffeeccd2527d6ab619 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 14 Feb 2013 15:51:58 -0500 Subject: [PATCH 2738/4607] ext4: add debugging context for warning in ext4_da_update_reserve_space() Print some additional debugging context to hopefully help to debug a warning which is getting triggered by xfstests #74. Also remove extraneous newlines from when printk's were converted to ext4_warning() and ext4_msg(). Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index b85d5dae726b..c4e9177f60c6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -352,10 +352,12 @@ void ext4_da_update_reserve_space(struct inode *inode, } if (unlikely(ei->i_allocated_meta_blocks > ei->i_reserved_meta_blocks)) { - ext4_warning(inode->i_sb, "%s: ino %lu, allocated %d " - "with only %d reserved metadata blocks\n", __func__, - inode->i_ino, ei->i_allocated_meta_blocks, - ei->i_reserved_meta_blocks); + ext4_warning(inode->i_sb, "ino %lu, allocated %d " + "with only %d reserved metadata blocks " + "(releasing %d blocks with reserved %d data blocks)", + inode->i_ino, ei->i_allocated_meta_blocks, + ei->i_reserved_meta_blocks, used, + ei->i_reserved_data_blocks); WARN_ON(1); ei->i_allocated_meta_blocks = ei->i_reserved_meta_blocks; } @@ -1609,7 +1611,7 @@ static void mpage_da_map_and_submit(struct mpage_da_data *mpd) (unsigned long long) next, mpd->b_size >> mpd->inode->i_blkbits, err); ext4_msg(sb, KERN_CRIT, - "This should not happen!! Data will be lost\n"); + "This should not happen!! Data will be lost"); if (err == -ENOSPC) ext4_print_free_blocks(mpd->inode); } From 5b279a11d32998aad1e45fe9de225302b6a8e8ba Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 14:02:12 -0700 Subject: [PATCH 2739/4607] vfio-pci: Cleanup read/write functions The read and write functions are nearly identical, combine them and convert to a switch statement. This also makes it easy to narrow the scope of when we use the io/mem accessors in case new regions are added. Signed-off-by: Alex Williamson --- drivers/vfio/pci/vfio_pci.c | 59 ++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index b28e66c4376a..469e110d7ea6 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -366,8 +366,8 @@ static long vfio_pci_ioctl(void *device_data, return -ENOTTY; } -static ssize_t vfio_pci_read(void *device_data, char __user *buf, - size_t count, loff_t *ppos) +static ssize_t vfio_pci_rw(void *device_data, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) { unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); struct vfio_pci_device *vdev = device_data; @@ -376,42 +376,41 @@ static ssize_t vfio_pci_read(void *device_data, char __user *buf, if (index >= VFIO_PCI_NUM_REGIONS) return -EINVAL; - if (index == VFIO_PCI_CONFIG_REGION_INDEX) - return vfio_pci_config_readwrite(vdev, buf, count, ppos, false); - else if (index == VFIO_PCI_ROM_REGION_INDEX) - return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); - else if (pci_resource_flags(pdev, index) & IORESOURCE_IO) - return vfio_pci_io_readwrite(vdev, buf, count, ppos, false); - else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) + switch (index) { + case VFIO_PCI_CONFIG_REGION_INDEX: + return vfio_pci_config_readwrite(vdev, buf, count, + ppos, iswrite); + case VFIO_PCI_ROM_REGION_INDEX: + if (iswrite) + return -EINVAL; return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); + case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: + { + unsigned long flags = pci_resource_flags(pdev, index); + + if (flags & IORESOURCE_IO) + return vfio_pci_io_readwrite(vdev, buf, count, + ppos, iswrite); + if (flags & IORESOURCE_MEM) + return vfio_pci_mem_readwrite(vdev, buf, count, + ppos, iswrite); + } + } + return -EINVAL; } +static ssize_t vfio_pci_read(void *device_data, char __user *buf, + size_t count, loff_t *ppos) +{ + return vfio_pci_rw(device_data, buf, count, ppos, false); +} + static ssize_t vfio_pci_write(void *device_data, const char __user *buf, size_t count, loff_t *ppos) { - unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - struct vfio_pci_device *vdev = device_data; - struct pci_dev *pdev = vdev->pdev; - - if (index >= VFIO_PCI_NUM_REGIONS) - return -EINVAL; - - if (index == VFIO_PCI_CONFIG_REGION_INDEX) - return vfio_pci_config_readwrite(vdev, (char __user *)buf, - count, ppos, true); - else if (index == VFIO_PCI_ROM_REGION_INDEX) - return -EINVAL; - else if (pci_resource_flags(pdev, index) & IORESOURCE_IO) - return vfio_pci_io_readwrite(vdev, (char __user *)buf, - count, ppos, true); - else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) { - return vfio_pci_mem_readwrite(vdev, (char __user *)buf, - count, ppos, true); - } - - return -EINVAL; + return vfio_pci_rw(device_data, buf, count, ppos, true); } static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) From 906ee99dd2a5c819c1171ce5eaf6c080c027e58c Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 14:02:12 -0700 Subject: [PATCH 2740/4607] vfio-pci: Cleanup BAR access We can actually handle MMIO and I/O port from the same access function since PCI already does abstraction of this. The ROM BAR only requires a minor difference, so it gets included too. vfio_pci_config_readwrite gets renamed for consistency. Signed-off-by: Alex Williamson --- drivers/vfio/pci/vfio_pci.c | 26 ++- drivers/vfio/pci/vfio_pci_config.c | 5 +- drivers/vfio/pci/vfio_pci_private.h | 15 +- drivers/vfio/pci/vfio_pci_rdwr.c | 280 ++++++++++------------------ 4 files changed, 113 insertions(+), 213 deletions(-) diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 469e110d7ea6..bb8c8c2be960 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -371,31 +371,21 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf, { unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos); struct vfio_pci_device *vdev = device_data; - struct pci_dev *pdev = vdev->pdev; if (index >= VFIO_PCI_NUM_REGIONS) return -EINVAL; switch (index) { case VFIO_PCI_CONFIG_REGION_INDEX: - return vfio_pci_config_readwrite(vdev, buf, count, - ppos, iswrite); + return vfio_pci_config_rw(vdev, buf, count, ppos, iswrite); + case VFIO_PCI_ROM_REGION_INDEX: if (iswrite) return -EINVAL; - return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false); + return vfio_pci_bar_rw(vdev, buf, count, ppos, false); case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: - { - unsigned long flags = pci_resource_flags(pdev, index); - - if (flags & IORESOURCE_IO) - return vfio_pci_io_readwrite(vdev, buf, count, - ppos, iswrite); - if (flags & IORESOURCE_MEM) - return vfio_pci_mem_readwrite(vdev, buf, count, - ppos, iswrite); - } + return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite); } return -EINVAL; @@ -404,13 +394,19 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf, static ssize_t vfio_pci_read(void *device_data, char __user *buf, size_t count, loff_t *ppos) { + if (!count) + return 0; + return vfio_pci_rw(device_data, buf, count, ppos, false); } static ssize_t vfio_pci_write(void *device_data, const char __user *buf, size_t count, loff_t *ppos) { - return vfio_pci_rw(device_data, buf, count, ppos, true); + if (!count) + return 0; + + return vfio_pci_rw(device_data, (char __user *)buf, count, ppos, true); } static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma) diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index c975d91e1ccc..f1dde2c0fe99 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -1501,9 +1501,8 @@ static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf, return ret; } -ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite) +ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) { size_t done = 0; int ret = 0; diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index 611827cba8cd..00d19b953ce4 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h @@ -70,15 +70,12 @@ extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags, unsigned index, unsigned start, unsigned count, void *data); -extern ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite); -extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite); -extern ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, - char __user *buf, size_t count, - loff_t *ppos, bool iswrite); +extern ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev, + char __user *buf, size_t count, + loff_t *ppos, bool iswrite); + +extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite); extern int vfio_pci_init_perm_bits(void); extern void vfio_pci_uninit_perm_bits(void); diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c index f72323ef618f..e9d78eb91ed7 100644 --- a/drivers/vfio/pci/vfio_pci_rdwr.c +++ b/drivers/vfio/pci/vfio_pci_rdwr.c @@ -20,205 +20,57 @@ #include "vfio_pci_private.h" -/* I/O Port BAR access */ -ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf, - size_t count, loff_t *ppos, bool iswrite) -{ - struct pci_dev *pdev = vdev->pdev; - loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; - int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - void __iomem *io; - size_t done = 0; - - if (!pci_resource_start(pdev, bar)) - return -EINVAL; - - if (pos + count > pci_resource_len(pdev, bar)) - return -EINVAL; - - if (!vdev->barmap[bar]) { - int ret; - - ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); - if (ret) - return ret; - - vdev->barmap[bar] = pci_iomap(pdev, bar, 0); - - if (!vdev->barmap[bar]) { - pci_release_selected_regions(pdev, 1 << bar); - return -EINVAL; - } - } - - io = vdev->barmap[bar]; - - while (count) { - int filled; - - if (count >= 3 && !(pos % 4)) { - __le32 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 4)) - return -EFAULT; - - iowrite32(le32_to_cpu(val), io + pos); - } else { - val = cpu_to_le32(ioread32(io + pos)); - - if (copy_to_user(buf, &val, 4)) - return -EFAULT; - } - - filled = 4; - - } else if ((pos % 2) == 0 && count >= 2) { - __le16 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 2)) - return -EFAULT; - - iowrite16(le16_to_cpu(val), io + pos); - } else { - val = cpu_to_le16(ioread16(io + pos)); - - if (copy_to_user(buf, &val, 2)) - return -EFAULT; - } - - filled = 2; - } else { - u8 val; - - if (iswrite) { - if (copy_from_user(&val, buf, 1)) - return -EFAULT; - - iowrite8(val, io + pos); - } else { - val = ioread8(io + pos); - - if (copy_to_user(buf, &val, 1)) - return -EFAULT; - } - - filled = 1; - } - - count -= filled; - done += filled; - buf += filled; - pos += filled; - } - - *ppos += done; - - return done; -} - /* - * MMIO BAR access - * We handle two excluded ranges here as well, if the user tries to read - * the ROM beyond what PCI tells us is available or the MSI-X table region, - * we return 0xFF and writes are dropped. + * Read or write from an __iomem region (MMIO or I/O port) with an excluded + * range which is inaccessible. The excluded range drops writes and fills + * reads with -1. This is intended for handling MSI-X vector tables and + * leftover space for ROM BARs. */ -ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf, - size_t count, loff_t *ppos, bool iswrite) +static ssize_t do_io_rw(void __iomem *io, char __user *buf, + loff_t off, size_t count, size_t x_start, + size_t x_end, bool iswrite) { - struct pci_dev *pdev = vdev->pdev; - loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; - int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); - void __iomem *io; - resource_size_t end; - size_t done = 0; - size_t x_start = 0, x_end = 0; /* excluded range */ - - if (!pci_resource_start(pdev, bar)) - return -EINVAL; - - end = pci_resource_len(pdev, bar); - - if (pos > end) - return -EINVAL; - - if (pos == end) - return 0; - - if (pos + count > end) - count = end - pos; - - if (bar == PCI_ROM_RESOURCE) { - io = pci_map_rom(pdev, &x_start); - x_end = end; - } else { - if (!vdev->barmap[bar]) { - int ret; - - ret = pci_request_selected_regions(pdev, 1 << bar, - "vfio"); - if (ret) - return ret; - - vdev->barmap[bar] = pci_iomap(pdev, bar, 0); - - if (!vdev->barmap[bar]) { - pci_release_selected_regions(pdev, 1 << bar); - return -EINVAL; - } - } - - io = vdev->barmap[bar]; - - if (bar == vdev->msix_bar) { - x_start = vdev->msix_offset; - x_end = vdev->msix_offset + vdev->msix_size; - } - } - - if (!io) - return -EINVAL; + ssize_t done = 0; while (count) { size_t fillable, filled; - if (pos < x_start) - fillable = x_start - pos; - else if (pos >= x_end) - fillable = end - pos; + if (off < x_start) + fillable = min(count, (size_t)(x_start - off)); + else if (off >= x_end) + fillable = count; else fillable = 0; - if (fillable >= 4 && !(pos % 4) && (count >= 4)) { + if (fillable >= 4 && !(off % 4)) { __le32 val; if (iswrite) { if (copy_from_user(&val, buf, 4)) - goto out; + return -EFAULT; - iowrite32(le32_to_cpu(val), io + pos); + iowrite32(le32_to_cpu(val), io + off); } else { - val = cpu_to_le32(ioread32(io + pos)); + val = cpu_to_le32(ioread32(io + off)); if (copy_to_user(buf, &val, 4)) - goto out; + return -EFAULT; } filled = 4; - } else if (fillable >= 2 && !(pos % 2) && (count >= 2)) { + } else if (fillable >= 2 && !(off % 2)) { __le16 val; if (iswrite) { if (copy_from_user(&val, buf, 2)) - goto out; + return -EFAULT; - iowrite16(le16_to_cpu(val), io + pos); + iowrite16(le16_to_cpu(val), io + off); } else { - val = cpu_to_le16(ioread16(io + pos)); + val = cpu_to_le16(ioread16(io + off)); if (copy_to_user(buf, &val, 2)) - goto out; + return -EFAULT; } filled = 2; @@ -227,43 +79,99 @@ ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf, if (iswrite) { if (copy_from_user(&val, buf, 1)) - goto out; + return -EFAULT; - iowrite8(val, io + pos); + iowrite8(val, io + off); } else { - val = ioread8(io + pos); + val = ioread8(io + off); if (copy_to_user(buf, &val, 1)) - goto out; + return -EFAULT; } filled = 1; } else { - /* Drop writes, fill reads with FF */ - filled = min((size_t)(x_end - pos), count); + /* Fill reads with -1, drop writes */ + filled = min(count, (size_t)(x_end - off)); if (!iswrite) { - char val = 0xFF; + u8 val = 0xFF; size_t i; - for (i = 0; i < filled; i++) { - if (put_user(val, buf + i)) - goto out; - } + for (i = 0; i < filled; i++) + if (copy_to_user(buf + i, &val, 1)) + return -EFAULT; } - } count -= filled; done += filled; + off += filled; buf += filled; - pos += filled; } - *ppos += done; + return done; +} + +ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) +{ + struct pci_dev *pdev = vdev->pdev; + loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK; + int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos); + size_t x_start = 0, x_end = 0; + resource_size_t end; + void __iomem *io; + ssize_t done; + + if (!pci_resource_start(pdev, bar)) + return -EINVAL; + + end = pci_resource_len(pdev, bar); + + if (pos >= end) + return -EINVAL; + + count = min(count, (size_t)(end - pos)); + + if (bar == PCI_ROM_RESOURCE) { + /* + * The ROM can fill less space than the BAR, so we start the + * excluded range at the end of the actual ROM. This makes + * filling large ROM BARs much faster. + */ + io = pci_map_rom(pdev, &x_start); + if (!io) + return -ENOMEM; + x_end = end; + } else if (!vdev->barmap[bar]) { + int ret; + + ret = pci_request_selected_regions(pdev, 1 << bar, "vfio"); + if (ret) + return ret; + + io = pci_iomap(pdev, bar, 0); + if (!io) { + pci_release_selected_regions(pdev, 1 << bar); + return -ENOMEM; + } + + vdev->barmap[bar] = io; + } else + io = vdev->barmap[bar]; + + if (bar == vdev->msix_bar) { + x_start = vdev->msix_offset; + x_end = vdev->msix_offset + vdev->msix_size; + } + + done = do_io_rw(io, buf, pos, count, x_start, x_end, iswrite); + + if (done >= 0) + *ppos += done; -out: if (bar == PCI_ROM_RESOURCE) pci_unmap_rom(pdev, io); - return count ? -EFAULT : done; + return done; } From e014e9444aedc365742d533e1443b22470cc67b9 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 14:02:13 -0700 Subject: [PATCH 2741/4607] vfio: Protect vfio_dev_present against device_del vfio_dev_present is meant to give us a wait_event callback so that we can block removing a device from vfio until it becomes unused. The root of this check depends on being able to get the iommu group from the device. Unfortunately if the BUS_NOTIFY_DEL_DEVICE notifier has fired then the device-group reference is no longer searchable and we fail the lookup. We don't need to go to such extents for this though. We have a reference to the device, from which we can acquire a reference to the group. We can then use the group reference to search for the device and properly block removal. Signed-off-by: Alex Williamson --- drivers/vfio/vfio.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 12c264d3b058..8e6dcecbc407 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -642,33 +642,16 @@ int vfio_add_group_dev(struct device *dev, } EXPORT_SYMBOL_GPL(vfio_add_group_dev); -/* Test whether a struct device is present in our tracking */ -static bool vfio_dev_present(struct device *dev) +/* Given a referenced group, check if it contains the device */ +static bool vfio_dev_present(struct vfio_group *group, struct device *dev) { - struct iommu_group *iommu_group; - struct vfio_group *group; struct vfio_device *device; - iommu_group = iommu_group_get(dev); - if (!iommu_group) - return false; - - group = vfio_group_get_from_iommu(iommu_group); - if (!group) { - iommu_group_put(iommu_group); - return false; - } - device = vfio_group_get_device(group, dev); - if (!device) { - vfio_group_put(group); - iommu_group_put(iommu_group); + if (!device) return false; - } vfio_device_put(device); - vfio_group_put(group); - iommu_group_put(iommu_group); return true; } @@ -682,10 +665,18 @@ void *vfio_del_group_dev(struct device *dev) struct iommu_group *iommu_group = group->iommu_group; void *device_data = device->device_data; + /* + * The group exists so long as we have a device reference. Get + * a group reference and use it to scan for the device going away. + */ + vfio_group_get(group); + vfio_device_put(device); /* TODO send a signal to encourage this to be released */ - wait_event(vfio.release_q, !vfio_dev_present(dev)); + wait_event(vfio.release_q, !vfio_dev_present(group, dev)); + + vfio_group_put(group); iommu_group_put(iommu_group); From 2b489a45f63102205cece37057c21f6fa66f6ce4 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 14:02:13 -0700 Subject: [PATCH 2742/4607] vfio: whitelist pcieport pcieport does nice things like manage AER and we know it doesn't do DMA or expose any user accessible devices on the host. It also keeps the Memory, I/O, and Busmaster bits enabled, which is pretty handy when trying to use anyting below it. Devices owned by pcieport cannot be given to users via vfio, but we can tolerate them not being owned by vfio-pci. Signed-off-by: Alex Williamson --- drivers/vfio/vfio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 8e6dcecbc407..28e2d5b2c0c7 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -442,7 +442,7 @@ static struct vfio_device *vfio_group_get_device(struct vfio_group *group, * a device. It's not always practical to leave a device within a group * driverless as it could get re-bound to something unsafe. */ -static const char * const vfio_driver_whitelist[] = { "pci-stub" }; +static const char * const vfio_driver_whitelist[] = { "pci-stub", "pcieport" }; static bool vfio_whitelisted_driver(struct device_driver *drv) { From 95c9608478d639dcffc14ea47b31bff021a99ed1 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 14 Feb 2013 14:02:52 -0800 Subject: [PATCH 2743/4607] x86, mm: Move reserving low memory later in initialization Move the reservation of low memory, except for the 4K which actually does belong to the BIOS, later in the initialization; in particular, after we have already reserved the trampoline. The current code locates the trampoline as high as possible, so by deferring the allocation we will still be able to reserve as much memory as is possible. This allows us to run with reservelow=640k without getting a crash on system startup. Signed-off-by: H. Peter Anvin Link: http://lkml.kernel.org/n/tip-0y9dqmmsousf69wutxwl3kkf@git.kernel.org --- arch/x86/kernel/setup.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 8354399b3aae..0aebd776018e 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -608,8 +608,6 @@ static __init void reserve_ibft_region(void) memblock_reserve(addr, size); } -static unsigned reserve_low = CONFIG_X86_RESERVE_LOW << 10; - static bool __init snb_gfx_workaround_needed(void) { #ifdef CONFIG_PCI @@ -698,8 +696,7 @@ static void __init trim_bios_range(void) * since some BIOSes are known to corrupt low memory. See the * Kconfig help text for X86_RESERVE_LOW. */ - e820_update_range(0, ALIGN(reserve_low, PAGE_SIZE), - E820_RAM, E820_RESERVED); + e820_update_range(0, PAGE_SIZE, E820_RAM, E820_RESERVED); /* * special case: Some BIOSen report the PC BIOS @@ -711,6 +708,8 @@ static void __init trim_bios_range(void) sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); } +static unsigned reserve_low = CONFIG_X86_RESERVE_LOW << 10; + static int __init parse_reservelow(char *p) { unsigned long long size; @@ -733,6 +732,11 @@ static int __init parse_reservelow(char *p) early_param("reservelow", parse_reservelow); +static void __init trim_low_memory_range(void) +{ + memblock_reserve(0, ALIGN(reserve_low, PAGE_SIZE)); +} + /* * Determine if we were loaded by an EFI loader. If so, then we have also been * passed the efi memmap, systab, etc., so we should use these data structures @@ -987,6 +991,7 @@ void __init setup_arch(char **cmdline_p) setup_real_mode(); trim_platform_memory_ranges(); + trim_low_memory_range(); init_gbpages(); From 55abf8df0aa080eb474f7f46337503351890b361 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:50 +0000 Subject: [PATCH 2744/4607] RDMA/cxgb4: Abort connections that receive unexpected streaming mode data This error means the RDMA connection was knocked out of RDMA mode, probably due to an error on the connection. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index c13745cde7fa..9cab6a6eb96a 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1391,30 +1391,31 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb) skb_pull(skb, sizeof(*hdr)); skb_trim(skb, dlen); - ep->rcv_seq += dlen; - BUG_ON(ep->rcv_seq != (ntohl(hdr->seq) + dlen)); - /* update RX credits */ update_rx_credits(ep, dlen); switch (state_read(&ep->com)) { case MPA_REQ_SENT: + ep->rcv_seq += dlen; process_mpa_reply(ep, skb); break; case MPA_REQ_WAIT: + ep->rcv_seq += dlen; process_mpa_request(ep, skb); break; - case MPA_REP_SENT: - break; default: pr_err("%s Unexpected streaming data." \ " ep %p state %d tid %u status %d\n", __func__, ep, state_read(&ep->com), ep->hwtid, status); - /* - * The ep will timeout and inform the ULP of the failure. - * See ep_timeout(). - */ + if (ep->com.qp) { + struct c4iw_qp_attributes attrs; + + attrs.next_state = C4IW_QP_STATE_ERROR; + c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); + } + c4iw_ep_disconnect(ep, 1, GFP_KERNEL); break; } return 0; From 91e9c07195032bbde47489b8b423053cff5f413d Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:51 +0000 Subject: [PATCH 2745/4607] RDMA/cxgb4: Abort connections when moving to ERROR state If a FINI operation fails, then we need to ABORT instead of CLOSE. Also, if we ABORT due to unexpected STREAMING data, then wake up anybody blocked in FINI... Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 1 + drivers/infiniband/hw/cxgb4/qp.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 9cab6a6eb96a..3dce47a63709 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1438,6 +1438,7 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb) mutex_lock(&ep->com.mutex); switch (ep->com.state) { case ABORTING: + c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET); __state_set(&ep->com, DEAD); release = 1; break; diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index 05bfe53bff64..17ba4f8bc12d 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -1383,6 +1383,7 @@ err: qhp->ep = NULL; set_state(qhp, C4IW_QP_STATE_ERROR); free = 1; + abort = 1; wake_up(&qhp->wait); BUG_ON(!ep); flush_qp(qhp); From 1557967bf921e787f0c9236c2899603d85f44d31 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:52 +0000 Subject: [PATCH 2746/4607] RDMA/cxgb4: Display streaming mode error only if detected in RTS With later firmware, the chances of getting streaming mode data after we exit RTS is likely, so we don't need to warn for it. The only real case where we don't expect it is when the QP is in RTS. Move QP to ERROR when streaming mode data received. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 3dce47a63709..31d1fac605d3 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1403,21 +1403,23 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb) ep->rcv_seq += dlen; process_mpa_request(ep, skb); break; - default: - pr_err("%s Unexpected streaming data." \ - " ep %p state %d tid %u status %d\n", - __func__, ep, state_read(&ep->com), ep->hwtid, status); - - if (ep->com.qp) { - struct c4iw_qp_attributes attrs; - - attrs.next_state = C4IW_QP_STATE_ERROR; - c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, - C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); - } + case FPDU_MODE: { + struct c4iw_qp_attributes attrs; + BUG_ON(!ep->com.qp); + if (ep->com.qp->attr.state == C4IW_QP_STATE_RTS) + pr_err("%s Unexpected streaming data." \ + " ep %p state %d tid %u status %d\n", + __func__, ep, state_read(&ep->com), + ep->hwtid, status); + attrs.next_state = C4IW_QP_STATE_ERROR; + c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, + C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); c4iw_ep_disconnect(ep, 1, GFP_KERNEL); break; } + default: + break; + } return 0; } From 325abead6cc7ef50572c53b1adc4d2442234b50f Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:53 +0000 Subject: [PATCH 2747/4607] RDMA/cxgb4: Keep QP referenced until TID released The driver is currently releasing the last ref on the QP too early. This can cause bus errors due to HW still fetching WRs from the HW queue. The fix is to keep a qp ref until we release the HW TID. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 20 ++++++++++++++++---- drivers/infiniband/hw/cxgb4/iw_cxgb4.h | 1 + 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 31d1fac605d3..ebcdb3ff0cf4 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -143,6 +143,18 @@ static void connect_reply_upcall(struct c4iw_ep *ep, int status); static LIST_HEAD(timeout_list); static spinlock_t timeout_lock; +static void deref_qp(struct c4iw_ep *ep) +{ + c4iw_qp_rem_ref(&ep->com.qp->ibqp); + clear_bit(QP_REFERENCED, &ep->com.flags); +} + +static void ref_qp(struct c4iw_ep *ep) +{ + set_bit(QP_REFERENCED, &ep->com.flags); + c4iw_qp_add_ref(&ep->com.qp->ibqp); +} + static void start_ep_timer(struct c4iw_ep *ep) { PDBG("%s ep %p\n", __func__, ep); @@ -271,6 +283,8 @@ void _c4iw_free_ep(struct kref *kref) ep = container_of(kref, struct c4iw_ep, com.kref); PDBG("%s ep %p state %s\n", __func__, ep, states[state_read(&ep->com)]); + if (test_bit(QP_REFERENCED, &ep->com.flags)) + deref_qp(ep); if (test_bit(RELEASE_RESOURCES, &ep->com.flags)) { cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, ep->hwtid); dst_release(ep->dst); @@ -863,7 +877,6 @@ static void close_complete_upcall(struct c4iw_ep *ep) ep->com.cm_id->event_handler(ep->com.cm_id, &event); ep->com.cm_id->rem_ref(ep->com.cm_id); ep->com.cm_id = NULL; - ep->com.qp = NULL; set_bit(CLOSE_UPCALL, &ep->com.history); } } @@ -906,7 +919,6 @@ static void peer_abort_upcall(struct c4iw_ep *ep) ep->com.cm_id->event_handler(ep->com.cm_id, &event); ep->com.cm_id->rem_ref(ep->com.cm_id); ep->com.cm_id = NULL; - ep->com.qp = NULL; set_bit(ABORT_UPCALL, &ep->com.history); } } @@ -946,7 +958,6 @@ static void connect_reply_upcall(struct c4iw_ep *ep, int status) if (status < 0) { ep->com.cm_id->rem_ref(ep->com.cm_id); ep->com.cm_id = NULL; - ep->com.qp = NULL; } } @@ -2434,6 +2445,7 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) cm_id->add_ref(cm_id); ep->com.cm_id = cm_id; ep->com.qp = qp; + ref_qp(ep); /* bind QP to EP and move to RTS */ attrs.mpa_attr = ep->mpa_attr; @@ -2464,7 +2476,6 @@ int c4iw_accept_cr(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) return 0; err1: ep->com.cm_id = NULL; - ep->com.qp = NULL; cm_id->rem_ref(cm_id); err: c4iw_put_ep(&ep->com); @@ -2505,6 +2516,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) ep->com.cm_id = cm_id; ep->com.qp = get_qhp(dev, conn_param->qpn); BUG_ON(!ep->com.qp); + ref_qp(ep); PDBG("%s qpn 0x%x qp %p cm_id %p\n", __func__, conn_param->qpn, ep->com.qp, cm_id); diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h index 9c1644fb0259..0aaaa0e81f29 100644 --- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h +++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h @@ -716,6 +716,7 @@ enum c4iw_ep_flags { ABORT_REQ_IN_PROGRESS = 1, RELEASE_RESOURCES = 2, CLOSE_SENT = 3, + QP_REFERENCED = 5, }; enum c4iw_ep_history { From 04236df2a5b1e9881c90f2d1f63fbee3659297a7 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:54 +0000 Subject: [PATCH 2748/4607] RDMA/cxgb4: Always log async errors Log AEs even if the QP isn't in RTS. It is useful information. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 6 +++--- drivers/infiniband/hw/cxgb4/ev.c | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index ebcdb3ff0cf4..5989991e31a1 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1419,9 +1419,9 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb) BUG_ON(!ep->com.qp); if (ep->com.qp->attr.state == C4IW_QP_STATE_RTS) pr_err("%s Unexpected streaming data." \ - " ep %p state %d tid %u status %d\n", - __func__, ep, state_read(&ep->com), - ep->hwtid, status); + " qpid %u ep %p state %d tid %u status %d\n", + __func__, ep->com.qp->wq.sq.qid, ep, + state_read(&ep->com), ep->hwtid, status); attrs.next_state = C4IW_QP_STATE_ERROR; c4iw_modify_qp(ep->com.qp->rhp, ep->com.qp, C4IW_QP_ATTR_NEXT_STATE, &attrs, 1); diff --git a/drivers/infiniband/hw/cxgb4/ev.c b/drivers/infiniband/hw/cxgb4/ev.c index cf2f6b47617a..1a840b2211dd 100644 --- a/drivers/infiniband/hw/cxgb4/ev.c +++ b/drivers/infiniband/hw/cxgb4/ev.c @@ -46,9 +46,11 @@ static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp, if ((qhp->attr.state == C4IW_QP_STATE_ERROR) || (qhp->attr.state == C4IW_QP_STATE_TERMINATE)) { - PDBG("%s AE received after RTS - " - "qp state %d qpid 0x%x status 0x%x\n", __func__, - qhp->attr.state, qhp->wq.sq.qid, CQE_STATUS(err_cqe)); + pr_err("%s AE after RTS - qpid 0x%x opcode %d status 0x%x "\ + "type %d wrid.hi 0x%x wrid.lo 0x%x\n", + __func__, CQE_QPID(err_cqe), CQE_OPCODE(err_cqe), + CQE_STATUS(err_cqe), CQE_TYPE(err_cqe), + CQE_WRID_HI(err_cqe), CQE_WRID_LOW(err_cqe)); return; } From e8e5b9278ba0502ada73b8b94b8498cc19def743 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:55 +0000 Subject: [PATCH 2749/4607] RDMA/cxgb4: Only log rx_data warnings if cpl status is non-zero With newer firmware, we can get streaming data due to connection errors before the driver moves the QP out of RTS. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 5989991e31a1..51ceb618beb2 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1417,7 +1417,7 @@ static int rx_data(struct c4iw_dev *dev, struct sk_buff *skb) case FPDU_MODE: { struct c4iw_qp_attributes attrs; BUG_ON(!ep->com.qp); - if (ep->com.qp->attr.state == C4IW_QP_STATE_RTS) + if (status) pr_err("%s Unexpected streaming data." \ " qpid %u ep %p state %d tid %u status %d\n", __func__, ep->com.qp->wq.sq.qid, ep, From 1ec779cc29238e6f4d315bff53cd36165819bfd5 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:56 +0000 Subject: [PATCH 2750/4607] RDMA/cxgb4: Fix endpoint timeout race condition The endpoint timeout logic had a race that could cause an endpoint object to be freed while it was still on the timedout list. This can happen if the timer is stopped after it had fired, but before the timedout thread processed the endpoint timeout. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 29 ++++++++++++++------------ drivers/infiniband/hw/cxgb4/iw_cxgb4.h | 1 + 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 51ceb618beb2..ab5b4dd39dec 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -159,10 +159,12 @@ static void start_ep_timer(struct c4iw_ep *ep) { PDBG("%s ep %p\n", __func__, ep); if (timer_pending(&ep->timer)) { - PDBG("%s stopped / restarted timer ep %p\n", __func__, ep); - del_timer_sync(&ep->timer); - } else - c4iw_get_ep(&ep->com); + pr_err("%s timer already started! ep %p\n", + __func__, ep); + return; + } + clear_bit(TIMEOUT, &ep->com.flags); + c4iw_get_ep(&ep->com); ep->timer.expires = jiffies + ep_timeout_secs * HZ; ep->timer.data = (unsigned long)ep; ep->timer.function = ep_timeout; @@ -171,14 +173,10 @@ static void start_ep_timer(struct c4iw_ep *ep) static void stop_ep_timer(struct c4iw_ep *ep) { - PDBG("%s ep %p\n", __func__, ep); - if (!timer_pending(&ep->timer)) { - WARN(1, "%s timer stopped when its not running! " - "ep %p state %u\n", __func__, ep, ep->com.state); - return; - } + PDBG("%s ep %p stopping\n", __func__, ep); del_timer_sync(&ep->timer); - c4iw_put_ep(&ep->com); + if (!test_and_set_bit(TIMEOUT, &ep->com.flags)) + c4iw_put_ep(&ep->com); } static int c4iw_l2t_send(struct c4iw_rdev *rdev, struct sk_buff *skb, @@ -3191,11 +3189,16 @@ static DECLARE_WORK(skb_work, process_work); static void ep_timeout(unsigned long arg) { struct c4iw_ep *ep = (struct c4iw_ep *)arg; + int kickit = 0; spin_lock(&timeout_lock); - list_add_tail(&ep->entry, &timeout_list); + if (!test_and_set_bit(TIMEOUT, &ep->com.flags)) { + list_add_tail(&ep->entry, &timeout_list); + kickit = 1; + } spin_unlock(&timeout_lock); - queue_work(workq, &skb_work); + if (kickit) + queue_work(workq, &skb_work); } /* diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h index 0aaaa0e81f29..94a3b3c47a8a 100644 --- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h +++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h @@ -716,6 +716,7 @@ enum c4iw_ep_flags { ABORT_REQ_IN_PROGRESS = 1, RELEASE_RESOURCES = 2, CLOSE_SENT = 3, + TIMEOUT = 4, QP_REFERENCED = 5, }; From fe7e0a4dd0304745b57e08827fde13e1c2376e66 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:57 +0000 Subject: [PATCH 2751/4607] RDMA/cxgb4: Don't reconnect on abort for mpa_rev 1 Only reconnect if the endpoint wasn't freed. peer_abort() should only attempt to reconnect if the endpoint wasn't freed. Also remove hwtid from the debugfs idr. Add missing check for peer2peer in MPAv2 code Use correct mpa version on reject. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index ab5b4dd39dec..88933af05c5c 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -284,10 +284,10 @@ void _c4iw_free_ep(struct kref *kref) if (test_bit(QP_REFERENCED, &ep->com.flags)) deref_qp(ep); if (test_bit(RELEASE_RESOURCES, &ep->com.flags)) { + remove_handle(ep->com.dev, &ep->com.dev->hwtid_idr, ep->hwtid); cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, ep->hwtid); dst_release(ep->dst); cxgb4_l2t_release(ep->l2t); - remove_handle(ep->com.dev, &ep->com.dev->hwtid_idr, ep->hwtid); } kfree(ep); } @@ -699,7 +699,7 @@ static int send_mpa_reject(struct c4iw_ep *ep, const void *pdata, u8 plen) memset(mpa, 0, sizeof(*mpa)); memcpy(mpa->key, MPA_KEY_REP, sizeof(mpa->key)); mpa->flags = MPA_REJECT; - mpa->revision = mpa_rev; + mpa->revision = ep->mpa_attr.version; mpa->private_data_size = htons(plen); if (ep->mpa_attr.version == 2 && ep->mpa_attr.enhanced_rdma_conn) { @@ -2176,7 +2176,7 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) break; case MPA_REQ_SENT: stop_ep_timer(ep); - if (mpa_rev == 2 && ep->tried_with_mpa_v1) + if (mpa_rev == 1 || (mpa_rev == 2 && ep->tried_with_mpa_v1)) connect_reply_upcall(ep, -ECONNRESET); else { /* @@ -2248,9 +2248,8 @@ static int peer_abort(struct c4iw_dev *dev, struct sk_buff *skb) out: if (release) release_ep_resources(ep); - - /* retry with mpa-v1 */ - if (ep && ep->retry_with_mpa_v1) { + else if (ep->retry_with_mpa_v1) { + remove_handle(ep->com.dev, &ep->com.dev->hwtid_idr, ep->hwtid); cxgb4_remove_tid(ep->com.dev->rdev.lldi.tids, 0, ep->hwtid); dst_release(ep->dst); cxgb4_l2t_release(ep->l2t); From 7c0a33d61187a413f29f63d106b503b9c91680e8 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:58 +0000 Subject: [PATCH 2752/4607] RDMA/cxgb4: Don't wakeup threads for MPAv2 Don't wakeup threads blocked in rdma_init/rdma_fini if we are on MPAv2, and want to retry connection with MPAv1. Stop ep-timer on getting MPA version mismatch, before doing the abort_connection - in process_mpa_request. Take care to stop ep-timer in error paths for process_mpa_request. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 88933af05c5c..06c3a527d6f8 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1300,11 +1300,13 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb) if (mpa->revision > mpa_rev) { printk(KERN_ERR MOD "%s MPA version mismatch. Local = %d," " Received = %d\n", __func__, mpa_rev, mpa->revision); + stop_ep_timer(ep); abort_connection(ep, skb, GFP_KERNEL); return; } if (memcmp(mpa->key, MPA_KEY_REQ, sizeof(mpa->key))) { + stop_ep_timer(ep); abort_connection(ep, skb, GFP_KERNEL); return; } @@ -1315,6 +1317,7 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb) * Fail if there's too much private data. */ if (plen > MPA_MAX_PRIVATE_DATA) { + stop_ep_timer(ep); abort_connection(ep, skb, GFP_KERNEL); return; } @@ -1323,6 +1326,7 @@ static void process_mpa_request(struct c4iw_ep *ep, struct sk_buff *skb) * If plen does not account for pkt size */ if (ep->mpa_pkt_len > (sizeof(*mpa) + plen)) { + stop_ep_timer(ep); abort_connection(ep, skb, GFP_KERNEL); return; } @@ -3286,8 +3290,14 @@ static int peer_abort_intr(struct c4iw_dev *dev, struct sk_buff *skb) /* * Wake up any threads in rdma_init() or rdma_fini(). + * However, if we are on MPAv2 and want to retry with MPAv1 + * then, don't wake up yet. */ - c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET); + if (mpa_rev == 2 && !ep->tried_with_mpa_v1) { + if (ep->com.state != MPA_REQ_SENT) + c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET); + } else + c4iw_wake_up(&ep->com.wr_wait, -ECONNRESET); sched(dev, skb); return 0; } From b3de6cfebc6167761c40947f05f4c817531f37d5 Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:11:59 +0000 Subject: [PATCH 2753/4607] RDMA/cxgb4: Insert hwtid in pass_accept_req instead in pass_establish CPL_ABORT_REQ_RSS can come before TCP connection is established. In such case peer_abort was trying to remove the hwtid, which was not inserted. To avoid this we insert the hwtid when we are sure that we are surely going to send passive accept request. Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 06c3a527d6f8..37ea2fcf3b10 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -2010,6 +2010,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) init_timer(&child_ep->timer); cxgb4_insert_tid(t, child_ep, hwtid); + insert_handle(dev, &dev->hwtid_idr, child_ep, child_ep->hwtid); accept_cr(child_ep, peer_ip, skb, req); set_bit(PASS_ACCEPT_REQ, &child_ep->com.history); goto out; @@ -2035,7 +2036,6 @@ static int pass_establish(struct c4iw_dev *dev, struct sk_buff *skb) ntohs(req->tcp_opt)); set_emss(ep, ntohs(req->tcp_opt)); - insert_handle(dev, &dev->hwtid_idr, ep, ep->hwtid); dst_confirm(ep->dst); state_set(&ep->com, MPA_REQ_WAIT); From ef5d6355ed4bcf574e8473c3ce667cbf6c66a0ee Mon Sep 17 00:00:00 2001 From: Vipul Pandya Date: Mon, 7 Jan 2013 13:12:00 +0000 Subject: [PATCH 2754/4607] RDMA/cxgb4: Address sparse warnings Fixe the following types of sparse warnings - cast to pointer from integer of different size - cast from pointer to integer of different size - incorrect type in assignment (different base types) - incorrect type in argument 1 (different base types) - cast from restricted __be64 - cast from restricted __be32 Signed-off-by: Vipul Pandya Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 68 ++++++++++++++++------------ drivers/infiniband/hw/cxgb4/device.c | 3 +- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 37ea2fcf3b10..dfca515cc933 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1492,11 +1492,11 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid) V_FW_OFLD_CONNECTION_WR_ASTID(atid)); req->tcb.cplrxdataack_cplpassacceptrpl = htons(F_FW_OFLD_CONNECTION_WR_CPLRXDATAACK); - req->tcb.tx_max = jiffies; + req->tcb.tx_max = (__force __be32) jiffies; req->tcb.rcv_adv = htons(1); cxgb4_best_mtu(ep->com.dev->rdev.lldi.mtus, ep->mtu, &mtu_idx); wscale = compute_wscale(rcv_win); - req->tcb.opt0 = TCAM_BYPASS(1) | + req->tcb.opt0 = (__force __be64) (TCAM_BYPASS(1) | (nocong ? NO_CONG(1) : 0) | KEEP_ALIVE(1) | DELACK(1) | @@ -1507,20 +1507,20 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid) SMAC_SEL(ep->smac_idx) | DSCP(ep->tos) | ULP_MODE(ULP_MODE_TCPDDP) | - RCV_BUFSIZ(rcv_win >> 10); - req->tcb.opt2 = PACE(1) | + RCV_BUFSIZ(rcv_win >> 10)); + req->tcb.opt2 = (__force __be32) (PACE(1) | TX_QUEUE(ep->com.dev->rdev.lldi.tx_modq[ep->tx_chan]) | RX_CHANNEL(0) | CCTRL_ECN(enable_ecn) | - RSS_QUEUE_VALID | RSS_QUEUE(ep->rss_qid); + RSS_QUEUE_VALID | RSS_QUEUE(ep->rss_qid)); if (enable_tcp_timestamps) - req->tcb.opt2 |= TSTAMPS_EN(1); + req->tcb.opt2 |= (__force __be32) TSTAMPS_EN(1); if (enable_tcp_sack) - req->tcb.opt2 |= SACK_EN(1); + req->tcb.opt2 |= (__force __be32) SACK_EN(1); if (wscale && enable_tcp_window_scaling) - req->tcb.opt2 |= WND_SCALE_EN(1); - req->tcb.opt0 = cpu_to_be64(req->tcb.opt0); - req->tcb.opt2 = cpu_to_be32(req->tcb.opt2); + req->tcb.opt2 |= (__force __be32) WND_SCALE_EN(1); + req->tcb.opt0 = cpu_to_be64((__force u64) req->tcb.opt0); + req->tcb.opt2 = cpu_to_be32((__force u32) req->tcb.opt2); set_wr_txq(skb, CPL_PRIORITY_CONTROL, ep->ctrlq_idx); set_bit(ACT_OFLD_CONN, &ep->com.history); c4iw_l2t_send(&ep->com.dev->rdev, skb, ep->l2t); @@ -2773,7 +2773,8 @@ static void active_ofld_conn_reply(struct c4iw_dev *dev, struct sk_buff *skb, struct c4iw_ep *ep; int atid = be32_to_cpu(req->tid); - ep = (struct c4iw_ep *)lookup_atid(dev->rdev.lldi.tids, req->tid); + ep = (struct c4iw_ep *)lookup_atid(dev->rdev.lldi.tids, + (__force u32) req->tid); if (!ep) return; @@ -2817,7 +2818,7 @@ static void passive_ofld_conn_reply(struct c4iw_dev *dev, struct sk_buff *skb, struct cpl_pass_accept_req *cpl; int ret; - rpl_skb = (struct sk_buff *)cpu_to_be64(req->cookie); + rpl_skb = (__force struct sk_buff *)cpu_to_be64(req->cookie); BUG_ON(!rpl_skb); if (req->retval) { PDBG("%s passive open failure %d\n", __func__, req->retval); @@ -2828,7 +2829,8 @@ static void passive_ofld_conn_reply(struct c4iw_dev *dev, struct sk_buff *skb, } else { cpl = (struct cpl_pass_accept_req *)cplhdr(rpl_skb); OPCODE_TID(cpl) = htonl(MK_OPCODE_TID(CPL_PASS_ACCEPT_REQ, - htonl(req->tid))); + (__force u32) htonl( + (__force u32) req->tid))); ret = pass_accept_req(dev, rpl_skb); if (!ret) kfree_skb(rpl_skb); @@ -2874,10 +2876,10 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos) struct tcp_options_received tmp_opt; /* Store values from cpl_rx_pkt in temporary location. */ - vlantag = cpl->vlan; - len = cpl->len; - l2info = cpl->l2info; - hdr_len = cpl->hdr_len; + vlantag = (__force u16) cpl->vlan; + len = (__force u16) cpl->len; + l2info = (__force u32) cpl->l2info; + hdr_len = (__force u16) cpl->hdr_len; intf = cpl->iff; __skb_pull(skb, sizeof(*req) + sizeof(struct rss_header)); @@ -2888,19 +2890,24 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos) */ memset(&tmp_opt, 0, sizeof(tmp_opt)); tcp_clear_options(&tmp_opt); - tcp_parse_options(skb, &tmp_opt, 0, 0, NULL); + tcp_parse_options(skb, &tmp_opt, NULL, 0, NULL); req = (struct cpl_pass_accept_req *)__skb_push(skb, sizeof(*req)); memset(req, 0, sizeof(*req)); req->l2info = cpu_to_be16(V_SYN_INTF(intf) | - V_SYN_MAC_IDX(G_RX_MACIDX(htonl(l2info))) | + V_SYN_MAC_IDX(G_RX_MACIDX( + (__force int) htonl(l2info))) | F_SYN_XACT_MATCH); - req->hdr_len = cpu_to_be32(V_SYN_RX_CHAN(G_RX_CHAN(htonl(l2info))) | - V_TCP_HDR_LEN(G_RX_TCPHDR_LEN(htons(hdr_len))) | - V_IP_HDR_LEN(G_RX_IPHDR_LEN(htons(hdr_len))) | - V_ETH_HDR_LEN(G_RX_ETHHDR_LEN(htonl(l2info)))); - req->vlan = vlantag; - req->len = len; + req->hdr_len = cpu_to_be32(V_SYN_RX_CHAN(G_RX_CHAN( + (__force int) htonl(l2info))) | + V_TCP_HDR_LEN(G_RX_TCPHDR_LEN( + (__force int) htons(hdr_len))) | + V_IP_HDR_LEN(G_RX_IPHDR_LEN( + (__force int) htons(hdr_len))) | + V_ETH_HDR_LEN(G_RX_ETHHDR_LEN( + (__force int) htonl(l2info)))); + req->vlan = (__force __be16) vlantag; + req->len = (__force __be16) len; req->tos_stid = cpu_to_be32(PASS_OPEN_TID(stid) | PASS_OPEN_TOS(tos)); req->tcpopt.mss = htons(tmp_opt.mss_clamp); @@ -2929,7 +2936,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb, req->op_compl = htonl(V_WR_OP(FW_OFLD_CONNECTION_WR) | FW_WR_COMPL(1)); req->len16_pkd = htonl(FW_WR_LEN16(DIV_ROUND_UP(sizeof(*req), 16))); req->le.version_cpl = htonl(F_FW_OFLD_CONNECTION_WR_CPL); - req->le.filter = filter; + req->le.filter = (__force __be32) filter; req->le.lport = lport; req->le.pport = rport; req->le.u.ipv4.lip = laddr; @@ -2955,7 +2962,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb, * TP will ignore any value > 0 for MSS index. */ req->tcb.opt0 = cpu_to_be64(V_MSS_IDX(0xF)); - req->cookie = cpu_to_be64((u64)skb); + req->cookie = (__force __u64) cpu_to_be64((u64)skb); set_wr_txq(req_skb, CPL_PRIORITY_CONTROL, port_id); cxgb4_ofld_send(dev->rdev.lldi.ports[0], req_skb); @@ -3005,7 +3012,8 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb) /* * Calculate the server tid from filter hit index from cpl_rx_pkt. */ - stid = cpu_to_be32(rss->hash_val) - dev->rdev.lldi.tids->sftid_base + stid = (__force int) cpu_to_be32((__force u32) rss->hash_val) + - dev->rdev.lldi.tids->sftid_base + dev->rdev.lldi.tids->nstids; lep = (struct c4iw_ep *)lookup_stid(dev->rdev.lldi.tids, stid); @@ -3066,10 +3074,10 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb) step = dev->rdev.lldi.nrxq / dev->rdev.lldi.nchan; rss_qid = dev->rdev.lldi.rxq_ids[pi->port_id * step]; - window = htons(tcph->window); + window = (__force u16) htons((__force u16)tcph->window); /* Calcuate filter portion for LE region. */ - filter = cpu_to_be32(select_ntuple(dev, dst, e)); + filter = (__force unsigned int) cpu_to_be32(select_ntuple(dev, dst, e)); /* * Synthesize the cpl_pass_accept_req. We have everything except the diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index ba11c76c0b5a..dc6adb213cd6 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -797,7 +797,8 @@ static int c4iw_uld_rx_handler(void *handle, const __be64 *rsp, "RSS %#llx, FL %#llx, len %u\n", pci_name(ctx->lldi.pdev), gl->va, (unsigned long long)be64_to_cpu(*rsp), - (unsigned long long)be64_to_cpu(*(u64 *)gl->va), + (unsigned long long)be64_to_cpu( + *(__force __be64 *)gl->va), gl->tot_len); return 0; From 710a31102be46ffc2f087119dca19c894dc237eb Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Tue, 5 Feb 2013 20:51:30 +0000 Subject: [PATCH 2755/4607] RDMA/cxgb4: "cookie" can stay in host endianness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Work requests are passed between the host and the firmware with a "cookie". This cookie is swapped to big-endian when passed to the firmware and back to host endianness on return. This swapping seems to be implemented incorrectly. Moreover, the byte swapping triggers GCC warnings on 32 bit: drivers/infiniband/hw/cxgb4/cm.c: In function ‘passive_ofld_conn_reply’: drivers/infiniband/hw/cxgb4/cm.c:2803:12: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] drivers/infiniband/hw/cxgb4/cm.c: In function ‘send_fw_pass_open_req’: drivers/infiniband/hw/cxgb4/cm.c:2941:16: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] [...] But byte swapping isn't needed as the firmware doesn't actually touch the cookie. Dropping byte swapping makes the warnings go away too. Signed-off-by: Paul Bolle Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/cm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index dfca515cc933..565bfb161c1a 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -2818,7 +2818,7 @@ static void passive_ofld_conn_reply(struct c4iw_dev *dev, struct sk_buff *skb, struct cpl_pass_accept_req *cpl; int ret; - rpl_skb = (__force struct sk_buff *)cpu_to_be64(req->cookie); + rpl_skb = (struct sk_buff *)(unsigned long)req->cookie; BUG_ON(!rpl_skb); if (req->retval) { PDBG("%s passive open failure %d\n", __func__, req->retval); @@ -2962,7 +2962,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb, * TP will ignore any value > 0 for MSS index. */ req->tcb.opt0 = cpu_to_be64(V_MSS_IDX(0xF)); - req->cookie = (__force __u64) cpu_to_be64((u64)skb); + req->cookie = (unsigned long)skb; set_wr_txq(req_skb, CPL_PRIORITY_CONTROL, port_id); cxgb4_ofld_send(dev->rdev.lldi.ports[0], req_skb); From 93711d8becca550154da3b9e422be77083ab51ad Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 8 Feb 2013 15:27:01 -0700 Subject: [PATCH 2756/4607] drm/pci: define drm_pcie_get_speed_cap_mask() only when CONFIG_PCI=y Move drm_pcie_get_speed_cap_mask() under #ifdef CONFIG_PCI because it it used only for PCI devices (evergreen, r600, r770), and it uses PCI interfaces that only exist when CONFIG_PCI=y. Previously, we tried to compile drm_pcie_get_speed_cap_mask() even when CONFIG_PCI=n, which fails. Signed-off-by: Bjorn Helgaas Acked-by: Arnd Bergmann Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_pci.c | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 86102a08f65c..bd719e936e13 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -439,33 +439,6 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) return 0; } -#else - -int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) -{ - return -1; -} - -#endif - -EXPORT_SYMBOL(drm_pci_init); - -/*@}*/ -void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) -{ - struct drm_device *dev, *tmp; - DRM_DEBUG("\n"); - - if (driver->driver_features & DRIVER_MODESET) { - pci_unregister_driver(pdriver); - } else { - list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) - drm_put_dev(dev); - } - DRM_INFO("Module unloaded\n"); -} -EXPORT_SYMBOL(drm_pci_exit); - int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) { struct pci_dev *root; @@ -503,3 +476,30 @@ int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) return 0; } EXPORT_SYMBOL(drm_pcie_get_speed_cap_mask); + +#else + +int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver) +{ + return -1; +} + +#endif + +EXPORT_SYMBOL(drm_pci_init); + +/*@}*/ +void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver) +{ + struct drm_device *dev, *tmp; + DRM_DEBUG("\n"); + + if (driver->driver_features & DRIVER_MODESET) { + pci_unregister_driver(pdriver); + } else { + list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) + drm_put_dev(dev); + } + DRM_INFO("Module unloaded\n"); +} +EXPORT_SYMBOL(drm_pci_exit); From 3e2b756ba330343c960c332695608b9c5881a173 Mon Sep 17 00:00:00 2001 From: Chris Metcalf Date: Fri, 1 Feb 2013 13:44:33 -0500 Subject: [PATCH 2757/4607] drm: fix compile failure by including On tile architecture (with "make allyesconfig") including is required to call swiotlb_nr_tbl(). Signed-off-by: Chris Metcalf Acked-by: Maarten Lankhorst Signed-off-by: Dave Airlie --- drivers/gpu/drm/nouveau/nouveau_bo.c | 1 + drivers/gpu/drm/radeon/radeon_ttm.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 64d6e3047dee..2f2741483b51 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -28,6 +28,7 @@ */ #include +#include #include #include diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 1d8ff2f850ba..93f760e27a92 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "radeon_reg.h" #include "radeon.h" From f934ec8c34b9dcefb5a4f35b0bda33bca289cbe6 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 29 Jan 2013 14:27:39 +0100 Subject: [PATCH 2758/4607] drm: shut up invalid edid messages My cheapo monitor has an invalid block 1, resulting in a lot of dmesg spam every few seconds. I get it the first time that the entire block is all 0xff.. Signed-off-by: Maarten Lankhorst Cc: stable@vger.kernel.org [v3.7] Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 51324256a657..e1aca7b53987 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -354,10 +354,14 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) break; } } - if (i == 4) + + if (i == 4 && print_bad_edid) { dev_warn(connector->dev->dev, "%s: Ignoring invalid EDID block %d.\n", drm_get_connector_name(connector), j); + + connector->bad_edid_counter++; + } } if (valid_extensions != block[0x7e]) { From bcc9b67a5b65ec2e1ec5371226a729ec1b380860 Mon Sep 17 00:00:00 2001 From: Mike Marciniszyn Date: Thu, 7 Feb 2013 20:47:51 +0000 Subject: [PATCH 2759/4607] IB/qib: Fix QP locate/remove race remove_qp() can execute concurrently with a qib_lookup_qpn() on another CPU, which in of itself, is ok, given the RCU locking. The issue is that remove_qp() NULLs out the qp->next field so that a qib_lookup_qpn() might fail to find a qp if it occurs after the one that is being deleted. This is a momentary issue and subsequent qib_lookup_qpn() calls would find the qp's since the search restarts from the bucket head. At scale, the issue might causes dropped packets and unnecessary retransmissions. The fix just deletes the qp->next NULL assignment to prevent the remove_qp() from hiding qp's from qib_lookup_qpn(). Reviewed-by: Dean Luick Signed-off-by: Mike Marciniszyn Signed-off-by: Roland Dreier --- drivers/infiniband/hw/qib/qib_qp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/hw/qib/qib_qp.c b/drivers/infiniband/hw/qib/qib_qp.c index 35275099cafd..a6a2cc2ba260 100644 --- a/drivers/infiniband/hw/qib/qib_qp.c +++ b/drivers/infiniband/hw/qib/qib_qp.c @@ -268,8 +268,9 @@ static void remove_qp(struct qib_ibdev *dev, struct qib_qp *qp) qpp = &q->next) if (q == qp) { atomic_dec(&qp->refcount); - *qpp = qp->next; - rcu_assign_pointer(qp->next, NULL); + rcu_assign_pointer(*qpp, + rcu_dereference_protected(qp->next, + lockdep_is_held(&dev->qpt_lock))); break; } } From 2a745b14bc99d52c29d0c886a110321f651cf183 Mon Sep 17 00:00:00 2001 From: Arne Jansen Date: Wed, 13 Feb 2013 04:20:01 -0700 Subject: [PATCH 2760/4607] Btrfs: fix crash in log replay with qgroups enabled When replaying a log tree with qgroups enabled, tree_mod_log_rewind does a sanity-check of the number of items against the maximum possible number. It calculates that number with the nodesize of fs_root. Unfortunately fs_root is not yet set at this stage. So instead use the nodesize from tree_root, which is already initialized. Signed-off-by: Arne Jansen Signed-off-by: Chris Mason --- fs/btrfs/ctree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index eea5da7a2b9a..6eff0fa9ecaa 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -1222,7 +1222,7 @@ tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb, __tree_mod_log_rewind(eb_rewin, time_seq, tm); WARN_ON(btrfs_header_nritems(eb_rewin) > - BTRFS_NODEPTRS_PER_BLOCK(fs_info->fs_root)); + BTRFS_NODEPTRS_PER_BLOCK(fs_info->tree_root)); return eb_rewin; } From dc6982ff4db1f47da73b1967ef5302d6721e5b95 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 14 Feb 2013 23:59:26 -0500 Subject: [PATCH 2761/4607] ext4: refactor code to read directory blocks into ext4_read_dirblock() The code to read in directory blocks and verify their metadata checksums was replicated in ten different places across fs/ext4/namei.c, and the code was buggy in subtle ways in a number of those replicated sites. In some cases, ext4_error() was called with a training newline. In others, in particularly in empty_dir(), it was possible to call ext4_dirent_csum_verify() on an index block, which would trigger false warnings requesting the system adminsitrator to run e2fsck. By refactoring the code, we make the code more readable, as well as shrinking the compiled object file by over 700 bytes and 50 lines of code. Signed-off-by: "Theodore Ts'o" --- fs/ext4/namei.c | 291 ++++++++++++++++++++---------------------------- 1 file changed, 119 insertions(+), 172 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 3e1529c39808..0e28c749e273 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -83,6 +83,86 @@ static struct buffer_head *ext4_append(handle_t *handle, return bh; } +static int ext4_dx_csum_verify(struct inode *inode, + struct ext4_dir_entry *dirent); + +typedef enum { + EITHER, INDEX, DIRENT +} dirblock_type_t; + +#define ext4_read_dirblock(inode, block, type) \ + __ext4_read_dirblock((inode), (block), (type), __LINE__) + +static struct buffer_head *__ext4_read_dirblock(struct inode *inode, + ext4_lblk_t block, + dirblock_type_t type, + unsigned int line) +{ + struct buffer_head *bh; + struct ext4_dir_entry *dirent; + int err = 0, is_dx_block = 0; + + bh = ext4_bread(NULL, inode, block, 0, &err); + if (!bh) { + if (err == 0) { + ext4_error_inode(inode, __func__, line, block, + "Directory hole found"); + return ERR_PTR(-EIO); + } + __ext4_warning(inode->i_sb, __func__, line, + "error reading directory block " + "(ino %lu, block %lu)", inode->i_ino, + (unsigned long) block); + return ERR_PTR(err); + } + dirent = (struct ext4_dir_entry *) bh->b_data; + /* Determine whether or not we have an index block */ + if (is_dx(inode)) { + if (block == 0) + is_dx_block = 1; + else if (ext4_rec_len_from_disk(dirent->rec_len, + inode->i_sb->s_blocksize) == + inode->i_sb->s_blocksize) + is_dx_block = 1; + } + if (!is_dx_block && type == INDEX) { + ext4_error_inode(inode, __func__, line, block, + "directory leaf block found instead of index block"); + return ERR_PTR(-EIO); + } + if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) || + buffer_verified(bh)) + return bh; + + /* + * An empty leaf block can get mistaken for a index block; for + * this reason, we can only check the index checksum when the + * caller is sure it should be an index block. + */ + if (is_dx_block && type == INDEX) { + if (ext4_dx_csum_verify(inode, dirent)) + set_buffer_verified(bh); + else { + ext4_error_inode(inode, __func__, line, block, + "Directory index failed checksum"); + brelse(bh); + return ERR_PTR(-EIO); + } + } + if (!is_dx_block) { + if (ext4_dirent_csum_verify(inode, dirent)) + set_buffer_verified(bh); + else { + ext4_error_inode(inode, __func__, line, block, + "Directory block failed checksum"); + brelse(bh); + return ERR_PTR(-EIO); + } + } + return bh; +} + #ifndef assert #define assert(test) J_ASSERT(test) #endif @@ -604,9 +684,9 @@ dx_probe(const struct qstr *d_name, struct inode *dir, u32 hash; frame->bh = NULL; - if (!(bh = ext4_bread(NULL, dir, 0, 0, err))) { - if (*err == 0) - *err = ERR_BAD_DX_DIR; + bh = ext4_read_dirblock(dir, 0, INDEX); + if (IS_ERR(bh)) { + *err = PTR_ERR(bh); goto fail; } root = (struct dx_root *) bh->b_data; @@ -643,15 +723,6 @@ dx_probe(const struct qstr *d_name, struct inode *dir, goto fail; } - if (!buffer_verified(bh) && - !ext4_dx_csum_verify(dir, (struct ext4_dir_entry *)bh->b_data)) { - ext4_warning(dir->i_sb, "Root failed checksum"); - brelse(bh); - *err = ERR_BAD_DX_DIR; - goto fail; - } - set_buffer_verified(bh); - entries = (struct dx_entry *) (((char *)&root->info) + root->info.info_length); @@ -709,23 +780,13 @@ dx_probe(const struct qstr *d_name, struct inode *dir, frame->entries = entries; frame->at = at; if (!indirect--) return frame; - if (!(bh = ext4_bread(NULL, dir, dx_get_block(at), 0, err))) { - if (!(*err)) - *err = ERR_BAD_DX_DIR; + bh = ext4_read_dirblock(dir, dx_get_block(at), INDEX); + if (IS_ERR(bh)) { + *err = PTR_ERR(bh); goto fail2; } entries = ((struct dx_node *) bh->b_data)->entries; - if (!buffer_verified(bh) && - !ext4_dx_csum_verify(dir, - (struct ext4_dir_entry *)bh->b_data)) { - ext4_warning(dir->i_sb, "Node failed checksum"); - brelse(bh); - *err = ERR_BAD_DX_DIR; - goto fail2; - } - set_buffer_verified(bh); - if (dx_get_limit(entries) != dx_node_limit (dir)) { ext4_warning(dir->i_sb, "dx entry: limit != node limit"); @@ -783,7 +844,7 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash, { struct dx_frame *p; struct buffer_head *bh; - int err, num_frames = 0; + int num_frames = 0; __u32 bhash; p = frame; @@ -822,26 +883,9 @@ static int ext4_htree_next_block(struct inode *dir, __u32 hash, * block so no check is necessary */ while (num_frames--) { - if (!(bh = ext4_bread(NULL, dir, dx_get_block(p->at), - 0, &err))) { - if (!err) { - ext4_error(dir->i_sb, - "Directory hole detected on inode %lu\n", - dir->i_ino); - return -EIO; - } - return err; /* Failure */ - } - - if (!buffer_verified(bh) && - !ext4_dx_csum_verify(dir, - (struct ext4_dir_entry *)bh->b_data)) { - ext4_warning(dir->i_sb, "Node failed checksum"); - brelse(bh); - return -EIO; - } - set_buffer_verified(bh); - + bh = ext4_read_dirblock(dir, dx_get_block(p->at), INDEX); + if (IS_ERR(bh)) + return PTR_ERR(bh); p++; brelse(p->bh); p->bh = bh; @@ -867,23 +911,9 @@ static int htree_dirblock_to_tree(struct file *dir_file, dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n", (unsigned long)block)); - if (!(bh = ext4_bread(NULL, dir, block, 0, &err))) { - if (!err) { - err = -EIO; - ext4_error(dir->i_sb, - "Directory hole detected on inode %lu\n", - dir->i_ino); - } - return err; - } - - if (!buffer_verified(bh) && - !ext4_dirent_csum_verify(dir, - (struct ext4_dir_entry *)bh->b_data)) { - brelse(bh); - return -EIO; - } - set_buffer_verified(bh); + bh = ext4_read_dirblock(dir, block, DIRENT); + if (IS_ERR(bh)) + return PTR_ERR(bh); de = (struct ext4_dir_entry_2 *) bh->b_data; top = (struct ext4_dir_entry_2 *) ((char *) de + @@ -1337,26 +1367,11 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q return NULL; do { block = dx_get_block(frame->at); - if (!(bh = ext4_bread(NULL, dir, block, 0, err))) { - if (!(*err)) { - *err = -EIO; - ext4_error(dir->i_sb, - "Directory hole detected on inode %lu\n", - dir->i_ino); - } + bh = ext4_read_dirblock(dir, block, DIRENT); + if (IS_ERR(bh)) { + *err = PTR_ERR(bh); goto errout; } - - if (!buffer_verified(bh) && - !ext4_dirent_csum_verify(dir, - (struct ext4_dir_entry *)bh->b_data)) { - EXT4_ERROR_INODE(dir, "checksumming directory " - "block %lu", (unsigned long)block); - brelse(bh); - *err = -EIO; - goto errout; - } - set_buffer_verified(bh); retval = search_dirblock(bh, dir, d_name, block << EXT4_BLOCK_SIZE_BITS(sb), res_dir); @@ -1920,22 +1935,10 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, } blocks = dir->i_size >> sb->s_blocksize_bits; for (block = 0; block < blocks; block++) { - if (!(bh = ext4_bread(handle, dir, block, 0, &retval))) { - if (!retval) { - retval = -EIO; - ext4_error(inode->i_sb, - "Directory hole detected on inode %lu\n", - inode->i_ino); - } - return retval; - } - if (!buffer_verified(bh) && - !ext4_dirent_csum_verify(dir, - (struct ext4_dir_entry *)bh->b_data)) { - brelse(bh); - return -EIO; - } - set_buffer_verified(bh); + bh = ext4_read_dirblock(dir, block, DIRENT); + if (IS_ERR(bh)) + return PTR_ERR(bh); + retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh); if (retval != -ENOSPC) { brelse(bh); @@ -1986,22 +1989,13 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, return err; entries = frame->entries; at = frame->at; - - if (!(bh = ext4_bread(handle, dir, dx_get_block(frame->at), 0, &err))) { - if (!err) { - err = -EIO; - ext4_error(dir->i_sb, - "Directory hole detected on inode %lu\n", - dir->i_ino); - } + bh = ext4_read_dirblock(dir, dx_get_block(frame->at), DIRENT); + if (IS_ERR(bh)) { + err = PTR_ERR(bh); + bh = NULL; goto cleanup; } - if (!buffer_verified(bh) && - !ext4_dirent_csum_verify(dir, (struct ext4_dir_entry *)bh->b_data)) - goto journal_error; - set_buffer_verified(bh); - BUFFER_TRACE(bh, "get_write_access"); err = ext4_journal_get_write_access(handle, bh); if (err) @@ -2352,6 +2346,7 @@ static int ext4_init_new_dir(handle_t *handle, struct inode *dir, struct buffer_head *dir_block = NULL; struct ext4_dir_entry_2 *de; struct ext4_dir_entry_tail *t; + ext4_lblk_t block = 0; unsigned int blocksize = dir->i_sb->s_blocksize; int csum_size = 0; int err; @@ -2368,16 +2363,9 @@ static int ext4_init_new_dir(handle_t *handle, struct inode *dir, goto out; } - inode->i_size = EXT4_I(inode)->i_disksize = blocksize; - if (!(dir_block = ext4_bread(handle, inode, 0, 1, &err))) { - if (!err) { - err = -EIO; - ext4_error(inode->i_sb, - "Directory hole detected on inode %lu\n", - inode->i_ino); - } + inode->i_size = 0; + if (!(dir_block = ext4_append(handle, inode, &block, &err))) goto out; - } BUFFER_TRACE(dir_block, "get_write_access"); err = ext4_journal_get_write_access(handle, dir_block); if (err) @@ -2477,26 +2465,14 @@ static int empty_dir(struct inode *inode) } sb = inode->i_sb; - if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2) || - !(bh = ext4_bread(NULL, inode, 0, 0, &err))) { - if (err) - EXT4_ERROR_INODE(inode, - "error %d reading directory lblock 0", err); - else - ext4_warning(inode->i_sb, - "bad directory (dir #%lu) - no data block", - inode->i_ino); + if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2)) { + EXT4_ERROR_INODE(inode, "invalid size"); return 1; } - if (!buffer_verified(bh) && - !ext4_dirent_csum_verify(inode, - (struct ext4_dir_entry *)bh->b_data)) { - EXT4_ERROR_INODE(inode, "checksum error reading directory " - "lblock 0"); - brelse(bh); - return -EIO; - } - set_buffer_verified(bh); + bh = ext4_read_dirblock(inode, 0, EITHER); + if (IS_ERR(bh)) + return 1; + de = (struct ext4_dir_entry_2 *) bh->b_data; de1 = ext4_next_entry(de, sb->s_blocksize); if (le32_to_cpu(de->inode) != inode->i_ino || @@ -2519,29 +2495,9 @@ static int empty_dir(struct inode *inode) err = 0; brelse(bh); lblock = offset >> EXT4_BLOCK_SIZE_BITS(sb); - bh = ext4_bread(NULL, inode, lblock, 0, &err); - if (!bh) { - if (err) - EXT4_ERROR_INODE(inode, - "error %d reading directory " - "lblock %u", err, lblock); - else - ext4_warning(inode->i_sb, - "bad directory (dir #%lu) - no data block", - inode->i_ino); - - offset += sb->s_blocksize; - continue; - } - if (!buffer_verified(bh) && - !ext4_dirent_csum_verify(inode, - (struct ext4_dir_entry *)bh->b_data)) { - EXT4_ERROR_INODE(inode, "checksum error " - "reading directory lblock 0"); - brelse(bh); - return -EIO; - } - set_buffer_verified(bh); + bh = ext4_read_dirblock(inode, lblock, EITHER); + if (IS_ERR(bh)) + return 1; de = (struct ext4_dir_entry_2 *) bh->b_data; } if (ext4_check_dir_entry(inode, NULL, de, bh, @@ -3004,13 +2960,9 @@ static struct buffer_head *ext4_get_first_dir_block(handle_t *handle, struct buffer_head *bh; if (!ext4_has_inline_data(inode)) { - if (!(bh = ext4_bread(handle, inode, 0, 0, retval))) { - if (!*retval) { - *retval = -EIO; - ext4_error(inode->i_sb, - "Directory hole detected on inode %lu\n", - inode->i_ino); - } + bh = ext4_read_dirblock(inode, 0, EITHER); + if (IS_ERR(bh)) { + *retval = PTR_ERR(bh); return NULL; } *parent_de = ext4_next_entry( @@ -3089,11 +3041,6 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, &inlined); if (!dir_bh) goto end_rename; - if (!inlined && !buffer_verified(dir_bh) && - !ext4_dirent_csum_verify(old_inode, - (struct ext4_dir_entry *)dir_bh->b_data)) - goto end_rename; - set_buffer_verified(dir_bh); if (le32_to_cpu(parent_de->inode) != old_dir->i_ino) goto end_rename; retval = -EMLINK; From 6100209bf6632d908ebcdab339a83f2e083b94e7 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 4 Feb 2013 18:09:40 +0000 Subject: [PATCH 2762/4607] powerpc: Remove Cell-specific relocation-on interrupt vector code The Cell processor doesn't support relocation-on interrupts, so we don't need relocation-on versions of the interrupt vectors that are purely Cell-specific. This removes them. Signed-off-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/exceptions-64s.S | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 7a1c87cbb890..dc64165d1c0b 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -817,26 +817,16 @@ vsx_unavailable_relon_pSeries_1: . = 0x4f40 b vsx_unavailable_relon_pSeries -#ifdef CONFIG_CBE_RAS - STD_RELON_EXCEPTION_HV(0x5200, 0x1202, cbe_system_error) -#endif /* CONFIG_CBE_RAS */ STD_RELON_EXCEPTION_PSERIES(0x5300, 0x1300, instruction_breakpoint) #ifdef CONFIG_PPC_DENORMALISATION . = 0x5500 b denorm_exception_hv #endif -#ifdef CONFIG_CBE_RAS - STD_RELON_EXCEPTION_HV(0x5600, 0x1602, cbe_maintenance) -#else #ifdef CONFIG_HVC_SCOM STD_RELON_EXCEPTION_HV(0x5600, 0x1600, maintence_interrupt) KVM_HANDLER_SKIP(PACA_EXGEN, EXC_HV, 0x1600) #endif /* CONFIG_HVC_SCOM */ -#endif /* CONFIG_CBE_RAS */ STD_RELON_EXCEPTION_PSERIES(0x5700, 0x1700, altivec_assist) -#ifdef CONFIG_CBE_RAS - STD_RELON_EXCEPTION_HV(0x5800, 0x1802, cbe_thermal) -#endif /* CONFIG_CBE_RAS */ /* Other future vectors */ .align 7 From 1707dd161349e6c54170c88d94fed012e3d224e3 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 4 Feb 2013 18:10:15 +0000 Subject: [PATCH 2763/4607] powerpc: Save CFAR before branching in interrupt entry paths Some of the interrupt vectors on 64-bit POWER server processors are only 32 bytes long, which is not enough for the full first-level interrupt handler. For these we currently just have a branch to an out-of-line handler. However, this means that we corrupt the CFAR (come-from address register) on POWER7 and later processors. To fix this, we split the EXCEPTION_PROLOG_1 macro into two pieces: EXCEPTION_PROLOG_0 contains the part up to the point where the CFAR is saved in the PACA, and EXCEPTION_PROLOG_1 contains the rest. We then put EXCEPTION_PROLOG_0 in the short interrupt vectors before we branch to the out-of-line handler, which contains the rest of the first-level interrupt handler. To facilitate this, we define new _OOL (out of line) variants of STD_EXCEPTION_PSERIES, etc. In order to get EXCEPTION_PROLOG_0 to be short enough, i.e., no more than 6 instructions, it was necessary to move the stores that move the PPR and CFAR values into the PACA into __EXCEPTION_PROLOG_1 and to get rid of one of the two HMT_MEDIUM instructions. Previously there was a HMT_MEDIUM_PPR_DISCARD before the prolog, which was nop'd out on processors with the PPR (POWER7 and later), and then another HMT_MEDIUM inside the HMT_MEDIUM_PPR_SAVE macro call inside __EXCEPTION_PROLOG_1, which was nop'd out on processors without PPR. Now the HMT_MEDIUM inside EXCEPTION_PROLOG_0 is there unconditionally and the HMT_MEDIUM_PPR_DISCARD is not strictly necessary, although this leaves it in for the interrupt vectors where there is room for it. Previously we had a handler for hypervisor maintenance interrupts at 0xe50, which doesn't leave enough room for the vector for hypervisor emulation assist interrupts at 0xe40, since we need 8 instructions. The 0xe50 vector was only used on POWER6, as the HMI vector was moved to 0xe60 on POWER7. Since we don't support running in hypervisor mode on POWER6, we just remove the handler at 0xe50. This also changes denorm_exception_hv to use EXCEPTION_PROLOG_0 instead of open-coding it, and removes the HMT_MEDIUM_PPR_DISCARD from the relocation-on vectors (since any CPU that supports relocation-on interrupts also has the PPR). Signed-off-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/exception-64s.h | 84 +++++++++++++++++---- arch/powerpc/kernel/exceptions-64s.S | 95 ++++++++++++++++-------- 2 files changed, 133 insertions(+), 46 deletions(-) diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 370298a0bca3..4dfc51588be5 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -50,7 +50,7 @@ #define EX_PPR 88 /* SMT thread status register (priority) */ #ifdef CONFIG_RELOCATABLE -#define EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) \ +#define __EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) \ ld r12,PACAKBASE(r13); /* get high part of &label */ \ mfspr r11,SPRN_##h##SRR0; /* save SRR0 */ \ LOAD_HANDLER(r12,label); \ @@ -61,13 +61,15 @@ blr; #else /* If not relocatable, we can jump directly -- and save messing with LR */ -#define EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) \ +#define __EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) \ mfspr r11,SPRN_##h##SRR0; /* save SRR0 */ \ mfspr r12,SPRN_##h##SRR1; /* and SRR1 */ \ li r10,MSR_RI; \ mtmsrd r10,1; /* Set RI (EE=0) */ \ b label; #endif +#define EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) \ + __EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) \ /* * As EXCEPTION_PROLOG_PSERIES(), except we've already got relocation on @@ -75,6 +77,7 @@ * case EXCEPTION_RELON_PROLOG_PSERIES_1 will be using lr. */ #define EXCEPTION_RELON_PROLOG_PSERIES(area, label, h, extra, vec) \ + EXCEPTION_PROLOG_0(area); \ EXCEPTION_PROLOG_1(area, extra, vec); \ EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) @@ -135,25 +138,32 @@ BEGIN_FTR_SECTION_NESTED(942) \ END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,0,942) /*non P7*/ /* - * Save PPR in paca whenever some register is available to use. - * Then increase the priority. + * Get an SPR into a register if the CPU has the given feature */ -#define HMT_MEDIUM_PPR_SAVE(area, ra) \ +#define OPT_GET_SPR(ra, spr, ftr) \ BEGIN_FTR_SECTION_NESTED(943) \ - mfspr ra,SPRN_PPR; \ - std ra,area+EX_PPR(r13); \ - HMT_MEDIUM; \ -END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,943) + mfspr ra,spr; \ +END_FTR_SECTION_NESTED(ftr,ftr,943) -#define __EXCEPTION_PROLOG_1(area, extra, vec) \ +/* + * Save a register to the PACA if the CPU has the given feature + */ +#define OPT_SAVE_REG_TO_PACA(offset, ra, ftr) \ +BEGIN_FTR_SECTION_NESTED(943) \ + std ra,offset(r13); \ +END_FTR_SECTION_NESTED(ftr,ftr,943) + +#define EXCEPTION_PROLOG_0(area) \ GET_PACA(r13); \ std r9,area+EX_R9(r13); /* save r9 */ \ - HMT_MEDIUM_PPR_SAVE(area, r9); \ + OPT_GET_SPR(r9, SPRN_PPR, CPU_FTR_HAS_PPR); \ + HMT_MEDIUM; \ std r10,area+EX_R10(r13); /* save r10 - r12 */ \ - BEGIN_FTR_SECTION_NESTED(66); \ - mfspr r10,SPRN_CFAR; \ - std r10,area+EX_CFAR(r13); \ - END_FTR_SECTION_NESTED(CPU_FTR_CFAR, CPU_FTR_CFAR, 66); \ + OPT_GET_SPR(r10, SPRN_CFAR, CPU_FTR_CFAR) + +#define __EXCEPTION_PROLOG_1(area, extra, vec) \ + OPT_SAVE_REG_TO_PACA(area+EX_PPR, r9, CPU_FTR_HAS_PPR); \ + OPT_SAVE_REG_TO_PACA(area+EX_CFAR, r10, CPU_FTR_CFAR); \ SAVE_LR(r10, area); \ mfcr r9; \ extra(vec); \ @@ -178,6 +188,7 @@ END_FTR_SECTION_NESTED(CPU_FTR_HAS_PPR,CPU_FTR_HAS_PPR,943) __EXCEPTION_PROLOG_PSERIES_1(label, h) #define EXCEPTION_PROLOG_PSERIES(area, label, h, extra, vec) \ + EXCEPTION_PROLOG_0(area); \ EXCEPTION_PROLOG_1(area, extra, vec); \ EXCEPTION_PROLOG_PSERIES_1(label, h); @@ -312,6 +323,13 @@ label##_pSeries: \ EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ EXC_STD, KVMTEST_PR, vec) +/* Version of above for when we have to branch out-of-line */ +#define STD_EXCEPTION_PSERIES_OOL(vec, label) \ + .globl label##_pSeries; \ +label##_pSeries: \ + EXCEPTION_PROLOG_1(PACA_EXGEN, KVMTEST_PR, vec); \ + EXCEPTION_PROLOG_PSERIES_1(label##_common, EXC_STD) + #define STD_EXCEPTION_HV(loc, vec, label) \ . = loc; \ .globl label##_hv; \ @@ -321,6 +339,13 @@ label##_hv: \ EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ EXC_HV, KVMTEST, vec) +/* Version of above for when we have to branch out-of-line */ +#define STD_EXCEPTION_HV_OOL(vec, label) \ + .globl label##_hv; \ +label##_hv: \ + EXCEPTION_PROLOG_1(PACA_EXGEN, KVMTEST, vec); \ + EXCEPTION_PROLOG_PSERIES_1(label##_common, EXC_HV) + #define STD_RELON_EXCEPTION_PSERIES(loc, vec, label) \ . = loc; \ .globl label##_relon_pSeries; \ @@ -331,6 +356,12 @@ label##_relon_pSeries: \ EXCEPTION_RELON_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ EXC_STD, KVMTEST_PR, vec) +#define STD_RELON_EXCEPTION_PSERIES_OOL(vec, label) \ + .globl label##_relon_pSeries; \ +label##_relon_pSeries: \ + EXCEPTION_PROLOG_1(PACA_EXGEN, KVMTEST_PR, vec); \ + EXCEPTION_RELON_PROLOG_PSERIES_1(label##_common, EXC_STD) + #define STD_RELON_EXCEPTION_HV(loc, vec, label) \ . = loc; \ .globl label##_relon_hv; \ @@ -341,6 +372,12 @@ label##_relon_hv: \ EXCEPTION_RELON_PROLOG_PSERIES(PACA_EXGEN, label##_common, \ EXC_HV, KVMTEST, vec) +#define STD_RELON_EXCEPTION_HV_OOL(vec, label) \ + .globl label##_relon_hv; \ +label##_relon_hv: \ + EXCEPTION_PROLOG_1(PACA_EXGEN, KVMTEST, vec); \ + EXCEPTION_RELON_PROLOG_PSERIES_1(label##_common, EXC_HV) + /* This associate vector numbers with bits in paca->irq_happened */ #define SOFTEN_VALUE_0x500 PACA_IRQ_EE #define SOFTEN_VALUE_0x502 PACA_IRQ_EE @@ -375,8 +412,10 @@ label##_relon_hv: \ #define __MASKABLE_EXCEPTION_PSERIES(vec, label, h, extra) \ HMT_MEDIUM_PPR_DISCARD; \ SET_SCRATCH0(r13); /* save r13 */ \ - __EXCEPTION_PROLOG_1(PACA_EXGEN, extra, vec); \ + EXCEPTION_PROLOG_0(PACA_EXGEN); \ + __EXCEPTION_PROLOG_1(PACA_EXGEN, extra, vec); \ EXCEPTION_PROLOG_PSERIES_1(label##_common, h); + #define _MASKABLE_EXCEPTION_PSERIES(vec, label, h, extra) \ __MASKABLE_EXCEPTION_PSERIES(vec, label, h, extra) @@ -394,9 +433,16 @@ label##_hv: \ _MASKABLE_EXCEPTION_PSERIES(vec, label, \ EXC_HV, SOFTEN_TEST_HV) +#define MASKABLE_EXCEPTION_HV_OOL(vec, label) \ + .globl label##_hv; \ +label##_hv: \ + EXCEPTION_PROLOG_1(PACA_EXGEN, SOFTEN_TEST_HV, vec); \ + EXCEPTION_PROLOG_PSERIES_1(label##_common, EXC_HV); + #define __MASKABLE_RELON_EXCEPTION_PSERIES(vec, label, h, extra) \ HMT_MEDIUM_PPR_DISCARD; \ SET_SCRATCH0(r13); /* save r13 */ \ + EXCEPTION_PROLOG_0(PACA_EXGEN); \ __EXCEPTION_PROLOG_1(PACA_EXGEN, extra, vec); \ EXCEPTION_RELON_PROLOG_PSERIES_1(label##_common, h); #define _MASKABLE_RELON_EXCEPTION_PSERIES(vec, label, h, extra) \ @@ -416,6 +462,12 @@ label##_relon_hv: \ _MASKABLE_RELON_EXCEPTION_PSERIES(vec, label, \ EXC_HV, SOFTEN_NOTEST_HV) +#define MASKABLE_RELON_EXCEPTION_HV_OOL(vec, label) \ + .globl label##_relon_hv; \ +label##_relon_hv: \ + EXCEPTION_PROLOG_1(PACA_EXGEN, SOFTEN_NOTEST_HV, vec); \ + EXCEPTION_PROLOG_PSERIES_1(label##_common, EXC_HV); + /* * Our exception common code can be passed various "additions" * to specify the behaviour of interrupts, whether to kick the diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index dc64165d1c0b..b9bcf21dbb11 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -153,7 +153,10 @@ machine_check_pSeries_1: * some code path might still want to branch into the original * vector */ - b machine_check_pSeries + HMT_MEDIUM_PPR_DISCARD + SET_SCRATCH0(r13) /* save r13 */ + EXCEPTION_PROLOG_0(PACA_EXMC) + b machine_check_pSeries_0 . = 0x300 .globl data_access_pSeries @@ -172,6 +175,7 @@ END_MMU_FTR_SECTION_IFCLR(MMU_FTR_SLB) data_access_slb_pSeries: HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXSLB) EXCEPTION_PROLOG_1(PACA_EXSLB, KVMTEST, 0x380) std r3,PACA_EXSLB+EX_R3(r13) mfspr r3,SPRN_DAR @@ -203,6 +207,7 @@ data_access_slb_pSeries: instruction_access_slb_pSeries: HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXSLB) EXCEPTION_PROLOG_1(PACA_EXSLB, KVMTEST_PR, 0x480) std r3,PACA_EXSLB+EX_R3(r13) mfspr r3,SPRN_SRR0 /* SRR0 is faulting address */ @@ -284,16 +289,28 @@ system_call_pSeries: */ . = 0xe00 hv_exception_trampoline: + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b h_data_storage_hv + . = 0xe20 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b h_instr_storage_hv + . = 0xe40 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b emulation_assist_hv - . = 0xe50 - b hmi_exception_hv + . = 0xe60 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b hmi_exception_hv + . = 0xe80 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b h_doorbell_hv /* We need to deal with the Altivec unavailable exception @@ -303,14 +320,20 @@ hv_exception_trampoline: */ performance_monitor_pSeries_1: . = 0xf00 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b performance_monitor_pSeries altivec_unavailable_pSeries_1: . = 0xf20 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b altivec_unavailable_pSeries vsx_unavailable_pSeries_1: . = 0xf40 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b vsx_unavailable_pSeries #ifdef CONFIG_CBE_RAS @@ -326,10 +349,7 @@ vsx_unavailable_pSeries_1: denorm_exception_hv: HMT_MEDIUM_PPR_DISCARD mtspr SPRN_SPRG_HSCRATCH0,r13 - mfspr r13,SPRN_SPRG_HPACA - std r9,PACA_EXGEN+EX_R9(r13) - HMT_MEDIUM_PPR_SAVE(PACA_EXGEN, r9) - std r10,PACA_EXGEN+EX_R10(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) std r11,PACA_EXGEN+EX_R11(r13) std r12,PACA_EXGEN+EX_R12(r13) mfspr r9,SPRN_SPRG_HSCRATCH0 @@ -372,8 +392,10 @@ machine_check_pSeries: machine_check_fwnmi: HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) /* save r13 */ - EXCEPTION_PROLOG_PSERIES(PACA_EXMC, machine_check_common, - EXC_STD, KVMTEST, 0x200) + EXCEPTION_PROLOG_0(PACA_EXMC) +machine_check_pSeries_0: + EXCEPTION_PROLOG_1(PACA_EXMC, KVMTEST, 0x200) + EXCEPTION_PROLOG_PSERIES_1(machine_check_common, EXC_STD) KVM_HANDLER_SKIP(PACA_EXMC, EXC_STD, 0x200) /* moved from 0x300 */ @@ -510,23 +532,23 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_206) .align 7 /* moved from 0xe00 */ - STD_EXCEPTION_HV(., 0xe02, h_data_storage) + STD_EXCEPTION_HV_OOL(0xe02, h_data_storage) KVM_HANDLER_SKIP(PACA_EXGEN, EXC_HV, 0xe02) - STD_EXCEPTION_HV(., 0xe22, h_instr_storage) + STD_EXCEPTION_HV_OOL(0xe22, h_instr_storage) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe22) - STD_EXCEPTION_HV(., 0xe42, emulation_assist) + STD_EXCEPTION_HV_OOL(0xe42, emulation_assist) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe42) - STD_EXCEPTION_HV(., 0xe62, hmi_exception) /* need to flush cache ? */ + STD_EXCEPTION_HV_OOL(0xe62, hmi_exception) /* need to flush cache ? */ KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe62) - MASKABLE_EXCEPTION_HV(., 0xe82, h_doorbell) + MASKABLE_EXCEPTION_HV_OOL(0xe82, h_doorbell) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe82) /* moved from 0xf00 */ - STD_EXCEPTION_PSERIES(., 0xf00, performance_monitor) + STD_EXCEPTION_PSERIES_OOL(0xf00, performance_monitor) KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf00) - STD_EXCEPTION_PSERIES(., 0xf20, altivec_unavailable) + STD_EXCEPTION_PSERIES_OOL(0xf20, altivec_unavailable) KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf20) - STD_EXCEPTION_PSERIES(., 0xf40, vsx_unavailable) + STD_EXCEPTION_PSERIES_OOL(0xf40, vsx_unavailable) KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf40) /* @@ -718,8 +740,8 @@ machine_check_common: . = 0x4380 .globl data_access_slb_relon_pSeries data_access_slb_relon_pSeries: - HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXSLB) EXCEPTION_PROLOG_1(PACA_EXSLB, NOTEST, 0x380) std r3,PACA_EXSLB+EX_R3(r13) mfspr r3,SPRN_DAR @@ -743,8 +765,8 @@ data_access_slb_relon_pSeries: . = 0x4480 .globl instruction_access_slb_relon_pSeries instruction_access_slb_relon_pSeries: - HMT_MEDIUM_PPR_DISCARD SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXSLB) EXCEPTION_PROLOG_1(PACA_EXSLB, NOTEST, 0x480) std r3,PACA_EXSLB+EX_R3(r13) mfspr r3,SPRN_SRR0 /* SRR0 is faulting address */ @@ -788,33 +810,46 @@ system_call_relon_pSeries: STD_RELON_EXCEPTION_PSERIES(0x4d00, 0xd00, single_step) . = 0x4e00 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b h_data_storage_relon_hv . = 0x4e20 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b h_instr_storage_relon_hv . = 0x4e40 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b emulation_assist_relon_hv - . = 0x4e50 - b hmi_exception_relon_hv - . = 0x4e60 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b hmi_exception_relon_hv . = 0x4e80 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b h_doorbell_relon_hv performance_monitor_relon_pSeries_1: . = 0x4f00 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b performance_monitor_relon_pSeries altivec_unavailable_relon_pSeries_1: . = 0x4f20 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b altivec_unavailable_relon_pSeries vsx_unavailable_relon_pSeries_1: . = 0x4f40 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) b vsx_unavailable_relon_pSeries STD_RELON_EXCEPTION_PSERIES(0x5300, 0x1300, instruction_breakpoint) @@ -1171,20 +1206,20 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) __end_handlers: /* Equivalents to the above handlers for relocation-on interrupt vectors */ - STD_RELON_EXCEPTION_HV(., 0xe00, h_data_storage) + STD_RELON_EXCEPTION_HV_OOL(0xe00, h_data_storage) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe00) - STD_RELON_EXCEPTION_HV(., 0xe20, h_instr_storage) + STD_RELON_EXCEPTION_HV_OOL(0xe20, h_instr_storage) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe20) - STD_RELON_EXCEPTION_HV(., 0xe40, emulation_assist) + STD_RELON_EXCEPTION_HV_OOL(0xe40, emulation_assist) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe40) - STD_RELON_EXCEPTION_HV(., 0xe60, hmi_exception) + STD_RELON_EXCEPTION_HV_OOL(0xe60, hmi_exception) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe60) - MASKABLE_RELON_EXCEPTION_HV(., 0xe80, h_doorbell) + MASKABLE_RELON_EXCEPTION_HV_OOL(0xe80, h_doorbell) KVM_HANDLER(PACA_EXGEN, EXC_HV, 0xe80) - STD_RELON_EXCEPTION_PSERIES(., 0xf00, performance_monitor) - STD_RELON_EXCEPTION_PSERIES(., 0xf20, altivec_unavailable) - STD_RELON_EXCEPTION_PSERIES(., 0xf40, vsx_unavailable) + STD_RELON_EXCEPTION_PSERIES_OOL(0xf00, performance_monitor) + STD_RELON_EXCEPTION_PSERIES_OOL(0xf20, altivec_unavailable) + STD_RELON_EXCEPTION_PSERIES_OOL(0xf40, vsx_unavailable) #if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* From 0acb91112a148fbb31678e66839ef757f3be3aa4 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 4 Feb 2013 18:10:51 +0000 Subject: [PATCH 2764/4607] powerpc/kvm/book3s_hv: Preserve guest CFAR register value The CFAR (Come-From Address Register) is a useful debugging aid that exists on POWER7 processors. Currently HV KVM doesn't save or restore the CFAR register for guest vcpus, making the CFAR of limited use in guests. This adds the necessary code to capture the CFAR value saved in the early exception entry code (it has to be saved before any branch is executed), save it in the vcpu.arch struct, and restore it on entry to the guest. Signed-off-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/exception-64s.h | 8 ++++++-- arch/powerpc/include/asm/kvm_book3s_asm.h | 3 +++ arch/powerpc/include/asm/kvm_host.h | 1 + arch/powerpc/kernel/asm-offsets.c | 5 +++++ arch/powerpc/kvm/book3s_hv_rmhandlers.S | 9 +++++++++ 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/include/asm/exception-64s.h b/arch/powerpc/include/asm/exception-64s.h index 4dfc51588be5..05e6d2ee1db9 100644 --- a/arch/powerpc/include/asm/exception-64s.h +++ b/arch/powerpc/include/asm/exception-64s.h @@ -199,10 +199,14 @@ END_FTR_SECTION_NESTED(ftr,ftr,943) #define __KVM_HANDLER(area, h, n) \ do_kvm_##n: \ + BEGIN_FTR_SECTION_NESTED(947) \ + ld r10,area+EX_CFAR(r13); \ + std r10,HSTATE_CFAR(r13); \ + END_FTR_SECTION_NESTED(CPU_FTR_CFAR,CPU_FTR_CFAR,947); \ ld r10,area+EX_R10(r13); \ - stw r9,HSTATE_SCRATCH1(r13); \ + stw r9,HSTATE_SCRATCH1(r13); \ ld r9,area+EX_R9(r13); \ - std r12,HSTATE_SCRATCH0(r13); \ + std r12,HSTATE_SCRATCH0(r13); \ li r12,n; \ b kvmppc_interrupt diff --git a/arch/powerpc/include/asm/kvm_book3s_asm.h b/arch/powerpc/include/asm/kvm_book3s_asm.h index 88609b23b775..cdc3d2717cc6 100644 --- a/arch/powerpc/include/asm/kvm_book3s_asm.h +++ b/arch/powerpc/include/asm/kvm_book3s_asm.h @@ -93,6 +93,9 @@ struct kvmppc_host_state { u64 host_dscr; u64 dec_expires; #endif +#ifdef CONFIG_PPC_BOOK3S_64 + u64 cfar; +#endif }; struct kvmppc_book3s_shadow_vcpu { diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index ca9bf459db6a..03d7beae89a0 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -440,6 +440,7 @@ struct kvm_vcpu_arch { ulong uamor; u32 ctrl; ulong dabr; + ulong cfar; #endif u32 vrsave; /* also USPRG0 */ u32 mmucr; diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index beddba432518..e295a09b1f06 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -479,6 +479,7 @@ int main(void) DEFINE(VCPU_LAST_INST, offsetof(struct kvm_vcpu, arch.last_inst)); DEFINE(VCPU_TRAP, offsetof(struct kvm_vcpu, arch.trap)); DEFINE(VCPU_PTID, offsetof(struct kvm_vcpu, arch.ptid)); + DEFINE(VCPU_CFAR, offsetof(struct kvm_vcpu, arch.cfar)); DEFINE(VCORE_ENTRY_EXIT, offsetof(struct kvmppc_vcore, entry_exit_count)); DEFINE(VCORE_NAP_COUNT, offsetof(struct kvmppc_vcore, nap_count)); DEFINE(VCORE_IN_GUEST, offsetof(struct kvmppc_vcore, in_guest)); @@ -558,6 +559,10 @@ int main(void) DEFINE(IPI_PRIORITY, IPI_PRIORITY); #endif /* CONFIG_KVM_BOOK3S_64_HV */ +#ifdef CONFIG_PPC_BOOK3S_64 + HSTATE_FIELD(HSTATE_CFAR, cfar); +#endif /* CONFIG_PPC_BOOK3S_64 */ + #else /* CONFIG_PPC_BOOK3S */ DEFINE(VCPU_CR, offsetof(struct kvm_vcpu, arch.cr)); DEFINE(VCPU_XER, offsetof(struct kvm_vcpu, arch.xer)); diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 10b6c358dd77..e33d11f1b977 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -539,6 +539,11 @@ fast_guest_return: /* Enter guest */ +BEGIN_FTR_SECTION + ld r5, VCPU_CFAR(r4) + mtspr SPRN_CFAR, r5 +END_FTR_SECTION_IFSET(CPU_FTR_CFAR) + ld r5, VCPU_LR(r4) lwz r6, VCPU_CR(r4) mtlr r5 @@ -604,6 +609,10 @@ kvmppc_interrupt: lwz r4, HSTATE_SCRATCH1(r13) std r3, VCPU_GPR(R12)(r9) stw r4, VCPU_CR(r9) +BEGIN_FTR_SECTION + ld r3, HSTATE_CFAR(r13) + std r3, VCPU_CFAR(r9) +END_FTR_SECTION_IFSET(CPU_FTR_CFAR) /* Restore R1/R2 so we can handle faults */ ld r1, HSTATE_HOST_R1(r13) From deb26c274d48419c2711708a4564213d13ffebb4 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 4 Feb 2013 18:11:44 +0000 Subject: [PATCH 2765/4607] powerpc/kvm/book3s_pr: Fix compilation on 32-bit machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit a413f474a0 ("powerpc: Disable relocation on exceptions whenever PR KVM is active") added calls to pSeries_disable_reloc_on_exc() and pSeries_enable_reloc_on_exc() to book3s_pr.c, and added declarations of those functions to , but didn't add an include of to book3s_pr.c. 64-bit kernels seem to get hvcall.h included via some other path, but 32-bit kernels fail to compile with: arch/powerpc/kvm/book3s_pr.c: In function ‘kvmppc_core_init_vm’: arch/powerpc/kvm/book3s_pr.c:1300:4: error: implicit declaration of function ‘pSeries_disable_reloc_on_exc’ [-Werror=implicit-function-declaration] arch/powerpc/kvm/book3s_pr.c: In function ‘kvmppc_core_destroy_vm’: arch/powerpc/kvm/book3s_pr.c:1316:4: error: implicit declaration of function ‘pSeries_enable_reloc_on_exc’ [-Werror=implicit-function-declaration] cc1: all warnings being treated as errors make[2]: *** [arch/powerpc/kvm/book3s_pr.o] Error 1 make[1]: *** [arch/powerpc/kvm] Error 2 make: *** [sub-make] Error 2 This fixes it by adding an include of hvcall.h. Signed-off-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kvm/book3s_pr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 67e4708388a0..6702442ca818 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include From 4a564c4d1fc7f077c6135afe5c2890a262d71264 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 13 Feb 2013 17:03:16 +0000 Subject: [PATCH 2766/4607] powerpc/ps3: Add macro PS3_VERBOSE_RESULT To allow more control of the verbosity of ps3_result() add a check for the preprocessor macro PS3_VERBOSE_RESULT that builds a verbose verion of the ps3_result() routine. Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/ps3.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/ps3.h b/arch/powerpc/include/asm/ps3.h index 0e15db4d703b..678a7c1d9cb8 100644 --- a/arch/powerpc/include/asm/ps3.h +++ b/arch/powerpc/include/asm/ps3.h @@ -245,7 +245,7 @@ enum lv1_result { static inline const char* ps3_result(int result) { -#if defined(DEBUG) +#if defined(DEBUG) || defined(PS3_VERBOSE_RESULT) switch (result) { case LV1_SUCCESS: return "LV1_SUCCESS (0)"; From 97db7f7d056ceb017b1bec2657c58f64e7b160ba Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 13 Feb 2013 17:03:16 +0000 Subject: [PATCH 2767/4607] powerpc/ps3: Increase verbosity of htab errors Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/ps3/htab.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c index d00d7b0a3bda..6cc58201db8c 100644 --- a/arch/powerpc/platforms/ps3/htab.c +++ b/arch/powerpc/platforms/ps3/htab.c @@ -27,6 +27,7 @@ #include #include +#define PS3_VERBOSE_RESULT #include "platform.h" /** @@ -75,8 +76,9 @@ static long ps3_hpte_insert(unsigned long hpte_group, unsigned long vpn, if (result) { /* all entries bolted !*/ - pr_info("%s:result=%d vpn=%lx pa=%lx ix=%lx v=%llx r=%llx\n", - __func__, result, vpn, pa, hpte_group, hpte_v, hpte_r); + pr_info("%s:result=%s vpn=%lx pa=%lx ix=%lx v=%llx r=%llx\n", + __func__, ps3_result(result), vpn, pa, hpte_group, + hpte_v, hpte_r); BUG(); } @@ -125,8 +127,8 @@ static long ps3_hpte_updatepp(unsigned long slot, unsigned long newpp, &hpte_rs); if (result) { - pr_info("%s: res=%d read vpn=%lx slot=%lx psize=%d\n", - __func__, result, vpn, slot, psize); + pr_info("%s: result=%s read vpn=%lx slot=%lx psize=%d\n", + __func__, ps3_result(result), vpn, slot, psize); BUG(); } @@ -170,8 +172,8 @@ static void ps3_hpte_invalidate(unsigned long slot, unsigned long vpn, result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, slot, 0, 0); if (result) { - pr_info("%s: res=%d vpn=%lx slot=%lx psize=%d\n", - __func__, result, vpn, slot, psize); + pr_info("%s: result=%s vpn=%lx slot=%lx psize=%d\n", + __func__, ps3_result(result), vpn, slot, psize); BUG(); } From 69fde0210e9cac299d6713549a9cba612c1fd727 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 13 Feb 2013 17:03:16 +0000 Subject: [PATCH 2768/4607] powerpc/ps3: Refresh ps3_defconfig Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/configs/ps3_defconfig | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index c2f4b4a86ece..7a5c15fcc7cf 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig @@ -6,6 +6,7 @@ CONFIG_NR_CPUS=2 CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y +CONFIG_HIGH_RES_TIMERS=y CONFIG_BLK_DEV_INITRD=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_EMBEDDED=y @@ -24,12 +25,13 @@ CONFIG_PS3_DISK=y CONFIG_PS3_ROM=y CONFIG_PS3_FLASH=y CONFIG_PS3_VRAM=m +CONFIG_PS3_LPM=m # CONFIG_PPC_OF_BOOT_TRAMPOLINE is not set -CONFIG_HIGH_RES_TIMERS=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_BINFMT_MISC=y CONFIG_KEXEC=y # CONFIG_SPARSEMEM_VMEMMAP is not set +# CONFIG_COMPACTION is not set CONFIG_SCHED_SMT=y CONFIG_CMDLINE_BOOL=y CONFIG_CMDLINE="" @@ -59,6 +61,7 @@ CONFIG_BT_BNEP_PROTO_FILTER=y CONFIG_BT_HIDP=m CONFIG_BT_HCIBTUSB=m CONFIG_CFG80211=m +CONFIG_CFG80211_WEXT=y CONFIG_MAC80211=m CONFIG_MAC80211_RC_PID=y # CONFIG_MAC80211_RC_MINSTREL is not set @@ -78,7 +81,6 @@ CONFIG_MD=y CONFIG_BLK_DEV_DM=m CONFIG_NETDEVICES=y # CONFIG_NET_VENDOR_BROADCOM is not set -# CONFIG_NET_VENDOR_CHELSIO is not set # CONFIG_NET_VENDOR_INTEL is not set # CONFIG_NET_VENDOR_MARVELL is not set # CONFIG_NET_VENDOR_MICREL is not set @@ -119,21 +121,21 @@ CONFIG_SND=m # CONFIG_SND_DRIVERS is not set CONFIG_SND_USB_AUDIO=m CONFIG_HIDRAW=y -CONFIG_USB_HIDDEV=y CONFIG_HID_APPLE=m CONFIG_HID_BELKIN=m CONFIG_HID_CHERRY=m CONFIG_HID_EZKEY=m CONFIG_HID_TWINHAN=m CONFIG_HID_LOGITECH=m +CONFIG_HID_LOGITECH_DJ=m CONFIG_HID_MICROSOFT=m +CONFIG_HID_PS3REMOTE=m CONFIG_HID_SONY=m CONFIG_HID_SUNPLUS=m CONFIG_HID_SMARTJOYPLUS=m +CONFIG_USB_HIDDEV=y CONFIG_USB=m CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -CONFIG_USB_DEVICEFS=y -# CONFIG_USB_DEVICE_CLASS is not set CONFIG_USB_SUSPEND=y CONFIG_USB_MON=m CONFIG_USB_EHCI_HCD=m @@ -158,8 +160,8 @@ CONFIG_PROC_KCORE=y CONFIG_TMPFS=y CONFIG_HUGETLBFS=y CONFIG_NFS_FS=y -CONFIG_NFS_V3=y CONFIG_NFS_V4=y +CONFIG_NFS_SWAP=y CONFIG_ROOT_NFS=y CONFIG_CIFS=m CONFIG_NLS=y @@ -176,6 +178,7 @@ CONFIG_DEBUG_INFO=y CONFIG_DEBUG_WRITECOUNT=y CONFIG_DEBUG_MEMORY_INIT=y CONFIG_DEBUG_LIST=y +CONFIG_RCU_CPU_STALL_TIMEOUT=60 # CONFIG_FTRACE is not set CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_CRYPTO_CCM=m From 6a7e406419d8b176efbc5be41a82299025ad1b43 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 13 Feb 2013 17:03:16 +0000 Subject: [PATCH 2769/4607] powerpc: Move boot_paca into early_setup The powerpc boot_paca symbol is now only used within the early_setup() routine, so move it from its global definition into early_setup(). Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/paca.h | 1 - arch/powerpc/kernel/paca.c | 2 -- arch/powerpc/kernel/setup_64.c | 2 ++ 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h index c47d687ab01b..4b74c6c9d82a 100644 --- a/arch/powerpc/include/asm/paca.h +++ b/arch/powerpc/include/asm/paca.h @@ -167,7 +167,6 @@ struct paca_struct { }; extern struct paca_struct *paca; -extern __initdata struct paca_struct boot_paca; extern void initialise_paca(struct paca_struct *new_paca, int cpu); extern void setup_paca(struct paca_struct *new_paca); extern void allocate_pacas(void); diff --git a/arch/powerpc/kernel/paca.c b/arch/powerpc/kernel/paca.c index cd6da855090c..f8f24685f10a 100644 --- a/arch/powerpc/kernel/paca.c +++ b/arch/powerpc/kernel/paca.c @@ -120,8 +120,6 @@ struct slb_shadow slb_shadow[] __cacheline_aligned = { struct paca_struct *paca; EXPORT_SYMBOL(paca); -struct paca_struct boot_paca; - void __init initialise_paca(struct paca_struct *new_paca, int cpu) { /* The TOC register (GPR2) points 32kB into the TOC, so that 64kB diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 6da881b35dac..f2514e13062b 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -177,6 +177,8 @@ early_param("smt-enabled", early_smt_enabled); void __init early_setup(unsigned long dt_ptr) { + static __initdata struct paca_struct boot_paca; + /* -------- printk is _NOT_ safe to use here ! ------- */ /* Identify CPU type */ From 25e138149c19fa0680147b825be475f5fd57f155 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 12 Feb 2013 14:44:50 +0000 Subject: [PATCH 2770/4607] powerpc: Apply early paca fixups to boot_paca and the boot cpu's paca In commit 466921c we added a hack to set the paca data_offset to zero so that per-cpu accesses would work on the boot cpu prior to per-cpu areas being setup. This fixed a problem with lockdep touching per-cpu areas very early in boot. However if we combine CONFIG_LOCK_STAT=y with any of the PPC_EARLY_DEBUG options, we can hit the same problem in udbg_early_init(). To avoid that we need to set the data_offset of the boot_paca also. So factor out the fixup logic and call it for both the boot_paca, and "the paca of the boot cpu". Signed-off-by: Michael Ellerman Tested-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/setup_64.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index f2514e13062b..75fbaceb5c87 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -156,6 +156,15 @@ early_param("smt-enabled", early_smt_enabled); #define check_smt_enabled() #endif /* CONFIG_SMP */ +/** Fix up paca fields required for the boot cpu */ +static void fixup_boot_paca(void) +{ + /* The boot cpu is started */ + get_paca()->cpu_start = 1; + /* Allow percpu accesses to work until we setup percpu data */ + get_paca()->data_offset = 0; +} + /* * Early initialization entry point. This is called by head.S * with MMU translation disabled. We rely on the "feature" of @@ -187,6 +196,7 @@ void __init early_setup(unsigned long dt_ptr) /* Assume we're on cpu 0 for now. Don't write to the paca yet! */ initialise_paca(&boot_paca, 0); setup_paca(&boot_paca); + fixup_boot_paca(); /* Initialize lockdep early or else spinlocks will blow */ lockdep_init(); @@ -207,11 +217,7 @@ void __init early_setup(unsigned long dt_ptr) /* Now we know the logical id of our boot cpu, setup the paca. */ setup_paca(&paca[boot_cpuid]); - - /* Fix up paca fields required for the boot cpu */ - get_paca()->cpu_start = 1; - /* Allow percpu accesses to "work" until we setup percpu data */ - get_paca()->data_offset = 0; + fixup_boot_paca(); /* Probe the machine type */ probe_machine(); From 6a6d541f33111385ec2746caa39377ffcd30f095 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:29 +0000 Subject: [PATCH 2771/4607] powerpc: Add new CPU feature bit for transactional memory Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/cputable.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 5f1938fcce40..36360813382c 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -220,6 +220,13 @@ extern const char *powerpc_base_platform; #define PPC_FEATURE_HAS_EFP_DOUBLE_COMP 0 #endif +/* We only set the TM feature if the kernel was compiled with TM supprt */ +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +#define CPU_FTR_TM_COMP CPU_FTR_TM +#else +#define CPU_FTR_TM_COMP 0 +#endif + /* We need to mark all pages as being coherent if we're SMP or we have a * 74[45]x and an MPC107 host bridge. Also 83xx and PowerQUICC II * require it for PCI "streaming/prefetch" to work properly. From 14c39a4cf6bc4c3ef8ec40caa366047a87b0e031 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:30 +0000 Subject: [PATCH 2772/4607] powerpc: Add new instructions for transactional memory Here we define the new instructions we need for transactional memory in the kernel. This is so we can support compiling with binutils that don't support the new transactional memory instructions. Transactional memory results in two sets of architected state (GPRs/VSRs etc). treclaim allows us to read the checkpointed state (from the tbegin) so that we can store it away on a context switch. It does this by overwriting the exiting architected state, so you have to save that away before you treclaim. treclaim will also abort a transaction, so you can give a register value which contains an abort reason. trecheckpoint allows us to inject into the checkpointed state as if it were at the tbegin. It does this by copying the current architected state into the checkpointed state. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/ppc-opcode.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h index 0fd1928efb93..8752bc8e34a3 100644 --- a/arch/powerpc/include/asm/ppc-opcode.h +++ b/arch/powerpc/include/asm/ppc-opcode.h @@ -129,6 +129,9 @@ #define PPC_INST_TLBSRX_DOT 0x7c0006a5 #define PPC_INST_XXLOR 0xf0000510 #define PPC_INST_XVCPSGNDP 0xf0000780 +#define PPC_INST_TRECHKPT 0x7c0007dd +#define PPC_INST_TRECLAIM 0x7c00075d +#define PPC_INST_TABORT 0x7c00071d #define PPC_INST_NAP 0x4c000364 #define PPC_INST_SLEEP 0x4c0003a4 @@ -294,4 +297,11 @@ #define PPC_NAP stringify_in_c(.long PPC_INST_NAP) #define PPC_SLEEP stringify_in_c(.long PPC_INST_SLEEP) +/* Transactional memory instructions */ +#define TRECHKPT stringify_in_c(.long PPC_INST_TRECHKPT) +#define TRECLAIM(r) stringify_in_c(.long PPC_INST_TRECLAIM \ + | __PPC_RA(r)) +#define TABORT(r) stringify_in_c(.long PPC_INST_TABORT \ + | __PPC_RA(r)) + #endif /* _ASM_POWERPC_PPC_OPCODE_H */ From f4c3aff2230bfe696a0683073b77446248d7895c Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:31 +0000 Subject: [PATCH 2773/4607] powerpc: Add additional state needed for transactional memory to thread struct Set of new archtected state for saving away on context switch. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/processor.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 42ac53cebacd..fc41ab3aa114 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -246,6 +246,34 @@ struct thread_struct { unsigned long spefscr; /* SPE & eFP status */ int used_spe; /* set if process has used spe */ #endif /* CONFIG_SPE */ +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + u64 tm_tfhar; /* Transaction fail handler addr */ + u64 tm_texasr; /* Transaction exception & summary */ + u64 tm_tfiar; /* Transaction fail instr address reg */ + unsigned long tm_orig_msr; /* Thread's MSR on ctx switch */ + struct pt_regs ckpt_regs; /* Checkpointed registers */ + + /* + * Transactional FP and VSX 0-31 register set. + * NOTE: the sense of these is the opposite of the integer ckpt_regs! + * + * When a transaction is active/signalled/scheduled etc., *regs is the + * most recent set of/speculated GPRs with ckpt_regs being the older + * checkpointed regs to which we roll back if transaction aborts. + * + * However, fpr[] is the checkpointed 'base state' of FP regs, and + * transact_fpr[] is the new set of transactional values. + * VRs work the same way. + */ + double transact_fpr[32][TS_FPRWIDTH]; + struct { + unsigned int pad; + unsigned int val; /* Floating point status */ + } transact_fpscr; + vector128 transact_vr[32] __attribute__((aligned(16))); + vector128 transact_vscr __attribute__((aligned(16))); + unsigned long transact_vrsave; +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ #ifdef CONFIG_KVM_BOOK3S_32_HANDLER void* kvm_shadow_vcpu; /* KVM internal data */ #endif /* CONFIG_KVM_BOOK3S_32_HANDLER */ From 8b3c34cf0e0ab334a24aad7367cd06a5ba09a898 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:32 +0000 Subject: [PATCH 2774/4607] powerpc: New macros for transactional memory support This adds new macros for saving and restoring checkpointed architected state from and to the thread_struct. It also adds some debugging macros for when your brain explodes trying to debug your transactional memory enabled kernel. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/ppc_asm.h | 83 ++++++++++++++++++++++++++++ arch/powerpc/include/asm/processor.h | 1 + arch/powerpc/kernel/asm-offsets.c | 23 ++++++++ arch/powerpc/kernel/fpu.S | 12 ++++ arch/powerpc/kernel/process.c | 7 +++ arch/powerpc/kernel/traps.c | 8 +++ 6 files changed, 134 insertions(+) diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h index c2d0e58aba31..54219cea9e88 100644 --- a/arch/powerpc/include/asm/ppc_asm.h +++ b/arch/powerpc/include/asm/ppc_asm.h @@ -123,6 +123,89 @@ END_FW_FTR_SECTION_IFSET(FW_FEATURE_SPLPAR) #define REST_16VRS(n,b,base) REST_8VRS(n,b,base); REST_8VRS(n+8,b,base) #define REST_32VRS(n,b,base) REST_16VRS(n,b,base); REST_16VRS(n+16,b,base) +/* Save/restore FPRs, VRs and VSRs from their checkpointed backups in + * thread_struct: + */ +#define SAVE_FPR_TRANSACT(n, base) stfd n,THREAD_TRANSACT_FPR0+ \ + 8*TS_FPRWIDTH*(n)(base) +#define SAVE_2FPRS_TRANSACT(n, base) SAVE_FPR_TRANSACT(n, base); \ + SAVE_FPR_TRANSACT(n+1, base) +#define SAVE_4FPRS_TRANSACT(n, base) SAVE_2FPRS_TRANSACT(n, base); \ + SAVE_2FPRS_TRANSACT(n+2, base) +#define SAVE_8FPRS_TRANSACT(n, base) SAVE_4FPRS_TRANSACT(n, base); \ + SAVE_4FPRS_TRANSACT(n+4, base) +#define SAVE_16FPRS_TRANSACT(n, base) SAVE_8FPRS_TRANSACT(n, base); \ + SAVE_8FPRS_TRANSACT(n+8, base) +#define SAVE_32FPRS_TRANSACT(n, base) SAVE_16FPRS_TRANSACT(n, base); \ + SAVE_16FPRS_TRANSACT(n+16, base) + +#define REST_FPR_TRANSACT(n, base) lfd n,THREAD_TRANSACT_FPR0+ \ + 8*TS_FPRWIDTH*(n)(base) +#define REST_2FPRS_TRANSACT(n, base) REST_FPR_TRANSACT(n, base); \ + REST_FPR_TRANSACT(n+1, base) +#define REST_4FPRS_TRANSACT(n, base) REST_2FPRS_TRANSACT(n, base); \ + REST_2FPRS_TRANSACT(n+2, base) +#define REST_8FPRS_TRANSACT(n, base) REST_4FPRS_TRANSACT(n, base); \ + REST_4FPRS_TRANSACT(n+4, base) +#define REST_16FPRS_TRANSACT(n, base) REST_8FPRS_TRANSACT(n, base); \ + REST_8FPRS_TRANSACT(n+8, base) +#define REST_32FPRS_TRANSACT(n, base) REST_16FPRS_TRANSACT(n, base); \ + REST_16FPRS_TRANSACT(n+16, base) + + +#define SAVE_VR_TRANSACT(n,b,base) li b,THREAD_TRANSACT_VR0+(16*(n)); \ + stvx n,b,base +#define SAVE_2VRS_TRANSACT(n,b,base) SAVE_VR_TRANSACT(n,b,base); \ + SAVE_VR_TRANSACT(n+1,b,base) +#define SAVE_4VRS_TRANSACT(n,b,base) SAVE_2VRS_TRANSACT(n,b,base); \ + SAVE_2VRS_TRANSACT(n+2,b,base) +#define SAVE_8VRS_TRANSACT(n,b,base) SAVE_4VRS_TRANSACT(n,b,base); \ + SAVE_4VRS_TRANSACT(n+4,b,base) +#define SAVE_16VRS_TRANSACT(n,b,base) SAVE_8VRS_TRANSACT(n,b,base); \ + SAVE_8VRS_TRANSACT(n+8,b,base) +#define SAVE_32VRS_TRANSACT(n,b,base) SAVE_16VRS_TRANSACT(n,b,base); \ + SAVE_16VRS_TRANSACT(n+16,b,base) + +#define REST_VR_TRANSACT(n,b,base) li b,THREAD_TRANSACT_VR0+(16*(n)); \ + lvx n,b,base +#define REST_2VRS_TRANSACT(n,b,base) REST_VR_TRANSACT(n,b,base); \ + REST_VR_TRANSACT(n+1,b,base) +#define REST_4VRS_TRANSACT(n,b,base) REST_2VRS_TRANSACT(n,b,base); \ + REST_2VRS_TRANSACT(n+2,b,base) +#define REST_8VRS_TRANSACT(n,b,base) REST_4VRS_TRANSACT(n,b,base); \ + REST_4VRS_TRANSACT(n+4,b,base) +#define REST_16VRS_TRANSACT(n,b,base) REST_8VRS_TRANSACT(n,b,base); \ + REST_8VRS_TRANSACT(n+8,b,base) +#define REST_32VRS_TRANSACT(n,b,base) REST_16VRS_TRANSACT(n,b,base); \ + REST_16VRS_TRANSACT(n+16,b,base) + + +#define SAVE_VSR_TRANSACT(n,b,base) li b,THREAD_TRANSACT_VSR0+(16*(n)); \ + STXVD2X(n,R##base,R##b) +#define SAVE_2VSRS_TRANSACT(n,b,base) SAVE_VSR_TRANSACT(n,b,base); \ + SAVE_VSR_TRANSACT(n+1,b,base) +#define SAVE_4VSRS_TRANSACT(n,b,base) SAVE_2VSRS_TRANSACT(n,b,base); \ + SAVE_2VSRS_TRANSACT(n+2,b,base) +#define SAVE_8VSRS_TRANSACT(n,b,base) SAVE_4VSRS_TRANSACT(n,b,base); \ + SAVE_4VSRS_TRANSACT(n+4,b,base) +#define SAVE_16VSRS_TRANSACT(n,b,base) SAVE_8VSRS_TRANSACT(n,b,base); \ + SAVE_8VSRS_TRANSACT(n+8,b,base) +#define SAVE_32VSRS_TRANSACT(n,b,base) SAVE_16VSRS_TRANSACT(n,b,base); \ + SAVE_16VSRS_TRANSACT(n+16,b,base) + +#define REST_VSR_TRANSACT(n,b,base) li b,THREAD_TRANSACT_VSR0+(16*(n)); \ + LXVD2X(n,R##base,R##b) +#define REST_2VSRS_TRANSACT(n,b,base) REST_VSR_TRANSACT(n,b,base); \ + REST_VSR_TRANSACT(n+1,b,base) +#define REST_4VSRS_TRANSACT(n,b,base) REST_2VSRS_TRANSACT(n,b,base); \ + REST_2VSRS_TRANSACT(n+2,b,base) +#define REST_8VSRS_TRANSACT(n,b,base) REST_4VSRS_TRANSACT(n,b,base); \ + REST_4VSRS_TRANSACT(n+4,b,base) +#define REST_16VSRS_TRANSACT(n,b,base) REST_8VSRS_TRANSACT(n,b,base); \ + REST_8VSRS_TRANSACT(n+8,b,base) +#define REST_32VSRS_TRANSACT(n,b,base) REST_16VSRS_TRANSACT(n,b,base); \ + REST_16VSRS_TRANSACT(n+16,b,base) + /* Save the lower 32 VSRs in the thread VSR region */ #define SAVE_VSR(n,b,base) li b,THREAD_VSR0+(16*(n)); STXVD2X(n,R##base,R##b) #define SAVE_2VSRS(n,b,base) SAVE_VSR(n,b,base); SAVE_VSR(n+1,b,base) diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index fc41ab3aa114..7ff9eaa3ea6c 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -152,6 +152,7 @@ typedef struct { #define TS_FPROFFSET 0 #define TS_VSRLOWOFFSET 1 #define TS_FPR(i) fpr[i][TS_FPROFFSET] +#define TS_TRANS_FPR(i) transact_fpr[i][TS_FPROFFSET] struct thread_struct { unsigned long ksp; /* Kernel stack pointer */ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index e295a09b1f06..0fdc97496d7c 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -125,6 +125,29 @@ int main(void) #ifdef CONFIG_PPC_BOOK3S_64 DEFINE(THREAD_TAR, offsetof(struct thread_struct, tar)); #endif +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + DEFINE(THREAD_TM_TFHAR, offsetof(struct thread_struct, tm_tfhar)); + DEFINE(THREAD_TM_TEXASR, offsetof(struct thread_struct, tm_texasr)); + DEFINE(THREAD_TM_TFIAR, offsetof(struct thread_struct, tm_tfiar)); + DEFINE(PT_CKPT_REGS, offsetof(struct thread_struct, ckpt_regs)); + DEFINE(THREAD_TRANSACT_VR0, offsetof(struct thread_struct, + transact_vr[0])); + DEFINE(THREAD_TRANSACT_VSCR, offsetof(struct thread_struct, + transact_vscr)); + DEFINE(THREAD_TRANSACT_VRSAVE, offsetof(struct thread_struct, + transact_vrsave)); + DEFINE(THREAD_TRANSACT_FPR0, offsetof(struct thread_struct, + transact_fpr[0])); + DEFINE(THREAD_TRANSACT_FPSCR, offsetof(struct thread_struct, + transact_fpscr)); +#ifdef CONFIG_VSX + DEFINE(THREAD_TRANSACT_VSR0, offsetof(struct thread_struct, + transact_fpr[0])); +#endif + /* Local pt_regs on stack for Transactional Memory funcs. */ + DEFINE(TM_FRAME_SIZE, STACK_FRAME_OVERHEAD + + sizeof(struct pt_regs) + 16); +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_LOCAL_FLAGS, offsetof(struct thread_info, local_flags)); diff --git a/arch/powerpc/kernel/fpu.S b/arch/powerpc/kernel/fpu.S index e0ada05f2df3..adb155195394 100644 --- a/arch/powerpc/kernel/fpu.S +++ b/arch/powerpc/kernel/fpu.S @@ -35,6 +35,15 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ 2: REST_32VSRS(n,c,base); \ 3: +#define __REST_32FPVSRS_TRANSACT(n,c,base) \ +BEGIN_FTR_SECTION \ + b 2f; \ +END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ + REST_32FPRS_TRANSACT(n,base); \ + b 3f; \ +2: REST_32VSRS_TRANSACT(n,c,base); \ +3: + #define __SAVE_32FPVSRS(n,c,base) \ BEGIN_FTR_SECTION \ b 2f; \ @@ -45,9 +54,12 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ 3: #else #define __REST_32FPVSRS(n,b,base) REST_32FPRS(n, base) +#define __REST_32FPVSRS_TRANSACT(n,b,base) REST_32FPRS(n, base) #define __SAVE_32FPVSRS(n,b,base) SAVE_32FPRS(n, base) #endif #define REST_32FPVSRS(n,c,base) __REST_32FPVSRS(n,__REG_##c,__REG_##base) +#define REST_32FPVSRS_TRANSACT(n,c,base) \ + __REST_32FPVSRS_TRANSACT(n,__REG_##c,__REG_##base) #define SAVE_32FPVSRS(n,c,base) __SAVE_32FPVSRS(n,__REG_##c,__REG_##base) /* diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 96e31de89b43..b0a0321e4bb6 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -57,6 +57,13 @@ #include #include +/* Transactional Memory debug */ +#ifdef TM_DEBUG_SW +#define TM_DEBUG(x...) printk(KERN_INFO x) +#else +#define TM_DEBUG(x...) do { } while(0) +#endif + extern unsigned long _get_SP(void); #ifndef CONFIG_SMP diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index a008cf5c0fce..bd5de5deaf51 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -78,6 +78,13 @@ EXPORT_SYMBOL(__debugger_break_match); EXPORT_SYMBOL(__debugger_fault_handler); #endif +/* Transactional Memory trap debug */ +#ifdef TM_DEBUG_SW +#define TM_DEBUG(x...) printk(KERN_INFO x) +#else +#define TM_DEBUG(x...) do { } while(0) +#endif + /* * Trap & Exception support */ @@ -350,6 +357,7 @@ static inline int check_io_access(struct pt_regs *regs) exception is in the MSR. */ #define get_reason(regs) ((regs)->msr) #define get_mc_reason(regs) ((regs)->msr) +#define REASON_TM 0x200000 #define REASON_FP 0x100000 #define REASON_ILLEGAL 0x80000 #define REASON_PRIVILEGED 0x40000 From 97a0aac9b844a8bcfebb6805322998f8ca4db9dc Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:33 +0000 Subject: [PATCH 2775/4607] powerpc: Register defines for various transactional memory registers Defines for MSR bits and transactional memory related SPRs TFIAR, TEXASR and TEXASRU. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/reg.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index e09ac51961d7..7844c285f6e1 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -29,6 +29,10 @@ #define MSR_SF_LG 63 /* Enable 64 bit mode */ #define MSR_ISF_LG 61 /* Interrupt 64b mode valid on 630 */ #define MSR_HV_LG 60 /* Hypervisor state */ +#define MSR_TS_T_LG 34 /* Trans Mem state: Transactional */ +#define MSR_TS_S_LG 33 /* Trans Mem state: Suspended */ +#define MSR_TS_LG 33 /* Trans Mem state (2 bits) */ +#define MSR_TM_LG 32 /* Trans Mem Available */ #define MSR_VEC_LG 25 /* Enable AltiVec */ #define MSR_VSX_LG 23 /* Enable VSX */ #define MSR_POW_LG 18 /* Enable Power Management */ @@ -98,6 +102,25 @@ #define MSR_RI __MASK(MSR_RI_LG) /* Recoverable Exception */ #define MSR_LE __MASK(MSR_LE_LG) /* Little Endian */ +#define MSR_TM __MASK(MSR_TM_LG) /* Transactional Mem Available */ +#define MSR_TS_N 0 /* Non-transactional */ +#define MSR_TS_S __MASK(MSR_TS_S_LG) /* Transaction Suspended */ +#define MSR_TS_T __MASK(MSR_TS_T_LG) /* Transaction Transactional */ +#define MSR_TS_MASK (MSR_TS_T | MSR_TS_S) /* Transaction State bits */ +#define MSR_TM_ACTIVE(x) (((x) & MSR_TS_MASK) != 0) /* Transaction active? */ +#define MSR_TM_TRANSACTIONAL(x) (((x) & MSR_TS_MASK) == MSR_TS_T) +#define MSR_TM_SUSPENDED(x) (((x) & MSR_TS_MASK) == MSR_TS_S) + +/* Reason codes describing kernel causes for transaction aborts. By + convention, bit0 is copied to TEXASR[56] (IBM bit 7) which is set if + the failure is persistent. +*/ +#define TM_CAUSE_RESCHED 0xfe +#define TM_CAUSE_TLBI 0xfc +#define TM_CAUSE_FAC_UNAV 0xfa +#define TM_CAUSE_SYSCALL 0xf9 /* Persistent */ +#define TM_CAUSE_MISC 0xf6 + #if defined(CONFIG_PPC_BOOK3S_64) #define MSR_64BIT MSR_SF @@ -193,6 +216,10 @@ #define SPRN_UAMOR 0x9d /* User Authority Mask Override Register */ #define SPRN_AMOR 0x15d /* Authority Mask Override Register */ #define SPRN_ACOP 0x1F /* Available Coprocessor Register */ +#define SPRN_TFIAR 0x81 /* Transaction Failure Inst Addr */ +#define SPRN_TEXASR 0x82 /* Transaction EXception & Summary */ +#define SPRN_TEXASRU 0x83 /* '' '' '' Upper 32 */ +#define SPRN_TFHAR 0x80 /* Transaction Failure Handler Addr */ #define SPRN_CTRLF 0x088 #define SPRN_CTRLT 0x098 #define CTRL_CT 0xc0000000 /* current thread */ From afc07701ced6463786d09a3b9baf894c1397e991 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:34 +0000 Subject: [PATCH 2776/4607] powerpc: Add transactional memory paca scratch register to show_regs Add transactional memory paca scratch register to show_regs. This is useful for debugging. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/paca.h | 3 +++ arch/powerpc/kernel/asm-offsets.c | 1 + arch/powerpc/kernel/entry_64.S | 4 ++++ arch/powerpc/kernel/process.c | 3 +++ 4 files changed, 11 insertions(+) diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h index 4b74c6c9d82a..77c91e74b612 100644 --- a/arch/powerpc/include/asm/paca.h +++ b/arch/powerpc/include/asm/paca.h @@ -137,6 +137,9 @@ struct paca_struct { u8 irq_work_pending; /* IRQ_WORK interrupt while soft-disable */ u8 nap_state_lost; /* NV GPR values lost in power7_idle */ u64 sprg3; /* Saved user-visible sprg */ +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + u64 tm_scratch; /* TM scratch area for reclaim */ +#endif #ifdef CONFIG_PPC_POWERNV /* Pointer to OPAL machine check event structure set by the diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 0fdc97496d7c..781190367292 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -126,6 +126,7 @@ int main(void) DEFINE(THREAD_TAR, offsetof(struct thread_struct, tar)); #endif #ifdef CONFIG_PPC_TRANSACTIONAL_MEM + DEFINE(PACATMSCRATCH, offsetof(struct paca_struct, tm_scratch)); DEFINE(THREAD_TM_TFHAR, offsetof(struct thread_struct, tm_tfhar)); DEFINE(THREAD_TM_TEXASR, offsetof(struct thread_struct, tm_texasr)); DEFINE(THREAD_TM_TFIAR, offsetof(struct thread_struct, tm_tfiar)); diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 9ae8451bbc83..612ea130c5d2 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -785,6 +785,10 @@ fast_exception_return: andc r4,r4,r0 /* r0 contains MSR_RI here */ mtmsrd r4,1 +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + /* TM debug */ + std r3, PACATMSCRATCH(r13) /* Stash returned-to MSR */ +#endif /* * r13 is our per cpu area, only restore it if we are returning to * userspace the value stored in the stack frame may belong to diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index b0a0321e4bb6..1cc40533021b 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -753,6 +753,9 @@ void show_regs(struct pt_regs * regs) */ printk("NIP ["REG"] %pS\n", regs->nip, (void *)regs->nip); printk("LR ["REG"] %pS\n", regs->link, (void *)regs->link); +#endif +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + printk("PACATMSCRATCH [%llx]\n", get_paca()->tm_scratch); #endif show_stack(current, (unsigned long *) regs->gpr[1]); if (!user_mode(regs)) From 98ae22e15b430bfed5def01ac1a88ec9396284c8 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:35 +0000 Subject: [PATCH 2777/4607] powerpc: Add helper functions for transactional memory context switching Here we add the helper functions to be used when context switching. These allow us to fully reclaim and recheckpoint a transaction. We introduce a new paca field called tm_scratch to help us store away register values when doing the low level tm reclaim register save. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/reg.h | 2 +- arch/powerpc/include/asm/tm.h | 20 ++ arch/powerpc/kernel/Makefile | 2 + arch/powerpc/kernel/tm.S | 388 +++++++++++++++++++++++++++++++++ 4 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 arch/powerpc/include/asm/tm.h create mode 100644 arch/powerpc/kernel/tm.S diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 7844c285f6e1..eee2a60994bf 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -797,7 +797,7 @@ * HV mode in which case it is HSPRG0 * * 64-bit server: - * - SPRG0 unused (reserved for HV on Power4) + * - SPRG0 scratch for TM recheckpoint/reclaim (reserved for HV on Power4) * - SPRG2 scratch for exception vectors * - SPRG3 CPU and NUMA node for VDSO getcpu (user visible) * - HSPRG0 stores PACA in HV mode diff --git a/arch/powerpc/include/asm/tm.h b/arch/powerpc/include/asm/tm.h new file mode 100644 index 000000000000..4b4449abf3f8 --- /dev/null +++ b/arch/powerpc/include/asm/tm.h @@ -0,0 +1,20 @@ +/* + * Transactional memory support routines to reclaim and recheckpoint + * transactional process state. + * + * Copyright 2012 Matt Evans & Michael Neuling, IBM Corporation. + */ + +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +extern void do_load_up_transact_fpu(struct thread_struct *thread); +extern void do_load_up_transact_altivec(struct thread_struct *thread); +#endif + +extern void tm_enable(void); +extern void tm_reclaim(struct thread_struct *thread, + unsigned long orig_msr, uint8_t cause); +extern void tm_recheckpoint(struct thread_struct *thread, + unsigned long orig_msr); +extern void tm_abort(uint8_t cause); +extern void tm_save_sprs(struct thread_struct *thread); +extern void tm_restore_sprs(struct thread_struct *thread); diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index b4f0e360e414..f960a7944553 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -121,6 +121,8 @@ ifneq ($(CONFIG_PPC_INDIRECT_IO),y) obj-y += iomap.o endif +obj64-$(CONFIG_PPC_TRANSACTIONAL_MEM) += tm.o + obj-$(CONFIG_PPC64) += $(obj64-y) obj-$(CONFIG_PPC32) += $(obj32-y) diff --git a/arch/powerpc/kernel/tm.S b/arch/powerpc/kernel/tm.S new file mode 100644 index 000000000000..84dbace657ce --- /dev/null +++ b/arch/powerpc/kernel/tm.S @@ -0,0 +1,388 @@ +/* + * Transactional memory support routines to reclaim and recheckpoint + * transactional process state. + * + * Copyright 2012 Matt Evans & Michael Neuling, IBM Corporation. + */ + +#include +#include +#include +#include +#include + +#ifdef CONFIG_VSX +/* See fpu.S, this is very similar but to save/restore checkpointed FPRs/VSRs */ +#define __SAVE_32FPRS_VSRS_TRANSACT(n,c,base) \ +BEGIN_FTR_SECTION \ + b 2f; \ +END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ + SAVE_32FPRS_TRANSACT(n,base); \ + b 3f; \ +2: SAVE_32VSRS_TRANSACT(n,c,base); \ +3: +/* ...and this is just plain borrowed from there. */ +#define __REST_32FPRS_VSRS(n,c,base) \ +BEGIN_FTR_SECTION \ + b 2f; \ +END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ + REST_32FPRS(n,base); \ + b 3f; \ +2: REST_32VSRS(n,c,base); \ +3: +#else +#define __SAVE_32FPRS_VSRS_TRANSACT(n,c,base) SAVE_32FPRS_TRANSACT(n, base) +#define __REST_32FPRS_VSRS(n,c,base) REST_32FPRS(n, base) +#endif +#define SAVE_32FPRS_VSRS_TRANSACT(n,c,base) \ + __SAVE_32FPRS_VSRS_TRANSACT(n,__REG_##c,__REG_##base) +#define REST_32FPRS_VSRS(n,c,base) \ + __REST_32FPRS_VSRS(n,__REG_##c,__REG_##base) + +/* Stack frame offsets for local variables. */ +#define TM_FRAME_L0 TM_FRAME_SIZE-16 +#define TM_FRAME_L1 TM_FRAME_SIZE-8 +#define STACK_PARAM(x) (48+((x)*8)) + + +/* In order to access the TM SPRs, TM must be enabled. So, do so: */ +_GLOBAL(tm_enable) + mfmsr r4 + li r3, MSR_TM >> 32 + sldi r3, r3, 32 + and. r0, r4, r3 + bne 1f + or r4, r4, r3 + mtmsrd r4 +1: blr + +_GLOBAL(tm_save_sprs) + mfspr r0, SPRN_TFHAR + std r0, THREAD_TM_TFHAR(r3) + mfspr r0, SPRN_TEXASR + std r0, THREAD_TM_TEXASR(r3) + mfspr r0, SPRN_TFIAR + std r0, THREAD_TM_TFIAR(r3) + blr + +_GLOBAL(tm_restore_sprs) + ld r0, THREAD_TM_TFHAR(r3) + mtspr SPRN_TFHAR, r0 + ld r0, THREAD_TM_TEXASR(r3) + mtspr SPRN_TEXASR, r0 + ld r0, THREAD_TM_TFIAR(r3) + mtspr SPRN_TFIAR, r0 + blr + + /* Passed an 8-bit failure cause as first argument. */ +_GLOBAL(tm_abort) + TABORT(R3) + blr + + +/* void tm_reclaim(struct thread_struct *thread, + * unsigned long orig_msr, + * uint8_t cause) + * + * - Performs a full reclaim. This destroys outstanding + * transactions and updates thread->regs.tm_ckpt_* with the + * original checkpointed state. Note that thread->regs is + * unchanged. + * - FP regs are written back to thread->transact_fpr before + * reclaiming. These are the transactional (current) versions. + * + * Purpose is to both abort transactions of, and preserve the state of, + * a transactions at a context switch. We preserve/restore both sets of process + * state to restore them when the thread's scheduled again. We continue in + * userland as though nothing happened, but when the transaction is resumed + * they will abort back to the checkpointed state we save out here. + * + * Call with IRQs off, stacks get all out of sync for some periods in here! + */ +_GLOBAL(tm_reclaim) + mfcr r6 + mflr r0 + std r6, 8(r1) + std r0, 16(r1) + std r2, 40(r1) + stdu r1, -TM_FRAME_SIZE(r1) + + /* We've a struct pt_regs at [r1+STACK_FRAME_OVERHEAD]. */ + + std r3, STACK_PARAM(0)(r1) + SAVE_NVGPRS(r1) + + mfmsr r14 + mr r15, r14 + ori r15, r15, MSR_FP + oris r15, r15, MSR_VEC@h +#ifdef CONFIG_VSX + BEGIN_FTR_SECTION + oris r15,r15, MSR_VSX@h + END_FTR_SECTION_IFSET(CPU_FTR_VSX) +#endif + mtmsrd r15 + std r14, TM_FRAME_L0(r1) + + /* Stash the stack pointer away for use after reclaim */ + std r1, PACAR1(r13) + + /* ******************** FPR/VR/VSRs ************ + * Before reclaiming, capture the current/transactional FPR/VR + * versions /if used/. + * + * (If VSX used, FP and VMX are implied. Or, we don't need to look + * at MSR.VSX as copying FP regs if .FP, vector regs if .VMX covers it.) + * + * We're passed the thread's MSR as parameter 2. + * + * We enabled VEC/FP/VSX in the msr above, so we can execute these + * instructions! + */ + andis. r0, r4, MSR_VEC@h + beq dont_backup_vec + + SAVE_32VRS_TRANSACT(0, r6, r3) /* r6 scratch, r3 thread */ + mfvscr vr0 + li r6, THREAD_TRANSACT_VSCR + stvx vr0, r3, r6 + mfspr r0, SPRN_VRSAVE + std r0, THREAD_TRANSACT_VRSAVE(r3) + +dont_backup_vec: + andi. r0, r4, MSR_FP + beq dont_backup_fp + + SAVE_32FPRS_VSRS_TRANSACT(0, R6, R3) /* r6 scratch, r3 thread */ + + mffs fr0 + stfd fr0,THREAD_TRANSACT_FPSCR(r3) + +dont_backup_fp: + /* The moment we treclaim, ALL of our GPRs will switch + * to user register state. (FPRs, CCR etc. also!) + * Use an sprg and a tm_scratch in the PACA to shuffle. + */ + TRECLAIM(R5) /* Cause in r5 */ + + /* ******************** GPRs ******************** */ + /* Stash the checkpointed r13 away in the scratch SPR and get the real + * paca + */ + SET_SCRATCH0(r13) + GET_PACA(r13) + + /* Stash the checkpointed r1 away in paca tm_scratch and get the real + * stack pointer back + */ + std r1, PACATMSCRATCH(r13) + ld r1, PACAR1(r13) + + /* Now get some more GPRS free */ + std r7, GPR7(r1) /* Temporary stash */ + std r12, GPR12(r1) /* '' '' '' */ + ld r12, STACK_PARAM(0)(r1) /* Param 0, thread_struct * */ + + addi r7, r12, PT_CKPT_REGS /* Thread's ckpt_regs */ + + /* Make r7 look like an exception frame so that we + * can use the neat GPRx(n) macros. r7 is NOT a pt_regs ptr! + */ + subi r7, r7, STACK_FRAME_OVERHEAD + + /* Sync the userland GPRs 2-12, 14-31 to thread->regs: */ + SAVE_GPR(0, r7) /* user r0 */ + SAVE_GPR(2, r7) /* user r2 */ + SAVE_4GPRS(3, r7) /* user r3-r6 */ + SAVE_4GPRS(8, r7) /* user r8-r11 */ + ld r3, PACATMSCRATCH(r13) /* user r1 */ + ld r4, GPR7(r1) /* user r7 */ + ld r5, GPR12(r1) /* user r12 */ + GET_SCRATCH0(6) /* user r13 */ + std r3, GPR1(r7) + std r4, GPR7(r7) + std r5, GPR12(r7) + std r6, GPR13(r7) + + SAVE_NVGPRS(r7) /* user r14-r31 */ + + /* ******************** NIP ******************** */ + mfspr r3, SPRN_TFHAR + std r3, _NIP(r7) /* Returns to failhandler */ + /* The checkpointed NIP is ignored when rescheduling/rechkpting, + * but is used in signal return to 'wind back' to the abort handler. + */ + + /* ******************** CR,LR,CCR,MSR ********** */ + mfctr r3 + mflr r4 + mfcr r5 + mfxer r6 + + std r3, _CTR(r7) + std r4, _LINK(r7) + std r5, _CCR(r7) + std r6, _XER(r7) + + /* MSR and flags: We don't change CRs, and we don't need to alter + * MSR. + */ + + /* TM regs, incl TEXASR -- these live in thread_struct. Note they've + * been updated by the treclaim, to explain to userland the failure + * cause (aborted). + */ + mfspr r0, SPRN_TEXASR + mfspr r3, SPRN_TFHAR + mfspr r4, SPRN_TFIAR + std r0, THREAD_TM_TEXASR(r12) + std r3, THREAD_TM_TFHAR(r12) + std r4, THREAD_TM_TFIAR(r12) + + /* AMR and PPR are checkpointed too, but are unsupported by Linux. */ + + /* Restore original MSR/IRQ state & clear TM mode */ + ld r14, TM_FRAME_L0(r1) /* Orig MSR */ + li r15, 0 + rldimi r14, r15, MSR_TS_LG, (63-MSR_TS_LG)-1 + mtmsrd r14 + + REST_NVGPRS(r1) + + addi r1, r1, TM_FRAME_SIZE + ld r4, 8(r1) + ld r0, 16(r1) + mtcr r4 + mtlr r0 + ld r2, 40(r1) + blr + + + /* void tm_recheckpoint(struct thread_struct *thread, + * unsigned long orig_msr) + * - Restore the checkpointed register state saved by tm_reclaim + * when we switch_to a process. + * + * Call with IRQs off, stacks get all out of sync for + * some periods in here! + */ +_GLOBAL(tm_recheckpoint) + mfcr r5 + mflr r0 + std r5, 8(r1) + std r0, 16(r1) + std r2, 40(r1) + stdu r1, -TM_FRAME_SIZE(r1) + + /* We've a struct pt_regs at [r1+STACK_FRAME_OVERHEAD]. + * This is used for backing up the NVGPRs: + */ + SAVE_NVGPRS(r1) + + std r1, PACAR1(r13) + + /* Load complete register state from ts_ckpt* registers */ + + addi r7, r3, PT_CKPT_REGS /* Thread's ckpt_regs */ + + /* Make r7 look like an exception frame so that we + * can use the neat GPRx(n) macros. r7 is now NOT a pt_regs ptr! + */ + subi r7, r7, STACK_FRAME_OVERHEAD + + SET_SCRATCH0(r1) + + mfmsr r6 + /* R4 = original MSR to indicate whether thread used FP/Vector etc. */ + + /* Enable FP/vec in MSR if necessary! */ + lis r5, MSR_VEC@h + ori r5, r5, MSR_FP + and. r5, r4, r5 + beq restore_gprs /* if neither, skip both */ + +#ifdef CONFIG_VSX + BEGIN_FTR_SECTION + oris r5, r5, MSR_VSX@h + END_FTR_SECTION_IFSET(CPU_FTR_VSX) +#endif + or r5, r6, r5 /* Set MSR.FP+.VSX/.VEC */ + mtmsr r5 + + /* FP and VEC registers: These are recheckpointed from thread.fpr[] + * and thread.vr[] respectively. The thread.transact_fpr[] version + * is more modern, and will be loaded subsequently by any FPUnavailable + * trap. + */ + andis. r0, r4, MSR_VEC@h + beq dont_restore_vec + + li r5, THREAD_VSCR + lvx vr0, r3, r5 + mtvscr vr0 + REST_32VRS(0, r5, r3) /* r5 scratch, r3 THREAD ptr */ + ld r5, THREAD_VRSAVE(r3) + mtspr SPRN_VRSAVE, r5 + +dont_restore_vec: + andi. r0, r4, MSR_FP + beq dont_restore_fp + + lfd fr0, THREAD_FPSCR(r3) + MTFSF_L(fr0) + REST_32FPRS_VSRS(0, R4, R3) + +dont_restore_fp: + mtmsr r6 /* FP/Vec off again! */ + +restore_gprs: + /* ******************** CR,LR,CCR,MSR ********** */ + ld r3, _CTR(r7) + ld r4, _LINK(r7) + ld r5, _CCR(r7) + ld r6, _XER(r7) + + mtctr r3 + mtlr r4 + mtcr r5 + mtxer r6 + + /* MSR and flags: We don't change CRs, and we don't need to alter + * MSR. + */ + + REST_4GPRS(0, r7) /* GPR0-3 */ + REST_GPR(4, r7) /* GPR4-6 */ + REST_GPR(5, r7) + REST_GPR(6, r7) + REST_4GPRS(8, r7) /* GPR8-11 */ + REST_2GPRS(12, r7) /* GPR12-13 */ + + REST_NVGPRS(r7) /* GPR14-31 */ + + ld r7, GPR7(r7) /* GPR7 */ + + /* Commit register state as checkpointed state: */ + TRECHKPT + + /* Our transactional state has now changed. + * + * Now just get out of here. Transactional (current) state will be + * updated once restore is called on the return path in the _switch-ed + * -to process. + */ + + GET_PACA(r13) + GET_SCRATCH0(r1) + + REST_NVGPRS(r1) + + addi r1, r1, TM_FRAME_SIZE + ld r4, 8(r1) + ld r0, 16(r1) + mtcr r4 + mtlr r0 + ld r2, 40(r1) + blr + + /* ****************************************************************** */ From a2dcbb32f0647a3b8230c146a2f980654a7738af Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:36 +0000 Subject: [PATCH 2778/4607] powerpc: Add FP/VSX and VMX register load functions for transactional memory This adds functions to restore the state of the FP/VSX registers from what's stored in the thread_struct. Two version for FP/VSX are required since one restores them from transactional/checkpoint side of the thread_struct and the other from the speculated side. Similar functions are added for VMX registers. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/fpu.S | 54 ++++++++++++++++++++++++++++++++++++ arch/powerpc/kernel/vector.S | 51 ++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/arch/powerpc/kernel/fpu.S b/arch/powerpc/kernel/fpu.S index adb155195394..caeaabf11a2f 100644 --- a/arch/powerpc/kernel/fpu.S +++ b/arch/powerpc/kernel/fpu.S @@ -62,6 +62,60 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX); \ __REST_32FPVSRS_TRANSACT(n,__REG_##c,__REG_##base) #define SAVE_32FPVSRS(n,c,base) __SAVE_32FPVSRS(n,__REG_##c,__REG_##base) +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +/* + * Wrapper to call load_up_fpu from C. + * void do_load_up_fpu(struct pt_regs *regs); + */ +_GLOBAL(do_load_up_fpu) + mflr r0 + std r0, 16(r1) + stdu r1, -112(r1) + + subi r6, r3, STACK_FRAME_OVERHEAD + /* load_up_fpu expects r12=MSR, r13=PACA, and returns + * with r12 = new MSR. + */ + ld r12,_MSR(r6) + GET_PACA(r13) + + bl load_up_fpu + std r12,_MSR(r6) + + ld r0, 112+16(r1) + addi r1, r1, 112 + mtlr r0 + blr + + +/* void do_load_up_transact_fpu(struct thread_struct *thread) + * + * This is similar to load_up_fpu but for the transactional version of the FP + * register set. It doesn't mess with the task MSR or valid flags. + * Furthermore, we don't do lazy FP with TM currently. + */ +_GLOBAL(do_load_up_transact_fpu) + mfmsr r6 + ori r5,r6,MSR_FP +#ifdef CONFIG_VSX +BEGIN_FTR_SECTION + oris r5,r5,MSR_VSX@h +END_FTR_SECTION_IFSET(CPU_FTR_VSX) +#endif + SYNC + MTMSRD(r5) + + lfd fr0,THREAD_TRANSACT_FPSCR(r3) + MTFSF_L(fr0) + REST_32FPVSRS_TRANSACT(0, R4, R3) + + /* FP/VSX off again */ + MTMSRD(r6) + SYNC + + blr +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ + /* * This task wants to use the FPU now. * On UP, disable FP for the task which had the FPU previously, diff --git a/arch/powerpc/kernel/vector.S b/arch/powerpc/kernel/vector.S index e830289d2e48..9e20999aaef2 100644 --- a/arch/powerpc/kernel/vector.S +++ b/arch/powerpc/kernel/vector.S @@ -7,6 +7,57 @@ #include #include +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +/* + * Wrapper to call load_up_altivec from C. + * void do_load_up_altivec(struct pt_regs *regs); + */ +_GLOBAL(do_load_up_altivec) + mflr r0 + std r0, 16(r1) + stdu r1, -112(r1) + + subi r6, r3, STACK_FRAME_OVERHEAD + /* load_up_altivec expects r12=MSR, r13=PACA, and returns + * with r12 = new MSR. + */ + ld r12,_MSR(r6) + GET_PACA(r13) + bl load_up_altivec + std r12,_MSR(r6) + + ld r0, 112+16(r1) + addi r1, r1, 112 + mtlr r0 + blr + +/* void do_load_up_transact_altivec(struct thread_struct *thread) + * + * This is similar to load_up_altivec but for the transactional version of the + * vector regs. It doesn't mess with the task MSR or valid flags. + * Furthermore, VEC laziness is not supported with TM currently. + */ +_GLOBAL(do_load_up_transact_altivec) + mfmsr r6 + oris r5,r6,MSR_VEC@h + MTMSRD(r5) + isync + + li r4,1 + stw r4,THREAD_USED_VR(r3) + + li r10,THREAD_TRANSACT_VSCR + lvx vr0,r10,r3 + mtvscr vr0 + REST_32VRS_TRANSACT(0,r4,r3) + + /* Disable VEC again. */ + MTMSRD(r6) + isync + + blr +#endif + /* * load_up_altivec(unused, unused, tsk) * Disable VMX for the task which had it previously, From fb09692e71f13af7298eb603a1975850b1c7a8d8 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:37 +0000 Subject: [PATCH 2779/4607] powerpc: Add reclaim and recheckpoint functions for context switching transactional memory processes When we switch out a task, we need to save both the checkpointed and the speculated state into the thread struct. Similarly when we are switching in a task we need to load both the checkpointed and speculated state. If the task was using FP, we non-lazily reload both the original and the speculative FP register states. This is because the kernel doesn't see if/when a TM rollback occurs, so if we take an FP unavoidable later, we are unable to determine which set of FP regs need to be restored. This simply adds these functions. It doesn't hook them into the existing code yet. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/process.c | 112 ++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 1cc40533021b..48a987579e4f 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #ifdef CONFIG_PPC64 #include @@ -467,6 +468,117 @@ static inline bool hw_brk_match(struct arch_hw_breakpoint *a, return false; return true; } +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +static inline void tm_reclaim_task(struct task_struct *tsk) +{ + /* We have to work out if we're switching from/to a task that's in the + * middle of a transaction. + * + * In switching we need to maintain a 2nd register state as + * oldtask->thread.ckpt_regs. We tm_reclaim(oldproc); this saves the + * checkpointed (tbegin) state in ckpt_regs and saves the transactional + * (current) FPRs into oldtask->thread.transact_fpr[]. + * + * We also context switch (save) TFHAR/TEXASR/TFIAR in here. + */ + struct thread_struct *thr = &tsk->thread; + + if (!thr->regs) + return; + + if (!MSR_TM_ACTIVE(thr->regs->msr)) + goto out_and_saveregs; + + /* Stash the original thread MSR, as giveup_fpu et al will + * modify it. We hold onto it to see whether the task used + * FP & vector regs. + */ + thr->tm_orig_msr = thr->regs->msr; + + TM_DEBUG("--- tm_reclaim on pid %d (NIP=%lx, " + "ccr=%lx, msr=%lx, trap=%lx)\n", + tsk->pid, thr->regs->nip, + thr->regs->ccr, thr->regs->msr, + thr->regs->trap); + + tm_reclaim(thr, thr->regs->msr, TM_CAUSE_RESCHED); + + TM_DEBUG("--- tm_reclaim on pid %d complete\n", + tsk->pid); + +out_and_saveregs: + /* Always save the regs here, even if a transaction's not active. + * This context-switches a thread's TM info SPRs. We do it here to + * be consistent with the restore path (in recheckpoint) which + * cannot happen later in _switch(). + */ + tm_save_sprs(thr); +} + +static inline void __maybe_unused tm_recheckpoint_new_task(struct task_struct *new) +{ + unsigned long msr; + + if (!cpu_has_feature(CPU_FTR_TM)) + return; + + /* Recheckpoint the registers of the thread we're about to switch to. + * + * If the task was using FP, we non-lazily reload both the original and + * the speculative FP register states. This is because the kernel + * doesn't see if/when a TM rollback occurs, so if we take an FP + * unavoidable later, we are unable to determine which set of FP regs + * need to be restored. + */ + if (!new->thread.regs) + return; + + /* The TM SPRs are restored here, so that TEXASR.FS can be set + * before the trecheckpoint and no explosion occurs. + */ + tm_restore_sprs(&new->thread); + + if (!MSR_TM_ACTIVE(new->thread.regs->msr)) + return; + msr = new->thread.tm_orig_msr; + /* Recheckpoint to restore original checkpointed register state. */ + TM_DEBUG("*** tm_recheckpoint of pid %d " + "(new->msr 0x%lx, new->origmsr 0x%lx)\n", + new->pid, new->thread.regs->msr, msr); + + /* This loads the checkpointed FP/VEC state, if used */ + tm_recheckpoint(&new->thread, msr); + + /* This loads the speculative FP/VEC state, if used */ + if (msr & MSR_FP) { + do_load_up_transact_fpu(&new->thread); + new->thread.regs->msr |= + (MSR_FP | new->thread.fpexc_mode); + } + if (msr & MSR_VEC) { + do_load_up_transact_altivec(&new->thread); + new->thread.regs->msr |= MSR_VEC; + } + /* We may as well turn on VSX too since all the state is restored now */ + if (msr & MSR_VSX) + new->thread.regs->msr |= MSR_VSX; + + TM_DEBUG("*** tm_recheckpoint of pid %d complete " + "(kernel msr 0x%lx)\n", + new->pid, mfmsr()); +} + +static inline void __switch_to_tm(struct task_struct *prev) +{ + if (cpu_has_feature(CPU_FTR_TM)) { + tm_enable(); + tm_reclaim_task(prev); + } +} +#else +#define tm_recheckpoint_new_task(new) +#define __switch_to_tm(prev) +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ struct task_struct *__switch_to(struct task_struct *prev, struct task_struct *new) From d0c0c9a13f682157e8610565b6125a31d24434bc Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:38 +0000 Subject: [PATCH 2780/4607] powerpc: Add transactional memory unavaliable execption handler These should never happen since we always turn on MSR TM when in userspace. We don't do lazy TM. Hence if we hit this, we barf and kill the task as something's gone horribly wrong. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/exceptions-64s.S | 23 +++++++++++++++++++++++ arch/powerpc/kernel/traps.c | 21 +++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index b9bcf21dbb11..c6b88c1f3ebd 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -336,6 +336,11 @@ vsx_unavailable_pSeries_1: EXCEPTION_PROLOG_0(PACA_EXGEN) b vsx_unavailable_pSeries + . = 0xf60 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) + b tm_unavailable_pSeries + #ifdef CONFIG_CBE_RAS STD_EXCEPTION_HV(0x1200, 0x1202, cbe_system_error) KVM_HANDLER_SKIP(PACA_EXGEN, EXC_HV, 0x1202) @@ -550,6 +555,8 @@ ALT_FTR_SECTION_END_IFCLR(CPU_FTR_ARCH_206) KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf20) STD_EXCEPTION_PSERIES_OOL(0xf40, vsx_unavailable) KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf40) + STD_EXCEPTION_PSERIES_OOL(0xf60, tm_unavailable) + KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xf60) /* * An interrupt came in while soft-disabled. We set paca->irq_happened, then: @@ -852,6 +859,12 @@ vsx_unavailable_relon_pSeries_1: EXCEPTION_PROLOG_0(PACA_EXGEN) b vsx_unavailable_relon_pSeries +tm_unavailable_relon_pSeries_1: + . = 0x4f60 + SET_SCRATCH0(r13) + EXCEPTION_PROLOG_0(PACA_EXGEN) + b tm_unavailable_relon_pSeries + STD_RELON_EXCEPTION_PSERIES(0x5300, 0x1300, instruction_breakpoint) #ifdef CONFIG_PPC_DENORMALISATION . = 0x5500 @@ -1201,6 +1214,15 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) bl .vsx_unavailable_exception b .ret_from_except + .align 7 + .globl tm_unavailable_common +tm_unavailable_common: + EXCEPTION_PROLOG_COMMON(0xf60, PACA_EXGEN) + bl .save_nvgprs + addi r3,r1,STACK_FRAME_OVERHEAD + bl .tm_unavailable_exception + b .ret_from_except + .align 7 .globl __end_handlers __end_handlers: @@ -1220,6 +1242,7 @@ __end_handlers: STD_RELON_EXCEPTION_PSERIES_OOL(0xf00, performance_monitor) STD_RELON_EXCEPTION_PSERIES_OOL(0xf20, altivec_unavailable) STD_RELON_EXCEPTION_PSERIES_OOL(0xf40, vsx_unavailable) + STD_RELON_EXCEPTION_PSERIES_OOL(0xf60, tm_unavailable) #if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index bd5de5deaf51..f829e05d8b1b 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1168,6 +1168,27 @@ void vsx_unavailable_exception(struct pt_regs *regs) die("Unrecoverable VSX Unavailable Exception", regs, SIGABRT); } +void tm_unavailable_exception(struct pt_regs *regs) +{ + /* We restore the interrupt state now */ + if (!arch_irq_disabled_regs(regs)) + local_irq_enable(); + + /* Currently we never expect a TMU exception. Catch + * this and kill the process! + */ + printk(KERN_EMERG "Unexpected TM unavailable exception at %lx " + "(msr %lx)\n", + regs->nip, regs->msr); + + if (user_mode(regs)) { + _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); + return; + } + + die("Unexpected TM unavailable exception", regs, SIGABRT); +} + void performance_monitor_exception(struct pt_regs *regs) { __get_cpu_var(irq_stat).pmu_irqs++; From f54db641b9bbca91b071128e4d71f0f6af9a3c25 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:39 +0000 Subject: [PATCH 2781/4607] powerpc: Routines for FP/VSX/VMX unavailable during a transaction We do lazy FP but not lazy TM (ie. userspace starts with MSR TM=1 FP=0). Hence if userspace does an FP instruction during a transaction, we'll take an fp unavailable exception. This adds functions needed to handle this case. We have to inject the current FP state into the checkpoint so that the hardware can decide what to do with the transaction. We can't inject only the FP so we have to do a full treclaim and recheckpoint to inject just the FP state. This will cause the transaction to be marked as aborted by the hardware. This just add the routines needed to do this for FP, VMX and VSX. It doesn't hook them into the rest of the code yet. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/traps.c | 83 +++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index f829e05d8b1b..5c304697fe84 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) @@ -1189,6 +1190,88 @@ void tm_unavailable_exception(struct pt_regs *regs) die("Unexpected TM unavailable exception", regs, SIGABRT); } +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + +extern void do_load_up_fpu(struct pt_regs *regs); + +void fp_unavailable_tm(struct pt_regs *regs) +{ + /* Note: This does not handle any kind of FP laziness. */ + + TM_DEBUG("FP Unavailable trap whilst transactional at 0x%lx, MSR=%lx\n", + regs->nip, regs->msr); + tm_enable(); + + /* We can only have got here if the task started using FP after + * beginning the transaction. So, the transactional regs are just a + * copy of the checkpointed ones. But, we still need to recheckpoint + * as we're enabling FP for the process; it will return, abort the + * transaction, and probably retry but now with FP enabled. So the + * checkpointed FP registers need to be loaded. + */ + tm_reclaim(¤t->thread, current->thread.regs->msr, + TM_CAUSE_FAC_UNAV); + /* Reclaim didn't save out any FPRs to transact_fprs. */ + + /* Enable FP for the task: */ + regs->msr |= (MSR_FP | current->thread.fpexc_mode); + + /* This loads and recheckpoints the FP registers from + * thread.fpr[]. They will remain in registers after the + * checkpoint so we don't need to reload them after. + */ + tm_recheckpoint(¤t->thread, regs->msr); +} + +#ifdef CONFIG_ALTIVEC +extern void do_load_up_altivec(struct pt_regs *regs); + +void altivec_unavailable_tm(struct pt_regs *regs) +{ + /* See the comments in fp_unavailable_tm(). This function operates + * the same way. + */ + + TM_DEBUG("Vector Unavailable trap whilst transactional at 0x%lx," + "MSR=%lx\n", + regs->nip, regs->msr); + tm_enable(); + tm_reclaim(¤t->thread, current->thread.regs->msr, + TM_CAUSE_FAC_UNAV); + regs->msr |= MSR_VEC; + tm_recheckpoint(¤t->thread, regs->msr); + current->thread.used_vr = 1; +} +#endif + +#ifdef CONFIG_VSX +void vsx_unavailable_tm(struct pt_regs *regs) +{ + /* See the comments in fp_unavailable_tm(). This works similarly, + * though we're loading both FP and VEC registers in here. + * + * If FP isn't in use, load FP regs. If VEC isn't in use, load VEC + * regs. Either way, set MSR_VSX. + */ + + TM_DEBUG("VSX Unavailable trap whilst transactional at 0x%lx," + "MSR=%lx\n", + regs->nip, regs->msr); + + tm_enable(); + /* This reclaims FP and/or VR regs if they're already enabled */ + tm_reclaim(¤t->thread, current->thread.regs->msr, + TM_CAUSE_FAC_UNAV); + + regs->msr |= MSR_VEC | MSR_FP | current->thread.fpexc_mode | + MSR_VSX; + /* This loads & recheckpoints FP and VRs. */ + tm_recheckpoint(¤t->thread, regs->msr); + current->thread.used_vsr = 1; +} +#endif +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ + void performance_monitor_exception(struct pt_regs *regs) { __get_cpu_var(irq_stat).pmu_irqs++; From bc2a9408fa65195288b41751016c36fd00a75a85 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:40 +0000 Subject: [PATCH 2782/4607] powerpc: Hook in new transactional memory code This hooks the new transactional memory code into context switching, FP/VMX/VMX unavailable and exception return. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/exceptions-64s.S | 56 +++++++++++++++++++++++++++- arch/powerpc/kernel/process.c | 15 +++++++- arch/powerpc/kernel/traps.c | 32 ++++++++++++++++ arch/powerpc/mm/hash_utils_64.c | 16 ++++++++ 4 files changed, 115 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index c6b88c1f3ebd..a8a5361fb70c 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -1176,9 +1176,26 @@ fp_unavailable_common: addi r3,r1,STACK_FRAME_OVERHEAD bl .kernel_fp_unavailable_exception BUG_OPCODE -1: bl .load_up_fpu +1: +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +BEGIN_FTR_SECTION + /* Test if 2 TM state bits are zero. If non-zero (ie. userspace was in + * transaction), go do TM stuff + */ + rldicl. r0, r12, (64-MSR_TS_LG), (64-2) + bne- 2f +END_FTR_SECTION_IFSET(CPU_FTR_TM) +#endif + bl .load_up_fpu b fast_exception_return - +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +2: /* User process was in a transaction */ + bl .save_nvgprs + DISABLE_INTS + addi r3,r1,STACK_FRAME_OVERHEAD + bl .fp_unavailable_tm + b .ret_from_except +#endif .align 7 .globl altivec_unavailable_common altivec_unavailable_common: @@ -1186,8 +1203,25 @@ altivec_unavailable_common: #ifdef CONFIG_ALTIVEC BEGIN_FTR_SECTION beq 1f +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + BEGIN_FTR_SECTION_NESTED(69) + /* Test if 2 TM state bits are zero. If non-zero (ie. userspace was in + * transaction), go do TM stuff + */ + rldicl. r0, r12, (64-MSR_TS_LG), (64-2) + bne- 2f + END_FTR_SECTION_NESTED(CPU_FTR_TM, CPU_FTR_TM, 69) +#endif bl .load_up_altivec b fast_exception_return +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +2: /* User process was in a transaction */ + bl .save_nvgprs + DISABLE_INTS + addi r3,r1,STACK_FRAME_OVERHEAD + bl .altivec_unavailable_tm + b .ret_from_except +#endif 1: END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) #endif @@ -1204,7 +1238,24 @@ vsx_unavailable_common: #ifdef CONFIG_VSX BEGIN_FTR_SECTION beq 1f +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + BEGIN_FTR_SECTION_NESTED(69) + /* Test if 2 TM state bits are zero. If non-zero (ie. userspace was in + * transaction), go do TM stuff + */ + rldicl. r0, r12, (64-MSR_TS_LG), (64-2) + bne- 2f + END_FTR_SECTION_NESTED(CPU_FTR_TM, CPU_FTR_TM, 69) +#endif b .load_up_vsx +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +2: /* User process was in a transaction */ + bl .save_nvgprs + DISABLE_INTS + addi r3,r1,STACK_FRAME_OVERHEAD + bl .vsx_unavailable_tm + b .ret_from_except +#endif 1: END_FTR_SECTION_IFSET(CPU_FTR_VSX) #endif @@ -1219,6 +1270,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_VSX) tm_unavailable_common: EXCEPTION_PROLOG_COMMON(0xf60, PACA_EXGEN) bl .save_nvgprs + DISABLE_INTS addi r3,r1,STACK_FRAME_OVERHEAD bl .tm_unavailable_exception b .ret_from_except diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 48a987579e4f..59dd545fdde1 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -515,7 +515,7 @@ out_and_saveregs: tm_save_sprs(thr); } -static inline void __maybe_unused tm_recheckpoint_new_task(struct task_struct *new) +static inline void tm_recheckpoint_new_task(struct task_struct *new) { unsigned long msr; @@ -590,6 +590,8 @@ struct task_struct *__switch_to(struct task_struct *prev, struct ppc64_tlb_batch *batch; #endif + __switch_to_tm(prev); + #ifdef CONFIG_SMP /* avoid complexity of lazy save/restore of fpu * by just saving it every time we switch out if @@ -705,6 +707,9 @@ struct task_struct *__switch_to(struct task_struct *prev, * of sync. Hard disable here. */ hard_irq_disable(); + + tm_recheckpoint_new_task(new); + last = _switch(old_thread, new_thread); #ifdef CONFIG_PPC_BOOK3S_64 @@ -1080,7 +1085,6 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp) regs->msr = MSR_USER32; } #endif - discard_lazy_cpu_state(); #ifdef CONFIG_VSX current->thread.used_vsr = 0; @@ -1100,6 +1104,13 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp) current->thread.spefscr = 0; current->thread.used_spe = 0; #endif /* CONFIG_SPE */ +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + if (cpu_has_feature(CPU_FTR_TM)) + regs->msr |= MSR_TM; + current->thread.tm_tfhar = 0; + current->thread.tm_texasr = 0; + current->thread.tm_tfiar = 0; +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ } #define PR_FP_ALL_EXCEPT (PR_FP_EXC_DIV | PR_FP_EXC_OVF | PR_FP_EXC_UND \ diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 5c304697fe84..f9b751b29558 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1029,6 +1029,38 @@ void __kprobes program_check_exception(struct pt_regs *regs) _exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip); return; } +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + if (reason & REASON_TM) { + /* This is a TM "Bad Thing Exception" program check. + * This occurs when: + * - An rfid/hrfid/mtmsrd attempts to cause an illegal + * transition in TM states. + * - A trechkpt is attempted when transactional. + * - A treclaim is attempted when non transactional. + * - A tend is illegally attempted. + * - writing a TM SPR when transactional. + */ + if (!user_mode(regs) && + report_bug(regs->nip, regs) == BUG_TRAP_TYPE_WARN) { + regs->nip += 4; + return; + } + /* If usermode caused this, it's done something illegal and + * gets a SIGILL slap on the wrist. We call it an illegal + * operand to distinguish from the instruction just being bad + * (e.g. executing a 'tend' on a CPU without TM!); it's an + * illegal /placement/ of a valid instruction. + */ + if (user_mode(regs)) { + _exception(SIGILL, regs, ILL_ILLOPN, regs->nip); + return; + } else { + printk(KERN_EMERG "Unexpected TM Bad Thing exception " + "at %lx (msr 0x%x)\n", regs->nip, reason); + die("Unrecoverable exception", regs, SIGABRT); + } + } +#endif /* We restore the interrupt state now */ if (!arch_irq_disabled_regs(regs)) diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 3a292be2e079..1b6e1271719f 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -55,6 +55,7 @@ #include #include #include +#include #ifdef DEBUG #define DBG(fmt...) udbg_printf(fmt) @@ -1171,6 +1172,21 @@ void flush_hash_page(unsigned long vpn, real_pte_t pte, int psize, int ssize, DBG_LOW(" sub %ld: hash=%lx, hidx=%lx\n", index, slot, hidx); ppc_md.hpte_invalidate(slot, vpn, psize, ssize, local); } pte_iterate_hashed_end(); + +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + /* Transactions are not aborted by tlbiel, only tlbie. + * Without, syncing a page back to a block device w/ PIO could pick up + * transactional data (bad!) so we force an abort here. Before the + * sync the page will be made read-only, which will flush_hash_page. + * BIG ISSUE here: if the kernel uses a page from userspace without + * unmapping it first, it may see the speculated version. + */ + if (local && cpu_has_feature(CPU_FTR_TM) && + MSR_TM_ACTIVE(current->thread.regs->msr)) { + tm_enable(); + tm_abort(TM_CAUSE_TLBI); + } +#endif } void flush_hash_range(unsigned long number, int local) From 2b0a576d15e0e14751f00f9c87e46bad27f217e7 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:41 +0000 Subject: [PATCH 2783/4607] powerpc: Add new transactional memory state to the signal context This adds the new transactional memory archtected state to the signal context in both 32 and 64 bit. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/reg.h | 1 + arch/powerpc/kernel/signal.h | 8 + arch/powerpc/kernel/signal_32.c | 504 +++++++++++++++++++++++++++++++- arch/powerpc/kernel/signal_64.c | 337 ++++++++++++++++++++- 4 files changed, 832 insertions(+), 18 deletions(-) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index eee2a60994bf..7035e608f3fa 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -120,6 +120,7 @@ #define TM_CAUSE_FAC_UNAV 0xfa #define TM_CAUSE_SYSCALL 0xf9 /* Persistent */ #define TM_CAUSE_MISC 0xf6 +#define TM_CAUSE_SIGNAL 0xf4 #if defined(CONFIG_PPC_BOOK3S_64) #define MSR_64BIT MSR_SF diff --git a/arch/powerpc/kernel/signal.h b/arch/powerpc/kernel/signal.h index e00acb413934..ec84c901ceab 100644 --- a/arch/powerpc/kernel/signal.h +++ b/arch/powerpc/kernel/signal.h @@ -25,13 +25,21 @@ extern int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, extern unsigned long copy_fpr_to_user(void __user *to, struct task_struct *task); +extern unsigned long copy_transact_fpr_to_user(void __user *to, + struct task_struct *task); extern unsigned long copy_fpr_from_user(struct task_struct *task, void __user *from); +extern unsigned long copy_transact_fpr_from_user(struct task_struct *task, + void __user *from); #ifdef CONFIG_VSX extern unsigned long copy_vsx_to_user(void __user *to, struct task_struct *task); +extern unsigned long copy_transact_vsx_to_user(void __user *to, + struct task_struct *task); extern unsigned long copy_vsx_from_user(struct task_struct *task, void __user *from); +extern unsigned long copy_transact_vsx_from_user(struct task_struct *task, + void __user *from); #endif #ifdef CONFIG_PPC64 diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c index 804e323c139d..e4a88d340de6 100644 --- a/arch/powerpc/kernel/signal_32.c +++ b/arch/powerpc/kernel/signal_32.c @@ -43,6 +43,7 @@ #include #include #include +#include #ifdef CONFIG_PPC64 #include "ppc32.h" #include @@ -293,6 +294,10 @@ long sys_sigaction(int sig, struct old_sigaction __user *act, struct sigframe { struct sigcontext sctx; /* the sigcontext */ struct mcontext mctx; /* all the register values */ +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + struct sigcontext sctx_transact; + struct mcontext mctx_transact; +#endif /* * Programs using the rs6000/xcoff abi can save up to 19 gp * regs and 18 fp regs below sp before decrementing it. @@ -321,6 +326,9 @@ struct rt_sigframe { struct siginfo info; #endif struct ucontext uc; +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + struct ucontext uc_transact; +#endif /* * Programs using the rs6000/xcoff abi can save up to 19 gp * regs and 18 fp regs below sp before decrementing it. @@ -381,6 +389,61 @@ unsigned long copy_vsx_from_user(struct task_struct *task, task->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i]; return 0; } + +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +unsigned long copy_transact_fpr_to_user(void __user *to, + struct task_struct *task) +{ + double buf[ELF_NFPREG]; + int i; + + /* save FPR copy to local buffer then write to the thread_struct */ + for (i = 0; i < (ELF_NFPREG - 1) ; i++) + buf[i] = task->thread.TS_TRANS_FPR(i); + memcpy(&buf[i], &task->thread.transact_fpscr, sizeof(double)); + return __copy_to_user(to, buf, ELF_NFPREG * sizeof(double)); +} + +unsigned long copy_transact_fpr_from_user(struct task_struct *task, + void __user *from) +{ + double buf[ELF_NFPREG]; + int i; + + if (__copy_from_user(buf, from, ELF_NFPREG * sizeof(double))) + return 1; + for (i = 0; i < (ELF_NFPREG - 1) ; i++) + task->thread.TS_TRANS_FPR(i) = buf[i]; + memcpy(&task->thread.transact_fpscr, &buf[i], sizeof(double)); + + return 0; +} + +unsigned long copy_transact_vsx_to_user(void __user *to, + struct task_struct *task) +{ + double buf[ELF_NVSRHALFREG]; + int i; + + /* save FPR copy to local buffer then write to the thread_struct */ + for (i = 0; i < ELF_NVSRHALFREG; i++) + buf[i] = task->thread.transact_fpr[i][TS_VSRLOWOFFSET]; + return __copy_to_user(to, buf, ELF_NVSRHALFREG * sizeof(double)); +} + +unsigned long copy_transact_vsx_from_user(struct task_struct *task, + void __user *from) +{ + double buf[ELF_NVSRHALFREG]; + int i; + + if (__copy_from_user(buf, from, ELF_NVSRHALFREG * sizeof(double))) + return 1; + for (i = 0; i < ELF_NVSRHALFREG ; i++) + task->thread.transact_fpr[i][TS_VSRLOWOFFSET] = buf[i]; + return 0; +} +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ #else inline unsigned long copy_fpr_to_user(void __user *to, struct task_struct *task) @@ -395,6 +458,22 @@ inline unsigned long copy_fpr_from_user(struct task_struct *task, return __copy_from_user(task->thread.fpr, from, ELF_NFPREG * sizeof(double)); } + +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +inline unsigned long copy_transact_fpr_to_user(void __user *to, + struct task_struct *task) +{ + return __copy_to_user(to, task->thread.transact_fpr, + ELF_NFPREG * sizeof(double)); +} + +inline unsigned long copy_transact_fpr_from_user(struct task_struct *task, + void __user *from) +{ + return __copy_from_user(task->thread.transact_fpr, from, + ELF_NFPREG * sizeof(double)); +} +#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */ #endif /* @@ -483,6 +562,156 @@ static int save_user_regs(struct pt_regs *regs, struct mcontext __user *frame, return 0; } +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +/* + * Save the current user registers on the user stack. + * We only save the altivec/spe registers if the process has used + * altivec/spe instructions at some point. + * We also save the transactional registers to a second ucontext in the + * frame. + * + * See save_user_regs() and signal_64.c:setup_tm_sigcontexts(). + */ +static int save_tm_user_regs(struct pt_regs *regs, + struct mcontext __user *frame, + struct mcontext __user *tm_frame, int sigret) +{ + unsigned long msr = regs->msr; + + /* tm_reclaim rolls back all reg states, updating thread.ckpt_regs, + * thread.transact_fpr[], thread.transact_vr[], etc. + */ + tm_enable(); + tm_reclaim(¤t->thread, msr, TM_CAUSE_SIGNAL); + + /* Make sure floating point registers are stored in regs */ + flush_fp_to_thread(current); + + /* Save both sets of general registers */ + if (save_general_regs(¤t->thread.ckpt_regs, frame) + || save_general_regs(regs, tm_frame)) + return 1; + + /* Stash the top half of the 64bit MSR into the 32bit MSR word + * of the transactional mcontext. This way we have a backward-compatible + * MSR in the 'normal' (checkpointed) mcontext and additionally one can + * also look at what type of transaction (T or S) was active at the + * time of the signal. + */ + if (__put_user((msr >> 32), &tm_frame->mc_gregs[PT_MSR])) + return 1; + +#ifdef CONFIG_ALTIVEC + /* save altivec registers */ + if (current->thread.used_vr) { + flush_altivec_to_thread(current); + if (__copy_to_user(&frame->mc_vregs, current->thread.vr, + ELF_NVRREG * sizeof(vector128))) + return 1; + if (msr & MSR_VEC) { + if (__copy_to_user(&tm_frame->mc_vregs, + current->thread.transact_vr, + ELF_NVRREG * sizeof(vector128))) + return 1; + } else { + if (__copy_to_user(&tm_frame->mc_vregs, + current->thread.vr, + ELF_NVRREG * sizeof(vector128))) + return 1; + } + + /* set MSR_VEC in the saved MSR value to indicate that + * frame->mc_vregs contains valid data + */ + msr |= MSR_VEC; + } + + /* We always copy to/from vrsave, it's 0 if we don't have or don't + * use altivec. Since VSCR only contains 32 bits saved in the least + * significant bits of a vector, we "cheat" and stuff VRSAVE in the + * most significant bits of that same vector. --BenH + */ + if (__put_user(current->thread.vrsave, + (u32 __user *)&frame->mc_vregs[32])) + return 1; + if (msr & MSR_VEC) { + if (__put_user(current->thread.transact_vrsave, + (u32 __user *)&tm_frame->mc_vregs[32])) + return 1; + } else { + if (__put_user(current->thread.vrsave, + (u32 __user *)&tm_frame->mc_vregs[32])) + return 1; + } +#endif /* CONFIG_ALTIVEC */ + + if (copy_fpr_to_user(&frame->mc_fregs, current)) + return 1; + if (msr & MSR_FP) { + if (copy_transact_fpr_to_user(&tm_frame->mc_fregs, current)) + return 1; + } else { + if (copy_fpr_to_user(&tm_frame->mc_fregs, current)) + return 1; + } + +#ifdef CONFIG_VSX + /* + * Copy VSR 0-31 upper half from thread_struct to local + * buffer, then write that to userspace. Also set MSR_VSX in + * the saved MSR value to indicate that frame->mc_vregs + * contains valid data + */ + if (current->thread.used_vsr) { + __giveup_vsx(current); + if (copy_vsx_to_user(&frame->mc_vsregs, current)) + return 1; + if (msr & MSR_VSX) { + if (copy_transact_vsx_to_user(&tm_frame->mc_vsregs, + current)) + return 1; + } else { + if (copy_vsx_to_user(&tm_frame->mc_vsregs, current)) + return 1; + } + + msr |= MSR_VSX; + } +#endif /* CONFIG_VSX */ +#ifdef CONFIG_SPE + /* SPE regs are not checkpointed with TM, so this section is + * simply the same as in save_user_regs(). + */ + if (current->thread.used_spe) { + flush_spe_to_thread(current); + if (__copy_to_user(&frame->mc_vregs, current->thread.evr, + ELF_NEVRREG * sizeof(u32))) + return 1; + /* set MSR_SPE in the saved MSR value to indicate that + * frame->mc_vregs contains valid data */ + msr |= MSR_SPE; + } + + /* We always copy to/from spefscr */ + if (__put_user(current->thread.spefscr, (u32 __user *)&frame->mc_vregs + ELF_NEVRREG)) + return 1; +#endif /* CONFIG_SPE */ + + if (__put_user(msr, &frame->mc_gregs[PT_MSR])) + return 1; + if (sigret) { + /* Set up the sigreturn trampoline: li r0,sigret; sc */ + if (__put_user(0x38000000UL + sigret, &frame->tramp[0]) + || __put_user(0x44000002UL, &frame->tramp[1])) + return 1; + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + } + + return 0; +} +#endif + /* * Restore the current user register values from the user stack, * (except for MSR). @@ -588,6 +817,139 @@ static long restore_user_regs(struct pt_regs *regs, return 0; } +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +/* + * Restore the current user register values from the user stack, except for + * MSR, and recheckpoint the original checkpointed register state for processes + * in transactions. + */ +static long restore_tm_user_regs(struct pt_regs *regs, + struct mcontext __user *sr, + struct mcontext __user *tm_sr) +{ + long err; + unsigned long msr; +#ifdef CONFIG_VSX + int i; +#endif + + /* + * restore general registers but not including MSR or SOFTE. Also + * take care of keeping r2 (TLS) intact if not a signal. + * See comment in signal_64.c:restore_tm_sigcontexts(); + * TFHAR is restored from the checkpointed NIP; TEXASR and TFIAR + * were set by the signal delivery. + */ + err = restore_general_regs(regs, tm_sr); + err |= restore_general_regs(¤t->thread.ckpt_regs, sr); + + err |= __get_user(current->thread.tm_tfhar, &sr->mc_gregs[PT_NIP]); + + err |= __get_user(msr, &sr->mc_gregs[PT_MSR]); + if (err) + return 1; + + /* Restore the previous little-endian mode */ + regs->msr = (regs->msr & ~MSR_LE) | (msr & MSR_LE); + + /* + * Do this before updating the thread state in + * current->thread.fpr/vr/evr. That way, if we get preempted + * and another task grabs the FPU/Altivec/SPE, it won't be + * tempted to save the current CPU state into the thread_struct + * and corrupt what we are writing there. + */ + discard_lazy_cpu_state(); + +#ifdef CONFIG_ALTIVEC + regs->msr &= ~MSR_VEC; + if (msr & MSR_VEC) { + /* restore altivec registers from the stack */ + if (__copy_from_user(current->thread.vr, &sr->mc_vregs, + sizeof(sr->mc_vregs)) || + __copy_from_user(current->thread.transact_vr, + &tm_sr->mc_vregs, + sizeof(sr->mc_vregs))) + return 1; + } else if (current->thread.used_vr) { + memset(current->thread.vr, 0, ELF_NVRREG * sizeof(vector128)); + memset(current->thread.transact_vr, 0, + ELF_NVRREG * sizeof(vector128)); + } + + /* Always get VRSAVE back */ + if (__get_user(current->thread.vrsave, + (u32 __user *)&sr->mc_vregs[32]) || + __get_user(current->thread.transact_vrsave, + (u32 __user *)&tm_sr->mc_vregs[32])) + return 1; +#endif /* CONFIG_ALTIVEC */ + + regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1); + + if (copy_fpr_from_user(current, &sr->mc_fregs) || + copy_transact_fpr_from_user(current, &tm_sr->mc_fregs)) + return 1; + +#ifdef CONFIG_VSX + regs->msr &= ~MSR_VSX; + if (msr & MSR_VSX) { + /* + * Restore altivec registers from the stack to a local + * buffer, then write this out to the thread_struct + */ + if (copy_vsx_from_user(current, &sr->mc_vsregs) || + copy_transact_vsx_from_user(current, &tm_sr->mc_vsregs)) + return 1; + } else if (current->thread.used_vsr) + for (i = 0; i < 32 ; i++) { + current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; + current->thread.transact_fpr[i][TS_VSRLOWOFFSET] = 0; + } +#endif /* CONFIG_VSX */ + +#ifdef CONFIG_SPE + /* SPE regs are not checkpointed with TM, so this section is + * simply the same as in restore_user_regs(). + */ + regs->msr &= ~MSR_SPE; + if (msr & MSR_SPE) { + if (__copy_from_user(current->thread.evr, &sr->mc_vregs, + ELF_NEVRREG * sizeof(u32))) + return 1; + } else if (current->thread.used_spe) + memset(current->thread.evr, 0, ELF_NEVRREG * sizeof(u32)); + + /* Always get SPEFSCR back */ + if (__get_user(current->thread.spefscr, (u32 __user *)&sr->mc_vregs + + ELF_NEVRREG)) + return 1; +#endif /* CONFIG_SPE */ + + /* Now, recheckpoint. This loads up all of the checkpointed (older) + * registers, including FP and V[S]Rs. After recheckpointing, the + * transactional versions should be loaded. + */ + tm_enable(); + /* This loads the checkpointed FP/VEC state, if used */ + tm_recheckpoint(¤t->thread, msr); + /* The task has moved into TM state S, so ensure MSR reflects this */ + regs->msr = (regs->msr & ~MSR_TS_MASK) | MSR_TS_S; + + /* This loads the speculative FP/VEC state, if used */ + if (msr & MSR_FP) { + do_load_up_transact_fpu(¤t->thread); + regs->msr |= (MSR_FP | current->thread.fpexc_mode); + } + if (msr & MSR_VEC) { + do_load_up_transact_altivec(¤t->thread); + regs->msr |= MSR_VEC; + } + + return 0; +} +#endif + #ifdef CONFIG_PPC64 long compat_sys_rt_sigaction(int sig, const struct sigaction32 __user *act, struct sigaction32 __user *oact, size_t sigsetsize) @@ -827,6 +1189,8 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, struct mcontext __user *frame; void __user *addr; unsigned long newsp = 0; + int sigret; + unsigned long tramp; /* Set up Signal Frame */ /* Put a Real Time Context onto stack */ @@ -838,7 +1202,6 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, /* Put the siginfo & fill in most of the ucontext */ if (copy_siginfo_to_user(&rt_sf->info, info) || __put_user(0, &rt_sf->uc.uc_flags) - || __put_user(0, &rt_sf->uc.uc_link) || __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp) || __put_user(sas_ss_flags(regs->gpr[1]), &rt_sf->uc.uc_stack.ss_flags) @@ -852,15 +1215,38 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, frame = &rt_sf->uc.uc_mcontext; addr = frame; if (vdso32_rt_sigtramp && current->mm->context.vdso_base) { - if (save_user_regs(regs, frame, 0, 1)) - goto badframe; - regs->link = current->mm->context.vdso_base + vdso32_rt_sigtramp; + sigret = 0; + tramp = current->mm->context.vdso_base + vdso32_rt_sigtramp; } else { - if (save_user_regs(regs, frame, __NR_rt_sigreturn, 1)) - goto badframe; - regs->link = (unsigned long) frame->tramp; + sigret = __NR_rt_sigreturn; + tramp = (unsigned long) frame->tramp; } +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + if (MSR_TM_ACTIVE(regs->msr)) { + if (save_tm_user_regs(regs, &rt_sf->uc.uc_mcontext, + &rt_sf->uc_transact.uc_mcontext, sigret)) + goto badframe; + } + else +#endif + if (save_user_regs(regs, frame, sigret, 1)) + goto badframe; + regs->link = tramp; + +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + if (MSR_TM_ACTIVE(regs->msr)) { + if (__put_user((unsigned long)&rt_sf->uc_transact, + &rt_sf->uc.uc_link) + || __put_user(to_user_ptr(&rt_sf->uc_transact.uc_mcontext), + &rt_sf->uc_transact.uc_regs)) + goto badframe; + } + else +#endif + if (__put_user(0, &rt_sf->uc.uc_link)) + goto badframe; + current->thread.fpscr.val = 0; /* turn off all fp exceptions */ /* create a stack frame for the caller of the handler */ @@ -878,6 +1264,13 @@ int handle_rt_signal32(unsigned long sig, struct k_sigaction *ka, regs->nip = (unsigned long) ka->sa.sa_handler; /* enter the signal handler in big-endian mode */ regs->msr &= ~MSR_LE; +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + /* Remove TM bits from thread's MSR. The MSR in the sigcontext + * just indicates to userland that we were doing a transaction, but we + * don't want to return in transactional state: + */ + regs->msr &= ~MSR_TS_MASK; +#endif return 1; badframe: @@ -925,6 +1318,35 @@ static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs, int return 0; } +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +static int do_setcontext_tm(struct ucontext __user *ucp, + struct ucontext __user *tm_ucp, + struct pt_regs *regs) +{ + sigset_t set; + struct mcontext __user *mcp; + struct mcontext __user *tm_mcp; + u32 cmcp; + u32 tm_cmcp; + + if (get_sigset_t(&set, &ucp->uc_sigmask)) + return -EFAULT; + + if (__get_user(cmcp, &ucp->uc_regs) || + __get_user(tm_cmcp, &tm_ucp->uc_regs)) + return -EFAULT; + mcp = (struct mcontext __user *)(u64)cmcp; + tm_mcp = (struct mcontext __user *)(u64)tm_cmcp; + /* no need to check access_ok(mcp), since mcp < 4GB */ + + set_current_blocked(&set); + if (restore_tm_user_regs(regs, mcp, tm_mcp)) + return -EFAULT; + + return 0; +} +#endif + long sys_swapcontext(struct ucontext __user *old_ctx, struct ucontext __user *new_ctx, int ctx_size, int r6, int r7, int r8, struct pt_regs *regs) @@ -1020,7 +1442,12 @@ long sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, struct pt_regs *regs) { struct rt_sigframe __user *rt_sf; - +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + struct ucontext __user *uc_transact; + unsigned long msr_hi; + unsigned long tmp; + int tm_restore = 0; +#endif /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; @@ -1028,6 +1455,34 @@ long sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, (regs->gpr[1] + __SIGNAL_FRAMESIZE + 16); if (!access_ok(VERIFY_READ, rt_sf, sizeof(*rt_sf))) goto bad; +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + if (__get_user(tmp, &rt_sf->uc.uc_link)) + goto bad; + uc_transact = (struct ucontext __user *)(uintptr_t)tmp; + if (uc_transact) { + u32 cmcp; + struct mcontext __user *mcp; + + if (__get_user(cmcp, &uc_transact->uc_regs)) + return -EFAULT; + mcp = (struct mcontext __user *)(u64)cmcp; + /* The top 32 bits of the MSR are stashed in the transactional + * ucontext. */ + if (__get_user(msr_hi, &mcp->mc_gregs[PT_MSR])) + goto bad; + + if (MSR_TM_SUSPENDED(msr_hi<<32)) { + /* We only recheckpoint on return if we're + * transaction. + */ + tm_restore = 1; + if (do_setcontext_tm(&rt_sf->uc, uc_transact, regs)) + goto bad; + } + } + if (!tm_restore) + /* Fall through, for non-TM restore */ +#endif if (do_setcontext(&rt_sf->uc, regs, 1)) goto bad; @@ -1179,6 +1634,8 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka, struct sigcontext __user *sc; struct sigframe __user *frame; unsigned long newsp = 0; + int sigret; + unsigned long tramp; /* Set up Signal Frame */ frame = get_sigframe(ka, regs, sizeof(*frame), 1); @@ -1201,15 +1658,26 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka, goto badframe; if (vdso32_sigtramp && current->mm->context.vdso_base) { - if (save_user_regs(regs, &frame->mctx, 0, 1)) - goto badframe; - regs->link = current->mm->context.vdso_base + vdso32_sigtramp; + sigret = 0; + tramp = current->mm->context.vdso_base + vdso32_sigtramp; } else { - if (save_user_regs(regs, &frame->mctx, __NR_sigreturn, 1)) - goto badframe; - regs->link = (unsigned long) frame->mctx.tramp; + sigret = __NR_sigreturn; + tramp = (unsigned long) frame->mctx.tramp; } +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + if (MSR_TM_ACTIVE(regs->msr)) { + if (save_tm_user_regs(regs, &frame->mctx, &frame->mctx_transact, + sigret)) + goto badframe; + } + else +#endif + if (save_user_regs(regs, &frame->mctx, sigret, 1)) + goto badframe; + + regs->link = tramp; + current->thread.fpscr.val = 0; /* turn off all fp exceptions */ /* create a stack frame for the caller of the handler */ @@ -1223,7 +1691,13 @@ int handle_signal32(unsigned long sig, struct k_sigaction *ka, regs->nip = (unsigned long) ka->sa.sa_handler; /* enter the signal handler in big-endian mode */ regs->msr &= ~MSR_LE; - +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + /* Remove TM bits from thread's MSR. The MSR in the sigcontext + * just indicates to userland that we were doing a transaction, but we + * don't want to return in transactional state: + */ + regs->msr &= ~MSR_TS_MASK; +#endif return 1; badframe: diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c index 1ca045d44324..7a76ee48a952 100644 --- a/arch/powerpc/kernel/signal_64.c +++ b/arch/powerpc/kernel/signal_64.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "signal.h" @@ -56,6 +57,9 @@ struct rt_sigframe { /* sys_rt_sigreturn requires the ucontext be the first field */ struct ucontext uc; +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + struct ucontext uc_transact; +#endif unsigned long _unused[2]; unsigned int tramp[TRAMP_SIZE]; struct siginfo __user *pinfo; @@ -145,6 +149,145 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, return err; } +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +/* + * As above, but Transactional Memory is in use, so deliver sigcontexts + * containing checkpointed and transactional register states. + * + * To do this, we treclaim to gather both sets of registers and set up the + * 'normal' sigcontext registers with rolled-back register values such that a + * simple signal handler sees a correct checkpointed register state. + * If interested, a TM-aware sighandler can examine the transactional registers + * in the 2nd sigcontext to determine the real origin of the signal. + */ +static long setup_tm_sigcontexts(struct sigcontext __user *sc, + struct sigcontext __user *tm_sc, + struct pt_regs *regs, + int signr, sigset_t *set, unsigned long handler) +{ + /* When CONFIG_ALTIVEC is set, we _always_ setup v_regs even if the + * process never used altivec yet (MSR_VEC is zero in pt_regs of + * the context). This is very important because we must ensure we + * don't lose the VRSAVE content that may have been set prior to + * the process doing its first vector operation + * Userland shall check AT_HWCAP to know wether it can rely on the + * v_regs pointer or not. + */ +#ifdef CONFIG_ALTIVEC + elf_vrreg_t __user *v_regs = (elf_vrreg_t __user *) + (((unsigned long)sc->vmx_reserve + 15) & ~0xful); + elf_vrreg_t __user *tm_v_regs = (elf_vrreg_t __user *) + (((unsigned long)tm_sc->vmx_reserve + 15) & ~0xful); +#endif + unsigned long msr = regs->msr; + long err = 0; + + BUG_ON(!MSR_TM_ACTIVE(regs->msr)); + + /* tm_reclaim rolls back all reg states, saving checkpointed (older) + * GPRs to thread.ckpt_regs and (if used) FPRs to (newer) + * thread.transact_fp and/or VRs to (newer) thread.transact_vr. + * THEN we save out FP/VRs, if necessary, to the checkpointed (older) + * thread.fr[]/vr[]s. The transactional (newer) GPRs are on the + * stack, in *regs. + */ + tm_enable(); + tm_reclaim(¤t->thread, msr, TM_CAUSE_SIGNAL); + + flush_fp_to_thread(current); + +#ifdef CONFIG_ALTIVEC + err |= __put_user(v_regs, &sc->v_regs); + err |= __put_user(tm_v_regs, &tm_sc->v_regs); + + /* save altivec registers */ + if (current->thread.used_vr) { + flush_altivec_to_thread(current); + /* Copy 33 vec registers (vr0..31 and vscr) to the stack */ + err |= __copy_to_user(v_regs, current->thread.vr, + 33 * sizeof(vector128)); + /* If VEC was enabled there are transactional VRs valid too, + * else they're a copy of the checkpointed VRs. + */ + if (msr & MSR_VEC) + err |= __copy_to_user(tm_v_regs, + current->thread.transact_vr, + 33 * sizeof(vector128)); + else + err |= __copy_to_user(tm_v_regs, + current->thread.vr, + 33 * sizeof(vector128)); + + /* set MSR_VEC in the MSR value in the frame to indicate + * that sc->v_reg contains valid data. + */ + msr |= MSR_VEC; + } + /* We always copy to/from vrsave, it's 0 if we don't have or don't + * use altivec. + */ + err |= __put_user(current->thread.vrsave, (u32 __user *)&v_regs[33]); + if (msr & MSR_VEC) + err |= __put_user(current->thread.transact_vrsave, + (u32 __user *)&tm_v_regs[33]); + else + err |= __put_user(current->thread.vrsave, + (u32 __user *)&tm_v_regs[33]); + +#else /* CONFIG_ALTIVEC */ + err |= __put_user(0, &sc->v_regs); + err |= __put_user(0, &tm_sc->v_regs); +#endif /* CONFIG_ALTIVEC */ + + /* copy fpr regs and fpscr */ + err |= copy_fpr_to_user(&sc->fp_regs, current); + if (msr & MSR_FP) + err |= copy_transact_fpr_to_user(&tm_sc->fp_regs, current); + else + err |= copy_fpr_to_user(&tm_sc->fp_regs, current); + +#ifdef CONFIG_VSX + /* + * Copy VSX low doubleword to local buffer for formatting, + * then out to userspace. Update v_regs to point after the + * VMX data. + */ + if (current->thread.used_vsr) { + __giveup_vsx(current); + v_regs += ELF_NVRREG; + tm_v_regs += ELF_NVRREG; + + err |= copy_vsx_to_user(v_regs, current); + + if (msr & MSR_VSX) + err |= copy_transact_vsx_to_user(tm_v_regs, current); + else + err |= copy_vsx_to_user(tm_v_regs, current); + + /* set MSR_VSX in the MSR value in the frame to + * indicate that sc->vs_reg) contains valid data. + */ + msr |= MSR_VSX; + } +#endif /* CONFIG_VSX */ + + err |= __put_user(&sc->gp_regs, &sc->regs); + err |= __put_user(&tm_sc->gp_regs, &tm_sc->regs); + WARN_ON(!FULL_REGS(regs)); + err |= __copy_to_user(&tm_sc->gp_regs, regs, GP_REGS_SIZE); + err |= __copy_to_user(&sc->gp_regs, + ¤t->thread.ckpt_regs, GP_REGS_SIZE); + err |= __put_user(msr, &tm_sc->gp_regs[PT_MSR]); + err |= __put_user(msr, &sc->gp_regs[PT_MSR]); + err |= __put_user(signr, &sc->signal); + err |= __put_user(handler, &sc->handler); + if (set != NULL) + err |= __put_user(set->sig[0], &sc->oldmask); + + return err; +} +#endif + /* * Restore the sigcontext from the signal frame. */ @@ -241,6 +384,153 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig, return err; } +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM +/* + * Restore the two sigcontexts from the frame of a transactional processes. + */ + +static long restore_tm_sigcontexts(struct pt_regs *regs, + struct sigcontext __user *sc, + struct sigcontext __user *tm_sc) +{ +#ifdef CONFIG_ALTIVEC + elf_vrreg_t __user *v_regs, *tm_v_regs; +#endif + unsigned long err = 0; + unsigned long msr; +#ifdef CONFIG_VSX + int i; +#endif + /* copy the GPRs */ + err |= __copy_from_user(regs->gpr, tm_sc->gp_regs, sizeof(regs->gpr)); + err |= __copy_from_user(¤t->thread.ckpt_regs, sc->gp_regs, + sizeof(regs->gpr)); + + /* + * TFHAR is restored from the checkpointed 'wound-back' ucontext's NIP. + * TEXASR was set by the signal delivery reclaim, as was TFIAR. + * Users doing anything abhorrent like thread-switching w/ signals for + * TM-Suspended code will have to back TEXASR/TFIAR up themselves. + * For the case of getting a signal and simply returning from it, + * we don't need to re-copy them here. + */ + err |= __get_user(regs->nip, &tm_sc->gp_regs[PT_NIP]); + err |= __get_user(current->thread.tm_tfhar, &sc->gp_regs[PT_NIP]); + + /* get MSR separately, transfer the LE bit if doing signal return */ + err |= __get_user(msr, &sc->gp_regs[PT_MSR]); + regs->msr = (regs->msr & ~MSR_LE) | (msr & MSR_LE); + + /* The following non-GPR non-FPR non-VR state is also checkpointed: */ + err |= __get_user(regs->ctr, &tm_sc->gp_regs[PT_CTR]); + err |= __get_user(regs->link, &tm_sc->gp_regs[PT_LNK]); + err |= __get_user(regs->xer, &tm_sc->gp_regs[PT_XER]); + err |= __get_user(regs->ccr, &tm_sc->gp_regs[PT_CCR]); + err |= __get_user(current->thread.ckpt_regs.ctr, + &sc->gp_regs[PT_CTR]); + err |= __get_user(current->thread.ckpt_regs.link, + &sc->gp_regs[PT_LNK]); + err |= __get_user(current->thread.ckpt_regs.xer, + &sc->gp_regs[PT_XER]); + err |= __get_user(current->thread.ckpt_regs.ccr, + &sc->gp_regs[PT_CCR]); + + /* These regs are not checkpointed; they can go in 'regs'. */ + err |= __get_user(regs->trap, &sc->gp_regs[PT_TRAP]); + err |= __get_user(regs->dar, &sc->gp_regs[PT_DAR]); + err |= __get_user(regs->dsisr, &sc->gp_regs[PT_DSISR]); + err |= __get_user(regs->result, &sc->gp_regs[PT_RESULT]); + + /* + * Do this before updating the thread state in + * current->thread.fpr/vr. That way, if we get preempted + * and another task grabs the FPU/Altivec, it won't be + * tempted to save the current CPU state into the thread_struct + * and corrupt what we are writing there. + */ + discard_lazy_cpu_state(); + + /* + * Force reload of FP/VEC. + * This has to be done before copying stuff into current->thread.fpr/vr + * for the reasons explained in the previous comment. + */ + regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC | MSR_VSX); + +#ifdef CONFIG_ALTIVEC + err |= __get_user(v_regs, &sc->v_regs); + err |= __get_user(tm_v_regs, &tm_sc->v_regs); + if (err) + return err; + if (v_regs && !access_ok(VERIFY_READ, v_regs, 34 * sizeof(vector128))) + return -EFAULT; + if (tm_v_regs && !access_ok(VERIFY_READ, + tm_v_regs, 34 * sizeof(vector128))) + return -EFAULT; + /* Copy 33 vec registers (vr0..31 and vscr) from the stack */ + if (v_regs != 0 && tm_v_regs != 0 && (msr & MSR_VEC) != 0) { + err |= __copy_from_user(current->thread.vr, v_regs, + 33 * sizeof(vector128)); + err |= __copy_from_user(current->thread.transact_vr, tm_v_regs, + 33 * sizeof(vector128)); + } + else if (current->thread.used_vr) { + memset(current->thread.vr, 0, 33 * sizeof(vector128)); + memset(current->thread.transact_vr, 0, 33 * sizeof(vector128)); + } + /* Always get VRSAVE back */ + if (v_regs != 0 && tm_v_regs != 0) { + err |= __get_user(current->thread.vrsave, + (u32 __user *)&v_regs[33]); + err |= __get_user(current->thread.transact_vrsave, + (u32 __user *)&tm_v_regs[33]); + } + else { + current->thread.vrsave = 0; + current->thread.transact_vrsave = 0; + } +#endif /* CONFIG_ALTIVEC */ + /* restore floating point */ + err |= copy_fpr_from_user(current, &sc->fp_regs); + err |= copy_transact_fpr_from_user(current, &tm_sc->fp_regs); +#ifdef CONFIG_VSX + /* + * Get additional VSX data. Update v_regs to point after the + * VMX data. Copy VSX low doubleword from userspace to local + * buffer for formatting, then into the taskstruct. + */ + if (v_regs && ((msr & MSR_VSX) != 0)) { + v_regs += ELF_NVRREG; + tm_v_regs += ELF_NVRREG; + err |= copy_vsx_from_user(current, v_regs); + err |= copy_transact_vsx_from_user(current, tm_v_regs); + } else { + for (i = 0; i < 32 ; i++) { + current->thread.fpr[i][TS_VSRLOWOFFSET] = 0; + current->thread.transact_fpr[i][TS_VSRLOWOFFSET] = 0; + } + } +#endif + tm_enable(); + /* This loads the checkpointed FP/VEC state, if used */ + tm_recheckpoint(¤t->thread, msr); + /* The task has moved into TM state S, so ensure MSR reflects this: */ + regs->msr = (regs->msr & ~MSR_TS_MASK) | __MASK(33); + + /* This loads the speculative FP/VEC state, if used */ + if (msr & MSR_FP) { + do_load_up_transact_fpu(¤t->thread); + regs->msr |= (MSR_FP | current->thread.fpexc_mode); + } + if (msr & MSR_VEC) { + do_load_up_transact_altivec(¤t->thread); + regs->msr |= MSR_VEC; + } + + return err; +} +#endif + /* * Setup the trampoline code on the stack */ @@ -355,6 +645,9 @@ int sys_rt_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5, { struct ucontext __user *uc = (struct ucontext __user *)regs->gpr[1]; sigset_t set; +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + unsigned long msr; +#endif /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; @@ -365,6 +658,21 @@ int sys_rt_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5, if (__copy_from_user(&set, &uc->uc_sigmask, sizeof(set))) goto badframe; set_current_blocked(&set); +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + if (__get_user(msr, &uc->uc_mcontext.gp_regs[PT_MSR])) + goto badframe; + if (MSR_TM_SUSPENDED(msr)) { + /* We recheckpoint on return. */ + struct ucontext __user *uc_transact; + if (__get_user(uc_transact, &uc->uc_link)) + goto badframe; + if (restore_tm_sigcontexts(regs, &uc->uc_mcontext, + &uc_transact->uc_mcontext)) + goto badframe; + } + else + /* Fall through, for non-TM restore */ +#endif if (restore_sigcontext(regs, NULL, 1, &uc->uc_mcontext)) goto badframe; @@ -415,19 +723,42 @@ int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info, /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); err |= __put_user(sas_ss_flags(regs->gpr[1]), &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); - err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, signr, NULL, - (unsigned long)ka->sa.sa_handler, 1); +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + if (MSR_TM_ACTIVE(regs->msr)) { + /* The ucontext_t passed to userland points to the second + * ucontext_t (for transactional state) with its uc_link ptr. + */ + err |= __put_user(&frame->uc_transact, &frame->uc.uc_link); + err |= setup_tm_sigcontexts(&frame->uc.uc_mcontext, + &frame->uc_transact.uc_mcontext, + regs, signr, + NULL, + (unsigned long)ka->sa.sa_handler); + } else +#endif + { + err |= __put_user(0, &frame->uc.uc_link); + err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, signr, + NULL, (unsigned long)ka->sa.sa_handler, + 1); + } err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (err) goto badframe; /* Make sure signal handler doesn't get spurious FP exceptions */ current->thread.fpscr.val = 0; +#ifdef CONFIG_PPC_TRANSACTIONAL_MEM + /* Remove TM bits from thread's MSR. The MSR in the sigcontext + * just indicates to userland that we were doing a transaction, but we + * don't want to return in transactional state: + */ + regs->msr &= ~MSR_TS_MASK; +#endif /* Set up to return from userspace. */ if (vdso64_rt_sigtramp && current->mm->context.vdso_base) { From b9eaee5a8ac18dcfebf6bb295b6eced1d934be51 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:42 +0000 Subject: [PATCH 2784/4607] powerpc: Add transactional memory to POWER8 cpu features Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/cputable.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 36360813382c..fb3245e928ea 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -421,7 +421,8 @@ extern const char *powerpc_base_platform; CPU_FTR_DSCR | CPU_FTR_SAO | \ CPU_FTR_STCX_CHECKS_ADDRESS | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ CPU_FTR_ICSWX | CPU_FTR_CFAR | CPU_FTR_HVMODE | CPU_FTR_VMX_COPY | \ - CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_DAWR | CPU_FTR_BCTAR) + CPU_FTR_DBELL | CPU_FTR_HAS_PPR | CPU_FTR_DAWR | CPU_FTR_BCTAR | \ + CPU_FTR_TM_COMP) #define CPU_FTRS_CELL (CPU_FTR_USE_TB | CPU_FTR_LWSYNC | \ CPU_FTR_PPCAS_ARCH_V2 | CPU_FTR_CTRL | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ From 3d72bbc40777437a78db70e398981c828240c81f Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:43 +0000 Subject: [PATCH 2785/4607] powerpc: Add config option for transactional memory Kconfig option for transactional memory on powerpc. Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 4b27edbdb799..85ff3a080360 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -313,6 +313,14 @@ config MATH_EMULATION unit, which will allow programs that use floating-point instructions to run. +config PPC_TRANSACTIONAL_MEM + bool "Transactional Memory support for POWERPC" + depends on PPC_BOOK3S_64 + depends on SMP + default n + ---help--- + Support user-mode Transactional Memory on POWERPC. + config 8XX_MINIMAL_FPEMU bool "Minimal math emulation for 8xx" depends on 8xx && !MATH_EMULATION From 81afe7d1017ff7f1753fcfaa8b0fd3abc4c7219d Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:44 +0000 Subject: [PATCH 2786/4607] powerpc: Add transactional memory to pseries and ppc64 defconfigs Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/configs/ppc64_defconfig | 2 ++ arch/powerpc/configs/pseries_defconfig | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig index 6cbdeb4290e2..aef3f71de5ad 100644 --- a/arch/powerpc/configs/ppc64_defconfig +++ b/arch/powerpc/configs/ppc64_defconfig @@ -49,6 +49,8 @@ CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_PMAC64=y CONFIG_HZ_100=y CONFIG_BINFMT_MISC=m +CONFIG_PPC_TRANSACTIONAL_MEM=y +CONFIG_HOTPLUG_CPU=y CONFIG_KEXEC=y CONFIG_IRQ_ALL_CPUS=y CONFIG_MEMORY_HOTREMOVE=y diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index 04fa7f2e295f..c4dfbaf8b192 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -43,6 +43,8 @@ CONFIG_RTAS_FLASH=m CONFIG_IBMEBUS=y CONFIG_HZ_100=y CONFIG_BINFMT_MISC=m +CONFIG_PPC_TRANSACTIONAL_MEM=y +CONFIG_HOTPLUG_CPU=y CONFIG_KEXEC=y CONFIG_IRQ_ALL_CPUS=y CONFIG_MEMORY_HOTPLUG=y From db8ff907027b63b02c8cef385ea95445b7a41357 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Wed, 13 Feb 2013 16:21:45 +0000 Subject: [PATCH 2787/4607] powerpc: Documentation for transactional memory on powerpc Signed-off-by: Matt Evans Signed-off-by: Michael Neuling Signed-off-by: Benjamin Herrenschmidt --- .../powerpc/transactional_memory.txt | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 Documentation/powerpc/transactional_memory.txt diff --git a/Documentation/powerpc/transactional_memory.txt b/Documentation/powerpc/transactional_memory.txt new file mode 100644 index 000000000000..c907be41d60f --- /dev/null +++ b/Documentation/powerpc/transactional_memory.txt @@ -0,0 +1,175 @@ +Transactional Memory support +============================ + +POWER kernel support for this feature is currently limited to supporting +its use by user programs. It is not currently used by the kernel itself. + +This file aims to sum up how it is supported by Linux and what behaviour you +can expect from your user programs. + + +Basic overview +============== + +Hardware Transactional Memory is supported on POWER8 processors, and is a +feature that enables a different form of atomic memory access. Several new +instructions are presented to delimit transactions; transactions are +guaranteed to either complete atomically or roll back and undo any partial +changes. + +A simple transaction looks like this: + +begin_move_money: + tbegin + beq abort_handler + + ld r4, SAVINGS_ACCT(r3) + ld r5, CURRENT_ACCT(r3) + subi r5, r5, 1 + addi r4, r4, 1 + std r4, SAVINGS_ACCT(r3) + std r5, CURRENT_ACCT(r3) + + tend + + b continue + +abort_handler: + ... test for odd failures ... + + /* Retry the transaction if it failed because it conflicted with + * someone else: */ + b begin_move_money + + +The 'tbegin' instruction denotes the start point, and 'tend' the end point. +Between these points the processor is in 'Transactional' state; any memory +references will complete in one go if there are no conflicts with other +transactional or non-transactional accesses within the system. In this +example, the transaction completes as though it were normal straight-line code +IF no other processor has touched SAVINGS_ACCT(r3) or CURRENT_ACCT(r3); an +atomic move of money from the current account to the savings account has been +performed. Even though the normal ld/std instructions are used (note no +lwarx/stwcx), either *both* SAVINGS_ACCT(r3) and CURRENT_ACCT(r3) will be +updated, or neither will be updated. + +If, in the meantime, there is a conflict with the locations accessed by the +transaction, the transaction will be aborted by the CPU. Register and memory +state will roll back to that at the 'tbegin', and control will continue from +'tbegin+4'. The branch to abort_handler will be taken this second time; the +abort handler can check the cause of the failure, and retry. + +Checkpointed registers include all GPRs, FPRs, VRs/VSRs, LR, CCR/CR, CTR, FPCSR +and a few other status/flag regs; see the ISA for details. + +Causes of transaction aborts +============================ + +- Conflicts with cache lines used by other processors +- Signals +- Context switches +- See the ISA for full documentation of everything that will abort transactions. + + +Syscalls +======== + +Performing syscalls from within transaction is not recommended, and can lead +to unpredictable results. + +Syscalls do not by design abort transactions, but beware: The kernel code will +not be running in transactional state. The effect of syscalls will always +remain visible, but depending on the call they may abort your transaction as a +side-effect, read soon-to-be-aborted transactional data that should not remain +invisible, etc. If you constantly retry a transaction that constantly aborts +itself by calling a syscall, you'll have a livelock & make no progress. + +Simple syscalls (e.g. sigprocmask()) "could" be OK. Even things like write() +from, say, printf() should be OK as long as the kernel does not access any +memory that was accessed transactionally. + +Consider any syscalls that happen to work as debug-only -- not recommended for +production use. Best to queue them up till after the transaction is over. + + +Signals +======= + +Delivery of signals (both sync and async) during transactions provides a second +thread state (ucontext/mcontext) to represent the second transactional register +state. Signal delivery 'treclaim's to capture both register states, so signals +abort transactions. The usual ucontext_t passed to the signal handler +represents the checkpointed/original register state; the signal appears to have +arisen at 'tbegin+4'. + +If the sighandler ucontext has uc_link set, a second ucontext has been +delivered. For future compatibility the MSR.TS field should be checked to +determine the transactional state -- if so, the second ucontext in uc->uc_link +represents the active transactional registers at the point of the signal. + +For 64-bit processes, uc->uc_mcontext.regs->msr is a full 64-bit MSR and its TS +field shows the transactional mode. + +For 32-bit processes, the mcontext's MSR register is only 32 bits; the top 32 +bits are stored in the MSR of the second ucontext, i.e. in +uc->uc_link->uc_mcontext.regs->msr. The top word contains the transactional +state TS. + +However, basic signal handlers don't need to be aware of transactions +and simply returning from the handler will deal with things correctly: + +Transaction-aware signal handlers can read the transactional register state +from the second ucontext. This will be necessary for crash handlers to +determine, for example, the address of the instruction causing the SIGSEGV. + +Example signal handler: + + void crash_handler(int sig, siginfo_t *si, void *uc) + { + ucontext_t *ucp = uc; + ucontext_t *transactional_ucp = ucp->uc_link; + + if (ucp_link) { + u64 msr = ucp->uc_mcontext.regs->msr; + /* May have transactional ucontext! */ +#ifndef __powerpc64__ + msr |= ((u64)transactional_ucp->uc_mcontext.regs->msr) << 32; +#endif + if (MSR_TM_ACTIVE(msr)) { + /* Yes, we crashed during a transaction. Oops. */ + fprintf(stderr, "Transaction to be restarted at 0x%llx, but " + "crashy instruction was at 0x%llx\n", + ucp->uc_mcontext.regs->nip, + transactional_ucp->uc_mcontext.regs->nip); + } + } + + fix_the_problem(ucp->dar); + } + + +Failure cause codes used by kernel +================================== + +These are defined in , and distinguish different reasons why the +kernel aborted a transaction: + + TM_CAUSE_RESCHED Thread was rescheduled. + TM_CAUSE_FAC_UNAV FP/VEC/VSX unavailable trap. + TM_CAUSE_SYSCALL Currently unused; future syscalls that must abort + transactions for consistency will use this. + TM_CAUSE_SIGNAL Signal delivered. + TM_CAUSE_MISC Currently unused. + +These can be checked by the user program's abort handler as TEXASR[0:7]. + + +GDB +=== + +GDB and ptrace are not currently TM-aware. If one stops during a transaction, +it looks like the transaction has just started (the checkpointed state is +presented). The transaction cannot then be continued and will take the failure +handler route. Furthermore, the transactional 2nd register state will be +inaccessible. GDB can currently be used on programs using TM, but not sensibly +in parts within transactions. From 0f70b40613ee14b0cadafeb461034cff81b4419a Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 15 Feb 2013 03:35:57 -0500 Subject: [PATCH 2788/4607] ext4: use ERR_PTR() abstraction for ext4_append() Use ERR_PTR()/IS_ERR() abstraction instead of passing in a separate pointer to an integer for the error code, as a code cleanup. Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 2 ++ fs/ext4/namei.c | 65 +++++++++++++++++++++++-------------------------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index c4e9177f60c6..f4466c3650dc 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -707,6 +707,8 @@ struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode, /* ensure we send some value back into *errp */ *errp = 0; + if (create && err == 0) + err = -ENOSPC; /* should never happen */ if (err < 0) *errp = err; if (err <= 0) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 0e28c749e273..f58c053d13e0 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -51,34 +51,28 @@ static struct buffer_head *ext4_append(handle_t *handle, struct inode *inode, - ext4_lblk_t *block, int *err) + ext4_lblk_t *block) { struct buffer_head *bh; + int err = 0; if (unlikely(EXT4_SB(inode->i_sb)->s_max_dir_size_kb && ((inode->i_size >> 10) >= - EXT4_SB(inode->i_sb)->s_max_dir_size_kb))) { - *err = -ENOSPC; - return NULL; - } + EXT4_SB(inode->i_sb)->s_max_dir_size_kb))) + return ERR_PTR(-ENOSPC); *block = inode->i_size >> inode->i_sb->s_blocksize_bits; - bh = ext4_bread(handle, inode, *block, 1, err); - if (bh) { - inode->i_size += inode->i_sb->s_blocksize; - EXT4_I(inode)->i_disksize = inode->i_size; - *err = ext4_journal_get_write_access(handle, bh); - if (*err) { - brelse(bh); - bh = NULL; - } - } - if (!bh && !(*err)) { - *err = -EIO; - ext4_error(inode->i_sb, - "Directory hole detected on inode %lu\n", - inode->i_ino); + bh = ext4_bread(handle, inode, *block, 1, &err); + if (!bh) + return ERR_PTR(err); + inode->i_size += inode->i_sb->s_blocksize; + EXT4_I(inode)->i_disksize = inode->i_size; + err = ext4_journal_get_write_access(handle, bh); + if (err) { + brelse(bh); + ext4_std_error(inode->i_sb, err); + return ERR_PTR(err); } return bh; } @@ -1555,11 +1549,12 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) csum_size = sizeof(struct ext4_dir_entry_tail); - bh2 = ext4_append (handle, dir, &newblock, &err); - if (!(bh2)) { + bh2 = ext4_append(handle, dir, &newblock); + if (IS_ERR(bh2)) { brelse(*bh); *bh = NULL; - goto errout; + *error = PTR_ERR(bh2); + return NULL; } BUFFER_TRACE(*bh, "get_write_access"); @@ -1640,7 +1635,6 @@ journal_error: brelse(bh2); *bh = NULL; ext4_std_error(dir->i_sb, err); -errout: *error = err; return NULL; } @@ -1815,10 +1809,10 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry, len = ((char *) root) + (blocksize - csum_size) - (char *) de; /* Allocate new block for the 0th block's dirents */ - bh2 = ext4_append(handle, dir, &block, &retval); - if (!(bh2)) { + bh2 = ext4_append(handle, dir, &block); + if (IS_ERR(bh2)) { brelse(bh); - return retval; + return PTR_ERR(bh2); } ext4_set_inode_flag(dir, EXT4_INODE_INDEX); data1 = bh2->b_data; @@ -1950,9 +1944,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry, return make_indexed_dir(handle, dentry, inode, bh); brelse(bh); } - bh = ext4_append(handle, dir, &block, &retval); - if (!bh) - return retval; + bh = ext4_append(handle, dir, &block); + if (IS_ERR(bh)) + return PTR_ERR(bh); de = (struct ext4_dir_entry_2 *) bh->b_data; de->inode = 0; de->rec_len = ext4_rec_len_to_disk(blocksize - csum_size, blocksize); @@ -2023,9 +2017,11 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, err = -ENOSPC; goto cleanup; } - bh2 = ext4_append (handle, dir, &newblock, &err); - if (!(bh2)) + bh2 = ext4_append(handle, dir, &newblock); + if (IS_ERR(bh2)) { + err = PTR_ERR(bh2); goto cleanup; + } node2 = (struct dx_node *)(bh2->b_data); entries2 = node2->entries; memset(&node2->fake, 0, sizeof(struct fake_dirent)); @@ -2364,8 +2360,9 @@ static int ext4_init_new_dir(handle_t *handle, struct inode *dir, } inode->i_size = 0; - if (!(dir_block = ext4_append(handle, inode, &block, &err))) - goto out; + dir_block = ext4_append(handle, inode, &block); + if (IS_ERR(dir_block)) + return PTR_ERR(dir_block); BUFFER_TRACE(dir_block, "get_write_access"); err = ext4_journal_get_write_access(handle, dir_block); if (err) From cf0a6584aa6d382f802f2c3cacac23ccbccde0cd Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 6 Feb 2013 11:24:41 +0100 Subject: [PATCH 2789/4607] drm/i915: write backlight harder 770c12312ad617172b1a65b911d3e6564fc5aca8 is the first bad commit commit 770c12312ad617172b1a65b911d3e6564fc5aca8 Author: Takashi Iwai Date: Sat Aug 11 08:56:42 2012 +0200 drm/i915: Fix blank panel at reopening lid changed the register write sequence for restoring the backlight, which helped prevent non-working backlights on some machines. Turns out that the original sequence was the right thing to do for a different set of machines. Worse, setting the backlight level _after_ enabling it seems to reset it somehow. So we need to make that one conditional upon the backlight having been reset to zero, and add the old one back. Cargo-culting at it's best, but it seems to work. Cc: stable@vger.kernel.org Cc: Takashi Iwai Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=47941 Reviewed-by: Jani Nikula Acked-by: Takashi Iwai Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_panel.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index bee8cb6108a7..a3730e0289e5 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -321,6 +321,9 @@ void intel_panel_enable_backlight(struct drm_device *dev, if (dev_priv->backlight_level == 0) dev_priv->backlight_level = intel_panel_get_max_backlight(dev); + dev_priv->backlight_enabled = true; + intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); + if (INTEL_INFO(dev)->gen >= 4) { uint32_t reg, tmp; @@ -356,12 +359,12 @@ void intel_panel_enable_backlight(struct drm_device *dev, } set_level: - /* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1. - * BLC_PWM_CPU_CTL may be cleared to zero automatically when these - * registers are set. + /* Check the current backlight level and try to set again if it's zero. + * On some machines, BLC_PWM_CPU_CTL is cleared to zero automatically + * when BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1 are written. */ - dev_priv->backlight_enabled = true; - intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); + if (!intel_panel_get_backlight(dev)) + intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); } static void intel_panel_init_backlight(struct drm_device *dev) From 07ea0d85ac8adb87b817913d9720e3c76171b1f6 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 7 Feb 2013 13:34:19 -0800 Subject: [PATCH 2790/4607] drm/i915: Clarify HW context size logic This was a rebase error from when the patches originally landed. Since the context size is unsigned, there is also no use in checking if it's less than 0. The existing code is not really wrong, but it's not simple as it should be. Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_context.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index a3f06bcad551..e7ecafa8e598 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -242,7 +242,6 @@ err_destroy: void i915_gem_context_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - uint32_t ctx_size; if (!HAS_HW_CONTEXTS(dev)) { dev_priv->hw_contexts_disabled = true; @@ -254,11 +253,9 @@ void i915_gem_context_init(struct drm_device *dev) dev_priv->ring[RCS].default_context) return; - ctx_size = get_context_size(dev); - dev_priv->hw_context_size = get_context_size(dev); - dev_priv->hw_context_size = round_up(dev_priv->hw_context_size, 4096); + dev_priv->hw_context_size = round_up(get_context_size(dev), 4096); - if (ctx_size <= 0 || ctx_size > (1<<20)) { + if (dev_priv->hw_context_size > (1<<20)) { dev_priv->hw_contexts_disabled = true; return; } From 26739f12cf210cb8df35969258a1f064e8e12b63 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 7 Feb 2013 12:42:32 +0100 Subject: [PATCH 2791/4607] drm/i915: unify HDMI/DP hpd definitions They're physically the same pins and also the same bits, duplicating only confuses the reader. This also makes it a bit obvious that we have quite some code duplication going on here. Squashing that is for a larger rework in our hpd handling though. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 36 +++++++++++++++---------------- drivers/gpu/drm/i915/i915_reg.h | 28 ++++++++---------------- drivers/gpu/drm/i915/intel_dp.c | 12 +++++------ drivers/gpu/drm/i915/intel_hdmi.c | 10 ++++----- 4 files changed, 38 insertions(+), 48 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 13bb8d3f2a77..b27ffdbc98a7 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2137,12 +2137,12 @@ static void valleyview_hpd_irq_setup(struct drm_device *dev) u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); /* Note HDMI and DP share bits */ - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915) hotplug_en |= SDVOC_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915) @@ -2408,12 +2408,12 @@ static void i915_hpd_irq_setup(struct drm_device *dev) if (I915_HAS_HOTPLUG(dev)) { hotplug_en = I915_READ(PORT_HOTPLUG_EN); - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915) hotplug_en |= SDVOC_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915) @@ -2642,12 +2642,12 @@ static void i965_hpd_irq_setup(struct drm_device *dev) /* Note HDMI and DP share hotplug bits */ hotplug_en = 0; - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (IS_G4X(dev)) { if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_G4X) hotplug_en |= SDVOC_HOTPLUG_INT_EN; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 8754f9160aef..b9fdc9963817 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1625,12 +1625,9 @@ /* Hotplug control (945+ only) */ #define PORT_HOTPLUG_EN (dev_priv->info->display_mmio_offset + 0x61110) -#define HDMIB_HOTPLUG_INT_EN (1 << 29) -#define DPB_HOTPLUG_INT_EN (1 << 29) -#define HDMIC_HOTPLUG_INT_EN (1 << 28) -#define DPC_HOTPLUG_INT_EN (1 << 28) -#define HDMID_HOTPLUG_INT_EN (1 << 27) -#define DPD_HOTPLUG_INT_EN (1 << 27) +#define PORTB_HOTPLUG_INT_EN (1 << 29) +#define PORTC_HOTPLUG_INT_EN (1 << 28) +#define PORTD_HOTPLUG_INT_EN (1 << 27) #define SDVOB_HOTPLUG_INT_EN (1 << 26) #define SDVOC_HOTPLUG_INT_EN (1 << 25) #define TV_HOTPLUG_INT_EN (1 << 18) @@ -1653,19 +1650,12 @@ #define PORT_HOTPLUG_STAT (dev_priv->info->display_mmio_offset + 0x61114) /* HDMI/DP bits are gen4+ */ -#define DPB_HOTPLUG_LIVE_STATUS (1 << 29) -#define DPC_HOTPLUG_LIVE_STATUS (1 << 28) -#define DPD_HOTPLUG_LIVE_STATUS (1 << 27) -#define DPD_HOTPLUG_INT_STATUS (3 << 21) -#define DPC_HOTPLUG_INT_STATUS (3 << 19) -#define DPB_HOTPLUG_INT_STATUS (3 << 17) -/* HDMI bits are shared with the DP bits */ -#define HDMIB_HOTPLUG_LIVE_STATUS (1 << 29) -#define HDMIC_HOTPLUG_LIVE_STATUS (1 << 28) -#define HDMID_HOTPLUG_LIVE_STATUS (1 << 27) -#define HDMID_HOTPLUG_INT_STATUS (3 << 21) -#define HDMIC_HOTPLUG_INT_STATUS (3 << 19) -#define HDMIB_HOTPLUG_INT_STATUS (3 << 17) +#define PORTB_HOTPLUG_LIVE_STATUS (1 << 29) +#define PORTC_HOTPLUG_LIVE_STATUS (1 << 28) +#define PORTD_HOTPLUG_LIVE_STATUS (1 << 27) +#define PORTD_HOTPLUG_INT_STATUS (3 << 21) +#define PORTC_HOTPLUG_INT_STATUS (3 << 19) +#define PORTB_HOTPLUG_INT_STATUS (3 << 17) /* CRT/TV common between gen3+ */ #define CRT_HOTPLUG_INT_STATUS (1 << 11) #define TV_HOTPLUG_INT_STATUS (1 << 10) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 56e408b2e3c8..7b8bfe8982e6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2302,13 +2302,13 @@ g4x_dp_detect(struct intel_dp *intel_dp) switch (intel_dig_port->port) { case PORT_B: - bit = DPB_HOTPLUG_LIVE_STATUS; + bit = PORTB_HOTPLUG_LIVE_STATUS; break; case PORT_C: - bit = DPC_HOTPLUG_LIVE_STATUS; + bit = PORTC_HOTPLUG_LIVE_STATUS; break; case PORT_D: - bit = DPD_HOTPLUG_LIVE_STATUS; + bit = PORTD_HOTPLUG_LIVE_STATUS; break; default: return connector_status_unknown; @@ -2838,15 +2838,15 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, name = "DPDDC-A"; break; case PORT_B: - dev_priv->hotplug_supported_mask |= DPB_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS; name = "DPDDC-B"; break; case PORT_C: - dev_priv->hotplug_supported_mask |= DPC_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS; name = "DPDDC-C"; break; case PORT_D: - dev_priv->hotplug_supported_mask |= DPD_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS; name = "DPDDC-D"; break; default: diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 5b4efd64c2f9..5a6138c62fe9 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -802,10 +802,10 @@ static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi) switch (intel_dig_port->port) { case PORT_B: - bit = HDMIB_HOTPLUG_LIVE_STATUS; + bit = PORTB_HOTPLUG_LIVE_STATUS; break; case PORT_C: - bit = HDMIC_HOTPLUG_LIVE_STATUS; + bit = PORTC_HOTPLUG_LIVE_STATUS; break; default: bit = 0; @@ -1022,15 +1022,15 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, switch (port) { case PORT_B: intel_hdmi->ddc_bus = GMBUS_PORT_DPB; - dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS; break; case PORT_C: intel_hdmi->ddc_bus = GMBUS_PORT_DPC; - dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS; break; case PORT_D: intel_hdmi->ddc_bus = GMBUS_PORT_DPD; - dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS; break; case PORT_A: /* Internal port only for eDP. */ From 2c6602df1a7ff79c9e489602445a6d7eb728744e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 8 Feb 2013 23:13:35 +0200 Subject: [PATCH 2792/4607] drm/i915: Fix sprite_scaling_enabled for multiple sprites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have more than one sprite, so a boolean simply won't cut it. Turn sprite_scaling_enabled into a bitmask and track the state of sprite scaler for each sprite independently. Also don't re-enable LP watermarks until the sprite registers have actually been written, and thus sprite scaling has really been disabled. Signed-off-by: Ville Syrjälä Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/intel_sprite.c | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4e5a3776a3ea..a7860ea21af9 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -925,7 +925,7 @@ typedef struct drm_i915_private { /* overlay */ struct intel_overlay *overlay; - bool sprite_scaling_enabled; + unsigned int sprite_scaling_enabled; /* LVDS info */ int backlight_level; /* restore backlight to this value */ diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index f8293061d6bd..03cfd62d17e2 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -50,6 +50,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, u32 sprctl, sprscale = 0; unsigned long sprsurf_offset, linear_offset; int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + bool scaling_was_enabled = dev_priv->sprite_scaling_enabled; sprctl = I915_READ(SPRCTL(pipe)); @@ -103,19 +104,15 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, * when scaling is disabled. */ if (crtc_w != src_w || crtc_h != src_h) { - if (!dev_priv->sprite_scaling_enabled) { - dev_priv->sprite_scaling_enabled = true; + dev_priv->sprite_scaling_enabled |= 1 << pipe; + + if (!scaling_was_enabled) { intel_update_watermarks(dev); intel_wait_for_vblank(dev, pipe); } sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h; - } else { - if (dev_priv->sprite_scaling_enabled) { - dev_priv->sprite_scaling_enabled = false; - /* potentially re-enable LP watermarks */ - intel_update_watermarks(dev); - } - } + } else + dev_priv->sprite_scaling_enabled &= ~(1 << pipe); I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x); @@ -141,6 +138,10 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, I915_WRITE(SPRCTL(pipe), sprctl); I915_MODIFY_DISPBASE(SPRSURF(pipe), obj->gtt_offset + sprsurf_offset); POSTING_READ(SPRSURF(pipe)); + + /* potentially re-enable LP watermarks */ + if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled) + intel_update_watermarks(dev); } static void @@ -150,6 +151,7 @@ ivb_disable_plane(struct drm_plane *plane) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); int pipe = intel_plane->pipe; + bool scaling_was_enabled = dev_priv->sprite_scaling_enabled; I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE); /* Can't leave the scaler enabled... */ @@ -159,8 +161,11 @@ ivb_disable_plane(struct drm_plane *plane) I915_MODIFY_DISPBASE(SPRSURF(pipe), 0); POSTING_READ(SPRSURF(pipe)); - dev_priv->sprite_scaling_enabled = false; - intel_update_watermarks(dev); + dev_priv->sprite_scaling_enabled &= ~(1 << pipe); + + /* potentially re-enable LP watermarks */ + if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled) + intel_update_watermarks(dev); } static int From d46da4377689bd938795e53c4e2fb54dbcaeea44 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 8 Feb 2013 17:35:15 -0200 Subject: [PATCH 2793/4607] drm/i915: add ibx_irq_postinstall So we can remove duplicated code. Note that this function is used not only on IBX, but also CPT and LPT. Signed-off-by: Paulo Zanoni Reviewed-by: Ben Widawsky [danvet: Also bikeshed s/ironlake_enable_pch_hotplug/ibx_enable_hotplug to keep consistent with our ibx for pch naming scheme.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 68 ++++++++++++--------------------- 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b27ffdbc98a7..2cd97d1cc920 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1924,7 +1924,7 @@ static void valleyview_irq_preinstall(struct drm_device *dev) * This register is the same on all known PCH chips. */ -static void ironlake_enable_pch_hotplug(struct drm_device *dev) +static void ibx_enable_hotplug(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 hotplug; @@ -1937,6 +1937,28 @@ static void ironlake_enable_pch_hotplug(struct drm_device *dev) I915_WRITE(PCH_PORT_HOTPLUG, hotplug); } +static void ibx_irq_postinstall(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 mask; + + if (HAS_PCH_IBX(dev)) + mask = SDE_HOTPLUG_MASK | + SDE_GMBUS | + SDE_AUX_MASK; + else + mask = SDE_HOTPLUG_MASK_CPT | + SDE_GMBUS_CPT | + SDE_AUX_MASK_CPT; + + I915_WRITE(SDEIIR, I915_READ(SDEIIR)); + I915_WRITE(SDEIMR, ~mask); + I915_WRITE(SDEIER, mask); + POSTING_READ(SDEIER); + + ibx_enable_hotplug(dev); +} + static int ironlake_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -1945,8 +1967,6 @@ static int ironlake_irq_postinstall(struct drm_device *dev) DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | DE_AUX_CHANNEL_A; u32 render_irqs; - u32 hotplug_mask; - u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -1974,30 +1994,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev) I915_WRITE(GTIER, render_irqs); POSTING_READ(GTIER); - if (HAS_PCH_CPT(dev)) { - hotplug_mask = (SDE_CRT_HOTPLUG_CPT | - SDE_PORTB_HOTPLUG_CPT | - SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT | - SDE_AUX_MASK_CPT); - } else { - hotplug_mask = (SDE_CRT_HOTPLUG | - SDE_PORTB_HOTPLUG | - SDE_PORTC_HOTPLUG | - SDE_PORTD_HOTPLUG | - SDE_GMBUS | - SDE_AUX_MASK); - } - - pch_irq_mask = ~hotplug_mask; - - I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, pch_irq_mask); - I915_WRITE(SDEIER, hotplug_mask); - POSTING_READ(SDEIER); - - ironlake_enable_pch_hotplug(dev); + ibx_irq_postinstall(dev); if (IS_IRONLAKE_M(dev)) { /* Clear & enable PCU event interrupts */ @@ -2020,8 +2017,6 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB; u32 render_irqs; - u32 hotplug_mask; - u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -2045,20 +2040,7 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) I915_WRITE(GTIER, render_irqs); POSTING_READ(GTIER); - hotplug_mask = (SDE_CRT_HOTPLUG_CPT | - SDE_PORTB_HOTPLUG_CPT | - SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT | - SDE_AUX_MASK_CPT); - pch_irq_mask = ~hotplug_mask; - - I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, pch_irq_mask); - I915_WRITE(SDEIER, hotplug_mask); - POSTING_READ(SDEIER); - - ironlake_enable_pch_hotplug(dev); + ibx_irq_postinstall(dev); return 0; } From 41907ddc1b71aaa4ef5290f46f0ec49d581d6aac Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 8 Feb 2013 11:32:47 -0800 Subject: [PATCH 2794/4607] drm/i915: Fix gen2 mappable calculations When I refactored the code initially, I forgot that gen2 uses a different bar for the CPU mappable aperture. The agp-less code knows nothing of generations less than 5, so we have to expand the gtt_probe function to include the mappable base and end. It was originally broken by me: commit baa09f5fd8a6d033ec075355dda99a65b7f6a0f3 Author: Ben Widawsky Date: Thu Jan 24 13:49:57 2013 -0800 drm/i915: Add probe and remove to the gtt ops Reported-by: Chris Wilson Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-gtt.c | 5 ++++- drivers/gpu/drm/i915/i915_drv.h | 3 ++- drivers/gpu/drm/i915/i915_gem_gtt.c | 23 ++++++++++++++--------- include/drm/intel-gtt.h | 3 ++- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index d8e7e6c9114e..207e5c36e9ec 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -1371,10 +1371,13 @@ int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, } EXPORT_SYMBOL(intel_gmch_probe); -void intel_gtt_get(size_t *gtt_total, size_t *stolen_size) +void intel_gtt_get(size_t *gtt_total, size_t *stolen_size, + phys_addr_t *mappable_base, unsigned long *mappable_end) { *gtt_total = intel_private.gtt_total_entries << PAGE_SHIFT; *stolen_size = intel_private.stolen_size; + *mappable_base = intel_private.gma_bus_addr; + *mappable_end = intel_private.gtt_mappable_entries << PAGE_SHIFT; } EXPORT_SYMBOL(intel_gtt_get); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a7860ea21af9..0f1d15bac517 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -399,7 +399,8 @@ struct i915_gtt { /* global gtt ops */ int (*gtt_probe)(struct drm_device *dev, size_t *gtt_total, - size_t *stolen); + size_t *stolen, phys_addr_t *mappable_base, + unsigned long *mappable_end); void (*gtt_remove)(struct drm_device *dev); void (*gtt_clear_range)(struct drm_device *dev, unsigned int first_entry, diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index bdaca3f47988..926a1e2dd234 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -725,7 +725,9 @@ static inline size_t gen7_get_stolen_size(u16 snb_gmch_ctl) static int gen6_gmch_probe(struct drm_device *dev, size_t *gtt_total, - size_t *stolen) + size_t *stolen, + phys_addr_t *mappable_base, + unsigned long *mappable_end) { struct drm_i915_private *dev_priv = dev->dev_private; phys_addr_t gtt_bus_addr; @@ -733,11 +735,13 @@ static int gen6_gmch_probe(struct drm_device *dev, u16 snb_gmch_ctl; int ret; + *mappable_base = pci_resource_start(dev->pdev, 2); + *mappable_end = pci_resource_len(dev->pdev, 2); + /* 64/512MB is the current min/max we actually know of, but this is just * a coarse sanity check. */ - if ((dev_priv->gtt.mappable_end < (64<<20) || - (dev_priv->gtt.mappable_end > (512<<20)))) { + if ((*mappable_end < (64<<20) || (*mappable_end > (512<<20)))) { DRM_ERROR("Unknown GMADR size (%lx)\n", dev_priv->gtt.mappable_end); return -ENXIO; @@ -782,7 +786,9 @@ static void gen6_gmch_remove(struct drm_device *dev) static int i915_gmch_probe(struct drm_device *dev, size_t *gtt_total, - size_t *stolen) + size_t *stolen, + phys_addr_t *mappable_base, + unsigned long *mappable_end) { struct drm_i915_private *dev_priv = dev->dev_private; int ret; @@ -793,7 +799,7 @@ static int i915_gmch_probe(struct drm_device *dev, return -EIO; } - intel_gtt_get(gtt_total, stolen); + intel_gtt_get(gtt_total, stolen, mappable_base, mappable_end); dev_priv->gtt.do_idle_maps = needs_idle_maps(dev_priv->dev); dev_priv->gtt.gtt_clear_range = i915_ggtt_clear_range; @@ -814,9 +820,6 @@ int i915_gem_gtt_init(struct drm_device *dev) unsigned long gtt_size; int ret; - gtt->mappable_base = pci_resource_start(dev->pdev, 2); - gtt->mappable_end = pci_resource_len(dev->pdev, 2); - if (INTEL_INFO(dev)->gen <= 5) { dev_priv->gtt.gtt_probe = i915_gmch_probe; dev_priv->gtt.gtt_remove = i915_gmch_remove; @@ -826,7 +829,9 @@ int i915_gem_gtt_init(struct drm_device *dev) } ret = dev_priv->gtt.gtt_probe(dev, &dev_priv->gtt.total, - &dev_priv->gtt.stolen_size); + &dev_priv->gtt.stolen_size, + >t->mappable_base, + >t->mappable_end); if (ret) return ret; diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index cf105557fea9..b08bdade6002 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -3,7 +3,8 @@ #ifndef _DRM_INTEL_GTT_H #define _DRM_INTEL_GTT_H -void intel_gtt_get(size_t *gtt_total, size_t *stolen_size); +void intel_gtt_get(size_t *gtt_total, size_t *stolen_size, + phys_addr_t *mappable_base, unsigned long *mappable_end); int intel_gmch_probe(struct pci_dev *bridge_pdev, struct pci_dev *gpu_pdev, struct agp_bridge_data *bridge); From 4fc7c971c3aedf937f824c063d698779d25c3330 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 8 Feb 2013 11:49:24 -0800 Subject: [PATCH 2795/4607] drm/i915: Extract ring init from hw_init The ring initialization will differ a bit in upcoming generations, and this split will prepare the code for what's needed. This patch also fixes a bug introduced in: commit 99433931950f33039d9e1a52b4ed9af3f1b58e84 Author: Mika Kuoppala Date: Tue Jan 22 14:12:17 2013 +0200 drm/i915: use gem_set_seqno() on hardware init After doing the extraction, the bad error handling became obvious. I acknowledge that this should be two patches, but it's a pretty small/trivial patch. If requested, I can certainly do the fix as a distinct patch. v2: Should be cleanup blt, not init blt on failure (Chris) v3: Forgot to git add on v2 Cc: Mika Kuoppala Reviewed-by: Chris Wilson Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 53 +++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 62be74899c2b..d8618f32557e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3930,22 +3930,11 @@ intel_enable_blt(struct drm_device *dev) return true; } -int -i915_gem_init_hw(struct drm_device *dev) +static int i915_gem_init_rings(struct drm_device *dev) { - drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_private *dev_priv = dev->dev_private; int ret; - if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt()) - return -EIO; - - if (IS_HASWELL(dev) && (I915_READ(0x120010) == 1)) - I915_WRITE(0x9008, I915_READ(0x9008) | 0xf0000); - - i915_gem_l3_remap(dev); - - i915_gem_init_swizzling(dev); - ret = intel_init_render_ring_buffer(dev); if (ret) return ret; @@ -3963,6 +3952,38 @@ i915_gem_init_hw(struct drm_device *dev) } ret = i915_gem_set_seqno(dev, ((u32)~0 - 0x1000)); + if (ret) + goto cleanup_blt_ring; + + return 0; + +cleanup_blt_ring: + intel_cleanup_ring_buffer(&dev_priv->ring[BCS]); +cleanup_bsd_ring: + intel_cleanup_ring_buffer(&dev_priv->ring[VCS]); +cleanup_render_ring: + intel_cleanup_ring_buffer(&dev_priv->ring[RCS]); + + return ret; +} + +int +i915_gem_init_hw(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; + + if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt()) + return -EIO; + + if (IS_HASWELL(dev) && (I915_READ(0x120010) == 1)) + I915_WRITE(0x9008, I915_READ(0x9008) | 0xf0000); + + i915_gem_l3_remap(dev); + + i915_gem_init_swizzling(dev); + + ret = i915_gem_init_rings(dev); if (ret) return ret; @@ -3974,12 +3995,6 @@ i915_gem_init_hw(struct drm_device *dev) i915_gem_init_ppgtt(dev); return 0; - -cleanup_bsd_ring: - intel_cleanup_ring_buffer(&dev_priv->ring[VCS]); -cleanup_render_ring: - intel_cleanup_ring_buffer(&dev_priv->ring[RCS]); - return ret; } int i915_gem_init(struct drm_device *dev) From f73f760725636b9d0c3786273e185b053516d1eb Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 11 Feb 2013 13:31:27 -0800 Subject: [PATCH 2796/4607] drm/i915/ctx: Remove bad invariant It's not that the assertion is incorrect, but rather that we can call do_destroy early in loading, and we will falsely BUG(). Since contexts have been in for a while now, and in the internal APIs are pretty stable, it should be fairly safe to remove this. v2: Remove unused dev_priv, and dev Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_context.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index e7ecafa8e598..21177d9df423 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -126,13 +126,8 @@ static int get_context_size(struct drm_device *dev) static void do_destroy(struct i915_hw_context *ctx) { - struct drm_device *dev = ctx->obj->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; - if (ctx->file_priv) idr_remove(&ctx->file_priv->context_idr, ctx->id); - else - BUG_ON(ctx != dev_priv->ring[RCS].default_context); drm_gem_object_unreference(&ctx->obj->base); kfree(ctx); From 55ccb1a8b4c14c086427fd6b7272448fbd0c4449 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 14 Feb 2013 17:47:35 +0100 Subject: [PATCH 2797/4607] ARM: omap2: include linux/errno.h in hwmod_reset The newly created omap_hwmod_reset.c is missing an include of linux/errno.h in commit c02060d8 "ARM: OMAP4+: AESS: enable internal auto-gating during initial setup". It still works in omap2_defconfig, but not in all other combinations. Without this patch, building allmodconfig results in: arch/arm/mach-omap2/omap_hwmod_reset.c: In function 'omap_hwmod_aess_preprogram': arch/arm/mach-omap2/omap_hwmod_reset.c:47:11: error: 'EINVAL' undeclared (first use in this function) arch/arm/mach-omap2/omap_hwmod_reset.c:47:11: note: each undeclared identifier is reported only once for each function it appears in Signed-off-by: Arnd Bergmann Acked-by: Tony Lindgren Cc: Paul Walmsley Cc: Sebastien Guiriec --- arch/arm/mach-omap2/omap_hwmod_reset.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-omap2/omap_hwmod_reset.c b/arch/arm/mach-omap2/omap_hwmod_reset.c index bba43fa627d3..65e186c9df55 100644 --- a/arch/arm/mach-omap2/omap_hwmod_reset.c +++ b/arch/arm/mach-omap2/omap_hwmod_reset.c @@ -24,6 +24,7 @@ * 02110-1301 USA */ #include +#include #include From 483479c26a65e5f0cc95e9324f313bc95c7dc6fd Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 4 Feb 2013 14:02:35 +0300 Subject: [PATCH 2798/4607] NFS: use SUNRPC cache creation and destruction helper for DNS cache This cache was the first containerized and doesn't use net-aware cache creation and destruction helpers. This is a cleanup patch which just makes code looks clearer and reduce amount of lines of code. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfs/dns_resolve.c | 58 ++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index 62c8c4127219..d5ce5f4f0f5f 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -353,48 +353,39 @@ ssize_t nfs_dns_resolve_name(struct net *net, char *name, } EXPORT_SYMBOL_GPL(nfs_dns_resolve_name); +static struct cache_detail nfs_dns_resolve_template = { + .owner = THIS_MODULE, + .hash_size = NFS_DNS_HASHTBL_SIZE, + .name = "dns_resolve", + .cache_put = nfs_dns_ent_put, + .cache_upcall = nfs_dns_upcall, + .cache_parse = nfs_dns_parse, + .cache_show = nfs_dns_show, + .match = nfs_dns_match, + .init = nfs_dns_ent_init, + .update = nfs_dns_ent_update, + .alloc = nfs_dns_ent_alloc, +}; + + int nfs_dns_resolver_cache_init(struct net *net) { - int err = -ENOMEM; + int err; struct nfs_net *nn = net_generic(net, nfs_net_id); - struct cache_detail *cd; - struct cache_head **tbl; - cd = kzalloc(sizeof(struct cache_detail), GFP_KERNEL); - if (cd == NULL) - goto err_cd; + nn->nfs_dns_resolve = cache_create_net(&nfs_dns_resolve_template, net); + if (IS_ERR(nn->nfs_dns_resolve)) + return PTR_ERR(nn->nfs_dns_resolve); - tbl = kzalloc(NFS_DNS_HASHTBL_SIZE * sizeof(struct cache_head *), - GFP_KERNEL); - if (tbl == NULL) - goto err_tbl; - - cd->owner = THIS_MODULE, - cd->hash_size = NFS_DNS_HASHTBL_SIZE, - cd->hash_table = tbl, - cd->name = "dns_resolve", - cd->cache_put = nfs_dns_ent_put, - cd->cache_upcall = nfs_dns_upcall, - cd->cache_parse = nfs_dns_parse, - cd->cache_show = nfs_dns_show, - cd->match = nfs_dns_match, - cd->init = nfs_dns_ent_init, - cd->update = nfs_dns_ent_update, - cd->alloc = nfs_dns_ent_alloc, - - nfs_cache_init(cd); - err = nfs_cache_register_net(net, cd); + nfs_cache_init(nn->nfs_dns_resolve); + err = nfs_cache_register_net(net, nn->nfs_dns_resolve); if (err) goto err_reg; - nn->nfs_dns_resolve = cd; return 0; err_reg: - nfs_cache_destroy(cd); - kfree(cd->hash_table); -err_tbl: - kfree(cd); -err_cd: + nfs_cache_destroy(nn->nfs_dns_resolve); + cache_destroy_net(nn->nfs_dns_resolve, net); return err; } @@ -405,8 +396,7 @@ void nfs_dns_resolver_cache_destroy(struct net *net) nfs_cache_unregister_net(net, cd); nfs_cache_destroy(cd); - kfree(cd->hash_table); - kfree(cd); + cache_destroy_net(nn->nfs_dns_resolve, net); } static int rpc_pipefs_event(struct notifier_block *nb, unsigned long event, From 462b8f6bf1d3f5feb7a346394036dbc1df3a8ed5 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 4 Feb 2013 14:02:40 +0300 Subject: [PATCH 2799/4607] NFS: simplify and clean cache library This is a cleanup patch. Such helpers like nfs_cache_init() and nfs_cache_destroy() are redundant, because they are just a wrappers around sunrpc_init_cache_detail() and sunrpc_destroy_cache_detail() respectively. So let's remove them completely and move corresponding logic to nfs_cache_register_net() and nfs_cache_unregister_net() respectively (since they are called together anyway). Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfs/cache_lib.c | 12 +++--------- fs/nfs/cache_lib.h | 2 -- fs/nfs/dns_resolve.c | 6 +----- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/fs/nfs/cache_lib.c b/fs/nfs/cache_lib.c index 862a2f16db64..5f7b053720ee 100644 --- a/fs/nfs/cache_lib.c +++ b/fs/nfs/cache_lib.c @@ -128,10 +128,13 @@ int nfs_cache_register_net(struct net *net, struct cache_detail *cd) struct super_block *pipefs_sb; int ret = 0; + sunrpc_init_cache_detail(cd); pipefs_sb = rpc_get_sb_net(net); if (pipefs_sb) { ret = nfs_cache_register_sb(pipefs_sb, cd); rpc_put_sb_net(net); + if (ret) + sunrpc_destroy_cache_detail(cd); } return ret; } @@ -151,14 +154,5 @@ void nfs_cache_unregister_net(struct net *net, struct cache_detail *cd) nfs_cache_unregister_sb(pipefs_sb, cd); rpc_put_sb_net(net); } -} - -void nfs_cache_init(struct cache_detail *cd) -{ - sunrpc_init_cache_detail(cd); -} - -void nfs_cache_destroy(struct cache_detail *cd) -{ sunrpc_destroy_cache_detail(cd); } diff --git a/fs/nfs/cache_lib.h b/fs/nfs/cache_lib.h index 317db95e37f8..4116d2c3f52f 100644 --- a/fs/nfs/cache_lib.h +++ b/fs/nfs/cache_lib.h @@ -23,8 +23,6 @@ extern struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void); extern void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq); extern int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq); -extern void nfs_cache_init(struct cache_detail *cd); -extern void nfs_cache_destroy(struct cache_detail *cd); extern int nfs_cache_register_net(struct net *net, struct cache_detail *cd); extern void nfs_cache_unregister_net(struct net *net, struct cache_detail *cd); extern int nfs_cache_register_sb(struct super_block *sb, diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index d5ce5f4f0f5f..9cbc98a4159d 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -377,14 +377,12 @@ int nfs_dns_resolver_cache_init(struct net *net) if (IS_ERR(nn->nfs_dns_resolve)) return PTR_ERR(nn->nfs_dns_resolve); - nfs_cache_init(nn->nfs_dns_resolve); err = nfs_cache_register_net(net, nn->nfs_dns_resolve); if (err) goto err_reg; return 0; err_reg: - nfs_cache_destroy(nn->nfs_dns_resolve); cache_destroy_net(nn->nfs_dns_resolve, net); return err; } @@ -392,10 +390,8 @@ err_reg: void nfs_dns_resolver_cache_destroy(struct net *net) { struct nfs_net *nn = net_generic(net, nfs_net_id); - struct cache_detail *cd = nn->nfs_dns_resolve; - nfs_cache_unregister_net(net, cd); - nfs_cache_destroy(cd); + nfs_cache_unregister_net(net, nn->nfs_dns_resolve); cache_destroy_net(nn->nfs_dns_resolve, net); } From 73fb847a44224d5708550e4be7baba9da75e00af Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 4 Feb 2013 14:02:45 +0300 Subject: [PATCH 2800/4607] SUNRPC: introduce cache_detail->cache_request callback This callback will allow to simplify upcalls in further patches in this series. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfs/dns_resolve.c | 3 ++- fs/nfsd/export.c | 6 ++++-- fs/nfsd/nfs4idmap.c | 6 ++++-- include/linux/sunrpc/cache.h | 4 ++++ net/sunrpc/auth_gss/svcauth_gss.c | 3 ++- net/sunrpc/svcauth_unix.c | 6 ++++-- 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index 9cbc98a4159d..55bc5d11457c 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -144,7 +144,7 @@ static int nfs_dns_upcall(struct cache_detail *cd, ret = nfs_cache_upcall(cd, key->hostname); if (ret) - ret = sunrpc_cache_pipe_upcall(cd, ch, nfs_dns_request); + ret = sunrpc_cache_pipe_upcall(cd, ch, cd->cache_request); return ret; } @@ -359,6 +359,7 @@ static struct cache_detail nfs_dns_resolve_template = { .name = "dns_resolve", .cache_put = nfs_dns_ent_put, .cache_upcall = nfs_dns_upcall, + .cache_request = nfs_dns_request, .cache_parse = nfs_dns_parse, .cache_show = nfs_dns_show, .match = nfs_dns_match, diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 8e9df45b3d3c..0e16c7fa6800 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -69,7 +69,7 @@ static void expkey_request(struct cache_detail *cd, static int expkey_upcall(struct cache_detail *cd, struct cache_head *h) { - return sunrpc_cache_pipe_upcall(cd, h, expkey_request); + return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); } static struct svc_expkey *svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new, @@ -246,6 +246,7 @@ static struct cache_detail svc_expkey_cache_template = { .name = "nfsd.fh", .cache_put = expkey_put, .cache_upcall = expkey_upcall, + .cache_request = expkey_request, .cache_parse = expkey_parse, .cache_show = expkey_show, .match = expkey_match, @@ -340,7 +341,7 @@ static void svc_export_request(struct cache_detail *cd, static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h) { - return sunrpc_cache_pipe_upcall(cd, h, svc_export_request); + return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); } static struct svc_export *svc_export_update(struct svc_export *new, @@ -714,6 +715,7 @@ static struct cache_detail svc_export_cache_template = { .name = "nfsd.export", .cache_put = svc_export_put, .cache_upcall = svc_export_upcall, + .cache_request = svc_export_request, .cache_parse = svc_export_parse, .cache_show = svc_export_show, .match = svc_export_match, diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index a1f10c0a6255..9033dfde1812 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -142,7 +142,7 @@ idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, static int idtoname_upcall(struct cache_detail *cd, struct cache_head *ch) { - return sunrpc_cache_pipe_upcall(cd, ch, idtoname_request); + return sunrpc_cache_pipe_upcall(cd, ch, cd->cache_request); } static int @@ -193,6 +193,7 @@ static struct cache_detail idtoname_cache_template = { .name = "nfs4.idtoname", .cache_put = ent_put, .cache_upcall = idtoname_upcall, + .cache_request = idtoname_request, .cache_parse = idtoname_parse, .cache_show = idtoname_show, .warn_no_listener = warn_no_idmapd, @@ -323,7 +324,7 @@ nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, static int nametoid_upcall(struct cache_detail *cd, struct cache_head *ch) { - return sunrpc_cache_pipe_upcall(cd, ch, nametoid_request); + return sunrpc_cache_pipe_upcall(cd, ch, cd->cache_request); } static int @@ -366,6 +367,7 @@ static struct cache_detail nametoid_cache_template = { .name = "nfs4.nametoid", .cache_put = ent_put, .cache_upcall = nametoid_upcall, + .cache_request = nametoid_request, .cache_parse = nametoid_parse, .cache_show = nametoid_show, .warn_no_listener = warn_no_idmapd, diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 5dc9ee4d616e..4f1c8582053c 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -83,6 +83,10 @@ struct cache_detail { int (*cache_upcall)(struct cache_detail *, struct cache_head *); + void (*cache_request)(struct cache_detail *cd, + struct cache_head *ch, + char **bpp, int *blen); + int (*cache_parse)(struct cache_detail *, char *buf, int len); diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index a5b41e2ac25a..1b0df530b59d 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -184,7 +184,7 @@ static void rsi_request(struct cache_detail *cd, static int rsi_upcall(struct cache_detail *cd, struct cache_head *h) { - return sunrpc_cache_pipe_upcall(cd, h, rsi_request); + return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); } @@ -276,6 +276,7 @@ static struct cache_detail rsi_cache_template = { .name = "auth.rpcsec.init", .cache_put = rsi_put, .cache_upcall = rsi_upcall, + .cache_request = rsi_request, .cache_parse = rsi_parse, .match = rsi_match, .init = rsi_init, diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index ce34c8e5b8eb..18b8742eaa50 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -159,7 +159,7 @@ static void ip_map_request(struct cache_detail *cd, static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h) { - return sunrpc_cache_pipe_upcall(cd, h, ip_map_request); + return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); } static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class, struct in6_addr *addr); @@ -472,7 +472,7 @@ static void unix_gid_request(struct cache_detail *cd, static int unix_gid_upcall(struct cache_detail *cd, struct cache_head *h) { - return sunrpc_cache_pipe_upcall(cd, h, unix_gid_request); + return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); } static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, uid_t uid); @@ -578,6 +578,7 @@ static struct cache_detail unix_gid_cache_template = { .name = "auth.unix.gid", .cache_put = unix_gid_put, .cache_upcall = unix_gid_upcall, + .cache_request = unix_gid_request, .cache_parse = unix_gid_parse, .cache_show = unix_gid_show, .match = unix_gid_match, @@ -875,6 +876,7 @@ static struct cache_detail ip_map_cache_template = { .name = "auth.unix.ip", .cache_put = ip_map_put, .cache_upcall = ip_map_upcall, + .cache_request = ip_map_request, .cache_parse = ip_map_parse, .cache_show = ip_map_show, .match = ip_map_match, From 2d4383383b0b04ca380b67aa2d7397d0b399dcbf Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 4 Feb 2013 14:02:50 +0300 Subject: [PATCH 2801/4607] SUNRPC: rework cache upcall logic For most of SUNRPC caches (except NFS DNS cache) cache_detail->cache_upcall is redundant since all that it's implementations are doing is calling sunrpc_cache_pipe_upcall() with proper function address argument. Cache request function address is now stored on cache_detail structure and thus all the code can be simplified. Now, for those cache details, which doesn't have cache_upcall callback (the only one, which still has is nfs_dns_resolve_template) sunrpc_cache_pipe_upcall will be called instead. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 12 ------------ fs/nfsd/nfs4idmap.c | 14 -------------- net/sunrpc/auth_gss/svcauth_gss.c | 7 ------- net/sunrpc/cache.c | 11 +++++++---- net/sunrpc/svcauth_unix.c | 12 ------------ 5 files changed, 7 insertions(+), 49 deletions(-) diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 0e16c7fa6800..15ebf91982b0 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -67,11 +67,6 @@ static void expkey_request(struct cache_detail *cd, (*bpp)[-1] = '\n'; } -static int expkey_upcall(struct cache_detail *cd, struct cache_head *h) -{ - return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); -} - static struct svc_expkey *svc_expkey_update(struct cache_detail *cd, struct svc_expkey *new, struct svc_expkey *old); static struct svc_expkey *svc_expkey_lookup(struct cache_detail *cd, struct svc_expkey *); @@ -245,7 +240,6 @@ static struct cache_detail svc_expkey_cache_template = { .hash_size = EXPKEY_HASHMAX, .name = "nfsd.fh", .cache_put = expkey_put, - .cache_upcall = expkey_upcall, .cache_request = expkey_request, .cache_parse = expkey_parse, .cache_show = expkey_show, @@ -339,11 +333,6 @@ static void svc_export_request(struct cache_detail *cd, (*bpp)[-1] = '\n'; } -static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h) -{ - return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); -} - static struct svc_export *svc_export_update(struct svc_export *new, struct svc_export *old); static struct svc_export *svc_export_lookup(struct svc_export *); @@ -714,7 +703,6 @@ static struct cache_detail svc_export_cache_template = { .hash_size = EXPORT_HASHMAX, .name = "nfsd.export", .cache_put = svc_export_put, - .cache_upcall = svc_export_upcall, .cache_request = svc_export_request, .cache_parse = svc_export_parse, .cache_show = svc_export_show, diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 9033dfde1812..d9402ea9d751 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -139,12 +139,6 @@ idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, (*bpp)[-1] = '\n'; } -static int -idtoname_upcall(struct cache_detail *cd, struct cache_head *ch) -{ - return sunrpc_cache_pipe_upcall(cd, ch, cd->cache_request); -} - static int idtoname_match(struct cache_head *ca, struct cache_head *cb) { @@ -192,7 +186,6 @@ static struct cache_detail idtoname_cache_template = { .hash_size = ENT_HASHMAX, .name = "nfs4.idtoname", .cache_put = ent_put, - .cache_upcall = idtoname_upcall, .cache_request = idtoname_request, .cache_parse = idtoname_parse, .cache_show = idtoname_show, @@ -321,12 +314,6 @@ nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp, (*bpp)[-1] = '\n'; } -static int -nametoid_upcall(struct cache_detail *cd, struct cache_head *ch) -{ - return sunrpc_cache_pipe_upcall(cd, ch, cd->cache_request); -} - static int nametoid_match(struct cache_head *ca, struct cache_head *cb) { @@ -366,7 +353,6 @@ static struct cache_detail nametoid_cache_template = { .hash_size = ENT_HASHMAX, .name = "nfs4.nametoid", .cache_put = ent_put, - .cache_upcall = nametoid_upcall, .cache_request = nametoid_request, .cache_parse = nametoid_parse, .cache_show = nametoid_show, diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 1b0df530b59d..eb2b1f74d054 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -182,12 +182,6 @@ static void rsi_request(struct cache_detail *cd, (*bpp)[-1] = '\n'; } -static int rsi_upcall(struct cache_detail *cd, struct cache_head *h) -{ - return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); -} - - static int rsi_parse(struct cache_detail *cd, char *mesg, int mlen) { @@ -275,7 +269,6 @@ static struct cache_detail rsi_cache_template = { .hash_size = RSI_HASHMAX, .name = "auth.rpcsec.init", .cache_put = rsi_put, - .cache_upcall = rsi_upcall, .cache_request = rsi_request, .cache_parse = rsi_parse, .match = rsi_match, diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 9f8470353362..51853d8ed0bc 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -196,9 +196,9 @@ EXPORT_SYMBOL_GPL(sunrpc_cache_update); static int cache_make_upcall(struct cache_detail *cd, struct cache_head *h) { - if (!cd->cache_upcall) - return -EINVAL; - return cd->cache_upcall(cd, h); + if (cd->cache_upcall) + return cd->cache_upcall(cd, h); + return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); } static inline int cache_is_valid(struct cache_detail *detail, struct cache_head *h) @@ -1152,6 +1152,9 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h, char *bp; int len; + if (!detail->cache_request) + return -EINVAL; + if (!cache_listeners_exist(detail)) { warn_no_listener(detail); return -EINVAL; @@ -1605,7 +1608,7 @@ static int create_cache_proc_entries(struct cache_detail *cd, struct net *net) if (p == NULL) goto out_nomem; - if (cd->cache_upcall || cd->cache_parse) { + if (cd->cache_request || cd->cache_parse) { p = proc_create_data("channel", S_IFREG|S_IRUSR|S_IWUSR, cd->u.procfs.proc_ent, &cache_file_operations_procfs, cd); diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 18b8742eaa50..5085804ec8a7 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -157,11 +157,6 @@ static void ip_map_request(struct cache_detail *cd, (*bpp)[-1] = '\n'; } -static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h) -{ - return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); -} - static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class, struct in6_addr *addr); static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm, struct unix_domain *udom, time_t expiry); @@ -470,11 +465,6 @@ static void unix_gid_request(struct cache_detail *cd, (*bpp)[-1] = '\n'; } -static int unix_gid_upcall(struct cache_detail *cd, struct cache_head *h) -{ - return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); -} - static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, uid_t uid); static int unix_gid_parse(struct cache_detail *cd, @@ -577,7 +567,6 @@ static struct cache_detail unix_gid_cache_template = { .hash_size = GID_HASHMAX, .name = "auth.unix.gid", .cache_put = unix_gid_put, - .cache_upcall = unix_gid_upcall, .cache_request = unix_gid_request, .cache_parse = unix_gid_parse, .cache_show = unix_gid_show, @@ -875,7 +864,6 @@ static struct cache_detail ip_map_cache_template = { .hash_size = IP_HASHMAX, .name = "auth.unix.ip", .cache_put = ip_map_put, - .cache_upcall = ip_map_upcall, .cache_request = ip_map_request, .cache_parse = ip_map_parse, .cache_show = ip_map_show, From 21cd1254d3402a72927ed744e8ac1a7cf532f1ea Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 4 Feb 2013 14:02:55 +0300 Subject: [PATCH 2802/4607] SUNRPC: remove "cache_request" argument in sunrpc_cache_pipe_upcall() function Passing this pointer is redundant since it's stored on cache_detail structure, which is also passed to sunrpc_cache_pipe_upcall () function. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfs/dns_resolve.c | 2 +- include/linux/sunrpc/cache.h | 6 +----- net/sunrpc/cache.c | 10 +++------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index 55bc5d11457c..945527092295 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -144,7 +144,7 @@ static int nfs_dns_upcall(struct cache_detail *cd, ret = nfs_cache_upcall(cd, key->hostname); if (ret) - ret = sunrpc_cache_pipe_upcall(cd, ch, cd->cache_request); + ret = sunrpc_cache_pipe_upcall(cd, ch); return ret; } diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 4f1c8582053c..303399b1ba59 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -161,11 +161,7 @@ sunrpc_cache_update(struct cache_detail *detail, struct cache_head *new, struct cache_head *old, int hash); extern int -sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h, - void (*cache_request)(struct cache_detail *, - struct cache_head *, - char **, - int *)); +sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h); extern void cache_clean_deferred(void *owner); diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 51853d8ed0bc..8ffec5aebeff 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -198,7 +198,7 @@ static int cache_make_upcall(struct cache_detail *cd, struct cache_head *h) { if (cd->cache_upcall) return cd->cache_upcall(cd, h); - return sunrpc_cache_pipe_upcall(cd, h, cd->cache_request); + return sunrpc_cache_pipe_upcall(cd, h); } static inline int cache_is_valid(struct cache_detail *detail, struct cache_head *h) @@ -1140,11 +1140,7 @@ static bool cache_listeners_exist(struct cache_detail *detail) * * Each request is at most one page long. */ -int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h, - void (*cache_request)(struct cache_detail *, - struct cache_head *, - char **, - int *)) +int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) { char *buf; @@ -1172,7 +1168,7 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h, bp = buf; len = PAGE_SIZE; - cache_request(detail, h, &bp, &len); + detail->cache_request(detail, h, &bp, &len); if (len < 0) { kfree(buf); From d94af6dea9cd680fb795dbc409a7360f1c63dc34 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Mon, 4 Feb 2013 14:03:03 +0300 Subject: [PATCH 2803/4607] SUNRPC: move cache_detail->cache_request callback call to cache_read() The reason to move cache_request() callback call from sunrpc_cache_pipe_upcall() to cache_read() is that this garantees, that cache access will be done userspace process context (only userspace process have proper root context). This is required for NFSd support in container: svc_export_request() (which is cache_request callback) calls d_path(), which, in turn, traverse dentry up to current->fs->root. Kernel threads always have global root, while container have be in "root jail" - i.e. have it's own nested root. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- net/sunrpc/cache.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 8ffec5aebeff..55024714f001 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -750,6 +750,18 @@ struct cache_reader { int offset; /* if non-0, we have a refcnt on next request */ }; +static int cache_request(struct cache_detail *detail, + struct cache_request *crq) +{ + char *bp = crq->buf; + int len = PAGE_SIZE; + + detail->cache_request(detail, crq->item, &bp, &len); + if (len < 0) + return -EAGAIN; + return PAGE_SIZE - len; +} + static ssize_t cache_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos, struct cache_detail *cd) { @@ -784,6 +796,13 @@ static ssize_t cache_read(struct file *filp, char __user *buf, size_t count, rq->readers++; spin_unlock(&queue_lock); + if (rq->len == 0) { + err = cache_request(cd, rq); + if (err < 0) + goto out; + rq->len = err; + } + if (rp->offset == 0 && !test_bit(CACHE_PENDING, &rq->item->flags)) { err = -EAGAIN; spin_lock(&queue_lock); @@ -1145,8 +1164,6 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) char *buf; struct cache_request *crq; - char *bp; - int len; if (!detail->cache_request) return -EINVAL; @@ -1166,19 +1183,10 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h) return -EAGAIN; } - bp = buf; len = PAGE_SIZE; - - detail->cache_request(detail, h, &bp, &len); - - if (len < 0) { - kfree(buf); - kfree(crq); - return -EAGAIN; - } crq->q.reader = 0; crq->item = cache_get(h); crq->buf = buf; - crq->len = PAGE_SIZE - len; + crq->len = 0; crq->readers = 0; spin_lock(&queue_lock); list_add_tail(&crq->q.list, &detail->queue); From 1ac8362977b9ec75779170ac3074c7b36ab19b82 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 14 Feb 2013 16:45:13 -0500 Subject: [PATCH 2804/4607] nfsd: fix comments on nfsd_cache_lookup Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfscache.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c index 40db57eb2b06..2f9c2d26a2b9 100644 --- a/fs/nfsd/nfscache.c +++ b/fs/nfsd/nfscache.c @@ -302,8 +302,10 @@ nfsd_cache_search(struct svc_rqst *rqstp, __wsum csum) /* * Try to find an entry matching the current call in the cache. When none - * is found, we grab the oldest unlocked entry off the LRU list. - * Note that no operation within the loop may sleep. + * is found, we try to grab the oldest expired entry off the LRU list. If + * a suitable one isn't there, then drop the cache_lock and allocate a + * new one, then search again in case one got inserted while this thread + * didn't hold the lock. */ int nfsd_cache_lookup(struct svc_rqst *rqstp) @@ -344,6 +346,7 @@ nfsd_cache_lookup(struct svc_rqst *rqstp) } } + /* Drop the lock and allocate a new entry */ spin_unlock(&cache_lock); rp = nfsd_reply_cache_alloc(); if (!rp) { From 686855f5d833178e518d79e7912cdb3268a9fa69 Mon Sep 17 00:00:00 2001 From: Vladimir Davydov Date: Thu, 14 Feb 2013 18:19:58 +0400 Subject: [PATCH 2805/4607] sched: add wait_for_completion_io[_timeout] The only difference between wait_for_completion[_timeout]() and wait_for_completion_io[_timeout]() is that the latter calls io_schedule_timeout() instead of schedule_timeout() so that the caller is accounted as waiting for IO, not just sleeping. These functions can be used for correct iowait time accounting when the completion struct is actually used for waiting for IO (e.g. completion of a bio request in the block layer). Signed-off-by: Vladimir Davydov Acked-by: Ingo Molnar Signed-off-by: Jens Axboe --- include/linux/completion.h | 3 ++ kernel/sched/core.c | 57 ++++++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/include/linux/completion.h b/include/linux/completion.h index 51494e6b5548..33f0280fd533 100644 --- a/include/linux/completion.h +++ b/include/linux/completion.h @@ -77,10 +77,13 @@ static inline void init_completion(struct completion *x) } extern void wait_for_completion(struct completion *); +extern void wait_for_completion_io(struct completion *); extern int wait_for_completion_interruptible(struct completion *x); extern int wait_for_completion_killable(struct completion *x); extern unsigned long wait_for_completion_timeout(struct completion *x, unsigned long timeout); +extern unsigned long wait_for_completion_io_timeout(struct completion *x, + unsigned long timeout); extern long wait_for_completion_interruptible_timeout( struct completion *x, unsigned long timeout); extern long wait_for_completion_killable_timeout( diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 257002c13bb0..d6fdcdcbb9b1 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -3267,7 +3267,8 @@ void complete_all(struct completion *x) EXPORT_SYMBOL(complete_all); static inline long __sched -do_wait_for_common(struct completion *x, long timeout, int state) +do_wait_for_common(struct completion *x, + long (*action)(long), long timeout, int state) { if (!x->done) { DECLARE_WAITQUEUE(wait, current); @@ -3280,7 +3281,7 @@ do_wait_for_common(struct completion *x, long timeout, int state) } __set_current_state(state); spin_unlock_irq(&x->wait.lock); - timeout = schedule_timeout(timeout); + timeout = action(timeout); spin_lock_irq(&x->wait.lock); } while (!x->done && timeout); __remove_wait_queue(&x->wait, &wait); @@ -3291,17 +3292,30 @@ do_wait_for_common(struct completion *x, long timeout, int state) return timeout ?: 1; } -static long __sched -wait_for_common(struct completion *x, long timeout, int state) +static inline long __sched +__wait_for_common(struct completion *x, + long (*action)(long), long timeout, int state) { might_sleep(); spin_lock_irq(&x->wait.lock); - timeout = do_wait_for_common(x, timeout, state); + timeout = do_wait_for_common(x, action, timeout, state); spin_unlock_irq(&x->wait.lock); return timeout; } +static long __sched +wait_for_common(struct completion *x, long timeout, int state) +{ + return __wait_for_common(x, schedule_timeout, timeout, state); +} + +static long __sched +wait_for_common_io(struct completion *x, long timeout, int state) +{ + return __wait_for_common(x, io_schedule_timeout, timeout, state); +} + /** * wait_for_completion: - waits for completion of a task * @x: holds the state of this particular completion @@ -3337,6 +3351,39 @@ wait_for_completion_timeout(struct completion *x, unsigned long timeout) } EXPORT_SYMBOL(wait_for_completion_timeout); +/** + * wait_for_completion_io: - waits for completion of a task + * @x: holds the state of this particular completion + * + * This waits to be signaled for completion of a specific task. It is NOT + * interruptible and there is no timeout. The caller is accounted as waiting + * for IO. + */ +void __sched wait_for_completion_io(struct completion *x) +{ + wait_for_common_io(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE); +} +EXPORT_SYMBOL(wait_for_completion_io); + +/** + * wait_for_completion_io_timeout: - waits for completion of a task (w/timeout) + * @x: holds the state of this particular completion + * @timeout: timeout value in jiffies + * + * This waits for either a completion of a specific task to be signaled or for a + * specified timeout to expire. The timeout is in jiffies. It is not + * interruptible. The caller is accounted as waiting for IO. + * + * The return value is 0 if timed out, and positive (at least 1, or number of + * jiffies left till timeout) if completed. + */ +unsigned long __sched +wait_for_completion_io_timeout(struct completion *x, unsigned long timeout) +{ + return wait_for_common_io(x, timeout, TASK_UNINTERRUPTIBLE); +} +EXPORT_SYMBOL(wait_for_completion_io_timeout); + /** * wait_for_completion_interruptible: - waits for completion of a task (w/intr) * @x: holds the state of this particular completion From 5577022f4ed8973762450ebe7fe7ebfd953817db Mon Sep 17 00:00:00 2001 From: Vladimir Davydov Date: Thu, 14 Feb 2013 18:19:59 +0400 Subject: [PATCH 2806/4607] block: account iowait time when waiting for completion of IO request Using wait_for_completion() for waiting for a IO request to be executed results in wrong iowait time accounting. For example, a system having the only task doing write() and fdatasync() on a block device can be reported being idle instead of iowaiting as it should because blkdev_issue_flush() calls wait_for_completion() which in turn calls schedule() that does not increment the iowait proc counter and thus does not turn on iowait time accounting. The patch makes block layer use wait_for_completion_io() instead of wait_for_completion() where appropriate to account iowait time correctly. Signed-off-by: Vladimir Davydov Signed-off-by: Jens Axboe --- block/blk-exec.c | 4 ++-- block/blk-flush.c | 2 +- block/blk-lib.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/block/blk-exec.c b/block/blk-exec.c index 74638ec234c8..f634de772b0c 100644 --- a/block/blk-exec.c +++ b/block/blk-exec.c @@ -120,9 +120,9 @@ int blk_execute_rq(struct request_queue *q, struct gendisk *bd_disk, /* Prevent hang_check timer from firing at us during very long I/O */ hang_check = sysctl_hung_task_timeout_secs; if (hang_check) - while (!wait_for_completion_timeout(&wait, hang_check * (HZ/2))); + while (!wait_for_completion_io_timeout(&wait, hang_check * (HZ/2))); else - wait_for_completion(&wait); + wait_for_completion_io(&wait); if (rq->errors) err = -EIO; diff --git a/block/blk-flush.c b/block/blk-flush.c index 720ad607ff91..db8f1b507857 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -436,7 +436,7 @@ int blkdev_issue_flush(struct block_device *bdev, gfp_t gfp_mask, bio_get(bio); submit_bio(WRITE_FLUSH, bio); - wait_for_completion(&wait); + wait_for_completion_io(&wait); /* * The driver must store the error location in ->bi_sector, if diff --git a/block/blk-lib.c b/block/blk-lib.c index b3a1f2b70b31..d6f50d572565 100644 --- a/block/blk-lib.c +++ b/block/blk-lib.c @@ -126,7 +126,7 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector, /* Wait for bios in-flight */ if (!atomic_dec_and_test(&bb.done)) - wait_for_completion(&wait); + wait_for_completion_io(&wait); if (!test_bit(BIO_UPTODATE, &bb.flags)) ret = -EIO; @@ -200,7 +200,7 @@ int blkdev_issue_write_same(struct block_device *bdev, sector_t sector, /* Wait for bios in-flight */ if (!atomic_dec_and_test(&bb.done)) - wait_for_completion(&wait); + wait_for_completion_io(&wait); if (!test_bit(BIO_UPTODATE, &bb.flags)) ret = -ENOTSUPP; @@ -262,7 +262,7 @@ int __blkdev_issue_zeroout(struct block_device *bdev, sector_t sector, /* Wait for bios in-flight */ if (!atomic_dec_and_test(&bb.done)) - wait_for_completion(&wait); + wait_for_completion_io(&wait); if (!test_bit(BIO_UPTODATE, &bb.flags)) /* One of bios in the batch was completed with error.*/ From 11f779421a39b86da8a523d97e5fd3477878d44f Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 1 Feb 2013 15:56:12 +0300 Subject: [PATCH 2807/4607] nfsd: containerize NFSd filesystem This patch makes NFSD file system superblock to be created per net. This makes possible to get proper network namespace from superblock instead of using hard-coded "init_net". Note: NFSd fs super-block holds network namespace. This garantees, that network namespace won't disappear from underneath of it. This, obviously, means, that in case of kill of a container's "init" (which is not a mount namespace, but network namespace creator) netowrk namespace won't be destroyed. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsctl.c | 46 +++++++++++++++++++++++++++++++++------------- fs/nfsd/nfssvc.c | 5 ++--- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 29c3f0d25469..f6d448e6eb28 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -220,6 +220,7 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size) struct sockaddr *sap = (struct sockaddr *)&address; size_t salen = sizeof(address); char *fo_path; + struct net *net = file->f_dentry->d_sb->s_fs_info; /* sanity check */ if (size == 0) @@ -232,7 +233,7 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size) if (qword_get(&buf, fo_path, size) < 0) return -EINVAL; - if (rpc_pton(&init_net, fo_path, size, sap, salen) == 0) + if (rpc_pton(net, fo_path, size, sap, salen) == 0) return -EINVAL; return nlmsvc_unlock_all_by_ip(sap); @@ -317,6 +318,7 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size) int len; struct auth_domain *dom; struct knfsd_fh fh; + struct net *net = file->f_dentry->d_sb->s_fs_info; if (size == 0) return -EINVAL; @@ -352,7 +354,7 @@ static ssize_t write_filehandle(struct file *file, char *buf, size_t size) if (!dom) return -ENOMEM; - len = exp_rootfh(&init_net, dom, path, &fh, maxsize); + len = exp_rootfh(net, dom, path, &fh, maxsize); auth_domain_put(dom); if (len) return len; @@ -396,7 +398,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) { char *mesg = buf; int rv; - struct net *net = &init_net; + struct net *net = file->f_dentry->d_sb->s_fs_info; if (size > 0) { int newthreads; @@ -447,7 +449,7 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) int len; int npools; int *nthreads; - struct net *net = &init_net; + struct net *net = file->f_dentry->d_sb->s_fs_info; mutex_lock(&nfsd_mutex); npools = nfsd_nrpools(net); @@ -510,7 +512,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) unsigned minor; ssize_t tlen = 0; char *sep; - struct net *net = &init_net; + struct net *net = file->f_dentry->d_sb->s_fs_info; struct nfsd_net *nn = net_generic(net, nfsd_net_id); if (size>0) { @@ -792,7 +794,7 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size, static ssize_t write_ports(struct file *file, char *buf, size_t size) { ssize_t rv; - struct net *net = &init_net; + struct net *net = file->f_dentry->d_sb->s_fs_info; mutex_lock(&nfsd_mutex); rv = __write_ports(file, buf, size, net); @@ -827,7 +829,7 @@ int nfsd_max_blksize; static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) { char *mesg = buf; - struct net *net = &init_net; + struct net *net = file->f_dentry->d_sb->s_fs_info; struct nfsd_net *nn = net_generic(net, nfsd_net_id); if (size > 0) { @@ -923,7 +925,8 @@ static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, */ static ssize_t write_leasetime(struct file *file, char *buf, size_t size) { - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); + struct net *net = file->f_dentry->d_sb->s_fs_info; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); return nfsd4_write_time(file, buf, size, &nn->nfsd4_lease, nn); } @@ -939,7 +942,8 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size) */ static ssize_t write_gracetime(struct file *file, char *buf, size_t size) { - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); + struct net *net = file->f_dentry->d_sb->s_fs_info; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); return nfsd4_write_time(file, buf, size, &nn->nfsd4_grace, nn); } @@ -995,7 +999,8 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size, static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) { ssize_t rv; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); + struct net *net = file->f_dentry->d_sb->s_fs_info; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); mutex_lock(&nfsd_mutex); rv = __write_recoverydir(file, buf, size, nn); @@ -1037,20 +1042,35 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) #endif /* last one */ {""} }; - return simple_fill_super(sb, 0x6e667364, nfsd_files); + struct net *net = data; + int ret; + + ret = simple_fill_super(sb, 0x6e667364, nfsd_files); + if (ret) + return ret; + sb->s_fs_info = get_net(net); + return 0; } static struct dentry *nfsd_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { - return mount_single(fs_type, flags, data, nfsd_fill_super); + return mount_ns(fs_type, flags, current->nsproxy->net_ns, nfsd_fill_super); +} + +static void nfsd_umount(struct super_block *sb) +{ + struct net *net = sb->s_fs_info; + + kill_litter_super(sb); + put_net(net); } static struct file_system_type nfsd_fs_type = { .owner = THIS_MODULE, .name = "nfsd", .mount = nfsd_mount, - .kill_sb = kill_litter_super, + .kill_sb = nfsd_umount, }; #ifdef CONFIG_PROC_FS diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 40cb1cbaba4e..6cee5db72047 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -702,8 +702,7 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) int nfsd_pool_stats_open(struct inode *inode, struct file *file) { int ret; - struct net *net = &init_net; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct nfsd_net *nn = net_generic(inode->i_sb->s_fs_info, nfsd_net_id); mutex_lock(&nfsd_mutex); if (nn->nfsd_serv == NULL) { @@ -720,7 +719,7 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file) int nfsd_pool_stats_release(struct inode *inode, struct file *file) { int ret = seq_release(inode, file); - struct net *net = &init_net; + struct net *net = inode->i_sb->s_fs_info; mutex_lock(&nfsd_mutex); /* this function really, really should have been called svc_put() */ From 96d851c4d28de8cc83fe2bd5c6bc2eb8f253a6c5 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 1 Feb 2013 15:56:17 +0300 Subject: [PATCH 2808/4607] nfsd: use proper net while reading "exports" file Functuon "exports_open" is used for both "/proc/fs/nfs/exports" and "/proc/fs/nfsd/exports" files. Now NFSd filesystem is containerised, so proper net can be taken from superblock for "/proc/fs/nfsd/exports" reader. But for "/proc/fs/nfsd/exports" only current->nsproxy->net_ns can be used. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsctl.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index f6d448e6eb28..8ead2c25ce65 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -125,11 +125,11 @@ static const struct file_operations transaction_ops = { .llseek = default_llseek, }; -static int exports_open(struct inode *inode, struct file *file) +static int exports_net_open(struct net *net, struct file *file) { int err; struct seq_file *seq; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); + struct nfsd_net *nn = net_generic(net, nfsd_net_id); err = seq_open(file, &nfs_exports_op); if (err) @@ -140,8 +140,26 @@ static int exports_open(struct inode *inode, struct file *file) return 0; } -static const struct file_operations exports_operations = { - .open = exports_open, +static int exports_proc_open(struct inode *inode, struct file *file) +{ + return exports_net_open(current->nsproxy->net_ns, file); +} + +static const struct file_operations exports_proc_operations = { + .open = exports_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, + .owner = THIS_MODULE, +}; + +static int exports_nfsd_open(struct inode *inode, struct file *file) +{ + return exports_net_open(inode->i_sb->s_fs_info, file); +} + +static const struct file_operations exports_nfsd_operations = { + .open = exports_nfsd_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, @@ -1018,7 +1036,7 @@ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) static int nfsd_fill_super(struct super_block * sb, void * data, int silent) { static struct tree_descr nfsd_files[] = { - [NFSD_List] = {"exports", &exports_operations, S_IRUGO}, + [NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO}, [NFSD_Export_features] = {"export_features", &export_features_operations, S_IRUGO}, [NFSD_FO_UnlockIP] = {"unlock_ip", @@ -1081,7 +1099,8 @@ static int create_proc_exports_entry(void) entry = proc_mkdir("fs/nfs", NULL); if (!entry) return -ENOMEM; - entry = proc_create("exports", 0, entry, &exports_operations); + entry = proc_create("exports", 0, entry, + &exports_proc_operations); if (!entry) return -ENOMEM; return 0; From 71a50306934f416e74ba27cbfb88855c22251525 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 1 Feb 2013 15:56:22 +0300 Subject: [PATCH 2809/4607] nfsd: disable usermode helper client tracker in container This tracker uses khelper kthread to execute binaries. Execution itself is done from kthread context - i.e. global root is used. This is not suitable for containers with own root. So, disable this tracker for a while. Note: one of possible solutions can be pass "init" callback to khelper, which will swap root to desired one. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4recover.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index ba6fdd4a0455..e0ae1cf18a82 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -1185,6 +1185,12 @@ bin_to_hex_dup(const unsigned char *src, int srclen) static int nfsd4_umh_cltrack_init(struct net __attribute__((unused)) *net) { + /* XXX: The usermode helper s not working in container yet. */ + if (net != &init_net) { + WARN(1, KERN_ERR "NFSD: attempt to initialize umh client " + "tracking in a container!\n"); + return -EINVAL; + } return nfsd4_umh_cltrack_upcall("init", NULL, NULL); } From deb4534f4f3be7aea7d9d24c3b0d58f370cbf9ef Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 1 Feb 2013 15:56:27 +0300 Subject: [PATCH 2810/4607] nfsd: enable NFSv4 state in containers Currently, NFSd is ready to operate in network namespace based containers. So let's drop check for "init_net" and make it able to fly. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c1a6ddf3a84a..f194f869be4c 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4939,16 +4939,6 @@ nfs4_state_start_net(struct net *net) struct nfsd_net *nn = net_generic(net, nfsd_net_id); int ret; - /* - * FIXME: For now, we hang most of the pernet global stuff off of - * init_net until nfsd is fully containerized. Eventually, we'll - * need to pass a net pointer into this function, take a reference - * to that instead and then do most of the rest of this on a per-net - * basis. - */ - if (net != &init_net) - return -EINVAL; - ret = nfs4_state_create_net(net); if (ret) return ret; From f25cc71e634edcf8a15bc60a48f2b5f3ec9fbb1d Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Wed, 13 Feb 2013 08:40:16 -0700 Subject: [PATCH 2811/4607] lockd: nlmclnt_reclaim(): avoid stack overflow Even though nlmclnt_reclaim() is only one call into the stack frame, 928 bytes on the stack seems like a lot. Recode to dynamically allocate the request structure once from within the reclaimer task, then pass this pointer into nlmclnt_reclaim() for reuse on subsequent calls. smatch analysis: fs/lockd/clntproc.c:620 nlmclnt_reclaim() warn: 'reqst' puts 928 bytes on stack Also remove redundant assignment of 0 after memset. Cc: Trond Myklebust Signed-off-by: Tim Gardner Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/lockd/clntlock.c | 12 +++++++++++- fs/lockd/clntproc.c | 6 ++---- include/linux/lockd/lockd.h | 3 ++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index 4885b53e56bd..6cd673d34fb9 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -220,10 +220,19 @@ reclaimer(void *ptr) { struct nlm_host *host = (struct nlm_host *) ptr; struct nlm_wait *block; + struct nlm_rqst *req; struct file_lock *fl, *next; u32 nsmstate; struct net *net = host->net; + req = kmalloc(sizeof(*req), GFP_KERNEL); + if (!req) { + printk(KERN_ERR "lockd: reclaimer unable to alloc memory." + " Locks for %s won't be reclaimed!\n", + host->h_name); + return 0; + } + allow_signal(SIGKILL); down_write(&host->h_rwsem); @@ -253,7 +262,7 @@ restart: */ if (signalled()) continue; - if (nlmclnt_reclaim(host, fl) != 0) + if (nlmclnt_reclaim(host, fl, req) != 0) continue; list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted); if (host->h_nsmstate != nsmstate) { @@ -279,5 +288,6 @@ restart: /* Release host handle after use */ nlmclnt_release_host(host); lockd_down(net); + kfree(req); return 0; } diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 54f9e6ce0430..b43114c4332a 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -615,17 +615,15 @@ out_unlock: * RECLAIM: Try to reclaim a lock */ int -nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl) +nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl, + struct nlm_rqst *req) { - struct nlm_rqst reqst, *req; int status; - req = &reqst; memset(req, 0, sizeof(*req)); locks_init_lock(&req->a_args.lock.fl); locks_init_lock(&req->a_res.lock.fl); req->a_host = host; - req->a_flags = 0; /* Set up the argument struct */ nlmclnt_setlockargs(req, fl); diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index f5a051a79273..a395f1e7998f 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -212,7 +212,8 @@ int nlmclnt_block(struct nlm_wait *block, struct nlm_rqst *req, long timeout) __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock); void nlmclnt_recovery(struct nlm_host *); -int nlmclnt_reclaim(struct nlm_host *, struct file_lock *); +int nlmclnt_reclaim(struct nlm_host *, struct file_lock *, + struct nlm_rqst *); void nlmclnt_next_cookie(struct nlm_cookie *); /* From a586069b0f56a700d6f6249a64cbc313dd4a97e0 Mon Sep 17 00:00:00 2001 From: Krishna Mohan Date: Wed, 13 Feb 2013 16:33:04 -0800 Subject: [PATCH 2812/4607] libfc: XenServer fails to mount root filesystem. schedule_delayed_work() is using msec instead of jiffies. On PLOGI reject from target, remote port retry is scheduled @ 20 sec instead of 2sec(FC_DEF_E_D_TOV). XenServer dom0 kernel is configured with CONFIG_HZ=100Hz Signed-off-by: Krishna Mohan Signed-off-by: Robert Love --- drivers/scsi/libfc/fc_rport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 83aa1efec875..d518d17e940f 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -582,7 +582,7 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp) static void fc_rport_error_retry(struct fc_rport_priv *rdata, struct fc_frame *fp) { - unsigned long delay = FC_DEF_E_D_TOV; + unsigned long delay = msecs_to_jiffies(FC_DEF_E_D_TOV); /* make sure this isn't an FC_EX_CLOSED error, never retry those */ if (PTR_ERR(fp) == -FC_EX_CLOSED) From c3581039b6c51a778a70accec53a9bb7ad9a4d32 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:19 +0530 Subject: [PATCH 2813/4607] ARC: Signal handling Includes following fixes courtesy review by Al-Viro * Tracer poke to Callee-regs were lost Before going off into do_signal( ) we save the user-mode callee regs (as they are not saved by default as part of pt_regs). This is to make sure that that a Tracer (if tracing related signal) is able to do likes of PEEKUSR(callee-reg). However in return path we were simply discarding the user-mode callee regs, which would break a POKEUSR(callee-reg) from a tracer. * Issue related to multiple syscall restarts are addressed in next patch Signed-off-by: Vineet Gupta Cc: Al Viro Acked-by: Jonas Bonn --- arch/arc/Kconfig | 1 + arch/arc/include/asm/entry.h | 35 +++ arch/arc/include/asm/sigcontext.h | 22 ++ arch/arc/include/asm/signal.h | 27 +++ arch/arc/kernel/entry.S | 11 +- arch/arc/kernel/signal.c | 356 ++++++++++++++++++++++++++++++ 6 files changed, 449 insertions(+), 3 deletions(-) create mode 100644 arch/arc/include/asm/sigcontext.h create mode 100644 arch/arc/include/asm/signal.h create mode 100644 arch/arc/kernel/signal.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index a4e9806ace23..756beffd20cb 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -20,6 +20,7 @@ config ARC select GENERIC_KERNEL_EXECVE select GENERIC_KERNEL_THREAD select GENERIC_PENDING_IRQ if SMP + select GENERIC_SIGALTSTACK select GENERIC_SMP_IDLE_THREAD select HAVE_GENERIC_HARDIRQS select MODULES_USE_ELF_RELA diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h index 63705b12d911..6b42bf5c45ec 100644 --- a/arch/arc/include/asm/entry.h +++ b/arch/arc/include/asm/entry.h @@ -165,6 +165,41 @@ .endm +/*-------------------------------------------------------------- + * RESTORE_CALLEE_SAVED_USER: + * This is called after do_signal where tracer might have changed callee regs + * thus we need to restore the reg file. + * Special case handling is required for r25 in case it is used by kernel + * for caching task ptr. Ptrace would have modified on-kernel-stack value of + * r25, which needs to be shoved back into task->thread.user_r25 where from + * Low level exception/ISR return code will retrieve to populate with rest of + * callee reg-file. + *-------------------------------------------------------------*/ +.macro RESTORE_CALLEE_SAVED_USER + + add sp, sp, 4 /* skip "callee_regs->stack_place_holder" */ + +#ifdef CONFIG_ARC_CURR_IN_REG + ld.ab r12, [sp, 4] + st r12, [r25, TASK_THREAD + THREAD_USER_R25] +#else + ld.ab r25, [sp, 4] +#endif + + ld.ab r24, [sp, 4] + ld.ab r23, [sp, 4] + ld.ab r22, [sp, 4] + ld.ab r21, [sp, 4] + ld.ab r20, [sp, 4] + ld.ab r19, [sp, 4] + ld.ab r18, [sp, 4] + ld.ab r17, [sp, 4] + ld.ab r16, [sp, 4] + ld.ab r15, [sp, 4] + ld.ab r14, [sp, 4] + ld.ab r13, [sp, 4] +.endm + /*-------------------------------------------------------------- * Super FAST Restore callee saved regs by simply re-adjusting SP *-------------------------------------------------------------*/ diff --git a/arch/arc/include/asm/sigcontext.h b/arch/arc/include/asm/sigcontext.h new file mode 100644 index 000000000000..9678a11fc158 --- /dev/null +++ b/arch/arc/include/asm/sigcontext.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_SIGCONTEXT_H +#define _ASM_ARC_SIGCONTEXT_H + +#include + +/* + * Signal context structure - contains all info to do with the state + * before the signal handler was invoked. + */ +struct sigcontext { + struct user_regs_struct regs; +}; + +#endif /* _ASM_ARC_SIGCONTEXT_H */ diff --git a/arch/arc/include/asm/signal.h b/arch/arc/include/asm/signal.h new file mode 100644 index 000000000000..fad62f7f42d6 --- /dev/null +++ b/arch/arc/include/asm/signal.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Amit Bhor, Sameer Dhavale: Codito Technologies 2004 + */ + +#ifndef _ASM_ARC_SIGNAL_H +#define _ASM_ARC_SIGNAL_H + +/* + * This is much needed for ARC sigreturn optimization. + * This allows uClibc to piggback the addr of a sigreturn stub in sigaction, + * which allows sigreturn based re-entry into kernel after handling signal. + * W/o this kernel needs to "synthesize" the sigreturn trampoline on user + * mode stack which in turn forces the following: + * -TLB Flush (after making the stack page executable) + * -Cache line Flush (to make I/D Cache lines coherent) + */ +#define SA_RESTORER 0x04000000 + +#include + +#endif /* _ASM_ARC_SIGNAL_H */ diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index ed08ac14fbc4..d625b77c14bd 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -470,7 +470,11 @@ resume_user_mode_begin: bbit0 r9, TIF_SIGPENDING, .Lchk_notify_resume - ; save CALLEE Regs. + ; Normal Trap/IRQ entry only saves Scratch (caller-saved) regs + ; in pt_reg since the "C" ABI (kernel code) will automatically + ; save/restore callee-saved regs. + ; + ; However, here we need to explicitly save callee regs because ; (i) If this signal causes coredump - full regfile needed ; (ii) If signal is SIGTRAP/SIGSTOP, task is being traced thus ; tracer might call PEEKUSR(CALLEE reg) @@ -484,8 +488,9 @@ resume_user_mode_begin: bl @do_signal - ; unwind SP for cheap discard of Callee saved Regs - DISCARD_CALLEE_SAVED_USER + ; Ideally we want to discard the Callee reg above, however if this was + ; a tracing signal, tracer could have done a POKEUSR(CALLEE reg) + RESTORE_CALLEE_SAVED_USER b resume_user_mode_begin ; loop back to start of U mode ret diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c new file mode 100644 index 000000000000..ab8ed5a55461 --- /dev/null +++ b/arch/arc/kernel/signal.c @@ -0,0 +1,356 @@ +/* + * Signal Handling for ARC + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: Jan 2010 (Restarting of timer related syscalls) + * + * vineetg: Nov 2009 (Everything needed for TIF_RESTORE_SIGMASK) + * -do_signal() supports TIF_RESTORE_SIGMASK + * -do_signal() no loner needs oldset, required by OLD sys_sigsuspend + * -sys_rt_sigsuspend() now comes from generic code, so discard arch implemen + * -sys_sigsuspend() no longer needs to fudge ptregs, hence that arg removed + * -sys_sigsuspend() no longer loops for do_signal(), sets TIF_xxx and leaves + * the job to do_signal() + * + * vineetg: July 2009 + * -Modified Code to support the uClibc provided userland sigreturn stub + * to avoid kernel synthesing it on user stack at runtime, costing TLB + * probes and Cache line flushes. + * + * vineetg: July 2009 + * -In stash_usr_regs( ) and restore_usr_regs( ), save/restore of user regs + * in done in block copy rather than one word at a time. + * This saves around 2K of code and improves LMBench lat_sig + * + * rajeshwarr: Feb 2009 + * - Support for Realtime Signals + * + * vineetg: Aug 11th 2008: Bug #94183 + * -ViXS were still seeing crashes when using insmod to load drivers. + * It turned out that the code to change Execute permssions for TLB entries + * of user was not guarded for interrupts (mod_tlb_permission) + * This was cauing TLB entries to be overwritten on unrelated indexes + * + * Vineetg: July 15th 2008: Bug #94183 + * -Exception happens in Delay slot of a JMP, and before user space resumes, + * Signal is delivered (Ctrl + C) = >SIGINT. + * setup_frame( ) sets up PC,SP,BLINK to enable user space signal handler + * to run, but doesn't clear the Delay slot bit from status32. As a result, + * on resuming user mode, signal handler branches off to BTA of orig JMP + * -FIX: clear the DE bit from status32 in setup_frame( ) + * + * Rahul Trivedi, Kanika Nema: Codito Technologies 2004 + */ + +#include +#include +#include +#include +#include +#include +#include + +struct rt_sigframe { + struct siginfo info; + struct ucontext uc; +#define MAGIC_SIGALTSTK 0x07302004 + unsigned int sigret_magic; +}; + +static int +stash_usr_regs(struct rt_sigframe __user *sf, struct pt_regs *regs, + sigset_t *set) +{ + int err; + err = __copy_to_user(&(sf->uc.uc_mcontext.regs), regs, + sizeof(sf->uc.uc_mcontext.regs.scratch)); + err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(sigset_t)); + + return err; +} + +static int restore_usr_regs(struct pt_regs *regs, struct rt_sigframe __user *sf) +{ + sigset_t set; + int err; + + err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set)); + if (!err) + set_current_blocked(&set); + + err |= __copy_from_user(regs, &(sf->uc.uc_mcontext.regs), + sizeof(sf->uc.uc_mcontext.regs.scratch)); + + return err; +} + +static inline int is_do_ss_needed(unsigned int magic) +{ + if (MAGIC_SIGALTSTK == magic) + return 1; + else + return 0; +} + +SYSCALL_DEFINE0(rt_sigreturn) +{ + struct rt_sigframe __user *sf; + unsigned int magic; + int err; + struct pt_regs *regs = current_pt_regs(); + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + /* Since we stacked the signal on a word boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + if (regs->sp & 3) + goto badframe; + + sf = (struct rt_sigframe __force __user *)(regs->sp); + + if (!access_ok(VERIFY_READ, sf, sizeof(*sf))) + goto badframe; + + err = restore_usr_regs(regs, sf); + err |= __get_user(magic, &sf->sigret_magic); + if (err) + goto badframe; + + if (unlikely(is_do_ss_needed(magic))) + if (restore_altstack(&sf->uc.uc_stack)) + goto badframe; + + return regs->r0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Determine which stack to use.. + */ +static inline void __user *get_sigframe(struct k_sigaction *ka, + struct pt_regs *regs, + unsigned long framesize) +{ + unsigned long sp = regs->sp; + void __user *frame; + + /* This is the X/Open sanctioned signal stack switching */ + if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + + /* No matter what happens, 'sp' must be word + * aligned otherwise nasty things could happen + */ + + /* ATPCS B01 mandates 8-byte alignment */ + frame = (void __user *)((sp - framesize) & ~7); + + /* Check that we can actually write to the signal frame */ + if (!access_ok(VERIFY_WRITE, frame, framesize)) + frame = NULL; + + return frame; +} + +/* + * translate the signal + */ +static inline int map_sig(int sig) +{ + struct thread_info *thread = current_thread_info(); + if (thread->exec_domain && thread->exec_domain->signal_invmap + && sig < 32) + sig = thread->exec_domain->signal_invmap[sig]; + return sig; +} + +static int +setup_rt_frame(int signo, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + struct rt_sigframe __user *sf; + unsigned int magic = 0; + int err = 0; + + sf = get_sigframe(ka, regs, sizeof(struct rt_sigframe)); + if (!sf) + return 1; + + /* + * SA_SIGINFO requires 3 args to signal handler: + * #1: sig-no (common to any handler) + * #2: struct siginfo + * #3: struct ucontext (completely populated) + */ + if (unlikely(ka->sa.sa_flags & SA_SIGINFO)) { + err |= copy_siginfo_to_user(&sf->info, info); + err |= __put_user(0, &sf->uc.uc_flags); + err |= __put_user(NULL, &sf->uc.uc_link); + err |= __save_altstack(&sf->uc.uc_stack, regs->sp); + + /* setup args 2 and 3 for user mode handler */ + regs->r1 = (unsigned long)&sf->info; + regs->r2 = (unsigned long)&sf->uc; + + /* + * small optim to avoid unconditonally calling do_sigaltstack + * in sigreturn path, now that we only have rt_sigreturn + */ + magic = MAGIC_SIGALTSTK; + } + + /* + * w/o SA_SIGINFO, struct ucontext is partially populated (only + * uc_mcontext/uc_sigmask) for kernel's normal user state preservation + * during signal handler execution. This works for SA_SIGINFO as well + * although the semantics are now overloaded (the same reg state can be + * inspected by userland: but are they allowed to fiddle with it ? + */ + err |= stash_usr_regs(sf, regs, set); + err |= __put_user(magic, &sf->sigret_magic); + if (err) + return err; + + /* #1 arg to the user Signal handler */ + regs->r0 = map_sig(signo); + + /* setup PC of user space signal handler */ + regs->ret = (unsigned long)ka->sa.sa_handler; + + /* + * handler returns using sigreturn stub provided already by userpsace + */ + BUG_ON(!(ka->sa.sa_flags & SA_RESTORER)); + regs->blink = (unsigned long)ka->sa.sa_restorer; + + /* User Stack for signal handler will be above the frame just carved */ + regs->sp = (unsigned long)sf; + + /* + * Bug 94183, Clear the DE bit, so that when signal handler + * starts to run, it doesn't use BTA + */ + regs->status32 &= ~STATUS_DE_MASK; + regs->status32 |= STATUS_L_MASK; + + return err; +} + +static void arc_restart_syscall(struct k_sigaction *ka, struct pt_regs *regs) +{ + switch (regs->r0) { + case -ERESTART_RESTARTBLOCK: + case -ERESTARTNOHAND: + /* + * ERESTARTNOHAND means that the syscall should + * only be restarted if there was no handler for + * the signal, and since we only get here if there + * is a handler, we don't restart + */ + regs->r0 = -EINTR; /* ERESTART_xxx is internal */ + break; + + case -ERESTARTSYS: + /* + * ERESTARTSYS means to restart the syscall if + * there is no handler or the handler was + * registered with SA_RESTART + */ + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->r0 = -EINTR; + break; + } + /* fallthrough */ + + case -ERESTARTNOINTR: + /* + * ERESTARTNOINTR means that the syscall should + * be called again after the signal handler returns. + * Setup reg state just as it was before doing the trap + * r0 has been clobbered with sys call ret code thus it + * needs to be reloaded with orig first arg to syscall + * in orig_r0. Rest of relevant reg-file: + * r8 (syscall num) and (r1 - r7) will be reset to + * their orig user space value when we ret from kernel + */ + regs->r0 = regs->orig_r0; + regs->ret -= 4; + break; + } +} + +/* + * OK, we're invoking a handler + */ +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, + struct pt_regs *regs) +{ + sigset_t *oldset = sigmask_to_save(); + int ret; + + /* Set up the stack frame */ + ret = setup_rt_frame(sig, ka, info, oldset, regs); + + if (ret) + force_sigsegv(sig, current); + else + signal_delivered(sig, info, ka, regs, 0); +} + +void do_signal(struct pt_regs *regs) +{ + struct k_sigaction ka; + siginfo_t info; + int signr; + int restart_scall; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + + /* Are we from a system call? */ + restart_scall = in_syscall(regs); + + if (signr > 0) { + if (restart_scall) + arc_restart_syscall(&ka, regs); + + handle_signal(signr, &ka, &info, regs); + return; + } + + if (restart_scall) { + /* No handler for syscall: restart it */ + if (regs->r0 == -ERESTARTNOHAND || + regs->r0 == -ERESTARTSYS || regs->r0 == -ERESTARTNOINTR) { + regs->r0 = regs->orig_r0; + regs->ret -= 4; + } else if (regs->r0 == -ERESTART_RESTARTBLOCK) { + regs->r8 = __NR_restart_syscall; + regs->ret -= 4; + } + } + + /* If there's no signal to deliver, restore the saved sigmask back */ + restore_saved_sigmask(); +} + +void do_notify_resume(struct pt_regs *regs) +{ + /* + * ASM glue gaurantees that this is only called when returning to + * user mode + */ + if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) + tracehook_notify_resume(regs); +} From ff7109fa632654eaef657186f2942f5b679023d6 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Thu, 14 Feb 2013 11:35:04 -0600 Subject: [PATCH 2814/4607] mfd: lpc_ich: Use devres API to allocate private data And fix a kzalloc argument inversion bug while converting to devres. Signed-off-by: Aaron Sierra Signed-off-by: Samuel Ortiz --- drivers/mfd/lpc_ich.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 5c2ef41fa24c..9f12f91d6296 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -916,7 +916,8 @@ static int lpc_ich_probe(struct pci_dev *dev, int ret; bool cell_added = false; - priv = kmalloc(GFP_KERNEL, sizeof(struct lpc_ich_priv)); + priv = devm_kzalloc(&dev->dev, + sizeof(struct lpc_ich_priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -952,7 +953,6 @@ static int lpc_ich_probe(struct pci_dev *dev, dev_warn(&dev->dev, "No MFD cells added\n"); lpc_ich_restore_config_space(dev); pci_set_drvdata(dev, NULL); - kfree(priv); return -ENODEV; } @@ -961,12 +961,9 @@ static int lpc_ich_probe(struct pci_dev *dev, static void lpc_ich_remove(struct pci_dev *dev) { - void *priv = pci_get_drvdata(dev); - mfd_remove_devices(&dev->dev); lpc_ich_restore_config_space(dev); pci_set_drvdata(dev, NULL); - kfree(priv); } static struct pci_driver lpc_ich_driver = { From 5c39c0ab5e862cf71cda1fc39a5cedd4e2f18c6e Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Mon, 11 Feb 2013 20:01:24 +0530 Subject: [PATCH 2815/4607] ARC: [Review] Preparing to fix incorrect syscall restarts due to signals To avoid multiple syscall restarts (multiple signals) or no restart at all (sigreturn), we need just an extra bit of state "literally 1 bit" in struct pt_regs. orig_r8 is the best place to do this, however given the way it is encoded currently, we can't add anything simplistically. Current orig_r8: * syscalls -> 1 to NR_SYSCALLS * Exceptions -> NR_SYSCALLS + 1 * Break-point-> NR_SYSCALLS + 2 In new scheme it is a bit-field * lower short word contains the exact event type (and a new bit to represent restart semantics : if syscall was already / can't be restarted) * upper short word optionally containing the syscall num - needed by likes of tracehooks etc This patch only changes how orig_r8 is organised and nothing should change behaviourily. Signed-off-by: Vineet Gupta Cc: Al Viro --- arch/arc/include/asm/entry.h | 29 +++++++++++++++++------------ arch/arc/include/asm/ptrace.h | 24 ++++++++++++++++++++---- arch/arc/kernel/asm-offsets.c | 2 +- arch/arc/kernel/entry.S | 4 ++-- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h index 6b42bf5c45ec..9eada5b28be6 100644 --- a/arch/arc/include/asm/entry.h +++ b/arch/arc/include/asm/entry.h @@ -343,18 +343,12 @@ *-------------------------------------------------------------*/ .macro SAVE_ALL_EXCEPTION marker + st \marker, [sp, 8] + st r0, [sp, 4] /* orig_r0, needed only for sys calls */ + /* Restore r9 used to code the early prologue */ EXCPN_PROLOG_RESTORE_REG r9 - /* Save the complete regfile now */ - - /* orig_r8 marker: - * syscalls -> 1 to NR_SYSCALLS - * Exceptions -> NR_SYSCALLS + 1 - * Break-point-> NR_SYSCALLS + 2 - */ - st \marker, [sp, 8] - st r0, [sp, 4] /* orig_r0, needed only for sys calls */ SAVE_CALLER_SAVED st.a r26, [sp, -4] /* gp */ st.a fp, [sp, -4] @@ -384,14 +378,25 @@ * Save scratch regs for exceptions *-------------------------------------------------------------*/ .macro SAVE_ALL_SYS - SAVE_ALL_EXCEPTION (NR_syscalls + 1) + SAVE_ALL_EXCEPTION orig_r8_IS_EXCPN .endm /*-------------------------------------------------------------- * Save scratch regs for sys calls *-------------------------------------------------------------*/ .macro SAVE_ALL_TRAP - SAVE_ALL_EXCEPTION r8 + /* + * Setup pt_regs->orig_r8. + * Encode syscall number (r8) in upper short word of event type (r9) + * N.B. #1: This is already endian safe (see ptrace.h) + * #2: Only r9 can be used as scratch as it is already clobbered + * and it's contents are no longer needed by the latter part + * of exception prologue + */ + lsl r9, r8, 16 + or r9, r9, orig_r8_IS_SCALL + + SAVE_ALL_EXCEPTION r9 .endm /*-------------------------------------------------------------- @@ -442,7 +447,7 @@ ld r9, [@int1_saved_reg] /* now we are ready to save the remaining context :) */ - st -1, [sp, 8] /* orig_r8, -1 for interuppt level one */ + st orig_r8_IS_IRQ1, [sp, 8] /* Event Type */ st 0, [sp, 4] /* orig_r0 , N/A for IRQ */ SAVE_CALLER_SAVED st.a r26, [sp, -4] /* gp */ diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index 3afadefe335f..3ec89f467ac3 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h @@ -50,7 +50,17 @@ struct pt_regs { long r0; long sp; /* user/kernel sp depending on where we came from */ long orig_r0; - long orig_r8; /*to distinguish bet excp, sys call, int1 or int2 */ + + /*to distinguish bet excp, syscall, irq */ + union { +#ifdef CONFIG_CPU_BIG_ENDIAN + /* so that assembly code is same for LE/BE */ + unsigned long orig_r8:16, event:16; +#else + unsigned long event:16, orig_r8:16; +#endif + long orig_r8_word; + }; }; /* Callee saved registers - need to be saved only when you are scheduled out */ @@ -87,9 +97,8 @@ struct callee_regs { sp; \ }) -/* return 1 if in syscall, 0 if Intr or Exception */ -#define in_syscall(regs) (((regs->orig_r8) >= 0 && \ - (regs->orig_r8 <= NR_syscalls)) ? 1 : 0) +#define in_syscall(regs) (regs->event & orig_r8_IS_SCALL) +#define in_brkpt_trap(regs) (regs->event & orig_r8_IS_BRKPT) #define current_pt_regs() \ ({ \ @@ -101,6 +110,13 @@ struct callee_regs { #endif /* !__ASSEMBLY__ */ +#define orig_r8_IS_SCALL 0x0001 +#define orig_r8_IS_SCALL_RESTARTED 0x0002 +#define orig_r8_IS_BRKPT 0x0004 +#define orig_r8_IS_EXCPN 0x0004 +#define orig_r8_IS_IRQ1 0x0010 +#define orig_r8_IS_IRQ2 0x0020 + #endif /* __KERNEL__ */ #ifndef __ASSEMBLY__ diff --git a/arch/arc/kernel/asm-offsets.c b/arch/arc/kernel/asm-offsets.c index 64b2c2fa7b54..d7770cc9aee3 100644 --- a/arch/arc/kernel/asm-offsets.c +++ b/arch/arc/kernel/asm-offsets.c @@ -46,7 +46,7 @@ int main(void) BLANK(); DEFINE(PT_status32, offsetof(struct pt_regs, status32)); - DEFINE(PT_orig_r8, offsetof(struct pt_regs, orig_r8)); + DEFINE(PT_orig_r8, offsetof(struct pt_regs, orig_r8_word)); DEFINE(PT_sp, offsetof(struct pt_regs, sp)); DEFINE(PT_r0, offsetof(struct pt_regs, r0)); DEFINE(PT_r1, offsetof(struct pt_regs, r1)); diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index d625b77c14bd..ce8670da8306 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -350,8 +350,8 @@ ARC_EXIT EV_Extension trap_with_param: - ;make sure orig_r8 is a positive value - st NR_syscalls + 2, [sp, PT_orig_r8] + ; stop_pc info by gdb needs this info + st orig_r8_IS_BRKPT, [sp, PT_orig_r8] mov r0, r12 lr r1, [efa] From 55bb9480f9159b229ac3c3454c97b62d1e0a7e80 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:19 +0530 Subject: [PATCH 2816/4607] ARC: [Review] Prevent incorrect syscall restarts Per Al Viro's "signals for dummies" https://lkml.org/lkml/2012/12/6/366 there are 3 golden rules for (not) restarting syscalls: " What we need to guarantee is * restarts do not happen on signals caught in interrupts or exceptions * restarts do not happen on signals caught in sigreturn() * restart should happen only once, even if we get through do_signal() many times." ARC Port already handled #1, this patch fixes #2 and #3. We use the additional state in pt_regs->orig_r8 to ckh if restarting has already been done once. Thanks to Al Viro for spotting this. Signed-off-by: Vineet Gupta Cc: Al Viro --- arch/arc/include/asm/ptrace.h | 3 +++ arch/arc/kernel/signal.c | 12 ++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index 3ec89f467ac3..a98957a95fb3 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h @@ -100,6 +100,9 @@ struct callee_regs { #define in_syscall(regs) (regs->event & orig_r8_IS_SCALL) #define in_brkpt_trap(regs) (regs->event & orig_r8_IS_BRKPT) +#define syscall_wont_restart(regs) (regs->event |= orig_r8_IS_SCALL_RESTARTED) +#define syscall_restartable(regs) !(regs->event & orig_r8_IS_SCALL_RESTARTED) + #define current_pt_regs() \ ({ \ /* open-coded current_thread_info() */ \ diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c index ab8ed5a55461..ee6ef2f60a28 100644 --- a/arch/arc/kernel/signal.c +++ b/arch/arc/kernel/signal.c @@ -128,6 +128,9 @@ SYSCALL_DEFINE0(rt_sigreturn) if (restore_altstack(&sf->uc.uc_stack)) goto badframe; + /* Don't restart from sigreturn */ + syscall_wont_restart(regs); + return regs->r0; badframe: @@ -318,13 +321,13 @@ void do_signal(struct pt_regs *regs) signr = get_signal_to_deliver(&info, &ka, regs, NULL); - /* Are we from a system call? */ - restart_scall = in_syscall(regs); + restart_scall = in_syscall(regs) && syscall_restartable(regs); if (signr > 0) { - if (restart_scall) + if (restart_scall) { arc_restart_syscall(&ka, regs); - + syscall_wont_restart(regs); /* No more restarts */ + } handle_signal(signr, &ka, &info, regs); return; } @@ -339,6 +342,7 @@ void do_signal(struct pt_regs *regs) regs->r8 = __NR_restart_syscall; regs->ret -= 4; } + syscall_wont_restart(regs); /* No more restarts */ } /* If there's no signal to deliver, restore the saved sigmask back */ From 95d6976d20a25fa1684f849f26cd3387b5ba7150 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:19 +0530 Subject: [PATCH 2817/4607] ARC: Cache Flush Management * ARC700 has VIPT L1 Caches * Caches don't snoop and are not coherent * Given the PAGE_SIZE and Cache associativity, we don't support aliasing D$ configurations (yet), but do allow aliasing I$ configs Signed-off-by: Vineet Gupta --- arch/arc/include/asm/arcregs.h | 80 ++++ arch/arc/include/asm/cache.h | 54 +++ arch/arc/include/asm/cachectl.h | 28 ++ arch/arc/include/asm/cacheflush.h | 67 +++ arch/arc/mm/cache_arc700.c | 725 ++++++++++++++++++++++++++++++ 5 files changed, 954 insertions(+) create mode 100644 arch/arc/include/asm/cachectl.h create mode 100644 arch/arc/include/asm/cacheflush.h create mode 100644 arch/arc/mm/cache_arc700.c diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index 5131bb3d4fcd..c6e28053fb70 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -58,6 +58,33 @@ #define TIMER_CTRL_IE (1 << 0) /* Interupt when Count reachs limit */ #define TIMER_CTRL_NH (1 << 1) /* Count only when CPU NOT halted */ +/* Instruction cache related Auxiliary registers */ +#define ARC_REG_IC_BCR 0x77 /* Build Config reg */ +#define ARC_REG_IC_IVIC 0x10 +#define ARC_REG_IC_CTRL 0x11 +#define ARC_REG_IC_IVIL 0x19 +#if (CONFIG_ARC_MMU_VER > 2) +#define ARC_REG_IC_PTAG 0x1E +#endif + +/* Bit val in IC_CTRL */ +#define IC_CTRL_CACHE_DISABLE 0x1 + +/* Data cache related Auxiliary registers */ +#define ARC_REG_DC_BCR 0x72 +#define ARC_REG_DC_IVDC 0x47 +#define ARC_REG_DC_CTRL 0x48 +#define ARC_REG_DC_IVDL 0x4A +#define ARC_REG_DC_FLSH 0x4B +#define ARC_REG_DC_FLDL 0x4C +#if (CONFIG_ARC_MMU_VER > 2) +#define ARC_REG_DC_PTAG 0x5C +#endif + +/* Bit val in DC_CTRL */ +#define DC_CTRL_INV_MODE_FLUSH 0x40 +#define DC_CTRL_FLUSH_STATUS 0x100 + /* * Floating Pt Registers * Status regs are read-only (build-time) so need not be saved/restored @@ -132,6 +159,31 @@ #endif +#define READ_BCR(reg, into) \ +{ \ + unsigned int tmp; \ + tmp = read_aux_reg(reg); \ + if (sizeof(tmp) == sizeof(into)) { \ + into = *((typeof(into) *)&tmp); \ + } else { \ + extern void bogus_undefined(void); \ + bogus_undefined(); \ + } \ +} + +#define WRITE_BCR(reg, into) \ +{ \ + unsigned int tmp; \ + if (sizeof(tmp) == sizeof(into)) { \ + tmp = (*(unsigned int *)(into)); \ + write_aux_reg(reg, tmp); \ + } else { \ + extern void bogus_undefined(void); \ + bogus_undefined(); \ + } \ +} + + #ifdef CONFIG_ARC_FPU_SAVE_RESTORE /* These DPFP regs need to be saved/restored across ctx-sw */ struct arc_fpu { @@ -141,6 +193,34 @@ struct arc_fpu { }; #endif +/* + *************************************************************** + * Build Configuration Registers, with encoded hardware config + */ + +struct bcr_cache { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int pad:12, line_len:4, sz:4, config:4, ver:8; +#else + unsigned int ver:8, config:4, sz:4, line_len:4, pad:12; +#endif +}; + +/* + ******************************************************************* + * Generic structures to hold build configuration used at runtime + */ + +struct cpuinfo_arc_cache { + unsigned int has_aliasing, sz, line_len, assoc, ver; +}; + +struct cpuinfo_arc { + struct cpuinfo_arc_cache icache, dcache; +}; + +extern struct cpuinfo_arc cpuinfo_arc700[]; + #endif /* __ASEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/arch/arc/include/asm/cache.h b/arch/arc/include/asm/cache.h index 30c72a4d2d9f..6632273861fd 100644 --- a/arch/arc/include/asm/cache.h +++ b/arch/arc/include/asm/cache.h @@ -18,4 +18,58 @@ #define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) +#define ARC_ICACHE_WAYS 2 +#define ARC_DCACHE_WAYS 4 + +/* Helpers */ +#define ARC_ICACHE_LINE_LEN L1_CACHE_BYTES +#define ARC_DCACHE_LINE_LEN L1_CACHE_BYTES + +#define ICACHE_LINE_MASK (~(ARC_ICACHE_LINE_LEN - 1)) +#define DCACHE_LINE_MASK (~(ARC_DCACHE_LINE_LEN - 1)) + +#if ARC_ICACHE_LINE_LEN != ARC_DCACHE_LINE_LEN +#error "Need to fix some code as I/D cache lines not same" +#else +#define is_not_cache_aligned(p) ((unsigned long)p & (~DCACHE_LINE_MASK)) +#endif + +#ifndef __ASSEMBLY__ + +/* Uncached access macros */ +#define arc_read_uncached_32(ptr) \ +({ \ + unsigned int __ret; \ + __asm__ __volatile__( \ + " ld.di %0, [%1] \n" \ + : "=r"(__ret) \ + : "r"(ptr)); \ + __ret; \ +}) + +#define arc_write_uncached_32(ptr, data)\ +({ \ + __asm__ __volatile__( \ + " st.di %0, [%1] \n" \ + : \ + : "r"(data), "r"(ptr)); \ +}) + +/* used to give SHMLBA a value to avoid Cache Aliasing */ +extern unsigned int ARC_shmlba; + +#define ARCH_DMA_MINALIGN L1_CACHE_BYTES + +/* + * ARC700 doesn't cache any access in top 256M. + * Ideal for wiring memory mapped peripherals as we don't need to do + * explicit uncached accesses (LD.di/ST.di) hence more portable drivers + */ +#define ARC_UNCACHED_ADDR_SPACE 0xc0000000 + +extern void arc_cache_init(void); +extern char *arc_cache_mumbojumbo(int cpu_id, char *buf, int len); +extern void __init read_decode_cache_bcr(void); +#endif + #endif /* _ASM_CACHE_H */ diff --git a/arch/arc/include/asm/cachectl.h b/arch/arc/include/asm/cachectl.h new file mode 100644 index 000000000000..51c73f0255b3 --- /dev/null +++ b/arch/arc/include/asm/cachectl.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 __ARC_ASM_CACHECTL_H +#define __ARC_ASM_CACHECTL_H + +/* + * ARC ABI flags defined for Android's finegrained cacheflush requirements + */ +#define CF_I_INV 0x0002 +#define CF_D_FLUSH 0x0010 +#define CF_D_FLUSH_INV 0x0020 + +#define CF_DEFAULT (CF_I_INV | CF_D_FLUSH) + +/* + * Standard flags expected by cacheflush system call users + */ +#define ICACHE CF_I_INV +#define DCACHE CF_D_FLUSH +#define BCACHE (CF_I_INV | CF_D_FLUSH) + +#endif diff --git a/arch/arc/include/asm/cacheflush.h b/arch/arc/include/asm/cacheflush.h new file mode 100644 index 000000000000..97ee96f26505 --- /dev/null +++ b/arch/arc/include/asm/cacheflush.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: May 2011: for Non-aliasing VIPT D-cache following can be NOPs + * -flush_cache_dup_mm (fork) + * -likewise for flush_cache_mm (exit/execve) + * -likewise for flush_cache_{range,page} (munmap, exit, COW-break) + * + * vineetg: April 2008 + * -Added a critical CacheLine flush to copy_to_user_page( ) which + * was causing gdbserver to not setup breakpoints consistently + */ + +#ifndef _ASM_CACHEFLUSH_H +#define _ASM_CACHEFLUSH_H + +#include + +void flush_cache_all(void); + +void flush_icache_range(unsigned long start, unsigned long end); +void flush_icache_page(struct vm_area_struct *vma, struct page *page); +void flush_icache_range_vaddr(unsigned long paddr, unsigned long u_vaddr, + int len); + +#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1 + +void flush_dcache_page(struct page *page); + +void dma_cache_wback_inv(unsigned long start, unsigned long sz); +void dma_cache_inv(unsigned long start, unsigned long sz); +void dma_cache_wback(unsigned long start, unsigned long sz); + +#define flush_dcache_mmap_lock(mapping) do { } while (0) +#define flush_dcache_mmap_unlock(mapping) do { } while (0) + +/* TBD: optimize this */ +#define flush_cache_vmap(start, end) flush_cache_all() +#define flush_cache_vunmap(start, end) flush_cache_all() + +/* + * VM callbacks when entire/range of user-space V-P mappings are + * torn-down/get-invalidated + * + * Currently we don't support D$ aliasing configs for our VIPT caches + * NOPS for VIPT Cache with non-aliasing D$ configurations only + */ +#define flush_cache_dup_mm(mm) /* called on fork */ +#define flush_cache_mm(mm) /* called on munmap/exit */ +#define flush_cache_range(mm, u_vstart, u_vend) +#define flush_cache_page(vma, u_vaddr, pfn) /* PF handling/COW-break */ + +#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ +do { \ + memcpy(dst, src, len); \ + if (vma->vm_flags & VM_EXEC) \ + flush_icache_range_vaddr((unsigned long)(dst), vaddr, len);\ +} while (0) + +#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ + memcpy(dst, src, len); \ + +#endif diff --git a/arch/arc/mm/cache_arc700.c b/arch/arc/mm/cache_arc700.c new file mode 100644 index 000000000000..670f65bb2ef1 --- /dev/null +++ b/arch/arc/mm/cache_arc700.c @@ -0,0 +1,725 @@ +/* + * ARC700 VIPT Cache Management + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: May 2011: for Non-aliasing VIPT D-cache following can be NOPs + * -flush_cache_dup_mm (fork) + * -likewise for flush_cache_mm (exit/execve) + * -likewise for flush_cache_range,flush_cache_page (munmap, exit, COW-break) + * + * vineetg: Apr 2011 + * -Now that MMU can support larger pg sz (16K), the determiniation of + * aliasing shd not be based on assumption of 8k pg + * + * vineetg: Mar 2011 + * -optimised version of flush_icache_range( ) for making I/D coherent + * when vaddr is available (agnostic of num of aliases) + * + * vineetg: Mar 2011 + * -Added documentation about I-cache aliasing on ARC700 and the way it + * was handled up until MMU V2. + * -Spotted a three year old bug when killing the 4 aliases, which needs + * bottom 2 bits, so we need to do paddr | {0x00, 0x01, 0x02, 0x03} + * instead of paddr | {0x00, 0x01, 0x10, 0x11} + * (Rajesh you owe me one now) + * + * vineetg: Dec 2010 + * -Off-by-one error when computing num_of_lines to flush + * This broke signal handling with bionic which uses synthetic sigret stub + * + * vineetg: Mar 2010 + * -GCC can't generate ZOL for core cache flush loops. + * Conv them into iterations based as opposed to while (start < end) types + * + * Vineetg: July 2009 + * -In I-cache flush routine we used to chk for aliasing for every line INV. + * Instead now we setup routines per cache geometry and invoke them + * via function pointers. + * + * Vineetg: Jan 2009 + * -Cache Line flush routines used to flush an extra line beyond end addr + * because check was while (end >= start) instead of (end > start) + * =Some call sites had to work around by doing -1, -4 etc to end param + * =Some callers didnt care. This was spec bad in case of INV routines + * which would discard valid data (cause of the horrible ext2 bug + * in ARC IDE driver) + * + * vineetg: June 11th 2008: Fixed flush_icache_range( ) + * -Since ARC700 caches are not coherent (I$ doesnt snoop D$) both need + * to be flushed, which it was not doing. + * -load_module( ) passes vmalloc addr (Kernel Virtual Addr) to the API, + * however ARC cache maintenance OPs require PHY addr. Thus need to do + * vmalloc_to_phy. + * -Also added optimisation there, that for range > PAGE SIZE we flush the + * entire cache in one shot rather than line by line. For e.g. a module + * with Code sz 600k, old code flushed 600k worth of cache (line-by-line), + * while cache is only 16 or 32k. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef CONFIG_ARC_HAS_ICACHE +static void __ic_line_inv_no_alias(unsigned long, int); +static void __ic_line_inv_2_alias(unsigned long, int); +static void __ic_line_inv_4_alias(unsigned long, int); + +/* Holds the ptr to flush routine, dependign on size due to aliasing issues */ +static void (*___flush_icache_rtn) (unsigned long, int); +#endif + +/* + * Read the Cache Build Confuration Registers, Decode them and save into + * the cpuinfo structure for later use. + * No Validation done here, simply read/convert the BCRs + */ +void __init read_decode_cache_bcr(void) +{ + struct bcr_cache ibcr, dbcr; + struct cpuinfo_arc_cache *p_ic, *p_dc; + unsigned int cpu = smp_processor_id(); + + p_ic = &cpuinfo_arc700[cpu].icache; + READ_BCR(ARC_REG_IC_BCR, ibcr); + + if (ibcr.config == 0x3) + p_ic->assoc = 2; + p_ic->line_len = 8 << ibcr.line_len; + p_ic->sz = 0x200 << ibcr.sz; + p_ic->ver = ibcr.ver; + + p_dc = &cpuinfo_arc700[cpu].dcache; + READ_BCR(ARC_REG_DC_BCR, dbcr); + + if (dbcr.config == 0x2) + p_dc->assoc = 4; + p_dc->line_len = 16 << dbcr.line_len; + p_dc->sz = 0x200 << dbcr.sz; + p_dc->ver = dbcr.ver; +} + +/* + * 1. Validate the Cache Geomtery (compile time config matches hardware) + * 2. If I-cache suffers from aliasing, setup work arounds (difft flush rtn) + * (aliasing D-cache configurations are not supported YET) + * 3. Enable the Caches, setup default flush mode for D-Cache + * 3. Calculate the SHMLBA used by user space + */ +void __init arc_cache_init(void) +{ + unsigned int temp; +#ifdef CONFIG_ARC_CACHE + unsigned int cpu = smp_processor_id(); +#endif +#ifdef CONFIG_ARC_HAS_ICACHE + struct cpuinfo_arc_cache *ic; +#endif +#ifdef CONFIG_ARC_HAS_DCACHE + struct cpuinfo_arc_cache *dc; +#endif + int way_pg_ratio = way_pg_ratio; + +#ifdef CONFIG_ARC_HAS_ICACHE + ic = &cpuinfo_arc700[cpu].icache; + + /* + * if Cache way size is <= page size then no aliasing exhibited + * otherwise ratio determines num of aliases. + * e.g. 32K I$, 2 way set assoc, 8k pg size + * way-sz = 32k/2 = 16k + * way-pg-ratio = 16k/8k = 2, so 2 aliases possible + * (meaning 1 line could be in 2 possible locations). + */ + way_pg_ratio = ic->sz / ARC_ICACHE_WAYS / PAGE_SIZE; + switch (way_pg_ratio) { + case 0: + case 1: + ___flush_icache_rtn = __ic_line_inv_no_alias; + break; + case 2: + ___flush_icache_rtn = __ic_line_inv_2_alias; + break; + case 4: + ___flush_icache_rtn = __ic_line_inv_4_alias; + break; + default: + panic("Unsupported I-Cache Sz\n"); + } +#endif + + /* Enable/disable I-Cache */ + temp = read_aux_reg(ARC_REG_IC_CTRL); + +#ifdef CONFIG_ARC_HAS_ICACHE + temp &= ~IC_CTRL_CACHE_DISABLE; +#else + temp |= IC_CTRL_CACHE_DISABLE; +#endif + + write_aux_reg(ARC_REG_IC_CTRL, temp); + +#ifdef CONFIG_ARC_HAS_DCACHE + dc = &cpuinfo_arc700[cpu].dcache; + + /* check for D-Cache aliasing */ + if ((dc->sz / ARC_DCACHE_WAYS) > PAGE_SIZE) + panic("D$ aliasing not handled right now\n"); +#endif + + /* Set the default Invalidate Mode to "simpy discard dirty lines" + * as this is more frequent then flush before invalidate + * Ofcourse we toggle this default behviour when desired + */ + temp = read_aux_reg(ARC_REG_DC_CTRL); + temp &= ~DC_CTRL_INV_MODE_FLUSH; + +#ifdef CONFIG_ARC_HAS_DCACHE + /* Enable D-Cache: Clear Bit 0 */ + write_aux_reg(ARC_REG_DC_CTRL, temp & ~IC_CTRL_CACHE_DISABLE); +#else + /* Flush D cache */ + write_aux_reg(ARC_REG_DC_FLSH, 0x1); + /* Disable D cache */ + write_aux_reg(ARC_REG_DC_CTRL, temp | IC_CTRL_CACHE_DISABLE); +#endif + + return; +} + +#define OP_INV 0x1 +#define OP_FLUSH 0x2 +#define OP_FLUSH_N_INV 0x3 + +#ifdef CONFIG_ARC_HAS_DCACHE + +/*************************************************************** + * Machine specific helpers for Entire D-Cache or Per Line ops + */ + +static inline void wait_for_flush(void) +{ + while (read_aux_reg(ARC_REG_DC_CTRL) & DC_CTRL_FLUSH_STATUS) + ; +} + +/* + * Operation on Entire D-Cache + * @cacheop = {OP_INV, OP_FLUSH, OP_FLUSH_N_INV} + * Note that constant propagation ensures all the checks are gone + * in generated code + */ +static inline void __dc_entire_op(const int cacheop) +{ + unsigned long flags, tmp = tmp; + int aux; + + local_irq_save(flags); + + if (cacheop == OP_FLUSH_N_INV) { + /* Dcache provides 2 cmd: FLUSH or INV + * INV inturn has sub-modes: DISCARD or FLUSH-BEFORE + * flush-n-inv is achieved by INV cmd but with IM=1 + * Default INV sub-mode is DISCARD, which needs to be toggled + */ + tmp = read_aux_reg(ARC_REG_DC_CTRL); + write_aux_reg(ARC_REG_DC_CTRL, tmp | DC_CTRL_INV_MODE_FLUSH); + } + + if (cacheop & OP_INV) /* Inv or flush-n-inv use same cmd reg */ + aux = ARC_REG_DC_IVDC; + else + aux = ARC_REG_DC_FLSH; + + write_aux_reg(aux, 0x1); + + if (cacheop & OP_FLUSH) /* flush / flush-n-inv both wait */ + wait_for_flush(); + + /* Switch back the DISCARD ONLY Invalidate mode */ + if (cacheop == OP_FLUSH_N_INV) + write_aux_reg(ARC_REG_DC_CTRL, tmp & ~DC_CTRL_INV_MODE_FLUSH); + + local_irq_restore(flags); +} + +/* + * Per Line Operation on D-Cache + * Doesn't deal with type-of-op/IRQ-disabling/waiting-for-flush-to-complete + * It's sole purpose is to help gcc generate ZOL + */ +static inline void __dc_line_loop(unsigned long start, unsigned long sz, + int aux_reg) +{ + int num_lines, slack; + + /* Ensure we properly floor/ceil the non-line aligned/sized requests + * and have @start - aligned to cache line and integral @num_lines. + * This however can be avoided for page sized since: + * -@start will be cache-line aligned already (being page aligned) + * -@sz will be integral multiple of line size (being page sized). + */ + if (!(__builtin_constant_p(sz) && sz == PAGE_SIZE)) { + slack = start & ~DCACHE_LINE_MASK; + sz += slack; + start -= slack; + } + + num_lines = DIV_ROUND_UP(sz, ARC_DCACHE_LINE_LEN); + + while (num_lines-- > 0) { +#if (CONFIG_ARC_MMU_VER > 2) + /* + * Just as for I$, in MMU v3, D$ ops also require + * "tag" bits in DC_PTAG, "index" bits in FLDL,IVDL ops + * But we pass phy addr for both. This works since Linux + * doesn't support aliasing configs for D$, yet. + * Thus paddr is enough to provide both tag and index. + */ + write_aux_reg(ARC_REG_DC_PTAG, start); +#endif + write_aux_reg(aux_reg, start); + start += ARC_DCACHE_LINE_LEN; + } +} + +/* + * D-Cache : Per Line INV (discard or wback+discard) or FLUSH (wback) + */ +static inline void __dc_line_op(unsigned long start, unsigned long sz, + const int cacheop) +{ + unsigned long flags, tmp = tmp; + int aux; + + local_irq_save(flags); + + if (cacheop == OP_FLUSH_N_INV) { + /* + * Dcache provides 2 cmd: FLUSH or INV + * INV inturn has sub-modes: DISCARD or FLUSH-BEFORE + * flush-n-inv is achieved by INV cmd but with IM=1 + * Default INV sub-mode is DISCARD, which needs to be toggled + */ + tmp = read_aux_reg(ARC_REG_DC_CTRL); + write_aux_reg(ARC_REG_DC_CTRL, tmp | DC_CTRL_INV_MODE_FLUSH); + } + + if (cacheop & OP_INV) /* Inv / flush-n-inv use same cmd reg */ + aux = ARC_REG_DC_IVDL; + else + aux = ARC_REG_DC_FLDL; + + __dc_line_loop(start, sz, aux); + + if (cacheop & OP_FLUSH) /* flush / flush-n-inv both wait */ + wait_for_flush(); + + /* Switch back the DISCARD ONLY Invalidate mode */ + if (cacheop == OP_FLUSH_N_INV) + write_aux_reg(ARC_REG_DC_CTRL, tmp & ~DC_CTRL_INV_MODE_FLUSH); + + local_irq_restore(flags); +} + +#else + +#define __dc_entire_op(cacheop) +#define __dc_line_op(start, sz, cacheop) + +#endif /* CONFIG_ARC_HAS_DCACHE */ + + +#ifdef CONFIG_ARC_HAS_ICACHE + +/* + * I-Cache Aliasing in ARC700 VIPT caches + * + * For fetching code from I$, ARC700 uses vaddr (embedded in program code) + * to "index" into SET of cache-line and paddr from MMU to match the TAG + * in the WAYS of SET. + * + * However the CDU iterface (to flush/inv) lines from software, only takes + * paddr (to have simpler hardware interface). For simpler cases, using paddr + * alone suffices. + * e.g. 2-way-set-assoc, 16K I$ (8k MMU pg sz, 32b cache line size): + * way_sz = cache_sz / num_ways = 16k/2 = 8k + * num_sets = way_sz / line_sz = 8k/32 = 256 => 8 bits + * Ignoring the bottom 5 bits corresp to the off within a 32b cacheline, + * bits req for calc set-index = bits 12:5 (0 based). Since this range fits + * inside the bottom 13 bits of paddr, which are same for vaddr and paddr + * (with 8k pg sz), paddr alone can be safely used by CDU to unambigously + * locate a cache-line. + * + * However for a difft sized cache, say 32k I$, above math yields need + * for 14 bits of vaddr to locate a cache line, which can't be provided by + * paddr, since the bit 13 (0 based) might differ between the two. + * + * This lack of extra bits needed for correct line addressing, defines the + * classical problem of Cache aliasing with VIPT architectures + * num_aliases = 1 << extra_bits + * e.g. 2-way-set-assoc, 32K I$ with 8k MMU pg sz => 2 aliases + * 2-way-set-assoc, 64K I$ with 8k MMU pg sz => 4 aliases + * 2-way-set-assoc, 16K I$ with 8k MMU pg sz => NO aliases + * + * ------------------ + * MMU v1/v2 (Fixed Page Size 8k) + * ------------------ + * The solution was to provide CDU with these additonal vaddr bits. These + * would be bits [x:13], x would depend on cache-geom. + * H/w folks chose [17:13] to be a future safe range, and moreso these 5 bits + * of vaddr could easily be "stuffed" in the paddr as bits [4:0] since the + * orig 5 bits of paddr were anyways ignored by CDU line ops, as they + * represent the offset within cache-line. The adv of using this "clumsy" + * interface for additional info was no new reg was needed in CDU. + * + * 17:13 represented the max num of bits passable, actual bits needed were + * fewer, based on the num-of-aliases possible. + * -for 2 alias possibility, only bit 13 needed (32K cache) + * -for 4 alias possibility, bits 14:13 needed (64K cache) + * + * Since vaddr was not available for all instances of I$ flush req by core + * kernel, the only safe way (non-optimal though) was to kill all possible + * lines which could represent an alias (even if they didnt represent one + * in execution). + * e.g. for 64K I$, 4 aliases possible, so we did + * flush start + * flush start | 0x01 + * flush start | 0x2 + * flush start | 0x3 + * + * The penalty was invoking the operation itself, since tag match is anyways + * paddr based, a line which didn't represent an alias would not match the + * paddr, hence wont be killed + * + * Note that aliasing concerns are independent of line-sz for a given cache + * geometry (size + set_assoc) because the extra bits required by line-sz are + * reduced from the set calc. + * e.g. 2-way-set-assoc, 32K I$ with 8k MMU pg sz and using math above + * 32b line-sz: 9 bits set-index-calc, 5 bits offset-in-line => 1 extra bit + * 64b line-sz: 8 bits set-index-calc, 6 bits offset-in-line => 1 extra bit + * + * ------------------ + * MMU v3 + * ------------------ + * This ver of MMU supports var page sizes (1k-16k) - Linux will support + * 8k (default), 16k and 4k. + * However from hardware perspective, smaller page sizes aggrevate aliasing + * meaning more vaddr bits needed to disambiguate the cache-line-op ; + * the existing scheme of piggybacking won't work for certain configurations. + * Two new registers IC_PTAG and DC_PTAG inttoduced. + * "tag" bits are provided in PTAG, index bits in existing IVIL/IVDL/FLDL regs + */ + +/*********************************************************** + * Machine specific helpers for per line I-Cache invalidate. + * 3 routines to accpunt for 1, 2, 4 aliases possible + */ + +static void __ic_line_inv_no_alias(unsigned long start, int num_lines) +{ + while (num_lines-- > 0) { +#if (CONFIG_ARC_MMU_VER > 2) + write_aux_reg(ARC_REG_IC_PTAG, start); +#endif + write_aux_reg(ARC_REG_IC_IVIL, start); + start += ARC_ICACHE_LINE_LEN; + } +} + +static void __ic_line_inv_2_alias(unsigned long start, int num_lines) +{ + while (num_lines-- > 0) { + +#if (CONFIG_ARC_MMU_VER > 2) + /* + * MMU v3, CDU prog model (for line ops) now uses a new IC_PTAG + * reg to pass the "tag" bits and existing IVIL reg only looks + * at bits relevant for "index" (details above) + * Programming Notes: + * -when writing tag to PTAG reg, bit chopping can be avoided, + * CDU ignores non-tag bits. + * -Ideally "index" must be computed from vaddr, but it is not + * avail in these rtns. So to be safe, we kill the lines in all + * possible indexes corresp to num of aliases possible for + * given cache config. + */ + write_aux_reg(ARC_REG_IC_PTAG, start); + write_aux_reg(ARC_REG_IC_IVIL, + start & ~(0x1 << PAGE_SHIFT)); + write_aux_reg(ARC_REG_IC_IVIL, start | (0x1 << PAGE_SHIFT)); +#else + write_aux_reg(ARC_REG_IC_IVIL, start); + write_aux_reg(ARC_REG_IC_IVIL, start | 0x01); +#endif + start += ARC_ICACHE_LINE_LEN; + } +} + +static void __ic_line_inv_4_alias(unsigned long start, int num_lines) +{ + while (num_lines-- > 0) { + +#if (CONFIG_ARC_MMU_VER > 2) + write_aux_reg(ARC_REG_IC_PTAG, start); + + write_aux_reg(ARC_REG_IC_IVIL, + start & ~(0x3 << PAGE_SHIFT)); + write_aux_reg(ARC_REG_IC_IVIL, + start & ~(0x2 << PAGE_SHIFT)); + write_aux_reg(ARC_REG_IC_IVIL, + start & ~(0x1 << PAGE_SHIFT)); + write_aux_reg(ARC_REG_IC_IVIL, start | (0x3 << PAGE_SHIFT)); +#else + write_aux_reg(ARC_REG_IC_IVIL, start); + write_aux_reg(ARC_REG_IC_IVIL, start | 0x01); + write_aux_reg(ARC_REG_IC_IVIL, start | 0x02); + write_aux_reg(ARC_REG_IC_IVIL, start | 0x03); +#endif + start += ARC_ICACHE_LINE_LEN; + } +} + +static void __ic_line_inv(unsigned long start, unsigned long sz) +{ + unsigned long flags; + int num_lines, slack; + + /* + * Ensure we properly floor/ceil the non-line aligned/sized requests + * and have @start - aligned to cache line, and integral @num_lines + * However page sized flushes can be compile time optimised. + * -@start will be cache-line aligned already (being page aligned) + * -@sz will be integral multiple of line size (being page sized). + */ + if (!(__builtin_constant_p(sz) && sz == PAGE_SIZE)) { + slack = start & ~ICACHE_LINE_MASK; + sz += slack; + start -= slack; + } + + num_lines = DIV_ROUND_UP(sz, ARC_ICACHE_LINE_LEN); + + local_irq_save(flags); + (*___flush_icache_rtn) (start, num_lines); + local_irq_restore(flags); +} + +/* Unlike routines above, having vaddr for flush op (along with paddr), + * prevents the need to speculatively kill the lines in multiple sets + * based on ratio of way_sz : pg_sz + */ +static void __ic_line_inv_vaddr(unsigned long phy_start, + unsigned long vaddr, unsigned long sz) +{ + unsigned long flags; + int num_lines, slack; + unsigned int addr; + + slack = phy_start & ~ICACHE_LINE_MASK; + sz += slack; + phy_start -= slack; + num_lines = DIV_ROUND_UP(sz, ARC_ICACHE_LINE_LEN); + +#if (CONFIG_ARC_MMU_VER > 2) + vaddr &= ~ICACHE_LINE_MASK; + addr = phy_start; +#else + /* bits 17:13 of vaddr go as bits 4:0 of paddr */ + addr = phy_start | ((vaddr >> 13) & 0x1F); +#endif + + local_irq_save(flags); + while (num_lines-- > 0) { +#if (CONFIG_ARC_MMU_VER > 2) + /* tag comes from phy addr */ + write_aux_reg(ARC_REG_IC_PTAG, addr); + + /* index bits come from vaddr */ + write_aux_reg(ARC_REG_IC_IVIL, vaddr); + vaddr += ARC_ICACHE_LINE_LEN; +#else + /* this paddr contains vaddrs bits as needed */ + write_aux_reg(ARC_REG_IC_IVIL, addr); +#endif + addr += ARC_ICACHE_LINE_LEN; + } + local_irq_restore(flags); +} + +#else + +#define __ic_line_inv(start, sz) +#define __ic_line_inv_vaddr(pstart, vstart, sz) + +#endif /* CONFIG_ARC_HAS_ICACHE */ + + +/*********************************************************** + * Exported APIs + */ + +/* TBD: use pg_arch_1 to optimize this */ +void flush_dcache_page(struct page *page) +{ + __dc_line_op((unsigned long)page_address(page), PAGE_SIZE, OP_FLUSH); +} +EXPORT_SYMBOL(flush_dcache_page); + + +void dma_cache_wback_inv(unsigned long start, unsigned long sz) +{ + __dc_line_op(start, sz, OP_FLUSH_N_INV); +} +EXPORT_SYMBOL(dma_cache_wback_inv); + +void dma_cache_inv(unsigned long start, unsigned long sz) +{ + __dc_line_op(start, sz, OP_INV); +} +EXPORT_SYMBOL(dma_cache_inv); + +void dma_cache_wback(unsigned long start, unsigned long sz) +{ + __dc_line_op(start, sz, OP_FLUSH); +} +EXPORT_SYMBOL(dma_cache_wback); + +/* + * This is API for making I/D Caches consistent when modifying code + * (loadable modules, kprobes, etc) + * This is called on insmod, with kernel virtual address for CODE of + * the module. ARC cache maintenance ops require PHY address thus we + * need to convert vmalloc addr to PHY addr + */ +void flush_icache_range(unsigned long kstart, unsigned long kend) +{ + unsigned int tot_sz, off, sz; + unsigned long phy, pfn; + unsigned long flags; + + /* printk("Kernel Cache Cohenercy: %lx to %lx\n",kstart, kend); */ + + /* This is not the right API for user virtual address */ + if (kstart < TASK_SIZE) { + BUG_ON("Flush icache range for user virtual addr space"); + return; + } + + /* Shortcut for bigger flush ranges. + * Here we don't care if this was kernel virtual or phy addr + */ + tot_sz = kend - kstart; + if (tot_sz > PAGE_SIZE) { + flush_cache_all(); + return; + } + + /* Case: Kernel Phy addr (0x8000_0000 onwards) */ + if (likely(kstart > PAGE_OFFSET)) { + __ic_line_inv(kstart, kend - kstart); + __dc_line_op(kstart, kend - kstart, OP_FLUSH); + return; + } + + /* + * Case: Kernel Vaddr (0x7000_0000 to 0x7fff_ffff) + * (1) ARC Cache Maintenance ops only take Phy addr, hence special + * handling of kernel vaddr. + * + * (2) Despite @tot_sz being < PAGE_SIZE (bigger cases handled already), + * it still needs to handle a 2 page scenario, where the range + * straddles across 2 virtual pages and hence need for loop + */ + while (tot_sz > 0) { + off = kstart % PAGE_SIZE; + pfn = vmalloc_to_pfn((void *)kstart); + phy = (pfn << PAGE_SHIFT) + off; + sz = min_t(unsigned int, tot_sz, PAGE_SIZE - off); + local_irq_save(flags); + __dc_line_op(phy, sz, OP_FLUSH); + __ic_line_inv(phy, sz); + local_irq_restore(flags); + kstart += sz; + tot_sz -= sz; + } +} + +/* + * Optimised ver of flush_icache_range() with spec callers: ptrace/signals + * where vaddr is also available. This allows passing both vaddr and paddr + * bits to CDU for cache flush, short-circuting the current pessimistic algo + * which kills all possible aliases. + * An added adv of knowing that vaddr is user-vaddr avoids various checks + * and handling for k-vaddr, k-paddr as done in orig ver above + */ +void flush_icache_range_vaddr(unsigned long paddr, unsigned long u_vaddr, + int len) +{ + __ic_line_inv_vaddr(paddr, u_vaddr, len); + __dc_line_op(paddr, len, OP_FLUSH); +} + +/* + * XXX: This also needs to be optim using pg_arch_1 + * This is called when a page-cache page is about to be mapped into a + * user process' address space. It offers an opportunity for a + * port to ensure d-cache/i-cache coherency if necessary. + */ +void flush_icache_page(struct vm_area_struct *vma, struct page *page) +{ + if (!(vma->vm_flags & VM_EXEC)) + return; + + __ic_line_inv((unsigned long)page_address(page), PAGE_SIZE); +} + +void flush_icache_all(void) +{ + unsigned long flags; + + local_irq_save(flags); + + write_aux_reg(ARC_REG_IC_IVIC, 1); + + /* lr will not complete till the icache inv operation is not over */ + read_aux_reg(ARC_REG_IC_CTRL); + local_irq_restore(flags); +} + +noinline void flush_cache_all(void) +{ + unsigned long flags; + + local_irq_save(flags); + + flush_icache_all(); + __dc_entire_op(OP_FLUSH_N_INV); + + local_irq_restore(flags); + +} + +/********************************************************************** + * Explicit Cache flush request from user space via syscall + * Needed for JITs which generate code on the fly + */ +SYSCALL_DEFINE3(cacheflush, uint32_t, start, uint32_t, sz, uint32_t, flags) +{ + /* TBD: optimize this */ + flush_cache_all(); + return 0; +} From 5dda4dc570ac41e3bd73ef871c500aeb7005c6b0 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:19 +0530 Subject: [PATCH 2818/4607] ARC: Page Table Management Signed-off-by: Vineet Gupta --- arch/arc/include/asm/page.h | 92 ++++++++ arch/arc/include/asm/pgalloc.h | 134 +++++++++++ arch/arc/include/asm/pgtable.h | 401 +++++++++++++++++++++++++++++++++ 3 files changed, 627 insertions(+) create mode 100644 arch/arc/include/asm/pgalloc.h create mode 100644 arch/arc/include/asm/pgtable.h diff --git a/arch/arc/include/asm/page.h b/arch/arc/include/asm/page.h index 7cd1224d8e7d..d111d0cea9cc 100644 --- a/arch/arc/include/asm/page.h +++ b/arch/arc/include/asm/page.h @@ -35,6 +35,98 @@ #define PAGE_MASK (~(PAGE_SIZE-1)) +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ + +#define get_user_page(vaddr) __get_free_page(GFP_KERNEL) +#define free_user_page(page, addr) free_page(addr) + +/* TBD: for now don't worry about VIPT D$ aliasing */ +#define clear_page(paddr) memset((paddr), 0, PAGE_SIZE) +#define copy_page(to, from) memcpy((to), (from), PAGE_SIZE) + +#define clear_user_page(addr, vaddr, pg) clear_page(addr) +#define copy_user_page(vto, vfrom, vaddr, pg) copy_page(vto, vfrom) + +#undef STRICT_MM_TYPECHECKS + +#ifdef STRICT_MM_TYPECHECKS +/* + * These are used to make use of C type-checking.. + */ +typedef struct { + unsigned long pte; +} pte_t; +typedef struct { + unsigned long pgd; +} pgd_t; +typedef struct { + unsigned long pgprot; +} pgprot_t; +typedef unsigned long pgtable_t; + +#define pte_val(x) ((x).pte) +#define pgd_val(x) ((x).pgd) +#define pgprot_val(x) ((x).pgprot) + +#define __pte(x) ((pte_t) { (x) }) +#define __pgd(x) ((pgd_t) { (x) }) +#define __pgprot(x) ((pgprot_t) { (x) }) + +#else /* !STRICT_MM_TYPECHECKS */ + +typedef unsigned long pte_t; +typedef unsigned long pgd_t; +typedef unsigned long pgprot_t; +typedef unsigned long pgtable_t; + +#define pte_val(x) (x) +#define pgd_val(x) (x) +#define pgprot_val(x) (x) +#define __pte(x) (x) +#define __pgprot(x) (x) + +#endif + +#define ARCH_PFN_OFFSET (CONFIG_LINUX_LINK_BASE >> PAGE_SHIFT) + +#define pfn_valid(pfn) (((pfn) - ARCH_PFN_OFFSET) < max_mapnr) + +/* + * __pa, __va, virt_to_page (ALERT: deprecated, don't use them) + * + * These macros have historically been misnamed + * virt here means link-address/program-address as embedded in object code. + * So if kernel img is linked at 0x8000_0000 onwards, 0x8010_0000 will be + * 128th page, and virt_to_page( ) will return the struct page corresp to it. + * mem_map[ ] is an array of struct page for each page frame in the system + * + * Independent of where linux is linked at, link-addr = physical address + * So the old macro __pa = vaddr + PAGE_OFFSET - CONFIG_LINUX_LINK_BASE + * would have been wrong in case kernel is not at 0x8zs + */ +#define __pa(vaddr) ((unsigned long)vaddr) +#define __va(paddr) ((void *)((unsigned long)(paddr))) + +#define virt_to_page(kaddr) \ + (mem_map + ((__pa(kaddr) - CONFIG_LINUX_LINK_BASE) >> PAGE_SHIFT)) + +#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) + +/* Default Permissions for page, used in mmap.c */ +#ifdef CONFIG_ARC_STACK_NONEXEC +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE) +#else +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#endif + +#define WANT_PAGE_VIRTUAL 1 + +#include /* page_to_pfn, pfn_to_page */ +#include + #endif /* !__ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/arch/arc/include/asm/pgalloc.h b/arch/arc/include/asm/pgalloc.h new file mode 100644 index 000000000000..36a9f20c21a3 --- /dev/null +++ b/arch/arc/include/asm/pgalloc.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: June 2011 + * -"/proc/meminfo | grep PageTables" kept on increasing + * Recently added pgtable dtor was not getting called. + * + * vineetg: May 2011 + * -Variable pg-sz means that Page Tables could be variable sized themselves + * So calculate it based on addr traversal split [pgd-bits:pte-bits:xxx] + * -Page Table size capped to max 1 to save memory - hence verified. + * -Since these deal with constants, gcc compile-time optimizes them. + * + * vineetg: Nov 2010 + * -Added pgtable ctor/dtor used for pgtable mem accounting + * + * vineetg: April 2010 + * -Switched pgtable_t from being struct page * to unsigned long + * =Needed so that Page Table allocator (pte_alloc_one) is not forced to + * to deal with struct page. Thay way in future we can make it allocate + * multiple PG Tbls in one Page Frame + * =sweet side effect is avoiding calls to ugly page_address( ) from the + * pg-tlb allocator sub-sys (pte_alloc_one, ptr_free, pmd_populate + * + * Amit Bhor, Sameer Dhavale: Codito Technologies 2004 + */ + +#ifndef _ASM_ARC_PGALLOC_H +#define _ASM_ARC_PGALLOC_H + +#include +#include + +static inline void +pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte) +{ + pmd_set(pmd, pte); +} + +static inline void +pmd_populate(struct mm_struct *mm, pmd_t *pmd, pgtable_t ptep) +{ + pmd_set(pmd, (pte_t *) ptep); +} + +static inline int __get_order_pgd(void) +{ + return get_order(PTRS_PER_PGD * 4); +} + +static inline pgd_t *pgd_alloc(struct mm_struct *mm) +{ + int num, num2; + pgd_t *ret = (pgd_t *) __get_free_pages(GFP_KERNEL, __get_order_pgd()); + + if (ret) { + num = USER_PTRS_PER_PGD + USER_KERNEL_GUTTER / PGDIR_SIZE; + memzero(ret, num * sizeof(pgd_t)); + + num2 = VMALLOC_SIZE / PGDIR_SIZE; + memcpy(ret + num, swapper_pg_dir + num, num2 * sizeof(pgd_t)); + + memzero(ret + num + num2, + (PTRS_PER_PGD - num - num2) * sizeof(pgd_t)); + + } + return ret; +} + +static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd) +{ + free_pages((unsigned long)pgd, __get_order_pgd()); +} + + +/* + * With software-only page-tables, addr-split for traversal is tweakable and + * that directly governs how big tables would be at each level. + * Further, the MMU page size is configurable. + * Thus we need to programatically assert the size constraint + * All of this is const math, allowing gcc to do constant folding/propagation. + */ + +static inline int __get_order_pte(void) +{ + return get_order(PTRS_PER_PTE * 4); +} + +static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, + unsigned long address) +{ + pte_t *pte; + + pte = (pte_t *) __get_free_pages(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO, + __get_order_pte()); + + return pte; +} + +static inline pgtable_t +pte_alloc_one(struct mm_struct *mm, unsigned long address) +{ + pgtable_t pte_pg; + + pte_pg = __get_free_pages(GFP_KERNEL | __GFP_REPEAT, __get_order_pte()); + if (pte_pg) { + memzero((void *)pte_pg, PTRS_PER_PTE * 4); + pgtable_page_ctor(virt_to_page(pte_pg)); + } + + return pte_pg; +} + +static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) +{ + free_pages((unsigned long)pte, __get_order_pte()); /* takes phy addr */ +} + +static inline void pte_free(struct mm_struct *mm, pgtable_t ptep) +{ + pgtable_page_dtor(virt_to_page(ptep)); + free_pages(ptep, __get_order_pte()); +} + +#define __pte_free_tlb(tlb, pte, addr) pte_free((tlb)->mm, pte) + +#define check_pgt_cache() do { } while (0) +#define pmd_pgtable(pmd) pmd_page_vaddr(pmd) + +#endif /* _ASM_ARC_PGALLOC_H */ diff --git a/arch/arc/include/asm/pgtable.h b/arch/arc/include/asm/pgtable.h new file mode 100644 index 000000000000..dcb0701528aa --- /dev/null +++ b/arch/arc/include/asm/pgtable.h @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: May 2011 + * -Folded PAGE_PRESENT (used by VM) and PAGE_VALID (used by MMU) into 1. + * They are semantically the same although in different contexts + * VALID marks a TLB entry exists and it will only happen if PRESENT + * - Utilise some unused free bits to confine PTE flags to 12 bits + * This is a must for 4k pg-sz + * + * vineetg: Mar 2011 - changes to accomodate MMU TLB Page Descriptor mods + * -TLB Locking never really existed, except for initial specs + * -SILENT_xxx not needed for our port + * -Per my request, MMU V3 changes the layout of some of the bits + * to avoid a few shifts in TLB Miss handlers. + * + * vineetg: April 2010 + * -PGD entry no longer contains any flags. If empty it is 0, otherwise has + * Pg-Tbl ptr. Thus pmd_present(), pmd_valid(), pmd_set( ) become simpler + * + * vineetg: April 2010 + * -Switched form 8:11:13 split for page table lookup to 11:8:13 + * -this speeds up page table allocation itself as we now have to memset 1K + * instead of 8k per page table. + * -TODO: Right now page table alloc is 8K and rest 7K is unused + * need to optimise it + * + * Amit Bhor, Sameer Dhavale: Codito Technologies 2004 + */ + +#ifndef _ASM_ARC_PGTABLE_H +#define _ASM_ARC_PGTABLE_H + +#include +#include +#include + +/************************************************************************** + * Page Table Flags + * + * ARC700 MMU only deals with softare managed TLB entries. + * Page Tables are purely for Linux VM's consumption and the bits below are + * suited to that (uniqueness). Hence some are not implemented in the TLB and + * some have different value in TLB. + * e.g. MMU v2: K_READ bit is 8 and so is GLOBAL (possible becoz they live in + * seperate PD0 and PD1, which combined forms a translation entry) + * while for PTE perspective, they are 8 and 9 respectively + * with MMU v3: Most bits (except SHARED) represent the exact hardware pos + * (saves some bit shift ops in TLB Miss hdlrs) + */ + +#if (CONFIG_ARC_MMU_VER <= 2) + +#define _PAGE_ACCESSED (1<<1) /* Page is accessed (S) */ +#define _PAGE_CACHEABLE (1<<2) /* Page is cached (H) */ +#define _PAGE_EXECUTE (1<<3) /* Page has user execute perm (H) */ +#define _PAGE_WRITE (1<<4) /* Page has user write perm (H) */ +#define _PAGE_READ (1<<5) /* Page has user read perm (H) */ +#define _PAGE_K_EXECUTE (1<<6) /* Page has kernel execute perm (H) */ +#define _PAGE_K_WRITE (1<<7) /* Page has kernel write perm (H) */ +#define _PAGE_K_READ (1<<8) /* Page has kernel perm (H) */ +#define _PAGE_GLOBAL (1<<9) /* Page is global (H) */ +#define _PAGE_MODIFIED (1<<10) /* Page modified (dirty) (S) */ +#define _PAGE_FILE (1<<10) /* page cache/ swap (S) */ +#define _PAGE_PRESENT (1<<11) /* TLB entry is valid (H) */ + +#else + +/* PD1 */ +#define _PAGE_CACHEABLE (1<<0) /* Page is cached (H) */ +#define _PAGE_EXECUTE (1<<1) /* Page has user execute perm (H) */ +#define _PAGE_WRITE (1<<2) /* Page has user write perm (H) */ +#define _PAGE_READ (1<<3) /* Page has user read perm (H) */ +#define _PAGE_K_EXECUTE (1<<4) /* Page has kernel execute perm (H) */ +#define _PAGE_K_WRITE (1<<5) /* Page has kernel write perm (H) */ +#define _PAGE_K_READ (1<<6) /* Page has kernel perm (H) */ +#define _PAGE_ACCESSED (1<<7) /* Page is accessed (S) */ + +/* PD0 */ +#define _PAGE_GLOBAL (1<<8) /* Page is global (H) */ +#define _PAGE_PRESENT (1<<9) /* TLB entry is valid (H) */ +#define _PAGE_SHARED_CODE (1<<10) /* Shared Code page with cmn vaddr + usable for shared TLB entries (H) */ + +#define _PAGE_MODIFIED (1<<11) /* Page modified (dirty) (S) */ +#define _PAGE_FILE (1<<12) /* page cache/ swap (S) */ + +#define _PAGE_SHARED_CODE_H (1<<31) /* Hardware counterpart of above */ +#endif + +/* Kernel allowed all permissions for all pages */ +#define _K_PAGE_PERMS (_PAGE_K_EXECUTE | _PAGE_K_WRITE | _PAGE_K_READ) + +#ifdef CONFIG_ARC_CACHE_PAGES +#define _PAGE_DEF_CACHEABLE _PAGE_CACHEABLE +#else +#define _PAGE_DEF_CACHEABLE (0) +#endif + +/* Helper for every "user" page + * -kernel can R/W/X + * -by default cached, unless config otherwise + * -present in memory + */ +#define ___DEF (_PAGE_PRESENT | _K_PAGE_PERMS | _PAGE_DEF_CACHEABLE) + +/* Set of bits not changed in pte_modify */ +#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_MODIFIED) + +/* More Abbrevaited helpers */ +#define PAGE_U_NONE __pgprot(___DEF) +#define PAGE_U_R __pgprot(___DEF | _PAGE_READ) +#define PAGE_U_W_R __pgprot(___DEF | _PAGE_READ | _PAGE_WRITE) +#define PAGE_U_X_R __pgprot(___DEF | _PAGE_READ | _PAGE_EXECUTE) +#define PAGE_U_X_W_R __pgprot(___DEF | _PAGE_READ | _PAGE_WRITE | \ + _PAGE_EXECUTE) + +#define PAGE_SHARED PAGE_U_W_R + +/* While kernel runs out of unstrslated space, vmalloc/modules use a chunk of + * kernel vaddr space - visible in all addr spaces, but kernel mode only + * Thus Global, all-kernel-access, no-user-access, cached + */ +#define PAGE_KERNEL __pgprot(___DEF | _PAGE_GLOBAL) + +/* ioremap */ +#define PAGE_KERNEL_NO_CACHE __pgprot(_PAGE_PRESENT | _K_PAGE_PERMS | \ + _PAGE_GLOBAL) + +/************************************************************************** + * Mapping of vm_flags (Generic VM) to PTE flags (arch specific) + * + * Certain cases have 1:1 mapping + * e.g. __P101 means VM_READ, VM_EXEC and !VM_SHARED + * which directly corresponds to PAGE_U_X_R + * + * Other rules which cause the divergence from 1:1 mapping + * + * 1. Although ARC700 can do exclusive execute/write protection (meaning R + * can be tracked independet of X/W unlike some other CPUs), still to + * keep things consistent with other archs: + * -Write implies Read: W => R + * -Execute implies Read: X => R + * + * 2. Pvt Writable doesn't have Write Enabled initially: Pvt-W => !W + * This is to enable COW mechanism + */ + /* xwr */ +#define __P000 PAGE_U_NONE +#define __P001 PAGE_U_R +#define __P010 PAGE_U_R /* Pvt-W => !W */ +#define __P011 PAGE_U_R /* Pvt-W => !W */ +#define __P100 PAGE_U_X_R /* X => R */ +#define __P101 PAGE_U_X_R +#define __P110 PAGE_U_X_R /* Pvt-W => !W and X => R */ +#define __P111 PAGE_U_X_R /* Pvt-W => !W */ + +#define __S000 PAGE_U_NONE +#define __S001 PAGE_U_R +#define __S010 PAGE_U_W_R /* W => R */ +#define __S011 PAGE_U_W_R +#define __S100 PAGE_U_X_R /* X => R */ +#define __S101 PAGE_U_X_R +#define __S110 PAGE_U_X_W_R /* X => R */ +#define __S111 PAGE_U_X_W_R + +/**************************************************************** + * Page Table Lookup split + * + * We implement 2 tier paging and since this is all software, we are free + * to customize the span of a PGD / PTE entry to suit us + * + * 32 bit virtual address + * ------------------------------------------------------- + * | BITS_FOR_PGD | BITS_FOR_PTE | BITS_IN_PAGE | + * ------------------------------------------------------- + * | | | + * | | --> off in page frame + * | | + * | ---> index into Page Table + * | + * ----> index into Page Directory + */ + +#define BITS_IN_PAGE PAGE_SHIFT + +/* Optimal Sizing of Pg Tbl - based on MMU page size */ +#if defined(CONFIG_ARC_PAGE_SIZE_8K) +#define BITS_FOR_PTE 8 +#elif defined(CONFIG_ARC_PAGE_SIZE_16K) +#define BITS_FOR_PTE 8 +#elif defined(CONFIG_ARC_PAGE_SIZE_4K) +#define BITS_FOR_PTE 9 +#endif + +#define BITS_FOR_PGD (32 - BITS_FOR_PTE - BITS_IN_PAGE) + +#define PGDIR_SHIFT (BITS_FOR_PTE + BITS_IN_PAGE) +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) /* vaddr span, not PDG sz */ +#define PGDIR_MASK (~(PGDIR_SIZE-1)) + +#ifdef __ASSEMBLY__ +#define PTRS_PER_PTE (1 << BITS_FOR_PTE) +#define PTRS_PER_PGD (1 << BITS_FOR_PGD) +#else +#define PTRS_PER_PTE (1UL << BITS_FOR_PTE) +#define PTRS_PER_PGD (1UL << BITS_FOR_PGD) +#endif +/* + * Number of entries a user land program use. + * TASK_SIZE is the maximum vaddr that can be used by a userland program. + */ +#define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE) + +/* + * No special requirements for lowest virtual address we permit any user space + * mapping to be mapped at. + */ +#define FIRST_USER_ADDRESS 0 + + +/**************************************************************** + * Bucket load of VM Helpers + */ + +#ifndef __ASSEMBLY__ + +#define pte_ERROR(e) \ + pr_crit("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) +#define pgd_ERROR(e) \ + pr_crit("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) + +/* the zero page used for uninitialized and anonymous pages */ +extern char empty_zero_page[PAGE_SIZE]; +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) + +#define pte_unmap(pte) do { } while (0) +#define pte_unmap_nested(pte) do { } while (0) + +#define set_pte(pteptr, pteval) ((*(pteptr)) = (pteval)) +#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval) + +/* find the page descriptor of the Page Tbl ref by PMD entry */ +#define pmd_page(pmd) virt_to_page(pmd_val(pmd) & PAGE_MASK) + +/* find the logical addr (phy for ARC) of the Page Tbl ref by PMD entry */ +#define pmd_page_vaddr(pmd) (pmd_val(pmd) & PAGE_MASK) + +/* In a 2 level sys, setup the PGD entry with PTE value */ +static inline void pmd_set(pmd_t *pmdp, pte_t *ptep) +{ + pmd_val(*pmdp) = (unsigned long)ptep; +} + +#define pte_none(x) (!pte_val(x)) +#define pte_present(x) (pte_val(x) & _PAGE_PRESENT) +#define pte_clear(mm, addr, ptep) set_pte_at(mm, addr, ptep, __pte(0)) + +#define pmd_none(x) (!pmd_val(x)) +#define pmd_bad(x) ((pmd_val(x) & ~PAGE_MASK)) +#define pmd_present(x) (pmd_val(x)) +#define pmd_clear(xp) do { pmd_val(*(xp)) = 0; } while (0) + +#define pte_page(x) (mem_map + \ + (unsigned long)(((pte_val(x) - PAGE_OFFSET) >> PAGE_SHIFT))) + +#define mk_pte(page, pgprot) \ +({ \ + pte_t pte; \ + pte_val(pte) = __pa(page_address(page)) + pgprot_val(pgprot); \ + pte; \ +}) + +/* TBD: Non linear mapping stuff */ +static inline int pte_file(pte_t pte) +{ + return pte_val(pte) & _PAGE_FILE; +} + +#define PTE_FILE_MAX_BITS 30 +#define pgoff_to_pte(x) __pte(x) +#define pte_to_pgoff(x) (pte_val(x) >> 2) +#define pte_pfn(pte) (pte_val(pte) >> PAGE_SHIFT) +#define pfn_pte(pfn, prot) (__pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot))) +#define __pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) + +/* + * pte_offset gets a @ptr to PMD entry (PGD in our 2-tier paging system) + * and returns ptr to PTE entry corresponding to @addr + */ +#define pte_offset(dir, addr) ((pte_t *)(pmd_page_vaddr(*dir)) +\ + __pte_index(addr)) + +/* No mapping of Page Tables in high mem etc, so following same as above */ +#define pte_offset_kernel(dir, addr) pte_offset(dir, addr) +#define pte_offset_map(dir, addr) pte_offset(dir, addr) + +/* Zoo of pte_xxx function */ +#define pte_read(pte) (pte_val(pte) & _PAGE_READ) +#define pte_write(pte) (pte_val(pte) & _PAGE_WRITE) +#define pte_dirty(pte) (pte_val(pte) & _PAGE_MODIFIED) +#define pte_young(pte) (pte_val(pte) & _PAGE_ACCESSED) +#define pte_special(pte) (0) + +#define PTE_BIT_FUNC(fn, op) \ + static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; } + +PTE_BIT_FUNC(wrprotect, &= ~(_PAGE_WRITE)); +PTE_BIT_FUNC(mkwrite, |= (_PAGE_WRITE)); +PTE_BIT_FUNC(mkclean, &= ~(_PAGE_MODIFIED)); +PTE_BIT_FUNC(mkdirty, |= (_PAGE_MODIFIED)); +PTE_BIT_FUNC(mkold, &= ~(_PAGE_ACCESSED)); +PTE_BIT_FUNC(mkyoung, |= (_PAGE_ACCESSED)); +PTE_BIT_FUNC(exprotect, &= ~(_PAGE_EXECUTE)); +PTE_BIT_FUNC(mkexec, |= (_PAGE_EXECUTE)); + +static inline pte_t pte_mkspecial(pte_t pte) { return pte; } + +static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) +{ + return __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot)); +} + +/* Macro to mark a page protection as uncacheable */ +#define pgprot_noncached(prot) (__pgprot(pgprot_val(prot) & ~_PAGE_CACHEABLE)) + +static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval) +{ + set_pte(ptep, pteval); +} + +/* + * All kernel related VM pages are in init's mm. + */ +#define pgd_offset_k(address) pgd_offset(&init_mm, address) +#define pgd_index(addr) ((addr) >> PGDIR_SHIFT) +#define pgd_offset(mm, addr) (((mm)->pgd)+pgd_index(addr)) + +/* + * Macro to quickly access the PGD entry, utlising the fact that some + * arch may cache the pointer to Page Directory of "current" task + * in a MMU register + * + * Thus task->mm->pgd (3 pointer dereferences, cache misses etc simply + * becomes read a register + * + * ********CAUTION*******: + * Kernel code might be dealing with some mm_struct of NON "current" + * Thus use this macro only when you are certain that "current" is current + * e.g. when dealing with signal frame setup code etc + */ +#define pgd_offset_fast(mm, addr) \ +({ \ + pgd_t *pgd_base = (pgd_t *) read_aux_reg(ARC_REG_SCRATCH_DATA0); \ + pgd_base + pgd_index(addr); \ +}) + +extern void paging_init(void); +extern pgd_t swapper_pg_dir[] __aligned(PAGE_SIZE); +void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, + pte_t *ptep); + +/* Encode swap {type,off} tuple into PTE + * We reserve 13 bits for 5-bit @type, keeping bits 12-5 zero, ensuring that + * both PAGE_FILE and PAGE_PRESENT are zero in a PTE holding swap "identifier" + */ +#define __swp_entry(type, off) ((swp_entry_t) { \ + ((type) & 0x1f) | ((off) << 13) }) + +/* Decode a PTE containing swap "identifier "into constituents */ +#define __swp_type(pte_lookalike) (((pte_lookalike).val) & 0x1f) +#define __swp_offset(pte_lookalike) ((pte_lookalike).val << 13) + +/* NOPs, to keep generic kernel happy */ +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) +#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) + +#define kern_addr_valid(addr) (1) + +/* + * remap a physical page `pfn' of size `size' with page protection `prot' + * into virtual address `from' + */ +#define io_remap_pfn_range(vma, from, pfn, size, prot) \ + remap_pfn_range(vma, from, pfn, size, prot) + +#include + +/* + * No page table caches to initialise + */ +#define pgtable_cache_init() do { } while (0) + +#endif /* __ASSEMBLY__ */ + +#endif From f1f3347da9440eedd2350f4f5d13d8860f570b92 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:19 +0530 Subject: [PATCH 2819/4607] ARC: MMU Context Management ARC700 MMU provides for tagging TLB entries with a 8-bit ASID to avoid having to flush the TLB every task switch. It also allows for a quick way to invalidate all the TLB entries for task useful for: * COW sementics during fork() * task exit()ing Signed-off-by: Vineet Gupta --- arch/arc/include/asm/arcregs.h | 7 + arch/arc/include/asm/mmu.h | 23 ++++ arch/arc/include/asm/mmu_context.h | 209 +++++++++++++++++++++++++++++ arch/arc/mm/tlb.c | 23 ++++ 4 files changed, 262 insertions(+) create mode 100644 arch/arc/include/asm/mmu.h create mode 100644 arch/arc/include/asm/mmu_context.h create mode 100644 arch/arc/mm/tlb.c diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index c6e28053fb70..c12eb9b4f449 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -85,6 +85,13 @@ #define DC_CTRL_INV_MODE_FLUSH 0x40 #define DC_CTRL_FLUSH_STATUS 0x100 +/* MMU Management regs */ +#define ARC_REG_PID 0x409 +#define ARC_REG_SCRATCH_DATA0 0x418 + +/* Bits in MMU PID register */ +#define MMU_ENABLE (1 << 31) /* Enable MMU for process */ + /* * Floating Pt Registers * Status regs are read-only (build-time) so need not be saved/restored diff --git a/arch/arc/include/asm/mmu.h b/arch/arc/include/asm/mmu.h new file mode 100644 index 000000000000..56b02320f1a9 --- /dev/null +++ b/arch/arc/include/asm/mmu.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_MMU_H +#define _ASM_ARC_MMU_H + +#ifndef __ASSEMBLY__ + +typedef struct { + unsigned long asid; /* Pvt Addr-Space ID for mm */ +#ifdef CONFIG_ARC_TLB_DBG + struct task_struct *tsk; +#endif +} mm_context_t; + +#endif + +#endif diff --git a/arch/arc/include/asm/mmu_context.h b/arch/arc/include/asm/mmu_context.h new file mode 100644 index 000000000000..d12f3dec8b70 --- /dev/null +++ b/arch/arc/include/asm/mmu_context.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: May 2011 + * -Refactored get_new_mmu_context( ) to only handle live-mm. + * retiring-mm handled in other hooks + * + * Vineetg: March 25th, 2008: Bug #92690 + * -Major rewrite of Core ASID allocation routine get_new_mmu_context + * + * Amit Bhor, Sameer Dhavale: Codito Technologies 2004 + */ + +#ifndef _ASM_ARC_MMU_CONTEXT_H +#define _ASM_ARC_MMU_CONTEXT_H + +#include +#include + +#include + +/* ARC700 ASID Management + * + * ARC MMU provides 8-bit ASID (0..255) to TAG TLB entries, allowing entries + * with same vaddr (different tasks) to co-exit. This provides for + * "Fast Context Switch" i.e. no TLB flush on ctxt-switch + * + * Linux assigns each task a unique ASID. A simple round-robin allocation + * of H/w ASID is done using software tracker @asid_cache. + * When it reaches max 255, the allocation cycle starts afresh by flushing + * the entire TLB and wrapping ASID back to zero. + * + * For book-keeping, Linux uses a couple of data-structures: + * -mm_struct has an @asid field to keep a note of task's ASID (needed at the + * time of say switch_mm( ) + * -An array of mm structs @asid_mm_map[] for asid->mm the reverse mapping, + * given an ASID, finding the mm struct associated. + * + * The round-robin allocation algorithm allows for ASID stealing. + * If asid tracker is at "x-1", a new req will allocate "x", even if "x" was + * already assigned to another (switched-out) task. Obviously the prev owner + * is marked with an invalid ASID to make it request for a new ASID when it + * gets scheduled next time. However its TLB entries (with ASID "x") could + * exist, which must be cleared before the same ASID is used by the new owner. + * Flushing them would be plausible but costly solution. Instead we force a + * allocation policy quirk, which ensures that a stolen ASID won't have any + * TLB entries associates, alleviating the need to flush. + * The quirk essentially is not allowing ASID allocated in prev cycle + * to be used past a roll-over in the next cycle. + * When this happens (i.e. task ASID > asid tracker), task needs to refresh + * its ASID, aligning it to current value of tracker. If the task doesn't get + * scheduled past a roll-over, hence its ASID is not yet realigned with + * tracker, such ASID is anyways safely reusable because it is + * gauranteed that TLB entries with that ASID wont exist. + */ + +#define FIRST_ASID 0 +#define MAX_ASID 255 /* 8 bit PID field in PID Aux reg */ +#define NO_ASID (MAX_ASID + 1) /* ASID Not alloc to mmu ctxt */ +#define NUM_ASID ((MAX_ASID - FIRST_ASID) + 1) + +/* ASID to mm struct mapping */ +extern struct mm_struct *asid_mm_map[NUM_ASID + 1]; + +extern int asid_cache; + +/* + * Assign a new ASID to task. If the task already has an ASID, it is + * relinquished. + */ +static inline void get_new_mmu_context(struct mm_struct *mm) +{ + struct mm_struct *prev_owner; + unsigned long flags; + + local_irq_save(flags); + + /* + * Relinquish the currently owned ASID (if any). + * Doing unconditionally saves a cmp-n-branch; for already unused + * ASID slot, the value was/remains NULL + */ + asid_mm_map[mm->context.asid] = (struct mm_struct *)NULL; + + /* move to new ASID */ + if (++asid_cache > MAX_ASID) { /* ASID roll-over */ + asid_cache = FIRST_ASID; + flush_tlb_all(); + } + + /* + * Is next ASID already owned by some-one else (we are stealing it). + * If so, let the orig owner be aware of this, so when it runs, it + * asks for a brand new ASID. This would only happen for a long-lived + * task with ASID from prev allocation cycle (before ASID roll-over). + * + * This might look wrong - if we are re-using some other task's ASID, + * won't we use it's stale TLB entries too. Actually switch_mm( ) takes + * care of such a case: it ensures that task with ASID from prev alloc + * cycle, when scheduled will refresh it's ASID: see switch_mm( ) below + * The stealing scenario described here will only happen if that task + * didn't get a chance to refresh it's ASID - implying stale entries + * won't exist. + */ + prev_owner = asid_mm_map[asid_cache]; + if (prev_owner) + prev_owner->context.asid = NO_ASID; + + /* Assign new ASID to tsk */ + asid_mm_map[asid_cache] = mm; + mm->context.asid = asid_cache; + +#ifdef CONFIG_ARC_TLB_DBG + pr_info("ARC_TLB_DBG: NewMM=0x%x OldMM=0x%x task_struct=0x%x Task: %s," + " pid:%u, assigned asid:%lu\n", + (unsigned int)mm, (unsigned int)prev_owner, + (unsigned int)(mm->context.tsk), (mm->context.tsk)->comm, + (mm->context.tsk)->pid, mm->context.asid); +#endif + + write_aux_reg(ARC_REG_PID, asid_cache | MMU_ENABLE); + + local_irq_restore(flags); +} + +/* + * Initialize the context related info for a new mm_struct + * instance. + */ +static inline int +init_new_context(struct task_struct *tsk, struct mm_struct *mm) +{ + mm->context.asid = NO_ASID; +#ifdef CONFIG_ARC_TLB_DBG + mm->context.tsk = tsk; +#endif + return 0; +} + +/* Prepare the MMU for task: setup PID reg with allocated ASID + If task doesn't have an ASID (never alloc or stolen, get a new ASID) +*/ +static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk) +{ + /* PGD cached in MMU reg to avoid 3 mem lookups: task->mm->pgd */ + write_aux_reg(ARC_REG_SCRATCH_DATA0, next->pgd); + + /* + * Get a new ASID if task doesn't have a valid one. Possible when + * -task never had an ASID (fresh after fork) + * -it's ASID was stolen - past an ASID roll-over. + * -There's a third obscure scenario (if this task is running for the + * first time afer an ASID rollover), where despite having a valid + * ASID, we force a get for new ASID - see comments at top. + * + * Both the non-alloc scenario and first-use-after-rollover can be + * detected using the single condition below: NO_ASID = 256 + * while asid_cache is always a valid ASID value (0-255). + */ + if (next->context.asid > asid_cache) { + get_new_mmu_context(next); + } else { + /* + * XXX: This will never happen given the chks above + * BUG_ON(next->context.asid > MAX_ASID); + */ + write_aux_reg(ARC_REG_PID, next->context.asid | MMU_ENABLE); + } + +} + +static inline void destroy_context(struct mm_struct *mm) +{ + unsigned long flags; + + local_irq_save(flags); + + asid_mm_map[mm->context.asid] = NULL; + mm->context.asid = NO_ASID; + + local_irq_restore(flags); +} + +/* it seemed that deactivate_mm( ) is a reasonable place to do book-keeping + * for retiring-mm. However destroy_context( ) still needs to do that because + * between mm_release( ) = >deactive_mm( ) and + * mmput => .. => __mmdrop( ) => destroy_context( ) + * there is a good chance that task gets sched-out/in, making it's ASID valid + * again (this teased me for a whole day). + */ +#define deactivate_mm(tsk, mm) do { } while (0) + +static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next) +{ + write_aux_reg(ARC_REG_SCRATCH_DATA0, next->pgd); + + /* Unconditionally get a new ASID */ + get_new_mmu_context(next); + +} + +#define enter_lazy_tlb(mm, tsk) + +#endif /* __ASM_ARC_MMU_CONTEXT_H */ diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c new file mode 100644 index 000000000000..f1edae2410a7 --- /dev/null +++ b/arch/arc/mm/tlb.c @@ -0,0 +1,23 @@ +/* + * TLB Management (flush/create/diagnostics) for ARC700 + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include + +/* A copy of the ASID from the PID reg is kept in asid_cache */ +int asid_cache = FIRST_ASID; + +/* ASID to mm struct mapping. We have one extra entry corresponding to + * NO_ASID to save us a compare when clearing the mm entry for old asid + * see get_new_mmu_context (asm-arc/mmu_context.h) + */ +struct mm_struct *asid_mm_map[NUM_ASID + 1]; From cc562d2eae93bc2768a6575d31c089719e8939e8 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:19 +0530 Subject: [PATCH 2820/4607] ARC: MMU Exception Handling * MMU I-TLB / D-TLB Miss Exceptions - Fast Path TLB Refill Handler - slowpath TLB creation via do_page_fault() -> update_mmu_cache() * Duplicate PD Exception Handler Signed-off-by: Vineet Gupta --- arch/arc/include/asm/arcregs.h | 91 +++++++++ arch/arc/include/asm/tlb-mmu1.h | 104 ++++++++++ arch/arc/include/asm/tlb.h | 41 ++++ arch/arc/mm/tlb.c | 267 ++++++++++++++++++++++++ arch/arc/mm/tlbex.S | 351 ++++++++++++++++++++++++++++++++ 5 files changed, 854 insertions(+) create mode 100644 arch/arc/include/asm/tlb-mmu1.h create mode 100644 arch/arc/include/asm/tlb.h create mode 100644 arch/arc/mm/tlbex.S diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index c12eb9b4f449..1c24485fd04b 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -13,6 +13,7 @@ /* Build Configuration Registers */ #define ARC_REG_VECBASE_BCR 0x68 +#define ARC_REG_MMU_BCR 0x6f /* status32 Bits Positions */ #define STATUS_H_BIT 0 /* CPU Halted */ @@ -36,6 +37,35 @@ #define STATUS_U_MASK (1<= 2) +#define TLBWriteNI 0x5 /* write JTLB without inv uTLBs */ +#define TLBIVUTLB 0x6 /* explicitly inv uTLBs */ +#else +#undef TLBWriteNI /* These cmds don't exist on older MMU */ +#undef TLBIVUTLB +#endif + /* Instruction cache related Auxiliary registers */ #define ARC_REG_IC_BCR 0x77 /* Build Config reg */ #define ARC_REG_IC_IVIC 0x10 @@ -205,6 +273,24 @@ struct arc_fpu { * Build Configuration Registers, with encoded hardware config */ +struct bcr_mmu_1_2 { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int ver:8, ways:4, sets:4, u_itlb:8, u_dtlb:8; +#else + unsigned int u_dtlb:8, u_itlb:8, sets:4, ways:4, ver:8; +#endif +}; + +struct bcr_mmu_3 { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int ver:8, ways:4, sets:4, osm:1, reserv:3, pg_sz:4, + u_itlb:4, u_dtlb:4; +#else + unsigned int u_dtlb:4, u_itlb:4, pg_sz:4, reserv:3, osm:1, sets:4, + ways:4, ver:8; +#endif +}; + struct bcr_cache { #ifdef CONFIG_CPU_BIG_ENDIAN unsigned int pad:12, line_len:4, sz:4, config:4, ver:8; @@ -218,12 +304,17 @@ struct bcr_cache { * Generic structures to hold build configuration used at runtime */ +struct cpuinfo_arc_mmu { + unsigned int ver, pg_sz, sets, ways, u_dtlb, u_itlb, num_tlb; +}; + struct cpuinfo_arc_cache { unsigned int has_aliasing, sz, line_len, assoc, ver; }; struct cpuinfo_arc { struct cpuinfo_arc_cache icache, dcache; + struct cpuinfo_arc_mmu mmu; }; extern struct cpuinfo_arc cpuinfo_arc700[]; diff --git a/arch/arc/include/asm/tlb-mmu1.h b/arch/arc/include/asm/tlb-mmu1.h new file mode 100644 index 000000000000..a5ff961b1efc --- /dev/null +++ b/arch/arc/include/asm/tlb-mmu1.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_TLB_MMU_V1_H__ +#define __ASM_TLB_MMU_V1_H__ + +#if defined(__ASSEMBLY__) && defined(CONFIG_ARC_MMU_VER == 1) + +#include + +.macro TLB_WRITE_HEURISTICS + +#define JH_HACK1 +#undef JH_HACK2 +#undef JH_HACK3 + +#ifdef JH_HACK3 +; Calculate set index for 2-way MMU +; -avoiding use of GetIndex from MMU +; and its unpleasant LFSR pseudo-random sequence +; +; r1 = TLBPD0 from TLB_RELOAD above +; +; -- jh_ex_way_set not cleared on startup +; didn't want to change setup.c +; hence extra instruction to clean +; +; -- should be in cache since in same line +; as r0/r1 saves above +; +ld r0,[jh_ex_way_sel] ; victim pointer +and r0,r0,1 ; clean +xor.f r0,r0,1 ; flip +st r0,[jh_ex_way_sel] ; store back +asr r0,r1,12 ; get set # <<1, note bit 12=R=0 +or.nz r0,r0,1 ; set way bit +and r0,r0,0xff ; clean +sr r0,[ARC_REG_TLBINDEX] +#endif + +#ifdef JH_HACK2 +; JH hack #2 +; Faster than hack #1 in non-thrash case, but hard-coded for 2-way MMU +; Slower in thrash case (where it matters) because more code is executed +; Inefficient due to two-register paradigm of this miss handler +; +/* r1 = data TLBPD0 at this point */ +lr r0,[eret] /* instruction address */ +xor r0,r0,r1 /* compare set # */ +and.f r0,r0,0x000fe000 /* 2-way MMU mask */ +bne 88f /* not in same set - no need to probe */ + +lr r0,[eret] /* instruction address */ +and r0,r0,PAGE_MASK /* VPN of instruction address */ +; lr r1,[ARC_REG_TLBPD0] /* Data VPN+ASID - already in r1 from TLB_RELOAD*/ +and r1,r1,0xff /* Data ASID */ +or r0,r0,r1 /* Instruction address + Data ASID */ + +lr r1,[ARC_REG_TLBPD0] /* save TLBPD0 containing data TLB*/ +sr r0,[ARC_REG_TLBPD0] /* write instruction address to TLBPD0 */ +sr TLBProbe, [ARC_REG_TLBCOMMAND] /* Look for instruction */ +lr r0,[ARC_REG_TLBINDEX] /* r0 = index where instruction is, if at all */ +sr r1,[ARC_REG_TLBPD0] /* restore TLBPD0 */ + +xor r0,r0,1 /* flip bottom bit of data index */ +b.d 89f +sr r0,[ARC_REG_TLBINDEX] /* and put it back */ +88: +sr TLBGetIndex, [ARC_REG_TLBCOMMAND] +89: +#endif + +#ifdef JH_HACK1 +; +; Always checks whether instruction will be kicked out by dtlb miss +; +mov_s r3, r1 ; save PD0 prepared by TLB_RELOAD in r3 +lr r0,[eret] /* instruction address */ +and r0,r0,PAGE_MASK /* VPN of instruction address */ +bmsk r1,r3,7 /* Data ASID, bits 7-0 */ +or_s r0,r0,r1 /* Instruction address + Data ASID */ + +sr r0,[ARC_REG_TLBPD0] /* write instruction address to TLBPD0 */ +sr TLBProbe, [ARC_REG_TLBCOMMAND] /* Look for instruction */ +lr r0,[ARC_REG_TLBINDEX] /* r0 = index where instruction is, if at all */ +sr r3,[ARC_REG_TLBPD0] /* restore TLBPD0 */ + +sr TLBGetIndex, [ARC_REG_TLBCOMMAND] +lr r1,[ARC_REG_TLBINDEX] /* r1 = index where MMU wants to put data */ +cmp r0,r1 /* if no match on indices, go around */ +xor.eq r1,r1,1 /* flip bottom bit of data index */ +sr r1,[ARC_REG_TLBINDEX] /* and put it back */ +#endif + +.endm + +#endif + +#endif diff --git a/arch/arc/include/asm/tlb.h b/arch/arc/include/asm/tlb.h new file mode 100644 index 000000000000..b571e121929b --- /dev/null +++ b/arch/arc/include/asm/tlb.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_TLB_H +#define _ASM_ARC_TLB_H + +#ifdef __KERNEL__ + +#include + +/* Masks for actual TLB "PD"s */ +#define PTE_BITS_IN_PD0 (_PAGE_GLOBAL | _PAGE_PRESENT) +#define PTE_BITS_IN_PD1 (PAGE_MASK | _PAGE_CACHEABLE | \ + _PAGE_EXECUTE | _PAGE_WRITE | _PAGE_READ | \ + _PAGE_K_EXECUTE | _PAGE_K_WRITE | _PAGE_K_READ) + +#ifndef __ASSEMBLY__ + +#include +#include + +#ifdef CONFIG_ARC_DBG_TLB_PARANOIA +void tlb_paranoid_check(unsigned int pid_sw, unsigned long address); +#else +#define tlb_paranoid_check(a, b) +#endif + +void arc_mmu_init(void); +extern char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len); +void __init read_decode_mmu_bcr(void); + +#endif /* __ASSEMBLY__ */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_ARC_TLB_H */ diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c index f1edae2410a7..404e5be4f704 100644 --- a/arch/arc/mm/tlb.c +++ b/arch/arc/mm/tlb.c @@ -21,3 +21,270 @@ int asid_cache = FIRST_ASID; * see get_new_mmu_context (asm-arc/mmu_context.h) */ struct mm_struct *asid_mm_map[NUM_ASID + 1]; + + +/* + * Routine to create a TLB entry + */ +void create_tlb(struct vm_area_struct *vma, unsigned long address, pte_t *ptep) +{ + unsigned long flags; + unsigned int idx, asid_or_sasid; + unsigned long pd0_flags; + + /* + * create_tlb() assumes that current->mm == vma->mm, since + * -it ASID for TLB entry is fetched from MMU ASID reg (valid for curr) + * -completes the lazy write to SASID reg (again valid for curr tsk) + * + * Removing the assumption involves + * -Using vma->mm->context{ASID,SASID}, as opposed to MMU reg. + * -Fix the TLB paranoid debug code to not trigger false negatives. + * -More importantly it makes this handler inconsistent with fast-path + * TLB Refill handler which always deals with "current" + * + * Lets see the use cases when current->mm != vma->mm and we land here + * 1. execve->copy_strings()->__get_user_pages->handle_mm_fault + * Here VM wants to pre-install a TLB entry for user stack while + * current->mm still points to pre-execve mm (hence the condition). + * However the stack vaddr is soon relocated (randomization) and + * move_page_tables() tries to undo that TLB entry. + * Thus not creating TLB entry is not any worse. + * + * 2. ptrace(POKETEXT) causes a CoW - debugger(current) inserting a + * breakpoint in debugged task. Not creating a TLB now is not + * performance critical. + * + * Both the cases above are not good enough for code churn. + */ + if (current->active_mm != vma->vm_mm) + return; + + local_irq_save(flags); + + tlb_paranoid_check(vma->vm_mm->context.asid, address); + + address &= PAGE_MASK; + + /* update this PTE credentials */ + pte_val(*ptep) |= (_PAGE_PRESENT | _PAGE_ACCESSED); + + /* Create HW TLB entry Flags (in PD0) from PTE Flags */ +#if (CONFIG_ARC_MMU_VER <= 2) + pd0_flags = ((pte_val(*ptep) & PTE_BITS_IN_PD0) >> 1); +#else + pd0_flags = ((pte_val(*ptep) & PTE_BITS_IN_PD0)); +#endif + + /* ASID for this task */ + asid_or_sasid = read_aux_reg(ARC_REG_PID) & 0xff; + + write_aux_reg(ARC_REG_TLBPD0, address | pd0_flags | asid_or_sasid); + + /* Load remaining info in PD1 (Page Frame Addr and Kx/Kw/Kr Flags) */ + write_aux_reg(ARC_REG_TLBPD1, (pte_val(*ptep) & PTE_BITS_IN_PD1)); + + /* First verify if entry for this vaddr+ASID already exists */ + write_aux_reg(ARC_REG_TLBCOMMAND, TLBProbe); + idx = read_aux_reg(ARC_REG_TLBINDEX); + + /* + * If Not already present get a free slot from MMU. + * Otherwise, Probe would have located the entry and set INDEX Reg + * with existing location. This will cause Write CMD to over-write + * existing entry with new PD0 and PD1 + */ + if (likely(idx & TLB_LKUP_ERR)) + write_aux_reg(ARC_REG_TLBCOMMAND, TLBGetIndex); + + /* + * Commit the Entry to MMU + * It doesnt sound safe to use the TLBWriteNI cmd here + * which doesn't flush uTLBs. I'd rather be safe than sorry. + */ + write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite); + + local_irq_restore(flags); +} + +/* arch hook called by core VM at the end of handle_mm_fault( ), + * when a new PTE is entered in Page Tables or an existing one + * is modified. We aggresively pre-install a TLB entry + */ + +void update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddress, + pte_t *ptep) +{ + + create_tlb(vma, vaddress, ptep); +} + +/* Read the Cache Build Confuration Registers, Decode them and save into + * the cpuinfo structure for later use. + * No Validation is done here, simply read/convert the BCRs + */ +void __init read_decode_mmu_bcr(void) +{ + unsigned int tmp; + struct bcr_mmu_1_2 *mmu2; /* encoded MMU2 attr */ + struct bcr_mmu_3 *mmu3; /* encoded MMU3 attr */ + struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; + + tmp = read_aux_reg(ARC_REG_MMU_BCR); + mmu->ver = (tmp >> 24); + + if (mmu->ver <= 2) { + mmu2 = (struct bcr_mmu_1_2 *)&tmp; + mmu->pg_sz = PAGE_SIZE; + mmu->sets = 1 << mmu2->sets; + mmu->ways = 1 << mmu2->ways; + mmu->u_dtlb = mmu2->u_dtlb; + mmu->u_itlb = mmu2->u_itlb; + } else { + mmu3 = (struct bcr_mmu_3 *)&tmp; + mmu->pg_sz = 512 << mmu3->pg_sz; + mmu->sets = 1 << mmu3->sets; + mmu->ways = 1 << mmu3->ways; + mmu->u_dtlb = mmu3->u_dtlb; + mmu->u_itlb = mmu3->u_itlb; + } + + mmu->num_tlb = mmu->sets * mmu->ways; +} + +void __init arc_mmu_init(void) +{ + /* + * ASID mgmt data structures are compile time init + * asid_cache = FIRST_ASID and asid_mm_map[] all zeroes + */ + + local_flush_tlb_all(); + + /* Enable the MMU */ + write_aux_reg(ARC_REG_PID, MMU_ENABLE); +} + +/* + * TLB Programmer's Model uses Linear Indexes: 0 to {255, 511} for 128 x {2,4} + * The mapping is Column-first. + * --------------------- ----------- + * |way0|way1|way2|way3| |way0|way1| + * --------------------- ----------- + * [set0] | 0 | 1 | 2 | 3 | | 0 | 1 | + * [set1] | 4 | 5 | 6 | 7 | | 2 | 3 | + * ~ ~ ~ ~ + * [set127] | 508| 509| 510| 511| | 254| 255| + * --------------------- ----------- + * For normal operations we don't(must not) care how above works since + * MMU cmd getIndex(vaddr) abstracts that out. + * However for walking WAYS of a SET, we need to know this + */ +#define SET_WAY_TO_IDX(mmu, set, way) ((set) * mmu->ways + (way)) + +/* Handling of Duplicate PD (TLB entry) in MMU. + * -Could be due to buggy customer tapeouts or obscure kernel bugs + * -MMU complaints not at the time of duplicate PD installation, but at the + * time of lookup matching multiple ways. + * -Ideally these should never happen - but if they do - workaround by deleting + * the duplicate one. + * -Knob to be verbose abt it.(TODO: hook them up to debugfs) + */ +volatile int dup_pd_verbose = 1;/* Be slient abt it or complain (default) */ + +void do_tlb_overlap_fault(unsigned long cause, unsigned long address, + struct pt_regs *regs) +{ + int set, way, n; + unsigned int pd0[4], pd1[4]; /* assume max 4 ways */ + unsigned long flags, is_valid; + struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; + + local_irq_save(flags); + + /* re-enable the MMU */ + write_aux_reg(ARC_REG_PID, MMU_ENABLE | read_aux_reg(ARC_REG_PID)); + + /* loop thru all sets of TLB */ + for (set = 0; set < mmu->sets; set++) { + + /* read out all the ways of current set */ + for (way = 0, is_valid = 0; way < mmu->ways; way++) { + write_aux_reg(ARC_REG_TLBINDEX, + SET_WAY_TO_IDX(mmu, set, way)); + write_aux_reg(ARC_REG_TLBCOMMAND, TLBRead); + pd0[way] = read_aux_reg(ARC_REG_TLBPD0); + pd1[way] = read_aux_reg(ARC_REG_TLBPD1); + is_valid |= pd0[way] & _PAGE_PRESENT; + } + + /* If all the WAYS in SET are empty, skip to next SET */ + if (!is_valid) + continue; + + /* Scan the set for duplicate ways: needs a nested loop */ + for (way = 0; way < mmu->ways; way++) { + if (!pd0[way]) + continue; + + for (n = way + 1; n < mmu->ways; n++) { + if ((pd0[way] & PAGE_MASK) == + (pd0[n] & PAGE_MASK)) { + + if (dup_pd_verbose) { + pr_info("Duplicate PD's @" + "[%d:%d]/[%d:%d]\n", + set, way, set, n); + pr_info("TLBPD0[%u]: %08x\n", + way, pd0[way]); + } + + /* + * clear entry @way and not @n. This is + * critical to our optimised loop + */ + pd0[way] = pd1[way] = 0; + write_aux_reg(ARC_REG_TLBINDEX, + SET_WAY_TO_IDX(mmu, set, way)); + __tlb_entry_erase(); + } + } + } + } + + local_irq_restore(flags); +} + +/*********************************************************************** + * Diagnostic Routines + * -Called from Low Level TLB Hanlders if things don;t look good + **********************************************************************/ + +#ifdef CONFIG_ARC_DBG_TLB_PARANOIA + +/* + * Low Level ASM TLB handler calls this if it finds that HW and SW ASIDS + * don't match + */ +void print_asid_mismatch(int is_fast_path) +{ + int pid_sw, pid_hw; + pid_sw = current->active_mm->context.asid; + pid_hw = read_aux_reg(ARC_REG_PID) & 0xff; + + pr_emerg("ASID Mismatch in %s Path Handler: sw-pid=0x%x hw-pid=0x%x\n", + is_fast_path ? "Fast" : "Slow", pid_sw, pid_hw); + + __asm__ __volatile__("flag 1"); +} + +void tlb_paranoid_check(unsigned int pid_sw, unsigned long addr) +{ + unsigned int pid_hw; + + pid_hw = read_aux_reg(ARC_REG_PID) & 0xff; + + if (addr < 0x70000000 && ((pid_hw != pid_sw) || (pid_sw == NO_ASID))) + print_asid_mismatch(0); +} +#endif diff --git a/arch/arc/mm/tlbex.S b/arch/arc/mm/tlbex.S new file mode 100644 index 000000000000..fc5b97129e55 --- /dev/null +++ b/arch/arc/mm/tlbex.S @@ -0,0 +1,351 @@ +/* + * TLB Exception Handling for ARC + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Vineetg: April 2011 : + * -MMU v1: moved out legacy code into a seperate file + * -MMU v3: PD{0,1} bits layout changed: They don't overlap anymore, + * helps avoid a shift when preparing PD0 from PTE + * + * Vineetg: July 2009 + * -For MMU V2, we need not do heuristics at the time of commiting a D-TLB + * entry, so that it doesn't knock out it's I-TLB entry + * -Some more fine tuning: + * bmsk instead of add, asl.cc instead of branch, delay slot utilise etc + * + * Vineetg: July 2009 + * -Practically rewrote the I/D TLB Miss handlers + * Now 40 and 135 instructions a peice as compared to 131 and 449 resp. + * Hence Leaner by 1.5 K + * Used Conditional arithmetic to replace excessive branching + * Also used short instructions wherever possible + * + * Vineetg: Aug 13th 2008 + * -Passing ECR (Exception Cause REG) to do_page_fault( ) for printing + * more information in case of a Fatality + * + * Vineetg: March 25th Bug #92690 + * -Added Debug Code to check if sw-ASID == hw-ASID + + * Rahul Trivedi, Amit Bhor: Codito Technologies 2004 + */ + + .cpu A7 + +#include +#include +#include +#include +#include +#include +#include +#if (CONFIG_ARC_MMU_VER == 1) +#include +#endif + +;-------------------------------------------------------------------------- +; scratch memory to save the registers (r0-r3) used to code TLB refill Handler +; For details refer to comments before TLBMISS_FREEUP_REGS below +;-------------------------------------------------------------------------- + + .section .data + .global ex_saved_reg1 + .align 1 << L1_CACHE_SHIFT ; IMP: Must be Cache Line aligned + .type ex_saved_reg1, @object + .size ex_saved_reg1, 16 +ex_saved_reg1: + .zero 16 + +;============================================================================ +; Troubleshooting Stuff +;============================================================================ + +; Linux keeps ASID (Address Space ID) in task->active_mm->context.asid +; When Creating TLB Entries, instead of doing 3 dependent loads from memory, +; we use the MMU PID Reg to get current ASID. +; In bizzare scenrios SW and HW ASID can get out-of-sync which is trouble. +; So we try to detect this in TLB Mis shandler + + +.macro DBG_ASID_MISMATCH + +#ifdef CONFIG_ARC_DBG_TLB_PARANOIA + + ; make sure h/w ASID is same as s/w ASID + + GET_CURR_TASK_ON_CPU r3 + ld r0, [r3, TASK_ACT_MM] + ld r0, [r0, MM_CTXT+MM_CTXT_ASID] + + lr r1, [ARC_REG_PID] + and r1, r1, 0xFF + breq r1, r0, 5f + + ; Error if H/w and S/w ASID don't match, but NOT if in kernel mode + lr r0, [erstatus] + bbit0 r0, STATUS_U_BIT, 5f + + ; We sure are in troubled waters, Flag the error, but to do so + ; need to switch to kernel mode stack to call error routine + GET_TSK_STACK_BASE r3, sp + + ; Call printk to shoutout aloud + mov r0, 1 + j print_asid_mismatch + +5: ; ASIDs match so proceed normally + nop + +#endif + +.endm + +;============================================================================ +;TLB Miss handling Code +;============================================================================ + +;----------------------------------------------------------------------------- +; This macro does the page-table lookup for the faulting address. +; OUT: r0 = PTE faulted on, r1 = ptr to PTE, r2 = Faulting V-address +.macro LOAD_FAULT_PTE + + lr r2, [efa] + + lr r1, [ARC_REG_SCRATCH_DATA0] ; current pgd + + lsr r0, r2, PGDIR_SHIFT ; Bits for indexing into PGD + ld.as r1, [r1, r0] ; PGD entry corresp to faulting addr + and.f r1, r1, PAGE_MASK ; Ignoring protection and other flags + ; contains Ptr to Page Table + bz.d do_slow_path_pf ; if no Page Table, do page fault + + ; Get the PTE entry: The idea is + ; (1) x = addr >> PAGE_SHIFT -> masks page-off bits from @fault-addr + ; (2) y = x & (PTRS_PER_PTE - 1) -> to get index + ; (3) z = pgtbl[y] + ; To avoid the multiply by in end, we do the -2, <<2 below + + lsr r0, r2, (PAGE_SHIFT - 2) + and r0, r0, ( (PTRS_PER_PTE - 1) << 2) + ld.aw r0, [r1, r0] ; get PTE and PTE ptr for fault addr + +.endm + +;----------------------------------------------------------------- +; Convert Linux PTE entry into TLB entry +; A one-word PTE entry is programmed as two-word TLB Entry [PD0:PD1] in mmu +; IN: r0 = PTE, r1 = ptr to PTE + +.macro CONV_PTE_TO_TLB + and r3, r0, PTE_BITS_IN_PD1 ; Extract permission flags+PFN from PTE + sr r3, [ARC_REG_TLBPD1] ; these go in PD1 + + and r2, r0, PTE_BITS_IN_PD0 ; Extract other PTE flags: (V)alid, (G)lb +#if (CONFIG_ARC_MMU_VER <= 2) /* Neednot be done with v3 onwards */ + lsr r2, r2 ; shift PTE flags to match layout in PD0 +#endif + + lr r3,[ARC_REG_TLBPD0] ; MMU prepares PD0 with vaddr and asid + + or r3, r3, r2 ; S | vaddr | {sasid|asid} + sr r3,[ARC_REG_TLBPD0] ; rewrite PD0 +.endm + +;----------------------------------------------------------------- +; Commit the TLB entry into MMU + +.macro COMMIT_ENTRY_TO_MMU + + /* Get free TLB slot: Set = computed from vaddr, way = random */ + sr TLBGetIndex, [ARC_REG_TLBCOMMAND] + + /* Commit the Write */ +#if (CONFIG_ARC_MMU_VER >= 2) /* introduced in v2 */ + sr TLBWriteNI, [ARC_REG_TLBCOMMAND] +#else + sr TLBWrite, [ARC_REG_TLBCOMMAND] +#endif +.endm + +;----------------------------------------------------------------- +; ARC700 Exception Handling doesn't auto-switch stack and it only provides +; ONE scratch AUX reg "ARC_REG_SCRATCH_DATA0" +; +; For Non-SMP, the scratch AUX reg is repurposed to cache task PGD, so a +; "global" is used to free-up FIRST core reg to be able to code the rest of +; exception prologue (IRQ auto-disabled on Exceptions, so it's IRQ-safe). +; Since the Fast Path TLB Miss handler is coded with 4 regs, the remaining 3 +; need to be saved as well by extending the "global" to be 4 words. Hence +; ".size ex_saved_reg1, 16" +; [All of this dance is to avoid stack switching for each TLB Miss, since we +; only need to save only a handful of regs, as opposed to complete reg file] + +; As simple as that.... + +.macro TLBMISS_FREEUP_REGS + st r0, [@ex_saved_reg1] + mov_s r0, @ex_saved_reg1 + st_s r1, [r0, 4] + st_s r2, [r0, 8] + st_s r3, [r0, 12] + + ; VERIFY if the ASID in MMU-PID Reg is same as + ; one in Linux data structures + + DBG_ASID_MISMATCH +.endm + +;----------------------------------------------------------------- +.macro TLBMISS_RESTORE_REGS + mov_s r0, @ex_saved_reg1 + ld_s r3, [r0,12] + ld_s r2, [r0, 8] + ld_s r1, [r0, 4] + ld_s r0, [r0] +.endm + +.section .text, "ax",@progbits ;Fast Path Code, candidate for ICCM + +;----------------------------------------------------------------------------- +; I-TLB Miss Exception Handler +;----------------------------------------------------------------------------- + +ARC_ENTRY EV_TLBMissI + + TLBMISS_FREEUP_REGS + + ;---------------------------------------------------------------- + ; Get the PTE corresponding to V-addr accessed + LOAD_FAULT_PTE + + ;---------------------------------------------------------------- + ; VERIFY_PTE: Check if PTE permissions approp for executing code + cmp_s r2, VMALLOC_START + mov.lo r2, (_PAGE_PRESENT | _PAGE_READ | _PAGE_EXECUTE) + mov.hs r2, (_PAGE_PRESENT | _PAGE_K_READ | _PAGE_K_EXECUTE) + + and r3, r0, r2 ; Mask out NON Flag bits from PTE + xor.f r3, r3, r2 ; check ( ( pte & flags_test ) == flags_test ) + bnz do_slow_path_pf + + ; Let Linux VM know that the page was accessed + or r0, r0, (_PAGE_PRESENT | _PAGE_ACCESSED) ; set Accessed Bit + st_s r0, [r1] ; Write back PTE + + CONV_PTE_TO_TLB + COMMIT_ENTRY_TO_MMU + TLBMISS_RESTORE_REGS + rtie + +ARC_EXIT EV_TLBMissI + +;----------------------------------------------------------------------------- +; D-TLB Miss Exception Handler +;----------------------------------------------------------------------------- + +ARC_ENTRY EV_TLBMissD + + TLBMISS_FREEUP_REGS + + ;---------------------------------------------------------------- + ; Get the PTE corresponding to V-addr accessed + ; If PTE exists, it will setup, r0 = PTE, r1 = Ptr to PTE + LOAD_FAULT_PTE + + ;---------------------------------------------------------------- + ; VERIFY_PTE: Chk if PTE permissions approp for data access (R/W/R+W) + + mov_s r2, 0 + lr r3, [ecr] + btst_s r3, ECR_C_BIT_DTLB_LD_MISS ; Read Access + or.nz r2, r2, _PAGE_READ ; chk for Read flag in PTE + btst_s r3, ECR_C_BIT_DTLB_ST_MISS ; Write Access + or.nz r2, r2, _PAGE_WRITE ; chk for Write flag in PTE + ; Above laddering takes care of XCHG access + ; which is both Read and Write + + ; If kernel mode access, ; make _PAGE_xx flags as _PAGE_K_xx + ; For copy_(to|from)_user, despite exception taken in kernel mode, + ; this code is not hit, because EFA would still be the user mode + ; address (EFA < 0x6000_0000). + ; This code is for legit kernel mode faults, vmalloc specifically + ; (EFA: 0x7000_0000 to 0x7FFF_FFFF) + + lr r3, [efa] + cmp r3, VMALLOC_START - 1 ; If kernel mode access + asl.hi r2, r2, 3 ; make _PAGE_xx flags as _PAGE_K_xx + or r2, r2, _PAGE_PRESENT ; Common flag for K/U mode + + ; By now, r2 setup with all the Flags we need to check in PTE + and r3, r0, r2 ; Mask out NON Flag bits from PTE + brne.d r3, r2, do_slow_path_pf ; is ((pte & flags_test) == flags_test) + + ;---------------------------------------------------------------- + ; UPDATE_PTE: Let Linux VM know that page was accessed/dirty + lr r3, [ecr] + or r0, r0, (_PAGE_PRESENT | _PAGE_ACCESSED) ; Accessed bit always + btst_s r3, ECR_C_BIT_DTLB_ST_MISS ; See if it was a Write Access ? + or.nz r0, r0, _PAGE_MODIFIED ; if Write, set Dirty bit as well + st_s r0, [r1] ; Write back PTE + + CONV_PTE_TO_TLB + +#if (CONFIG_ARC_MMU_VER == 1) + ; MMU with 2 way set assoc J-TLB, needs some help in pathetic case of + ; memcpy where 3 parties contend for 2 ways, ensuing a livelock. + ; But only for old MMU or one with Metal Fix + TLB_WRITE_HEURISTICS +#endif + + COMMIT_ENTRY_TO_MMU + TLBMISS_RESTORE_REGS + rtie + +;-------- Common routine to call Linux Page Fault Handler ----------- +do_slow_path_pf: + + ; Restore the 4-scratch regs saved by fast path miss handler + TLBMISS_RESTORE_REGS + + ; Slow path TLB Miss handled as a regular ARC Exception + ; (stack switching / save the complete reg-file). + ; That requires freeing up r9 + EXCPN_PROLOG_FREEUP_REG r9 + + lr r9, [erstatus] + + SWITCH_TO_KERNEL_STK + SAVE_ALL_SYS + + ; ------- setup args for Linux Page fault Hanlder --------- + mov_s r0, sp + lr r2, [efa] + lr r3, [ecr] + + ; Both st and ex imply WRITE access of some sort, hence do_page_fault( ) + ; invoked with write=1 for DTLB-st/ex Miss and write=0 for ITLB miss or + ; DTLB-ld Miss + ; DTLB Miss Cause code is ld = 0x01 , st = 0x02, ex = 0x03 + ; Following code uses that fact that st/ex have one bit in common + + btst_s r3, ECR_C_BIT_DTLB_ST_MISS + mov.z r1, 0 + mov.nz r1, 1 + + ; We don't want exceptions to be disabled while the fault is handled. + ; Now that we have saved the context we return from exception hence + ; exceptions get re-enable + + FAKE_RET_FROM_EXCPN r9 + + bl do_page_fault + b ret_from_exception + +ARC_EXIT EV_TLBMissD + +ARC_ENTRY EV_TLBMissB ; Bogus entry to measure sz of DTLBMiss hdlr From d79e678d746d3d4234477f08ce7d27d55ebe283a Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:20 +0530 Subject: [PATCH 2821/4607] ARC: TLB flush Handling Signed-off-by: Vineet Gupta --- arch/arc/include/asm/tlb.h | 17 ++ arch/arc/include/asm/tlbflush.h | 28 +++ arch/arc/mm/tlb.c | 311 ++++++++++++++++++++++++++++++++ 3 files changed, 356 insertions(+) create mode 100644 arch/arc/include/asm/tlbflush.h diff --git a/arch/arc/include/asm/tlb.h b/arch/arc/include/asm/tlb.h index b571e121929b..3eb2ce0bdfa3 100644 --- a/arch/arc/include/asm/tlb.h +++ b/arch/arc/include/asm/tlb.h @@ -21,6 +21,23 @@ #ifndef __ASSEMBLY__ +#define tlb_flush(tlb) local_flush_tlb_mm((tlb)->mm) + +/* + * This pair is called at time of munmap/exit to flush cache and TLB entries + * for mappings being torn down. + * 1) cache-flush part -implemented via tlb_start_vma( ) can be NOP (for now) + * as we don't support aliasing configs in our VIPT D$. + * 2) tlb-flush part - implemted via tlb_end_vma( ) can be NOP as well- + * albiet for difft reasons - its better handled by moving to new ASID + * + * Note, read http://lkml.org/lkml/2004/1/15/6 + */ +#define tlb_start_vma(tlb, vma) +#define tlb_end_vma(tlb, vma) + +#define __tlb_remove_tlb_entry(tlb, ptep, address) + #include #include diff --git a/arch/arc/include/asm/tlbflush.h b/arch/arc/include/asm/tlbflush.h new file mode 100644 index 000000000000..b2f9bc7f68c8 --- /dev/null +++ b/arch/arc/include/asm/tlbflush.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_TLBFLUSH__ +#define __ASM_ARC_TLBFLUSH__ + +#include + +void local_flush_tlb_all(void); +void local_flush_tlb_mm(struct mm_struct *mm); +void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page); +void local_flush_tlb_kernel_range(unsigned long start, unsigned long end); +void local_flush_tlb_range(struct vm_area_struct *vma, + unsigned long start, unsigned long end); + +/* XXX: Revisit for SMP */ +#define flush_tlb_range(vma, s, e) local_flush_tlb_range(vma, s, e) +#define flush_tlb_page(vma, page) local_flush_tlb_page(vma, page) +#define flush_tlb_kernel_range(s, e) local_flush_tlb_kernel_range(s, e) +#define flush_tlb_all() local_flush_tlb_all() +#define flush_tlb_mm(mm) local_flush_tlb_mm(mm) + +#endif diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c index 404e5be4f704..232a0ff80a5e 100644 --- a/arch/arc/mm/tlb.c +++ b/arch/arc/mm/tlb.c @@ -6,13 +6,97 @@ * 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. + * + * vineetg: Aug 2011 + * -Reintroduce duplicate PD fixup - some customer chips still have the issue + * + * vineetg: May 2011 + * -No need to flush_cache_page( ) for each call to update_mmu_cache() + * some of the LMBench tests improved amazingly + * = page-fault thrice as fast (75 usec to 28 usec) + * = mmap twice as fast (9.6 msec to 4.6 msec), + * = fork (5.3 msec to 3.7 msec) + * + * vineetg: April 2011 : + * -MMU v3: PD{0,1} bits layout changed: They don't overlap anymore, + * helps avoid a shift when preparing PD0 from PTE + * + * vineetg: April 2011 : Preparing for MMU V3 + * -MMU v2/v3 BCRs decoded differently + * -Remove TLB_SIZE hardcoding as it's variable now: 256 or 512 + * -tlb_entry_erase( ) can be void + * -local_flush_tlb_range( ): + * = need not "ceil" @end + * = walks MMU only if range spans < 32 entries, as opposed to 256 + * + * Vineetg: Sept 10th 2008 + * -Changes related to MMU v2 (Rel 4.8) + * + * Vineetg: Aug 29th 2008 + * -In TLB Flush operations (Metal Fix MMU) there is a explict command to + * flush Micro-TLBS. If TLB Index Reg is invalid prior to TLBIVUTLB cmd, + * it fails. Thus need to load it with ANY valid value before invoking + * TLBIVUTLB cmd + * + * Vineetg: Aug 21th 2008: + * -Reduced the duration of IRQ lockouts in TLB Flush routines + * -Multiple copies of TLB erase code seperated into a "single" function + * -In TLB Flush routines, interrupt disabling moved UP to retrieve ASID + * in interrupt-safe region. + * + * Vineetg: April 23rd Bug #93131 + * Problem: tlb_flush_kernel_range() doesnt do anything if the range to + * flush is more than the size of TLB itself. + * + * Rahul Trivedi : Codito Technologies 2004 */ #include #include +#include #include #include +/* Need for ARC MMU v2 + * + * ARC700 MMU-v1 had a Joint-TLB for Code and Data and is 2 way set-assoc. + * For a memcpy operation with 3 players (src/dst/code) such that all 3 pages + * map into same set, there would be contention for the 2 ways causing severe + * Thrashing. + * + * Although J-TLB is 2 way set assoc, ARC700 caches J-TLB into uTLBS which has + * much higher associativity. u-D-TLB is 8 ways, u-I-TLB is 4 ways. + * Given this, the thrasing problem should never happen because once the 3 + * J-TLB entries are created (even though 3rd will knock out one of the prev + * two), the u-D-TLB and u-I-TLB will have what is required to accomplish memcpy + * + * Yet we still see the Thrashing because a J-TLB Write cause flush of u-TLBs. + * This is a simple design for keeping them in sync. So what do we do? + * The solution which James came up was pretty neat. It utilised the assoc + * of uTLBs by not invalidating always but only when absolutely necessary. + * + * - Existing TLB commands work as before + * - New command (TLBWriteNI) for TLB write without clearing uTLBs + * - New command (TLBIVUTLB) to invalidate uTLBs. + * + * The uTLBs need only be invalidated when pages are being removed from the + * OS page table. If a 'victim' TLB entry is being overwritten in the main TLB + * as a result of a miss, the removed entry is still allowed to exist in the + * uTLBs as it is still valid and present in the OS page table. This allows the + * full associativity of the uTLBs to hide the limited associativity of the main + * TLB. + * + * During a miss handler, the new "TLBWriteNI" command is used to load + * entries without clearing the uTLBs. + * + * When the OS page table is updated, TLB entries that may be associated with a + * removed page are removed (flushed) from the TLB using TLBWrite. In this + * circumstance, the uTLBs must also be cleared. This is done by using the + * existing TLBWrite command. An explicit IVUTLB is also required for those + * corner cases when TLBWrite was not executed at all because the corresp + * J-TLB entry got evicted/replaced. + */ + /* A copy of the ASID from the PID reg is kept in asid_cache */ int asid_cache = FIRST_ASID; @@ -22,6 +106,233 @@ int asid_cache = FIRST_ASID; */ struct mm_struct *asid_mm_map[NUM_ASID + 1]; +/* + * Utility Routine to erase a J-TLB entry + * The procedure is to look it up in the MMU. If found, ERASE it by + * issuing a TlbWrite CMD with PD0 = PD1 = 0 + */ + +static void __tlb_entry_erase(void) +{ + write_aux_reg(ARC_REG_TLBPD1, 0); + write_aux_reg(ARC_REG_TLBPD0, 0); + write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite); +} + +static void tlb_entry_erase(unsigned int vaddr_n_asid) +{ + unsigned int idx; + + /* Locate the TLB entry for this vaddr + ASID */ + write_aux_reg(ARC_REG_TLBPD0, vaddr_n_asid); + write_aux_reg(ARC_REG_TLBCOMMAND, TLBProbe); + idx = read_aux_reg(ARC_REG_TLBINDEX); + + /* No error means entry found, zero it out */ + if (likely(!(idx & TLB_LKUP_ERR))) { + __tlb_entry_erase(); + } else { /* Some sort of Error */ + + /* Duplicate entry error */ + if (idx & 0x1) { + /* TODO we need to handle this case too */ + pr_emerg("unhandled Duplicate flush for %x\n", + vaddr_n_asid); + } + /* else entry not found so nothing to do */ + } +} + +/**************************************************************************** + * ARC700 MMU caches recently used J-TLB entries (RAM) as uTLBs (FLOPs) + * + * New IVUTLB cmd in MMU v2 explictly invalidates the uTLB + * + * utlb_invalidate ( ) + * -For v2 MMU calls Flush uTLB Cmd + * -For v1 MMU does nothing (except for Metal Fix v1 MMU) + * This is because in v1 TLBWrite itself invalidate uTLBs + ***************************************************************************/ + +static void utlb_invalidate(void) +{ +#if (CONFIG_ARC_MMU_VER >= 2) + +#if (CONFIG_ARC_MMU_VER < 3) + /* MMU v2 introduced the uTLB Flush command. + * There was however an obscure hardware bug, where uTLB flush would + * fail when a prior probe for J-TLB (both totally unrelated) would + * return lkup err - because the entry didnt exist in MMU. + * The Workround was to set Index reg with some valid value, prior to + * flush. This was fixed in MMU v3 hence not needed any more + */ + unsigned int idx; + + /* make sure INDEX Reg is valid */ + idx = read_aux_reg(ARC_REG_TLBINDEX); + + /* If not write some dummy val */ + if (unlikely(idx & TLB_LKUP_ERR)) + write_aux_reg(ARC_REG_TLBINDEX, 0xa); +#endif + + write_aux_reg(ARC_REG_TLBCOMMAND, TLBIVUTLB); +#endif + +} + +/* + * Un-conditionally (without lookup) erase the entire MMU contents + */ + +noinline void local_flush_tlb_all(void) +{ + unsigned long flags; + unsigned int entry; + struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; + + local_irq_save(flags); + + /* Load PD0 and PD1 with template for a Blank Entry */ + write_aux_reg(ARC_REG_TLBPD1, 0); + write_aux_reg(ARC_REG_TLBPD0, 0); + + for (entry = 0; entry < mmu->num_tlb; entry++) { + /* write this entry to the TLB */ + write_aux_reg(ARC_REG_TLBINDEX, entry); + write_aux_reg(ARC_REG_TLBCOMMAND, TLBWrite); + } + + utlb_invalidate(); + + local_irq_restore(flags); +} + +/* + * Flush the entrie MM for userland. The fastest way is to move to Next ASID + */ +noinline void local_flush_tlb_mm(struct mm_struct *mm) +{ + /* + * Small optimisation courtesy IA64 + * flush_mm called during fork,exit,munmap etc, multiple times as well. + * Only for fork( ) do we need to move parent to a new MMU ctxt, + * all other cases are NOPs, hence this check. + */ + if (atomic_read(&mm->mm_users) == 0) + return; + + /* + * Workaround for Android weirdism: + * A binder VMA could end up in a task such that vma->mm != tsk->mm + * old code would cause h/w - s/w ASID to get out of sync + */ + if (current->mm != mm) + destroy_context(mm); + else + get_new_mmu_context(mm); +} + +/* + * Flush a Range of TLB entries for userland. + * @start is inclusive, while @end is exclusive + * Difference between this and Kernel Range Flush is + * -Here the fastest way (if range is too large) is to move to next ASID + * without doing any explicit Shootdown + * -In case of kernel Flush, entry has to be shot down explictly + */ +void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + unsigned long flags; + unsigned int asid; + + /* If range @start to @end is more than 32 TLB entries deep, + * its better to move to a new ASID rather than searching for + * individual entries and then shooting them down + * + * The calc above is rough, doesn't account for unaligned parts, + * since this is heuristics based anyways + */ + if (unlikely((end - start) >= PAGE_SIZE * 32)) { + local_flush_tlb_mm(vma->vm_mm); + return; + } + + /* + * @start moved to page start: this alone suffices for checking + * loop end condition below, w/o need for aligning @end to end + * e.g. 2000 to 4001 will anyhow loop twice + */ + start &= PAGE_MASK; + + local_irq_save(flags); + asid = vma->vm_mm->context.asid; + + if (asid != NO_ASID) { + while (start < end) { + tlb_entry_erase(start | (asid & 0xff)); + start += PAGE_SIZE; + } + } + + utlb_invalidate(); + + local_irq_restore(flags); +} + +/* Flush the kernel TLB entries - vmalloc/modules (Global from MMU perspective) + * @start, @end interpreted as kvaddr + * Interestingly, shared TLB entries can also be flushed using just + * @start,@end alone (interpreted as user vaddr), although technically SASID + * is also needed. However our smart TLbProbe lookup takes care of that. + */ +void local_flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ + unsigned long flags; + + /* exactly same as above, except for TLB entry not taking ASID */ + + if (unlikely((end - start) >= PAGE_SIZE * 32)) { + local_flush_tlb_all(); + return; + } + + start &= PAGE_MASK; + + local_irq_save(flags); + while (start < end) { + tlb_entry_erase(start); + start += PAGE_SIZE; + } + + utlb_invalidate(); + + local_irq_restore(flags); +} + +/* + * Delete TLB entry in MMU for a given page (??? address) + * NOTE One TLB entry contains translation for single PAGE + */ + +void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + unsigned long flags; + + /* Note that it is critical that interrupts are DISABLED between + * checking the ASID and using it flush the TLB entry + */ + local_irq_save(flags); + + if (vma->vm_mm->context.asid != NO_ASID) { + tlb_entry_erase((page & PAGE_MASK) | + (vma->vm_mm->context.asid & 0xff)); + utlb_invalidate(); + } + + local_irq_restore(flags); +} /* * Routine to create a TLB entry From fbd7053a7854b12b0fdc415089c59baabf25c625 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:20 +0530 Subject: [PATCH 2822/4607] ARC: Page Fault handling This includes recent changes to make handler "retry" and/or "killable" The killable (early exit) logic is loosely based on how SH implements it return if SIGKILL + either of VM_FAULT_OOM or VM_FAULT_RETRY which is different from Hexagon implementation which would NOT early exit for SIGKILL + VM_FAULT_OOM + !VM_FAULT_RETRY credits: Non executable stack support from Simon Spooner Signed-off-by: Vineet Gupta --- arch/arc/mm/fault.c | 228 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 arch/arc/mm/fault.c diff --git a/arch/arc/mm/fault.c b/arch/arc/mm/fault.c new file mode 100644 index 000000000000..af55aab803d2 --- /dev/null +++ b/arch/arc/mm/fault.c @@ -0,0 +1,228 @@ +/* Page Fault Handling for ARC (TLB Miss / ProtV) + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +static int handle_vmalloc_fault(struct mm_struct *mm, unsigned long address) +{ + /* + * Synchronize this task's top level page-table + * with the 'reference' page table. + */ + pgd_t *pgd, *pgd_k; + pud_t *pud, *pud_k; + pmd_t *pmd, *pmd_k; + + pgd = pgd_offset_fast(mm, address); + pgd_k = pgd_offset_k(address); + + if (!pgd_present(*pgd_k)) + goto bad_area; + + pud = pud_offset(pgd, address); + pud_k = pud_offset(pgd_k, address); + if (!pud_present(*pud_k)) + goto bad_area; + + pmd = pmd_offset(pud, address); + pmd_k = pmd_offset(pud_k, address); + if (!pmd_present(*pmd_k)) + goto bad_area; + + set_pmd(pmd, *pmd_k); + + /* XXX: create the TLB entry here */ + return 0; + +bad_area: + return 1; +} + +void do_page_fault(struct pt_regs *regs, int write, unsigned long address, + unsigned long cause_code) +{ + struct vm_area_struct *vma = NULL; + struct task_struct *tsk = current; + struct mm_struct *mm = tsk->mm; + siginfo_t info; + int fault, ret; + unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | + (write ? FAULT_FLAG_WRITE : 0); + + /* + * We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + * + * NOTE! We MUST NOT take any locks for this case. We may + * be in an interrupt or a critical region, and should + * only copy the information from the master page table, + * nothing more. + */ + if (address >= VMALLOC_START && address <= VMALLOC_END) { + ret = handle_vmalloc_fault(mm, address); + if (unlikely(ret)) + goto bad_area_nosemaphore; + else + return; + } + + info.si_code = SEGV_MAPERR; + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_atomic() || !mm) + goto no_context; + +retry: + down_read(&mm->mmap_sem); + vma = find_vma(mm, address); + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (expand_stack(vma, address)) + goto bad_area; + + /* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + info.si_code = SEGV_ACCERR; + + /* Handle protection violation, execute on heap or stack */ + + if (cause_code == ((ECR_V_PROTV << 16) | ECR_C_PROTV_INST_FETCH)) + goto bad_area; + + if (write) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + } else { + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + +survive: + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + fault = handle_mm_fault(mm, vma, address, flags); + + /* If Pagefault was interrupted by SIGKILL, exit page fault "early" */ + if (unlikely(fatal_signal_pending(current))) { + if ((fault & VM_FAULT_ERROR) && !(fault & VM_FAULT_RETRY)) + up_read(&mm->mmap_sem); + if (user_mode(regs)) + return; + } + + if (likely(!(fault & VM_FAULT_ERROR))) { + if (flags & FAULT_FLAG_ALLOW_RETRY) { + /* To avoid updating stats twice for retry case */ + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; + + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + flags |= FAULT_FLAG_TRIED; + goto retry; + } + } + + /* Fault Handled Gracefully */ + up_read(&mm->mmap_sem); + return; + } + + /* TBD: switch to pagefault_out_of_memory() */ + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + + /* no man's land */ + BUG(); + + /* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up_read(&mm->mmap_sem); + +bad_area_nosemaphore: + /* User mode accesses just cause a SIGSEGV */ + if (user_mode(regs)) { + tsk->thread.fault_address = address; + tsk->thread.cause_code = cause_code; + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void __user *)address; + force_sig_info(SIGSEGV, &info, tsk); + return; + } + +no_context: + /* Are we prepared to handle this kernel fault? + * + * (The kernel has valid exception-points in the source + * when it acesses user-memory. When it fails in one + * of those points, we find it in a table and do a jump + * to some fixup code that loads an appropriate error + * code) + */ + if (fixup_exception(regs)) + return; + + die("Oops", regs, address, cause_code); + +out_of_memory: + if (is_global_init(tsk)) { + yield(); + goto survive; + } + up_read(&mm->mmap_sem); + + if (user_mode(regs)) + do_group_exit(SIGKILL); /* This will never return */ + + goto no_context; + +do_sigbus: + up_read(&mm->mmap_sem); + + if (!user_mode(regs)) + goto no_context; + + tsk->thread.fault_address = address; + tsk->thread.cause_code = cause_code; + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void __user *)address; + force_sig_info(SIGBUS, &info, tsk); +} From 1162b0701b14ba112d4e3fe5c27c694caf983539 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:20 +0530 Subject: [PATCH 2823/4607] ARC: I/O and DMA Mappings Signed-off-by: Vineet Gupta --- arch/arc/include/asm/dma-mapping.h | 205 ++++++++++++++++++ arch/arc/include/asm/dma.h | 14 ++ arch/arc/include/asm/io.h | 103 +++++++++ arch/arc/mm/dma.c | 94 ++++++++ arch/arc/mm/ioremap.c | 67 ++++++ arch/arc/plat-arcfpga/include/plat/dma_addr.h | 45 ++++ 6 files changed, 528 insertions(+) create mode 100644 arch/arc/include/asm/dma-mapping.h create mode 100644 arch/arc/include/asm/dma.h create mode 100644 arch/arc/include/asm/io.h create mode 100644 arch/arc/mm/dma.c create mode 100644 arch/arc/mm/ioremap.c create mode 100644 arch/arc/plat-arcfpga/include/plat/dma_addr.h diff --git a/arch/arc/include/asm/dma-mapping.h b/arch/arc/include/asm/dma-mapping.h new file mode 100644 index 000000000000..7fd150e97eb2 --- /dev/null +++ b/arch/arc/include/asm/dma-mapping.h @@ -0,0 +1,205 @@ +/* + * DMA Mapping glue for ARC + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_DMA_MAPPING_H +#define ASM_ARC_DMA_MAPPING_H + +#include +#include +#include + +void *dma_alloc_noncoherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp); + +void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle); + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp); + +void dma_free_coherent(struct device *dev, size_t size, void *kvaddr, + dma_addr_t dma_handle); + +/* drivers/base/dma-mapping.c */ +extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size); +extern int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, + size_t size); + +#define dma_mmap_coherent(d, v, c, h, s) dma_common_mmap(d, v, c, h, s) +#define dma_get_sgtable(d, t, v, h, s) dma_common_get_sgtable(d, t, v, h, s) + +/* + * streaming DMA Mapping API... + * CPU accesses page via normal paddr, thus needs to explicitly made + * consistent before each use + */ + +static inline void __inline_dma_cache_sync(unsigned long paddr, size_t size, + enum dma_data_direction dir) +{ + switch (dir) { + case DMA_FROM_DEVICE: + dma_cache_inv(paddr, size); + break; + case DMA_TO_DEVICE: + dma_cache_wback(paddr, size); + break; + case DMA_BIDIRECTIONAL: + dma_cache_wback_inv(paddr, size); + break; + default: + pr_err("Invalid DMA dir [%d] for OP @ %lx\n", dir, paddr); + } +} + +void __arc_dma_cache_sync(unsigned long paddr, size_t size, + enum dma_data_direction dir); + +#define _dma_cache_sync(addr, sz, dir) \ +do { \ + if (__builtin_constant_p(dir)) \ + __inline_dma_cache_sync(addr, sz, dir); \ + else \ + __arc_dma_cache_sync(addr, sz, dir); \ +} \ +while (0); + +static inline dma_addr_t +dma_map_single(struct device *dev, void *cpu_addr, size_t size, + enum dma_data_direction dir) +{ + _dma_cache_sync((unsigned long)cpu_addr, size, dir); + return plat_kernel_addr_to_dma(dev, cpu_addr); +} + +static inline void +dma_unmap_single(struct device *dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction dir) +{ +} + +static inline dma_addr_t +dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir) +{ + unsigned long paddr = page_to_phys(page) + offset; + return dma_map_single(dev, (void *)paddr, size, dir); +} + +static inline void +dma_unmap_page(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir) +{ +} + +static inline int +dma_map_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir) +{ + struct scatterlist *s; + int i; + + for_each_sg(sg, s, nents, i) + sg->dma_address = dma_map_page(dev, sg_page(s), s->offset, + s->length, dir); + + return nents; +} + +static inline void +dma_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir) +{ + struct scatterlist *s; + int i; + + for_each_sg(sg, s, nents, i) + dma_unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir); +} + +static inline void +dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir) +{ + _dma_cache_sync(plat_dma_addr_to_kernel(dev, dma_handle), size, + DMA_FROM_DEVICE); +} + +static inline void +dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir) +{ + _dma_cache_sync(plat_dma_addr_to_kernel(dev, dma_handle), size, + DMA_TO_DEVICE); +} + +static inline void +dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + _dma_cache_sync(plat_dma_addr_to_kernel(dev, dma_handle) + offset, + size, DMA_FROM_DEVICE); +} + +static inline void +dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + _dma_cache_sync(plat_dma_addr_to_kernel(dev, dma_handle) + offset, + size, DMA_TO_DEVICE); +} + +static inline void +dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction dir) +{ + int i; + + for (i = 0; i < nelems; i++, sg++) + _dma_cache_sync((unsigned int)sg_virt(sg), sg->length, dir); +} + +static inline void +dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction dir) +{ + int i; + + for (i = 0; i < nelems; i++, sg++) + _dma_cache_sync((unsigned int)sg_virt(sg), sg->length, dir); +} + +static inline int dma_supported(struct device *dev, u64 dma_mask) +{ + /* Support 32 bit DMA mask exclusively */ + return dma_mask == DMA_BIT_MASK(32); +} + +static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + return 0; +} + +static inline int dma_set_mask(struct device *dev, u64 dma_mask) +{ + if (!dev->dma_mask || !dma_supported(dev, dma_mask)) + return -EIO; + + *dev->dma_mask = dma_mask; + + return 0; +} + +#endif diff --git a/arch/arc/include/asm/dma.h b/arch/arc/include/asm/dma.h new file mode 100644 index 000000000000..ca7c45181de9 --- /dev/null +++ b/arch/arc/include/asm/dma.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_DMA_H +#define ASM_ARC_DMA_H + +#define MAX_DMA_ADDRESS 0xC0000000 + +#endif diff --git a/arch/arc/include/asm/io.h b/arch/arc/include/asm/io.h new file mode 100644 index 000000000000..d20051bbbf02 --- /dev/null +++ b/arch/arc/include/asm/io.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_IO_H +#define _ASM_ARC_IO_H + +#include +#include +#include + +#define PCI_IOBASE ((void __iomem *)0) + +extern void __iomem *ioremap(unsigned long physaddr, unsigned long size); +extern void iounmap(const void __iomem *addr); + +#define ioremap_nocache(phy, sz) ioremap(phy, sz) +#define ioremap_wc(phy, sz) ioremap(phy, sz) + +/* Change struct page to physical address */ +#define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) + +#define __raw_readb __raw_readb +static inline u8 __raw_readb(const volatile void __iomem *addr) +{ + u8 b; + + __asm__ __volatile__( + " ldb%U1 %0, %1 \n" + : "=r" (b) + : "m" (*(volatile u8 __force *)addr) + : "memory"); + + return b; +} + +#define __raw_readw __raw_readw +static inline u16 __raw_readw(const volatile void __iomem *addr) +{ + u16 s; + + __asm__ __volatile__( + " ldw%U1 %0, %1 \n" + : "=r" (s) + : "m" (*(volatile u16 __force *)addr) + : "memory"); + + return s; +} + +#define __raw_readl __raw_readl +static inline u32 __raw_readl(const volatile void __iomem *addr) +{ + u32 w; + + __asm__ __volatile__( + " ld%U1 %0, %1 \n" + : "=r" (w) + : "m" (*(volatile u32 __force *)addr) + : "memory"); + + return w; +} + +#define __raw_writeb __raw_writeb +static inline void __raw_writeb(u8 b, volatile void __iomem *addr) +{ + __asm__ __volatile__( + " stb%U1 %0, %1 \n" + : + : "r" (b), "m" (*(volatile u8 __force *)addr) + : "memory"); +} + +#define __raw_writew __raw_writew +static inline void __raw_writew(u16 s, volatile void __iomem *addr) +{ + __asm__ __volatile__( + " stw%U1 %0, %1 \n" + : + : "r" (s), "m" (*(volatile u16 __force *)addr) + : "memory"); + +} + +#define __raw_writel __raw_writel +static inline void __raw_writel(u32 w, volatile void __iomem *addr) +{ + __asm__ __volatile__( + " st%U1 %0, %1 \n" + : + : "r" (w), "m" (*(volatile u32 __force *)addr) + : "memory"); + +} + +#include + +#endif /* _ASM_ARC_IO_H */ diff --git a/arch/arc/mm/dma.c b/arch/arc/mm/dma.c new file mode 100644 index 000000000000..12cc6485b218 --- /dev/null +++ b/arch/arc/mm/dma.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + */ + +/* + * DMA Coherent API Notes + * + * I/O is inherently non-coherent on ARC. So a coherent DMA buffer is + * implemented by accessintg it using a kernel virtual address, with + * Cache bit off in the TLB entry. + * + * The default DMA address == Phy address which is 0x8000_0000 based. + * A platform/device can make it zero based, by over-riding + * plat_{dma,kernel}_addr_to_{kernel,dma} + */ + +#include +#include +#include +#include + +/* + * Helpers for Coherent DMA API. + */ +void *dma_alloc_noncoherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp) +{ + void *paddr; + + /* This is linear addr (0x8000_0000 based) */ + paddr = alloc_pages_exact(size, gfp); + if (!paddr) + return NULL; + + /* This is bus address, platform dependent */ + *dma_handle = plat_kernel_addr_to_dma(dev, paddr); + + return paddr; +} +EXPORT_SYMBOL(dma_alloc_noncoherent); + +void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + free_pages_exact((void *)plat_dma_addr_to_kernel(dev, dma_handle), + size); +} +EXPORT_SYMBOL(dma_free_noncoherent); + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp) +{ + void *paddr, *kvaddr; + + /* This is linear addr (0x8000_0000 based) */ + paddr = alloc_pages_exact(size, gfp); + if (!paddr) + return NULL; + + /* This is kernel Virtual address (0x7000_0000 based) */ + kvaddr = ioremap_nocache((unsigned long)paddr, size); + if (kvaddr != NULL) + memset(kvaddr, 0, size); + + /* This is bus address, platform dependent */ + *dma_handle = plat_kernel_addr_to_dma(dev, paddr); + + return kvaddr; +} +EXPORT_SYMBOL(dma_alloc_coherent); + +void dma_free_coherent(struct device *dev, size_t size, void *kvaddr, + dma_addr_t dma_handle) +{ + iounmap((void __force __iomem *)kvaddr); + + free_pages_exact((void *)plat_dma_addr_to_kernel(dev, dma_handle), + size); +} +EXPORT_SYMBOL(dma_free_coherent); + +/* + * Helper for streaming DMA... + */ +void __arc_dma_cache_sync(unsigned long paddr, size_t size, + enum dma_data_direction dir) +{ + __inline_dma_cache_sync(paddr, size, dir); +} +EXPORT_SYMBOL(__arc_dma_cache_sync); diff --git a/arch/arc/mm/ioremap.c b/arch/arc/mm/ioremap.c new file mode 100644 index 000000000000..52518b6d0f28 --- /dev/null +++ b/arch/arc/mm/ioremap.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include +#include +#include +#include + +void __iomem *ioremap(unsigned long paddr, unsigned long size) +{ + unsigned long vaddr; + struct vm_struct *area; + unsigned long off, end; + const pgprot_t prot = PAGE_KERNEL_NO_CACHE; + + /* Don't allow wraparound or zero size */ + end = paddr + size - 1; + if (!size || (end < paddr)) + return NULL; + + /* If the region is h/w uncached, nothing special needed */ + if (paddr >= ARC_UNCACHED_ADDR_SPACE) + return (void __iomem *)paddr; + + /* An early platform driver might end up here */ + if (!slab_is_available()) + return NULL; + + /* Mappings have to be page-aligned, page-sized */ + off = paddr & ~PAGE_MASK; + paddr &= PAGE_MASK; + size = PAGE_ALIGN(end + 1) - paddr; + + /* + * Ok, go for it.. + */ + area = get_vm_area(size, VM_IOREMAP); + if (!area) + return NULL; + + area->phys_addr = paddr; + vaddr = (unsigned long)area->addr; + if (ioremap_page_range(vaddr, vaddr + size, paddr, prot)) { + vfree(area->addr); + return NULL; + } + + return (void __iomem *)(off + (char __iomem *)vaddr); +} +EXPORT_SYMBOL(ioremap); + +void iounmap(const void __iomem *addr) +{ + if (addr >= (void __force __iomem *)ARC_UNCACHED_ADDR_SPACE) + return; + + vfree((void *)(PAGE_MASK & (unsigned long __force)addr)); +} +EXPORT_SYMBOL(iounmap); diff --git a/arch/arc/plat-arcfpga/include/plat/dma_addr.h b/arch/arc/plat-arcfpga/include/plat/dma_addr.h new file mode 100644 index 000000000000..0e963431b729 --- /dev/null +++ b/arch/arc/plat-arcfpga/include/plat/dma_addr.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: Feb 2009 + * -For AA4 board, kernel to DMA address APIs + */ + +/* + * kernel addresses are 0x800_000 based, while Bus addr are 0 based + */ + +#ifndef __PLAT_DMA_ADDR_H +#define __PLAT_DMA_ADDR_H + +#include + +static inline unsigned long plat_dma_addr_to_kernel(struct device *dev, + dma_addr_t dma_addr) +{ + return dma_addr + PAGE_OFFSET; +} + +static inline dma_addr_t plat_kernel_addr_to_dma(struct device *dev, void *ptr) +{ + unsigned long addr = (unsigned long)ptr; + /* + * To Catch buggy drivers which can call DMA map API with kernel vaddr + * i.e. for buffers alloc via vmalloc or ioremap which are not + * gaurnateed to be PHY contiguous and hence unfit for DMA anyways. + * On ARC kernel virtual address is 0x7000_0000 to 0x7FFF_FFFF, so + * ideally we want to check this range here, but our implementation is + * better as it checks for even worse user virtual address as well. + */ + if (likely(addr >= PAGE_OFFSET)) + return addr - PAGE_OFFSET; + + BUG(); + return addr; +} + +#endif From c121c5063c0674fad6811f0b0d86ec3bc6eecbbd Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:20 +0530 Subject: [PATCH 2824/4607] ARC: Boot #1: low-level, setup_arch(), /proc/cpuinfo, mem init Signed-off-by: Vineet Gupta Acked-by: Arnd Bergmann --- arch/arc/Kconfig | 2 + arch/arc/include/asm/arcregs.h | 5 + arch/arc/include/asm/setup.h | 22 ++++ arch/arc/kernel/head.S | 78 ++++++++++++++ arch/arc/kernel/reset.c | 33 ++++++ arch/arc/kernel/setup.c | 166 ++++++++++++++++++++++++++++++ arch/arc/mm/init.c | 171 +++++++++++++++++++++++++++++++ arch/arc/plat-arcfpga/platform.c | 29 ++++++ 8 files changed, 506 insertions(+) create mode 100644 arch/arc/include/asm/setup.h create mode 100644 arch/arc/kernel/head.S create mode 100644 arch/arc/kernel/reset.c create mode 100644 arch/arc/kernel/setup.c create mode 100644 arch/arc/mm/init.c create mode 100644 arch/arc/plat-arcfpga/platform.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 756beffd20cb..a3538493e353 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -23,7 +23,9 @@ config ARC select GENERIC_SIGALTSTACK select GENERIC_SMP_IDLE_THREAD select HAVE_GENERIC_HARDIRQS + select HAVE_MEMBLOCK select MODULES_USE_ELF_RELA + select NO_BOOTMEM config SCHED_OMIT_FRAME_POINTER def_bool y diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index 1c24485fd04b..9e42611e39d3 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -258,6 +258,11 @@ } \ } +/* Helpers */ +#define TO_KB(bytes) ((bytes) >> 10) +#define TO_MB(bytes) (TO_KB(bytes) >> 10) +#define PAGES_TO_KB(n_pages) ((n_pages) << (PAGE_SHIFT - 10)) +#define PAGES_TO_MB(n_pages) (PAGES_TO_KB(n_pages) >> 10) #ifdef CONFIG_ARC_FPU_SAVE_RESTORE /* These DPFP regs need to be saved/restored across ctx-sw */ diff --git a/arch/arc/include/asm/setup.h b/arch/arc/include/asm/setup.h new file mode 100644 index 000000000000..ab427e6a2cb5 --- /dev/null +++ b/arch/arc/include/asm/setup.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 __ASMARC_SETUP_H +#define __ASMARC_SETUP_H + +#include + +#define COMMAND_LINE_SIZE 256 + +extern int root_mountflags, end_mem; +extern int running_on_hw; + +void __init setup_processor(void); +void __init setup_arch_memory(void); + +#endif /* __ASMARC_SETUP_H */ diff --git a/arch/arc/kernel/head.S b/arch/arc/kernel/head.S new file mode 100644 index 000000000000..e63f6a43abb1 --- /dev/null +++ b/arch/arc/kernel/head.S @@ -0,0 +1,78 @@ +/* + * ARC CPU startup Code + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Vineetg: Dec 2007 + * -Check if we are running on Simulator or on real hardware + * to skip certain things during boot on simulator + */ + +#include +#include +#include +#include + + .cpu A7 + + .section .init.text, "ax",@progbits + .type stext, @function + .globl stext +stext: + ;------------------------------------------------------------------- + ; Don't clobber r0-r4 yet. It might have bootloader provided info + ;------------------------------------------------------------------- + + ; Clear BSS before updating any globals + ; XXX: use ZOL here + mov r5, __bss_start + mov r6, __bss_stop +1: + st.ab 0, [r5,4] + brlt r5, r6, 1b + +#ifdef CONFIG_CMDLINE_UBOOT + ; support for bootloader provided cmdline + ; If cmdline passed by u-boot, then + ; r0 = 1 (because ATAGS parsing, now retired, used to use 0) + ; r1 = magic number (board identity) + ; r2 = addr of cmdline string (somewhere in memory/flash) + + brne r0, 1, .Lother_bootup_chores ; u-boot didn't pass cmdline + breq r2, 0, .Lother_bootup_chores ; or cmdline is NULL + + mov r5, @command_line +1: + ldb.ab r6, [r2, 1] + breq r6, 0, .Lother_bootup_chores + b.d 1b + stb.ab r6, [r5, 1] +#endif + +.Lother_bootup_chores: + + ; Identify if running on ISS vs Silicon + ; IDENTITY Reg [ 3 2 1 0 ] + ; (chip-id) ^^^^^ ==> 0xffff for ISS + lr r0, [identity] + lsr r3, r0, 16 + cmp r3, 0xffff + mov.z r4, 0 + mov.nz r4, 1 + st r4, [@running_on_hw] + + ; setup "current" tsk and optionally cache it in dedicated r25 + mov r9, @init_task + SET_CURR_TASK_ON_CPU r9, r0 ; r9 = tsk, r0 = scratch + + ; setup stack (fp, sp) + mov fp, 0 + + ; tsk->thread_info is really a PAGE, whose bottom hoists stack + GET_TSK_STACK_BASE r9, sp ; r9 = tsk, sp = stack base(output) + + j start_kernel ; "C" entry point diff --git a/arch/arc/kernel/reset.c b/arch/arc/kernel/reset.c new file mode 100644 index 000000000000..e227a2b1c943 --- /dev/null +++ b/arch/arc/kernel/reset.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include + +void machine_halt(void) +{ + /* Halt the processor */ + __asm__ __volatile__("flag 1\n"); +} + +void machine_restart(char *__unused) +{ + /* Soft reset : jump to reset vector */ + pr_info("Put your restart handler here\n"); + machine_halt(); +} + +void machine_power_off(void) +{ + /* FIXME :: power off ??? */ + machine_halt(); +} + +void (*pm_power_off) (void) = NULL; diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c new file mode 100644 index 000000000000..82ac20603398 --- /dev/null +++ b/arch/arc/kernel/setup.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FIX_PTR(x) __asm__ __volatile__(";" : "+r"(x)) + +int running_on_hw = 1; /* vs. on ISS */ + +char __initdata command_line[COMMAND_LINE_SIZE]; + +struct task_struct *_current_task[NR_CPUS]; /* For stack switching */ + +struct cpuinfo_arc cpuinfo_arc700[NR_CPUS]; + +void __init read_arc_build_cfg_regs(void) +{ + read_decode_mmu_bcr(); + read_decode_cache_bcr(); +} + +/* + * Initialize and setup the processor core + * This is called by all the CPUs thus should not do special case stuff + * such as only for boot CPU etc + */ + +void __init setup_processor(void) +{ + read_arc_build_cfg_regs(); + arc_init_IRQ(); + arc_mmu_init(); + arc_cache_init(); +} + +void __init __attribute__((weak)) arc_platform_early_init(void) +{ +} + +void __init setup_arch(char **cmdline_p) +{ +#ifdef CONFIG_CMDLINE_UBOOT + /* Make sure that a whitespace is inserted before */ + strlcat(command_line, " ", sizeof(command_line)); +#endif + /* + * Append .config cmdline to base command line, which might already + * contain u-boot "bootargs" (handled by head.S, if so configured) + */ + strlcat(command_line, CONFIG_CMDLINE, sizeof(command_line)); + + /* Save unparsed command line copy for /proc/cmdline */ + strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); + *cmdline_p = command_line; + + /* To force early parsing of things like mem=xxx */ + parse_early_param(); + + /* Platform/board specific: e.g. early console registration */ + arc_platform_early_init(); + + setup_processor(); + + setup_arch_memory(); + + /* Can be issue if someone passes cmd line arg "ro" + * But that is unlikely so keeping it as it is + */ + root_mountflags &= ~MS_RDONLY; + + console_verbose(); + +#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif + +} + +/* + * Get CPU information for use by the procfs. + */ + +#define cpu_to_ptr(c) ((void *)(0xFFFF0000 | (unsigned int)(c))) +#define ptr_to_cpu(p) (~0xFFFF0000UL & (unsigned int)(p)) + +static int show_cpuinfo(struct seq_file *m, void *v) +{ + char *str; + int cpu_id = ptr_to_cpu(v); + + str = (char *)__get_free_page(GFP_TEMPORARY); + if (!str) + goto done; + + seq_printf(m, "ARC700 #%d\n", cpu_id); + + seq_printf(m, "Bogo MIPS : \t%lu.%02lu\n", + loops_per_jiffy / (500000 / HZ), + (loops_per_jiffy / (5000 / HZ)) % 100); + + free_page((unsigned long)str); +done: + seq_printf(m, "\n\n"); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + /* + * Callback returns cpu-id to iterator for show routine, NULL to stop. + * However since NULL is also a valid cpu-id (0), we use a round-about + * way to pass it w/o having to kmalloc/free a 2 byte string. + * Encode cpu-id as 0xFFcccc, which is decoded by show routine. + */ + return *pos < num_possible_cpus() ? cpu_to_ptr(*pos) : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +const struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo +}; + +static DEFINE_PER_CPU(struct cpu, cpu_topology); + +static int __init topology_init(void) +{ + int cpu; + + for_each_present_cpu(cpu) + register_cpu(&per_cpu(cpu_topology, cpu), cpu); + + return 0; +} + +subsys_initcall(topology_init); diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c new file mode 100644 index 000000000000..63da3478521f --- /dev/null +++ b/arch/arc/mm/init.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include +#ifdef CONFIG_BLOCK_DEV_RAM +#include +#endif +#include +#include +#include +#include +#include +#include + +pgd_t swapper_pg_dir[PTRS_PER_PGD] __aligned(PAGE_SIZE); +char empty_zero_page[PAGE_SIZE] __aligned(PAGE_SIZE); +EXPORT_SYMBOL(empty_zero_page); + +/* Default tot mem from .config */ +static unsigned long arc_mem_sz = CONFIG_ARC_PLAT_SDRAM_SIZE; + +/* User can over-ride above with "mem=nnn[KkMm]" in cmdline */ +static int __init setup_mem_sz(char *str) +{ + arc_mem_sz = memparse(str, NULL) & PAGE_MASK; + + /* early console might not be setup yet - it will show up later */ + pr_info("\"mem=%s\": mem sz set to %ldM\n", str, TO_MB(arc_mem_sz)); + + return 0; +} +early_param("mem", setup_mem_sz); + +/* + * First memory setup routine called from setup_arch() + * 1. setup swapper's mm @init_mm + * 2. Count the pages we have and setup bootmem allocator + * 3. zone setup + */ +void __init setup_arch_memory(void) +{ + unsigned long zones_size[MAX_NR_ZONES] = { 0, 0 }; + unsigned long end_mem = CONFIG_LINUX_LINK_BASE + arc_mem_sz; + + init_mm.start_code = (unsigned long)_text; + init_mm.end_code = (unsigned long)_etext; + init_mm.end_data = (unsigned long)_edata; + init_mm.brk = (unsigned long)_end; + + /* + * We do it here, so that memory is correctly instantiated + * even if "mem=xxx" cmline over-ride is not given + */ + memblock_add(CONFIG_LINUX_LINK_BASE, arc_mem_sz); + + /*------------- externs in mm need setting up ---------------*/ + + /* first page of system - kernel .vector starts here */ + min_low_pfn = PFN_DOWN(CONFIG_LINUX_LINK_BASE); + + /* Last usable page of low mem (no HIGHMEM yet for ARC port) */ + max_low_pfn = max_pfn = PFN_DOWN(end_mem); + + max_mapnr = num_physpages = max_low_pfn - min_low_pfn; + + /*------------- reserve kernel image -----------------------*/ + memblock_reserve(CONFIG_LINUX_LINK_BASE, + __pa(_end) - CONFIG_LINUX_LINK_BASE); + + memblock_dump_all(); + + /*-------------- node setup --------------------------------*/ + memset(zones_size, 0, sizeof(zones_size)); + zones_size[ZONE_NORMAL] = num_physpages; + + /* + * We can't use the helper free_area_init(zones[]) because it uses + * PAGE_OFFSET to compute the @min_low_pfn which would be wrong + * when our kernel doesn't start at PAGE_OFFSET, i.e. + * PAGE_OFFSET != CONFIG_LINUX_LINK_BASE + */ + free_area_init_node(0, /* node-id */ + zones_size, /* num pages per zone */ + min_low_pfn, /* first pfn of node */ + NULL); /* NO holes */ +} + +/* + * mem_init - initializes memory + * + * Frees up bootmem + * Calculates and displays memory available/used + */ +void __init mem_init(void) +{ + int codesize, datasize, initsize, reserved_pages, free_pages; + int tmp; + + high_memory = (void *)(CONFIG_LINUX_LINK_BASE + arc_mem_sz); + + totalram_pages = free_all_bootmem(); + + /* count all reserved pages [kernel code/data/mem_map..] */ + reserved_pages = 0; + for (tmp = 0; tmp < max_mapnr; tmp++) + if (PageReserved(mem_map + tmp)) + reserved_pages++; + + /* XXX: nr_free_pages() is equivalent */ + free_pages = max_mapnr - reserved_pages; + + /* + * For the purpose of display below, split the "reserve mem" + * kernel code/data is already shown explicitly, + * Show any other reservations (mem_map[ ] et al) + */ + reserved_pages -= (((unsigned int)_end - CONFIG_LINUX_LINK_BASE) >> + PAGE_SHIFT); + + codesize = _etext - _text; + datasize = _end - _etext; + initsize = __init_end - __init_begin; + + pr_info("Memory Available: %dM / %ldM (%dK code, %dK data, %dK init, %dK reserv)\n", + PAGES_TO_MB(free_pages), + TO_MB(arc_mem_sz), + TO_KB(codesize), TO_KB(datasize), TO_KB(initsize), + PAGES_TO_KB(reserved_pages)); +} + +static void __init free_init_pages(const char *what, unsigned long begin, + unsigned long end) +{ + unsigned long addr; + + pr_info("Freeing %s: %ldk [%lx] to [%lx]\n", + what, TO_KB(end - begin), begin, end); + + /* need to check that the page we free is not a partial page */ + for (addr = begin; addr + PAGE_SIZE <= end; addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + init_page_count(virt_to_page(addr)); + free_page(addr); + totalram_pages++; + } +} + +/* + * free_initmem: Free all the __init memory. + */ +void __init_refok free_initmem(void) +{ + free_init_pages("unused kernel memory", + (unsigned long)__init_begin, + (unsigned long)__init_end); +} + +#ifdef CONFIG_BLK_DEV_INITRD +void __init free_initrd_mem(unsigned long start, unsigned long end) +{ + free_init_pages("initrd memory", start, end); +} +#endif diff --git a/arch/arc/plat-arcfpga/platform.c b/arch/arc/plat-arcfpga/platform.c new file mode 100644 index 000000000000..7b5dcab5becb --- /dev/null +++ b/arch/arc/plat-arcfpga/platform.c @@ -0,0 +1,29 @@ +/* + * ARC FPGA Platform support code + * + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include + +/* + * Early Platform Initialization called from setup_arch() + */ +void __init arc_platform_early_init(void) +{ + pr_info("[plat-arcfpga]: registering early dev resources\n"); +} + +int __init fpga_plat_init(void) +{ + pr_info("[plat-arcfpga]: registering device resources\n"); + + return 0; +} +arch_initcall(fpga_plat_init); From ee36d1722112f33725ec1a7fc02f6c46e630fd27 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:20 +0530 Subject: [PATCH 2825/4607] ARC: [plat-arcfpga] Static platform device for CONFIG_SERIAL_ARC N.B. This is old style of hardcoding platform device specific info in code and it's instantiation thererof using platform_add_devices(). Subsequent patches replace this with DeviceTree based runtime probe. This patch has been retained just as an example of "don't-do-this" for newer kernel ports. Signed-off-by: Vineet Gupta Cc: Arnd Bergmann --- arch/arc/include/asm/irq.h | 1 + arch/arc/plat-arcfpga/include/plat/irq.h | 27 +++++ arch/arc/plat-arcfpga/include/plat/memmap.h | 31 ++++++ arch/arc/plat-arcfpga/platform.c | 108 ++++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100644 arch/arc/plat-arcfpga/include/plat/irq.h create mode 100644 arch/arc/plat-arcfpga/include/plat/memmap.h diff --git a/arch/arc/include/asm/irq.h b/arch/arc/include/asm/irq.h index 514b30287445..c91b2e4344a1 100644 --- a/arch/arc/include/asm/irq.h +++ b/arch/arc/include/asm/irq.h @@ -13,6 +13,7 @@ #define TIMER0_IRQ 3 #define TIMER1_IRQ 4 +#include /* Board Specific IRQ assignments */ #include extern void __init arc_init_IRQ(void); diff --git a/arch/arc/plat-arcfpga/include/plat/irq.h b/arch/arc/plat-arcfpga/include/plat/irq.h new file mode 100644 index 000000000000..b34e08734c65 --- /dev/null +++ b/arch/arc/plat-arcfpga/include/plat/irq.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: Feb 2009 + * -For AA4 board, IRQ assignments to peripherals + */ + +#ifndef __PLAT_IRQ_H +#define __PLAT_IRQ_H + +#define NR_IRQS 16 + +#define UART0_IRQ 5 +#define UART1_IRQ 10 +#define UART2_IRQ 11 + +#define VMAC_IRQ 6 + +#define IDE_IRQ 13 +#define PCI_IRQ 14 +#define PS2_IRQ 15 + +#endif diff --git a/arch/arc/plat-arcfpga/include/plat/memmap.h b/arch/arc/plat-arcfpga/include/plat/memmap.h new file mode 100644 index 000000000000..1663f3388085 --- /dev/null +++ b/arch/arc/plat-arcfpga/include/plat/memmap.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * vineetg: Feb 2009 + * -For AA4 board, System Memory Map for Peripherals etc + */ + +#ifndef __PLAT_MEMMAP_H +#define __PLAT_MEMMAP_H + +#define UART0_BASE 0xC0FC1000 +#define UART1_BASE 0xC0FC1100 + +#define VMAC_REG_BASEADDR 0xC0FC2000 + +#define IDE_CONTROLLER_BASE 0xC0FC9000 + +#define AHB_PCI_HOST_BRG_BASE 0xC0FD0000 + +#define PGU_BASEADDR 0xC0FC8000 +#define VLCK_ADDR 0xC0FCF028 + +#define BVCI_LAT_UNIT_BASE 0xC0FED000 + +#define PS2_BASE_ADDR 0xC0FCC000 + +#endif diff --git a/arch/arc/plat-arcfpga/platform.c b/arch/arc/plat-arcfpga/platform.c index 7b5dcab5becb..5388b31e2ead 100644 --- a/arch/arc/plat-arcfpga/platform.c +++ b/arch/arc/plat-arcfpga/platform.c @@ -10,7 +10,102 @@ #include #include +#include #include +#include +#include +#include +#include +#include + +/*----------------------- Platform Devices -----------------------------*/ + +#if defined(CONFIG_SERIAL_ARC) || defined(CONFIG_SERIAL_ARC_MODULE) + +static unsigned long arc_uart_info[] = { + CONFIG_ARC_SERIAL_BAUD, /* uart->baud */ + -1, /* uart->port.uartclk */ + -1, /* uart->is_emulated (runtime @running_on_hw) */ + 0 +}; + +#define ARC_UART_DEV(n) \ + \ +static struct resource arc_uart##n##_res[] = { \ + { \ + .start = UART##n##_BASE, \ + .end = UART##n##_BASE + 0xFF, \ + .flags = IORESOURCE_MEM, \ + }, \ + { \ + .start = UART##n##_IRQ, \ + .end = UART##n##_IRQ, \ + .flags = IORESOURCE_IRQ, \ + }, \ +}; \ + \ +static struct platform_device arc_uart##n##_dev = { \ + .name = "arc-uart", \ + .id = n, \ + .num_resources = ARRAY_SIZE(arc_uart##n##_res), \ + .resource = arc_uart##n##_res, \ + .dev = { \ + .platform_data = &arc_uart_info, \ + }, \ +} + +ARC_UART_DEV(0); +#if CONFIG_SERIAL_ARC_NR_PORTS > 1 +ARC_UART_DEV(1); +#endif + +static struct platform_device *fpga_early_devs[] __initdata = { +#if defined(CONFIG_SERIAL_ARC_CONSOLE) + &arc_uart0_dev, +#endif +}; + +static void arc_fpga_serial_init(void) +{ + arc_uart_info[1] = arc_get_core_freq(); + + /* To let driver workaround ISS bug: baudh Reg can't be set to 0 */ + arc_uart_info[2] = !running_on_hw; + + early_platform_add_devices(fpga_early_devs, + ARRAY_SIZE(fpga_early_devs)); + + /* + * ARC console driver registers itself as an early platform driver + * of class "earlyprintk". + * Install it here, followed by probe of devices. + * The installation here doesn't require earlyprintk in command line + * To do so however, replace the lines below with + * parse_early_param(); + * early_platform_driver_probe("earlyprintk", 1, 1); + * ^^ + */ + early_platform_driver_register_all("earlyprintk"); + early_platform_driver_probe("earlyprintk", 1, 0); + + /* + * This is to make sure that arc uart would be preferred console + * despite one/more of following: + * -command line lacked "console=ttyARC0" or + * -CONFIG_VT_CONSOLE was enabled (for no reason whatsoever) + * Note that this needs to be done after above early console is reg, + * otherwise the early console never gets a chance to run. + */ + add_preferred_console("ttyARC", 0, "115200"); +} + +#else + +static void arc_fpga_serial_init(void) +{ +} + +#endif /* CONFIG_SERIAL_ARC */ /* * Early Platform Initialization called from setup_arch() @@ -18,12 +113,25 @@ void __init arc_platform_early_init(void) { pr_info("[plat-arcfpga]: registering early dev resources\n"); + + arc_fpga_serial_init(); } +static struct platform_device *fpga_devs[] __initdata = { +#if defined(CONFIG_SERIAL_ARC) || defined(CONFIG_SERIAL_ARC_MODULE) + &arc_uart0_dev, +#if CONFIG_SERIAL_ARC_NR_PORTS > 1 + &arc_uart1_dev, +#endif +#endif +}; + int __init fpga_plat_init(void) { pr_info("[plat-arcfpga]: registering device resources\n"); + platform_add_devices(fpga_devs, ARRAY_SIZE(fpga_devs)); + return 0; } arch_initcall(fpga_plat_init); From 999159a5381bff3bd6f688c5d20fbec9d8789e53 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Tue, 22 Jan 2013 17:00:52 +0530 Subject: [PATCH 2826/4607] ARC: [DeviceTree] Basic support This is minimal infrastructure needed for devicetree work. It uses an a sample "skeleton" devicetree - embedded in kernel image - to print the board, manufacturer by parsing the top-level "compatible" string. As of now we don't need any additional "board" specific "machine_desc". TODO: support interpreting the command line as boot-loader passed dtb Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Cc: Grant Likely Cc: devicetree-discuss@lists.ozlabs.org Cc: Rob Herring Cc: James Hogan Reviewed-by: Rob Herring Reviewed-by: James Hogan --- arch/arc/Kconfig | 9 +++++ arch/arc/Makefile | 9 +++++ arch/arc/boot/dts/Makefile | 13 +++++++ arch/arc/boot/dts/skeleton.dts | 10 ++++++ arch/arc/boot/dts/skeleton.dtsi | 21 +++++++++++ arch/arc/include/asm/prom.h | 15 ++++++++ arch/arc/include/asm/sections.h | 1 + arch/arc/kernel/Makefile | 1 + arch/arc/kernel/devtree.c | 64 +++++++++++++++++++++++++++++++++ arch/arc/kernel/setup.c | 9 +++++ arch/arc/mm/init.c | 13 +++++++ 11 files changed, 165 insertions(+) create mode 100644 arch/arc/boot/dts/Makefile create mode 100644 arch/arc/boot/dts/skeleton.dts create mode 100644 arch/arc/boot/dts/skeleton.dtsi create mode 100644 arch/arc/include/asm/prom.h create mode 100644 arch/arc/kernel/devtree.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index a3538493e353..76668579b543 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -24,8 +24,11 @@ config ARC select GENERIC_SMP_IDLE_THREAD select HAVE_GENERIC_HARDIRQS select HAVE_MEMBLOCK + select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM + select OF + select OF_EARLY_FLATTREE config SCHED_OMIT_FRAME_POINTER def_bool y @@ -320,6 +323,12 @@ config CMDLINE_UBOOT to it. kernel startup code will copy the string into cmdline buffer and also append CONFIG_CMDLINE. +config ARC_BUILTIN_DTB_NAME + string "Built in DTB" + help + Set the name of the DTB to embed in the vmlinux binary + Leaving it blank selects the minimal "skeleton" dtb + source "kernel/Kconfig.preempt" endmenu # "ARC Architecture Configuration" diff --git a/arch/arc/Makefile b/arch/arc/Makefile index 4d52a3bb68a0..29b5fcd9c4b6 100644 --- a/arch/arc/Makefile +++ b/arch/arc/Makefile @@ -83,6 +83,9 @@ head-y := arch/arc/kernel/head.o # See arch/arc/Kbuild for content of core part of the kernel core-y += arch/arc/ +# w/o this dtb won't embed into kernel binary +core-y += arch/arc/boot/dts/ + # w/o this ifneq, make ARCH=arc clean was crapping out ifneq ($(platform-y),) core-y += arch/arc/plat-$(PLATFORM)/ @@ -101,6 +104,12 @@ bootpImage: vmlinux uImage: vmlinux $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ +%.dtb %.dtb.S %.dtb.o: scripts + $(Q)$(MAKE) $(build)=$(boot)/dts $(boot)/dts/$@ + +dtbs: scripts + $(Q)$(MAKE) $(build)=$(boot)/dts dtbs + archclean: $(Q)$(MAKE) $(clean)=$(boot) diff --git a/arch/arc/boot/dts/Makefile b/arch/arc/boot/dts/Makefile new file mode 100644 index 000000000000..17dc7b8b05fa --- /dev/null +++ b/arch/arc/boot/dts/Makefile @@ -0,0 +1,13 @@ +# Built-in dtb +builtindtb-y := skeleton + +ifneq ($(CONFIG_ARC_BUILTIN_DTB_NAME),"") + builtindtb-y := $(patsubst "%",%,$(CONFIG_ARC_BUILTIN_DTB_NAME)) +endif + +obj-y += $(builtindtb-y).dtb.o +targets += $(builtindtb-y).dtb + +dtbs: $(addprefix $(obj)/, $(builtindtb-y).dtb) + +clean-files := *.dtb diff --git a/arch/arc/boot/dts/skeleton.dts b/arch/arc/boot/dts/skeleton.dts new file mode 100644 index 000000000000..25a84fb5b3dc --- /dev/null +++ b/arch/arc/boot/dts/skeleton.dts @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + */ +/dts-v1/; + +/include/ "skeleton.dtsi" diff --git a/arch/arc/boot/dts/skeleton.dtsi b/arch/arc/boot/dts/skeleton.dtsi new file mode 100644 index 000000000000..9b357d802a10 --- /dev/null +++ b/arch/arc/boot/dts/skeleton.dtsi @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + */ + +/* + * Skeleton device tree; the bare minimum needed to boot; just include and + * add a compatible value. + */ + +/ { + compatible = "snps,arc"; + #address-cells = <1>; + #size-cells = <1>; + chosen { }; + aliases { }; + memory { device_type = "memory"; reg = <0 0>; }; +}; diff --git a/arch/arc/include/asm/prom.h b/arch/arc/include/asm/prom.h new file mode 100644 index 000000000000..f54489bc4eca --- /dev/null +++ b/arch/arc/include/asm/prom.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_PROM_H_ +#define _ASM_ARC_PROM_H_ + +#define HAVE_ARCH_DEVTREE_FIXUPS +extern int __init setup_machine_fdt(void *dt); + +#endif diff --git a/arch/arc/include/asm/sections.h b/arch/arc/include/asm/sections.h index fc15f2e1a12e..6fc1159dfefe 100644 --- a/arch/arc/include/asm/sections.h +++ b/arch/arc/include/asm/sections.h @@ -13,5 +13,6 @@ extern char _int_vec_base_lds[]; extern char __arc_dccm_base[]; +extern char __dtb_start[]; #endif diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index 6d834312f755..c9ec0662a4ec 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -7,6 +7,7 @@ obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o clk.o +obj-y += devtree.o obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o CFLAGS_fpu.o += -mdpfp diff --git a/arch/arc/kernel/devtree.c b/arch/arc/kernel/devtree.c new file mode 100644 index 000000000000..48e157efad15 --- /dev/null +++ b/arch/arc/kernel/devtree.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * Based on reduced version of METAG + * + * 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 +#include +#include +#include +#include +#include + +/* called from unflatten_device_tree() to bootstrap devicetree itself */ +void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) +{ + return __va(memblock_alloc(size, align)); +} + +/** + * setup_machine_fdt - Machine setup when an dtb was passed to the kernel + * @dt: virtual address pointer to dt blob + * + * If a dtb was passed to the kernel, then use it to choose the correct + * machine_desc and to setup the system. + */ +int __init setup_machine_fdt(void *dt) +{ + struct boot_param_header *devtree = dt; + unsigned long dt_root; + char *model, *compat; + char manufacturer[16]; + + /* check device tree validity */ + if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) + return 1; + + /* Search the mdescs for the 'best' compatible value match */ + initial_boot_params = devtree; + dt_root = of_get_flat_dt_root(); + + /* compat = "," */ + compat = of_get_flat_dt_prop(dt_root, "compatible", NULL); + if (!compat) + compat = ""; + + model = strchr(compat, ','); + if (model) + model++; + + strlcpy(manufacturer, compat, model ? model - compat : strlen(compat)); + + pr_info("Board \"%s\" from %s (Manufacturer)\n", model, manufacturer); + + /* Retrieve various information from the /chosen node */ + of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); + + return 0; +} diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 82ac20603398..27aebd6d9513 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -20,6 +22,7 @@ #include #include #include +#include #define FIX_PTR(x) __asm__ __volatile__(";" : "+r"(x)) @@ -57,6 +60,8 @@ void __init __attribute__((weak)) arc_platform_early_init(void) void __init setup_arch(char **cmdline_p) { + int rc; + #ifdef CONFIG_CMDLINE_UBOOT /* Make sure that a whitespace is inserted before */ strlcat(command_line, " ", sizeof(command_line)); @@ -71,6 +76,8 @@ void __init setup_arch(char **cmdline_p) strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); *cmdline_p = command_line; + rc = setup_machine_fdt(__dtb_start); + /* To force early parsing of things like mem=xxx */ parse_early_param(); @@ -81,6 +88,8 @@ void __init setup_arch(char **cmdline_p) setup_arch_memory(); + unflatten_device_tree(); + /* Can be issue if someone passes cmd line arg "ro" * But that is unlikely so keeping it as it is */ diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c index 63da3478521f..682cf5740483 100644 --- a/arch/arc/mm/init.c +++ b/arch/arc/mm/init.c @@ -39,6 +39,11 @@ static int __init setup_mem_sz(char *str) } early_param("mem", setup_mem_sz); +void __init early_init_dt_add_memory_arch(u64 base, u64 size) +{ + pr_err("%s(%llx, %llx)\n", __func__, base, size); +} + /* * First memory setup routine called from setup_arch() * 1. setup swapper's mm @init_mm @@ -169,3 +174,11 @@ void __init free_initrd_mem(unsigned long start, unsigned long end) free_init_pages("initrd memory", start, end); } #endif + +#ifdef CONFIG_OF_FLATTREE +void __init early_init_dt_setup_initrd_arch(unsigned long start, + unsigned long end) +{ + pr_err("%s(%lx, %lx)\n", __func__, start, end); +} +#endif /* CONFIG_OF_FLATTREE */ From 450dd430bf45ab212a91acfb9bed2528d17f30cd Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:20 +0530 Subject: [PATCH 2827/4607] ARC: [DeviceTree] Convert some Kconfig items to runtime values * mem size now runtime configured (prev CONFIG_ARC_PLAT_SDRAM_SIZE) * core cpu clk runtime configured (prev CONFIG_ARC_PLAT_CLK) Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Cc: Grant Likely --- arch/arc/Kconfig | 12 ------------ arch/arc/boot/dts/skeleton.dtsi | 6 +++++- arch/arc/include/asm/clk.h | 2 ++ arch/arc/kernel/clk.c | 12 +++++++++++- arch/arc/kernel/devtree.c | 13 +++++++++++++ arch/arc/mm/init.c | 9 ++++++--- 6 files changed, 37 insertions(+), 17 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 76668579b543..7db978570fb8 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -249,10 +249,6 @@ source "arch/arc/plat-arcfpga/Kconfig" #New platform adds here -config ARC_PLAT_CLK - int "Clk speed in Hz" - default "80000000" - config LINUX_LINK_BASE hex "Linux Link Address" default "0x80000000" @@ -266,14 +262,6 @@ config LINUX_LINK_BASE Linux needs to be scooted a bit. If you don't know what the above means, leave this setting alone. -config ARC_PLAT_SDRAM_SIZE - hex "SD RAM Size" - default "0x10000000" - help - Implies the amount of SDRAM/DRAM Linux is going to claim/own. - The actual memory itself could be larger than this number. But for - all software purposes, this is the amt of memory. - endmenu # "Platform Board Configuration" config ARC_STACK_NONEXEC diff --git a/arch/arc/boot/dts/skeleton.dtsi b/arch/arc/boot/dts/skeleton.dtsi index 9b357d802a10..eb8b77367352 100644 --- a/arch/arc/boot/dts/skeleton.dtsi +++ b/arch/arc/boot/dts/skeleton.dtsi @@ -13,9 +13,13 @@ / { compatible = "snps,arc"; + clock-frequency = <80000000>; /* 80 MHZ */ #address-cells = <1>; #size-cells = <1>; chosen { }; aliases { }; - memory { device_type = "memory"; reg = <0 0>; }; + memory { + device_type = "memory"; + reg = <0x00000000 0x10000000>; /* 256M */ + }; }; diff --git a/arch/arc/include/asm/clk.h b/arch/arc/include/asm/clk.h index 61956436c75c..bf9d29f5bd53 100644 --- a/arch/arc/include/asm/clk.h +++ b/arch/arc/include/asm/clk.h @@ -17,4 +17,6 @@ static inline unsigned long arc_get_core_freq(void) return core_freq; } +extern int arc_set_core_freq(unsigned long); + #endif diff --git a/arch/arc/kernel/clk.c b/arch/arc/kernel/clk.c index 64925db9eb5e..66ce0dc917fb 100644 --- a/arch/arc/kernel/clk.c +++ b/arch/arc/kernel/clk.c @@ -8,4 +8,14 @@ #include -unsigned long core_freq = CONFIG_ARC_PLAT_CLK; +unsigned long core_freq = 800000000; + +/* + * As of now we default to device-tree provided clock + * In future we can determine this in early boot + */ +int arc_set_core_freq(unsigned long freq) +{ + core_freq = freq; + return 0; +} diff --git a/arch/arc/kernel/devtree.c b/arch/arc/kernel/devtree.c index 48e157efad15..c8166dc02c38 100644 --- a/arch/arc/kernel/devtree.c +++ b/arch/arc/kernel/devtree.c @@ -15,6 +15,7 @@ #include #include #include +#include /* called from unflatten_device_tree() to bootstrap devicetree itself */ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) @@ -34,7 +35,9 @@ int __init setup_machine_fdt(void *dt) struct boot_param_header *devtree = dt; unsigned long dt_root; char *model, *compat; + void *clk; char manufacturer[16]; + unsigned long len; /* check device tree validity */ if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) @@ -60,5 +63,15 @@ int __init setup_machine_fdt(void *dt) /* Retrieve various information from the /chosen node */ of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); + /* Initialize {size,address}-cells info */ + of_scan_flat_dt(early_init_dt_scan_root, NULL); + + /* Setup memory, calling early_init_dt_add_memory_arch */ + of_scan_flat_dt(early_init_dt_scan_memory, NULL); + + clk = of_get_flat_dt_prop(dt_root, "clock-frequency", &len); + if (clk) + arc_set_core_freq(of_read_ulong(clk, len/4)); + return 0; } diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c index 682cf5740483..caf797de23fc 100644 --- a/arch/arc/mm/init.c +++ b/arch/arc/mm/init.c @@ -25,7 +25,7 @@ char empty_zero_page[PAGE_SIZE] __aligned(PAGE_SIZE); EXPORT_SYMBOL(empty_zero_page); /* Default tot mem from .config */ -static unsigned long arc_mem_sz = CONFIG_ARC_PLAT_SDRAM_SIZE; +static unsigned long arc_mem_sz = 0x20000000; /* some default */ /* User can over-ride above with "mem=nnn[KkMm]" in cmdline */ static int __init setup_mem_sz(char *str) @@ -41,7 +41,8 @@ early_param("mem", setup_mem_sz); void __init early_init_dt_add_memory_arch(u64 base, u64 size) { - pr_err("%s(%llx, %llx)\n", __func__, base, size); + arc_mem_sz = size & PAGE_MASK; + pr_info("Memory size set via devicetree %ldM\n", TO_MB(arc_mem_sz)); } /* @@ -62,7 +63,9 @@ void __init setup_arch_memory(void) /* * We do it here, so that memory is correctly instantiated - * even if "mem=xxx" cmline over-ride is not given + * even if "mem=xxx" cmline over-ride is given and/or + * DT has memory node. Each causes an update to @arc_mem_sz + * and we finally add memory one here */ memblock_add(CONFIG_LINUX_LINK_BASE, arc_mem_sz); From abe11ddea1d759f9995a9a4636c28c9b40856ca8 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:21 +0530 Subject: [PATCH 2828/4607] ARC: [plat-arcfpga]: Enabling DeviceTree for Angel4 board * arc-uart platform device now populated dynamically, using of_platform_populate() - applies to any other device whatsoever. * uart in turn requires incore arc-intc to be also present in DT * A irq-domain needs to be instantiated for IRQ requests by DT probed device (e.g. arc-uart) TODO: switch over to linear irq domain once all devs have been transitioned to DT Signed-off-by: Vineet Gupta Cc: Grant Likely Cc: Arnd Bergmann --- .../devicetree/bindings/arc/interrupts.txt | 24 +++++ arch/arc/boot/dts/Makefile | 2 +- arch/arc/boot/dts/angel4.dts | 55 +++++++++++ arch/arc/boot/dts/skeleton.dtsi | 12 +++ arch/arc/kernel/irq.c | 38 ++++++-- arch/arc/plat-arcfpga/platform.c | 97 +++++++++---------- 6 files changed, 170 insertions(+), 58 deletions(-) create mode 100644 Documentation/devicetree/bindings/arc/interrupts.txt create mode 100644 arch/arc/boot/dts/angel4.dts diff --git a/Documentation/devicetree/bindings/arc/interrupts.txt b/Documentation/devicetree/bindings/arc/interrupts.txt new file mode 100644 index 000000000000..9a5d562435ea --- /dev/null +++ b/Documentation/devicetree/bindings/arc/interrupts.txt @@ -0,0 +1,24 @@ +* ARC700 incore Interrupt Controller + + The core interrupt controller provides 32 prioritised interrupts (2 levels) + to ARC700 core. + +Properties: + +- compatible: "snps,arc700-intc" +- interrupt-controller: This is an interrupt controller. +- #interrupt-cells: Must be <1>. + + Single Cell "interrupts" property of a device specifies the IRQ number + between 0 to 31 + + intc accessed via the special ARC AUX register interface, hence "reg" property + is not specified. + +Example: + + intc: interrupt-controller { + compatible = "snps,arc700-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; diff --git a/arch/arc/boot/dts/Makefile b/arch/arc/boot/dts/Makefile index 17dc7b8b05fa..5776835d583f 100644 --- a/arch/arc/boot/dts/Makefile +++ b/arch/arc/boot/dts/Makefile @@ -1,5 +1,5 @@ # Built-in dtb -builtindtb-y := skeleton +builtindtb-y := angel4 ifneq ($(CONFIG_ARC_BUILTIN_DTB_NAME),"") builtindtb-y := $(patsubst "%",%,$(CONFIG_ARC_BUILTIN_DTB_NAME)) diff --git a/arch/arc/boot/dts/angel4.dts b/arch/arc/boot/dts/angel4.dts new file mode 100644 index 000000000000..4c188d5bc10c --- /dev/null +++ b/arch/arc/boot/dts/angel4.dts @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + */ +/dts-v1/; + +/include/ "skeleton.dtsi" + +/ { + compatible = "snps,arc-angel4"; + clock-frequency = <80000000>; /* 80 MHZ */ + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&intc>; + + chosen { + bootargs = "console=ttyARC0,115200n8"; + }; + + aliases { + serial0 = &arcuart0; + }; + + memory { + device_type = "memory"; + reg = <0x00000000 0x10000000>; /* 256M */ + }; + + fpga { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + + /* child and parent address space 1:1 mapped */ + ranges; + + intc: interrupt-controller { + compatible = "snps,arc700-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + + arcuart0: serial@c0fc1000 { + compatible = "snps,arc-uart"; + reg = <0xc0fc1000 0x100>; + interrupts = <5>; + clock-frequency = <80000000>; + baud = <115200>; + status = "okay"; + }; + }; +}; diff --git a/arch/arc/boot/dts/skeleton.dtsi b/arch/arc/boot/dts/skeleton.dtsi index eb8b77367352..a870bdd5e404 100644 --- a/arch/arc/boot/dts/skeleton.dtsi +++ b/arch/arc/boot/dts/skeleton.dtsi @@ -18,6 +18,18 @@ #size-cells = <1>; chosen { }; aliases { }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "snps,arc770d"; + reg = <0>; + }; + }; + memory { device_type = "memory"; reg = <0x00000000 0x10000000>; /* 256M */ diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c index 2f399eb88f9d..3c18e66386c7 100644 --- a/arch/arc/kernel/irq.c +++ b/arch/arc/kernel/irq.c @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include @@ -59,16 +61,40 @@ static struct irq_chip onchip_intc = { .irq_unmask = arc_unmask_irq, }; +static int arc_intc_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + if (irq == TIMER0_IRQ) + irq_set_chip_and_handler(irq, &onchip_intc, handle_percpu_irq); + else + irq_set_chip_and_handler(irq, &onchip_intc, handle_level_irq); + + return 0; +} + +static const struct irq_domain_ops arc_intc_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = arc_intc_domain_map, +}; + +static struct irq_domain *root_domain; + void __init init_onchip_IRQ(void) { - int i; + struct device_node *intc = NULL; - for (i = 0; i < NR_IRQS; i++) - irq_set_chip_and_handler(i, &onchip_intc, handle_level_irq); + intc = of_find_compatible_node(NULL, NULL, "snps,arc700-intc"); + if(!intc) + panic("DeviceTree Missing incore intc\n"); -#ifdef CONFIG_SMP - irq_set_chip_and_handler(TIMER0_IRQ, &onchip_intc, handle_percpu_irq); -#endif + root_domain = irq_domain_add_legacy(intc, NR_IRQS, 0, 0, + &arc_intc_domain_ops, NULL); + + if (!root_domain) + panic("root irq domain not avail\n"); + + /* with this we don't need to export root_domain */ + irq_set_default_host(root_domain); } /* diff --git a/arch/arc/plat-arcfpga/platform.c b/arch/arc/plat-arcfpga/platform.c index 5388b31e2ead..33bcac8bd6b8 100644 --- a/arch/arc/plat-arcfpga/platform.c +++ b/arch/arc/plat-arcfpga/platform.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -20,58 +21,56 @@ /*----------------------- Platform Devices -----------------------------*/ -#if defined(CONFIG_SERIAL_ARC) || defined(CONFIG_SERIAL_ARC_MODULE) - static unsigned long arc_uart_info[] = { - CONFIG_ARC_SERIAL_BAUD, /* uart->baud */ - -1, /* uart->port.uartclk */ - -1, /* uart->is_emulated (runtime @running_on_hw) */ + 0, /* uart->is_emulated (runtime @running_on_hw) */ + 0, /* uart->port.uartclk */ + 0, /* uart->baud */ 0 }; -#define ARC_UART_DEV(n) \ - \ -static struct resource arc_uart##n##_res[] = { \ - { \ - .start = UART##n##_BASE, \ - .end = UART##n##_BASE + 0xFF, \ - .flags = IORESOURCE_MEM, \ - }, \ - { \ - .start = UART##n##_IRQ, \ - .end = UART##n##_IRQ, \ - .flags = IORESOURCE_IRQ, \ - }, \ -}; \ - \ -static struct platform_device arc_uart##n##_dev = { \ - .name = "arc-uart", \ - .id = n, \ - .num_resources = ARRAY_SIZE(arc_uart##n##_res), \ - .resource = arc_uart##n##_res, \ - .dev = { \ - .platform_data = &arc_uart_info, \ - }, \ -} +#if defined(CONFIG_SERIAL_ARC_CONSOLE) +/* + * static platform data - but only for early serial + * TBD: derive this from a special DT node + */ +static struct resource arc_uart0_res[] = { + { + .start = UART0_BASE, + .end = UART0_BASE + 0xFF, + .flags = IORESOURCE_MEM, + }, + { + .start = UART0_IRQ, + .end = UART0_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; -ARC_UART_DEV(0); -#if CONFIG_SERIAL_ARC_NR_PORTS > 1 -ARC_UART_DEV(1); -#endif +static struct platform_device arc_uart0_dev = { + .name = "arc-uart", + .id = 0, + .num_resources = ARRAY_SIZE(arc_uart0_res), + .resource = arc_uart0_res, + .dev = { + .platform_data = &arc_uart_info, + }, +}; static struct platform_device *fpga_early_devs[] __initdata = { -#if defined(CONFIG_SERIAL_ARC_CONSOLE) &arc_uart0_dev, -#endif }; +#endif static void arc_fpga_serial_init(void) { + /* To let driver workaround ISS bug: baudh Reg can't be set to 0 */ + arc_uart_info[0] = !running_on_hw; + arc_uart_info[1] = arc_get_core_freq(); - /* To let driver workaround ISS bug: baudh Reg can't be set to 0 */ - arc_uart_info[2] = !running_on_hw; + arc_uart_info[2] = CONFIG_ARC_SERIAL_BAUD; +#if defined(CONFIG_SERIAL_ARC_CONSOLE) early_platform_add_devices(fpga_early_devs, ARRAY_SIZE(fpga_early_devs)); @@ -97,16 +96,9 @@ static void arc_fpga_serial_init(void) * otherwise the early console never gets a chance to run. */ add_preferred_console("ttyARC", 0, "115200"); +#endif } -#else - -static void arc_fpga_serial_init(void) -{ -} - -#endif /* CONFIG_SERIAL_ARC */ - /* * Early Platform Initialization called from setup_arch() */ @@ -117,20 +109,23 @@ void __init arc_platform_early_init(void) arc_fpga_serial_init(); } -static struct platform_device *fpga_devs[] __initdata = { +static struct of_dev_auxdata plat_auxdata_lookup[] __initdata = { #if defined(CONFIG_SERIAL_ARC) || defined(CONFIG_SERIAL_ARC_MODULE) - &arc_uart0_dev, -#if CONFIG_SERIAL_ARC_NR_PORTS > 1 - &arc_uart1_dev, -#endif + OF_DEV_AUXDATA("snps,arc-uart", UART0_BASE, "arc-uart", arc_uart_info), #endif + {} }; int __init fpga_plat_init(void) { pr_info("[plat-arcfpga]: registering device resources\n"); - platform_add_devices(fpga_devs, ARRAY_SIZE(fpga_devs)); + /* + * Traverses flattened DeviceTree - registering platform devices + * complete with their resources + */ + of_platform_populate(NULL, of_default_bus_match_table, + plat_auxdata_lookup, NULL); return 0; } From c08098f28e3f3830086b1b542c2d2646a84e109c Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:21 +0530 Subject: [PATCH 2829/4607] ARC: Last bits (stubs) to get to a running kernel with UART This was part of port buildup strategy from Arnd to have a minimal kernel at first and then add optional features (stacktracing, ptrace, smp, kprobes, oprofile....) Signed-off-by: Vineet Gupta --- arch/arc/kernel/ptrace.c | 26 ++++++++++++++++++++ arch/arc/kernel/stacktrace.c | 43 ++++++++++++++++++++++++++++++++++ arch/arc/kernel/troubleshoot.c | 17 ++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 arch/arc/kernel/ptrace.c create mode 100644 arch/arc/kernel/stacktrace.c create mode 100644 arch/arc/kernel/troubleshoot.c diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c new file mode 100644 index 000000000000..1cf944a77f33 --- /dev/null +++ b/arch/arc/kernel/ptrace.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 + +void ptrace_disable(struct task_struct *child) +{ +} + +long arch_ptrace(struct task_struct *child, long request, + unsigned long addr, unsigned long data) +{ + int ret = -EIO; + return ret; +} + + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ + return (const struct user_regset_view *)NULL; +} diff --git a/arch/arc/kernel/stacktrace.c b/arch/arc/kernel/stacktrace.c new file mode 100644 index 000000000000..b9d1646d1220 --- /dev/null +++ b/arch/arc/kernel/stacktrace.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include + +/*------------------------------------------------------------------------- + * APIs expected by various kernel sub-systems + *------------------------------------------------------------------------- + */ + +noinline void show_stacktrace(struct task_struct *tsk, struct pt_regs *regs) +{ + pr_info("\nStack Trace: NOT Available\n"); +} +EXPORT_SYMBOL(show_stacktrace); + +/* Expected by sched Code */ +void show_stack(struct task_struct *tsk, unsigned long *sp) +{ + show_stacktrace(tsk, NULL); +} + +/* Expected by Rest of kernel code */ +void dump_stack(void) +{ + show_stacktrace(NULL, NULL); +} +EXPORT_SYMBOL(dump_stack); + +/* Another API expected by schedular, shows up in "ps" as Wait Channel + * Ofcourse just returning schedule( ) would be pointless so unwind until + * the function is not in schedular code + */ +unsigned int get_wchan(struct task_struct *tsk) +{ + return 0; +} diff --git a/arch/arc/kernel/troubleshoot.c b/arch/arc/kernel/troubleshoot.c new file mode 100644 index 000000000000..80bfe2a15a98 --- /dev/null +++ b/arch/arc/kernel/troubleshoot.c @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 + */ + +#include + +void show_regs(struct pt_regs *regs) +{ +} + +void show_kernel_fault_diag(const char *str, struct pt_regs *regs, + unsigned long address, unsigned long cause_reg) +{ +} From 8872e9e513eba636c8d4266fe667660f73cd66e6 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:22 +0530 Subject: [PATCH 2830/4607] ARC: [plat-arcfpga] defconfig With this we get to a running kernel on ISS ---------------------------------->8----------------------------------- Linux version 3.8.0-rc3+ (vineetg@vineetg-Latitude) (gcc version 4.4.7 (ARCompact elf32 toolchain (built 20121213)) ) #3 Thu Jan 17 14:22:05 IST 2013 Board "arc-angel4" from snps (Manufacturer) Memory size set via devicetree 256M [plat-arcfpga]: registering early dev resources bootconsole [early_ARCuart0] enabled pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768 pcpu-alloc: [0] 0 Built 1 zonelists in Zone order, mobility grouping on. Total pages: 32624 Kernel command line: console=ttyARC0,115200n8 PID hash table entries: 1024 (order: -1, 4096 bytes) Dentry cache hash table entries: 32768 (order: 4, 131072 bytes) Inode-cache hash table entries: 16384 (order: 3, 65536 bytes) Memory Available: 248M / 256M (1312K code, 463K data, 4184K init, 1400K reserv) SLUB: Genslabs=12, HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1 NR_IRQS:16 Console: colour dummy device 80x25 Calibrating delay loop... 39.73 BogoMIPS (lpj=198656) pid_max: default: 32768 minimum: 301 Mount-cache hash table entries: 1024 devtmpfs: initialized [plat-arcfpga]: registering device resources bio: create slab at 0 Switching to clocksource ARC RTSC io scheduler noop registered (default) arc-uart: ttyARC0 at MMIO 0xc0fc1000 (irq = 5) is a arc-uart console [ttyARC0] enabled, bootconsole disabled console [ttyARC0] enabled, bootconsole disabled mousedev: PS/2 mouse device common for all mice Warning: unable to open an initial console. Freeing unused kernel memory: 4184k [80002000] to [80418000] Mounting proc Mounting sysfs Mounting devpts Setting hostname to ARCLinux Starting System logger (syslogd) Bringing up loopback device ifconfig: socket: Function not implemented route: socket: Function not implemented Disk not detected ! Mounting tmpfs mount: mounting tmpfs on /dev/shm failed: Invalid argument /etc/init.d/rcS: line 76: can't create /proc/sys/kernel/msgmni: nonexistent directory Please press Enter to activate this console. *********************************************************************** Welcome to ARCLinux *********************************************************************** [ARCLinux]$ ---------------------------------->8----------------------------------- Signed-off-by: Vineet Gupta --- arch/arc/configs/fpga_defconfig | 46 +++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 arch/arc/configs/fpga_defconfig diff --git a/arch/arc/configs/fpga_defconfig b/arch/arc/configs/fpga_defconfig new file mode 100644 index 000000000000..8638e101fa07 --- /dev/null +++ b/arch/arc/configs/fpga_defconfig @@ -0,0 +1,46 @@ +CONFIG_CROSS_COMPILE="arc-elf32-" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_DEFAULT_HOSTNAME="ARCLinux" +# CONFIG_SWAP is not set +CONFIG_HIGH_RES_TIMERS=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_PID_NS is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="../arc_initramfs" +CONFIG_EXPERT=y +# CONFIG_FUTEX is not set +# CONFIG_COMPAT_BRK is not set +# CONFIG_LBDAF is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_ARC_BUILTIN_DTB_NAME="angel4" +# CONFIG_COMPACTION is not set +# CONFIG_CROSS_MEMORY_ATTACH is not set +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FIRMWARE_IN_KERNEL is not set +# CONFIG_BLK_DEV is not set +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_ARC=y +CONFIG_SERIAL_ARC_CONSOLE=y +# CONFIG_HW_RANDOM is not set +# CONFIG_HWMON is not set +# CONFIG_VGA_CONSOLE is not set +# CONFIG_HID is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_IOMMU_SUPPORT is not set +# CONFIG_DNOTIFY is not set +# CONFIG_INOTIFY_USER is not set +# CONFIG_MISC_FILESYSTEMS is not set +# CONFIG_ENABLE_WARN_DEPRECATED is not set +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_XZ_DEC=y From 080c37473eb671a037b3e9a315303851f0675be5 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Mon, 11 Feb 2013 19:52:57 +0530 Subject: [PATCH 2831/4607] ARC: [optim] Cache "current" in Register r25 Signed-off-by: Vineet Gupta --- arch/arc/Kconfig | 7 +++++ arch/arc/Makefile | 9 +++++++ arch/arc/include/asm/Kbuild | 1 - arch/arc/include/asm/current.h | 32 +++++++++++++++++++++++ arch/arc/include/asm/entry.h | 45 ++++++++++++++++++++++++++++++++ arch/arc/include/asm/processor.h | 3 +++ arch/arc/kernel/asm-offsets.c | 3 +++ arch/arc/kernel/ctx_sw.c | 7 +++++ arch/arc/kernel/entry.S | 14 ++++++++++ 9 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 arch/arc/include/asm/current.h diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 7db978570fb8..03347cbde9bd 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -264,6 +264,13 @@ config LINUX_LINK_BASE endmenu # "Platform Board Configuration" +config ARC_CURR_IN_REG + bool "Dedicate Register r25 for current_task pointer" + default y + help + This reserved Register R25 to point to Current Task in + kernel mode. This saves memory access for each such access + config ARC_STACK_NONEXEC bool "Make stack non-executable" default n diff --git a/arch/arc/Makefile b/arch/arc/Makefile index 29b5fcd9c4b6..5c98fc19d99d 100644 --- a/arch/arc/Makefile +++ b/arch/arc/Makefile @@ -20,6 +20,15 @@ export PLATFORM cflags-y += -Iarch/arc/plat-$(PLATFORM)/include cflags-y += -mA7 -fno-common -pipe -fno-builtin -D__linux__ +ifdef CONFIG_ARC_CURR_IN_REG +# For a global register defintion, make sure it gets passed to every file +# We had a customer reported bug where some code built in kernel was NOT using +# any kernel headers, and missing the r25 global register +# Can't do unconditionally (like above) because of recursive include issues +# due to +LINUXINCLUDE += -include ${src}/arch/arc/include/asm/current.h +endif + atleast_gcc44 := $(call cc-ifversion, -gt, 0402, y) cflags-$(atleast_gcc44) += -fsection-anchors diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild index 105ec1188aaf..78e982dad537 100644 --- a/arch/arc/include/asm/Kbuild +++ b/arch/arc/include/asm/Kbuild @@ -11,7 +11,6 @@ generic-y += bugs.h generic-y += bitsperlong.h generic-y += clkdev.h generic-y += cputime.h -generic-y += current.h generic-y += device.h generic-y += div64.h generic-y += emergency-restart.h diff --git a/arch/arc/include/asm/current.h b/arch/arc/include/asm/current.h new file mode 100644 index 000000000000..87b918585c4a --- /dev/null +++ b/arch/arc/include/asm/current.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Vineetg: May 16th, 2008 + * - Current macro is now implemented as "global register" r25 + */ + +#ifndef _ASM_ARC_CURRENT_H +#define _ASM_ARC_CURRENT_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ + +#ifdef CONFIG_ARC_CURR_IN_REG + +register struct task_struct *curr_arc asm("r25"); +#define current (curr_arc) + +#else +#include +#endif /* ! CONFIG_ARC_CURR_IN_REG */ + +#endif /* ! __ASSEMBLY__ */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_ARC_CURRENT_H */ diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h index 9eada5b28be6..716f4f7b5cd2 100644 --- a/arch/arc/include/asm/entry.h +++ b/arch/arc/include/asm/entry.h @@ -13,6 +13,8 @@ * was being "CLEARED" rather then "SET". Actually "SET" clears ZOL context * * Vineetg: May 5th 2008 + * -Modified CALLEE_REG save/restore macros to handle the fact that + * r25 contains the kernel current task ptr * - Defined Stack Switching Macro to be reused in all intr/excp hdlrs * - Shaved off 11 instructions from RESTORE_ALL_INT1 by using the * address Write back load ld.ab instead of seperate ld/add instn @@ -28,6 +30,7 @@ #include #include #include +#include /* For VMALLOC_START */ #include /* For THREAD_SIZE */ /* Note on the LD/ST addr modes with addr reg wback @@ -106,7 +109,14 @@ st.a r22, [sp, -4] st.a r23, [sp, -4] st.a r24, [sp, -4] + +#ifdef CONFIG_ARC_CURR_IN_REG + ; Retrieve orig r25 and save it on stack + ld r12, [r25, TASK_THREAD + THREAD_USER_R25] + st.a r12, [sp, -4] +#else st.a r25, [sp, -4] +#endif /* move up by 1 word to "create" callee_regs->"stack_place_holder" */ sub sp, sp, 4 @@ -131,8 +141,12 @@ st.a r22, [sp, -4] st.a r23, [sp, -4] st.a r24, [sp, -4] +#ifdef CONFIG_ARC_CURR_IN_REG + sub sp, sp, 8 +#else st.a r25, [sp, -4] sub sp, sp, 4 +#endif .endm /*-------------------------------------------------------------- @@ -148,8 +162,14 @@ *-------------------------------------------------------------*/ .macro RESTORE_CALLEE_SAVED_KERNEL + +#ifdef CONFIG_ARC_CURR_IN_REG + add sp, sp, 8 /* skip callee_reg gutter and user r25 placeholder */ +#else add sp, sp, 4 /* skip "callee_regs->stack_place_holder" */ ld.ab r25, [sp, 4] +#endif + ld.ab r24, [sp, 4] ld.ab r23, [sp, 4] ld.ab r22, [sp, 4] @@ -235,6 +255,7 @@ * * Entry : r9 contains pre-IRQ/exception/trap status32 * Exit : SP is set to kernel mode stack pointer + * If CURR_IN_REG, r25 set to "current" task pointer * Clobbers: r9 *-------------------------------------------------------------*/ @@ -259,6 +280,16 @@ GET_CURR_TASK_ON_CPU r9 +#ifdef CONFIG_ARC_CURR_IN_REG + + /* If current task pointer cached in r25, time to + * -safekeep USER r25 in task->thread_struct->user_r25 + * -load r25 with current task ptr + */ + st.as r25, [r9, (TASK_THREAD + THREAD_USER_R25)/4] + mov r25, r9 +#endif + /* With current tsk in r9, get it's kernel mode stack base */ GET_TSK_STACK_BASE r9, r9 @@ -519,17 +550,31 @@ .macro SET_CURR_TASK_ON_CPU tsk, tmp st \tsk, [@_current_task] +#ifdef CONFIG_ARC_CURR_IN_REG + mov r25, \tsk +#endif .endm /* ------------------------------------------------------------------ * Get the ptr to some field of Current Task at @off in task struct + * -Uses r25 for Current task ptr if that is enabled */ +#ifdef CONFIG_ARC_CURR_IN_REG + +.macro GET_CURR_TASK_FIELD_PTR off, reg + add \reg, r25, \off +.endm + +#else + .macro GET_CURR_TASK_FIELD_PTR off, reg GET_CURR_TASK_ON_CPU \reg add \reg, \reg, \off .endm +#endif /* CONFIG_ARC_CURR_IN_REG */ + #endif /* __ASSEMBLY__ */ #endif /* __ASM_ARC_ENTRY_H */ diff --git a/arch/arc/include/asm/processor.h b/arch/arc/include/asm/processor.h index 860252ec3fa7..b7b155610067 100644 --- a/arch/arc/include/asm/processor.h +++ b/arch/arc/include/asm/processor.h @@ -29,6 +29,9 @@ struct thread_struct { unsigned long callee_reg; /* pointer to callee regs */ unsigned long fault_address; /* dbls as brkpt holder as well */ unsigned long cause_code; /* Exception Cause Code (ECR) */ +#ifdef CONFIG_ARC_CURR_IN_REG + unsigned long user_r25; +#endif #ifdef CONFIG_ARC_FPU_SAVE_RESTORE struct arc_fpu fpu; #endif diff --git a/arch/arc/kernel/asm-offsets.c b/arch/arc/kernel/asm-offsets.c index d7770cc9aee3..0dc148ebce74 100644 --- a/arch/arc/kernel/asm-offsets.c +++ b/arch/arc/kernel/asm-offsets.c @@ -24,6 +24,9 @@ int main(void) DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); DEFINE(THREAD_CALLEE_REG, offsetof(struct thread_struct, callee_reg)); +#ifdef CONFIG_ARC_CURR_IN_REG + DEFINE(THREAD_USER_R25, offsetof(struct thread_struct, user_r25)); +#endif DEFINE(THREAD_FAULT_ADDR, offsetof(struct thread_struct, fault_address)); diff --git a/arch/arc/kernel/ctx_sw.c b/arch/arc/kernel/ctx_sw.c index 647e37a5165e..fbf739cbaf7d 100644 --- a/arch/arc/kernel/ctx_sw.c +++ b/arch/arc/kernel/ctx_sw.c @@ -24,6 +24,9 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task) unsigned int prev = (unsigned int)prev_task; unsigned int next = (unsigned int)next_task; int num_words_to_skip = 1; +#ifdef CONFIG_ARC_CURR_IN_REG + num_words_to_skip++; +#endif __asm__ __volatile__( /* FP/BLINK save generated by gcc (standard function prologue */ @@ -39,7 +42,9 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task) "st.a r22, [sp, -4] \n\t" "st.a r23, [sp, -4] \n\t" "st.a r24, [sp, -4] \n\t" +#ifndef CONFIG_ARC_CURR_IN_REG "st.a r25, [sp, -4] \n\t" +#endif "sub sp, sp, %4 \n\t" /* create gutter at top */ /* set ksp of outgoing task in tsk->thread.ksp */ @@ -62,7 +67,9 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task) "add sp, sp, %4 \n\t" /* skip gutter at top */ +#ifndef CONFIG_ARC_CURR_IN_REG "ld.ab r25, [sp, 4] \n\t" +#endif "ld.ab r24, [sp, 4] \n\t" "ld.ab r23, [sp, 4] \n\t" "ld.ab r22, [sp, 4] \n\t" diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index ce8670da8306..69d0d376e28b 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -32,6 +32,9 @@ * was being "CLEARED" rather then "SET". Since it is Loop INHIBIT Bit, * setting it and not clearing it clears ZOL context * + * Vineetg: May 16th, 2008 + * - r25 now contains the Current Task when in kernel + * * Vineetg: Dec 22, 2007 * Minor Surgery of Low Level ISR to make it SMP safe * - MMU_SCRATCH0 Reg used for freeing up r9 in Level 1 ISR @@ -535,6 +538,17 @@ restore_regs : ; XXX can this be optimised out IRQ_DISABLE_SAVE r9, r10 ;@r10 has prisitine (pre-disable) copy +#ifdef CONFIG_ARC_CURR_IN_REG + ; Restore User R25 + ; Earlier this used to be only for returning to user mode + ; However with 2 levels of IRQ this can also happen even if + ; in kernel mode + ld r9, [sp, PT_sp] + brhs r9, VMALLOC_START, 8f + RESTORE_USER_R25 +8: +#endif + ; Restore REG File. In case multiple Events outstanding, ; use the same priorty as rtie: EXCPN, L2 IRQ, L1 IRQ, None ; Note that we use realtime STATUS32 (not pt_regs->status32) to From 547f112571904da03589beb8434185294c77896a Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:22 +0530 Subject: [PATCH 2832/4607] ARC: ptrace support Signed-off-by: Vineet Gupta Acked-by: Arnd Bergmann --- arch/arc/Kconfig | 1 + arch/arc/kernel/Makefile | 3 + arch/arc/kernel/entry.S | 68 +++++++++++++++++++ arch/arc/kernel/ptrace.c | 138 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 207 insertions(+), 3 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 03347cbde9bd..409b9378032e 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -22,6 +22,7 @@ config ARC select GENERIC_PENDING_IRQ if SMP select GENERIC_SIGALTSTACK select GENERIC_SMP_IDLE_THREAD + select HAVE_ARCH_TRACEHOOK select HAVE_GENERIC_HARDIRQS select HAVE_MEMBLOCK select IRQ_DOMAIN diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index c9ec0662a4ec..f4885891dabb 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -5,6 +5,9 @@ # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. +# Pass UTS_MACHINE for user_regset definition +CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' + obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o clk.o obj-y += devtree.o diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 69d0d376e28b..76697aecd165 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -7,6 +7,13 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * + * vineetg: Feb 2011 (ptrace low level code fixes) + * -traced syscall return code (r0) was not saved into pt_regs for restoring + * into user reg-file when traded task rets to user space. + * -syscalls needing arch-wrappers (mainly for passing sp as pt_regs) + * were not invoking post-syscall trace hook (jumping directly into + * ret_from_system_call) + * * vineetg: Nov 2010: * -Vector table jumps (@8 bytes) converted into branches (@4 bytes) * -To maintain the slot size of 8 bytes/vector, added nop, which is @@ -347,6 +354,50 @@ ARC_ENTRY EV_Extension b ret_from_exception ARC_EXIT EV_Extension +;######################### System Call Tracing ######################### + +tracesys: + ; save EFA in case tracer wants the PC of traced task + ; using ERET won't work since next-PC has already committed + lr r12, [efa] + GET_CURR_TASK_FIELD_PTR TASK_THREAD, r11 + st r12, [r11, THREAD_FAULT_ADDR] + + ; PRE Sys Call Ptrace hook + mov r0, sp ; pt_regs needed + bl @syscall_trace_entry + + ; Tracing code now returns the syscall num (orig or modif) + mov r8, r0 + + ; Do the Sys Call as we normally would. + ; Validate the Sys Call number + cmp r8, NR_syscalls + mov.hi r0, -ENOSYS + bhi tracesys_exit + + ; Restore the sys-call args. Mere invocation of the hook abv could have + ; clobbered them (since they are in scratch regs). The tracer could also + ; have deliberately changed the syscall args: r0-r7 + ld r0, [sp, PT_r0] + ld r1, [sp, PT_r1] + ld r2, [sp, PT_r2] + ld r3, [sp, PT_r3] + ld r4, [sp, PT_r4] + ld r5, [sp, PT_r5] + ld r6, [sp, PT_r6] + ld r7, [sp, PT_r7] + ld.as r9, [sys_call_table, r8] + jl [r9] ; Entry into Sys Call Handler + +tracesys_exit: + st r0, [sp, PT_r0] ; sys call return value in pt_regs + + ;POST Sys Call Ptrace Hook + bl @syscall_trace_exit + b ret_from_exception ; NOT ret_from_system_call at is saves r0 which + ; we'd done before calling post hook above + ;################### Break Point TRAP ########################## ; ======= (5b) Trap is due to Break-Point ========= @@ -412,6 +463,11 @@ ARC_ENTRY EV_Trap ; Before doing anything, return from CPU Exception Mode FAKE_RET_FROM_EXCPN r11 + ; If syscall tracing ongoing, invoke pre-pos-hooks + GET_CURR_THR_INFO_FLAGS r10 + btst r10, TIF_SYSCALL_TRACE + bnz tracesys ; this never comes back + ;============ This is normal System Call case ========== ; Sys-call num shd not exceed the total system calls avail cmp r8, NR_syscalls @@ -608,6 +664,10 @@ ARC_ENTRY sys_fork_wrapper bl @sys_fork DISCARD_CALLEE_SAVED_USER + GET_CURR_THR_INFO_FLAGS r10 + btst r10, TIF_SYSCALL_TRACE + bnz tracesys_exit + b ret_from_system_call ARC_EXIT sys_fork_wrapper @@ -616,6 +676,10 @@ ARC_ENTRY sys_vfork_wrapper bl @sys_vfork DISCARD_CALLEE_SAVED_USER + GET_CURR_THR_INFO_FLAGS r10 + btst r10, TIF_SYSCALL_TRACE + bnz tracesys_exit + b ret_from_system_call ARC_EXIT sys_vfork_wrapper @@ -624,5 +688,9 @@ ARC_ENTRY sys_clone_wrapper bl @sys_clone DISCARD_CALLEE_SAVED_USER + GET_CURR_THR_INFO_FLAGS r10 + btst r10, TIF_SYSCALL_TRACE + bnz tracesys_exit + b ret_from_system_call ARC_EXIT sys_clone_wrapper diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c index 1cf944a77f33..c6a81c58d0f3 100644 --- a/arch/arc/kernel/ptrace.c +++ b/arch/arc/kernel/ptrace.c @@ -7,6 +7,122 @@ */ #include +#include +#include +#include +#include + +static struct callee_regs *task_callee_regs(struct task_struct *tsk) +{ + struct callee_regs *tmp = (struct callee_regs *)tsk->thread.callee_reg; + return tmp; +} + +static int genregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct pt_regs *ptregs = task_pt_regs(target); + const struct callee_regs *cregs = task_callee_regs(target); + int ret = 0; + unsigned int stop_pc_val; + +#define REG_O_CHUNK(START, END, PTR) \ + if (!ret) \ + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ + offsetof(struct user_regs_struct, START), \ + offsetof(struct user_regs_struct, END)); + +#define REG_O_ONE(LOC, PTR) \ + if (!ret) \ + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ + offsetof(struct user_regs_struct, LOC), \ + offsetof(struct user_regs_struct, LOC) + 4); + + REG_O_CHUNK(scratch, callee, ptregs); + REG_O_CHUNK(callee, efa, cregs); + REG_O_CHUNK(efa, stop_pc, &target->thread.fault_address); + + if (!ret) { + if (in_brkpt_trap(ptregs)) { + stop_pc_val = target->thread.fault_address; + pr_debug("\t\tstop_pc (brk-pt)\n"); + } else { + stop_pc_val = ptregs->ret; + pr_debug("\t\tstop_pc (others)\n"); + } + + REG_O_ONE(stop_pc, &stop_pc_val); + } + + return ret; +} + +static int genregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + const struct pt_regs *ptregs = task_pt_regs(target); + const struct callee_regs *cregs = task_callee_regs(target); + int ret = 0; + +#define REG_IN_CHUNK(FIRST, NEXT, PTR) \ + if (!ret) \ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ + (void *)(PTR), \ + offsetof(struct user_regs_struct, FIRST), \ + offsetof(struct user_regs_struct, NEXT)); + +#define REG_IN_ONE(LOC, PTR) \ + if (!ret) \ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ + (void *)(PTR), \ + offsetof(struct user_regs_struct, LOC), \ + offsetof(struct user_regs_struct, LOC) + 4); + +#define REG_IGNORE_ONE(LOC) \ + if (!ret) \ + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, \ + offsetof(struct user_regs_struct, LOC), \ + offsetof(struct user_regs_struct, LOC) + 4); + + /* TBD: disallow updates to STATUS32, orig_r8 etc*/ + REG_IN_CHUNK(scratch, callee, ptregs); /* pt_regs[bta..orig_r8] */ + REG_IN_CHUNK(callee, efa, cregs); /* callee_regs[r25..r13] */ + REG_IGNORE_ONE(efa); /* efa update invalid */ + REG_IN_ONE(stop_pc, &ptregs->ret); /* stop_pc: PC update */ + + return ret; +} + +enum arc_getset { + REGSET_GENERAL, +}; + +static const struct user_regset arc_regsets[] = { + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = ELF_NGREG, + .size = sizeof(unsigned long), + .align = sizeof(unsigned long), + .get = genregs_get, + .set = genregs_set, + } +}; + +static const struct user_regset_view user_arc_view = { + .name = UTS_MACHINE, + .e_machine = EM_ARCOMPACT, + .regsets = arc_regsets, + .n = ARRAY_SIZE(arc_regsets) +}; + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ + return &user_arc_view; +} void ptrace_disable(struct task_struct *child) { @@ -16,11 +132,27 @@ long arch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { int ret = -EIO; + + pr_debug("REQ=%ld: ADDR =0x%lx, DATA=0x%lx)\n", request, addr, data); + + switch (request) { + default: + ret = ptrace_request(child, request, addr, data); + break; + } + return ret; } - -const struct user_regset_view *task_user_regset_view(struct task_struct *task) +asmlinkage int syscall_trace_entry(struct pt_regs *regs) { - return (const struct user_regset_view *)NULL; + if (tracehook_report_syscall_entry(regs)) + return ULONG_MAX; + + return regs->r8; +} + +asmlinkage void syscall_trace_exit(struct pt_regs *regs) +{ + tracehook_report_syscall_exit(regs, 0); } From 01b812bcce4920d9587fab7a964493d368be1522 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:22 +0530 Subject: [PATCH 2833/4607] ARC: Futex support Signed-off-by: Vineet Gupta --- arch/arc/include/asm/futex.h | 151 +++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 arch/arc/include/asm/futex.h diff --git a/arch/arc/include/asm/futex.h b/arch/arc/include/asm/futex.h new file mode 100644 index 000000000000..4dc64ddebece --- /dev/null +++ b/arch/arc/include/asm/futex.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Vineetg: August 2010: From Android kernel work + */ + +#ifndef _ASM_FUTEX_H +#define _ASM_FUTEX_H + +#include +#include +#include +#include + +#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg)\ + \ + __asm__ __volatile__( \ + "1: ld %1, [%2] \n" \ + insn "\n" \ + "2: st %0, [%2] \n" \ + " mov %0, 0 \n" \ + "3: \n" \ + " .section .fixup,\"ax\" \n" \ + " .align 4 \n" \ + "4: mov %0, %4 \n" \ + " b 3b \n" \ + " .previous \n" \ + " .section __ex_table,\"a\" \n" \ + " .align 4 \n" \ + " .word 1b, 4b \n" \ + " .word 2b, 4b \n" \ + " .previous \n" \ + \ + : "=&r" (ret), "=&r" (oldval) \ + : "r" (uaddr), "r" (oparg), "ir" (-EFAULT) \ + : "cc", "memory") + +static inline int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) +{ + int op = (encoded_op >> 28) & 7; + int cmp = (encoded_op >> 24) & 15; + int oparg = (encoded_op << 8) >> 20; + int cmparg = (encoded_op << 20) >> 20; + int oldval = 0, ret; + + if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) + oparg = 1 << oparg; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + return -EFAULT; + + pagefault_disable(); /* implies preempt_disable() */ + + switch (op) { + case FUTEX_OP_SET: + __futex_atomic_op("mov %0, %3", ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_ADD: + __futex_atomic_op("add %0, %1, %3", ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_OR: + __futex_atomic_op("or %0, %1, %3", ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_ANDN: + __futex_atomic_op("bic %0, %1, %3", ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_XOR: + __futex_atomic_op("xor %0, %1, %3", ret, oldval, uaddr, oparg); + break; + default: + ret = -ENOSYS; + } + + pagefault_enable(); /* subsumes preempt_enable() */ + + if (!ret) { + switch (cmp) { + case FUTEX_OP_CMP_EQ: + ret = (oldval == cmparg); + break; + case FUTEX_OP_CMP_NE: + ret = (oldval != cmparg); + break; + case FUTEX_OP_CMP_LT: + ret = (oldval < cmparg); + break; + case FUTEX_OP_CMP_GE: + ret = (oldval >= cmparg); + break; + case FUTEX_OP_CMP_LE: + ret = (oldval <= cmparg); + break; + case FUTEX_OP_CMP_GT: + ret = (oldval > cmparg); + break; + default: + ret = -ENOSYS; + } + } + return ret; +} + +/* Compare-xchg with preemption disabled. + * Notes: + * -Best-Effort: Exchg happens only if compare succeeds. + * If compare fails, returns; leaving retry/looping to upper layers + * -successful cmp-xchg: return orig value in @addr (same as cmp val) + * -Compare fails: return orig value in @addr + * -user access r/w fails: return -EFAULT + */ +static inline int +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, + u32 newval) +{ + u32 val; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(int))) + return -EFAULT; + + pagefault_disable(); /* implies preempt_disable() */ + + /* TBD : can use llock/scond */ + __asm__ __volatile__( + "1: ld %0, [%3] \n" + " brne %0, %1, 3f \n" + "2: st %2, [%3] \n" + "3: \n" + " .section .fixup,\"ax\" \n" + "4: mov %0, %4 \n" + " b 3b \n" + " .previous \n" + " .section __ex_table,\"a\" \n" + " .align 4 \n" + " .word 1b, 4b \n" + " .word 2b, 4b \n" + " .previous\n" + : "=&r"(val) + : "r"(oldval), "r"(newval), "r"(uaddr), "ir"(-EFAULT) + : "cc", "memory"); + + pagefault_enable(); /* subsumes preempt_enable() */ + + *uval = val; + return val; +} + +#endif From 769bc1fd7b8591a312d4c5c8834bc6510272938e Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Tue, 22 Jan 2013 17:02:38 +0530 Subject: [PATCH 2834/4607] ARC: OProfile support Signed-off-by: Vineet Gupta Cc: Robert Richter Cc: oprofile-list@lists.sf.net Reviewed-by: James Hogan --- arch/arc/Kconfig | 1 + arch/arc/Makefile | 2 ++ arch/arc/oprofile/Makefile | 9 +++++++++ arch/arc/oprofile/common.c | 26 ++++++++++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 arch/arc/oprofile/Makefile create mode 100644 arch/arc/oprofile/common.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 409b9378032e..405ea7a756b8 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -25,6 +25,7 @@ config ARC select HAVE_ARCH_TRACEHOOK select HAVE_GENERIC_HARDIRQS select HAVE_MEMBLOCK + select HAVE_OPROFILE select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM diff --git a/arch/arc/Makefile b/arch/arc/Makefile index 5c98fc19d99d..642c0406d600 100644 --- a/arch/arc/Makefile +++ b/arch/arc/Makefile @@ -100,6 +100,8 @@ ifneq ($(platform-y),) core-y += arch/arc/plat-$(PLATFORM)/ endif +drivers-$(CONFIG_OPROFILE) += arch/arc/oprofile/ + libs-y += arch/arc/lib/ $(LIBGCC) #default target for make without any arguements. diff --git a/arch/arc/oprofile/Makefile b/arch/arc/oprofile/Makefile new file mode 100644 index 000000000000..ce417a6e70b8 --- /dev/null +++ b/arch/arc/oprofile/Makefile @@ -0,0 +1,9 @@ +obj-$(CONFIG_OPROFILE) += oprofile.o + +DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ + oprof.o cpu_buffer.o buffer_sync.o \ + event_buffer.o oprofile_files.o \ + oprofilefs.o oprofile_stats.o \ + timer_int.o ) + +oprofile-y := $(DRIVER_OBJS) common.o diff --git a/arch/arc/oprofile/common.c b/arch/arc/oprofile/common.c new file mode 100644 index 000000000000..c80fcad4a5a7 --- /dev/null +++ b/arch/arc/oprofile/common.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Based on orig code from @author John Levon + */ + +#include +#include + +int __init oprofile_arch_init(struct oprofile_operations *ops) +{ + /* + * A failure here, forces oprofile core to switch to Timer based PC + * sampling, which will happen if say perf is not enabled/available + */ + return oprofile_perf_init(ops); +} + +void oprofile_arch_exit(void) +{ + oprofile_perf_exit(); +} From 4788a5942bc896803c87005be8c6dd14c373a2d3 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:22 +0530 Subject: [PATCH 2835/4607] ARC: Support for high priority interrupts in the in-core intc There is a bit of hack/kludge right now where we disable preemption if a L2 (High prio) IRQ is taken while L1 (Low prio) is active. Need to revisit this Signed-off-by: Vineet Gupta --- arch/arc/Kconfig | 19 ++++++ arch/arc/include/asm/entry.h | 95 ++++++++++++++++++++++++++ arch/arc/include/asm/irqflags.h | 6 +- arch/arc/kernel/entry.S | 117 ++++++++++++++++++++++++++++++++ arch/arc/kernel/irq.c | 104 +++++++++++++++++++++++++++- 5 files changed, 339 insertions(+), 2 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 405ea7a756b8..68350aa3d297 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -208,6 +208,25 @@ config ARC_PAGE_SIZE_4K endchoice +config ARC_COMPACT_IRQ_LEVELS + bool "ARCompact IRQ Priorities: High(2)/Low(1)" + default n + # Timer HAS to be high priority, for any other high priority config + select ARC_IRQ3_LV2 + +if ARC_COMPACT_IRQ_LEVELS + +config ARC_IRQ3_LV2 + bool + +config ARC_IRQ5_LV2 + bool + +config ARC_IRQ6_LV2 + bool + +endif + config ARC_FPU_SAVE_RESTORE bool "Enable FPU state persistence across context switch" default n diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h index 716f4f7b5cd2..23ef2de1e09f 100644 --- a/arch/arc/include/asm/entry.h +++ b/arch/arc/include/asm/entry.h @@ -5,6 +5,12 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * + * Vineetg: March 2009 (Supporting 2 levels of Interrupts) + * Stack switching code can no longer reliably rely on the fact that + * if we are NOT in user mode, stack is switched to kernel mode. + * e.g. L2 IRQ interrupted a L1 ISR which had not yet completed + * it's prologue including stack switching from user mode + * * Vineetg: Aug 28th 2008: Bug #94984 * -Zero Overhead Loop Context shd be cleared when entering IRQ/EXcp/Trap * Normally CPU does this automatically, however when doing FAKE rtie, @@ -268,6 +274,33 @@ * assume SP is kernel mode SP. _NO_ need to do any stack switching */ +#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS + /* However.... + * If Level 2 Interrupts enabled, we may end up with a corner case: + * 1. User Task executing + * 2. L1 IRQ taken, ISR starts (CPU auto-switched to KERNEL mode) + * 3. But before it could switch SP from USER to KERNEL stack + * a L2 IRQ "Interrupts" L1 + * Thay way although L2 IRQ happened in Kernel mode, stack is still + * not switched. + * To handle this, we may need to switch stack even if in kernel mode + * provided SP has values in range of USER mode stack ( < 0x7000_0000 ) + */ + brlo sp, VMALLOC_START, 88f + + /* TODO: vineetg: + * We need to be a bit more cautious here. What if a kernel bug in + * L1 ISR, caused SP to go whaco (some small value which looks like + * USER stk) and then we take L2 ISR. + * Above brlo alone would treat it as a valid L1-L2 sceanrio + * instead of shouting alound + * The only feasible way is to make sure this L2 happened in + * L1 prelogue ONLY i.e. ilink2 is less than a pre-set marker in + * L1 ISR before it switches stack + */ + +#endif + /* Save Pre Intr/Exception KERNEL MODE SP on kernel stack * safe-keeping not really needed, but it keeps the epilogue code * (SP restore) simpler/uniform. @@ -503,6 +536,42 @@ sub sp, sp, 4 .endm +.macro SAVE_ALL_INT2 + + /* TODO-vineetg: SMP we can't use global nor can we use + * SCRATCH0 as we do for int1 because while int1 is using + * it, int2 can come + */ + /* retsore original r9 , saved in sys_saved_r9 */ + ld r9, [@int2_saved_reg] + + /* now we are ready to save the remaining context :) */ + st orig_r8_IS_IRQ2, [sp, 8] /* Event Type */ + st 0, [sp, 4] /* orig_r0 , N/A for IRQ */ + SAVE_CALLER_SAVED + st.a r26, [sp, -4] /* gp */ + st.a fp, [sp, -4] + st.a blink, [sp, -4] + st.a ilink2, [sp, -4] + lr r9, [status32_l2] + st.a r9, [sp, -4] + st.a lp_count, [sp, -4] + lr r9, [lp_end] + st.a r9, [sp, -4] + lr r9, [lp_start] + st.a r9, [sp, -4] + lr r9, [bta_l2] + st.a r9, [sp, -4] + +#ifdef PT_REGS_CANARY + mov r9, 0xdeadbee2 + st r9, [sp, -4] +#endif + + /* move up by 1 word to "create" pt_regs->"stack_place_holder" */ + sub sp, sp, 4 +.endm + /*-------------------------------------------------------------- * Restore all registers used by interrupt handlers. * @@ -537,6 +606,32 @@ /* orig_r0 and orig_r8 skipped automatically */ .endm +.macro RESTORE_ALL_INT2 + add sp, sp, 4 /* hop over unused "pt_regs->stack_place_holder" */ + + ld.ab r9, [sp, 4] + sr r9, [bta_l2] + ld.ab r9, [sp, 4] + sr r9, [lp_start] + ld.ab r9, [sp, 4] + sr r9, [lp_end] + ld.ab r9, [sp, 4] + mov lp_count, r9 + ld.ab r9, [sp, 4] + sr r9, [status32_l2] + ld.ab r9, [sp, 4] + mov ilink2, r9 + ld.ab blink, [sp, 4] + ld.ab fp, [sp, 4] + ld.ab r26, [sp, 4] /* gp */ + RESTORE_CALLER_SAVED + + ld sp, [sp] /* restore original sp */ + /* orig_r0 and orig_r8 skipped automatically */ + +.endm + + /* Get CPU-ID of this core */ .macro GET_CPU_ID reg lr \reg, [identity] diff --git a/arch/arc/include/asm/irqflags.h b/arch/arc/include/asm/irqflags.h index 5cc1080d7c26..ccd84806b62f 100644 --- a/arch/arc/include/asm/irqflags.h +++ b/arch/arc/include/asm/irqflags.h @@ -95,7 +95,11 @@ static inline long arch_local_save_flags(void) */ static inline int arch_irqs_disabled_flags(unsigned long flags) { - return !(flags & (STATUS_E1_MASK)); + return !(flags & (STATUS_E1_MASK +#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS + | STATUS_E2_MASK +#endif + )); } static inline int arch_irqs_disabled(void) diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 76697aecd165..e33a0bf45589 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -31,6 +31,8 @@ * exception. Thus FAKE RTIE needed in low level Priv-Violation handler. * Instr Error could also cause similar scenario, so same there as well. * + * Vineetg: March 2009 (Supporting 2 levels of Interrupts) + * * Vineetg: Aug 28th 2008: Bug #94984 * -Zero Overhead Loop Context shd be cleared when entering IRQ/EXcp/Trap * Normally CPU does this automatically, however when doing FAKE rtie, @@ -96,13 +98,25 @@ VECTOR mem_service ; 0x8, Mem exception (0x1) VECTOR instr_service ; 0x10, Instrn Error (0x2) ; ******************** Device ISRs ********************** +#ifdef CONFIG_ARC_IRQ3_LV2 +VECTOR handle_interrupt_level2 +#else VECTOR handle_interrupt_level1 +#endif VECTOR handle_interrupt_level1 +#ifdef CONFIG_ARC_IRQ5_LV2 +VECTOR handle_interrupt_level2 +#else VECTOR handle_interrupt_level1 +#endif +#ifdef CONFIG_ARC_IRQ6_LV2 +VECTOR handle_interrupt_level2 +#else VECTOR handle_interrupt_level1 +#endif .rept 25 VECTOR handle_interrupt_level1 ; Other devices @@ -139,6 +153,17 @@ VECTOR reserved ; Reserved Exceptions int1_saved_reg: .zero 4 +/* Each Interrupt level needs it's own scratch */ +#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS + + .section .data ; NOT .global + .type int2_saved_reg, @object + .size int2_saved_reg, 4 +int2_saved_reg: + .zero 4 + +#endif + ; --------------------------------------------- .section .text, "ax",@progbits @@ -152,6 +177,55 @@ reserved: ; processor restart ;##################### Interrupt Handling ############################## +#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS +; --------------------------------------------- +; Level 2 ISR: Can interrupt a Level 1 ISR +; --------------------------------------------- +ARC_ENTRY handle_interrupt_level2 + + ; TODO-vineetg for SMP this wont work + ; free up r9 as scratchpad + st r9, [@int2_saved_reg] + + ;Which mode (user/kernel) was the system in when intr occured + lr r9, [status32_l2] + + SWITCH_TO_KERNEL_STK + SAVE_ALL_INT2 + + ;------------------------------------------------------ + ; if L2 IRQ interrupted a L1 ISR, disable preemption + ;------------------------------------------------------ + + ld r9, [sp, PT_status32] ; get statu32_l2 (saved in pt_regs) + bbit0 r9, STATUS_A1_BIT, 1f ; L1 not active when L2 IRQ, so normal + + ; A1 is set in status32_l2 + ; bump thread_info->preempt_count (Disable preemption) + GET_CURR_THR_INFO_FROM_SP r10 + ld r9, [r10, THREAD_INFO_PREEMPT_COUNT] + add r9, r9, 1 + st r9, [r10, THREAD_INFO_PREEMPT_COUNT] + +1: + ;------------------------------------------------------ + ; setup params for Linux common ISR and invoke it + ;------------------------------------------------------ + lr r0, [icause2] + and r0, r0, 0x1f + + bl.d @arch_do_IRQ + mov r1, sp + + mov r8,0x2 + sr r8, [AUX_IRQ_LV12] ; clear bit in Sticky Status Reg + + b ret_from_exception + +ARC_EXIT handle_interrupt_level2 + +#endif + ; --------------------------------------------- ; Level 1 ISR ; --------------------------------------------- @@ -619,6 +693,49 @@ restore_regs : not_exception: +#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS + + bbit0 r10, STATUS_A2_BIT, not_level2_interrupt + + ;------------------------------------------------------------------ + ; if L2 IRQ interrupted a L1 ISR, we'd disbaled preemption earlier + ; so that sched doesnt move to new task, causing L1 to be delayed + ; undeterministically. Now that we've achieved that, lets reset + ; things to what they were, before returning from L2 context + ;---------------------------------------------------------------- + + ld r9, [sp, PT_orig_r8] ; get orig_r8 to make sure it is + brne r9, orig_r8_IS_IRQ2, 149f ; infact a L2 ISR ret path + + ld r9, [sp, PT_status32] ; get statu32_l2 (saved in pt_regs) + bbit0 r9, STATUS_A1_BIT, 149f ; L1 not active when L2 IRQ, so normal + + ; A1 is set in status32_l2 + ; decrement thread_info->preempt_count (re-enable preemption) + GET_CURR_THR_INFO_FROM_SP r10 + ld r9, [r10, THREAD_INFO_PREEMPT_COUNT] + + ; paranoid check, given A1 was active when A2 happened, preempt count + ; must not be 0 beccause we would have incremented it. + ; If this does happen we simply HALT as it means a BUG !!! + cmp r9, 0 + bnz 2f + flag 1 + +2: + sub r9, r9, 1 + st r9, [r10, THREAD_INFO_PREEMPT_COUNT] + +149: + ;return from level 2 + RESTORE_ALL_INT2 +debug_marker_l2: + rtie + +not_level2_interrupt: + +#endif + bbit0 r10, STATUS_A1_BIT, not_level1_interrupt ;return from level 1 diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c index 3c18e66386c7..ca70894e2309 100644 --- a/arch/arc/kernel/irq.c +++ b/arch/arc/kernel/irq.c @@ -23,15 +23,32 @@ * what it does ? * -setup Vector Table Base Reg - in case Linux not linked at 0x8000_0000 * -Disable all IRQs (on CPU side) + * -Optionally, setup the High priority Interrupts as Level 2 IRQs */ void __init arc_init_IRQ(void) { - int level_mask = level_mask; + int level_mask = 0; write_aux_reg(AUX_INTR_VEC_BASE, _int_vec_base_lds); /* Disable all IRQs: enable them as devices request */ write_aux_reg(AUX_IENABLE, 0); + + /* setup any high priority Interrupts (Level2 in ARCompact jargon) */ +#ifdef CONFIG_ARC_IRQ3_LV2 + level_mask |= (1 << 3); +#endif +#ifdef CONFIG_ARC_IRQ5_LV2 + level_mask |= (1 << 5); +#endif +#ifdef CONFIG_ARC_IRQ6_LV2 + level_mask |= (1 << 6); +#endif + + if (level_mask) { + pr_info("Level-2 interrupts bitset %x\n", level_mask); + write_aux_reg(AUX_IRQ_LEV, level_mask); + } } /* @@ -141,6 +158,90 @@ int __init get_hw_config_num_irq(void) return 0; } +/* + * arch_local_irq_enable - Enable interrupts. + * + * 1. Explicitly called to re-enable interrupts + * 2. Implicitly called from spin_unlock_irq, write_unlock_irq etc + * which maybe in hard ISR itself + * + * Semantics of this function change depending on where it is called from: + * + * -If called from hard-ISR, it must not invert interrupt priorities + * e.g. suppose TIMER is high priority (Level 2) IRQ + * Time hard-ISR, timer_interrupt( ) calls spin_unlock_irq several times. + * Here local_irq_enable( ) shd not re-enable lower priority interrupts + * -If called from soft-ISR, it must re-enable all interrupts + * soft ISR are low prioity jobs which can be very slow, thus all IRQs + * must be enabled while they run. + * Now hardware context wise we may still be in L2 ISR (not done rtie) + * still we must re-enable both L1 and L2 IRQs + * Another twist is prev scenario with flow being + * L1 ISR ==> interrupted by L2 ISR ==> L2 soft ISR + * here we must not re-enable Ll as prev Ll Interrupt's h/w context will get + * over-written (this is deficiency in ARC700 Interrupt mechanism) + */ + +#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS /* Complex version for 2 IRQ levels */ + +void arch_local_irq_enable(void) +{ + + unsigned long flags; + flags = arch_local_save_flags(); + + /* Allow both L1 and L2 at the onset */ + flags |= (STATUS_E1_MASK | STATUS_E2_MASK); + + /* Called from hard ISR (between irq_enter and irq_exit) */ + if (in_irq()) { + + /* If in L2 ISR, don't re-enable any further IRQs as this can + * cause IRQ priorities to get upside down. e.g. it could allow + * L1 be taken while in L2 hard ISR which is wrong not only in + * theory, it can also cause the dreaded L1-L2-L1 scenario + */ + if (flags & STATUS_A2_MASK) + flags &= ~(STATUS_E1_MASK | STATUS_E2_MASK); + + /* Even if in L1 ISR, allowe Higher prio L2 IRQs */ + else if (flags & STATUS_A1_MASK) + flags &= ~(STATUS_E1_MASK); + } + + /* called from soft IRQ, ideally we want to re-enable all levels */ + + else if (in_softirq()) { + + /* However if this is case of L1 interrupted by L2, + * re-enabling both may cause whaco L1-L2-L1 scenario + * because ARC700 allows level 1 to interrupt an active L2 ISR + * Thus we disable both + * However some code, executing in soft ISR wants some IRQs + * to be enabled so we re-enable L2 only + * + * How do we determine L1 intr by L2 + * -A2 is set (means in L2 ISR) + * -E1 is set in this ISR's pt_regs->status32 which is + * saved copy of status32_l2 when l2 ISR happened + */ + struct pt_regs *pt = get_irq_regs(); + if ((flags & STATUS_A2_MASK) && pt && + (pt->status32 & STATUS_A1_MASK)) { + /*flags &= ~(STATUS_E1_MASK | STATUS_E2_MASK); */ + flags &= ~(STATUS_E1_MASK); + } + } + + arch_local_irq_restore(flags); +} + +#else /* ! CONFIG_ARC_COMPACT_IRQ_LEVELS */ + +/* + * Simpler version for only 1 level of interrupt + * Here we only Worry about Level 1 Bits + */ void arch_local_irq_enable(void) { unsigned long flags; @@ -158,4 +259,5 @@ void arch_local_irq_enable(void) flags |= (STATUS_E1_MASK | STATUS_E2_MASK); arch_local_irq_restore(flags); } +#endif EXPORT_SYMBOL(arch_local_irq_enable); From fa1c3ff935179453801d763940c38c3ac2d581eb Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Tue, 29 Jan 2013 19:28:05 +0530 Subject: [PATCH 2836/4607] ARC: Module support Signed-off-by: Vineet Gupta --- arch/arc/include/asm/module.h | 4 ++ arch/arc/kernel/Makefile | 2 + arch/arc/kernel/module.c | 87 +++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 arch/arc/kernel/module.c diff --git a/arch/arc/include/asm/module.h b/arch/arc/include/asm/module.h index 165d768d00cf..234b4354e2b3 100644 --- a/arch/arc/include/asm/module.h +++ b/arch/arc/include/asm/module.h @@ -14,4 +14,8 @@ #include +#define MODULE_PROC_FAMILY "ARC700" + +#define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY + #endif /* _ASM_ARC_MODULE_H */ diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index f4885891dabb..f32f65f98850 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -12,6 +12,8 @@ obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o clk.o obj-y += devtree.o +obj-$(CONFIG_MODULES) += arcksyms.o module.o + obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o CFLAGS_fpu.o += -mdpfp diff --git a/arch/arc/kernel/module.c b/arch/arc/kernel/module.c new file mode 100644 index 000000000000..a1bb70d6e97d --- /dev/null +++ b/arch/arc/kernel/module.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +static inline void arc_write_me(unsigned short *addr, unsigned long value) +{ + *addr = (value & 0xffff0000) >> 16; + *(addr + 1) = (value & 0xffff); +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, /* sec index for sym tbl */ + unsigned int relsec, /* sec index for relo sec */ + struct module *module) +{ + int i, n; + Elf32_Rela *rel_entry = (void *)sechdrs[relsec].sh_addr; + Elf32_Sym *sym_entry, *sym_sec; + Elf32_Addr relocation; + Elf32_Addr location; + Elf32_Addr sec_to_patch; + int relo_type; + + sec_to_patch = sechdrs[sechdrs[relsec].sh_info].sh_addr; + sym_sec = (Elf32_Sym *) sechdrs[symindex].sh_addr; + n = sechdrs[relsec].sh_size / sizeof(*rel_entry); + + pr_debug("\n========== Module Sym reloc ===========================\n"); + pr_debug("Section to fixup %x\n", sec_to_patch); + pr_debug("=========================================================\n"); + pr_debug("rela->r_off | rela->addend | sym->st_value | ADDR | VALUE\n"); + pr_debug("=========================================================\n"); + + /* Loop thru entries in relocation section */ + for (i = 0; i < n; i++) { + + /* This is where to make the change */ + location = sec_to_patch + rel_entry[i].r_offset; + + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + sym_entry = sym_sec + ELF32_R_SYM(rel_entry[i].r_info); + + relocation = sym_entry->st_value + rel_entry[i].r_addend; + + pr_debug("\t%x\t\t%x\t\t%x %x %x [%s]\n", + rel_entry[i].r_offset, rel_entry[i].r_addend, + sym_entry->st_value, location, relocation, + strtab + sym_entry->st_name); + + /* This assumes modules are built with -mlong-calls + * so any branches/jumps are absolute 32 bit jmps + * global data access again is abs 32 bit. + * Both of these are handled by same relocation type + */ + relo_type = ELF32_R_TYPE(rel_entry[i].r_info); + + if (likely(R_ARC_32_ME == relo_type)) + arc_write_me((unsigned short *)location, relocation); + else if (R_ARC_32 == relo_type) + *((Elf32_Addr *) location) = relocation; + else + goto relo_err; + + } + return 0; + +relo_err: + pr_err("%s: unknown relocation: %u\n", + module->name, ELF32_R_TYPE(rel_entry[i].r_info)); + return -ENOEXEC; + +} From 0ef88a54aa341f754707414500158addbf35c780 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:23 +0530 Subject: [PATCH 2837/4607] ARC: Diagnostics: show_regs() etc Signed-off-by: Vineet Gupta --- arch/arc/kernel/troubleshoot.c | 305 +++++++++++++++++++++++++++++++++ arch/arc/mm/tlbex.S | 20 +++ 2 files changed, 325 insertions(+) diff --git a/arch/arc/kernel/troubleshoot.c b/arch/arc/kernel/troubleshoot.c index 80bfe2a15a98..7c10873c311f 100644 --- a/arch/arc/kernel/troubleshoot.c +++ b/arch/arc/kernel/troubleshoot.c @@ -6,12 +6,317 @@ */ #include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Common routine to print scratch regs (r0-r12) or callee regs (r13-r25) + * -Prints 3 regs per line and a CR. + * -To continue, callee regs right after scratch, special handling of CR + */ +static noinline void print_reg_file(long *reg_rev, int start_num) +{ + unsigned int i; + char buf[512]; + int n = 0, len = sizeof(buf); + + /* weird loop because pt_regs regs rev r12..r0, r25..r13 */ + for (i = start_num; i < start_num + 13; i++) { + n += scnprintf(buf + n, len - n, "r%02u: 0x%08lx\t", + i, (unsigned long)*reg_rev); + + if (((i + 1) % 3) == 0) + n += scnprintf(buf + n, len - n, "\n"); + + reg_rev--; + } + + if (start_num != 0) + n += scnprintf(buf + n, len - n, "\n\n"); + + pr_info("%s", buf); +} + +static void show_callee_regs(struct callee_regs *cregs) +{ + print_reg_file(&(cregs->r13), 13); +} + +void print_task_path_n_nm(struct task_struct *tsk, char *buf) +{ + struct path path; + char *path_nm = NULL; + struct mm_struct *mm; + struct file *exe_file; + + mm = get_task_mm(tsk); + if (!mm) + goto done; + + exe_file = get_mm_exe_file(mm); + mmput(mm); + + if (exe_file) { + path = exe_file->f_path; + path_get(&exe_file->f_path); + fput(exe_file); + path_nm = d_path(&path, buf, 255); + path_put(&path); + } + +done: + pr_info("%s, TGID %u\n", path_nm, tsk->tgid); +} +EXPORT_SYMBOL(print_task_path_n_nm); + +static void show_faulting_vma(unsigned long address, char *buf) +{ + struct vm_area_struct *vma; + struct inode *inode; + unsigned long ino = 0; + dev_t dev = 0; + char *nm = buf; + + vma = find_vma(current->active_mm, address); + + /* check against the find_vma( ) behaviour which returns the next VMA + * if the container VMA is not found + */ + if (vma && (vma->vm_start <= address)) { + struct file *file = vma->vm_file; + if (file) { + struct path *path = &file->f_path; + nm = d_path(path, buf, PAGE_SIZE - 1); + inode = vma->vm_file->f_path.dentry->d_inode; + dev = inode->i_sb->s_dev; + ino = inode->i_ino; + } + pr_info(" @off 0x%lx in [%s]\n" + " VMA: 0x%08lx to 0x%08lx\n\n", + address - vma->vm_start, nm, vma->vm_start, vma->vm_end); + } else + pr_info(" @No matching VMA found\n"); +} + +static void show_ecr_verbose(struct pt_regs *regs) +{ + unsigned int vec, cause_code, cause_reg; + unsigned long address; + + cause_reg = current->thread.cause_code; + pr_info("\n[ECR]: 0x%08x => ", cause_reg); + + /* For Data fault, this is data address not instruction addr */ + address = current->thread.fault_address; + + vec = cause_reg >> 16; + cause_code = (cause_reg >> 8) & 0xFF; + + /* For DTLB Miss or ProtV, display the memory involved too */ + if (vec == ECR_V_DTLB_MISS) { + pr_cont("Invalid (%s) @ 0x%08lx by insn @ 0x%08lx\n", + (cause_code == 0x01) ? "Read From" : + ((cause_code == 0x02) ? "Write to" : "EX"), + address, regs->ret); + } else if (vec == ECR_V_ITLB_MISS) { + pr_cont("Insn could not be fetched\n"); + } else if (vec == ECR_V_MACH_CHK) { + pr_cont("%s\n", (cause_code == 0x0) ? + "Double Fault" : "Other Fatal Err"); + + } else if (vec == ECR_V_PROTV) { + if (cause_code == ECR_C_PROTV_INST_FETCH) + pr_cont("Execute from Non-exec Page\n"); + else if (cause_code == ECR_C_PROTV_LOAD) + pr_cont("Read from Non-readable Page\n"); + else if (cause_code == ECR_C_PROTV_STORE) + pr_cont("Write to Non-writable Page\n"); + else if (cause_code == ECR_C_PROTV_XCHG) + pr_cont("Data exchange protection violation\n"); + else if (cause_code == ECR_C_PROTV_MISALIG_DATA) + pr_cont("Misaligned r/w from 0x%08lx\n", address); + } else if (vec == ECR_V_INSN_ERR) { + pr_cont("Illegal Insn\n"); + } else { + pr_cont("Check Programmer's Manual\n"); + } +} + +/************************************************************************ + * API called by rest of kernel + ***********************************************************************/ void show_regs(struct pt_regs *regs) { + struct task_struct *tsk = current; + struct callee_regs *cregs; + char *buf; + + buf = (char *)__get_free_page(GFP_TEMPORARY); + if (!buf) + return; + + print_task_path_n_nm(tsk, buf); + + if (current->thread.cause_code) + show_ecr_verbose(regs); + + pr_info("[EFA]: 0x%08lx\n", current->thread.fault_address); + pr_info("[ERET]: 0x%08lx (PC of Faulting Instr)\n", regs->ret); + + show_faulting_vma(regs->ret, buf); /* faulting code, not data */ + + /* can't use print_vma_addr() yet as it doesn't check for + * non-inclusive vma + */ + + /* print special regs */ + pr_info("status32: 0x%08lx\n", regs->status32); + pr_info(" SP: 0x%08lx\tFP: 0x%08lx\n", regs->sp, regs->fp); + pr_info("BTA: 0x%08lx\tBLINK: 0x%08lx\n", + regs->bta, regs->blink); + pr_info("LPS: 0x%08lx\tLPE: 0x%08lx\tLPC: 0x%08lx\n", + regs->lp_start, regs->lp_end, regs->lp_count); + + /* print regs->r0 thru regs->r12 + * Sequential printing was generating horrible code + */ + print_reg_file(&(regs->r0), 0); + + /* If Callee regs were saved, display them too */ + cregs = (struct callee_regs *)current->thread.callee_reg; + if (cregs) + show_callee_regs(cregs); + + free_page((unsigned long)buf); } void show_kernel_fault_diag(const char *str, struct pt_regs *regs, unsigned long address, unsigned long cause_reg) { + current->thread.fault_address = address; + current->thread.cause_code = cause_reg; + + /* Caller and Callee regs */ + show_regs(regs); + + /* Show stack trace if this Fatality happened in kernel mode */ + if (!user_mode(regs)) + show_stacktrace(current, regs); } + +#ifdef CONFIG_DEBUG_FS + +#include +#include +#include +#include +#include +#include +#include + +static struct dentry *test_dentry; +static struct dentry *test_dir; +static struct dentry *test_u32_dentry; + +static u32 clr_on_read = 1; + +#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT +u32 numitlb, numdtlb, num_pte_not_present; + +static int fill_display_data(char *kbuf) +{ + size_t num = 0; + num += sprintf(kbuf + num, "I-TLB Miss %x\n", numitlb); + num += sprintf(kbuf + num, "D-TLB Miss %x\n", numdtlb); + num += sprintf(kbuf + num, "PTE not present %x\n", num_pte_not_present); + + if (clr_on_read) + numitlb = numdtlb = num_pte_not_present = 0; + + return num; +} + +static int tlb_stats_open(struct inode *inode, struct file *file) +{ + file->private_data = (void *)__get_free_page(GFP_KERNEL); + return 0; +} + +/* called on user read(): display the couters */ +static ssize_t tlb_stats_output(struct file *file, /* file descriptor */ + char __user *user_buf, /* user buffer */ + size_t len, /* length of buffer */ + loff_t *offset) /* offset in the file */ +{ + size_t num; + char *kbuf = (char *)file->private_data; + + /* All of the data can he shoved in one iteration */ + if (*offset != 0) + return 0; + + num = fill_display_data(kbuf); + + /* simple_read_from_buffer() is helper for copy to user space + It copies up to @2 (num) bytes from kernel buffer @4 (kbuf) at offset + @3 (offset) into the user space address starting at @1 (user_buf). + @5 (len) is max size of user buffer + */ + return simple_read_from_buffer(user_buf, num, offset, kbuf, len); +} + +/* called on user write : clears the counters */ +static ssize_t tlb_stats_clear(struct file *file, const char __user *user_buf, + size_t length, loff_t *offset) +{ + numitlb = numdtlb = num_pte_not_present = 0; + return length; +} + +static int tlb_stats_close(struct inode *inode, struct file *file) +{ + free_page((unsigned long)(file->private_data)); + return 0; +} + +static const struct file_operations tlb_stats_file_ops = { + .read = tlb_stats_output, + .write = tlb_stats_clear, + .open = tlb_stats_open, + .release = tlb_stats_close +}; +#endif + +static int __init arc_debugfs_init(void) +{ + test_dir = debugfs_create_dir("arc", NULL); + +#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT + test_dentry = debugfs_create_file("tlb_stats", 0444, test_dir, NULL, + &tlb_stats_file_ops); +#endif + + test_u32_dentry = + debugfs_create_u32("clr_on_read", 0444, test_dir, &clr_on_read); + + return 0; +} + +module_init(arc_debugfs_init); + +static void __exit arc_debugfs_exit(void) +{ + debugfs_remove(test_u32_dentry); + debugfs_remove(test_dentry); + debugfs_remove(test_dir); +} +module_exit(arc_debugfs_exit); + +#endif diff --git a/arch/arc/mm/tlbex.S b/arch/arc/mm/tlbex.S index fc5b97129e55..164b02169498 100644 --- a/arch/arc/mm/tlbex.S +++ b/arch/arc/mm/tlbex.S @@ -133,6 +133,14 @@ ex_saved_reg1: lsr r0, r2, (PAGE_SHIFT - 2) and r0, r0, ( (PTRS_PER_PTE - 1) << 2) ld.aw r0, [r1, r0] ; get PTE and PTE ptr for fault addr +#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT + and.f 0, r0, _PAGE_PRESENT + bz 1f + ld r2, [num_pte_not_present] + add r2, r2, 1 + st r2, [num_pte_not_present] +1: +#endif .endm @@ -219,6 +227,12 @@ ARC_ENTRY EV_TLBMissI TLBMISS_FREEUP_REGS +#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT + ld r0, [@numitlb] + add r0, r0, 1 + st r0, [@numitlb] +#endif + ;---------------------------------------------------------------- ; Get the PTE corresponding to V-addr accessed LOAD_FAULT_PTE @@ -252,6 +266,12 @@ ARC_ENTRY EV_TLBMissD TLBMISS_FREEUP_REGS +#ifdef CONFIG_ARC_DBG_TLB_MISS_COUNT + ld r0, [@numdtlb] + add r0, r0, 1 + st r0, [@numdtlb] +#endif + ;---------------------------------------------------------------- ; Get the PTE corresponding to V-addr accessed ; If PTE exists, it will setup, r0 = PTE, r1 = Ptr to PTE From 41195d236e84458bebd4fdc218610a92231ac791 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:23 +0530 Subject: [PATCH 2838/4607] ARC: SMP support ARC common code to enable a SMP system + ISS provided SMP extensions. ARC700 natively lacks SMP support, hence some of the core features are are only enabled if SoCs have the necessary h/w pixie-dust. This includes: -Inter Processor Interrupts (IPI) -Cache coherency -load-locked/store-conditional ... The low level exception handling would be completely broken in SMP because we don't have hardware assisted stack switching. Thus a fair bit of this code is repurposing the MMU_SCRATCH reg for event handler prologues to keep them re-entrant. Many thanks to Rajeshwar Ranga for his initial "major" contributions to SMP Port (back in 2008), and to Noam Camus and Gilad Ben-Yossef for help with resurrecting that in 3.2 kernel (2012). Note that this platform code is again singleton design pattern - so multiple SMP platforms won't build at the moment - this deficiency is addressed in subsequent patches within this series. Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Cc: Thomas Gleixner Cc: Rajeshwar Ranga Cc: Noam Camus Cc: Gilad Ben-Yossef --- arch/arc/Kconfig | 39 ++- arch/arc/Makefile | 3 + arch/arc/include/asm/entry.h | 49 ++++ arch/arc/include/asm/mmu_context.h | 4 + arch/arc/include/asm/mutex.h | 9 + arch/arc/include/asm/pgtable.h | 4 + arch/arc/include/asm/processor.h | 8 + arch/arc/include/asm/smp.h | 107 ++++++++ arch/arc/kernel/Makefile | 1 + arch/arc/kernel/ctx_sw.c | 11 + arch/arc/kernel/entry.S | 4 + arch/arc/kernel/head.S | 33 +++ arch/arc/kernel/irq.c | 5 + arch/arc/kernel/setup.c | 4 + arch/arc/kernel/smp.c | 320 +++++++++++++++++++++++ arch/arc/mm/tlb.c | 6 + arch/arc/mm/tlbex.S | 38 +++ arch/arc/plat-arcfpga/Kconfig | 14 + arch/arc/plat-arcfpga/Makefile | 1 + arch/arc/plat-arcfpga/include/plat/irq.h | 10 +- arch/arc/plat-arcfpga/include/plat/smp.h | 115 ++++++++ arch/arc/plat-arcfpga/irq.c | 10 + arch/arc/plat-arcfpga/smp.c | 167 ++++++++++++ 23 files changed, 960 insertions(+), 2 deletions(-) create mode 100644 arch/arc/kernel/smp.c create mode 100644 arch/arc/plat-arcfpga/include/plat/smp.h create mode 100644 arch/arc/plat-arcfpga/smp.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 68350aa3d297..52f5c072f6da 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -116,9 +116,42 @@ config CPU_BIG_ENDIAN help Build kernel for Big Endian Mode of ARC CPU +config SMP + bool "Symmetric Multi-Processing (Incomplete)" + default n + select USE_GENERIC_SMP_HELPERS + help + This enables support for systems with more than one CPU. If you have + a system with only one CPU, like most personal computers, say N. If + you have a system with more than one CPU, say Y. + +if SMP + +config ARC_HAS_COH_CACHES + def_bool n + +config ARC_HAS_COH_LLSC + def_bool n + +config ARC_HAS_COH_RTSC + def_bool n + +config ARC_HAS_REENTRANT_IRQ_LV2 + def_bool n + +endif + +config NR_CPUS + int "Maximum number of CPUs (2-32)" + range 2 32 + depends on SMP + default "2" + menuconfig ARC_CACHE bool "Enable Cache Support" default y + # if SMP, cache enabled ONLY if ARC implementation has cache coherency + depends on !SMP || ARC_HAS_COH_CACHES if ARC_CACHE @@ -213,6 +246,8 @@ config ARC_COMPACT_IRQ_LEVELS default n # Timer HAS to be high priority, for any other high priority config select ARC_IRQ3_LV2 + # if SMP, LV2 enabled ONLY if ARC implementation has LV2 re-entrancy + depends on !SMP || ARC_HAS_REENTRANT_IRQ_LV2 if ARC_COMPACT_IRQ_LEVELS @@ -261,6 +296,8 @@ config ARC_HAS_RTSC bool "Insn: RTSC (64-bit r/o cycle counter)" default y depends on ARC_CPU_REL_4_10 + # if SMP, enable RTSC only if counter is coherent across cores + depends on !SMP || ARC_HAS_COH_RTSC endmenu # "ARC CPU Configuration" @@ -309,7 +346,7 @@ menuconfig ARC_DBG config ARC_DBG_TLB_PARANOIA bool "Paranoia Checks in Low Level TLB Handlers" - depends on ARC_DBG + depends on ARC_DBG && !SMP default n config ARC_DBG_TLB_MISS_COUNT diff --git a/arch/arc/Makefile b/arch/arc/Makefile index 642c0406d600..fae66bfc2bdb 100644 --- a/arch/arc/Makefile +++ b/arch/arc/Makefile @@ -133,3 +133,6 @@ archclean: # Thus forcing all exten calls in this file to be long calls export CFLAGS_decompress_inflate.o = -mmedium-calls export CFLAGS_initramfs.o = -mmedium-calls +ifdef CONFIG_SMP +export CFLAGS_core.o = -mmedium-calls +endif diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h index 23ef2de1e09f..23daa326fc9b 100644 --- a/arch/arc/include/asm/entry.h +++ b/arch/arc/include/asm/entry.h @@ -389,11 +389,19 @@ * to be saved again on kernel mode stack, as part of ptregs. *-------------------------------------------------------------*/ .macro EXCPN_PROLOG_FREEUP_REG reg +#ifdef CONFIG_SMP + sr \reg, [ARC_REG_SCRATCH_DATA0] +#else st \reg, [@ex_saved_reg1] +#endif .endm .macro EXCPN_PROLOG_RESTORE_REG reg +#ifdef CONFIG_SMP + lr \reg, [ARC_REG_SCRATCH_DATA0] +#else ld \reg, [@ex_saved_reg1] +#endif .endm /*-------------------------------------------------------------- @@ -508,7 +516,11 @@ /* restore original r9 , saved in int1_saved_reg * It will be saved on stack in macro: SAVE_CALLER_SAVED */ +#ifdef CONFIG_SMP + lr r9, [ARC_REG_SCRATCH_DATA0] +#else ld r9, [@int1_saved_reg] +#endif /* now we are ready to save the remaining context :) */ st orig_r8_IS_IRQ1, [sp, 8] /* Event Type */ @@ -639,6 +651,41 @@ bmsk \reg, \reg, 7 .endm +#ifdef CONFIG_SMP + +/*------------------------------------------------- + * Retrieve the current running task on this CPU + * 1. Determine curr CPU id. + * 2. Use it to index into _current_task[ ] + */ +.macro GET_CURR_TASK_ON_CPU reg + GET_CPU_ID \reg + ld.as \reg, [@_current_task, \reg] +.endm + +/*------------------------------------------------- + * Save a new task as the "current" task on this CPU + * 1. Determine curr CPU id. + * 2. Use it to index into _current_task[ ] + * + * Coded differently than GET_CURR_TASK_ON_CPU (which uses LD.AS) + * because ST r0, [r1, offset] can ONLY have s9 @offset + * while LD can take s9 (4 byte insn) or LIMM (8 byte insn) + */ + +.macro SET_CURR_TASK_ON_CPU tsk, tmp + GET_CPU_ID \tmp + add2 \tmp, @_current_task, \tmp + st \tsk, [\tmp] +#ifdef CONFIG_ARC_CURR_IN_REG + mov r25, \tsk +#endif + +.endm + + +#else /* Uniprocessor implementation of macros */ + .macro GET_CURR_TASK_ON_CPU reg ld \reg, [@_current_task] .endm @@ -650,6 +697,8 @@ #endif .endm +#endif /* SMP / UNI */ + /* ------------------------------------------------------------------ * Get the ptr to some field of Current Task at @off in task struct * -Uses r25 for Current task ptr if that is enabled diff --git a/arch/arc/include/asm/mmu_context.h b/arch/arc/include/asm/mmu_context.h index d12f3dec8b70..0d71fb11b57c 100644 --- a/arch/arc/include/asm/mmu_context.h +++ b/arch/arc/include/asm/mmu_context.h @@ -147,8 +147,10 @@ init_new_context(struct task_struct *tsk, struct mm_struct *mm) static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) { +#ifndef CONFIG_SMP /* PGD cached in MMU reg to avoid 3 mem lookups: task->mm->pgd */ write_aux_reg(ARC_REG_SCRATCH_DATA0, next->pgd); +#endif /* * Get a new ASID if task doesn't have a valid one. Possible when @@ -197,7 +199,9 @@ static inline void destroy_context(struct mm_struct *mm) static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next) { +#ifndef CONFIG_SMP write_aux_reg(ARC_REG_SCRATCH_DATA0, next->pgd); +#endif /* Unconditionally get a new ASID */ get_new_mmu_context(next); diff --git a/arch/arc/include/asm/mutex.h b/arch/arc/include/asm/mutex.h index 3be5e64da139..a2f88ff9f506 100644 --- a/arch/arc/include/asm/mutex.h +++ b/arch/arc/include/asm/mutex.h @@ -6,4 +6,13 @@ * published by the Free Software Foundation. */ +/* + * xchg() based mutex fast path maintains a state of 0 or 1, as opposed to + * atomic dec based which can "count" any number of lock contenders. + * This ideally needs to be fixed in core, but for now switching to dec ver. + */ +#if defined(CONFIG_SMP) && (CONFIG_NR_CPUS > 2) +#include +#else #include +#endif diff --git a/arch/arc/include/asm/pgtable.h b/arch/arc/include/asm/pgtable.h index dcb0701528aa..b7e36684c091 100644 --- a/arch/arc/include/asm/pgtable.h +++ b/arch/arc/include/asm/pgtable.h @@ -354,11 +354,15 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, * Thus use this macro only when you are certain that "current" is current * e.g. when dealing with signal frame setup code etc */ +#ifndef CONFIG_SMP #define pgd_offset_fast(mm, addr) \ ({ \ pgd_t *pgd_base = (pgd_t *) read_aux_reg(ARC_REG_SCRATCH_DATA0); \ pgd_base + pgd_index(addr); \ }) +#else +#define pgd_offset_fast(mm, addr) pgd_offset(mm, addr) +#endif extern void paging_init(void); extern pgd_t swapper_pg_dir[] __aligned(PAGE_SIZE); diff --git a/arch/arc/include/asm/processor.h b/arch/arc/include/asm/processor.h index b7b155610067..5f26b2c1cba0 100644 --- a/arch/arc/include/asm/processor.h +++ b/arch/arc/include/asm/processor.h @@ -58,7 +58,15 @@ unsigned long thread_saved_pc(struct task_struct *t); /* Prepare to copy thread state - unlazy all lazy status */ #define prepare_to_copy(tsk) do { } while (0) +/* + * A lot of busy-wait loops in SMP are based off of non-volatile data otherwise + * get optimised away by gcc + */ +#ifdef CONFIG_SMP +#define cpu_relax() __asm__ __volatile__ ("" : : : "memory") +#else #define cpu_relax() do { } while (0) +#endif #define copy_segments(tsk, mm) do { } while (0) #define release_segments(mm) do { } while (0) diff --git a/arch/arc/include/asm/smp.h b/arch/arc/include/asm/smp.h index 4341f3ba7d92..f91f1946272f 100644 --- a/arch/arc/include/asm/smp.h +++ b/arch/arc/include/asm/smp.h @@ -9,6 +9,69 @@ #ifndef __ASM_ARC_SMP_H #define __ASM_ARC_SMP_H +#ifdef CONFIG_SMP + +#include +#include +#include + +#define raw_smp_processor_id() (current_thread_info()->cpu) + +/* including cpumask.h leads to cyclic deps hence this Forward declaration */ +struct cpumask; + +/* + * APIs provided by arch SMP code to generic code + */ +extern void arch_send_call_function_single_ipi(int cpu); +extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); + +/* + * APIs provided by arch SMP code to rest of arch code + */ +extern void __init smp_init_cpus(void); +extern void __init first_lines_of_secondary(void); + +/* + * API expected BY platform smp code (FROM arch smp code) + * + * smp_ipi_irq_setup: + * Takes @cpu and @irq to which the arch-common ISR is hooked up + */ +extern int smp_ipi_irq_setup(int cpu, int irq); + +/* + * APIs expected FROM platform smp code + * + * arc_platform_smp_cpuinfo: + * returns a string containing info for /proc/cpuinfo + * + * arc_platform_smp_init_cpu: + * Called from start_kernel_secondary to do any CPU local setup + * such as starting a timer, setting up IPI etc + * + * arc_platform_smp_wait_to_boot: + * Called from early bootup code for non-Master CPUs to "park" them + * + * arc_platform_smp_wakeup_cpu: + * Called from __cpu_up (Master CPU) to kick start another one + * + * arc_platform_ipi_send: + * Takes @cpumask to which IPI(s) would be sent. + * The actual msg-id/buffer is manager in arch-common code + * + * arc_platform_ipi_clear: + * Takes @cpu which got IPI at @irq to do any IPI clearing + */ +extern const char *arc_platform_smp_cpuinfo(void); +extern void arc_platform_smp_init_cpu(void); +extern void arc_platform_smp_wait_to_boot(int cpu); +extern void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc); +extern void arc_platform_ipi_send(const struct cpumask *callmap); +extern void arc_platform_ipi_clear(int cpu, int irq); + +#endif /* CONFIG_SMP */ + /* * ARC700 doesn't support atomic Read-Modify-Write ops. * Originally Interrupts had to be disabled around code to gaurantee atomicity. @@ -18,10 +81,52 @@ * * (1) These insn were introduced only in 4.10 release. So for older released * support needed. + * + * (2) In a SMP setup, the LLOCK/SCOND atomiticity across CPUs needs to be + * gaurantted by the platform (not something which core handles). + * Assuming a platform won't, SMP Linux needs to use spinlocks + local IRQ + * disabling for atomicity. + * + * However exported spinlock API is not usable due to cyclic hdr deps + * (even after system.h disintegration upstream) + * asm/bitops.h -> linux/spinlock.h -> linux/preempt.h + * -> linux/thread_info.h -> linux/bitops.h -> asm/bitops.h + * + * So the workaround is to use the lowest level arch spinlock API. + * The exported spinlock API is smart enough to be NOP for !CONFIG_SMP, + * but same is not true for ARCH backend, hence the need for 2 variants */ #ifndef CONFIG_ARC_HAS_LLSC #include +#ifdef CONFIG_SMP + +#include + +extern arch_spinlock_t smp_atomic_ops_lock; +extern arch_spinlock_t smp_bitops_lock; + +#define atomic_ops_lock(flags) do { \ + local_irq_save(flags); \ + arch_spin_lock(&smp_atomic_ops_lock); \ +} while (0) + +#define atomic_ops_unlock(flags) do { \ + arch_spin_unlock(&smp_atomic_ops_lock); \ + local_irq_restore(flags); \ +} while (0) + +#define bitops_lock(flags) do { \ + local_irq_save(flags); \ + arch_spin_lock(&smp_bitops_lock); \ +} while (0) + +#define bitops_unlock(flags) do { \ + arch_spin_unlock(&smp_bitops_lock); \ + local_irq_restore(flags); \ +} while (0) + +#else /* !CONFIG_SMP */ #define atomic_ops_lock(flags) local_irq_save(flags) #define atomic_ops_unlock(flags) local_irq_restore(flags) @@ -29,6 +134,8 @@ #define bitops_lock(flags) local_irq_save(flags) #define bitops_unlock(flags) local_irq_restore(flags) +#endif /* !CONFIG_SMP */ + #endif /* !CONFIG_ARC_HAS_LLSC */ #endif diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index f32f65f98850..46c15ff97e97 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -13,6 +13,7 @@ obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o clk.o obj-y += devtree.o obj-$(CONFIG_MODULES) += arcksyms.o module.o +obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o CFLAGS_fpu.o += -mdpfp diff --git a/arch/arc/kernel/ctx_sw.c b/arch/arc/kernel/ctx_sw.c index fbf739cbaf7d..60844dac6132 100644 --- a/arch/arc/kernel/ctx_sw.c +++ b/arch/arc/kernel/ctx_sw.c @@ -58,7 +58,18 @@ __switch_to(struct task_struct *prev_task, struct task_struct *next_task) * For SMP extra work to get to &_current_task[cpu] * (open coded SET_CURR_TASK_ON_CPU) */ +#ifndef CONFIG_SMP "st %2, [@_current_task] \n\t" +#else + "lr r24, [identity] \n\t" + "lsr r24, r24, 8 \n\t" + "bmsk r24, r24, 7 \n\t" + "add2 r24, @_current_task, r24 \n\t" + "st %2, [r24] \n\t" +#endif +#ifdef CONFIG_ARC_CURR_IN_REG + "mov r25, %2 \n\t" +#endif /* get ksp of incoming task from tsk->thread.ksp */ "ld.as sp, [%2, %1] \n\t" diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index e33a0bf45589..3f6ce98fea11 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -232,7 +232,11 @@ ARC_EXIT handle_interrupt_level2 ARC_ENTRY handle_interrupt_level1 /* free up r9 as scratchpad */ +#ifdef CONFIG_SMP + sr r9, [ARC_REG_SCRATCH_DATA0] +#else st r9, [@int1_saved_reg] +#endif ;Which mode (user/kernel) was the system in when intr occured lr r9, [status32_l1] diff --git a/arch/arc/kernel/head.S b/arch/arc/kernel/head.S index e63f6a43abb1..006dec3fc353 100644 --- a/arch/arc/kernel/head.S +++ b/arch/arc/kernel/head.S @@ -27,6 +27,15 @@ stext: ; Don't clobber r0-r4 yet. It might have bootloader provided info ;------------------------------------------------------------------- +#ifdef CONFIG_SMP + ; Only Boot (Master) proceeds. Others wait in platform dependent way + ; IDENTITY Reg [ 3 2 1 0 ] + ; (cpu-id) ^^^ => Zero for UP ARC700 + ; => #Core-ID if SMP (Master 0) + GET_CPU_ID r5 + cmp r5, 0 + jnz arc_platform_smp_wait_to_boot +#endif ; Clear BSS before updating any globals ; XXX: use ZOL here mov r5, __bss_start @@ -76,3 +85,27 @@ stext: GET_TSK_STACK_BASE r9, sp ; r9 = tsk, sp = stack base(output) j start_kernel ; "C" entry point + +#ifdef CONFIG_SMP +;---------------------------------------------------------------- +; First lines of code run by secondary before jumping to 'C' +;---------------------------------------------------------------- + .section .init.text, "ax",@progbits + .type first_lines_of_secondary, @function + .globl first_lines_of_secondary + +first_lines_of_secondary: + + ; setup per-cpu idle task as "current" on this CPU + ld r0, [@secondary_idle_tsk] + SET_CURR_TASK_ON_CPU r0, r1 + + ; setup stack (fp, sp) + mov fp, 0 + + ; set it's stack base to tsk->thread_info bottom + GET_TSK_STACK_BASE r0, sp + + j start_kernel_secondary + +#endif diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c index ca70894e2309..df7da2b5a5bd 100644 --- a/arch/arc/kernel/irq.c +++ b/arch/arc/kernel/irq.c @@ -124,6 +124,11 @@ void __init init_IRQ(void) { init_onchip_IRQ(); plat_init_IRQ(); + +#ifdef CONFIG_SMP + /* Master CPU can initialize it's side of IPI */ + arc_platform_smp_init_cpu(); +#endif } /* diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 27aebd6d9513..4026b5a004d2 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -86,6 +86,10 @@ void __init setup_arch(char **cmdline_p) setup_processor(); +#ifdef CONFIG_SMP + smp_init_cpus(); +#endif + setup_arch_memory(); unflatten_device_tree(); diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c new file mode 100644 index 000000000000..1f762ad6969b --- /dev/null +++ b/arch/arc/kernel/smp.c @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * RajeshwarR: Dec 11, 2007 + * -- Added support for Inter Processor Interrupts + * + * Vineetg: Nov 1st, 2007 + * -- Initial Write (Borrowed heavily from ARM) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +arch_spinlock_t smp_atomic_ops_lock = __ARCH_SPIN_LOCK_UNLOCKED; +arch_spinlock_t smp_bitops_lock = __ARCH_SPIN_LOCK_UNLOCKED; + +/* XXX: per cpu ? Only needed once in early seconday boot */ +struct task_struct *secondary_idle_tsk; + +/* Called from start_kernel */ +void __init smp_prepare_boot_cpu(void) +{ +} + +/* + * Initialise the CPU possible map early - this describes the CPUs + * which may be present or become present in the system. + */ +void __init smp_init_cpus(void) +{ + unsigned int i; + + for (i = 0; i < NR_CPUS; i++) + set_cpu_possible(i, true); +} + +/* called from init ( ) => process 1 */ +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + int i; + + /* + * Initialise the present map, which describes the set of CPUs + * actually populated at the present time. + */ + for (i = 0; i < max_cpus; i++) + set_cpu_present(i, true); +} + +void __init smp_cpus_done(unsigned int max_cpus) +{ + +} + +/* + * After power-up, a non Master CPU needs to wait for Master to kick start it + * + * The default implementation halts + * + * This relies on platform specific support allowing Master to directly set + * this CPU's PC (to be @first_lines_of_secondary() and kick start it. + * + * In lack of such h/w assist, platforms can override this function + * - make this function busy-spin on a token, eventually set by Master + * (from arc_platform_smp_wakeup_cpu()) + * - Once token is available, jump to @first_lines_of_secondary + * (using inline asm). + * + * Alert: can NOT use stack here as it has not been determined/setup for CPU. + * If it turns out to be elaborate, it's better to code it in assembly + * + */ +void __attribute__((weak)) arc_platform_smp_wait_to_boot(int cpu) +{ + /* + * As a hack for debugging - since debugger will single-step over the + * FLAG insn - wrap the halt itself it in a self loop + */ + __asm__ __volatile__( + "1: \n" + " flag 1 \n" + " b 1b \n"); +} + +/* + * The very first "C" code executed by secondary + * Called from asm stub in head.S + * "current"/R25 already setup by low level boot code + */ +void __cpuinit start_kernel_secondary(void) +{ + struct mm_struct *mm = &init_mm; + unsigned int cpu = smp_processor_id(); + + /* MMU, Caches, Vector Table, Interrupts etc */ + setup_processor(); + + atomic_inc(&mm->mm_users); + atomic_inc(&mm->mm_count); + current->active_mm = mm; + + notify_cpu_starting(cpu); + set_cpu_online(cpu, true); + + pr_info("## CPU%u LIVE ##: Executing Code...\n", cpu); + + arc_platform_smp_init_cpu(); + + arc_local_timer_setup(cpu); + + local_irq_enable(); + preempt_disable(); + cpu_idle(); +} + +/* + * Called from kernel_init( ) -> smp_init( ) - for each CPU + * + * At this point, Secondary Processor is "HALT"ed: + * -It booted, but was halted in head.S + * -It was configured to halt-on-reset + * So need to wake it up. + * + * Essential requirements being where to run from (PC) and stack (SP) +*/ +int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) +{ + unsigned long wait_till; + + secondary_idle_tsk = idle; + + pr_info("Idle Task [%d] %p", cpu, idle); + pr_info("Trying to bring up CPU%u ...\n", cpu); + + arc_platform_smp_wakeup_cpu(cpu, + (unsigned long)first_lines_of_secondary); + + /* wait for 1 sec after kicking the secondary */ + wait_till = jiffies + HZ; + while (time_before(jiffies, wait_till)) { + if (cpu_online(cpu)) + break; + } + + if (!cpu_online(cpu)) { + pr_info("Timeout: CPU%u FAILED to comeup !!!\n", cpu); + return -1; + } + + secondary_idle_tsk = NULL; + + return 0; +} + +/* + * not supported here + */ +int __init setup_profiling_timer(unsigned int multiplier) +{ + return -EINVAL; +} + +/*****************************************************************************/ +/* Inter Processor Interrupt Handling */ +/*****************************************************************************/ + +/* + * structures for inter-processor calls + * A Collection of single bit ipi messages + * + */ + +/* + * TODO_rajesh investigate tlb message types. + * IPI Timer not needed because each ARC has an individual Interrupting Timer + */ +enum ipi_msg_type { + IPI_NOP = 0, + IPI_RESCHEDULE = 1, + IPI_CALL_FUNC, + IPI_CALL_FUNC_SINGLE, + IPI_CPU_STOP +}; + +struct ipi_data { + unsigned long bits; +}; + +static DEFINE_PER_CPU(struct ipi_data, ipi_data); + +static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg) +{ + unsigned long flags; + unsigned int cpu; + + local_irq_save(flags); + + for_each_cpu(cpu, callmap) { + struct ipi_data *ipi = &per_cpu(ipi_data, cpu); + set_bit(msg, &ipi->bits); + } + + /* Call the platform specific cross-CPU call function */ + arc_platform_ipi_send(callmap); + + local_irq_restore(flags); +} + +void smp_send_reschedule(int cpu) +{ + ipi_send_msg(cpumask_of(cpu), IPI_RESCHEDULE); +} + +void smp_send_stop(void) +{ + struct cpumask targets; + cpumask_copy(&targets, cpu_online_mask); + cpumask_clear_cpu(smp_processor_id(), &targets); + ipi_send_msg(&targets, IPI_CPU_STOP); +} + +void arch_send_call_function_single_ipi(int cpu) +{ + ipi_send_msg(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE); +} + +void arch_send_call_function_ipi_mask(const struct cpumask *mask) +{ + ipi_send_msg(mask, IPI_CALL_FUNC); +} + +/* + * ipi_cpu_stop - handle IPI from smp_send_stop() + */ +static void ipi_cpu_stop(unsigned int cpu) +{ + machine_halt(); +} + +static inline void __do_IPI(unsigned long *ops, struct ipi_data *ipi, int cpu) +{ + unsigned long msg = 0; + + do { + msg = find_next_bit(ops, BITS_PER_LONG, msg+1); + + switch (msg) { + case IPI_RESCHEDULE: + scheduler_ipi(); + break; + + case IPI_CALL_FUNC: + generic_smp_call_function_interrupt(); + break; + + case IPI_CALL_FUNC_SINGLE: + generic_smp_call_function_single_interrupt(); + break; + + case IPI_CPU_STOP: + ipi_cpu_stop(cpu); + break; + } + } while (msg < BITS_PER_LONG); + +} + +/* + * arch-common ISR to handle for inter-processor interrupts + * Has hooks for platform specific IPI + */ +irqreturn_t do_IPI(int irq, void *dev_id) +{ + int cpu = smp_processor_id(); + struct ipi_data *ipi = &per_cpu(ipi_data, cpu); + unsigned long ops; + + arc_platform_ipi_clear(cpu, irq); + + /* + * XXX: is this loop really needed + * And do we need to move ipi_clean inside + */ + while ((ops = xchg(&ipi->bits, 0)) != 0) + __do_IPI(&ops, ipi, cpu); + + return IRQ_HANDLED; +} + +/* + * API called by platform code to hookup arch-common ISR to their IPI IRQ + */ +static DEFINE_PER_CPU(int, ipi_dev); +int smp_ipi_irq_setup(int cpu, int irq) +{ + int *dev_id = &per_cpu(ipi_dev, smp_processor_id()); + return request_percpu_irq(irq, do_IPI, "IPI Interrupt", dev_id); +} diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c index 232a0ff80a5e..e96030c13b52 100644 --- a/arch/arc/mm/tlb.c +++ b/arch/arc/mm/tlb.c @@ -474,6 +474,12 @@ void __init arc_mmu_init(void) /* Enable the MMU */ write_aux_reg(ARC_REG_PID, MMU_ENABLE); + + /* In smp we use this reg for interrupt 1 scratch */ +#ifndef CONFIG_SMP + /* swapper_pg_dir is the pgd for the kernel, used by vmalloc */ + write_aux_reg(ARC_REG_SCRATCH_DATA0, swapper_pg_dir); +#endif } /* diff --git a/arch/arc/mm/tlbex.S b/arch/arc/mm/tlbex.S index 164b02169498..4b1ad2d905ca 100644 --- a/arch/arc/mm/tlbex.S +++ b/arch/arc/mm/tlbex.S @@ -57,9 +57,15 @@ .global ex_saved_reg1 .align 1 << L1_CACHE_SHIFT ; IMP: Must be Cache Line aligned .type ex_saved_reg1, @object +#ifdef CONFIG_SMP + .size ex_saved_reg1, (CONFIG_NR_CPUS << L1_CACHE_SHIFT) +ex_saved_reg1: + .zero (CONFIG_NR_CPUS << L1_CACHE_SHIFT) +#else .size ex_saved_reg1, 16 ex_saved_reg1: .zero 16 +#endif ;============================================================================ ; Troubleshooting Stuff @@ -116,7 +122,13 @@ ex_saved_reg1: lr r2, [efa] +#ifndef CONFIG_SMP lr r1, [ARC_REG_SCRATCH_DATA0] ; current pgd +#else + GET_CURR_TASK_ON_CPU r1 + ld r1, [r1, TASK_ACT_MM] + ld r1, [r1, MM_PGD] +#endif lsr r0, r2, PGDIR_SHIFT ; Bits for indexing into PGD ld.as r1, [r1, r0] ; PGD entry corresp to faulting addr @@ -192,12 +204,28 @@ ex_saved_reg1: ; ".size ex_saved_reg1, 16" ; [All of this dance is to avoid stack switching for each TLB Miss, since we ; only need to save only a handful of regs, as opposed to complete reg file] +; +; For ARC700 SMP, the "global" obviously can't be used for free up the FIRST +; core reg as it will not be SMP safe. +; Thus scratch AUX reg is used (and no longer used to cache task PGD). +; To save the rest of 3 regs - per cpu, the global is made "per-cpu". +; Epilogue thus has to locate the "per-cpu" storage for regs. +; To avoid cache line bouncing the per-cpu global is aligned/sized per +; L1_CACHE_SHIFT, despite fundamentally needing to be 12 bytes only. Hence +; ".size ex_saved_reg1, (CONFIG_NR_CPUS << L1_CACHE_SHIFT)" ; As simple as that.... .macro TLBMISS_FREEUP_REGS +#ifdef CONFIG_SMP + sr r0, [ARC_REG_SCRATCH_DATA0] ; freeup r0 to code with + GET_CPU_ID r0 ; get to per cpu scratch mem, + lsl r0, r0, L1_CACHE_SHIFT ; cache line wide per cpu + add r0, @ex_saved_reg1, r0 +#else st r0, [@ex_saved_reg1] mov_s r0, @ex_saved_reg1 +#endif st_s r1, [r0, 4] st_s r2, [r0, 8] st_s r3, [r0, 12] @@ -210,11 +238,21 @@ ex_saved_reg1: ;----------------------------------------------------------------- .macro TLBMISS_RESTORE_REGS +#ifdef CONFIG_SMP + GET_CPU_ID r0 ; get to per cpu scratch mem + lsl r0, r0, L1_CACHE_SHIFT ; each is cache line wide + add r0, @ex_saved_reg1, r0 + ld_s r3, [r0,12] + ld_s r2, [r0, 8] + ld_s r1, [r0, 4] + lr r0, [ARC_REG_SCRATCH_DATA0] +#else mov_s r0, @ex_saved_reg1 ld_s r3, [r0,12] ld_s r2, [r0, 8] ld_s r1, [r0, 4] ld_s r0, [r0] +#endif .endm .section .text, "ax",@progbits ;Fast Path Code, candidate for ICCM diff --git a/arch/arc/plat-arcfpga/Kconfig b/arch/arc/plat-arcfpga/Kconfig index 7af3a4e7f7d2..38752bfb91e0 100644 --- a/arch/arc/plat-arcfpga/Kconfig +++ b/arch/arc/plat-arcfpga/Kconfig @@ -13,6 +13,7 @@ choice config ARC_BOARD_ANGEL4 bool "ARC Angel4" + select ISS_SMP_EXTN if SMP help ARC Angel4 FPGA Ref Platform (Xilinx Virtex Based) @@ -21,6 +22,19 @@ config ARC_BOARD_ML509 help ARC ML509 FPGA Ref Platform (Xilinx Virtex-5 Based) +config ISS_SMP_EXTN + bool "ARC SMP Extensions (ISS Models only)" + default n + depends on SMP + select ARC_HAS_COH_RTSC + help + SMP Extensions to ARC700, in a "simulation only" Model, supported in + ARC ISS (Instruction Set Simulator). + The SMP extensions include: + -IDU (Interrupt Distribution Unit) + -XTL (To enable CPU start/stop/set-PC for another CPU) + It doesn't provide coherent Caches and/or Atomic Ops (LLOCK/SCOND) + endchoice config ARC_SERIAL_BAUD diff --git a/arch/arc/plat-arcfpga/Makefile b/arch/arc/plat-arcfpga/Makefile index 385eb9d83b63..2a828bec8212 100644 --- a/arch/arc/plat-arcfpga/Makefile +++ b/arch/arc/plat-arcfpga/Makefile @@ -7,3 +7,4 @@ # obj-y := platform.o irq.o +obj-$(CONFIG_SMP) += smp.o diff --git a/arch/arc/plat-arcfpga/include/plat/irq.h b/arch/arc/plat-arcfpga/include/plat/irq.h index b34e08734c65..255b90e973ee 100644 --- a/arch/arc/plat-arcfpga/include/plat/irq.h +++ b/arch/arc/plat-arcfpga/include/plat/irq.h @@ -12,7 +12,11 @@ #ifndef __PLAT_IRQ_H #define __PLAT_IRQ_H -#define NR_IRQS 16 +#ifdef CONFIG_SMP +#define NR_IRQS 32 +#else +#define NR_IRQS 16 +#endif #define UART0_IRQ 5 #define UART1_IRQ 10 @@ -24,4 +28,8 @@ #define PCI_IRQ 14 #define PS2_IRQ 15 +#ifdef CONFIG_SMP +#define IDU_INTERRUPT_0 16 +#endif + #endif diff --git a/arch/arc/plat-arcfpga/include/plat/smp.h b/arch/arc/plat-arcfpga/include/plat/smp.h new file mode 100644 index 000000000000..8c5e46c01b15 --- /dev/null +++ b/arch/arc/plat-arcfpga/include/plat/smp.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Rajeshwar Ranga: Interrupt Distribution Unit API's + */ + +#ifndef __PLAT_ARCFPGA_SMP_H +#define __PLAT_ARCFPGA_SMP_H + +#ifdef CONFIG_SMP + +#include +#include + +#define ARC_AUX_IDU_REG_CMD 0x2000 +#define ARC_AUX_IDU_REG_PARAM 0x2001 + +#define ARC_AUX_XTL_REG_CMD 0x2002 +#define ARC_AUX_XTL_REG_PARAM 0x2003 + +#define ARC_REG_MP_BCR 0x2021 + +#define ARC_XTL_CMD_WRITE_PC 0x04 +#define ARC_XTL_CMD_CLEAR_HALT 0x02 + +/* + * Build Configuration Register which identifies the sub-components + */ +struct bcr_mp { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int mp_arch:16, pad:5, sdu:1, idu:1, scu:1, ver:8; +#else + unsigned int ver:8, scu:1, idu:1, sdu:1, pad:5, mp_arch:16; +#endif +}; + +/* IDU supports 256 common interrupts */ +#define NR_IDU_IRQS 256 + +/* + * The Aux Regs layout is same bit-by-bit in both BE/LE modes. + * However when casted as a bitfield encoded "C" struct, gcc treats it as + * memory, generating different code for BE/LE, requiring strcture adj (see + * include/asm/arcregs.h) + * + * However when manually "carving" the value for a Aux, no special handling + * of BE is needed because of the property discribed above + */ +#define IDU_SET_COMMAND(irq, cmd) \ +do { \ + uint32_t __val; \ + __val = (((irq & 0xFF) << 8) | (cmd & 0xFF)); \ + write_aux_reg(ARC_AUX_IDU_REG_CMD, __val); \ +} while (0) + +#define IDU_SET_PARAM(par) write_aux_reg(ARC_AUX_IDU_REG_PARAM, par) +#define IDU_GET_PARAM() read_aux_reg(ARC_AUX_IDU_REG_PARAM) + +/* IDU Commands */ +#define IDU_DISABLE 0x00 +#define IDU_ENABLE 0x01 +#define IDU_IRQ_CLEAR 0x02 +#define IDU_IRQ_ASSERT 0x03 +#define IDU_IRQ_WMODE 0x04 +#define IDU_IRQ_STATUS 0x05 +#define IDU_IRQ_ACK 0x06 +#define IDU_IRQ_PEND 0x07 +#define IDU_IRQ_RMODE 0x08 +#define IDU_IRQ_WBITMASK 0x09 +#define IDU_IRQ_RBITMASK 0x0A + +#define idu_enable() IDU_SET_COMMAND(0, IDU_ENABLE) +#define idu_disable() IDU_SET_COMMAND(0, IDU_DISABLE) + +#define idu_irq_assert(irq) IDU_SET_COMMAND((irq), IDU_IRQ_ASSERT) +#define idu_irq_clear(irq) IDU_SET_COMMAND((irq), IDU_IRQ_CLEAR) + +/* IDU Interrupt Mode - Destination Encoding */ +#define IDU_IRQ_MOD_DISABLE 0x00 +#define IDU_IRQ_MOD_ROUND_RECP 0x01 +#define IDU_IRQ_MOD_TCPU_FIRSTRECP 0x02 +#define IDU_IRQ_MOD_TCPU_ALLRECP 0x03 + +/* IDU Interrupt Mode - Triggering Mode */ +#define IDU_IRQ_MODE_LEVEL_TRIG 0x00 +#define IDU_IRQ_MODE_PULSE_TRIG 0x01 + +#define IDU_IRQ_MODE_PARAM(dest_mode, trig_mode) \ + (((trig_mode & 0x01) << 15) | (dest_mode & 0xFF)) + +struct idu_irq_config { + uint8_t irq; + uint8_t dest_mode; + uint8_t trig_mode; +}; + +struct idu_irq_status { + uint8_t irq; + bool enabled; + bool status; + bool ack; + bool pend; + uint8_t next_rr; +}; + +extern void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask); +extern void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode); + +#endif /* CONFIG_SMP */ + +#endif diff --git a/arch/arc/plat-arcfpga/irq.c b/arch/arc/plat-arcfpga/irq.c index ed726360b9f6..590edd174c47 100644 --- a/arch/arc/plat-arcfpga/irq.c +++ b/arch/arc/plat-arcfpga/irq.c @@ -9,7 +9,17 @@ */ #include +#include void __init plat_init_IRQ(void) { + /* + * SMP Hack because UART IRQ hardwired to cpu0 (boot-cpu) but if the + * request_irq() comes from any other CPU, the low level IRQ unamsking + * essential for getting Interrupts won't be enabled on cpu0, locking + * up the UART state machine. + */ +#ifdef CONFIG_SMP + arch_unmask_irq(UART0_IRQ); +#endif } diff --git a/arch/arc/plat-arcfpga/smp.c b/arch/arc/plat-arcfpga/smp.c new file mode 100644 index 000000000000..a95fcdb29033 --- /dev/null +++ b/arch/arc/plat-arcfpga/smp.c @@ -0,0 +1,167 @@ +/* + * ARC700 Simulation-only Extensions for SMP + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Vineet Gupta - 2012 : split off arch common and plat specific SMP + * Rajeshwar Ranga - 2007 : Interrupt Distribution Unit API's + */ + +#include +#include +#include + +static char smp_cpuinfo_buf[128]; + +/* + *------------------------------------------------------------------- + * Platform specific callbacks expected by arch SMP code + *------------------------------------------------------------------- + */ + +const char *arc_platform_smp_cpuinfo(void) +{ +#define IS_AVAIL1(var, str) ((var) ? str : "") + + struct bcr_mp mp; + + READ_BCR(ARC_REG_MP_BCR, mp); + + sprintf(smp_cpuinfo_buf, "Extn [700-SMP]: v%d, arch(%d) %s %s %s\n", + mp.ver, mp.mp_arch, IS_AVAIL1(mp.scu, "SCU"), + IS_AVAIL1(mp.idu, "IDU"), IS_AVAIL1(mp.sdu, "SDU")); + + return smp_cpuinfo_buf; +} + +/* + * Master kick starting another CPU + */ +void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc) +{ + /* setup the start PC */ + write_aux_reg(ARC_AUX_XTL_REG_PARAM, pc); + + /* Trigger WRITE_PC cmd for this cpu */ + write_aux_reg(ARC_AUX_XTL_REG_CMD, + (ARC_XTL_CMD_WRITE_PC | (cpu << 8))); + + /* Take the cpu out of Halt */ + write_aux_reg(ARC_AUX_XTL_REG_CMD, + (ARC_XTL_CMD_CLEAR_HALT | (cpu << 8))); + +} + +/* + * Any SMP specific init any CPU does when it comes up. + * Here we setup the CPU to enable Inter-Processor-Interrupts + * Called for each CPU + * -Master : init_IRQ() + * -Other(s) : start_kernel_secondary() + */ +void arc_platform_smp_init_cpu(void) +{ + int cpu = smp_processor_id(); + + /* Check if CPU is configured for more than 16 interrupts */ + if (NR_IRQS <= 16 || get_hw_config_num_irq() <= 16) + panic("[arcfpga] IRQ system can't support IDU IPI\n"); + + idu_disable(); + + /**************************************************************** + * IDU provides a set of Common IRQs, each of which can be dynamically + * attached to (1|many|all) CPUs. + * The Common IRQs [0-15] are mapped as CPU pvt [16-31] + * + * Here we use a simple 1:1 mapping: + * A CPU 'x' is wired to Common IRQ 'x'. + * So an IDU ASSERT on IRQ 'x' will trigger Interupt on CPU 'x', which + * makes up for our simple IPI plumbing. + * + * TBD: Have a dedicated multicast IRQ for sending IPIs to all CPUs + * w/o having to do one-at-a-time + ******************************************************************/ + + /* + * Claim an IRQ which would trigger IPI on this CPU. + * In IDU parlance it involves setting up a cpu bitmask for the IRQ + * The bitmap here contains only 1 CPU (self). + */ + idu_irq_set_tgtcpu(cpu, 0x1 << cpu); + + /* Set the IRQ destination to use the bitmask above */ + idu_irq_set_mode(cpu, 7, /* XXX: IDU_IRQ_MOD_TCPU_ALLRECP: ISS bug */ + IDU_IRQ_MODE_PULSE_TRIG); + + idu_enable(); + + /* Attach the arch-common IPI ISR to our IDU IRQ */ + smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu); +} + +void arc_platform_ipi_send(const struct cpumask *callmap) +{ + unsigned int cpu; + + for_each_cpu(cpu, callmap) + idu_irq_assert(cpu); +} + +void arc_platform_ipi_clear(int cpu, int irq) +{ + idu_irq_clear(IDU_INTERRUPT_0 + cpu); +} + +/* + *------------------------------------------------------------------- + * Low level Platform IPI Providers + *------------------------------------------------------------------- + */ + +/* Set the Mode for the Common IRQ */ +void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode) +{ + uint32_t par = IDU_IRQ_MODE_PARAM(dest_mode, trig_mode); + + IDU_SET_PARAM(par); + IDU_SET_COMMAND(irq, IDU_IRQ_WMODE); +} + +/* Set the target cpu Bitmask for Common IRQ */ +void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask) +{ + IDU_SET_PARAM(mask); + IDU_SET_COMMAND(irq, IDU_IRQ_WBITMASK); +} + +/* Get the Interrupt Acknowledged status for IRQ (as CPU Bitmask) */ +bool idu_irq_get_ack(uint8_t irq) +{ + uint32_t val; + + IDU_SET_COMMAND(irq, IDU_IRQ_ACK); + val = IDU_GET_PARAM(); + + return val & (1 << irq); +} + +/* + * Get the Interrupt Pending status for IRQ (as CPU Bitmask) + * -Pending means CPU has not yet noticed the IRQ (e.g. disabled) + * -After Interrupt has been taken, the IPI expcitily needs to be + * cleared, to be acknowledged. + */ +bool idu_irq_get_pend(uint8_t irq) +{ + uint32_t val; + + IDU_SET_COMMAND(irq, IDU_IRQ_PEND); + val = IDU_GET_PARAM(); + + return val & (1 << irq); +} From 854a0d95056c265d96cb449bc97bc5ef9bbed835 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Tue, 22 Jan 2013 17:03:19 +0530 Subject: [PATCH 2839/4607] ARC: DWARF2 .debug_frame based stack unwinder -Originally written by Rajeshwar Ranga -Derived off of generic unwinder in 2.6.19 and adapted to ARC Signed-off-by: Vineet Gupta Cc: Rajeshwar Ranga --- arch/arc/Kconfig | 15 + arch/arc/include/asm/module.h | 7 + arch/arc/include/asm/unwind.h | 163 ++++ arch/arc/kernel/Makefile | 6 + arch/arc/kernel/entry.S | 9 + arch/arc/kernel/module.c | 58 ++ arch/arc/kernel/setup.c | 3 + arch/arc/kernel/unwind.c | 1329 +++++++++++++++++++++++++++++++++ arch/arc/kernel/vmlinux.lds.S | 23 +- 9 files changed, 1612 insertions(+), 1 deletion(-) create mode 100644 arch/arc/include/asm/unwind.h create mode 100644 arch/arc/kernel/unwind.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 52f5c072f6da..0979d8e6fc16 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -25,6 +25,7 @@ config ARC select HAVE_ARCH_TRACEHOOK select HAVE_GENERIC_HARDIRQS select HAVE_MEMBLOCK + select HAVE_MOD_ARCH_SPECIFIC if ARC_DW2_UNWIND select HAVE_OPROFILE select IRQ_DOMAIN select MODULES_USE_ELF_RELA @@ -344,6 +345,20 @@ menuconfig ARC_DBG bool "ARC debugging" default y +config ARC_DW2_UNWIND + bool "Enable DWARF specific kernel stack unwind" + depends on ARC_DBG + default y + select KALLSYMS + help + Compiles the kernel with DWARF unwind information and can be used + to get stack backtraces. + + If you say Y here the resulting kernel image will be slightly larger + but not slower, and it will give very useful debugging information. + If you don't debug the kernel, you can say N, but we may not be able + to solve problems without frame unwind information + config ARC_DBG_TLB_PARANOIA bool "Paranoia Checks in Low Level TLB Handlers" depends on ARC_DBG && !SMP diff --git a/arch/arc/include/asm/module.h b/arch/arc/include/asm/module.h index 234b4354e2b3..518222bb3f8e 100644 --- a/arch/arc/include/asm/module.h +++ b/arch/arc/include/asm/module.h @@ -14,6 +14,13 @@ #include +#ifdef CONFIG_ARC_DW2_UNWIND +struct mod_arch_specific { + void *unw_info; + int unw_sec_idx; +}; +#endif + #define MODULE_PROC_FAMILY "ARC700" #define MODULE_ARCH_VERMAGIC MODULE_PROC_FAMILY diff --git a/arch/arc/include/asm/unwind.h b/arch/arc/include/asm/unwind.h new file mode 100644 index 000000000000..7ca628b6ee2a --- /dev/null +++ b/arch/arc/include/asm/unwind.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_UNWIND_H +#define _ASM_ARC_UNWIND_H + +#ifdef CONFIG_ARC_DW2_UNWIND + +#include + +struct arc700_regs { + unsigned long r0; + unsigned long r1; + unsigned long r2; + unsigned long r3; + unsigned long r4; + unsigned long r5; + unsigned long r6; + unsigned long r7; + unsigned long r8; + unsigned long r9; + unsigned long r10; + unsigned long r11; + unsigned long r12; + unsigned long r13; + unsigned long r14; + unsigned long r15; + unsigned long r16; + unsigned long r17; + unsigned long r18; + unsigned long r19; + unsigned long r20; + unsigned long r21; + unsigned long r22; + unsigned long r23; + unsigned long r24; + unsigned long r25; + unsigned long r26; + unsigned long r27; /* fp */ + unsigned long r28; /* sp */ + unsigned long r29; + unsigned long r30; + unsigned long r31; /* blink */ + unsigned long r63; /* pc */ +}; + +struct unwind_frame_info { + struct arc700_regs regs; + struct task_struct *task; + unsigned call_frame:1; +}; + +#define UNW_PC(frame) ((frame)->regs.r63) +#define UNW_SP(frame) ((frame)->regs.r28) +#define UNW_BLINK(frame) ((frame)->regs.r31) + +/* Rajesh FIXME */ +#ifdef CONFIG_FRAME_POINTER +#define UNW_FP(frame) ((frame)->regs.r27) +#define FRAME_RETADDR_OFFSET 4 +#define FRAME_LINK_OFFSET 0 +#define STACK_BOTTOM_UNW(tsk) STACK_LIMIT((tsk)->thread.ksp) +#define STACK_TOP_UNW(tsk) ((tsk)->thread.ksp) +#else +#define UNW_FP(frame) ((void)(frame), 0) +#endif + +#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1)) + +#define UNW_REGISTER_INFO \ + PTREGS_INFO(r0), \ + PTREGS_INFO(r1), \ + PTREGS_INFO(r2), \ + PTREGS_INFO(r3), \ + PTREGS_INFO(r4), \ + PTREGS_INFO(r5), \ + PTREGS_INFO(r6), \ + PTREGS_INFO(r7), \ + PTREGS_INFO(r8), \ + PTREGS_INFO(r9), \ + PTREGS_INFO(r10), \ + PTREGS_INFO(r11), \ + PTREGS_INFO(r12), \ + PTREGS_INFO(r13), \ + PTREGS_INFO(r14), \ + PTREGS_INFO(r15), \ + PTREGS_INFO(r16), \ + PTREGS_INFO(r17), \ + PTREGS_INFO(r18), \ + PTREGS_INFO(r19), \ + PTREGS_INFO(r20), \ + PTREGS_INFO(r21), \ + PTREGS_INFO(r22), \ + PTREGS_INFO(r23), \ + PTREGS_INFO(r24), \ + PTREGS_INFO(r25), \ + PTREGS_INFO(r26), \ + PTREGS_INFO(r27), \ + PTREGS_INFO(r28), \ + PTREGS_INFO(r29), \ + PTREGS_INFO(r30), \ + PTREGS_INFO(r31), \ + PTREGS_INFO(r63) + +#define UNW_DEFAULT_RA(raItem, dataAlign) \ + ((raItem).where == Memory && !((raItem).value * (dataAlign) + 4)) + +extern int arc_unwind(struct unwind_frame_info *frame); +extern void arc_unwind_init(void); +extern void arc_unwind_setup(void); +extern void *unwind_add_table(struct module *module, const void *table_start, + unsigned long table_size); +extern void unwind_remove_table(void *handle, int init_only); + +static inline int +arch_unwind_init_running(struct unwind_frame_info *info, + int (*callback) (struct unwind_frame_info *info, + void *arg), + void *arg) +{ + return 0; +} + +static inline int arch_unw_user_mode(const struct unwind_frame_info *info) +{ + return 0; +} + +static inline void arch_unw_init_blocked(struct unwind_frame_info *info) +{ + return; +} + +static inline void arch_unw_init_frame_info(struct unwind_frame_info *info, + struct pt_regs *regs) +{ + return; +} + +#else + +#define UNW_PC(frame) ((void)(frame), 0) +#define UNW_SP(frame) ((void)(frame), 0) +#define UNW_FP(frame) ((void)(frame), 0) + +static inline void arc_unwind_init(void) +{ +} + +static inline void arc_unwind_setup(void) +{ +} +#define unwind_add_table(a, b, c) +#define unwind_remove_table(a, b) + +#endif /* CONFIG_ARC_DW2_UNWIND */ + +#endif /* _ASM_ARC_UNWIND_H */ diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index 46c15ff97e97..38e4715f3349 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -14,10 +14,16 @@ obj-y += devtree.o obj-$(CONFIG_MODULES) += arcksyms.o module.o obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o CFLAGS_fpu.o += -mdpfp +ifdef CONFIG_ARC_DW2_UNWIND +CFLAGS_ctx_sw.o += -fno-omit-frame-pointer +obj-y += ctx_sw.o +else obj-y += ctx_sw_asm.o +endif extra-y := vmlinux.lds head.o diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 3f6ce98fea11..021cfa46a1dc 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -815,3 +815,12 @@ ARC_ENTRY sys_clone_wrapper b ret_from_system_call ARC_EXIT sys_clone_wrapper + +#ifdef CONFIG_ARC_DW2_UNWIND +; Workaround for bug 94179 (STAR ): +; Despite -fasynchronous-unwind-tables, linker is not making dwarf2 unwinder +; section (.debug_frame) as loadable. So we force it here. +; This also fixes STAR 9000487933 where the prev-workaround (objcopy --setflag) +; would not work after a clean build due to kernel build system dependencies. +.section .debug_frame, "wa",@progbits +#endif diff --git a/arch/arc/kernel/module.c b/arch/arc/kernel/module.c index a1bb70d6e97d..cdd359352c0a 100644 --- a/arch/arc/kernel/module.c +++ b/arch/arc/kernel/module.c @@ -14,6 +14,7 @@ #include #include #include +#include static inline void arc_write_me(unsigned short *addr, unsigned long value) { @@ -21,6 +22,42 @@ static inline void arc_write_me(unsigned short *addr, unsigned long value) *(addr + 1) = (value & 0xffff); } +/* ARC specific section quirks - before relocation loop in generic loader + * + * For dwarf unwinding out of modules, this needs to + * 1. Ensure the .debug_frame is allocatable (ARC Linker bug: despite + * -fasynchronous-unwind-tables it doesn't). + * 2. Since we are iterating thru sec hdr tbl anyways, make a note of + * the exact section index, for later use. + */ +int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, + char *secstr, struct module *mod) +{ +#ifdef CONFIG_ARC_DW2_UNWIND + int i; + + mod->arch.unw_sec_idx = 0; + mod->arch.unw_info = NULL; + + for (i = 1; i < hdr->e_shnum; i++) { + if (strcmp(secstr+sechdrs[i].sh_name, ".debug_frame") == 0) { + sechdrs[i].sh_flags |= SHF_ALLOC; + mod->arch.unw_sec_idx = i; + break; + } + } +#endif + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ +#ifdef CONFIG_ARC_DW2_UNWIND + if (mod->arch.unw_info) + unwind_remove_table(mod->arch.unw_info, 0); +#endif +} + int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, /* sec index for sym tbl */ @@ -85,3 +122,24 @@ relo_err: return -ENOEXEC; } + +/* Just before lift off: After sections have been relocated, we add the + * dwarf section to unwinder table pool + * This couldn't be done in module_frob_arch_sections() because + * relocations had not been applied by then + */ +int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, + struct module *mod) +{ +#ifdef CONFIG_ARC_DW2_UNWIND + void *unw; + int unwsec = mod->arch.unw_sec_idx; + + if (unwsec) { + unw = unwind_add_table(mod, (void *)sechdrs[unwsec].sh_addr, + sechdrs[unwsec].sh_size); + mod->arch.unw_info = unw; + } +#endif + return 0; +} diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 4026b5a004d2..6e3996cb9df9 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -23,6 +23,7 @@ #include #include #include +#include #define FIX_PTR(x) __asm__ __volatile__(";" : "+r"(x)) @@ -105,6 +106,8 @@ void __init setup_arch(char **cmdline_p) conswitchp = &dummy_con; #endif + arc_unwind_init(); + arc_unwind_setup(); } /* diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c new file mode 100644 index 000000000000..a8d02223da44 --- /dev/null +++ b/arch/arc/kernel/unwind.c @@ -0,0 +1,1329 @@ +/* + * Copyright (C) 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * Copyright (C) 2002-2006 Novell, Inc. + * Jan Beulich + * + * 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. + * + * A simple API for unwinding kernel stacks. This is used for + * debugging and error reporting purposes. The kernel doesn't need + * full-blown stack unwinding with all the bells and whistles, so there + * is not much point in implementing the full Dwarf2 unwind API. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char __start_unwind[], __end_unwind[]; +/* extern const u8 __start_unwind_hdr[], __end_unwind_hdr[];*/ + +/* #define UNWIND_DEBUG */ + +#ifdef UNWIND_DEBUG +int dbg_unw; +#define unw_debug(fmt, ...) \ +do { \ + if (dbg_unw) \ + pr_info(fmt, ##__VA_ARGS__); \ +} while (0); +#else +#define unw_debug(fmt, ...) +#endif + +#define MAX_STACK_DEPTH 8 + +#define EXTRA_INFO(f) { \ + BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) \ + % FIELD_SIZEOF(struct unwind_frame_info, f)) \ + + offsetof(struct unwind_frame_info, f) \ + / FIELD_SIZEOF(struct unwind_frame_info, f), \ + FIELD_SIZEOF(struct unwind_frame_info, f) \ + } +#define PTREGS_INFO(f) EXTRA_INFO(regs.f) + +static const struct { + unsigned offs:BITS_PER_LONG / 2; + unsigned width:BITS_PER_LONG / 2; +} reg_info[] = { +UNW_REGISTER_INFO}; + +#undef PTREGS_INFO +#undef EXTRA_INFO + +#ifndef REG_INVALID +#define REG_INVALID(r) (reg_info[r].width == 0) +#endif + +#define DW_CFA_nop 0x00 +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_def_cfa_expression 0x0f +#define DW_CFA_expression 0x10 +#define DW_CFA_offset_extended_sf 0x11 +#define DW_CFA_def_cfa_sf 0x12 +#define DW_CFA_def_cfa_offset_sf 0x13 +#define DW_CFA_val_offset 0x14 +#define DW_CFA_val_offset_sf 0x15 +#define DW_CFA_val_expression 0x16 +#define DW_CFA_lo_user 0x1c +#define DW_CFA_GNU_window_save 0x2d +#define DW_CFA_GNU_args_size 0x2e +#define DW_CFA_GNU_negative_offset_extended 0x2f +#define DW_CFA_hi_user 0x3f + +#define DW_EH_PE_FORM 0x07 +#define DW_EH_PE_native 0x00 +#define DW_EH_PE_leb128 0x01 +#define DW_EH_PE_data2 0x02 +#define DW_EH_PE_data4 0x03 +#define DW_EH_PE_data8 0x04 +#define DW_EH_PE_signed 0x08 +#define DW_EH_PE_ADJUST 0x70 +#define DW_EH_PE_abs 0x00 +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 +#define DW_EH_PE_indirect 0x80 +#define DW_EH_PE_omit 0xff + +typedef unsigned long uleb128_t; +typedef signed long sleb128_t; + +static struct unwind_table { + struct { + unsigned long pc; + unsigned long range; + } core, init; + const void *address; + unsigned long size; + const unsigned char *header; + unsigned long hdrsz; + struct unwind_table *link; + const char *name; +} root_table; + +struct unwind_item { + enum item_location { + Nowhere, + Memory, + Register, + Value + } where; + uleb128_t value; +}; + +struct unwind_state { + uleb128_t loc, org; + const u8 *cieStart, *cieEnd; + uleb128_t codeAlign; + sleb128_t dataAlign; + struct cfa { + uleb128_t reg, offs; + } cfa; + struct unwind_item regs[ARRAY_SIZE(reg_info)]; + unsigned stackDepth:8; + unsigned version:8; + const u8 *label; + const u8 *stack[MAX_STACK_DEPTH]; +}; + +static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; + +static struct unwind_table *find_table(unsigned long pc) +{ + struct unwind_table *table; + + for (table = &root_table; table; table = table->link) + if ((pc >= table->core.pc + && pc < table->core.pc + table->core.range) + || (pc >= table->init.pc + && pc < table->init.pc + table->init.range)) + break; + + return table; +} + +static unsigned long read_pointer(const u8 **pLoc, + const void *end, signed ptrType); + +static void init_unwind_table(struct unwind_table *table, const char *name, + const void *core_start, unsigned long core_size, + const void *init_start, unsigned long init_size, + const void *table_start, unsigned long table_size, + const u8 *header_start, unsigned long header_size) +{ + const u8 *ptr = header_start + 4; + const u8 *end = header_start + header_size; + + table->core.pc = (unsigned long)core_start; + table->core.range = core_size; + table->init.pc = (unsigned long)init_start; + table->init.range = init_size; + table->address = table_start; + table->size = table_size; + + /* See if the linker provided table looks valid. */ + if (header_size <= 4 + || header_start[0] != 1 + || (void *)read_pointer(&ptr, end, header_start[1]) != table_start + || header_start[2] == DW_EH_PE_omit + || read_pointer(&ptr, end, header_start[2]) <= 0 + || header_start[3] == DW_EH_PE_omit) + header_start = NULL; + + table->hdrsz = header_size; + smp_wmb(); + table->header = header_start; + table->link = NULL; + table->name = name; +} + +void __init arc_unwind_init(void) +{ + init_unwind_table(&root_table, "kernel", _text, _end - _text, NULL, 0, + __start_unwind, __end_unwind - __start_unwind, + NULL, 0); + /*__start_unwind_hdr, __end_unwind_hdr - __start_unwind_hdr);*/ +} + +static const u32 bad_cie, not_fde; +static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *); +static signed fde_pointer_type(const u32 *cie); + +struct eh_frame_hdr_table_entry { + unsigned long start, fde; +}; + +static int cmp_eh_frame_hdr_table_entries(const void *p1, const void *p2) +{ + const struct eh_frame_hdr_table_entry *e1 = p1; + const struct eh_frame_hdr_table_entry *e2 = p2; + + return (e1->start > e2->start) - (e1->start < e2->start); +} + +static void swap_eh_frame_hdr_table_entries(void *p1, void *p2, int size) +{ + struct eh_frame_hdr_table_entry *e1 = p1; + struct eh_frame_hdr_table_entry *e2 = p2; + unsigned long v; + + v = e1->start; + e1->start = e2->start; + e2->start = v; + v = e1->fde; + e1->fde = e2->fde; + e2->fde = v; +} + +static void __init setup_unwind_table(struct unwind_table *table, + void *(*alloc) (unsigned long)) +{ + const u8 *ptr; + unsigned long tableSize = table->size, hdrSize; + unsigned n; + const u32 *fde; + struct { + u8 version; + u8 eh_frame_ptr_enc; + u8 fde_count_enc; + u8 table_enc; + unsigned long eh_frame_ptr; + unsigned int fde_count; + struct eh_frame_hdr_table_entry table[]; + } __attribute__ ((__packed__)) *header; + + if (table->header) + return; + + if (table->hdrsz) + pr_warn(".eh_frame_hdr for '%s' present but unusable\n", + table->name); + + if (tableSize & (sizeof(*fde) - 1)) + return; + + for (fde = table->address, n = 0; + tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; + tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { + const u32 *cie = cie_for_fde(fde, table); + signed ptrType; + + if (cie == ¬_fde) + continue; + if (cie == NULL || cie == &bad_cie) + return; + ptrType = fde_pointer_type(cie); + if (ptrType < 0) + return; + + ptr = (const u8 *)(fde + 2); + if (!read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, + ptrType)) { + /* FIXME_Rajesh We have 4 instances of null addresses + * instead of the initial loc addr + * return; + */ + } + ++n; + } + + if (tableSize || !n) + return; + + hdrSize = 4 + sizeof(unsigned long) + sizeof(unsigned int) + + 2 * n * sizeof(unsigned long); + header = alloc(hdrSize); + if (!header) + return; + header->version = 1; + header->eh_frame_ptr_enc = DW_EH_PE_abs | DW_EH_PE_native; + header->fde_count_enc = DW_EH_PE_abs | DW_EH_PE_data4; + header->table_enc = DW_EH_PE_abs | DW_EH_PE_native; + put_unaligned((unsigned long)table->address, &header->eh_frame_ptr); + BUILD_BUG_ON(offsetof(typeof(*header), fde_count) + % __alignof(typeof(header->fde_count))); + header->fde_count = n; + + BUILD_BUG_ON(offsetof(typeof(*header), table) + % __alignof(typeof(*header->table))); + for (fde = table->address, tableSize = table->size, n = 0; + tableSize; + tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { + /* const u32 *cie = fde + 1 - fde[1] / sizeof(*fde); */ + const u32 *cie = (const u32 *)(fde[1]); + + if (fde[1] == 0xffffffff) + continue; /* this is a CIE */ + ptr = (const u8 *)(fde + 2); + header->table[n].start = read_pointer(&ptr, + (const u8 *)(fde + 1) + + *fde, + fde_pointer_type(cie)); + header->table[n].fde = (unsigned long)fde; + ++n; + } + WARN_ON(n != header->fde_count); + + sort(header->table, + n, + sizeof(*header->table), + cmp_eh_frame_hdr_table_entries, swap_eh_frame_hdr_table_entries); + + table->hdrsz = hdrSize; + smp_wmb(); + table->header = (const void *)header; +} + +static void *__init balloc(unsigned long sz) +{ + return __alloc_bootmem_nopanic(sz, + sizeof(unsigned int), + __pa(MAX_DMA_ADDRESS)); +} + +void __init arc_unwind_setup(void) +{ + setup_unwind_table(&root_table, balloc); +} + +#ifdef CONFIG_MODULES + +static struct unwind_table *last_table; + +/* Must be called with module_mutex held. */ +void *unwind_add_table(struct module *module, const void *table_start, + unsigned long table_size) +{ + struct unwind_table *table; + + if (table_size <= 0) + return NULL; + + table = kmalloc(sizeof(*table), GFP_KERNEL); + if (!table) + return NULL; + + init_unwind_table(table, module->name, + module->module_core, module->core_size, + module->module_init, module->init_size, + table_start, table_size, + NULL, 0); + +#ifdef UNWIND_DEBUG + unw_debug("Table added for [%s] %lx %lx\n", + module->name, table->core.pc, table->core.range); +#endif + if (last_table) + last_table->link = table; + else + root_table.link = table; + last_table = table; + + return table; +} + +struct unlink_table_info { + struct unwind_table *table; + int init_only; +}; + +static int unlink_table(void *arg) +{ + struct unlink_table_info *info = arg; + struct unwind_table *table = info->table, *prev; + + for (prev = &root_table; prev->link && prev->link != table; + prev = prev->link) + ; + + if (prev->link) { + if (info->init_only) { + table->init.pc = 0; + table->init.range = 0; + info->table = NULL; + } else { + prev->link = table->link; + if (!prev->link) + last_table = prev; + } + } else + info->table = NULL; + + return 0; +} + +/* Must be called with module_mutex held. */ +void unwind_remove_table(void *handle, int init_only) +{ + struct unwind_table *table = handle; + struct unlink_table_info info; + + if (!table || table == &root_table) + return; + + if (init_only && table == last_table) { + table->init.pc = 0; + table->init.range = 0; + return; + } + + info.table = table; + info.init_only = init_only; + + unlink_table(&info); /* XXX: SMP */ + kfree(table); +} + +#endif /* CONFIG_MODULES */ + +static uleb128_t get_uleb128(const u8 **pcur, const u8 *end) +{ + const u8 *cur = *pcur; + uleb128_t value; + unsigned shift; + + for (shift = 0, value = 0; cur < end; shift += 7) { + if (shift + 7 > 8 * sizeof(value) + && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { + cur = end + 1; + break; + } + value |= (uleb128_t) (*cur & 0x7f) << shift; + if (!(*cur++ & 0x80)) + break; + } + *pcur = cur; + + return value; +} + +static sleb128_t get_sleb128(const u8 **pcur, const u8 *end) +{ + const u8 *cur = *pcur; + sleb128_t value; + unsigned shift; + + for (shift = 0, value = 0; cur < end; shift += 7) { + if (shift + 7 > 8 * sizeof(value) + && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { + cur = end + 1; + break; + } + value |= (sleb128_t) (*cur & 0x7f) << shift; + if (!(*cur & 0x80)) { + value |= -(*cur++ & 0x40) << shift; + break; + } + } + *pcur = cur; + + return value; +} + +static const u32 *cie_for_fde(const u32 *fde, const struct unwind_table *table) +{ + const u32 *cie; + + if (!*fde || (*fde & (sizeof(*fde) - 1))) + return &bad_cie; + + if (fde[1] == 0xffffffff) + return ¬_fde; /* this is a CIE */ + + if ((fde[1] & (sizeof(*fde) - 1))) +/* || fde[1] > (unsigned long)(fde + 1) - (unsigned long)table->address) */ + return NULL; /* this is not a valid FDE */ + + /* cie = fde + 1 - fde[1] / sizeof(*fde); */ + cie = (u32 *) fde[1]; + + if (*cie <= sizeof(*cie) + 4 || *cie >= fde[1] - sizeof(*fde) + || (*cie & (sizeof(*cie) - 1)) + || (cie[1] != 0xffffffff)) + return NULL; /* this is not a (valid) CIE */ + return cie; +} + +static unsigned long read_pointer(const u8 **pLoc, const void *end, + signed ptrType) +{ + unsigned long value = 0; + union { + const u8 *p8; + const u16 *p16u; + const s16 *p16s; + const u32 *p32u; + const s32 *p32s; + const unsigned long *pul; + } ptr; + + if (ptrType < 0 || ptrType == DW_EH_PE_omit) + return 0; + ptr.p8 = *pLoc; + switch (ptrType & DW_EH_PE_FORM) { + case DW_EH_PE_data2: + if (end < (const void *)(ptr.p16u + 1)) + return 0; + if (ptrType & DW_EH_PE_signed) + value = get_unaligned((u16 *) ptr.p16s++); + else + value = get_unaligned((u16 *) ptr.p16u++); + break; + case DW_EH_PE_data4: +#ifdef CONFIG_64BIT + if (end < (const void *)(ptr.p32u + 1)) + return 0; + if (ptrType & DW_EH_PE_signed) + value = get_unaligned(ptr.p32s++); + else + value = get_unaligned(ptr.p32u++); + break; + case DW_EH_PE_data8: + BUILD_BUG_ON(sizeof(u64) != sizeof(value)); +#else + BUILD_BUG_ON(sizeof(u32) != sizeof(value)); +#endif + case DW_EH_PE_native: + if (end < (const void *)(ptr.pul + 1)) + return 0; + value = get_unaligned((unsigned long *)ptr.pul++); + break; + case DW_EH_PE_leb128: + BUILD_BUG_ON(sizeof(uleb128_t) > sizeof(value)); + value = ptrType & DW_EH_PE_signed ? get_sleb128(&ptr.p8, end) + : get_uleb128(&ptr.p8, end); + if ((const void *)ptr.p8 > end) + return 0; + break; + default: + return 0; + } + switch (ptrType & DW_EH_PE_ADJUST) { + case DW_EH_PE_abs: + break; + case DW_EH_PE_pcrel: + value += (unsigned long)*pLoc; + break; + default: + return 0; + } + if ((ptrType & DW_EH_PE_indirect) + && __get_user(value, (unsigned long __user *)value)) + return 0; + *pLoc = ptr.p8; + + return value; +} + +static signed fde_pointer_type(const u32 *cie) +{ + const u8 *ptr = (const u8 *)(cie + 2); + unsigned version = *ptr; + + if (version != 1) + return -1; /* unsupported */ + + if (*++ptr) { + const char *aug; + const u8 *end = (const u8 *)(cie + 1) + *cie; + uleb128_t len; + + /* check if augmentation size is first (and thus present) */ + if (*ptr != 'z') + return -1; + + /* check if augmentation string is nul-terminated */ + aug = (const void *)ptr; + ptr = memchr(aug, 0, end - ptr); + if (ptr == NULL) + return -1; + + ++ptr; /* skip terminator */ + get_uleb128(&ptr, end); /* skip code alignment */ + get_sleb128(&ptr, end); /* skip data alignment */ + /* skip return address column */ + version <= 1 ? (void) ++ptr : (void)get_uleb128(&ptr, end); + len = get_uleb128(&ptr, end); /* augmentation length */ + + if (ptr + len < ptr || ptr + len > end) + return -1; + + end = ptr + len; + while (*++aug) { + if (ptr >= end) + return -1; + switch (*aug) { + case 'L': + ++ptr; + break; + case 'P':{ + signed ptrType = *ptr++; + + if (!read_pointer(&ptr, end, ptrType) + || ptr > end) + return -1; + } + break; + case 'R': + return *ptr; + default: + return -1; + } + } + } + return DW_EH_PE_native | DW_EH_PE_abs; +} + +static int advance_loc(unsigned long delta, struct unwind_state *state) +{ + state->loc += delta * state->codeAlign; + + /* FIXME_Rajesh: Probably we are defining for the initial range as well; + return delta > 0; + */ + unw_debug("delta %3lu => loc 0x%lx: ", delta, state->loc); + return 1; +} + +static void set_rule(uleb128_t reg, enum item_location where, uleb128_t value, + struct unwind_state *state) +{ + if (reg < ARRAY_SIZE(state->regs)) { + state->regs[reg].where = where; + state->regs[reg].value = value; + +#ifdef UNWIND_DEBUG + unw_debug("r%lu: ", reg); + switch (where) { + case Nowhere: + unw_debug("s "); + break; + case Memory: + unw_debug("c(%lu) ", value); + break; + case Register: + unw_debug("r(%lu) ", value); + break; + case Value: + unw_debug("v(%lu) ", value); + break; + default: + break; + } +#endif + } +} + +static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, + signed ptrType, struct unwind_state *state) +{ + union { + const u8 *p8; + const u16 *p16; + const u32 *p32; + } ptr; + int result = 1; + u8 opcode; + + if (start != state->cieStart) { + state->loc = state->org; + result = + processCFI(state->cieStart, state->cieEnd, 0, ptrType, + state); + if (targetLoc == 0 && state->label == NULL) + return result; + } + for (ptr.p8 = start; result && ptr.p8 < end;) { + switch (*ptr.p8 >> 6) { + uleb128_t value; + + case 0: + opcode = *ptr.p8++; + + switch (opcode) { + case DW_CFA_nop: + unw_debug("cfa nop "); + break; + case DW_CFA_set_loc: + state->loc = read_pointer(&ptr.p8, end, + ptrType); + if (state->loc == 0) + result = 0; + unw_debug("cfa_set_loc: 0x%lx ", state->loc); + break; + case DW_CFA_advance_loc1: + unw_debug("\ncfa advance loc1:"); + result = ptr.p8 < end + && advance_loc(*ptr.p8++, state); + break; + case DW_CFA_advance_loc2: + value = *ptr.p8++; + value += *ptr.p8++ << 8; + unw_debug("\ncfa advance loc2:"); + result = ptr.p8 <= end + 2 + /* && advance_loc(*ptr.p16++, state); */ + && advance_loc(value, state); + break; + case DW_CFA_advance_loc4: + unw_debug("\ncfa advance loc4:"); + result = ptr.p8 <= end + 4 + && advance_loc(*ptr.p32++, state); + break; + case DW_CFA_offset_extended: + value = get_uleb128(&ptr.p8, end); + unw_debug("cfa_offset_extended: "); + set_rule(value, Memory, + get_uleb128(&ptr.p8, end), state); + break; + case DW_CFA_val_offset: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Value, + get_uleb128(&ptr.p8, end), state); + break; + case DW_CFA_offset_extended_sf: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Memory, + get_sleb128(&ptr.p8, end), state); + break; + case DW_CFA_val_offset_sf: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Value, + get_sleb128(&ptr.p8, end), state); + break; + case DW_CFA_restore_extended: + unw_debug("cfa_restore_extended: "); + case DW_CFA_undefined: + unw_debug("cfa_undefined: "); + case DW_CFA_same_value: + unw_debug("cfa_same_value: "); + set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0, + state); + break; + case DW_CFA_register: + unw_debug("cfa_register: "); + value = get_uleb128(&ptr.p8, end); + set_rule(value, + Register, + get_uleb128(&ptr.p8, end), state); + break; + case DW_CFA_remember_state: + unw_debug("cfa_remember_state: "); + if (ptr.p8 == state->label) { + state->label = NULL; + return 1; + } + if (state->stackDepth >= MAX_STACK_DEPTH) + return 0; + state->stack[state->stackDepth++] = ptr.p8; + break; + case DW_CFA_restore_state: + unw_debug("cfa_restore_state: "); + if (state->stackDepth) { + const uleb128_t loc = state->loc; + const u8 *label = state->label; + + state->label = + state->stack[state->stackDepth - 1]; + memcpy(&state->cfa, &badCFA, + sizeof(state->cfa)); + memset(state->regs, 0, + sizeof(state->regs)); + state->stackDepth = 0; + result = + processCFI(start, end, 0, ptrType, + state); + state->loc = loc; + state->label = label; + } else + return 0; + break; + case DW_CFA_def_cfa: + state->cfa.reg = get_uleb128(&ptr.p8, end); + unw_debug("cfa_def_cfa: r%lu ", state->cfa.reg); + /*nobreak*/ + case DW_CFA_def_cfa_offset: + state->cfa.offs = get_uleb128(&ptr.p8, end); + unw_debug("cfa_def_cfa_offset: 0x%lx ", + state->cfa.offs); + break; + case DW_CFA_def_cfa_sf: + state->cfa.reg = get_uleb128(&ptr.p8, end); + /*nobreak */ + case DW_CFA_def_cfa_offset_sf: + state->cfa.offs = get_sleb128(&ptr.p8, end) + * state->dataAlign; + break; + case DW_CFA_def_cfa_register: + unw_debug("cfa_def_cfa_regsiter: "); + state->cfa.reg = get_uleb128(&ptr.p8, end); + break; + /*todo case DW_CFA_def_cfa_expression: */ + /*todo case DW_CFA_expression: */ + /*todo case DW_CFA_val_expression: */ + case DW_CFA_GNU_args_size: + get_uleb128(&ptr.p8, end); + break; + case DW_CFA_GNU_negative_offset_extended: + value = get_uleb128(&ptr.p8, end); + set_rule(value, + Memory, + (uleb128_t) 0 - get_uleb128(&ptr.p8, + end), + state); + break; + case DW_CFA_GNU_window_save: + default: + unw_debug("UNKNOW OPCODE 0x%x\n", opcode); + result = 0; + break; + } + break; + case 1: + unw_debug("\ncfa_adv_loc: "); + result = advance_loc(*ptr.p8++ & 0x3f, state); + break; + case 2: + unw_debug("cfa_offset: "); + value = *ptr.p8++ & 0x3f; + set_rule(value, Memory, get_uleb128(&ptr.p8, end), + state); + break; + case 3: + unw_debug("cfa_restore: "); + set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); + break; + } + + if (ptr.p8 > end) + result = 0; + if (result && targetLoc != 0 && targetLoc < state->loc) + return 1; + } + + return result && ptr.p8 == end && (targetLoc == 0 || ( + /*todo While in theory this should apply, gcc in practice omits + everything past the function prolog, and hence the location + never reaches the end of the function. + targetLoc < state->loc && */ state->label == NULL)); +} + +/* Unwind to previous to frame. Returns 0 if successful, negative + * number in case of an error. */ +int arc_unwind(struct unwind_frame_info *frame) +{ +#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) + const u32 *fde = NULL, *cie = NULL; + const u8 *ptr = NULL, *end = NULL; + unsigned long pc = UNW_PC(frame) - frame->call_frame; + unsigned long startLoc = 0, endLoc = 0, cfa; + unsigned i; + signed ptrType = -1; + uleb128_t retAddrReg = 0; + const struct unwind_table *table; + struct unwind_state state; + unsigned long *fptr; + unsigned long addr; + + unw_debug("\n\nUNWIND FRAME:\n"); + unw_debug("PC: 0x%lx BLINK: 0x%lx, SP: 0x%lx, FP: 0x%x\n", + UNW_PC(frame), UNW_BLINK(frame), UNW_SP(frame), + UNW_FP(frame)); + + if (UNW_PC(frame) == 0) + return -EINVAL; + +#ifdef UNWIND_DEBUG + { + unsigned long *sptr = (unsigned long *)UNW_SP(frame); + unw_debug("\nStack Dump:\n"); + for (i = 0; i < 20; i++, sptr++) + unw_debug("0x%p: 0x%lx\n", sptr, *sptr); + unw_debug("\n"); + } +#endif + + table = find_table(pc); + if (table != NULL + && !(table->size & (sizeof(*fde) - 1))) { + const u8 *hdr = table->header; + unsigned long tableSize; + + smp_rmb(); + if (hdr && hdr[0] == 1) { + switch (hdr[3] & DW_EH_PE_FORM) { + case DW_EH_PE_native: + tableSize = sizeof(unsigned long); + break; + case DW_EH_PE_data2: + tableSize = 2; + break; + case DW_EH_PE_data4: + tableSize = 4; + break; + case DW_EH_PE_data8: + tableSize = 8; + break; + default: + tableSize = 0; + break; + } + ptr = hdr + 4; + end = hdr + table->hdrsz; + if (tableSize && read_pointer(&ptr, end, hdr[1]) + == (unsigned long)table->address + && (i = read_pointer(&ptr, end, hdr[2])) > 0 + && i == (end - ptr) / (2 * tableSize) + && !((end - ptr) % (2 * tableSize))) { + do { + const u8 *cur = + ptr + (i / 2) * (2 * tableSize); + + startLoc = read_pointer(&cur, + cur + tableSize, + hdr[3]); + if (pc < startLoc) + i /= 2; + else { + ptr = cur - tableSize; + i = (i + 1) / 2; + } + } while (startLoc && i > 1); + if (i == 1 + && (startLoc = read_pointer(&ptr, + ptr + tableSize, + hdr[3])) != 0 + && pc >= startLoc) + fde = (void *)read_pointer(&ptr, + ptr + + tableSize, + hdr[3]); + } + } + + if (fde != NULL) { + cie = cie_for_fde(fde, table); + ptr = (const u8 *)(fde + 2); + if (cie != NULL + && cie != &bad_cie + && cie != ¬_fde + && (ptrType = fde_pointer_type(cie)) >= 0 + && read_pointer(&ptr, + (const u8 *)(fde + 1) + *fde, + ptrType) == startLoc) { + if (!(ptrType & DW_EH_PE_indirect)) + ptrType &= + DW_EH_PE_FORM | DW_EH_PE_signed; + endLoc = + startLoc + read_pointer(&ptr, + (const u8 *)(fde + + 1) + + *fde, ptrType); + if (pc >= endLoc) + fde = NULL; + } else + fde = NULL; + } + if (fde == NULL) { + for (fde = table->address, tableSize = table->size; + cie = NULL, tableSize > sizeof(*fde) + && tableSize - sizeof(*fde) >= *fde; + tableSize -= sizeof(*fde) + *fde, + fde += 1 + *fde / sizeof(*fde)) { + cie = cie_for_fde(fde, table); + if (cie == &bad_cie) { + cie = NULL; + break; + } + if (cie == NULL + || cie == ¬_fde + || (ptrType = fde_pointer_type(cie)) < 0) + continue; + ptr = (const u8 *)(fde + 2); + startLoc = read_pointer(&ptr, + (const u8 *)(fde + 1) + + *fde, ptrType); + if (!startLoc) + continue; + if (!(ptrType & DW_EH_PE_indirect)) + ptrType &= + DW_EH_PE_FORM | DW_EH_PE_signed; + endLoc = + startLoc + read_pointer(&ptr, + (const u8 *)(fde + + 1) + + *fde, ptrType); + if (pc >= startLoc && pc < endLoc) + break; + } + } + } + if (cie != NULL) { + memset(&state, 0, sizeof(state)); + state.cieEnd = ptr; /* keep here temporarily */ + ptr = (const u8 *)(cie + 2); + end = (const u8 *)(cie + 1) + *cie; + frame->call_frame = 1; + if ((state.version = *ptr) != 1) + cie = NULL; /* unsupported version */ + else if (*++ptr) { + /* check if augmentation size is first (thus present) */ + if (*ptr == 'z') { + while (++ptr < end && *ptr) { + switch (*ptr) { + /* chk for ignorable or already handled + * nul-terminated augmentation string */ + case 'L': + case 'P': + case 'R': + continue; + case 'S': + frame->call_frame = 0; + continue; + default: + break; + } + break; + } + } + if (ptr >= end || *ptr) + cie = NULL; + } + ++ptr; + } + if (cie != NULL) { + /* get code aligment factor */ + state.codeAlign = get_uleb128(&ptr, end); + /* get data aligment factor */ + state.dataAlign = get_sleb128(&ptr, end); + if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) + cie = NULL; + else { + retAddrReg = + state.version <= 1 ? *ptr++ : get_uleb128(&ptr, + end); + unw_debug("CIE Frame Info:\n"); + unw_debug("return Address register 0x%lx\n", + retAddrReg); + unw_debug("data Align: %ld\n", state.dataAlign); + unw_debug("code Align: %lu\n", state.codeAlign); + /* skip augmentation */ + if (((const char *)(cie + 2))[1] == 'z') { + uleb128_t augSize = get_uleb128(&ptr, end); + + ptr += augSize; + } + if (ptr > end || retAddrReg >= ARRAY_SIZE(reg_info) + || REG_INVALID(retAddrReg) + || reg_info[retAddrReg].width != + sizeof(unsigned long)) + cie = NULL; + } + } + if (cie != NULL) { + state.cieStart = ptr; + ptr = state.cieEnd; + state.cieEnd = end; + end = (const u8 *)(fde + 1) + *fde; + /* skip augmentation */ + if (((const char *)(cie + 2))[1] == 'z') { + uleb128_t augSize = get_uleb128(&ptr, end); + + if ((ptr += augSize) > end) + fde = NULL; + } + } + if (cie == NULL || fde == NULL) { +#ifdef CONFIG_FRAME_POINTER + unsigned long top, bottom; + + top = STACK_TOP_UNW(frame->task); + bottom = STACK_BOTTOM_UNW(frame->task); +#if FRAME_RETADDR_OFFSET < 0 + if (UNW_SP(frame) < top && UNW_FP(frame) <= UNW_SP(frame) + && bottom < UNW_FP(frame) +#else + if (UNW_SP(frame) > top && UNW_FP(frame) >= UNW_SP(frame) + && bottom > UNW_FP(frame) +#endif + && !((UNW_SP(frame) | UNW_FP(frame)) + & (sizeof(unsigned long) - 1))) { + unsigned long link; + + if (!__get_user(link, (unsigned long *) + (UNW_FP(frame) + FRAME_LINK_OFFSET)) +#if FRAME_RETADDR_OFFSET < 0 + && link > bottom && link < UNW_FP(frame) +#else + && link > UNW_FP(frame) && link < bottom +#endif + && !(link & (sizeof(link) - 1)) + && !__get_user(UNW_PC(frame), + (unsigned long *)(UNW_FP(frame) + + FRAME_RETADDR_OFFSET))) + { + UNW_SP(frame) = + UNW_FP(frame) + FRAME_RETADDR_OFFSET +#if FRAME_RETADDR_OFFSET < 0 + - +#else + + +#endif + sizeof(UNW_PC(frame)); + UNW_FP(frame) = link; + return 0; + } + } +#endif + return -ENXIO; + } + state.org = startLoc; + memcpy(&state.cfa, &badCFA, sizeof(state.cfa)); + + unw_debug("\nProcess instructions\n"); + + /* process instructions + * For ARC, we optimize by having blink(retAddrReg) with + * the sameValue in the leaf function, so we should not check + * state.regs[retAddrReg].where == Nowhere + */ + if (!processCFI(ptr, end, pc, ptrType, &state) + || state.loc > endLoc +/* || state.regs[retAddrReg].where == Nowhere */ + || state.cfa.reg >= ARRAY_SIZE(reg_info) + || reg_info[state.cfa.reg].width != sizeof(unsigned long) + || state.cfa.offs % sizeof(unsigned long)) + return -EIO; + +#ifdef UNWIND_DEBUG + unw_debug("\n"); + + unw_debug("\nRegister State Based on the rules parsed from FDE:\n"); + for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { + + if (REG_INVALID(i)) + continue; + + switch (state.regs[i].where) { + case Nowhere: + break; + case Memory: + unw_debug(" r%d: c(%lu),", i, state.regs[i].value); + break; + case Register: + unw_debug(" r%d: r(%lu),", i, state.regs[i].value); + break; + case Value: + unw_debug(" r%d: v(%lu),", i, state.regs[i].value); + break; + } + } + + unw_debug("\n"); +#endif + + /* update frame */ +#ifndef CONFIG_AS_CFI_SIGNAL_FRAME + if (frame->call_frame + && !UNW_DEFAULT_RA(state.regs[retAddrReg], state.dataAlign)) + frame->call_frame = 0; +#endif + cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs; + startLoc = min_t(unsigned long, UNW_SP(frame), cfa); + endLoc = max_t(unsigned long, UNW_SP(frame), cfa); + if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) { + startLoc = min(STACK_LIMIT(cfa), cfa); + endLoc = max(STACK_LIMIT(cfa), cfa); + } + + unw_debug("\nCFA reg: 0x%lx, offset: 0x%lx => 0x%lx\n", + state.cfa.reg, state.cfa.offs, cfa); + + for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { + if (REG_INVALID(i)) { + if (state.regs[i].where == Nowhere) + continue; + return -EIO; + } + switch (state.regs[i].where) { + default: + break; + case Register: + if (state.regs[i].value >= ARRAY_SIZE(reg_info) + || REG_INVALID(state.regs[i].value) + || reg_info[i].width > + reg_info[state.regs[i].value].width) + return -EIO; + switch (reg_info[state.regs[i].value].width) { + case sizeof(u8): + state.regs[i].value = + FRAME_REG(state.regs[i].value, const u8); + break; + case sizeof(u16): + state.regs[i].value = + FRAME_REG(state.regs[i].value, const u16); + break; + case sizeof(u32): + state.regs[i].value = + FRAME_REG(state.regs[i].value, const u32); + break; +#ifdef CONFIG_64BIT + case sizeof(u64): + state.regs[i].value = + FRAME_REG(state.regs[i].value, const u64); + break; +#endif + default: + return -EIO; + } + break; + } + } + + unw_debug("\nRegister state after evaluation with realtime Stack:\n"); + fptr = (unsigned long *)(&frame->regs); + for (i = 0; i < ARRAY_SIZE(state.regs); ++i, fptr++) { + + if (REG_INVALID(i)) + continue; + switch (state.regs[i].where) { + case Nowhere: + if (reg_info[i].width != sizeof(UNW_SP(frame)) + || &FRAME_REG(i, __typeof__(UNW_SP(frame))) + != &UNW_SP(frame)) + continue; + UNW_SP(frame) = cfa; + break; + case Register: + switch (reg_info[i].width) { + case sizeof(u8): + FRAME_REG(i, u8) = state.regs[i].value; + break; + case sizeof(u16): + FRAME_REG(i, u16) = state.regs[i].value; + break; + case sizeof(u32): + FRAME_REG(i, u32) = state.regs[i].value; + break; +#ifdef CONFIG_64BIT + case sizeof(u64): + FRAME_REG(i, u64) = state.regs[i].value; + break; +#endif + default: + return -EIO; + } + break; + case Value: + if (reg_info[i].width != sizeof(unsigned long)) + return -EIO; + FRAME_REG(i, unsigned long) = cfa + state.regs[i].value + * state.dataAlign; + break; + case Memory: + addr = cfa + state.regs[i].value * state.dataAlign; + + if ((state.regs[i].value * state.dataAlign) + % sizeof(unsigned long) + || addr < startLoc + || addr + sizeof(unsigned long) < addr + || addr + sizeof(unsigned long) > endLoc) + return -EIO; + + switch (reg_info[i].width) { + case sizeof(u8): + __get_user(FRAME_REG(i, u8), + (u8 __user *)addr); + break; + case sizeof(u16): + __get_user(FRAME_REG(i, u16), + (u16 __user *)addr); + break; + case sizeof(u32): + __get_user(FRAME_REG(i, u32), + (u32 __user *)addr); + break; +#ifdef CONFIG_64BIT + case sizeof(u64): + __get_user(FRAME_REG(i, u64), + (u64 __user *)addr); + break; +#endif + default: + return -EIO; + } + + break; + } + unw_debug("r%d: 0x%lx ", i, *fptr); + } + + return 0; +#undef FRAME_REG +} +EXPORT_SYMBOL(arc_unwind); diff --git a/arch/arc/kernel/vmlinux.lds.S b/arch/arc/kernel/vmlinux.lds.S index 8c72bc37568b..303ea01c85c8 100644 --- a/arch/arc/kernel/vmlinux.lds.S +++ b/arch/arc/kernel/vmlinux.lds.S @@ -100,17 +100,38 @@ SECTIONS BSS_SECTION(0, 0, 0) +#ifdef CONFIG_ARC_DW2_UNWIND + . = ALIGN(PAGE_SIZE); + .debug_frame : { + __start_unwind = .; + *(.debug_frame) + __end_unwind = .; + } +#else + /DISCARD/ : { *(.debug_frame) } +#endif + NOTES . = ALIGN(PAGE_SIZE); _end = . ; STABS_DEBUG - DWARF_DEBUG DISCARDS .arcextmap 0 : { *(.gnu.linkonce.arcextmap.*) *(.arcextmap.*) } + + /* open-coded because we need .debug_frame seperately for unwinding */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + } From 44c8bb914028f34c622c30340fa21e186e4cf993 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:23 +0530 Subject: [PATCH 2840/4607] ARC: stacktracing APIs based on dw2 unwinder Signed-off-by: Vineet Gupta --- arch/arc/Kconfig | 4 + arch/arc/kernel/stacktrace.c | 215 ++++++++++++++++++++++++++++++++++- 2 files changed, 217 insertions(+), 2 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 0979d8e6fc16..84b23fae661c 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -60,6 +60,10 @@ config GENERIC_HWEIGHT config BINFMT_ELF def_bool y +config STACKTRACE_SUPPORT + def_bool y + select STACKTRACE + config HAVE_LATENCYTOP_SUPPORT def_bool y diff --git a/arch/arc/kernel/stacktrace.c b/arch/arc/kernel/stacktrace.c index b9d1646d1220..a63ff842564b 100644 --- a/arch/arc/kernel/stacktrace.c +++ b/arch/arc/kernel/stacktrace.c @@ -1,13 +1,206 @@ /* + * stacktrace.c : stacktracing APIs needed by rest of kernel + * (wrappers over ARC dwarf based unwinder) + * * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) * * 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. + * + * vineetg: aug 2009 + * -Implemented CONFIG_STACKTRACE APIs, primarily save_stack_trace_tsk( ) + * for displaying task's kernel mode call stack in /proc//stack + * -Iterator based approach to have single copy of unwinding core and APIs + * needing unwinding, implement the logic in iterator regarding: + * = which frame onwards to start capture + * = which frame to stop capturing (wchan) + * = specifics of data structs where trace is saved(CONFIG_STACKTRACE etc) + * + * vineetg: March 2009 + * -Implemented correct versions of thread_saved_pc() and get_wchan() + * + * rajeshwarr: 2008 + * -Initial implementation */ #include #include +#include +#include +#include +#include +#include + +/*------------------------------------------------------------------------- + * Unwinder Iterator + *------------------------------------------------------------------------- + */ + +#ifdef CONFIG_ARC_DW2_UNWIND + +static void seed_unwind_frame_info(struct task_struct *tsk, + struct pt_regs *regs, + struct unwind_frame_info *frame_info) +{ + if (tsk == NULL && regs == NULL) { + unsigned long fp, sp, blink, ret; + frame_info->task = current; + + __asm__ __volatile__( + "mov %0,r27\n\t" + "mov %1,r28\n\t" + "mov %2,r31\n\t" + "mov %3,r63\n\t" + : "=r"(fp), "=r"(sp), "=r"(blink), "=r"(ret) + ); + + frame_info->regs.r27 = fp; + frame_info->regs.r28 = sp; + frame_info->regs.r31 = blink; + frame_info->regs.r63 = ret; + frame_info->call_frame = 0; + } else if (regs == NULL) { + + frame_info->task = tsk; + + frame_info->regs.r27 = KSTK_FP(tsk); + frame_info->regs.r28 = KSTK_ESP(tsk); + frame_info->regs.r31 = KSTK_BLINK(tsk); + frame_info->regs.r63 = (unsigned int)__switch_to; + + /* In the prologue of __switch_to, first FP is saved on stack + * and then SP is copied to FP. Dwarf assumes cfa as FP based + * but we didn't save FP. The value retrieved above is FP's + * state in previous frame. + * As a work around for this, we unwind from __switch_to start + * and adjust SP accordingly. The other limitation is that + * __switch_to macro is dwarf rules are not generated for inline + * assembly code + */ + frame_info->regs.r27 = 0; + frame_info->regs.r28 += 64; + frame_info->call_frame = 0; + + } else { + frame_info->task = tsk; + + frame_info->regs.r27 = regs->fp; + frame_info->regs.r28 = regs->sp; + frame_info->regs.r31 = regs->blink; + frame_info->regs.r63 = regs->ret; + frame_info->call_frame = 0; + } +} + +#endif + +static noinline unsigned int +arc_unwind_core(struct task_struct *tsk, struct pt_regs *regs, + int (*consumer_fn) (unsigned int, void *), void *arg) +{ +#ifdef CONFIG_ARC_DW2_UNWIND + int ret = 0; + unsigned int address; + struct unwind_frame_info frame_info; + + seed_unwind_frame_info(tsk, regs, &frame_info); + + while (1) { + address = UNW_PC(&frame_info); + + if (address && __kernel_text_address(address)) { + if (consumer_fn(address, arg) == -1) + break; + } + + ret = arc_unwind(&frame_info); + + if (ret == 0) { + frame_info.regs.r63 = frame_info.regs.r31; + continue; + } else { + break; + } + } + + return address; /* return the last address it saw */ +#else + /* On ARC, only Dward based unwinder works. fp based backtracing is + * not possible (-fno-omit-frame-pointer) because of the way function + * prelogue is setup (callee regs saved and then fp set and not other + * way around + */ + pr_warn("CONFIG_ARC_DW2_UNWIND needs to be enabled\n"); + return 0; + +#endif +} + +/*------------------------------------------------------------------------- + * callbacks called by unwinder iterator to implement kernel APIs + * + * The callback can return -1 to force the iterator to stop, which by default + * keeps going till the bottom-most frame. + *------------------------------------------------------------------------- + */ + +/* Call-back which plugs into unwinding core to dump the stack in + * case of panic/OOPs/BUG etc + */ +static int __print_sym(unsigned int address, void *unused) +{ + __print_symbol(" %s\n", address); + return 0; +} + +#ifdef CONFIG_STACKTRACE + +/* Call-back which plugs into unwinding core to capture the + * traces needed by kernel on /proc//stack + */ +static int __collect_all(unsigned int address, void *arg) +{ + struct stack_trace *trace = arg; + + if (trace->skip > 0) + trace->skip--; + else + trace->entries[trace->nr_entries++] = address; + + if (trace->nr_entries >= trace->max_entries) + return -1; + + return 0; +} + +static int __collect_all_but_sched(unsigned int address, void *arg) +{ + struct stack_trace *trace = arg; + + if (in_sched_functions(address)) + return 0; + + if (trace->skip > 0) + trace->skip--; + else + trace->entries[trace->nr_entries++] = address; + + if (trace->nr_entries >= trace->max_entries) + return -1; + + return 0; +} + +#endif + +static int __get_first_nonsched(unsigned int address, void *unused) +{ + if (in_sched_functions(address)) + return 0; + + return -1; +} /*------------------------------------------------------------------------- * APIs expected by various kernel sub-systems @@ -16,7 +209,8 @@ noinline void show_stacktrace(struct task_struct *tsk, struct pt_regs *regs) { - pr_info("\nStack Trace: NOT Available\n"); + pr_info("\nStack Trace:\n"); + arc_unwind_core(tsk, regs, __print_sym, NULL); } EXPORT_SYMBOL(show_stacktrace); @@ -39,5 +233,22 @@ EXPORT_SYMBOL(dump_stack); */ unsigned int get_wchan(struct task_struct *tsk) { - return 0; + return arc_unwind_core(tsk, NULL, __get_first_nonsched, NULL); } + +#ifdef CONFIG_STACKTRACE + +/* + * API required by CONFIG_STACKTRACE, CONFIG_LATENCYTOP. + * A typical use is when /proc//stack is queried by userland + */ +void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) +{ + arc_unwind_core(tsk, NULL, __collect_all_but_sched, trace); +} + +void save_stack_trace(struct stack_trace *trace) +{ + arc_unwind_core(current, NULL, __collect_all, trace); +} +#endif From e65ab5a875d9e8ad8ff37529c2ae844699fefad1 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Wed, 30 Jan 2013 17:46:13 +0530 Subject: [PATCH 2841/4607] ARC: disassembly (needed by kprobes/kgdb/unaligned-access-emul) In-kernel disassembler Due Credits * Orig written by Rajeshwar Ranga * Consolidation/cleanups by Mischa Jonker Signed-off-by: Vineet Gupta Cc: Rajeshwar Ranga Cc: Mischa Jonker --- arch/arc/include/asm/disasm.h | 116 ++++++++ arch/arc/kernel/Makefile | 2 +- arch/arc/kernel/disasm.c | 539 ++++++++++++++++++++++++++++++++++ 3 files changed, 656 insertions(+), 1 deletion(-) create mode 100644 arch/arc/include/asm/disasm.h create mode 100644 arch/arc/kernel/disasm.c diff --git a/arch/arc/include/asm/disasm.h b/arch/arc/include/asm/disasm.h new file mode 100644 index 000000000000..f1cce3d059a1 --- /dev/null +++ b/arch/arc/include/asm/disasm.h @@ -0,0 +1,116 @@ +/* + * several functions that help interpret ARC instructions + * used for unaligned accesses, kprobes and kgdb + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 __ARC_DISASM_H__ +#define __ARC_DISASM_H__ + +enum { + op_Bcc = 0, op_BLcc = 1, op_LD = 2, op_ST = 3, op_MAJOR_4 = 4, + op_MAJOR_5 = 5, op_LD_ADD = 12, op_ADD_SUB_SHIFT = 13, + op_ADD_MOV_CMP = 14, op_S = 15, op_LD_S = 16, op_LDB_S = 17, + op_LDW_S = 18, op_LDWX_S = 19, op_ST_S = 20, op_STB_S = 21, + op_STW_S = 22, op_Su5 = 23, op_SP = 24, op_GP = 25, + op_Pcl = 26, op_MOV_S = 27, op_ADD_CMP = 28, op_BR_S = 29, + op_B_S = 30, op_BL_S = 31 +}; + +enum flow { + noflow, + direct_jump, + direct_call, + indirect_jump, + indirect_call, + invalid_instr +}; + +#define IS_BIT(word, n) ((word) & (1<> (s)) & (~((-2) << ((e) - (s))))) + +#define MAJOR_OPCODE(word) (BITS((word), 27, 31)) +#define MINOR_OPCODE(word) (BITS((word), 16, 21)) +#define FIELD_A(word) (BITS((word), 0, 5)) +#define FIELD_B(word) ((BITS((word), 12, 14)<<3) | \ + (BITS((word), 24, 26))) +#define FIELD_C(word) (BITS((word), 6, 11)) +#define FIELD_u6(word) FIELDC(word) +#define FIELD_s12(word) sign_extend(((BITS((word), 0, 5) << 6) | \ + BITS((word), 6, 11)), 12) + +/* note that for BL/BRcc these two macro's need another AND statement to mask + * out bit 1 (make the result a multiple of 4) */ +#define FIELD_s9(word) sign_extend(((BITS(word, 15, 15) << 8) | \ + BITS(word, 16, 23)), 9) +#define FIELD_s21(word) sign_extend(((BITS(word, 6, 15) << 11) | \ + (BITS(word, 17, 26) << 1)), 12) +#define FIELD_s25(word) sign_extend(((BITS(word, 0, 3) << 21) | \ + (BITS(word, 6, 15) << 11) | \ + (BITS(word, 17, 26) << 1)), 12) + +/* note: these operate on 16 bits! */ +#define FIELD_S_A(word) ((BITS((word), 2, 2)<<3) | BITS((word), 0, 2)) +#define FIELD_S_B(word) ((BITS((word), 10, 10)<<3) | \ + BITS((word), 8, 10)) +#define FIELD_S_C(word) ((BITS((word), 7, 7)<<3) | BITS((word), 5, 7)) +#define FIELD_S_H(word) ((BITS((word), 0, 2)<<3) | BITS((word), 5, 8)) +#define FIELD_S_u5(word) (BITS((word), 0, 4)) +#define FIELD_S_u6(word) (BITS((word), 0, 4) << 1) +#define FIELD_S_u7(word) (BITS((word), 0, 4) << 2) +#define FIELD_S_u10(word) (BITS((word), 0, 7) << 2) +#define FIELD_S_s7(word) sign_extend(BITS((word), 0, 5) << 1, 9) +#define FIELD_S_s8(word) sign_extend(BITS((word), 0, 7) << 1, 9) +#define FIELD_S_s9(word) sign_extend(BITS((word), 0, 8), 9) +#define FIELD_S_s10(word) sign_extend(BITS((word), 0, 8) << 1, 10) +#define FIELD_S_s11(word) sign_extend(BITS((word), 0, 8) << 2, 11) +#define FIELD_S_s13(word) sign_extend(BITS((word), 0, 10) << 2, 13) + +#define STATUS32_L 0x00000100 +#define REG_LIMM 62 + +struct disasm_state { + /* generic info */ + unsigned long words[2]; + int instr_len; + int major_opcode; + /* info for branch/jump */ + int is_branch; + int target; + int delay_slot; + enum flow flow; + /* info for load/store */ + int src1, src2, src3, dest, wb_reg; + int zz, aa, x, pref, di; + int fault, write; +}; + +static inline int sign_extend(int value, int bits) +{ + if (IS_BIT(value, (bits - 1))) + value |= (0xffffffff << bits); + + return value; +} + +static inline int is_short_instr(unsigned long addr) +{ + uint16_t word = *((uint16_t *)addr); + int opcode = (word >> 11) & 0x1F; + return (opcode >= 0x0B); +} + +void disasm_instr(unsigned long addr, struct disasm_state *state, + int userspace, struct pt_regs *regs, struct callee_regs *cregs); +int disasm_next_pc(unsigned long pc, struct pt_regs *regs, struct callee_regs + *cregs, unsigned long *fall_thru, unsigned long *target); +long get_reg(int reg, struct pt_regs *regs, struct callee_regs *cregs); +void set_reg(int reg, long val, struct pt_regs *regs, + struct callee_regs *cregs); + +#endif /* __ARC_DISASM_H__ */ diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index 38e4715f3349..6ae4dd4ecea0 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -9,7 +9,7 @@ CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' obj-y := arcksyms.o setup.o irq.o time.o reset.o ptrace.o entry.o process.o -obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o clk.o +obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o disasm.o clk.o obj-y += devtree.o obj-$(CONFIG_MODULES) += arcksyms.o module.o diff --git a/arch/arc/kernel/disasm.c b/arch/arc/kernel/disasm.c new file mode 100644 index 000000000000..254d11a4f495 --- /dev/null +++ b/arch/arc/kernel/disasm.c @@ -0,0 +1,539 @@ +/* + * several functions that help interpret ARC instructions + * used for unaligned accesses, kprobes and kgdb + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include +#include + +#if defined(CONFIG_KGDB) || defined(CONFIG_MISALIGN_ACCESS) || \ + defined(CONFIG_KPROBES) + +/* disasm_instr: Analyses instruction at addr, stores + * findings in *state + */ +void __kprobes disasm_instr(unsigned long addr, struct disasm_state *state, + int userspace, struct pt_regs *regs, struct callee_regs *cregs) +{ + int fieldA = 0; + int fieldC = 0, fieldCisReg = 0; + uint16_t word1 = 0, word0 = 0; + int subopcode, is_linked, op_format; + uint16_t *ins_ptr; + uint16_t ins_buf[4]; + int bytes_not_copied = 0; + + memset(state, 0, sizeof(struct disasm_state)); + + /* This fetches the upper part of the 32 bit instruction + * in both the cases of Little Endian or Big Endian configurations. */ + if (userspace) { + bytes_not_copied = copy_from_user(ins_buf, + (const void __user *) addr, 8); + if (bytes_not_copied > 6) + goto fault; + ins_ptr = ins_buf; + } else { + ins_ptr = (uint16_t *) addr; + } + + word1 = *((uint16_t *)addr); + + state->major_opcode = (word1 >> 11) & 0x1F; + + /* Check if the instruction is 32 bit or 16 bit instruction */ + if (state->major_opcode < 0x0B) { + if (bytes_not_copied > 4) + goto fault; + state->instr_len = 4; + word0 = *((uint16_t *)(addr+2)); + state->words[0] = (word1 << 16) | word0; + } else { + state->instr_len = 2; + state->words[0] = word1; + } + + /* Read the second word in case of limm */ + word1 = *((uint16_t *)(addr + state->instr_len)); + word0 = *((uint16_t *)(addr + state->instr_len + 2)); + state->words[1] = (word1 << 16) | word0; + + switch (state->major_opcode) { + case op_Bcc: + state->is_branch = 1; + + /* unconditional branch s25, conditional branch s21 */ + fieldA = (IS_BIT(state->words[0], 16)) ? + FIELD_s25(state->words[0]) : + FIELD_s21(state->words[0]); + + state->delay_slot = IS_BIT(state->words[0], 5); + state->target = fieldA + (addr & ~0x3); + state->flow = direct_jump; + break; + + case op_BLcc: + if (IS_BIT(state->words[0], 16)) { + /* Branch and Link*/ + /* unconditional branch s25, conditional branch s21 */ + fieldA = (IS_BIT(state->words[0], 17)) ? + (FIELD_s25(state->words[0]) & ~0x3) : + FIELD_s21(state->words[0]); + + state->flow = direct_call; + } else { + /*Branch On Compare */ + fieldA = FIELD_s9(state->words[0]) & ~0x3; + state->flow = direct_jump; + } + + state->delay_slot = IS_BIT(state->words[0], 5); + state->target = fieldA + (addr & ~0x3); + state->is_branch = 1; + break; + + case op_LD: /* LD a,[b,s9] */ + state->write = 0; + state->di = BITS(state->words[0], 11, 11); + if (state->di) + break; + state->x = BITS(state->words[0], 6, 6); + state->zz = BITS(state->words[0], 7, 8); + state->aa = BITS(state->words[0], 9, 10); + state->wb_reg = FIELD_B(state->words[0]); + if (state->wb_reg == REG_LIMM) { + state->instr_len += 4; + state->aa = 0; + state->src1 = state->words[1]; + } else { + state->src1 = get_reg(state->wb_reg, regs, cregs); + } + state->src2 = FIELD_s9(state->words[0]); + state->dest = FIELD_A(state->words[0]); + state->pref = (state->dest == REG_LIMM); + break; + + case op_ST: + state->write = 1; + state->di = BITS(state->words[0], 5, 5); + if (state->di) + break; + state->aa = BITS(state->words[0], 3, 4); + state->zz = BITS(state->words[0], 1, 2); + state->src1 = FIELD_C(state->words[0]); + if (state->src1 == REG_LIMM) { + state->instr_len += 4; + state->src1 = state->words[1]; + } else { + state->src1 = get_reg(state->src1, regs, cregs); + } + state->wb_reg = FIELD_B(state->words[0]); + if (state->wb_reg == REG_LIMM) { + state->aa = 0; + state->instr_len += 4; + state->src2 = state->words[1]; + } else { + state->src2 = get_reg(state->wb_reg, regs, cregs); + } + state->src3 = FIELD_s9(state->words[0]); + break; + + case op_MAJOR_4: + subopcode = MINOR_OPCODE(state->words[0]); + switch (subopcode) { + case 32: /* Jcc */ + case 33: /* Jcc.D */ + case 34: /* JLcc */ + case 35: /* JLcc.D */ + is_linked = 0; + + if (subopcode == 33 || subopcode == 35) + state->delay_slot = 1; + + if (subopcode == 34 || subopcode == 35) + is_linked = 1; + + fieldCisReg = 0; + op_format = BITS(state->words[0], 22, 23); + if (op_format == 0 || ((op_format == 3) && + (!IS_BIT(state->words[0], 5)))) { + fieldC = FIELD_C(state->words[0]); + + if (fieldC == REG_LIMM) { + fieldC = state->words[1]; + state->instr_len += 4; + } else { + fieldCisReg = 1; + } + } else if (op_format == 1 || ((op_format == 3) + && (IS_BIT(state->words[0], 5)))) { + fieldC = FIELD_C(state->words[0]); + } else { + /* op_format == 2 */ + fieldC = FIELD_s12(state->words[0]); + } + + if (!fieldCisReg) { + state->target = fieldC; + state->flow = is_linked ? + direct_call : direct_jump; + } else { + state->target = get_reg(fieldC, regs, cregs); + state->flow = is_linked ? + indirect_call : indirect_jump; + } + state->is_branch = 1; + break; + + case 40: /* LPcc */ + if (BITS(state->words[0], 22, 23) == 3) { + /* Conditional LPcc u7 */ + fieldC = FIELD_C(state->words[0]); + + fieldC = fieldC << 1; + fieldC += (addr & ~0x03); + state->is_branch = 1; + state->flow = direct_jump; + state->target = fieldC; + } + /* For Unconditional lp, next pc is the fall through + * which is updated */ + break; + + case 48 ... 55: /* LD a,[b,c] */ + state->di = BITS(state->words[0], 15, 15); + if (state->di) + break; + state->x = BITS(state->words[0], 16, 16); + state->zz = BITS(state->words[0], 17, 18); + state->aa = BITS(state->words[0], 22, 23); + state->wb_reg = FIELD_B(state->words[0]); + if (state->wb_reg == REG_LIMM) { + state->instr_len += 4; + state->src1 = state->words[1]; + } else { + state->src1 = get_reg(state->wb_reg, regs, + cregs); + } + state->src2 = FIELD_C(state->words[0]); + if (state->src2 == REG_LIMM) { + state->instr_len += 4; + state->src2 = state->words[1]; + } else { + state->src2 = get_reg(state->src2, regs, + cregs); + } + state->dest = FIELD_A(state->words[0]); + if (state->dest == REG_LIMM) + state->pref = 1; + break; + + case 10: /* MOV */ + /* still need to check for limm to extract instr len */ + /* MOV is special case because it only takes 2 args */ + switch (BITS(state->words[0], 22, 23)) { + case 0: /* OP a,b,c */ + if (FIELD_C(state->words[0]) == REG_LIMM) + state->instr_len += 4; + break; + case 1: /* OP a,b,u6 */ + break; + case 2: /* OP b,b,s12 */ + break; + case 3: /* OP.cc b,b,c/u6 */ + if ((!IS_BIT(state->words[0], 5)) && + (FIELD_C(state->words[0]) == REG_LIMM)) + state->instr_len += 4; + break; + } + break; + + + default: + /* Not a Load, Jump or Loop instruction */ + /* still need to check for limm to extract instr len */ + switch (BITS(state->words[0], 22, 23)) { + case 0: /* OP a,b,c */ + if ((FIELD_B(state->words[0]) == REG_LIMM) || + (FIELD_C(state->words[0]) == REG_LIMM)) + state->instr_len += 4; + break; + case 1: /* OP a,b,u6 */ + break; + case 2: /* OP b,b,s12 */ + break; + case 3: /* OP.cc b,b,c/u6 */ + if ((!IS_BIT(state->words[0], 5)) && + ((FIELD_B(state->words[0]) == REG_LIMM) || + (FIELD_C(state->words[0]) == REG_LIMM))) + state->instr_len += 4; + break; + } + break; + } + break; + + /* 16 Bit Instructions */ + case op_LD_ADD: /* LD_S|LDB_S|LDW_S a,[b,c] */ + state->zz = BITS(state->words[0], 3, 4); + state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs); + state->src2 = get_reg(FIELD_S_C(state->words[0]), regs, cregs); + state->dest = FIELD_S_A(state->words[0]); + break; + + case op_ADD_MOV_CMP: + /* check for limm, ignore mov_s h,b (== mov_s 0,b) */ + if ((BITS(state->words[0], 3, 4) < 3) && + (FIELD_S_H(state->words[0]) == REG_LIMM)) + state->instr_len += 4; + break; + + case op_S: + subopcode = BITS(state->words[0], 5, 7); + switch (subopcode) { + case 0: /* j_s */ + case 1: /* j_s.d */ + case 2: /* jl_s */ + case 3: /* jl_s.d */ + state->target = get_reg(FIELD_S_B(state->words[0]), + regs, cregs); + state->delay_slot = subopcode & 1; + state->flow = (subopcode >= 2) ? + direct_call : indirect_jump; + break; + case 7: + switch (BITS(state->words[0], 8, 10)) { + case 4: /* jeq_s [blink] */ + case 5: /* jne_s [blink] */ + case 6: /* j_s [blink] */ + case 7: /* j_s.d [blink] */ + state->delay_slot = (subopcode == 7); + state->flow = indirect_jump; + state->target = get_reg(31, regs, cregs); + default: + break; + } + default: + break; + } + break; + + case op_LD_S: /* LD_S c, [b, u7] */ + state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs); + state->src2 = FIELD_S_u7(state->words[0]); + state->dest = FIELD_S_C(state->words[0]); + break; + + case op_LDB_S: + case op_STB_S: + /* no further handling required as byte accesses should not + * cause an unaligned access exception */ + state->zz = 1; + break; + + case op_LDWX_S: /* LDWX_S c, [b, u6] */ + state->x = 1; + /* intentional fall-through */ + + case op_LDW_S: /* LDW_S c, [b, u6] */ + state->zz = 2; + state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs); + state->src2 = FIELD_S_u6(state->words[0]); + state->dest = FIELD_S_C(state->words[0]); + break; + + case op_ST_S: /* ST_S c, [b, u7] */ + state->write = 1; + state->src1 = get_reg(FIELD_S_C(state->words[0]), regs, cregs); + state->src2 = get_reg(FIELD_S_B(state->words[0]), regs, cregs); + state->src3 = FIELD_S_u7(state->words[0]); + break; + + case op_STW_S: /* STW_S c,[b,u6] */ + state->write = 1; + state->zz = 2; + state->src1 = get_reg(FIELD_S_C(state->words[0]), regs, cregs); + state->src2 = get_reg(FIELD_S_B(state->words[0]), regs, cregs); + state->src3 = FIELD_S_u6(state->words[0]); + break; + + case op_SP: /* LD_S|LDB_S b,[sp,u7], ST_S|STB_S b,[sp,u7] */ + /* note: we are ignoring possibility of: + * ADD_S, SUB_S, PUSH_S, POP_S as these should not + * cause unaliged exception anyway */ + state->write = BITS(state->words[0], 6, 6); + state->zz = BITS(state->words[0], 5, 5); + if (state->zz) + break; /* byte accesses should not come here */ + if (!state->write) { + state->src1 = get_reg(28, regs, cregs); + state->src2 = FIELD_S_u7(state->words[0]); + state->dest = FIELD_S_B(state->words[0]); + } else { + state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, + cregs); + state->src2 = get_reg(28, regs, cregs); + state->src3 = FIELD_S_u7(state->words[0]); + } + break; + + case op_GP: /* LD_S|LDB_S|LDW_S r0,[gp,s11/s9/s10] */ + /* note: ADD_S r0, gp, s11 is ignored */ + state->zz = BITS(state->words[0], 9, 10); + state->src1 = get_reg(26, regs, cregs); + state->src2 = state->zz ? FIELD_S_s10(state->words[0]) : + FIELD_S_s11(state->words[0]); + state->dest = 0; + break; + + case op_Pcl: /* LD_S b,[pcl,u10] */ + state->src1 = regs->ret & ~3; + state->src2 = FIELD_S_u10(state->words[0]); + state->dest = FIELD_S_B(state->words[0]); + break; + + case op_BR_S: + state->target = FIELD_S_s8(state->words[0]) + (addr & ~0x03); + state->flow = direct_jump; + state->is_branch = 1; + break; + + case op_B_S: + fieldA = (BITS(state->words[0], 9, 10) == 3) ? + FIELD_S_s7(state->words[0]) : + FIELD_S_s10(state->words[0]); + state->target = fieldA + (addr & ~0x03); + state->flow = direct_jump; + state->is_branch = 1; + break; + + case op_BL_S: + state->target = FIELD_S_s13(state->words[0]) + (addr & ~0x03); + state->flow = direct_call; + state->is_branch = 1; + break; + + default: + break; + } + + if (bytes_not_copied <= (8 - state->instr_len)) + return; + +fault: state->fault = 1; +} + +long __kprobes get_reg(int reg, struct pt_regs *regs, + struct callee_regs *cregs) +{ + long *p; + + if (reg <= 12) { + p = ®s->r0; + return p[-reg]; + } + + if (cregs && (reg <= 25)) { + p = &cregs->r13; + return p[13-reg]; + } + + if (reg == 26) + return regs->r26; + if (reg == 27) + return regs->fp; + if (reg == 28) + return regs->sp; + if (reg == 31) + return regs->blink; + + return 0; +} + +void __kprobes set_reg(int reg, long val, struct pt_regs *regs, + struct callee_regs *cregs) +{ + long *p; + + switch (reg) { + case 0 ... 12: + p = ®s->r0; + p[-reg] = val; + break; + case 13 ... 25: + if (cregs) { + p = &cregs->r13; + p[13-reg] = val; + } + break; + case 26: + regs->r26 = val; + break; + case 27: + regs->fp = val; + break; + case 28: + regs->sp = val; + break; + case 31: + regs->blink = val; + break; + default: + break; + } +} + +/* + * Disassembles the insn at @pc and sets @next_pc to next PC (which could be + * @pc +2/4/6 (ARCompact ISA allows free intermixing of 16/32 bit insns). + * + * If @pc is a branch + * -@tgt_if_br is set to branch target. + * -If branch has delay slot, @next_pc updated with actual next PC. + * + */ +int __kprobes disasm_next_pc(unsigned long pc, struct pt_regs *regs, + struct callee_regs *cregs, + unsigned long *next_pc, unsigned long *tgt_if_br) +{ + struct disasm_state instr; + + memset(&instr, 0, sizeof(struct disasm_state)); + disasm_instr(pc, &instr, 0, regs, cregs); + + *next_pc = pc + instr.instr_len; + + /* Instruction with possible two targets branch, jump and loop */ + if (instr.is_branch) + *tgt_if_br = instr.target; + + /* For the instructions with delay slots, the fall through is the + * instruction following the instruction in delay slot. + */ + if (instr.delay_slot) { + struct disasm_state instr_d; + + disasm_instr(*next_pc, &instr_d, 0, regs, cregs); + + *next_pc += instr_d.instr_len; + } + + /* Zero Overhead Loop - end of the loop */ + if (!(regs->status32 & STATUS32_L) && (*next_pc == regs->lp_end) + && (regs->lp_count > 1)) { + *next_pc = regs->lp_start; + } + + return instr.is_branch; +} + +#endif /* CONFIG_KGDB || CONFIG_MISALIGN_ACCESS || CONFIG_KPROBES */ From 4d86dfbbda09b3c67bcaeb370f22a2cc7f39205b Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Tue, 22 Jan 2013 17:03:59 +0530 Subject: [PATCH 2842/4607] ARC: kprobes support Origin port done by Rajeshwar Ranga Signed-off-by: Vineet Gupta Cc: Rajeshwar Ranga --- arch/arc/Kconfig | 2 + arch/arc/include/asm/kprobes.h | 62 ++++ arch/arc/include/asm/ptrace.h | 5 + arch/arc/kernel/Makefile | 1 + arch/arc/kernel/disasm.c | 5 +- arch/arc/kernel/kprobes.c | 525 +++++++++++++++++++++++++++++++++ arch/arc/kernel/traps.c | 13 + 7 files changed, 610 insertions(+), 3 deletions(-) create mode 100644 arch/arc/include/asm/kprobes.h create mode 100644 arch/arc/kernel/kprobes.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 84b23fae661c..cde8d3fcec94 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -24,6 +24,8 @@ config ARC select GENERIC_SMP_IDLE_THREAD select HAVE_ARCH_TRACEHOOK select HAVE_GENERIC_HARDIRQS + select HAVE_KPROBES + select HAVE_KRETPROBES select HAVE_MEMBLOCK select HAVE_MOD_ARCH_SPECIFIC if ARC_DW2_UNWIND select HAVE_OPROFILE diff --git a/arch/arc/include/asm/kprobes.h b/arch/arc/include/asm/kprobes.h new file mode 100644 index 000000000000..4d9c211fce70 --- /dev/null +++ b/arch/arc/include/asm/kprobes.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 _ARC_KPROBES_H +#define _ARC_KPROBES_H + +#ifdef CONFIG_KPROBES + +typedef u16 kprobe_opcode_t; + +#define UNIMP_S_INSTRUCTION 0x79e0 +#define TRAP_S_2_INSTRUCTION 0x785e + +#define MAX_INSN_SIZE 8 +#define MAX_STACK_SIZE 64 + +struct arch_specific_insn { + int is_short; + kprobe_opcode_t *t1_addr, *t2_addr; + kprobe_opcode_t t1_opcode, t2_opcode; +}; + +#define flush_insn_slot(p) do { } while (0) + +#define kretprobe_blacklist_size 0 + +struct kprobe; + +void arch_remove_kprobe(struct kprobe *p); + +int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data); + +struct prev_kprobe { + struct kprobe *kp; + unsigned long status; +}; + +struct kprobe_ctlblk { + unsigned int kprobe_status; + struct pt_regs jprobe_saved_regs; + char jprobes_stack[MAX_STACK_SIZE]; + struct prev_kprobe prev_kprobe; +}; + +int kprobe_fault_handler(struct pt_regs *regs, unsigned long cause); +void kretprobe_trampoline(void); +void trap_is_kprobe(unsigned long cause, unsigned long address, + struct pt_regs *regs); +#else +static void trap_is_kprobe(unsigned long cause, unsigned long address, + struct pt_regs *regs) +{ +} +#endif + +#endif diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index a98957a95fb3..063ed0040ef7 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h @@ -111,6 +111,11 @@ struct callee_regs { (struct pt_regs *)(pg_start + THREAD_SIZE - 4) - 1; \ }) +static inline long regs_return_value(struct pt_regs *regs) +{ + return regs->r0; +} + #endif /* !__ASSEMBLY__ */ #define orig_r8_IS_SCALL 0x0001 diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index 6ae4dd4ecea0..d331bd7047a1 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -15,6 +15,7 @@ obj-y += devtree.o obj-$(CONFIG_MODULES) += arcksyms.o module.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o +obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o CFLAGS_fpu.o += -mdpfp diff --git a/arch/arc/kernel/disasm.c b/arch/arc/kernel/disasm.c index 254d11a4f495..51bad8ff373b 100644 --- a/arch/arc/kernel/disasm.c +++ b/arch/arc/kernel/disasm.c @@ -497,9 +497,8 @@ void __kprobes set_reg(int reg, long val, struct pt_regs *regs, * @pc +2/4/6 (ARCompact ISA allows free intermixing of 16/32 bit insns). * * If @pc is a branch - * -@tgt_if_br is set to branch target. - * -If branch has delay slot, @next_pc updated with actual next PC. - * + * -@tgt_if_br is set to branch target. + * -If branch has delay slot, @next_pc updated with actual next PC. */ int __kprobes disasm_next_pc(unsigned long pc, struct pt_regs *regs, struct callee_regs *cregs, diff --git a/arch/arc/kernel/kprobes.c b/arch/arc/kernel/kprobes.c new file mode 100644 index 000000000000..47e42f29f927 --- /dev/null +++ b/arch/arc/kernel/kprobes.c @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MIN_STACK_SIZE(addr) min((unsigned long)MAX_STACK_SIZE, \ + (unsigned long)current_thread_info() + THREAD_SIZE - (addr)) + +DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; +DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); + +int __kprobes arch_prepare_kprobe(struct kprobe *p) +{ + /* Attempt to probe at unaligned address */ + if ((unsigned long)p->addr & 0x01) + return -EINVAL; + + /* Address should not be in exception handling code */ + + p->ainsn.is_short = is_short_instr((unsigned long)p->addr); + p->opcode = *p->addr; + + return 0; +} + +void __kprobes arch_arm_kprobe(struct kprobe *p) +{ + *p->addr = UNIMP_S_INSTRUCTION; + + flush_icache_range((unsigned long)p->addr, + (unsigned long)p->addr + sizeof(kprobe_opcode_t)); +} + +void __kprobes arch_disarm_kprobe(struct kprobe *p) +{ + *p->addr = p->opcode; + + flush_icache_range((unsigned long)p->addr, + (unsigned long)p->addr + sizeof(kprobe_opcode_t)); +} + +void __kprobes arch_remove_kprobe(struct kprobe *p) +{ + arch_disarm_kprobe(p); + + /* Can we remove the kprobe in the middle of kprobe handling? */ + if (p->ainsn.t1_addr) { + *(p->ainsn.t1_addr) = p->ainsn.t1_opcode; + + flush_icache_range((unsigned long)p->ainsn.t1_addr, + (unsigned long)p->ainsn.t1_addr + + sizeof(kprobe_opcode_t)); + + p->ainsn.t1_addr = NULL; + } + + if (p->ainsn.t2_addr) { + *(p->ainsn.t2_addr) = p->ainsn.t2_opcode; + + flush_icache_range((unsigned long)p->ainsn.t2_addr, + (unsigned long)p->ainsn.t2_addr + + sizeof(kprobe_opcode_t)); + + p->ainsn.t2_addr = NULL; + } +} + +static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + kcb->prev_kprobe.kp = kprobe_running(); + kcb->prev_kprobe.status = kcb->kprobe_status; +} + +static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; + kcb->kprobe_status = kcb->prev_kprobe.status; +} + +static inline void __kprobes set_current_kprobe(struct kprobe *p) +{ + __get_cpu_var(current_kprobe) = p; +} + +static void __kprobes resume_execution(struct kprobe *p, unsigned long addr, + struct pt_regs *regs) +{ + /* Remove the trap instructions inserted for single step and + * restore the original instructions + */ + if (p->ainsn.t1_addr) { + *(p->ainsn.t1_addr) = p->ainsn.t1_opcode; + + flush_icache_range((unsigned long)p->ainsn.t1_addr, + (unsigned long)p->ainsn.t1_addr + + sizeof(kprobe_opcode_t)); + + p->ainsn.t1_addr = NULL; + } + + if (p->ainsn.t2_addr) { + *(p->ainsn.t2_addr) = p->ainsn.t2_opcode; + + flush_icache_range((unsigned long)p->ainsn.t2_addr, + (unsigned long)p->ainsn.t2_addr + + sizeof(kprobe_opcode_t)); + + p->ainsn.t2_addr = NULL; + } + + return; +} + +static void __kprobes setup_singlestep(struct kprobe *p, struct pt_regs *regs) +{ + unsigned long next_pc; + unsigned long tgt_if_br = 0; + int is_branch; + unsigned long bta; + + /* Copy the opcode back to the kprobe location and execute the + * instruction. Because of this we will not be able to get into the + * same kprobe until this kprobe is done + */ + *(p->addr) = p->opcode; + + flush_icache_range((unsigned long)p->addr, + (unsigned long)p->addr + sizeof(kprobe_opcode_t)); + + /* Now we insert the trap at the next location after this instruction to + * single step. If it is a branch we insert the trap at possible branch + * targets + */ + + bta = regs->bta; + + if (regs->status32 & 0x40) { + /* We are in a delay slot with the branch taken */ + + next_pc = bta & ~0x01; + + if (!p->ainsn.is_short) { + if (bta & 0x01) + regs->blink += 2; + else { + /* Branch not taken */ + next_pc += 2; + + /* next pc is taken from bta after executing the + * delay slot instruction + */ + regs->bta += 2; + } + } + + is_branch = 0; + } else + is_branch = + disasm_next_pc((unsigned long)p->addr, regs, + (struct callee_regs *) current->thread.callee_reg, + &next_pc, &tgt_if_br); + + p->ainsn.t1_addr = (kprobe_opcode_t *) next_pc; + p->ainsn.t1_opcode = *(p->ainsn.t1_addr); + *(p->ainsn.t1_addr) = TRAP_S_2_INSTRUCTION; + + flush_icache_range((unsigned long)p->ainsn.t1_addr, + (unsigned long)p->ainsn.t1_addr + + sizeof(kprobe_opcode_t)); + + if (is_branch) { + p->ainsn.t2_addr = (kprobe_opcode_t *) tgt_if_br; + p->ainsn.t2_opcode = *(p->ainsn.t2_addr); + *(p->ainsn.t2_addr) = TRAP_S_2_INSTRUCTION; + + flush_icache_range((unsigned long)p->ainsn.t2_addr, + (unsigned long)p->ainsn.t2_addr + + sizeof(kprobe_opcode_t)); + } +} + +int __kprobes arc_kprobe_handler(unsigned long addr, struct pt_regs *regs) +{ + struct kprobe *p; + struct kprobe_ctlblk *kcb; + + preempt_disable(); + + kcb = get_kprobe_ctlblk(); + p = get_kprobe((unsigned long *)addr); + + if (p) { + /* + * We have reentered the kprobe_handler, since another kprobe + * was hit while within the handler, we save the original + * kprobes and single step on the instruction of the new probe + * without calling any user handlers to avoid recursive + * kprobes. + */ + if (kprobe_running()) { + save_previous_kprobe(kcb); + set_current_kprobe(p); + kprobes_inc_nmissed_count(p); + setup_singlestep(p, regs); + kcb->kprobe_status = KPROBE_REENTER; + return 1; + } + + set_current_kprobe(p); + kcb->kprobe_status = KPROBE_HIT_ACTIVE; + + /* If we have no pre-handler or it returned 0, we continue with + * normal processing. If we have a pre-handler and it returned + * non-zero - which is expected from setjmp_pre_handler for + * jprobe, we return without single stepping and leave that to + * the break-handler which is invoked by a kprobe from + * jprobe_return + */ + if (!p->pre_handler || !p->pre_handler(p, regs)) { + setup_singlestep(p, regs); + kcb->kprobe_status = KPROBE_HIT_SS; + } + + return 1; + } else if (kprobe_running()) { + p = __get_cpu_var(current_kprobe); + if (p->break_handler && p->break_handler(p, regs)) { + setup_singlestep(p, regs); + kcb->kprobe_status = KPROBE_HIT_SS; + return 1; + } + } + + /* no_kprobe: */ + preempt_enable_no_resched(); + return 0; +} + +static int __kprobes arc_post_kprobe_handler(unsigned long addr, + struct pt_regs *regs) +{ + struct kprobe *cur = kprobe_running(); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + if (!cur) + return 0; + + resume_execution(cur, addr, regs); + + /* Rearm the kprobe */ + arch_arm_kprobe(cur); + + /* + * When we return from trap instruction we go to the next instruction + * We restored the actual instruction in resume_exectuiont and we to + * return to the same address and execute it + */ + regs->ret = addr; + + if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) { + kcb->kprobe_status = KPROBE_HIT_SSDONE; + cur->post_handler(cur, regs, 0); + } + + if (kcb->kprobe_status == KPROBE_REENTER) { + restore_previous_kprobe(kcb); + goto out; + } + + reset_current_kprobe(); + +out: + preempt_enable_no_resched(); + return 1; +} + +/* + * Fault can be for the instruction being single stepped or for the + * pre/post handlers in the module. + * This is applicable for applications like user probes, where we have the + * probe in user space and the handlers in the kernel + */ + +int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned long trapnr) +{ + struct kprobe *cur = kprobe_running(); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + + switch (kcb->kprobe_status) { + case KPROBE_HIT_SS: + case KPROBE_REENTER: + /* + * We are here because the instruction being single stepped + * caused the fault. We reset the current kprobe and allow the + * exception handler as if it is regular exception. In our + * case it doesn't matter because the system will be halted + */ + resume_execution(cur, (unsigned long)cur->addr, regs); + + if (kcb->kprobe_status == KPROBE_REENTER) + restore_previous_kprobe(kcb); + else + reset_current_kprobe(); + + preempt_enable_no_resched(); + break; + + case KPROBE_HIT_ACTIVE: + case KPROBE_HIT_SSDONE: + /* + * We are here because the instructions in the pre/post handler + * caused the fault. + */ + + /* We increment the nmissed count for accounting, + * we can also use npre/npostfault count for accouting + * these specific fault cases. + */ + kprobes_inc_nmissed_count(cur); + + /* + * We come here because instructions in the pre/post + * handler caused the page_fault, this could happen + * if handler tries to access user space by + * copy_from_user(), get_user() etc. Let the + * user-specified handler try to fix it first. + */ + if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) + return 1; + + /* + * In case the user-specified fault handler returned zero, + * try to fix up. + */ + if (fixup_exception(regs)) + return 1; + + /* + * fixup_exception() could not handle it, + * Let do_page_fault() fix it. + */ + break; + + default: + break; + } + return 0; +} + +int __kprobes kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + struct die_args *args = data; + unsigned long addr = args->err; + int ret = NOTIFY_DONE; + + switch (val) { + case DIE_IERR: + if (arc_kprobe_handler(addr, args->regs)) + return NOTIFY_STOP; + break; + + case DIE_TRAP: + if (arc_post_kprobe_handler(addr, args->regs)) + return NOTIFY_STOP; + break; + + default: + break; + } + + return ret; +} + +int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + unsigned long sp_addr = regs->sp; + + kcb->jprobe_saved_regs = *regs; + memcpy(kcb->jprobes_stack, (void *)sp_addr, MIN_STACK_SIZE(sp_addr)); + regs->ret = (unsigned long)(jp->entry); + + return 1; +} + +void __kprobes jprobe_return(void) +{ + __asm__ __volatile__("unimp_s"); + return; +} + +int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); + unsigned long sp_addr; + + *regs = kcb->jprobe_saved_regs; + sp_addr = regs->sp; + memcpy((void *)sp_addr, kcb->jprobes_stack, MIN_STACK_SIZE(sp_addr)); + preempt_enable_no_resched(); + + return 1; +} + +static void __used kretprobe_trampoline_holder(void) +{ + __asm__ __volatile__(".global kretprobe_trampoline\n" + "kretprobe_trampoline:\n" "nop\n"); +} + +void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + + ri->ret_addr = (kprobe_opcode_t *) regs->blink; + + /* Replace the return addr with trampoline addr */ + regs->blink = (unsigned long)&kretprobe_trampoline; +} + +static int __kprobes trampoline_probe_handler(struct kprobe *p, + struct pt_regs *regs) +{ + struct kretprobe_instance *ri = NULL; + struct hlist_head *head, empty_rp; + struct hlist_node *node, *tmp; + unsigned long flags, orig_ret_address = 0; + unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; + + INIT_HLIST_HEAD(&empty_rp); + kretprobe_hash_lock(current, &head, &flags); + + /* + * It is possible to have multiple instances associated with a given + * task either because an multiple functions in the call path + * have a return probe installed on them, and/or more than one return + * return probe was registered for a target function. + * + * We can handle this because: + * - instances are always inserted at the head of the list + * - when multiple return probes are registered for the same + * function, the first instance's ret_addr will point to the + * real return address, and all the rest will point to + * kretprobe_trampoline + */ + hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { + if (ri->task != current) + /* another task is sharing our hash bucket */ + continue; + + if (ri->rp && ri->rp->handler) + ri->rp->handler(ri, regs); + + orig_ret_address = (unsigned long)ri->ret_addr; + recycle_rp_inst(ri, &empty_rp); + + if (orig_ret_address != trampoline_address) { + /* + * This is the real return address. Any other + * instances associated with this task are for + * other calls deeper on the call stack + */ + break; + } + } + + kretprobe_assert(ri, orig_ret_address, trampoline_address); + regs->ret = orig_ret_address; + + reset_current_kprobe(); + kretprobe_hash_unlock(current, &flags); + preempt_enable_no_resched(); + + hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { + hlist_del(&ri->hlist); + kfree(ri); + } + + /* By returning a non zero value, we are telling the kprobe handler + * that we don't want the post_handler to run + */ + return 1; +} + +static struct kprobe trampoline_p = { + .addr = (kprobe_opcode_t *) &kretprobe_trampoline, + .pre_handler = trampoline_probe_handler +}; + +int __init arch_init_kprobes(void) +{ + /* Registering the trampoline code for the kret probe */ + return register_kprobe(&trampoline_p); +} + +int __kprobes arch_trampoline_kprobe(struct kprobe *p) +{ + if (p->addr == (kprobe_opcode_t *) &kretprobe_trampoline) + return 1; + + return 0; +} + +void trap_is_kprobe(unsigned long cause, unsigned long address, + struct pt_regs *regs) +{ + notify_die(DIE_TRAP, "kprobe_trap", regs, address, cause, SIGTRAP); +} diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c index fd2457cec226..c6396b48fcd2 100644 --- a/arch/arc/kernel/traps.c +++ b/arch/arc/kernel/traps.c @@ -15,6 +15,7 @@ #include #include #include +#include void __init trap_init(void) { @@ -90,6 +91,7 @@ void do_machine_check_fault(unsigned long cause, unsigned long address, die("Machine Check Exception", regs, address, cause); } + /* * Entry point for traps induced by ARCompact TRAP_S insn * This is same family as TRAP0/SWI insn (use the same vector). @@ -109,6 +111,10 @@ void do_non_swi_trap(unsigned long cause, unsigned long address, trap_is_brkpt(cause, address, regs); break; + case 2: + trap_is_kprobe(param, address, regs); + break; + default: break; } @@ -116,10 +122,17 @@ void do_non_swi_trap(unsigned long cause, unsigned long address, /* * Entry point for Instruction Error Exception + * -For a corner case, ARC kprobes implementation resorts to using + * this exception, hence the check */ void do_insterror_or_kprobe(unsigned long cause, unsigned long address, struct pt_regs *regs) { + /* Check if this exception is caused by kprobes */ + if (notify_die(DIE_IERR, "kprobe_ierr", regs, address, + cause, SIGILL) == NOTIFY_STOP) + return; + insterror_is_error(cause, address, regs); } From bf14e3b979a01cd7298d631736f965fe83c6e2bc Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:24 +0530 Subject: [PATCH 2843/4607] sysctl: Enable PARISC "unaligned-trap" to be used cross-arch PARISC defines /proc/sys/kernel/unaligned-trap to runtime toggle unaligned access emulation. The exact mechanics of enablig/disabling are still arch specific, we can make the sysctl usable by other arches. Signed-off-by: Vineet Gupta Acked-by: Helge Deller Cc: "James E.J. Bottomley" Cc: Helge Deller Cc: "Eric W. Biederman" Cc: Serge Hallyn --- arch/parisc/Kconfig | 1 + init/Kconfig | 8 ++++++++ kernel/sysctl.c | 5 +++++ 3 files changed, 14 insertions(+) diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index b77feffbadea..8c7609539717 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -20,6 +20,7 @@ config PARISC select ARCH_HAVE_NMI_SAFE_CMPXCHG select GENERIC_SMP_IDLE_THREAD select GENERIC_STRNCPY_FROM_USER + select SYSCTL_ARCH_UNALIGN_ALLOW select HAVE_MOD_ARCH_SPECIFIC select MODULES_USE_ELF_RELA select CLONE_BACKWARDS diff --git a/init/Kconfig b/init/Kconfig index be8b7f55312d..01180da49add 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1232,6 +1232,14 @@ config SYSCTL_EXCEPTION_TRACE help Enable support for /proc/sys/debug/exception-trace. +config SYSCTL_ARCH_UNALIGN_ALLOW + bool + help + Enable support for /proc/sys/kernel/unaligned-trap + Allows arches to define/use @unaligned_enabled to runtime toggle + the unaligned access emulation. + see arch/parisc/kernel/unaligned.c for reference + config KALLSYMS bool "Load all symbols for debugging/ksymoops" if EXPERT default y diff --git a/kernel/sysctl.c b/kernel/sysctl.c index c88878db491e..878b4c41cfb7 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -157,6 +157,9 @@ extern int sysctl_tsb_ratio; #ifdef __hppa__ extern int pwrsw_enabled; +#endif + +#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW extern int unaligned_enabled; #endif @@ -545,6 +548,8 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, +#endif +#ifdef CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW { .procname = "unaligned-trap", .data = &unaligned_enabled, From 2e651ea1596b0ee25af4fcdc4cd13cbb33ffc254 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Wed, 23 Jan 2013 16:30:36 +0530 Subject: [PATCH 2844/4607] ARC: Unaligned access emulation ARC700 doesn't natively support unaligned access, but can be emulated -Unaligned Access Exception -Disassembly at the Fault address to find the exact insn (long/short) Also per Arnd's comment, we runtime control it using 2 sysctl knobs: * SYSCTL_ARCH_UNALIGN_ALLOW: Runtime enable/disble * SYSCTL_ARCH_UNALIGN_NO_WARN: Warn on each emulation attempt Originally contributed by Tim Yao Signed-off-by: Vineet Gupta Cc: Tim Yao Acked-by: Arnd Bergmann --- arch/arc/Kconfig | 11 ++ arch/arc/include/asm/Kbuild | 1 - arch/arc/include/asm/ptrace.h | 3 + arch/arc/include/asm/unaligned.h | 29 ++++ arch/arc/kernel/Makefile | 1 + arch/arc/kernel/disasm.c | 2 +- arch/arc/kernel/entry.S | 13 ++ arch/arc/kernel/traps.c | 26 ++++ arch/arc/kernel/unaligned.c | 245 +++++++++++++++++++++++++++++++ 9 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 arch/arc/include/asm/unaligned.h create mode 100644 arch/arc/kernel/unaligned.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index cde8d3fcec94..f8042835e746 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -336,6 +336,17 @@ config ARC_CURR_IN_REG This reserved Register R25 to point to Current Task in kernel mode. This saves memory access for each such access + +config ARC_MISALIGN_ACCESS + bool "Emulate unaligned memory access (userspace only)" + default N + select SYSCTL_ARCH_UNALIGN_NO_WARN + select SYSCTL_ARCH_UNALIGN_ALLOW + help + This enables misaligned 16 & 32 bit memory access from user space. + Use ONLY-IF-ABS-NECESSARY as it will be very slow and also can hide + potential bugs in code + config ARC_STACK_NONEXEC bool "Make stack non-executable" default n diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild index 78e982dad537..b24089c974a9 100644 --- a/arch/arc/include/asm/Kbuild +++ b/arch/arc/include/asm/Kbuild @@ -52,7 +52,6 @@ generic-y += topology.h generic-y += trace_clock.h generic-y += types.h generic-y += ucontext.h -generic-y += unaligned.h generic-y += user.h generic-y += vga.h generic-y += xor.h diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index 063ed0040ef7..df5b95213776 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h @@ -97,6 +97,9 @@ struct callee_regs { sp; \ }) +/* return 1 if PC in delay slot */ +#define delay_mode(regs) ((regs->status32 & STATUS_DE_MASK) == STATUS_DE_MASK) + #define in_syscall(regs) (regs->event & orig_r8_IS_SCALL) #define in_brkpt_trap(regs) (regs->event & orig_r8_IS_BRKPT) diff --git a/arch/arc/include/asm/unaligned.h b/arch/arc/include/asm/unaligned.h new file mode 100644 index 000000000000..5dbe63f17b66 --- /dev/null +++ b/arch/arc/include/asm/unaligned.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_UNALIGNED_H +#define _ASM_ARC_UNALIGNED_H + +/* ARC700 can't handle unaligned Data accesses. */ + +#include +#include + +#ifdef CONFIG_ARC_MISALIGN_ACCESS +int misaligned_fixup(unsigned long address, struct pt_regs *regs, + unsigned long cause, struct callee_regs *cregs); +#else +static inline int +misaligned_fixup(unsigned long address, struct pt_regs *regs, + unsigned long cause, struct callee_regs *cregs) +{ + return 0; +} +#endif + +#endif /* _ASM_ARC_UNALIGNED_H */ diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index d331bd7047a1..507a33d4a255 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_MODULES) += arcksyms.o module.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o obj-$(CONFIG_KPROBES) += kprobes.o +obj-$(CONFIG_ARC_MISALIGN_ACCESS) += unaligned.o obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o CFLAGS_fpu.o += -mdpfp diff --git a/arch/arc/kernel/disasm.c b/arch/arc/kernel/disasm.c index 51bad8ff373b..2f390289a792 100644 --- a/arch/arc/kernel/disasm.c +++ b/arch/arc/kernel/disasm.c @@ -15,7 +15,7 @@ #include #include -#if defined(CONFIG_KGDB) || defined(CONFIG_MISALIGN_ACCESS) || \ +#if defined(CONFIG_KGDB) || defined(CONFIG_ARC_MISALIGN_ACCESS) || \ defined(CONFIG_KPROBES) /* disasm_instr: Analyses instruction at addr, stores diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 021cfa46a1dc..f8efade15368 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -7,6 +7,9 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * + * vineetg: May 2011 + * -Userspace unaligned access emulation + * * vineetg: Feb 2011 (ptrace low level code fixes) * -traced syscall return code (r0) was not saved into pt_regs for restoring * into user reg-file when traded task rets to user space. @@ -387,7 +390,17 @@ ARC_ENTRY EV_TLBProtV mov r1, r4 ; faulting address mov r2, sp ; pt_regs +#ifdef CONFIG_ARC_MISALIGN_ACCESS + SAVE_CALLEE_SAVED_USER + mov r3, sp ; callee_regs +#endif + bl do_misaligned_access + +#ifdef CONFIG_ARC_MISALIGN_ACCESS + DISCARD_CALLEE_SAVED_USER +#endif + b ret_from_exception ARC_EXIT EV_TLBProtV diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c index c6396b48fcd2..ec802c52a1ca 100644 --- a/arch/arc/kernel/traps.c +++ b/arch/arc/kernel/traps.c @@ -7,6 +7,9 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * + * vineetg: May 2011 + * -user-space unaligned access emulation + * * Rahul Trivedi: Codito Technologies 2004 */ @@ -16,6 +19,7 @@ #include #include #include +#include void __init trap_init(void) { @@ -79,7 +83,29 @@ DO_ERROR_INFO(SIGILL, "Illegal Insn (or Seq)", insterror_is_error, ILL_ILLOPC) DO_ERROR_INFO(SIGBUS, "Invalid Mem Access", do_memory_error, BUS_ADRERR) DO_ERROR_INFO(SIGTRAP, "Breakpoint Set", trap_is_brkpt, TRAP_BRKPT) +#ifdef CONFIG_ARC_MISALIGN_ACCESS +/* + * Entry Point for Misaligned Data access Exception, for emulating in software + */ +int do_misaligned_access(unsigned long cause, unsigned long address, + struct pt_regs *regs, struct callee_regs *cregs) +{ + if (misaligned_fixup(address, regs, cause, cregs) != 0) { + siginfo_t info; + + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRALN; + info.si_addr = (void __user *)address; + return handle_exception(cause, "Misaligned Access", regs, + &info); + } + return 0; +} + +#else DO_ERROR_INFO(SIGSEGV, "Misaligned Access", do_misaligned_access, SEGV_ACCERR) +#endif /* * Entry point for miscll errors such as Nested Exceptions diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c new file mode 100644 index 000000000000..4cd81633febd --- /dev/null +++ b/arch/arc/kernel/unaligned.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2011-2012 Synopsys (www.synopsys.com) + * + * 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. + * + * vineetg : May 2011 + * -Adapted (from .26 to .35) + * -original contribution by Tim.yao@amlogic.com + * + */ + +#include +#include +#include +#include + +#define __get8_unaligned_check(val, addr, err) \ + __asm__( \ + "1: ldb.ab %1, [%2, 1]\n" \ + "2:\n" \ + " .section .fixup,\"ax\"\n" \ + " .align 4\n" \ + "3: mov %0, 1\n" \ + " b 2b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 1b, 3b\n" \ + " .previous\n" \ + : "=r" (err), "=&r" (val), "=r" (addr) \ + : "0" (err), "2" (addr)) + +#define get16_unaligned_check(val, addr) \ + do { \ + unsigned int err = 0, v, a = addr; \ + __get8_unaligned_check(v, a, err); \ + val = v ; \ + __get8_unaligned_check(v, a, err); \ + val |= v << 8; \ + if (err) \ + goto fault; \ + } while (0) + +#define get32_unaligned_check(val, addr) \ + do { \ + unsigned int err = 0, v, a = addr; \ + __get8_unaligned_check(v, a, err); \ + val = v << 0; \ + __get8_unaligned_check(v, a, err); \ + val |= v << 8; \ + __get8_unaligned_check(v, a, err); \ + val |= v << 16; \ + __get8_unaligned_check(v, a, err); \ + val |= v << 24; \ + if (err) \ + goto fault; \ + } while (0) + +#define put16_unaligned_check(val, addr) \ + do { \ + unsigned int err = 0, v = val, a = addr;\ + \ + __asm__( \ + "1: stb.ab %1, [%2, 1]\n" \ + " lsr %1, %1, 8\n" \ + "2: stb %1, [%2]\n" \ + "3:\n" \ + " .section .fixup,\"ax\"\n" \ + " .align 4\n" \ + "4: mov %0, 1\n" \ + " b 3b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 1b, 4b\n" \ + " .long 2b, 4b\n" \ + " .previous\n" \ + : "=r" (err), "=&r" (v), "=&r" (a) \ + : "0" (err), "1" (v), "2" (a)); \ + \ + if (err) \ + goto fault; \ + } while (0) + +#define put32_unaligned_check(val, addr) \ + do { \ + unsigned int err = 0, v = val, a = addr;\ + __asm__( \ + \ + "1: stb.ab %1, [%2, 1]\n" \ + " lsr %1, %1, 8\n" \ + "2: stb.ab %1, [%2, 1]\n" \ + " lsr %1, %1, 8\n" \ + "3: stb.ab %1, [%2, 1]\n" \ + " lsr %1, %1, 8\n" \ + "4: stb %1, [%2]\n" \ + "5:\n" \ + " .section .fixup,\"ax\"\n" \ + " .align 4\n" \ + "6: mov %0, 1\n" \ + " b 5b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 1b, 6b\n" \ + " .long 2b, 6b\n" \ + " .long 3b, 6b\n" \ + " .long 4b, 6b\n" \ + " .previous\n" \ + : "=r" (err), "=&r" (v), "=&r" (a) \ + : "0" (err), "1" (v), "2" (a)); \ + \ + if (err) \ + goto fault; \ + } while (0) + +/* sysctl hooks */ +int unaligned_enabled __read_mostly = 1; /* Enabled by default */ +int no_unaligned_warning __read_mostly = 1; /* Only 1 warning by default */ + +static void fixup_load(struct disasm_state *state, struct pt_regs *regs, + struct callee_regs *cregs) +{ + int val; + + /* register write back */ + if ((state->aa == 1) || (state->aa == 2)) { + set_reg(state->wb_reg, state->src1 + state->src2, regs, cregs); + + if (state->aa == 2) + state->src2 = 0; + } + + if (state->zz == 0) { + get32_unaligned_check(val, state->src1 + state->src2); + } else { + get16_unaligned_check(val, state->src1 + state->src2); + + if (state->x) + val = (val << 16) >> 16; + } + + if (state->pref == 0) + set_reg(state->dest, val, regs, cregs); + + return; + +fault: state->fault = 1; +} + +static void fixup_store(struct disasm_state *state, struct pt_regs *regs, + struct callee_regs *cregs) +{ + /* register write back */ + if ((state->aa == 1) || (state->aa == 2)) { + set_reg(state->wb_reg, state->src2 + state->src3, regs, cregs); + + if (state->aa == 3) + state->src3 = 0; + } else if (state->aa == 3) { + if (state->zz == 2) { + set_reg(state->wb_reg, state->src2 + (state->src3 << 1), + regs, cregs); + } else if (!state->zz) { + set_reg(state->wb_reg, state->src2 + (state->src3 << 2), + regs, cregs); + } else { + goto fault; + } + } + + /* write fix-up */ + if (!state->zz) + put32_unaligned_check(state->src1, state->src2 + state->src3); + else + put16_unaligned_check(state->src1, state->src2 + state->src3); + + return; + +fault: state->fault = 1; +} + +/* + * Handle an unaligned access + * Returns 0 if successfully handled, 1 if some error happened + */ +int misaligned_fixup(unsigned long address, struct pt_regs *regs, + unsigned long cause, struct callee_regs *cregs) +{ + struct disasm_state state; + char buf[TASK_COMM_LEN]; + + /* handle user mode only and only if enabled by sysadmin */ + if (!user_mode(regs) || !unaligned_enabled) + return 1; + + if (no_unaligned_warning) { + pr_warn_once("%s(%d) made unaligned access which was emulated" + " by kernel assist\n. This can degrade application" + " performance significantly\n. To enable further" + " logging of such instances, please \n" + " echo 0 > /proc/sys/kernel/ignore-unaligned-usertrap\n", + get_task_comm(buf, current), task_pid_nr(current)); + } else { + /* Add rate limiting if it gets down to it */ + pr_warn("%s(%d): unaligned access to/from 0x%lx by PC: 0x%lx\n", + get_task_comm(buf, current), task_pid_nr(current), + address, regs->ret); + + } + + disasm_instr(regs->ret, &state, 1, regs, cregs); + + if (state.fault) + goto fault; + + /* ldb/stb should not have unaligned exception */ + if ((state.zz == 1) || (state.di)) + goto fault; + + if (!state.write) + fixup_load(&state, regs, cregs); + else + fixup_store(&state, regs, cregs); + + if (state.fault) + goto fault; + + if (delay_mode(regs)) { + regs->ret = regs->bta; + regs->status32 &= ~STATUS_DE_MASK; + } else { + regs->ret += state.instr_len; + } + + return 0; + +fault: + pr_err("Alignment trap: fault in fix-up %08lx at [<%08lx>]\n", + state.words[0], address); + + return 1; +} From f46121bd26d7957866739313f1e098a682e8d3e4 Mon Sep 17 00:00:00 2001 From: Mischa Jonker Date: Fri, 18 Jan 2013 15:12:24 +0530 Subject: [PATCH 2845/4607] ARC: kgdb support Signed-off-by: Mischa Jonker Signed-off-by: Vineet Gupta Cc: Jason Wessel Acked-by: Jason Wessel --- arch/arc/Kconfig | 3 +- arch/arc/include/asm/kgdb.h | 61 +++++++++++ arch/arc/kernel/Makefile | 1 + arch/arc/kernel/kgdb.c | 205 ++++++++++++++++++++++++++++++++++++ arch/arc/kernel/traps.c | 6 ++ 5 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 arch/arc/include/asm/kgdb.h create mode 100644 arch/arc/kernel/kgdb.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index f8042835e746..69a939af72c6 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -22,6 +22,7 @@ config ARC select GENERIC_PENDING_IRQ if SMP select GENERIC_SIGALTSTACK select GENERIC_SMP_IDLE_THREAD + select HAVE_ARCH_KGDB select HAVE_ARCH_TRACEHOOK select HAVE_GENERIC_HARDIRQS select HAVE_KPROBES @@ -378,7 +379,7 @@ config ARC_DW2_UNWIND config ARC_DBG_TLB_PARANOIA bool "Paranoia Checks in Low Level TLB Handlers" - depends on ARC_DBG && !SMP + depends on ARC_DBG default n config ARC_DBG_TLB_MISS_COUNT diff --git a/arch/arc/include/asm/kgdb.h b/arch/arc/include/asm/kgdb.h new file mode 100644 index 000000000000..f3c4934f0ca9 --- /dev/null +++ b/arch/arc/include/asm/kgdb.h @@ -0,0 +1,61 @@ +/* + * kgdb support for ARC + * + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 __ARC_KGDB_H__ +#define __ARC_KGDB_H__ + +#ifdef CONFIG_KGDB + +#include + +/* to ensure compatibility with Linux 2.6.35, we don't implement the get/set + * register API yet */ +#undef DBG_MAX_REG_NUM + +#define GDB_MAX_REGS 39 + +#define BREAK_INSTR_SIZE 2 +#define CACHE_FLUSH_IS_SAFE 1 +#define NUMREGBYTES (GDB_MAX_REGS * 4) +#define BUFMAX 2048 + +static inline void arch_kgdb_breakpoint(void) +{ + __asm__ __volatile__ ("trap_s 0x4\n"); +} + +extern void kgdb_trap(struct pt_regs *regs, int param); + +enum arc700_linux_regnums { + _R0 = 0, + _R1, _R2, _R3, _R4, _R5, _R6, _R7, _R8, _R9, _R10, _R11, _R12, _R13, + _R14, _R15, _R16, _R17, _R18, _R19, _R20, _R21, _R22, _R23, _R24, + _R25, _R26, + _BTA = 27, + _LP_START = 28, + _LP_END = 29, + _LP_COUNT = 30, + _STATUS32 = 31, + _BLINK = 32, + _FP = 33, + __SP = 34, + _EFA = 35, + _RET = 36, + _ORIG_R8 = 37, + _STOP_PC = 38 +}; + +#else +static inline void kgdb_trap(struct pt_regs *regs, int param) +{ +} +#endif + +#endif /* __ARC_KGDB_H__ */ diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index 507a33d4a255..6da2b12cf7e3 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_ARC_MISALIGN_ACCESS) += unaligned.o +obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o CFLAGS_fpu.o += -mdpfp diff --git a/arch/arc/kernel/kgdb.c b/arch/arc/kernel/kgdb.c new file mode 100644 index 000000000000..2888ba5be47e --- /dev/null +++ b/arch/arc/kernel/kgdb.c @@ -0,0 +1,205 @@ +/* + * kgdb support for ARC + * + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 +#include +#include + +static void to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs, + struct callee_regs *cregs) +{ + int regno; + + for (regno = 0; regno <= 26; regno++) + gdb_regs[_R0 + regno] = get_reg(regno, kernel_regs, cregs); + + for (regno = 27; regno < GDB_MAX_REGS; regno++) + gdb_regs[regno] = 0; + + gdb_regs[_FP] = kernel_regs->fp; + gdb_regs[__SP] = kernel_regs->sp; + gdb_regs[_BLINK] = kernel_regs->blink; + gdb_regs[_RET] = kernel_regs->ret; + gdb_regs[_STATUS32] = kernel_regs->status32; + gdb_regs[_LP_COUNT] = kernel_regs->lp_count; + gdb_regs[_LP_END] = kernel_regs->lp_end; + gdb_regs[_LP_START] = kernel_regs->lp_start; + gdb_regs[_BTA] = kernel_regs->bta; + gdb_regs[_STOP_PC] = kernel_regs->ret; +} + +static void from_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs, + struct callee_regs *cregs) +{ + int regno; + + for (regno = 0; regno <= 26; regno++) + set_reg(regno, gdb_regs[regno + _R0], kernel_regs, cregs); + + kernel_regs->fp = gdb_regs[_FP]; + kernel_regs->sp = gdb_regs[__SP]; + kernel_regs->blink = gdb_regs[_BLINK]; + kernel_regs->ret = gdb_regs[_RET]; + kernel_regs->status32 = gdb_regs[_STATUS32]; + kernel_regs->lp_count = gdb_regs[_LP_COUNT]; + kernel_regs->lp_end = gdb_regs[_LP_END]; + kernel_regs->lp_start = gdb_regs[_LP_START]; + kernel_regs->bta = gdb_regs[_BTA]; +} + + +void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs) +{ + to_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *) + current->thread.callee_reg); +} + +void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs) +{ + from_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *) + current->thread.callee_reg); +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, + struct task_struct *task) +{ + if (task) + to_gdb_regs(gdb_regs, task_pt_regs(task), + (struct callee_regs *) task->thread.callee_reg); +} + +struct single_step_data_t { + uint16_t opcode[2]; + unsigned long address[2]; + int is_branch; + int armed; +} single_step_data; + +static void undo_single_step(struct pt_regs *regs) +{ + if (single_step_data.armed) { + int i; + + for (i = 0; i < (single_step_data.is_branch ? 2 : 1); i++) { + memcpy((void *) single_step_data.address[i], + &single_step_data.opcode[i], + BREAK_INSTR_SIZE); + + flush_icache_range(single_step_data.address[i], + single_step_data.address[i] + + BREAK_INSTR_SIZE); + } + single_step_data.armed = 0; + } +} + +static void place_trap(unsigned long address, void *save) +{ + memcpy(save, (void *) address, BREAK_INSTR_SIZE); + memcpy((void *) address, &arch_kgdb_ops.gdb_bpt_instr, + BREAK_INSTR_SIZE); + flush_icache_range(address, address + BREAK_INSTR_SIZE); +} + +static void do_single_step(struct pt_regs *regs) +{ + single_step_data.is_branch = disasm_next_pc((unsigned long) + regs->ret, regs, (struct callee_regs *) + current->thread.callee_reg, + &single_step_data.address[0], + &single_step_data.address[1]); + + place_trap(single_step_data.address[0], &single_step_data.opcode[0]); + + if (single_step_data.is_branch) { + place_trap(single_step_data.address[1], + &single_step_data.opcode[1]); + } + + single_step_data.armed++; +} + +int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, + char *remcomInBuffer, char *remcomOutBuffer, + struct pt_regs *regs) +{ + unsigned long addr; + char *ptr; + + undo_single_step(regs); + + switch (remcomInBuffer[0]) { + case 's': + case 'c': + ptr = &remcomInBuffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + regs->ret = addr; + + case 'D': + case 'k': + atomic_set(&kgdb_cpu_doing_single_step, -1); + + if (remcomInBuffer[0] == 's') { + do_single_step(regs); + atomic_set(&kgdb_cpu_doing_single_step, + smp_processor_id()); + } + + return 0; + } + return -1; +} + +unsigned long kgdb_arch_pc(int exception, struct pt_regs *regs) +{ + return instruction_pointer(regs); +} + +int kgdb_arch_init(void) +{ + single_step_data.armed = 0; + return 0; +} + +void kgdb_trap(struct pt_regs *regs, int param) +{ + /* trap_s 3 is used for breakpoints that overwrite existing + * instructions, while trap_s 4 is used for compiled breakpoints. + * + * with trap_s 3 breakpoints the original instruction needs to be + * restored and continuation needs to start at the location of the + * breakpoint. + * + * with trap_s 4 (compiled) breakpoints, continuation needs to + * start after the breakpoint. + */ + if (param == 3) + instruction_pointer(regs) -= BREAK_INSTR_SIZE; + + kgdb_handle_exception(1, SIGTRAP, 0, regs); +} + +void kgdb_arch_exit(void) +{ +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip) +{ + instruction_pointer(regs) = ip; +} + +struct kgdb_arch arch_kgdb_ops = { + /* breakpoint instruction: TRAP_S 0x3 */ +#ifdef CONFIG_CPU_BIG_ENDIAN + .gdb_bpt_instr = {0x78, 0x7e}, +#else + .gdb_bpt_instr = {0x7e, 0x78}, +#endif +}; diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c index ec802c52a1ca..7496995371e8 100644 --- a/arch/arc/kernel/traps.c +++ b/arch/arc/kernel/traps.c @@ -20,6 +20,7 @@ #include #include #include +#include void __init trap_init(void) { @@ -141,6 +142,11 @@ void do_non_swi_trap(unsigned long cause, unsigned long address, trap_is_kprobe(param, address, regs); break; + case 3: + case 4: + kgdb_trap(regs, param); + break; + default: break; } From af61742813aa9dde65ca796801e36d03b83fa79f Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:24 +0530 Subject: [PATCH 2846/4607] ARC: Boot #2: Verbose Boot reporting / feature verification Signed-off-by: Vineet Gupta --- arch/arc/Makefile | 2 + arch/arc/include/asm/arcregs.h | 122 ++++++++++++++++-- arch/arc/include/asm/defines.h | 56 +++++++++ arch/arc/include/asm/setup.h | 14 +++ arch/arc/kernel/setup.c | 223 ++++++++++++++++++++++++++++++++- arch/arc/mm/cache_arc700.c | 46 +++++++ arch/arc/mm/tlb.c | 38 ++++++ 7 files changed, 490 insertions(+), 11 deletions(-) create mode 100644 arch/arc/include/asm/defines.h diff --git a/arch/arc/Makefile b/arch/arc/Makefile index fae66bfc2bdb..9a36c04e4306 100644 --- a/arch/arc/Makefile +++ b/arch/arc/Makefile @@ -20,6 +20,8 @@ export PLATFORM cflags-y += -Iarch/arc/plat-$(PLATFORM)/include cflags-y += -mA7 -fno-common -pipe -fno-builtin -D__linux__ +LINUXINCLUDE += -include ${src}/arch/arc/include/asm/defines.h + ifdef CONFIG_ARC_CURR_IN_REG # For a global register defintion, make sure it gets passed to every file # We had a customer reported bug where some code built in kernel was NOT using diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index 9e42611e39d3..1b907c465666 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -12,8 +12,26 @@ #ifdef __KERNEL__ /* Build Configuration Registers */ +#define ARC_REG_DCCMBASE_BCR 0x61 /* DCCM Base Addr */ +#define ARC_REG_CRC_BCR 0x62 +#define ARC_REG_DVFB_BCR 0x64 +#define ARC_REG_EXTARITH_BCR 0x65 #define ARC_REG_VECBASE_BCR 0x68 +#define ARC_REG_PERIBASE_BCR 0x69 +#define ARC_REG_FP_BCR 0x6B /* Single-Precision FPU */ +#define ARC_REG_DPFP_BCR 0x6C /* Dbl Precision FPU */ #define ARC_REG_MMU_BCR 0x6f +#define ARC_REG_DCCM_BCR 0x74 /* DCCM Present + SZ */ +#define ARC_REG_TIMERS_BCR 0x75 +#define ARC_REG_ICCM_BCR 0x78 +#define ARC_REG_XY_MEM_BCR 0x79 +#define ARC_REG_MAC_BCR 0x7a +#define ARC_REG_MUL_BCR 0x7b +#define ARC_REG_SWAP_BCR 0x7c +#define ARC_REG_NORM_BCR 0x7d +#define ARC_REG_MIXMAX_BCR 0x7e +#define ARC_REG_BARREL_BCR 0x7f +#define ARC_REG_D_UNCACH_BCR 0x6A /* status32 Bits Positions */ #define STATUS_H_BIT 0 /* CPU Halted */ @@ -88,16 +106,6 @@ #define TIMER_CTRL_IE (1 << 0) /* Interupt when Count reachs limit */ #define TIMER_CTRL_NH (1 << 1) /* Count only when CPU NOT halted */ -#if defined(CONFIG_ARC_MMU_V1) -#define CONFIG_ARC_MMU_VER 1 -#elif defined(CONFIG_ARC_MMU_V2) -#define CONFIG_ARC_MMU_VER 2 -#elif defined(CONFIG_ARC_MMU_V3) -#define CONFIG_ARC_MMU_VER 3 -#else -#error "Error: MMU ver" -#endif - /* MMU Management regs */ #define ARC_REG_TLBPD0 0x405 #define ARC_REG_TLBPD1 0x406 @@ -277,6 +285,13 @@ struct arc_fpu { *************************************************************** * Build Configuration Registers, with encoded hardware config */ +struct bcr_identity { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int chip_id:16, cpu_id:8, family:8; +#else + unsigned int family:8, cpu_id:8, chip_id:16; +#endif +}; struct bcr_mmu_1_2 { #ifdef CONFIG_CPU_BIG_ENDIAN @@ -296,6 +311,38 @@ struct bcr_mmu_3 { #endif }; +#define EXTN_SWAP_VALID 0x1 +#define EXTN_NORM_VALID 0x2 +#define EXTN_MINMAX_VALID 0x2 +#define EXTN_BARREL_VALID 0x2 + +struct bcr_extn { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int pad:20, crc:1, ext_arith:2, mul:2, barrel:2, minmax:2, + norm:2, swap:1; +#else + unsigned int swap:1, norm:2, minmax:2, barrel:2, mul:2, ext_arith:2, + crc:1, pad:20; +#endif +}; + +/* DSP Options Ref Manual */ +struct bcr_extn_mac_mul { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int pad:16, type:8, ver:8; +#else + unsigned int ver:8, type:8, pad:16; +#endif +}; + +struct bcr_extn_xymem { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int ram_org:2, num_banks:4, bank_sz:4, ver:8; +#else + unsigned int ver:8, bank_sz:4, num_banks:4, ram_org:2; +#endif +}; + struct bcr_cache { #ifdef CONFIG_CPU_BIG_ENDIAN unsigned int pad:12, line_len:4, sz:4, config:4, ver:8; @@ -304,6 +351,48 @@ struct bcr_cache { #endif }; +struct bcr_perip { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int start:8, pad2:8, sz:8, pad:8; +#else + unsigned int pad:8, sz:8, pad2:8, start:8; +#endif +}; +struct bcr_iccm { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int base:16, pad:5, sz:3, ver:8; +#else + unsigned int ver:8, sz:3, pad:5, base:16; +#endif +}; + +/* DCCM Base Address Register: ARC_REG_DCCMBASE_BCR */ +struct bcr_dccm_base { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int addr:24, ver:8; +#else + unsigned int ver:8, addr:24; +#endif +}; + +/* DCCM RAM Configuration Register: ARC_REG_DCCM_BCR */ +struct bcr_dccm { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int res:21, sz:3, ver:8; +#else + unsigned int ver:8, sz:3, res:21; +#endif +}; + +/* Both SP and DP FPU BCRs have same format */ +struct bcr_fp { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int fast:1, ver:8; +#else + unsigned int ver:8, fast:1; +#endif +}; + /* ******************************************************************* * Generic structures to hold build configuration used at runtime @@ -317,9 +406,22 @@ struct cpuinfo_arc_cache { unsigned int has_aliasing, sz, line_len, assoc, ver; }; +struct cpuinfo_arc_ccm { + unsigned int base_addr, sz; +}; + struct cpuinfo_arc { struct cpuinfo_arc_cache icache, dcache; struct cpuinfo_arc_mmu mmu; + struct bcr_identity core; + unsigned int timers; + unsigned int vec_base; + unsigned int uncached_base; + struct cpuinfo_arc_ccm iccm, dccm; + struct bcr_extn extn; + struct bcr_extn_xymem extn_xymem; + struct bcr_extn_mac_mul extn_mac_mul; + struct bcr_fp fp, dpfp; }; extern struct cpuinfo_arc cpuinfo_arc700[]; diff --git a/arch/arc/include/asm/defines.h b/arch/arc/include/asm/defines.h new file mode 100644 index 000000000000..6097bb439cc5 --- /dev/null +++ b/arch/arc/include/asm/defines.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 __ARC_ASM_DEFINES_H__ +#define __ARC_ASM_DEFINES_H__ + +#if defined(CONFIG_ARC_MMU_V1) +#define CONFIG_ARC_MMU_VER 1 +#elif defined(CONFIG_ARC_MMU_V2) +#define CONFIG_ARC_MMU_VER 2 +#elif defined(CONFIG_ARC_MMU_V3) +#define CONFIG_ARC_MMU_VER 3 +#endif + +#ifdef CONFIG_ARC_HAS_LLSC +#define __CONFIG_ARC_HAS_LLSC_VAL 1 +#else +#define __CONFIG_ARC_HAS_LLSC_VAL 0 +#endif + +#ifdef CONFIG_ARC_HAS_SWAPE +#define __CONFIG_ARC_HAS_SWAPE_VAL 1 +#else +#define __CONFIG_ARC_HAS_SWAPE_VAL 0 +#endif + +#ifdef CONFIG_ARC_HAS_RTSC +#define __CONFIG_ARC_HAS_RTSC_VAL 1 +#else +#define __CONFIG_ARC_HAS_RTSC_VAL 0 +#endif + +#ifdef CONFIG_ARC_MMU_SASID +#define __CONFIG_ARC_MMU_SASID_VAL 1 +#else +#define __CONFIG_ARC_MMU_SASID_VAL 0 +#endif + +#ifdef CONFIG_ARC_HAS_ICACHE +#define __CONFIG_ARC_HAS_ICACHE 1 +#else +#define __CONFIG_ARC_HAS_ICACHE 0 +#endif + +#ifdef CONFIG_ARC_HAS_DCACHE +#define __CONFIG_ARC_HAS_DCACHE 1 +#else +#define __CONFIG_ARC_HAS_DCACHE 0 +#endif + +#endif /* __ARC_ASM_DEFINES_H__ */ diff --git a/arch/arc/include/asm/setup.h b/arch/arc/include/asm/setup.h index ab427e6a2cb5..fc97411269fe 100644 --- a/arch/arc/include/asm/setup.h +++ b/arch/arc/include/asm/setup.h @@ -13,6 +13,20 @@ #define COMMAND_LINE_SIZE 256 +/* + * Data structure to map a ID to string + * Used a lot for bootup reporting of hardware diversity + */ +struct id_to_str { + int id; + const char *str; +}; + +struct cpuinfo_data { + struct id_to_str info; + int up_range; +}; + extern int root_mountflags, end_mem; extern int running_on_hw; diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 6e3996cb9df9..e25538e29fe7 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -24,6 +24,7 @@ #include #include #include +#include #define FIX_PTR(x) __asm__ __volatile__(";" : "+r"(x)) @@ -35,10 +36,205 @@ struct task_struct *_current_task[NR_CPUS]; /* For stack switching */ struct cpuinfo_arc cpuinfo_arc700[NR_CPUS]; + void __init read_arc_build_cfg_regs(void) { + struct bcr_perip uncached_space; + struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; + FIX_PTR(cpu); + + READ_BCR(AUX_IDENTITY, cpu->core); + + cpu->timers = read_aux_reg(ARC_REG_TIMERS_BCR); + + cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE); + if (cpu->vec_base == 0) + cpu->vec_base = (unsigned int)_int_vec_base_lds; + + READ_BCR(ARC_REG_D_UNCACH_BCR, uncached_space); + cpu->uncached_base = uncached_space.start << 24; + + cpu->extn.mul = read_aux_reg(ARC_REG_MUL_BCR); + cpu->extn.swap = read_aux_reg(ARC_REG_SWAP_BCR); + cpu->extn.norm = read_aux_reg(ARC_REG_NORM_BCR); + cpu->extn.minmax = read_aux_reg(ARC_REG_MIXMAX_BCR); + cpu->extn.barrel = read_aux_reg(ARC_REG_BARREL_BCR); + READ_BCR(ARC_REG_MAC_BCR, cpu->extn_mac_mul); + + cpu->extn.ext_arith = read_aux_reg(ARC_REG_EXTARITH_BCR); + cpu->extn.crc = read_aux_reg(ARC_REG_CRC_BCR); + + READ_BCR(ARC_REG_XY_MEM_BCR, cpu->extn_xymem); + read_decode_mmu_bcr(); read_decode_cache_bcr(); + + READ_BCR(ARC_REG_FP_BCR, cpu->fp); + READ_BCR(ARC_REG_DPFP_BCR, cpu->dpfp); +} + +static const struct cpuinfo_data arc_cpu_tbl[] = { + { {0x10, "ARCTangent A5"}, 0x1F}, + { {0x20, "ARC 600" }, 0x2F}, + { {0x30, "ARC 700" }, 0x33}, + { {0x34, "ARC 700 R4.10"}, 0x34}, + { {0x00, NULL } } +}; + +char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len) +{ + int n = 0; + struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id]; + struct bcr_identity *core = &cpu->core; + const struct cpuinfo_data *tbl; + int be = 0; +#ifdef CONFIG_CPU_BIG_ENDIAN + be = 1; +#endif + FIX_PTR(cpu); + + n += scnprintf(buf + n, len - n, + "\nARC IDENTITY\t: Family [%#02x]" + " Cpu-id [%#02x] Chip-id [%#4x]\n", + core->family, core->cpu_id, + core->chip_id); + + for (tbl = &arc_cpu_tbl[0]; tbl->info.id != 0; tbl++) { + if ((core->family >= tbl->info.id) && + (core->family <= tbl->up_range)) { + n += scnprintf(buf + n, len - n, + "processor\t: %s %s\n", + tbl->info.str, + be ? "[Big Endian]" : ""); + break; + } + } + + if (tbl->info.id == 0) + n += scnprintf(buf + n, len - n, "UNKNOWN ARC Processor\n"); + + n += scnprintf(buf + n, len - n, "CPU speed\t: %u.%02u Mhz\n", + (unsigned int)(arc_get_core_freq() / 1000000), + (unsigned int)(arc_get_core_freq() / 10000) % 100); + + n += scnprintf(buf + n, len - n, "Timers\t\t: %s %s\n", + (cpu->timers & 0x200) ? "TIMER1" : "", + (cpu->timers & 0x100) ? "TIMER0" : ""); + + n += scnprintf(buf + n, len - n, "Vect Tbl Base\t: %#x\n", + cpu->vec_base); + + n += scnprintf(buf + n, len - n, "UNCACHED Base\t: %#x\n", + cpu->uncached_base); + + return buf; +} + +static const struct id_to_str mul_type_nm[] = { + { 0x0, "N/A"}, + { 0x1, "32x32 (spl Result Reg)" }, + { 0x2, "32x32 (ANY Result Reg)" } +}; + +static const struct id_to_str mac_mul_nm[] = { + {0x0, "N/A"}, + {0x1, "N/A"}, + {0x2, "Dual 16 x 16"}, + {0x3, "N/A"}, + {0x4, "32x16"}, + {0x5, "N/A"}, + {0x6, "Dual 16x16 and 32x16"} +}; + +char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len) +{ + int n = 0; + struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id]; + + FIX_PTR(cpu); +#define IS_AVAIL1(var, str) ((var) ? str : "") +#define IS_AVAIL2(var, str) ((var == 0x2) ? str : "") +#define IS_USED(var) ((var) ? "(in-use)" : "(not used)") + + n += scnprintf(buf + n, len - n, + "Extn [700-Base]\t: %s %s %s %s %s %s\n", + IS_AVAIL2(cpu->extn.norm, "norm,"), + IS_AVAIL2(cpu->extn.barrel, "barrel-shift,"), + IS_AVAIL1(cpu->extn.swap, "swap,"), + IS_AVAIL2(cpu->extn.minmax, "minmax,"), + IS_AVAIL1(cpu->extn.crc, "crc,"), + IS_AVAIL2(cpu->extn.ext_arith, "ext-arith")); + + n += scnprintf(buf + n, len - n, "Extn [700-MPY]\t: %s", + mul_type_nm[cpu->extn.mul].str); + + n += scnprintf(buf + n, len - n, " MAC MPY: %s\n", + mac_mul_nm[cpu->extn_mac_mul.type].str); + + if (cpu->core.family == 0x34) { + n += scnprintf(buf + n, len - n, + "Extn [700-4.10]\t: LLOCK/SCOND %s, SWAPE %s, RTSC %s\n", + IS_USED(__CONFIG_ARC_HAS_LLSC_VAL), + IS_USED(__CONFIG_ARC_HAS_SWAPE_VAL), + IS_USED(__CONFIG_ARC_HAS_RTSC_VAL)); + } + + n += scnprintf(buf + n, len - n, "Extn [CCM]\t: %s", + !(cpu->dccm.sz || cpu->iccm.sz) ? "N/A" : ""); + + if (cpu->dccm.sz) + n += scnprintf(buf + n, len - n, "DCCM: @ %x, %d KB ", + cpu->dccm.base_addr, TO_KB(cpu->dccm.sz)); + + if (cpu->iccm.sz) + n += scnprintf(buf + n, len - n, "ICCM: @ %x, %d KB", + cpu->iccm.base_addr, TO_KB(cpu->iccm.sz)); + + n += scnprintf(buf + n, len - n, "\nExtn [FPU]\t: %s", + !(cpu->fp.ver || cpu->dpfp.ver) ? "N/A" : ""); + + if (cpu->fp.ver) + n += scnprintf(buf + n, len - n, "SP [v%d] %s", + cpu->fp.ver, cpu->fp.fast ? "(fast)" : ""); + + if (cpu->dpfp.ver) + n += scnprintf(buf + n, len - n, "DP [v%d] %s", + cpu->dpfp.ver, cpu->dpfp.fast ? "(fast)" : ""); + + n += scnprintf(buf + n, len - n, "\n"); + +#ifdef _ASM_GENERIC_UNISTD_H + n += scnprintf(buf + n, len - n, + "OS ABI [v2]\t: asm-generic/{unistd,stat,fcntl}\n"); +#endif + + return buf; +} + +/* + * Ensure that FP hardware and kernel config match + * -If hardware contains DPFP, kernel needs to save/restore FPU state + * across context switches + * -If hardware lacks DPFP, but kernel configured to save FPU state then + * kernel trying to access non-existant DPFP regs will crash + * + * We only check for Dbl precision Floating Point, because only DPFP + * hardware has dedicated regs which need to be saved/restored on ctx-sw + * (Single Precision uses core regs), thus kernel is kind of oblivious to it + */ +void __init arc_chk_fpu(void) +{ + struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; + + if (cpu->dpfp.ver) { +#ifndef CONFIG_ARC_FPU_SAVE_RESTORE + pr_warn("DPFP support broken in this kernel...\n"); +#endif + } else { +#ifdef CONFIG_ARC_FPU_SAVE_RESTORE + panic("H/w lacks DPFP support, apps won't work\n"); +#endif + } } /* @@ -49,10 +245,25 @@ void __init read_arc_build_cfg_regs(void) void __init setup_processor(void) { + char str[512]; + int cpu_id = smp_processor_id(); + read_arc_build_cfg_regs(); arc_init_IRQ(); + + printk(arc_cpu_mumbojumbo(cpu_id, str, sizeof(str))); + arc_mmu_init(); arc_cache_init(); + + + printk(arc_extn_mumbojumbo(cpu_id, str, sizeof(str))); + +#ifdef CONFIG_SMP + printk(arc_platform_smp_cpuinfo()); +#endif + + arc_chk_fpu(); } void __init __attribute__((weak)) arc_platform_early_init(void) @@ -126,12 +337,22 @@ static int show_cpuinfo(struct seq_file *m, void *v) if (!str) goto done; - seq_printf(m, "ARC700 #%d\n", cpu_id); + seq_printf(m, arc_cpu_mumbojumbo(cpu_id, str, PAGE_SIZE)); seq_printf(m, "Bogo MIPS : \t%lu.%02lu\n", loops_per_jiffy / (500000 / HZ), (loops_per_jiffy / (5000 / HZ)) % 100); + seq_printf(m, arc_mmu_mumbojumbo(cpu_id, str, PAGE_SIZE)); + + seq_printf(m, arc_cache_mumbojumbo(cpu_id, str, PAGE_SIZE)); + + seq_printf(m, arc_extn_mumbojumbo(cpu_id, str, PAGE_SIZE)); + +#ifdef CONFIG_SMP + seq_printf(m, arc_platform_smp_cpuinfo()); +#endif + free_page((unsigned long)str); done: seq_printf(m, "\n\n"); diff --git a/arch/arc/mm/cache_arc700.c b/arch/arc/mm/cache_arc700.c index 670f65bb2ef1..c299b30924ee 100644 --- a/arch/arc/mm/cache_arc700.c +++ b/arch/arc/mm/cache_arc700.c @@ -82,6 +82,28 @@ static void __ic_line_inv_4_alias(unsigned long, int); static void (*___flush_icache_rtn) (unsigned long, int); #endif +char *arc_cache_mumbojumbo(int cpu_id, char *buf, int len) +{ + int n = 0; + unsigned int c = smp_processor_id(); + +#define PR_CACHE(p, enb, str) \ +{ \ + if (!(p)->ver) \ + n += scnprintf(buf + n, len - n, str"\t\t: N/A\n"); \ + else \ + n += scnprintf(buf + n, len - n, \ + str"\t\t: (%uK) VIPT, %dway set-asc, %ub Line %s\n", \ + TO_KB((p)->sz), (p)->assoc, (p)->line_len, \ + enb ? "" : "DISABLED (kernel-build)"); \ +} + + PR_CACHE(&cpuinfo_arc700[c].icache, __CONFIG_ARC_HAS_ICACHE, "I-Cache"); + PR_CACHE(&cpuinfo_arc700[c].dcache, __CONFIG_ARC_HAS_DCACHE, "D-Cache"); + + return buf; +} + /* * Read the Cache Build Confuration Registers, Decode them and save into * the cpuinfo structure for later use. @@ -132,10 +154,29 @@ void __init arc_cache_init(void) struct cpuinfo_arc_cache *dc; #endif int way_pg_ratio = way_pg_ratio; + char str[256]; + + printk(arc_cache_mumbojumbo(0, str, sizeof(str))); #ifdef CONFIG_ARC_HAS_ICACHE ic = &cpuinfo_arc700[cpu].icache; + /* 1. Confirm some of I-cache params which Linux assumes */ + if ((ic->assoc != ARC_ICACHE_WAYS) || + (ic->line_len != ARC_ICACHE_LINE_LEN)) { + panic("Cache H/W doesn't match kernel Config"); + } +#if (CONFIG_ARC_MMU_VER > 2) + if (ic->ver != 3) { + if (running_on_hw) + panic("Cache ver doesn't match MMU ver\n"); + + /* For ISS - suggest the toggles to use */ + pr_err("Use -prop=icache_version=3,-prop=dcache_version=3\n"); + + } +#endif + /* * if Cache way size is <= page size then no aliasing exhibited * otherwise ratio determines num of aliases. @@ -175,6 +216,11 @@ void __init arc_cache_init(void) #ifdef CONFIG_ARC_HAS_DCACHE dc = &cpuinfo_arc700[cpu].dcache; + if ((dc->assoc != ARC_DCACHE_WAYS) || + (dc->line_len != ARC_DCACHE_LINE_LEN)) { + panic("Cache H/W doesn't match kernel Config"); + } + /* check for D-Cache aliasing */ if ((dc->sz / ARC_DCACHE_WAYS) > PAGE_SIZE) panic("D$ aliasing not handled right now\n"); diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c index e96030c13b52..9b9ce23f4ec3 100644 --- a/arch/arc/mm/tlb.c +++ b/arch/arc/mm/tlb.c @@ -463,8 +463,46 @@ void __init read_decode_mmu_bcr(void) mmu->num_tlb = mmu->sets * mmu->ways; } +char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len) +{ + int n = 0; + struct cpuinfo_arc_mmu *p_mmu = &cpuinfo_arc700[smp_processor_id()].mmu; + + n += scnprintf(buf + n, len - n, "ARC700 MMU [v%x]\t: %dk PAGE, ", + p_mmu->ver, TO_KB(p_mmu->pg_sz)); + + n += scnprintf(buf + n, len - n, + "J-TLB %d (%dx%d), uDTLB %d, uITLB %d, %s\n", + p_mmu->num_tlb, p_mmu->sets, p_mmu->ways, + p_mmu->u_dtlb, p_mmu->u_itlb, + __CONFIG_ARC_MMU_SASID_VAL ? "SASID" : ""); + + return buf; +} + void __init arc_mmu_init(void) { + char str[256]; + struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; + + printk(arc_mmu_mumbojumbo(0, str, sizeof(str))); + + /* For efficiency sake, kernel is compile time built for a MMU ver + * This must match the hardware it is running on. + * Linux built for MMU V2, if run on MMU V1 will break down because V1 + * hardware doesn't understand cmds such as WriteNI, or IVUTLB + * On the other hand, Linux built for V1 if run on MMU V2 will do + * un-needed workarounds to prevent memcpy thrashing. + * Similarly MMU V3 has new features which won't work on older MMU + */ + if (mmu->ver != CONFIG_ARC_MMU_VER) { + panic("MMU ver %d doesn't match kernel built for %d...\n", + mmu->ver, CONFIG_ARC_MMU_VER); + } + + if (mmu->pg_sz != PAGE_SIZE) + panic("MMU pg size != PAGE_SIZE (%luk)\n", TO_KB(PAGE_SIZE)); + /* * ASID mgmt data structures are compile time init * asid_cache = FIRST_ASID and asid_mm_map[] all zeroes From 7fadc1e8fe89698caac213ff6d631b811fc7b393 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:24 +0530 Subject: [PATCH 2847/4607] ARC: [plat-arfpga] BVCI Latency Unit setup Signed-off-by: Vineet Gupta --- arch/arc/plat-arcfpga/Kconfig | 32 ++++++++++++++++++ arch/arc/plat-arcfpga/platform.c | 56 ++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/arch/arc/plat-arcfpga/Kconfig b/arch/arc/plat-arcfpga/Kconfig index 38752bfb91e0..9912d9c107a0 100644 --- a/arch/arc/plat-arcfpga/Kconfig +++ b/arch/arc/plat-arcfpga/Kconfig @@ -44,4 +44,36 @@ config ARC_SERIAL_BAUD help Baud rate for the ARC UART +menuconfig ARC_HAS_BVCI_LAT_UNIT + bool "BVCI Bus Latency Unit" + depends on ARC_BOARD_ML509 || ARC_BOARD_ANGEL4 + help + IP to add artifical latency to BVCI Bus Based FPGA builds. + The default latency (even worst case) for FPGA is non-realistic + (~10 SDRAM, ~5 SSRAM). + +config BVCI_LAT_UNITS + hex "Latency Unit(s) Bitmap" + default "0x0" + depends on ARC_HAS_BVCI_LAT_UNIT + help + There are multiple Latency Units corresponding to the many + interfaces of the system bus arbiter (both CPU side as well as + the peripheral side). + To add latency to ALL memory transaction, choose Unit 0, otherwise + for finer grainer - interface wise latency, specify a bitmap (1 bit + per unit) of all units. e.g. 1,2,12 will be 0x1003 + + Unit 0 - System Arb and Mem Controller + Unit 1 - I$ and System Bus + Unit 2 - D$ and System Bus + .. + Unit 12 - IDE Disk controller and System Bus + +config BVCI_LAT_CYCLES + int "Latency Value in cycles" + range 0 63 + default "30" + depends on ARC_HAS_BVCI_LAT_UNIT + endif diff --git a/arch/arc/plat-arcfpga/platform.c b/arch/arc/plat-arcfpga/platform.c index 33bcac8bd6b8..b7f63e3f3cae 100644 --- a/arch/arc/plat-arcfpga/platform.c +++ b/arch/arc/plat-arcfpga/platform.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,59 @@ #include #include +/*-----------------------BVCI Latency Unit -----------------------------*/ + +#ifdef CONFIG_ARC_HAS_BVCI_LAT_UNIT + +int lat_cycles = CONFIG_BVCI_LAT_CYCLES; + +/* BVCI Bus Profiler: Latency Unit */ +static void __init setup_bvci_lat_unit(void) +{ +#define MAX_BVCI_UNITS 12 + + unsigned int i; + unsigned int *base = (unsigned int *)BVCI_LAT_UNIT_BASE; + const unsigned long units_req = CONFIG_BVCI_LAT_UNITS; + const unsigned int REG_UNIT = 21; + const unsigned int REG_VAL = 22; + + /* + * There are multiple Latency Units corresponding to the many + * interfaces of the system bus arbiter (both CPU side as well as + * the peripheral side). + * + * Unit 0 - System Arb and Mem Controller - adds latency to all + * memory trasactions + * Unit 1 - I$ and System Bus + * Unit 2 - D$ and System Bus + * .. + * Unit 12 - IDE Disk controller and System Bus + * + * The programmers model requires writing to lat_unit reg first + * and then the latency value (cycles) to lat_value reg + */ + + if (CONFIG_BVCI_LAT_UNITS == 0) { + writel(0, base + REG_UNIT); + writel(lat_cycles, base + REG_VAL); + pr_info("BVCI Latency for all Memory Transactions %d cycles\n", + lat_cycles); + } else { + for_each_set_bit(i, &units_req, MAX_BVCI_UNITS) { + writel(i + 1, base + REG_UNIT); /* loop is 0 based */ + writel(lat_cycles, base + REG_VAL); + pr_info("BVCI Latency for Unit[%d] = %d cycles\n", + (i + 1), lat_cycles); + } + } +} +#else +static void __init setup_bvci_lat_unit(void) +{ +} +#endif + /*----------------------- Platform Devices -----------------------------*/ static unsigned long arc_uart_info[] = { @@ -106,6 +160,8 @@ void __init arc_platform_early_init(void) { pr_info("[plat-arcfpga]: registering early dev resources\n"); + setup_bvci_lat_unit(); + arc_fpga_serial_init(); } From 9854783e55d5f42a0ea3e951fad3a5910023c1b1 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:24 +0530 Subject: [PATCH 2848/4607] perf, ARC: Enable building perf tools for ARC Although with uClibc there's more we need to do Signed-off-by: Vineet Gupta Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo --- tools/perf/perf.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 2c340e7da458..8a68ad39e1b6 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -98,6 +98,12 @@ void get_term_dimensions(struct winsize *ws); #define CPUINFO_PROC "cpu model" #endif +#ifdef __arc__ +#define rmb() asm volatile("" ::: "memory") +#define cpu_relax() rmb() +#define CPUINFO_PROC "Processor" +#endif + #include #include #include From 9c57564e26c5392ac7f0e08cc0ad8d29e225a3a3 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:24 +0530 Subject: [PATCH 2849/4607] ARC: perf support (software counters only) Signed-off-by: Vineet Gupta --- arch/arc/Kconfig | 3 +++ arch/arc/include/asm/perf_event.h | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 arch/arc/include/asm/perf_event.h diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 69a939af72c6..03183f690849 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -25,16 +25,19 @@ config ARC select HAVE_ARCH_KGDB select HAVE_ARCH_TRACEHOOK select HAVE_GENERIC_HARDIRQS + select HAVE_IRQ_WORK select HAVE_KPROBES select HAVE_KRETPROBES select HAVE_MEMBLOCK select HAVE_MOD_ARCH_SPECIFIC if ARC_DW2_UNWIND select HAVE_OPROFILE + select HAVE_PERF_EVENTS select IRQ_DOMAIN select MODULES_USE_ELF_RELA select NO_BOOTMEM select OF select OF_EARLY_FLATTREE + select PERF_USE_VMALLOC config SCHED_OMIT_FRAME_POINTER def_bool y diff --git a/arch/arc/include/asm/perf_event.h b/arch/arc/include/asm/perf_event.h new file mode 100644 index 000000000000..115ad96480e6 --- /dev/null +++ b/arch/arc/include/asm/perf_event.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_PERF_EVENT_H +#define __ASM_PERF_EVENT_H + +#endif /* __ASM_PERF_EVENT_H */ From 8b5850f8ac8d9b809db4588b80b568faca5aaaaf Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:25 +0530 Subject: [PATCH 2850/4607] ARC: Support for single cycle Close Coupled Mem (CCM) * Includes mapping of CCMs in address space * Annotations to move arbitrary code/data into CCM * Moving some of the critical code/data into CCM * Runtime detection/reporting Signed-off-by: Vineet Gupta --- arch/arc/Kconfig | 27 +++++++++++++++++ arch/arc/include/asm/linkage.h | 33 +++++++++++++++++++++ arch/arc/kernel/entry.S | 4 +-- arch/arc/kernel/setup.c | 53 +++++++++++++++++++++++++++++++++- arch/arc/kernel/vmlinux.lds.S | 21 ++++++++++++++ arch/arc/mm/tlbex.S | 5 ++-- 6 files changed, 137 insertions(+), 6 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 03183f690849..2611a60cb059 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -198,6 +198,33 @@ config ARC_CACHE_PAGES endif #ARC_CACHE +config ARC_HAS_ICCM + bool "Use ICCM" + help + Single Cycle RAMS to store Fast Path Code + default n + +config ARC_ICCM_SZ + int "ICCM Size in KB" + default "64" + depends on ARC_HAS_ICCM + +config ARC_HAS_DCCM + bool "Use DCCM" + help + Single Cycle RAMS to store Fast Path Data + default n + +config ARC_DCCM_SZ + int "DCCM Size in KB" + default "64" + depends on ARC_HAS_DCCM + +config ARC_DCCM_BASE + hex "DCCM map address" + default "0xA0000000" + depends on ARC_HAS_DCCM + config ARC_HAS_HW_MPY bool "Use Hardware Multiplier (Normal or Faster XMAC)" default y diff --git a/arch/arc/include/asm/linkage.h b/arch/arc/include/asm/linkage.h index a45d1bb50e41..0283e9e44e0d 100644 --- a/arch/arc/include/asm/linkage.h +++ b/arch/arc/include/asm/linkage.h @@ -25,6 +25,39 @@ .size \ name, ASM_PREV_SYM_ADDR(\name) .endm +/* annotation for data we want in DCCM - if enabled in .config */ +.macro ARCFP_DATA nm +#ifdef CONFIG_ARC_HAS_DCCM + .section .data.arcfp +#else + .section .data +#endif + .global \nm +.endm + +/* annotation for data we want in DCCM - if enabled in .config */ +.macro ARCFP_CODE +#ifdef CONFIG_ARC_HAS_ICCM + .section .text.arcfp, "ax",@progbits +#else + .section .text, "ax",@progbits +#endif +.endm + +#else /* !__ASSEMBLY__ */ + +#ifdef CONFIG_ARC_HAS_ICCM +#define __arcfp_code __attribute__((__section__(".text.arcfp"))) +#else +#define __arcfp_code __attribute__((__section__(".text"))) +#endif + +#ifdef CONFIG_ARC_HAS_DCCM +#define __arcfp_data __attribute__((__section__(".data.arcfp"))) +#else +#define __arcfp_data __attribute__((__section__(".data"))) +#endif + #endif /* __ASSEMBLY__ */ #endif diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index f8efade15368..3f628ca9b71a 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -149,7 +149,7 @@ VECTOR reserved ; Reserved Exceptions ;##################### Scratch Mem for IRQ stack switching ############# - .section .data ; NOT .global +ARCFP_DATA int1_saved_reg .align 32 .type int1_saved_reg, @object .size int1_saved_reg, 4 @@ -159,7 +159,7 @@ int1_saved_reg: /* Each Interrupt level needs it's own scratch */ #ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS - .section .data ; NOT .global +ARCFP_DATA int2_saved_reg .type int2_saved_reg, @object .size int2_saved_reg, 4 int2_saved_reg: diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index e25538e29fe7..6cc361c6751a 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -64,6 +64,33 @@ void __init read_arc_build_cfg_regs(void) cpu->extn.ext_arith = read_aux_reg(ARC_REG_EXTARITH_BCR); cpu->extn.crc = read_aux_reg(ARC_REG_CRC_BCR); + /* Note that we read the CCM BCRs independent of kernel config + * This is to catch the cases where user doesn't know that + * CCMs are present in hardware build + */ + { + struct bcr_iccm iccm; + struct bcr_dccm dccm; + struct bcr_dccm_base dccm_base; + unsigned int bcr_32bit_val; + + bcr_32bit_val = read_aux_reg(ARC_REG_ICCM_BCR); + if (bcr_32bit_val) { + iccm = *((struct bcr_iccm *)&bcr_32bit_val); + cpu->iccm.base_addr = iccm.base << 16; + cpu->iccm.sz = 0x2000 << (iccm.sz - 1); + } + + bcr_32bit_val = read_aux_reg(ARC_REG_DCCM_BCR); + if (bcr_32bit_val) { + dccm = *((struct bcr_dccm *)&bcr_32bit_val); + cpu->dccm.sz = 0x800 << (dccm.sz); + + READ_BCR(ARC_REG_DCCMBASE_BCR, dccm_base); + cpu->dccm.base_addr = dccm_base.addr << 8; + } + } + READ_BCR(ARC_REG_XY_MEM_BCR, cpu->extn_xymem); read_decode_mmu_bcr(); @@ -211,6 +238,30 @@ char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len) return buf; } +void __init arc_chk_ccms(void) +{ +#if defined(CONFIG_ARC_HAS_DCCM) || defined(CONFIG_ARC_HAS_ICCM) + struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; + +#ifdef CONFIG_ARC_HAS_DCCM + /* + * DCCM can be arbit placed in hardware. + * Make sure it's placement/sz matches what Linux is built with + */ + if ((unsigned int)__arc_dccm_base != cpu->dccm.base_addr) + panic("Linux built with incorrect DCCM Base address\n"); + + if (CONFIG_ARC_DCCM_SZ != cpu->dccm.sz) + panic("Linux built with incorrect DCCM Size\n"); +#endif + +#ifdef CONFIG_ARC_HAS_ICCM + if (CONFIG_ARC_ICCM_SZ != cpu->iccm.sz) + panic("Linux built with incorrect ICCM Size\n"); +#endif +#endif +} + /* * Ensure that FP hardware and kernel config match * -If hardware contains DPFP, kernel needs to save/restore FPU state @@ -255,7 +306,7 @@ void __init setup_processor(void) arc_mmu_init(); arc_cache_init(); - + arc_chk_ccms(); printk(arc_extn_mumbojumbo(cpu_id, str, sizeof(str))); diff --git a/arch/arc/kernel/vmlinux.lds.S b/arch/arc/kernel/vmlinux.lds.S index 303ea01c85c8..8d3b0d447498 100644 --- a/arch/arc/kernel/vmlinux.lds.S +++ b/arch/arc/kernel/vmlinux.lds.S @@ -23,6 +23,12 @@ jiffies = jiffies_64; SECTIONS { + /* + * ICCM starts at 0x8000_0000. So if kernel is relocated to some other + * address, make sure peripheral at 0x8z doesn't clash with ICCM + * Essentially vector is also in ICCM. + */ + . = CONFIG_LINUX_LINK_BASE; _int_vec_base_lds = .; @@ -31,6 +37,13 @@ SECTIONS . = ALIGN(PAGE_SIZE); } +#ifdef CONFIG_ARC_HAS_ICCM + .text.arcfp : { + *(.text.arcfp) + . = ALIGN(CONFIG_ARC_ICCM_SZ * 1024); + } +#endif + /* * The reason for having a seperate subsection .init.ramfs is to * prevent objump from including it in kernel dumps @@ -134,4 +147,12 @@ SECTIONS .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } +#ifdef CONFIG_ARC_HAS_DCCM + . = CONFIG_ARC_DCCM_BASE; + __arc_dccm_base = .; + .data.arcfp : { + *(.data.arcfp) + } + . = ALIGN(CONFIG_ARC_DCCM_SZ * 1024); +#endif } diff --git a/arch/arc/mm/tlbex.S b/arch/arc/mm/tlbex.S index 4b1ad2d905ca..9df765dc7c3a 100644 --- a/arch/arc/mm/tlbex.S +++ b/arch/arc/mm/tlbex.S @@ -53,8 +53,7 @@ ; For details refer to comments before TLBMISS_FREEUP_REGS below ;-------------------------------------------------------------------------- - .section .data - .global ex_saved_reg1 +ARCFP_DATA ex_saved_reg1 .align 1 << L1_CACHE_SHIFT ; IMP: Must be Cache Line aligned .type ex_saved_reg1, @object #ifdef CONFIG_SMP @@ -255,7 +254,7 @@ ex_saved_reg1: #endif .endm -.section .text, "ax",@progbits ;Fast Path Code, candidate for ICCM +ARCFP_CODE ;Fast Path Code, candidate for ICCM ;----------------------------------------------------------------------------- ; I-TLB Miss Exception Handler From cbe056f76a386708f3807b274322f78269aee0f6 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:25 +0530 Subject: [PATCH 2851/4607] ARC: Hostlink Pseudo-Driver for Metaware Debugger This allows ARC Target to do I/O to host in absence of any peripherals whatsoever, assisted by Metaware Hostlink facility. Further we have a FUSE based filesystem which makes us mount/access host filesystem on target and do fops. Signed-off-by: Vineet Gupta --- arch/arc/Kconfig | 9 ++++++ arch/arc/kernel/Makefile | 1 + arch/arc/kernel/arc_hostlink.c | 58 ++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 arch/arc/kernel/arc_hostlink.c diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 2611a60cb059..cd4ad61c4c14 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -389,6 +389,15 @@ config HZ int "Timer Frequency" default 100 +config ARC_METAWARE_HLINK + bool "Support for Metaware debugger assisted Host access" + default n + help + This options allows a Linux userland apps to directly access + host file system (open/creat/read/write etc) with help from + Metaware Debugger. This can come in handy for Linux-host communication + when there is no real usable peripheral such as EMAC. + menuconfig ARC_DBG bool "ARC debugging" default y diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index 6da2b12cf7e3..c242ef07ba70 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_ARC_DW2_UNWIND) += unwind.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_ARC_MISALIGN_ACCESS) += unaligned.o obj-$(CONFIG_KGDB) += kgdb.o +obj-$(CONFIG_ARC_METAWARE_HLINK) += arc_hostlink.o obj-$(CONFIG_ARC_FPU_SAVE_RESTORE) += fpu.o CFLAGS_fpu.o += -mdpfp diff --git a/arch/arc/kernel/arc_hostlink.c b/arch/arc/kernel/arc_hostlink.c new file mode 100644 index 000000000000..47b2a17cc52a --- /dev/null +++ b/arch/arc/kernel/arc_hostlink.c @@ -0,0 +1,58 @@ +/* + * arc_hostlink.c: Pseudo-driver for Metaware provided "hostlink" facility + * + * Allows Linux userland access to host in absence of any peripherals. + * + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 /* file_operations */ +#include +#include /* VM_IO */ +#include +#include + +static unsigned char __HOSTLINK__[4 * PAGE_SIZE] __aligned(PAGE_SIZE); + +static int arc_hl_mmap(struct file *fp, struct vm_area_struct *vma) +{ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) { + pr_warn("Hostlink buffer mmap ERROR\n"); + return -EAGAIN; + } + return 0; +} + +static long arc_hl_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + /* we only support, returning the physical addr to mmap in user space */ + put_user((unsigned int)__HOSTLINK__, (int __user *)arg); + return 0; +} + +static const struct file_operations arc_hl_fops = { + .unlocked_ioctl = arc_hl_ioctl, + .mmap = arc_hl_mmap, +}; + +static struct miscdevice arc_hl_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "hostlink", + .fops = &arc_hl_fops +}; + +static int __init arc_hl_init(void) +{ + pr_info("ARC Hostlink driver mmap at 0x%p\n", __HOSTLINK__); + return misc_register(&arc_hl_dev); +} +module_init(arc_hl_init); From 8c2f4a8dd0e0fc9dcaf14c768544039eddfa7375 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Mon, 11 Feb 2013 19:55:33 +0530 Subject: [PATCH 2852/4607] ARC: UAPI Disintegrate arch/arc/include/asm 1. ./genfilelist.pl arch/arc/include/asm/ 2. Create arch/arc/include/uapi/asm/Kbuild as follows +# UAPI Header export list +include include/uapi/asm-generic/Kbuild.asm 3. ./disintegrate-one.pl arch/arc/include/{,uapi/}asm/ 4. Edit arch/arc/include/asm/Kbuild to remove ref to asm-generic/Kbuild.asm - To work around empty uapi/asm/setup.h added a placholder comment. - Also a manual #ifdef __ASSEMBLY__ for a late ptrace change Signed-off-by: Vineet Gupta Cc: David Howells --- arch/arc/include/asm/Kbuild | 8 ---- arch/arc/include/asm/page.h | 30 +----------- arch/arc/include/asm/ptrace.h | 38 +--------------- arch/arc/include/asm/setup.h | 3 +- arch/arc/include/uapi/asm/Kbuild | 11 +++++ arch/arc/include/{ => uapi}/asm/byteorder.h | 0 arch/arc/include/{ => uapi}/asm/cachectl.h | 0 arch/arc/include/uapi/asm/page.h | 39 ++++++++++++++++ arch/arc/include/uapi/asm/ptrace.h | 48 ++++++++++++++++++++ arch/arc/include/uapi/asm/setup.h | 6 +++ arch/arc/include/{ => uapi}/asm/sigcontext.h | 0 arch/arc/include/{ => uapi}/asm/signal.h | 0 arch/arc/include/{ => uapi}/asm/swab.h | 0 arch/arc/include/{ => uapi}/asm/unistd.h | 0 14 files changed, 108 insertions(+), 75 deletions(-) create mode 100644 arch/arc/include/uapi/asm/Kbuild rename arch/arc/include/{ => uapi}/asm/byteorder.h (100%) rename arch/arc/include/{ => uapi}/asm/cachectl.h (100%) create mode 100644 arch/arc/include/uapi/asm/page.h create mode 100644 arch/arc/include/uapi/asm/ptrace.h create mode 100644 arch/arc/include/uapi/asm/setup.h rename arch/arc/include/{ => uapi}/asm/sigcontext.h (100%) rename arch/arc/include/{ => uapi}/asm/signal.h (100%) rename arch/arc/include/{ => uapi}/asm/swab.h (100%) rename arch/arc/include/{ => uapi}/asm/unistd.h (100%) diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild index b24089c974a9..48af742f8b5a 100644 --- a/arch/arc/include/asm/Kbuild +++ b/arch/arc/include/asm/Kbuild @@ -1,11 +1,3 @@ -include include/asm-generic/Kbuild.asm - -# 7-Oct-12: Jeremy Bennett . Some of these -# headers, beyond those specified in the generic set are needed by user code. - -header-y += page.h -header-y += cachectl.h - generic-y += auxvec.h generic-y += bugs.h generic-y += bitsperlong.h diff --git a/arch/arc/include/asm/page.h b/arch/arc/include/asm/page.h index d111d0cea9cc..dfe1f8a95f1d 100644 --- a/arch/arc/include/asm/page.h +++ b/arch/arc/include/asm/page.h @@ -5,37 +5,11 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - #ifndef __ASM_ARC_PAGE_H #define __ASM_ARC_PAGE_H -/* PAGE_SHIFT determines the page size */ -#if defined(CONFIG_ARC_PAGE_SIZE_16K) -#define PAGE_SHIFT 14 -#elif defined(CONFIG_ARC_PAGE_SIZE_4K) -#define PAGE_SHIFT 12 -#else -/* - * Default 8k - * done this way (instead of under CONFIG_ARC_PAGE_SIZE_8K) because adhoc - * user code (busybox appletlib.h) expects PAGE_SHIFT to be defined w/o - * using the correct uClibc header and in their build our autoconf.h is - * not available - */ -#define PAGE_SHIFT 13 -#endif +#include -#ifdef __ASSEMBLY__ -#define PAGE_SIZE (1 << PAGE_SHIFT) -#define PAGE_OFFSET (0x80000000) -#else -#define PAGE_SIZE (1UL << PAGE_SHIFT) /* Default 8K */ -#define PAGE_OFFSET (0x80000000UL) /* Kernel starts at 2G onwards */ -#endif - -#define PAGE_MASK (~(PAGE_SIZE-1)) - -#ifdef __KERNEL__ #ifndef __ASSEMBLY__ @@ -129,6 +103,4 @@ typedef unsigned long pgtable_t; #endif /* !__ASSEMBLY__ */ -#endif /* __KERNEL__ */ - #endif diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index df5b95213776..8ae783d20a81 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h @@ -7,11 +7,10 @@ * * Amit Bhor, Sameer Dhavale: Codito Technologies 2004 */ - #ifndef __ASM_ARC_PTRACE_H #define __ASM_ARC_PTRACE_H -#ifdef __KERNEL__ +#include #ifndef __ASSEMBLY__ @@ -128,39 +127,4 @@ static inline long regs_return_value(struct pt_regs *regs) #define orig_r8_IS_IRQ1 0x0010 #define orig_r8_IS_IRQ2 0x0020 -#endif /* __KERNEL__ */ - -#ifndef __ASSEMBLY__ -/* - * Userspace ABI: Register state needed by - * -ptrace (gdbserver) - * -sigcontext (SA_SIGNINFO signal frame) - * - * This is to decouple pt_regs from user-space ABI, to be able to change it - * w/o affecting the ABI. - * Although the layout (initial padding) is similar to pt_regs to have some - * optimizations when copying pt_regs to/from user_regs_struct. - * - * Also, sigcontext only care about the scratch regs as that is what we really - * save/restore for signal handling. -*/ -struct user_regs_struct { - - struct scratch { - long pad; - long bta, lp_start, lp_end, lp_count; - long status32, ret, blink, fp, gp; - long r12, r11, r10, r9, r8, r7, r6, r5, r4, r3, r2, r1, r0; - long sp; - } scratch; - struct callee { - long pad; - long r25, r24, r23, r22, r21, r20; - long r19, r18, r17, r16, r15, r14, r13; - } callee; - long efa; /* break pt addr, for break points in delay slots */ - long stop_pc; /* give dbg stop_pc directly after checking orig_r8 */ -}; -#endif /* !__ASSEMBLY__ */ - #endif /* __ASM_PTRACE_H */ diff --git a/arch/arc/include/asm/setup.h b/arch/arc/include/asm/setup.h index fc97411269fe..229e50681497 100644 --- a/arch/arc/include/asm/setup.h +++ b/arch/arc/include/asm/setup.h @@ -5,11 +5,12 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - #ifndef __ASMARC_SETUP_H #define __ASMARC_SETUP_H + #include +#include #define COMMAND_LINE_SIZE 256 diff --git a/arch/arc/include/uapi/asm/Kbuild b/arch/arc/include/uapi/asm/Kbuild new file mode 100644 index 000000000000..27362446c710 --- /dev/null +++ b/arch/arc/include/uapi/asm/Kbuild @@ -0,0 +1,11 @@ +# UAPI Header export list +include include/uapi/asm-generic/Kbuild.asm +header-y += page.h +header-y += setup.h +header-y += byteorder.h +header-y += cachectl.h +header-y += ptrace.h +header-y += sigcontext.h +header-y += signal.h +header-y += swab.h +header-y += unistd.h diff --git a/arch/arc/include/asm/byteorder.h b/arch/arc/include/uapi/asm/byteorder.h similarity index 100% rename from arch/arc/include/asm/byteorder.h rename to arch/arc/include/uapi/asm/byteorder.h diff --git a/arch/arc/include/asm/cachectl.h b/arch/arc/include/uapi/asm/cachectl.h similarity index 100% rename from arch/arc/include/asm/cachectl.h rename to arch/arc/include/uapi/asm/cachectl.h diff --git a/arch/arc/include/uapi/asm/page.h b/arch/arc/include/uapi/asm/page.h new file mode 100644 index 000000000000..e5d41e08240c --- /dev/null +++ b/arch/arc/include/uapi/asm/page.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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 _UAPI__ASM_ARC_PAGE_H +#define _UAPI__ASM_ARC_PAGE_H + +/* PAGE_SHIFT determines the page size */ +#if defined(CONFIG_ARC_PAGE_SIZE_16K) +#define PAGE_SHIFT 14 +#elif defined(CONFIG_ARC_PAGE_SIZE_4K) +#define PAGE_SHIFT 12 +#else +/* + * Default 8k + * done this way (instead of under CONFIG_ARC_PAGE_SIZE_8K) because adhoc + * user code (busybox appletlib.h) expects PAGE_SHIFT to be defined w/o + * using the correct uClibc header and in their build our autoconf.h is + * not available + */ +#define PAGE_SHIFT 13 +#endif + +#ifdef __ASSEMBLY__ +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_OFFSET (0x80000000) +#else +#define PAGE_SIZE (1UL << PAGE_SHIFT) /* Default 8K */ +#define PAGE_OFFSET (0x80000000UL) /* Kernel starts at 2G onwards */ +#endif + +#define PAGE_MASK (~(PAGE_SIZE-1)) + + +#endif /* _UAPI__ASM_ARC_PAGE_H */ diff --git a/arch/arc/include/uapi/asm/ptrace.h b/arch/arc/include/uapi/asm/ptrace.h new file mode 100644 index 000000000000..6afa4f702075 --- /dev/null +++ b/arch/arc/include/uapi/asm/ptrace.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * 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. + * + * Amit Bhor, Sameer Dhavale: Codito Technologies 2004 + */ + +#ifndef _UAPI__ASM_ARC_PTRACE_H +#define _UAPI__ASM_ARC_PTRACE_H + + +#ifndef __ASSEMBLY__ +/* + * Userspace ABI: Register state needed by + * -ptrace (gdbserver) + * -sigcontext (SA_SIGNINFO signal frame) + * + * This is to decouple pt_regs from user-space ABI, to be able to change it + * w/o affecting the ABI. + * Although the layout (initial padding) is similar to pt_regs to have some + * optimizations when copying pt_regs to/from user_regs_struct. + * + * Also, sigcontext only care about the scratch regs as that is what we really + * save/restore for signal handling. +*/ +struct user_regs_struct { + + struct scratch { + long pad; + long bta, lp_start, lp_end, lp_count; + long status32, ret, blink, fp, gp; + long r12, r11, r10, r9, r8, r7, r6, r5, r4, r3, r2, r1, r0; + long sp; + } scratch; + struct callee { + long pad; + long r25, r24, r23, r22, r21, r20; + long r19, r18, r17, r16, r15, r14, r13; + } callee; + long efa; /* break pt addr, for break points in delay slots */ + long stop_pc; /* give dbg stop_pc directly after checking orig_r8 */ +}; +#endif /* !__ASSEMBLY__ */ + +#endif /* _UAPI__ASM_ARC_PTRACE_H */ diff --git a/arch/arc/include/uapi/asm/setup.h b/arch/arc/include/uapi/asm/setup.h new file mode 100644 index 000000000000..a6d4e44938be --- /dev/null +++ b/arch/arc/include/uapi/asm/setup.h @@ -0,0 +1,6 @@ +/* + * setup.h is part of userspace header ABI so UAPI scripts have to generate it + * even if there's nothing to export - causing empty + * However to prevent "patch" from discarding it we add this placeholder + * comment + */ diff --git a/arch/arc/include/asm/sigcontext.h b/arch/arc/include/uapi/asm/sigcontext.h similarity index 100% rename from arch/arc/include/asm/sigcontext.h rename to arch/arc/include/uapi/asm/sigcontext.h diff --git a/arch/arc/include/asm/signal.h b/arch/arc/include/uapi/asm/signal.h similarity index 100% rename from arch/arc/include/asm/signal.h rename to arch/arc/include/uapi/asm/signal.h diff --git a/arch/arc/include/asm/swab.h b/arch/arc/include/uapi/asm/swab.h similarity index 100% rename from arch/arc/include/asm/swab.h rename to arch/arc/include/uapi/asm/swab.h diff --git a/arch/arc/include/asm/unistd.h b/arch/arc/include/uapi/asm/unistd.h similarity index 100% rename from arch/arc/include/asm/unistd.h rename to arch/arc/include/uapi/asm/unistd.h From 4368902bb90f0e208387f336c3fce0e6b2a110fc Mon Sep 17 00:00:00 2001 From: Gilad Ben-Yossef Date: Tue, 22 Jan 2013 16:48:45 +0530 Subject: [PATCH 2853/4607] ARC: Add support for ioremap_prot API Implement ioremap_prot() to allow mapping IO memory with variable protection via TLB. Implementing this allows the /dev/mem driver to use its generic access() VMA callback, which in turn allows ptrace to examine data in memory mapped regions mapped via /dev/mem, such as Arc DCCM. The end result is that it is possible to examine values of variables placed into DCCM in user space programs via GDB. CC: Alexey Brodkin CC: Noam Camus Acked-by: Vineet Gupta Signed-off-by: Gilad Ben-Yossef Signed-off-by: Vineet Gupta --- arch/arc/Kconfig | 1 + arch/arc/include/asm/io.h | 2 ++ arch/arc/include/asm/page.h | 3 +++ arch/arc/mm/ioremap.c | 48 +++++++++++++++++++++++++++---------- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index cd4ad61c4c14..2dab40dd8161 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -25,6 +25,7 @@ config ARC select HAVE_ARCH_KGDB select HAVE_ARCH_TRACEHOOK select HAVE_GENERIC_HARDIRQS + select HAVE_IOREMAP_PROT select HAVE_IRQ_WORK select HAVE_KPROBES select HAVE_KRETPROBES diff --git a/arch/arc/include/asm/io.h b/arch/arc/include/asm/io.h index d20051bbbf02..473424d7528b 100644 --- a/arch/arc/include/asm/io.h +++ b/arch/arc/include/asm/io.h @@ -16,6 +16,8 @@ #define PCI_IOBASE ((void __iomem *)0) extern void __iomem *ioremap(unsigned long physaddr, unsigned long size); +extern void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size, + unsigned long flags); extern void iounmap(const void __iomem *addr); #define ioremap_nocache(phy, sz) ioremap(phy, sz) diff --git a/arch/arc/include/asm/page.h b/arch/arc/include/asm/page.h index dfe1f8a95f1d..bdf546104551 100644 --- a/arch/arc/include/asm/page.h +++ b/arch/arc/include/asm/page.h @@ -48,6 +48,8 @@ typedef unsigned long pgtable_t; #define __pgd(x) ((pgd_t) { (x) }) #define __pgprot(x) ((pgprot_t) { (x) }) +#define pte_pgprot(x) __pgprot(pte_val(x)) + #else /* !STRICT_MM_TYPECHECKS */ typedef unsigned long pte_t; @@ -60,6 +62,7 @@ typedef unsigned long pgtable_t; #define pgprot_val(x) (x) #define __pte(x) (x) #define __pgprot(x) (x) +#define pte_pgprot(x) (x) #endif diff --git a/arch/arc/mm/ioremap.c b/arch/arc/mm/ioremap.c index 52518b6d0f28..3e5c92c79936 100644 --- a/arch/arc/mm/ioremap.c +++ b/arch/arc/mm/ioremap.c @@ -16,25 +16,49 @@ void __iomem *ioremap(unsigned long paddr, unsigned long size) { - unsigned long vaddr; - struct vm_struct *area; - unsigned long off, end; - const pgprot_t prot = PAGE_KERNEL_NO_CACHE; + unsigned long end; /* Don't allow wraparound or zero size */ end = paddr + size - 1; if (!size || (end < paddr)) return NULL; - /* If the region is h/w uncached, nothing special needed */ + /* If the region is h/w uncached, avoid MMU mappings */ if (paddr >= ARC_UNCACHED_ADDR_SPACE) return (void __iomem *)paddr; + return ioremap_prot(paddr, size, PAGE_KERNEL_NO_CACHE); +} +EXPORT_SYMBOL(ioremap); + +/* + * ioremap with access flags + * Cache semantics wise it is same as ioremap - "forced" uncached. + * However unline vanilla ioremap which bypasses ARC MMU for addresses in + * ARC hardware uncached region, this one still goes thru the MMU as caller + * might need finer access control (R/W/X) + */ +void __iomem *ioremap_prot(phys_addr_t paddr, unsigned long size, + unsigned long flags) +{ + void __iomem *vaddr; + struct vm_struct *area; + unsigned long off, end; + pgprot_t prot = __pgprot(flags); + + /* Don't allow wraparound, zero size */ + end = paddr + size - 1; + if ((!size) || (end < paddr)) + return NULL; + /* An early platform driver might end up here */ if (!slab_is_available()) return NULL; - /* Mappings have to be page-aligned, page-sized */ + /* force uncached */ + prot = pgprot_noncached(prot); + + /* Mappings have to be page-aligned */ off = paddr & ~PAGE_MASK; paddr &= PAGE_MASK; size = PAGE_ALIGN(end + 1) - paddr; @@ -45,17 +69,17 @@ void __iomem *ioremap(unsigned long paddr, unsigned long size) area = get_vm_area(size, VM_IOREMAP); if (!area) return NULL; - area->phys_addr = paddr; - vaddr = (unsigned long)area->addr; - if (ioremap_page_range(vaddr, vaddr + size, paddr, prot)) { - vfree(area->addr); + vaddr = (void __iomem *)area->addr; + if (ioremap_page_range((unsigned long)vaddr, + (unsigned long)vaddr + size, paddr, prot)) { + vunmap((void __force *)vaddr); return NULL; } - return (void __iomem *)(off + (char __iomem *)vaddr); } -EXPORT_SYMBOL(ioremap); +EXPORT_SYMBOL(ioremap_prot); + void iounmap(const void __iomem *addr) { From 53d98958f585517f362733a1dbdb69e2dc5153a3 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:25 +0530 Subject: [PATCH 2854/4607] ARC: [Review] Multi-platform image #1: Kconfig enablement This mini patchseries addresses the lack of multi-platform-image support in ARC port. Older build system only supported one platform(soc) to build at a time and further only one board of that platform could be built. There was no technical reason for that - we just didn't have the need. So the first step towards multi-platform (and multi-board) builds it to allow build system to do that. So as applicable, => Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Acked-by: Arnd Bergmann --- arch/arc/Kconfig | 6 ++---- arch/arc/plat-arcfpga/Kconfig | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 2dab40dd8161..3fdd6a53e5d6 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -82,9 +82,7 @@ source "kernel/Kconfig.freezer" menu "ARC Architecture Configuration" -choice - prompt "ARC Platform" - default ARC_PLAT_FPGA_LEGACY +menu "ARC Platform/SoC" config ARC_PLAT_FPGA_LEGACY bool "\"Legacy\" ARC FPGA dev platform" @@ -96,7 +94,7 @@ config ARC_PLAT_FPGA_LEGACY - MetaWare ISS #New platform adds here -endchoice +endmenu menu "ARC CPU Configuration" diff --git a/arch/arc/plat-arcfpga/Kconfig b/arch/arc/plat-arcfpga/Kconfig index 9912d9c107a0..ae2c017151fa 100644 --- a/arch/arc/plat-arcfpga/Kconfig +++ b/arch/arc/plat-arcfpga/Kconfig @@ -8,8 +8,7 @@ if ARC_PLAT_FPGA_LEGACY -choice - prompt "FPGA Board" +menu "FPGA Board" config ARC_BOARD_ANGEL4 bool "ARC Angel4" @@ -35,7 +34,7 @@ config ISS_SMP_EXTN -XTL (To enable CPU start/stop/set-PC for another CPU) It doesn't provide coherent Caches and/or Atomic Ops (LLOCK/SCOND) -endchoice +endmenu config ARC_SERIAL_BAUD int "UART Baud rate" From 93ad700de2abc111c50bb961c150a9968d5b3982 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Tue, 22 Jan 2013 16:51:50 +0530 Subject: [PATCH 2855/4607] ARC: Fold boards sub-menu into platform/SoC menu This is more natural and is now doable since the choice constructs are gone. Signed-off-by: Vineet Gupta Acked-by: Arnd Bergmann --- arch/arc/Kconfig | 21 +++------------------ arch/arc/plat-arcfpga/Kconfig | 16 +++++++++++----- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 3fdd6a53e5d6..ac173687c056 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -82,18 +82,11 @@ source "kernel/Kconfig.freezer" menu "ARC Architecture Configuration" -menu "ARC Platform/SoC" - -config ARC_PLAT_FPGA_LEGACY - bool "\"Legacy\" ARC FPGA dev platform" - help - Support for ARC development platforms, provided by Synopsys. - These are based on FPGA or ISS. e.g. - - ARCAngel4 - - ML509 - - MetaWare ISS +menu "ARC Platform/SoC/Board" +source "arch/arc/plat-arcfpga/Kconfig" #New platform adds here + endmenu menu "ARC CPU Configuration" @@ -338,12 +331,6 @@ config ARC_HAS_RTSC endmenu # "ARC CPU Configuration" -menu "Platform Board Configuration" - -source "arch/arc/plat-arcfpga/Kconfig" - -#New platform adds here - config LINUX_LINK_BASE hex "Linux Link Address" default "0x80000000" @@ -357,8 +344,6 @@ config LINUX_LINK_BASE Linux needs to be scooted a bit. If you don't know what the above means, leave this setting alone. -endmenu # "Platform Board Configuration" - config ARC_CURR_IN_REG bool "Dedicate Register r25 for current_task pointer" default y diff --git a/arch/arc/plat-arcfpga/Kconfig b/arch/arc/plat-arcfpga/Kconfig index ae2c017151fa..b41e786cdbc0 100644 --- a/arch/arc/plat-arcfpga/Kconfig +++ b/arch/arc/plat-arcfpga/Kconfig @@ -6,13 +6,21 @@ # published by the Free Software Foundation. # -if ARC_PLAT_FPGA_LEGACY +menuconfig ARC_PLAT_FPGA_LEGACY + bool "\"Legacy\" ARC FPGA dev Boards" + select ISS_SMP_EXTN if SMP + help + Support for ARC development boards, provided by Synopsys. + These are based on FPGA or ISS. e.g. + - ARCAngel4 + - ML509 + - MetaWare ISS -menu "FPGA Board" +if ARC_PLAT_FPGA_LEGACY config ARC_BOARD_ANGEL4 bool "ARC Angel4" - select ISS_SMP_EXTN if SMP + default y help ARC Angel4 FPGA Ref Platform (Xilinx Virtex Based) @@ -34,8 +42,6 @@ config ISS_SMP_EXTN -XTL (To enable CPU start/stop/set-PC for another CPU) It doesn't provide coherent Caches and/or Atomic Ops (LLOCK/SCOND) -endmenu - config ARC_SERIAL_BAUD int "UART Baud rate" default "115200" From 03a6d28cdddfbd11b338c23e7fe51d0816b9bdef Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:26 +0530 Subject: [PATCH 2856/4607] ARC: [Review] Multi-platform image #2: Board callback Infrastructure The orig platform code orgnaization was singleton design pattern - only one platform (and board thereof) would build at a time. Thus any platform/board specific code (e.g. irq init, early init ...) expected by ARC common code was exported as well defined set of APIs, with only ONE instance building ever. Now with multiple-platform build requirement, that design of code no longer holds - multiple board specific calls need to build at the same time - so ARC common code can't use the API approach, it needs a callback based design where each board registers it's specific set of functions, and at runtime, depending on board detection, the callbacks are used from the registry. This commit adds all the infrastructure, where board specific callbacks are specified as a "maThine description". All the hooks are placed in right spots, no board callbacks registered yet (with MACHINE_STARt/END constructs) so the hooks will not run. Next commit will actually convert the platform to this infrastructure. Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Acked-by: Arnd Bergmann --- arch/arc/include/asm/mach_desc.h | 85 ++++++++++++++++++++++++++++++++ arch/arc/include/asm/prom.h | 1 - arch/arc/kernel/devtree.c | 47 +++++++++++++++--- arch/arc/kernel/irq.c | 7 +++ arch/arc/kernel/setup.c | 28 +++++++++-- arch/arc/kernel/smp.c | 3 ++ arch/arc/kernel/time.c | 4 ++ arch/arc/kernel/vmlinux.lds.S | 6 +++ 8 files changed, 169 insertions(+), 12 deletions(-) create mode 100644 arch/arc/include/asm/mach_desc.h diff --git a/arch/arc/include/asm/mach_desc.h b/arch/arc/include/asm/mach_desc.h new file mode 100644 index 000000000000..eaebaf835f85 --- /dev/null +++ b/arch/arc/include/asm/mach_desc.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * based on METAG mach/arch.h (which in turn was based on ARM) + * + * 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_ARC_MACH_DESC_H_ +#define _ASM_ARC_MACH_DESC_H_ + +/** + * struct machine_desc - Board specific callbacks, called from ARC common code + * Provided by each ARC board using MACHINE_START()/MACHINE_END(), so + * a multi-platform kernel builds with array of such descriptors. + * We extend the early DT scan to also match the DT's "compatible" string + * against the @dt_compat of all such descriptors, and one with highest + * "DT score" is selected as global @machine_desc. + * + * @name: Board/SoC name + * @dt_compat: Array of device tree 'compatible' strings + * (XXX: although only 1st entry is looked at) + * @init_early: Very early callback [called from setup_arch()] + * @init_irq: setup external IRQ controllers [called from init_IRQ()] + * @init_smp: for each CPU (e.g. setup IPI) + * [(M):init_IRQ(), (o):start_kernel_secondary()] + * @init_time: platform specific clocksource/clockevent registration + * [called from time_init()] + * @init_machine: arch initcall level callback (e.g. populate static + * platform devices or parse Devicetree) + * @init_late: Late initcall level callback + * + */ +struct machine_desc { + const char *name; + const char **dt_compat; + + void (*init_early)(void); + void (*init_irq)(void); +#ifdef CONFIG_SMP + void (*init_smp)(unsigned int); +#endif + void (*init_time)(void); + void (*init_machine)(void); + void (*init_late)(void); + +}; + +/* + * Current machine - only accessible during boot. + */ +extern struct machine_desc *machine_desc; + +/* + * Machine type table - also only accessible during boot + */ +extern struct machine_desc __arch_info_begin[], __arch_info_end[]; +#define for_each_machine_desc(p) \ + for (p = __arch_info_begin; p < __arch_info_end; p++) + +static inline struct machine_desc *default_machine_desc(void) +{ + /* the default machine is the last one linked in */ + if (__arch_info_end - 1 < __arch_info_begin) + return NULL; + return __arch_info_end - 1; +} + +/* + * Set of macros to define architecture features. + * This is built into a table by the linker. + */ +#define MACHINE_START(_type, _name) \ +static const struct machine_desc __mach_desc_##_type \ +__used \ +__attribute__((__section__(".arch.info.init"))) = { \ + .name = _name, + +#define MACHINE_END \ +}; + +extern struct machine_desc *setup_machine_fdt(void *dt); +#endif diff --git a/arch/arc/include/asm/prom.h b/arch/arc/include/asm/prom.h index f54489bc4eca..692d0d0789a7 100644 --- a/arch/arc/include/asm/prom.h +++ b/arch/arc/include/asm/prom.h @@ -10,6 +10,5 @@ #define _ASM_ARC_PROM_H_ #define HAVE_ARCH_DEVTREE_FIXUPS -extern int __init setup_machine_fdt(void *dt); #endif diff --git a/arch/arc/kernel/devtree.c b/arch/arc/kernel/devtree.c index c8166dc02c38..a7d98b30358b 100644 --- a/arch/arc/kernel/devtree.c +++ b/arch/arc/kernel/devtree.c @@ -16,6 +16,7 @@ #include #include #include +#include /* called from unflatten_device_tree() to bootstrap devicetree itself */ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) @@ -30,27 +31,57 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) * If a dtb was passed to the kernel, then use it to choose the correct * machine_desc and to setup the system. */ -int __init setup_machine_fdt(void *dt) +struct machine_desc * __init setup_machine_fdt(void *dt) { struct boot_param_header *devtree = dt; + struct machine_desc *mdesc = NULL, *mdesc_best = NULL; + unsigned int score, mdesc_score = ~1; unsigned long dt_root; - char *model, *compat; + const char *model, *compat; void *clk; char manufacturer[16]; unsigned long len; /* check device tree validity */ if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) - return 1; + return NULL; - /* Search the mdescs for the 'best' compatible value match */ initial_boot_params = devtree; dt_root = of_get_flat_dt_root(); + /* + * The kernel could be multi-platform enabled, thus could have many + * "baked-in" machine descriptors. Search thru all for the best + * "compatible" string match. + */ + for_each_machine_desc(mdesc) { + score = of_flat_dt_match(dt_root, mdesc->dt_compat); + if (score > 0 && score < mdesc_score) { + mdesc_best = mdesc; + mdesc_score = score; + } + } + if (!mdesc_best) { + const char *prop; + long size; + + pr_err("\n unrecognized device tree list:\n[ "); + + prop = of_get_flat_dt_prop(dt_root, "compatible", &size); + if (prop) { + while (size > 0) { + printk("'%s' ", prop); + size -= strlen(prop) + 1; + prop += strlen(prop) + 1; + } + } + printk("]\n\n"); + + machine_halt(); + } + /* compat = "," */ - compat = of_get_flat_dt_prop(dt_root, "compatible", NULL); - if (!compat) - compat = ""; + compat = mdesc_best->dt_compat[0]; model = strchr(compat, ','); if (model) @@ -73,5 +104,5 @@ int __init setup_machine_fdt(void *dt) if (clk) arc_set_core_freq(of_read_ulong(clk, len/4)); - return 0; + return mdesc_best; } diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c index df7da2b5a5bd..1198168850e8 100644 --- a/arch/arc/kernel/irq.c +++ b/arch/arc/kernel/irq.c @@ -13,6 +13,7 @@ #include #include #include +#include /* * Early Hardware specific Interrupt setup @@ -125,9 +126,15 @@ void __init init_IRQ(void) init_onchip_IRQ(); plat_init_IRQ(); + /* Any external intc can be setup here */ + if (machine_desc->init_irq) + machine_desc->init_irq(); + #ifdef CONFIG_SMP /* Master CPU can initialize it's side of IPI */ arc_platform_smp_init_cpu(); + if (machine_desc->init_smp) + machine_desc->init_smp(smp_processor_id()); #endif } diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 6cc361c6751a..20273b89e545 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -25,12 +25,14 @@ #include #include #include +#include #define FIX_PTR(x) __asm__ __volatile__(";" : "+r"(x)) int running_on_hw = 1; /* vs. on ISS */ char __initdata command_line[COMMAND_LINE_SIZE]; +struct machine_desc *machine_desc __initdata; struct task_struct *_current_task[NR_CPUS]; /* For stack switching */ @@ -323,8 +325,6 @@ void __init __attribute__((weak)) arc_platform_early_init(void) void __init setup_arch(char **cmdline_p) { - int rc; - #ifdef CONFIG_CMDLINE_UBOOT /* Make sure that a whitespace is inserted before */ strlcat(command_line, " ", sizeof(command_line)); @@ -339,13 +339,17 @@ void __init setup_arch(char **cmdline_p) strlcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); *cmdline_p = command_line; - rc = setup_machine_fdt(__dtb_start); + machine_desc = setup_machine_fdt(__dtb_start); + if (!machine_desc) + panic("Embedded DT invalid\n"); /* To force early parsing of things like mem=xxx */ parse_early_param(); /* Platform/board specific: e.g. early console registration */ arc_platform_early_init(); + if (machine_desc->init_early) + machine_desc->init_early(); setup_processor(); @@ -372,6 +376,24 @@ void __init setup_arch(char **cmdline_p) arc_unwind_setup(); } +static int __init customize_machine(void) +{ + /* Add platform devices */ + if (machine_desc->init_machine) + machine_desc->init_machine(); + + return 0; +} +arch_initcall(customize_machine); + +static int __init init_late_machine(void) +{ + if (machine_desc->init_late) + machine_desc->init_late(); + + return 0; +} +late_initcall(init_late_machine); /* * Get CPU information for use by the procfs. */ diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index 1f762ad6969b..ea15f073452f 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c @@ -32,6 +32,7 @@ #include #include #include +#include arch_spinlock_t smp_atomic_ops_lock = __ARCH_SPIN_LOCK_UNLOCKED; arch_spinlock_t smp_bitops_lock = __ARCH_SPIN_LOCK_UNLOCKED; @@ -127,6 +128,8 @@ void __cpuinit start_kernel_secondary(void) pr_info("## CPU%u LIVE ##: Executing Code...\n", cpu); arc_platform_smp_init_cpu(); + if (machine_desc->init_smp) + machine_desc->init_smp(smp_processor_id()); arc_local_timer_setup(cpu); diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c index 05dba11fdb2d..0ce0e6f76eb0 100644 --- a/arch/arc/kernel/time.c +++ b/arch/arc/kernel/time.c @@ -43,6 +43,7 @@ #include #include #include +#include #define ARC_TIMER_MAX 0xFFFFFFFF @@ -258,6 +259,9 @@ void __init time_init(void) /* sets up the periodic event timer */ arc_local_timer_setup(smp_processor_id()); + + if (machine_desc->init_time) + machine_desc->init_time(); } #ifdef CONFIG_ARC_HAS_RTSC diff --git a/arch/arc/kernel/vmlinux.lds.S b/arch/arc/kernel/vmlinux.lds.S index 8d3b0d447498..622d8b665a68 100644 --- a/arch/arc/kernel/vmlinux.lds.S +++ b/arch/arc/kernel/vmlinux.lds.S @@ -75,6 +75,12 @@ SECTIONS SECURITY_INITCALL } + .init.arch.info : { + __arch_info_begin = .; + *(.arch.info.init) + __arch_info_end = .; + } + PERCPU_SECTION(L1_CACHE_BYTES) /* From 877768c84d6ca8f7dedafff0e44615a12e82f8f4 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Wed, 23 Jan 2013 16:32:48 +0530 Subject: [PATCH 2857/4607] ARC: [Review] Multi-platform image #3: switch to board callback -platform API is retired and instead callbacks are used Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Acked-by: Arnd Bergmann --- arch/arc/include/asm/irq.h | 1 - arch/arc/include/asm/smp.h | 5 --- arch/arc/kernel/irq.c | 2 - arch/arc/kernel/setup.c | 5 --- arch/arc/kernel/smp.c | 1 - arch/arc/plat-arcfpga/include/plat/irq.h | 2 + arch/arc/plat-arcfpga/include/plat/smp.h | 2 + arch/arc/plat-arcfpga/irq.c | 2 +- arch/arc/plat-arcfpga/platform.c | 50 ++++++++++++++++++++---- arch/arc/plat-arcfpga/smp.c | 4 +- 10 files changed, 48 insertions(+), 26 deletions(-) diff --git a/arch/arc/include/asm/irq.h b/arch/arc/include/asm/irq.h index c91b2e4344a1..f1b318d92877 100644 --- a/arch/arc/include/asm/irq.h +++ b/arch/arc/include/asm/irq.h @@ -17,7 +17,6 @@ #include extern void __init arc_init_IRQ(void); -extern void __init plat_init_IRQ(void); extern int __init get_hw_config_num_irq(void); void __cpuinit arc_local_timer_setup(unsigned int cpu); diff --git a/arch/arc/include/asm/smp.h b/arch/arc/include/asm/smp.h index f91f1946272f..0cff3a548136 100644 --- a/arch/arc/include/asm/smp.h +++ b/arch/arc/include/asm/smp.h @@ -46,10 +46,6 @@ extern int smp_ipi_irq_setup(int cpu, int irq); * arc_platform_smp_cpuinfo: * returns a string containing info for /proc/cpuinfo * - * arc_platform_smp_init_cpu: - * Called from start_kernel_secondary to do any CPU local setup - * such as starting a timer, setting up IPI etc - * * arc_platform_smp_wait_to_boot: * Called from early bootup code for non-Master CPUs to "park" them * @@ -64,7 +60,6 @@ extern int smp_ipi_irq_setup(int cpu, int irq); * Takes @cpu which got IPI at @irq to do any IPI clearing */ extern const char *arc_platform_smp_cpuinfo(void); -extern void arc_platform_smp_init_cpu(void); extern void arc_platform_smp_wait_to_boot(int cpu); extern void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc); extern void arc_platform_ipi_send(const struct cpumask *callmap); diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c index 1198168850e8..551c10dff481 100644 --- a/arch/arc/kernel/irq.c +++ b/arch/arc/kernel/irq.c @@ -124,7 +124,6 @@ void __init init_onchip_IRQ(void) void __init init_IRQ(void) { init_onchip_IRQ(); - plat_init_IRQ(); /* Any external intc can be setup here */ if (machine_desc->init_irq) @@ -132,7 +131,6 @@ void __init init_IRQ(void) #ifdef CONFIG_SMP /* Master CPU can initialize it's side of IPI */ - arc_platform_smp_init_cpu(); if (machine_desc->init_smp) machine_desc->init_smp(smp_processor_id()); #endif diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 20273b89e545..e591c6ae88a6 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -319,10 +319,6 @@ void __init setup_processor(void) arc_chk_fpu(); } -void __init __attribute__((weak)) arc_platform_early_init(void) -{ -} - void __init setup_arch(char **cmdline_p) { #ifdef CONFIG_CMDLINE_UBOOT @@ -347,7 +343,6 @@ void __init setup_arch(char **cmdline_p) parse_early_param(); /* Platform/board specific: e.g. early console registration */ - arc_platform_early_init(); if (machine_desc->init_early) machine_desc->init_early(); diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index ea15f073452f..8ee010fdbc18 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c @@ -127,7 +127,6 @@ void __cpuinit start_kernel_secondary(void) pr_info("## CPU%u LIVE ##: Executing Code...\n", cpu); - arc_platform_smp_init_cpu(); if (machine_desc->init_smp) machine_desc->init_smp(smp_processor_id()); diff --git a/arch/arc/plat-arcfpga/include/plat/irq.h b/arch/arc/plat-arcfpga/include/plat/irq.h index 255b90e973ee..6515df232f21 100644 --- a/arch/arc/plat-arcfpga/include/plat/irq.h +++ b/arch/arc/plat-arcfpga/include/plat/irq.h @@ -32,4 +32,6 @@ #define IDU_INTERRUPT_0 16 #endif +extern void __init plat_fpga_init_IRQ(void); + #endif diff --git a/arch/arc/plat-arcfpga/include/plat/smp.h b/arch/arc/plat-arcfpga/include/plat/smp.h index 8c5e46c01b15..27822ac8155e 100644 --- a/arch/arc/plat-arcfpga/include/plat/smp.h +++ b/arch/arc/plat-arcfpga/include/plat/smp.h @@ -110,6 +110,8 @@ struct idu_irq_status { extern void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask); extern void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode); +extern void iss_model_init_smp(unsigned int cpu); + #endif /* CONFIG_SMP */ #endif diff --git a/arch/arc/plat-arcfpga/irq.c b/arch/arc/plat-arcfpga/irq.c index 590edd174c47..0ea43c26f16a 100644 --- a/arch/arc/plat-arcfpga/irq.c +++ b/arch/arc/plat-arcfpga/irq.c @@ -11,7 +11,7 @@ #include #include -void __init plat_init_IRQ(void) +void __init plat_fpga_init_IRQ(void) { /* * SMP Hack because UART IRQ hardwired to cpu0 (boot-cpu) but if the diff --git a/arch/arc/plat-arcfpga/platform.c b/arch/arc/plat-arcfpga/platform.c index b7f63e3f3cae..ac85d6927334 100644 --- a/arch/arc/plat-arcfpga/platform.c +++ b/arch/arc/plat-arcfpga/platform.c @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include /*-----------------------BVCI Latency Unit -----------------------------*/ @@ -153,10 +155,7 @@ static void arc_fpga_serial_init(void) #endif } -/* - * Early Platform Initialization called from setup_arch() - */ -void __init arc_platform_early_init(void) +static void __init plat_fpga_early_init(void) { pr_info("[plat-arcfpga]: registering early dev resources\n"); @@ -172,7 +171,7 @@ static struct of_dev_auxdata plat_auxdata_lookup[] __initdata = { {} }; -int __init fpga_plat_init(void) +static void __init plat_fpga_populate_dev(void) { pr_info("[plat-arcfpga]: registering device resources\n"); @@ -182,7 +181,42 @@ int __init fpga_plat_init(void) */ of_platform_populate(NULL, of_default_bus_match_table, plat_auxdata_lookup, NULL); - - return 0; } -arch_initcall(fpga_plat_init); + +/*----------------------- Machine Descriptions ------------------------------ + * + * Machine description is simply a set of platform/board specific callbacks + * This is not directly related to DeviceTree based dynamic device creation, + * however as part of early device tree scan, we also select the right + * callback set, by matching the DT compatible name. + */ + +static const char *aa4_compat[] __initdata = { + "snps,arc-angel4", + NULL, +}; + +MACHINE_START(ANGEL4, "angel4") + .dt_compat = aa4_compat, + .init_early = plat_fpga_early_init, + .init_machine = plat_fpga_populate_dev, + .init_irq = plat_fpga_init_IRQ, +#ifdef CONFIG_SMP + .init_smp = iss_model_init_smp, +#endif +MACHINE_END + +static const char *ml509_compat[] __initdata = { + "snps,arc-ml509", + NULL, +}; + +MACHINE_START(ML509, "ml509") + .dt_compat = ml509_compat, + .init_early = plat_fpga_early_init, + .init_machine = plat_fpga_populate_dev, + .init_irq = plat_fpga_init_IRQ, +#ifdef CONFIG_SMP + .init_smp = iss_model_init_smp, +#endif +MACHINE_END diff --git a/arch/arc/plat-arcfpga/smp.c b/arch/arc/plat-arcfpga/smp.c index a95fcdb29033..fec18793bbe0 100644 --- a/arch/arc/plat-arcfpga/smp.c +++ b/arch/arc/plat-arcfpga/smp.c @@ -63,10 +63,8 @@ void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc) * -Master : init_IRQ() * -Other(s) : start_kernel_secondary() */ -void arc_platform_smp_init_cpu(void) +void iss_model_init_smp(unsigned int cpu) { - int cpu = smp_processor_id(); - /* Check if CPU is configured for more than 16 interrupts */ if (NR_IRQS <= 16 || get_hw_config_num_irq() <= 16) panic("[arcfpga] IRQ system can't support IDU IPI\n"); From e97ff121ae61ba80e41b2cc3bd6dfe9886d22505 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:26 +0530 Subject: [PATCH 2858/4607] ARC: [Review] Multi-platform image #4: Isolate platform headers -Top level ARC makefile removes -I for platform headers -asm/irq.h no longer includes plat/irq.h -platform makefile adds -I for it's specfic platform headers -platform code to directly include it's plat/irq.h -Linker script needed plat/memmap.h for CCM info, already in .config Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Acked-by: Arnd Bergmann --- arch/arc/Makefile | 16 +--------------- arch/arc/include/asm/irq.h | 1 - arch/arc/kernel/vmlinux.lds.S | 1 - arch/arc/plat-arcfpga/Makefile | 2 ++ arch/arc/plat-arcfpga/irq.c | 2 +- arch/arc/plat-arcfpga/platform.c | 2 +- arch/arc/plat-arcfpga/smp.c | 3 ++- 7 files changed, 7 insertions(+), 20 deletions(-) diff --git a/arch/arc/Makefile b/arch/arc/Makefile index 9a36c04e4306..92379c7cbc1a 100644 --- a/arch/arc/Makefile +++ b/arch/arc/Makefile @@ -10,14 +10,6 @@ UTS_MACHINE := arc KBUILD_DEFCONFIG := fpga_defconfig -# For ARC FPGA Platforms -platform-$(CONFIG_ARC_PLAT_FPGA_LEGACY) := arcfpga -#New platform adds here - -PLATFORM := $(platform-y) -export PLATFORM - -cflags-y += -Iarch/arc/plat-$(PLATFORM)/include cflags-y += -mA7 -fno-common -pipe -fno-builtin -D__linux__ LINUXINCLUDE += -include ${src}/arch/arc/include/asm/defines.h @@ -86,9 +78,6 @@ KBUILD_CFLAGS += $(cflags-y) KBUILD_AFLAGS += $(KBUILD_CFLAGS) LDFLAGS += $(ldflags-y) -# Needed for Linker script preprocessing -KBUILD_CPPFLAGS += -Iarch/arc/plat-$(PLATFORM)/include - head-y := arch/arc/kernel/head.o # See arch/arc/Kbuild for content of core part of the kernel @@ -97,10 +86,7 @@ core-y += arch/arc/ # w/o this dtb won't embed into kernel binary core-y += arch/arc/boot/dts/ -# w/o this ifneq, make ARCH=arc clean was crapping out -ifneq ($(platform-y),) -core-y += arch/arc/plat-$(PLATFORM)/ -endif +core-$(CONFIG_ARC_PLAT_FPGA_LEGACY) += arch/arc/plat-arcfpga/ drivers-$(CONFIG_OPROFILE) += arch/arc/oprofile/ diff --git a/arch/arc/include/asm/irq.h b/arch/arc/include/asm/irq.h index f1b318d92877..20aaab8971ad 100644 --- a/arch/arc/include/asm/irq.h +++ b/arch/arc/include/asm/irq.h @@ -13,7 +13,6 @@ #define TIMER0_IRQ 3 #define TIMER1_IRQ 4 -#include /* Board Specific IRQ assignments */ #include extern void __init arc_init_IRQ(void); diff --git a/arch/arc/kernel/vmlinux.lds.S b/arch/arc/kernel/vmlinux.lds.S index 622d8b665a68..d3c92f52d444 100644 --- a/arch/arc/kernel/vmlinux.lds.S +++ b/arch/arc/kernel/vmlinux.lds.S @@ -10,7 +10,6 @@ #include #include #include -#include OUTPUT_ARCH(arc) ENTRY(_stext) diff --git a/arch/arc/plat-arcfpga/Makefile b/arch/arc/plat-arcfpga/Makefile index 2a828bec8212..a44e22ebc1b7 100644 --- a/arch/arc/plat-arcfpga/Makefile +++ b/arch/arc/plat-arcfpga/Makefile @@ -6,5 +6,7 @@ # published by the Free Software Foundation. # +KBUILD_CFLAGS += -Iarch/arc/plat-arcfpga/include + obj-y := platform.o irq.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/arc/plat-arcfpga/irq.c b/arch/arc/plat-arcfpga/irq.c index 0ea43c26f16a..d2215fd889c2 100644 --- a/arch/arc/plat-arcfpga/irq.c +++ b/arch/arc/plat-arcfpga/irq.c @@ -9,7 +9,7 @@ */ #include -#include +#include void __init plat_fpga_init_IRQ(void) { diff --git a/arch/arc/plat-arcfpga/platform.c b/arch/arc/plat-arcfpga/platform.c index ac85d6927334..4024f10a39ca 100644 --- a/arch/arc/plat-arcfpga/platform.c +++ b/arch/arc/plat-arcfpga/platform.c @@ -16,11 +16,11 @@ #include #include #include -#include #include #include #include #include +#include /*-----------------------BVCI Latency Unit -----------------------------*/ diff --git a/arch/arc/plat-arcfpga/smp.c b/arch/arc/plat-arcfpga/smp.c index fec18793bbe0..68a53b153d03 100644 --- a/arch/arc/plat-arcfpga/smp.c +++ b/arch/arc/plat-arcfpga/smp.c @@ -12,7 +12,8 @@ */ #include -#include +#include +#include #include static char smp_cpuinfo_buf[128]; From decae9d3e87b5454b3b190d8e00b063175a3a091 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:26 +0530 Subject: [PATCH 2859/4607] ARC: [Review] Multi-platform image #5: NR_IRQS defined by ARC core For now this will suffice for all platforms, later exotic ones needs to get this from DeviceTree Signed-off-by: Vineet Gupta Cc: Arnd Bergmann --- arch/arc/include/asm/irq.h | 2 ++ arch/arc/plat-arcfpga/include/plat/irq.h | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/arc/include/asm/irq.h b/arch/arc/include/asm/irq.h index 20aaab8971ad..4c588f9820cf 100644 --- a/arch/arc/include/asm/irq.h +++ b/arch/arc/include/asm/irq.h @@ -9,6 +9,8 @@ #ifndef __ASM_ARC_IRQ_H #define __ASM_ARC_IRQ_H +#define NR_IRQS 32 + /* Platform Independent IRQs */ #define TIMER0_IRQ 3 #define TIMER1_IRQ 4 diff --git a/arch/arc/plat-arcfpga/include/plat/irq.h b/arch/arc/plat-arcfpga/include/plat/irq.h index 6515df232f21..41e335670f60 100644 --- a/arch/arc/plat-arcfpga/include/plat/irq.h +++ b/arch/arc/plat-arcfpga/include/plat/irq.h @@ -12,12 +12,6 @@ #ifndef __PLAT_IRQ_H #define __PLAT_IRQ_H -#ifdef CONFIG_SMP -#define NR_IRQS 32 -#else -#define NR_IRQS 16 -#endif - #define UART0_IRQ 5 #define UART1_IRQ 10 #define UART2_IRQ 11 From fc7943d29e9f6f5f6d4b111120b66ec86501673e Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Tue, 22 Jan 2013 16:53:57 +0530 Subject: [PATCH 2860/4607] ARC: [Review] Multi-platform image #6: cpu-to-dma-addr optional All the current platforms can work with 0x8000_0000 based dma_addr_t since the Bus Bridges typically ignore the top bit (the only excpetion was Angel4 PCI-AHB bridge which we no longer care for). That way we don't need plat-specific cpu-addr to bus-addr conversion. Hooks still provided - just in case a platform has an obscure device which say needs 0 based bus address. That way no longer needs to unconditinally include Also verfied that on Angel4 board, other peripherals (IDE-disk / EMAC) work fine with 0x8000_0000 based dma addresses. Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Acked-by: Arnd Bergmann --- arch/arc/Kconfig | 4 ++ arch/arc/include/asm/dma-mapping.h | 16 +++++++ arch/arc/plat-arcfpga/include/plat/dma_addr.h | 45 ------------------- 3 files changed, 20 insertions(+), 45 deletions(-) delete mode 100644 arch/arc/plat-arcfpga/include/plat/dma_addr.h diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index ac173687c056..be85ceb8b264 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -119,6 +119,10 @@ config CPU_BIG_ENDIAN help Build kernel for Big Endian Mode of ARC CPU +# If a platform can't work with 0x8000_0000 based dma_addr_t +config ARC_PLAT_NEEDS_CPU_TO_DMA + bool + config SMP bool "Symmetric Multi-Processing (Incomplete)" default n diff --git a/arch/arc/include/asm/dma-mapping.h b/arch/arc/include/asm/dma-mapping.h index 7fd150e97eb2..31f77aec0823 100644 --- a/arch/arc/include/asm/dma-mapping.h +++ b/arch/arc/include/asm/dma-mapping.h @@ -13,7 +13,23 @@ #include #include + +#ifndef CONFIG_ARC_PLAT_NEEDS_CPU_TO_DMA +/* + * dma_map_* API take cpu addresses, which is kernel logical address in the + * untranslated address space (0x8000_0000) based. The dma address (bus addr) + * ideally needs to be 0x0000_0000 based hence these glue routines. + * However given that intermediate bus bridges can ignore the high bit, we can + * do with these routines being no-ops. + * If a platform/device comes up which sriclty requires 0 based bus addr + * (e.g. AHB-PCI bridge on Angel4 board), then it can provide it's own versions + */ +#define plat_dma_addr_to_kernel(dev, addr) ((unsigned long)(addr)) +#define plat_kernel_addr_to_dma(dev, ptr) ((dma_addr_t)(ptr)) + +#else #include +#endif void *dma_alloc_noncoherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t gfp); diff --git a/arch/arc/plat-arcfpga/include/plat/dma_addr.h b/arch/arc/plat-arcfpga/include/plat/dma_addr.h deleted file mode 100644 index 0e963431b729..000000000000 --- a/arch/arc/plat-arcfpga/include/plat/dma_addr.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) - * - * 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. - * - * vineetg: Feb 2009 - * -For AA4 board, kernel to DMA address APIs - */ - -/* - * kernel addresses are 0x800_000 based, while Bus addr are 0 based - */ - -#ifndef __PLAT_DMA_ADDR_H -#define __PLAT_DMA_ADDR_H - -#include - -static inline unsigned long plat_dma_addr_to_kernel(struct device *dev, - dma_addr_t dma_addr) -{ - return dma_addr + PAGE_OFFSET; -} - -static inline dma_addr_t plat_kernel_addr_to_dma(struct device *dev, void *ptr) -{ - unsigned long addr = (unsigned long)ptr; - /* - * To Catch buggy drivers which can call DMA map API with kernel vaddr - * i.e. for buffers alloc via vmalloc or ioremap which are not - * gaurnateed to be PHY contiguous and hence unfit for DMA anyways. - * On ARC kernel virtual address is 0x7000_0000 to 0x7FFF_FFFF, so - * ideally we want to check this range here, but our implementation is - * better as it checks for even worse user virtual address as well. - */ - if (likely(addr >= PAGE_OFFSET)) - return addr - PAGE_OFFSET; - - BUG(); - return addr; -} - -#endif From 10b1271875abb9d590c14fa6c8b24b0d6f768ca2 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:26 +0530 Subject: [PATCH 2861/4607] ARC: [Review] Multi-platform image #7: SMP common code to use callbacks This again is for switch from singleton platform SMP API to multi-platform paradigm Platform code is not yet setup to populate the callbacks, that happens in next commit Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Acked-by: Arnd Bergmann --- arch/arc/include/asm/smp.h | 36 +++++++++++++++--------------------- arch/arc/kernel/smp.c | 16 +++++++++++++--- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/arch/arc/include/asm/smp.h b/arch/arc/include/asm/smp.h index 0cff3a548136..c4fb211dcd25 100644 --- a/arch/arc/include/asm/smp.h +++ b/arch/arc/include/asm/smp.h @@ -31,6 +31,7 @@ extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); */ extern void __init smp_init_cpus(void); extern void __init first_lines_of_secondary(void); +extern const char *arc_platform_smp_cpuinfo(void); /* * API expected BY platform smp code (FROM arch smp code) @@ -41,29 +42,22 @@ extern void __init first_lines_of_secondary(void); extern int smp_ipi_irq_setup(int cpu, int irq); /* - * APIs expected FROM platform smp code + * struct plat_smp_ops - SMP callbacks provided by platform to ARC SMP * - * arc_platform_smp_cpuinfo: - * returns a string containing info for /proc/cpuinfo - * - * arc_platform_smp_wait_to_boot: - * Called from early bootup code for non-Master CPUs to "park" them - * - * arc_platform_smp_wakeup_cpu: - * Called from __cpu_up (Master CPU) to kick start another one - * - * arc_platform_ipi_send: - * Takes @cpumask to which IPI(s) would be sent. - * The actual msg-id/buffer is manager in arch-common code - * - * arc_platform_ipi_clear: - * Takes @cpu which got IPI at @irq to do any IPI clearing + * @info: SoC SMP specific info for /proc/cpuinfo etc + * @cpu_kick: For Master to kickstart a cpu (optionally at a PC) + * @ipi_send: To send IPI to a @cpumask + * @ips_clear: To clear IPI received by @cpu at @irq */ -extern const char *arc_platform_smp_cpuinfo(void); -extern void arc_platform_smp_wait_to_boot(int cpu); -extern void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc); -extern void arc_platform_ipi_send(const struct cpumask *callmap); -extern void arc_platform_ipi_clear(int cpu, int irq); +struct plat_smp_ops { + const char *info; + void (*cpu_kick)(int cpu, unsigned long pc); + void (*ipi_send)(void *callmap); + void (*ipi_clear)(int cpu, int irq); +}; + +/* TBD: stop exporting it for direct population by platform */ +extern struct plat_smp_ops plat_smp_ops; #endif /* CONFIG_SMP */ diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index 8ee010fdbc18..3af3e06dcf02 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c @@ -37,6 +37,8 @@ arch_spinlock_t smp_atomic_ops_lock = __ARCH_SPIN_LOCK_UNLOCKED; arch_spinlock_t smp_bitops_lock = __ARCH_SPIN_LOCK_UNLOCKED; +struct plat_smp_ops plat_smp_ops; + /* XXX: per cpu ? Only needed once in early seconday boot */ struct task_struct *secondary_idle_tsk; @@ -105,6 +107,11 @@ void __attribute__((weak)) arc_platform_smp_wait_to_boot(int cpu) " b 1b \n"); } +const char *arc_platform_smp_cpuinfo(void) +{ + return plat_smp_ops.info; +} + /* * The very first "C" code executed by secondary * Called from asm stub in head.S @@ -156,7 +163,8 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) pr_info("Idle Task [%d] %p", cpu, idle); pr_info("Trying to bring up CPU%u ...\n", cpu); - arc_platform_smp_wakeup_cpu(cpu, + if (plat_smp_ops.cpu_kick) + plat_smp_ops.cpu_kick(cpu, (unsigned long)first_lines_of_secondary); /* wait for 1 sec after kicking the secondary */ @@ -225,7 +233,8 @@ static void ipi_send_msg(const struct cpumask *callmap, enum ipi_msg_type msg) } /* Call the platform specific cross-CPU call function */ - arc_platform_ipi_send(callmap); + if (plat_smp_ops.ipi_send) + plat_smp_ops.ipi_send((void *)callmap); local_irq_restore(flags); } @@ -299,7 +308,8 @@ irqreturn_t do_IPI(int irq, void *dev_id) struct ipi_data *ipi = &per_cpu(ipi_data, cpu); unsigned long ops; - arc_platform_ipi_clear(cpu, irq); + if (plat_smp_ops.ipi_clear) + plat_smp_ops.ipi_clear(cpu, irq); /* * XXX: is this loop really needed From b830cde5a486d1157105fe928dfa0ddcb9f1b840 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:26 +0530 Subject: [PATCH 2862/4607] ARC: [Review] Multi-platform image #8: platform registers SMP callbacks Platforms export their SMP callbacks by populating arc_smp_ops. The population itself needs to be done pretty early, from init_early callback. Signed-off-by: Vineet Gupta Cc: Arnd Bergmann --- arch/arc/plat-arcfpga/include/plat/smp.h | 1 + arch/arc/plat-arcfpga/platform.c | 4 +++ arch/arc/plat-arcfpga/smp.c | 41 +++++++++++++----------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/arch/arc/plat-arcfpga/include/plat/smp.h b/arch/arc/plat-arcfpga/include/plat/smp.h index 27822ac8155e..c09eb4cfc77c 100644 --- a/arch/arc/plat-arcfpga/include/plat/smp.h +++ b/arch/arc/plat-arcfpga/include/plat/smp.h @@ -111,6 +111,7 @@ extern void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask); extern void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode); extern void iss_model_init_smp(unsigned int cpu); +extern void iss_model_init_early_smp(void); #endif /* CONFIG_SMP */ diff --git a/arch/arc/plat-arcfpga/platform.c b/arch/arc/plat-arcfpga/platform.c index 4024f10a39ca..4e20a1a5104d 100644 --- a/arch/arc/plat-arcfpga/platform.c +++ b/arch/arc/plat-arcfpga/platform.c @@ -162,6 +162,10 @@ static void __init plat_fpga_early_init(void) setup_bvci_lat_unit(); arc_fpga_serial_init(); + +#ifdef CONFIG_SMP + iss_model_init_early_smp(); +#endif } static struct of_dev_auxdata plat_auxdata_lookup[] __initdata = { diff --git a/arch/arc/plat-arcfpga/smp.c b/arch/arc/plat-arcfpga/smp.c index 68a53b153d03..91b55349a5f8 100644 --- a/arch/arc/plat-arcfpga/smp.c +++ b/arch/arc/plat-arcfpga/smp.c @@ -24,25 +24,10 @@ static char smp_cpuinfo_buf[128]; *------------------------------------------------------------------- */ -const char *arc_platform_smp_cpuinfo(void) -{ -#define IS_AVAIL1(var, str) ((var) ? str : "") - - struct bcr_mp mp; - - READ_BCR(ARC_REG_MP_BCR, mp); - - sprintf(smp_cpuinfo_buf, "Extn [700-SMP]: v%d, arch(%d) %s %s %s\n", - mp.ver, mp.mp_arch, IS_AVAIL1(mp.scu, "SCU"), - IS_AVAIL1(mp.idu, "IDU"), IS_AVAIL1(mp.sdu, "SDU")); - - return smp_cpuinfo_buf; -} - /* * Master kick starting another CPU */ -void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc) +static void iss_model_smp_wakeup_cpu(int cpu, unsigned long pc) { /* setup the start PC */ write_aux_reg(ARC_AUX_XTL_REG_PARAM, pc); @@ -103,19 +88,39 @@ void iss_model_init_smp(unsigned int cpu) smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu); } -void arc_platform_ipi_send(const struct cpumask *callmap) +static void iss_model_ipi_send(void *arg) { + struct cpumask *callmap = arg; unsigned int cpu; for_each_cpu(cpu, callmap) idu_irq_assert(cpu); } -void arc_platform_ipi_clear(int cpu, int irq) +static void iss_model_ipi_clear(int cpu, int irq) { idu_irq_clear(IDU_INTERRUPT_0 + cpu); } +void iss_model_init_early_smp(void) +{ +#define IS_AVAIL1(var, str) ((var) ? str : "") + + struct bcr_mp mp; + + READ_BCR(ARC_REG_MP_BCR, mp); + + sprintf(smp_cpuinfo_buf, "Extn [ISS-SMP]: v%d, arch(%d) %s %s %s\n", + mp.ver, mp.mp_arch, IS_AVAIL1(mp.scu, "SCU"), + IS_AVAIL1(mp.idu, "IDU"), IS_AVAIL1(mp.sdu, "SDU")); + + plat_smp_ops.info = smp_cpuinfo_buf; + + plat_smp_ops.cpu_kick = iss_model_smp_wakeup_cpu; + plat_smp_ops.ipi_send = iss_model_ipi_send; + plat_smp_ops.ipi_clear = iss_model_ipi_clear; +} + /* *------------------------------------------------------------------- * Low level Platform IPI Providers From 95461a64e9bf8dae80c0309a6dd90c3872548129 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:26 +0530 Subject: [PATCH 2863/4607] ARC: [plat-arcfpga] defconfig for fully loaded ARC Linux Signed-off-by: Vineet Gupta --- arch/arc/configs/fpga_defconfig | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/arch/arc/configs/fpga_defconfig b/arch/arc/configs/fpga_defconfig index 8638e101fa07..b8698067ebbe 100644 --- a/arch/arc/configs/fpga_defconfig +++ b/arch/arc/configs/fpga_defconfig @@ -10,16 +10,29 @@ CONFIG_NAMESPACES=y # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="../arc_initramfs" -CONFIG_EXPERT=y -# CONFIG_FUTEX is not set +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set +CONFIG_KPROBES=y +CONFIG_MODULES=y # CONFIG_LBDAF is not set # CONFIG_BLK_DEV_BSG is not set # CONFIG_IOSCHED_DEADLINE is not set # CONFIG_IOSCHED_CFQ is not set +CONFIG_ARC_PLAT_FPGA_LEGACY=y +CONFIG_ARC_BOARD_ML509=y +# CONFIG_ARC_HAS_RTSC is not set CONFIG_ARC_BUILTIN_DTB_NAME="angel4" # CONFIG_COMPACTION is not set # CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=y +CONFIG_NET_KEY=y +CONFIG_INET=y +# CONFIG_IPV6 is not set # CONFIG_STANDALONE is not set # CONFIG_PREVENT_FIRMWARE_BUILD is not set # CONFIG_FIRMWARE_IN_KERNEL is not set @@ -38,9 +51,11 @@ CONFIG_SERIAL_ARC_CONSOLE=y # CONFIG_HID is not set # CONFIG_USB_SUPPORT is not set # CONFIG_IOMMU_SUPPORT is not set -# CONFIG_DNOTIFY is not set -# CONFIG_INOTIFY_USER is not set +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_TMPFS=y # CONFIG_MISC_FILESYSTEMS is not set +CONFIG_NFS_FS=y # CONFIG_ENABLE_WARN_DEPRECATED is not set # CONFIG_ENABLE_MUST_CHECK is not set CONFIG_XZ_DEC=y From 0208c96a9df98999dd150430b37983e9a003ea0a Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Wed, 23 Jan 2013 17:24:17 +0530 Subject: [PATCH 2864/4607] ARC: Provide a default serial.h for uart drivers needing BASE_BAUD Signed-off-by: Vineet Gupta --- arch/arc/include/asm/serial.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 arch/arc/include/asm/serial.h diff --git a/arch/arc/include/asm/serial.h b/arch/arc/include/asm/serial.h new file mode 100644 index 000000000000..4dff5a1e4128 --- /dev/null +++ b/arch/arc/include/asm/serial.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com) + * + * 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_ARC_SERIAL_H +#define _ASM_ARC_SERIAL_H + +/* + * early-8250 requires BASE_BAUD to be defined and includes this header. + * We put in a typical value: + * (core clk / 16) - i.e. UART samples 16 times per sec. + * Athough in multi-platform-image this might not work, specially if the + * clk driving the UART is different. + * We can't use DeviceTree as this is typically for early serial. + */ + +#include + +#define BASE_BAUD (arc_get_core_freq() / 16) + +#endif /* _ASM_ARC_SERIAL_H */ From db8e35d5b28fe6608e73913c919e4a6568f7c09c Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:25 +0530 Subject: [PATCH 2865/4607] ARC: Add self to MAINTAINERS Signed-off-by: Vineet Gupta --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8ae709e34523..25893af4c3e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7418,6 +7418,12 @@ F: lib/swiotlb.c F: arch/*/kernel/pci-swiotlb.c F: include/linux/swiotlb.h +SYNOPSYS ARC ARCHITECTURE +M: Vineet Gupta +L: linux-snps-arc@vger.kernel.org +S: Supported +F: arch/arc/ + SYSV FILESYSTEM M: Christoph Hellwig S: Maintained From d626f547dd0457ab36f6151673fcc78fc3c63eaa Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Mon, 28 Jan 2013 15:07:31 +0530 Subject: [PATCH 2866/4607] ARC: Don't fiddle with non-existent caches !CONFIG_ARC_HAS_(I|D)CACHE makes Linux disable caches (assuming they exist in hardware) - mostly for debugging issues with new peripherals. However, independent of CONFIG_ARC_HAS_(I|D)CACHE, Linux also needs to handle, non-existant caches, using the information in Cache BCRs (Build Configuration Reg) Reported-by: Alexey Brodkin Signed-off-by: Vineet Gupta --- arch/arc/mm/cache_arc700.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/arch/arc/mm/cache_arc700.c b/arch/arc/mm/cache_arc700.c index c299b30924ee..88d617d84234 100644 --- a/arch/arc/mm/cache_arc700.c +++ b/arch/arc/mm/cache_arc700.c @@ -144,23 +144,18 @@ void __init read_decode_cache_bcr(void) void __init arc_cache_init(void) { unsigned int temp; -#ifdef CONFIG_ARC_CACHE unsigned int cpu = smp_processor_id(); -#endif -#ifdef CONFIG_ARC_HAS_ICACHE - struct cpuinfo_arc_cache *ic; -#endif -#ifdef CONFIG_ARC_HAS_DCACHE - struct cpuinfo_arc_cache *dc; -#endif + struct cpuinfo_arc_cache *ic = &cpuinfo_arc700[cpu].icache; + struct cpuinfo_arc_cache *dc = &cpuinfo_arc700[cpu].dcache; int way_pg_ratio = way_pg_ratio; char str[256]; printk(arc_cache_mumbojumbo(0, str, sizeof(str))); -#ifdef CONFIG_ARC_HAS_ICACHE - ic = &cpuinfo_arc700[cpu].icache; + if (!ic->ver) + goto chk_dc; +#ifdef CONFIG_ARC_HAS_ICACHE /* 1. Confirm some of I-cache params which Linux assumes */ if ((ic->assoc != ARC_ICACHE_WAYS) || (ic->line_len != ARC_ICACHE_LINE_LEN)) { @@ -213,9 +208,11 @@ void __init arc_cache_init(void) write_aux_reg(ARC_REG_IC_CTRL, temp); -#ifdef CONFIG_ARC_HAS_DCACHE - dc = &cpuinfo_arc700[cpu].dcache; +chk_dc: + if (!dc->ver) + return; +#ifdef CONFIG_ARC_HAS_DCACHE if ((dc->assoc != ARC_DCACHE_WAYS) || (dc->line_len != ARC_DCACHE_LINE_LEN)) { panic("Cache H/W doesn't match kernel Config"); From 1e266629933bb3e40ac7db128f3b661f5bab56c1 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Wed, 6 Feb 2013 15:09:13 +0530 Subject: [PATCH 2867/4607] ARC: 64bit RTSC timestamp hardware issue The 64bit RTSC is not reliable, causing spurious "jumps" in higher word, making Linux timekeeping go bonkers. So as of now just use the lower 32bit timestamp. A cleaner approach would have been removing RTSC support altogether as the 32bit RTSC is equivalent to old TIMER1 based solution, but some customers can use the 32bit RTSC in SMP syn fashion (vs. TIMER1 which being incore can't be done easily). A fallout of this is sched_clock()'s hardware assisted version needs to go away since it can't use 32bit wrapping counter - instead we use the generic "weak" jiffies based version. Signed-off-by: Vineet Gupta --- arch/arc/kernel/time.c | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c index 0ce0e6f76eb0..f13f72807aa5 100644 --- a/arch/arc/kernel/time.c +++ b/arch/arc/kernel/time.c @@ -76,7 +76,7 @@ static cycle_t arc_counter_read(struct clocksource *cs) __asm__ __volatile( " .extCoreRegister tsch, 58, r, cannot_shortcut \n" " rtsc %0, 0 \n" - " mov %1, tsch \n" /* TSCH is extn core reg 58 */ + " mov %1, 0 \n" : "=r" (stamp.low), "=r" (stamp.high)); arch_local_irq_restore(flags); @@ -88,7 +88,7 @@ static struct clocksource arc_counter = { .name = "ARC RTSC", .rating = 300, .read = arc_counter_read, - .mask = CLOCKSOURCE_MASK(64), + .mask = CLOCKSOURCE_MASK(32), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; @@ -263,37 +263,3 @@ void __init time_init(void) if (machine_desc->init_time) machine_desc->init_time(); } - -#ifdef CONFIG_ARC_HAS_RTSC -/* - * sched_clock math assist - * ns = cycles * (ns_per_sec / cpu_freq_hz) - * ns = cycles * (10^6 / cpu_freq_khz) - * ns = cycles * (10^6 * 2^SF / cpu_freq_khz) / 2^SF - * ns = cycles * cyc2ns_scale >> SF - */ -#define CYC2NS_SF 10 /* 2^10, carefully chosen */ -#define CYC2NS_SCALE ((1000000 << CYC2NS_SF) / (arc_get_core_freq() / 1000)) - -static unsigned long long cycles2ns(unsigned long long cyc) -{ - return (cyc * CYC2NS_SCALE ) >> CYC2NS_SF; -} - -/* - * Scheduler clock - a monotonically increasing clock in nanosec units. - * It's return value must NOT wrap around. - * - * - Since 32bit TIMER1 will overflow almost immediately (53sec @ 80MHz), it - * can't be used directly. - * - Using getrawmonotonic (TIMER1 based, but with state for last + current - * snapshots), is no-good either because of seqlock deadlock possibilities - * - So only with native 64bit timer we do this, otherwise fallback to generic - * jiffies based version - which despite not being fine grained gaurantees - * the monotonically increasing semantics. - */ -unsigned long long sched_clock(void) -{ - return cycles2ns(arc_counter_read(NULL)); -} -#endif From 7f85e5ec0d451e4a12037dcd6936f2d7494b2c6c Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 8 Feb 2013 12:10:17 +0530 Subject: [PATCH 2868/4607] ARC: [3.9] Fallout of hlist iterator update Commit 0bbacca "hlist: drop the node parameter from iterators" changed the iterator across the board - but ARC port being out-of-tree missed it. Signed-off-by: Vineet Gupta --- arch/arc/kernel/kprobes.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arc/kernel/kprobes.c b/arch/arc/kernel/kprobes.c index 47e42f29f927..3bfeacb674de 100644 --- a/arch/arc/kernel/kprobes.c +++ b/arch/arc/kernel/kprobes.c @@ -440,7 +440,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, { struct kretprobe_instance *ri = NULL; struct hlist_head *head, empty_rp; - struct hlist_node *node, *tmp; + struct hlist_node *tmp; unsigned long flags, orig_ret_address = 0; unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline; @@ -460,7 +460,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, * real return address, and all the rest will point to * kretprobe_trampoline */ - hlist_for_each_entry_safe(ri, node, tmp, head, hlist) { + hlist_for_each_entry_safe(ri, tmp, head, hlist) { if (ri->task != current) /* another task is sharing our hash bucket */ continue; @@ -488,7 +488,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, kretprobe_hash_unlock(current, &flags); preempt_enable_no_resched(); - hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) { + hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); kfree(ri); } From 3eb3e7dd53a83cbe6e7b95aa87d2c5dc861233a6 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Mon, 11 Feb 2013 14:40:55 +0530 Subject: [PATCH 2869/4607] ARC: Fix pt_orig_r8 access Syscall restarting fixes made pt_regs->orig_r8 a short word, which was not reflected in the assembler code - thus could potentially break gdb debugging. Signed-off-by: Vineet Gupta --- arch/arc/kernel/entry.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 3f628ca9b71a..ef6800ba2f03 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -496,7 +496,7 @@ tracesys_exit: trap_with_param: ; stop_pc info by gdb needs this info - st orig_r8_IS_BRKPT, [sp, PT_orig_r8] + stw orig_r8_IS_BRKPT, [sp, PT_orig_r8] mov r0, r12 lr r1, [efa] @@ -721,7 +721,7 @@ not_exception: ; things to what they were, before returning from L2 context ;---------------------------------------------------------------- - ld r9, [sp, PT_orig_r8] ; get orig_r8 to make sure it is + ldw r9, [sp, PT_orig_r8] ; get orig_r8 to make sure it is brne r9, orig_r8_IS_IRQ2, 149f ; infact a L2 ISR ret path ld r9, [sp, PT_status32] ; get statu32_l2 (saved in pt_regs) From 70b319f2beb12e766b12ed2858ee54ff4623ac03 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Mon, 11 Feb 2013 17:19:32 +0530 Subject: [PATCH 2870/4607] ARC: Ensure CONFIG_VIRT_TO_BUS is not enabled Signed-off-by: Vineet Gupta Reported-by: James Hogan --- arch/arc/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index be85ceb8b264..4fef29f56b52 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -8,7 +8,6 @@ config ARC def_bool y - select ARCH_NO_VIRT_TO_BUS select CLONE_BACKWARDS # ARC Busybox based initramfs absolutely relies on DEVTMPFS for /dev select DEVTMPFS if !INITRAMFS_SOURCE="" @@ -77,6 +76,9 @@ config HAVE_LATENCYTOP_SUPPORT config NO_DMA def_bool n +config ARCH_NO_VIRT_TO_BUS + def_bool y + source "init/Kconfig" source "kernel/Kconfig.freezer" From fc32781bfdb56dad883469b65e468e749ef35fe5 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Mon, 11 Feb 2013 15:02:08 +0530 Subject: [PATCH 2871/4607] ARC: [plat-arcfpga] DT arc-uart bindings change: "baud" => "current-speed" Per Grant's review comment - driver changes via tty tree Signed-off-by: Vineet Gupta --- arch/arc/boot/dts/angel4.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arc/boot/dts/angel4.dts b/arch/arc/boot/dts/angel4.dts index 4c188d5bc10c..bae4f936cb03 100644 --- a/arch/arc/boot/dts/angel4.dts +++ b/arch/arc/boot/dts/angel4.dts @@ -48,7 +48,7 @@ reg = <0xc0fc1000 0x100>; interrupts = <5>; clock-frequency = <80000000>; - baud = <115200>; + current-speed = <115200>; status = "okay"; }; }; From bb76563214ec371a461ca5038904a5b93dbd6b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 26 Dec 2012 08:29:17 +0000 Subject: [PATCH 2872/4607] MIPS: bcm47xx: separate functions finding flash window addr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also check if parallel flash is present at all before accessing it and add support for serial flash on BCMA bus. Signed-off-by: Rafał Miłecki Acked-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4738/ Signed-off-by: John Crispin --- arch/mips/bcm47xx/nvram.c | 87 +++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 27 deletions(-) diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c index 48a4c70b3842..64613678ce84 100644 --- a/arch/mips/bcm47xx/nvram.c +++ b/arch/mips/bcm47xx/nvram.c @@ -23,39 +23,13 @@ static char nvram_buf[NVRAM_SPACE]; -/* Probe for NVRAM header */ -static void early_nvram_init(void) +static void nvram_find_and_copy(u32 base, u32 lim) { -#ifdef CONFIG_BCM47XX_SSB - struct ssb_mipscore *mcore_ssb; -#endif -#ifdef CONFIG_BCM47XX_BCMA - struct bcma_drv_cc *bcma_cc; -#endif struct nvram_header *header; int i; - u32 base = 0; - u32 lim = 0; u32 off; u32 *src, *dst; - switch (bcm47xx_bus_type) { -#ifdef CONFIG_BCM47XX_SSB - case BCM47XX_BUS_TYPE_SSB: - mcore_ssb = &bcm47xx_bus.ssb.mipscore; - base = mcore_ssb->pflash.window; - lim = mcore_ssb->pflash.window_size; - break; -#endif -#ifdef CONFIG_BCM47XX_BCMA - case BCM47XX_BUS_TYPE_BCMA: - bcma_cc = &bcm47xx_bus.bcma.bus.drv_cc; - base = bcma_cc->pflash.window; - lim = bcma_cc->pflash.window_size; - break; -#endif - } - off = FLASH_MIN; while (off <= lim) { /* Windowed flash access */ @@ -86,6 +60,65 @@ found: *dst++ = le32_to_cpu(*src++); } +#ifdef CONFIG_BCM47XX_SSB +static void nvram_init_ssb(void) +{ + struct ssb_mipscore *mcore = &bcm47xx_bus.ssb.mipscore; + u32 base; + u32 lim; + + if (mcore->pflash.present) { + base = mcore->pflash.window; + lim = mcore->pflash.window_size; + } else { + pr_err("Couldn't find supported flash memory\n"); + return; + } + + nvram_find_and_copy(base, lim); +} +#endif + +#ifdef CONFIG_BCM47XX_BCMA +static void nvram_init_bcma(void) +{ + struct bcma_drv_cc *cc = &bcm47xx_bus.bcma.bus.drv_cc; + u32 base; + u32 lim; + + if (cc->pflash.present) { + base = cc->pflash.window; + lim = cc->pflash.window_size; +#ifdef CONFIG_BCMA_SFLASH + } else if (cc->sflash.present) { + base = cc->sflash.window; + lim = cc->sflash.size; +#endif + } else { + pr_err("Couldn't find supported flash memory\n"); + return; + } + + nvram_find_and_copy(base, lim); +} +#endif + +static void early_nvram_init(void) +{ + switch (bcm47xx_bus_type) { +#ifdef CONFIG_BCM47XX_SSB + case BCM47XX_BUS_TYPE_SSB: + nvram_init_ssb(); + break; +#endif +#ifdef CONFIG_BCM47XX_BCMA + case BCM47XX_BUS_TYPE_BCMA: + nvram_init_bcma(); + break; +#endif + } +} + int nvram_getenv(char *name, char *val, size_t val_len) { char *var, *value, *end, *eq; From ee7e2f3c235f43d815c0be285101b5b91a3bcaa5 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 26 Dec 2012 19:51:09 +0000 Subject: [PATCH 2873/4607] MIPS: BCM47XX: use common error codes in nvram reads Instead of using our own error codes use some common codes. Signed-off-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4739/ Signed-off-by: John Crispin --- arch/mips/bcm47xx/nvram.c | 4 ++-- arch/mips/bcm47xx/sprom.c | 2 +- arch/mips/include/asm/mach-bcm47xx/nvram.h | 3 --- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c index 64613678ce84..e19fc2600b76 100644 --- a/arch/mips/bcm47xx/nvram.c +++ b/arch/mips/bcm47xx/nvram.c @@ -124,7 +124,7 @@ int nvram_getenv(char *name, char *val, size_t val_len) char *var, *value, *end, *eq; if (!name) - return NVRAM_ERR_INV_PARAM; + return -EINVAL; if (!nvram_buf[0]) early_nvram_init(); @@ -143,6 +143,6 @@ int nvram_getenv(char *name, char *val, size_t val_len) return snprintf(val, val_len, "%s", value); } } - return NVRAM_ERR_ENVNOTFOUND; + return -ENOENT; } EXPORT_SYMBOL(nvram_getenv); diff --git a/arch/mips/bcm47xx/sprom.c b/arch/mips/bcm47xx/sprom.c index 289cc0a38638..66b71c363300 100644 --- a/arch/mips/bcm47xx/sprom.c +++ b/arch/mips/bcm47xx/sprom.c @@ -51,7 +51,7 @@ static int get_nvram_var(const char *prefix, const char *postfix, create_key(prefix, postfix, name, key, sizeof(key)); err = nvram_getenv(key, buf, len); - if (fallback && err == NVRAM_ERR_ENVNOTFOUND && prefix) { + if (fallback && err == -ENOENT && prefix) { create_key(NULL, postfix, name, key, sizeof(key)); err = nvram_getenv(key, buf, len); } diff --git a/arch/mips/include/asm/mach-bcm47xx/nvram.h b/arch/mips/include/asm/mach-bcm47xx/nvram.h index 69ef3efe06e7..550a7fc932c9 100644 --- a/arch/mips/include/asm/mach-bcm47xx/nvram.h +++ b/arch/mips/include/asm/mach-bcm47xx/nvram.h @@ -32,9 +32,6 @@ struct nvram_header { #define NVRAM_MAX_VALUE_LEN 255 #define NVRAM_MAX_PARAM_LEN 64 -#define NVRAM_ERR_INV_PARAM -8 -#define NVRAM_ERR_ENVNOTFOUND -9 - extern int nvram_getenv(char *name, char *val, size_t val_len); static inline void nvram_parse_macaddr(char *buf, u8 macaddr[6]) From cc4403e02541af226ae6b7da0917c8959dd73a75 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 26 Dec 2012 19:51:10 +0000 Subject: [PATCH 2874/4607] MIPS: BCM47XX: return error when init of nvram failed This makes it possible to handle the case of not being able to read the nvram ram. This could happen when the code searching for the specific flash chip have not run jet. Signed-off-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4740/ Signed-off-by: John Crispin --- arch/mips/bcm47xx/nvram.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c index e19fc2600b76..80e352e0c995 100644 --- a/arch/mips/bcm47xx/nvram.c +++ b/arch/mips/bcm47xx/nvram.c @@ -23,7 +23,7 @@ static char nvram_buf[NVRAM_SPACE]; -static void nvram_find_and_copy(u32 base, u32 lim) +static int nvram_find_and_copy(u32 base, u32 lim) { struct nvram_header *header; int i; @@ -49,7 +49,7 @@ static void nvram_find_and_copy(u32 base, u32 lim) if (header->magic == NVRAM_HEADER) goto found; - return; + return -ENXIO; found: src = (u32 *) header; @@ -58,10 +58,12 @@ found: *dst++ = *src++; for (; i < header->len && i < NVRAM_SPACE; i += 4) *dst++ = le32_to_cpu(*src++); + + return 0; } #ifdef CONFIG_BCM47XX_SSB -static void nvram_init_ssb(void) +static int nvram_init_ssb(void) { struct ssb_mipscore *mcore = &bcm47xx_bus.ssb.mipscore; u32 base; @@ -72,15 +74,15 @@ static void nvram_init_ssb(void) lim = mcore->pflash.window_size; } else { pr_err("Couldn't find supported flash memory\n"); - return; + return -ENXIO; } - nvram_find_and_copy(base, lim); + return nvram_find_and_copy(base, lim); } #endif #ifdef CONFIG_BCM47XX_BCMA -static void nvram_init_bcma(void) +static int nvram_init_bcma(void) { struct bcma_drv_cc *cc = &bcm47xx_bus.bcma.bus.drv_cc; u32 base; @@ -96,38 +98,41 @@ static void nvram_init_bcma(void) #endif } else { pr_err("Couldn't find supported flash memory\n"); - return; + return -ENXIO; } - nvram_find_and_copy(base, lim); + return nvram_find_and_copy(base, lim); } #endif -static void early_nvram_init(void) +static int early_nvram_init(void) { switch (bcm47xx_bus_type) { #ifdef CONFIG_BCM47XX_SSB case BCM47XX_BUS_TYPE_SSB: - nvram_init_ssb(); - break; + return nvram_init_ssb(); #endif #ifdef CONFIG_BCM47XX_BCMA case BCM47XX_BUS_TYPE_BCMA: - nvram_init_bcma(); - break; + return nvram_init_bcma(); #endif } + return -ENXIO; } int nvram_getenv(char *name, char *val, size_t val_len) { char *var, *value, *end, *eq; + int err; if (!name) return -EINVAL; - if (!nvram_buf[0]) - early_nvram_init(); + if (!nvram_buf[0]) { + err = early_nvram_init(); + if (err) + return err; + } /* Look for name=value and return value */ var = &nvram_buf[sizeof(struct nvram_header)]; From c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 26 Dec 2012 19:51:11 +0000 Subject: [PATCH 2875/4607] MIPS: BCM47XX: nvram add nand flash support Signed-off-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4741/ Signed-off-by: John Crispin --- arch/mips/bcm47xx/nvram.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c index 80e352e0c995..42e527121314 100644 --- a/arch/mips/bcm47xx/nvram.c +++ b/arch/mips/bcm47xx/nvram.c @@ -30,6 +30,7 @@ static int nvram_find_and_copy(u32 base, u32 lim) u32 off; u32 *src, *dst; + /* TODO: when nvram is on nand flash check for bad blocks first. */ off = FLASH_MIN; while (off <= lim) { /* Windowed flash access */ @@ -88,6 +89,12 @@ static int nvram_init_bcma(void) u32 base; u32 lim; +#ifdef CONFIG_BCMA_NFLASH + if (cc->nflash.boot) { + base = BCMA_SOC_FLASH1; + lim = BCMA_SOC_FLASH1_SZ; + } else +#endif if (cc->pflash.present) { base = cc->pflash.window; lim = cc->pflash.window_size; From e58da16f716c0e7822e64a5d8a1f413041bc912e Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 26 Dec 2012 19:51:12 +0000 Subject: [PATCH 2876/4607] MIPS: BCM47XX: rename early_nvram_init to nvram_init Signed-off-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4742/ Signed-off-by: John Crispin --- arch/mips/bcm47xx/nvram.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c index 42e527121314..6cf3ef29d844 100644 --- a/arch/mips/bcm47xx/nvram.c +++ b/arch/mips/bcm47xx/nvram.c @@ -112,7 +112,7 @@ static int nvram_init_bcma(void) } #endif -static int early_nvram_init(void) +static int nvram_init(void) { switch (bcm47xx_bus_type) { #ifdef CONFIG_BCM47XX_SSB @@ -136,7 +136,7 @@ int nvram_getenv(char *name, char *val, size_t val_len) return -EINVAL; if (!nvram_buf[0]) { - err = early_nvram_init(); + err = nvram_init(); if (err) return err; } From f36738ddfeea02867b393e7f34da0cec48bafc54 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 26 Dec 2012 19:51:13 +0000 Subject: [PATCH 2877/4607] MIPS: BCM47XX: handle different nvram sizes The old code just worked for nvram with a size of 0x8000 bytes. This patch adds support for reading nvram from partitions of 0xF000 and 0x10000 bytes. There is just 32KB space for the nvram, but most devices do not use the full size and this code reads the first 32KB in that case and prints a warning. Signed-off-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4743/ Signed-off-by: John Crispin --- arch/mips/bcm47xx/nvram.c | 46 +++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c index 6cf3ef29d844..b4a47fcb4f64 100644 --- a/arch/mips/bcm47xx/nvram.c +++ b/arch/mips/bcm47xx/nvram.c @@ -3,7 +3,7 @@ * * Copyright (C) 2005 Broadcom Corporation * Copyright (C) 2006 Felix Fietkau - * Copyright (C) 2010-2011 Hauke Mehrtens + * Copyright (C) 2010-2012 Hauke Mehrtens * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -23,42 +23,74 @@ static char nvram_buf[NVRAM_SPACE]; +static u32 find_nvram_size(u32 end) +{ + struct nvram_header *header; + u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000}; + int i; + + for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { + header = (struct nvram_header *)KSEG1ADDR(end - nvram_sizes[i]); + if (header->magic == NVRAM_HEADER) + return nvram_sizes[i]; + } + + return 0; +} + +/* Probe for NVRAM header */ static int nvram_find_and_copy(u32 base, u32 lim) { struct nvram_header *header; int i; u32 off; u32 *src, *dst; + u32 size; /* TODO: when nvram is on nand flash check for bad blocks first. */ off = FLASH_MIN; while (off <= lim) { /* Windowed flash access */ - header = (struct nvram_header *) - KSEG1ADDR(base + off - NVRAM_SPACE); - if (header->magic == NVRAM_HEADER) + size = find_nvram_size(base + off); + if (size) { + header = (struct nvram_header *)KSEG1ADDR(base + off - + size); goto found; + } off <<= 1; } /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ header = (struct nvram_header *) KSEG1ADDR(base + 4096); - if (header->magic == NVRAM_HEADER) + if (header->magic == NVRAM_HEADER) { + size = NVRAM_SPACE; goto found; + } header = (struct nvram_header *) KSEG1ADDR(base + 1024); - if (header->magic == NVRAM_HEADER) + if (header->magic == NVRAM_HEADER) { + size = NVRAM_SPACE; goto found; + } + pr_err("no nvram found\n"); return -ENXIO; found: + + if (header->len > size) + pr_err("The nvram size accoridng to the header seems to be bigger than the partition on flash\n"); + if (header->len > NVRAM_SPACE) + pr_err("nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n", + header->len, NVRAM_SPACE); + src = (u32 *) header; dst = (u32 *) nvram_buf; for (i = 0; i < sizeof(struct nvram_header); i += 4) *dst++ = *src++; - for (; i < header->len && i < NVRAM_SPACE; i += 4) + for (; i < header->len && i < NVRAM_SPACE && i < size; i += 4) *dst++ = le32_to_cpu(*src++); + memset(dst, 0x0, NVRAM_SPACE - i); return 0; } From 111bd981e2216827aab95503596501ab71bc7a7d Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 26 Dec 2012 19:51:14 +0000 Subject: [PATCH 2878/4607] MIPS: BCM47XX: add bcm47xx prefix in front of nvram function names The nvram functions are exported and used by some normal drivers. To prevent name clashes with ofter parts of the kernel code add a bcm47xx_ prefix in front of the function names and the header file name. Signed-off-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4744/ Signed-off-by: John Crispin --- arch/mips/bcm47xx/nvram.c | 6 +++--- arch/mips/bcm47xx/setup.c | 6 +++--- arch/mips/bcm47xx/sprom.c | 8 ++++---- .../asm/mach-bcm47xx/{nvram.h => bcm47xx_nvram.h} | 10 +++++----- drivers/mtd/bcm47xxpart.c | 2 +- drivers/net/ethernet/broadcom/b44.c | 4 ++-- drivers/ssb/driver_chipcommon_pmu.c | 4 ++-- include/linux/ssb/ssb_driver_gige.h | 6 +++--- 8 files changed, 23 insertions(+), 23 deletions(-) rename arch/mips/include/asm/mach-bcm47xx/{nvram.h => bcm47xx_nvram.h} (84%) diff --git a/arch/mips/bcm47xx/nvram.c b/arch/mips/bcm47xx/nvram.c index b4a47fcb4f64..e63bd5abd9a2 100644 --- a/arch/mips/bcm47xx/nvram.c +++ b/arch/mips/bcm47xx/nvram.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include static char nvram_buf[NVRAM_SPACE]; @@ -159,7 +159,7 @@ static int nvram_init(void) return -ENXIO; } -int nvram_getenv(char *name, char *val, size_t val_len) +int bcm47xx_nvram_getenv(char *name, char *val, size_t val_len) { char *var, *value, *end, *eq; int err; @@ -189,4 +189,4 @@ int nvram_getenv(char *name, char *val, size_t val_len) } return -ENOENT; } -EXPORT_SYMBOL(nvram_getenv); +EXPORT_SYMBOL(bcm47xx_nvram_getenv); diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c index 4d54b58dbd32..b2246cd9ca12 100644 --- a/arch/mips/bcm47xx/setup.c +++ b/arch/mips/bcm47xx/setup.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include union bcm47xx_bus bcm47xx_bus; EXPORT_SYMBOL(bcm47xx_bus); @@ -115,7 +115,7 @@ static int bcm47xx_get_invariants(struct ssb_bus *bus, memset(&iv->sprom, 0, sizeof(struct ssb_sprom)); bcm47xx_fill_sprom(&iv->sprom, NULL, false); - if (nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) + if (bcm47xx_nvram_getenv("cardbus", buf, sizeof(buf)) >= 0) iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10); return 0; @@ -138,7 +138,7 @@ static void __init bcm47xx_register_ssb(void) panic("Failed to initialize SSB bus (err %d)", err); mcore = &bcm47xx_bus.ssb.mipscore; - if (nvram_getenv("kernel_args", buf, sizeof(buf)) >= 0) { + if (bcm47xx_nvram_getenv("kernel_args", buf, sizeof(buf)) >= 0) { if (strstr(buf, "console=ttyS1")) { struct ssb_serial_port port; diff --git a/arch/mips/bcm47xx/sprom.c b/arch/mips/bcm47xx/sprom.c index 66b71c363300..89b9bf46b13b 100644 --- a/arch/mips/bcm47xx/sprom.c +++ b/arch/mips/bcm47xx/sprom.c @@ -27,7 +27,7 @@ */ #include -#include +#include static void create_key(const char *prefix, const char *postfix, const char *name, char *buf, int len) @@ -50,10 +50,10 @@ static int get_nvram_var(const char *prefix, const char *postfix, create_key(prefix, postfix, name, key, sizeof(key)); - err = nvram_getenv(key, buf, len); + err = bcm47xx_nvram_getenv(key, buf, len); if (fallback && err == -ENOENT && prefix) { create_key(NULL, postfix, name, key, sizeof(key)); - err = nvram_getenv(key, buf, len); + err = bcm47xx_nvram_getenv(key, buf, len); } return err; } @@ -144,7 +144,7 @@ static void nvram_read_macaddr(const char *prefix, const char *name, if (err < 0) return; - nvram_parse_macaddr(buf, *val); + bcm47xx_nvram_parse_macaddr(buf, *val); } static void nvram_read_alpha2(const char *prefix, const char *name, diff --git a/arch/mips/include/asm/mach-bcm47xx/nvram.h b/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h similarity index 84% rename from arch/mips/include/asm/mach-bcm47xx/nvram.h rename to arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h index 550a7fc932c9..b8e7be8f34dd 100644 --- a/arch/mips/include/asm/mach-bcm47xx/nvram.h +++ b/arch/mips/include/asm/mach-bcm47xx/bcm47xx_nvram.h @@ -8,8 +8,8 @@ * option) any later version. */ -#ifndef __NVRAM_H -#define __NVRAM_H +#ifndef __BCM47XX_NVRAM_H +#define __BCM47XX_NVRAM_H #include #include @@ -32,9 +32,9 @@ struct nvram_header { #define NVRAM_MAX_VALUE_LEN 255 #define NVRAM_MAX_PARAM_LEN 64 -extern int nvram_getenv(char *name, char *val, size_t val_len); +extern int bcm47xx_nvram_getenv(char *name, char *val, size_t val_len); -static inline void nvram_parse_macaddr(char *buf, u8 macaddr[6]) +static inline void bcm47xx_nvram_parse_macaddr(char *buf, u8 macaddr[6]) { if (strchr(buf, ':')) sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddr[0], @@ -48,4 +48,4 @@ static inline void nvram_parse_macaddr(char *buf, u8 macaddr[6]) printk(KERN_WARNING "Can not parse mac address: %s\n", buf); } -#endif +#endif /* __BCM47XX_NVRAM_H */ diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index e06d782489a6..6a1180502cc1 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include /* 10 parts were found on sflash on Netgear WNDR4500 */ #define BCM47XXPART_MAX_PARTS 12 diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 219f6226fcb1..330b0908bf97 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -381,7 +381,7 @@ static void b44_set_flow_ctrl(struct b44 *bp, u32 local, u32 remote) } #ifdef CONFIG_BCM47XX -#include +#include static void b44_wap54g10_workaround(struct b44 *bp) { char buf[20]; @@ -393,7 +393,7 @@ static void b44_wap54g10_workaround(struct b44 *bp) * see https://dev.openwrt.org/ticket/146 * check and reset bit "isolate" */ - if (nvram_getenv("boardnum", buf, sizeof(buf)) < 0) + if (bcm47xx_nvram_getenv("boardnum", buf, sizeof(buf)) < 0) return; if (simple_strtoul(buf, NULL, 0) == 2) { err = __b44_readphy(bp, 0, MII_BMCR, &val); diff --git a/drivers/ssb/driver_chipcommon_pmu.c b/drivers/ssb/driver_chipcommon_pmu.c index a43415a7fbed..4c0f6d883dd3 100644 --- a/drivers/ssb/driver_chipcommon_pmu.c +++ b/drivers/ssb/driver_chipcommon_pmu.c @@ -14,7 +14,7 @@ #include #include #ifdef CONFIG_BCM47XX -#include +#include #endif #include "ssb_private.h" @@ -322,7 +322,7 @@ static void ssb_pmu_pll_init(struct ssb_chipcommon *cc) if (bus->bustype == SSB_BUSTYPE_SSB) { #ifdef CONFIG_BCM47XX char buf[20]; - if (nvram_getenv("xtalfreq", buf, sizeof(buf)) >= 0) + if (bcm47xx_nvram_getenv("xtalfreq", buf, sizeof(buf)) >= 0) crystalfreq = simple_strtoul(buf, NULL, 0); #endif } diff --git a/include/linux/ssb/ssb_driver_gige.h b/include/linux/ssb/ssb_driver_gige.h index 6b05dcd927ff..6d92a92573df 100644 --- a/include/linux/ssb/ssb_driver_gige.h +++ b/include/linux/ssb/ssb_driver_gige.h @@ -98,14 +98,14 @@ static inline bool ssb_gige_must_flush_posted_writes(struct pci_dev *pdev) } #ifdef CONFIG_BCM47XX -#include +#include /* Get the device MAC address */ static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) { char buf[20]; - if (nvram_getenv("et0macaddr", buf, sizeof(buf)) < 0) + if (bcm47xx_nvram_getenv("et0macaddr", buf, sizeof(buf)) < 0) return; - nvram_parse_macaddr(buf, macaddr); + bcm47xx_nvram_parse_macaddr(buf, macaddr); } #else static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) From 924ffb7dba80f44211d4ba08157496c6e37d307a Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 26 Dec 2012 19:53:26 +0000 Subject: [PATCH 2879/4607] MIPS: BCM47XX: trim the nvram values for parsing Some nvram values on some devices have a newline character at the end of the value, that caused read errors. Trim the string before reading the number. Signed-off-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4745/ Signed-off-by: John Crispin --- arch/mips/bcm47xx/sprom.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/bcm47xx/sprom.c b/arch/mips/bcm47xx/sprom.c index 89b9bf46b13b..38492301a7b5 100644 --- a/arch/mips/bcm47xx/sprom.c +++ b/arch/mips/bcm47xx/sprom.c @@ -71,7 +71,7 @@ static void nvram_read_ ## type (const char *prefix, \ fallback); \ if (err < 0) \ return; \ - err = kstrto ## type (buf, 0, &var); \ + err = kstrto ## type(strim(buf), 0, &var); \ if (err) { \ pr_warn("can not parse nvram name %s%s%s with value %s got %i\n", \ prefix, name, postfix, buf, err); \ @@ -99,7 +99,7 @@ static void nvram_read_u32_2(const char *prefix, const char *name, err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback); if (err < 0) return; - err = kstrtou32(buf, 0, &val); + err = kstrtou32(strim(buf), 0, &val); if (err) { pr_warn("can not parse nvram name %s%s with value %s got %i\n", prefix, name, buf, err); @@ -120,7 +120,7 @@ static void nvram_read_leddc(const char *prefix, const char *name, err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback); if (err < 0) return; - err = kstrtou32(buf, 0, &val); + err = kstrtou32(strim(buf), 0, &val); if (err) { pr_warn("can not parse nvram name %s%s with value %s got %i\n", prefix, name, buf, err); From fe08f8c2c5cf4fa95d9d6d7c1577c6fbb963b5c1 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 26 Dec 2012 20:06:17 +0000 Subject: [PATCH 2880/4607] MIPS: BCM47XX: select BOOT_RAW All the boot loaders I have seen are booting the kernel in raw mode by default. CFE seems to support elf kernel images too, but the default case is raw for the devices I know of. Select this option to make the kernel boot on most of the devices with the default options. Signed-off-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4746/ Signed-off-by: John Crispin --- arch/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 2ac626ab9d43..c87455eedb8f 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -107,6 +107,7 @@ config ATH79 config BCM47XX bool "Broadcom BCM47XX based boards" select ARCH_WANT_OPTIONAL_GPIOLIB + select BOOT_RAW select CEVT_R4K select CSRC_R4K select DMA_NONCOHERENT From dd54dedd947dd801ea60099837ac009eb235a61d Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Wed, 26 Dec 2012 20:06:18 +0000 Subject: [PATCH 2881/4607] MIPS: BCM47XX: select NO_EXCEPT_FILL The kernel is loaded to 0x80001000 so there is some space left for the exception handlers and the kernel do not have to reserve some extra space for them. Signed-off-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4747/ Signed-off-by: John Crispin --- arch/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index c87455eedb8f..daeafe26cfb2 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -114,6 +114,7 @@ config BCM47XX select FW_CFE select HW_HAS_PCI select IRQ_CPU + select NO_EXCEPT_FILL select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_LITTLE_ENDIAN select SYS_HAS_EARLY_PRINTK From 6404b7cb83273623dc836a33427a30e371dab11b Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 6 Dec 2012 21:25:05 +0000 Subject: [PATCH 2882/4607] MIPS: BCM47XX: use fallback sprom var for board_{rev,type} An SoC normally do not define path variables for board_rev and board_type and the Broadcom SDK also uses the nvram values without a prefix in such cases. Do the same to fill these sprom attributes from nvram and do not leave them empty, because brcmsmac do not like this. Signed-off-by: Hauke Mehrtens Patchwork: http://patchwork.linux-mips.org/patch/4679/ Signed-off-by: John Crispin --- arch/mips/bcm47xx/sprom.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/mips/bcm47xx/sprom.c b/arch/mips/bcm47xx/sprom.c index 38492301a7b5..7d97a5a4bd3f 100644 --- a/arch/mips/bcm47xx/sprom.c +++ b/arch/mips/bcm47xx/sprom.c @@ -652,12 +652,10 @@ static void bcm47xx_fill_sprom_ethernet(struct ssb_sprom *sprom, static void bcm47xx_fill_board_data(struct ssb_sprom *sprom, const char *prefix, bool fallback) { - nvram_read_u16(prefix, NULL, "boardrev", &sprom->board_rev, 0, - fallback); + nvram_read_u16(prefix, NULL, "boardrev", &sprom->board_rev, 0, true); nvram_read_u16(prefix, NULL, "boardnum", &sprom->board_num, 0, fallback); - nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0, - fallback); + nvram_read_u16(prefix, NULL, "boardtype", &sprom->board_type, 0, true); nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo, &sprom->boardflags_hi, fallback); nvram_read_u32_2(prefix, "boardflags2", &sprom->boardflags2_lo, From ab1a2e038ff2336502e95ec6492c0364a9fc70d0 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 19 Jan 2013 00:07:42 +0800 Subject: [PATCH 2883/4607] ACPI / PCI: Make pci_slot built-in only, not a module As discussed in thread at https://patchwork.kernel.org/patch/1946851/, there's no value in supporting CONFIG_ACPI_PCI_SLOT=m any more. So change Kconfig and code to only support building pci_slot as built-in driver. Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/acpi/Kconfig | 5 +---- drivers/acpi/internal.h | 5 +++++ drivers/acpi/pci_slot.c | 13 +------------ drivers/acpi/scan.c | 1 + 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 38c5078da11d..27fde5e8d31a 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -308,7 +308,7 @@ config ACPI_DEBUG_FUNC_TRACE is about half of the penalty and is rarely useful. config ACPI_PCI_SLOT - tristate "PCI slot detection driver" + bool "PCI slot detection driver" depends on SYSFS default n help @@ -317,9 +317,6 @@ config ACPI_PCI_SLOT i.e., segment/bus/device/function tuples, with physical slots in the system. If you are unsure, say N. - To compile this driver as a module, choose M here: - the module will be called pci_slot. - config X86_PM_TIMER bool "Power Management Timer Support" if EXPERT depends on X86 diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e050254ae143..7374cfc5917a 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -67,6 +67,11 @@ struct acpi_ec { extern struct acpi_ec *first_ec; +#ifdef CONFIG_ACPI_PCI_SLOT +void acpi_pci_slot_init(void); +#else +static inline void acpi_pci_slot_init(void) { } +#endif int acpi_pci_root_init(void); int acpi_ec_init(void); int acpi_ec_ecdt_probe(void); diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index d22585f21aeb..a7d7e7710f9e 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -330,19 +330,8 @@ static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = { {} }; -static int __init -acpi_pci_slot_init(void) +void __init acpi_pci_slot_init(void) { dmi_check_system(acpi_pci_slot_dmi_table); acpi_pci_register_driver(&acpi_pci_slot_driver); - return 0; } - -static void __exit -acpi_pci_slot_exit(void) -{ - acpi_pci_unregister_driver(&acpi_pci_slot_driver); -} - -module_init(acpi_pci_slot_init); -module_exit(acpi_pci_slot_exit); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7c43bdc36abc..236e476b09c9 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1687,6 +1687,7 @@ int __init acpi_scan_init(void) acpi_power_init(); acpi_pci_root_init(); + acpi_pci_slot_init(); /* * Enumerate devices in the ACPI namespace. From 969daa349f4821a02936af7202b51a9affc7b6da Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Feb 2013 11:35:42 -0700 Subject: [PATCH 2884/4607] PCI: Fix PCI Express Capability accessors for PCI_EXP_FLAGS PCI_EXP_FLAGS_TYPE is a mask, not an offset. Fix it. Previously, pcie_capability_read_word(..., PCI_EXP_FLAGS, ...) would fail. [bhelgaas: tweak changelog] Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas CC: stable@vger.kernel.org # v3.7+ --- drivers/pci/access.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 5278ac692cbf..1cc23661f79b 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -515,7 +515,7 @@ static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos) return false; switch (pos) { - case PCI_EXP_FLAGS_TYPE: + case PCI_EXP_FLAGS: return true; case PCI_EXP_DEVCAP: case PCI_EXP_DEVCTL: From a3fc0ff00a46c4b32e7214961a5be9a1dc39b60e Mon Sep 17 00:00:00 2001 From: James Ralston Date: Thu, 14 Feb 2013 09:15:33 +0000 Subject: [PATCH 2885/4607] i2c: i801: Add Device IDs for Intel Wellsburg PCH This patch adds the SMBus Device IDs for the Intel Wellsburg PCH Signed-off-by: James Ralston Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-i801 | 1 + drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-i801.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index 8d71d5705b2a..d55b8ab2d10f 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -23,6 +23,7 @@ Supported adapters: * Intel Lynx Point (PCH) * Intel Lynx Point-LP (PCH) * Intel Avoton (SOC) + * Intel Wellsburg (PCH) Datasheets: Publicly available at the Intel website On Intel Patsburg and later chipsets, both the normal host SMBus controller diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8cbd9cd1e0fa..b588317f2393 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -107,6 +107,7 @@ config I2C_I801 Lynx Point (PCH) Lynx Point-LP (PCH) Avoton (SOC) + Wellsburg (PCH) This driver can also be built as a module. If so, the module will be called i2c-i801. diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index b00c29d8a5f1..76febfb09760 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -54,6 +54,10 @@ Lynx Point (PCH) 0x8c22 32 hard yes yes yes Lynx Point-LP (PCH) 0x9c22 32 hard yes yes yes Avoton (SOC) 0x1f3c 32 hard yes yes yes + Wellsburg (PCH) 0x8d22 32 hard yes yes yes + Wellsburg (PCH) MS 0x8d7d 32 hard yes yes yes + Wellsburg (PCH) MS 0x8d7e 32 hard yes yes yes + Wellsburg (PCH) MS 0x8d7f 32 hard yes yes yes Features supported by this driver: Software PEC no @@ -167,6 +171,10 @@ #define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS 0x8d22 +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0 0x8d7d +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1 0x8d7e +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2 0x8d7f #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22 struct i801_mux_config { @@ -801,6 +809,10 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) }, { 0, } }; @@ -1106,6 +1118,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0: case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1: case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2: + case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0: + case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1: + case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2: priv->features |= FEATURE_IDF; /* fall through */ default: From 724d5edac76d8c9a4198b74c80286df38ed81679 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 15 Feb 2013 08:51:40 +0000 Subject: [PATCH 2886/4607] i2c: fix i2c-ismt.c printk format warning Fix printk format warning. dma_addr_t can be 32-bit or 64-bit, so cast it to long long for printing. This also matches the printk format specifier that is already used. drivers/i2c/busses/i2c-ismt.c:532:3: warning: format '%llX' expects argument of type 'long long unsigned int', but argument 4 has type 'dma_addr_t' [-Wformat] Signed-off-by: Randy Dunlap Acked-by: Neil Horman Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-ismt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c index 3f7a9cb6dfdd..e9205ee8cf94 100644 --- a/drivers/i2c/busses/i2c-ismt.c +++ b/drivers/i2c/busses/i2c-ismt.c @@ -530,7 +530,7 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr, } dev_dbg(dev, " dma_addr = 0x%016llX\n", - dma_addr); + (unsigned long long)dma_addr); desc->dptr_low = lower_32_bits(dma_addr); desc->dptr_high = upper_32_bits(dma_addr); From 4700ebd1c355102f711dbf49ce7b22e943b1132b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 21 Jan 2013 14:02:52 +0100 Subject: [PATCH 2887/4607] arch/powerpc/platforms/85xx/p1022_ds.c: adjust duplicate test Delete successive tests to the same location. The code tested the result of a previous call, that itself was already tested. It is changed to test the result of the most recent call. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @s exists@ local idexpression y; expression x,e; @@ *if ( \(x == NULL\|IS_ERR(x)\|y != 0\) ) { ... when forall return ...; } ... when != \(y = e\|y += e\|y -= e\|y |= e\|y &= e\|y++\|y--\|&y\) when != \(XT_GETPAGE(...,y)\|WMI_CMD_BUF(...)\) *if ( \(x == NULL\|IS_ERR(x)\|y != 0\) ) { ... when forall return ...; } // Signed-off-by: Julia Lawall Signed-off-by: Kumar Gala --- arch/powerpc/platforms/85xx/p1022_ds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index 7328b8d74129..29b6a2cde56c 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -302,7 +302,7 @@ static void p1022ds_set_monitor_port(enum fsl_diu_monitor_port port) goto exit; } cs1_addr = lbc_br_to_phys(ecm, num_laws, br1); - if (!cs0_addr) { + if (!cs1_addr) { pr_err("p1022ds: could not determine physical address for CS1" " (BR1=%08x)\n", br1); goto exit; From d4d801d13b522099493f56b5909b1c44e63e3e2f Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 17 Jan 2013 17:26:54 -0600 Subject: [PATCH 2888/4607] powerpc/fsl: remove extraneous DIU platform functions The Freescale DIU driver was recently updated to not require every DIU platform function, so now we can remove the unneeded functions from some boards. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala --- arch/powerpc/platforms/512x/mpc512x_shared.c | 5 --- arch/powerpc/platforms/85xx/p1022_ds.c | 38 -------------------- arch/powerpc/platforms/85xx/p1022_rdk.c | 12 ------- 3 files changed, 55 deletions(-) diff --git a/arch/powerpc/platforms/512x/mpc512x_shared.c b/arch/powerpc/platforms/512x/mpc512x_shared.c index 35f14fda108a..c7f47cfa9c29 100644 --- a/arch/powerpc/platforms/512x/mpc512x_shared.c +++ b/arch/powerpc/platforms/512x/mpc512x_shared.c @@ -68,10 +68,6 @@ struct fsl_diu_shared_fb { bool in_use; }; -void mpc512x_set_monitor_port(enum fsl_diu_monitor_port port) -{ -} - #define DIU_DIV_MASK 0x000000ff void mpc512x_set_pixel_clock(unsigned int pixclock) { @@ -303,7 +299,6 @@ void __init mpc512x_setup_diu(void) } } - diu_ops.set_monitor_port = mpc512x_set_monitor_port; diu_ops.set_pixel_clock = mpc512x_set_pixel_clock; diu_ops.valid_monitor_port = mpc512x_valid_monitor_port; diu_ops.release_bootmem = mpc512x_release_bootmem; diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c index 29b6a2cde56c..c3e47144d0e4 100644 --- a/arch/powerpc/platforms/85xx/p1022_ds.c +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -106,42 +106,6 @@ (c2 << AD_COMP_2_SHIFT) | (c1 << AD_COMP_1_SHIFT) | \ (c0 << AD_COMP_0_SHIFT) | (size << AD_PIXEL_S_SHIFT)) -/** - * p1022ds_get_pixel_format: return the Area Descriptor for a given pixel depth - * - * The Area Descriptor is a 32-bit value that determine which bits in each - * pixel are to be used for each color. - */ -static u32 p1022ds_get_pixel_format(enum fsl_diu_monitor_port port, - unsigned int bits_per_pixel) -{ - switch (bits_per_pixel) { - case 32: - /* 0x88883316 */ - return MAKE_AD(3, 2, 0, 1, 3, 8, 8, 8, 8); - case 24: - /* 0x88082219 */ - return MAKE_AD(4, 0, 1, 2, 2, 0, 8, 8, 8); - case 16: - /* 0x65053118 */ - return MAKE_AD(4, 2, 1, 0, 1, 5, 6, 5, 0); - default: - pr_err("fsl-diu: unsupported pixel depth %u\n", bits_per_pixel); - return 0; - } -} - -/** - * p1022ds_set_gamma_table: update the gamma table, if necessary - * - * On some boards, the gamma table for some ports may need to be modified. - * This is not the case on the P1022DS, so we do nothing. -*/ -static void p1022ds_set_gamma_table(enum fsl_diu_monitor_port port, - char *gamma_table_base) -{ -} - struct fsl_law { u32 lawbar; u32 reserved1; @@ -510,8 +474,6 @@ static void __init p1022_ds_setup_arch(void) ppc_md.progress("p1022_ds_setup_arch()", 0); #if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) - diu_ops.get_pixel_format = p1022ds_get_pixel_format; - diu_ops.set_gamma_table = p1022ds_set_gamma_table; diu_ops.set_monitor_port = p1022ds_set_monitor_port; diu_ops.set_pixel_clock = p1022ds_set_pixel_clock; diu_ops.valid_monitor_port = p1022ds_valid_monitor_port; diff --git a/arch/powerpc/platforms/85xx/p1022_rdk.c b/arch/powerpc/platforms/85xx/p1022_rdk.c index 55ffa1cc380c..8c9297112b30 100644 --- a/arch/powerpc/platforms/85xx/p1022_rdk.c +++ b/arch/powerpc/platforms/85xx/p1022_rdk.c @@ -34,17 +34,6 @@ #define CLKDVDR_PXCKDLY 0x06000000 #define CLKDVDR_PXCLK_MASK 0x00FF0000 -/** - * p1022rdk_set_monitor_port: switch the output to a different monitor port - */ -static void p1022rdk_set_monitor_port(enum fsl_diu_monitor_port port) -{ - if (port != FSL_DIU_PORT_DVI) { - pr_err("p1022rdk: unsupported monitor port %i\n", port); - return; - } -} - /** * p1022rdk_set_pixel_clock: program the DIU's clock * @@ -124,7 +113,6 @@ static void __init p1022_rdk_setup_arch(void) ppc_md.progress("p1022_rdk_setup_arch()", 0); #if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) - diu_ops.set_monitor_port = p1022rdk_set_monitor_port; diu_ops.set_pixel_clock = p1022rdk_set_pixel_clock; diu_ops.valid_monitor_port = p1022rdk_valid_monitor_port; #endif From e0e8398e3a06580583b08ed756567a0c66fd725b Mon Sep 17 00:00:00 2001 From: Stef van Os Date: Wed, 13 Feb 2013 15:09:00 +0100 Subject: [PATCH 2889/4607] powerpc/85xx: Board support for ppa8548 Initial board support for the Prodrive PPA8548 AMC module. Board is an MPC8548 AMC platform used in RapidIO systems. This module is also used to test/work on mainline linux RapidIO software. PPA8548 overview: - 1.3 GHz Freescale PowerQUICC III MPC8548 processor - 1 GB DDR2 @ 266 MHz - 8 MB NOR flash - Serial RapidIO 1.2 - 1 x 10/100/1000 BASE-T front ethernet - 1 x 1000 BASE-BX ethernet on AMC connector Signed-off-by: Stef van Os Acked-by: Timur Tabi Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/ppa8548.dts | 166 ++++++++++++++++++++ arch/powerpc/configs/85xx/ppa8548_defconfig | 65 ++++++++ arch/powerpc/platforms/85xx/Kconfig | 7 + arch/powerpc/platforms/85xx/Makefile | 1 + arch/powerpc/platforms/85xx/ppa8548.c | 98 ++++++++++++ 5 files changed, 337 insertions(+) create mode 100644 arch/powerpc/boot/dts/ppa8548.dts create mode 100644 arch/powerpc/configs/85xx/ppa8548_defconfig create mode 100644 arch/powerpc/platforms/85xx/ppa8548.c diff --git a/arch/powerpc/boot/dts/ppa8548.dts b/arch/powerpc/boot/dts/ppa8548.dts new file mode 100644 index 000000000000..f97eceed610a --- /dev/null +++ b/arch/powerpc/boot/dts/ppa8548.dts @@ -0,0 +1,166 @@ +/* + * PPA8548 Device Tree Source (36-bit address map) + * Copyright 2013 Prodrive B.V. + * + * Based on: + * MPC8548 CDS Device Tree Source (36-bit address map) + * Copyright 2012 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/include/ "fsl/mpc8548si-pre.dtsi" + +/ { + model = "ppa8548"; + compatible = "ppa8548"; + #address-cells = <2>; + #size-cells = <2>; + interrupt-parent = <&mpic>; + + memory { + device_type = "memory"; + reg = <0 0 0x0 0x40000000>; + }; + + lbc: localbus@fe0005000 { + reg = <0xf 0xe0005000 0 0x1000>; + ranges = <0x0 0x0 0xf 0xff800000 0x00800000>; + }; + + soc: soc8548@fe0000000 { + ranges = <0 0xf 0xe0000000 0x100000>; + }; + + pci0: pci@fe0008000 { + /* ppa8548 board doesn't support PCI */ + status = "disabled"; + }; + + pci1: pci@fe0009000 { + /* ppa8548 board doesn't support PCI */ + status = "disabled"; + }; + + pci2: pcie@fe000a000 { + /* ppa8548 board doesn't support PCI */ + status = "disabled"; + }; + + rio: rapidio@fe00c0000 { + reg = <0xf 0xe00c0000 0x0 0x11000>; + port1 { + ranges = <0x0 0x0 0x0 0x80000000 0x0 0x40000000>; + }; + }; +}; + +&lbc { + nor@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "cfi-flash"; + reg = <0x0 0x0 0x00800000>; + bank-width = <2>; + device-width = <2>; + + partition@0 { + reg = <0x0 0x7A0000>; + label = "user"; + }; + + partition@7A0000 { + reg = <0x7A0000 0x20000>; + label = "env"; + read-only; + }; + + partition@7C0000 { + reg = <0x7C0000 0x40000>; + label = "u-boot"; + read-only; + }; + }; +}; + +&soc { + i2c@3000 { + rtc@6f { + compatible = "intersil,isl1208"; + reg = <0x6f>; + }; + }; + + i2c@3100 { + }; + + /* + * Only ethernet controller @25000 and @26000 are used. + * Use alias enet2 and enet3 for the remainig controllers, + * to stay compatible with mpc8548si-pre.dtsi. + */ + enet2: ethernet@24000 { + status = "disabled"; + }; + + mdio@24520 { + phy0: ethernet-phy@0 { + interrupts = <7 1 0 0>; + reg = <0x0>; + device_type = "ethernet-phy"; + }; + phy1: ethernet-phy@1 { + interrupts = <8 1 0 0>; + reg = <0x1>; + device_type = "ethernet-phy"; + }; + tbi0: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet0: ethernet@25000 { + tbi-handle = <&tbi1>; + phy-handle = <&phy0>; + }; + + mdio@25520 { + tbi1: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet1: ethernet@26000 { + tbi-handle = <&tbi2>; + phy-handle = <&phy1>; + }; + + mdio@26520 { + tbi2: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + enet3: ethernet@27000 { + status = "disabled"; + }; + + mdio@27520 { + tbi3: tbi-phy@11 { + reg = <0x11>; + device_type = "tbi-phy"; + }; + }; + + crypto@30000 { + status = "disabled"; + }; +}; + +/include/ "fsl/mpc8548si-post.dtsi" diff --git a/arch/powerpc/configs/85xx/ppa8548_defconfig b/arch/powerpc/configs/85xx/ppa8548_defconfig new file mode 100644 index 000000000000..a11337de8aa2 --- /dev/null +++ b/arch/powerpc/configs/85xx/ppa8548_defconfig @@ -0,0 +1,65 @@ +CONFIG_PPC_85xx=y +CONFIG_PPA8548=y +CONFIG_DTC=y +CONFIG_DEFAULT_UIMAGE=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +# CONFIG_PCI is not set +# CONFIG_USB_SUPPORT is not set +CONFIG_ADVANCED_OPTIONS=y +CONFIG_LOWMEM_SIZE_BOOL=y +CONFIG_LOWMEM_SIZE=0x40000000 +CONFIG_LOWMEM_CAM_NUM_BOOL=y +CONFIG_LOWMEM_CAM_NUM=4 +CONFIG_PAGE_OFFSET_BOOL=y +CONFIG_PAGE_OFFSET=0xb0000000 +CONFIG_KERNEL_START_BOOL=y +CONFIG_KERNEL_START=0xb0000000 +# CONFIG_PHYSICAL_START_BOOL is not set +CONFIG_PHYSICAL_START=0x00000000 +CONFIG_PHYSICAL_ALIGN=0x04000000 +CONFIG_TASK_SIZE_BOOL=y +CONFIG_TASK_SIZE=0xb0000000 + +CONFIG_FSL_LBC=y +CONFIG_FSL_DMA=y +CONFIG_FSL_RIO=y + +CONFIG_RAPIDIO=y +CONFIG_RAPIDIO_DMA_ENGINE=y +CONFIG_RAPIDIO_TSI57X=y +CONFIG_RAPIDIO_TSI568=y +CONFIG_RAPIDIO_CPS_XX=y +CONFIG_RAPIDIO_CPS_GEN2=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_PROC_DEVICETREE=y + +CONFIG_MTD=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_PHYSMAP_OF=y + +CONFIG_I2C=y +CONFIG_I2C_MPC=y +CONFIG_I2C_CHARDEV +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_DRV_ISL1208=y + +CONFIG_NET=y +CONFIG_INET=y +CONFIG_IP_PNP=y +CONFIG_NETDEVICES=y +CONFIG_MII=y +CONFIG_GIANFAR=y +CONFIG_MARVELL_PHY=y +CONFIG_NFS_FS=y +CONFIG_ROOT_NFS=y diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index 651788cbc6e6..bcc53aa09bf7 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -191,6 +191,13 @@ config SBC8548 help This option enables support for the Wind River SBC8548 board +config PPA8548 + bool "Prodrive PPA8548" + help + This option enables support for the Prodrive PPA8548 board. + select DEFAULT_UIMAGE + select HAS_RAPIDIO + config GE_IMP3A bool "GE Intelligent Platforms IMP3A" select DEFAULT_UIMAGE diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile index 9db31dcbd320..07d0dbb141c0 100644 --- a/arch/powerpc/platforms/85xx/Makefile +++ b/arch/powerpc/platforms/85xx/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_P5040_DS) += p5040_ds.o corenet_ds.o obj-$(CONFIG_STX_GP3) += stx_gp3.o obj-$(CONFIG_TQM85xx) += tqm85xx.o obj-$(CONFIG_SBC8548) += sbc8548.o +obj-$(CONFIG_PPA8548) += ppa8548.o obj-$(CONFIG_SOCRATES) += socrates.o socrates_fpga_pic.o obj-$(CONFIG_KSI8560) += ksi8560.o obj-$(CONFIG_XES_MPC85xx) += xes_mpc85xx.o diff --git a/arch/powerpc/platforms/85xx/ppa8548.c b/arch/powerpc/platforms/85xx/ppa8548.c new file mode 100644 index 000000000000..6a7704b92c3b --- /dev/null +++ b/arch/powerpc/platforms/85xx/ppa8548.c @@ -0,0 +1,98 @@ +/* + * ppa8548 setup and early boot code. + * + * Copyright 2009 Prodrive B.V.. + * + * By Stef van Os (see MAINTAINERS for contact information) + * + * Based on the SBC8548 support - Copyright 2007 Wind River Systems Inc. + * Based on the MPC8548CDS support - Copyright 2005 Freescale Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static void __init ppa8548_pic_init(void) +{ + struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN, + 0, 256, " OpenPIC "); + BUG_ON(mpic == NULL); + mpic_init(mpic); +} + +/* + * Setup the architecture + */ +static void __init ppa8548_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("ppa8548_setup_arch()", 0); +} + +static void ppa8548_show_cpuinfo(struct seq_file *m) +{ + uint32_t svid, phid1; + + svid = mfspr(SPRN_SVR); + + seq_printf(m, "Vendor\t\t: Prodrive B.V.\n"); + seq_printf(m, "SVR\t\t: 0x%x\n", svid); + + /* Display cpu Pll setting */ + phid1 = mfspr(SPRN_HID1); + seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f)); +} + +static struct of_device_id __initdata of_bus_ids[] = { + { .name = "soc", }, + { .type = "soc", }, + { .compatible = "simple-bus", }, + { .compatible = "gianfar", }, + { .compatible = "fsl,srio", }, + {}, +}; + +static int __init declare_of_platform_devices(void) +{ + of_platform_bus_probe(NULL, of_bus_ids, NULL); + + return 0; +} +machine_device_initcall(ppa8548, declare_of_platform_devices); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init ppa8548_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "ppa8548"); +} + +define_machine(ppa8548) { + .name = "ppa8548", + .probe = ppa8548_probe, + .setup_arch = ppa8548_setup_arch, + .init_IRQ = ppa8548_pic_init, + .show_cpuinfo = ppa8548_show_cpuinfo, + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; From 58823c72f6b95414fa11cb8a0e5f1d0e548c2f34 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 14 Feb 2013 18:13:33 +0530 Subject: [PATCH 2890/4607] i2c: tegra: remove warning dump if timeout happen in transfer If timeout error occurs in the i2c transfer then it was dumping warning of call stack. Remove the warning dump as there is may be possibility that some slave devices are busy and not responding the i2c communication. Signed-off-by: Laxman Dewangan Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 2dadb964c464..e58a58d7231c 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -588,7 +588,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, ret = wait_for_completion_timeout(&i2c_dev->msg_complete, TEGRA_I2C_TIMEOUT); tegra_i2c_mask_irq(i2c_dev, int_mask); - if (WARN_ON(ret == 0)) { + if (ret == 0) { dev_err(i2c_dev->dev, "i2c transfer timed out\n"); tegra_i2c_init(i2c_dev); From 52c5affc545053d37c0b05224bbf70f5336caa20 Mon Sep 17 00:00:00 2001 From: Varun Sethi Date: Mon, 14 Jan 2013 16:58:00 +0530 Subject: [PATCH 2891/4607] powerpc/fsl_pci: Store the pci ctlr device ptr in the pci ctlr struct The pci controller structure has a provision to store the device structure pointer of the corresponding platform device. Currently this information is not stored during fsl pci controller initialization. This information is required while dealing with iommu groups for pci devices connected to the fsl pci controller. For the case where the pci devices can't be paritioned, they would fall under the same device group as the pci controller. This patch stores the platform device information in the pci controller structure during initialization. Signed-off-by: Varun Sethi Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_pci.c | 9 +++++++-- arch/powerpc/sysdev/fsl_pci.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index f213fb6dabfc..682084dba19b 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -421,13 +421,16 @@ void fsl_pcibios_fixup_bus(struct pci_bus *bus) } } -int __init fsl_add_bridge(struct device_node *dev, int is_primary) +int __init fsl_add_bridge(struct platform_device *pdev, int is_primary) { int len; struct pci_controller *hose; struct resource rsrc; const int *bus_range; u8 hdr_type, progif; + struct device_node *dev; + + dev = pdev->dev.of_node; if (!of_device_is_available(dev)) { pr_warning("%s: disabled\n", dev->full_name); @@ -453,6 +456,8 @@ int __init fsl_add_bridge(struct device_node *dev, int is_primary) if (!hose) return -ENOMEM; + /* set platform device as the parent */ + hose->parent = &pdev->dev; hose->first_busno = bus_range ? bus_range[0] : 0x0; hose->last_busno = bus_range ? bus_range[1] : 0xff; @@ -885,7 +890,7 @@ static int fsl_pci_probe(struct platform_device *pdev) #endif node = pdev->dev.of_node; - ret = fsl_add_bridge(node, fsl_pci_primary == node); + ret = fsl_add_bridge(pdev, fsl_pci_primary == node); #ifdef CONFIG_SWIOTLB if (ret == 0) { diff --git a/arch/powerpc/sysdev/fsl_pci.h b/arch/powerpc/sysdev/fsl_pci.h index d078537adece..c495c00c8740 100644 --- a/arch/powerpc/sysdev/fsl_pci.h +++ b/arch/powerpc/sysdev/fsl_pci.h @@ -91,7 +91,7 @@ struct ccsr_pci { __be32 pex_err_cap_r3; /* 0x.e34 - PCIE error capture register 0 */ }; -extern int fsl_add_bridge(struct device_node *dev, int is_primary); +extern int fsl_add_bridge(struct platform_device *pdev, int is_primary); extern void fsl_pcibios_fixup_bus(struct pci_bus *bus); extern int mpc83xx_add_bridge(struct device_node *dev); u64 fsl_pci_immrbar_base(struct pci_controller *hose); From 7c509ee01496a170fe4329f076c334591b6f49d0 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 21 Jan 2013 19:56:41 -0600 Subject: [PATCH 2892/4607] powerpc/mpic: allow coreint to be determined by MPIC version This will be used by the qemu-e500 platform, as the MPIC version (and thus whether we have coreint) depends on how QEMU is configured. Signed-off-by: Scott Wood Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/mpic.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 66944255520d..d30e6a676c89 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1182,6 +1182,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, const char *vers; const u32 *psrc; u32 last_irq; + u32 fsl_version = 0; /* Default MPIC search parameters */ static const struct of_device_id __initconst mpic_device_id[] = { @@ -1314,7 +1315,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); if (mpic->flags & MPIC_FSL) { - u32 brr1, version; + u32 brr1; int ret; /* @@ -1327,7 +1328,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, MPIC_FSL_BRR1); - version = brr1 & MPIC_FSL_BRR1_VER; + fsl_version = brr1 & MPIC_FSL_BRR1_VER; /* Error interrupt mask register (EIMR) is required for * handling individual device error interrupts. EIMR @@ -1342,11 +1343,30 @@ struct mpic * __init mpic_alloc(struct device_node *node, * is the number of vectors which have been consumed by * ipis and timer interrupts. */ - if (version >= 0x401) { + if (fsl_version >= 0x401) { ret = mpic_setup_error_int(mpic, intvec_top - 12); if (ret) return NULL; } + + } + + /* + * EPR is only available starting with v4.0. To support + * platforms that don't know the MPIC version at compile-time, + * such as qemu-e500, turn off coreint if this MPIC doesn't + * support it. Note that we never enable it if it wasn't + * requested in the first place. + * + * This is done outside the MPIC_FSL check, so that we + * also disable coreint if the MPIC node doesn't have + * an "fsl,mpic" compatible at all. This will be the case + * with device trees generated by older versions of QEMU. + * fsl_version will be zero if MPIC_FSL is not set. + */ + if (fsl_version < 0x400 && (flags & MPIC_ENABLE_COREINT)) { + WARN_ON(ppc_md.get_irq != mpic_get_coreint_irq); + ppc_md.get_irq = mpic_get_irq; } /* Reset */ From 64871ff6e8f19c99bc6b9a93e4f8926e1650e61e Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 21 Jan 2013 19:56:43 -0600 Subject: [PATCH 2893/4607] powerpc/e500/qemu-e500: enable coreint The MPIC code will disable coreint if it detects an insufficient MPIC version. Signed-off-by: Scott Wood Signed-off-by: Kumar Gala --- arch/powerpc/platforms/85xx/qemu_e500.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/85xx/qemu_e500.c b/arch/powerpc/platforms/85xx/qemu_e500.c index f6ea5618c733..5cefc5a9a144 100644 --- a/arch/powerpc/platforms/85xx/qemu_e500.c +++ b/arch/powerpc/platforms/85xx/qemu_e500.c @@ -29,9 +29,10 @@ void __init qemu_e500_pic_init(void) { struct mpic *mpic; + unsigned int flags = MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU | + MPIC_ENABLE_COREINT; - mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU, - 0, 256, " OpenPIC "); + mpic = mpic_alloc(NULL, 0, flags, 0, 256, " OpenPIC "); BUG_ON(mpic == NULL); mpic_init(mpic); @@ -66,7 +67,7 @@ define_machine(qemu_e500) { #ifdef CONFIG_PCI .pcibios_fixup_bus = fsl_pcibios_fixup_bus, #endif - .get_irq = mpic_get_irq, + .get_irq = mpic_get_coreint_irq, .restart = fsl_rstcr_restart, .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, From 6f60cbd3ae442cb35861bb522f388db123d42ec1 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Fri, 15 Feb 2013 11:31:02 -0700 Subject: [PATCH 2894/4607] btrfs: access superblock via pagecache in scan_one_device btrfs_scan_one_device is calling set_blocksize() which can race with a concurrent process making dirty page cache pages. It can end up dropping dirty page cache pages on the floor, which isn't very nice when someone is just running btrfs dev scan to find filesystems on the box. Now that udev is registering btrfs devices as it discovers them, we can actually end up racing with our own mkfs program too. When this happens, we drop some of the important blocks written by mkfs. This commit changes scan_one_device to read the super out of the page cache instead of trying to use bread. This way we don't have to care about the blocksize of the device. This also drops the invalidate_bdev() call. It wasn't very polite to invalidate during the scan either. mkfs is putting the super into the page cache, there's no reason to invalidate at this point. Signed-off-by: David Sterba Signed-off-by: Chris Mason --- fs/btrfs/volumes.c | 70 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 5cbb7f4b1672..5349e17d8863 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -792,26 +792,77 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, return ret; } +/* + * Look for a btrfs signature on a device. This may be called out of the mount path + * and we are not allowed to call set_blocksize during the scan. The superblock + * is read via pagecache + */ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, struct btrfs_fs_devices **fs_devices_ret) { struct btrfs_super_block *disk_super; struct block_device *bdev; - struct buffer_head *bh; - int ret; + struct page *page; + void *p; + int ret = -EINVAL; u64 devid; u64 transid; u64 total_devices; + u64 bytenr; + pgoff_t index; + /* + * we would like to check all the supers, but that would make + * a btrfs mount succeed after a mkfs from a different FS. + * So, we need to add a special mount option to scan for + * later supers, using BTRFS_SUPER_MIRROR_MAX instead + */ + bytenr = btrfs_sb_offset(0); flags |= FMODE_EXCL; mutex_lock(&uuid_mutex); - ret = btrfs_get_bdev_and_sb(path, flags, holder, 0, &bdev, &bh); - if (ret) + + bdev = blkdev_get_by_path(path, flags, holder); + + if (IS_ERR(bdev)) { + ret = PTR_ERR(bdev); + printk(KERN_INFO "btrfs: open %s failed\n", path); goto error; - disk_super = (struct btrfs_super_block *)bh->b_data; + } + + /* make sure our super fits in the device */ + if (bytenr + PAGE_CACHE_SIZE >= i_size_read(bdev->bd_inode)) + goto error_bdev_put; + + /* make sure our super fits in the page */ + if (sizeof(*disk_super) > PAGE_CACHE_SIZE) + goto error_bdev_put; + + /* make sure our super doesn't straddle pages on disk */ + index = bytenr >> PAGE_CACHE_SHIFT; + if ((bytenr + sizeof(*disk_super) - 1) >> PAGE_CACHE_SHIFT != index) + goto error_bdev_put; + + /* pull in the page with our super */ + page = read_cache_page_gfp(bdev->bd_inode->i_mapping, + index, GFP_NOFS); + + if (IS_ERR_OR_NULL(page)) + goto error_bdev_put; + + p = kmap(page); + + /* align our pointer to the offset of the super block */ + disk_super = p + (bytenr & ~PAGE_CACHE_MASK); + + if (btrfs_super_bytenr(disk_super) != bytenr || + strncmp((char *)(&disk_super->magic), BTRFS_MAGIC, + sizeof(disk_super->magic))) + goto error_unmap; + devid = btrfs_stack_device_id(&disk_super->dev_item); transid = btrfs_super_generation(disk_super); total_devices = btrfs_super_num_devices(disk_super); + if (disk_super->label[0]) { if (disk_super->label[BTRFS_LABEL_SIZE - 1]) disk_super->label[BTRFS_LABEL_SIZE - 1] = '\0'; @@ -819,12 +870,19 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder, } else { printk(KERN_INFO "device fsid %pU ", disk_super->fsid); } + printk(KERN_CONT "devid %llu transid %llu %s\n", (unsigned long long)devid, (unsigned long long)transid, path); + ret = device_list_add(path, disk_super, devid, fs_devices_ret); if (!ret && fs_devices_ret) (*fs_devices_ret)->total_devices = total_devices; - brelse(bh); + +error_unmap: + kunmap(page); + page_cache_release(page); + +error_bdev_put: blkdev_put(bdev, flags); error: mutex_unlock(&uuid_mutex); From 42913c7992e9aca4deded016a05f6654e9b0807b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 3 Jan 2013 09:34:20 +0000 Subject: [PATCH 2895/4607] MIPS: Loongson2: Use clk API instead of direct dereferences A struct clk value is intended to be an abstract pointer, so it should be manipulated using the various API functions. clk_put is additionally added on the failure paths. The semantic match that finds the first problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression e,e1; identifier i; @@ *e = clk_get(...) ... when != e = e1 when any *e->i // Signed-off-by: Julia Lawall Cc: kernel-janitors@vger.kernel.org Cc: linux-mips@linux-mips.org, Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/4751/ Signed-off-by: Ralf Baechle --- arch/mips/kernel/cpufreq/loongson2_cpufreq.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/arch/mips/kernel/cpufreq/loongson2_cpufreq.c b/arch/mips/kernel/cpufreq/loongson2_cpufreq.c index bb51d3193ad4..3237c5235f9c 100644 --- a/arch/mips/kernel/cpufreq/loongson2_cpufreq.c +++ b/arch/mips/kernel/cpufreq/loongson2_cpufreq.c @@ -107,6 +107,8 @@ static int loongson2_cpufreq_target(struct cpufreq_policy *policy, static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy) { int i; + unsigned long rate; + int ret; if (!cpu_online(policy->cpu)) return -ENODEV; @@ -117,15 +119,22 @@ static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy) return PTR_ERR(cpuclk); } - cpuclk->rate = cpu_clock_freq / 1000; - if (!cpuclk->rate) + rate = cpu_clock_freq / 1000; + if (!rate) { + clk_put(cpuclk); return -EINVAL; + } + ret = clk_set_rate(cpuclk, rate); + if (ret) { + clk_put(cpuclk); + return ret; + } /* clock table init */ for (i = 2; (loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END); i++) - loongson2_clockmod_table[i].frequency = (cpuclk->rate * i) / 8; + loongson2_clockmod_table[i].frequency = (rate * i) / 8; policy->cur = loongson2_cpufreq_get(policy->cpu); From 0e49caf661fbba12c9a38eca13b64d6680259018 Mon Sep 17 00:00:00 2001 From: Venkat Subbiah Date: Sun, 2 Dec 2012 00:51:26 +0000 Subject: [PATCH 2896/4607] MIPS: Octeon: Adding driver to measure interrupt latency on Octeon. Signed-off-by: Venkat Subbiah [Rewrote timeing calculations] Signed-off-by: David Daney Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/4660/ Signed-off-by: Ralf Baechle --- arch/mips/cavium-octeon/Kconfig | 9 ++ arch/mips/cavium-octeon/Makefile | 1 + arch/mips/cavium-octeon/oct_ilm.c | 206 ++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+) create mode 100644 arch/mips/cavium-octeon/oct_ilm.c diff --git a/arch/mips/cavium-octeon/Kconfig b/arch/mips/cavium-octeon/Kconfig index 2f4f6d5e05b6..75a6df7fd265 100644 --- a/arch/mips/cavium-octeon/Kconfig +++ b/arch/mips/cavium-octeon/Kconfig @@ -94,4 +94,13 @@ config SWIOTLB select NEED_SG_DMA_LENGTH +config OCTEON_ILM + tristate "Module to measure interrupt latency using Octeon CIU Timer" + help + This driver is a module to measure interrupt latency using the + the CIU Timers on Octeon. + + To compile this driver as a module, choose M here. The module + will be called octeon-ilm + endif # CPU_CAVIUM_OCTEON diff --git a/arch/mips/cavium-octeon/Makefile b/arch/mips/cavium-octeon/Makefile index 3751c3918571..3595affb9772 100644 --- a/arch/mips/cavium-octeon/Makefile +++ b/arch/mips/cavium-octeon/Makefile @@ -18,6 +18,7 @@ obj-y += octeon-memcpy.o obj-y += executive/ obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_OCTEON_ILM) += oct_ilm.o DTS_FILES = octeon_3xxx.dts octeon_68xx.dts DTB_FILES = $(patsubst %.dts, %.dtb, $(DTS_FILES)) diff --git a/arch/mips/cavium-octeon/oct_ilm.c b/arch/mips/cavium-octeon/oct_ilm.c new file mode 100644 index 000000000000..71b213dbb621 --- /dev/null +++ b/arch/mips/cavium-octeon/oct_ilm.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMER_NUM 3 + +static bool reset_stats; + +struct latency_info { + u64 io_interval; + u64 cpu_interval; + u64 timer_start1; + u64 timer_start2; + u64 max_latency; + u64 min_latency; + u64 latency_sum; + u64 average_latency; + u64 interrupt_cnt; +}; + +static struct latency_info li; +static struct dentry *dir; + +static int show_latency(struct seq_file *m, void *v) +{ + u64 cpuclk, avg, max, min; + struct latency_info curr_li = li; + + cpuclk = octeon_get_clock_rate(); + + max = (curr_li.max_latency * 1000000000) / cpuclk; + min = (curr_li.min_latency * 1000000000) / cpuclk; + avg = (curr_li.latency_sum * 1000000000) / (cpuclk * curr_li.interrupt_cnt); + + seq_printf(m, "cnt: %10lld, avg: %7lld ns, max: %7lld ns, min: %7lld ns\n", + curr_li.interrupt_cnt, avg, max, min); + return 0; +} + +static int oct_ilm_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_latency, NULL); +} + +static const struct file_operations oct_ilm_ops = { + .open = oct_ilm_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int reset_statistics(void *data, u64 value) +{ + reset_stats = true; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(reset_statistics_ops, NULL, reset_statistics, "%llu\n"); + +static int init_debufs(void) +{ + struct dentry *show_dentry; + dir = debugfs_create_dir("oct_ilm", 0); + if (!dir) { + pr_err("oct_ilm: failed to create debugfs entry oct_ilm\n"); + return -1; + } + + show_dentry = debugfs_create_file("statistics", 0222, dir, NULL, + &oct_ilm_ops); + if (!show_dentry) { + pr_err("oct_ilm: failed to create debugfs entry oct_ilm/statistics\n"); + return -1; + } + + show_dentry = debugfs_create_file("reset", 0222, dir, NULL, + &reset_statistics_ops); + if (!show_dentry) { + pr_err("oct_ilm: failed to create debugfs entry oct_ilm/reset\n"); + return -1; + } + + return 0; + +} + +static void init_latency_info(struct latency_info *li, int startup) +{ + /* interval in milli seconds after which the interrupt will + * be triggered + */ + int interval = 1; + + if (startup) { + /* Calculating by the amounts io clock and cpu clock would + * increment in interval amount of ms + */ + li->io_interval = (octeon_get_io_clock_rate() * interval) / 1000; + li->cpu_interval = (octeon_get_clock_rate() * interval) / 1000; + } + li->timer_start1 = 0; + li->timer_start2 = 0; + li->max_latency = 0; + li->min_latency = (u64)-1; + li->latency_sum = 0; + li->interrupt_cnt = 0; +} + + +static void start_timer(int timer, u64 interval) +{ + union cvmx_ciu_timx timx; + unsigned long flags; + + timx.u64 = 0; + timx.s.one_shot = 1; + timx.s.len = interval; + raw_local_irq_save(flags); + li.timer_start1 = read_c0_cvmcount(); + cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64); + /* Read it back to force wait until register is written. */ + timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer)); + li.timer_start2 = read_c0_cvmcount(); + raw_local_irq_restore(flags); +} + + +static irqreturn_t cvm_oct_ciu_timer_interrupt(int cpl, void *dev_id) +{ + u64 last_latency; + u64 last_int_cnt; + + if (reset_stats) { + init_latency_info(&li, 0); + reset_stats = false; + } else { + last_int_cnt = read_c0_cvmcount(); + last_latency = last_int_cnt - (li.timer_start1 + li.cpu_interval); + li.interrupt_cnt++; + li.latency_sum += last_latency; + if (last_latency > li.max_latency) + li.max_latency = last_latency; + if (last_latency < li.min_latency) + li.min_latency = last_latency; + } + start_timer(TIMER_NUM, li.io_interval); + return IRQ_HANDLED; +} + +static void disable_timer(int timer) +{ + union cvmx_ciu_timx timx; + + timx.s.one_shot = 0; + timx.s.len = 0; + cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64); + /* Read it back to force immediate write of timer register*/ + timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer)); +} + +static __init int oct_ilm_module_init(void) +{ + int rc; + int irq = OCTEON_IRQ_TIMER0 + TIMER_NUM; + + rc = init_debufs(); + if (rc) { + WARN(1, "Could not create debugfs entries"); + return rc; + } + + rc = request_irq(irq, cvm_oct_ciu_timer_interrupt, IRQF_NO_THREAD, + "oct_ilm", 0); + if (rc) { + WARN(1, "Could not acquire IRQ %d", irq); + goto err_irq; + } + + init_latency_info(&li, 1); + start_timer(TIMER_NUM, li.io_interval); + + return 0; +err_irq: + debugfs_remove_recursive(dir); + return rc; +} + +static __exit void oct_ilm_module_exit(void) +{ + disable_timer(TIMER_NUM); + if (dir) + debugfs_remove_recursive(dir); + free_irq(OCTEON_IRQ_TIMER0 + TIMER_NUM, 0); +} + +module_exit(oct_ilm_module_exit); +module_init(oct_ilm_module_init); +MODULE_AUTHOR("Venkat Subbiah, Cavium"); +MODULE_DESCRIPTION("Measures interrupt latency on Octeon chips."); +MODULE_LICENSE("GPL"); From a96102be700f87283f168942cd09a2b30f86f324 Mon Sep 17 00:00:00 2001 From: "Steven J. Hill" Date: Fri, 7 Dec 2012 04:31:36 +0000 Subject: [PATCH 2897/4607] MIPS: Add printing of ISA version in cpuinfo. Display the MIPS ISA version release in the /proc/cpuinfo file. [ralf@linux-mips.org: Add support for MIPS I ... IV legacy architecture revisions. Also differenciate between MIPS32 and MIPS64 versions instead of lumping them together as just r1 and r2. Note to application programmers: this indicates the CPU's ISA level It does not imply the current execution environment does support it. For example an O32 application seeing "mips64r2" would still be restricted by by the execution environment to 32-bit - but the kernel could run mips64r2 code. The same for a 32-bit kernel running on a 64-bit processor. This field doesn't include ASEs or optional architecture modules nor other detailed flags such as the availability of an FPU.] Signed-off-by: Steven J. Hill Cc: linux-mips@linux-mips.org Patchwork: http://patchwork.linux-mips.org/patch/4714/ Signed-off-by: Ralf Baechle --- arch/mips/include/asm/cpu-features.h | 13 ++++ arch/mips/kernel/cpu-probe.c | 88 ++++++++++++++++++---------- arch/mips/kernel/proc.c | 22 +++++++ 3 files changed, 93 insertions(+), 30 deletions(-) diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h index e0ac24759d92..1e83b24fa461 100644 --- a/arch/mips/include/asm/cpu-features.h +++ b/arch/mips/include/asm/cpu-features.h @@ -130,6 +130,19 @@ #endif #endif +# define cpu_has_mips_1 (cpu_data[0].isa_level & MIPS_CPU_ISA_I) +#ifndef cpu_has_mips_2 +# define cpu_has_mips_2 (cpu_data[0].isa_level & MIPS_CPU_ISA_II) +#endif +#ifndef cpu_has_mips_3 +# define cpu_has_mips_3 (cpu_data[0].isa_level & MIPS_CPU_ISA_III) +#endif +#ifndef cpu_has_mips_4 +# define cpu_has_mips_4 (cpu_data[0].isa_level & MIPS_CPU_ISA_IV) +#endif +#ifndef cpu_has_mips_5 +# define cpu_has_mips_5 (cpu_data[0].isa_level & MIPS_CPU_ISA_V) +#endif # ifndef cpu_has_mips32r1 # define cpu_has_mips32r1 (cpu_data[0].isa_level & MIPS_CPU_ISA_M32R1) # endif diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 760139ee7a99..2656c898e337 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -331,6 +331,34 @@ static inline void cpu_probe_vmbits(struct cpuinfo_mips *c) #endif } +static void __cpuinit set_isa(struct cpuinfo_mips *c, unsigned int isa) +{ + switch (isa) { + case MIPS_CPU_ISA_M64R2: + c->isa_level |= MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2; + case MIPS_CPU_ISA_M64R1: + c->isa_level |= MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1; + case MIPS_CPU_ISA_V: + c->isa_level |= MIPS_CPU_ISA_V; + case MIPS_CPU_ISA_IV: + c->isa_level |= MIPS_CPU_ISA_IV; + case MIPS_CPU_ISA_III: + c->isa_level |= MIPS_CPU_ISA_I | MIPS_CPU_ISA_II | + MIPS_CPU_ISA_III; + break; + + case MIPS_CPU_ISA_M32R2: + c->isa_level |= MIPS_CPU_ISA_M32R2; + case MIPS_CPU_ISA_M32R1: + c->isa_level |= MIPS_CPU_ISA_M32R1; + case MIPS_CPU_ISA_II: + c->isa_level |= MIPS_CPU_ISA_II; + case MIPS_CPU_ISA_I: + c->isa_level |= MIPS_CPU_ISA_I; + break; + } +} + static char unknown_isa[] __cpuinitdata = KERN_ERR \ "Unsupported ISA type, c0.config0: %d."; @@ -348,10 +376,10 @@ static inline unsigned int decode_config0(struct cpuinfo_mips *c) case 0: switch ((config0 & MIPS_CONF_AR) >> 10) { case 0: - c->isa_level = MIPS_CPU_ISA_M32R1; + set_isa(c, MIPS_CPU_ISA_M32R1); break; case 1: - c->isa_level = MIPS_CPU_ISA_M32R2; + set_isa(c, MIPS_CPU_ISA_M32R2); break; default: goto unknown; @@ -360,10 +388,10 @@ static inline unsigned int decode_config0(struct cpuinfo_mips *c) case 2: switch ((config0 & MIPS_CONF_AR) >> 10) { case 0: - c->isa_level = MIPS_CPU_ISA_M64R1; + set_isa(c, MIPS_CPU_ISA_M64R1); break; case 1: - c->isa_level = MIPS_CPU_ISA_M64R2; + set_isa(c, MIPS_CPU_ISA_M64R2); break; default: goto unknown; @@ -494,7 +522,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R2000: c->cputype = CPU_R2000; __cpu_name[cpu] = "R2000"; - c->isa_level = MIPS_CPU_ISA_I; + set_isa(c, MIPS_CPU_ISA_I); c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE | MIPS_CPU_NOFPUEX; if (__cpu_has_fpu()) @@ -514,7 +542,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) c->cputype = CPU_R3000; __cpu_name[cpu] = "R3000"; } - c->isa_level = MIPS_CPU_ISA_I; + set_isa(c, MIPS_CPU_ISA_I); c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE | MIPS_CPU_NOFPUEX; if (__cpu_has_fpu()) @@ -540,7 +568,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) } } - c->isa_level = MIPS_CPU_ISA_III; + set_isa(c, MIPS_CPU_ISA_III); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_WATCH | MIPS_CPU_VCE | MIPS_CPU_LLSC; @@ -580,14 +608,14 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) __cpu_name[cpu] = "NEC Vr41xx"; break; } - c->isa_level = MIPS_CPU_ISA_III; + set_isa(c, MIPS_CPU_ISA_III); c->options = R4K_OPTS; c->tlbsize = 32; break; case PRID_IMP_R4300: c->cputype = CPU_R4300; __cpu_name[cpu] = "R4300"; - c->isa_level = MIPS_CPU_ISA_III; + set_isa(c, MIPS_CPU_ISA_III); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; c->tlbsize = 32; @@ -595,7 +623,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R4600: c->cputype = CPU_R4600; __cpu_name[cpu] = "R4600"; - c->isa_level = MIPS_CPU_ISA_III; + set_isa(c, MIPS_CPU_ISA_III); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; c->tlbsize = 48; @@ -610,13 +638,13 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) */ c->cputype = CPU_R4650; __cpu_name[cpu] = "R4650"; - c->isa_level = MIPS_CPU_ISA_III; + set_isa(c, MIPS_CPU_ISA_III); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC; c->tlbsize = 48; break; #endif case PRID_IMP_TX39: - c->isa_level = MIPS_CPU_ISA_I; + set_isa(c, MIPS_CPU_ISA_I); c->options = MIPS_CPU_TLB | MIPS_CPU_TX39_CACHE; if ((c->processor_id & 0xf0) == (PRID_REV_TX3927 & 0xf0)) { @@ -641,7 +669,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R4700: c->cputype = CPU_R4700; __cpu_name[cpu] = "R4700"; - c->isa_level = MIPS_CPU_ISA_III; + set_isa(c, MIPS_CPU_ISA_III); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; c->tlbsize = 48; @@ -649,7 +677,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_TX49: c->cputype = CPU_TX49XX; __cpu_name[cpu] = "R49XX"; - c->isa_level = MIPS_CPU_ISA_III; + set_isa(c, MIPS_CPU_ISA_III); c->options = R4K_OPTS | MIPS_CPU_LLSC; if (!(c->processor_id & 0x08)) c->options |= MIPS_CPU_FPU | MIPS_CPU_32FPR; @@ -658,7 +686,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R5000: c->cputype = CPU_R5000; __cpu_name[cpu] = "R5000"; - c->isa_level = MIPS_CPU_ISA_IV; + set_isa(c, MIPS_CPU_ISA_IV); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; c->tlbsize = 48; @@ -666,7 +694,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R5432: c->cputype = CPU_R5432; __cpu_name[cpu] = "R5432"; - c->isa_level = MIPS_CPU_ISA_IV; + set_isa(c, MIPS_CPU_ISA_IV); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_WATCH | MIPS_CPU_LLSC; c->tlbsize = 48; @@ -674,7 +702,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R5500: c->cputype = CPU_R5500; __cpu_name[cpu] = "R5500"; - c->isa_level = MIPS_CPU_ISA_IV; + set_isa(c, MIPS_CPU_ISA_IV); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_WATCH | MIPS_CPU_LLSC; c->tlbsize = 48; @@ -682,7 +710,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_NEVADA: c->cputype = CPU_NEVADA; __cpu_name[cpu] = "Nevada"; - c->isa_level = MIPS_CPU_ISA_IV; + set_isa(c, MIPS_CPU_ISA_IV); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_DIVEC | MIPS_CPU_LLSC; c->tlbsize = 48; @@ -690,7 +718,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R6000: c->cputype = CPU_R6000; __cpu_name[cpu] = "R6000"; - c->isa_level = MIPS_CPU_ISA_II; + set_isa(c, MIPS_CPU_ISA_II); c->options = MIPS_CPU_TLB | MIPS_CPU_FPU | MIPS_CPU_LLSC; c->tlbsize = 32; @@ -698,7 +726,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R6000A: c->cputype = CPU_R6000A; __cpu_name[cpu] = "R6000A"; - c->isa_level = MIPS_CPU_ISA_II; + set_isa(c, MIPS_CPU_ISA_II); c->options = MIPS_CPU_TLB | MIPS_CPU_FPU | MIPS_CPU_LLSC; c->tlbsize = 32; @@ -706,7 +734,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_RM7000: c->cputype = CPU_RM7000; __cpu_name[cpu] = "RM7000"; - c->isa_level = MIPS_CPU_ISA_IV; + set_isa(c, MIPS_CPU_ISA_IV); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; /* @@ -722,7 +750,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_RM9000: c->cputype = CPU_RM9000; __cpu_name[cpu] = "RM9000"; - c->isa_level = MIPS_CPU_ISA_IV; + set_isa(c, MIPS_CPU_ISA_IV); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; /* @@ -737,7 +765,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R8000: c->cputype = CPU_R8000; __cpu_name[cpu] = "RM8000"; - c->isa_level = MIPS_CPU_ISA_IV; + set_isa(c, MIPS_CPU_ISA_IV); c->options = MIPS_CPU_TLB | MIPS_CPU_4KEX | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_LLSC; @@ -746,7 +774,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R10000: c->cputype = CPU_R10000; __cpu_name[cpu] = "R10000"; - c->isa_level = MIPS_CPU_ISA_IV; + set_isa(c, MIPS_CPU_ISA_IV); c->options = MIPS_CPU_TLB | MIPS_CPU_4K_CACHE | MIPS_CPU_4KEX | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_COUNTER | MIPS_CPU_WATCH | @@ -756,7 +784,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R12000: c->cputype = CPU_R12000; __cpu_name[cpu] = "R12000"; - c->isa_level = MIPS_CPU_ISA_IV; + set_isa(c, MIPS_CPU_ISA_IV); c->options = MIPS_CPU_TLB | MIPS_CPU_4K_CACHE | MIPS_CPU_4KEX | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_COUNTER | MIPS_CPU_WATCH | @@ -766,7 +794,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_R14000: c->cputype = CPU_R14000; __cpu_name[cpu] = "R14000"; - c->isa_level = MIPS_CPU_ISA_IV; + set_isa(c, MIPS_CPU_ISA_IV); c->options = MIPS_CPU_TLB | MIPS_CPU_4K_CACHE | MIPS_CPU_4KEX | MIPS_CPU_FPU | MIPS_CPU_32FPR | MIPS_CPU_COUNTER | MIPS_CPU_WATCH | @@ -786,7 +814,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) break; } - c->isa_level = MIPS_CPU_ISA_III; + set_isa(c, MIPS_CPU_ISA_III); c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC | MIPS_CPU_32FPR; @@ -946,7 +974,7 @@ static inline void cpu_probe_nxp(struct cpuinfo_mips *c, unsigned int cpu) case PRID_IMP_PR4450: c->cputype = CPU_PR4450; __cpu_name[cpu] = "Philips PR4450"; - c->isa_level = MIPS_CPU_ISA_M32R1; + set_isa(c, MIPS_CPU_ISA_M32R1); break; } } @@ -1105,12 +1133,12 @@ static inline void cpu_probe_netlogic(struct cpuinfo_mips *c, int cpu) } if (c->cputype == CPU_XLP) { - c->isa_level = MIPS_CPU_ISA_M64R2; + set_isa(c, MIPS_CPU_ISA_M64R2); c->options |= (MIPS_CPU_FPU | MIPS_CPU_ULRI | MIPS_CPU_MCHECK); /* This will be updated again after all threads are woken up */ c->tlbsize = ((read_c0_config6() >> 16) & 0xffff) + 1; } else { - c->isa_level = MIPS_CPU_ISA_M64R1; + set_isa(c, MIPS_CPU_ISA_M64R1); c->tlbsize = ((read_c0_config1() >> 25) & 0x3f) + 1; } } diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 9dafed058136..79d4b8edbd76 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -64,6 +64,28 @@ static int show_cpuinfo(struct seq_file *m, void *v) cpu_data[n].watch_reg_masks[i]); seq_printf(m, "]\n"); } + if (cpu_has_mips_r) { + seq_printf(m, "isa\t\t\t:"); + if (cpu_has_mips_1) + seq_printf(m, "%s", "mips1"); + if (cpu_has_mips_2) + seq_printf(m, "%s", " mips2"); + if (cpu_has_mips_3) + seq_printf(m, "%s", " mips3"); + if (cpu_has_mips_4) + seq_printf(m, "%s", " mips4"); + if (cpu_has_mips_5) + seq_printf(m, "%s", " mips5"); + if (cpu_has_mips32r1) + seq_printf(m, "%s", " mips32r1"); + if (cpu_has_mips32r2) + seq_printf(m, "%s", " mips32r2"); + if (cpu_has_mips64r1) + seq_printf(m, "%s", " mips64r1"); + if (cpu_has_mips64r2) + seq_printf(m, "%s", " mips64r2"); + seq_printf(m, "\n"); + } seq_printf(m, "ASEs implemented\t:"); if (cpu_has_mips16) seq_printf(m, "%s", " mips16"); From 8e8dc33543504830e6444607ba856e60679995f1 Mon Sep 17 00:00:00 2001 From: "Steven J. Hill" Date: Thu, 24 Jan 2013 16:26:35 +0000 Subject: [PATCH 2898/4607] MIPS: Redefine value of BRK_BUG. The BRK_BUG value is used in the BUG and __BUG_ON inline macros. For standard MIPS cores the code in the 'tne' instruction is 10-bits long. In microMIPS, the 'tne' instruction is recoded and the code can only be 4-bits long. We change the value to 12 instead of 512 so that both classic and microMIPS kernels build. [ralf@linux-mips.org: Many of the break codes starting from 0 are used across many MIPS UNIX variants. Codes starting from 512 are operating system specific additions. 1023 again is also used by other operating systems] Signed-off-by: Steven J. Hill Signed-off-by: Ralf Baechle --- arch/mips/include/uapi/asm/break.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/include/uapi/asm/break.h b/arch/mips/include/uapi/asm/break.h index e5fa7b5b0556..73455e9be7a8 100644 --- a/arch/mips/include/uapi/asm/break.h +++ b/arch/mips/include/uapi/asm/break.h @@ -27,7 +27,7 @@ #define BRK_STACKOVERFLOW 9 /* For Ada stackchecking */ #define BRK_NORLD 10 /* No rld found - not used by Linux/MIPS */ #define _BRK_THREADBP 11 /* For threads, user bp (used by debuggers) */ -#define BRK_BUG 512 /* Used by BUG() */ +#define BRK_BUG 12 /* Used by BUG() */ #define BRK_KDB 513 /* Used in KDB_ENTER() */ #define BRK_MEMU 514 /* Used by FPU emulator */ #define BRK_KPROBE_BP 515 /* Kprobe break */ From 5b003119ab6a12f03ab6ec43a1c921c9469945a4 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 24 Jan 2013 22:01:48 +0100 Subject: [PATCH 2899/4607] MIPS: Cleanup break and trap codes. Very ancient out-of-tree KDB versions were using BRK_KDB code but it's unused in modern kernels since a long time. Delete it. The microMIPS encoding only reserves 4 bits for a trap code so it's time for further weedkilling. Signed-off-by: Ralf Baechle --- arch/mips/include/uapi/asm/break.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/mips/include/uapi/asm/break.h b/arch/mips/include/uapi/asm/break.h index 73455e9be7a8..6f61d08e3d18 100644 --- a/arch/mips/include/uapi/asm/break.h +++ b/arch/mips/include/uapi/asm/break.h @@ -16,19 +16,11 @@ * non-Linux/MIPS object files or make use of them in the future. */ #define BRK_USERBP 0 /* User bp (used by debuggers) */ -#define BRK_KERNELBP 1 /* Break in the kernel */ -#define BRK_ABORT 2 /* Sometimes used by abort(3) to SIGIOT */ -#define BRK_BD_TAKEN 3 /* For bd slot emulation - not implemented */ -#define BRK_BD_NOTTAKEN 4 /* For bd slot emulation - not implemented */ #define BRK_SSTEPBP 5 /* User bp (used by debuggers) */ #define BRK_OVERFLOW 6 /* Overflow check */ #define BRK_DIVZERO 7 /* Divide by zero check */ #define BRK_RANGE 8 /* Range error check */ -#define BRK_STACKOVERFLOW 9 /* For Ada stackchecking */ -#define BRK_NORLD 10 /* No rld found - not used by Linux/MIPS */ -#define _BRK_THREADBP 11 /* For threads, user bp (used by debuggers) */ #define BRK_BUG 12 /* Used by BUG() */ -#define BRK_KDB 513 /* Used in KDB_ENTER() */ #define BRK_MEMU 514 /* Used by FPU emulator */ #define BRK_KPROBE_BP 515 /* Kprobe break */ #define BRK_KPROBE_SSTEPBP 516 /* Kprobe single step software implementation */ From 838bf09d4fa01d39417e42c731aa31d7a256fb7c Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 15 Feb 2013 15:02:22 -0700 Subject: [PATCH 2900/4607] pwm: tegra: assume CONFIG_OF Tegra only supports, and always enables, device tree. Remove all ifdefs for DT support from the driver. Signed-off-by: Stephen Warren Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 30c0e2b70ce8..f7770312c1a9 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -233,7 +233,6 @@ static int tegra_pwm_remove(struct platform_device *pdev) return pwmchip_remove(&pc->chip); } -#ifdef CONFIG_OF static struct of_device_id tegra_pwm_of_match[] = { { .compatible = "nvidia,tegra20-pwm" }, { .compatible = "nvidia,tegra30-pwm" }, @@ -241,12 +240,11 @@ static struct of_device_id tegra_pwm_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_pwm_of_match); -#endif static struct platform_driver tegra_pwm_driver = { .driver = { .name = "tegra-pwm", - .of_match_table = of_match_ptr(tegra_pwm_of_match), + .of_match_table = tegra_pwm_of_match, }, .probe = tegra_pwm_probe, .remove = tegra_pwm_remove, From b23523d8585906c0fb7c78616e3deca18c7fcce2 Mon Sep 17 00:00:00 2001 From: Stefan Hasko Date: Sat, 22 Dec 2012 02:29:21 +0000 Subject: [PATCH 2901/4607] RDMA/cxgb4: Fix cast warning Fix compile warning about cast to pointer from integer of different size. Signed-off-by: Stefan Hasko Acked-by: Steve Wise Signed-off-by: Roland Dreier --- drivers/infiniband/hw/cxgb4/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index dc6adb213cd6..80069ad595c1 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -533,7 +533,7 @@ static int c4iw_rdev_open(struct c4iw_rdev *rdev) PDBG("udb len 0x%x udb base %p db_reg %p gts_reg %p qpshift %lu " "qpmask 0x%x cqshift %lu cqmask 0x%x\n", (unsigned)pci_resource_len(rdev->lldi.pdev, 2), - (void *)pci_resource_start(rdev->lldi.pdev, 2), + (void *)(unsigned long)pci_resource_start(rdev->lldi.pdev, 2), rdev->lldi.db_reg, rdev->lldi.gts_reg, rdev->qpshift, rdev->qpmask, From 8ab10f75372fc59b95d96af40b9a3b4c2a0da059 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 10 Oct 2012 13:10:43 +0000 Subject: [PATCH 2902/4607] RDMA/amso1100: Use module_pci_driver() to simplify the code Use the module_pci_driver() macro to make the code simpler by eliminating module_init and module_exit calls. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Reviewed-by: Steve WIse Signed-off-by: Roland Dreier --- drivers/infiniband/hw/amso1100/c2.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/infiniband/hw/amso1100/c2.c b/drivers/infiniband/hw/amso1100/c2.c index 7275e727e0f5..d53cf519f42a 100644 --- a/drivers/infiniband/hw/amso1100/c2.c +++ b/drivers/infiniband/hw/amso1100/c2.c @@ -1238,15 +1238,4 @@ static struct pci_driver c2_pci_driver = { .remove = c2_remove, }; -static int __init c2_init_module(void) -{ - return pci_register_driver(&c2_pci_driver); -} - -static void __exit c2_exit_module(void) -{ - pci_unregister_driver(&c2_pci_driver); -} - -module_init(c2_init_module); -module_exit(c2_exit_module); +module_pci_driver(c2_pci_driver); From cab66d1273e6490603cf5256155584d38cecca68 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 4 Feb 2013 11:22:36 +0000 Subject: [PATCH 2903/4607] IB/mlx4: Fix bug unwinding on error in mlx4_ib_init_sriov() We have to decrement "i" before calling mlx4_ib_free_demux_ctx() or we free something that wasn't allocated. That's fine for free_pv_object() but it would lead to a NULL dereference calling mlx4_ib_free_demux_ctx(). The null dereference is because ->tun is NULL when we check: if (!ctx->tun[i]) Also we didn't free ->sriov.demux[0] so it was a small leak. Signed-off-by: Dan Carpenter Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/mad.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 0a903c129f0a..934792c477bc 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -1999,16 +1999,17 @@ int mlx4_ib_init_sriov(struct mlx4_ib_dev *dev) goto demux_err; err = mlx4_ib_alloc_demux_ctx(dev, &dev->sriov.demux[i], i + 1); if (err) - goto demux_err; + goto free_pv; } mlx4_ib_master_tunnels(dev, 1); return 0; +free_pv: + free_pv_object(dev, mlx4_master_func_num(dev->dev), i + 1); demux_err: - while (i > 0) { + while (--i >= 0) { free_pv_object(dev, mlx4_master_func_num(dev->dev), i + 1); mlx4_ib_free_demux_ctx(&dev->sriov.demux[i]); - --i; } mlx4_ib_device_unregister_sysfs(dev); From 6950a235b86cf4e73d2b8e5476e7d0eb8f61af63 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 21 Jan 2013 13:02:58 +0000 Subject: [PATCH 2904/4607] IB/mlx4: Adjust duplicate test Delete successive tests to the same location. The code tested the result of a previous allocation, that itself was already tested. It is changed to test the result of the most recent allocation. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @s exists@ local idexpression y; expression x,e; @@ *if ( \(x == NULL\|IS_ERR(x)\|y != 0\) ) { ... when forall return ...; } ... when != \(y = e\|y += e\|y -= e\|y |= e\|y &= e\|y++\|y--\|&y\) when != \(XT_GETPAGE(...,y)\|WMI_CMD_BUF(...)\) *if ( \(x == NULL\|IS_ERR(x)\|y != 0\) ) { ... when forall return ...; } // Signed-off-by: Julia Lawall Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c index 5b2a01dfb907..97516eb363b7 100644 --- a/drivers/infiniband/hw/mlx4/sysfs.c +++ b/drivers/infiniband/hw/mlx4/sysfs.c @@ -732,7 +732,7 @@ int mlx4_ib_device_register_sysfs(struct mlx4_ib_dev *dev) dev->ports_parent = kobject_create_and_add("ports", kobject_get(dev->iov_parent)); - if (!dev->iov_parent) { + if (!dev->ports_parent) { ret = -ENOMEM; goto err_ports; } From 181380b702eee1a9aca51354d7b87c7b08541fcf Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 16 Feb 2013 11:58:34 -0700 Subject: [PATCH 2905/4607] PCI/ACPI: Don't cache _PRT, and don't associate them with bus numbers Previously, we cached _PRT (PCI routing table, ACPI 5.0 sec 6.2.12) contents and associated each _PRT entry with a PCI bus number. The bus number association means dependencies on PCI device enumeration and bus number assignment, as well as on the PCI/ACPI binding process. After 4f535093cf ("PCI: Put pci_dev in device tree as early as possible"), these dependencies caused the IRQ issues reported by Peter: pci 0000:00:1e.0: PCI bridge to [bus 09] (subtractive decode) pci 0000:00:1e.0: can't derive routing for PCI INT A snd_ctxfi 0000:09:02.0: PCI INT A: no GSI - using ISA IRQ 5 irq 18: nobody cared (try booting with the "irqpoll" option) This patch removes _PRT caching. Instead, we evaluate _PRT as needed in the pci_enable_device() path. This also removes the dependency on PCI bus numbers: we can simply look at the _PRT associated with each bridge as we walk upstream toward the root. [bhelgaas: changelog] Reference: https://bugzilla.kernel.org/show_bug.cgi?id=53561 Reported-and-tested-by: Peter Hurley Suggested-by: Bjorn Helgaas Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_irq.c | 102 ++++++++++++------------------------ drivers/acpi/pci_root.c | 18 ------- drivers/pci/pci-acpi.c | 24 --------- include/acpi/acpi_drivers.h | 5 -- 4 files changed, 34 insertions(+), 115 deletions(-) diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 68a921d03247..41c5e1b799ef 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -53,9 +53,6 @@ struct acpi_prt_entry { u32 index; /* GSI, or link _CRS index */ }; -static LIST_HEAD(acpi_prt_list); -static DEFINE_SPINLOCK(acpi_prt_lock); - static inline char pin_name(int pin) { return 'A' + pin - 1; @@ -65,28 +62,6 @@ static inline char pin_name(int pin) PCI IRQ Routing Table (PRT) Support -------------------------------------------------------------------------- */ -static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(struct pci_dev *dev, - int pin) -{ - struct acpi_prt_entry *entry; - int segment = pci_domain_nr(dev->bus); - int bus = dev->bus->number; - int device = PCI_SLOT(dev->devfn); - - spin_lock(&acpi_prt_lock); - list_for_each_entry(entry, &acpi_prt_list, list) { - if ((segment == entry->id.segment) - && (bus == entry->id.bus) - && (device == entry->id.device) - && (pin == entry->pin)) { - spin_unlock(&acpi_prt_lock); - return entry; - } - } - spin_unlock(&acpi_prt_lock); - return NULL; -} - /* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */ static const struct dmi_system_id medion_md9580[] = { { @@ -184,11 +159,19 @@ static void do_prt_fixups(struct acpi_prt_entry *entry, } } -static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, - struct acpi_pci_routing_table *prt) +static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev, + int pin, struct acpi_pci_routing_table *prt, + struct acpi_prt_entry **entry_ptr) { + int segment = pci_domain_nr(dev->bus); + int bus = dev->bus->number; + int device = PCI_SLOT(dev->devfn); struct acpi_prt_entry *entry; + if (((prt->address >> 16) & 0xffff) != device || + prt->pin + 1 != pin) + return -ENODEV; + entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -237,43 +220,37 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, entry->id.device, pin_name(entry->pin), prt->source, entry->index)); - spin_lock(&acpi_prt_lock); - list_add_tail(&entry->list, &acpi_prt_list); - spin_unlock(&acpi_prt_lock); + *entry_ptr = entry; return 0; } -int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) +static int acpi_pci_irq_find_prt_entry(struct pci_dev *dev, + int pin, struct acpi_prt_entry **entry_ptr) { acpi_status status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_pci_routing_table *entry; + acpi_handle handle = NULL; - /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ - status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - if (ACPI_FAILURE(status)) + if (dev->bus->bridge) + handle = ACPI_HANDLE(dev->bus->bridge); + + if (!handle) return -ENODEV; - printk(KERN_DEBUG "ACPI: PCI Interrupt Routing Table [%s._PRT]\n", - (char *) buffer.pointer); - - kfree(buffer.pointer); - - buffer.length = ACPI_ALLOCATE_BUFFER; - buffer.pointer = NULL; - + /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ status = acpi_get_irq_routing_table(handle, &buffer); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRT [%s]", - acpi_format_exception(status))); kfree(buffer.pointer); return -ENODEV; } entry = buffer.pointer; while (entry && (entry->length > 0)) { - acpi_pci_irq_add_entry(handle, segment, bus, entry); + if (!acpi_pci_irq_check_entry(handle, dev, pin, + entry, entry_ptr)) + break; entry = (struct acpi_pci_routing_table *) ((unsigned long)entry + entry->length); } @@ -282,23 +259,6 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) return 0; } -void acpi_pci_irq_del_prt(int segment, int bus) -{ - struct acpi_prt_entry *entry, *tmp; - - printk(KERN_DEBUG - "ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n", - segment, bus); - spin_lock(&acpi_prt_lock); - list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) { - if (segment == entry->id.segment && bus == entry->id.bus) { - list_del(&entry->list); - kfree(entry); - } - } - spin_unlock(&acpi_prt_lock); -} - /* -------------------------------------------------------------------------- PCI Interrupt Routing Support -------------------------------------------------------------------------- */ @@ -359,12 +319,13 @@ static int acpi_reroute_boot_interrupt(struct pci_dev *dev, static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) { - struct acpi_prt_entry *entry; + struct acpi_prt_entry *entry = NULL; struct pci_dev *bridge; u8 bridge_pin, orig_pin = pin; + int ret; - entry = acpi_pci_irq_find_prt_entry(dev, pin); - if (entry) { + ret = acpi_pci_irq_find_prt_entry(dev, pin, &entry); + if (!ret && entry) { #ifdef CONFIG_X86_IO_APIC acpi_reroute_boot_interrupt(dev, entry); #endif /* CONFIG_X86_IO_APIC */ @@ -373,7 +334,7 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) return entry; } - /* + /* * Attempt to derive an IRQ for this device from a parent bridge's * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge). */ @@ -393,8 +354,8 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) pin = bridge_pin; } - entry = acpi_pci_irq_find_prt_entry(bridge, pin); - if (entry) { + ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry); + if (!ret && entry) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Derived GSI for %s INT %c from %s\n", pci_name(dev), pin_name(orig_pin), @@ -470,6 +431,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) dev_warn(&dev->dev, "PCI INT %c: no GSI\n", pin_name(pin)); } + return 0; } @@ -477,6 +439,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) if (rc < 0) { dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n", pin_name(pin)); + kfree(entry); return rc; } dev->irq = rc; @@ -491,6 +454,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq); + kfree(entry); return 0; } @@ -513,6 +477,8 @@ void acpi_pci_irq_disable(struct pci_dev *dev) else gsi = entry->index; + kfree(entry); + /* * TBD: It might be worth clearing dev->irq by magic constant * (e.g. PCI_UNDEFINED_IRQ). diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index fd59f57d3829..8545b1d22811 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -434,7 +434,6 @@ static int acpi_pci_root_add(struct acpi_device *device) acpi_status status; int result; struct acpi_pci_root *root; - acpi_handle handle; struct acpi_pci_driver *driver; u32 flags, base_flags; bool is_osc_granted = false; @@ -489,16 +488,6 @@ static int acpi_pci_root_add(struct acpi_device *device) acpi_device_name(device), acpi_device_bid(device), root->segment, &root->secondary); - /* - * PCI Routing Table - * ----------------- - * Evaluate and parse _PRT, if exists. - */ - status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); - if (ACPI_SUCCESS(status)) - result = acpi_pci_irq_add_prt(device->handle, root->segment, - root->secondary.start); - root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle); /* @@ -623,7 +612,6 @@ out_del_root: list_del(&root->node); mutex_unlock(&acpi_pci_root_lock); - acpi_pci_irq_del_prt(root->segment, root->secondary.start); end: kfree(root); return result; @@ -631,8 +619,6 @@ end: static int acpi_pci_root_remove(struct acpi_device *device, int type) { - acpi_status status; - acpi_handle handle; struct acpi_pci_root *root = acpi_driver_data(device); struct acpi_pci_driver *driver; @@ -647,10 +633,6 @@ static int acpi_pci_root_remove(struct acpi_device *device, int type) device_set_run_wake(root->bus->bridge, false); pci_acpi_remove_bus_pm_notifier(device); - status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); - if (ACPI_SUCCESS(status)) - acpi_pci_irq_del_prt(root->segment, root->secondary.start); - pci_remove_root_bus(root->bus); mutex_lock(&acpi_pci_root_lock); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 42736e213f25..9ba9df5f6161 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -325,25 +325,6 @@ static void pci_acpi_setup(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); acpi_handle handle = ACPI_HANDLE(dev); struct acpi_device *adev; - acpi_status status; - acpi_handle dummy; - - /* - * Evaluate and parse _PRT, if exists. This code allows parsing of - * _PRT objects within the scope of non-bridge devices. Note that - * _PRTs within the scope of a PCI bridge assume the bridge's - * subordinate bus number. - * - * TBD: Can _PRTs exist within the scope of non-bridge PCI devices? - */ - status = acpi_get_handle(handle, METHOD_NAME__PRT, &dummy); - if (ACPI_SUCCESS(status)) { - unsigned char bus; - - bus = pci_dev->subordinate ? - pci_dev->subordinate->number : pci_dev->bus->number; - acpi_pci_irq_add_prt(handle, pci_domain_nr(pci_dev->bus), bus); - } acpi_power_resource_register_device(dev, handle); if (acpi_bus_get_device(handle, &adev) || !adev->wakeup.flags.valid) @@ -359,7 +340,6 @@ static void pci_acpi_setup(struct device *dev) static void pci_acpi_cleanup(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); acpi_handle handle = ACPI_HANDLE(dev); struct acpi_device *adev; @@ -369,10 +349,6 @@ static void pci_acpi_cleanup(struct device *dev) pci_acpi_remove_pm_notifier(adev); } acpi_power_resource_unregister_device(dev, handle); - - if (pci_dev->subordinate) - acpi_pci_irq_del_prt(pci_domain_nr(pci_dev->bus), - pci_dev->subordinate->number); } static struct acpi_bus_type acpi_pci_bus = { diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 8b1d7a6a9695..627749af0ba7 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -90,11 +90,6 @@ int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, int *polarity, char **name); int acpi_pci_link_free_irq(acpi_handle handle); -/* ACPI PCI Interrupt Routing (pci_irq.c) */ - -int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus); -void acpi_pci_irq_del_prt(int segment, int bus); - /* ACPI PCI Device Binding (pci_bind.c) */ struct pci_bus; From 70730bca1331fc50c3caacaea00439de1325bd6e Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 14 Feb 2013 15:13:55 -0800 Subject: [PATCH 2906/4607] kernel: Replace timeconst.pl with a bc script bc is the standard tool for multi-precision arithmetic. We switched to Perl because akpm reported a hard-to-reproduce build hang, which was very odd because affected and unaffected machines were all running the same version of GNU bc. Unfortunately switching to Perl required a really ugly "canning" mechanism to support Perl < 5.8 installations lacking the Math::BigInt module. It was recently pointed out to me that some very old versions of GNU make had problems with pipes in subshells, which was indeed the construct used in the Makefile rules in that version of the patch; Perl didn't need it so switching to Perl fixed the problem for unrelated reasons. With the problem (hopefully) root-caused, we can switch back to bc and do the arbitrary-precision arithmetic naturally. Signed-off-by: H. Peter Anvin Cc: Andrew Morton Acked-by: Sam Ravnborg Signed-off-by: Michal Marek --- kernel/Makefile | 16 +- kernel/timeconst.bc | 108 +++++++++++++ kernel/timeconst.pl | 378 -------------------------------------------- 3 files changed, 120 insertions(+), 382 deletions(-) create mode 100644 kernel/timeconst.bc delete mode 100644 kernel/timeconst.pl diff --git a/kernel/Makefile b/kernel/Makefile index 6c072b6da239..ab1e0386bb2d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -127,11 +127,19 @@ $(obj)/config_data.h: $(obj)/config_data.gz FORCE $(obj)/time.o: $(obj)/timeconst.h -quiet_cmd_timeconst = TIMEC $@ - cmd_timeconst = $(PERL) $< $(CONFIG_HZ) > $@ +quiet_cmd_hzfile = HZFILE $@ + cmd_hzfile = echo "hz=$(CONFIG_HZ)" > $@ + +targets += hz.bc +$(obj)/hz.bc: $(objtree)/include/config/hz.h FORCE + $(call if_changed,hzfile) + +quiet_cmd_bc = BC $@ + cmd_bc = bc -q $(filter-out FORCE,$^) > $@ + targets += timeconst.h -$(obj)/timeconst.h: $(src)/timeconst.pl FORCE - $(call if_changed,timeconst) +$(obj)/timeconst.h: $(obj)/hz.bc $(src)/timeconst.bc FORCE + $(call if_changed,bc) ifeq ($(CONFIG_MODULE_SIG),y) # diff --git a/kernel/timeconst.bc b/kernel/timeconst.bc new file mode 100644 index 000000000000..511bdf2cafda --- /dev/null +++ b/kernel/timeconst.bc @@ -0,0 +1,108 @@ +scale=0 + +define gcd(a,b) { + auto t; + while (b) { + t = b; + b = a % b; + a = t; + } + return a; +} + +/* Division by reciprocal multiplication. */ +define fmul(b,n,d) { + return (2^b*n+d-1)/d; +} + +/* Adjustment factor when a ceiling value is used. Use as: + (imul * n) + (fmulxx * n + fadjxx) >> xx) */ +define fadj(b,n,d) { + auto v; + d = d/gcd(n,d); + v = 2^b*(d-1)/d; + return v; +} + +/* Compute the appropriate mul/adj values as well as a shift count, + which brings the mul value into the range 2^b-1 <= x < 2^b. Such + a shift value will be correct in the signed integer range and off + by at most one in the upper half of the unsigned range. */ +define fmuls(b,n,d) { + auto s, m; + for (s = 0; 1; s++) { + m = fmul(s,n,d); + if (m >= 2^(b-1)) + return s; + } + return 0; +} + +define timeconst(hz) { + print "/* Automatically generated by kernel/timeconst.bc */\n" + print "/* Time conversion constants for HZ == ", hz, " */\n" + print "\n" + + print "#ifndef KERNEL_TIMECONST_H\n" + print "#define KERNEL_TIMECONST_H\n\n" + + print "#include \n" + print "#include \n\n" + + print "#if HZ != ", hz, "\n" + print "#error \qkernel/timeconst.h has the wrong HZ value!\q\n" + print "#endif\n\n" + + if (hz < 2) { + print "#error Totally bogus HZ value!\n" + } else { + s=fmuls(32,1000,hz) + obase=16 + print "#define HZ_TO_MSEC_MUL32\tU64_C(0x", fmul(s,1000,hz), ")\n" + print "#define HZ_TO_MSEC_ADJ32\tU64_C(0x", fadj(s,1000,hz), ")\n" + obase=10 + print "#define HZ_TO_MSEC_SHR32\t", s, "\n" + + s=fmuls(32,hz,1000) + obase=16 + print "#define MSEC_TO_HZ_MUL32\tU64_C(0x", fmul(s,hz,1000), ")\n" + print "#define MSEC_TO_HZ_ADJ32\tU64_C(0x", fadj(s,hz,1000), ")\n" + obase=10 + print "#define MSEC_TO_HZ_SHR32\t", s, "\n" + + obase=10 + cd=gcd(hz,1000) + print "#define HZ_TO_MSEC_NUM\t\t", 1000/cd, "\n" + print "#define HZ_TO_MSEC_DEN\t\t", hz/cd, "\n" + print "#define MSEC_TO_HZ_NUM\t\t", hz/cd, "\n" + print "#define MSEC_TO_HZ_DEN\t\t", 1000/cd, "\n" + print "\n" + + s=fmuls(32,1000000,hz) + obase=16 + print "#define HZ_TO_USEC_MUL32\tU64_C(0x", fmul(s,1000000,hz), ")\n" + print "#define HZ_TO_USEC_ADJ32\tU64_C(0x", fadj(s,1000000,hz), ")\n" + obase=10 + print "#define HZ_TO_USEC_SHR32\t", s, "\n" + + s=fmuls(32,hz,1000000) + obase=16 + print "#define USEC_TO_HZ_MUL32\tU64_C(0x", fmul(s,hz,1000000), ")\n" + print "#define USEC_TO_HZ_ADJ32\tU64_C(0x", fadj(s,hz,1000000), ")\n" + obase=10 + print "#define USEC_TO_HZ_SHR32\t", s, "\n" + + obase=10 + cd=gcd(hz,1000000) + print "#define HZ_TO_USEC_NUM\t\t", 1000000/cd, "\n" + print "#define HZ_TO_USEC_DEN\t\t", hz/cd, "\n" + print "#define USEC_TO_HZ_NUM\t\t", hz/cd, "\n" + print "#define USEC_TO_HZ_DEN\t\t", 1000000/cd, "\n" + print "\n" + + print "#endif /* KERNEL_TIMECONST_H */\n" + } + halt +} + +timeconst(hz) diff --git a/kernel/timeconst.pl b/kernel/timeconst.pl deleted file mode 100644 index eb51d76e058a..000000000000 --- a/kernel/timeconst.pl +++ /dev/null @@ -1,378 +0,0 @@ -#!/usr/bin/perl -# ----------------------------------------------------------------------- -# -# Copyright 2007-2008 rPath, Inc. - All Rights Reserved -# -# This file is part of the Linux kernel, and is made available under -# the terms of the GNU General Public License version 2 or (at your -# option) any later version; incorporated herein by reference. -# -# ----------------------------------------------------------------------- -# - -# -# Usage: timeconst.pl HZ > timeconst.h -# - -# Precomputed values for systems without Math::BigInt -# Generated by: -# timeconst.pl --can 24 32 48 64 100 122 128 200 250 256 300 512 1000 1024 1200 -%canned_values = ( - 24 => [ - '0xa6aaaaab','0x2aaaaaa',26, - 125,3, - '0xc49ba5e4','0x1fbe76c8b4',37, - 3,125, - '0xa2c2aaab','0xaaaa',16, - 125000,3, - '0xc9539b89','0x7fffbce4217d',47, - 3,125000, - ], 32 => [ - '0xfa000000','0x6000000',27, - 125,4, - '0x83126e98','0xfdf3b645a',36, - 4,125, - '0xf4240000','0x0',17, - 31250,1, - '0x8637bd06','0x3fff79c842fa',46, - 1,31250, - ], 48 => [ - '0xa6aaaaab','0x6aaaaaa',27, - 125,6, - '0xc49ba5e4','0xfdf3b645a',36, - 6,125, - '0xa2c2aaab','0x15555',17, - 62500,3, - '0xc9539b89','0x3fffbce4217d',46, - 3,62500, - ], 64 => [ - '0xfa000000','0xe000000',28, - 125,8, - '0x83126e98','0x7ef9db22d',35, - 8,125, - '0xf4240000','0x0',18, - 15625,1, - '0x8637bd06','0x1fff79c842fa',45, - 1,15625, - ], 100 => [ - '0xa0000000','0x0',28, - 10,1, - '0xcccccccd','0x733333333',35, - 1,10, - '0x9c400000','0x0',18, - 10000,1, - '0xd1b71759','0x1fff2e48e8a7',45, - 1,10000, - ], 122 => [ - '0x8325c53f','0xfbcda3a',28, - 500,61, - '0xf9db22d1','0x7fbe76c8b',35, - 61,500, - '0x8012e2a0','0x3ef36',18, - 500000,61, - '0xffda4053','0x1ffffbce4217',45, - 61,500000, - ], 128 => [ - '0xfa000000','0x1e000000',29, - 125,16, - '0x83126e98','0x3f7ced916',34, - 16,125, - '0xf4240000','0x40000',19, - 15625,2, - '0x8637bd06','0xfffbce4217d',44, - 2,15625, - ], 200 => [ - '0xa0000000','0x0',29, - 5,1, - '0xcccccccd','0x333333333',34, - 1,5, - '0x9c400000','0x0',19, - 5000,1, - '0xd1b71759','0xfff2e48e8a7',44, - 1,5000, - ], 250 => [ - '0x80000000','0x0',29, - 4,1, - '0x80000000','0x180000000',33, - 1,4, - '0xfa000000','0x0',20, - 4000,1, - '0x83126e98','0x7ff7ced9168',43, - 1,4000, - ], 256 => [ - '0xfa000000','0x3e000000',30, - 125,32, - '0x83126e98','0x1fbe76c8b',33, - 32,125, - '0xf4240000','0xc0000',20, - 15625,4, - '0x8637bd06','0x7ffde7210be',43, - 4,15625, - ], 300 => [ - '0xd5555556','0x2aaaaaaa',30, - 10,3, - '0x9999999a','0x1cccccccc',33, - 3,10, - '0xd0555556','0xaaaaa',20, - 10000,3, - '0x9d495183','0x7ffcb923a29',43, - 3,10000, - ], 512 => [ - '0xfa000000','0x7e000000',31, - 125,64, - '0x83126e98','0xfdf3b645',32, - 64,125, - '0xf4240000','0x1c0000',21, - 15625,8, - '0x8637bd06','0x3ffef39085f',42, - 8,15625, - ], 1000 => [ - '0x80000000','0x0',31, - 1,1, - '0x80000000','0x0',31, - 1,1, - '0xfa000000','0x0',22, - 1000,1, - '0x83126e98','0x1ff7ced9168',41, - 1,1000, - ], 1024 => [ - '0xfa000000','0xfe000000',32, - 125,128, - '0x83126e98','0x7ef9db22',31, - 128,125, - '0xf4240000','0x3c0000',22, - 15625,16, - '0x8637bd06','0x1fff79c842f',41, - 16,15625, - ], 1200 => [ - '0xd5555556','0xd5555555',32, - 5,6, - '0x9999999a','0x66666666',31, - 6,5, - '0xd0555556','0x2aaaaa',22, - 2500,3, - '0x9d495183','0x1ffcb923a29',41, - 3,2500, - ] -); - -$has_bigint = eval 'use Math::BigInt qw(bgcd); 1;'; - -sub bint($) -{ - my($x) = @_; - return Math::BigInt->new($x); -} - -# -# Constants for division by reciprocal multiplication. -# (bits, numerator, denominator) -# -sub fmul($$$) -{ - my ($b,$n,$d) = @_; - - $n = bint($n); - $d = bint($d); - - return scalar (($n << $b)+$d-bint(1))/$d; -} - -sub fadj($$$) -{ - my($b,$n,$d) = @_; - - $n = bint($n); - $d = bint($d); - - $d = $d/bgcd($n, $d); - return scalar (($d-bint(1)) << $b)/$d; -} - -sub fmuls($$$) { - my($b,$n,$d) = @_; - my($s,$m); - my($thres) = bint(1) << ($b-1); - - $n = bint($n); - $d = bint($d); - - for ($s = 0; 1; $s++) { - $m = fmul($s,$n,$d); - return $s if ($m >= $thres); - } - return 0; -} - -# Generate a hex value if the result fits in 64 bits; -# otherwise skip. -sub bignum_hex($) { - my($x) = @_; - my $s = $x->as_hex(); - - return (length($s) > 18) ? undef : $s; -} - -# Provides mul, adj, and shr factors for a specific -# (bit, time, hz) combination -sub muladj($$$) { - my($b, $t, $hz) = @_; - my $s = fmuls($b, $t, $hz); - my $m = fmul($s, $t, $hz); - my $a = fadj($s, $t, $hz); - return (bignum_hex($m), bignum_hex($a), $s); -} - -# Provides numerator, denominator values -sub numden($$) { - my($n, $d) = @_; - my $g = bgcd($n, $d); - return ($n/$g, $d/$g); -} - -# All values for a specific (time, hz) combo -sub conversions($$) { - my ($t, $hz) = @_; - my @val = (); - - # HZ_TO_xx - push(@val, muladj(32, $t, $hz)); - push(@val, numden($t, $hz)); - - # xx_TO_HZ - push(@val, muladj(32, $hz, $t)); - push(@val, numden($hz, $t)); - - return @val; -} - -sub compute_values($) { - my($hz) = @_; - my @val = (); - my $s, $m, $a, $g; - - if (!$has_bigint) { - die "$0: HZ == $hz not canned and ". - "Math::BigInt not available\n"; - } - - # MSEC conversions - push(@val, conversions(1000, $hz)); - - # USEC conversions - push(@val, conversions(1000000, $hz)); - - return @val; -} - -sub outputval($$) -{ - my($name, $val) = @_; - my $csuf; - - if (defined($val)) { - if ($name !~ /SHR/) { - $val = "U64_C($val)"; - } - printf "#define %-23s %s\n", $name.$csuf, $val.$csuf; - } -} - -sub output($@) -{ - my($hz, @val) = @_; - my $pfx, $bit, $suf, $s, $m, $a; - - print "/* Automatically generated by kernel/timeconst.pl */\n"; - print "/* Conversion constants for HZ == $hz */\n"; - print "\n"; - print "#ifndef KERNEL_TIMECONST_H\n"; - print "#define KERNEL_TIMECONST_H\n"; - print "\n"; - - print "#include \n"; - print "#include \n"; - - print "\n"; - print "#if HZ != $hz\n"; - print "#error \"kernel/timeconst.h has the wrong HZ value!\"\n"; - print "#endif\n"; - print "\n"; - - foreach $pfx ('HZ_TO_MSEC','MSEC_TO_HZ', - 'HZ_TO_USEC','USEC_TO_HZ') { - foreach $bit (32) { - foreach $suf ('MUL', 'ADJ', 'SHR') { - outputval("${pfx}_$suf$bit", shift(@val)); - } - } - foreach $suf ('NUM', 'DEN') { - outputval("${pfx}_$suf", shift(@val)); - } - } - - print "\n"; - print "#endif /* KERNEL_TIMECONST_H */\n"; -} - -# Pretty-print Perl values -sub perlvals(@) { - my $v; - my @l = (); - - foreach $v (@_) { - if (!defined($v)) { - push(@l, 'undef'); - } elsif ($v =~ /^0x/) { - push(@l, "\'".$v."\'"); - } else { - push(@l, $v.''); - } - } - return join(',', @l); -} - -($hz) = @ARGV; - -# Use this to generate the %canned_values structure -if ($hz eq '--can') { - shift(@ARGV); - @hzlist = sort {$a <=> $b} (@ARGV); - - print "# Precomputed values for systems without Math::BigInt\n"; - print "# Generated by:\n"; - print "# timeconst.pl --can ", join(' ', @hzlist), "\n"; - print "\%canned_values = (\n"; - my $pf = "\t"; - foreach $hz (@hzlist) { - my @values = compute_values($hz); - print "$pf$hz => [\n"; - while (scalar(@values)) { - my $bit; - foreach $bit (32) { - my $m = shift(@values); - my $a = shift(@values); - my $s = shift(@values); - print "\t\t", perlvals($m,$a,$s), ",\n"; - } - my $n = shift(@values); - my $d = shift(@values); - print "\t\t", perlvals($n,$d), ",\n"; - } - print "\t]"; - $pf = ', '; - } - print "\n);\n"; -} else { - $hz += 0; # Force to number - if ($hz < 1) { - die "Usage: $0 HZ\n"; - } - - @val = @{$canned_values{$hz}}; - if (!defined(@val)) { - @val = compute_values($hz); - } - output($hz, @val); -} -exit 0; From 4836d15789c85224662d3e037694d15ec9901631 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Wed, 19 Dec 2012 14:53:37 -0600 Subject: [PATCH 2907/4607] drm/omap: Add PM capabilities Added power management capabilities into the omapdrm and DMM drivers. During suspend, we don't need to do anything to maintain the state of the LUT. We have all the necessary information to recreate the mappings of the GEM object list maintained by the omapdrm driver. On resume, the DMM resume handler will first reprogram the LUT to point to the dummy page. The subsequent resume handler in the omapdrm will call into the DMM and reprogram each of the buffer objects. This will ensure that all of the necessary objects will be pinned into the DMM properly. Order of suspend/resume handlers is done by device creation. We create the DMM device before the omapdrm, so the correct order is maintained. Signed-off-by: Andy Gross Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman --- drivers/staging/omapdrm/omap_dmm_tiler.c | 34 ++++++++++++++++++++++++ drivers/staging/omapdrm/omap_drv.c | 14 ++++++++++ drivers/staging/omapdrm/omap_drv.h | 4 +++ drivers/staging/omapdrm/omap_gem.c | 28 +++++++++++++++++++ 4 files changed, 80 insertions(+) diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.c b/drivers/staging/omapdrm/omap_dmm_tiler.c index 59bf43899fc0..c42c5e500e02 100644 --- a/drivers/staging/omapdrm/omap_dmm_tiler.c +++ b/drivers/staging/omapdrm/omap_dmm_tiler.c @@ -903,12 +903,46 @@ error: } #endif +#ifdef CONFIG_PM +static int omap_dmm_resume(struct device *dev) +{ + struct tcm_area area; + int i; + + if (!omap_dmm) + return -ENODEV; + + area = (struct tcm_area) { + .is2d = true, + .tcm = NULL, + .p1.x = omap_dmm->container_width - 1, + .p1.y = omap_dmm->container_height - 1, + }; + + /* initialize all LUTs to dummy page entries */ + for (i = 0; i < omap_dmm->num_lut; i++) { + area.tcm = omap_dmm->tcm[i]; + if (fill(&area, NULL, 0, 0, true)) + dev_err(dev, "refill failed"); + } + + return 0; +} + +static const struct dev_pm_ops omap_dmm_pm_ops = { + .resume = omap_dmm_resume, +}; +#endif + struct platform_driver omap_dmm_driver = { .probe = omap_dmm_probe, .remove = omap_dmm_remove, .driver = { .owner = THIS_MODULE, .name = DMM_DRIVER_NAME, +#ifdef CONFIG_PM + .pm = &omap_dmm_pm_ops, +#endif }, }; diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c index dfdb4ba1e7c6..9d6a584113d2 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/staging/omapdrm/omap_drv.c @@ -368,6 +368,9 @@ static int dev_load(struct drm_device *dev, unsigned long flags) /* well, limp along without an fbdev.. maybe X11 will work? */ } + /* store off drm_device for use in pm ops */ + dev_set_drvdata(dev->dev, dev); + drm_kms_helper_poll_init(dev); return 0; @@ -393,6 +396,8 @@ static int dev_unload(struct drm_device *dev) kfree(dev->dev_private); dev->dev_private = NULL; + dev_set_drvdata(dev->dev, NULL); + return 0; } @@ -558,10 +563,19 @@ static int pdev_remove(struct platform_device *device) return 0; } +#ifdef CONFIG_PM +static const struct dev_pm_ops omapdrm_pm_ops = { + .resume = omap_gem_resume, +}; +#endif + struct platform_driver pdev = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &omapdrm_pm_ops, +#endif }, .probe = pdev_probe, .remove = pdev_remove, diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index cd1f22b0b124..f921027e7500 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h @@ -135,6 +135,10 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m); void omap_gem_describe_objects(struct list_head *list, struct seq_file *m); #endif +#ifdef CONFIG_PM +int omap_gem_resume(struct device *dev); +#endif + int omap_irq_enable_vblank(struct drm_device *dev, int crtc); void omap_irq_disable_vblank(struct drm_device *dev, int crtc); irqreturn_t omap_irq_handler(DRM_IRQ_ARGS); diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c index c38992b76fc9..08f1e292ed20 100644 --- a/drivers/staging/omapdrm/omap_gem.c +++ b/drivers/staging/omapdrm/omap_gem.c @@ -964,6 +964,34 @@ void *omap_gem_vaddr(struct drm_gem_object *obj) return omap_obj->vaddr; } +#ifdef CONFIG_PM +/* re-pin objects in DMM in resume path: */ +int omap_gem_resume(struct device *dev) +{ + struct drm_device *drm_dev = dev_get_drvdata(dev); + struct omap_drm_private *priv = drm_dev->dev_private; + struct omap_gem_object *omap_obj; + int ret = 0; + + list_for_each_entry(omap_obj, &priv->obj_list, mm_list) { + if (omap_obj->block) { + struct drm_gem_object *obj = &omap_obj->base; + uint32_t npages = obj->size >> PAGE_SHIFT; + WARN_ON(!omap_obj->pages); /* this can't happen */ + ret = tiler_pin(omap_obj->block, + omap_obj->pages, npages, + omap_obj->roll, true); + if (ret) { + dev_err(dev, "could not repin: %d\n", ret); + return ret; + } + } + } + + return 0; +} +#endif + #ifdef CONFIG_DEBUG_FS void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) { From 238083ad5d67d1c9ba6e7158a027e834a48bb23b Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Wed, 19 Dec 2012 14:53:38 -0600 Subject: [PATCH 2908/4607] drm/omap: Add OMAP5 support Add support for OMAP5 processor. The main differences are that the OMAP5 has 2 containers, one for 1D and one for 2D. Each container is 128MiB in size. Signed-off-by: Andy Gross Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman --- drivers/staging/omapdrm/omap_dmm_priv.h | 5 + drivers/staging/omapdrm/omap_dmm_tiler.c | 131 +++++++++++++++-------- drivers/staging/omapdrm/tcm.h | 2 + 3 files changed, 92 insertions(+), 46 deletions(-) diff --git a/drivers/staging/omapdrm/omap_dmm_priv.h b/drivers/staging/omapdrm/omap_dmm_priv.h index 273ec12c028a..58bcd6ae0255 100644 --- a/drivers/staging/omapdrm/omap_dmm_priv.h +++ b/drivers/staging/omapdrm/omap_dmm_priv.h @@ -118,6 +118,11 @@ struct pat { #define DESCR_SIZE 128 #define REFILL_BUFFER_SIZE ((4 * 128 * 256) + (3 * DESCR_SIZE)) +/* For OMAP5, a fixed offset is added to all Y coordinates for 1D buffers. + * This is used in programming to address the upper portion of the LUT +*/ +#define OMAP5_LUT_OFFSET 128 + struct dmm; struct dmm_txn { diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.c b/drivers/staging/omapdrm/omap_dmm_tiler.c index c42c5e500e02..391021537105 100644 --- a/drivers/staging/omapdrm/omap_dmm_tiler.c +++ b/drivers/staging/omapdrm/omap_dmm_tiler.c @@ -213,6 +213,11 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area, txn->last_pat->next_pa = (uint32_t)pat_pa; pat->area = *area; + + /* adjust Y coordinates based off of container parameters */ + pat->area.y0 += engine->tcm->y_offset; + pat->area.y1 += engine->tcm->y_offset; + pat->ctrl = (struct pat_ctrl){ .start = 1, .lut_id = engine->tcm->lut_id, @@ -622,6 +627,11 @@ static int omap_dmm_probe(struct platform_device *dev) omap_dmm->lut_width = ((pat_geom >> 16) & 0xF) << 5; omap_dmm->lut_height = ((pat_geom >> 24) & 0xF) << 5; + /* increment LUT by one if on OMAP5 */ + /* LUT has twice the height, and is split into a separate container */ + if (omap_dmm->lut_height != omap_dmm->container_height) + omap_dmm->num_lut++; + /* initialize DMM registers */ writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__0); writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__1); @@ -701,6 +711,9 @@ static int omap_dmm_probe(struct platform_device *dev) } /* init containers */ + /* Each LUT is associated with a TCM (container manager). We use the + lut_id to denote the lut_id used to identify the correct LUT for + programming during reill operations */ for (i = 0; i < omap_dmm->num_lut; i++) { omap_dmm->tcm[i] = sita_init(omap_dmm->container_width, omap_dmm->container_height, @@ -717,13 +730,23 @@ static int omap_dmm_probe(struct platform_device *dev) /* assign access mode containers to applicable tcm container */ /* OMAP 4 has 1 container for all 4 views */ + /* OMAP 5 has 2 containers, 1 for 2D and 1 for 1D */ containers[TILFMT_8BIT] = omap_dmm->tcm[0]; containers[TILFMT_16BIT] = omap_dmm->tcm[0]; containers[TILFMT_32BIT] = omap_dmm->tcm[0]; - containers[TILFMT_PAGE] = omap_dmm->tcm[0]; + + if (omap_dmm->container_height != omap_dmm->lut_height) { + /* second LUT is used for PAGE mode. Programming must use + y offset that is added to all y coordinates. LUT id is still + 0, because it is the same LUT, just the upper 128 lines */ + containers[TILFMT_PAGE] = omap_dmm->tcm[1]; + omap_dmm->tcm[1]->y_offset = OMAP5_LUT_OFFSET; + omap_dmm->tcm[1]->lut_id = 0; + } else { + containers[TILFMT_PAGE] = omap_dmm->tcm[0]; + } area = (struct tcm_area) { - .is2d = true, .tcm = NULL, .p1.x = omap_dmm->container_width - 1, .p1.y = omap_dmm->container_height - 1, @@ -835,64 +858,81 @@ int tiler_map_show(struct seq_file *s, void *arg) int h_adj; int w_adj; unsigned long flags; + int lut_idx; + if (!omap_dmm) { /* early return if dmm/tiler device is not initialized */ return 0; } - h_adj = omap_dmm->lut_height / ydiv; - w_adj = omap_dmm->lut_width / xdiv; + h_adj = omap_dmm->container_height / ydiv; + w_adj = omap_dmm->container_width / xdiv; - map = kzalloc(h_adj * sizeof(*map), GFP_KERNEL); - global_map = kzalloc((w_adj + 1) * h_adj, GFP_KERNEL); + map = kmalloc(h_adj * sizeof(*map), GFP_KERNEL); + global_map = kmalloc((w_adj + 1) * h_adj, GFP_KERNEL); if (!map || !global_map) goto error; - memset(global_map, ' ', (w_adj + 1) * h_adj); - for (i = 0; i < omap_dmm->lut_height; i++) { - map[i] = global_map + i * (w_adj + 1); - map[i][w_adj] = 0; - } - spin_lock_irqsave(&list_lock, flags); + for (lut_idx = 0; lut_idx < omap_dmm->num_lut; lut_idx++) { + memset(map, 0, sizeof(h_adj * sizeof(*map))); + memset(global_map, ' ', (w_adj + 1) * h_adj); - list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) { - if (block->fmt != TILFMT_PAGE) { - fill_map(map, xdiv, ydiv, &block->area, *m2dp, true); - if (!*++a2dp) - a2dp = a2d; - if (!*++m2dp) - m2dp = m2d; - map_2d_info(map, xdiv, ydiv, nice, &block->area); - } else { - bool start = read_map_pt(map, xdiv, ydiv, - &block->area.p0) - == ' '; - bool end = read_map_pt(map, xdiv, ydiv, &block->area.p1) - == ' '; - tcm_for_each_slice(a, block->area, p) - fill_map(map, xdiv, ydiv, &a, '=', true); - fill_map_pt(map, xdiv, ydiv, &block->area.p0, - start ? '<' : 'X'); - fill_map_pt(map, xdiv, ydiv, &block->area.p1, - end ? '>' : 'X'); - map_1d_info(map, xdiv, ydiv, nice, &block->area); + for (i = 0; i < omap_dmm->container_height; i++) { + map[i] = global_map + i * (w_adj + 1); + map[i][w_adj] = 0; } - } - spin_unlock_irqrestore(&list_lock, flags); + spin_lock_irqsave(&list_lock, flags); - if (s) { - seq_printf(s, "BEGIN DMM TILER MAP\n"); - for (i = 0; i < 128; i++) - seq_printf(s, "%03d:%s\n", i, map[i]); - seq_printf(s, "END TILER MAP\n"); - } else { - dev_dbg(omap_dmm->dev, "BEGIN DMM TILER MAP\n"); - for (i = 0; i < 128; i++) - dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]); - dev_dbg(omap_dmm->dev, "END TILER MAP\n"); + list_for_each_entry(block, &omap_dmm->alloc_head, alloc_node) { + if (block->area.tcm == omap_dmm->tcm[lut_idx]) { + if (block->fmt != TILFMT_PAGE) { + fill_map(map, xdiv, ydiv, &block->area, + *m2dp, true); + if (!*++a2dp) + a2dp = a2d; + if (!*++m2dp) + m2dp = m2d; + map_2d_info(map, xdiv, ydiv, nice, + &block->area); + } else { + bool start = read_map_pt(map, xdiv, + ydiv, &block->area.p0) == ' '; + bool end = read_map_pt(map, xdiv, ydiv, + &block->area.p1) == ' '; + + tcm_for_each_slice(a, block->area, p) + fill_map(map, xdiv, ydiv, &a, + '=', true); + fill_map_pt(map, xdiv, ydiv, + &block->area.p0, + start ? '<' : 'X'); + fill_map_pt(map, xdiv, ydiv, + &block->area.p1, + end ? '>' : 'X'); + map_1d_info(map, xdiv, ydiv, nice, + &block->area); + } + } + } + + spin_unlock_irqrestore(&list_lock, flags); + + if (s) { + seq_printf(s, "CONTAINER %d DUMP BEGIN\n", lut_idx); + for (i = 0; i < 128; i++) + seq_printf(s, "%03d:%s\n", i, map[i]); + seq_printf(s, "CONTAINER %d DUMP END\n", lut_idx); + } else { + dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP BEGIN\n", + lut_idx); + for (i = 0; i < 128; i++) + dev_dbg(omap_dmm->dev, "%03d:%s\n", i, map[i]); + dev_dbg(omap_dmm->dev, "CONTAINER %d DUMP END\n", + lut_idx); + } } error: @@ -913,7 +953,6 @@ static int omap_dmm_resume(struct device *dev) return -ENODEV; area = (struct tcm_area) { - .is2d = true, .tcm = NULL, .p1.x = omap_dmm->container_width - 1, .p1.y = omap_dmm->container_height - 1, diff --git a/drivers/staging/omapdrm/tcm.h b/drivers/staging/omapdrm/tcm.h index d273e3ee0b4c..a8d5ce47686f 100644 --- a/drivers/staging/omapdrm/tcm.h +++ b/drivers/staging/omapdrm/tcm.h @@ -59,6 +59,8 @@ struct tcm { u16 width, height; /* container dimensions */ int lut_id; /* Lookup table identifier */ + unsigned int y_offset; /* offset to use for y coordinates */ + /* 'pvt' structure shall contain any tcm details (attr) along with linked list of allocated areas and mutex for mutually exclusive access to the list. It may also contain copies of width and height to notice From 32ac1a5286d56b3c822f4a1a8d0f7fc5d586de38 Mon Sep 17 00:00:00 2001 From: Cong Ding Date: Tue, 15 Jan 2013 20:46:50 +0100 Subject: [PATCH 2909/4607] staging: omapdrm/omap_gem_dmabuf.c: fix memory leakage There is a memory leakage in variable sg if it goes to error. Signed-off-by: Cong Ding Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman --- drivers/staging/omapdrm/omap_gem_dmabuf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/omapdrm/omap_gem_dmabuf.c b/drivers/staging/omapdrm/omap_gem_dmabuf.c index b6c5b5c6c8c5..a3236abfca3d 100644 --- a/drivers/staging/omapdrm/omap_gem_dmabuf.c +++ b/drivers/staging/omapdrm/omap_gem_dmabuf.c @@ -53,10 +53,10 @@ static struct sg_table *omap_gem_map_dma_buf( /* this should be after _get_paddr() to ensure we have pages attached */ omap_gem_dma_sync(obj, dir); -out: - if (ret) - return ERR_PTR(ret); return sg; +out: + kfree(sg); + return ERR_PTR(ret); } static void omap_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, From a4462f246c8821f625f45bce52c7ca7e0207dffe Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Sat, 26 Jan 2013 00:40:13 +0100 Subject: [PATCH 2910/4607] staging/omapdrm: Use kmemdup rather than duplicating its implementation Found with coccicheck. The semantic patch that makes this change is available in scripts/coccinelle/api/memdup.cocci. Signed-off-by: Peter Huewe Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman --- drivers/staging/omapdrm/omap_gem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c index 08f1e292ed20..f9297eb2599e 100644 --- a/drivers/staging/omapdrm/omap_gem.c +++ b/drivers/staging/omapdrm/omap_gem.c @@ -1267,12 +1267,12 @@ int omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj) if ((omap_obj->flags & OMAP_BO_EXT_SYNC) && !syncobj) { /* clearing a previously set syncobj */ - syncobj = kzalloc(sizeof(*omap_obj->sync), GFP_ATOMIC); + syncobj = kmemdup(omap_obj->sync, sizeof(*omap_obj->sync), + GFP_ATOMIC); if (!syncobj) { ret = -ENOMEM; goto unlock; } - memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync)); omap_obj->flags &= ~OMAP_BO_EXT_SYNC; omap_obj->sync = syncobj; } else if (syncobj && !(omap_obj->flags & OMAP_BO_EXT_SYNC)) { From 8bb0daffb0b8e45188066255b4203446eae181f1 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 11 Feb 2013 12:43:09 -0500 Subject: [PATCH 2911/4607] drm/omap: move out of staging Now that the omapdss interface has been reworked so that omapdrm can use dispc directly, we have been able to fix the remaining functional kms issues with omapdrm. And in the mean time the PM sequencing and many other of that open issues have been solved. So I think it makes sense to finally move omapdrm out of staging. Signed-off-by: Rob Clark --- drivers/gpu/drm/Kconfig | 2 ++ drivers/gpu/drm/Makefile | 1 + drivers/{staging => gpu/drm}/omapdrm/Kconfig | 0 drivers/{staging => gpu/drm}/omapdrm/Makefile | 0 drivers/gpu/drm/omapdrm/TODO | 23 +++++++++++++ .../drm}/omapdrm/omap_connector.c | 2 +- .../{staging => gpu/drm}/omapdrm/omap_crtc.c | 2 +- .../drm}/omapdrm/omap_debugfs.c | 2 +- .../drm}/omapdrm/omap_dmm_priv.h | 0 .../drm}/omapdrm/omap_dmm_tiler.c | 0 .../drm}/omapdrm/omap_dmm_tiler.h | 0 .../{staging => gpu/drm}/omapdrm/omap_drv.c | 2 +- .../{staging => gpu/drm}/omapdrm/omap_drv.h | 4 +-- .../drm}/omapdrm/omap_encoder.c | 2 +- .../{staging => gpu/drm}/omapdrm/omap_fb.c | 2 +- .../{staging => gpu/drm}/omapdrm/omap_fbdev.c | 2 +- .../{staging => gpu/drm}/omapdrm/omap_gem.c | 2 +- .../drm}/omapdrm/omap_gem_dmabuf.c | 2 +- .../drm}/omapdrm/omap_gem_helpers.c | 2 +- .../{staging => gpu/drm}/omapdrm/omap_irq.c | 2 +- .../{staging => gpu/drm}/omapdrm/omap_plane.c | 2 +- .../{staging => gpu/drm}/omapdrm/tcm-sita.c | 0 .../{staging => gpu/drm}/omapdrm/tcm-sita.h | 0 drivers/{staging => gpu/drm}/omapdrm/tcm.h | 0 drivers/staging/Kconfig | 2 -- drivers/staging/Makefile | 1 - drivers/staging/omapdrm/TODO | 32 ------------------- .../omapdrm => include/uapi/drm}/omap_drm.h | 2 +- 28 files changed, 41 insertions(+), 50 deletions(-) rename drivers/{staging => gpu/drm}/omapdrm/Kconfig (100%) rename drivers/{staging => gpu/drm}/omapdrm/Makefile (100%) create mode 100644 drivers/gpu/drm/omapdrm/TODO rename drivers/{staging => gpu/drm}/omapdrm/omap_connector.c (99%) rename drivers/{staging => gpu/drm}/omapdrm/omap_crtc.c (99%) rename drivers/{staging => gpu/drm}/omapdrm/omap_debugfs.c (98%) rename drivers/{staging => gpu/drm}/omapdrm/omap_dmm_priv.h (100%) rename drivers/{staging => gpu/drm}/omapdrm/omap_dmm_tiler.c (100%) rename drivers/{staging => gpu/drm}/omapdrm/omap_dmm_tiler.h (100%) rename drivers/{staging => gpu/drm}/omapdrm/omap_drv.c (99%) rename drivers/{staging => gpu/drm}/omapdrm/omap_drv.h (99%) rename drivers/{staging => gpu/drm}/omapdrm/omap_encoder.c (99%) rename drivers/{staging => gpu/drm}/omapdrm/omap_fb.c (99%) rename drivers/{staging => gpu/drm}/omapdrm/omap_fbdev.c (99%) rename drivers/{staging => gpu/drm}/omapdrm/omap_gem.c (99%) rename drivers/{staging => gpu/drm}/omapdrm/omap_gem_dmabuf.c (99%) rename drivers/{staging => gpu/drm}/omapdrm/omap_gem_helpers.c (98%) rename drivers/{staging => gpu/drm}/omapdrm/omap_irq.c (99%) rename drivers/{staging => gpu/drm}/omapdrm/omap_plane.c (99%) rename drivers/{staging => gpu/drm}/omapdrm/tcm-sita.c (100%) rename drivers/{staging => gpu/drm}/omapdrm/tcm-sita.h (100%) rename drivers/{staging => gpu/drm}/omapdrm/tcm.h (100%) delete mode 100644 drivers/staging/omapdrm/TODO rename {drivers/staging/omapdrm => include/uapi/drm}/omap_drm.h (99%) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ed9e3af17b31..0ce5f52ac56e 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -215,3 +215,5 @@ source "drivers/gpu/drm/cirrus/Kconfig" source "drivers/gpu/drm/shmobile/Kconfig" source "drivers/gpu/drm/tegra/Kconfig" + +source "drivers/gpu/drm/omapdrm/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 6f58c81cfcbc..b6b43cbc18e4 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -50,4 +50,5 @@ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-$(CONFIG_DRM_TEGRA) += tegra/ +obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-y += i2c/ diff --git a/drivers/staging/omapdrm/Kconfig b/drivers/gpu/drm/omapdrm/Kconfig similarity index 100% rename from drivers/staging/omapdrm/Kconfig rename to drivers/gpu/drm/omapdrm/Kconfig diff --git a/drivers/staging/omapdrm/Makefile b/drivers/gpu/drm/omapdrm/Makefile similarity index 100% rename from drivers/staging/omapdrm/Makefile rename to drivers/gpu/drm/omapdrm/Makefile diff --git a/drivers/gpu/drm/omapdrm/TODO b/drivers/gpu/drm/omapdrm/TODO new file mode 100644 index 000000000000..4d8c18aa5dd7 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/TODO @@ -0,0 +1,23 @@ +TODO +. Where should we do eviction (detatch_pages())? We aren't necessarily + accessing the pages via a GART, so maybe we need some other threshold + to put a cap on the # of pages that can be pin'd. + . Use mm_shrinker to trigger unpinning pages. + . This is mainly theoretical since most of these devices don't actually + have swap or harddrive. +. GEM/shmem backed pages can have existing mappings (kernel linear map, + etc..), which isn't really ideal. +. Revisit GEM sync object infrastructure.. TTM has some framework for this + already. Possibly this could be refactored out and made more common? + There should be some way to do this with less wheel-reinvention. + . This can be handled by the dma-buf fence/reservation stuff when it + lands + +Userspace: +. git://anongit.freedesktop.org/xorg/driver/xf86-video-omap + +Currently tested on +. OMAP3530 beagleboard +. OMAP4430 pandaboard +. OMAP4460 pandaboard +. OMAP5432 uEVM diff --git a/drivers/staging/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c similarity index 99% rename from drivers/staging/omapdrm/omap_connector.c rename to drivers/gpu/drm/omapdrm/omap_connector.c index 4cc9ee733c5f..44284fd981fc 100644 --- a/drivers/staging/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_connector.c + * drivers/gpu/drm/omapdrm/omap_connector.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c similarity index 99% rename from drivers/staging/omapdrm/omap_crtc.c rename to drivers/gpu/drm/omapdrm/omap_crtc.c index 510942e67020..2b97cf90071b 100644 --- a/drivers/staging/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_crtc.c + * drivers/gpu/drm/omapdrm/omap_crtc.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/omap_debugfs.c b/drivers/gpu/drm/omapdrm/omap_debugfs.c similarity index 98% rename from drivers/staging/omapdrm/omap_debugfs.c rename to drivers/gpu/drm/omapdrm/omap_debugfs.c index e95540b3e2f6..c0aa40f8ad6a 100644 --- a/drivers/staging/omapdrm/omap_debugfs.c +++ b/drivers/gpu/drm/omapdrm/omap_debugfs.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_debugfs.c + * drivers/gpu/drm/omapdrm/omap_debugfs.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/omap_dmm_priv.h b/drivers/gpu/drm/omapdrm/omap_dmm_priv.h similarity index 100% rename from drivers/staging/omapdrm/omap_dmm_priv.h rename to drivers/gpu/drm/omapdrm/omap_dmm_priv.h diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c similarity index 100% rename from drivers/staging/omapdrm/omap_dmm_tiler.c rename to drivers/gpu/drm/omapdrm/omap_dmm_tiler.c diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.h b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.h similarity index 100% rename from drivers/staging/omapdrm/omap_dmm_tiler.h rename to drivers/gpu/drm/omapdrm/omap_dmm_tiler.h diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c similarity index 99% rename from drivers/staging/omapdrm/omap_drv.c rename to drivers/gpu/drm/omapdrm/omap_drv.c index 9d6a584113d2..9083538bd16a 100644 --- a/drivers/staging/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_drv.c + * drivers/gpu/drm/omapdrm/omap_drv.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h similarity index 99% rename from drivers/staging/omapdrm/omap_drv.h rename to drivers/gpu/drm/omapdrm/omap_drv.h index f921027e7500..d4f997bb4ac0 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_drv.h + * drivers/gpu/drm/omapdrm/omap_drv.h * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark @@ -25,8 +25,8 @@ #include #include #include +#include #include -#include "omap_drm.h" #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) diff --git a/drivers/staging/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c similarity index 99% rename from drivers/staging/omapdrm/omap_encoder.c rename to drivers/gpu/drm/omapdrm/omap_encoder.c index e053160d2db3..7e1f2ab65372 100644 --- a/drivers/staging/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_encoder.c + * drivers/gpu/drm/omapdrm/omap_encoder.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c similarity index 99% rename from drivers/staging/omapdrm/omap_fb.c rename to drivers/gpu/drm/omapdrm/omap_fb.c index bf6421f26c40..9d5f6f696c72 100644 --- a/drivers/staging/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_fb.c + * drivers/gpu/drm/omapdrm/omap_fb.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c similarity index 99% rename from drivers/staging/omapdrm/omap_fbdev.c rename to drivers/gpu/drm/omapdrm/omap_fbdev.c index caefdf9430f8..11eed30efe06 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_fbdev.c + * drivers/gpu/drm/omapdrm/omap_fbdev.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c similarity index 99% rename from drivers/staging/omapdrm/omap_gem.c rename to drivers/gpu/drm/omapdrm/omap_gem.c index f9297eb2599e..e8302b02691d 100644 --- a/drivers/staging/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_gem.c + * drivers/gpu/drm/omapdrm/omap_gem.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c similarity index 99% rename from drivers/staging/omapdrm/omap_gem_dmabuf.c rename to drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index a3236abfca3d..ac74d1bc67bf 100644 --- a/drivers/staging/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_gem_dmabuf.c + * drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/omap_gem_helpers.c b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c similarity index 98% rename from drivers/staging/omapdrm/omap_gem_helpers.c rename to drivers/gpu/drm/omapdrm/omap_gem_helpers.c index ffb8cceaeb46..e4a66a35fc6a 100644 --- a/drivers/staging/omapdrm/omap_gem_helpers.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_gem_helpers.c + * drivers/gpu/drm/omapdrm/omap_gem_helpers.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c similarity index 99% rename from drivers/staging/omapdrm/omap_irq.c rename to drivers/gpu/drm/omapdrm/omap_irq.c index 2629ba7be6c8..e01303ee00c3 100644 --- a/drivers/staging/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_irq.c + * drivers/gpu/drm/omapdrm/omap_irq.c * * Copyright (C) 2012 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c similarity index 99% rename from drivers/staging/omapdrm/omap_plane.c rename to drivers/gpu/drm/omapdrm/omap_plane.c index bb989d7f026d..dd68d14ce615 100644 --- a/drivers/staging/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -1,5 +1,5 @@ /* - * drivers/staging/omapdrm/omap_plane.c + * drivers/gpu/drm/omapdrm/omap_plane.c * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark diff --git a/drivers/staging/omapdrm/tcm-sita.c b/drivers/gpu/drm/omapdrm/tcm-sita.c similarity index 100% rename from drivers/staging/omapdrm/tcm-sita.c rename to drivers/gpu/drm/omapdrm/tcm-sita.c diff --git a/drivers/staging/omapdrm/tcm-sita.h b/drivers/gpu/drm/omapdrm/tcm-sita.h similarity index 100% rename from drivers/staging/omapdrm/tcm-sita.h rename to drivers/gpu/drm/omapdrm/tcm-sita.h diff --git a/drivers/staging/omapdrm/tcm.h b/drivers/gpu/drm/omapdrm/tcm.h similarity index 100% rename from drivers/staging/omapdrm/tcm.h rename to drivers/gpu/drm/omapdrm/tcm.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 329bdb42109f..eca907bf8b6d 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -114,8 +114,6 @@ source "drivers/staging/media/Kconfig" source "drivers/staging/net/Kconfig" -source "drivers/staging/omapdrm/Kconfig" - source "drivers/staging/android/Kconfig" source "drivers/staging/ozwpan/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index c7ec486680f7..d810ed729add 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -49,7 +49,6 @@ obj-$(CONFIG_SPEAKUP) += speakup/ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ obj-$(CONFIG_MFD_NVEC) += nvec/ -obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/ obj-$(CONFIG_USB_G_CCG) += ccg/ diff --git a/drivers/staging/omapdrm/TODO b/drivers/staging/omapdrm/TODO deleted file mode 100644 index abeeb00aaa12..000000000000 --- a/drivers/staging/omapdrm/TODO +++ /dev/null @@ -1,32 +0,0 @@ -TODO -. add video decode/encode support (via syslink3 + codec-engine) - . NOTE: with dmabuf this probably could be split into different driver - so perhaps this TODO doesn't belong here -. where should we do eviction (detatch_pages())? We aren't necessarily - accessing the pages via a GART, so maybe we need some other threshold - to put a cap on the # of pages that can be pin'd. (It is mostly only - of interest in case you have a swap partition/file.. which a lot of - these devices do not.. but it doesn't hurt for the driver to do the - right thing anyways.) - . Use mm_shrinker to trigger unpinning pages. Need to figure out how - to handle next issue first (I think?) - . Note TTM already has some mm_shrinker stuff.. maybe an argument to - move to TTM? Or maybe something that could be factored out in common? -. GEM/shmem backed pages can have existing mappings (kernel linear map, - etc..), which isn't really ideal. -. Revisit GEM sync object infrastructure.. TTM has some framework for this - already. Possibly this could be refactored out and made more common? - There should be some way to do this with less wheel-reinvention. -. Solve PM sequencing on resume. DMM/TILER must be reloaded before any - access is made from any component in the system. Which means on suspend - CRTC's should be disabled, and on resume the LUT should be reprogrammed - before CRTC's are re-enabled, to prevent DSS from trying to DMA from a - buffer mapped in DMM/TILER before LUT is reloaded. - -Userspace: -. git://github.com/robclark/xf86-video-omap.git - -Currently tested on -. OMAP3530 beagleboard -. OMAP4430 pandaboard -. OMAP4460 pandaboard diff --git a/drivers/staging/omapdrm/omap_drm.h b/include/uapi/drm/omap_drm.h similarity index 99% rename from drivers/staging/omapdrm/omap_drm.h rename to include/uapi/drm/omap_drm.h index f0ac34a8973e..1d0b1172664e 100644 --- a/drivers/staging/omapdrm/omap_drm.h +++ b/include/uapi/drm/omap_drm.h @@ -1,5 +1,5 @@ /* - * include/drm/omap_drm.h + * include/uapi/drm/omap_drm.h * * Copyright (C) 2011 Texas Instruments * Author: Rob Clark From 16ef3dfe460616f14120973f78fe640e79862654 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 24 Jan 2013 17:20:33 +0100 Subject: [PATCH 2912/4607] omapdrm: only take crtc->mutex in crtc callbacks Omapdrm doesn't do anything nefarious with crtc load detection or has any shared resources, so this is enough. We also need to adjust the WARN_ON. Signed-off-by: Daniel Vetter Signed-off-by: Rob Clark --- drivers/gpu/drm/omapdrm/omap_crtc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 2b97cf90071b..ac2258f59805 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -274,17 +274,16 @@ static void page_flip_worker(struct work_struct *work) struct omap_crtc *omap_crtc = container_of(work, struct omap_crtc, page_flip_work); struct drm_crtc *crtc = &omap_crtc->base; - struct drm_device *dev = crtc->dev; struct drm_display_mode *mode = &crtc->mode; struct drm_gem_object *bo; - drm_modeset_lock_all(dev); + mutex_lock(&crtc->mutex); omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb, 0, 0, mode->hdisplay, mode->vdisplay, crtc->x << 16, crtc->y << 16, mode->hdisplay << 16, mode->vdisplay << 16, vblank_cb, crtc); - drm_modeset_unlock_all(dev); + mutex_unlock(&crtc->mutex); bo = omap_framebuffer_bo(crtc->fb, 0); drm_gem_object_unreference_unlocked(bo); @@ -417,7 +416,7 @@ static void apply_worker(struct work_struct *work) * the callbacks and list modification all serialized * with respect to modesetting ioctls from userspace. */ - drm_modeset_lock_all(dev); + mutex_lock(&crtc->mutex); dispc_runtime_get(); /* @@ -462,16 +461,15 @@ static void apply_worker(struct work_struct *work) out: dispc_runtime_put(); - drm_modeset_unlock_all(dev); + mutex_unlock(&crtc->mutex); } int omap_crtc_apply(struct drm_crtc *crtc, struct omap_drm_apply *apply) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - struct drm_device *dev = crtc->dev; - WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + WARN_ON(!mutex_is_locked(&crtc->mutex)); /* no need to queue it again if it is already queued: */ if (apply->queued) From dfe96ddcfa22b44100814b9435770f6ff1309d37 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 24 Jan 2013 17:20:34 +0100 Subject: [PATCH 2913/4607] omapdrm: simplify locking in the fb debugfs file We don't need to hold onto mode_config.mutex any more to keep the fb objects around. And locking dev->struct_mutex is also not required, since omap_gem_describe only reads data anyway. And for a debug interface it's better to grab fewer locks in case the driver is deadlocked already ... The only thing we need is to hold onto mode_config.fb_lock to ensure the user-created fbs don't disappear. The fbcon fb doesn't need any protection, since it lives as long as the driver (and so the debugfs files) itself. And if the teardown/setup isn't following the right sequence grabbing locks won't prevent a NULL deref on priv->fbdev if the fb is not yet (or no longer) there. Signed-off-by: Daniel Vetter Signed-off-by: Rob Clark --- drivers/gpu/drm/omapdrm/omap_debugfs.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/omap_debugfs.c b/drivers/gpu/drm/omapdrm/omap_debugfs.c index c0aa40f8ad6a..c27f59da7f29 100644 --- a/drivers/gpu/drm/omapdrm/omap_debugfs.c +++ b/drivers/gpu/drm/omapdrm/omap_debugfs.c @@ -57,17 +57,6 @@ static int fb_show(struct seq_file *m, void *arg) struct drm_device *dev = node->minor->dev; struct omap_drm_private *priv = dev->dev_private; struct drm_framebuffer *fb; - int ret; - - ret = mutex_lock_interruptible(&dev->mode_config.mutex); - if (ret) - return ret; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) { - mutex_unlock(&dev->mode_config.mutex); - return ret; - } seq_printf(m, "fbcon "); omap_framebuffer_describe(priv->fbdev->fb, m); @@ -82,9 +71,6 @@ static int fb_show(struct seq_file *m, void *arg) } mutex_unlock(&dev->mode_config.fb_lock); - mutex_unlock(&dev->struct_mutex); - mutex_unlock(&dev->mode_config.mutex); - return 0; } From 8e44770f093c003d11784060fea4ffc82ad576eb Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Sat, 16 Feb 2013 16:40:36 -0500 Subject: [PATCH 2914/4607] drm/omap: remove fbdev debug enter/leave hooks This will result in badness for drivers that do not implement mode_set_base_atomic(). So don't pretend like we can support this. Signed-off-by: Rob Clark --- drivers/gpu/drm/omapdrm/omap_fbdev.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 11eed30efe06..f0033bd3e4ae 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -131,9 +131,6 @@ static struct fb_ops omap_fb_ops = { .fb_pan_display = omap_fbdev_pan_display, .fb_blank = drm_fb_helper_blank, .fb_setcmap = drm_fb_helper_setcmap, - - .fb_debug_enter = drm_fb_helper_debug_enter, - .fb_debug_leave = drm_fb_helper_debug_leave, }; static int omap_fbdev_create(struct drm_fb_helper *helper, From 42f3caef039571df8c30a78a51c09cbdbaa7863b Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 11 Jan 2013 22:44:10 +0100 Subject: [PATCH 2915/4607] MIPS: show correct cpu name for 24KEc Make sure 24KEc is properly identified inside /proc/cpuinfo Signed-off-by: John Crispin --- arch/mips/kernel/cpu-probe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index cce3782c96c9..9f31334ed1de 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -838,10 +838,13 @@ static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu) __cpu_name[cpu] = "MIPS 20Kc"; break; case PRID_IMP_24K: - case PRID_IMP_24KE: c->cputype = CPU_24K; __cpu_name[cpu] = "MIPS 24Kc"; break; + case PRID_IMP_24KE: + c->cputype = CPU_24K; + __cpu_name[cpu] = "MIPS 24KEc"; + break; case PRID_IMP_25KF: c->cputype = CPU_25KF; __cpu_name[cpu] = "MIPS 25Kc"; From 3d18c17e4f1699c3a4f47d2483c5d4c3ab3a242b Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sat, 19 Jan 2013 08:54:23 +0000 Subject: [PATCH 2916/4607] MIPS: lantiq: trivial typo fix "nodes" is written with a single "s" Signed-off-by: John Crispin Patchwork: http://patchwork.linux-mips.org/patch/4814/ --- arch/mips/lantiq/xway/sysctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c index 3925e6609acc..1aaa726aad47 100644 --- a/arch/mips/lantiq/xway/sysctrl.c +++ b/arch/mips/lantiq/xway/sysctrl.c @@ -305,7 +305,7 @@ void __init ltq_soc_init(void) /* check if all the core register ranges are available */ if (!np_pmu || !np_cgu || !np_ebu) - panic("Failed to load core nodess from devicetree"); + panic("Failed to load core nodes from devicetree"); if (of_address_to_resource(np_pmu, 0, &res_pmu) || of_address_to_resource(np_cgu, 0, &res_cgu) || From 740c606e8e79c3e3800afbc32b4e6123da403d6c Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sat, 19 Jan 2013 08:54:24 +0000 Subject: [PATCH 2917/4607] MIPS: lantiq: adds static clock for PP32 The Lantiq DSL SoCs have an internal networking processor. Add code to read the static clock rate. Signed-off-by: John Crispin Patchwork: http://patchwork.linux-mips.org/patch/4815/ --- arch/mips/include/asm/mach-lantiq/lantiq.h | 1 + arch/mips/lantiq/clk.c | 12 +++++- arch/mips/lantiq/clk.h | 7 +++- arch/mips/lantiq/falcon/sysctrl.c | 4 +- arch/mips/lantiq/xway/clk.c | 43 ++++++++++++++++++++++ arch/mips/lantiq/xway/sysctrl.c | 12 +++--- 6 files changed, 69 insertions(+), 10 deletions(-) diff --git a/arch/mips/include/asm/mach-lantiq/lantiq.h b/arch/mips/include/asm/mach-lantiq/lantiq.h index 5e8a6e965756..76be7a09b9aa 100644 --- a/arch/mips/include/asm/mach-lantiq/lantiq.h +++ b/arch/mips/include/asm/mach-lantiq/lantiq.h @@ -41,6 +41,7 @@ extern void clk_deactivate(struct clk *clk); extern struct clk *clk_get_cpu(void); extern struct clk *clk_get_fpi(void); extern struct clk *clk_get_io(void); +extern struct clk *clk_get_ppe(void); /* find out what bootsource we have */ extern unsigned char ltq_boot_select(void); diff --git a/arch/mips/lantiq/clk.c b/arch/mips/lantiq/clk.c index ce2f129b081f..d90356004027 100644 --- a/arch/mips/lantiq/clk.c +++ b/arch/mips/lantiq/clk.c @@ -26,13 +26,15 @@ #include "prom.h" /* lantiq socs have 3 static clocks */ -static struct clk cpu_clk_generic[3]; +static struct clk cpu_clk_generic[4]; -void clkdev_add_static(unsigned long cpu, unsigned long fpi, unsigned long io) +void clkdev_add_static(unsigned long cpu, unsigned long fpi, + unsigned long io, unsigned long ppe) { cpu_clk_generic[0].rate = cpu; cpu_clk_generic[1].rate = fpi; cpu_clk_generic[2].rate = io; + cpu_clk_generic[3].rate = ppe; } struct clk *clk_get_cpu(void) @@ -51,6 +53,12 @@ struct clk *clk_get_io(void) return &cpu_clk_generic[2]; } +struct clk *clk_get_ppe(void) +{ + return &cpu_clk_generic[3]; +} +EXPORT_SYMBOL_GPL(clk_get_ppe); + static inline int clk_good(struct clk *clk) { return clk && !IS_ERR(clk); diff --git a/arch/mips/lantiq/clk.h b/arch/mips/lantiq/clk.h index fa670602b91b..77e4bdb1fe8c 100644 --- a/arch/mips/lantiq/clk.h +++ b/arch/mips/lantiq/clk.h @@ -27,12 +27,15 @@ #define CLOCK_167M 166666667 #define CLOCK_196_608M 196608000 #define CLOCK_200M 200000000 +#define CLOCK_222M 222000000 +#define CLOCK_240M 240000000 #define CLOCK_250M 250000000 #define CLOCK_266M 266666666 #define CLOCK_300M 300000000 #define CLOCK_333M 333333333 #define CLOCK_393M 393215332 #define CLOCK_400M 400000000 +#define CLOCK_450M 450000000 #define CLOCK_500M 500000000 #define CLOCK_600M 600000000 @@ -64,15 +67,17 @@ struct clk { }; extern void clkdev_add_static(unsigned long cpu, unsigned long fpi, - unsigned long io); + unsigned long io, unsigned long ppe); extern unsigned long ltq_danube_cpu_hz(void); extern unsigned long ltq_danube_fpi_hz(void); +extern unsigned long ltq_danube_pp32_hz(void); extern unsigned long ltq_ar9_cpu_hz(void); extern unsigned long ltq_ar9_fpi_hz(void); extern unsigned long ltq_vr9_cpu_hz(void); extern unsigned long ltq_vr9_fpi_hz(void); +extern unsigned long ltq_vr9_pp32_hz(void); #endif diff --git a/arch/mips/lantiq/falcon/sysctrl.c b/arch/mips/lantiq/falcon/sysctrl.c index 2d4ced332b37..ff4894a833ee 100644 --- a/arch/mips/lantiq/falcon/sysctrl.c +++ b/arch/mips/lantiq/falcon/sysctrl.c @@ -241,9 +241,9 @@ void __init ltq_soc_init(void) /* get our 3 static rates for cpu, fpi and io clocks */ if (ltq_sys1_r32(SYS1_CPU0CC) & CPU0CC_CPUDIV) - clkdev_add_static(CLOCK_200M, CLOCK_100M, CLOCK_200M); + clkdev_add_static(CLOCK_200M, CLOCK_100M, CLOCK_200M, 0); else - clkdev_add_static(CLOCK_400M, CLOCK_100M, CLOCK_200M); + clkdev_add_static(CLOCK_400M, CLOCK_100M, CLOCK_200M, 0); /* add our clock domains */ clkdev_add_sys("1d810000.gpio", SYSCTL_SYSETH, ACTS_P0); diff --git a/arch/mips/lantiq/xway/clk.c b/arch/mips/lantiq/xway/clk.c index 9aa17f79a742..1ab576dc9bd1 100644 --- a/arch/mips/lantiq/xway/clk.c +++ b/arch/mips/lantiq/xway/clk.c @@ -53,6 +53,29 @@ unsigned long ltq_danube_cpu_hz(void) } } +unsigned long ltq_danube_pp32_hz(void) +{ + unsigned int clksys = (ltq_cgu_r32(CGU_SYS) >> 7) & 3; + unsigned long clk; + + switch (clksys) { + case 1: + clk = CLOCK_240M; + break; + case 2: + clk = CLOCK_222M; + break; + case 3: + clk = CLOCK_133M; + break; + default: + clk = CLOCK_266M; + break; + } + + return clk; +} + unsigned long ltq_ar9_sys_hz(void) { if (((ltq_cgu_r32(CGU_SYS) >> 3) & 0x3) == 0x2) @@ -149,3 +172,23 @@ unsigned long ltq_vr9_fpi_hz(void) return clk; } + +unsigned long ltq_vr9_pp32_hz(void) +{ + unsigned int clksys = (ltq_cgu_r32(CGU_SYS) >> 16) & 3; + unsigned long clk; + + switch (clksys) { + case 1: + clk = CLOCK_450M; + break; + case 2: + clk = CLOCK_300M; + break; + default: + clk = CLOCK_500M; + break; + } + + return clk; +} diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c index 1aaa726aad47..3390fcd6ee56 100644 --- a/arch/mips/lantiq/xway/sysctrl.c +++ b/arch/mips/lantiq/xway/sysctrl.c @@ -356,14 +356,16 @@ void __init ltq_soc_init(void) if (of_machine_is_compatible("lantiq,ase")) { if (ltq_cgu_r32(CGU_SYS) & (1 << 5)) - clkdev_add_static(CLOCK_266M, CLOCK_133M, CLOCK_133M); + clkdev_add_static(CLOCK_266M, CLOCK_133M, + CLOCK_133M, CLOCK_266M); else - clkdev_add_static(CLOCK_133M, CLOCK_133M, CLOCK_133M); + clkdev_add_static(CLOCK_133M, CLOCK_133M, + CLOCK_133M, CLOCK_133M); clkdev_add_cgu("1e180000.etop", "ephycgu", CGU_EPHY), clkdev_add_pmu("1e180000.etop", "ephy", 0, PMU_EPHY); } else if (of_machine_is_compatible("lantiq,vr9")) { clkdev_add_static(ltq_vr9_cpu_hz(), ltq_vr9_fpi_hz(), - ltq_vr9_fpi_hz()); + ltq_vr9_fpi_hz(), ltq_vr9_pp32_hz()); clkdev_add_pmu("1d900000.pcie", "phy", 1, PMU1_PCIE_PHY); clkdev_add_pmu("1d900000.pcie", "bus", 0, PMU_PCIE_CLK); clkdev_add_pmu("1d900000.pcie", "msi", 1, PMU1_PCIE_MSI); @@ -376,10 +378,10 @@ void __init ltq_soc_init(void) PMU_PPE_QSB | PMU_PPE_TOP); } else if (of_machine_is_compatible("lantiq,ar9")) { clkdev_add_static(ltq_ar9_cpu_hz(), ltq_ar9_fpi_hz(), - ltq_ar9_fpi_hz()); + ltq_ar9_fpi_hz(), CLOCK_250M); clkdev_add_pmu("1e180000.etop", "switch", 0, PMU_SWITCH); } else { clkdev_add_static(ltq_danube_cpu_hz(), ltq_danube_fpi_hz(), - ltq_danube_fpi_hz()); + ltq_danube_fpi_hz(), ltq_danube_pp32_hz()); } } From d0c550dc36881fda171ec8ad3dcc67491ad968eb Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sat, 19 Jan 2013 08:54:25 +0000 Subject: [PATCH 2918/4607] MIPS: lantiq: add GPHY clock gate bits Explicitly enable the clock gate of the internal GPHYs found on xrx200. Signed-off-by: John Crispin Patchwork: http://patchwork.linux-mips.org/patch/4816/ --- arch/mips/lantiq/xway/reset.c | 9 +++++++++ arch/mips/lantiq/xway/sysctrl.c | 1 + 2 files changed, 10 insertions(+) diff --git a/arch/mips/lantiq/xway/reset.c b/arch/mips/lantiq/xway/reset.c index 544dbb7fb421..1fa0f175357e 100644 --- a/arch/mips/lantiq/xway/reset.c +++ b/arch/mips/lantiq/xway/reset.c @@ -78,10 +78,19 @@ static struct ltq_xrx200_gphy_reset { /* reset and boot a gphy. these phys only exist on xrx200 SoC */ int xrx200_gphy_boot(struct device *dev, unsigned int id, dma_addr_t dev_addr) { + struct clk *clk; + if (!of_device_is_compatible(ltq_rcu_np, "lantiq,rcu-xrx200")) { dev_err(dev, "this SoC has no GPHY\n"); return -EINVAL; } + + clk = clk_get_sys("1f203000.rcu", "gphy"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + clk_enable(clk); + if (id > 1) { dev_err(dev, "%u is an invalid gphy id\n", id); return -EINVAL; diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c index 3390fcd6ee56..c24924fe087d 100644 --- a/arch/mips/lantiq/xway/sysctrl.c +++ b/arch/mips/lantiq/xway/sysctrl.c @@ -376,6 +376,7 @@ void __init ltq_soc_init(void) PMU_SWITCH | PMU_PPE_DPLUS | PMU_PPE_DPLUM | PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 | PMU_PPE_QSB | PMU_PPE_TOP); + clkdev_add_pmu("1f203000.rcu", "gphy", 0, PMU_GPHY); } else if (of_machine_is_compatible("lantiq,ar9")) { clkdev_add_static(ltq_ar9_cpu_hz(), ltq_ar9_fpi_hz(), ltq_ar9_fpi_hz(), CLOCK_250M); From bae696a267d81ea268f4de1e396b8c82154f22ed Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sat, 19 Jan 2013 08:54:26 +0000 Subject: [PATCH 2919/4607] MIPS: lantiq: improve pci reset gpio handling We need to make sure that the reset gpio is available and also set a sane default state. Signed-off-by: John Crispin Patchwork: http://patchwork.linux-mips.org/patch/4817/ --- arch/mips/pci/pci-lantiq.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c index 95681789b51e..f32664bbbe17 100644 --- a/arch/mips/pci/pci-lantiq.c +++ b/arch/mips/pci/pci-lantiq.c @@ -129,8 +129,16 @@ static int ltq_pci_startup(struct platform_device *pdev) /* setup reset gpio used by pci */ reset_gpio = of_get_named_gpio(node, "gpio-reset", 0); - if (gpio_is_valid(reset_gpio)) - devm_gpio_request(&pdev->dev, reset_gpio, "pci-reset"); + if (gpio_is_valid(reset_gpio)) { + int ret = devm_gpio_request(&pdev->dev, + reset_gpio, "pci-reset"); + if (ret) { + dev_err(&pdev->dev, + "failed to request gpio %d\n", reset_gpio); + return ret; + } + gpio_direction_output(reset_gpio, 1); + } /* enable auto-switching between PCI and EBU */ ltq_pci_w32(0xa, PCI_CR_CLK_CTRL); From 26365625947fb7d6501065272a1fd59460c0f4ed Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sat, 19 Jan 2013 08:54:27 +0000 Subject: [PATCH 2920/4607] MIPS: lantiq: rework external irq code This code makes the irqs used by the EIU loadable from the DT. Additionally we add a helper that allows the pinctrl layer to map external irqs to real irq numbers. Signed-off-by: John Crispin Patchwork: http://patchwork.linux-mips.org/patch/4818/ --- arch/mips/include/asm/mach-lantiq/lantiq.h | 1 + arch/mips/lantiq/irq.c | 105 ++++++++++++++------- 2 files changed, 74 insertions(+), 32 deletions(-) diff --git a/arch/mips/include/asm/mach-lantiq/lantiq.h b/arch/mips/include/asm/mach-lantiq/lantiq.h index 76be7a09b9aa..f196cceb7322 100644 --- a/arch/mips/include/asm/mach-lantiq/lantiq.h +++ b/arch/mips/include/asm/mach-lantiq/lantiq.h @@ -34,6 +34,7 @@ extern spinlock_t ebu_lock; extern void ltq_disable_irq(struct irq_data *data); extern void ltq_mask_and_ack_irq(struct irq_data *data); extern void ltq_enable_irq(struct irq_data *data); +extern int ltq_eiu_get_irq(int exin); /* clock handling */ extern int clk_activate(struct clk *clk); diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c index a7935bf0fecb..51194875f158 100644 --- a/arch/mips/lantiq/irq.c +++ b/arch/mips/lantiq/irq.c @@ -33,17 +33,10 @@ /* register definitions - external irqs */ #define LTQ_EIU_EXIN_C 0x0000 #define LTQ_EIU_EXIN_INIC 0x0004 +#define LTQ_EIU_EXIN_INC 0x0008 #define LTQ_EIU_EXIN_INEN 0x000C -/* irq numbers used by the external interrupt unit (EIU) */ -#define LTQ_EIU_IR0 (INT_NUM_IM4_IRL0 + 30) -#define LTQ_EIU_IR1 (INT_NUM_IM3_IRL0 + 31) -#define LTQ_EIU_IR2 (INT_NUM_IM1_IRL0 + 26) -#define LTQ_EIU_IR3 INT_NUM_IM1_IRL0 -#define LTQ_EIU_IR4 (INT_NUM_IM1_IRL0 + 1) -#define LTQ_EIU_IR5 (INT_NUM_IM1_IRL0 + 2) -#define LTQ_EIU_IR6 (INT_NUM_IM2_IRL0 + 30) -#define XWAY_EXIN_COUNT 3 +/* number of external interrupts */ #define MAX_EIU 6 /* the performance counter */ @@ -72,20 +65,19 @@ int gic_present; #endif -static unsigned short ltq_eiu_irq[MAX_EIU] = { - LTQ_EIU_IR0, - LTQ_EIU_IR1, - LTQ_EIU_IR2, - LTQ_EIU_IR3, - LTQ_EIU_IR4, - LTQ_EIU_IR5, -}; - static int exin_avail; +static struct resource ltq_eiu_irq[MAX_EIU]; static void __iomem *ltq_icu_membase[MAX_IM]; static void __iomem *ltq_eiu_membase; static struct irq_domain *ltq_domain; +int ltq_eiu_get_irq(int exin) +{ + if (exin < exin_avail) + return ltq_eiu_irq[exin].start; + return -1; +} + void ltq_disable_irq(struct irq_data *d) { u32 ier = LTQ_ICU_IM0_IER; @@ -128,19 +120,65 @@ void ltq_enable_irq(struct irq_data *d) ltq_icu_w32(im, ltq_icu_r32(im, ier) | BIT(offset), ier); } +static int ltq_eiu_settype(struct irq_data *d, unsigned int type) +{ + int i; + + for (i = 0; i < MAX_EIU; i++) { + if (d->hwirq == ltq_eiu_irq[i].start) { + int val = 0; + int edge = 0; + + switch (type) { + case IRQF_TRIGGER_NONE: + break; + case IRQF_TRIGGER_RISING: + val = 1; + edge = 1; + break; + case IRQF_TRIGGER_FALLING: + val = 2; + edge = 1; + break; + case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING: + val = 3; + edge = 1; + break; + case IRQF_TRIGGER_HIGH: + val = 5; + break; + case IRQF_TRIGGER_LOW: + val = 6; + break; + default: + pr_err("invalid type %d for irq %ld\n", + type, d->hwirq); + return -EINVAL; + } + + if (edge) + irq_set_handler(d->hwirq, handle_edge_irq); + + ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) | + (val << (i * 4)), LTQ_EIU_EXIN_C); + } + } + + return 0; +} + static unsigned int ltq_startup_eiu_irq(struct irq_data *d) { int i; ltq_enable_irq(d); for (i = 0; i < MAX_EIU; i++) { - if (d->hwirq == ltq_eiu_irq[i]) { - /* low level - we should really handle set_type */ - ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) | - (0x6 << (i * 4)), LTQ_EIU_EXIN_C); + if (d->hwirq == ltq_eiu_irq[i].start) { + /* by default we are low level triggered */ + ltq_eiu_settype(d, IRQF_TRIGGER_LOW); /* clear all pending */ - ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INIC) & ~BIT(i), - LTQ_EIU_EXIN_INIC); + ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INC) & ~BIT(i), + LTQ_EIU_EXIN_INC); /* enable */ ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) | BIT(i), LTQ_EIU_EXIN_INEN); @@ -157,7 +195,7 @@ static void ltq_shutdown_eiu_irq(struct irq_data *d) ltq_disable_irq(d); for (i = 0; i < MAX_EIU; i++) { - if (d->hwirq == ltq_eiu_irq[i]) { + if (d->hwirq == ltq_eiu_irq[i].start) { /* disable */ ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) & ~BIT(i), LTQ_EIU_EXIN_INEN); @@ -186,6 +224,7 @@ static struct irq_chip ltq_eiu_type = { .irq_ack = ltq_ack_irq, .irq_mask = ltq_disable_irq, .irq_mask_ack = ltq_mask_and_ack_irq, + .irq_set_type = ltq_eiu_settype, }; static void ltq_hw_irqdispatch(int module) @@ -301,7 +340,7 @@ static int icu_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) return 0; for (i = 0; i < exin_avail; i++) - if (hw == ltq_eiu_irq[i]) + if (hw == ltq_eiu_irq[i].start) chip = <q_eiu_type; irq_set_chip_and_handler(hw, chip, handle_level_irq); @@ -323,7 +362,7 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent) { struct device_node *eiu_node; struct resource res; - int i; + int i, ret; for (i = 0; i < MAX_IM; i++) { if (of_address_to_resource(node, i, &res)) @@ -340,17 +379,19 @@ int __init icu_of_init(struct device_node *node, struct device_node *parent) } /* the external interrupts are optional and xway only */ - eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu"); + eiu_node = of_find_compatible_node(NULL, NULL, "lantiq,eiu-xway"); if (eiu_node && !of_address_to_resource(eiu_node, 0, &res)) { /* find out how many external irq sources we have */ - const __be32 *count = of_get_property(node, - "lantiq,count", NULL); + exin_avail = of_irq_count(eiu_node); - if (count) - exin_avail = *count; if (exin_avail > MAX_EIU) exin_avail = MAX_EIU; + ret = of_irq_to_resource_table(eiu_node, + ltq_eiu_irq, exin_avail); + if (ret != exin_avail) + panic("failed to load external irq resources\n"); + if (request_mem_region(res.start, resource_size(&res), res.name) < 0) pr_err("Failed to request eiu memory"); From f0cb40e5c384cf2cc4b2b932b61474544ac1fc9a Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Mon, 14 Jan 2013 15:11:53 +0000 Subject: [PATCH 2921/4607] MIPS: Netlogic: add XLS6xx to FMN config Add support for XLS6xx CPUs to the Fast Message Network (FMN) configuration. Signed-off-by: Jayachandran C Patchwork: http://patchwork.linux-mips.org/patch/4785/ Signed-off-by: John Crispin --- arch/mips/netlogic/xlr/fmn-config.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/netlogic/xlr/fmn-config.c b/arch/mips/netlogic/xlr/fmn-config.c index bed2cffa1008..e8071d6e8553 100644 --- a/arch/mips/netlogic/xlr/fmn-config.c +++ b/arch/mips/netlogic/xlr/fmn-config.c @@ -216,6 +216,8 @@ void xlr_board_info_setup(void) case PRID_IMP_NETLOGIC_XLS404B: case PRID_IMP_NETLOGIC_XLS408B: case PRID_IMP_NETLOGIC_XLS416B: + case PRID_IMP_NETLOGIC_XLS608B: + case PRID_IMP_NETLOGIC_XLS616B: setup_fmn_cc(&gmac[0], FMN_STNID_GMAC0, FMN_STNID_GMAC0_TX3, 8, 8, 32); setup_fmn_cc(&gmac[1], FMN_STNID_GMAC1_FR_0, From 220d9122e8c5a467fdeefc1857e077f29a623bfd Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Mon, 14 Jan 2013 15:11:54 +0000 Subject: [PATCH 2922/4607] MIPS: Netlogic: Optimize EIMR/EIRR accesses in 32-bit Provide functions ack_c0_eirr(), set_c0_eimr(), clear_c0_eimr() and read_c0_eirr_and_eimr() that do the EIMR and EIRR operations and update the interrupt handling code to use these functions. Also, use the EIMR register functions to mask interrupts in the irq code. The 64-bit interrupt request and mask registers (EIRR and EIMR) are accessed when the interrupts are off, and the common operations are to set or clear a bit in these registers. Using the 64-bit c0 access functions for these operations is not optimal in 32-bit, because it will disable/restore interrupts and split/join the 64-bit value during each register access. Signed-off-by: Jayachandran C Patchwork: http://patchwork.linux-mips.org/patch/4790/ Signed-off-by: John Crispin --- arch/mips/include/asm/netlogic/mips-extns.h | 79 +++++++++++++++++++++ arch/mips/netlogic/common/irq.c | 39 ++++------ arch/mips/netlogic/common/smp.c | 8 ++- 3 files changed, 98 insertions(+), 28 deletions(-) diff --git a/arch/mips/include/asm/netlogic/mips-extns.h b/arch/mips/include/asm/netlogic/mips-extns.h index 32ba6d95d47c..cc4296595df5 100644 --- a/arch/mips/include/asm/netlogic/mips-extns.h +++ b/arch/mips/include/asm/netlogic/mips-extns.h @@ -68,6 +68,85 @@ do { \ __write_64bit_c0_register($9, 7, (val)); \ } while (0) +/* + * Handling the 64 bit EIMR and EIRR registers in 32-bit mode with + * standard functions will be very inefficient. This provides + * optimized functions for the normal operations on the registers. + * + * Call with interrupts disabled. + */ +static inline void ack_c0_eirr(int irq) +{ + __asm__ __volatile__( + ".set push\n\t" + ".set mips64\n\t" + ".set noat\n\t" + "li $1, 1\n\t" + "dsllv $1, $1, %0\n\t" + "dmtc0 $1, $9, 6\n\t" + ".set pop" + : : "r" (irq)); +} + +static inline void set_c0_eimr(int irq) +{ + __asm__ __volatile__( + ".set push\n\t" + ".set mips64\n\t" + ".set noat\n\t" + "li $1, 1\n\t" + "dsllv %0, $1, %0\n\t" + "dmfc0 $1, $9, 7\n\t" + "or $1, %0\n\t" + "dmtc0 $1, $9, 7\n\t" + ".set pop" + : "+r" (irq)); +} + +static inline void clear_c0_eimr(int irq) +{ + __asm__ __volatile__( + ".set push\n\t" + ".set mips64\n\t" + ".set noat\n\t" + "li $1, 1\n\t" + "dsllv %0, $1, %0\n\t" + "dmfc0 $1, $9, 7\n\t" + "or $1, %0\n\t" + "xor $1, %0\n\t" + "dmtc0 $1, $9, 7\n\t" + ".set pop" + : "+r" (irq)); +} + +/* + * Read c0 eimr and c0 eirr, do AND of the two values, the result is + * the interrupts which are raised and are not masked. + */ +static inline uint64_t read_c0_eirr_and_eimr(void) +{ + uint64_t val; + +#ifdef CONFIG_64BIT + val = read_c0_eimr() & read_c0_eirr(); +#else + __asm__ __volatile__( + ".set push\n\t" + ".set mips64\n\t" + ".set noat\n\t" + "dmfc0 %M0, $9, 6\n\t" + "dmfc0 %L0, $9, 7\n\t" + "and %M0, %L0\n\t" + "dsll %L0, %M0, 32\n\t" + "dsra %M0, %M0, 32\n\t" + "dsra %L0, %L0, 32\n\t" + ".set pop" + : "=r" (val)); +#endif + + return val; +} + static inline int hard_smp_processor_id(void) { return __read_32bit_c0_register($15, 1) & 0x3ff; diff --git a/arch/mips/netlogic/common/irq.c b/arch/mips/netlogic/common/irq.c index 00dcc7a2bc5a..d42cd1a2a124 100644 --- a/arch/mips/netlogic/common/irq.c +++ b/arch/mips/netlogic/common/irq.c @@ -105,21 +105,23 @@ static void xlp_pic_disable(struct irq_data *d) static void xlp_pic_mask_ack(struct irq_data *d) { struct nlm_pic_irq *pd = irq_data_get_irq_handler_data(d); - uint64_t mask = 1ull << pd->picirq; - write_c0_eirr(mask); /* ack by writing EIRR */ + clear_c0_eimr(pd->picirq); + ack_c0_eirr(pd->picirq); } static void xlp_pic_unmask(struct irq_data *d) { struct nlm_pic_irq *pd = irq_data_get_irq_handler_data(d); - if (!pd) - return; + BUG_ON(!pd); if (pd->extra_ack) pd->extra_ack(d); + /* re-enable the intr on this cpu */ + set_c0_eimr(pd->picirq); + /* Ack is a single write, no need to lock */ nlm_pic_ack(pd->node->picbase, pd->irt); } @@ -134,32 +136,17 @@ static struct irq_chip xlp_pic = { static void cpuintr_disable(struct irq_data *d) { - uint64_t eimr; - uint64_t mask = 1ull << d->irq; - - eimr = read_c0_eimr(); - write_c0_eimr(eimr & ~mask); + clear_c0_eimr(d->irq); } static void cpuintr_enable(struct irq_data *d) { - uint64_t eimr; - uint64_t mask = 1ull << d->irq; - - eimr = read_c0_eimr(); - write_c0_eimr(eimr | mask); + set_c0_eimr(d->irq); } static void cpuintr_ack(struct irq_data *d) { - uint64_t mask = 1ull << d->irq; - - write_c0_eirr(mask); -} - -static void cpuintr_nop(struct irq_data *d) -{ - WARN(d->irq >= PIC_IRQ_BASE, "Bad irq %d", d->irq); + ack_c0_eirr(d->irq); } /* @@ -170,9 +157,9 @@ struct irq_chip nlm_cpu_intr = { .name = "XLP-CPU-INTR", .irq_enable = cpuintr_enable, .irq_disable = cpuintr_disable, - .irq_mask = cpuintr_nop, - .irq_ack = cpuintr_nop, - .irq_eoi = cpuintr_ack, + .irq_mask = cpuintr_disable, + .irq_ack = cpuintr_ack, + .irq_eoi = cpuintr_enable, }; static void __init nlm_init_percpu_irqs(void) @@ -265,7 +252,7 @@ asmlinkage void plat_irq_dispatch(void) int i, node; node = nlm_nodeid(); - eirr = read_c0_eirr() & read_c0_eimr(); + eirr = read_c0_eirr_and_eimr(); i = __ilog2_u64(eirr); if (i == -1) diff --git a/arch/mips/netlogic/common/smp.c b/arch/mips/netlogic/common/smp.c index a080d9ee3cd7..2bb95dcfe20a 100644 --- a/arch/mips/netlogic/common/smp.c +++ b/arch/mips/netlogic/common/smp.c @@ -84,15 +84,19 @@ void nlm_send_ipi_mask(const struct cpumask *mask, unsigned int action) /* IRQ_IPI_SMP_FUNCTION Handler */ void nlm_smp_function_ipi_handler(unsigned int irq, struct irq_desc *desc) { - write_c0_eirr(1ull << irq); + clear_c0_eimr(irq); + ack_c0_eirr(irq); smp_call_function_interrupt(); + set_c0_eimr(irq); } /* IRQ_IPI_SMP_RESCHEDULE handler */ void nlm_smp_resched_ipi_handler(unsigned int irq, struct irq_desc *desc) { - write_c0_eirr(1ull << irq); + clear_c0_eimr(irq); + ack_c0_eirr(irq); scheduler_ipi(); + set_c0_eimr(irq); } /* From a264b5e8dc3cae1b07cea010d6283be6e67b0209 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Wed, 16 Jan 2013 11:12:40 +0000 Subject: [PATCH 2923/4607] MIPS: PCI: Byteswap not needed in little-endian mode Rename function xlp_enable_pci_bswap() to xlp_config_pci_bswap(), which is a better description for its functionality. When compiled in big-endian mode, xlp_config_pci_bswap() will configure the PCIe links to byteswap. In little-endian mode, no swap configuration is needed for the PCIe controller, and the function is empty. Signed-off-by: Jayachandran C Patchwork: http://patchwork.linux-mips.org/patch/4802/ Signed-off-by: John Crispin --- arch/mips/pci/pci-xlp.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/arch/mips/pci/pci-xlp.c b/arch/mips/pci/pci-xlp.c index 140557a20488..5077148bd67d 100644 --- a/arch/mips/pci/pci-xlp.c +++ b/arch/mips/pci/pci-xlp.c @@ -191,7 +191,13 @@ int pcibios_plat_dev_init(struct pci_dev *dev) return 0; } -static int xlp_enable_pci_bswap(void) +/* + * If big-endian, enable hardware byteswap on the PCIe bridges. + * This will make both the SoC and PCIe devices behave consistently with + * readl/writel. + */ +#ifdef __BIG_ENDIAN +static void xlp_config_pci_bswap(void) { uint64_t pciebase, sysbase; int node, i; @@ -222,8 +228,11 @@ static int xlp_enable_pci_bswap(void) reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEIO_LIMIT0 + i); nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff); } - return 0; } +#else +/* Swap configuration not needed in little-endian mode */ +static inline void xlp_config_pci_bswap(void) {} +#endif /* __BIG_ENDIAN */ static int __init pcibios_init(void) { @@ -235,7 +244,7 @@ static int __init pcibios_init(void) ioport_resource.start = 0; ioport_resource.end = ~0; - xlp_enable_pci_bswap(); + xlp_config_pci_bswap(); set_io_port_base(CKSEG1); nlm_pci_controller.io_map_base = CKSEG1; From a69ba6293d11b7dfd395a742f3449d6ddda8ecad Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Mon, 14 Jan 2013 15:11:56 +0000 Subject: [PATCH 2924/4607] MIPS: Netlogic: Split XLP L1 i-cache among threads Since we now use r4k cache code for Netlogic XLP, it is better to split L1 icache among the active threads, so that threads won't step on each other while flushing icache. The L1 dcache is already split among the threads in the core. Signed-off-by: Jayachandran C Patchwork: http://patchwork.linux-mips.org/patch/4787/ Signed-off-by: John Crispin --- arch/mips/include/asm/netlogic/xlp-hal/cpucontrol.h | 2 ++ arch/mips/netlogic/common/smpboot.S | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/arch/mips/include/asm/netlogic/xlp-hal/cpucontrol.h b/arch/mips/include/asm/netlogic/xlp-hal/cpucontrol.h index 7b63a6b722a0..6d2e58a9a542 100644 --- a/arch/mips/include/asm/netlogic/xlp-hal/cpucontrol.h +++ b/arch/mips/include/asm/netlogic/xlp-hal/cpucontrol.h @@ -46,6 +46,8 @@ #define CPU_BLOCKID_FPU 9 #define CPU_BLOCKID_MAP 10 +#define ICU_DEFEATURE 0x100 + #define LSU_DEFEATURE 0x304 #define LSU_DEBUG_ADDR 0x305 #define LSU_DEBUG_DATA0 0x306 diff --git a/arch/mips/netlogic/common/smpboot.S b/arch/mips/netlogic/common/smpboot.S index a0b74874bebe..d772a87fe846 100644 --- a/arch/mips/netlogic/common/smpboot.S +++ b/arch/mips/netlogic/common/smpboot.S @@ -69,6 +69,12 @@ #endif mtcr t1, t0 + li t0, ICU_DEFEATURE + mfcr t1, t0 + ori t1, 0x1000 /* Enable Icache partitioning */ + mtcr t1, t0 + + #ifdef XLP_AX_WORKAROUND li t0, SCHED_DEFEATURE lui t1, 0x0100 /* Disable BRU accepting ALU ops */ From 4e45e542cd742c1c3e30e7f252640644c66548b5 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Mon, 14 Jan 2013 15:11:57 +0000 Subject: [PATCH 2925/4607] MIPS: Netlogic: Use PIC timer as a clocksource The XLR/XLS/XLP PIC has a 8 countdown timers which run at the PIC frequencey. One of these can be used as a clocksource to provide timestamps that is common across cores. This can be used in place of the count/compare clocksource which is per-CPU. On XLR/XLS PIC registers are 32-bit, so we just use the lower 32-bits of the PIC counter. On XLP, the whole 64-bit can be used. Provide common macros and functions for PIC timer registers on XLR/XLS and XLP, and use them to register a PIC clocksource. Signed-off-by: Jayachandran C Patchwork: http://patchwork.linux-mips.org/patch/4786/ Signed-off-by: John Crispin --- arch/mips/include/asm/netlogic/xlp-hal/pic.h | 12 ++++- arch/mips/include/asm/netlogic/xlr/pic.h | 48 ++++++++++++++++-- arch/mips/netlogic/common/irq.c | 2 +- arch/mips/netlogic/common/time.c | 52 ++++++++++++++++++++ arch/mips/netlogic/xlr/platform.c | 2 +- arch/mips/netlogic/xlr/setup.c | 2 +- 6 files changed, 110 insertions(+), 8 deletions(-) diff --git a/arch/mips/include/asm/netlogic/xlp-hal/pic.h b/arch/mips/include/asm/netlogic/xlp-hal/pic.h index b2e53a5383ab..ea6768c3e9f8 100644 --- a/arch/mips/include/asm/netlogic/xlp-hal/pic.h +++ b/arch/mips/include/asm/netlogic/xlp-hal/pic.h @@ -261,6 +261,8 @@ #define PIC_LOCAL_SCHEDULING 1 #define PIC_GLOBAL_SCHEDULING 0 +#define PIC_CLK_HZ 133333333 + #define nlm_read_pic_reg(b, r) nlm_read_reg64(b, r) #define nlm_write_pic_reg(b, r, v) nlm_write_reg64(b, r, v) #define nlm_get_pic_pcibase(node) nlm_pcicfg_base(XLP_IO_PIC_OFFSET(node)) @@ -315,6 +317,12 @@ nlm_pic_read_timer(uint64_t base, int timer) return nlm_read_pic_reg(base, PIC_TIMER_COUNT(timer)); } +static inline uint32_t +nlm_pic_read_timer32(uint64_t base, int timer) +{ + return (uint32_t)nlm_read_pic_reg(base, PIC_TIMER_COUNT(timer)); +} + static inline void nlm_pic_write_timer(uint64_t base, int timer, uint64_t value) { @@ -376,9 +384,9 @@ nlm_pic_ack(uint64_t base, int irt_num) } static inline void -nlm_pic_init_irt(uint64_t base, int irt, int irq, int hwt) +nlm_pic_init_irt(uint64_t base, int irt, int irq, int hwt, int en) { - nlm_pic_write_irt_direct(base, irt, 0, 0, 0, irq, hwt); + nlm_pic_write_irt_direct(base, irt, en, 0, 0, irq, hwt); } int nlm_irq_to_irt(int irq); diff --git a/arch/mips/include/asm/netlogic/xlr/pic.h b/arch/mips/include/asm/netlogic/xlr/pic.h index 9a691b1f91ba..effa3377ded5 100644 --- a/arch/mips/include/asm/netlogic/xlr/pic.h +++ b/arch/mips/include/asm/netlogic/xlr/pic.h @@ -35,10 +35,11 @@ #ifndef _ASM_NLM_XLR_PIC_H #define _ASM_NLM_XLR_PIC_H -#define PIC_CLKS_PER_SEC 66666666ULL +#define PIC_CLK_HZ 66666666 /* PIC hardware interrupt numbers */ #define PIC_IRT_WD_INDEX 0 #define PIC_IRT_TIMER_0_INDEX 1 +#define PIC_IRT_TIMER_INDEX(i) ((i) + PIC_IRT_TIMER_0_INDEX) #define PIC_IRT_TIMER_1_INDEX 2 #define PIC_IRT_TIMER_2_INDEX 3 #define PIC_IRT_TIMER_3_INDEX 4 @@ -99,6 +100,7 @@ /* PIC Registers */ #define PIC_CTRL 0x00 +#define PIC_CTRL_STE 8 /* timer enable start bit */ #define PIC_IPI 0x04 #define PIC_INT_ACK 0x06 @@ -251,12 +253,52 @@ nlm_pic_ack(uint64_t base, int irt) } static inline void -nlm_pic_init_irt(uint64_t base, int irt, int irq, int hwt) +nlm_pic_init_irt(uint64_t base, int irt, int irq, int hwt, int en) { nlm_write_reg(base, PIC_IRT_0(irt), (1u << hwt)); /* local scheduling, invalid, level by default */ nlm_write_reg(base, PIC_IRT_1(irt), - (1 << 30) | (1 << 6) | irq); + (en << 30) | (1 << 6) | irq); +} + +static inline uint64_t +nlm_pic_read_timer(uint64_t base, int timer) +{ + uint32_t up1, up2, low; + + up1 = nlm_read_reg(base, PIC_TIMER_COUNT_1(timer)); + low = nlm_read_reg(base, PIC_TIMER_COUNT_0(timer)); + up2 = nlm_read_reg(base, PIC_TIMER_COUNT_1(timer)); + + if (up1 != up2) /* wrapped, get the new low */ + low = nlm_read_reg(base, PIC_TIMER_COUNT_0(timer)); + return ((uint64_t)up2 << 32) | low; + +} + +static inline uint32_t +nlm_pic_read_timer32(uint64_t base, int timer) +{ + return nlm_read_reg(base, PIC_TIMER_COUNT_0(timer)); +} + +static inline void +nlm_pic_set_timer(uint64_t base, int timer, uint64_t value, int irq, int cpu) +{ + uint32_t up, low; + uint64_t pic_ctrl = nlm_read_reg(base, PIC_CTRL); + int en; + + en = (irq > 0); + up = value >> 32; + low = value & 0xFFFFFFFF; + nlm_write_reg(base, PIC_TIMER_MAXVAL_0(timer), low); + nlm_write_reg(base, PIC_TIMER_MAXVAL_1(timer), up); + nlm_pic_init_irt(base, PIC_IRT_TIMER_INDEX(timer), irq, cpu, 0); + + /* enable the timer */ + pic_ctrl |= (1 << (PIC_CTRL_STE + timer)); + nlm_write_reg(base, PIC_CTRL, pic_ctrl); } #endif #endif /* _ASM_NLM_XLR_PIC_H */ diff --git a/arch/mips/netlogic/common/irq.c b/arch/mips/netlogic/common/irq.c index d42cd1a2a124..642f1e4c2717 100644 --- a/arch/mips/netlogic/common/irq.c +++ b/arch/mips/netlogic/common/irq.c @@ -217,7 +217,7 @@ static void nlm_init_node_irqs(int node) nlm_setup_pic_irq(node, i, i, irt); /* set interrupts to first cpu in node */ nlm_pic_init_irt(nodep->picbase, irt, i, - node * NLM_CPUS_PER_NODE); + node * NLM_CPUS_PER_NODE, 0); irqmask |= (1ull << i); } nodep->irqmask = irqmask; diff --git a/arch/mips/netlogic/common/time.c b/arch/mips/netlogic/common/time.c index bd3e498157ff..20f89bc0507f 100644 --- a/arch/mips/netlogic/common/time.c +++ b/arch/mips/netlogic/common/time.c @@ -35,16 +35,68 @@ #include #include +#include + #include #include +#include +#include + +#if defined(CONFIG_CPU_XLP) +#include +#include +#include +#elif defined(CONFIG_CPU_XLR) +#include +#include +#include +#else +#error "Unknown CPU" +#endif unsigned int __cpuinit get_c0_compare_int(void) { return IRQ_TIMER; } +static cycle_t nlm_get_pic_timer(struct clocksource *cs) +{ + uint64_t picbase = nlm_get_node(0)->picbase; + + return ~nlm_pic_read_timer(picbase, PIC_CLOCK_TIMER); +} + +static cycle_t nlm_get_pic_timer32(struct clocksource *cs) +{ + uint64_t picbase = nlm_get_node(0)->picbase; + + return ~nlm_pic_read_timer32(picbase, PIC_CLOCK_TIMER); +} + +static struct clocksource csrc_pic = { + .name = "PIC", + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void nlm_init_pic_timer(void) +{ + uint64_t picbase = nlm_get_node(0)->picbase; + + nlm_pic_set_timer(picbase, PIC_CLOCK_TIMER, ~0ULL, 0, 0); + if (current_cpu_data.cputype == CPU_XLR) { + csrc_pic.mask = CLOCKSOURCE_MASK(32); + csrc_pic.read = nlm_get_pic_timer32; + } else { + csrc_pic.mask = CLOCKSOURCE_MASK(64); + csrc_pic.read = nlm_get_pic_timer; + } + csrc_pic.rating = 1000; + clocksource_register_hz(&csrc_pic, PIC_CLK_HZ); +} + void __init plat_time_init(void) { + nlm_init_pic_timer(); mips_hpt_frequency = nlm_get_cpu_frequency(); pr_info("MIPS counter frequency [%ld]\n", (unsigned long)mips_hpt_frequency); diff --git a/arch/mips/netlogic/xlr/platform.c b/arch/mips/netlogic/xlr/platform.c index 507230eeb768..ce838f951356 100644 --- a/arch/mips/netlogic/xlr/platform.c +++ b/arch/mips/netlogic/xlr/platform.c @@ -64,7 +64,7 @@ void nlm_xlr_uart_out(struct uart_port *p, int offset, int value) .iotype = UPIO_MEM32, \ .flags = (UPF_SKIP_TEST | \ UPF_FIXED_TYPE | UPF_BOOT_AUTOCONF),\ - .uartclk = PIC_CLKS_PER_SEC, \ + .uartclk = PIC_CLK_HZ, \ .type = PORT_16550A, \ .serial_in = nlm_xlr_uart_in, \ .serial_out = nlm_xlr_uart_out, \ diff --git a/arch/mips/netlogic/xlr/setup.c b/arch/mips/netlogic/xlr/setup.c index c5ce6992ac4c..54b301c809e1 100644 --- a/arch/mips/netlogic/xlr/setup.c +++ b/arch/mips/netlogic/xlr/setup.c @@ -70,7 +70,7 @@ static void __init nlm_early_serial_setup(void) s.iotype = UPIO_MEM32; s.regshift = 2; s.irq = PIC_UART_0_IRQ; - s.uartclk = PIC_CLKS_PER_SEC; + s.uartclk = PIC_CLK_HZ; s.serial_in = nlm_xlr_uart_in; s.serial_out = nlm_xlr_uart_out; s.mapbase = uart_base; From 8cd3d64c5714de7e17eccde48837b329f67bd85e Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Mon, 14 Jan 2013 15:11:58 +0000 Subject: [PATCH 2926/4607] MIPS: PCI: Prevent hang on XLP reg read Reading PCI extended register at 0x255 on a bridge will hang if there is no device connected on the link. Make PCI read routine skip this register. Signed-off-by: Jayachandran C Patchwork: http://patchwork.linux-mips.org/patch/4789/ Signed-off-by: John Crispin --- arch/mips/pci/pci-xlp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/mips/pci/pci-xlp.c b/arch/mips/pci/pci-xlp.c index 5077148bd67d..fbf001a068a4 100644 --- a/arch/mips/pci/pci-xlp.c +++ b/arch/mips/pci/pci-xlp.c @@ -64,8 +64,12 @@ static inline u32 pci_cfg_read_32bit(struct pci_bus *bus, unsigned int devfn, u32 data; u32 *cfgaddr; + where &= ~3; + if (bus->number == 0 && PCI_SLOT(devfn) == 1 && where == 0x954) + return 0xffffffff; + cfgaddr = (u32 *)(pci_config_base + - pci_cfg_addr(bus->number, devfn, where & ~3)); + pci_cfg_addr(bus->number, devfn, where)); data = *cfgaddr; return data; } From 5a4cbe381122e41749cadf24d59cd63ed84f41c0 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Mon, 14 Jan 2013 15:11:59 +0000 Subject: [PATCH 2927/4607] MIPS: Netlogic: No hazards needed for XLR/XLS TLB and COP0 hazards are handled in hardware for Netlogic XLR/XLS SoCs. Update hazards.h to pick more optimal set of definitions when compiling for XLR/XLS. Signed-off-by: Jayachandran C Patchwork: http://patchwork.linux-mips.org/patch/4788/ Signed-off-by: John Crispin --- arch/mips/include/asm/hazards.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/include/asm/hazards.h b/arch/mips/include/asm/hazards.h index f0324e92d089..9c309ae4a00e 100644 --- a/arch/mips/include/asm/hazards.h +++ b/arch/mips/include/asm/hazards.h @@ -141,7 +141,7 @@ do { \ #elif defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_CPU_CAVIUM_OCTEON) || \ defined(CONFIG_CPU_LOONGSON2) || defined(CONFIG_CPU_R10000) || \ - defined(CONFIG_CPU_R5500) + defined(CONFIG_CPU_R5500) || defined(CONFIG_CPU_XLR) /* * R10000 rocks - all hazards handled in hardware, so this becomes a nobrainer. From 37a7059bc4228613867645be50120aff17a162a1 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Mon, 14 Jan 2013 15:12:00 +0000 Subject: [PATCH 2928/4607] MIPS: Netlogic: use preset loops per jiffy Doing calibrate delay on a hardware thread will be inaccurate since it depends on the load on other threads in the core. It will also slow down the boot process when done for 128 hardware threads. Switch to a pre-computed loops per jiffy based on the core frequency. The value is computed based on the core frequency and roughly matches the value calculated by calibrate_delay(). Signed-off-by: Jayachandran C Patchwork: http://patchwork.linux-mips.org/patch/4791/ Signed-off-by: John Crispin --- arch/mips/netlogic/common/time.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/mips/netlogic/common/time.c b/arch/mips/netlogic/common/time.c index 20f89bc0507f..5c56555380bb 100644 --- a/arch/mips/netlogic/common/time.c +++ b/arch/mips/netlogic/common/time.c @@ -98,6 +98,10 @@ void __init plat_time_init(void) { nlm_init_pic_timer(); mips_hpt_frequency = nlm_get_cpu_frequency(); + if (current_cpu_type() == CPU_XLR) + preset_lpj = mips_hpt_frequency / (3 * HZ); + else + preset_lpj = mips_hpt_frequency / (2 * HZ); pr_info("MIPS counter frequency [%ld]\n", (unsigned long)mips_hpt_frequency); } From cba3b643039b9d38284a5fd5143558622b9b64d9 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Mon, 14 Jan 2013 15:12:01 +0000 Subject: [PATCH 2929/4607] MIPS: Netlogic: Fix for quad-XLP boot On multi-chip boards, the first core on slave SoCs may take much more time to wakeup. Add code to wait for the core to come up before proceeding with the rest of the boot up. Update xlp_wakeup_core to also skip the boot node and the boot CPU initialization which is already complete. Signed-off-by: Jayachandran C Patchwork: http://patchwork.linux-mips.org/patch/4783/ Signed-off-by: John Crispin --- arch/mips/netlogic/xlp/wakeup.c | 35 +++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/arch/mips/netlogic/xlp/wakeup.c b/arch/mips/netlogic/xlp/wakeup.c index cb9010642ac3..abb3e08cc052 100644 --- a/arch/mips/netlogic/xlp/wakeup.c +++ b/arch/mips/netlogic/xlp/wakeup.c @@ -51,7 +51,7 @@ #include #include -static int xlp_wakeup_core(uint64_t sysbase, int core) +static int xlp_wakeup_core(uint64_t sysbase, int node, int core) { uint32_t coremask, value; int count; @@ -82,36 +82,51 @@ static void xlp_enable_secondary_cores(const cpumask_t *wakeup_mask) struct nlm_soc_info *nodep; uint64_t syspcibase; uint32_t syscoremask; - int core, n, cpu; + int core, n, cpu, count, val; for (n = 0; n < NLM_NR_NODES; n++) { syspcibase = nlm_get_sys_pcibase(n); if (nlm_read_reg(syspcibase, 0) == 0xffffffff) break; - /* read cores in reset from SYS and account for boot cpu */ - nlm_node_init(n); + /* read cores in reset from SYS */ + if (n != 0) + nlm_node_init(n); nodep = nlm_get_node(n); syscoremask = nlm_read_sys_reg(nodep->sysbase, SYS_CPU_RESET); - if (n == 0) + /* The boot cpu */ + if (n == 0) { syscoremask |= 1; + nodep->coremask = 1; + } for (core = 0; core < NLM_CORES_PER_NODE; core++) { + /* we will be on node 0 core 0 */ + if (n == 0 && core == 0) + continue; + /* see if the core exists */ if ((syscoremask & (1 << core)) == 0) continue; - /* see if at least the first thread is enabled */ + /* see if at least the first hw thread is enabled */ cpu = (n * NLM_CORES_PER_NODE + core) * NLM_THREADS_PER_CORE; if (!cpumask_test_cpu(cpu, wakeup_mask)) continue; /* wake up the core */ - if (xlp_wakeup_core(nodep->sysbase, core)) - nodep->coremask |= 1u << core; - else - pr_err("Failed to enable core %d\n", core); + if (!xlp_wakeup_core(nodep->sysbase, n, core)) + continue; + + /* core is up */ + nodep->coremask |= 1u << core; + + /* spin until the first hw thread sets its ready */ + count = 0x20000000; + do { + val = *(volatile int *)&nlm_cpu_ready[cpu]; + } while (val == 0 && --count > 0); } } } From 7b53eb4d40d702a7458588dcfcddaf4498dbbb36 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Wed, 16 Jan 2013 11:12:41 +0000 Subject: [PATCH 2930/4607] MIPS: PCI: Multi-node PCI support for Netlogic XLP On a multi-chip XLP board, each node can have 4 PCIe links. Update XLP PCI code to initialize PCIe on all the nodes. Signed-off-by: Jayachandran C Patchwork: http://patchwork.linux-mips.org/patch/4803/ Signed-off-by: John Crispin --- arch/mips/pci/pci-xlp.c | 109 ++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 43 deletions(-) diff --git a/arch/mips/pci/pci-xlp.c b/arch/mips/pci/pci-xlp.c index fbf001a068a4..dd2d3eb3ad31 100644 --- a/arch/mips/pci/pci-xlp.c +++ b/arch/mips/pci/pci-xlp.c @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -161,32 +162,38 @@ struct pci_controller nlm_pci_controller = { .io_offset = 0x00000000UL, }; -static int get_irq_vector(const struct pci_dev *dev) +static struct pci_dev *xlp_get_pcie_link(const struct pci_dev *dev) { - /* - * For XLP PCIe, there is an IRQ per Link, find out which - * link the device is on to assign interrupts - */ - if (dev->bus->self == NULL) - return 0; + struct pci_bus *bus, *p; - switch (dev->bus->self->devfn) { - case 0x8: - return PIC_PCIE_LINK_0_IRQ; - case 0x9: - return PIC_PCIE_LINK_1_IRQ; - case 0xa: - return PIC_PCIE_LINK_2_IRQ; - case 0xb: - return PIC_PCIE_LINK_3_IRQ; - } - WARN(1, "Unexpected devfn %d\n", dev->bus->self->devfn); - return 0; + /* Find the bridge on bus 0 */ + bus = dev->bus; + for (p = bus->parent; p && p->number != 0; p = p->parent) + bus = p; + + return p ? bus->self : NULL; +} + +static inline int nlm_pci_link_to_irq(int link) +{ + return PIC_PCIE_LINK_0_IRQ + link; } int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { - return get_irq_vector(dev); + struct pci_dev *lnkdev; + int lnkslot, lnkfunc; + + /* + * For XLP PCIe, there is an IRQ per Link, find out which + * link the device is on to assign interrupts + */ + lnkdev = xlp_get_pcie_link(dev); + if (lnkdev == NULL) + return 0; + lnkfunc = PCI_FUNC(lnkdev->devfn); + lnkslot = PCI_SLOT(lnkdev->devfn); + return nlm_irq_to_xirq(lnkslot / 8, nlm_pci_link_to_irq(lnkfunc)); } /* Do platform specific device initialization at pci_enable_device() time */ @@ -201,45 +208,42 @@ int pcibios_plat_dev_init(struct pci_dev *dev) * readl/writel. */ #ifdef __BIG_ENDIAN -static void xlp_config_pci_bswap(void) +static void xlp_config_pci_bswap(int node, int link) { - uint64_t pciebase, sysbase; - int node, i; + uint64_t nbubase, lnkbase; u32 reg; - /* Chip-0 so node set to 0 */ - node = 0; - sysbase = nlm_get_bridge_regbase(node); + nbubase = nlm_get_bridge_regbase(node); + lnkbase = nlm_get_pcie_base(node, link); + /* * Enable byte swap in hardware. Program each link's PCIe SWAP regions * from the link's address ranges. */ - for (i = 0; i < 4; i++) { - pciebase = nlm_pcicfg_base(XLP_IO_PCIE_OFFSET(node, i)); - if (nlm_read_pci_reg(pciebase, 0) == 0xffffffff) - continue; + reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_BASE0 + link); + nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_BASE, reg); - reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEMEM_BASE0 + i); - nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_MEM_BASE, reg); + reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_LIMIT0 + link); + nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_LIM, reg | 0xfff); - reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEMEM_LIMIT0 + i); - nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_MEM_LIM, - reg | 0xfff); + reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_BASE0 + link); + nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_BASE, reg); - reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEIO_BASE0 + i); - nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_IO_BASE, reg); - - reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEIO_LIMIT0 + i); - nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff); - } + reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_LIMIT0 + link); + nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff); } #else /* Swap configuration not needed in little-endian mode */ -static inline void xlp_config_pci_bswap(void) {} +static inline void xlp_config_pci_bswap(int node, int link) {} #endif /* __BIG_ENDIAN */ static int __init pcibios_init(void) { + struct nlm_soc_info *nodep; + uint64_t pciebase; + int link, n; + u32 reg; + /* Firmware assigns PCI resources */ pci_set_flags(PCI_PROBE_ONLY); pci_config_base = ioremap(XLP_DEFAULT_PCI_ECFG_BASE, 64 << 20); @@ -248,7 +252,26 @@ static int __init pcibios_init(void) ioport_resource.start = 0; ioport_resource.end = ~0; - xlp_config_pci_bswap(); + for (n = 0; n < NLM_NR_NODES; n++) { + nodep = nlm_get_node(n); + if (!nodep->coremask) + continue; /* node does not exist */ + + for (link = 0; link < 4; link++) { + pciebase = nlm_get_pcie_base(n, link); + if (nlm_read_pci_reg(pciebase, 0) == 0xffffffff) + continue; + xlp_config_pci_bswap(n, link); + + /* put in intpin and irq - u-boot does not */ + reg = nlm_read_pci_reg(pciebase, 0xf); + reg &= ~0x1fu; + reg |= (1 << 8) | nlm_pci_link_to_irq(link); + nlm_write_pci_reg(pciebase, 0xf, reg); + pr_info("XLP PCIe: Link %d-%d initialized.\n", n, link); + } + } + set_io_port_base(CKSEG1); nlm_pci_controller.io_map_base = CKSEG1; From 127993e561846e889004d7d21a84fb5a6c40b9c3 Mon Sep 17 00:00:00 2001 From: "Steven J. Hill" Date: Fri, 7 Dec 2012 04:15:03 +0000 Subject: [PATCH 2931/4607] MIPS: Clean-ups for MIPS Technologies Inc. generic header file. Clean up standard header text and remove unused #define. Signed-off-by: Steven J. Hill Patchwork: http://patchwork.linux-mips.org/patch/4703/ Signed-off-by: John Crispin --- arch/mips/include/asm/mips-boards/generic.h | 28 ++++++--------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/arch/mips/include/asm/mips-boards/generic.h b/arch/mips/include/asm/mips-boards/generic.h index 6e23ceb0ba8c..fa188ff08e5a 100644 --- a/arch/mips/include/asm/mips-boards/generic.h +++ b/arch/mips/include/asm/mips-boards/generic.h @@ -1,21 +1,14 @@ /* - * Carsten Langgaard, carstenl@mips.com - * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. - * - * This program is free software; you can distribute it and/or modify it - * under the terms of the GNU General Public License (Version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope 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 + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive * 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., - * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * * Defines of the MIPS boards specific address-MAP, registers, etc. + * + * Copyright (C) 2000,2012 MIPS Technologies, Inc. + * All rights reserved. + * Authors: Carsten Langgaard + * Steven J. Hill */ #ifndef __ASM_MIPS_BOARDS_GENERIC_H #define __ASM_MIPS_BOARDS_GENERIC_H @@ -30,13 +23,6 @@ #define ASCII_DISPLAY_WORD_BASE 0x1f000410 #define ASCII_DISPLAY_POS_BASE 0x1f000418 - -/* - * Yamon Prom print address. - */ -#define YAMON_PROM_PRINT_ADDR 0x1fc00504 - - /* * Reset register. */ From f8fa4811dbb264aef13f982e963389fd828b1ac0 Mon Sep 17 00:00:00 2001 From: "Steven J. Hill" Date: Fri, 7 Dec 2012 03:51:35 +0000 Subject: [PATCH 2932/4607] MIPS: Add support for the M14KEc core. Signed-off-by: Steven J. Hill Patchwork: http://patchwork.linux-mips.org/patch/4682/ Signed-off-by: John Crispin --- arch/mips/include/asm/cpu-features.h | 3 +++ arch/mips/include/asm/cpu.h | 3 +++ arch/mips/include/asm/mipsregs.h | 1 + arch/mips/kernel/cpu-probe.c | 7 +++++++ arch/mips/kernel/proc.c | 1 + arch/mips/mm/c-r4k.c | 1 + arch/mips/mm/tlbex.c | 1 + arch/mips/oprofile/common.c | 1 + arch/mips/oprofile/op_model_mipsxx.c | 4 ++++ 9 files changed, 22 insertions(+) diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h index c507b931b484..00171cddb6d5 100644 --- a/arch/mips/include/asm/cpu-features.h +++ b/arch/mips/include/asm/cpu-features.h @@ -98,6 +98,9 @@ #ifndef cpu_has_rixi #define cpu_has_rixi (cpu_data[0].options & MIPS_CPU_RIXI) #endif +#ifndef cpu_has_mmips +#define cpu_has_mmips (cpu_data[0].options & MIPS_CPU_MICROMIPS) +#endif #ifndef cpu_has_vtag_icache #define cpu_has_vtag_icache (cpu_data[0].icache.flags & MIPS_CACHE_VTAG) #endif diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h index 90112adb1940..2de2fee16cc4 100644 --- a/arch/mips/include/asm/cpu.h +++ b/arch/mips/include/asm/cpu.h @@ -96,6 +96,7 @@ #define PRID_IMP_1004K 0x9900 #define PRID_IMP_1074K 0x9a00 #define PRID_IMP_M14KC 0x9c00 +#define PRID_IMP_M14KEC 0x9e00 /* * These are the PRID's for when 23:16 == PRID_COMP_SIBYTE @@ -264,6 +265,7 @@ enum cpu_type_enum { CPU_4KC, CPU_4KEC, CPU_4KSC, CPU_24K, CPU_34K, CPU_1004K, CPU_74K, CPU_ALCHEMY, CPU_PR4450, CPU_BMIPS32, CPU_BMIPS3300, CPU_BMIPS4350, CPU_BMIPS4380, CPU_BMIPS5000, CPU_JZRISC, CPU_LOONGSON1, CPU_M14KC, + CPU_M14KEC, /* * MIPS64 class processors @@ -322,6 +324,7 @@ enum cpu_type_enum { #define MIPS_CPU_ULRI 0x00200000 /* CPU has ULRI feature */ #define MIPS_CPU_PCI 0x00400000 /* CPU has Perf Ctr Int indicator */ #define MIPS_CPU_RIXI 0x00800000 /* CPU has TLB Read/eXec Inhibit */ +#define MIPS_CPU_MICROMIPS 0x01000000 /* CPU has microMIPS capability */ /* * CPU ASE encodings diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index 7e4e6f8fab37..3e36745670b2 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -595,6 +595,7 @@ #define MIPS_CONF3_DSP2P (_ULCAST_(1) << 11) #define MIPS_CONF3_RXI (_ULCAST_(1) << 12) #define MIPS_CONF3_ULRI (_ULCAST_(1) << 13) +#define MIPS_CONF3_ISA (_ULCAST_(3) << 14) #define MIPS_CONF4_MMUSIZEEXT (_ULCAST_(255) << 0) #define MIPS_CONF4_MMUEXTDEF (_ULCAST_(3) << 14) diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 9f31334ed1de..ba169022fe1d 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -201,6 +201,7 @@ void __init check_wait(void) break; case CPU_M14KC: + case CPU_M14KEC: case CPU_24K: case CPU_34K: case CPU_1004K: @@ -439,6 +440,8 @@ static inline unsigned int decode_config3(struct cpuinfo_mips *c) c->ases |= MIPS_ASE_MIPSMT; if (config3 & MIPS_CONF3_ULRI) c->options |= MIPS_CPU_ULRI; + if (config3 & MIPS_CONF3_ISA) + c->options |= MIPS_CPU_MICROMIPS; return config3 & MIPS_CONF_M; } @@ -861,6 +864,10 @@ static inline void cpu_probe_mips(struct cpuinfo_mips *c, unsigned int cpu) c->cputype = CPU_M14KC; __cpu_name[cpu] = "MIPS M14Kc"; break; + case PRID_IMP_M14KEC: + c->cputype = CPU_M14KEC; + __cpu_name[cpu] = "MIPS M14KEc"; + break; case PRID_IMP_1004K: c->cputype = CPU_1004K; __cpu_name[cpu] = "MIPS 1004Kc"; diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 07dff54f2ce8..239ae03f3330 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -73,6 +73,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) if (cpu_has_dsp) seq_printf(m, "%s", " dsp"); if (cpu_has_dsp2) seq_printf(m, "%s", " dsp2"); if (cpu_has_mipsmt) seq_printf(m, "%s", " mt"); + if (cpu_has_mmips) seq_printf(m, "%s", " micromips"); seq_printf(m, "\n"); seq_printf(m, "shadow register sets\t: %d\n", diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index 0f7d788e8810..606e8286970c 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -1057,6 +1057,7 @@ static void __cpuinit probe_pcache(void) break; case CPU_M14KC: + case CPU_M14KEC: case CPU_24K: case CPU_34K: case CPU_74K: diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 1c8ac49ec72c..074d6595e7b3 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -581,6 +581,7 @@ static void __cpuinit build_tlb_write_entry(u32 **p, struct uasm_label **l, case CPU_4KC: case CPU_4KEC: case CPU_M14KC: + case CPU_M14KEC: case CPU_SB1: case CPU_SB1A: case CPU_4KSC: diff --git a/arch/mips/oprofile/common.c b/arch/mips/oprofile/common.c index e32db1ff02c7..fc41aa997f1a 100644 --- a/arch/mips/oprofile/common.c +++ b/arch/mips/oprofile/common.c @@ -78,6 +78,7 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) switch (current_cpu_type()) { case CPU_5KC: case CPU_M14KC: + case CPU_M14KEC: case CPU_20KC: case CPU_24K: case CPU_25KF: diff --git a/arch/mips/oprofile/op_model_mipsxx.c b/arch/mips/oprofile/op_model_mipsxx.c index 786254630403..18c3afefa67d 100644 --- a/arch/mips/oprofile/op_model_mipsxx.c +++ b/arch/mips/oprofile/op_model_mipsxx.c @@ -351,6 +351,10 @@ static int __init mipsxx_init(void) op_model_mipsxx_ops.cpu_type = "mips/M14Kc"; break; + case CPU_M14KEC: + op_model_mipsxx_ops.cpu_type = "mips/M14KEc"; + break; + case CPU_20KC: op_model_mipsxx_ops.cpu_type = "mips/20K"; break; From 32a7ede673cd0be580f24d855099a8a5f195e80c Mon Sep 17 00:00:00 2001 From: "Steven J. Hill" Date: Thu, 3 Jan 2013 19:01:52 +0000 Subject: [PATCH 2933/4607] MIPS: dsp: Add assembler support for DSP ASEs. Newer toolchains support the DSP and DSP Rev2 instructions. This patch performs a check for that support and adds compiler and assembler flags for only the files that need use those instructions. Signed-off-by: Steven J. Hill Acked-by: Florian Fainelli Patchwork: http://patchwork.linux-mips.org/patch/4752/ Signed-off-by: John Crispin --- arch/mips/include/asm/mipsregs.h | 65 +++++++++++++++++++++----------- arch/mips/kernel/Makefile | 31 +++++++++++++++ 2 files changed, 73 insertions(+), 23 deletions(-) diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index 3e36745670b2..578132219ff6 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -1155,6 +1155,48 @@ do { \ : "=r" (__res)); \ __res;}) +#ifdef HAVE_AS_DSP +#define rddsp(mask) \ +({ \ + unsigned int __dspctl; \ + \ + __asm__ __volatile__( \ + " rddsp %0, %x1 \n" \ + : "=r" (__dspctl) \ + : "i" (mask)); \ + __dspctl; \ +}) + +#define wrdsp(val, mask) \ +do { \ + __asm__ __volatile__( \ + " wrdsp %0, %x1 \n" \ + : \ + : "r" (val), "i" (mask)); \ +} while (0) + +#define mflo0() ({ long mflo0; __asm__("mflo %0, $ac0" : "=r" (mflo0)); mflo0;}) +#define mflo1() ({ long mflo1; __asm__("mflo %0, $ac1" : "=r" (mflo1)); mflo1;}) +#define mflo2() ({ long mflo2; __asm__("mflo %0, $ac2" : "=r" (mflo2)); mflo2;}) +#define mflo3() ({ long mflo3; __asm__("mflo %0, $ac3" : "=r" (mflo3)); mflo3;}) + +#define mfhi0() ({ long mfhi0; __asm__("mfhi %0, $ac0" : "=r" (mfhi0)); mfhi0;}) +#define mfhi1() ({ long mfhi1; __asm__("mfhi %0, $ac1" : "=r" (mfhi1)); mfhi1;}) +#define mfhi2() ({ long mfhi2; __asm__("mfhi %0, $ac2" : "=r" (mfhi2)); mfhi2;}) +#define mfhi3() ({ long mfhi3; __asm__("mfhi %0, $ac3" : "=r" (mfhi3)); mfhi3;}) + +#define mtlo0(x) __asm__("mtlo %0, $ac0" ::"r" (x)) +#define mtlo1(x) __asm__("mtlo %0, $ac1" ::"r" (x)) +#define mtlo2(x) __asm__("mtlo %0, $ac2" ::"r" (x)) +#define mtlo3(x) __asm__("mtlo %0, $ac3" ::"r" (x)) + +#define mthi0(x) __asm__("mthi %0, $ac0" ::"r" (x)) +#define mthi1(x) __asm__("mthi %0, $ac1" ::"r" (x)) +#define mthi2(x) __asm__("mthi %0, $ac2" ::"r" (x)) +#define mthi3(x) __asm__("mthi %0, $ac3" ::"r" (x)) + +#else + #define rddsp(mask) \ ({ \ unsigned int __res; \ @@ -1184,29 +1226,6 @@ do { \ : "r" (val), "i" (mask)); \ } while (0) -#if 0 /* Need DSP ASE capable assembler ... */ -#define mflo0() ({ long mflo0; __asm__("mflo %0, $ac0" : "=r" (mflo0)); mflo0;}) -#define mflo1() ({ long mflo1; __asm__("mflo %0, $ac1" : "=r" (mflo1)); mflo1;}) -#define mflo2() ({ long mflo2; __asm__("mflo %0, $ac2" : "=r" (mflo2)); mflo2;}) -#define mflo3() ({ long mflo3; __asm__("mflo %0, $ac3" : "=r" (mflo3)); mflo3;}) - -#define mfhi0() ({ long mfhi0; __asm__("mfhi %0, $ac0" : "=r" (mfhi0)); mfhi0;}) -#define mfhi1() ({ long mfhi1; __asm__("mfhi %0, $ac1" : "=r" (mfhi1)); mfhi1;}) -#define mfhi2() ({ long mfhi2; __asm__("mfhi %0, $ac2" : "=r" (mfhi2)); mfhi2;}) -#define mfhi3() ({ long mfhi3; __asm__("mfhi %0, $ac3" : "=r" (mfhi3)); mfhi3;}) - -#define mtlo0(x) __asm__("mtlo %0, $ac0" ::"r" (x)) -#define mtlo1(x) __asm__("mtlo %0, $ac1" ::"r" (x)) -#define mtlo2(x) __asm__("mtlo %0, $ac2" ::"r" (x)) -#define mtlo3(x) __asm__("mtlo %0, $ac3" ::"r" (x)) - -#define mthi0(x) __asm__("mthi %0, $ac0" ::"r" (x)) -#define mthi1(x) __asm__("mthi %0, $ac1" ::"r" (x)) -#define mthi2(x) __asm__("mthi %0, $ac2" ::"r" (x)) -#define mthi3(x) __asm__("mthi %0, $ac3" ::"r" (x)) - -#else - #define mfhi0() \ ({ \ unsigned long __treg; \ diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 007c33d73715..6c17e1f3d0ec 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -98,4 +98,35 @@ obj-$(CONFIG_HW_PERF_EVENTS) += perf_event_mipsxx.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o +# +# DSP ASE supported for MIPS32 or MIPS64 Release 2 cores only. It is safe +# to enable DSP assembler support here even if the MIPS Release 2 CPU we +# are targetting does not support DSP because all code-paths making use of +# it properly check that the running CPU *actually does* support these +# instructions. +# +ifeq ($(CONFIG_CPU_MIPSR2), y) +CFLAGS_DSP = -DHAVE_AS_DSP + +# +# Check if assembler supports DSP ASE +# +ifeq ($(call cc-option-yn,-mdsp), y) +CFLAGS_DSP += -mdsp +endif + +# +# Check if assembler supports DSP ASE Rev2 +# +ifeq ($(call cc-option-yn,-mdspr2), y) +CFLAGS_DSP += -mdspr2 +endif + +CFLAGS_signal.o = $(CFLAGS_DSP) +CFLAGS_signal32.o = $(CFLAGS_DSP) +CFLAGS_process.o = $(CFLAGS_DSP) +CFLAGS_branch.o = $(CFLAGS_DSP) +CFLAGS_ptrace.o = $(CFLAGS_DSP) +endif + CPPFLAGS_vmlinux.lds := $(KBUILD_CFLAGS) From d0c1b478e0b2f0bcbb2a58db6bc5e13354068064 Mon Sep 17 00:00:00 2001 From: "Steven J. Hill" Date: Fri, 7 Dec 2012 03:53:29 +0000 Subject: [PATCH 2934/4607] MIPS: dsp: Support toolchains without DSP ASE and microMIPS. Add macros to support the DSP ASE with microMIPS kernels when the toolchain does not have support. Signed-off-by: Steven J. Hill Patchwork: http://patchwork.linux-mips.org/patch/4686/ Signed-off-by: John Crispin --- arch/mips/include/asm/mipsregs.h | 89 ++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index 578132219ff6..24417de673c5 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -1197,6 +1197,94 @@ do { \ #else +#ifdef CONFIG_CPU_MICROMIPS +#define rddsp(mask) \ +({ \ + unsigned int __res; \ + \ + __asm__ __volatile__( \ + " .set push \n" \ + " .set noat \n" \ + " # rddsp $1, %x1 \n" \ + " .hword ((0x0020067c | (%x1 << 14)) >> 16) \n" \ + " .hword ((0x0020067c | (%x1 << 14)) & 0xffff) \n" \ + " move %0, $1 \n" \ + " .set pop \n" \ + : "=r" (__res) \ + : "i" (mask)); \ + __res; \ +}) + +#define wrdsp(val, mask) \ +do { \ + __asm__ __volatile__( \ + " .set push \n" \ + " .set noat \n" \ + " move $1, %0 \n" \ + " # wrdsp $1, %x1 \n" \ + " .hword ((0x0020167c | (%x1 << 14)) >> 16) \n" \ + " .hword ((0x0020167c | (%x1 << 14)) & 0xffff) \n" \ + " .set pop \n" \ + : \ + : "r" (val), "i" (mask)); \ +} while (0) + +#define _umips_dsp_mfxxx(ins) \ +({ \ + unsigned long __treg; \ + \ + __asm__ __volatile__( \ + " .set push \n" \ + " .set noat \n" \ + " .hword 0x0001 \n" \ + " .hword %x1 \n" \ + " move %0, $1 \n" \ + " .set pop \n" \ + : "=r" (__treg) \ + : "i" (ins)); \ + __treg; \ +}) + +#define _umips_dsp_mtxxx(val, ins) \ +do { \ + __asm__ __volatile__( \ + " .set push \n" \ + " .set noat \n" \ + " move $1, %0 \n" \ + " .hword 0x0001 \n" \ + " .hword %x1 \n" \ + " .set pop \n" \ + : \ + : "r" (val), "i" (ins)); \ +} while (0) + +#define _umips_dsp_mflo(reg) _umips_dsp_mfxxx((reg << 14) | 0x107c) +#define _umips_dsp_mfhi(reg) _umips_dsp_mfxxx((reg << 14) | 0x007c) + +#define _umips_dsp_mtlo(val, reg) _umips_dsp_mtxxx(val, ((reg << 14) | 0x307c)) +#define _umips_dsp_mthi(val, reg) _umips_dsp_mtxxx(val, ((reg << 14) | 0x207c)) + +#define mflo0() _umips_dsp_mflo(0) +#define mflo1() _umips_dsp_mflo(1) +#define mflo2() _umips_dsp_mflo(2) +#define mflo3() _umips_dsp_mflo(3) + +#define mfhi0() _umips_dsp_mfhi(0) +#define mfhi1() _umips_dsp_mfhi(1) +#define mfhi2() _umips_dsp_mfhi(2) +#define mfhi3() _umips_dsp_mfhi(3) + +#define mtlo0(x) _umips_dsp_mtlo(x, 0) +#define mtlo1(x) _umips_dsp_mtlo(x, 1) +#define mtlo2(x) _umips_dsp_mtlo(x, 2) +#define mtlo3(x) _umips_dsp_mtlo(x, 3) + +#define mthi0(x) _umips_dsp_mthi(x, 0) +#define mthi1(x) _umips_dsp_mthi(x, 1) +#define mthi2(x) _umips_dsp_mthi(x, 2) +#define mthi3(x) _umips_dsp_mthi(x, 3) + +#else /* !CONFIG_CPU_MICROMIPS */ #define rddsp(mask) \ ({ \ unsigned int __res; \ @@ -1450,6 +1538,7 @@ do { \ : "r" (x)); \ } while (0) +#endif /* CONFIG_CPU_MICROMIPS */ #endif /* From 4cb764b4541fe9eacc237b6851198a4c8497b9c4 Mon Sep 17 00:00:00 2001 From: "Steven J. Hill" Date: Fri, 7 Dec 2012 03:53:52 +0000 Subject: [PATCH 2935/4607] MIPS: dsp: Simplify the DSP macros. Simplify the DSP macros for vanilla (non-microMIPS) kernels and toolchains that do not support the DSP ASEs. Signed-off-by: Steven J. Hill Patchwork: http://patchwork.linux-mips.org/patch/4687/ Signed-off-by: John Crispin --- arch/mips/include/asm/mipsregs.h | 233 ++++--------------------------- 1 file changed, 31 insertions(+), 202 deletions(-) diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index 24417de673c5..9f47cda632ab 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -1314,229 +1314,58 @@ do { \ : "r" (val), "i" (mask)); \ } while (0) -#define mfhi0() \ +#define _dsp_mfxxx(ins) \ ({ \ unsigned long __treg; \ \ __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " # mfhi %0, $ac0 \n" \ - " .word 0x00000810 \n" \ - " move %0, $1 \n" \ - " .set pop \n" \ - : "=r" (__treg)); \ + " .set push \n" \ + " .set noat \n" \ + " .word (0x00000810 | %1) \n" \ + " move %0, $1 \n" \ + " .set pop \n" \ + : "=r" (__treg) \ + : "i" (ins)); \ __treg; \ }) -#define mfhi1() \ -({ \ - unsigned long __treg; \ - \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " # mfhi %0, $ac1 \n" \ - " .word 0x00200810 \n" \ - " move %0, $1 \n" \ - " .set pop \n" \ - : "=r" (__treg)); \ - __treg; \ -}) - -#define mfhi2() \ -({ \ - unsigned long __treg; \ - \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " # mfhi %0, $ac2 \n" \ - " .word 0x00400810 \n" \ - " move %0, $1 \n" \ - " .set pop \n" \ - : "=r" (__treg)); \ - __treg; \ -}) - -#define mfhi3() \ -({ \ - unsigned long __treg; \ - \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " # mfhi %0, $ac3 \n" \ - " .word 0x00600810 \n" \ - " move %0, $1 \n" \ - " .set pop \n" \ - : "=r" (__treg)); \ - __treg; \ -}) - -#define mflo0() \ -({ \ - unsigned long __treg; \ - \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " # mflo %0, $ac0 \n" \ - " .word 0x00000812 \n" \ - " move %0, $1 \n" \ - " .set pop \n" \ - : "=r" (__treg)); \ - __treg; \ -}) - -#define mflo1() \ -({ \ - unsigned long __treg; \ - \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " # mflo %0, $ac1 \n" \ - " .word 0x00200812 \n" \ - " move %0, $1 \n" \ - " .set pop \n" \ - : "=r" (__treg)); \ - __treg; \ -}) - -#define mflo2() \ -({ \ - unsigned long __treg; \ - \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " # mflo %0, $ac2 \n" \ - " .word 0x00400812 \n" \ - " move %0, $1 \n" \ - " .set pop \n" \ - : "=r" (__treg)); \ - __treg; \ -}) - -#define mflo3() \ -({ \ - unsigned long __treg; \ - \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " # mflo %0, $ac3 \n" \ - " .word 0x00600812 \n" \ - " move %0, $1 \n" \ - " .set pop \n" \ - : "=r" (__treg)); \ - __treg; \ -}) - -#define mthi0(x) \ +#define _dsp_mtxxx(val, ins) \ do { \ __asm__ __volatile__( \ " .set push \n" \ " .set noat \n" \ " move $1, %0 \n" \ - " # mthi $1, $ac0 \n" \ - " .word 0x00200011 \n" \ + " .word (0x00200011 | %1) \n" \ " .set pop \n" \ : \ - : "r" (x)); \ + : "r" (val), "i" (ins)); \ } while (0) -#define mthi1(x) \ -do { \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " move $1, %0 \n" \ - " # mthi $1, $ac1 \n" \ - " .word 0x00200811 \n" \ - " .set pop \n" \ - : \ - : "r" (x)); \ -} while (0) +#define _dsp_mflo(reg) _dsp_mfxxx((reg << 21) | 0x0002) +#define _dsp_mfhi(reg) _dsp_mfxxx((reg << 21) | 0x0000) -#define mthi2(x) \ -do { \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " move $1, %0 \n" \ - " # mthi $1, $ac2 \n" \ - " .word 0x00201011 \n" \ - " .set pop \n" \ - : \ - : "r" (x)); \ -} while (0) +#define _dsp_mtlo(val, reg) _dsp_mtxxx(val, ((reg << 11) | 0x0002)) +#define _dsp_mthi(val, reg) _dsp_mtxxx(val, ((reg << 11) | 0x0000)) -#define mthi3(x) \ -do { \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " move $1, %0 \n" \ - " # mthi $1, $ac3 \n" \ - " .word 0x00201811 \n" \ - " .set pop \n" \ - : \ - : "r" (x)); \ -} while (0) +#define mflo0() _dsp_mflo(0) +#define mflo1() _dsp_mflo(1) +#define mflo2() _dsp_mflo(2) +#define mflo3() _dsp_mflo(3) -#define mtlo0(x) \ -do { \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " move $1, %0 \n" \ - " # mtlo $1, $ac0 \n" \ - " .word 0x00200013 \n" \ - " .set pop \n" \ - : \ - : "r" (x)); \ -} while (0) +#define mfhi0() _dsp_mfhi(0) +#define mfhi1() _dsp_mfhi(1) +#define mfhi2() _dsp_mfhi(2) +#define mfhi3() _dsp_mfhi(3) -#define mtlo1(x) \ -do { \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " move $1, %0 \n" \ - " # mtlo $1, $ac1 \n" \ - " .word 0x00200813 \n" \ - " .set pop \n" \ - : \ - : "r" (x)); \ -} while (0) +#define mtlo0(x) _dsp_mtlo(x, 0) +#define mtlo1(x) _dsp_mtlo(x, 1) +#define mtlo2(x) _dsp_mtlo(x, 2) +#define mtlo3(x) _dsp_mtlo(x, 3) -#define mtlo2(x) \ -do { \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " move $1, %0 \n" \ - " # mtlo $1, $ac2 \n" \ - " .word 0x00201013 \n" \ - " .set pop \n" \ - : \ - : "r" (x)); \ -} while (0) - -#define mtlo3(x) \ -do { \ - __asm__ __volatile__( \ - " .set push \n" \ - " .set noat \n" \ - " move $1, %0 \n" \ - " # mtlo $1, $ac3 \n" \ - " .word 0x00201813 \n" \ - " .set pop \n" \ - : \ - : "r" (x)); \ -} while (0) +#define mthi0(x) _dsp_mthi(x, 0) +#define mthi1(x) _dsp_mthi(x, 1) +#define mthi2(x) _dsp_mthi(x, 2) +#define mthi3(x) _dsp_mthi(x, 3) #endif /* CONFIG_CPU_MICROMIPS */ #endif From 778eeb1b199b85bec79b49ac483b013e270636ea Mon Sep 17 00:00:00 2001 From: "Steven J. Hill" Date: Fri, 7 Dec 2012 03:51:04 +0000 Subject: [PATCH 2936/4607] MIPS: Add new GIC clocksource. Add new clocksource that uses the counter present on the MIPS Global Interrupt Controller. Signed-off-by: Steven J. Hill Patchwork: http://patchwork.linux-mips.org/patch/4681/ Signed-off-by: John Crispin --- arch/mips/Kconfig | 4 ++ arch/mips/include/asm/gic.h | 1 + arch/mips/include/asm/time.h | 2 +- arch/mips/kernel/Makefile | 1 + arch/mips/kernel/csrc-gic.c | 49 +++++++++++++++++++ arch/mips/mti-malta/malta-time.c | 83 +++++++++++++++++++------------- 6 files changed, 106 insertions(+), 34 deletions(-) create mode 100644 arch/mips/kernel/csrc-gic.c diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index daeafe26cfb2..8f8666c8f28d 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -296,6 +296,7 @@ config MIPS_MALTA select BOOT_RAW select CEVT_R4K select CSRC_R4K + select CSRC_GIC select DMA_NONCOHERENT select GENERIC_ISA_DMA select HAVE_PCSPKR_PLATFORM @@ -928,6 +929,9 @@ config CSRC_POWERTV config CSRC_R4K bool +config CSRC_GIC + bool + config CSRC_SB1250 bool diff --git a/arch/mips/include/asm/gic.h b/arch/mips/include/asm/gic.h index 37620db588be..bbddd257c243 100644 --- a/arch/mips/include/asm/gic.h +++ b/arch/mips/include/asm/gic.h @@ -359,6 +359,7 @@ struct gic_shared_intr_map { /* Mapped interrupt to pin X, then GIC will generate the vector (X+1). */ #define GIC_PIN_TO_VEC_OFFSET (1) +extern int gic_present; extern unsigned long _gic_base; extern unsigned int gic_irq_base; extern unsigned int gic_irq_flags[]; diff --git a/arch/mips/include/asm/time.h b/arch/mips/include/asm/time.h index 761f2e92119e..d77a535d83eb 100644 --- a/arch/mips/include/asm/time.h +++ b/arch/mips/include/asm/time.h @@ -75,7 +75,7 @@ extern int init_r4k_clocksource(void); static inline int init_mips_clocksource(void) { -#ifdef CONFIG_CSRC_R4K +#if defined(CONFIG_CSRC_R4K) && !defined(CONFIG_CSRC_GIC) return init_r4k_clocksource(); #else return 0; diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 6c17e1f3d0ec..f416de34c9eb 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_CSRC_IOASIC) += csrc-ioasic.o obj-$(CONFIG_CSRC_POWERTV) += csrc-powertv.o obj-$(CONFIG_CSRC_R4K) += csrc-r4k.o obj-$(CONFIG_CSRC_SB1250) += csrc-sb1250.o +obj-$(CONFIG_CSRC_GIC) += csrc-gic.o obj-$(CONFIG_SYNC_R4K) += sync-r4k.o obj-$(CONFIG_STACKTRACE) += stacktrace.o diff --git a/arch/mips/kernel/csrc-gic.c b/arch/mips/kernel/csrc-gic.c new file mode 100644 index 000000000000..5dca24bce51b --- /dev/null +++ b/arch/mips/kernel/csrc-gic.c @@ -0,0 +1,49 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. + */ +#include +#include + +#include +#include + +static cycle_t gic_hpt_read(struct clocksource *cs) +{ + unsigned int hi, hi2, lo; + + do { + GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi); + GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_31_00), lo); + GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi2); + } while (hi2 != hi); + + return (((cycle_t) hi) << 32) + lo; +} + +static struct clocksource gic_clocksource = { + .name = "GIC", + .read = gic_hpt_read, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +void __init gic_clocksource_init(unsigned int frequency) +{ + unsigned int config, bits; + + /* Calculate the clocksource mask. */ + GICREAD(GIC_REG(SHARED, GIC_SH_CONFIG), config); + bits = 32 + ((config & GIC_SH_CONFIG_COUNTBITS_MSK) >> + (GIC_SH_CONFIG_COUNTBITS_SHF - 2)); + + /* Set clocksource mask. */ + gic_clocksource.mask = CLOCKSOURCE_MASK(bits); + + /* Calculate a somewhat reasonable rating value. */ + gic_clocksource.rating = 200 + frequency / 10000000; + + clocksource_register_hz(&gic_clocksource, frequency); +} diff --git a/arch/mips/mti-malta/malta-time.c b/arch/mips/mti-malta/malta-time.c index 115f5bc06003..a144b89cf9ba 100644 --- a/arch/mips/mti-malta/malta-time.c +++ b/arch/mips/mti-malta/malta-time.c @@ -17,7 +17,6 @@ * * Setting up the clock on the MIPS boards. */ - #include #include #include @@ -25,7 +24,6 @@ #include #include #include -#include #include #include @@ -34,11 +32,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include @@ -46,6 +44,7 @@ #include unsigned long cpu_khz; +int gic_frequency; static int mips_cpu_timer_irq; static int mips_cpu_perf_irq; @@ -61,44 +60,50 @@ static void mips_perf_dispatch(void) do_IRQ(mips_cpu_perf_irq); } -/* - * Estimate CPU frequency. Sets mips_hpt_frequency as a side-effect - */ -static unsigned int __init estimate_cpu_frequency(void) +static unsigned int freqround(unsigned int freq, unsigned int amount) { - unsigned int prid = read_c0_prid() & 0xffff00; - unsigned int count; + freq += amount; + freq -= freq % (amount*2); + return freq; +} +/* + * Estimate CPU and GIC frequencies. + */ +static void __init estimate_frequencies(void) +{ unsigned long flags; - unsigned int start; + unsigned int count, start; + unsigned int giccount = 0, gicstart = 0; local_irq_save(flags); - /* Start counter exactly on falling edge of update flag */ + /* Start counter exactly on falling edge of update flag. */ while (CMOS_READ(RTC_REG_A) & RTC_UIP); while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); - /* Start r4k counter. */ + /* Initialize counters. */ start = read_c0_count(); + if (gic_present) + GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_31_00), gicstart); - /* Read counter exactly on falling edge of update flag */ + /* Read counter exactly on falling edge of update flag. */ while (CMOS_READ(RTC_REG_A) & RTC_UIP); while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); - count = read_c0_count() - start; + count = read_c0_count(); + if (gic_present) + GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_31_00), giccount); - /* restore interrupts */ local_irq_restore(flags); + count -= start; + if (gic_present) + giccount -= gicstart; + mips_hpt_frequency = count; - if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) && - (prid != (PRID_COMP_MIPS | PRID_IMP_25KF))) - count *= 2; - - count += 5000; /* round */ - count -= count%10000; - - return count; + if (gic_present) + gic_frequency = giccount; } void read_persistent_clock(struct timespec *ts) @@ -144,22 +149,34 @@ unsigned int __cpuinit get_c0_compare_int(void) void __init plat_time_init(void) { - unsigned int est_freq; + unsigned int prid = read_c0_prid() & 0xffff00; + unsigned int freq; - /* Set Data mode - binary. */ - CMOS_WRITE(CMOS_READ(RTC_CONTROL) | RTC_DM_BINARY, RTC_CONTROL); + estimate_frequencies(); - est_freq = estimate_cpu_frequency(); + freq = mips_hpt_frequency; + if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) && + (prid != (PRID_COMP_MIPS | PRID_IMP_25KF))) + freq *= 2; + freq = freqround(freq, 5000); + pr_debug("CPU frequency %d.%02d MHz\n", freq/1000000, + (freq%1000000)*100/1000000); + cpu_khz = freq / 1000; - printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, - (est_freq%1000000)*100/1000000); + if (gic_present) { + freq = freqround(gic_frequency, 5000); + pr_debug("GIC frequency %d.%02d MHz\n", freq/1000000, + (freq%1000000)*100/1000000); + gic_clocksource_init(gic_frequency); + } else + init_r4k_clocksource(); - cpu_khz = est_freq / 1000; - - mips_scroll_message(); -#ifdef CONFIG_I8253 /* Only Malta has a PIT */ +#ifdef CONFIG_I8253 + /* Only Malta has a PIT. */ setup_pit_timer(); #endif + mips_scroll_message(); + plat_perf_setup(); } From 8838becdf5f7261d7f5dfbbe957fe9b9ed188aec Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 29 Jan 2013 08:19:12 +0000 Subject: [PATCH 2937/4607] MIPS: ath79: fix GPIO function selection for AR934x SoCs GPIO function selection is not working on the AR934x SoCs because the offset of the function selection register is different on those. Add a helper routine which returns the correct register address based on the SoC type, and use that in the 'ath79_gpio_function_*' routines. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4870/ Signed-off-by: John Crispin --- arch/mips/ath79/gpio.c | 38 +++++++++++++------ .../mips/include/asm/mach-ath79/ar71xx_regs.h | 2 + 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/arch/mips/ath79/gpio.c b/arch/mips/ath79/gpio.c index 48fe762d2526..662a10ecd8e7 100644 --- a/arch/mips/ath79/gpio.c +++ b/arch/mips/ath79/gpio.c @@ -137,47 +137,61 @@ static struct gpio_chip ath79_gpio_chip = { .base = 0, }; +static void __iomem *ath79_gpio_get_function_reg(void) +{ + u32 reg = 0; + + if (soc_is_ar71xx() || + soc_is_ar724x() || + soc_is_ar913x() || + soc_is_ar933x()) + reg = AR71XX_GPIO_REG_FUNC; + else if (soc_is_ar934x()) + reg = AR934X_GPIO_REG_FUNC; + else + BUG(); + + return ath79_gpio_base + reg; +} + void ath79_gpio_function_enable(u32 mask) { - void __iomem *base = ath79_gpio_base; + void __iomem *reg = ath79_gpio_get_function_reg(); unsigned long flags; spin_lock_irqsave(&ath79_gpio_lock, flags); - __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_FUNC) | mask, - base + AR71XX_GPIO_REG_FUNC); + __raw_writel(__raw_readl(reg) | mask, reg); /* flush write */ - __raw_readl(base + AR71XX_GPIO_REG_FUNC); + __raw_readl(reg); spin_unlock_irqrestore(&ath79_gpio_lock, flags); } void ath79_gpio_function_disable(u32 mask) { - void __iomem *base = ath79_gpio_base; + void __iomem *reg = ath79_gpio_get_function_reg(); unsigned long flags; spin_lock_irqsave(&ath79_gpio_lock, flags); - __raw_writel(__raw_readl(base + AR71XX_GPIO_REG_FUNC) & ~mask, - base + AR71XX_GPIO_REG_FUNC); + __raw_writel(__raw_readl(reg) & ~mask, reg); /* flush write */ - __raw_readl(base + AR71XX_GPIO_REG_FUNC); + __raw_readl(reg); spin_unlock_irqrestore(&ath79_gpio_lock, flags); } void ath79_gpio_function_setup(u32 set, u32 clear) { - void __iomem *base = ath79_gpio_base; + void __iomem *reg = ath79_gpio_get_function_reg(); unsigned long flags; spin_lock_irqsave(&ath79_gpio_lock, flags); - __raw_writel((__raw_readl(base + AR71XX_GPIO_REG_FUNC) & ~clear) | set, - base + AR71XX_GPIO_REG_FUNC); + __raw_writel((__raw_readl(reg) & ~clear) | set, reg); /* flush write */ - __raw_readl(base + AR71XX_GPIO_REG_FUNC); + __raw_readl(reg); spin_unlock_irqrestore(&ath79_gpio_lock, flags); } diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index a5e0f17ea77c..7d44b5d5f609 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -401,6 +401,8 @@ #define AR71XX_GPIO_REG_INT_ENABLE 0x24 #define AR71XX_GPIO_REG_FUNC 0x28 +#define AR934X_GPIO_REG_FUNC 0x6c + #define AR71XX_GPIO_COUNT 16 #define AR7240_GPIO_COUNT 18 #define AR7241_GPIO_COUNT 20 From f160a289e0e8848391f5ec48ff1a014b9c04b162 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 29 Jan 2013 08:19:13 +0000 Subject: [PATCH 2938/4607] MIPS: ath79: simplify ath79_gpio_function_* routines Make ath79_gpio_function_{en,dis}able to be wrappers around ath79_gpio_function_setup. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4871/ Signed-off-by: John Crispin --- arch/mips/ath79/gpio.c | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/arch/mips/ath79/gpio.c b/arch/mips/ath79/gpio.c index 662a10ecd8e7..b7ed207e94a1 100644 --- a/arch/mips/ath79/gpio.c +++ b/arch/mips/ath79/gpio.c @@ -154,34 +154,6 @@ static void __iomem *ath79_gpio_get_function_reg(void) return ath79_gpio_base + reg; } -void ath79_gpio_function_enable(u32 mask) -{ - void __iomem *reg = ath79_gpio_get_function_reg(); - unsigned long flags; - - spin_lock_irqsave(&ath79_gpio_lock, flags); - - __raw_writel(__raw_readl(reg) | mask, reg); - /* flush write */ - __raw_readl(reg); - - spin_unlock_irqrestore(&ath79_gpio_lock, flags); -} - -void ath79_gpio_function_disable(u32 mask) -{ - void __iomem *reg = ath79_gpio_get_function_reg(); - unsigned long flags; - - spin_lock_irqsave(&ath79_gpio_lock, flags); - - __raw_writel(__raw_readl(reg) & ~mask, reg); - /* flush write */ - __raw_readl(reg); - - spin_unlock_irqrestore(&ath79_gpio_lock, flags); -} - void ath79_gpio_function_setup(u32 set, u32 clear) { void __iomem *reg = ath79_gpio_get_function_reg(); @@ -196,6 +168,16 @@ void ath79_gpio_function_setup(u32 set, u32 clear) spin_unlock_irqrestore(&ath79_gpio_lock, flags); } +void ath79_gpio_function_enable(u32 mask) +{ + ath79_gpio_function_setup(mask, 0); +} + +void ath79_gpio_function_disable(u32 mask) +{ + ath79_gpio_function_setup(0, mask); +} + void __init ath79_gpio_init(void) { int err; From 9c099c4e79b67d5578ce8142e6214950be4fcf43 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Tue, 29 Jan 2013 16:13:17 +0000 Subject: [PATCH 2939/4607] MIPS: ath79: simplify MISC IRQ handling The current code uses multiple if statements for demultiplexing the different interrupt sources. Additionally, the MISC interrupt controller has 32 interrupt sources and the current code does not handles all of them. Get rid of the if statements and process all interrupt sources in a loop to fix these issues. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4874/ Signed-off-by: John Crispin --- arch/mips/ath79/irq.c | 47 ++++++-------------------- arch/mips/include/asm/mach-ath79/irq.h | 1 + 2 files changed, 11 insertions(+), 37 deletions(-) diff --git a/arch/mips/ath79/irq.c b/arch/mips/ath79/irq.c index 90d09fc15398..219cfa1f5961 100644 --- a/arch/mips/ath79/irq.c +++ b/arch/mips/ath79/irq.c @@ -35,44 +35,17 @@ static void ath79_misc_irq_handler(unsigned int irq, struct irq_desc *desc) pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) & __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); - if (pending & MISC_INT_UART) - generic_handle_irq(ATH79_MISC_IRQ_UART); - - else if (pending & MISC_INT_DMA) - generic_handle_irq(ATH79_MISC_IRQ_DMA); - - else if (pending & MISC_INT_PERFC) - generic_handle_irq(ATH79_MISC_IRQ_PERFC); - - else if (pending & MISC_INT_TIMER) - generic_handle_irq(ATH79_MISC_IRQ_TIMER); - - else if (pending & MISC_INT_TIMER2) - generic_handle_irq(ATH79_MISC_IRQ_TIMER2); - - else if (pending & MISC_INT_TIMER3) - generic_handle_irq(ATH79_MISC_IRQ_TIMER3); - - else if (pending & MISC_INT_TIMER4) - generic_handle_irq(ATH79_MISC_IRQ_TIMER4); - - else if (pending & MISC_INT_OHCI) - generic_handle_irq(ATH79_MISC_IRQ_OHCI); - - else if (pending & MISC_INT_ERROR) - generic_handle_irq(ATH79_MISC_IRQ_ERROR); - - else if (pending & MISC_INT_GPIO) - generic_handle_irq(ATH79_MISC_IRQ_GPIO); - - else if (pending & MISC_INT_WDOG) - generic_handle_irq(ATH79_MISC_IRQ_WDOG); - - else if (pending & MISC_INT_ETHSW) - generic_handle_irq(ATH79_MISC_IRQ_ETHSW); - - else + if (!pending) { spurious_interrupt(); + return; + } + + while (pending) { + int bit = __ffs(pending); + + generic_handle_irq(ATH79_MISC_IRQ(bit)); + pending &= ~BIT(bit); + } } static void ar71xx_misc_irq_unmask(struct irq_data *d) diff --git a/arch/mips/include/asm/mach-ath79/irq.h b/arch/mips/include/asm/mach-ath79/irq.h index 0968f69e2018..158ad7f41313 100644 --- a/arch/mips/include/asm/mach-ath79/irq.h +++ b/arch/mips/include/asm/mach-ath79/irq.h @@ -14,6 +14,7 @@ #define ATH79_MISC_IRQ_BASE 8 #define ATH79_MISC_IRQ_COUNT 32 +#define ATH79_MISC_IRQ(_x) (ATH79_MISC_IRQ_BASE + (_x)) #define ATH79_PCI_IRQ_BASE (ATH79_MISC_IRQ_BASE + ATH79_MISC_IRQ_COUNT) #define ATH79_PCI_IRQ_COUNT 6 From 247796175ea5dc7c838e8e76a14795fb09360649 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 20 Jul 2012 18:58:34 +0200 Subject: [PATCH 2940/4607] Document: devicetree: add OF documents for lantiq serial port Signed-off-by: John Crispin Cc: Rob Herring Cc: devicetree-discuss@lists.ozlabs.org --- .../devicetree/bindings/serial/lantiq_asc.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/lantiq_asc.txt diff --git a/Documentation/devicetree/bindings/serial/lantiq_asc.txt b/Documentation/devicetree/bindings/serial/lantiq_asc.txt new file mode 100644 index 000000000000..5b78591aaa46 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/lantiq_asc.txt @@ -0,0 +1,16 @@ +Lantiq SoC ASC serial controller + +Required properties: +- compatible : Should be "lantiq,asc" +- reg : Address and length of the register set for the device +- interrupts: the 3 (tx rx err) interrupt numbers. The interrupt specifier + depends on the interrupt-parent interrupt controller. + +Example: + +asc1: serial@E100C00 { + compatible = "lantiq,asc"; + reg = <0xE100C00 0x400>; + interrupt-parent = <&icu0>; + interrupts = <112 113 114>; +}; From 8563991026ee98bb5e477167236972a45dfea0e3 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 21 Jan 2013 18:25:59 +0100 Subject: [PATCH 2941/4607] MIPS: ralink: adds include files Before we start adding the platform code we add the common include files. Signed-off-by: John Crispin Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4893/ --- .../include/asm/mach-ralink/ralink_regs.h | 39 ++++++++++++++++ arch/mips/include/asm/mach-ralink/war.h | 25 +++++++++++ arch/mips/ralink/common.h | 44 +++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 arch/mips/include/asm/mach-ralink/ralink_regs.h create mode 100644 arch/mips/include/asm/mach-ralink/war.h create mode 100644 arch/mips/ralink/common.h diff --git a/arch/mips/include/asm/mach-ralink/ralink_regs.h b/arch/mips/include/asm/mach-ralink/ralink_regs.h new file mode 100644 index 000000000000..5a508f9f9432 --- /dev/null +++ b/arch/mips/include/asm/mach-ralink/ralink_regs.h @@ -0,0 +1,39 @@ +/* + * Ralink SoC register definitions + * + * Copyright (C) 2013 John Crispin + * Copyright (C) 2008-2010 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * 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 _RALINK_REGS_H_ +#define _RALINK_REGS_H_ + +extern __iomem void *rt_sysc_membase; +extern __iomem void *rt_memc_membase; + +static inline void rt_sysc_w32(u32 val, unsigned reg) +{ + __raw_writel(val, rt_sysc_membase + reg); +} + +static inline u32 rt_sysc_r32(unsigned reg) +{ + return __raw_readl(rt_sysc_membase + reg); +} + +static inline void rt_memc_w32(u32 val, unsigned reg) +{ + __raw_writel(val, rt_memc_membase + reg); +} + +static inline u32 rt_memc_r32(unsigned reg) +{ + return __raw_readl(rt_memc_membase + reg); +} + +#endif /* _RALINK_REGS_H_ */ diff --git a/arch/mips/include/asm/mach-ralink/war.h b/arch/mips/include/asm/mach-ralink/war.h new file mode 100644 index 000000000000..a7b712cf2d28 --- /dev/null +++ b/arch/mips/include/asm/mach-ralink/war.h @@ -0,0 +1,25 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2002, 2004, 2007 by Ralf Baechle + */ +#ifndef __ASM_MACH_RALINK_WAR_H +#define __ASM_MACH_RALINK_WAR_H + +#define R4600_V1_INDEX_ICACHEOP_WAR 0 +#define R4600_V1_HIT_CACHEOP_WAR 0 +#define R4600_V2_HIT_CACHEOP_WAR 0 +#define R5432_CP0_INTERRUPT_WAR 0 +#define BCM1250_M3_WAR 0 +#define SIBYTE_1956_WAR 0 +#define MIPS4K_ICACHE_REFILL_WAR 0 +#define MIPS_CACHE_SYNC_WAR 0 +#define TX49XX_ICACHE_INDEX_INV_WAR 0 +#define RM9000_CDEX_SMP_WAR 0 +#define ICACHE_REFILLS_WORKAROUND_WAR 0 +#define R10000_LLSC_WAR 0 +#define MIPS34K_MISSED_ITLB_WAR 0 + +#endif /* __ASM_MACH_RALINK_WAR_H */ diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h new file mode 100644 index 000000000000..300990313e1b --- /dev/null +++ b/arch/mips/ralink/common.h @@ -0,0 +1,44 @@ +/* + * 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. + * + * Copyright (C) 2013 John Crispin + */ + +#ifndef _RALINK_COMMON_H__ +#define _RALINK_COMMON_H__ + +#define RAMIPS_SYS_TYPE_LEN 32 + +struct ralink_pinmux_grp { + const char *name; + u32 mask; + int gpio_first; + int gpio_last; +}; + +struct ralink_pinmux { + struct ralink_pinmux_grp *mode; + struct ralink_pinmux_grp *uart; + int uart_shift; + void (*wdt_reset)(void); +}; +extern struct ralink_pinmux gpio_pinmux; + +struct ralink_soc_info { + unsigned char sys_type[RAMIPS_SYS_TYPE_LEN]; + unsigned char *compatible; +}; +extern struct ralink_soc_info soc_info; + +extern void ralink_of_remap(void); + +extern void ralink_clk_init(void); +extern void ralink_clk_add(const char *dev, unsigned long rate); + +extern void prom_soc_init(struct ralink_soc_info *soc_info); + +__iomem void *plat_of_remap_node(const char *node); + +#endif /* _RALINK_COMMON_H__ */ From 19d3814e7b325f8965fd71f329b3467a97f8d217 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:00:50 +0100 Subject: [PATCH 2942/4607] MIPS: ralink: adds irq code All of the Ralink Wifi SoC currently supported by this series share the same interrupt controller (INTC). Signed-off-by: John Crispin Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4890/ --- arch/mips/ralink/irq.c | 176 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 arch/mips/ralink/irq.c diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c new file mode 100644 index 000000000000..e62c9751e2d8 --- /dev/null +++ b/arch/mips/ralink/irq.c @@ -0,0 +1,176 @@ +/* + * 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. + * + * Copyright (C) 2009 Gabor Juhos + * Copyright (C) 2013 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common.h" + +/* INTC register offsets */ +#define INTC_REG_STATUS0 0x00 +#define INTC_REG_STATUS1 0x04 +#define INTC_REG_TYPE 0x20 +#define INTC_REG_RAW_STATUS 0x30 +#define INTC_REG_ENABLE 0x34 +#define INTC_REG_DISABLE 0x38 + +#define INTC_INT_GLOBAL BIT(31) + +#define RALINK_CPU_IRQ_INTC (MIPS_CPU_IRQ_BASE + 2) +#define RALINK_CPU_IRQ_FE (MIPS_CPU_IRQ_BASE + 5) +#define RALINK_CPU_IRQ_WIFI (MIPS_CPU_IRQ_BASE + 6) +#define RALINK_CPU_IRQ_COUNTER (MIPS_CPU_IRQ_BASE + 7) + +/* we have a cascade of 8 irqs */ +#define RALINK_INTC_IRQ_BASE 8 + +/* we have 32 SoC irqs */ +#define RALINK_INTC_IRQ_COUNT 32 + +#define RALINK_INTC_IRQ_PERFC (RALINK_INTC_IRQ_BASE + 9) + +static void __iomem *rt_intc_membase; + +static inline void rt_intc_w32(u32 val, unsigned reg) +{ + __raw_writel(val, rt_intc_membase + reg); +} + +static inline u32 rt_intc_r32(unsigned reg) +{ + return __raw_readl(rt_intc_membase + reg); +} + +static void ralink_intc_irq_unmask(struct irq_data *d) +{ + rt_intc_w32(BIT(d->hwirq), INTC_REG_ENABLE); +} + +static void ralink_intc_irq_mask(struct irq_data *d) +{ + rt_intc_w32(BIT(d->hwirq), INTC_REG_DISABLE); +} + +static struct irq_chip ralink_intc_irq_chip = { + .name = "INTC", + .irq_unmask = ralink_intc_irq_unmask, + .irq_mask = ralink_intc_irq_mask, + .irq_mask_ack = ralink_intc_irq_mask, +}; + +unsigned int __cpuinit get_c0_compare_int(void) +{ + return CP0_LEGACY_COMPARE_IRQ; +} + +static void ralink_intc_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + u32 pending = rt_intc_r32(INTC_REG_STATUS0); + + if (pending) { + struct irq_domain *domain = irq_get_handler_data(irq); + generic_handle_irq(irq_find_mapping(domain, __ffs(pending))); + } else { + spurious_interrupt(); + } +} + +asmlinkage void plat_irq_dispatch(void) +{ + unsigned long pending; + + pending = read_c0_status() & read_c0_cause() & ST0_IM; + + if (pending & STATUSF_IP7) + do_IRQ(RALINK_CPU_IRQ_COUNTER); + + else if (pending & STATUSF_IP5) + do_IRQ(RALINK_CPU_IRQ_FE); + + else if (pending & STATUSF_IP6) + do_IRQ(RALINK_CPU_IRQ_WIFI); + + else if (pending & STATUSF_IP2) + do_IRQ(RALINK_CPU_IRQ_INTC); + + else + spurious_interrupt(); +} + +static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +{ + irq_set_chip_and_handler(irq, &ralink_intc_irq_chip, handle_level_irq); + + return 0; +} + +static const struct irq_domain_ops irq_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = intc_map, +}; + +static int __init intc_of_init(struct device_node *node, + struct device_node *parent) +{ + struct resource res; + struct irq_domain *domain; + + mips_cpu_irq_init(); + + if (of_address_to_resource(node, 0, &res)) + panic("Failed to get intc memory range"); + + if (request_mem_region(res.start, resource_size(&res), + res.name) < 0) + pr_err("Failed to request intc memory"); + + rt_intc_membase = ioremap_nocache(res.start, + resource_size(&res)); + if (!rt_intc_membase) + panic("Failed to remap intc memory"); + + /* disable all interrupts */ + rt_intc_w32(~0, INTC_REG_DISABLE); + + /* route all INTC interrupts to MIPS HW0 interrupt */ + rt_intc_w32(0, INTC_REG_TYPE); + + domain = irq_domain_add_legacy(node, RALINK_INTC_IRQ_COUNT, + RALINK_INTC_IRQ_BASE, 0, &irq_domain_ops, NULL); + if (!domain) + panic("Failed to add irqdomain"); + + rt_intc_w32(INTC_INT_GLOBAL, INTC_REG_ENABLE); + + irq_set_chained_handler(RALINK_CPU_IRQ_INTC, ralink_intc_irq_handler); + irq_set_handler_data(RALINK_CPU_IRQ_INTC, domain); + + cp0_perfcount_irq = irq_create_mapping(domain, 9); + + return 0; +} + +static struct of_device_id __initdata of_irq_ids[] = { + { .compatible = "ralink,rt2880-intc", .data = intc_of_init }, + {}, +}; + +void __init arch_init_irq(void) +{ + of_irq_init(of_irq_ids); +} + From c06e836ada59fbc6d1109277e693e5b3e056ac12 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:00:57 +0100 Subject: [PATCH 2943/4607] MIPS: ralink: adds reset code Resetting these SoCs requires no real magic. The code is straight forward. Signed-off-by: John Crispin Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4891/ --- arch/mips/ralink/reset.c | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 arch/mips/ralink/reset.c diff --git a/arch/mips/ralink/reset.c b/arch/mips/ralink/reset.c new file mode 100644 index 000000000000..22120e512e7e --- /dev/null +++ b/arch/mips/ralink/reset.c @@ -0,0 +1,44 @@ +/* + * 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. + * + * Copyright (C) 2008-2009 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * Copyright (C) 2013 John Crispin + */ + +#include +#include + +#include + +#include + +/* Reset Control */ +#define SYSC_REG_RESET_CTRL 0x034 +#define RSTCTL_RESET_SYSTEM BIT(0) + +static void ralink_restart(char *command) +{ + local_irq_disable(); + rt_sysc_w32(RSTCTL_RESET_SYSTEM, SYSC_REG_RESET_CTRL); + unreachable(); +} + +static void ralink_halt(void) +{ + local_irq_disable(); + unreachable(); +} + +static int __init mips_reboot_setup(void) +{ + _machine_restart = ralink_restart; + _machine_halt = ralink_halt; + pm_power_off = ralink_halt; + + return 0; +} + +arch_initcall(mips_reboot_setup); From 7e47cefa69c8ed2c889522ce29fcce73ce8cf08e Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:01:05 +0100 Subject: [PATCH 2944/4607] MIPS: ralink: adds prom and cmdline code Add minimal code to handle commandlines. Signed-off-by: John Crispin Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4892/ --- arch/mips/ralink/prom.c | 69 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 arch/mips/ralink/prom.c diff --git a/arch/mips/ralink/prom.c b/arch/mips/ralink/prom.c new file mode 100644 index 000000000000..9c64f029d047 --- /dev/null +++ b/arch/mips/ralink/prom.c @@ -0,0 +1,69 @@ +/* + * 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. + * + * Copyright (C) 2009 Gabor Juhos + * Copyright (C) 2010 Joonas Lahtinen + * Copyright (C) 2013 John Crispin + */ + +#include +#include +#include + +#include +#include + +#include "common.h" + +struct ralink_soc_info soc_info; + +const char *get_system_type(void) +{ + return soc_info.sys_type; +} + +static __init void prom_init_cmdline(int argc, char **argv) +{ + int i; + + pr_debug("prom: fw_arg0=%08x fw_arg1=%08x fw_arg2=%08x fw_arg3=%08x\n", + (unsigned int)fw_arg0, (unsigned int)fw_arg1, + (unsigned int)fw_arg2, (unsigned int)fw_arg3); + + argc = fw_arg0; + argv = (char **) KSEG1ADDR(fw_arg1); + + if (!argv) { + pr_debug("argv=%p is invalid, skipping\n", + argv); + return; + } + + for (i = 0; i < argc; i++) { + char *p = (char *) KSEG1ADDR(argv[i]); + + if (CPHYSADDR(p) && *p) { + pr_debug("argv[%d]: %s\n", i, p); + strlcat(arcs_cmdline, " ", sizeof(arcs_cmdline)); + strlcat(arcs_cmdline, p, sizeof(arcs_cmdline)); + } + } +} + +void __init prom_init(void) +{ + int argc; + char **argv; + + prom_soc_init(&soc_info); + + pr_info("SoC Type: %s\n", get_system_type()); + + prom_init_cmdline(argc, argv); +} + +void __init prom_free_prom_memory(void) +{ +} From 3f0a06b0368d25608841843e9d65a7289ad9f14a Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:01:29 +0100 Subject: [PATCH 2945/4607] MIPS: ralink: adds clkdev code These SoCs have a limited number of fixed rate clocks. Add support for the clk and clkdev api. Signed-off-by: John Crispin Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4894/ --- arch/mips/ralink/clk.c | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 arch/mips/ralink/clk.c diff --git a/arch/mips/ralink/clk.c b/arch/mips/ralink/clk.c new file mode 100644 index 000000000000..8dfa22ff300b --- /dev/null +++ b/arch/mips/ralink/clk.c @@ -0,0 +1,72 @@ +/* + * 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. + * + * Copyright (C) 2011 Gabor Juhos + * Copyright (C) 2013 John Crispin + */ + +#include +#include +#include +#include + +#include + +#include "common.h" + +struct clk { + struct clk_lookup cl; + unsigned long rate; +}; + +void ralink_clk_add(const char *dev, unsigned long rate) +{ + struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL); + + if (!clk) + panic("failed to add clock\n"); + + clk->cl.dev_id = dev; + clk->cl.clk = clk; + + clk->rate = rate; + + clkdev_add(&clk->cl); +} + +/* + * Linux clock API + */ +int clk_enable(struct clk *clk) +{ + return 0; +} +EXPORT_SYMBOL_GPL(clk_enable); + +void clk_disable(struct clk *clk) +{ +} +EXPORT_SYMBOL_GPL(clk_disable); + +unsigned long clk_get_rate(struct clk *clk) +{ + return clk->rate; +} +EXPORT_SYMBOL_GPL(clk_get_rate); + +void __init plat_time_init(void) +{ + struct clk *clk; + + ralink_of_remap(); + + ralink_clk_init(); + clk = clk_get_sys("cpu", NULL); + if (IS_ERR(clk)) + panic("unable to get CPU clock, err=%ld", PTR_ERR(clk)); + pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000); + mips_hpt_frequency = clk_get_rate(clk) / 2; + clk_put(clk); +} From 3a5bfe7bdbfd37c9206d7c6dfd7eb9664ccc5038 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:02:01 +0100 Subject: [PATCH 2946/4607] MIPS: ralink: adds OF code Until there is a generic MIPS way of handing the DTB over from bootloader to kernel we rely on a built in devicetrees. The OF code also remaps those register ranges that we use global in our drivers. Signed-off-by: John Crispin Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4895/ --- arch/mips/ralink/of.c | 107 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 arch/mips/ralink/of.c diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c new file mode 100644 index 000000000000..4165e70775be --- /dev/null +++ b/arch/mips/ralink/of.c @@ -0,0 +1,107 @@ +/* + * 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. + * + * Copyright (C) 2008 Imre Kaloz + * Copyright (C) 2008-2009 Gabor Juhos + * Copyright (C) 2013 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "common.h" + +__iomem void *rt_sysc_membase; +__iomem void *rt_memc_membase; + +extern struct boot_param_header __dtb_start; + +__iomem void *plat_of_remap_node(const char *node) +{ + struct resource res; + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, node); + if (!np) + panic("Failed to find %s node", node); + + if (of_address_to_resource(np, 0, &res)) + panic("Failed to get resource for %s", node); + + if ((request_mem_region(res.start, + resource_size(&res), + res.name) < 0)) + panic("Failed to request resources for %s", node); + + return ioremap_nocache(res.start, resource_size(&res)); +} + +void __init device_tree_init(void) +{ + unsigned long base, size; + void *fdt_copy; + + if (!initial_boot_params) + return; + + base = virt_to_phys((void *)initial_boot_params); + size = be32_to_cpu(initial_boot_params->totalsize); + + /* Before we do anything, lets reserve the dt blob */ + reserve_bootmem(base, size, BOOTMEM_DEFAULT); + + /* The strings in the flattened tree are referenced directly by the + * device tree, so copy the flattened device tree from init memory + * to regular memory. + */ + fdt_copy = alloc_bootmem(size); + memcpy(fdt_copy, initial_boot_params, size); + initial_boot_params = fdt_copy; + + unflatten_device_tree(); + + /* free the space reserved for the dt blob */ + free_bootmem(base, size); +} + +void __init plat_mem_setup(void) +{ + set_io_port_base(KSEG1); + + /* + * Load the builtin devicetree. This causes the chosen node to be + * parsed resulting in our memory appearing + */ + __dt_setup_arch(&__dtb_start); +} + +static int __init plat_of_setup(void) +{ + static struct of_device_id of_ids[3]; + int len = sizeof(of_ids[0].compatible); + + if (!of_have_populated_dt()) + panic("device tree not present"); + + strncpy(of_ids[0].compatible, soc_info.compatible, len); + strncpy(of_ids[1].compatible, "palmbus", len); + + if (of_platform_populate(NULL, of_ids, NULL, NULL)) + panic("failed to populate DT\n"); + + return 0; +} + +arch_initcall(plat_of_setup); From 5fff610b7c60195de98e68bec00c357f393ce634 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:02:55 +0100 Subject: [PATCH 2947/4607] MIPS: ralink: adds early_printk support Add the code needed to make early printk work. Signed-off-by: John Crispin Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4897/ --- arch/mips/ralink/early_printk.c | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 arch/mips/ralink/early_printk.c diff --git a/arch/mips/ralink/early_printk.c b/arch/mips/ralink/early_printk.c new file mode 100644 index 000000000000..c4ae47eb24ab --- /dev/null +++ b/arch/mips/ralink/early_printk.c @@ -0,0 +1,44 @@ +/* + * 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. + * + * Copyright (C) 2011-2012 Gabor Juhos + */ + +#include +#include + +#include + +#define EARLY_UART_BASE 0x10000c00 + +#define UART_REG_RX 0x00 +#define UART_REG_TX 0x04 +#define UART_REG_IER 0x08 +#define UART_REG_IIR 0x0c +#define UART_REG_FCR 0x10 +#define UART_REG_LCR 0x14 +#define UART_REG_MCR 0x18 +#define UART_REG_LSR 0x1c + +static __iomem void *uart_membase = (__iomem void *) KSEG1ADDR(EARLY_UART_BASE); + +static inline void uart_w32(u32 val, unsigned reg) +{ + __raw_writel(val, uart_membase + reg); +} + +static inline u32 uart_r32(unsigned reg) +{ + return __raw_readl(uart_membase + reg); +} + +void prom_putchar(unsigned char ch) +{ + while ((uart_r32(UART_REG_LSR) & UART_LSR_THRE) == 0) + ; + uart_w32(ch, UART_REG_TX); + while ((uart_r32(UART_REG_LSR) & UART_LSR_THRE) == 0) + ; +} From 2809b31770d7fd934a748692e1922a5e613f06e5 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:03:46 +0100 Subject: [PATCH 2948/4607] MIPS: ralink: adds support for RT305x SoC family Add support code for rt3050, rt3052, rt3350, rt3352 and rt5350 SOC. The code detects the SoC and registers the clk / pinmux settings. Signed-off-by: John Crispin Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4896/ --- arch/mips/include/asm/mach-ralink/rt305x.h | 139 ++++++++++++ arch/mips/ralink/rt305x.c | 242 +++++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 arch/mips/include/asm/mach-ralink/rt305x.h create mode 100644 arch/mips/ralink/rt305x.c diff --git a/arch/mips/include/asm/mach-ralink/rt305x.h b/arch/mips/include/asm/mach-ralink/rt305x.h new file mode 100644 index 000000000000..7d344f2d7d0a --- /dev/null +++ b/arch/mips/include/asm/mach-ralink/rt305x.h @@ -0,0 +1,139 @@ +/* + * 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. + * + * Parts of this file are based on Ralink's 2.6.21 BSP + * + * Copyright (C) 2008-2011 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * Copyright (C) 2013 John Crispin + */ + +#ifndef _RT305X_REGS_H_ +#define _RT305X_REGS_H_ + +enum rt305x_soc_type { + RT305X_SOC_UNKNOWN = 0, + RT305X_SOC_RT3050, + RT305X_SOC_RT3052, + RT305X_SOC_RT3350, + RT305X_SOC_RT3352, + RT305X_SOC_RT5350, +}; + +extern enum rt305x_soc_type rt305x_soc; + +static inline int soc_is_rt3050(void) +{ + return rt305x_soc == RT305X_SOC_RT3050; +} + +static inline int soc_is_rt3052(void) +{ + return rt305x_soc == RT305X_SOC_RT3052; +} + +static inline int soc_is_rt305x(void) +{ + return soc_is_rt3050() || soc_is_rt3052(); +} + +static inline int soc_is_rt3350(void) +{ + return rt305x_soc == RT305X_SOC_RT3350; +} + +static inline int soc_is_rt3352(void) +{ + return rt305x_soc == RT305X_SOC_RT3352; +} + +static inline int soc_is_rt5350(void) +{ + return rt305x_soc == RT305X_SOC_RT5350; +} + +#define RT305X_SYSC_BASE 0x10000000 + +#define SYSC_REG_CHIP_NAME0 0x00 +#define SYSC_REG_CHIP_NAME1 0x04 +#define SYSC_REG_CHIP_ID 0x0c +#define SYSC_REG_SYSTEM_CONFIG 0x10 + +#define RT3052_CHIP_NAME0 0x30335452 +#define RT3052_CHIP_NAME1 0x20203235 + +#define RT3350_CHIP_NAME0 0x33335452 +#define RT3350_CHIP_NAME1 0x20203035 + +#define RT3352_CHIP_NAME0 0x33335452 +#define RT3352_CHIP_NAME1 0x20203235 + +#define RT5350_CHIP_NAME0 0x33355452 +#define RT5350_CHIP_NAME1 0x20203035 + +#define CHIP_ID_ID_MASK 0xff +#define CHIP_ID_ID_SHIFT 8 +#define CHIP_ID_REV_MASK 0xff + +#define RT305X_SYSCFG_CPUCLK_SHIFT 18 +#define RT305X_SYSCFG_CPUCLK_MASK 0x1 +#define RT305X_SYSCFG_CPUCLK_LOW 0x0 +#define RT305X_SYSCFG_CPUCLK_HIGH 0x1 + +#define RT305X_SYSCFG_SRAM_CS0_MODE_SHIFT 2 +#define RT305X_SYSCFG_CPUCLK_MASK 0x1 +#define RT305X_SYSCFG_SRAM_CS0_MODE_WDT 0x1 + +#define RT3352_SYSCFG0_CPUCLK_SHIFT 8 +#define RT3352_SYSCFG0_CPUCLK_MASK 0x1 +#define RT3352_SYSCFG0_CPUCLK_LOW 0x0 +#define RT3352_SYSCFG0_CPUCLK_HIGH 0x1 + +#define RT5350_SYSCFG0_CPUCLK_SHIFT 8 +#define RT5350_SYSCFG0_CPUCLK_MASK 0x3 +#define RT5350_SYSCFG0_CPUCLK_360 0x0 +#define RT5350_SYSCFG0_CPUCLK_320 0x2 +#define RT5350_SYSCFG0_CPUCLK_300 0x3 + +/* multi function gpio pins */ +#define RT305X_GPIO_I2C_SD 1 +#define RT305X_GPIO_I2C_SCLK 2 +#define RT305X_GPIO_SPI_EN 3 +#define RT305X_GPIO_SPI_CLK 4 +/* GPIO 7-14 is shared between UART0, PCM and I2S interfaces */ +#define RT305X_GPIO_7 7 +#define RT305X_GPIO_10 10 +#define RT305X_GPIO_14 14 +#define RT305X_GPIO_UART1_TXD 15 +#define RT305X_GPIO_UART1_RXD 16 +#define RT305X_GPIO_JTAG_TDO 17 +#define RT305X_GPIO_JTAG_TDI 18 +#define RT305X_GPIO_MDIO_MDC 22 +#define RT305X_GPIO_MDIO_MDIO 23 +#define RT305X_GPIO_SDRAM_MD16 24 +#define RT305X_GPIO_SDRAM_MD31 39 +#define RT305X_GPIO_GE0_TXD0 40 +#define RT305X_GPIO_GE0_RXCLK 51 + +#define RT305X_GPIO_MODE_I2C BIT(0) +#define RT305X_GPIO_MODE_SPI BIT(1) +#define RT305X_GPIO_MODE_UART0_SHIFT 2 +#define RT305X_GPIO_MODE_UART0_MASK 0x7 +#define RT305X_GPIO_MODE_UART0(x) ((x) << RT305X_GPIO_MODE_UART0_SHIFT) +#define RT305X_GPIO_MODE_UARTF 0x0 +#define RT305X_GPIO_MODE_PCM_UARTF 0x1 +#define RT305X_GPIO_MODE_PCM_I2S 0x2 +#define RT305X_GPIO_MODE_I2S_UARTF 0x3 +#define RT305X_GPIO_MODE_PCM_GPIO 0x4 +#define RT305X_GPIO_MODE_GPIO_UARTF 0x5 +#define RT305X_GPIO_MODE_GPIO_I2S 0x6 +#define RT305X_GPIO_MODE_GPIO 0x7 +#define RT305X_GPIO_MODE_UART1 BIT(5) +#define RT305X_GPIO_MODE_JTAG BIT(6) +#define RT305X_GPIO_MODE_MDIO BIT(7) +#define RT305X_GPIO_MODE_SDRAM BIT(8) +#define RT305X_GPIO_MODE_RGMII BIT(9) + +#endif diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c new file mode 100644 index 000000000000..0a4bbdcf59d9 --- /dev/null +++ b/arch/mips/ralink/rt305x.c @@ -0,0 +1,242 @@ +/* + * 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. + * + * Parts of this file are based on Ralink's 2.6.21 BSP + * + * Copyright (C) 2008-2011 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * Copyright (C) 2013 John Crispin + */ + +#include +#include +#include + +#include +#include +#include + +#include "common.h" + +enum rt305x_soc_type rt305x_soc; + +struct ralink_pinmux_grp mode_mux[] = { + { + .name = "i2c", + .mask = RT305X_GPIO_MODE_I2C, + .gpio_first = RT305X_GPIO_I2C_SD, + .gpio_last = RT305X_GPIO_I2C_SCLK, + }, { + .name = "spi", + .mask = RT305X_GPIO_MODE_SPI, + .gpio_first = RT305X_GPIO_SPI_EN, + .gpio_last = RT305X_GPIO_SPI_CLK, + }, { + .name = "uartlite", + .mask = RT305X_GPIO_MODE_UART1, + .gpio_first = RT305X_GPIO_UART1_TXD, + .gpio_last = RT305X_GPIO_UART1_RXD, + }, { + .name = "jtag", + .mask = RT305X_GPIO_MODE_JTAG, + .gpio_first = RT305X_GPIO_JTAG_TDO, + .gpio_last = RT305X_GPIO_JTAG_TDI, + }, { + .name = "mdio", + .mask = RT305X_GPIO_MODE_MDIO, + .gpio_first = RT305X_GPIO_MDIO_MDC, + .gpio_last = RT305X_GPIO_MDIO_MDIO, + }, { + .name = "sdram", + .mask = RT305X_GPIO_MODE_SDRAM, + .gpio_first = RT305X_GPIO_SDRAM_MD16, + .gpio_last = RT305X_GPIO_SDRAM_MD31, + }, { + .name = "rgmii", + .mask = RT305X_GPIO_MODE_RGMII, + .gpio_first = RT305X_GPIO_GE0_TXD0, + .gpio_last = RT305X_GPIO_GE0_RXCLK, + }, {0} +}; + +struct ralink_pinmux_grp uart_mux[] = { + { + .name = "uartf", + .mask = RT305X_GPIO_MODE_UARTF, + .gpio_first = RT305X_GPIO_7, + .gpio_last = RT305X_GPIO_14, + }, { + .name = "pcm uartf", + .mask = RT305X_GPIO_MODE_PCM_UARTF, + .gpio_first = RT305X_GPIO_7, + .gpio_last = RT305X_GPIO_14, + }, { + .name = "pcm i2s", + .mask = RT305X_GPIO_MODE_PCM_I2S, + .gpio_first = RT305X_GPIO_7, + .gpio_last = RT305X_GPIO_14, + }, { + .name = "i2s uartf", + .mask = RT305X_GPIO_MODE_I2S_UARTF, + .gpio_first = RT305X_GPIO_7, + .gpio_last = RT305X_GPIO_14, + }, { + .name = "pcm gpio", + .mask = RT305X_GPIO_MODE_PCM_GPIO, + .gpio_first = RT305X_GPIO_10, + .gpio_last = RT305X_GPIO_14, + }, { + .name = "gpio uartf", + .mask = RT305X_GPIO_MODE_GPIO_UARTF, + .gpio_first = RT305X_GPIO_7, + .gpio_last = RT305X_GPIO_14, + }, { + .name = "gpio i2s", + .mask = RT305X_GPIO_MODE_GPIO_I2S, + .gpio_first = RT305X_GPIO_7, + .gpio_last = RT305X_GPIO_14, + }, { + .name = "gpio", + .mask = RT305X_GPIO_MODE_GPIO, + }, {0} +}; + +void rt305x_wdt_reset(void) +{ + u32 t; + + /* enable WDT reset output on pin SRAM_CS_N */ + t = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG); + t |= RT305X_SYSCFG_SRAM_CS0_MODE_WDT << + RT305X_SYSCFG_SRAM_CS0_MODE_SHIFT; + rt_sysc_w32(t, SYSC_REG_SYSTEM_CONFIG); +} + +struct ralink_pinmux gpio_pinmux = { + .mode = mode_mux, + .uart = uart_mux, + .uart_shift = RT305X_GPIO_MODE_UART0_SHIFT, + .wdt_reset = rt305x_wdt_reset, +}; + +void __init ralink_clk_init(void) +{ + unsigned long cpu_rate, sys_rate, wdt_rate, uart_rate; + u32 t = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG); + + if (soc_is_rt305x() || soc_is_rt3350()) { + t = (t >> RT305X_SYSCFG_CPUCLK_SHIFT) & + RT305X_SYSCFG_CPUCLK_MASK; + switch (t) { + case RT305X_SYSCFG_CPUCLK_LOW: + cpu_rate = 320000000; + break; + case RT305X_SYSCFG_CPUCLK_HIGH: + cpu_rate = 384000000; + break; + } + sys_rate = uart_rate = wdt_rate = cpu_rate / 3; + } else if (soc_is_rt3352()) { + t = (t >> RT3352_SYSCFG0_CPUCLK_SHIFT) & + RT3352_SYSCFG0_CPUCLK_MASK; + switch (t) { + case RT3352_SYSCFG0_CPUCLK_LOW: + cpu_rate = 384000000; + break; + case RT3352_SYSCFG0_CPUCLK_HIGH: + cpu_rate = 400000000; + break; + } + sys_rate = wdt_rate = cpu_rate / 3; + uart_rate = 40000000; + } else if (soc_is_rt5350()) { + t = (t >> RT5350_SYSCFG0_CPUCLK_SHIFT) & + RT5350_SYSCFG0_CPUCLK_MASK; + switch (t) { + case RT5350_SYSCFG0_CPUCLK_360: + cpu_rate = 360000000; + sys_rate = cpu_rate / 3; + break; + case RT5350_SYSCFG0_CPUCLK_320: + cpu_rate = 320000000; + sys_rate = cpu_rate / 4; + break; + case RT5350_SYSCFG0_CPUCLK_300: + cpu_rate = 300000000; + sys_rate = cpu_rate / 3; + break; + default: + BUG(); + } + uart_rate = 40000000; + wdt_rate = sys_rate; + } else { + BUG(); + } + + ralink_clk_add("cpu", cpu_rate); + ralink_clk_add("10000b00.spi", sys_rate); + ralink_clk_add("10000100.timer", wdt_rate); + ralink_clk_add("10000500.uart", uart_rate); + ralink_clk_add("10000c00.uartlite", uart_rate); +} + +void __init ralink_of_remap(void) +{ + rt_sysc_membase = plat_of_remap_node("ralink,rt3050-sysc"); + rt_memc_membase = plat_of_remap_node("ralink,rt3050-memc"); + + if (!rt_sysc_membase || !rt_memc_membase) + panic("Failed to remap core resources"); +} + +void prom_soc_init(struct ralink_soc_info *soc_info) +{ + void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT305X_SYSC_BASE); + unsigned char *name; + u32 n0; + u32 n1; + u32 id; + + n0 = __raw_readl(sysc + SYSC_REG_CHIP_NAME0); + n1 = __raw_readl(sysc + SYSC_REG_CHIP_NAME1); + + if (n0 == RT3052_CHIP_NAME0 && n1 == RT3052_CHIP_NAME1) { + unsigned long icache_sets; + + icache_sets = (read_c0_config1() >> 22) & 7; + if (icache_sets == 1) { + rt305x_soc = RT305X_SOC_RT3050; + name = "RT3050"; + soc_info->compatible = "ralink,rt3050-soc"; + } else { + rt305x_soc = RT305X_SOC_RT3052; + name = "RT3052"; + soc_info->compatible = "ralink,rt3052-soc"; + } + } else if (n0 == RT3350_CHIP_NAME0 && n1 == RT3350_CHIP_NAME1) { + rt305x_soc = RT305X_SOC_RT3350; + name = "RT3350"; + soc_info->compatible = "ralink,rt3350-soc"; + } else if (n0 == RT3352_CHIP_NAME0 && n1 == RT3352_CHIP_NAME1) { + rt305x_soc = RT305X_SOC_RT3352; + name = "RT3352"; + soc_info->compatible = "ralink,rt3352-soc"; + } else if (n0 == RT5350_CHIP_NAME0 && n1 == RT5350_CHIP_NAME1) { + rt305x_soc = RT305X_SOC_RT5350; + name = "RT5350"; + soc_info->compatible = "ralink,rt5350-soc"; + } else { + panic("rt305x: unknown SoC, n0:%08x n1:%08x\n", n0, n1); + } + + id = __raw_readl(sysc + SYSC_REG_CHIP_ID); + + snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN, + "Ralink %s id:%u rev:%u", + name, + (id >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK, + (id & CHIP_ID_REV_MASK)); +} From 5644da4f635a30fc03b4f12d81b2197d716d9cef Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 22 Jan 2013 20:19:33 +0100 Subject: [PATCH 2949/4607] MIPS: ralink: adds rt305x devicetree This adds the devicetree file that describes the rt305x evaluation kit. Signed-off-by: John Crispin Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4898/ --- arch/mips/ralink/dts/rt3050.dtsi | 96 ++++++++++++++++++++++++++++ arch/mips/ralink/dts/rt3052_eval.dts | 52 +++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 arch/mips/ralink/dts/rt3050.dtsi create mode 100644 arch/mips/ralink/dts/rt3052_eval.dts diff --git a/arch/mips/ralink/dts/rt3050.dtsi b/arch/mips/ralink/dts/rt3050.dtsi new file mode 100644 index 000000000000..fd49daacbf08 --- /dev/null +++ b/arch/mips/ralink/dts/rt3050.dtsi @@ -0,0 +1,96 @@ +/ { + #address-cells = <1>; + #size-cells = <1>; + compatible = "ralink,rt3050-soc", "ralink,rt3052-soc"; + + cpus { + cpu@0 { + compatible = "mips,mips24KEc"; + }; + }; + + chosen { + bootargs = "console=ttyS0,57600 init=/init"; + }; + + palmbus@10000000 { + compatible = "palmbus"; + reg = <0x10000000 0x200000>; + ranges = <0x0 0x10000000 0x1FFFFF>; + + #address-cells = <1>; + #size-cells = <1>; + + sysc@0 { + compatible = "ralink,rt3052-sysc", "ralink,rt3050-sysc"; + reg = <0x0 0x100>; + }; + + timer@100 { + compatible = "ralink,rt3052-wdt", "ralink,rt2880-wdt"; + reg = <0x100 0x100>; + }; + + intc: intc@200 { + compatible = "ralink,rt3052-intc", "ralink,rt2880-intc"; + reg = <0x200 0x100>; + + interrupt-controller; + #interrupt-cells = <1>; + }; + + memc@300 { + compatible = "ralink,rt3052-memc", "ralink,rt3050-memc"; + reg = <0x300 0x100>; + }; + + gpio0: gpio@600 { + compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; + reg = <0x600 0x34>; + + gpio-controller; + #gpio-cells = <2>; + + ralink,ngpio = <24>; + ralink,regs = [ 00 04 08 0c + 20 24 28 2c + 30 34 ]; + }; + + gpio1: gpio@638 { + compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; + reg = <0x638 0x24>; + + gpio-controller; + #gpio-cells = <2>; + + ralink,ngpio = <16>; + ralink,regs = [ 00 04 08 0c + 10 14 18 1c + 20 24 ]; + }; + + gpio2: gpio@660 { + compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; + reg = <0x660 0x24>; + + gpio-controller; + #gpio-cells = <2>; + + ralink,ngpio = <12>; + ralink,regs = [ 00 04 08 0c + 10 14 18 1c + 20 24 ]; + }; + + uartlite@c00 { + compatible = "ralink,rt3052-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0xc00 0x100>; + + interrupt-parent = <&intc>; + interrupts = <12>; + + reg-shift = <2>; + }; + }; +}; diff --git a/arch/mips/ralink/dts/rt3052_eval.dts b/arch/mips/ralink/dts/rt3052_eval.dts new file mode 100644 index 000000000000..148a590bc419 --- /dev/null +++ b/arch/mips/ralink/dts/rt3052_eval.dts @@ -0,0 +1,52 @@ +/dts-v1/; + +/include/ "rt3050.dtsi" + +/ { + #address-cells = <1>; + #size-cells = <1>; + compatible = "ralink,rt3052-eval-board", "ralink,rt3052-soc"; + model = "Ralink RT3052 evaluation board"; + + memory@0 { + reg = <0x0 0x2000000>; + }; + + palmbus@10000000 { + sysc@0 { + ralink,pinmmux = "uartlite", "spi"; + ralink,uartmux = "gpio"; + ralink,wdtmux = <0>; + }; + }; + + cfi@1f000000 { + compatible = "cfi-flash"; + reg = <0x1f000000 0x800000>; + + bank-width = <2>; + device-width = <2>; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "uboot"; + reg = <0x0 0x30000>; + read-only; + }; + partition@30000 { + label = "uboot-env"; + reg = <0x30000 0x10000>; + read-only; + }; + partition@40000 { + label = "calibration"; + reg = <0x40000 0x10000>; + read-only; + }; + partition@50000 { + label = "linux"; + reg = <0x50000 0x7b0000>; + }; + }; +}; From ae2b5bb6570481b50a7175c64176b82da0a81836 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:05:30 +0100 Subject: [PATCH 2950/4607] MIPS: ralink: adds Kbuild files Add the Kbuild symbols and Makefiles needed to actually build the ralink code from this series Signed-off-by: John Crispin Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4899/ --- arch/mips/Kbuild.platforms | 1 + arch/mips/Kconfig | 17 +++++++++++++++++ arch/mips/ralink/Kconfig | 32 ++++++++++++++++++++++++++++++++ arch/mips/ralink/Makefile | 15 +++++++++++++++ arch/mips/ralink/Platform | 10 ++++++++++ arch/mips/ralink/dts/Makefile | 1 + 6 files changed, 76 insertions(+) create mode 100644 arch/mips/ralink/Kconfig create mode 100644 arch/mips/ralink/Makefile create mode 100644 arch/mips/ralink/Platform create mode 100644 arch/mips/ralink/dts/Makefile diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms index 91b9d69f465c..9a73ce6f4c58 100644 --- a/arch/mips/Kbuild.platforms +++ b/arch/mips/Kbuild.platforms @@ -22,6 +22,7 @@ platforms += pmc-sierra platforms += pnx833x platforms += pnx8550 platforms += powertv +platforms += ralink platforms += rb532 platforms += sgi-ip22 platforms += sgi-ip27 diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 8f8666c8f28d..79ad1d09c255 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -437,6 +437,22 @@ config POWERTV help This enables support for the Cisco PowerTV Platform. +config RALINK + bool "Ralink based machines" + select CEVT_R4K + select CSRC_R4K + select BOOT_RAW + select DMA_NONCOHERENT + select IRQ_CPU + select USE_OF + select SYS_HAS_CPU_MIPS32_R1 + select SYS_HAS_CPU_MIPS32_R2 + select SYS_SUPPORTS_32BIT_KERNEL + select SYS_SUPPORTS_LITTLE_ENDIAN + select SYS_HAS_EARLY_PRINTK + select HAVE_MACH_CLKDEV + select CLKDEV_LOOKUP + config SGI_IP22 bool "SGI IP22 (Indy/Indigo2)" select FW_ARC @@ -849,6 +865,7 @@ source "arch/mips/lantiq/Kconfig" source "arch/mips/lasat/Kconfig" source "arch/mips/pmc-sierra/Kconfig" source "arch/mips/powertv/Kconfig" +source "arch/mips/ralink/Kconfig" source "arch/mips/sgi-ip27/Kconfig" source "arch/mips/sibyte/Kconfig" source "arch/mips/txx9/Kconfig" diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig new file mode 100644 index 000000000000..a0b0197cab0a --- /dev/null +++ b/arch/mips/ralink/Kconfig @@ -0,0 +1,32 @@ +if RALINK + +choice + prompt "Ralink SoC selection" + default SOC_RT305X + help + Select Ralink MIPS SoC type. + + config SOC_RT305X + bool "RT305x" + select USB_ARCH_HAS_HCD + select USB_ARCH_HAS_OHCI + select USB_ARCH_HAS_EHCI + +endchoice + +choice + prompt "Devicetree selection" + default DTB_RT_NONE + help + Select the devicetree. + + config DTB_RT_NONE + bool "None" + + config DTB_RT305X_EVAL + bool "RT305x eval kit" + depends on SOC_RT305X + +endchoice + +endif diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile new file mode 100644 index 000000000000..939757f0e71f --- /dev/null +++ b/arch/mips/ralink/Makefile @@ -0,0 +1,15 @@ +# 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.# +# Makefile for the Ralink common stuff +# +# Copyright (C) 2009-2011 Gabor Juhos +# Copyright (C) 2013 John Crispin + +obj-y := prom.o of.o reset.o clk.o irq.o + +obj-$(CONFIG_SOC_RT305X) += rt305x.o + +obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + +obj-y += dts/ diff --git a/arch/mips/ralink/Platform b/arch/mips/ralink/Platform new file mode 100644 index 000000000000..6babd65765e6 --- /dev/null +++ b/arch/mips/ralink/Platform @@ -0,0 +1,10 @@ +# +# Ralink SoC common stuff +# +core-$(CONFIG_RALINK) += arch/mips/ralink/ +cflags-$(CONFIG_RALINK) += -I$(srctree)/arch/mips/include/asm/mach-ralink + +# +# Ralink RT305x +# +load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 diff --git a/arch/mips/ralink/dts/Makefile b/arch/mips/ralink/dts/Makefile new file mode 100644 index 000000000000..1a69fb300955 --- /dev/null +++ b/arch/mips/ralink/dts/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o From 6d63d70f9fe4c1b3d293ac3b9d2fcaf937d95cea Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 1 Feb 2013 12:50:49 +0100 Subject: [PATCH 2951/4607] MIPS: ralink: adds default config file Signed-off-by: John Crispin --- arch/mips/configs/rt305x_defconfig | 167 +++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 arch/mips/configs/rt305x_defconfig diff --git a/arch/mips/configs/rt305x_defconfig b/arch/mips/configs/rt305x_defconfig new file mode 100644 index 000000000000..d1741bcf8949 --- /dev/null +++ b/arch/mips/configs/rt305x_defconfig @@ -0,0 +1,167 @@ +CONFIG_RALINK=y +CONFIG_DTB_RT305X_EVAL=y +CONFIG_CPU_MIPS32_R2=y +# CONFIG_COMPACTION is not set +# CONFIG_CROSS_MEMORY_ATTACH is not set +CONFIG_HZ_100=y +# CONFIG_SECCOMP is not set +CONFIG_EXPERIMENTAL=y +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SYSVIPC=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_INITRAMFS_ROOT_UID=1000 +CONFIG_INITRAMFS_ROOT_GID=1000 +# CONFIG_RD_GZIP is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_AIO is not set +CONFIG_EMBEDDED=y +# CONFIG_VM_EVENT_COUNTERS is not set +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_IOSCHED_CFQ is not set +# CONFIG_COREDUMP is not set +# CONFIG_SUSPEND is not set +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_ARPD=y +CONFIG_SYN_COOKIES=y +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +CONFIG_TCP_CONG_ADVANCED=y +# CONFIG_TCP_CONG_BIC is not set +# CONFIG_TCP_CONG_WESTWOOD is not set +# CONFIG_TCP_CONG_HTCP is not set +# CONFIG_IPV6 is not set +CONFIG_NETFILTER=y +# CONFIG_BRIDGE_NETFILTER is not set +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NETFILTER_XT_TARGET_CT=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NF_CONNTRACK_IPV4=m +# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_RAW=m +CONFIG_BRIDGE=y +# CONFIG_BRIDGE_IGMP_SNOOPING is not set +CONFIG_VLAN_8021Q=y +CONFIG_NET_SCHED=y +CONFIG_HAMRADIO=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_CFI=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_COMPLEX_MAPPINGS=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_M25P80=y +CONFIG_EEPROM_93CX6=m +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_NETDEVICES=y +# CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_PHYLIB=y +CONFIG_PPP=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=m +CONFIG_PPP_ASYNC=m +CONFIG_ISDN=y +CONFIG_INPUT=m +CONFIG_INPUT_POLLDEV=m +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_MISC=y +# CONFIG_SERIO is not set +# CONFIG_VT is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_RUNTIME_UARTS=2 +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SPI=y +# CONFIG_HWMON is not set +CONFIG_WATCHDOG=y +# CONFIG_HID is not set +# CONFIG_USB_HID is not set +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DEBUG=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y +CONFIG_STAGING=y +# CONFIG_IOMMU_SUPPORT is not set +# CONFIG_DNOTIFY is not set +# CONFIG_PROC_PAGE_MONITOR is not set +CONFIG_TMPFS=y +CONFIG_TMPFS_XATTR=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_FS_XATTR=y +# CONFIG_JFFS2_FS_POSIX_ACL is not set +# CONFIG_JFFS2_FS_SECURITY is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +# CONFIG_JFFS2_ZLIB is not set +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_ZLIB is not set +CONFIG_SQUASHFS_XZ=y +CONFIG_PRINTK_TIME=y +# CONFIG_ENABLE_MUST_CHECK is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_STRIP_ASM_SYMS=y +CONFIG_DEBUG_FS=y +# CONFIG_SCHED_DEBUG is not set +# CONFIG_FTRACE is not set +CONFIG_CMDLINE_BOOL=y +CONFIG_CRYPTO_MANAGER=m +CONFIG_CRYPTO_ARC4=m +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRC_ITU_T=m +CONFIG_CRC32_SARWATE=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +# CONFIG_XZ_DEC_ARM is not set +# CONFIG_XZ_DEC_ARMTHUMB is not set +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_AVERAGE=y From dcc7310e144c3bf17a86d2f058d60fb525d4b34a Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 31 Jan 2013 13:44:10 +0100 Subject: [PATCH 2952/4607] Document: devicetree: add OF documents for MIPS interrupt controller Signed-off-by: John Crispin Acked-by: David Daney Patchwork: http://patchwork.linux-mips.org/patch/4901/ --- .../devicetree/bindings/mips/cpu_irq.txt | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/mips/cpu_irq.txt diff --git a/Documentation/devicetree/bindings/mips/cpu_irq.txt b/Documentation/devicetree/bindings/mips/cpu_irq.txt new file mode 100644 index 000000000000..13aa4b62c62a --- /dev/null +++ b/Documentation/devicetree/bindings/mips/cpu_irq.txt @@ -0,0 +1,47 @@ +MIPS CPU interrupt controller + +On MIPS the mips_cpu_intc_init() helper can be used to initialize the 8 CPU +IRQs from a devicetree file and create a irq_domain for IRQ controller. + +With the irq_domain in place we can describe how the 8 IRQs are wired to the +platforms internal interrupt controller cascade. + +Below is an example of a platform describing the cascade inside the devicetree +and the code used to load it inside arch_init_irq(). + +Required properties: +- compatible : Should be "mti,cpu-interrupt-controller" + +Example devicetree: + cpu-irq: cpu-irq@0 { + #address-cells = <0>; + + interrupt-controller; + #interrupt-cells = <1>; + + compatible = "mti,cpu-interrupt-controller"; + }; + + intc: intc@200 { + compatible = "ralink,rt2880-intc"; + reg = <0x200 0x100>; + + interrupt-controller; + #interrupt-cells = <1>; + + interrupt-parent = <&cpu-irq>; + interrupts = <2>; + }; + + +Example platform irq.c: +static struct of_device_id __initdata of_irq_ids[] = { + { .compatible = "mti,cpu-interrupt-controller", .data = mips_cpu_intc_init }, + { .compatible = "ralink,rt2880-intc", .data = intc_of_init }, + {}, +}; + +void __init arch_init_irq(void) +{ + of_irq_init(of_irq_ids); +} From 0916b46962cbcac9465d253d0a398435b3965fd5 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 31 Jan 2013 12:20:43 +0000 Subject: [PATCH 2953/4607] MIPS: add irqdomain support for the CPU IRQ controller Add code to load a irq_domain for the MIPS IRQ controller from a devicetree file. Signed-off-by: Gabor Juhos Signed-off-by: John Crispin Acked-by: David Daney Patchwork: http://patchwork.linux-mips.org/patch/4902/ --- arch/mips/include/asm/irq_cpu.h | 6 +++++ arch/mips/kernel/irq_cpu.c | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/arch/mips/include/asm/irq_cpu.h b/arch/mips/include/asm/irq_cpu.h index ef6a07cddb23..3f11fdb3ed8c 100644 --- a/arch/mips/include/asm/irq_cpu.h +++ b/arch/mips/include/asm/irq_cpu.h @@ -17,4 +17,10 @@ extern void mips_cpu_irq_init(void); extern void rm7k_cpu_irq_init(void); extern void rm9k_cpu_irq_init(void); +#ifdef CONFIG_IRQ_DOMAIN +struct device_node; +extern int mips_cpu_intc_init(struct device_node *of_node, + struct device_node *parent); +#endif + #endif /* _ASM_IRQ_CPU_H */ diff --git a/arch/mips/kernel/irq_cpu.c b/arch/mips/kernel/irq_cpu.c index 972263bcf403..49bc9caaddf8 100644 --- a/arch/mips/kernel/irq_cpu.c +++ b/arch/mips/kernel/irq_cpu.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -113,3 +114,44 @@ void __init mips_cpu_irq_init(void) irq_set_chip_and_handler(i, &mips_cpu_irq_controller, handle_percpu_irq); } + +#ifdef CONFIG_IRQ_DOMAIN +static int mips_cpu_intc_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + static struct irq_chip *chip; + + if (hw < 2 && cpu_has_mipsmt) { + /* Software interrupts are used for MT/CMT IPI */ + chip = &mips_mt_cpu_irq_controller; + } else { + chip = &mips_cpu_irq_controller; + } + + irq_set_chip_and_handler(irq, chip, handle_percpu_irq); + + return 0; +} + +static const struct irq_domain_ops mips_cpu_intc_irq_domain_ops = { + .map = mips_cpu_intc_map, + .xlate = irq_domain_xlate_onecell, +}; + +int __init mips_cpu_intc_init(struct device_node *of_node, + struct device_node *parent) +{ + struct irq_domain *domain; + + /* Mask interrupts. */ + clear_c0_status(ST0_IM); + clear_c0_cause(CAUSEF_IP); + + domain = irq_domain_add_legacy(of_node, 8, MIPS_CPU_IRQ_BASE, 0, + &mips_cpu_intc_irq_domain_ops, NULL); + if (!domain) + panic("Failed to add irqdomain for MIPS CPU\n"); + + return 0; +} +#endif /* CONFIG_IRQ_DOMAIN */ From d3d2b4200b5a42851365e903d101f8f0882eb9eb Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 31 Jan 2013 20:43:30 +0100 Subject: [PATCH 2954/4607] MIPS: ralink: add CPU interrupt controller to of_irq_ids Convert the ralink IRQ code to make use of the new MIPS IRQ controller OF mappings. Signed-off-by: Gabor Juhos Signed-off-by: John Crispin Acked-by: David Daney Patchwork: http://patchwork.linux-mips.org/patch/4900/ --- arch/mips/ralink/dts/rt3050.dtsi | 10 ++++++++++ arch/mips/ralink/irq.c | 10 +++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/arch/mips/ralink/dts/rt3050.dtsi b/arch/mips/ralink/dts/rt3050.dtsi index fd49daacbf08..069d0660e1dd 100644 --- a/arch/mips/ralink/dts/rt3050.dtsi +++ b/arch/mips/ralink/dts/rt3050.dtsi @@ -13,6 +13,13 @@ bootargs = "console=ttyS0,57600 init=/init"; }; + cpuintc: cpuintc@0 { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + compatible = "mti,cpu-interrupt-controller"; + }; + palmbus@10000000 { compatible = "palmbus"; reg = <0x10000000 0x200000>; @@ -37,6 +44,9 @@ interrupt-controller; #interrupt-cells = <1>; + + interrupt-parent = <&cpuintc>; + interrupts = <2>; }; memc@300 { diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c index e62c9751e2d8..6d054c5ec9ab 100644 --- a/arch/mips/ralink/irq.c +++ b/arch/mips/ralink/irq.c @@ -128,8 +128,11 @@ static int __init intc_of_init(struct device_node *node, { struct resource res; struct irq_domain *domain; + int irq; - mips_cpu_irq_init(); + irq = irq_of_parse_and_map(node, 0); + if (!irq) + panic("Failed to get INTC IRQ"); if (of_address_to_resource(node, 0, &res)) panic("Failed to get intc memory range"); @@ -156,8 +159,8 @@ static int __init intc_of_init(struct device_node *node, rt_intc_w32(INTC_INT_GLOBAL, INTC_REG_ENABLE); - irq_set_chained_handler(RALINK_CPU_IRQ_INTC, ralink_intc_irq_handler); - irq_set_handler_data(RALINK_CPU_IRQ_INTC, domain); + irq_set_chained_handler(irq, ralink_intc_irq_handler); + irq_set_handler_data(irq, domain); cp0_perfcount_irq = irq_create_mapping(domain, 9); @@ -165,6 +168,7 @@ static int __init intc_of_init(struct device_node *node, } static struct of_device_id __initdata of_irq_ids[] = { + { .compatible = "mti,cpu-interrupt-controller", .data = mips_cpu_intc_init }, { .compatible = "ralink,rt2880-intc", .data = intc_of_init }, {}, }; From 58d2e9bcd682d76bcb9575dc56c85f1d82a81bfa Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sat, 2 Feb 2013 11:40:42 +0000 Subject: [PATCH 2955/4607] MIPS: pci-ar724x: convert into a platform driver The patch converts the pci-ar724x driver into a platform driver. This makes it possible to register the PCI controller as a plain platform device. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4905/ Signed-off-by: John Crispin --- arch/mips/pci/pci-ar724x.c | 57 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/arch/mips/pci/pci-ar724x.c b/arch/mips/pci/pci-ar724x.c index c11c75be2d7e..e7aca88ba0c0 100644 --- a/arch/mips/pci/pci-ar724x.c +++ b/arch/mips/pci/pci-ar724x.c @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include #include @@ -262,7 +264,7 @@ static struct irq_chip ar724x_pci_irq_chip = { .irq_mask_ack = ar724x_pci_irq_mask, }; -static void __init ar724x_pci_irq_init(int irq) +static void ar724x_pci_irq_init(int irq) { void __iomem *base; int i; @@ -282,7 +284,7 @@ static void __init ar724x_pci_irq_init(int irq) irq_set_chained_handler(irq, ar724x_pci_irq_handler); } -int __init ar724x_pcibios_init(int irq) +int ar724x_pcibios_init(int irq) { int ret; @@ -312,3 +314,54 @@ err_unmap_devcfg: err: return ret; } + +static int ar724x_pci_probe(struct platform_device *pdev) +{ + struct resource *res; + int irq; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl_base"); + if (!res) + return -EINVAL; + + ar724x_pci_ctrl_base = devm_request_and_ioremap(&pdev->dev, res); + if (ar724x_pci_ctrl_base == NULL) + return -EBUSY; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base"); + if (!res) + return -EINVAL; + + ar724x_pci_devcfg_base = devm_request_and_ioremap(&pdev->dev, res); + if (!ar724x_pci_devcfg_base) + return -EBUSY; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EINVAL; + + ar724x_pci_link_up = ar724x_pci_check_link(); + if (!ar724x_pci_link_up) + dev_warn(&pdev->dev, "PCIe link is down\n"); + + ar724x_pci_irq_init(irq); + + register_pci_controller(&ar724x_pci_controller); + + return 0; +} + +static struct platform_driver ar724x_pci_driver = { + .probe = ar724x_pci_probe, + .driver = { + .name = "ar724x-pci", + .owner = THIS_MODULE, + }, +}; + +static int __init ar724x_pci_init(void) +{ + return platform_driver_register(&ar724x_pci_driver); +} + +postcore_initcall(ar724x_pci_init); From fb167e891d5cc6386840dd092af2d461b38eb802 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sat, 2 Feb 2013 11:40:43 +0000 Subject: [PATCH 2956/4607] MIPS: pci-ar71xx: convert into a platform driver The patch converts the pci-ar71xx driver into a platform driver. This makes it possible to register the PCI controller as a plain platform device. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4906/ Signed-off-by: John Crispin --- arch/mips/pci/pci-ar71xx.c | 60 +++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/arch/mips/pci/pci-ar71xx.c b/arch/mips/pci/pci-ar71xx.c index 6eaa4f2d0e38..0d8412fc5047 100644 --- a/arch/mips/pci/pci-ar71xx.c +++ b/arch/mips/pci/pci-ar71xx.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -309,7 +311,7 @@ static struct irq_chip ar71xx_pci_irq_chip = { .irq_mask_ack = ar71xx_pci_irq_mask, }; -static __init void ar71xx_pci_irq_init(void) +static void ar71xx_pci_irq_init(int irq) { void __iomem *base = ath79_reset_base; int i; @@ -324,10 +326,10 @@ static __init void ar71xx_pci_irq_init(void) irq_set_chip_and_handler(i, &ar71xx_pci_irq_chip, handle_level_irq); - irq_set_chained_handler(ATH79_CPU_IRQ_IP2, ar71xx_pci_irq_handler); + irq_set_chained_handler(irq, ar71xx_pci_irq_handler); } -static __init void ar71xx_pci_reset(void) +static void ar71xx_pci_reset(void) { void __iomem *ddr_base = ath79_ddr_base; @@ -367,9 +369,59 @@ __init int ar71xx_pcibios_init(void) /* clear bus errors */ ar71xx_pci_check_error(1); - ar71xx_pci_irq_init(); + ar71xx_pci_irq_init(ATH79_CPU_IRQ_IP2); register_pci_controller(&ar71xx_pci_controller); return 0; } + +static int ar71xx_pci_probe(struct platform_device *pdev) +{ + struct resource *res; + int irq; + u32 t; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base"); + if (!res) + return -EINVAL; + + ar71xx_pcicfg_base = devm_request_and_ioremap(&pdev->dev, res); + if (!ar71xx_pcicfg_base) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EINVAL; + + ar71xx_pci_reset(); + + /* setup COMMAND register */ + t = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE + | PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK; + ar71xx_pci_local_write(PCI_COMMAND, 4, t); + + /* clear bus errors */ + ar71xx_pci_check_error(1); + + ar71xx_pci_irq_init(irq); + + register_pci_controller(&ar71xx_pci_controller); + + return 0; +} + +static struct platform_driver ar71xx_pci_driver = { + .probe = ar71xx_pci_probe, + .driver = { + .name = "ar71xx-pci", + .owner = THIS_MODULE, + }, +}; + +static int __init ar71xx_pci_init(void) +{ + return platform_driver_register(&ar71xx_pci_driver); +} + +postcore_initcall(ar71xx_pci_init); From ad4ce92e919f7ad5561a2060deb58899de58b40c Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 4 Feb 2013 11:56:53 +0100 Subject: [PATCH 2957/4607] MIPS: ath79: move global PCI defines into a common header The constants will be used by a subsequent patch. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4907/ Signed-off-by: John Crispin --- .../mips/include/asm/mach-ath79/ar71xx_regs.h | 24 +++++++++++++++++++ arch/mips/pci/pci-ar71xx.c | 16 ------------- arch/mips/pci/pci-ar724x.c | 8 ------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index 7d44b5d5f609..7c87bfe6e4ff 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -41,11 +41,35 @@ #define AR71XX_RESET_BASE (AR71XX_APB_BASE + 0x00060000) #define AR71XX_RESET_SIZE 0x100 +#define AR71XX_PCI_MEM_BASE 0x10000000 +#define AR71XX_PCI_MEM_SIZE 0x07000000 + +#define AR71XX_PCI_WIN0_OFFS 0x10000000 +#define AR71XX_PCI_WIN1_OFFS 0x11000000 +#define AR71XX_PCI_WIN2_OFFS 0x12000000 +#define AR71XX_PCI_WIN3_OFFS 0x13000000 +#define AR71XX_PCI_WIN4_OFFS 0x14000000 +#define AR71XX_PCI_WIN5_OFFS 0x15000000 +#define AR71XX_PCI_WIN6_OFFS 0x16000000 +#define AR71XX_PCI_WIN7_OFFS 0x07000000 + +#define AR71XX_PCI_CFG_BASE \ + (AR71XX_PCI_MEM_BASE + AR71XX_PCI_WIN7_OFFS + 0x10000) +#define AR71XX_PCI_CFG_SIZE 0x100 + #define AR7240_USB_CTRL_BASE (AR71XX_APB_BASE + 0x00030000) #define AR7240_USB_CTRL_SIZE 0x100 #define AR7240_OHCI_BASE 0x1b000000 #define AR7240_OHCI_SIZE 0x1000 +#define AR724X_PCI_MEM_BASE 0x10000000 +#define AR724X_PCI_MEM_SIZE 0x04000000 + +#define AR724X_PCI_CFG_BASE 0x14000000 +#define AR724X_PCI_CFG_SIZE 0x1000 +#define AR724X_PCI_CTRL_BASE (AR71XX_APB_BASE + 0x000f0000) +#define AR724X_PCI_CTRL_SIZE 0x100 + #define AR724X_EHCI_BASE 0x1b000000 #define AR724X_EHCI_SIZE 0x1000 diff --git a/arch/mips/pci/pci-ar71xx.c b/arch/mips/pci/pci-ar71xx.c index 0d8412fc5047..35ee23450d87 100644 --- a/arch/mips/pci/pci-ar71xx.c +++ b/arch/mips/pci/pci-ar71xx.c @@ -25,22 +25,6 @@ #include #include -#define AR71XX_PCI_MEM_BASE 0x10000000 -#define AR71XX_PCI_MEM_SIZE 0x07000000 - -#define AR71XX_PCI_WIN0_OFFS 0x10000000 -#define AR71XX_PCI_WIN1_OFFS 0x11000000 -#define AR71XX_PCI_WIN2_OFFS 0x12000000 -#define AR71XX_PCI_WIN3_OFFS 0x13000000 -#define AR71XX_PCI_WIN4_OFFS 0x14000000 -#define AR71XX_PCI_WIN5_OFFS 0x15000000 -#define AR71XX_PCI_WIN6_OFFS 0x16000000 -#define AR71XX_PCI_WIN7_OFFS 0x07000000 - -#define AR71XX_PCI_CFG_BASE \ - (AR71XX_PCI_MEM_BASE + AR71XX_PCI_WIN7_OFFS + 0x10000) -#define AR71XX_PCI_CFG_SIZE 0x100 - #define AR71XX_PCI_REG_CRP_AD_CBE 0x00 #define AR71XX_PCI_REG_CRP_WRDATA 0x04 #define AR71XX_PCI_REG_CRP_RDDATA 0x08 diff --git a/arch/mips/pci/pci-ar724x.c b/arch/mips/pci/pci-ar724x.c index e7aca88ba0c0..b3f9d093c066 100644 --- a/arch/mips/pci/pci-ar724x.c +++ b/arch/mips/pci/pci-ar724x.c @@ -17,14 +17,6 @@ #include #include -#define AR724X_PCI_CFG_BASE 0x14000000 -#define AR724X_PCI_CFG_SIZE 0x1000 -#define AR724X_PCI_CTRL_BASE (AR71XX_APB_BASE + 0x000f0000) -#define AR724X_PCI_CTRL_SIZE 0x100 - -#define AR724X_PCI_MEM_BASE 0x10000000 -#define AR724X_PCI_MEM_SIZE 0x04000000 - #define AR724X_PCI_REG_RESET 0x18 #define AR724X_PCI_REG_INT_STATUS 0x4c #define AR724X_PCI_REG_INT_MASK 0x50 From 9fc1ca5b73a82daedffa2d1d5daa48dd2093c39a Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sat, 2 Feb 2013 11:44:24 +0000 Subject: [PATCH 2958/4607] MIPS: ath79: register platform devices for the PCI controllers The pci-ar71xx and pci-ar724x drivers were converted into platform drivers. Register the corresponding platform devices for the PCI controllers instead of using the ar7{1x,24}x_pcibios_init functions. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4908/ Signed-off-by: John Crispin --- arch/mips/ath79/pci.c | 87 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 9 deletions(-) diff --git a/arch/mips/ath79/pci.c b/arch/mips/ath79/pci.c index ca83abd9d31e..81ef5797944a 100644 --- a/arch/mips/ath79/pci.c +++ b/arch/mips/ath79/pci.c @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include #include @@ -110,21 +112,88 @@ void __init ath79_pci_set_plat_dev_init(int (*func)(struct pci_dev *dev)) ath79_pci_plat_dev_init = func; } +static struct platform_device * +ath79_register_pci_ar71xx(void) +{ + struct platform_device *pdev; + struct resource res[2]; + + memset(res, 0, sizeof(res)); + + res[0].name = "cfg_base"; + res[0].flags = IORESOURCE_MEM; + res[0].start = AR71XX_PCI_CFG_BASE; + res[0].end = AR71XX_PCI_CFG_BASE + AR71XX_PCI_CFG_SIZE - 1; + + res[1].flags = IORESOURCE_IRQ; + res[1].start = ATH79_CPU_IRQ_IP2; + res[1].end = ATH79_CPU_IRQ_IP2; + + pdev = platform_device_register_simple("ar71xx-pci", -1, + res, ARRAY_SIZE(res)); + return pdev; +} + +static struct platform_device * +ath79_register_pci_ar724x(int id, + unsigned long cfg_base, + unsigned long ctrl_base, + int irq) +{ + struct platform_device *pdev; + struct resource res[3]; + + memset(res, 0, sizeof(res)); + + res[0].name = "cfg_base"; + res[0].flags = IORESOURCE_MEM; + res[0].start = cfg_base; + res[0].end = cfg_base + AR724X_PCI_CFG_SIZE - 1; + + res[1].name = "ctrl_base"; + res[1].flags = IORESOURCE_MEM; + res[1].start = ctrl_base; + res[1].end = ctrl_base + AR724X_PCI_CTRL_SIZE - 1; + + res[2].flags = IORESOURCE_IRQ; + res[2].start = irq; + res[2].end = irq; + + pdev = platform_device_register_simple("ar724x-pci", id, + res, ARRAY_SIZE(res)); + return pdev; +} + int __init ath79_register_pci(void) { - if (soc_is_ar71xx()) - return ar71xx_pcibios_init(); + struct platform_device *pdev = NULL; - if (soc_is_ar724x()) - return ar724x_pcibios_init(ATH79_CPU_IRQ_IP2); - - if (soc_is_ar9342() || soc_is_ar9344()) { + if (soc_is_ar71xx()) { + pdev = ath79_register_pci_ar71xx(); + } else if (soc_is_ar724x()) { + pdev = ath79_register_pci_ar724x(-1, + AR724X_PCI_CFG_BASE, + AR724X_PCI_CTRL_BASE, + ATH79_CPU_IRQ_IP2); + } else if (soc_is_ar9342() || + soc_is_ar9344()) { u32 bootstrap; bootstrap = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP); - if (bootstrap & AR934X_BOOTSTRAP_PCIE_RC) - return ar724x_pcibios_init(ATH79_IP2_IRQ(0)); + if ((bootstrap & AR934X_BOOTSTRAP_PCIE_RC) == 0) + return -ENODEV; + + pdev = ath79_register_pci_ar724x(-1, + AR724X_PCI_CFG_BASE, + AR724X_PCI_CTRL_BASE, + ATH79_IP2_IRQ(0)); + } else { + /* No PCI support */ + return -ENODEV; } - return -ENODEV; + if (!pdev) + pr_err("unable to register PCI controller device\n"); + + return pdev ? 0 : -ENODEV; } From 6e783865b4e60f2ecf7708f8ea24db5c5ea07ced Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 4 Feb 2013 11:58:49 +0100 Subject: [PATCH 2959/4607] MIPS: ath79: remove unused ar7{1x,24}x_pcibios_init functions The functions are unused now, so remove them. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4909/ Signed-off-by: John Crispin --- arch/mips/ath79/pci.c | 1 - arch/mips/include/asm/mach-ath79/pci.h | 28 ---------------------- arch/mips/pci/pci-ar71xx.c | 26 --------------------- arch/mips/pci/pci-ar724x.c | 32 -------------------------- 4 files changed, 87 deletions(-) delete mode 100644 arch/mips/include/asm/mach-ath79/pci.h diff --git a/arch/mips/ath79/pci.c b/arch/mips/ath79/pci.c index 81ef5797944a..c94bcec169ab 100644 --- a/arch/mips/ath79/pci.c +++ b/arch/mips/ath79/pci.c @@ -19,7 +19,6 @@ #include #include #include -#include #include "pci.h" static int (*ath79_pci_plat_dev_init)(struct pci_dev *dev); diff --git a/arch/mips/include/asm/mach-ath79/pci.h b/arch/mips/include/asm/mach-ath79/pci.h deleted file mode 100644 index 7868f7fa028f..000000000000 --- a/arch/mips/include/asm/mach-ath79/pci.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Atheros AR71XX/AR724X PCI support - * - * Copyright (C) 2011 René Bolldorf - * Copyright (C) 2008-2011 Gabor Juhos - * Copyright (C) 2008 Imre Kaloz - * - * 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_MACH_ATH79_PCI_H -#define __ASM_MACH_ATH79_PCI_H - -#if defined(CONFIG_PCI) && defined(CONFIG_SOC_AR71XX) -int ar71xx_pcibios_init(void); -#else -static inline int ar71xx_pcibios_init(void) { return 0; } -#endif - -#if defined(CONFIG_PCI_AR724X) -int ar724x_pcibios_init(int irq); -#else -static inline int ar724x_pcibios_init(int irq) { return 0; } -#endif - -#endif /* __ASM_MACH_ATH79_PCI_H */ diff --git a/arch/mips/pci/pci-ar71xx.c b/arch/mips/pci/pci-ar71xx.c index 35ee23450d87..69e0bb47de08 100644 --- a/arch/mips/pci/pci-ar71xx.c +++ b/arch/mips/pci/pci-ar71xx.c @@ -23,7 +23,6 @@ #include #include -#include #define AR71XX_PCI_REG_CRP_AD_CBE 0x00 #define AR71XX_PCI_REG_CRP_WRDATA 0x04 @@ -335,31 +334,6 @@ static void ar71xx_pci_reset(void) mdelay(100); } -__init int ar71xx_pcibios_init(void) -{ - u32 t; - - ar71xx_pcicfg_base = ioremap(AR71XX_PCI_CFG_BASE, AR71XX_PCI_CFG_SIZE); - if (ar71xx_pcicfg_base == NULL) - return -ENOMEM; - - ar71xx_pci_reset(); - - /* setup COMMAND register */ - t = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE - | PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK; - ar71xx_pci_local_write(PCI_COMMAND, 4, t); - - /* clear bus errors */ - ar71xx_pci_check_error(1); - - ar71xx_pci_irq_init(ATH79_CPU_IRQ_IP2); - - register_pci_controller(&ar71xx_pci_controller); - - return 0; -} - static int ar71xx_pci_probe(struct platform_device *pdev) { struct resource *res; diff --git a/arch/mips/pci/pci-ar724x.c b/arch/mips/pci/pci-ar724x.c index b3f9d093c066..8f008d9a112c 100644 --- a/arch/mips/pci/pci-ar724x.c +++ b/arch/mips/pci/pci-ar724x.c @@ -15,7 +15,6 @@ #include #include #include -#include #define AR724X_PCI_REG_RESET 0x18 #define AR724X_PCI_REG_INT_STATUS 0x4c @@ -276,37 +275,6 @@ static void ar724x_pci_irq_init(int irq) irq_set_chained_handler(irq, ar724x_pci_irq_handler); } -int ar724x_pcibios_init(int irq) -{ - int ret; - - ret = -ENOMEM; - - ar724x_pci_devcfg_base = ioremap(AR724X_PCI_CFG_BASE, - AR724X_PCI_CFG_SIZE); - if (ar724x_pci_devcfg_base == NULL) - goto err; - - ar724x_pci_ctrl_base = ioremap(AR724X_PCI_CTRL_BASE, - AR724X_PCI_CTRL_SIZE); - if (ar724x_pci_ctrl_base == NULL) - goto err_unmap_devcfg; - - ar724x_pci_link_up = ar724x_pci_check_link(); - if (!ar724x_pci_link_up) - pr_warn("ar724x: PCIe link is down\n"); - - ar724x_pci_irq_init(irq); - register_pci_controller(&ar724x_pci_controller); - - return PCIBIOS_SUCCESSFUL; - -err_unmap_devcfg: - iounmap(ar724x_pci_devcfg_base); -err: - return ret; -} - static int ar724x_pci_probe(struct platform_device *pdev) { struct resource *res; From 222831787704c9ad9215f6b56f975b233968607c Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sat, 2 Feb 2013 13:18:54 +0000 Subject: [PATCH 2960/4607] MIPS: avoid possible resource conflict in register_pci_controller The IO and memory resources of a PCI controller might already have a parent resource set when they are passed to 'register_pci_controller'. If the parent resource is set, the request_resource call will fail due to resource conflict and the current code will not be able to register the PCI controller. Use the parent resource if it is available in the request_resource call to avoid the isssue. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4910/ Signed-off-by: John Crispin --- arch/mips/pci/pci.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index a1843448fad3..eb653994a2f1 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -175,9 +175,20 @@ static DEFINE_MUTEX(pci_scan_mutex); void register_pci_controller(struct pci_controller *hose) { - if (request_resource(&iomem_resource, hose->mem_resource) < 0) + struct resource *parent; + + parent = hose->mem_resource->parent; + if (!parent) + parent = &iomem_resource; + + if (request_resource(parent, hose->mem_resource) < 0) goto out; - if (request_resource(&ioport_resource, hose->io_resource) < 0) { + + parent = hose->io_resource->parent; + if (!parent) + parent = &ioport_resource; + + if (request_resource(parent, hose->io_resource) < 0) { release_resource(hose->mem_resource); goto out; } From 15b6dcba427d70e61667c28b45e3f090ff00acb1 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sat, 2 Feb 2013 13:36:48 +0000 Subject: [PATCH 2961/4607] MIPS: add dummy pci_load_of_ranges The pci_load_of_ranges function is only available if CONFIG_OF is selected. If the function is used without CONFIG_OF being enabled it will cause a build error. Add a dummy inline function to avoid this. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4911/ Signed-off-by: John Crispin --- arch/mips/include/asm/pci.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/mips/include/asm/pci.h b/arch/mips/include/asm/pci.h index d69ea743272b..630645ea5d92 100644 --- a/arch/mips/include/asm/pci.h +++ b/arch/mips/include/asm/pci.h @@ -144,8 +144,13 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) extern char * (*pcibios_plat_setup)(char *str); +#ifdef CONFIG_OF /* this function parses memory ranges from a device node */ extern void pci_load_of_ranges(struct pci_controller *hose, struct device_node *node); +#else +static inline void pci_load_of_ranges(struct pci_controller *hose, + struct device_node *node) {} +#endif #endif /* _ASM_PCI_H */ From 617fed41e98417f3ea3e9974be251e125c8796f2 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sun, 3 Feb 2013 09:58:37 +0000 Subject: [PATCH 2962/4607] MIPS: ath79: allow to specify bus number in PCI IRQ maps This is needed for multiple PCI bus support. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4913/ Signed-off-by: John Crispin --- arch/mips/ath79/pci.c | 4 +++- arch/mips/ath79/pci.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/mips/ath79/pci.c b/arch/mips/ath79/pci.c index c94bcec169ab..d90e07136383 100644 --- a/arch/mips/ath79/pci.c +++ b/arch/mips/ath79/pci.c @@ -75,7 +75,9 @@ int __init pcibios_map_irq(const struct pci_dev *dev, uint8_t slot, uint8_t pin) const struct ath79_pci_irq *entry; entry = &ath79_pci_irq_map[i]; - if (entry->slot == slot && entry->pin == pin) { + if (entry->bus == dev->bus->number && + entry->slot == slot && + entry->pin == pin) { irq = entry->irq; break; } diff --git a/arch/mips/ath79/pci.h b/arch/mips/ath79/pci.h index 51c6625dcc6d..1d00a3803c37 100644 --- a/arch/mips/ath79/pci.h +++ b/arch/mips/ath79/pci.h @@ -14,6 +14,7 @@ #define _ATH79_PCI_H struct ath79_pci_irq { + int bus; u8 slot; u8 pin; int irq; From 908339ef25b1d5e80f1c6fab22b9958174708b4a Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sun, 3 Feb 2013 09:58:38 +0000 Subject: [PATCH 2963/4607] MIPS: pci-ar724x: use dynamically allocated PCI controller structure The current code uses static variables to store the PCI controller specific data. This works if the system contains one PCI controller only, however it becomes impractical when multiple PCI controllers are present. Move the variables into a dynamically allocated controller specific structure, and use that instead of the static variables. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4912/ Signed-off-by: John Crispin --- arch/mips/pci/pci-ar724x.c | 129 +++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 47 deletions(-) diff --git a/arch/mips/pci/pci-ar724x.c b/arch/mips/pci/pci-ar724x.c index 8f008d9a112c..93ab8778ce10 100644 --- a/arch/mips/pci/pci-ar724x.c +++ b/arch/mips/pci/pci-ar724x.c @@ -9,6 +9,7 @@ * by the Free Software Foundation. */ +#include #include #include #include @@ -28,38 +29,56 @@ #define AR7240_BAR0_WAR_VALUE 0xffff -static DEFINE_SPINLOCK(ar724x_pci_lock); -static void __iomem *ar724x_pci_devcfg_base; -static void __iomem *ar724x_pci_ctrl_base; +struct ar724x_pci_controller { + void __iomem *devcfg_base; + void __iomem *ctrl_base; -static u32 ar724x_pci_bar0_value; -static bool ar724x_pci_bar0_is_cached; -static bool ar724x_pci_link_up; + int irq; -static inline bool ar724x_pci_check_link(void) + bool link_up; + bool bar0_is_cached; + u32 bar0_value; + + spinlock_t lock; + + struct pci_controller pci_controller; +}; + +static inline bool ar724x_pci_check_link(struct ar724x_pci_controller *apc) { u32 reset; - reset = __raw_readl(ar724x_pci_ctrl_base + AR724X_PCI_REG_RESET); + reset = __raw_readl(apc->ctrl_base + AR724X_PCI_REG_RESET); return reset & AR724X_PCI_RESET_LINK_UP; } +static inline struct ar724x_pci_controller * +pci_bus_to_ar724x_controller(struct pci_bus *bus) +{ + struct pci_controller *hose; + + hose = (struct pci_controller *) bus->sysdata; + return container_of(hose, struct ar724x_pci_controller, pci_controller); +} + static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, uint32_t *value) { + struct ar724x_pci_controller *apc; unsigned long flags; void __iomem *base; u32 data; - if (!ar724x_pci_link_up) + apc = pci_bus_to_ar724x_controller(bus); + if (!apc->link_up) return PCIBIOS_DEVICE_NOT_FOUND; if (devfn) return PCIBIOS_DEVICE_NOT_FOUND; - base = ar724x_pci_devcfg_base; + base = apc->devcfg_base; - spin_lock_irqsave(&ar724x_pci_lock, flags); + spin_lock_irqsave(&apc->lock, flags); data = __raw_readl(base + (where & ~3)); switch (size) { @@ -78,17 +97,17 @@ static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where, case 4: break; default: - spin_unlock_irqrestore(&ar724x_pci_lock, flags); + spin_unlock_irqrestore(&apc->lock, flags); return PCIBIOS_BAD_REGISTER_NUMBER; } - spin_unlock_irqrestore(&ar724x_pci_lock, flags); + spin_unlock_irqrestore(&apc->lock, flags); if (where == PCI_BASE_ADDRESS_0 && size == 4 && - ar724x_pci_bar0_is_cached) { + apc->bar0_is_cached) { /* use the cached value */ - *value = ar724x_pci_bar0_value; + *value = apc->bar0_value; } else { *value = data; } @@ -99,12 +118,14 @@ static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where, static int ar724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, uint32_t value) { + struct ar724x_pci_controller *apc; unsigned long flags; void __iomem *base; u32 data; int s; - if (!ar724x_pci_link_up) + apc = pci_bus_to_ar724x_controller(bus); + if (!apc->link_up) return PCIBIOS_DEVICE_NOT_FOUND; if (devfn) @@ -122,18 +143,18 @@ static int ar724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where, * BAR0 register in order to make the device memory * accessible. */ - ar724x_pci_bar0_is_cached = true; - ar724x_pci_bar0_value = value; + apc->bar0_is_cached = true; + apc->bar0_value = value; value = AR7240_BAR0_WAR_VALUE; } else { - ar724x_pci_bar0_is_cached = false; + apc->bar0_is_cached = false; } } - base = ar724x_pci_devcfg_base; + base = apc->devcfg_base; - spin_lock_irqsave(&ar724x_pci_lock, flags); + spin_lock_irqsave(&apc->lock, flags); data = __raw_readl(base + (where & ~3)); switch (size) { @@ -151,7 +172,7 @@ static int ar724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where, data = value; break; default: - spin_unlock_irqrestore(&ar724x_pci_lock, flags); + spin_unlock_irqrestore(&apc->lock, flags); return PCIBIOS_BAD_REGISTER_NUMBER; } @@ -159,7 +180,7 @@ static int ar724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where, __raw_writel(data, base + (where & ~3)); /* flush write */ __raw_readl(base + (where & ~3)); - spin_unlock_irqrestore(&ar724x_pci_lock, flags); + spin_unlock_irqrestore(&apc->lock, flags); return PCIBIOS_SUCCESSFUL; } @@ -183,18 +204,14 @@ static struct resource ar724x_mem_resource = { .flags = IORESOURCE_MEM, }; -static struct pci_controller ar724x_pci_controller = { - .pci_ops = &ar724x_pci_ops, - .io_resource = &ar724x_io_resource, - .mem_resource = &ar724x_mem_resource, -}; - static void ar724x_pci_irq_handler(unsigned int irq, struct irq_desc *desc) { + struct ar724x_pci_controller *apc; void __iomem *base; u32 pending; - base = ar724x_pci_ctrl_base; + apc = irq_get_handler_data(irq); + base = apc->ctrl_base; pending = __raw_readl(base + AR724X_PCI_REG_INT_STATUS) & __raw_readl(base + AR724X_PCI_REG_INT_MASK); @@ -208,10 +225,12 @@ static void ar724x_pci_irq_handler(unsigned int irq, struct irq_desc *desc) static void ar724x_pci_irq_unmask(struct irq_data *d) { + struct ar724x_pci_controller *apc; void __iomem *base; u32 t; - base = ar724x_pci_ctrl_base; + apc = irq_data_get_irq_chip_data(d); + base = apc->ctrl_base; switch (d->irq) { case ATH79_PCI_IRQ(0): @@ -225,10 +244,12 @@ static void ar724x_pci_irq_unmask(struct irq_data *d) static void ar724x_pci_irq_mask(struct irq_data *d) { + struct ar724x_pci_controller *apc; void __iomem *base; u32 t; - base = ar724x_pci_ctrl_base; + apc = irq_data_get_irq_chip_data(d); + base = apc->ctrl_base; switch (d->irq) { case ATH79_PCI_IRQ(0): @@ -255,12 +276,12 @@ static struct irq_chip ar724x_pci_irq_chip = { .irq_mask_ack = ar724x_pci_irq_mask, }; -static void ar724x_pci_irq_init(int irq) +static void ar724x_pci_irq_init(struct ar724x_pci_controller *apc) { void __iomem *base; int i; - base = ar724x_pci_ctrl_base; + base = apc->ctrl_base; __raw_writel(0, base + AR724X_PCI_REG_INT_MASK); __raw_writel(0, base + AR724X_PCI_REG_INT_STATUS); @@ -268,45 +289,59 @@ static void ar724x_pci_irq_init(int irq) BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR724X_PCI_IRQ_COUNT); for (i = ATH79_PCI_IRQ_BASE; - i < ATH79_PCI_IRQ_BASE + AR724X_PCI_IRQ_COUNT; i++) + i < ATH79_PCI_IRQ_BASE + AR724X_PCI_IRQ_COUNT; i++) { irq_set_chip_and_handler(i, &ar724x_pci_irq_chip, handle_level_irq); + irq_set_chip_data(i, apc); + } - irq_set_chained_handler(irq, ar724x_pci_irq_handler); + irq_set_handler_data(apc->irq, apc); + irq_set_chained_handler(apc->irq, ar724x_pci_irq_handler); } static int ar724x_pci_probe(struct platform_device *pdev) { + struct ar724x_pci_controller *apc; struct resource *res; - int irq; + + apc = devm_kzalloc(&pdev->dev, sizeof(struct ar724x_pci_controller), + GFP_KERNEL); + if (!apc) + return -ENOMEM; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl_base"); if (!res) return -EINVAL; - ar724x_pci_ctrl_base = devm_request_and_ioremap(&pdev->dev, res); - if (ar724x_pci_ctrl_base == NULL) + apc->ctrl_base = devm_request_and_ioremap(&pdev->dev, res); + if (apc->ctrl_base == NULL) return -EBUSY; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base"); if (!res) return -EINVAL; - ar724x_pci_devcfg_base = devm_request_and_ioremap(&pdev->dev, res); - if (!ar724x_pci_devcfg_base) + apc->devcfg_base = devm_request_and_ioremap(&pdev->dev, res); + if (!apc->devcfg_base) return -EBUSY; - irq = platform_get_irq(pdev, 0); - if (irq < 0) + apc->irq = platform_get_irq(pdev, 0); + if (apc->irq < 0) return -EINVAL; - ar724x_pci_link_up = ar724x_pci_check_link(); - if (!ar724x_pci_link_up) + spin_lock_init(&apc->lock); + + apc->pci_controller.pci_ops = &ar724x_pci_ops; + apc->pci_controller.io_resource = &ar724x_io_resource; + apc->pci_controller.mem_resource = &ar724x_mem_resource; + + apc->link_up = ar724x_pci_check_link(apc); + if (!apc->link_up) dev_warn(&pdev->dev, "PCIe link is down\n"); - ar724x_pci_irq_init(irq); + ar724x_pci_irq_init(apc); - register_pci_controller(&ar724x_pci_controller); + register_pci_controller(&apc->pci_controller); return 0; } From 34b134aebda89888b6985b7a3139e9cbdf209236 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sun, 3 Feb 2013 09:59:45 +0000 Subject: [PATCH 2964/4607] MIPS: pci-ar724x: remove static PCI IO/MEM resources Static resources become impractical when multiple PCI controllers are present. Move the resources into the platform device registration code and change the probe routine to get those from there platform device's resources. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4914/ Signed-off-by: John Crispin --- arch/mips/ath79/pci.c | 21 +++++++++++++++++++- arch/mips/pci/pci-ar724x.c | 40 +++++++++++++++++++++++--------------- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/arch/mips/ath79/pci.c b/arch/mips/ath79/pci.c index d90e07136383..45d1112de50d 100644 --- a/arch/mips/ath79/pci.c +++ b/arch/mips/ath79/pci.c @@ -139,10 +139,13 @@ static struct platform_device * ath79_register_pci_ar724x(int id, unsigned long cfg_base, unsigned long ctrl_base, + unsigned long mem_base, + unsigned long mem_size, + unsigned long io_base, int irq) { struct platform_device *pdev; - struct resource res[3]; + struct resource res[5]; memset(res, 0, sizeof(res)); @@ -160,6 +163,16 @@ ath79_register_pci_ar724x(int id, res[2].start = irq; res[2].end = irq; + res[3].name = "mem_base"; + res[3].flags = IORESOURCE_MEM; + res[3].start = mem_base; + res[3].end = mem_base + mem_size - 1; + + res[4].name = "io_base"; + res[4].flags = IORESOURCE_IO; + res[4].start = io_base; + res[4].end = io_base; + pdev = platform_device_register_simple("ar724x-pci", id, res, ARRAY_SIZE(res)); return pdev; @@ -175,6 +188,9 @@ int __init ath79_register_pci(void) pdev = ath79_register_pci_ar724x(-1, AR724X_PCI_CFG_BASE, AR724X_PCI_CTRL_BASE, + AR724X_PCI_MEM_BASE, + AR724X_PCI_MEM_SIZE, + 0, ATH79_CPU_IRQ_IP2); } else if (soc_is_ar9342() || soc_is_ar9344()) { @@ -187,6 +203,9 @@ int __init ath79_register_pci(void) pdev = ath79_register_pci_ar724x(-1, AR724X_PCI_CFG_BASE, AR724X_PCI_CTRL_BASE, + AR724X_PCI_MEM_BASE, + AR724X_PCI_MEM_SIZE, + 0, ATH79_IP2_IRQ(0)); } else { /* No PCI support */ diff --git a/arch/mips/pci/pci-ar724x.c b/arch/mips/pci/pci-ar724x.c index 93ab8778ce10..d0d707de6c6c 100644 --- a/arch/mips/pci/pci-ar724x.c +++ b/arch/mips/pci/pci-ar724x.c @@ -42,6 +42,8 @@ struct ar724x_pci_controller { spinlock_t lock; struct pci_controller pci_controller; + struct resource io_res; + struct resource mem_res; }; static inline bool ar724x_pci_check_link(struct ar724x_pci_controller *apc) @@ -190,20 +192,6 @@ static struct pci_ops ar724x_pci_ops = { .write = ar724x_pci_write, }; -static struct resource ar724x_io_resource = { - .name = "PCI IO space", - .start = 0, - .end = 0, - .flags = IORESOURCE_IO, -}; - -static struct resource ar724x_mem_resource = { - .name = "PCI memory space", - .start = AR724X_PCI_MEM_BASE, - .end = AR724X_PCI_MEM_BASE + AR724X_PCI_MEM_SIZE - 1, - .flags = IORESOURCE_MEM, -}; - static void ar724x_pci_irq_handler(unsigned int irq, struct irq_desc *desc) { struct ar724x_pci_controller *apc; @@ -331,9 +319,29 @@ static int ar724x_pci_probe(struct platform_device *pdev) spin_lock_init(&apc->lock); + res = platform_get_resource_byname(pdev, IORESOURCE_IO, "io_base"); + if (!res) + return -EINVAL; + + apc->io_res.parent = res; + apc->io_res.name = "PCI IO space"; + apc->io_res.start = res->start; + apc->io_res.end = res->end; + apc->io_res.flags = IORESOURCE_IO; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem_base"); + if (!res) + return -EINVAL; + + apc->mem_res.parent = res; + apc->mem_res.name = "PCI memory space"; + apc->mem_res.start = res->start; + apc->mem_res.end = res->end; + apc->mem_res.flags = IORESOURCE_MEM; + apc->pci_controller.pci_ops = &ar724x_pci_ops; - apc->pci_controller.io_resource = &ar724x_io_resource; - apc->pci_controller.mem_resource = &ar724x_mem_resource; + apc->pci_controller.io_resource = &apc->io_res; + apc->pci_controller.mem_resource = &apc->mem_res; apc->link_up = ar724x_pci_check_link(apc); if (!apc->link_up) From 8b66d461187ff61c5755001af7296e6edde48423 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sun, 3 Feb 2013 10:00:16 +0000 Subject: [PATCH 2965/4607] MIPS: pci-ar724x: use per-controller IRQ base Change to the code to use per-controller IRQ base. This is needed for multiple PCI controller support. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4915/ Signed-off-by: John Crispin --- arch/mips/pci/pci-ar724x.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/arch/mips/pci/pci-ar724x.c b/arch/mips/pci/pci-ar724x.c index d0d707de6c6c..0440d8800f8a 100644 --- a/arch/mips/pci/pci-ar724x.c +++ b/arch/mips/pci/pci-ar724x.c @@ -34,6 +34,7 @@ struct ar724x_pci_controller { void __iomem *ctrl_base; int irq; + int irq_base; bool link_up; bool bar0_is_cached; @@ -205,7 +206,7 @@ static void ar724x_pci_irq_handler(unsigned int irq, struct irq_desc *desc) __raw_readl(base + AR724X_PCI_REG_INT_MASK); if (pending & AR724X_PCI_INT_DEV0) - generic_handle_irq(ATH79_PCI_IRQ(0)); + generic_handle_irq(apc->irq_base + 0); else spurious_interrupt(); @@ -215,13 +216,15 @@ static void ar724x_pci_irq_unmask(struct irq_data *d) { struct ar724x_pci_controller *apc; void __iomem *base; + int offset; u32 t; apc = irq_data_get_irq_chip_data(d); base = apc->ctrl_base; + offset = apc->irq_base - d->irq; - switch (d->irq) { - case ATH79_PCI_IRQ(0): + switch (offset) { + case 0: t = __raw_readl(base + AR724X_PCI_REG_INT_MASK); __raw_writel(t | AR724X_PCI_INT_DEV0, base + AR724X_PCI_REG_INT_MASK); @@ -234,13 +237,15 @@ static void ar724x_pci_irq_mask(struct irq_data *d) { struct ar724x_pci_controller *apc; void __iomem *base; + int offset; u32 t; apc = irq_data_get_irq_chip_data(d); base = apc->ctrl_base; + offset = apc->irq_base - d->irq; - switch (d->irq) { - case ATH79_PCI_IRQ(0): + switch (offset) { + case 0: t = __raw_readl(base + AR724X_PCI_REG_INT_MASK); __raw_writel(t & ~AR724X_PCI_INT_DEV0, base + AR724X_PCI_REG_INT_MASK); @@ -264,7 +269,8 @@ static struct irq_chip ar724x_pci_irq_chip = { .irq_mask_ack = ar724x_pci_irq_mask, }; -static void ar724x_pci_irq_init(struct ar724x_pci_controller *apc) +static void ar724x_pci_irq_init(struct ar724x_pci_controller *apc, + int id) { void __iomem *base; int i; @@ -274,10 +280,10 @@ static void ar724x_pci_irq_init(struct ar724x_pci_controller *apc) __raw_writel(0, base + AR724X_PCI_REG_INT_MASK); __raw_writel(0, base + AR724X_PCI_REG_INT_STATUS); - BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR724X_PCI_IRQ_COUNT); + apc->irq_base = ATH79_PCI_IRQ_BASE + (id * AR724X_PCI_IRQ_COUNT); - for (i = ATH79_PCI_IRQ_BASE; - i < ATH79_PCI_IRQ_BASE + AR724X_PCI_IRQ_COUNT; i++) { + for (i = apc->irq_base; + i < apc->irq_base + AR724X_PCI_IRQ_COUNT; i++) { irq_set_chip_and_handler(i, &ar724x_pci_irq_chip, handle_level_irq); irq_set_chip_data(i, apc); @@ -291,6 +297,11 @@ static int ar724x_pci_probe(struct platform_device *pdev) { struct ar724x_pci_controller *apc; struct resource *res; + int id; + + id = pdev->id; + if (id == -1) + id = 0; apc = devm_kzalloc(&pdev->dev, sizeof(struct ar724x_pci_controller), GFP_KERNEL); @@ -347,7 +358,7 @@ static int ar724x_pci_probe(struct platform_device *pdev) if (!apc->link_up) dev_warn(&pdev->dev, "PCIe link is down\n"); - ar724x_pci_irq_init(apc); + ar724x_pci_irq_init(apc, id); register_pci_controller(&apc->pci_controller); From 12401fc28d40aa5bf8bda6991a96b6d7a3dae3ac Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sun, 3 Feb 2013 14:52:47 +0000 Subject: [PATCH 2966/4607] MIPS: pci-ar724x: setup command register of the PCI controller The command register of the PCI controller is not initialized correctly by the bootloader on some boards and this leads to non working PCI bus. Add code to initialize the command register from the Linux code to avoid this. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4916/ Signed-off-by: John Crispin --- arch/mips/ath79/pci.c | 10 ++- .../mips/include/asm/mach-ath79/ar71xx_regs.h | 2 + arch/mips/pci/pci-ar724x.c | 63 +++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/arch/mips/ath79/pci.c b/arch/mips/ath79/pci.c index 45d1112de50d..942e3f9184f0 100644 --- a/arch/mips/ath79/pci.c +++ b/arch/mips/ath79/pci.c @@ -139,13 +139,14 @@ static struct platform_device * ath79_register_pci_ar724x(int id, unsigned long cfg_base, unsigned long ctrl_base, + unsigned long crp_base, unsigned long mem_base, unsigned long mem_size, unsigned long io_base, int irq) { struct platform_device *pdev; - struct resource res[5]; + struct resource res[6]; memset(res, 0, sizeof(res)); @@ -173,6 +174,11 @@ ath79_register_pci_ar724x(int id, res[4].start = io_base; res[4].end = io_base; + res[5].name = "crp_base"; + res[5].flags = IORESOURCE_MEM; + res[5].start = crp_base; + res[5].end = crp_base + AR724X_PCI_CRP_SIZE - 1; + pdev = platform_device_register_simple("ar724x-pci", id, res, ARRAY_SIZE(res)); return pdev; @@ -188,6 +194,7 @@ int __init ath79_register_pci(void) pdev = ath79_register_pci_ar724x(-1, AR724X_PCI_CFG_BASE, AR724X_PCI_CTRL_BASE, + AR724X_PCI_CRP_BASE, AR724X_PCI_MEM_BASE, AR724X_PCI_MEM_SIZE, 0, @@ -203,6 +210,7 @@ int __init ath79_register_pci(void) pdev = ath79_register_pci_ar724x(-1, AR724X_PCI_CFG_BASE, AR724X_PCI_CTRL_BASE, + AR724X_PCI_CRP_BASE, AR724X_PCI_MEM_BASE, AR724X_PCI_MEM_SIZE, 0, diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index 7c87bfe6e4ff..a77f6ee70ec1 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -67,6 +67,8 @@ #define AR724X_PCI_CFG_BASE 0x14000000 #define AR724X_PCI_CFG_SIZE 0x1000 +#define AR724X_PCI_CRP_BASE (AR71XX_APB_BASE + 0x000c0000) +#define AR724X_PCI_CRP_SIZE 0x1000 #define AR724X_PCI_CTRL_BASE (AR71XX_APB_BASE + 0x000f0000) #define AR724X_PCI_CTRL_SIZE 0x100 diff --git a/arch/mips/pci/pci-ar724x.c b/arch/mips/pci/pci-ar724x.c index 0440d8800f8a..8a0700d448fe 100644 --- a/arch/mips/pci/pci-ar724x.c +++ b/arch/mips/pci/pci-ar724x.c @@ -29,9 +29,17 @@ #define AR7240_BAR0_WAR_VALUE 0xffff +#define AR724X_PCI_CMD_INIT (PCI_COMMAND_MEMORY | \ + PCI_COMMAND_MASTER | \ + PCI_COMMAND_INVALIDATE | \ + PCI_COMMAND_PARITY | \ + PCI_COMMAND_SERR | \ + PCI_COMMAND_FAST_BACK) + struct ar724x_pci_controller { void __iomem *devcfg_base; void __iomem *ctrl_base; + void __iomem *crp_base; int irq; int irq_base; @@ -64,6 +72,51 @@ pci_bus_to_ar724x_controller(struct pci_bus *bus) return container_of(hose, struct ar724x_pci_controller, pci_controller); } +static int ar724x_pci_local_write(struct ar724x_pci_controller *apc, + int where, int size, u32 value) +{ + unsigned long flags; + void __iomem *base; + u32 data; + int s; + + WARN_ON(where & (size - 1)); + + if (!apc->link_up) + return PCIBIOS_DEVICE_NOT_FOUND; + + base = apc->crp_base; + + spin_lock_irqsave(&apc->lock, flags); + data = __raw_readl(base + (where & ~3)); + + switch (size) { + case 1: + s = ((where & 3) * 8); + data &= ~(0xff << s); + data |= ((value & 0xff) << s); + break; + case 2: + s = ((where & 2) * 8); + data &= ~(0xffff << s); + data |= ((value & 0xffff) << s); + break; + case 4: + data = value; + break; + default: + spin_unlock_irqrestore(&apc->lock, flags); + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + __raw_writel(data, base + (where & ~3)); + /* flush write */ + __raw_readl(base + (where & ~3)); + spin_unlock_irqrestore(&apc->lock, flags); + + return PCIBIOS_SUCCESSFUL; +} + static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, uint32_t *value) { @@ -324,6 +377,14 @@ static int ar724x_pci_probe(struct platform_device *pdev) if (!apc->devcfg_base) return -EBUSY; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "crp_base"); + if (!res) + return -EINVAL; + + apc->crp_base = devm_request_and_ioremap(&pdev->dev, res); + if (apc->crp_base == NULL) + return -EBUSY; + apc->irq = platform_get_irq(pdev, 0); if (apc->irq < 0) return -EINVAL; @@ -360,6 +421,8 @@ static int ar724x_pci_probe(struct platform_device *pdev) ar724x_pci_irq_init(apc, id); + ar724x_pci_local_write(apc, PCI_COMMAND, 4, AR724X_PCI_CMD_INIT); + register_pci_controller(&apc->pci_controller); return 0; From f18118a868f1f7e7bdfea176a204fcc44fae2985 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 7 Feb 2013 19:28:14 +0000 Subject: [PATCH 2967/4607] MIPS: pci-ar71xx: use dynamically allocated PCI controller structure Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4926/ Signed-off-by: John Crispin --- arch/mips/pci/pci-ar71xx.c | 84 ++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/arch/mips/pci/pci-ar71xx.c b/arch/mips/pci/pci-ar71xx.c index 69e0bb47de08..44dc5bf720c0 100644 --- a/arch/mips/pci/pci-ar71xx.c +++ b/arch/mips/pci/pci-ar71xx.c @@ -48,8 +48,12 @@ #define AR71XX_PCI_IRQ_COUNT 5 -static DEFINE_SPINLOCK(ar71xx_pci_lock); -static void __iomem *ar71xx_pcicfg_base; +struct ar71xx_pci_controller { + void __iomem *cfg_base; + spinlock_t lock; + int irq; + struct pci_controller pci_ctrl; +}; /* Byte lane enable bits */ static const u8 ar71xx_pci_ble_table[4][4] = { @@ -92,9 +96,18 @@ static inline u32 ar71xx_pci_bus_addr(struct pci_bus *bus, unsigned int devfn, return ret; } -static int ar71xx_pci_check_error(int quiet) +static inline struct ar71xx_pci_controller * +pci_bus_to_ar71xx_controller(struct pci_bus *bus) { - void __iomem *base = ar71xx_pcicfg_base; + struct pci_controller *hose; + + hose = (struct pci_controller *) bus->sysdata; + return container_of(hose, struct ar71xx_pci_controller, pci_ctrl); +} + +static int ar71xx_pci_check_error(struct ar71xx_pci_controller *apc, int quiet) +{ + void __iomem *base = apc->cfg_base; u32 pci_err; u32 ahb_err; @@ -129,9 +142,10 @@ static int ar71xx_pci_check_error(int quiet) return !!(ahb_err | pci_err); } -static inline void ar71xx_pci_local_write(int where, int size, u32 value) +static inline void ar71xx_pci_local_write(struct ar71xx_pci_controller *apc, + int where, int size, u32 value) { - void __iomem *base = ar71xx_pcicfg_base; + void __iomem *base = apc->cfg_base; u32 ad_cbe; value = value << (8 * (where & 3)); @@ -147,7 +161,8 @@ static inline int ar71xx_pci_set_cfgaddr(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 cmd) { - void __iomem *base = ar71xx_pcicfg_base; + struct ar71xx_pci_controller *apc = pci_bus_to_ar71xx_controller(bus); + void __iomem *base = apc->cfg_base; u32 addr; addr = ar71xx_pci_bus_addr(bus, devfn, where); @@ -156,13 +171,14 @@ static inline int ar71xx_pci_set_cfgaddr(struct pci_bus *bus, __raw_writel(cmd | ar71xx_pci_get_ble(where, size, 0), base + AR71XX_PCI_REG_CFG_CBE); - return ar71xx_pci_check_error(1); + return ar71xx_pci_check_error(apc, 1); } static int ar71xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value) { - void __iomem *base = ar71xx_pcicfg_base; + struct ar71xx_pci_controller *apc = pci_bus_to_ar71xx_controller(bus); + void __iomem *base = apc->cfg_base; unsigned long flags; u32 data; int err; @@ -171,7 +187,7 @@ static int ar71xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, ret = PCIBIOS_SUCCESSFUL; data = ~0; - spin_lock_irqsave(&ar71xx_pci_lock, flags); + spin_lock_irqsave(&apc->lock, flags); err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size, AR71XX_PCI_CFG_CMD_READ); @@ -180,7 +196,7 @@ static int ar71xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, else data = __raw_readl(base + AR71XX_PCI_REG_CFG_RDDATA); - spin_unlock_irqrestore(&ar71xx_pci_lock, flags); + spin_unlock_irqrestore(&apc->lock, flags); *value = (data >> (8 * (where & 3))) & ar71xx_pci_read_mask[size & 7]; @@ -190,7 +206,8 @@ static int ar71xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, static int ar71xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value) { - void __iomem *base = ar71xx_pcicfg_base; + struct ar71xx_pci_controller *apc = pci_bus_to_ar71xx_controller(bus); + void __iomem *base = apc->cfg_base; unsigned long flags; int err; int ret; @@ -198,7 +215,7 @@ static int ar71xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, value = value << (8 * (where & 3)); ret = PCIBIOS_SUCCESSFUL; - spin_lock_irqsave(&ar71xx_pci_lock, flags); + spin_lock_irqsave(&apc->lock, flags); err = ar71xx_pci_set_cfgaddr(bus, devfn, where, size, AR71XX_PCI_CFG_CMD_WRITE); @@ -207,7 +224,7 @@ static int ar71xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, else __raw_writel(value, base + AR71XX_PCI_REG_CFG_WRDATA); - spin_unlock_irqrestore(&ar71xx_pci_lock, flags); + spin_unlock_irqrestore(&apc->lock, flags); return ret; } @@ -231,12 +248,6 @@ static struct resource ar71xx_pci_mem_resource = { .flags = IORESOURCE_MEM }; -static struct pci_controller ar71xx_pci_controller = { - .pci_ops = &ar71xx_pci_ops, - .mem_resource = &ar71xx_pci_mem_resource, - .io_resource = &ar71xx_pci_io_resource, -}; - static void ar71xx_pci_irq_handler(unsigned int irq, struct irq_desc *desc) { void __iomem *base = ath79_reset_base; @@ -294,7 +305,7 @@ static struct irq_chip ar71xx_pci_irq_chip = { .irq_mask_ack = ar71xx_pci_irq_mask, }; -static void ar71xx_pci_irq_init(int irq) +static void ar71xx_pci_irq_init(struct ar71xx_pci_controller *apc) { void __iomem *base = ath79_reset_base; int i; @@ -309,7 +320,7 @@ static void ar71xx_pci_irq_init(int irq) irq_set_chip_and_handler(i, &ar71xx_pci_irq_chip, handle_level_irq); - irq_set_chained_handler(irq, ar71xx_pci_irq_handler); + irq_set_chained_handler(apc->irq, ar71xx_pci_irq_handler); } static void ar71xx_pci_reset(void) @@ -336,20 +347,27 @@ static void ar71xx_pci_reset(void) static int ar71xx_pci_probe(struct platform_device *pdev) { + struct ar71xx_pci_controller *apc; struct resource *res; - int irq; u32 t; + apc = devm_kzalloc(&pdev->dev, sizeof(struct ar71xx_pci_controller), + GFP_KERNEL); + if (!apc) + return -ENOMEM; + + spin_lock_init(&apc->lock); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base"); if (!res) return -EINVAL; - ar71xx_pcicfg_base = devm_request_and_ioremap(&pdev->dev, res); - if (!ar71xx_pcicfg_base) + apc->cfg_base = devm_request_and_ioremap(&pdev->dev, res); + if (!apc->cfg_base) return -ENOMEM; - irq = platform_get_irq(pdev, 0); - if (irq < 0) + apc->irq = platform_get_irq(pdev, 0); + if (apc->irq < 0) return -EINVAL; ar71xx_pci_reset(); @@ -357,14 +375,18 @@ static int ar71xx_pci_probe(struct platform_device *pdev) /* setup COMMAND register */ t = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK; - ar71xx_pci_local_write(PCI_COMMAND, 4, t); + ar71xx_pci_local_write(apc, PCI_COMMAND, 4, t); /* clear bus errors */ - ar71xx_pci_check_error(1); + ar71xx_pci_check_error(apc, 1); - ar71xx_pci_irq_init(irq); + ar71xx_pci_irq_init(apc); - register_pci_controller(&ar71xx_pci_controller); + apc->pci_ctrl.pci_ops = &ar71xx_pci_ops; + apc->pci_ctrl.mem_resource = &ar71xx_pci_mem_resource; + apc->pci_ctrl.io_resource = &ar71xx_pci_io_resource; + + register_pci_controller(&apc->pci_ctrl); return 0; } From 42cb60d1fab4c81ef24876d985e08fc5bb899e41 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 7 Feb 2013 19:28:15 +0000 Subject: [PATCH 2968/4607] MIPS: pci-ar71xx: remove static PCI IO/MEM resources Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4927/ Signed-off-by: John Crispin --- arch/mips/ath79/pci.c | 12 +++++++++++- arch/mips/pci/pci-ar71xx.c | 40 +++++++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/arch/mips/ath79/pci.c b/arch/mips/ath79/pci.c index 942e3f9184f0..ea8aa10893d8 100644 --- a/arch/mips/ath79/pci.c +++ b/arch/mips/ath79/pci.c @@ -117,7 +117,7 @@ static struct platform_device * ath79_register_pci_ar71xx(void) { struct platform_device *pdev; - struct resource res[2]; + struct resource res[4]; memset(res, 0, sizeof(res)); @@ -130,6 +130,16 @@ ath79_register_pci_ar71xx(void) res[1].start = ATH79_CPU_IRQ_IP2; res[1].end = ATH79_CPU_IRQ_IP2; + res[2].name = "io_base"; + res[2].flags = IORESOURCE_IO; + res[2].start = 0; + res[2].end = 0; + + res[3].name = "mem_base"; + res[3].flags = IORESOURCE_MEM; + res[3].start = AR71XX_PCI_MEM_BASE; + res[3].end = AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1; + pdev = platform_device_register_simple("ar71xx-pci", -1, res, ARRAY_SIZE(res)); return pdev; diff --git a/arch/mips/pci/pci-ar71xx.c b/arch/mips/pci/pci-ar71xx.c index 44dc5bf720c0..e48dddbb4919 100644 --- a/arch/mips/pci/pci-ar71xx.c +++ b/arch/mips/pci/pci-ar71xx.c @@ -53,6 +53,8 @@ struct ar71xx_pci_controller { spinlock_t lock; int irq; struct pci_controller pci_ctrl; + struct resource io_res; + struct resource mem_res; }; /* Byte lane enable bits */ @@ -234,20 +236,6 @@ static struct pci_ops ar71xx_pci_ops = { .write = ar71xx_pci_write_config, }; -static struct resource ar71xx_pci_io_resource = { - .name = "PCI IO space", - .start = 0, - .end = 0, - .flags = IORESOURCE_IO, -}; - -static struct resource ar71xx_pci_mem_resource = { - .name = "PCI memory space", - .start = AR71XX_PCI_MEM_BASE, - .end = AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1, - .flags = IORESOURCE_MEM -}; - static void ar71xx_pci_irq_handler(unsigned int irq, struct irq_desc *desc) { void __iomem *base = ath79_reset_base; @@ -370,6 +358,26 @@ static int ar71xx_pci_probe(struct platform_device *pdev) if (apc->irq < 0) return -EINVAL; + res = platform_get_resource_byname(pdev, IORESOURCE_IO, "io_base"); + if (!res) + return -EINVAL; + + apc->io_res.parent = res; + apc->io_res.name = "PCI IO space"; + apc->io_res.start = res->start; + apc->io_res.end = res->end; + apc->io_res.flags = IORESOURCE_IO; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem_base"); + if (!res) + return -EINVAL; + + apc->mem_res.parent = res; + apc->mem_res.name = "PCI memory space"; + apc->mem_res.start = res->start; + apc->mem_res.end = res->end; + apc->mem_res.flags = IORESOURCE_MEM; + ar71xx_pci_reset(); /* setup COMMAND register */ @@ -383,8 +391,8 @@ static int ar71xx_pci_probe(struct platform_device *pdev) ar71xx_pci_irq_init(apc); apc->pci_ctrl.pci_ops = &ar71xx_pci_ops; - apc->pci_ctrl.mem_resource = &ar71xx_pci_mem_resource; - apc->pci_ctrl.io_resource = &ar71xx_pci_io_resource; + apc->pci_ctrl.mem_resource = &apc->mem_res; + apc->pci_ctrl.io_resource = &apc->io_res; register_pci_controller(&apc->pci_ctrl); From 326e8d17d73fdf213f6334917ef46b2ba7b1354a Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 7 Feb 2013 19:29:38 +0000 Subject: [PATCH 2969/4607] MIPS: pci-ar71xx: move irq base to the controller structure Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4928/ Signed-off-by: John Crispin --- arch/mips/pci/pci-ar71xx.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/arch/mips/pci/pci-ar71xx.c b/arch/mips/pci/pci-ar71xx.c index e48dddbb4919..412ec025cf55 100644 --- a/arch/mips/pci/pci-ar71xx.c +++ b/arch/mips/pci/pci-ar71xx.c @@ -52,6 +52,7 @@ struct ar71xx_pci_controller { void __iomem *cfg_base; spinlock_t lock; int irq; + int irq_base; struct pci_controller pci_ctrl; struct resource io_res; struct resource mem_res; @@ -238,23 +239,26 @@ static struct pci_ops ar71xx_pci_ops = { static void ar71xx_pci_irq_handler(unsigned int irq, struct irq_desc *desc) { + struct ar71xx_pci_controller *apc; void __iomem *base = ath79_reset_base; u32 pending; + apc = irq_get_handler_data(irq); + pending = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_STATUS) & __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); if (pending & AR71XX_PCI_INT_DEV0) - generic_handle_irq(ATH79_PCI_IRQ(0)); + generic_handle_irq(apc->irq_base + 0); else if (pending & AR71XX_PCI_INT_DEV1) - generic_handle_irq(ATH79_PCI_IRQ(1)); + generic_handle_irq(apc->irq_base + 1); else if (pending & AR71XX_PCI_INT_DEV2) - generic_handle_irq(ATH79_PCI_IRQ(2)); + generic_handle_irq(apc->irq_base + 2); else if (pending & AR71XX_PCI_INT_CORE) - generic_handle_irq(ATH79_PCI_IRQ(4)); + generic_handle_irq(apc->irq_base + 4); else spurious_interrupt(); @@ -262,10 +266,14 @@ static void ar71xx_pci_irq_handler(unsigned int irq, struct irq_desc *desc) static void ar71xx_pci_irq_unmask(struct irq_data *d) { - unsigned int irq = d->irq - ATH79_PCI_IRQ_BASE; + struct ar71xx_pci_controller *apc; + unsigned int irq; void __iomem *base = ath79_reset_base; u32 t; + apc = irq_data_get_irq_chip_data(d); + irq = d->irq - apc->irq_base; + t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); __raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE); @@ -275,10 +283,14 @@ static void ar71xx_pci_irq_unmask(struct irq_data *d) static void ar71xx_pci_irq_mask(struct irq_data *d) { - unsigned int irq = d->irq - ATH79_PCI_IRQ_BASE; + struct ar71xx_pci_controller *apc; + unsigned int irq; void __iomem *base = ath79_reset_base; u32 t; + apc = irq_data_get_irq_chip_data(d); + irq = d->irq - apc->irq_base; + t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE); __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE); @@ -303,11 +315,15 @@ static void ar71xx_pci_irq_init(struct ar71xx_pci_controller *apc) BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR71XX_PCI_IRQ_COUNT); - for (i = ATH79_PCI_IRQ_BASE; - i < ATH79_PCI_IRQ_BASE + AR71XX_PCI_IRQ_COUNT; i++) + apc->irq_base = ATH79_PCI_IRQ_BASE; + for (i = apc->irq_base; + i < apc->irq_base + AR71XX_PCI_IRQ_COUNT; i++) { irq_set_chip_and_handler(i, &ar71xx_pci_irq_chip, handle_level_irq); + irq_set_chip_data(i, apc); + } + irq_set_handler_data(apc->irq, apc); irq_set_chained_handler(apc->irq, ar71xx_pci_irq_handler); } From 7e69c10a8ee1f201c040997c6742c27e915730ad Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 7 Feb 2013 19:32:23 +0000 Subject: [PATCH 2970/4607] ath79: add ATH79_CPU_IRQ() macro Remove the individual ATH79_CPU_IRQ_* constants and use the new macro instead of those. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4929/ Signed-off-by: John Crispin --- arch/mips/ath79/dev-usb.c | 12 +++++----- arch/mips/ath79/dev-wmac.c | 8 +++---- arch/mips/ath79/irq.c | 32 +++++++++++++------------- arch/mips/ath79/pci.c | 6 ++--- arch/mips/include/asm/mach-ath79/irq.h | 9 ++------ 5 files changed, 31 insertions(+), 36 deletions(-) diff --git a/arch/mips/ath79/dev-usb.c b/arch/mips/ath79/dev-usb.c index bd2bc108e1b5..2e041977c854 100644 --- a/arch/mips/ath79/dev-usb.c +++ b/arch/mips/ath79/dev-usb.c @@ -111,7 +111,7 @@ static void __init ath79_usb_setup(void) platform_device_register(&ath79_ohci_device); ath79_usb_init_resource(ath79_ehci_resources, AR71XX_EHCI_BASE, - AR71XX_EHCI_SIZE, ATH79_CPU_IRQ_USB); + AR71XX_EHCI_SIZE, ATH79_CPU_IRQ(3)); ath79_ehci_device.dev.platform_data = &ath79_ehci_pdata_v1; platform_device_register(&ath79_ehci_device); } @@ -136,7 +136,7 @@ static void __init ar7240_usb_setup(void) iounmap(usb_ctrl_base); ath79_usb_init_resource(ath79_ohci_resources, AR7240_OHCI_BASE, - AR7240_OHCI_SIZE, ATH79_CPU_IRQ_USB); + AR7240_OHCI_SIZE, ATH79_CPU_IRQ(3)); platform_device_register(&ath79_ohci_device); } @@ -152,7 +152,7 @@ static void __init ar724x_usb_setup(void) mdelay(10); ath79_usb_init_resource(ath79_ehci_resources, AR724X_EHCI_BASE, - AR724X_EHCI_SIZE, ATH79_CPU_IRQ_USB); + AR724X_EHCI_SIZE, ATH79_CPU_IRQ(3)); ath79_ehci_device.dev.platform_data = &ath79_ehci_pdata_v2; platform_device_register(&ath79_ehci_device); } @@ -169,7 +169,7 @@ static void __init ar913x_usb_setup(void) mdelay(10); ath79_usb_init_resource(ath79_ehci_resources, AR913X_EHCI_BASE, - AR913X_EHCI_SIZE, ATH79_CPU_IRQ_USB); + AR913X_EHCI_SIZE, ATH79_CPU_IRQ(3)); ath79_ehci_device.dev.platform_data = &ath79_ehci_pdata_v2; platform_device_register(&ath79_ehci_device); } @@ -186,7 +186,7 @@ static void __init ar933x_usb_setup(void) mdelay(10); ath79_usb_init_resource(ath79_ehci_resources, AR933X_EHCI_BASE, - AR933X_EHCI_SIZE, ATH79_CPU_IRQ_USB); + AR933X_EHCI_SIZE, ATH79_CPU_IRQ(3)); ath79_ehci_device.dev.platform_data = &ath79_ehci_pdata_v2; platform_device_register(&ath79_ehci_device); } @@ -212,7 +212,7 @@ static void __init ar934x_usb_setup(void) udelay(1000); ath79_usb_init_resource(ath79_ehci_resources, AR934X_EHCI_BASE, - AR934X_EHCI_SIZE, ATH79_CPU_IRQ_USB); + AR934X_EHCI_SIZE, ATH79_CPU_IRQ(3)); ath79_ehci_device.dev.platform_data = &ath79_ehci_pdata_v2; platform_device_register(&ath79_ehci_device); } diff --git a/arch/mips/ath79/dev-wmac.c b/arch/mips/ath79/dev-wmac.c index d6d893c16ad4..4f6c4e389172 100644 --- a/arch/mips/ath79/dev-wmac.c +++ b/arch/mips/ath79/dev-wmac.c @@ -55,8 +55,8 @@ static void __init ar913x_wmac_setup(void) ath79_wmac_resources[0].start = AR913X_WMAC_BASE; ath79_wmac_resources[0].end = AR913X_WMAC_BASE + AR913X_WMAC_SIZE - 1; - ath79_wmac_resources[1].start = ATH79_CPU_IRQ_IP2; - ath79_wmac_resources[1].end = ATH79_CPU_IRQ_IP2; + ath79_wmac_resources[1].start = ATH79_CPU_IRQ(2); + ath79_wmac_resources[1].end = ATH79_CPU_IRQ(2); } @@ -83,8 +83,8 @@ static void __init ar933x_wmac_setup(void) ath79_wmac_resources[0].start = AR933X_WMAC_BASE; ath79_wmac_resources[0].end = AR933X_WMAC_BASE + AR933X_WMAC_SIZE - 1; - ath79_wmac_resources[1].start = ATH79_CPU_IRQ_IP2; - ath79_wmac_resources[1].end = ATH79_CPU_IRQ_IP2; + ath79_wmac_resources[1].start = ATH79_CPU_IRQ(2); + ath79_wmac_resources[1].end = ATH79_CPU_IRQ(2); t = ath79_reset_rr(AR933X_RESET_REG_BOOTSTRAP); if (t & AR933X_BOOTSTRAP_REF_CLK_40) diff --git a/arch/mips/ath79/irq.c b/arch/mips/ath79/irq.c index 219cfa1f5961..29348956d15d 100644 --- a/arch/mips/ath79/irq.c +++ b/arch/mips/ath79/irq.c @@ -114,7 +114,7 @@ static void __init ath79_misc_irq_init(void) handle_level_irq); } - irq_set_chained_handler(ATH79_CPU_IRQ_MISC, ath79_misc_irq_handler); + irq_set_chained_handler(ATH79_CPU_IRQ(6), ath79_misc_irq_handler); } static void ar934x_ip2_irq_dispatch(unsigned int irq, struct irq_desc *desc) @@ -147,7 +147,7 @@ static void ar934x_ip2_irq_init(void) irq_set_chip_and_handler(i, &dummy_irq_chip, handle_level_irq); - irq_set_chained_handler(ATH79_CPU_IRQ_IP2, ar934x_ip2_irq_dispatch); + irq_set_chained_handler(ATH79_CPU_IRQ(2), ar934x_ip2_irq_dispatch); } asmlinkage void plat_irq_dispatch(void) @@ -157,22 +157,22 @@ asmlinkage void plat_irq_dispatch(void) pending = read_c0_status() & read_c0_cause() & ST0_IM; if (pending & STATUSF_IP7) - do_IRQ(ATH79_CPU_IRQ_TIMER); + do_IRQ(ATH79_CPU_IRQ(7)); else if (pending & STATUSF_IP2) ath79_ip2_handler(); else if (pending & STATUSF_IP4) - do_IRQ(ATH79_CPU_IRQ_GE0); + do_IRQ(ATH79_CPU_IRQ(4)); else if (pending & STATUSF_IP5) - do_IRQ(ATH79_CPU_IRQ_GE1); + do_IRQ(ATH79_CPU_IRQ(5)); else if (pending & STATUSF_IP3) ath79_ip3_handler(); else if (pending & STATUSF_IP6) - do_IRQ(ATH79_CPU_IRQ_MISC); + do_IRQ(ATH79_CPU_IRQ(6)); else spurious_interrupt(); @@ -188,60 +188,60 @@ asmlinkage void plat_irq_dispatch(void) static void ar71xx_ip2_handler(void) { ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_PCI); - do_IRQ(ATH79_CPU_IRQ_IP2); + do_IRQ(ATH79_CPU_IRQ(2)); } static void ar724x_ip2_handler(void) { ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_PCIE); - do_IRQ(ATH79_CPU_IRQ_IP2); + do_IRQ(ATH79_CPU_IRQ(2)); } static void ar913x_ip2_handler(void) { ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_WMAC); - do_IRQ(ATH79_CPU_IRQ_IP2); + do_IRQ(ATH79_CPU_IRQ(2)); } static void ar933x_ip2_handler(void) { ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_WMAC); - do_IRQ(ATH79_CPU_IRQ_IP2); + do_IRQ(ATH79_CPU_IRQ(2)); } static void ar934x_ip2_handler(void) { - do_IRQ(ATH79_CPU_IRQ_IP2); + do_IRQ(ATH79_CPU_IRQ(2)); } static void ar71xx_ip3_handler(void) { ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_USB); - do_IRQ(ATH79_CPU_IRQ_USB); + do_IRQ(ATH79_CPU_IRQ(3)); } static void ar724x_ip3_handler(void) { ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_USB); - do_IRQ(ATH79_CPU_IRQ_USB); + do_IRQ(ATH79_CPU_IRQ(3)); } static void ar913x_ip3_handler(void) { ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_USB); - do_IRQ(ATH79_CPU_IRQ_USB); + do_IRQ(ATH79_CPU_IRQ(3)); } static void ar933x_ip3_handler(void) { ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_USB); - do_IRQ(ATH79_CPU_IRQ_USB); + do_IRQ(ATH79_CPU_IRQ(3)); } static void ar934x_ip3_handler(void) { ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_USB); - do_IRQ(ATH79_CPU_IRQ_USB); + do_IRQ(ATH79_CPU_IRQ(3)); } void __init arch_init_irq(void) diff --git a/arch/mips/ath79/pci.c b/arch/mips/ath79/pci.c index ea8aa10893d8..4350c252bce5 100644 --- a/arch/mips/ath79/pci.c +++ b/arch/mips/ath79/pci.c @@ -127,8 +127,8 @@ ath79_register_pci_ar71xx(void) res[0].end = AR71XX_PCI_CFG_BASE + AR71XX_PCI_CFG_SIZE - 1; res[1].flags = IORESOURCE_IRQ; - res[1].start = ATH79_CPU_IRQ_IP2; - res[1].end = ATH79_CPU_IRQ_IP2; + res[1].start = ATH79_CPU_IRQ(2); + res[1].end = ATH79_CPU_IRQ(2); res[2].name = "io_base"; res[2].flags = IORESOURCE_IO; @@ -208,7 +208,7 @@ int __init ath79_register_pci(void) AR724X_PCI_MEM_BASE, AR724X_PCI_MEM_SIZE, 0, - ATH79_CPU_IRQ_IP2); + ATH79_CPU_IRQ(2)); } else if (soc_is_ar9342() || soc_is_ar9344()) { u32 bootstrap; diff --git a/arch/mips/include/asm/mach-ath79/irq.h b/arch/mips/include/asm/mach-ath79/irq.h index 158ad7f41313..3dda4c24571d 100644 --- a/arch/mips/include/asm/mach-ath79/irq.h +++ b/arch/mips/include/asm/mach-ath79/irq.h @@ -12,6 +12,8 @@ #define MIPS_CPU_IRQ_BASE 0 #define NR_IRQS 48 +#define ATH79_CPU_IRQ(_x) (MIPS_CPU_IRQ_BASE + (_x)) + #define ATH79_MISC_IRQ_BASE 8 #define ATH79_MISC_IRQ_COUNT 32 #define ATH79_MISC_IRQ(_x) (ATH79_MISC_IRQ_BASE + (_x)) @@ -24,13 +26,6 @@ #define ATH79_IP2_IRQ_COUNT 2 #define ATH79_IP2_IRQ(_x) (ATH79_IP2_IRQ_BASE + (_x)) -#define ATH79_CPU_IRQ_IP2 (MIPS_CPU_IRQ_BASE + 2) -#define ATH79_CPU_IRQ_USB (MIPS_CPU_IRQ_BASE + 3) -#define ATH79_CPU_IRQ_GE0 (MIPS_CPU_IRQ_BASE + 4) -#define ATH79_CPU_IRQ_GE1 (MIPS_CPU_IRQ_BASE + 5) -#define ATH79_CPU_IRQ_MISC (MIPS_CPU_IRQ_BASE + 6) -#define ATH79_CPU_IRQ_TIMER (MIPS_CPU_IRQ_BASE + 7) - #define ATH79_MISC_IRQ_TIMER (ATH79_MISC_IRQ_BASE + 0) #define ATH79_MISC_IRQ_ERROR (ATH79_MISC_IRQ_BASE + 1) #define ATH79_MISC_IRQ_GPIO (ATH79_MISC_IRQ_BASE + 2) From fd633cf1cfe978003888dc78ff94f926fbe7dd8a Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 7 Feb 2013 19:32:24 +0000 Subject: [PATCH 2971/4607] ath79: remove ATH79_MISC_IRQ_* defines Use the ATH79_MISC_IRQ() macro instead. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4930/ Signed-off-by: John Crispin --- arch/mips/ath79/dev-common.c | 6 +++--- arch/mips/ath79/dev-usb.c | 2 +- arch/mips/ath79/irq.c | 2 +- arch/mips/include/asm/mach-ath79/irq.h | 13 ------------- 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/arch/mips/ath79/dev-common.c b/arch/mips/ath79/dev-common.c index 45efc63b08b6..480f5eb9d300 100644 --- a/arch/mips/ath79/dev-common.c +++ b/arch/mips/ath79/dev-common.c @@ -36,7 +36,7 @@ static struct resource ath79_uart_resources[] = { static struct plat_serial8250_port ath79_uart_data[] = { { .mapbase = AR71XX_UART_BASE, - .irq = ATH79_MISC_IRQ_UART, + .irq = ATH79_MISC_IRQ(3), .flags = AR71XX_UART_FLAGS, .iotype = UPIO_MEM32, .regshift = 2, @@ -62,8 +62,8 @@ static struct resource ar933x_uart_resources[] = { .flags = IORESOURCE_MEM, }, { - .start = ATH79_MISC_IRQ_UART, - .end = ATH79_MISC_IRQ_UART, + .start = ATH79_MISC_IRQ(3), + .end = ATH79_MISC_IRQ(3), .flags = IORESOURCE_IRQ, }, }; diff --git a/arch/mips/ath79/dev-usb.c b/arch/mips/ath79/dev-usb.c index 2e041977c854..bcb165b17cae 100644 --- a/arch/mips/ath79/dev-usb.c +++ b/arch/mips/ath79/dev-usb.c @@ -107,7 +107,7 @@ static void __init ath79_usb_setup(void) mdelay(900); ath79_usb_init_resource(ath79_ohci_resources, AR71XX_OHCI_BASE, - AR71XX_OHCI_SIZE, ATH79_MISC_IRQ_OHCI); + AR71XX_OHCI_SIZE, ATH79_MISC_IRQ(6)); platform_device_register(&ath79_ohci_device); ath79_usb_init_resource(ath79_ehci_resources, AR71XX_EHCI_BASE, diff --git a/arch/mips/ath79/irq.c b/arch/mips/ath79/irq.c index 29348956d15d..df88d49bcb05 100644 --- a/arch/mips/ath79/irq.c +++ b/arch/mips/ath79/irq.c @@ -265,7 +265,7 @@ void __init arch_init_irq(void) BUG(); } - cp0_perfcount_irq = ATH79_MISC_IRQ_PERFC; + cp0_perfcount_irq = ATH79_MISC_IRQ(5); mips_cpu_irq_init(); ath79_misc_irq_init(); diff --git a/arch/mips/include/asm/mach-ath79/irq.h b/arch/mips/include/asm/mach-ath79/irq.h index 3dda4c24571d..23e2bba42482 100644 --- a/arch/mips/include/asm/mach-ath79/irq.h +++ b/arch/mips/include/asm/mach-ath79/irq.h @@ -26,19 +26,6 @@ #define ATH79_IP2_IRQ_COUNT 2 #define ATH79_IP2_IRQ(_x) (ATH79_IP2_IRQ_BASE + (_x)) -#define ATH79_MISC_IRQ_TIMER (ATH79_MISC_IRQ_BASE + 0) -#define ATH79_MISC_IRQ_ERROR (ATH79_MISC_IRQ_BASE + 1) -#define ATH79_MISC_IRQ_GPIO (ATH79_MISC_IRQ_BASE + 2) -#define ATH79_MISC_IRQ_UART (ATH79_MISC_IRQ_BASE + 3) -#define ATH79_MISC_IRQ_WDOG (ATH79_MISC_IRQ_BASE + 4) -#define ATH79_MISC_IRQ_PERFC (ATH79_MISC_IRQ_BASE + 5) -#define ATH79_MISC_IRQ_OHCI (ATH79_MISC_IRQ_BASE + 6) -#define ATH79_MISC_IRQ_DMA (ATH79_MISC_IRQ_BASE + 7) -#define ATH79_MISC_IRQ_TIMER2 (ATH79_MISC_IRQ_BASE + 8) -#define ATH79_MISC_IRQ_TIMER3 (ATH79_MISC_IRQ_BASE + 9) -#define ATH79_MISC_IRQ_TIMER4 (ATH79_MISC_IRQ_BASE + 10) -#define ATH79_MISC_IRQ_ETHSW (ATH79_MISC_IRQ_BASE + 12) - #include_next #endif /* __ASM_MACH_ATH79_IRQ_H */ From 90a938d1add4859ad3e43c3dd5ee54bd0627e42d Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sat, 9 Feb 2013 17:57:52 +0000 Subject: [PATCH 2972/4607] MIPS: ath79: use dynamically allocated USB platform devices The current code uses static resources and static platform device instances for the possible USB controllers in the system. These static variables contains initial values which leads to data segment pollution. Remove the static variables and use dynamically allocated structures instead. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4933/ Signed-off-by: John Crispin --- arch/mips/ath79/dev-usb.c | 111 ++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/arch/mips/ath79/dev-usb.c b/arch/mips/ath79/dev-usb.c index bcb165b17cae..02124d02cf6e 100644 --- a/arch/mips/ath79/dev-usb.c +++ b/arch/mips/ath79/dev-usb.c @@ -25,29 +25,11 @@ #include "common.h" #include "dev-usb.h" -static struct resource ath79_ohci_resources[2]; - -static u64 ath79_ohci_dmamask = DMA_BIT_MASK(32); +static u64 ath79_usb_dmamask = DMA_BIT_MASK(32); static struct usb_ohci_pdata ath79_ohci_pdata = { }; -static struct platform_device ath79_ohci_device = { - .name = "ohci-platform", - .id = -1, - .resource = ath79_ohci_resources, - .num_resources = ARRAY_SIZE(ath79_ohci_resources), - .dev = { - .dma_mask = &ath79_ohci_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(32), - .platform_data = &ath79_ohci_pdata, - }, -}; - -static struct resource ath79_ehci_resources[2]; - -static u64 ath79_ehci_dmamask = DMA_BIT_MASK(32); - static struct usb_ehci_pdata ath79_ehci_pdata_v1 = { .has_synopsys_hc_bug = 1, }; @@ -57,22 +39,16 @@ static struct usb_ehci_pdata ath79_ehci_pdata_v2 = { .has_tt = 1, }; -static struct platform_device ath79_ehci_device = { - .name = "ehci-platform", - .id = -1, - .resource = ath79_ehci_resources, - .num_resources = ARRAY_SIZE(ath79_ehci_resources), - .dev = { - .dma_mask = &ath79_ehci_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(32), - }, -}; - -static void __init ath79_usb_init_resource(struct resource res[2], - unsigned long base, - unsigned long size, - int irq) +static void __init ath79_usb_register(const char *name, int id, + unsigned long base, unsigned long size, + int irq, const void *data, + size_t data_size) { + struct resource res[2]; + struct platform_device *pdev; + + memset(res, 0, sizeof(res)); + res[0].flags = IORESOURCE_MEM; res[0].start = base; res[0].end = base + size - 1; @@ -80,6 +56,19 @@ static void __init ath79_usb_init_resource(struct resource res[2], res[1].flags = IORESOURCE_IRQ; res[1].start = irq; res[1].end = irq; + + pdev = platform_device_register_resndata(NULL, name, id, + res, ARRAY_SIZE(res), + data, data_size); + + if (IS_ERR(pdev)) { + pr_err("ath79: unable to register USB at %08lx, err=%d\n", + base, (int) PTR_ERR(pdev)); + return; + } + + pdev->dev.dma_mask = &ath79_usb_dmamask; + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); } #define AR71XX_USB_RESET_MASK (AR71XX_RESET_USB_HOST | \ @@ -106,14 +95,15 @@ static void __init ath79_usb_setup(void) mdelay(900); - ath79_usb_init_resource(ath79_ohci_resources, AR71XX_OHCI_BASE, - AR71XX_OHCI_SIZE, ATH79_MISC_IRQ(6)); - platform_device_register(&ath79_ohci_device); + ath79_usb_register("ohci-platform", -1, + AR71XX_OHCI_BASE, AR71XX_OHCI_SIZE, + ATH79_MISC_IRQ(6), + &ath79_ohci_pdata, sizeof(ath79_ohci_pdata)); - ath79_usb_init_resource(ath79_ehci_resources, AR71XX_EHCI_BASE, - AR71XX_EHCI_SIZE, ATH79_CPU_IRQ(3)); - ath79_ehci_device.dev.platform_data = &ath79_ehci_pdata_v1; - platform_device_register(&ath79_ehci_device); + ath79_usb_register("ehci-platform", -1, + AR71XX_EHCI_BASE, AR71XX_EHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ehci_pdata_v1, sizeof(ath79_ehci_pdata_v1)); } static void __init ar7240_usb_setup(void) @@ -135,9 +125,10 @@ static void __init ar7240_usb_setup(void) iounmap(usb_ctrl_base); - ath79_usb_init_resource(ath79_ohci_resources, AR7240_OHCI_BASE, - AR7240_OHCI_SIZE, ATH79_CPU_IRQ(3)); - platform_device_register(&ath79_ohci_device); + ath79_usb_register("ohci-platform", -1, + AR7240_OHCI_BASE, AR7240_OHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ohci_pdata, sizeof(ath79_ohci_pdata)); } static void __init ar724x_usb_setup(void) @@ -151,10 +142,10 @@ static void __init ar724x_usb_setup(void) ath79_device_reset_clear(AR724X_RESET_USB_PHY); mdelay(10); - ath79_usb_init_resource(ath79_ehci_resources, AR724X_EHCI_BASE, - AR724X_EHCI_SIZE, ATH79_CPU_IRQ(3)); - ath79_ehci_device.dev.platform_data = &ath79_ehci_pdata_v2; - platform_device_register(&ath79_ehci_device); + ath79_usb_register("ehci-platform", -1, + AR724X_EHCI_BASE, AR724X_EHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); } static void __init ar913x_usb_setup(void) @@ -168,10 +159,10 @@ static void __init ar913x_usb_setup(void) ath79_device_reset_clear(AR913X_RESET_USB_PHY); mdelay(10); - ath79_usb_init_resource(ath79_ehci_resources, AR913X_EHCI_BASE, - AR913X_EHCI_SIZE, ATH79_CPU_IRQ(3)); - ath79_ehci_device.dev.platform_data = &ath79_ehci_pdata_v2; - platform_device_register(&ath79_ehci_device); + ath79_usb_register("ehci-platform", -1, + AR913X_EHCI_BASE, AR913X_EHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); } static void __init ar933x_usb_setup(void) @@ -185,10 +176,10 @@ static void __init ar933x_usb_setup(void) ath79_device_reset_clear(AR933X_RESET_USB_PHY); mdelay(10); - ath79_usb_init_resource(ath79_ehci_resources, AR933X_EHCI_BASE, - AR933X_EHCI_SIZE, ATH79_CPU_IRQ(3)); - ath79_ehci_device.dev.platform_data = &ath79_ehci_pdata_v2; - platform_device_register(&ath79_ehci_device); + ath79_usb_register("ehci-platform", -1, + AR933X_EHCI_BASE, AR933X_EHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); } static void __init ar934x_usb_setup(void) @@ -211,10 +202,10 @@ static void __init ar934x_usb_setup(void) ath79_device_reset_clear(AR934X_RESET_USB_HOST); udelay(1000); - ath79_usb_init_resource(ath79_ehci_resources, AR934X_EHCI_BASE, - AR934X_EHCI_SIZE, ATH79_CPU_IRQ(3)); - ath79_ehci_device.dev.platform_data = &ath79_ehci_pdata_v2; - platform_device_register(&ath79_ehci_device); + ath79_usb_register("ehci-platform", -1, + AR934X_EHCI_BASE, AR934X_EHCI_SIZE, + ATH79_CPU_IRQ(3), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); } void __init ath79_register_usb(void) From 8668480eb79f0cbd79d6b584a10604d743853062 Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Thu, 14 Feb 2013 07:42:30 +0100 Subject: [PATCH 2973/4607] openrisc: update DTLB-miss handler last The self-modifying code that updates the TLB handler at start-up has a subtle ordering requirement: the DTLB handler must be the last thing changed. What I was seeing was the following: i) The DTLB handler was updated ii) The following printk caused a TLB miss and the look-up resulted in the page containing itlb_vector (0xc0000a00) being bounced from the TLB. iii) The subsequent access to itlb_vector caused a TLB miss and reload of the page containing itlb_vector from the page tables. iv) But this reload of the page in iii) was being done by the "new" DTLB-miss handler which resulted (correctly) in the page flags being set to read-only; the subsequent write-access to itlb_vector thus resulted in a page (access) fault. This is easily remedied if we ensure that the boot-time DTLB-miss handler continues running until the very last bit of self-modifying code has been executed. This patch should ensure that the very last thing updated is the DTLB-handler itself. Signed-off-by: Jonas Bonn Acked-by: Julius Baxter Tested-by: Sebastian Macke --- arch/openrisc/mm/init.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/arch/openrisc/mm/init.c b/arch/openrisc/mm/init.c index 79dea9740a3c..e7fdc50c4bf0 100644 --- a/arch/openrisc/mm/init.c +++ b/arch/openrisc/mm/init.c @@ -167,15 +167,26 @@ void __init paging_init(void) unsigned long *dtlb_vector = __va(0x900); unsigned long *itlb_vector = __va(0xa00); + printk(KERN_INFO "itlb_miss_handler %p\n", &itlb_miss_handler); + *itlb_vector = ((unsigned long)&itlb_miss_handler - + (unsigned long)itlb_vector) >> 2; + + /* Soft ordering constraint to ensure that dtlb_vector is + * the last thing updated + */ + barrier(); + printk(KERN_INFO "dtlb_miss_handler %p\n", &dtlb_miss_handler); *dtlb_vector = ((unsigned long)&dtlb_miss_handler - (unsigned long)dtlb_vector) >> 2; - printk(KERN_INFO "itlb_miss_handler %p\n", &itlb_miss_handler); - *itlb_vector = ((unsigned long)&itlb_miss_handler - - (unsigned long)itlb_vector) >> 2; } + /* Soft ordering constraint to ensure that cache invalidation and + * TLB flush really happen _after_ code has been modified. + */ + barrier(); + /* Invalidate instruction caches after code modification */ mtspr(SPR_ICBIR, 0x900); mtspr(SPR_ICBIR, 0xa00); From b4f5b53603468ef229d16350639e293f12128b62 Mon Sep 17 00:00:00 2001 From: Sebastian Macke Date: Fri, 15 Feb 2013 07:54:41 +0100 Subject: [PATCH 2974/4607] Add bitops include needed for ext2 filesystem Signed-off-by: Jonas Bonn --- arch/openrisc/include/asm/bitops.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/openrisc/include/asm/bitops.h b/arch/openrisc/include/asm/bitops.h index a9e11efae14d..2c64f2228dc7 100644 --- a/arch/openrisc/include/asm/bitops.h +++ b/arch/openrisc/include/asm/bitops.h @@ -54,6 +54,7 @@ #include #include +#include #include #endif /* __ASM_GENERIC_BITOPS_H */ From ae6fef1790512edde8776ee2a158b1e13d085f61 Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Fri, 15 Feb 2013 17:07:17 +0100 Subject: [PATCH 2975/4607] openrisc: really pass correct arg to schedule_tail Commit 287ad220cd8b5a9d29f71c78f6e4051093f051fc tried to set up the argument to schedule_tail, but ended up using TI_STACK which isn't a defined symbol. Sadly, the old openrisc compiler silently ignores this fact and it was first discovered now when building with an updated toolchain. Reported-by: Christian Svensson Signed-off-by: Jonas Bonn --- arch/openrisc/kernel/entry.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S index 3de971224cfc..e7fadc0234cd 100644 --- a/arch/openrisc/kernel/entry.S +++ b/arch/openrisc/kernel/entry.S @@ -1050,7 +1050,7 @@ ENTRY(_switch) * we are expected to have set up the arg to schedule_tail already, * hence we do so here unconditionally: */ - l.lwz r3,TI_STACK(r3) /* Load 'prev' as schedule_tail arg */ + l.lwz r3,TI_TASK(r3) /* Load 'prev' as schedule_tail arg */ l.jr r9 l.nop From 30f786170352b8264bc7b61c2482713e54accec8 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Sat, 16 Feb 2013 19:20:51 +0100 Subject: [PATCH 2976/4607] pwm: twl: Use to_twl() instead of container_of() Always use to_twl() for converting into private data instead of container_of(). Signed-off-by: Johannes Thumshirn Signed-off-by: Thierry Reding --- drivers/pwm/pwm-twl.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c index f783efcd14bb..bf3fda294223 100644 --- a/drivers/pwm/pwm-twl.c +++ b/drivers/pwm/pwm-twl.c @@ -200,8 +200,7 @@ out: static void twl4030_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, - chip); + struct twl_pwm_chip *twl = to_twl(chip); int ret; u8 val, mask; @@ -231,8 +230,7 @@ out: static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, - chip); + struct twl_pwm_chip *twl = to_twl(chip); int ret; u8 val; @@ -255,8 +253,7 @@ out: static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, - chip); + struct twl_pwm_chip *twl = to_twl(chip); int ret; u8 val; From e75bafbff2270993926abcc31358361db74a9bc2 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 10 Feb 2013 11:33:48 -0500 Subject: [PATCH 2977/4607] svcrpc: make svc_age_temp_xprts enqueue under sv_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit svc_age_temp_xprts expires xprts in a two-step process: first it takes the sv_lock and moves the xprts to expire off their server-wide list (sv_tempsocks or sv_permsocks) to a local list. Then it drops the sv_lock and enqueues and puts each one. I see no reason for this: svc_xprt_enqueue() will take sp_lock, but the sv_lock and sp_lock are not otherwise nested anywhere (and documentation at the top of this file claims it's correct to nest these with sp_lock inside.) Cc: stable@kernel.org Tested-by: Jason Tibbitts Tested-by: Paweł Sikora Signed-off-by: J. Bruce Fields --- net/sunrpc/svc_xprt.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 5a9d40c5a9f3..11a33c874848 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -863,7 +863,6 @@ static void svc_age_temp_xprts(unsigned long closure) struct svc_serv *serv = (struct svc_serv *)closure; struct svc_xprt *xprt; struct list_head *le, *next; - LIST_HEAD(to_be_aged); dprintk("svc_age_temp_xprts\n"); @@ -884,25 +883,15 @@ static void svc_age_temp_xprts(unsigned long closure) if (atomic_read(&xprt->xpt_ref.refcount) > 1 || test_bit(XPT_BUSY, &xprt->xpt_flags)) continue; - svc_xprt_get(xprt); - list_move(le, &to_be_aged); + list_del_init(le); set_bit(XPT_CLOSE, &xprt->xpt_flags); set_bit(XPT_DETACHED, &xprt->xpt_flags); - } - spin_unlock_bh(&serv->sv_lock); - - while (!list_empty(&to_be_aged)) { - le = to_be_aged.next; - /* fiddling the xpt_list node is safe 'cos we're XPT_DETACHED */ - list_del_init(le); - xprt = list_entry(le, struct svc_xprt, xpt_list); - dprintk("queuing xprt %p for closing\n", xprt); /* a thread will dequeue and close it soon */ svc_xprt_enqueue(xprt); - svc_xprt_put(xprt); } + spin_unlock_bh(&serv->sv_lock); mod_timer(&serv->sv_temptimer, jiffies + svc_conn_age_period * HZ); } From cc630d9f476445927fca599f81182c7f06f79058 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sun, 10 Feb 2013 16:08:11 -0500 Subject: [PATCH 2978/4607] svcrpc: fix rpc server shutdown races MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrite server shutdown to remove the assumption that there are no longer any threads running (no longer true, for example, when shutting down the service in one network namespace while it's still running in others). Do that by doing what we'd do in normal circumstances: just CLOSE each socket, then enqueue it. Since there may not be threads to handle the resulting queued xprts, also run a simplified version of the svc_recv() loop run by a server to clean up any closed xprts afterwards. Cc: stable@kernel.org Tested-by: Jason Tibbitts Tested-by: Paweł Sikora Acked-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- net/sunrpc/svc.c | 9 ------- net/sunrpc/svc_xprt.c | 57 ++++++++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index b9ba2a8c1c19..89a588b4478b 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -515,15 +515,6 @@ EXPORT_SYMBOL_GPL(svc_create_pooled); void svc_shutdown_net(struct svc_serv *serv, struct net *net) { - /* - * The set of xprts (contained in the sv_tempsocks and - * sv_permsocks lists) is now constant, since it is modified - * only by accepting new sockets (done by service threads in - * svc_recv) or aging old ones (done by sv_temptimer), or - * configuration changes (excluded by whatever locking the - * caller is using--nfsd_mutex in the case of nfsd). So it's - * safe to traverse those lists and shut everything down: - */ svc_close_net(serv, net); if (serv->sv_shutdown) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 11a33c874848..80a6640f329b 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -955,21 +955,24 @@ void svc_close_xprt(struct svc_xprt *xprt) } EXPORT_SYMBOL_GPL(svc_close_xprt); -static void svc_close_list(struct svc_serv *serv, struct list_head *xprt_list, struct net *net) +static int svc_close_list(struct svc_serv *serv, struct list_head *xprt_list, struct net *net) { struct svc_xprt *xprt; + int ret = 0; spin_lock(&serv->sv_lock); list_for_each_entry(xprt, xprt_list, xpt_list) { if (xprt->xpt_net != net) continue; + ret++; set_bit(XPT_CLOSE, &xprt->xpt_flags); - set_bit(XPT_BUSY, &xprt->xpt_flags); + svc_xprt_enqueue(xprt); } spin_unlock(&serv->sv_lock); + return ret; } -static void svc_clear_pools(struct svc_serv *serv, struct net *net) +static struct svc_xprt *svc_dequeue_net(struct svc_serv *serv, struct net *net) { struct svc_pool *pool; struct svc_xprt *xprt; @@ -984,42 +987,46 @@ static void svc_clear_pools(struct svc_serv *serv, struct net *net) if (xprt->xpt_net != net) continue; list_del_init(&xprt->xpt_ready); + spin_unlock_bh(&pool->sp_lock); + return xprt; } spin_unlock_bh(&pool->sp_lock); } + return NULL; } -static void svc_clear_list(struct svc_serv *serv, struct list_head *xprt_list, struct net *net) +static void svc_clean_up_xprts(struct svc_serv *serv, struct net *net) { struct svc_xprt *xprt; - struct svc_xprt *tmp; - LIST_HEAD(victims); - spin_lock(&serv->sv_lock); - list_for_each_entry_safe(xprt, tmp, xprt_list, xpt_list) { - if (xprt->xpt_net != net) - continue; - list_move(&xprt->xpt_list, &victims); - } - spin_unlock(&serv->sv_lock); - - list_for_each_entry_safe(xprt, tmp, &victims, xpt_list) + while ((xprt = svc_dequeue_net(serv, net))) { + set_bit(XPT_CLOSE, &xprt->xpt_flags); svc_delete_xprt(xprt); + } } +/* + * Server threads may still be running (especially in the case where the + * service is still running in other network namespaces). + * + * So we shut down sockets the same way we would on a running server, by + * setting XPT_CLOSE, enqueuing, and letting a thread pick it up to do + * the close. In the case there are no such other threads, + * threads running, svc_clean_up_xprts() does a simple version of a + * server's main event loop, and in the case where there are other + * threads, we may need to wait a little while and then check again to + * see if they're done. + */ void svc_close_net(struct svc_serv *serv, struct net *net) { - svc_close_list(serv, &serv->sv_tempsocks, net); - svc_close_list(serv, &serv->sv_permsocks, net); + int delay = 0; - svc_clear_pools(serv, net); - /* - * At this point the sp_sockets lists will stay empty, since - * svc_xprt_enqueue will not add new entries without taking the - * sp_lock and checking XPT_BUSY. - */ - svc_clear_list(serv, &serv->sv_tempsocks, net); - svc_clear_list(serv, &serv->sv_permsocks, net); + while (svc_close_list(serv, &serv->sv_permsocks, net) + + svc_close_list(serv, &serv->sv_tempsocks, net)) { + + svc_clean_up_xprts(serv, net); + msleep(delay++); + } } /* From 56edc86b5a72bdbc86358e57fb09165136baf0b8 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 15 Feb 2013 13:36:34 -0500 Subject: [PATCH 2979/4607] nfsd: fix compiler warning about ambiguous types in nfsd_cache_csum kbuild test robot says: tree: git://linux-nfs.org/~bfields/linux.git for-3.9 head: deb4534f4f3be7aea7d9d24c3b0d58f370cbf9ef commit: 01a7decf75930925322c5efc87af0b5e58eb8650 [32/44] nfsd: keep a checksum of the first 256 bytes of request config: i386-randconfig-x088 (attached as .config) All warnings: fs/nfsd/nfscache.c: In function 'nfsd_cache_csum': >> fs/nfsd/nfscache.c:266:9: warning: comparison of distinct pointer types lacks a cast [enabled by default] vim +266 fs/nfsd/nfscache.c 250 __wsum csum; 251 struct xdr_buf *buf = &rqstp->rq_arg; 252 const unsigned char *p = buf->head[0].iov_base; 253 size_t csum_len = min_t(size_t, buf->head[0].iov_len + buf->page_len, 254 RC_CSUMLEN); 255 size_t len = min(buf->head[0].iov_len, csum_len); 256 257 /* rq_arg.head first */ 258 csum = csum_partial(p, len, 0); 259 csum_len -= len; 260 261 /* Continue into page array */ 262 idx = buf->page_base / PAGE_SIZE; 263 base = buf->page_base & ~PAGE_MASK; 264 while (csum_len) { 265 p = page_address(buf->pages[idx]) + base; > 266 len = min(PAGE_SIZE - base, csum_len); 267 csum = csum_partial(p, len, csum); 268 csum_len -= len; 269 base = 0; 270 ++idx; 271 } 272 return csum; 273 } 274 Signed-off-by: Jeff Layton Reported-by: kbuild test robot Signed-off-by: J. Bruce Fields --- fs/nfsd/nfscache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c index 2f9c2d26a2b9..ca43664422f6 100644 --- a/fs/nfsd/nfscache.c +++ b/fs/nfsd/nfscache.c @@ -263,7 +263,7 @@ nfsd_cache_csum(struct svc_rqst *rqstp) base = buf->page_base & ~PAGE_MASK; while (csum_len) { p = page_address(buf->pages[idx]) + base; - len = min(PAGE_SIZE - base, csum_len); + len = min_t(size_t, PAGE_SIZE - base, csum_len); csum = csum_partial(p, len, csum); csum_len -= len; base = 0; From 50fdaae79abbfc4f04b5380ac5abf00fd448c5a5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 15 Feb 2013 10:24:35 +0000 Subject: [PATCH 2980/4607] drm/cma-helper: fixup compilation /me grabs a few brown paper bags So it looks like I've broken compilation in commit 6aed8ec3f76a22217c9ae183d32b1aa990bed069 Author: Daniel Vetter Date: Sun Jan 20 17:32:21 2013 +0100 drm: review locking for drm_fb_helper_restore_fbdev_mode Fix it up again. Reported-by: Wu Fengguang Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_cma_helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index e851658f87d5..ef68e34b16ec 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -377,6 +377,8 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); */ void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) { + struct drm_device *dev = fbdev_cma->fb_helper.dev; + drm_modeset_lock_all(dev); if (fbdev_cma) drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper); From 6e488c00457dad0d433af7f937211fba8d76fd04 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 15 Feb 2013 20:21:37 +0000 Subject: [PATCH 2981/4607] drm: Don't set the plane->fb to NULL on successfull set_plane MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to clear the local variable to get the refcounting right (since the reference drm_mode_setplane holds is transferred to the plane->fb pointer). But should be done _after_ we update the pointer. Breakage introduced in commit 6c2a75325c800de286166c693e0cd33c3a1c5ec8 Author: Daniel Vetter Date: Tue Dec 11 00:59:24 2012 +0100 drm: refcounting for sprite framebuffers Reported-by: Jesse Barnes Cc: Jesse Barnes Cc: Rob Clark Signed-off-by: Daniel Vetter Reviewed-by: Jesse Barnes Reviewed-by: Ville Syrjälä --- drivers/gpu/drm/drm_crtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f17077307c65..e7471b0880b7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1996,9 +1996,9 @@ int drm_mode_setplane(struct drm_device *dev, void *data, plane_req->src_w, plane_req->src_h); if (!ret) { old_fb = plane->fb; - fb = NULL; plane->crtc = crtc; plane->fb = fb; + fb = NULL; } drm_modeset_unlock_all(dev); From 21a245d2d62ef617978316203af032d499805cd2 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 10 Dec 2012 10:49:46 -0600 Subject: [PATCH 2982/4607] drm: small fix in drm_send_vblank_event() Initialize e->pipe.. some drivers set this themselves, others do not. Setting it in drm_send_vblank_event() should help ensure more consistent behavior with the different drivers. Signed-off-by: Rob Clark --- drivers/gpu/drm/drm_irq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 38e79927b2d7..a6a8643a6a77 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -867,6 +867,7 @@ void drm_send_vblank_event(struct drm_device *dev, int crtc, now = get_drm_timestamp(); } + e->pipe = crtc; send_vblank_event(dev, e, seq, &now); } EXPORT_SYMBOL(drm_send_vblank_event); From 6f646095ec2f5d38a6429dd896cb2d5cbbb776dc Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Mon, 10 Dec 2012 10:46:43 -0600 Subject: [PATCH 2983/4607] drm/cma: add debugfs helpers Add helper to display fb's which can be used directly in drm_info_list: static struct drm_info_list foo_debugfs_list[] = { ... { "fb", drm_fb_cma_debugfs_show, 0 }, }; to display information about CMA fb objects, as well as a drm_gem_cma_describe() which can be used if the driver bothers to keep a list of CMA GEM objects. Signed-off-by: Rob Clark --- drivers/gpu/drm/drm_fb_cma_helper.c | 53 ++++++++++++++++++++++++++++ drivers/gpu/drm/drm_gem_cma_helper.c | 21 +++++++++++ include/drm/drm_fb_cma_helper.h | 5 +++ include/drm/drm_gem_cma_helper.h | 4 +++ 4 files changed, 83 insertions(+) diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index ef68e34b16ec..8044c5e56665 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -180,6 +180,59 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, } EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj); +#ifdef CONFIG_DEBUG_FS +/** + * drm_fb_cma_describe() - Helper to dump information about a single + * CMA framebuffer object + */ +void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m) +{ + struct drm_fb_cma *fb_cma = to_fb_cma(fb); + int i, n = drm_format_num_planes(fb->pixel_format); + + seq_printf(m, "fb: %dx%d@%4.4s\n", fb->width, fb->height, + (char *)&fb->pixel_format); + + for (i = 0; i < n; i++) { + seq_printf(m, " %d: offset=%d pitch=%d, obj: ", + i, fb->offsets[i], fb->pitches[i]); + drm_gem_cma_describe(fb_cma->obj[i], m); + } +} +EXPORT_SYMBOL_GPL(drm_fb_cma_describe); + +/** + * drm_fb_cma_debugfs_show() - Helper to list CMA framebuffer objects + * in debugfs. + */ +int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_framebuffer *fb; + int ret; + + ret = mutex_lock_interruptible(&dev->mode_config.mutex); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) { + mutex_unlock(&dev->mode_config.mutex); + return ret; + } + + list_for_each_entry(fb, &dev->mode_config.fb_list, head) + drm_fb_cma_describe(fb, m); + + mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev->mode_config.mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(drm_fb_cma_debugfs_show); +#endif + static struct fb_ops drm_fbdev_cma_ops = { .owner = THIS_MODULE, .fb_fillrect = sys_fillrect, diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 1aa8fee1e865..0a7e011509bd 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -249,3 +249,24 @@ int drm_gem_cma_dumb_destroy(struct drm_file *file_priv, return drm_gem_handle_delete(file_priv, handle); } EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_destroy); + +#ifdef CONFIG_DEBUG_FS +void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m) +{ + struct drm_gem_object *obj = &cma_obj->base; + struct drm_device *dev = obj->dev; + uint64_t off = 0; + + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + + if (obj->map_list.map) + off = (uint64_t)obj->map_list.hash.key; + + seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d", + obj->name, obj->refcount.refcount.counter, + off, cma_obj->paddr, cma_obj->vaddr, obj->size); + + seq_printf(m, "\n"); +} +EXPORT_SYMBOL_GPL(drm_gem_cma_describe); +#endif diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h index 76c709837543..4a3fc244301c 100644 --- a/include/drm/drm_fb_cma_helper.h +++ b/include/drm/drm_fb_cma_helper.h @@ -23,5 +23,10 @@ struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev, struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, unsigned int plane); +#ifdef CONFIG_DEBUG_FS +void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m); +int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg); +#endif + #endif diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h index f0f6b1af25ad..63397ced9254 100644 --- a/include/drm/drm_gem_cma_helper.h +++ b/include/drm/drm_gem_cma_helper.h @@ -41,4 +41,8 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, extern const struct vm_operations_struct drm_gem_cma_vm_ops; +#ifdef CONFIG_DEBUG_FS +void drm_gem_cma_describe(struct drm_gem_cma_object *obj, struct seq_file *m); +#endif + #endif /* __DRM_GEM_CMA_HELPER_H__ */ From a7c47d6dc4a201b811e847e5449c8cffdc556deb Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 8 Jan 2013 17:50:48 -0600 Subject: [PATCH 2984/4607] drm: i2c encoder helper wrappers Simplify life for drivers using an encoder-slave, so that they can make their drm_encoder_helper_funcs const, rather than needing to dynamically allocate and populate them. Signed-off-by: Rob Clark --- drivers/gpu/drm/drm_encoder_slave.c | 63 +++++++++++++++++++++++++++++ include/drm/drm_encoder_slave.h | 20 +++++++++ 2 files changed, 83 insertions(+) diff --git a/drivers/gpu/drm/drm_encoder_slave.c b/drivers/gpu/drm/drm_encoder_slave.c index 63e733408b6d..48c52f7df4e6 100644 --- a/drivers/gpu/drm/drm_encoder_slave.c +++ b/drivers/gpu/drm/drm_encoder_slave.c @@ -123,3 +123,66 @@ void drm_i2c_encoder_destroy(struct drm_encoder *drm_encoder) module_put(module); } EXPORT_SYMBOL(drm_i2c_encoder_destroy); + +/* + * Wrapper fxns which can be plugged in to drm_encoder_helper_funcs: + */ + +static inline struct drm_encoder_slave_funcs * +get_slave_funcs(struct drm_encoder *enc) +{ + return to_encoder_slave(enc)->slave_funcs; +} + +void drm_i2c_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + get_slave_funcs(encoder)->dpms(encoder, mode); +} +EXPORT_SYMBOL(drm_i2c_encoder_dpms); + +bool drm_i2c_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return get_slave_funcs(encoder)->mode_fixup(encoder, mode, adjusted_mode); +} +EXPORT_SYMBOL(drm_i2c_encoder_mode_fixup); + +void drm_i2c_encoder_prepare(struct drm_encoder *encoder) +{ + drm_i2c_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); +} +EXPORT_SYMBOL(drm_i2c_encoder_prepare); + +void drm_i2c_encoder_commit(struct drm_encoder *encoder) +{ + drm_i2c_encoder_dpms(encoder, DRM_MODE_DPMS_ON); +} +EXPORT_SYMBOL(drm_i2c_encoder_commit); + +void drm_i2c_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode); +} +EXPORT_SYMBOL(drm_i2c_encoder_mode_set); + +enum drm_connector_status drm_i2c_encoder_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + return get_slave_funcs(encoder)->detect(encoder, connector); +} +EXPORT_SYMBOL(drm_i2c_encoder_detect); + +void drm_i2c_encoder_save(struct drm_encoder *encoder) +{ + get_slave_funcs(encoder)->save(encoder); +} +EXPORT_SYMBOL(drm_i2c_encoder_save); + +void drm_i2c_encoder_restore(struct drm_encoder *encoder) +{ + get_slave_funcs(encoder)->restore(encoder); +} +EXPORT_SYMBOL(drm_i2c_encoder_restore); diff --git a/include/drm/drm_encoder_slave.h b/include/drm/drm_encoder_slave.h index b0c11a7809bb..8b9cc3671858 100644 --- a/include/drm/drm_encoder_slave.h +++ b/include/drm/drm_encoder_slave.h @@ -159,4 +159,24 @@ static inline void drm_i2c_encoder_unregister(struct drm_i2c_encoder_driver *dri void drm_i2c_encoder_destroy(struct drm_encoder *encoder); + +/* + * Wrapper fxns which can be plugged in to drm_encoder_helper_funcs: + */ + +void drm_i2c_encoder_dpms(struct drm_encoder *encoder, int mode); +bool drm_i2c_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +void drm_i2c_encoder_prepare(struct drm_encoder *encoder); +void drm_i2c_encoder_commit(struct drm_encoder *encoder); +void drm_i2c_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +enum drm_connector_status drm_i2c_encoder_detect(struct drm_encoder *encoder, + struct drm_connector *connector); +void drm_i2c_encoder_save(struct drm_encoder *encoder); +void drm_i2c_encoder_restore(struct drm_encoder *encoder); + + #endif From b3d3de80133d32723224329f87edecaff230fefc Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 8 Jan 2013 19:19:13 -0600 Subject: [PATCH 2985/4607] drm/nouveau: use i2c encoder helper wrappers Signed-off-by: Rob Clark --- drivers/gpu/drm/nouveau/nv04_tv.c | 39 +++++++++++-------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c index 62e826a139b3..4a69ccdef9b4 100644 --- a/drivers/gpu/drm/nouveau/nv04_tv.c +++ b/drivers/gpu/drm/nouveau/nv04_tv.c @@ -184,14 +184,23 @@ static const struct drm_encoder_funcs nv04_tv_funcs = { .destroy = nv04_tv_destroy, }; +static const struct drm_encoder_helper_funcs nv04_tv_helper_funcs = { + .dpms = nv04_tv_dpms, + .save = drm_i2c_encoder_save, + .restore = drm_i2c_encoder_restore, + .mode_fixup = drm_i2c_encoder_mode_fixup, + .prepare = nv04_tv_prepare, + .commit = nv04_tv_commit, + .mode_set = nv04_tv_mode_set, + .detect = drm_i2c_encoder_detect, +}; + int nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) { struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; struct drm_device *dev = connector->dev; - struct drm_encoder_helper_funcs *hfuncs; - struct drm_encoder_slave_funcs *sfuncs; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_i2c *i2c = nouveau_i2c(drm->device); struct nouveau_i2c_port *port = i2c->find(i2c, entry->i2c_index); @@ -207,17 +216,11 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) if (!nv_encoder) return -ENOMEM; - hfuncs = kzalloc(sizeof(*hfuncs), GFP_KERNEL); - if (!hfuncs) { - ret = -ENOMEM; - goto fail_free; - } - /* Initialize the common members */ encoder = to_drm_encoder(nv_encoder); drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC); - drm_encoder_helper_add(encoder, hfuncs); + drm_encoder_helper_add(encoder, &nv04_tv_helper_funcs); encoder->possible_crtcs = entry->heads; encoder->possible_clones = 0; @@ -230,30 +233,14 @@ nv04_tv_create(struct drm_connector *connector, struct dcb_output *entry) if (ret < 0) goto fail_cleanup; - /* Fill the function pointers */ - sfuncs = get_slave_funcs(encoder); - - *hfuncs = (struct drm_encoder_helper_funcs) { - .dpms = nv04_tv_dpms, - .save = sfuncs->save, - .restore = sfuncs->restore, - .mode_fixup = sfuncs->mode_fixup, - .prepare = nv04_tv_prepare, - .commit = nv04_tv_commit, - .mode_set = nv04_tv_mode_set, - .detect = sfuncs->detect, - }; - /* Attach it to the specified connector. */ - sfuncs->create_resources(encoder, connector); + get_slave_funcs(encoder)->create_resources(encoder, connector); drm_mode_connector_attach_encoder(connector, encoder); return 0; fail_cleanup: drm_encoder_cleanup(encoder); - kfree(hfuncs); -fail_free: kfree(nv_encoder); return ret; } From 06b0c886214a223dde7b21cbfc3008fd20a8ce16 Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Mon, 18 Feb 2013 00:26:51 -0500 Subject: [PATCH 2986/4607] ext4: refine extent status tree This commit refines the extent status tree code. 1) A prefix 'es_' is added to to the extent status tree structure members. 2) Refactored es_remove_extent() so that __es_remove_extent() can be used by es_insert_extent() to remove the old extent entry(-ies) before inserting a new one. 3) Rename extent_status_end() to ext4_es_end() 4) ext4_es_can_be_merged() is define to check whether two extents can be merged or not. 5) Update and clarified comments. Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" Reviewed-by: Jan Kara --- fs/ext4/extents.c | 21 +-- fs/ext4/extents_status.c | 322 ++++++++++++++++++++---------------- fs/ext4/extents_status.h | 8 +- fs/ext4/file.c | 12 +- include/trace/events/ext4.h | 40 ++--- 5 files changed, 221 insertions(+), 182 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index b6b54d658dc2..37f94a751ad7 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -3528,13 +3528,14 @@ static int ext4_find_delalloc_range(struct inode *inode, { struct extent_status es; - es.start = lblk_start; - ext4_es_find_extent(inode, &es); - if (es.len == 0) + es.es_lblk = lblk_start; + (void)ext4_es_find_extent(inode, &es); + if (es.es_len == 0) return 0; /* there is no delay extent in this tree */ - else if (es.start <= lblk_start && lblk_start < es.start + es.len) + else if (es.es_lblk <= lblk_start && + lblk_start < es.es_lblk + es.es_len) return 1; - else if (lblk_start <= es.start && es.start <= lblk_end) + else if (lblk_start <= es.es_lblk && es.es_lblk <= lblk_end) return 1; else return 0; @@ -4569,7 +4570,7 @@ static int ext4_find_delayed_extent(struct inode *inode, struct extent_status es; ext4_lblk_t next_del; - es.start = newex->ec_block; + es.es_lblk = newex->ec_block; next_del = ext4_es_find_extent(inode, &es); if (newex->ec_start == 0) { @@ -4577,18 +4578,18 @@ static int ext4_find_delayed_extent(struct inode *inode, * No extent in extent-tree contains block @newex->ec_start, * then the block may stay in 1)a hole or 2)delayed-extent. */ - if (es.len == 0) + if (es.es_len == 0) /* A hole found. */ return 0; - if (es.start > newex->ec_block) { + if (es.es_lblk > newex->ec_block) { /* A hole found. */ - newex->ec_len = min(es.start - newex->ec_block, + newex->ec_len = min(es.es_lblk - newex->ec_block, newex->ec_len); return 0; } - newex->ec_len = es.start + es.len - newex->ec_block; + newex->ec_len = es.es_lblk + es.es_len - newex->ec_block; } return next_del; diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index 564d981a2fcc..c9921cf4f817 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -23,40 +23,53 @@ * (e.g. Reservation space warning), and provide extent-level locking. * Delay extent tree is the first step to achieve this goal. It is * original built by Yongqiang Yang. At that time it is called delay - * extent tree, whose goal is only track delay extent in memory to + * extent tree, whose goal is only track delayed extents in memory to * simplify the implementation of fiemap and bigalloc, and introduce * lseek SEEK_DATA/SEEK_HOLE support. That is why it is still called - * delay extent tree at the following comment. But for better - * understand what it does, it has been rename to extent status tree. + * delay extent tree at the first commit. But for better understand + * what it does, it has been rename to extent status tree. * - * Currently the first step has been done. All delay extents are - * tracked in the tree. It maintains the delay extent when a delay - * allocation is issued, and the delay extent is written out or + * Step1: + * Currently the first step has been done. All delayed extents are + * tracked in the tree. It maintains the delayed extent when a delayed + * allocation is issued, and the delayed extent is written out or * invalidated. Therefore the implementation of fiemap and bigalloc * are simplified, and SEEK_DATA/SEEK_HOLE are introduced. * * The following comment describes the implemenmtation of extent * status tree and future works. + * + * Step2: + * In this step all extent status are tracked by extent status tree. + * Thus, we can first try to lookup a block mapping in this tree before + * finding it in extent tree. Hence, single extent cache can be removed + * because extent status tree can do a better job. Extents in status + * tree are loaded on-demand. Therefore, the extent status tree may not + * contain all of the extents in a file. Meanwhile we define a shrinker + * to reclaim memory from extent status tree because fragmented extent + * tree will make status tree cost too much memory. written/unwritten/- + * hole extents in the tree will be reclaimed by this shrinker when we + * are under high memory pressure. Delayed extents will not be + * reclimed because fiemap, bigalloc, and seek_data/hole need it. */ /* - * extents status tree implementation for ext4. + * Extent status tree implementation for ext4. * * * ========================================================================== - * Extents status encompass delayed extents and extent locks + * Extent status tree tracks all extent status. * - * 1. Why delayed extent implementation ? + * 1. Why we need to implement extent status tree? * - * Without delayed extent, ext4 identifies a delayed extent by looking + * Without extent status tree, ext4 identifies a delayed extent by looking * up page cache, this has several deficiencies - complicated, buggy, * and inefficient code. * - * FIEMAP, SEEK_HOLE/DATA, bigalloc, punch hole and writeout all need - * to know if a block or a range of blocks are belonged to a delayed - * extent. + * FIEMAP, SEEK_HOLE/DATA, bigalloc, and writeout all need to know if a + * block or a range of blocks are belonged to a delayed extent. * - * Let us have a look at how they do without delayed extents implementation. + * Let us have a look at how they do without extent status tree. * -- FIEMAP * FIEMAP looks up page cache to identify delayed allocations from holes. * @@ -68,47 +81,48 @@ * already under delayed allocation or not to determine whether * quota reserving is needed for the cluster. * - * -- punch hole - * punch hole looks up page cache to identify a delayed extent. - * * -- writeout * Writeout looks up whole page cache to see if a buffer is * mapped, If there are not very many delayed buffers, then it is * time comsuming. * - * With delayed extents implementation, FIEMAP, SEEK_HOLE/DATA, + * With extent status tree implementation, FIEMAP, SEEK_HOLE/DATA, * bigalloc and writeout can figure out if a block or a range of * blocks is under delayed allocation(belonged to a delayed extent) or - * not by searching the delayed extent tree. + * not by searching the extent tree. * * * ========================================================================== - * 2. ext4 delayed extents impelmentation + * 2. Ext4 extent status tree impelmentation * - * -- delayed extent - * A delayed extent is a range of blocks which are contiguous - * logically and under delayed allocation. Unlike extent in - * ext4, delayed extent in ext4 is a in-memory struct, there is - * no corresponding on-disk data. There is no limit on length of - * delayed extent, so a delayed extent can contain as many blocks - * as they are contiguous logically. + * -- extent + * A extent is a range of blocks which are contiguous logically and + * physically. Unlike extent in extent tree, this extent in ext4 is + * a in-memory struct, there is no corresponding on-disk data. There + * is no limit on length of extent, so an extent can contain as many + * blocks as they are contiguous logically and physically. * - * -- delayed extent tree - * Every inode has a delayed extent tree and all under delayed - * allocation blocks are added to the tree as delayed extents. - * Delayed extents in the tree are ordered by logical block no. + * -- extent status tree + * Every inode has an extent status tree and all allocation blocks + * are added to the tree with different status. The extent in the + * tree are ordered by logical block no. * - * -- operations on a delayed extent tree - * There are three operations on a delayed extent tree: find next - * delayed extent, adding a space(a range of blocks) and removing - * a space. + * -- operations on a extent status tree + * There are three important operations on a delayed extent tree: find + * next extent, adding a extent(a range of blocks) and removing a extent. * - * -- race on a delayed extent tree - * Delayed extent tree is protected inode->i_es_lock. + * -- race on a extent status tree + * Extent status tree is protected by inode->i_es_lock. + * + * -- memory consumption + * Fragmented extent tree will make extent status tree cost too much + * memory. Hence, we will reclaim written/unwritten/hole extents from + * the tree under a heavy memory pressure. * * * ========================================================================== - * 3. performance analysis + * 3. Performance analysis + * * -- overhead * 1. There is a cache extent for write access, so if writes are * not very random, adding space operaions are in O(1) time. @@ -120,15 +134,19 @@ * * ========================================================================== * 4. TODO list - * -- Track all extent status * - * -- Improve get block process + * -- Refactor delayed space reservation * * -- Extent-level locking */ static struct kmem_cache *ext4_es_cachep; +static int __es_insert_extent(struct ext4_es_tree *tree, + struct extent_status *newes); +static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, + ext4_lblk_t end); + int __init ext4_init_es(void) { ext4_es_cachep = KMEM_CACHE(extent_status, SLAB_RECLAIM_ACCOUNT); @@ -161,7 +179,7 @@ static void ext4_es_print_tree(struct inode *inode) while (node) { struct extent_status *es; es = rb_entry(node, struct extent_status, rb_node); - printk(KERN_DEBUG " [%u/%u)", es->start, es->len); + printk(KERN_DEBUG " [%u/%u)", es->es_lblk, es->es_len); node = rb_next(node); } printk(KERN_DEBUG "\n"); @@ -170,10 +188,10 @@ static void ext4_es_print_tree(struct inode *inode) #define ext4_es_print_tree(inode) #endif -static inline ext4_lblk_t extent_status_end(struct extent_status *es) +static inline ext4_lblk_t ext4_es_end(struct extent_status *es) { - BUG_ON(es->start + es->len < es->start); - return es->start + es->len - 1; + BUG_ON(es->es_lblk + es->es_len < es->es_lblk); + return es->es_lblk + es->es_len - 1; } /* @@ -181,25 +199,25 @@ static inline ext4_lblk_t extent_status_end(struct extent_status *es) * it can't be found, try to find next extent. */ static struct extent_status *__es_tree_search(struct rb_root *root, - ext4_lblk_t offset) + ext4_lblk_t lblk) { struct rb_node *node = root->rb_node; struct extent_status *es = NULL; while (node) { es = rb_entry(node, struct extent_status, rb_node); - if (offset < es->start) + if (lblk < es->es_lblk) node = node->rb_left; - else if (offset > extent_status_end(es)) + else if (lblk > ext4_es_end(es)) node = node->rb_right; else return es; } - if (es && offset < es->start) + if (es && lblk < es->es_lblk) return es; - if (es && offset > extent_status_end(es)) { + if (es && lblk > ext4_es_end(es)) { node = rb_next(&es->rb_node); return node ? rb_entry(node, struct extent_status, rb_node) : NULL; @@ -209,8 +227,8 @@ static struct extent_status *__es_tree_search(struct rb_root *root, } /* - * ext4_es_find_extent: find the 1st delayed extent covering @es->start - * if it exists, otherwise, the next extent after @es->start. + * ext4_es_find_extent: find the 1st delayed extent covering @es->lblk + * if it exists, otherwise, the next extent after @es->lblk. * * @inode: the inode which owns delayed extents * @es: delayed extent that we found @@ -226,7 +244,7 @@ ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es) struct rb_node *node; ext4_lblk_t ret = EXT_MAX_BLOCKS; - trace_ext4_es_find_extent_enter(inode, es->start); + trace_ext4_es_find_extent_enter(inode, es->es_lblk); read_lock(&EXT4_I(inode)->i_es_lock); tree = &EXT4_I(inode)->i_es_tree; @@ -234,25 +252,25 @@ ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es) /* find delay extent in cache firstly */ if (tree->cache_es) { es1 = tree->cache_es; - if (in_range(es->start, es1->start, es1->len)) { + if (in_range(es->es_lblk, es1->es_lblk, es1->es_len)) { es_debug("%u cached by [%u/%u)\n", - es->start, es1->start, es1->len); + es->es_lblk, es1->es_lblk, es1->es_len); goto out; } } - es->len = 0; - es1 = __es_tree_search(&tree->root, es->start); + es->es_len = 0; + es1 = __es_tree_search(&tree->root, es->es_lblk); out: if (es1) { tree->cache_es = es1; - es->start = es1->start; - es->len = es1->len; + es->es_lblk = es1->es_lblk; + es->es_len = es1->es_len; node = rb_next(&es1->rb_node); if (node) { es1 = rb_entry(node, struct extent_status, rb_node); - ret = es1->start; + ret = es1->es_lblk; } } @@ -263,14 +281,14 @@ out: } static struct extent_status * -ext4_es_alloc_extent(ext4_lblk_t start, ext4_lblk_t len) +ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len) { struct extent_status *es; es = kmem_cache_alloc(ext4_es_cachep, GFP_ATOMIC); if (es == NULL) return NULL; - es->start = start; - es->len = len; + es->es_lblk = lblk; + es->es_len = len; return es; } @@ -279,6 +297,20 @@ static void ext4_es_free_extent(struct extent_status *es) kmem_cache_free(ext4_es_cachep, es); } +/* + * Check whether or not two extents can be merged + * Condition: + * - logical block number is contiguous + */ +static int ext4_es_can_be_merged(struct extent_status *es1, + struct extent_status *es2) +{ + if (es1->es_lblk + es1->es_len != es2->es_lblk) + return 0; + + return 1; +} + static struct extent_status * ext4_es_try_to_merge_left(struct ext4_es_tree *tree, struct extent_status *es) { @@ -290,8 +322,8 @@ ext4_es_try_to_merge_left(struct ext4_es_tree *tree, struct extent_status *es) return es; es1 = rb_entry(node, struct extent_status, rb_node); - if (es->start == extent_status_end(es1) + 1) { - es1->len += es->len; + if (ext4_es_can_be_merged(es1, es)) { + es1->es_len += es->es_len; rb_erase(&es->rb_node, &tree->root); ext4_es_free_extent(es); es = es1; @@ -311,8 +343,8 @@ ext4_es_try_to_merge_right(struct ext4_es_tree *tree, struct extent_status *es) return es; es1 = rb_entry(node, struct extent_status, rb_node); - if (es1->start == extent_status_end(es) + 1) { - es->len += es1->len; + if (ext4_es_can_be_merged(es, es1)) { + es->es_len += es1->es_len; rb_erase(node, &tree->root); ext4_es_free_extent(es1); } @@ -320,60 +352,43 @@ ext4_es_try_to_merge_right(struct ext4_es_tree *tree, struct extent_status *es) return es; } -static int __es_insert_extent(struct ext4_es_tree *tree, ext4_lblk_t offset, - ext4_lblk_t len) +static int __es_insert_extent(struct ext4_es_tree *tree, + struct extent_status *newes) { struct rb_node **p = &tree->root.rb_node; struct rb_node *parent = NULL; struct extent_status *es; - ext4_lblk_t end = offset + len - 1; - - BUG_ON(end < offset); - es = tree->cache_es; - if (es && offset == (extent_status_end(es) + 1)) { - es_debug("cached by [%u/%u)\n", es->start, es->len); - es->len += len; - es = ext4_es_try_to_merge_right(tree, es); - goto out; - } else if (es && es->start == end + 1) { - es_debug("cached by [%u/%u)\n", es->start, es->len); - es->start = offset; - es->len += len; - es = ext4_es_try_to_merge_left(tree, es); - goto out; - } else if (es && es->start <= offset && - end <= extent_status_end(es)) { - es_debug("cached by [%u/%u)\n", es->start, es->len); - goto out; - } while (*p) { parent = *p; es = rb_entry(parent, struct extent_status, rb_node); - if (offset < es->start) { - if (es->start == end + 1) { - es->start = offset; - es->len += len; + if (newes->es_lblk < es->es_lblk) { + if (ext4_es_can_be_merged(newes, es)) { + /* + * Here we can modify es_lblk directly + * because it isn't overlapped. + */ + es->es_lblk = newes->es_lblk; + es->es_len += newes->es_len; es = ext4_es_try_to_merge_left(tree, es); goto out; } p = &(*p)->rb_left; - } else if (offset > extent_status_end(es)) { - if (offset == extent_status_end(es) + 1) { - es->len += len; + } else if (newes->es_lblk > ext4_es_end(es)) { + if (ext4_es_can_be_merged(es, newes)) { + es->es_len += newes->es_len; es = ext4_es_try_to_merge_right(tree, es); goto out; } p = &(*p)->rb_right; } else { - if (extent_status_end(es) <= end) - es->len = offset - es->start + len; - goto out; + BUG_ON(1); + return -EINVAL; } } - es = ext4_es_alloc_extent(offset, len); + es = ext4_es_alloc_extent(newes->es_lblk, newes->es_len); if (!es) return -ENOMEM; rb_link_node(&es->rb_node, parent, p); @@ -385,27 +400,38 @@ out: } /* - * ext4_es_insert_extent() adds a space to a delayed extent tree. - * Caller holds inode->i_es_lock. + * ext4_es_insert_extent() adds a space to a extent status tree. * * ext4_es_insert_extent is called by ext4_da_write_begin and * ext4_es_remove_extent. * * Return 0 on success, error code on failure. */ -int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t offset, +int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len) { struct ext4_es_tree *tree; + struct extent_status newes; + ext4_lblk_t end = lblk + len - 1; int err = 0; - trace_ext4_es_insert_extent(inode, offset, len); + trace_ext4_es_insert_extent(inode, lblk, len); es_debug("add [%u/%u) to extent status tree of inode %lu\n", - offset, len, inode->i_ino); + lblk, len, inode->i_ino); + + BUG_ON(end < lblk); + + newes.es_lblk = lblk; + newes.es_len = len; write_lock(&EXT4_I(inode)->i_es_lock); tree = &EXT4_I(inode)->i_es_tree; - err = __es_insert_extent(tree, offset, len); + err = __es_remove_extent(tree, lblk, end); + if (err != 0) + goto error; + err = __es_insert_extent(tree, &newes); + +error: write_unlock(&EXT4_I(inode)->i_es_lock); ext4_es_print_tree(inode); @@ -413,57 +439,45 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t offset, return err; } -/* - * ext4_es_remove_extent() removes a space from a delayed extent tree. - * Caller holds inode->i_es_lock. - * - * Return 0 on success, error code on failure. - */ -int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t offset, - ext4_lblk_t len) +static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, + ext4_lblk_t end) { struct rb_node *node; - struct ext4_es_tree *tree; struct extent_status *es; struct extent_status orig_es; - ext4_lblk_t len1, len2, end; + ext4_lblk_t len1, len2; int err = 0; - trace_ext4_es_remove_extent(inode, offset, len); - es_debug("remove [%u/%u) from extent status tree of inode %lu\n", - offset, len, inode->i_ino); - - end = offset + len - 1; - BUG_ON(end < offset); - write_lock(&EXT4_I(inode)->i_es_lock); - tree = &EXT4_I(inode)->i_es_tree; - es = __es_tree_search(&tree->root, offset); + es = __es_tree_search(&tree->root, lblk); if (!es) goto out; - if (es->start > end) + if (es->es_lblk > end) goto out; /* Simply invalidate cache_es. */ tree->cache_es = NULL; - orig_es.start = es->start; - orig_es.len = es->len; - len1 = offset > es->start ? offset - es->start : 0; - len2 = extent_status_end(es) > end ? - extent_status_end(es) - end : 0; + orig_es.es_lblk = es->es_lblk; + orig_es.es_len = es->es_len; + len1 = lblk > es->es_lblk ? lblk - es->es_lblk : 0; + len2 = ext4_es_end(es) > end ? ext4_es_end(es) - end : 0; if (len1 > 0) - es->len = len1; + es->es_len = len1; if (len2 > 0) { if (len1 > 0) { - err = __es_insert_extent(tree, end + 1, len2); + struct extent_status newes; + + newes.es_lblk = end + 1; + newes.es_len = len2; + err = __es_insert_extent(tree, &newes); if (err) { - es->start = orig_es.start; - es->len = orig_es.len; + es->es_lblk = orig_es.es_lblk; + es->es_len = orig_es.es_len; goto out; } } else { - es->start = end + 1; - es->len = len2; + es->es_lblk = end + 1; + es->es_len = len2; } goto out; } @@ -476,7 +490,7 @@ int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t offset, es = NULL; } - while (es && extent_status_end(es) <= end) { + while (es && ext4_es_end(es) <= end) { node = rb_next(&es->rb_node); rb_erase(&es->rb_node, &tree->root); ext4_es_free_extent(es); @@ -487,13 +501,39 @@ int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t offset, es = rb_entry(node, struct extent_status, rb_node); } - if (es && es->start < end + 1) { - len1 = extent_status_end(es) - end; - es->start = end + 1; - es->len = len1; + if (es && es->es_lblk < end + 1) { + len1 = ext4_es_end(es) - end; + es->es_lblk = end + 1; + es->es_len = len1; } out: + return err; +} + +/* + * ext4_es_remove_extent() removes a space from a extent status tree. + * + * Return 0 on success, error code on failure. + */ +int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, + ext4_lblk_t len) +{ + struct ext4_es_tree *tree; + ext4_lblk_t end; + int err = 0; + + trace_ext4_es_remove_extent(inode, lblk, len); + es_debug("remove [%u/%u) from extent status tree of inode %lu\n", + lblk, len, inode->i_ino); + + end = lblk + len - 1; + BUG_ON(end < lblk); + + tree = &EXT4_I(inode)->i_es_tree; + + write_lock(&EXT4_I(inode)->i_es_lock); + err = __es_remove_extent(tree, lblk, end); write_unlock(&EXT4_I(inode)->i_es_lock); ext4_es_print_tree(inode); return err; diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index 077f82db092a..81e9339f23f1 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h @@ -22,8 +22,8 @@ struct extent_status { struct rb_node rb_node; - ext4_lblk_t start; /* first block extent covers */ - ext4_lblk_t len; /* length of extent in block */ + ext4_lblk_t es_lblk; /* first logical block extent covers */ + ext4_lblk_t es_len; /* length of extent in block */ }; struct ext4_es_tree { @@ -35,9 +35,9 @@ extern int __init ext4_init_es(void); extern void ext4_exit_es(void); extern void ext4_es_init_tree(struct ext4_es_tree *tree); -extern int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t start, +extern int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len); -extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t start, +extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len); extern ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es); diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 2cf8ab810687..2df9354b105e 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -464,10 +464,9 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize) * If there is a delay extent at this offset, * it will be as a data. */ - es.start = last; + es.es_lblk = last; (void)ext4_es_find_extent(inode, &es); - if (last >= es.start && - last < es.start + es.len) { + if (es.es_len != 0 && in_range(last, es.es_lblk, es.es_len)) { if (last != start) dataoff = last << blkbits; break; @@ -549,11 +548,10 @@ static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize) * If there is a delay extent at this offset, * we will skip this extent. */ - es.start = last; + es.es_lblk = last; (void)ext4_es_find_extent(inode, &es); - if (last >= es.start && - last < es.start + es.len) { - last = es.start + es.len; + if (es.es_len != 0 && in_range(last, es.es_lblk, es.es_len)) { + last = es.es_lblk + es.es_len; holeoff = last << blkbits; continue; } diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 6080ea1380b8..52c923851959 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -2093,75 +2093,75 @@ TRACE_EVENT(ext4_ext_remove_space_done, ); TRACE_EVENT(ext4_es_insert_extent, - TP_PROTO(struct inode *inode, ext4_lblk_t start, ext4_lblk_t len), + TP_PROTO(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len), - TP_ARGS(inode, start, len), + TP_ARGS(inode, lblk, len), TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, ino ) - __field( loff_t, start ) + __field( loff_t, lblk ) __field( loff_t, len ) ), TP_fast_assign( __entry->dev = inode->i_sb->s_dev; __entry->ino = inode->i_ino; - __entry->start = start; + __entry->lblk = lblk; __entry->len = len; ), TP_printk("dev %d,%d ino %lu es [%lld/%lld)", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - __entry->start, __entry->len) + __entry->lblk, __entry->len) ); TRACE_EVENT(ext4_es_remove_extent, - TP_PROTO(struct inode *inode, ext4_lblk_t start, ext4_lblk_t len), + TP_PROTO(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len), - TP_ARGS(inode, start, len), + TP_ARGS(inode, lblk, len), TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, ino ) - __field( loff_t, start ) + __field( loff_t, lblk ) __field( loff_t, len ) ), TP_fast_assign( __entry->dev = inode->i_sb->s_dev; __entry->ino = inode->i_ino; - __entry->start = start; + __entry->lblk = lblk; __entry->len = len; ), TP_printk("dev %d,%d ino %lu es [%lld/%lld)", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - __entry->start, __entry->len) + __entry->lblk, __entry->len) ); TRACE_EVENT(ext4_es_find_extent_enter, - TP_PROTO(struct inode *inode, ext4_lblk_t start), + TP_PROTO(struct inode *inode, ext4_lblk_t lblk), - TP_ARGS(inode, start), + TP_ARGS(inode, lblk), TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, ino ) - __field( ext4_lblk_t, start ) + __field( ext4_lblk_t, lblk ) ), TP_fast_assign( __entry->dev = inode->i_sb->s_dev; __entry->ino = inode->i_ino; - __entry->start = start; + __entry->lblk = lblk; ), - TP_printk("dev %d,%d ino %lu start %u", + TP_printk("dev %d,%d ino %lu lblk %u", MAJOR(__entry->dev), MINOR(__entry->dev), - (unsigned long) __entry->ino, __entry->start) + (unsigned long) __entry->ino, __entry->lblk) ); TRACE_EVENT(ext4_es_find_extent_exit, @@ -2173,7 +2173,7 @@ TRACE_EVENT(ext4_es_find_extent_exit, TP_STRUCT__entry( __field( dev_t, dev ) __field( ino_t, ino ) - __field( ext4_lblk_t, start ) + __field( ext4_lblk_t, lblk ) __field( ext4_lblk_t, len ) __field( ext4_lblk_t, ret ) ), @@ -2181,15 +2181,15 @@ TRACE_EVENT(ext4_es_find_extent_exit, TP_fast_assign( __entry->dev = inode->i_sb->s_dev; __entry->ino = inode->i_ino; - __entry->start = es->start; - __entry->len = es->len; + __entry->lblk = es->es_lblk; + __entry->len = es->es_len; __entry->ret = ret; ), TP_printk("dev %d,%d ino %lu es [%u/%u) ret %u", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - __entry->start, __entry->len, __entry->ret) + __entry->lblk, __entry->len, __entry->ret) ); #endif /* _TRACE_EXT4_H */ From fdc0212e86ca15c5cfed77088af7cc5eb79ccbc7 Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Mon, 18 Feb 2013 00:26:51 -0500 Subject: [PATCH 2987/4607] ext4: add physical block and status member into extent status tree This commit adds two members in extent_status structure to let it record physical block and extent status. Here es_pblk is used to record both of them because physical block only has 48 bits. So extent status could be stashed into it so that we can save some memory. Now written, unwritten, delayed and hole are defined as status. Due to new member is added into extent status tree, all interfaces need to be adjusted. Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" Reviewed-by: Jan Kara --- fs/ext4/extents_status.c | 67 ++++++++++++++++++++++++++++++------- fs/ext4/extents_status.h | 64 ++++++++++++++++++++++++++++++++++- fs/ext4/inode.c | 3 +- include/trace/events/ext4.h | 34 ++++++++++++------- 4 files changed, 142 insertions(+), 26 deletions(-) diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index c9921cf4f817..1f5fd44993e9 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -179,7 +179,9 @@ static void ext4_es_print_tree(struct inode *inode) while (node) { struct extent_status *es; es = rb_entry(node, struct extent_status, rb_node); - printk(KERN_DEBUG " [%u/%u)", es->es_lblk, es->es_len); + printk(KERN_DEBUG " [%u/%u) %llu %llx", + es->es_lblk, es->es_len, + ext4_es_pblock(es), ext4_es_status(es)); node = rb_next(node); } printk(KERN_DEBUG "\n"); @@ -234,7 +236,7 @@ static struct extent_status *__es_tree_search(struct rb_root *root, * @es: delayed extent that we found * * Returns the first block of the next extent after es, otherwise - * EXT_MAX_BLOCKS if no delay extent is found. + * EXT_MAX_BLOCKS if no extent is found. * Delayed extent is returned via @es. */ ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es) @@ -249,17 +251,18 @@ ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es) read_lock(&EXT4_I(inode)->i_es_lock); tree = &EXT4_I(inode)->i_es_tree; - /* find delay extent in cache firstly */ + /* find extent in cache firstly */ + es->es_len = es->es_pblk = 0; if (tree->cache_es) { es1 = tree->cache_es; if (in_range(es->es_lblk, es1->es_lblk, es1->es_len)) { - es_debug("%u cached by [%u/%u)\n", - es->es_lblk, es1->es_lblk, es1->es_len); + es_debug("%u cached by [%u/%u) %llu %llx\n", + es->es_lblk, es1->es_lblk, es1->es_len, + ext4_es_pblock(es1), ext4_es_status(es1)); goto out; } } - es->es_len = 0; es1 = __es_tree_search(&tree->root, es->es_lblk); out: @@ -267,6 +270,7 @@ out: tree->cache_es = es1; es->es_lblk = es1->es_lblk; es->es_len = es1->es_len; + es->es_pblk = es1->es_pblk; node = rb_next(&es1->rb_node); if (node) { es1 = rb_entry(node, struct extent_status, rb_node); @@ -281,7 +285,7 @@ out: } static struct extent_status * -ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len) +ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len, ext4_fsblk_t pblk) { struct extent_status *es; es = kmem_cache_alloc(ext4_es_cachep, GFP_ATOMIC); @@ -289,6 +293,7 @@ ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len) return NULL; es->es_lblk = lblk; es->es_len = len; + es->es_pblk = pblk; return es; } @@ -301,6 +306,8 @@ static void ext4_es_free_extent(struct extent_status *es) * Check whether or not two extents can be merged * Condition: * - logical block number is contiguous + * - physical block number is contiguous + * - status is equal */ static int ext4_es_can_be_merged(struct extent_status *es1, struct extent_status *es2) @@ -308,6 +315,13 @@ static int ext4_es_can_be_merged(struct extent_status *es1, if (es1->es_lblk + es1->es_len != es2->es_lblk) return 0; + if (ext4_es_status(es1) != ext4_es_status(es2)) + return 0; + + if ((ext4_es_is_written(es1) || ext4_es_is_unwritten(es1)) && + (ext4_es_pblock(es1) + es1->es_len != ext4_es_pblock(es2))) + return 0; + return 1; } @@ -371,6 +385,10 @@ static int __es_insert_extent(struct ext4_es_tree *tree, */ es->es_lblk = newes->es_lblk; es->es_len += newes->es_len; + if (ext4_es_is_written(es) || + ext4_es_is_unwritten(es)) + ext4_es_store_pblock(es, + newes->es_pblk); es = ext4_es_try_to_merge_left(tree, es); goto out; } @@ -388,7 +406,8 @@ static int __es_insert_extent(struct ext4_es_tree *tree, } } - es = ext4_es_alloc_extent(newes->es_lblk, newes->es_len); + es = ext4_es_alloc_extent(newes->es_lblk, newes->es_len, + newes->es_pblk); if (!es) return -ENOMEM; rb_link_node(&es->rb_node, parent, p); @@ -408,21 +427,24 @@ out: * Return 0 on success, error code on failure. */ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, - ext4_lblk_t len) + ext4_lblk_t len, ext4_fsblk_t pblk, + unsigned long long status) { struct ext4_es_tree *tree; struct extent_status newes; ext4_lblk_t end = lblk + len - 1; int err = 0; - trace_ext4_es_insert_extent(inode, lblk, len); - es_debug("add [%u/%u) to extent status tree of inode %lu\n", - lblk, len, inode->i_ino); + es_debug("add [%u/%u) %llu %llx to extent status tree of inode %lu\n", + lblk, len, pblk, status, inode->i_ino); BUG_ON(end < lblk); newes.es_lblk = lblk; newes.es_len = len; + ext4_es_store_pblock(&newes, pblk); + ext4_es_store_status(&newes, status); + trace_ext4_es_insert_extent(inode, &newes); write_lock(&EXT4_I(inode)->i_es_lock); tree = &EXT4_I(inode)->i_es_tree; @@ -446,6 +468,7 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, struct extent_status *es; struct extent_status orig_es; ext4_lblk_t len1, len2; + ext4_fsblk_t block; int err = 0; es = __es_tree_search(&tree->root, lblk); @@ -459,6 +482,8 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, orig_es.es_lblk = es->es_lblk; orig_es.es_len = es->es_len; + orig_es.es_pblk = es->es_pblk; + len1 = lblk > es->es_lblk ? lblk - es->es_lblk : 0; len2 = ext4_es_end(es) > end ? ext4_es_end(es) - end : 0; if (len1 > 0) @@ -469,6 +494,13 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, newes.es_lblk = end + 1; newes.es_len = len2; + if (ext4_es_is_written(&orig_es) || + ext4_es_is_unwritten(&orig_es)) { + block = ext4_es_pblock(&orig_es) + + orig_es.es_len - len2; + ext4_es_store_pblock(&newes, block); + } + ext4_es_store_status(&newes, ext4_es_status(&orig_es)); err = __es_insert_extent(tree, &newes); if (err) { es->es_lblk = orig_es.es_lblk; @@ -478,6 +510,11 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, } else { es->es_lblk = end + 1; es->es_len = len2; + if (ext4_es_is_written(es) || + ext4_es_is_unwritten(es)) { + block = orig_es.es_pblk + orig_es.es_len - len2; + ext4_es_store_pblock(es, block); + } } goto out; } @@ -502,9 +539,15 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, } if (es && es->es_lblk < end + 1) { + ext4_lblk_t orig_len = es->es_len; + len1 = ext4_es_end(es) - end; es->es_lblk = end + 1; es->es_len = len1; + if (ext4_es_is_written(es) || ext4_es_is_unwritten(es)) { + block = es->es_pblk + orig_len - len1; + ext4_es_store_pblock(es, block); + } } out: diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index 81e9339f23f1..3cad83303adb 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h @@ -20,10 +20,21 @@ #define es_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__) #endif +#define EXTENT_STATUS_WRITTEN 0x80000000 /* written extent */ +#define EXTENT_STATUS_UNWRITTEN 0x40000000 /* unwritten extent */ +#define EXTENT_STATUS_DELAYED 0x20000000 /* delayed extent */ +#define EXTENT_STATUS_HOLE 0x10000000 /* hole */ + +#define EXTENT_STATUS_FLAGS (EXTENT_STATUS_WRITTEN | \ + EXTENT_STATUS_UNWRITTEN | \ + EXTENT_STATUS_DELAYED | \ + EXTENT_STATUS_HOLE) + struct extent_status { struct rb_node rb_node; ext4_lblk_t es_lblk; /* first logical block extent covers */ ext4_lblk_t es_len; /* length of extent in block */ + ext4_fsblk_t es_pblk; /* first physical block */ }; struct ext4_es_tree { @@ -36,10 +47,61 @@ extern void ext4_exit_es(void); extern void ext4_es_init_tree(struct ext4_es_tree *tree); extern int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, - ext4_lblk_t len); + ext4_lblk_t len, ext4_fsblk_t pblk, + unsigned long long status); extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len); extern ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es); +static inline int ext4_es_is_written(struct extent_status *es) +{ + return (es->es_pblk & EXTENT_STATUS_WRITTEN); +} + +static inline int ext4_es_is_unwritten(struct extent_status *es) +{ + return (es->es_pblk & EXTENT_STATUS_UNWRITTEN); +} + +static inline int ext4_es_is_delayed(struct extent_status *es) +{ + return (es->es_pblk & EXTENT_STATUS_DELAYED); +} + +static inline int ext4_es_is_hole(struct extent_status *es) +{ + return (es->es_pblk & EXTENT_STATUS_HOLE); +} + +static inline ext4_fsblk_t ext4_es_status(struct extent_status *es) +{ + return (es->es_pblk & EXTENT_STATUS_FLAGS); +} + +static inline ext4_fsblk_t ext4_es_pblock(struct extent_status *es) +{ + return (es->es_pblk & ~EXTENT_STATUS_FLAGS); +} + +static inline void ext4_es_store_pblock(struct extent_status *es, + ext4_fsblk_t pb) +{ + ext4_fsblk_t block; + + block = (pb & ~EXTENT_STATUS_FLAGS) | + (es->es_pblk & EXTENT_STATUS_FLAGS); + es->es_pblk = block; +} + +static inline void ext4_es_store_status(struct extent_status *es, + unsigned long long status) +{ + ext4_fsblk_t block; + + block = (status & EXTENT_STATUS_FLAGS) | + (es->es_pblk & ~EXTENT_STATUS_FLAGS); + es->es_pblk = block; +} + #endif /* _EXT4_EXTENTS_STATUS_H */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index f4466c3650dc..e0e1cb0863f4 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1784,7 +1784,8 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, goto out_unlock; } - retval = ext4_es_insert_extent(inode, map->m_lblk, map->m_len); + retval = ext4_es_insert_extent(inode, map->m_lblk, map->m_len, + ~0, EXTENT_STATUS_DELAYED); if (retval) goto out_unlock; diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 52c923851959..0ee507ff216d 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -2093,28 +2093,33 @@ TRACE_EVENT(ext4_ext_remove_space_done, ); TRACE_EVENT(ext4_es_insert_extent, - TP_PROTO(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len), + TP_PROTO(struct inode *inode, struct extent_status *es), - TP_ARGS(inode, lblk, len), + TP_ARGS(inode, es), TP_STRUCT__entry( - __field( dev_t, dev ) - __field( ino_t, ino ) - __field( loff_t, lblk ) - __field( loff_t, len ) + __field( dev_t, dev ) + __field( ino_t, ino ) + __field( ext4_lblk_t, lblk ) + __field( ext4_lblk_t, len ) + __field( ext4_fsblk_t, pblk ) + __field( unsigned long long, status ) ), TP_fast_assign( __entry->dev = inode->i_sb->s_dev; __entry->ino = inode->i_ino; - __entry->lblk = lblk; - __entry->len = len; + __entry->lblk = es->es_lblk; + __entry->len = es->es_len; + __entry->pblk = ext4_es_pblock(es); + __entry->status = ext4_es_status(es); ), - TP_printk("dev %d,%d ino %lu es [%lld/%lld)", + TP_printk("dev %d,%d ino %lu es [%u/%u) mapped %llu status %llx", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - __entry->lblk, __entry->len) + __entry->lblk, __entry->len, + __entry->pblk, __entry->status) ); TRACE_EVENT(ext4_es_remove_extent, @@ -2175,6 +2180,8 @@ TRACE_EVENT(ext4_es_find_extent_exit, __field( ino_t, ino ) __field( ext4_lblk_t, lblk ) __field( ext4_lblk_t, len ) + __field( ext4_fsblk_t, pblk ) + __field( unsigned long long, status ) __field( ext4_lblk_t, ret ) ), @@ -2183,13 +2190,16 @@ TRACE_EVENT(ext4_es_find_extent_exit, __entry->ino = inode->i_ino; __entry->lblk = es->es_lblk; __entry->len = es->es_len; + __entry->pblk = ext4_es_pblock(es); + __entry->status = ext4_es_status(es); __entry->ret = ret; ), - TP_printk("dev %d,%d ino %lu es [%u/%u) ret %u", + TP_printk("dev %d,%d ino %lu es [%u/%u) mapped %llu status %llx ret %u", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, - __entry->lblk, __entry->len, __entry->ret) + __entry->lblk, __entry->len, + __entry->pblk, __entry->status, __entry->ret) ); #endif /* _TRACE_EXT4_H */ From be401363ac5ec652c706263a59b0bd0acc3612e8 Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Mon, 18 Feb 2013 00:27:26 -0500 Subject: [PATCH 2988/4607] ext4: rename and improbe ext4_es_find_extent() This commit renames ext4_es_find_extent with ext4_es_find_delayed_extent and improve this function. First, we split input and output parameter. Second, this function never return the first block of the next delayed extent after 'es'. Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" Cc: Jan kara --- fs/ext4/extents.c | 15 +++++++++----- fs/ext4/extents_status.c | 40 ++++++++++++++++++------------------- fs/ext4/extents_status.h | 4 ++-- fs/ext4/file.c | 6 ++---- include/trace/events/ext4.h | 15 ++++++-------- 5 files changed, 40 insertions(+), 40 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 37f94a751ad7..895c19595ecf 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -3528,8 +3528,7 @@ static int ext4_find_delalloc_range(struct inode *inode, { struct extent_status es; - es.es_lblk = lblk_start; - (void)ext4_es_find_extent(inode, &es); + ext4_es_find_delayed_extent(inode, lblk_start, &es); if (es.es_len == 0) return 0; /* there is no delay extent in this tree */ else if (es.es_lblk <= lblk_start && @@ -4568,10 +4567,9 @@ static int ext4_find_delayed_extent(struct inode *inode, struct ext4_ext_cache *newex) { struct extent_status es; - ext4_lblk_t next_del; + ext4_lblk_t block, next_del; - es.es_lblk = newex->ec_block; - next_del = ext4_es_find_extent(inode, &es); + ext4_es_find_delayed_extent(inode, newex->ec_block, &es); if (newex->ec_start == 0) { /* @@ -4592,6 +4590,13 @@ static int ext4_find_delayed_extent(struct inode *inode, newex->ec_len = es.es_lblk + es.es_len - newex->ec_block; } + block = newex->ec_block + newex->ec_len; + ext4_es_find_delayed_extent(inode, block, &es); + if (es.es_len == 0) + next_del = EXT_MAX_BLOCKS; + else + next_del = es.es_lblk; + return next_del; } /* fiemap flags we can handle specified here */ diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index 1f5fd44993e9..76f4351ea821 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -229,59 +229,59 @@ static struct extent_status *__es_tree_search(struct rb_root *root, } /* - * ext4_es_find_extent: find the 1st delayed extent covering @es->lblk + * ext4_es_find_delayed_extent: find the 1st delayed extent covering @es->lblk * if it exists, otherwise, the next extent after @es->lblk. * * @inode: the inode which owns delayed extents + * @lblk: the offset where we start to search * @es: delayed extent that we found - * - * Returns the first block of the next extent after es, otherwise - * EXT_MAX_BLOCKS if no extent is found. - * Delayed extent is returned via @es. */ -ext4_lblk_t ext4_es_find_extent(struct inode *inode, struct extent_status *es) +void ext4_es_find_delayed_extent(struct inode *inode, ext4_lblk_t lblk, + struct extent_status *es) { struct ext4_es_tree *tree = NULL; struct extent_status *es1 = NULL; struct rb_node *node; - ext4_lblk_t ret = EXT_MAX_BLOCKS; - trace_ext4_es_find_extent_enter(inode, es->es_lblk); + BUG_ON(es == NULL); + trace_ext4_es_find_delayed_extent_enter(inode, lblk); read_lock(&EXT4_I(inode)->i_es_lock); tree = &EXT4_I(inode)->i_es_tree; /* find extent in cache firstly */ - es->es_len = es->es_pblk = 0; + es->es_lblk = es->es_len = es->es_pblk = 0; if (tree->cache_es) { es1 = tree->cache_es; - if (in_range(es->es_lblk, es1->es_lblk, es1->es_len)) { + if (in_range(lblk, es1->es_lblk, es1->es_len)) { es_debug("%u cached by [%u/%u) %llu %llx\n", - es->es_lblk, es1->es_lblk, es1->es_len, + lblk, es1->es_lblk, es1->es_len, ext4_es_pblock(es1), ext4_es_status(es1)); goto out; } } - es1 = __es_tree_search(&tree->root, es->es_lblk); + es1 = __es_tree_search(&tree->root, lblk); out: - if (es1) { + if (es1 && !ext4_es_is_delayed(es1)) { + while ((node = rb_next(&es1->rb_node)) != NULL) { + es1 = rb_entry(node, struct extent_status, rb_node); + if (ext4_es_is_delayed(es1)) + break; + } + } + + if (es1 && ext4_es_is_delayed(es1)) { tree->cache_es = es1; es->es_lblk = es1->es_lblk; es->es_len = es1->es_len; es->es_pblk = es1->es_pblk; - node = rb_next(&es1->rb_node); - if (node) { - es1 = rb_entry(node, struct extent_status, rb_node); - ret = es1->es_lblk; - } } read_unlock(&EXT4_I(inode)->i_es_lock); - trace_ext4_es_find_extent_exit(inode, es, ret); - return ret; + trace_ext4_es_find_delayed_extent_exit(inode, es); } static struct extent_status * diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index 3cad83303adb..3f69d097c6e7 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h @@ -51,8 +51,8 @@ extern int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, unsigned long long status); extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len); -extern ext4_lblk_t ext4_es_find_extent(struct inode *inode, - struct extent_status *es); +extern void ext4_es_find_delayed_extent(struct inode *inode, ext4_lblk_t lblk, + struct extent_status *es); static inline int ext4_es_is_written(struct extent_status *es) { diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 2df9354b105e..7e85a10a6f4f 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -464,8 +464,7 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize) * If there is a delay extent at this offset, * it will be as a data. */ - es.es_lblk = last; - (void)ext4_es_find_extent(inode, &es); + ext4_es_find_delayed_extent(inode, last, &es); if (es.es_len != 0 && in_range(last, es.es_lblk, es.es_len)) { if (last != start) dataoff = last << blkbits; @@ -548,8 +547,7 @@ static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize) * If there is a delay extent at this offset, * we will skip this extent. */ - es.es_lblk = last; - (void)ext4_es_find_extent(inode, &es); + ext4_es_find_delayed_extent(inode, last, &es); if (es.es_len != 0 && in_range(last, es.es_lblk, es.es_len)) { last = es.es_lblk + es.es_len; holeoff = last << blkbits; diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 0ee507ff216d..c121cdf55ab3 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -2147,7 +2147,7 @@ TRACE_EVENT(ext4_es_remove_extent, __entry->lblk, __entry->len) ); -TRACE_EVENT(ext4_es_find_extent_enter, +TRACE_EVENT(ext4_es_find_delayed_extent_enter, TP_PROTO(struct inode *inode, ext4_lblk_t lblk), TP_ARGS(inode, lblk), @@ -2169,11 +2169,10 @@ TRACE_EVENT(ext4_es_find_extent_enter, (unsigned long) __entry->ino, __entry->lblk) ); -TRACE_EVENT(ext4_es_find_extent_exit, - TP_PROTO(struct inode *inode, struct extent_status *es, - ext4_lblk_t ret), +TRACE_EVENT(ext4_es_find_delayed_extent_exit, + TP_PROTO(struct inode *inode, struct extent_status *es), - TP_ARGS(inode, es, ret), + TP_ARGS(inode, es), TP_STRUCT__entry( __field( dev_t, dev ) @@ -2182,7 +2181,6 @@ TRACE_EVENT(ext4_es_find_extent_exit, __field( ext4_lblk_t, len ) __field( ext4_fsblk_t, pblk ) __field( unsigned long long, status ) - __field( ext4_lblk_t, ret ) ), TP_fast_assign( @@ -2192,14 +2190,13 @@ TRACE_EVENT(ext4_es_find_extent_exit, __entry->len = es->es_len; __entry->pblk = ext4_es_pblock(es); __entry->status = ext4_es_status(es); - __entry->ret = ret; ), - TP_printk("dev %d,%d ino %lu es [%u/%u) mapped %llu status %llx ret %u", + TP_printk("dev %d,%d ino %lu es [%u/%u) mapped %llu status %llx", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, __entry->lblk, __entry->len, - __entry->pblk, __entry->status, __entry->ret) + __entry->pblk, __entry->status) ); #endif /* _TRACE_EXT4_H */ From a25a4e1a5d5dc0f97dddbca44e695c532d8228c1 Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Mon, 18 Feb 2013 00:28:04 -0500 Subject: [PATCH 2989/4607] ext4: let ext4_ext_map_blocks return EXT4_MAP_UNWRITTEN flag This commit lets ext4_ext_map_blocks return EXT4_MAP_UNWRITTEN flag because in later commit ext4_map_blocks needs to use this flag to determine the extent status. Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" Reviewed-by: Jan Kara --- fs/ext4/extents.c | 6 +++++- fs/ext4/inode.c | 12 +++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 895c19595ecf..cae8ae3c1746 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -3659,6 +3659,7 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, ext4_set_io_unwritten_flag(inode, io); else ext4_set_inode_state(inode, EXT4_STATE_DIO_UNWRITTEN); + map->m_flags |= EXT4_MAP_UNWRITTEN; if (ext4_should_dioread_nolock(inode)) map->m_flags |= EXT4_MAP_UNINIT; goto out; @@ -3680,8 +3681,10 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, * repeat fallocate creation request * we already have an unwritten extent */ - if (flags & EXT4_GET_BLOCKS_UNINIT_EXT) + if (flags & EXT4_GET_BLOCKS_UNINIT_EXT) { + map->m_flags |= EXT4_MAP_UNWRITTEN; goto map_out; + } /* buffered READ or buffered write_begin() lookup */ if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) { @@ -4111,6 +4114,7 @@ got_allocated_blocks: /* Mark uninitialized */ if (flags & EXT4_GET_BLOCKS_UNINIT_EXT){ ext4_ext_mark_uninitialized(&newex); + map->m_flags |= EXT4_MAP_UNWRITTEN; /* * io_end structure was created for every IO write to an * uninitialized extent. To avoid unnecessary conversion, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index e0e1cb0863f4..084b8212cf95 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -559,16 +559,10 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, return retval; /* - * When we call get_blocks without the create flag, the - * BH_Unwritten flag could have gotten set if the blocks - * requested were part of a uninitialized extent. We need to - * clear this flag now that we are committed to convert all or - * part of the uninitialized extent to be an initialized - * extent. This is because we need to avoid the combination - * of BH_Unwritten and BH_Mapped flags being simultaneously - * set on the buffer_head. + * Here we clear m_flags because after allocating an new extent, + * it will be set again. */ - map->m_flags &= ~EXT4_MAP_UNWRITTEN; + map->m_flags &= ~EXT4_MAP_FLAGS; /* * New blocks allocate and/or writing to uninitialized extent From f7fec032aa782d3fd7e51fbdf08aa3a296c01500 Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Mon, 18 Feb 2013 00:28:47 -0500 Subject: [PATCH 2990/4607] ext4: track all extent status in extent status tree By recording the phycisal block and status, extent status tree is able to track the status of every extents. When we call _map_blocks functions to lookup an extent or create a new written/unwritten/delayed extent, this extent will be inserted into extent status tree. We don't load all extents from disk in alloc_inode() because it costs too much memory, and if a file is opened and closed frequently it will takes too much time to load all extent information. So currently when we create/lookup an extent, this extent will be inserted into extent status tree. Hence, the extent status tree may not comprehensively contain all of the extents found in the file. Here a condition we need to take care is that an extent might contains unwritten and delayed status simultaneously because an extent is delayed allocated and could be allocated by fallocate. At this time we need to keep delayed status because later we need to update delayed reservation space using it. Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" Cc: Jan kara --- fs/ext4/ext4.h | 3 ++ fs/ext4/extents.c | 20 +++++++++---- fs/ext4/inode.c | 76 +++++++++++++++++++++++++++++++---------------- 3 files changed, 69 insertions(+), 30 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index fc1c0375c9f2..5c31d6ac9500 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2602,6 +2602,9 @@ extern struct ext4_ext_path *ext4_ext_find_extent(struct inode *, ext4_lblk_t, struct ext4_ext_path *); extern void ext4_ext_drop_refs(struct ext4_ext_path *); extern int ext4_ext_check_inode(struct inode *inode); +extern int ext4_find_delalloc_range(struct inode *inode, + ext4_lblk_t lblk_start, + ext4_lblk_t lblk_end); extern int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk); extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len); diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index cae8ae3c1746..be0b1b3eed97 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -2076,8 +2076,18 @@ static int ext4_fill_fiemap_extents(struct inode *inode, break; } - /* This is possible iff next == next_del == EXT_MAX_BLOCKS */ - if (next == next_del) { + /* + * This is possible iff next == next_del == EXT_MAX_BLOCKS. + * we need to check next == EXT_MAX_BLOCKS because it is + * possible that an extent is with unwritten and delayed + * status due to when an extent is delayed allocated and + * is allocated by fallocate status tree will track both of + * them in a extent. + * + * So we could return a unwritten and delayed extent, and + * its block is equal to 'next'. + */ + if (next == next_del && next == EXT_MAX_BLOCKS) { flags |= FIEMAP_EXTENT_LAST; if (unlikely(next_del != EXT_MAX_BLOCKS || next != EXT_MAX_BLOCKS)) { @@ -3522,9 +3532,9 @@ out: * * Return 1 if there is a delalloc block in the range, otherwise 0. */ -static int ext4_find_delalloc_range(struct inode *inode, - ext4_lblk_t lblk_start, - ext4_lblk_t lblk_end) +int ext4_find_delalloc_range(struct inode *inode, + ext4_lblk_t lblk_start, + ext4_lblk_t lblk_end) { struct extent_status es; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 084b8212cf95..576b586b61aa 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -526,20 +526,26 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, retval = ext4_ind_map_blocks(handle, inode, map, flags & EXT4_GET_BLOCKS_KEEP_SIZE); } + if (retval > 0) { + int ret; + unsigned long long status; + + status = map->m_flags & EXT4_MAP_UNWRITTEN ? + EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; + if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) && + ext4_find_delalloc_range(inode, map->m_lblk, + map->m_lblk + map->m_len - 1)) + status |= EXTENT_STATUS_DELAYED; + ret = ext4_es_insert_extent(inode, map->m_lblk, + map->m_len, map->m_pblk, status); + if (ret < 0) + retval = ret; + } if (!(flags & EXT4_GET_BLOCKS_NO_LOCK)) up_read((&EXT4_I(inode)->i_data_sem)); if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { - int ret; - if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) { - /* delayed alloc may be allocated by fallocate and - * coverted to initialized by directIO. - * we need to handle delayed extent here. - */ - down_write((&EXT4_I(inode)->i_data_sem)); - goto delayed_mapped; - } - ret = check_block_validity(inode, map); + int ret = check_block_validity(inode, map); if (ret != 0) return ret; } @@ -608,18 +614,23 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE)) ext4_da_update_reserve_space(inode, retval, 1); } - if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) { + if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) ext4_clear_inode_state(inode, EXT4_STATE_DELALLOC_RESERVED); - if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { - int ret; -delayed_mapped: - /* delayed allocation blocks has been allocated */ - ret = ext4_es_remove_extent(inode, map->m_lblk, - map->m_len); - if (ret < 0) - retval = ret; - } + if (retval > 0) { + int ret; + unsigned long long status; + + status = map->m_flags & EXT4_MAP_UNWRITTEN ? + EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; + if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) && + ext4_find_delalloc_range(inode, map->m_lblk, + map->m_lblk + map->m_len - 1)) + status |= EXTENT_STATUS_DELAYED; + ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len, + map->m_pblk, status); + if (ret < 0) + retval = ret; } up_write((&EXT4_I(inode)->i_data_sem)); @@ -1765,6 +1776,7 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, retval = ext4_ind_map_blocks(NULL, inode, map, 0); if (retval == 0) { + int ret; /* * XXX: __block_prepare_write() unmaps passed block, * is it OK? @@ -1772,16 +1784,20 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, /* If the block was allocated from previously allocated cluster, * then we dont need to reserve it again. */ if (!(map->m_flags & EXT4_MAP_FROM_CLUSTER)) { - retval = ext4_da_reserve_space(inode, iblock); - if (retval) + ret = ext4_da_reserve_space(inode, iblock); + if (ret) { /* not enough space to reserve */ + retval = ret; goto out_unlock; + } } - retval = ext4_es_insert_extent(inode, map->m_lblk, map->m_len, - ~0, EXTENT_STATUS_DELAYED); - if (retval) + ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len, + ~0, EXTENT_STATUS_DELAYED); + if (ret) { + retval = ret; goto out_unlock; + } /* Clear EXT4_MAP_FROM_CLUSTER flag since its purpose is served * and it should not appear on the bh->b_state. @@ -1791,6 +1807,16 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, map_bh(bh, inode->i_sb, invalid_block); set_buffer_new(bh); set_buffer_delay(bh); + } else if (retval > 0) { + int ret; + unsigned long long status; + + status = map->m_flags & EXT4_MAP_UNWRITTEN ? + EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN; + ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len, + map->m_pblk, status); + if (ret != 0) + retval = ret; } out_unlock: From d100eef2440fea13e4f09e88b1c8bcbca64beb9f Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Mon, 18 Feb 2013 00:29:59 -0500 Subject: [PATCH 2991/4607] ext4: lookup block mapping in extent status tree After tracking all extent status, we already have a extent cache in memory. Every time we want to lookup a block mapping, we can first try to lookup it in extent status tree to avoid a potential disk I/O. A new function called ext4_es_lookup_extent is defined to finish this work. When we try to lookup a block mapping, we always call ext4_map_blocks and/or ext4_da_map_blocks. So in these functions we first try to lookup a block mapping in extent status tree. A new flag EXT4_GET_BLOCKS_NO_PUT_HOLE is used in ext4_da_map_blocks in order not to put a hole into extent status tree because this hole will be converted to delayed extent in the tree immediately. Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" Cc: Jan kara --- fs/ext4/ext4.h | 2 ++ fs/ext4/extents.c | 9 ++++- fs/ext4/extents_status.c | 60 +++++++++++++++++++++++++++++++++ fs/ext4/extents_status.h | 2 ++ fs/ext4/inode.c | 66 +++++++++++++++++++++++++++++++++++-- include/trace/events/ext4.h | 56 +++++++++++++++++++++++++++++++ 6 files changed, 192 insertions(+), 3 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 5c31d6ac9500..329e7fba47d6 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -579,6 +579,8 @@ enum { #define EXT4_GET_BLOCKS_KEEP_SIZE 0x0080 /* Do not take i_data_sem locking in ext4_map_blocks */ #define EXT4_GET_BLOCKS_NO_LOCK 0x0100 + /* Do not put hole in extent cache */ +#define EXT4_GET_BLOCKS_NO_PUT_HOLE 0x0200 /* * Flags used by ext4_free_blocks diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index be0b1b3eed97..b9d7a2363736 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -2167,6 +2167,9 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, block, le32_to_cpu(ex->ee_block), ext4_ext_get_actual_len(ex)); + if (!ext4_find_delalloc_range(inode, lblock, lblock + len - 1)) + ext4_es_insert_extent(inode, lblock, len, ~0, + EXTENT_STATUS_HOLE); } else if (block >= le32_to_cpu(ex->ee_block) + ext4_ext_get_actual_len(ex)) { ext4_lblk_t next; @@ -2180,6 +2183,9 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, block); BUG_ON(next == lblock); len = next - lblock; + if (!ext4_find_delalloc_range(inode, lblock, lblock + len - 1)) + ext4_es_insert_extent(inode, lblock, len, ~0, + EXTENT_STATUS_HOLE); } else { lblock = len = 0; BUG(); @@ -4018,7 +4024,8 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, * put just found gap into cache to speed up * subsequent requests */ - ext4_ext_put_gap_in_cache(inode, path, map->m_lblk); + if ((flags & EXT4_GET_BLOCKS_NO_PUT_HOLE) == 0) + ext4_ext_put_gap_in_cache(inode, path, map->m_lblk); goto out2; } diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index 76f4351ea821..eeb893122d8d 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -461,6 +461,66 @@ error: return err; } +/* + * ext4_es_lookup_extent() looks up an extent in extent status tree. + * + * ext4_es_lookup_extent is called by ext4_map_blocks/ext4_da_map_blocks. + * + * Return: 1 on found, 0 on not + */ +int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk, + struct extent_status *es) +{ + struct ext4_es_tree *tree; + struct extent_status *es1 = NULL; + struct rb_node *node; + int found = 0; + + trace_ext4_es_lookup_extent_enter(inode, lblk); + es_debug("lookup extent in block %u\n", lblk); + + tree = &EXT4_I(inode)->i_es_tree; + read_lock(&EXT4_I(inode)->i_es_lock); + + /* find extent in cache firstly */ + es->es_lblk = es->es_len = es->es_pblk = 0; + if (tree->cache_es) { + es1 = tree->cache_es; + if (in_range(lblk, es1->es_lblk, es1->es_len)) { + es_debug("%u cached by [%u/%u)\n", + lblk, es1->es_lblk, es1->es_len); + found = 1; + goto out; + } + } + + node = tree->root.rb_node; + while (node) { + es1 = rb_entry(node, struct extent_status, rb_node); + if (lblk < es1->es_lblk) + node = node->rb_left; + else if (lblk > ext4_es_end(es1)) + node = node->rb_right; + else { + found = 1; + break; + } + } + +out: + if (found) { + BUG_ON(!es1); + es->es_lblk = es1->es_lblk; + es->es_len = es1->es_len; + es->es_pblk = es1->es_pblk; + } + + read_unlock(&EXT4_I(inode)->i_es_lock); + + trace_ext4_es_lookup_extent_exit(inode, es, found); + return found; +} + static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, ext4_lblk_t end) { diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index 3f69d097c6e7..8ffc90c784fa 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h @@ -53,6 +53,8 @@ extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len); extern void ext4_es_find_delayed_extent(struct inode *inode, ext4_lblk_t lblk, struct extent_status *es); +extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk, + struct extent_status *es); static inline int ext4_es_is_written(struct extent_status *es) { diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 576b586b61aa..95a0c62c5683 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -507,12 +507,33 @@ static pgoff_t ext4_num_dirty_pages(struct inode *inode, pgoff_t idx, int ext4_map_blocks(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, int flags) { + struct extent_status es; int retval; map->m_flags = 0; ext_debug("ext4_map_blocks(): inode %lu, flag %d, max_blocks %u," "logical block %lu\n", inode->i_ino, flags, map->m_len, (unsigned long) map->m_lblk); + + /* Lookup extent status tree firstly */ + if (ext4_es_lookup_extent(inode, map->m_lblk, &es)) { + if (ext4_es_is_written(&es) || ext4_es_is_unwritten(&es)) { + map->m_pblk = ext4_es_pblock(&es) + + map->m_lblk - es.es_lblk; + map->m_flags |= ext4_es_is_written(&es) ? + EXT4_MAP_MAPPED : EXT4_MAP_UNWRITTEN; + retval = es.es_len - (map->m_lblk - es.es_lblk); + if (retval > map->m_len) + retval = map->m_len; + map->m_len = retval; + } else if (ext4_es_is_delayed(&es) || ext4_es_is_hole(&es)) { + retval = 0; + } else { + BUG_ON(1); + } + goto found; + } + /* * Try to see if we can get the block without requesting a new * file system block. @@ -544,6 +565,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, if (!(flags & EXT4_GET_BLOCKS_NO_LOCK)) up_read((&EXT4_I(inode)->i_data_sem)); +found: if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { int ret = check_block_validity(inode, map); if (ret != 0) @@ -1743,6 +1765,7 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, struct ext4_map_blocks *map, struct buffer_head *bh) { + struct extent_status es; int retval; sector_t invalid_block = ~((sector_t) 0xffff); @@ -1753,6 +1776,42 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, ext_debug("ext4_da_map_blocks(): inode %lu, max_blocks %u," "logical block %lu\n", inode->i_ino, map->m_len, (unsigned long) map->m_lblk); + + /* Lookup extent status tree firstly */ + if (ext4_es_lookup_extent(inode, iblock, &es)) { + + if (ext4_es_is_hole(&es)) { + retval = 0; + down_read((&EXT4_I(inode)->i_data_sem)); + goto add_delayed; + } + + /* + * Delayed extent could be allocated by fallocate. + * So we need to check it. + */ + if (ext4_es_is_delayed(&es) && !ext4_es_is_unwritten(&es)) { + map_bh(bh, inode->i_sb, invalid_block); + set_buffer_new(bh); + set_buffer_delay(bh); + return 0; + } + + map->m_pblk = ext4_es_pblock(&es) + iblock - es.es_lblk; + retval = es.es_len - (iblock - es.es_lblk); + if (retval > map->m_len) + retval = map->m_len; + map->m_len = retval; + if (ext4_es_is_written(&es)) + map->m_flags |= EXT4_MAP_MAPPED; + else if (ext4_es_is_unwritten(&es)) + map->m_flags |= EXT4_MAP_UNWRITTEN; + else + BUG_ON(1); + + return retval; + } + /* * Try to see if we can get the block without requesting a new * file system block. @@ -1771,10 +1830,13 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, map->m_flags |= EXT4_MAP_FROM_CLUSTER; retval = 0; } else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) - retval = ext4_ext_map_blocks(NULL, inode, map, 0); + retval = ext4_ext_map_blocks(NULL, inode, map, + EXT4_GET_BLOCKS_NO_PUT_HOLE); else - retval = ext4_ind_map_blocks(NULL, inode, map, 0); + retval = ext4_ind_map_blocks(NULL, inode, map, + EXT4_GET_BLOCKS_NO_PUT_HOLE); +add_delayed: if (retval == 0) { int ret; /* diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index c121cdf55ab3..1e590b68cec4 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -2199,6 +2199,62 @@ TRACE_EVENT(ext4_es_find_delayed_extent_exit, __entry->pblk, __entry->status) ); +TRACE_EVENT(ext4_es_lookup_extent_enter, + TP_PROTO(struct inode *inode, ext4_lblk_t lblk), + + TP_ARGS(inode, lblk), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( ino_t, ino ) + __field( ext4_lblk_t, lblk ) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->lblk = lblk; + ), + + TP_printk("dev %d,%d ino %lu lblk %u", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, __entry->lblk) +); + +TRACE_EVENT(ext4_es_lookup_extent_exit, + TP_PROTO(struct inode *inode, struct extent_status *es, + int found), + + TP_ARGS(inode, es, found), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( ino_t, ino ) + __field( ext4_lblk_t, lblk ) + __field( ext4_lblk_t, len ) + __field( ext4_fsblk_t, pblk ) + __field( unsigned long long, status ) + __field( int, found ) + ), + + TP_fast_assign( + __entry->dev = inode->i_sb->s_dev; + __entry->ino = inode->i_ino; + __entry->lblk = es->es_lblk; + __entry->len = es->es_len; + __entry->pblk = ext4_es_pblock(es); + __entry->status = ext4_es_status(es); + __entry->found = found; + ), + + TP_printk("dev %d,%d ino %lu found %d [%u/%u) %llu %llx", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, __entry->found, + __entry->lblk, __entry->len, + __entry->found ? __entry->pblk : 0, + __entry->found ? __entry->status : 0) +); + #endif /* _TRACE_EXT4_H */ /* This part must be outside protection */ From 69eb33dc24dc44d1128aa5e82d0976c42b440c1a Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Mon, 18 Feb 2013 00:31:07 -0500 Subject: [PATCH 2992/4607] ext4: remove single extent cache Single extent cache could be removed because we have extent status tree as a extent cache, and it would be better. Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" Cc: Jan kara --- fs/ext4/ext4.h | 12 --- fs/ext4/ext4_extents.h | 6 -- fs/ext4/extents.c | 179 +++++++++-------------------------------- fs/ext4/move_extent.c | 3 - fs/ext4/super.c | 1 - 5 files changed, 38 insertions(+), 163 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 329e7fba47d6..0c565c941f7a 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -809,17 +809,6 @@ do { \ #endif /* defined(__KERNEL__) || defined(__linux__) */ -/* - * storage for cached extent - * If ec_len == 0, then the cache is invalid. - * If ec_start == 0, then the cache represents a gap (null mapping) - */ -struct ext4_ext_cache { - ext4_fsblk_t ec_start; - ext4_lblk_t ec_block; - __u32 ec_len; /* must be 32bit to return holes */ -}; - #include "extents_status.h" /* @@ -886,7 +875,6 @@ struct ext4_inode_info { struct inode vfs_inode; struct jbd2_inode *jinode; - struct ext4_ext_cache i_cached_extent; /* * File creation time. Its function is same as that of * struct timespec i_{a,c,m}time in the generic inode. diff --git a/fs/ext4/ext4_extents.h b/fs/ext4/ext4_extents.h index 487fda12bc00..8643ff5bbeb7 100644 --- a/fs/ext4/ext4_extents.h +++ b/fs/ext4/ext4_extents.h @@ -193,12 +193,6 @@ static inline unsigned short ext_depth(struct inode *inode) return le16_to_cpu(ext_inode_hdr(inode)->eh_depth); } -static inline void -ext4_ext_invalidate_cache(struct inode *inode) -{ - EXT4_I(inode)->i_cached_extent.ec_len = 0; -} - static inline void ext4_ext_mark_uninitialized(struct ext4_extent *ext) { /* We can not have an uninitialized extent of zero length! */ diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index b9d7a2363736..372b2cbee07e 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -112,7 +112,7 @@ static int ext4_split_extent_at(handle_t *handle, int flags); static int ext4_find_delayed_extent(struct inode *inode, - struct ext4_ext_cache *newex); + struct extent_status *newes); static int ext4_ext_truncate_extend_restart(handle_t *handle, struct inode *inode, @@ -714,7 +714,6 @@ int ext4_ext_tree_init(handle_t *handle, struct inode *inode) eh->eh_magic = EXT4_EXT_MAGIC; eh->eh_max = cpu_to_le16(ext4_ext_space_root(inode, 0)); ext4_mark_inode_dirty(handle, inode); - ext4_ext_invalidate_cache(inode); return 0; } @@ -1963,7 +1962,6 @@ cleanup: ext4_ext_drop_refs(npath); kfree(npath); } - ext4_ext_invalidate_cache(inode); return err; } @@ -1972,8 +1970,8 @@ static int ext4_fill_fiemap_extents(struct inode *inode, struct fiemap_extent_info *fieinfo) { struct ext4_ext_path *path = NULL; - struct ext4_ext_cache newex; struct ext4_extent *ex; + struct extent_status es; ext4_lblk_t next, next_del, start = 0, end = 0; ext4_lblk_t last = block + num; int exists, depth = 0, err = 0; @@ -2047,31 +2045,31 @@ static int ext4_fill_fiemap_extents(struct inode *inode, BUG_ON(end <= start); if (!exists) { - newex.ec_block = start; - newex.ec_len = end - start; - newex.ec_start = 0; + es.es_lblk = start; + es.es_len = end - start; + es.es_pblk = 0; } else { - newex.ec_block = le32_to_cpu(ex->ee_block); - newex.ec_len = ext4_ext_get_actual_len(ex); - newex.ec_start = ext4_ext_pblock(ex); + es.es_lblk = le32_to_cpu(ex->ee_block); + es.es_len = ext4_ext_get_actual_len(ex); + es.es_pblk = ext4_ext_pblock(ex); if (ext4_ext_is_uninitialized(ex)) flags |= FIEMAP_EXTENT_UNWRITTEN; } /* - * Find delayed extent and update newex accordingly. We call - * it even in !exists case to find out whether newex is the + * Find delayed extent and update es accordingly. We call + * it even in !exists case to find out whether es is the * last existing extent or not. */ - next_del = ext4_find_delayed_extent(inode, &newex); + next_del = ext4_find_delayed_extent(inode, &es); if (!exists && next_del) { exists = 1; flags |= FIEMAP_EXTENT_DELALLOC; } up_read(&EXT4_I(inode)->i_data_sem); - if (unlikely(newex.ec_len == 0)) { - EXT4_ERROR_INODE(inode, "newex.ec_len == 0"); + if (unlikely(es.es_len == 0)) { + EXT4_ERROR_INODE(inode, "es.es_len == 0"); err = -EIO; break; } @@ -2102,9 +2100,9 @@ static int ext4_fill_fiemap_extents(struct inode *inode, if (exists) { err = fiemap_fill_next_extent(fieinfo, - (__u64)newex.ec_block << blksize_bits, - (__u64)newex.ec_start << blksize_bits, - (__u64)newex.ec_len << blksize_bits, + (__u64)es.es_lblk << blksize_bits, + (__u64)es.es_pblk << blksize_bits, + (__u64)es.es_len << blksize_bits, flags); if (err < 0) break; @@ -2114,7 +2112,7 @@ static int ext4_fill_fiemap_extents(struct inode *inode, } } - block = newex.ec_block + newex.ec_len; + block = es.es_lblk + es.es_len; } if (path) { @@ -2125,21 +2123,6 @@ static int ext4_fill_fiemap_extents(struct inode *inode, return err; } -static void -ext4_ext_put_in_cache(struct inode *inode, ext4_lblk_t block, - __u32 len, ext4_fsblk_t start) -{ - struct ext4_ext_cache *cex; - BUG_ON(len == 0); - spin_lock(&EXT4_I(inode)->i_block_reservation_lock); - trace_ext4_ext_put_in_cache(inode, block, len, start); - cex = &EXT4_I(inode)->i_cached_extent; - cex->ec_block = block; - cex->ec_len = len; - cex->ec_start = start; - spin_unlock(&EXT4_I(inode)->i_block_reservation_lock); -} - /* * ext4_ext_put_gap_in_cache: * calculate boundaries of the gap that the requested block fits into @@ -2156,9 +2139,10 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, ex = path[depth].p_ext; if (ex == NULL) { - /* there is no extent yet, so gap is [0;-] */ - lblock = 0; - len = EXT_MAX_BLOCKS; + /* + * there is no extent yet, so gap is [0;-] and we + * don't cache it + */ ext_debug("cache gap(whole file):"); } else if (block < le32_to_cpu(ex->ee_block)) { lblock = block; @@ -2192,52 +2176,6 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, } ext_debug(" -> %u:%lu\n", lblock, len); - ext4_ext_put_in_cache(inode, lblock, len, 0); -} - -/* - * ext4_ext_in_cache() - * Checks to see if the given block is in the cache. - * If it is, the cached extent is stored in the given - * cache extent pointer. - * - * @inode: The files inode - * @block: The block to look for in the cache - * @ex: Pointer where the cached extent will be stored - * if it contains block - * - * Return 0 if cache is invalid; 1 if the cache is valid - */ -static int -ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block, - struct ext4_extent *ex) -{ - struct ext4_ext_cache *cex; - int ret = 0; - - /* - * We borrow i_block_reservation_lock to protect i_cached_extent - */ - spin_lock(&EXT4_I(inode)->i_block_reservation_lock); - cex = &EXT4_I(inode)->i_cached_extent; - - /* has cache valid data? */ - if (cex->ec_len == 0) - goto errout; - - if (in_range(block, cex->ec_block, cex->ec_len)) { - ex->ee_block = cpu_to_le32(cex->ec_block); - ext4_ext_store_pblock(ex, cex->ec_start); - ex->ee_len = cpu_to_le16(cex->ec_len); - ext_debug("%u cached by %u:%u:%llu\n", - block, - cex->ec_block, cex->ec_len, cex->ec_start); - ret = 1; - } -errout: - trace_ext4_ext_in_cache(inode, block, ret); - spin_unlock(&EXT4_I(inode)->i_block_reservation_lock); - return ret; } /* @@ -2677,8 +2615,6 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, return PTR_ERR(handle); again: - ext4_ext_invalidate_cache(inode); - trace_ext4_ext_remove_space(inode, start, depth); /* @@ -3920,35 +3856,6 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, map->m_lblk, map->m_len, inode->i_ino); trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags); - /* check in cache */ - if (ext4_ext_in_cache(inode, map->m_lblk, &newex)) { - if (!newex.ee_start_lo && !newex.ee_start_hi) { - if ((sbi->s_cluster_ratio > 1) && - ext4_find_delalloc_cluster(inode, map->m_lblk)) - map->m_flags |= EXT4_MAP_FROM_CLUSTER; - - if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) { - /* - * block isn't allocated yet and - * user doesn't want to allocate it - */ - goto out2; - } - /* we should allocate requested block */ - } else { - /* block is already allocated */ - if (sbi->s_cluster_ratio > 1) - map->m_flags |= EXT4_MAP_FROM_CLUSTER; - newblock = map->m_lblk - - le32_to_cpu(newex.ee_block) - + ext4_ext_pblock(&newex); - /* number of remaining blocks in the extent */ - allocated = ext4_ext_get_actual_len(&newex) - - (map->m_lblk - le32_to_cpu(newex.ee_block)); - goto out; - } - } - /* find extent for this block */ path = ext4_ext_find_extent(inode, map->m_lblk, NULL); if (IS_ERR(path)) { @@ -3995,15 +3902,9 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ext_debug("%u fit into %u:%d -> %llu\n", map->m_lblk, ee_block, ee_len, newblock); - /* - * Do not put uninitialized extent - * in the cache - */ - if (!ext4_ext_is_uninitialized(ex)) { - ext4_ext_put_in_cache(inode, ee_block, - ee_len, ee_start); + if (!ext4_ext_is_uninitialized(ex)) goto out; - } + allocated = ext4_ext_handle_uninitialized_extents( handle, inode, map, path, flags, allocated, newblock); @@ -4265,10 +4166,9 @@ got_allocated_blocks: * Cache the extent and update transaction to commit on fdatasync only * when it is _not_ an uninitialized extent. */ - if ((flags & EXT4_GET_BLOCKS_UNINIT_EXT) == 0) { - ext4_ext_put_in_cache(inode, map->m_lblk, allocated, newblock); + if ((flags & EXT4_GET_BLOCKS_UNINIT_EXT) == 0) ext4_update_inode_fsync_trans(handle, inode, 1); - } else + else ext4_update_inode_fsync_trans(handle, inode, 0); out: if (allocated > map->m_len) @@ -4327,7 +4227,6 @@ void ext4_ext_truncate(struct inode *inode) goto out_stop; down_write(&EXT4_I(inode)->i_data_sem); - ext4_ext_invalidate_cache(inode); ext4_discard_preallocations(inode); @@ -4576,42 +4475,42 @@ int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset, } /* - * If newex is not existing extent (newex->ec_start equals zero) find - * delayed extent at start of newex and update newex accordingly and + * If newes is not existing extent (newes->ec_pblk equals zero) find + * delayed extent at start of newes and update newes accordingly and * return start of the next delayed extent. * - * If newex is existing extent (newex->ec_start is not equal zero) + * If newes is existing extent (newes->ec_pblk is not equal zero) * return start of next delayed extent or EXT_MAX_BLOCKS if no delayed - * extent found. Leave newex unmodified. + * extent found. Leave newes unmodified. */ static int ext4_find_delayed_extent(struct inode *inode, - struct ext4_ext_cache *newex) + struct extent_status *newes) { struct extent_status es; ext4_lblk_t block, next_del; - ext4_es_find_delayed_extent(inode, newex->ec_block, &es); + ext4_es_find_delayed_extent(inode, newes->es_lblk, &es); - if (newex->ec_start == 0) { + if (newes->es_pblk == 0) { /* - * No extent in extent-tree contains block @newex->ec_start, + * No extent in extent-tree contains block @newes->es_pblk, * then the block may stay in 1)a hole or 2)delayed-extent. */ if (es.es_len == 0) /* A hole found. */ return 0; - if (es.es_lblk > newex->ec_block) { + if (es.es_lblk > newes->es_lblk) { /* A hole found. */ - newex->ec_len = min(es.es_lblk - newex->ec_block, - newex->ec_len); + newes->es_len = min(es.es_lblk - newes->es_lblk, + newes->es_len); return 0; } - newex->ec_len = es.es_lblk + es.es_len - newex->ec_block; + newes->es_len = es.es_lblk + es.es_len - newes->es_lblk; } - block = newex->ec_block + newex->ec_len; + block = newes->es_lblk + newes->es_len; ext4_es_find_delayed_extent(inode, block, &es); if (es.es_len == 0) next_del = EXT_MAX_BLOCKS; @@ -4815,14 +4714,12 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length) goto out; down_write(&EXT4_I(inode)->i_data_sem); - ext4_ext_invalidate_cache(inode); ext4_discard_preallocations(inode); err = ext4_es_remove_extent(inode, first_block, stop_block - first_block); err = ext4_ext_remove_space(inode, first_block, stop_block - 1); - ext4_ext_invalidate_cache(inode); ext4_discard_preallocations(inode); if (IS_SYNC(inode)) diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 0d6734394eac..d78c33eed7e5 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -764,9 +764,6 @@ out: kfree(donor_path); } - ext4_ext_invalidate_cache(orig_inode); - ext4_ext_invalidate_cache(donor_inode); - return replaced_count; } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index ef6ac59e4961..d80bfe5ac11c 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -836,7 +836,6 @@ static struct inode *ext4_alloc_inode(struct super_block *sb) return NULL; ei->vfs_inode.i_version = 1; - memset(&ei->i_cached_extent, 0, sizeof(struct ext4_ext_cache)); INIT_LIST_HEAD(&ei->i_prealloc_list); spin_lock_init(&ei->i_prealloc_lock); ext4_es_init_tree(&ei->i_es_tree); From bdedbb7b8d5b960e1ff0d116f5d4935febe73183 Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Mon, 18 Feb 2013 00:32:02 -0500 Subject: [PATCH 2993/4607] ext4: adjust some functions for reclaiming extents from extent status tree This commit changes some interfaces in extent status tree because we need to use inode to count the cached objects in a extent status tree. Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" Cc: Jan kara --- fs/ext4/extents_status.c | 50 +++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index eeb893122d8d..cce152c3c8dc 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -142,9 +142,8 @@ static struct kmem_cache *ext4_es_cachep; -static int __es_insert_extent(struct ext4_es_tree *tree, - struct extent_status *newes); -static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, +static int __es_insert_extent(struct inode *inode, struct extent_status *newes); +static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t end); int __init ext4_init_es(void) @@ -285,7 +284,8 @@ out: } static struct extent_status * -ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len, ext4_fsblk_t pblk) +ext4_es_alloc_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len, + ext4_fsblk_t pblk) { struct extent_status *es; es = kmem_cache_alloc(ext4_es_cachep, GFP_ATOMIC); @@ -297,7 +297,7 @@ ext4_es_alloc_extent(ext4_lblk_t lblk, ext4_lblk_t len, ext4_fsblk_t pblk) return es; } -static void ext4_es_free_extent(struct extent_status *es) +static void ext4_es_free_extent(struct inode *inode, struct extent_status *es) { kmem_cache_free(ext4_es_cachep, es); } @@ -326,8 +326,9 @@ static int ext4_es_can_be_merged(struct extent_status *es1, } static struct extent_status * -ext4_es_try_to_merge_left(struct ext4_es_tree *tree, struct extent_status *es) +ext4_es_try_to_merge_left(struct inode *inode, struct extent_status *es) { + struct ext4_es_tree *tree = &EXT4_I(inode)->i_es_tree; struct extent_status *es1; struct rb_node *node; @@ -339,7 +340,7 @@ ext4_es_try_to_merge_left(struct ext4_es_tree *tree, struct extent_status *es) if (ext4_es_can_be_merged(es1, es)) { es1->es_len += es->es_len; rb_erase(&es->rb_node, &tree->root); - ext4_es_free_extent(es); + ext4_es_free_extent(inode, es); es = es1; } @@ -347,8 +348,9 @@ ext4_es_try_to_merge_left(struct ext4_es_tree *tree, struct extent_status *es) } static struct extent_status * -ext4_es_try_to_merge_right(struct ext4_es_tree *tree, struct extent_status *es) +ext4_es_try_to_merge_right(struct inode *inode, struct extent_status *es) { + struct ext4_es_tree *tree = &EXT4_I(inode)->i_es_tree; struct extent_status *es1; struct rb_node *node; @@ -360,15 +362,15 @@ ext4_es_try_to_merge_right(struct ext4_es_tree *tree, struct extent_status *es) if (ext4_es_can_be_merged(es, es1)) { es->es_len += es1->es_len; rb_erase(node, &tree->root); - ext4_es_free_extent(es1); + ext4_es_free_extent(inode, es1); } return es; } -static int __es_insert_extent(struct ext4_es_tree *tree, - struct extent_status *newes) +static int __es_insert_extent(struct inode *inode, struct extent_status *newes) { + struct ext4_es_tree *tree = &EXT4_I(inode)->i_es_tree; struct rb_node **p = &tree->root.rb_node; struct rb_node *parent = NULL; struct extent_status *es; @@ -389,14 +391,14 @@ static int __es_insert_extent(struct ext4_es_tree *tree, ext4_es_is_unwritten(es)) ext4_es_store_pblock(es, newes->es_pblk); - es = ext4_es_try_to_merge_left(tree, es); + es = ext4_es_try_to_merge_left(inode, es); goto out; } p = &(*p)->rb_left; } else if (newes->es_lblk > ext4_es_end(es)) { if (ext4_es_can_be_merged(es, newes)) { es->es_len += newes->es_len; - es = ext4_es_try_to_merge_right(tree, es); + es = ext4_es_try_to_merge_right(inode, es); goto out; } p = &(*p)->rb_right; @@ -406,7 +408,7 @@ static int __es_insert_extent(struct ext4_es_tree *tree, } } - es = ext4_es_alloc_extent(newes->es_lblk, newes->es_len, + es = ext4_es_alloc_extent(inode, newes->es_lblk, newes->es_len, newes->es_pblk); if (!es) return -ENOMEM; @@ -430,7 +432,6 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len, ext4_fsblk_t pblk, unsigned long long status) { - struct ext4_es_tree *tree; struct extent_status newes; ext4_lblk_t end = lblk + len - 1; int err = 0; @@ -447,11 +448,10 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, trace_ext4_es_insert_extent(inode, &newes); write_lock(&EXT4_I(inode)->i_es_lock); - tree = &EXT4_I(inode)->i_es_tree; - err = __es_remove_extent(tree, lblk, end); + err = __es_remove_extent(inode, lblk, end); if (err != 0) goto error; - err = __es_insert_extent(tree, &newes); + err = __es_insert_extent(inode, &newes); error: write_unlock(&EXT4_I(inode)->i_es_lock); @@ -521,9 +521,10 @@ out: return found; } -static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, - ext4_lblk_t end) +static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, + ext4_lblk_t end) { + struct ext4_es_tree *tree = &EXT4_I(inode)->i_es_tree; struct rb_node *node; struct extent_status *es; struct extent_status orig_es; @@ -561,7 +562,7 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, ext4_es_store_pblock(&newes, block); } ext4_es_store_status(&newes, ext4_es_status(&orig_es)); - err = __es_insert_extent(tree, &newes); + err = __es_insert_extent(inode, &newes); if (err) { es->es_lblk = orig_es.es_lblk; es->es_len = orig_es.es_len; @@ -590,7 +591,7 @@ static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, while (es && ext4_es_end(es) <= end) { node = rb_next(&es->rb_node); rb_erase(&es->rb_node, &tree->root); - ext4_es_free_extent(es); + ext4_es_free_extent(inode, es); if (!node) { es = NULL; break; @@ -622,7 +623,6 @@ out: int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len) { - struct ext4_es_tree *tree; ext4_lblk_t end; int err = 0; @@ -633,10 +633,8 @@ int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, end = lblk + len - 1; BUG_ON(end < lblk); - tree = &EXT4_I(inode)->i_es_tree; - write_lock(&EXT4_I(inode)->i_es_lock); - err = __es_remove_extent(tree, lblk, end); + err = __es_remove_extent(inode, lblk, end); write_unlock(&EXT4_I(inode)->i_es_lock); ext4_es_print_tree(inode); return err; From 74cd15cd02708c7188581f279f33a98b2ae8d322 Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Mon, 18 Feb 2013 00:32:55 -0500 Subject: [PATCH 2994/4607] ext4: reclaim extents from extent status tree Although extent status is loaded on-demand, we also need to reclaim extent from the tree when we are under a heavy memory pressure because in some cases fragmented extent tree causes status tree costs too much memory. Here we maintain a lru list in super_block. When the extent status of an inode is accessed and changed, this inode will be move to the tail of the list. The inode will be dropped from this list when it is cleared. In the inode, a counter is added to count the number of cached objects in extent status tree. Here only written/unwritten/hole extent is counted because delayed extent doesn't be reclaimed due to fiemap, bigalloc and seek_data/hole need it. The counter will be increased as a new extent is allocated, and it will be decreased as a extent is freed. In this commit we use normal shrinker framework to reclaim memory from the status tree. ext4_es_reclaim_extents_count() traverses the lru list to count the number of reclaimable extents. ext4_es_shrink() tries to reclaim written/unwritten/hole extents from extent status tree. The inode that has been shrunk is moved to the tail of lru list. Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" Cc: Jan kara --- fs/ext4/ext4.h | 7 ++ fs/ext4/extents_status.c | 156 ++++++++++++++++++++++++++++++++++++ fs/ext4/extents_status.h | 5 ++ fs/ext4/super.c | 7 ++ include/trace/events/ext4.h | 60 ++++++++++++++ 5 files changed, 235 insertions(+) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 0c565c941f7a..6e16c1867959 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -888,6 +888,8 @@ struct ext4_inode_info { /* extents status tree */ struct ext4_es_tree i_es_tree; rwlock_t i_es_lock; + struct list_head i_es_lru; + unsigned int i_es_lru_nr; /* protected by i_es_lock */ /* ialloc */ ext4_group_t i_last_alloc_group; @@ -1303,6 +1305,11 @@ struct ext4_sb_info { /* Precomputed FS UUID checksum for seeding other checksums */ __u32 s_csum_seed; + + /* Reclaim extents from extent status tree */ + struct shrinker s_es_shrinker; + struct list_head s_es_lru; + spinlock_t s_es_lru_lock ____cacheline_aligned_in_smp; }; static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb) diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index cce152c3c8dc..9f1380e05474 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -145,6 +145,9 @@ static struct kmem_cache *ext4_es_cachep; static int __es_insert_extent(struct inode *inode, struct extent_status *newes); static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t end); +static int __es_try_to_reclaim_extents(struct ext4_inode_info *ei, + int nr_to_scan); +static int ext4_es_reclaim_extents_count(struct super_block *sb); int __init ext4_init_es(void) { @@ -280,6 +283,7 @@ out: read_unlock(&EXT4_I(inode)->i_es_lock); + ext4_es_lru_add(inode); trace_ext4_es_find_delayed_extent_exit(inode, es); } @@ -294,11 +298,24 @@ ext4_es_alloc_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len, es->es_lblk = lblk; es->es_len = len; es->es_pblk = pblk; + + /* + * We don't count delayed extent because we never try to reclaim them + */ + if (!ext4_es_is_delayed(es)) + EXT4_I(inode)->i_es_lru_nr++; + return es; } static void ext4_es_free_extent(struct inode *inode, struct extent_status *es) { + /* Decrease the lru counter when this es is not delayed */ + if (!ext4_es_is_delayed(es)) { + BUG_ON(EXT4_I(inode)->i_es_lru_nr == 0); + EXT4_I(inode)->i_es_lru_nr--; + } + kmem_cache_free(ext4_es_cachep, es); } @@ -456,6 +473,7 @@ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, error: write_unlock(&EXT4_I(inode)->i_es_lock); + ext4_es_lru_add(inode); ext4_es_print_tree(inode); return err; @@ -517,6 +535,7 @@ out: read_unlock(&EXT4_I(inode)->i_es_lock); + ext4_es_lru_add(inode); trace_ext4_es_lookup_extent_exit(inode, es, found); return found; } @@ -639,3 +658,140 @@ int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, ext4_es_print_tree(inode); return err; } + +static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc) +{ + struct ext4_sb_info *sbi = container_of(shrink, + struct ext4_sb_info, s_es_shrinker); + struct ext4_inode_info *ei; + struct list_head *cur, *tmp, scanned; + int nr_to_scan = sc->nr_to_scan; + int ret, nr_shrunk = 0; + + trace_ext4_es_shrink_enter(sbi->s_sb, nr_to_scan); + + if (!nr_to_scan) + return ext4_es_reclaim_extents_count(sbi->s_sb); + + INIT_LIST_HEAD(&scanned); + + spin_lock(&sbi->s_es_lru_lock); + list_for_each_safe(cur, tmp, &sbi->s_es_lru) { + list_move_tail(cur, &scanned); + + ei = list_entry(cur, struct ext4_inode_info, i_es_lru); + + read_lock(&ei->i_es_lock); + if (ei->i_es_lru_nr == 0) { + read_unlock(&ei->i_es_lock); + continue; + } + read_unlock(&ei->i_es_lock); + + write_lock(&ei->i_es_lock); + ret = __es_try_to_reclaim_extents(ei, nr_to_scan); + write_unlock(&ei->i_es_lock); + + nr_shrunk += ret; + nr_to_scan -= ret; + if (nr_to_scan == 0) + break; + } + list_splice_tail(&scanned, &sbi->s_es_lru); + spin_unlock(&sbi->s_es_lru_lock); + trace_ext4_es_shrink_exit(sbi->s_sb, nr_shrunk); + + return ext4_es_reclaim_extents_count(sbi->s_sb); +} + +void ext4_es_register_shrinker(struct super_block *sb) +{ + struct ext4_sb_info *sbi; + + sbi = EXT4_SB(sb); + INIT_LIST_HEAD(&sbi->s_es_lru); + spin_lock_init(&sbi->s_es_lru_lock); + sbi->s_es_shrinker.shrink = ext4_es_shrink; + sbi->s_es_shrinker.seeks = DEFAULT_SEEKS; + register_shrinker(&sbi->s_es_shrinker); +} + +void ext4_es_unregister_shrinker(struct super_block *sb) +{ + unregister_shrinker(&EXT4_SB(sb)->s_es_shrinker); +} + +void ext4_es_lru_add(struct inode *inode) +{ + struct ext4_inode_info *ei = EXT4_I(inode); + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + + spin_lock(&sbi->s_es_lru_lock); + if (list_empty(&ei->i_es_lru)) + list_add_tail(&ei->i_es_lru, &sbi->s_es_lru); + else + list_move_tail(&ei->i_es_lru, &sbi->s_es_lru); + spin_unlock(&sbi->s_es_lru_lock); +} + +void ext4_es_lru_del(struct inode *inode) +{ + struct ext4_inode_info *ei = EXT4_I(inode); + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + + spin_lock(&sbi->s_es_lru_lock); + if (!list_empty(&ei->i_es_lru)) + list_del_init(&ei->i_es_lru); + spin_unlock(&sbi->s_es_lru_lock); +} + +static int ext4_es_reclaim_extents_count(struct super_block *sb) +{ + struct ext4_sb_info *sbi = EXT4_SB(sb); + struct ext4_inode_info *ei; + struct list_head *cur; + int nr_cached = 0; + + spin_lock(&sbi->s_es_lru_lock); + list_for_each(cur, &sbi->s_es_lru) { + ei = list_entry(cur, struct ext4_inode_info, i_es_lru); + read_lock(&ei->i_es_lock); + nr_cached += ei->i_es_lru_nr; + read_unlock(&ei->i_es_lock); + } + spin_unlock(&sbi->s_es_lru_lock); + trace_ext4_es_reclaim_extents_count(sb, nr_cached); + return nr_cached; +} + +static int __es_try_to_reclaim_extents(struct ext4_inode_info *ei, + int nr_to_scan) +{ + struct inode *inode = &ei->vfs_inode; + struct ext4_es_tree *tree = &ei->i_es_tree; + struct rb_node *node; + struct extent_status *es; + int nr_shrunk = 0; + + if (ei->i_es_lru_nr == 0) + return 0; + + node = rb_first(&tree->root); + while (node != NULL) { + es = rb_entry(node, struct extent_status, rb_node); + node = rb_next(&es->rb_node); + /* + * We can't reclaim delayed extent from status tree because + * fiemap, bigallic, and seek_data/hole need to use it. + */ + if (!ext4_es_is_delayed(es)) { + rb_erase(&es->rb_node, &tree->root); + ext4_es_free_extent(inode, es); + nr_shrunk++; + if (--nr_to_scan == 0) + break; + } + } + tree->cache_es = NULL; + return nr_shrunk; +} diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index 8ffc90c784fa..cf83e77b16cb 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h @@ -106,4 +106,9 @@ static inline void ext4_es_store_status(struct extent_status *es, es->es_pblk = block; } +extern void ext4_es_register_shrinker(struct super_block *sb); +extern void ext4_es_unregister_shrinker(struct super_block *sb); +extern void ext4_es_lru_add(struct inode *inode); +extern void ext4_es_lru_del(struct inode *inode); + #endif /* _EXT4_EXTENTS_STATUS_H */ diff --git a/fs/ext4/super.c b/fs/ext4/super.c index d80bfe5ac11c..373d46cd5d3f 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -755,6 +755,7 @@ static void ext4_put_super(struct super_block *sb) ext4_abort(sb, "Couldn't clean up the journal"); } + ext4_es_unregister_shrinker(sb); del_timer(&sbi->s_err_report); ext4_release_system_zone(sb); ext4_mb_release(sb); @@ -840,6 +841,8 @@ static struct inode *ext4_alloc_inode(struct super_block *sb) spin_lock_init(&ei->i_prealloc_lock); ext4_es_init_tree(&ei->i_es_tree); rwlock_init(&ei->i_es_lock); + INIT_LIST_HEAD(&ei->i_es_lru); + ei->i_es_lru_nr = 0; ei->i_reserved_data_blocks = 0; ei->i_reserved_meta_blocks = 0; ei->i_allocated_meta_blocks = 0; @@ -928,6 +931,7 @@ void ext4_clear_inode(struct inode *inode) dquot_drop(inode); ext4_discard_preallocations(inode); ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS); + ext4_es_lru_del(inode); if (EXT4_I(inode)->jinode) { jbd2_journal_release_jbd_inode(EXT4_JOURNAL(inode), EXT4_I(inode)->jinode); @@ -3693,6 +3697,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) sbi->s_max_writeback_mb_bump = 128; sbi->s_extent_max_zeroout_kb = 32; + /* Register extent status tree shrinker */ + ext4_es_register_shrinker(sb); + /* * set up enough so that it can read an inode */ diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 1e590b68cec4..c0457c0d1a68 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -2255,6 +2255,66 @@ TRACE_EVENT(ext4_es_lookup_extent_exit, __entry->found ? __entry->status : 0) ); +TRACE_EVENT(ext4_es_reclaim_extents_count, + TP_PROTO(struct super_block *sb, int nr_cached), + + TP_ARGS(sb, nr_cached), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( int, nr_cached ) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->nr_cached = nr_cached; + ), + + TP_printk("dev %d,%d cached objects nr %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->nr_cached) +); + +TRACE_EVENT(ext4_es_shrink_enter, + TP_PROTO(struct super_block *sb, int nr_to_scan), + + TP_ARGS(sb, nr_to_scan), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( int, nr_to_scan ) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->nr_to_scan = nr_to_scan; + ), + + TP_printk("dev %d,%d nr to scan %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->nr_to_scan) +); + +TRACE_EVENT(ext4_es_shrink_exit, + TP_PROTO(struct super_block *sb, int shrunk_nr), + + TP_ARGS(sb, shrunk_nr), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( int, shrunk_nr ) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->shrunk_nr = shrunk_nr; + ), + + TP_printk("dev %d,%d nr to scan %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->shrunk_nr) +); + #endif /* _TRACE_EXT4_H */ /* This part must be outside protection */ From 7328735cbf68b7cd4d7ef16e172013743cdc2bc4 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 18 Feb 2013 09:35:49 -0500 Subject: [PATCH 2995/4607] ktest: Remove indexes from warnings check The index of a line where a warning is tested can be returned differently on different versions of gcc (or same version compiled differently). That is, a tab + space can give different results. This causes the warning check to produce a false positive. Removing the index from the check fixes this issue. Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index f3bb5da3193b..4e67d52eb3a2 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1945,6 +1945,27 @@ sub start_monitor_and_boot { my $check_build_re = ".*:.*(warning|error|Error):.*"; my $utf8_quote = "\\x{e2}\\x{80}(\\x{98}|\\x{99})"; +sub process_warning_line { + my ($line) = @_; + + chomp $line; + + # for distcc heterogeneous systems, some compilers + # do things differently causing warning lines + # to be slightly different. This makes an attempt + # to fixe those issues. + + # chop off the index into the line + # using distcc, some compilers give different indexes + # depending on white space + $line =~ s/^(\s*\S+:\d+:)\d+/$1/; + + # Some compilers use UTF-8 extended for quotes and some don't. + $line =~ s/$utf8_quote/'/g; + + return $line; +} + # Read buildlog and check against warnings file for any # new warnings. # @@ -1965,8 +1986,9 @@ sub check_buildlog { while () { if (/$check_build_re/) { - chomp; - $warnings_list{$_} = 1; + my $warning = process_warning_line $_; + + $warnings_list{$warning} = 1; } } close(IN); @@ -1978,13 +2000,9 @@ sub check_buildlog { open(IN, $buildlog) or dodie "Can't open $buildlog"; while () { if (/$check_build_re/) { + my $warning = process_warning_line $_; - # Some compilers use UTF-8 extended for quotes - # for distcc heterogeneous systems, this causes issues - s/$utf8_quote/'/g; - - chomp; - if (!defined $warnings_list{$_}) { + if (!defined $warnings_list{$warning}) { fail "New warning found (not in $warnings_file)\n$_\n"; $no_reboot = $save_no_reboot; return 0; From 2dd1194833de133960f286903ce704cb10fa7eb0 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 18 Feb 2013 10:10:33 -0700 Subject: [PATCH 2996/4607] vfio-pci: Manage user power state transitions We give the user access to change the power state of the device but certain transitions result in an uninitialized state which the user cannot resolve. To fix this we need to mark the PowerState field of the PMCSR register read-only and effect the requested change on behalf of the user. This has the added benefit that pdev->current_state remains accurate while controlled by the user. The primary example of this bug is a QEMU guest doing a reboot where the device it put into D3 on shutdown and becomes unusable on the next boot because the device did a soft reset on D3->D0 (NoSoftRst-). Signed-off-by: Alex Williamson --- drivers/vfio/pci/vfio_pci_config.c | 41 +++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index f1dde2c0fe99..964ff22bf281 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -587,12 +587,46 @@ static int __init init_pci_cap_basic_perm(struct perm_bits *perm) return 0; } +static int vfio_pm_config_write(struct vfio_pci_device *vdev, int pos, + int count, struct perm_bits *perm, + int offset, __le32 val) +{ + count = vfio_default_config_write(vdev, pos, count, perm, offset, val); + if (count < 0) + return count; + + if (offset == PCI_PM_CTRL) { + pci_power_t state; + + switch (le32_to_cpu(val) & PCI_PM_CTRL_STATE_MASK) { + case 0: + state = PCI_D0; + break; + case 1: + state = PCI_D1; + break; + case 2: + state = PCI_D2; + break; + case 3: + state = PCI_D3hot; + break; + } + + pci_set_power_state(vdev->pdev, state); + } + + return count; +} + /* Permissions for the Power Management capability */ static int __init init_pci_cap_pm_perm(struct perm_bits *perm) { if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_PM])) return -ENOMEM; + perm->writefn = vfio_pm_config_write; + /* * We always virtualize the next field so we can remove * capabilities from the chain if we want to. @@ -600,10 +634,11 @@ static int __init init_pci_cap_pm_perm(struct perm_bits *perm) p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); /* - * Power management is defined *per function*, - * so we let the user write this + * Power management is defined *per function*, so we can let + * the user change power state, but we trap and initiate the + * change ourselves, so the state bits are read-only. */ - p_setd(perm, PCI_PM_CTRL, NO_VIRT, ALL_WRITE); + p_setd(perm, PCI_PM_CTRL, NO_VIRT, ~PCI_PM_CTRL_STATE_MASK); return 0; } From 84237a826b261de7ddd3d09ee53ee68cb4138937 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 18 Feb 2013 10:11:13 -0700 Subject: [PATCH 2997/4607] vfio-pci: Add support for VGA region access PCI defines display class VGA regions at I/O port address 0x3b0, 0x3c0 and MMIO address 0xa0000. As these are non-overlapping, we can ignore the I/O port vs MMIO difference and expose them both in a single region. We make use of the VGA arbiter around each access to configure chipset access as necessary. Signed-off-by: Alex Williamson --- drivers/vfio/pci/Kconfig | 10 +++++ drivers/vfio/pci/vfio_pci.c | 18 +++++++++ drivers/vfio/pci/vfio_pci_private.h | 4 ++ drivers/vfio/pci/vfio_pci_rdwr.c | 61 +++++++++++++++++++++++++++++ include/uapi/linux/vfio.h | 9 +++++ 5 files changed, 102 insertions(+) diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig index 5980758563eb..e84300b268b6 100644 --- a/drivers/vfio/pci/Kconfig +++ b/drivers/vfio/pci/Kconfig @@ -6,3 +6,13 @@ config VFIO_PCI use of PCI drivers using the VFIO framework. If you don't know what to do here, say N. + +config VFIO_PCI_VGA + bool "VFIO PCI support for VGA devices" + depends on VFIO_PCI && X86 && VGA_ARB && EXPERIMENTAL + help + Support for VGA extension to VFIO PCI. This exposes an additional + region on VGA devices for accessing legacy VGA addresses used by + BIOS and generic video drivers. + + If you don't know what to do here, say N. diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index bb8c8c2be960..8189cb6a86af 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -84,6 +84,11 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) } else vdev->msix_bar = 0xFF; +#ifdef CONFIG_VFIO_PCI_VGA + if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) + vdev->has_vga = true; +#endif + return 0; } @@ -285,6 +290,16 @@ static long vfio_pci_ioctl(void *device_data, info.flags = VFIO_REGION_INFO_FLAG_READ; break; } + case VFIO_PCI_VGA_REGION_INDEX: + if (!vdev->has_vga) + return -EINVAL; + + info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index); + info.size = 0xc0000; + info.flags = VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE; + + break; default: return -EINVAL; } @@ -386,6 +401,9 @@ static ssize_t vfio_pci_rw(void *device_data, char __user *buf, case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX: return vfio_pci_bar_rw(vdev, buf, count, ppos, iswrite); + + case VFIO_PCI_VGA_REGION_INDEX: + return vfio_pci_vga_rw(vdev, buf, count, ppos, iswrite); } return -EINVAL; diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index 00d19b953ce4..d7e55d03f49e 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h @@ -53,6 +53,7 @@ struct vfio_pci_device { bool reset_works; bool extended_caps; bool bardirty; + bool has_vga; struct pci_saved_state *pci_saved_state; atomic_t refcnt; }; @@ -77,6 +78,9 @@ extern ssize_t vfio_pci_config_rw(struct vfio_pci_device *vdev, extern ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, size_t count, loff_t *ppos, bool iswrite); +extern ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite); + extern int vfio_pci_init_perm_bits(void); extern void vfio_pci_uninit_perm_bits(void); diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c index e9d78eb91ed7..210db24d2204 100644 --- a/drivers/vfio/pci/vfio_pci_rdwr.c +++ b/drivers/vfio/pci/vfio_pci_rdwr.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "vfio_pci_private.h" @@ -175,3 +176,63 @@ ssize_t vfio_pci_bar_rw(struct vfio_pci_device *vdev, char __user *buf, return done; } + +ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, + size_t count, loff_t *ppos, bool iswrite) +{ + int ret; + loff_t off, pos = *ppos & VFIO_PCI_OFFSET_MASK; + void __iomem *iomem = NULL; + unsigned int rsrc; + bool is_ioport; + ssize_t done; + + if (!vdev->has_vga) + return -EINVAL; + + switch (pos) { + case 0xa0000 ... 0xbffff: + count = min(count, (size_t)(0xc0000 - pos)); + iomem = ioremap_nocache(0xa0000, 0xbffff - 0xa0000 + 1); + off = pos - 0xa0000; + rsrc = VGA_RSRC_LEGACY_MEM; + is_ioport = false; + break; + case 0x3b0 ... 0x3bb: + count = min(count, (size_t)(0x3bc - pos)); + iomem = ioport_map(0x3b0, 0x3bb - 0x3b0 + 1); + off = pos - 0x3b0; + rsrc = VGA_RSRC_LEGACY_IO; + is_ioport = true; + break; + case 0x3c0 ... 0x3df: + count = min(count, (size_t)(0x3e0 - pos)); + iomem = ioport_map(0x3c0, 0x3df - 0x3c0 + 1); + off = pos - 0x3c0; + rsrc = VGA_RSRC_LEGACY_IO; + is_ioport = true; + break; + default: + return -EINVAL; + } + + if (!iomem) + return -ENOMEM; + + ret = vga_get_interruptible(vdev->pdev, rsrc); + if (ret) { + is_ioport ? ioport_unmap(iomem) : iounmap(iomem); + return ret; + } + + done = do_io_rw(iomem, buf, off, count, 0, 0, iswrite); + + vga_put(vdev->pdev, rsrc); + + is_ioport ? ioport_unmap(iomem) : iounmap(iomem); + + if (done >= 0) + *ppos += done; + + return done; +} diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h index 4758d1bfcf41..4f41f309911e 100644 --- a/include/uapi/linux/vfio.h +++ b/include/uapi/linux/vfio.h @@ -303,6 +303,15 @@ enum { VFIO_PCI_BAR5_REGION_INDEX, VFIO_PCI_ROM_REGION_INDEX, VFIO_PCI_CONFIG_REGION_INDEX, + /* + * Expose VGA regions defined for PCI base class 03, subclass 00. + * This includes I/O port ranges 0x3b0 to 0x3bb and 0x3c0 to 0x3df + * as well as the MMIO range 0xa0000 to 0xbffff. Each implemented + * range is found at it's identity mapped offset from the region + * offset, for example 0x3b0 is region_info.offset + 0x3b0. Areas + * between described ranges are unimplemented. + */ + VFIO_PCI_VGA_REGION_INDEX, VFIO_PCI_NUM_REGIONS }; From 1231b3a1eb5740192aeebf5344dd6d6da000febf Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Mon, 18 Feb 2013 12:12:07 -0500 Subject: [PATCH 2998/4607] ext4: fix xattr block allocation/release with bigalloc Currently when new xattr block is created or released we we would call dquot_free_block() or dquot_alloc_block() respectively, among the else decrementing or incrementing the number of blocks assigned to the inode by one block. This however does not work for bigalloc file system because we always allocate/free the whole cluster so we have to count with that in dquot_free_block() and dquot_alloc_block() as well. Use the clusters-to-blocks conversion EXT4_C2B() when passing number of blocks to the dquot_alloc/free functions to fix the problem. The problem has been revealed by xfstests #117 (and possibly others). Signed-off-by: Lukas Czerner Signed-off-by: "Theodore Ts'o" Reviewed-by: Eric Sandeen Cc: stable@vger.kernel.org --- fs/ext4/xattr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index cc31da027596..3a120b277240 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -549,7 +549,7 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode, error = ext4_handle_dirty_xattr_block(handle, inode, bh); if (IS_SYNC(inode)) ext4_handle_sync(handle); - dquot_free_block(inode, 1); + dquot_free_block(inode, EXT4_C2B(EXT4_SB(inode->i_sb), 1)); ea_bdebug(bh, "refcount now=%d; releasing", le32_to_cpu(BHDR(bh)->h_refcount)); } @@ -832,7 +832,8 @@ inserted: else { /* The old block is released after updating the inode. */ - error = dquot_alloc_block(inode, 1); + error = dquot_alloc_block(inode, + EXT4_C2B(EXT4_SB(sb), 1)); if (error) goto cleanup; error = ext4_journal_get_write_access(handle, @@ -929,7 +930,7 @@ cleanup: return error; cleanup_dquot: - dquot_free_block(inode, 1); + dquot_free_block(inode, EXT4_C2B(EXT4_SB(sb), 1)); goto cleanup; bad_block: From e7e319a9c51409c7effe34333ea26facf2fab9e1 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 14 Feb 2013 12:16:43 -0600 Subject: [PATCH 2999/4607] libceph: improve packing in struct ceph_osd_req_op The layout of struct ceph_osd_req_op leaves lots of holes. Rearranging things a little for better field alignment reduces the size by a third. This resolves: http://tracker.ceph.com/issues/4163 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- include/linux/ceph/osd_client.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 69287ccfe68a..82bf6338d6c1 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -157,6 +157,7 @@ struct ceph_osd_client { struct ceph_osd_req_op { u16 op; /* CEPH_OSD_OP_* */ + u32 payload_len; union { struct { u64 offset, length; @@ -165,23 +166,24 @@ struct ceph_osd_req_op { } extent; struct { const char *name; - u32 name_len; const char *val; + u32 name_len; u32 value_len; __u8 cmp_op; /* CEPH_OSD_CMPXATTR_OP_* */ __u8 cmp_mode; /* CEPH_OSD_CMPXATTR_MODE_* */ } xattr; struct { const char *class_name; - __u8 class_len; const char *method_name; - __u8 method_len; - __u8 argc; const char *indata; u32 indata_len; + __u8 class_len; + __u8 method_len; + __u8 argc; } cls; struct { - u64 cookie, count; + u64 cookie; + u64 count; } pgls; struct { u64 snapid; @@ -189,12 +191,11 @@ struct ceph_osd_req_op { struct { u64 cookie; u64 ver; - __u8 flag; u32 prot_ver; u32 timeout; + __u8 flag; } watch; }; - u32 payload_len; }; extern int ceph_osdc_init(struct ceph_osd_client *osdc, From 87f979d390f9ecfa3d0038a9f9a002a62f8a1895 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:29 -0600 Subject: [PATCH 3000/4607] ceph: kill ceph_osdc_writepages() "nofail" parameter There is only one caller of ceph_osdc_writepages(), and it always passes the value true as its "nofail" argument. Get rid of that argument and replace its use in ceph_osdc_writepages() with the constant value true. This and a number of cleanup patches that follow resolve: http://tracker.ceph.com/issues/4126 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- fs/ceph/addr.c | 2 +- include/linux/ceph/osd_client.h | 2 +- net/ceph/osd_client.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 064d1a68d2c1..c7e401c96fc9 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -493,7 +493,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) page_off, len, ci->i_truncate_seq, ci->i_truncate_size, &inode->i_mtime, - &page, 1, 0, 0, true); + &page, 1, 0, 0); if (err < 0) { dout("writepage setting page/mapping error %d %p\n", err, page); SetPageError(page); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 82bf6338d6c1..afcb255b016a 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -275,7 +275,7 @@ extern int ceph_osdc_writepages(struct ceph_osd_client *osdc, u32 truncate_seq, u64 truncate_size, struct timespec *mtime, struct page **pages, int nr_pages, - int flags, int do_sync, bool nofail); + int flags, int do_sync); /* watch/notify events */ extern int ceph_osdc_create_event(struct ceph_osd_client *osdc, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index d9d58bbe9f9a..dd01b1340e95 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1867,7 +1867,7 @@ int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, u32 truncate_seq, u64 truncate_size, struct timespec *mtime, struct page **pages, int num_pages, - int flags, int do_sync, bool nofail) + int flags, int do_sync) { struct ceph_osd_request *req; int rc = 0; @@ -1880,7 +1880,7 @@ int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, CEPH_OSD_FLAG_WRITE, snapc, do_sync, truncate_seq, truncate_size, mtime, - nofail, 1, page_align); + true, 1, page_align); if (IS_ERR(req)) return PTR_ERR(req); @@ -1889,7 +1889,7 @@ int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, dout("writepages %llu~%llu (%d pages)\n", off, len, req->r_num_pages); - rc = ceph_osdc_start_request(osdc, req, nofail); + rc = ceph_osdc_start_request(osdc, req, true); if (!rc) rc = ceph_osdc_wait_request(osdc, req); From fbf8685fb155e12a9f4d4b966c7b3442ed557687 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:29 -0600 Subject: [PATCH 3001/4607] ceph: kill ceph_osdc_writepages() "dosync" parameter There is only one caller of ceph_osdc_writepages(), and it always passes 0 as its "dosync" argument. Get rid of that argument and replace its use in ceph_osdc_writepages() with 0. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- fs/ceph/addr.c | 2 +- include/linux/ceph/osd_client.h | 2 +- net/ceph/osd_client.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index c7e401c96fc9..bef552819312 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -493,7 +493,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) page_off, len, ci->i_truncate_seq, ci->i_truncate_size, &inode->i_mtime, - &page, 1, 0, 0); + &page, 1, 0); if (err < 0) { dout("writepage setting page/mapping error %d %p\n", err, page); SetPageError(page); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index afcb255b016a..7a63100a3e69 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -275,7 +275,7 @@ extern int ceph_osdc_writepages(struct ceph_osd_client *osdc, u32 truncate_seq, u64 truncate_size, struct timespec *mtime, struct page **pages, int nr_pages, - int flags, int do_sync); + int flags); /* watch/notify events */ extern int ceph_osdc_create_event(struct ceph_osd_client *osdc, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index dd01b1340e95..ac186b7c9986 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1867,7 +1867,7 @@ int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, u32 truncate_seq, u64 truncate_size, struct timespec *mtime, struct page **pages, int num_pages, - int flags, int do_sync) + int flags) { struct ceph_osd_request *req; int rc = 0; @@ -1878,7 +1878,7 @@ int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, CEPH_OSD_OP_WRITE, flags | CEPH_OSD_FLAG_ONDISK | CEPH_OSD_FLAG_WRITE, - snapc, do_sync, + snapc, 0, truncate_seq, truncate_size, mtime, true, 1, page_align); if (IS_ERR(req)) From 2480882611e3ab844563dd3d0a822227604ab8fe Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:29 -0600 Subject: [PATCH 3002/4607] ceph: kill ceph_osdc_writepages() "flags" parameter There is only one caller of ceph_osdc_writepages(), and it always passes 0 as its "flags" argument. Get rid of that argument and replace its use in ceph_osdc_writepages() with 0. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- fs/ceph/addr.c | 3 +-- include/linux/ceph/osd_client.h | 3 +-- net/ceph/osd_client.c | 6 ++---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index bef552819312..8d3240d6f289 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -492,8 +492,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) &ci->i_layout, snapc, page_off, len, ci->i_truncate_seq, ci->i_truncate_size, - &inode->i_mtime, - &page, 1, 0); + &inode->i_mtime, &page, 1); if (err < 0) { dout("writepage setting page/mapping error %d %p\n", err, page); SetPageError(page); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 7a63100a3e69..6540e8861998 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -274,8 +274,7 @@ extern int ceph_osdc_writepages(struct ceph_osd_client *osdc, u64 off, u64 len, u32 truncate_seq, u64 truncate_size, struct timespec *mtime, - struct page **pages, int nr_pages, - int flags); + struct page **pages, int nr_pages); /* watch/notify events */ extern int ceph_osdc_create_event(struct ceph_osd_client *osdc, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index ac186b7c9986..d4e3812bcebc 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1866,8 +1866,7 @@ int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, u64 off, u64 len, u32 truncate_seq, u64 truncate_size, struct timespec *mtime, - struct page **pages, int num_pages, - int flags) + struct page **pages, int num_pages) { struct ceph_osd_request *req; int rc = 0; @@ -1876,8 +1875,7 @@ int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, BUG_ON(vino.snap != CEPH_NOSNAP); req = ceph_osdc_new_request(osdc, layout, vino, off, &len, CEPH_OSD_OP_WRITE, - flags | CEPH_OSD_FLAG_ONDISK | - CEPH_OSD_FLAG_WRITE, + CEPH_OSD_FLAG_ONDISK | CEPH_OSD_FLAG_WRITE, snapc, 0, truncate_seq, truncate_size, mtime, true, 1, page_align); From a3bea47e8bdd51d921e5b2045720d60140612c7c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:29 -0600 Subject: [PATCH 3003/4607] ceph: kill ceph_osdc_new_request() "num_reply" parameter The "num_reply" parameter to ceph_osdc_new_request() is never used inside that function, so get rid of it. Note that ceph_sync_write() passes 2 for that argument, while all other callers pass 1. It doesn't matter, but perhaps someone should verify this doesn't indicate a problem. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- fs/ceph/addr.c | 4 ++-- fs/ceph/file.c | 2 +- include/linux/ceph/osd_client.h | 3 +-- net/ceph/osd_client.c | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 8d3240d6f289..fc613715af46 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -315,7 +315,7 @@ static int start_read(struct inode *inode, struct list_head *page_list, int max) CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, NULL, 0, ci->i_truncate_seq, ci->i_truncate_size, - NULL, false, 1, 0); + NULL, false, 0); if (IS_ERR(req)) return PTR_ERR(req); @@ -837,7 +837,7 @@ get_more_pages: snapc, do_sync, ci->i_truncate_seq, ci->i_truncate_size, - &inode->i_mtime, true, 1, 0); + &inode->i_mtime, true, 0); if (IS_ERR(req)) { rc = PTR_ERR(req); diff --git a/fs/ceph/file.c b/fs/ceph/file.c index a1e5b81e8118..9c4325e654ca 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -541,7 +541,7 @@ more: ci->i_snap_realm->cached_context, do_sync, ci->i_truncate_seq, ci->i_truncate_size, - &mtime, false, 2, page_align); + &mtime, false, page_align); if (IS_ERR(req)) return PTR_ERR(req); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 6540e8861998..5812802bd8ae 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -234,8 +234,7 @@ extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, int do_sync, u32 truncate_seq, u64 truncate_size, struct timespec *mtime, - bool use_mempool, int num_reply, - int page_align); + bool use_mempool, int page_align); extern void ceph_osdc_set_request_linger(struct ceph_osd_client *osdc, struct ceph_osd_request *req); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index d4e3812bcebc..d3e75138506b 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -393,7 +393,7 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, u32 truncate_seq, u64 truncate_size, struct timespec *mtime, - bool use_mempool, int num_reply, + bool use_mempool, int page_align) { struct ceph_osd_req_op ops[2]; @@ -1837,7 +1837,7 @@ int ceph_osdc_readpages(struct ceph_osd_client *osdc, req = ceph_osdc_new_request(osdc, layout, vino, off, plen, CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, NULL, 0, truncate_seq, truncate_size, NULL, - false, 1, page_align); + false, page_align); if (IS_ERR(req)) return PTR_ERR(req); @@ -1878,7 +1878,7 @@ int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, CEPH_OSD_FLAG_ONDISK | CEPH_OSD_FLAG_WRITE, snapc, 0, truncate_seq, truncate_size, mtime, - true, 1, page_align); + true, page_align); if (IS_ERR(req)) return PTR_ERR(req); From f9d251994522fed06f47855b534f21c07ecf7181 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:29 -0600 Subject: [PATCH 3004/4607] libceph: lock outside send_queued() Two of the three callers of the osd client's send_queued() function already hold the osd client mutex and drop it before the call. Change send_queued() so it assumes the caller holds the mutex, and update all callers accordingly. Rename it __send_queued() to match the convention used elsewhere in the file with respect to the lock. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- net/ceph/osd_client.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index d3e75138506b..edda0704f5a7 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -23,7 +23,7 @@ static const struct ceph_connection_operations osd_con_ops; -static void send_queued(struct ceph_osd_client *osdc); +static void __send_queued(struct ceph_osd_client *osdc); static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd); static void __register_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req); @@ -554,8 +554,8 @@ static void osd_reset(struct ceph_connection *con) down_read(&osdc->map_sem); mutex_lock(&osdc->request_mutex); __kick_osd_requests(osdc, osd); + __send_queued(osdc); mutex_unlock(&osdc->request_mutex); - send_queued(osdc); up_read(&osdc->map_sem); } @@ -997,16 +997,13 @@ static void __send_request(struct ceph_osd_client *osdc, /* * Send any requests in the queue (req_unsent). */ -static void send_queued(struct ceph_osd_client *osdc) +static void __send_queued(struct ceph_osd_client *osdc) { struct ceph_osd_request *req, *tmp; - dout("send_queued\n"); - mutex_lock(&osdc->request_mutex); - list_for_each_entry_safe(req, tmp, &osdc->req_unsent, r_req_lru_item) { + dout("__send_queued\n"); + list_for_each_entry_safe(req, tmp, &osdc->req_unsent, r_req_lru_item) __send_request(osdc, req); - } - mutex_unlock(&osdc->request_mutex); } /* @@ -1058,8 +1055,8 @@ static void handle_timeout(struct work_struct *work) } __schedule_osd_timeout(osdc); + __send_queued(osdc); mutex_unlock(&osdc->request_mutex); - send_queued(osdc); up_read(&osdc->map_sem); } @@ -1397,7 +1394,9 @@ done: if (ceph_osdmap_flag(osdc->osdmap, CEPH_OSDMAP_FULL)) ceph_monc_request_next_osdmap(&osdc->client->monc); - send_queued(osdc); + mutex_lock(&osdc->request_mutex); + __send_queued(osdc); + mutex_unlock(&osdc->request_mutex); up_read(&osdc->map_sem); wake_up_all(&osdc->client->auth_wq); return; From 60789380ae833061803030d51952a5a341e4dade Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:29 -0600 Subject: [PATCH 3005/4607] libdeph: don't export ceph_osdc_init() or ceph_osdc_stop() The only callers of ceph_osdc_init() and ceph_osdc_stop() ceph_create_client() and ceph_destroy_client() (respectively) and they are in the same kernel module as those two functions. There's therefore no need to export those interfaces, so don't. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- net/ceph/osd_client.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index edda0704f5a7..0d67cd37173b 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1799,7 +1799,6 @@ out_mempool: out: return err; } -EXPORT_SYMBOL(ceph_osdc_init); void ceph_osdc_stop(struct ceph_osd_client *osdc) { @@ -1816,7 +1815,6 @@ void ceph_osdc_stop(struct ceph_osd_client *osdc) ceph_msgpool_destroy(&osdc->msgpool_op); ceph_msgpool_destroy(&osdc->msgpool_op_reply); } -EXPORT_SYMBOL(ceph_osdc_stop); /* * Read some contiguous pages. If we cross a stripe boundary, shorten From 60e56f138180e72fa8487d4b9c1c916013494f46 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:29 -0600 Subject: [PATCH 3006/4607] libceph: kill ceph_calc_raw_layout() There is no caller of ceph_calc_raw_layout() outside of libceph, so there's no need to export from the module. Furthermore, there is only one caller, in calc_layout(), and it is not much more than a simple wrapper for that function. So get rid of ceph_calc_raw_layout() and embed it instead within calc_layout(). While touching "osd_client.c", get rid of the unnecessary forward declaration of __send_request(). Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- include/linux/ceph/osd_client.h | 5 --- net/ceph/osd_client.c | 77 ++++++++++++++------------------- 2 files changed, 32 insertions(+), 50 deletions(-) diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 5812802bd8ae..c39e7ed4b203 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -207,11 +207,6 @@ extern void ceph_osdc_handle_reply(struct ceph_osd_client *osdc, extern void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg); -extern int ceph_calc_raw_layout(struct ceph_file_layout *layout, - u64 off, u64 *plen, u64 *bno, - struct ceph_osd_request *req, - struct ceph_osd_req_op *op); - extern struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, struct ceph_snap_context *snapc, unsigned int num_op, diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 0d67cd37173b..cd3a489b7438 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -38,49 +38,6 @@ static int op_has_extent(int op) op == CEPH_OSD_OP_WRITE); } -int ceph_calc_raw_layout(struct ceph_file_layout *layout, - u64 off, u64 *plen, u64 *bno, - struct ceph_osd_request *req, - struct ceph_osd_req_op *op) -{ - u64 orig_len = *plen; - u64 objoff, objlen; /* extent in object */ - int r; - - /* object extent? */ - r = ceph_calc_file_object_mapping(layout, off, orig_len, bno, - &objoff, &objlen); - if (r < 0) - return r; - if (objlen < orig_len) { - *plen = objlen; - dout(" skipping last %llu, final file extent %llu~%llu\n", - orig_len - *plen, off, *plen); - } - - if (op_has_extent(op->op)) { - u32 osize = le32_to_cpu(layout->fl_object_size); - op->extent.offset = objoff; - op->extent.length = objlen; - if (op->extent.truncate_size <= off - objoff) { - op->extent.truncate_size = 0; - } else { - op->extent.truncate_size -= off - objoff; - if (op->extent.truncate_size > osize) - op->extent.truncate_size = osize; - } - } - req->r_num_pages = calc_pages_for(off, *plen); - req->r_page_alignment = off & ~PAGE_MASK; - if (op->op == CEPH_OSD_OP_WRITE) - op->payload_len = *plen; - - dout("calc_layout bno=%llx %llu~%llu (%d pages)\n", - *bno, objoff, objlen, req->r_num_pages); - return 0; -} -EXPORT_SYMBOL(ceph_calc_raw_layout); - /* * Implement client access to distributed object storage cluster. * @@ -112,12 +69,42 @@ static int calc_layout(struct ceph_vino vino, struct ceph_osd_request *req, struct ceph_osd_req_op *op) { - u64 bno; + u64 orig_len = *plen; + u64 bno = 0; + u64 objoff = 0; + u64 objlen = 0; int r; - r = ceph_calc_raw_layout(layout, off, plen, &bno, req, op); + /* object extent? */ + r = ceph_calc_file_object_mapping(layout, off, orig_len, &bno, + &objoff, &objlen); if (r < 0) return r; + if (objlen < orig_len) { + *plen = objlen; + dout(" skipping last %llu, final file extent %llu~%llu\n", + orig_len - *plen, off, *plen); + } + + if (op_has_extent(op->op)) { + u32 osize = le32_to_cpu(layout->fl_object_size); + op->extent.offset = objoff; + op->extent.length = objlen; + if (op->extent.truncate_size <= off - objoff) { + op->extent.truncate_size = 0; + } else { + op->extent.truncate_size -= off - objoff; + if (op->extent.truncate_size > osize) + op->extent.truncate_size = osize; + } + } + req->r_num_pages = calc_pages_for(off, *plen); + req->r_page_alignment = off & ~PAGE_MASK; + if (op->op == CEPH_OSD_OP_WRITE) + op->payload_len = *plen; + + dout("calc_layout bno=%llx %llu~%llu (%d pages)\n", + bno, objoff, objlen, req->r_num_pages); snprintf(req->r_oid, sizeof(req->r_oid), "%llx.%08llx", vino.ino, bno); req->r_oid_len = strlen(req->r_oid); From 3c663bbdcdf9296e0fe3362acb9e81f49d7b72c6 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:30 -0600 Subject: [PATCH 3007/4607] libceph: kill ceph_osdc_create_event() "one_shot" parameter There is only one caller of ceph_osdc_create_event(), and it provides 0 as its "one_shot" argument. Get rid of that argument and just use 0 in its place. Replace the code in handle_watch_notify() that executes if one_shot is nonzero in the event with a BUG_ON() call. While modifying "osd_client.c", give handle_watch_notify() static scope. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- drivers/block/rbd.c | 2 +- include/linux/ceph/osd_client.h | 3 +-- net/ceph/osd_client.c | 11 +++++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 982963ec2607..13ee789f2328 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1744,7 +1744,7 @@ static int rbd_dev_header_watch_sync(struct rbd_device *rbd_dev, int start) rbd_assert(start ^ !!rbd_dev->watch_request); if (start) { - ret = ceph_osdc_create_event(osdc, rbd_watch_cb, 0, rbd_dev, + ret = ceph_osdc_create_event(osdc, rbd_watch_cb, rbd_dev, &rbd_dev->watch_event); if (ret < 0) return ret; diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index c39e7ed4b203..39c55d61e159 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -273,8 +273,7 @@ extern int ceph_osdc_writepages(struct ceph_osd_client *osdc, /* watch/notify events */ extern int ceph_osdc_create_event(struct ceph_osd_client *osdc, void (*event_cb)(u64, u64, u8, void *), - int one_shot, void *data, - struct ceph_osd_event **pevent); + void *data, struct ceph_osd_event **pevent); extern void ceph_osdc_cancel_event(struct ceph_osd_event *event); extern int ceph_osdc_wait_event(struct ceph_osd_event *event, unsigned long timeout); diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index cd3a489b7438..4322faa7c811 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1477,8 +1477,7 @@ static void __remove_event(struct ceph_osd_event *event) int ceph_osdc_create_event(struct ceph_osd_client *osdc, void (*event_cb)(u64, u64, u8, void *), - int one_shot, void *data, - struct ceph_osd_event **pevent) + void *data, struct ceph_osd_event **pevent) { struct ceph_osd_event *event; @@ -1488,7 +1487,7 @@ int ceph_osdc_create_event(struct ceph_osd_client *osdc, dout("create_event %p\n", event); event->cb = event_cb; - event->one_shot = one_shot; + event->one_shot = 0; event->data = data; event->osdc = osdc; INIT_LIST_HEAD(&event->osd_node); @@ -1541,7 +1540,8 @@ static void do_event_work(struct work_struct *work) /* * Process osd watch notifications */ -void handle_watch_notify(struct ceph_osd_client *osdc, struct ceph_msg *msg) +static void handle_watch_notify(struct ceph_osd_client *osdc, + struct ceph_msg *msg) { void *p, *end; u8 proto_ver; @@ -1562,9 +1562,8 @@ void handle_watch_notify(struct ceph_osd_client *osdc, struct ceph_msg *msg) spin_lock(&osdc->event_lock); event = __find_event(osdc, cookie); if (event) { + BUG_ON(event->one_shot); get_event(event); - if (event->one_shot) - __remove_event(event); } spin_unlock(&osdc->event_lock); dout("handle_watch_notify cookie %lld ver %lld event %p\n", From 2d2f522699fe8b827087941eb31b9a12cf465f17 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:30 -0600 Subject: [PATCH 3008/4607] libceph: kill ceph_osdc_wait_event() There are no actual users of ceph_osdc_wait_event(). This would have been one-shot events, but we no longer support those so just get rid of this function. Since this leaves nothing else that waits for the completion of an event, we can get rid of the completion in a struct ceph_osd_event. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- include/linux/ceph/osd_client.h | 3 --- net/ceph/osd_client.c | 18 ------------------ 2 files changed, 21 deletions(-) diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index 39c55d61e159..388158ff0cbc 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -107,7 +107,6 @@ struct ceph_osd_event { struct rb_node node; struct list_head osd_node; struct kref kref; - struct completion completion; }; struct ceph_osd_event_work { @@ -275,8 +274,6 @@ extern int ceph_osdc_create_event(struct ceph_osd_client *osdc, void (*event_cb)(u64, u64, u8, void *), void *data, struct ceph_osd_event **pevent); extern void ceph_osdc_cancel_event(struct ceph_osd_event *event); -extern int ceph_osdc_wait_event(struct ceph_osd_event *event, - unsigned long timeout); extern void ceph_osdc_put_event(struct ceph_osd_event *event); #endif diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 4322faa7c811..ad6b8b35f5ca 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -1494,7 +1494,6 @@ int ceph_osdc_create_event(struct ceph_osd_client *osdc, RB_CLEAR_NODE(&event->node); kref_init(&event->kref); /* one ref for us */ kref_get(&event->kref); /* one ref for the caller */ - init_completion(&event->completion); spin_lock(&osdc->event_lock); event->cookie = ++osdc->event_count; @@ -1530,7 +1529,6 @@ static void do_event_work(struct work_struct *work) dout("do_event_work completing %p\n", event); event->cb(ver, notify_id, opcode, event->data); - complete(&event->completion); dout("do_event_work completed %p\n", event); ceph_osdc_put_event(event); kfree(event_work); @@ -1588,7 +1586,6 @@ static void handle_watch_notify(struct ceph_osd_client *osdc, return; done_err: - complete(&event->completion); ceph_osdc_put_event(event); return; @@ -1597,21 +1594,6 @@ bad: return; } -int ceph_osdc_wait_event(struct ceph_osd_event *event, unsigned long timeout) -{ - int err; - - dout("wait_event %p\n", event); - err = wait_for_completion_interruptible_timeout(&event->completion, - timeout * HZ); - ceph_osdc_put_event(event); - if (err > 0) - err = 0; - dout("wait_event %p returns %d\n", event, err); - return err; -} -EXPORT_SYMBOL(ceph_osdc_wait_event); - /* * Register request, send initial attempt. */ From 0315a7770983bbe69211efed1aaee08324acd54c Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:30 -0600 Subject: [PATCH 3009/4607] libceph: update rados.h Update most of "include/linux/ceph/rados.h" to match its user space counterpart in "src/include/rados.h" in the ceph tree. Almost everything that has changed is either: - added or revised comments - added definitions (therefore no real effect on existing code) - defining the same value a different way (e.g., "1 << 0" vs "1") The only exceptions are: - The declaration of ceph_osd_state_name() was excluded; that will be inserted in the next patch. - ceph_osd_op_mode_read() and ceph_osd_op_mode_modify() are defined differently, but they were never used in the kernel - CEPH_OSD_FLAG_PEERSTAT is now CEPH_OSD_FLAG_PEERSTAT_OLD, but that was never used in the kernel Anything that was present in this file but not in its user space counterpart was left intact here. I left the definitions of EOLDSNAPC and EBLACKLISTED using numerical values here; I'm not sure the right way to go with those. This and the next two commits resolve: http://tracker.ceph.com/issues/4164 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- include/linux/ceph/rados.h | 91 +++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 20 deletions(-) diff --git a/include/linux/ceph/rados.h b/include/linux/ceph/rados.h index 2c04afeead1c..9c3b4aaf516b 100644 --- a/include/linux/ceph/rados.h +++ b/include/linux/ceph/rados.h @@ -145,8 +145,10 @@ struct ceph_eversion { */ /* status bits */ -#define CEPH_OSD_EXISTS 1 -#define CEPH_OSD_UP 2 +#define CEPH_OSD_EXISTS (1<<0) +#define CEPH_OSD_UP (1<<1) +#define CEPH_OSD_AUTOOUT (1<<2) /* osd was automatically marked out */ +#define CEPH_OSD_NEW (1<<3) /* osd is new, never marked in */ /* osd weights. fixed point value: 0x10000 == 1.0 ("in"), 0 == "out" */ #define CEPH_OSD_IN 0x10000 @@ -161,9 +163,25 @@ struct ceph_eversion { #define CEPH_OSDMAP_PAUSERD (1<<2) /* pause all reads */ #define CEPH_OSDMAP_PAUSEWR (1<<3) /* pause all writes */ #define CEPH_OSDMAP_PAUSEREC (1<<4) /* pause recovery */ +#define CEPH_OSDMAP_NOUP (1<<5) /* block osd boot */ +#define CEPH_OSDMAP_NODOWN (1<<6) /* block osd mark-down/failure */ +#define CEPH_OSDMAP_NOOUT (1<<7) /* block osd auto mark-out */ +#define CEPH_OSDMAP_NOIN (1<<8) /* block osd auto mark-in */ +#define CEPH_OSDMAP_NOBACKFILL (1<<9) /* block osd backfill */ +#define CEPH_OSDMAP_NORECOVER (1<<10) /* block osd recovery and backfill */ + +/* + * The error code to return when an OSD can't handle a write + * because it is too large. + */ +#define OSD_WRITETOOBIG EMSGSIZE /* * osd ops + * + * WARNING: do not use these op codes directly. Use the helpers + * defined below instead. In certain cases, op code behavior was + * redefined, resulting in special-cases in the helpers. */ #define CEPH_OSD_OP_MODE 0xf000 #define CEPH_OSD_OP_MODE_RD 0x1000 @@ -177,6 +195,7 @@ struct ceph_eversion { #define CEPH_OSD_OP_TYPE_ATTR 0x0300 #define CEPH_OSD_OP_TYPE_EXEC 0x0400 #define CEPH_OSD_OP_TYPE_PG 0x0500 +#define CEPH_OSD_OP_TYPE_MULTI 0x0600 /* multiobject */ enum { /** data **/ @@ -217,6 +236,23 @@ enum { CEPH_OSD_OP_WATCH = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 15, + /* omap */ + CEPH_OSD_OP_OMAPGETKEYS = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 17, + CEPH_OSD_OP_OMAPGETVALS = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 18, + CEPH_OSD_OP_OMAPGETHEADER = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 19, + CEPH_OSD_OP_OMAPGETVALSBYKEYS = + CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 20, + CEPH_OSD_OP_OMAPSETVALS = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 21, + CEPH_OSD_OP_OMAPSETHEADER = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 22, + CEPH_OSD_OP_OMAPCLEAR = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 23, + CEPH_OSD_OP_OMAPRMKEYS = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 24, + CEPH_OSD_OP_OMAP_CMP = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 25, + + /** multi **/ + CEPH_OSD_OP_CLONERANGE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_MULTI | 1, + CEPH_OSD_OP_ASSERT_SRC_VERSION = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_MULTI | 2, + CEPH_OSD_OP_SRC_CMPXATTR = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_MULTI | 3, + /** attrs **/ /* read */ CEPH_OSD_OP_GETXATTR = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_ATTR | 1, @@ -238,6 +274,7 @@ enum { CEPH_OSD_OP_SCRUB_RESERVE = CEPH_OSD_OP_MODE_SUB | 6, CEPH_OSD_OP_SCRUB_UNRESERVE = CEPH_OSD_OP_MODE_SUB | 7, CEPH_OSD_OP_SCRUB_STOP = CEPH_OSD_OP_MODE_SUB | 8, + CEPH_OSD_OP_SCRUB_MAP = CEPH_OSD_OP_MODE_SUB | 9, /** lock **/ CEPH_OSD_OP_WRLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 1, @@ -248,10 +285,12 @@ enum { CEPH_OSD_OP_DNLOCK = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_LOCK | 6, /** exec **/ + /* note: the RD bit here is wrong; see special-case below in helper */ CEPH_OSD_OP_CALL = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_EXEC | 1, /** pg **/ CEPH_OSD_OP_PGLS = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_PG | 1, + CEPH_OSD_OP_PGLS_FILTER = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_PG | 2, }; static inline int ceph_osd_op_type_lock(int op) @@ -274,6 +313,10 @@ static inline int ceph_osd_op_type_pg(int op) { return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_PG; } +static inline int ceph_osd_op_type_multi(int op) +{ + return (op & CEPH_OSD_OP_TYPE) == CEPH_OSD_OP_TYPE_MULTI; +} static inline int ceph_osd_op_mode_subop(int op) { @@ -281,11 +324,12 @@ static inline int ceph_osd_op_mode_subop(int op) } static inline int ceph_osd_op_mode_read(int op) { - return (op & CEPH_OSD_OP_MODE) == CEPH_OSD_OP_MODE_RD; + return (op & CEPH_OSD_OP_MODE_RD) && + op != CEPH_OSD_OP_CALL; } static inline int ceph_osd_op_mode_modify(int op) { - return (op & CEPH_OSD_OP_MODE) == CEPH_OSD_OP_MODE_WR; + return op & CEPH_OSD_OP_MODE_WR; } /* @@ -294,34 +338,38 @@ static inline int ceph_osd_op_mode_modify(int op) */ #define CEPH_OSD_TMAP_HDR 'h' #define CEPH_OSD_TMAP_SET 's' +#define CEPH_OSD_TMAP_CREATE 'c' /* create key */ #define CEPH_OSD_TMAP_RM 'r' +#define CEPH_OSD_TMAP_RMSLOPPY 'R' extern const char *ceph_osd_op_name(int op); - /* * osd op flags * * An op may be READ, WRITE, or READ|WRITE. */ enum { - CEPH_OSD_FLAG_ACK = 1, /* want (or is) "ack" ack */ - CEPH_OSD_FLAG_ONNVRAM = 2, /* want (or is) "onnvram" ack */ - CEPH_OSD_FLAG_ONDISK = 4, /* want (or is) "ondisk" ack */ - CEPH_OSD_FLAG_RETRY = 8, /* resend attempt */ - CEPH_OSD_FLAG_READ = 16, /* op may read */ - CEPH_OSD_FLAG_WRITE = 32, /* op may write */ - CEPH_OSD_FLAG_ORDERSNAP = 64, /* EOLDSNAP if snapc is out of order */ - CEPH_OSD_FLAG_PEERSTAT = 128, /* msg includes osd_peer_stat */ - CEPH_OSD_FLAG_BALANCE_READS = 256, - CEPH_OSD_FLAG_PARALLELEXEC = 512, /* execute op in parallel */ - CEPH_OSD_FLAG_PGOP = 1024, /* pg op, no object */ - CEPH_OSD_FLAG_EXEC = 2048, /* op may exec */ - CEPH_OSD_FLAG_EXEC_PUBLIC = 4096, /* op may exec (public) */ + CEPH_OSD_FLAG_ACK = 0x0001, /* want (or is) "ack" ack */ + CEPH_OSD_FLAG_ONNVRAM = 0x0002, /* want (or is) "onnvram" ack */ + CEPH_OSD_FLAG_ONDISK = 0x0004, /* want (or is) "ondisk" ack */ + CEPH_OSD_FLAG_RETRY = 0x0008, /* resend attempt */ + CEPH_OSD_FLAG_READ = 0x0010, /* op may read */ + CEPH_OSD_FLAG_WRITE = 0x0020, /* op may write */ + CEPH_OSD_FLAG_ORDERSNAP = 0x0040, /* EOLDSNAP if snapc is out of order */ + CEPH_OSD_FLAG_PEERSTAT_OLD = 0x0080, /* DEPRECATED msg includes osd_peer_stat */ + CEPH_OSD_FLAG_BALANCE_READS = 0x0100, + CEPH_OSD_FLAG_PARALLELEXEC = 0x0200, /* execute op in parallel */ + CEPH_OSD_FLAG_PGOP = 0x0400, /* pg op, no object */ + CEPH_OSD_FLAG_EXEC = 0x0800, /* op may exec */ + CEPH_OSD_FLAG_EXEC_PUBLIC = 0x1000, /* DEPRECATED op may exec (public) */ + CEPH_OSD_FLAG_LOCALIZE_READS = 0x2000, /* read from nearby replica, if any */ + CEPH_OSD_FLAG_RWORDERED = 0x4000, /* order wrt concurrent reads */ }; enum { CEPH_OSD_OP_FLAG_EXCL = 1, /* EXCL object create */ + CEPH_OSD_OP_FLAG_FAILOK = 2, /* continue despite failure */ }; #define EOLDSNAPC ERESTART /* ORDERSNAP flag set; writer has old snapc*/ @@ -381,7 +429,11 @@ struct ceph_osd_op { __le64 ver; __u8 flag; /* 0 = unwatch, 1 = watch */ } __attribute__ ((packed)) watch; -}; + struct { + __le64 offset, length; + __le64 src_offset; + } __attribute__ ((packed)) clonerange; + }; __le32 payload_len; } __attribute__ ((packed)); @@ -424,5 +476,4 @@ struct ceph_osd_reply_head { } __attribute__ ((packed)); - #endif From 4b568b1aaf23d0ce64b98d01d5ad1bcc7694440a Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:30 -0600 Subject: [PATCH 3010/4607] libceph: add ceph_osd_state_name() Add the definition of ceph_osd_state_name(), to match its counterpart in user space. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- include/linux/ceph/rados.h | 2 ++ net/ceph/ceph_strings.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/linux/ceph/rados.h b/include/linux/ceph/rados.h index 9c3b4aaf516b..b65182aba6f7 100644 --- a/include/linux/ceph/rados.h +++ b/include/linux/ceph/rados.h @@ -150,6 +150,8 @@ struct ceph_eversion { #define CEPH_OSD_AUTOOUT (1<<2) /* osd was automatically marked out */ #define CEPH_OSD_NEW (1<<3) /* osd is new, never marked in */ +extern const char *ceph_osd_state_name(int s); + /* osd weights. fixed point value: 0x10000 == 1.0 ("in"), 0 == "out" */ #define CEPH_OSD_IN 0x10000 #define CEPH_OSD_OUT 0 diff --git a/net/ceph/ceph_strings.c b/net/ceph/ceph_strings.c index 3fbda04de29c..833075cff4e8 100644 --- a/net/ceph/ceph_strings.c +++ b/net/ceph/ceph_strings.c @@ -68,6 +68,21 @@ const char *ceph_osd_op_name(int op) return "???"; } +const char *ceph_osd_state_name(int s) +{ + switch (s) { + case CEPH_OSD_EXISTS: + return "exists"; + case CEPH_OSD_UP: + return "up"; + case CEPH_OSD_AUTOOUT: + return "autoout"; + case CEPH_OSD_NEW: + return "new"; + default: + return "???"; + } +} const char *ceph_pool_op_name(int op) { From 2979ddb11befcd757a6ab5a04fad9e264560385b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:30 -0600 Subject: [PATCH 3011/4607] libceph: update ceph_osd_op_name() Update ceph_osd_op_name() to include the newly-added definitions in "rados.h", and to match its counterpart in the user space code. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- net/ceph/ceph_strings.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/net/ceph/ceph_strings.c b/net/ceph/ceph_strings.c index 833075cff4e8..1348df96fe15 100644 --- a/net/ceph/ceph_strings.c +++ b/net/ceph/ceph_strings.c @@ -21,9 +21,15 @@ const char *ceph_osd_op_name(int op) switch (op) { case CEPH_OSD_OP_READ: return "read"; case CEPH_OSD_OP_STAT: return "stat"; + case CEPH_OSD_OP_MAPEXT: return "mapext"; + case CEPH_OSD_OP_SPARSE_READ: return "sparse-read"; + case CEPH_OSD_OP_NOTIFY: return "notify"; + case CEPH_OSD_OP_NOTIFY_ACK: return "notify-ack"; + case CEPH_OSD_OP_ASSERT_VER: return "assert-version"; case CEPH_OSD_OP_MASKTRUNC: return "masktrunc"; + case CEPH_OSD_OP_CREATE: return "create"; case CEPH_OSD_OP_WRITE: return "write"; case CEPH_OSD_OP_DELETE: return "delete"; case CEPH_OSD_OP_TRUNCATE: return "truncate"; @@ -39,6 +45,11 @@ const char *ceph_osd_op_name(int op) case CEPH_OSD_OP_TMAPUP: return "tmapup"; case CEPH_OSD_OP_TMAPGET: return "tmapget"; case CEPH_OSD_OP_TMAPPUT: return "tmapput"; + case CEPH_OSD_OP_WATCH: return "watch"; + + case CEPH_OSD_OP_CLONERANGE: return "clonerange"; + case CEPH_OSD_OP_ASSERT_SRC_VERSION: return "assert-src-version"; + case CEPH_OSD_OP_SRC_CMPXATTR: return "src-cmpxattr"; case CEPH_OSD_OP_GETXATTR: return "getxattr"; case CEPH_OSD_OP_GETXATTRS: return "getxattrs"; @@ -53,6 +64,10 @@ const char *ceph_osd_op_name(int op) case CEPH_OSD_OP_BALANCEREADS: return "balance-reads"; case CEPH_OSD_OP_UNBALANCEREADS: return "unbalance-reads"; case CEPH_OSD_OP_SCRUB: return "scrub"; + case CEPH_OSD_OP_SCRUB_RESERVE: return "scrub-reserve"; + case CEPH_OSD_OP_SCRUB_UNRESERVE: return "scrub-unreserve"; + case CEPH_OSD_OP_SCRUB_STOP: return "scrub-stop"; + case CEPH_OSD_OP_SCRUB_MAP: return "scrub-map"; case CEPH_OSD_OP_WRLOCK: return "wrlock"; case CEPH_OSD_OP_WRUNLOCK: return "wrunlock"; @@ -64,6 +79,15 @@ const char *ceph_osd_op_name(int op) case CEPH_OSD_OP_CALL: return "call"; case CEPH_OSD_OP_PGLS: return "pgls"; + case CEPH_OSD_OP_PGLS_FILTER: return "pgls-filter"; + case CEPH_OSD_OP_OMAPGETKEYS: return "omap-get-keys"; + case CEPH_OSD_OP_OMAPGETVALS: return "omap-get-vals"; + case CEPH_OSD_OP_OMAPGETHEADER: return "omap-get-header"; + case CEPH_OSD_OP_OMAPGETVALSBYKEYS: return "omap-get-vals-by-keys"; + case CEPH_OSD_OP_OMAPSETVALS: return "omap-set-vals"; + case CEPH_OSD_OP_OMAPSETHEADER: return "omap-set-header"; + case CEPH_OSD_OP_OMAPCLEAR: return "omap-clear"; + case CEPH_OSD_OP_OMAPRMKEYS: return "omap-rm-keys"; } return "???"; } From 4c46459cae3b945e6e167f3f3a12b68f55cc5937 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:30 -0600 Subject: [PATCH 3012/4607] libceph: report defined but unsupported osd ops If osd_req_encode_op() is given any opcode it doesn't recognize it reports an error. This patch fleshes out that routine to distinguish between well-defined but unsupported values and values that are simply bogus. This and the next commit are related to: http://tracker.ceph.com/issues/4126 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- net/ceph/osd_client.c | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index ad6b8b35f5ca..ac7bcbf19574 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -281,6 +281,60 @@ static void osd_req_encode_op(struct ceph_osd_request *req, pr_err("unrecognized osd opcode %d\n", dst->op); WARN_ON(1); break; + case CEPH_OSD_OP_STAT: + case CEPH_OSD_OP_MAPEXT: + case CEPH_OSD_OP_MASKTRUNC: + case CEPH_OSD_OP_SPARSE_READ: + case CEPH_OSD_OP_ASSERT_VER: + case CEPH_OSD_OP_WRITEFULL: + case CEPH_OSD_OP_TRUNCATE: + case CEPH_OSD_OP_ZERO: + case CEPH_OSD_OP_DELETE: + case CEPH_OSD_OP_APPEND: + case CEPH_OSD_OP_SETTRUNC: + case CEPH_OSD_OP_TRIMTRUNC: + case CEPH_OSD_OP_TMAPUP: + case CEPH_OSD_OP_TMAPPUT: + case CEPH_OSD_OP_TMAPGET: + case CEPH_OSD_OP_CREATE: + case CEPH_OSD_OP_OMAPGETKEYS: + case CEPH_OSD_OP_OMAPGETVALS: + case CEPH_OSD_OP_OMAPGETHEADER: + case CEPH_OSD_OP_OMAPGETVALSBYKEYS: + case CEPH_OSD_OP_MODE_RD: + case CEPH_OSD_OP_OMAPSETVALS: + case CEPH_OSD_OP_OMAPSETHEADER: + case CEPH_OSD_OP_OMAPCLEAR: + case CEPH_OSD_OP_OMAPRMKEYS: + case CEPH_OSD_OP_OMAP_CMP: + case CEPH_OSD_OP_CLONERANGE: + case CEPH_OSD_OP_ASSERT_SRC_VERSION: + case CEPH_OSD_OP_SRC_CMPXATTR: + case CEPH_OSD_OP_GETXATTRS: + case CEPH_OSD_OP_SETXATTRS: + case CEPH_OSD_OP_RESETXATTRS: + case CEPH_OSD_OP_RMXATTR: + case CEPH_OSD_OP_PULL: + case CEPH_OSD_OP_PUSH: + case CEPH_OSD_OP_BALANCEREADS: + case CEPH_OSD_OP_UNBALANCEREADS: + case CEPH_OSD_OP_SCRUB: + case CEPH_OSD_OP_SCRUB_RESERVE: + case CEPH_OSD_OP_SCRUB_UNRESERVE: + case CEPH_OSD_OP_SCRUB_STOP: + case CEPH_OSD_OP_SCRUB_MAP: + case CEPH_OSD_OP_WRLOCK: + case CEPH_OSD_OP_WRUNLOCK: + case CEPH_OSD_OP_RDLOCK: + case CEPH_OSD_OP_RDUNLOCK: + case CEPH_OSD_OP_UPLOCK: + case CEPH_OSD_OP_DNLOCK: + case CEPH_OSD_OP_PGLS: + case CEPH_OSD_OP_PGLS_FILTER: + pr_err("unsupported osd opcode %s\n", + ceph_osd_op_name(dst->op)); + WARN_ON(1); + break; } dst->payload_len = cpu_to_le32(src->payload_len); } From a9f36c3ed48dbfbad4cf8ba92142873b62923300 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:30 -0600 Subject: [PATCH 3013/4607] libceph: remove dead code in osd_req_encode_op() In osd_req_encode_op() there are a few cases that handle osd opcodes that are never used in the kernel. The presence of this code gives the impression it's correct (which really can't be assumed), and may impose some unnecessary restrictions on some upcoming refactoring of this code. So delete this effectively dead code, and report uses of the previously handled cases as unsupported. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- net/ceph/osd_client.c | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index ac7bcbf19574..bbd157592c6c 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -231,19 +231,6 @@ static void osd_req_encode_op(struct ceph_osd_request *req, dst->extent.truncate_seq = cpu_to_le32(src->extent.truncate_seq); break; - - case CEPH_OSD_OP_GETXATTR: - case CEPH_OSD_OP_SETXATTR: - case CEPH_OSD_OP_CMPXATTR: - dst->xattr.name_len = cpu_to_le32(src->xattr.name_len); - dst->xattr.value_len = cpu_to_le32(src->xattr.value_len); - dst->xattr.cmp_op = src->xattr.cmp_op; - dst->xattr.cmp_mode = src->xattr.cmp_mode; - ceph_pagelist_append(&req->r_trail, src->xattr.name, - src->xattr.name_len); - ceph_pagelist_append(&req->r_trail, src->xattr.val, - src->xattr.value_len); - break; case CEPH_OSD_OP_CALL: dst->cls.class_len = src->cls.class_len; dst->cls.method_len = src->cls.method_len; @@ -256,21 +243,8 @@ static void osd_req_encode_op(struct ceph_osd_request *req, ceph_pagelist_append(&req->r_trail, src->cls.indata, src->cls.indata_len); break; - case CEPH_OSD_OP_ROLLBACK: - dst->snap.snapid = cpu_to_le64(src->snap.snapid); - break; case CEPH_OSD_OP_STARTSYNC: break; - case CEPH_OSD_OP_NOTIFY: - { - __le32 prot_ver = cpu_to_le32(src->watch.prot_ver); - __le32 timeout = cpu_to_le32(src->watch.timeout); - - ceph_pagelist_append(&req->r_trail, - &prot_ver, sizeof(prot_ver)); - ceph_pagelist_append(&req->r_trail, - &timeout, sizeof(timeout)); - } case CEPH_OSD_OP_NOTIFY_ACK: case CEPH_OSD_OP_WATCH: dst->watch.cookie = cpu_to_le64(src->watch.cookie); @@ -285,6 +259,7 @@ static void osd_req_encode_op(struct ceph_osd_request *req, case CEPH_OSD_OP_MAPEXT: case CEPH_OSD_OP_MASKTRUNC: case CEPH_OSD_OP_SPARSE_READ: + case CEPH_OSD_OP_NOTIFY: case CEPH_OSD_OP_ASSERT_VER: case CEPH_OSD_OP_WRITEFULL: case CEPH_OSD_OP_TRUNCATE: @@ -297,6 +272,7 @@ static void osd_req_encode_op(struct ceph_osd_request *req, case CEPH_OSD_OP_TMAPPUT: case CEPH_OSD_OP_TMAPGET: case CEPH_OSD_OP_CREATE: + case CEPH_OSD_OP_ROLLBACK: case CEPH_OSD_OP_OMAPGETKEYS: case CEPH_OSD_OP_OMAPGETVALS: case CEPH_OSD_OP_OMAPGETHEADER: @@ -310,7 +286,10 @@ static void osd_req_encode_op(struct ceph_osd_request *req, case CEPH_OSD_OP_CLONERANGE: case CEPH_OSD_OP_ASSERT_SRC_VERSION: case CEPH_OSD_OP_SRC_CMPXATTR: + case CEPH_OSD_OP_GETXATTR: case CEPH_OSD_OP_GETXATTRS: + case CEPH_OSD_OP_CMPXATTR: + case CEPH_OSD_OP_SETXATTR: case CEPH_OSD_OP_SETXATTRS: case CEPH_OSD_OP_RESETXATTRS: case CEPH_OSD_OP_RMXATTR: From dd6f5e105d85e02bc41db0891eb07152b1746ad9 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:30 -0600 Subject: [PATCH 3014/4607] libceph: update ceph_fs.h Update most of "include/linux/ceph/ceph_fs.h" to match its user space counterpart in "src/include/ceph_fs.h" in the ceph tree. Everything that has changed is either: - added definitions (therefore no real effect on existing code) - deleting unused symbols - added or revised comments There were some differences between the struct definitions for ceph_mon_subscribe_item and the open field of ceph_mds_request_args; those differences remain. This and the next commit resolve: http://tracker.ceph.com/issues/4165 Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- include/linux/ceph/ceph_fs.h | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h index cf6f4d998a76..2ad7b860f062 100644 --- a/include/linux/ceph/ceph_fs.h +++ b/include/linux/ceph/ceph_fs.h @@ -21,16 +21,14 @@ * internal cluster protocols separately from the public, * client-facing protocol. */ -#define CEPH_OSD_PROTOCOL 8 /* cluster internal */ -#define CEPH_MDS_PROTOCOL 12 /* cluster internal */ -#define CEPH_MON_PROTOCOL 5 /* cluster internal */ #define CEPH_OSDC_PROTOCOL 24 /* server/client */ #define CEPH_MDSC_PROTOCOL 32 /* server/client */ #define CEPH_MONC_PROTOCOL 15 /* server/client */ -#define CEPH_INO_ROOT 1 -#define CEPH_INO_CEPH 2 /* hidden .ceph dir */ +#define CEPH_INO_ROOT 1 +#define CEPH_INO_CEPH 2 /* hidden .ceph dir */ +#define CEPH_INO_DOTDOT 3 /* used by ceph fuse for parent (..) */ /* arbitrary limit on max # of monitors (cluster of 3 is typical) */ #define CEPH_MAX_MON 31 @@ -51,7 +49,7 @@ struct ceph_file_layout { __le32 fl_object_stripe_unit; /* UNUSED. for per-object parity, if any */ /* object -> pg layout */ - __le32 fl_unused; /* unused; used to be preferred primary (-1) */ + __le32 fl_unused; /* unused; used to be preferred primary for pg (-1 for none) */ __le32 fl_pg_pool; /* namespace, crush ruleset, rep level */ } __attribute__ ((packed)); @@ -101,6 +99,8 @@ struct ceph_dir_layout { #define CEPH_MSG_MON_SUBSCRIBE_ACK 16 #define CEPH_MSG_AUTH 17 #define CEPH_MSG_AUTH_REPLY 18 +#define CEPH_MSG_MON_GET_VERSION 19 +#define CEPH_MSG_MON_GET_VERSION_REPLY 20 /* client <-> mds */ #define CEPH_MSG_MDS_MAP 21 @@ -220,6 +220,11 @@ struct ceph_mon_subscribe_ack { struct ceph_fsid fsid; } __attribute__ ((packed)); +/* + * mdsmap flags + */ +#define CEPH_MDSMAP_DOWN (1<<0) /* cluster deliberately down */ + /* * mds states * > 0 -> in @@ -233,6 +238,7 @@ struct ceph_mon_subscribe_ack { #define CEPH_MDS_STATE_CREATING -6 /* up, creating MDS instance. */ #define CEPH_MDS_STATE_STARTING -7 /* up, starting previously stopped mds */ #define CEPH_MDS_STATE_STANDBY_REPLAY -8 /* up, tailing active node's journal */ +#define CEPH_MDS_STATE_REPLAYONCE -9 /* up, replaying an active node's journal */ #define CEPH_MDS_STATE_REPLAY 8 /* up, replaying journal. */ #define CEPH_MDS_STATE_RESOLVE 9 /* up, disambiguating distributed @@ -264,6 +270,7 @@ extern const char *ceph_mds_state_name(int s); #define CEPH_LOCK_IXATTR 2048 #define CEPH_LOCK_IFLOCK 4096 /* advisory file locks */ #define CEPH_LOCK_INO 8192 /* immutable inode bits; not a lock */ +#define CEPH_LOCK_IPOLICY 16384 /* policy lock on dirs. MDS internal */ /* client_session ops */ enum { @@ -338,6 +345,12 @@ extern const char *ceph_mds_op_name(int op); #define CEPH_SETATTR_SIZE 32 #define CEPH_SETATTR_CTIME 64 +/* + * Ceph setxattr request flags. + */ +#define CEPH_XATTR_CREATE 1 +#define CEPH_XATTR_REPLACE 2 + union ceph_mds_request_args { struct { __le32 mask; /* CEPH_CAP_* */ @@ -522,14 +535,17 @@ int ceph_flags_to_mode(int flags); #define CEPH_CAP_GWREXTEND 64 /* (file) client can extend EOF */ #define CEPH_CAP_GLAZYIO 128 /* (file) client can perform lazy io */ +#define CEPH_CAP_SIMPLE_BITS 2 +#define CEPH_CAP_FILE_BITS 8 + /* per-lock shift */ #define CEPH_CAP_SAUTH 2 #define CEPH_CAP_SLINK 4 #define CEPH_CAP_SXATTR 6 #define CEPH_CAP_SFILE 8 -#define CEPH_CAP_SFLOCK 20 +#define CEPH_CAP_SFLOCK 20 -#define CEPH_CAP_BITS 22 +#define CEPH_CAP_BITS 22 /* composed values */ #define CEPH_CAP_AUTH_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SAUTH) From 0eb40bf65e2fcc1c23ed7c9a10cd0890ee59e68f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 15 Feb 2013 11:42:30 -0600 Subject: [PATCH 3015/4607] libceph: update ceph_mds_state_name() and ceph_mds_op_name() Update ceph_mds_state_name() and ceph_mds_op_name() to include the newly-added definitions in "ceph_fs.h", and to match its counterpart in the user space code. Signed-off-by: Alex Elder Reviewed-by: Josh Durgin --- fs/ceph/strings.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/ceph/strings.c b/fs/ceph/strings.c index cd5097d7c804..89fa4a940a0f 100644 --- a/fs/ceph/strings.c +++ b/fs/ceph/strings.c @@ -15,6 +15,7 @@ const char *ceph_mds_state_name(int s) case CEPH_MDS_STATE_BOOT: return "up:boot"; case CEPH_MDS_STATE_STANDBY: return "up:standby"; case CEPH_MDS_STATE_STANDBY_REPLAY: return "up:standby-replay"; + case CEPH_MDS_STATE_REPLAYONCE: return "up:oneshot-replay"; case CEPH_MDS_STATE_CREATING: return "up:creating"; case CEPH_MDS_STATE_STARTING: return "up:starting"; /* up and in */ @@ -50,10 +51,13 @@ const char *ceph_mds_op_name(int op) case CEPH_MDS_OP_LOOKUP: return "lookup"; case CEPH_MDS_OP_LOOKUPHASH: return "lookuphash"; case CEPH_MDS_OP_LOOKUPPARENT: return "lookupparent"; + case CEPH_MDS_OP_LOOKUPINO: return "lookupino"; case CEPH_MDS_OP_GETATTR: return "getattr"; case CEPH_MDS_OP_SETXATTR: return "setxattr"; case CEPH_MDS_OP_SETATTR: return "setattr"; case CEPH_MDS_OP_RMXATTR: return "rmxattr"; + case CEPH_MDS_OP_SETLAYOUT: return "setlayou"; + case CEPH_MDS_OP_SETDIRLAYOUT: return "setdirlayout"; case CEPH_MDS_OP_READDIR: return "readdir"; case CEPH_MDS_OP_MKNOD: return "mknod"; case CEPH_MDS_OP_LINK: return "link"; From c206c70924737db6836382c09ad2dacd04bb6204 Mon Sep 17 00:00:00 2001 From: Philip J Kelleher Date: Mon, 18 Feb 2013 21:35:59 +0100 Subject: [PATCH 3016/4607] block: IBM RamSan 70/80 driver fixes This patch includes the following driver fixes for the IBM RamSan 70/80 driver: o Changed the creg_ctrl lock from a mutex to a spinlock. o Added a count check for ioctl calls. o Removed unnecessary casting of void pointers. o Made every function static that needed to be. o Added comments to explain things more thoroughly. Signed-off-by: Philip J Kelleher Signed-off-by: Jens Axboe --- drivers/block/rsxx/config.c | 4 +-- drivers/block/rsxx/core.c | 44 ++++++++++++------------- drivers/block/rsxx/cregs.c | 59 +++++++++++++++++++++------------- drivers/block/rsxx/dev.c | 2 +- drivers/block/rsxx/dma.c | 10 +++--- drivers/block/rsxx/rsxx.h | 2 ++ drivers/block/rsxx/rsxx_priv.h | 11 +------ 7 files changed, 69 insertions(+), 63 deletions(-) diff --git a/drivers/block/rsxx/config.c b/drivers/block/rsxx/config.c index c8829cd4db11..a295e7e9ee41 100644 --- a/drivers/block/rsxx/config.c +++ b/drivers/block/rsxx/config.c @@ -31,7 +31,7 @@ static void initialize_config(void *config) { - struct rsxx_card_cfg *cfg = (struct rsxx_card_cfg *) config; + struct rsxx_card_cfg *cfg = config; cfg->hdr.version = RSXX_CFG_VERSION; @@ -97,7 +97,7 @@ static void config_data_cpu_to_le(struct rsxx_card_cfg *cfg) /*----------------- Config Operations ------------------*/ -int rsxx_save_config(struct rsxx_cardinfo *card) +static int rsxx_save_config(struct rsxx_cardinfo *card) { struct rsxx_card_cfg cfg; int st; diff --git a/drivers/block/rsxx/core.c b/drivers/block/rsxx/core.c index 83dadbee0375..e5162487686a 100644 --- a/drivers/block/rsxx/core.c +++ b/drivers/block/rsxx/core.c @@ -102,9 +102,9 @@ void rsxx_disable_ier_and_isr(struct rsxx_cardinfo *card, iowrite32(card->ier_mask, card->regmap + IER); } -irqreturn_t rsxx_isr(int irq, void *pdata) +static irqreturn_t rsxx_isr(int irq, void *pdata) { - struct rsxx_cardinfo *card = (struct rsxx_cardinfo *) pdata; + struct rsxx_cardinfo *card = pdata; unsigned int isr; int handled = 0; int reread_isr; @@ -161,6 +161,17 @@ irqreturn_t rsxx_isr(int irq, void *pdata) } /*----------------- Card Event Handler -------------------*/ +static char *rsxx_card_state_to_str(unsigned int state) +{ + static char *state_strings[] = { + "Unknown", "Shutdown", "Starting", "Formatting", + "Uninitialized", "Good", "Shutting Down", + "Fault", "Read Only Fault", "dStroying" + }; + + return state_strings[ffs(state)]; +} + static void card_state_change(struct rsxx_cardinfo *card, unsigned int new_state) { @@ -251,18 +262,6 @@ static void card_event_handler(struct work_struct *work) rsxx_read_hw_log(card); } - -char *rsxx_card_state_to_str(unsigned int state) -{ - static char *state_strings[] = { - "Unknown", "Shutdown", "Starting", "Formatting", - "Uninitialized", "Good", "Shutting Down", - "Fault", "Read Only Fault", "dStroying" - }; - - return state_strings[ffs(state)]; -} - /*----------------- Card Operations -------------------*/ static int card_shutdown(struct rsxx_cardinfo *card) { @@ -323,7 +322,6 @@ static int rsxx_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct rsxx_cardinfo *card; - unsigned long flags; int st; dev_info(&dev->dev, "PCI-Flash SSD discovered\n"); @@ -386,9 +384,9 @@ static int rsxx_pci_probe(struct pci_dev *dev, spin_lock_init(&card->irq_lock); card->halt = 0; - spin_lock_irqsave(&card->irq_lock, flags); + spin_lock_irq(&card->irq_lock); rsxx_disable_ier_and_isr(card, CR_INTR_ALL); - spin_unlock_irqrestore(&card->irq_lock, flags); + spin_unlock_irq(&card->irq_lock); if (!force_legacy) { st = pci_enable_msi(dev); @@ -408,9 +406,9 @@ static int rsxx_pci_probe(struct pci_dev *dev, /************* Setup Processor Command Interface *************/ rsxx_creg_setup(card); - spin_lock_irqsave(&card->irq_lock, flags); + spin_lock_irq(&card->irq_lock); rsxx_enable_ier_and_isr(card, CR_INTR_CREG); - spin_unlock_irqrestore(&card->irq_lock, flags); + spin_unlock_irq(&card->irq_lock); st = rsxx_compatibility_check(card); if (st) { @@ -463,9 +461,9 @@ static int rsxx_pci_probe(struct pci_dev *dev, * we can enable the event interrupt(it kicks off actions in * those layers so we couldn't enable it right away.) */ - spin_lock_irqsave(&card->irq_lock, flags); + spin_lock_irq(&card->irq_lock); rsxx_enable_ier_and_isr(card, CR_INTR_EVENT); - spin_unlock_irqrestore(&card->irq_lock, flags); + spin_unlock_irq(&card->irq_lock); if (card->state == CARD_STATE_SHUTDOWN) { st = rsxx_issue_card_cmd(card, CARD_CMD_STARTUP); @@ -487,9 +485,9 @@ failed_create_dev: rsxx_dma_destroy(card); failed_dma_setup: failed_compatiblity_check: - spin_lock_irqsave(&card->irq_lock, flags); + spin_lock_irq(&card->irq_lock); rsxx_disable_ier_and_isr(card, CR_INTR_ALL); - spin_unlock_irqrestore(&card->irq_lock, flags); + spin_unlock_irq(&card->irq_lock); free_irq(dev->irq, card); if (!force_legacy) pci_disable_msi(dev); diff --git a/drivers/block/rsxx/cregs.c b/drivers/block/rsxx/cregs.c index a31fd727e804..80bbe639fccd 100644 --- a/drivers/block/rsxx/cregs.c +++ b/drivers/block/rsxx/cregs.c @@ -107,10 +107,10 @@ static struct creg_cmd *pop_active_cmd(struct rsxx_cardinfo *card) * Spin lock is needed because this can be called in atomic/interrupt * context. */ - spin_lock_bh(&card->creg_ctrl.pop_lock); + spin_lock_bh(&card->creg_ctrl.lock); cmd = card->creg_ctrl.active_cmd; card->creg_ctrl.active_cmd = NULL; - spin_unlock_bh(&card->creg_ctrl.pop_lock); + spin_unlock_bh(&card->creg_ctrl.lock); return cmd; } @@ -126,7 +126,11 @@ static void creg_issue_cmd(struct rsxx_cardinfo *card, struct creg_cmd *cmd) cmd->buf, cmd->stream); } - /* Data copy must complete before initiating the command. */ + /* + * Data copy must complete before initiating the command. This is + * needed for weakly ordered processors (i.e. PowerPC), so that all + * neccessary registers are written before we kick the hardware. + */ wmb(); /* Setting the valid bit will kick off the command. */ @@ -192,11 +196,11 @@ static int creg_queue_cmd(struct rsxx_cardinfo *card, cmd->cb_private = cb_private; cmd->status = 0; - mutex_lock(&card->creg_ctrl.lock); + spin_lock(&card->creg_ctrl.lock); list_add_tail(&cmd->list, &card->creg_ctrl.queue); card->creg_ctrl.q_depth++; creg_kick_queue(card); - mutex_unlock(&card->creg_ctrl.lock); + spin_unlock(&card->creg_ctrl.lock); return 0; } @@ -219,10 +223,11 @@ static void creg_cmd_timed_out(unsigned long data) kmem_cache_free(creg_cmd_pool, cmd); - spin_lock(&card->creg_ctrl.pop_lock); + + spin_lock(&card->creg_ctrl.lock); card->creg_ctrl.active = 0; creg_kick_queue(card); - spin_unlock(&card->creg_ctrl.pop_lock); + spin_unlock(&card->creg_ctrl.lock); } @@ -291,10 +296,10 @@ creg_done: kmem_cache_free(creg_cmd_pool, cmd); - mutex_lock(&card->creg_ctrl.lock); + spin_lock(&card->creg_ctrl.lock); card->creg_ctrl.active = 0; creg_kick_queue(card); - mutex_unlock(&card->creg_ctrl.lock); + spin_unlock(&card->creg_ctrl.lock); } static void creg_reset(struct rsxx_cardinfo *card) @@ -303,6 +308,10 @@ static void creg_reset(struct rsxx_cardinfo *card) struct creg_cmd *tmp; unsigned long flags; + /* + * mutex_trylock is used here because if reset_lock is taken then a + * reset is already happening. So, we can just go ahead and return. + */ if (!mutex_trylock(&card->creg_ctrl.reset_lock)) return; @@ -315,7 +324,7 @@ static void creg_reset(struct rsxx_cardinfo *card) "Resetting creg interface for recovery\n"); /* Cancel outstanding commands */ - mutex_lock(&card->creg_ctrl.lock); + spin_lock(&card->creg_ctrl.lock); list_for_each_entry_safe(cmd, tmp, &card->creg_ctrl.queue, list) { list_del(&cmd->list); card->creg_ctrl.q_depth--; @@ -336,7 +345,7 @@ static void creg_reset(struct rsxx_cardinfo *card) card->creg_ctrl.active = 0; } - mutex_unlock(&card->creg_ctrl.lock); + spin_unlock(&card->creg_ctrl.lock); card->creg_ctrl.reset = 0; spin_lock_irqsave(&card->irq_lock, flags); @@ -359,7 +368,7 @@ static void creg_cmd_done_cb(struct rsxx_cardinfo *card, { struct creg_completion *cmd_completion; - cmd_completion = (struct creg_completion *)cmd->cb_private; + cmd_completion = cmd->cb_private; BUG_ON(!cmd_completion); cmd_completion->st = st; @@ -380,7 +389,6 @@ static int __issue_creg_rw(struct rsxx_cardinfo *card, unsigned long timeout; int st; - INIT_COMPLETION(cmd_done); completion.cmd_done = &cmd_done; completion.st = 0; completion.creg_status = 0; @@ -390,8 +398,13 @@ static int __issue_creg_rw(struct rsxx_cardinfo *card, if (st) return st; + /* + * This timeout is neccessary for unresponsive hardware. The additional + * 20 seconds to used to guarantee that each cregs requests has time to + * complete. + */ timeout = msecs_to_jiffies((CREG_TIMEOUT_MSEC * - card->creg_ctrl.q_depth) + 20000); + card->creg_ctrl.q_depth) + 20000); /* * The creg interface is guaranteed to complete. It has a timeout @@ -443,7 +456,7 @@ static int issue_creg_rw(struct rsxx_cardinfo *card, if (st) return st; - data = (void *)((char *)data + xfer); + data = (char *)data + xfer; addr += xfer; size8 -= xfer; } while (size8); @@ -558,9 +571,9 @@ static void hw_log_msg(struct rsxx_cardinfo *card, const char *str, int len) } /* - * The substrncpy() function copies to string(up to count bytes) point to by src - * (including the terminating '\0' character) to dest. Returns the number of - * bytes copied to dest. + * The substrncpy function copies the src string (which includes the + * terminating '\0' character), up to the count into the dest pointer. + * Returns the number of bytes copied to dest. */ static int substrncpy(char *dest, const char *src, int count) { @@ -657,6 +670,9 @@ int rsxx_reg_access(struct rsxx_cardinfo *card, if (st) return -EFAULT; + if (cmd.cnt > RSXX_MAX_REG_CNT) + return -EFAULT; + st = issue_reg_cmd(card, &cmd, read); if (st) return st; @@ -682,8 +698,7 @@ int rsxx_creg_setup(struct rsxx_cardinfo *card) INIT_WORK(&card->creg_ctrl.done_work, creg_cmd_done); mutex_init(&card->creg_ctrl.reset_lock); INIT_LIST_HEAD(&card->creg_ctrl.queue); - mutex_init(&card->creg_ctrl.lock); - spin_lock_init(&card->creg_ctrl.pop_lock); + spin_lock_init(&card->creg_ctrl.lock); setup_timer(&card->creg_ctrl.cmd_timer, creg_cmd_timed_out, (unsigned long) card); @@ -697,7 +712,7 @@ void rsxx_creg_destroy(struct rsxx_cardinfo *card) int cnt = 0; /* Cancel outstanding commands */ - mutex_lock(&card->creg_ctrl.lock); + spin_lock(&card->creg_ctrl.lock); list_for_each_entry_safe(cmd, tmp, &card->creg_ctrl.queue, list) { list_del(&cmd->list); if (cmd->cb) @@ -722,7 +737,7 @@ void rsxx_creg_destroy(struct rsxx_cardinfo *card) "Canceled active creg command\n"); kmem_cache_free(creg_cmd_pool, cmd); } - mutex_unlock(&card->creg_ctrl.lock); + spin_unlock(&card->creg_ctrl.lock); cancel_work_sync(&card->creg_ctrl.done_work); } diff --git a/drivers/block/rsxx/dev.c b/drivers/block/rsxx/dev.c index 96df053e0ed4..4346d17d2949 100644 --- a/drivers/block/rsxx/dev.c +++ b/drivers/block/rsxx/dev.c @@ -149,7 +149,7 @@ static void bio_dma_done_cb(struct rsxx_cardinfo *card, void *cb_data, unsigned int error) { - struct rsxx_bio_meta *meta = (struct rsxx_bio_meta *)cb_data; + struct rsxx_bio_meta *meta = cb_data; if (error) atomic_set(&meta->error, 1); diff --git a/drivers/block/rsxx/dma.c b/drivers/block/rsxx/dma.c index 872f50358624..63176e67662f 100644 --- a/drivers/block/rsxx/dma.c +++ b/drivers/block/rsxx/dma.c @@ -102,7 +102,7 @@ struct dma_tracker_list { /*----------------- Misc Utility Functions -------------------*/ -unsigned int rsxx_addr8_to_laddr(u64 addr8, struct rsxx_cardinfo *card) +static unsigned int rsxx_addr8_to_laddr(u64 addr8, struct rsxx_cardinfo *card) { unsigned long long tgt_addr8; @@ -113,7 +113,7 @@ unsigned int rsxx_addr8_to_laddr(u64 addr8, struct rsxx_cardinfo *card) return tgt_addr8; } -unsigned int rsxx_get_dma_tgt(struct rsxx_cardinfo *card, u64 addr8) +static unsigned int rsxx_get_dma_tgt(struct rsxx_cardinfo *card, u64 addr8) { unsigned int tgt; @@ -757,7 +757,7 @@ static int rsxx_dma_ctrl_init(struct pci_dev *dev, INIT_LIST_HEAD(&ctrl->queue); setup_timer(&ctrl->activity_timer, dma_engine_stalled, - (unsigned long)ctrl); + (unsigned long)ctrl); ctrl->issue_wq = alloc_ordered_workqueue(DRIVER_NAME"_issue", 0); if (!ctrl->issue_wq) @@ -803,7 +803,7 @@ static int rsxx_dma_ctrl_init(struct pci_dev *dev, return 0; } -int rsxx_dma_stripe_setup(struct rsxx_cardinfo *card, +static int rsxx_dma_stripe_setup(struct rsxx_cardinfo *card, unsigned int stripe_size8) { if (!is_power_of_2(stripe_size8)) { @@ -834,7 +834,7 @@ int rsxx_dma_stripe_setup(struct rsxx_cardinfo *card, return 0; } -int rsxx_dma_configure(struct rsxx_cardinfo *card) +static int rsxx_dma_configure(struct rsxx_cardinfo *card) { u32 intr_coal; diff --git a/drivers/block/rsxx/rsxx.h b/drivers/block/rsxx/rsxx.h index 9581e136a042..2e50b65902b7 100644 --- a/drivers/block/rsxx/rsxx.h +++ b/drivers/block/rsxx/rsxx.h @@ -35,6 +35,8 @@ struct rsxx_reg_access { __u32 data[8]; }; +#define RSXX_MAX_REG_CNT (8 * (sizeof(__u32))) + #define RSXX_IOC_MAGIC 'r' #define RSXX_GETREG _IOWR(RSXX_IOC_MAGIC, 0x20, struct rsxx_reg_access) diff --git a/drivers/block/rsxx/rsxx_priv.h b/drivers/block/rsxx/rsxx_priv.h index 3887e496f54b..a1ac907d8f4c 100644 --- a/drivers/block/rsxx/rsxx_priv.h +++ b/drivers/block/rsxx/rsxx_priv.h @@ -127,7 +127,7 @@ struct rsxx_cardinfo { /* Embedded CPU Communication */ struct { - struct mutex lock; + spinlock_t lock; bool active; struct creg_cmd *active_cmd; struct work_struct done_work; @@ -141,7 +141,6 @@ struct rsxx_cardinfo { } creg_stats; struct timer_list cmd_timer; struct mutex reset_lock; - spinlock_t pop_lock; int reset; } creg_ctrl; @@ -336,7 +335,6 @@ static inline unsigned int CREG_DATA(int N) /***** config.c *****/ int rsxx_load_config(struct rsxx_cardinfo *card); -int rsxx_save_config(struct rsxx_cardinfo *card); /***** core.c *****/ void rsxx_enable_ier(struct rsxx_cardinfo *card, unsigned int intr); @@ -345,8 +343,6 @@ void rsxx_enable_ier_and_isr(struct rsxx_cardinfo *card, unsigned int intr); void rsxx_disable_ier_and_isr(struct rsxx_cardinfo *card, unsigned int intr); -char *rsxx_card_state_to_str(unsigned int state); -irqreturn_t rsxx_isr(int irq, void *pdata); /***** dev.c *****/ int rsxx_attach_dev(struct rsxx_cardinfo *card); @@ -364,16 +360,11 @@ int rsxx_dma_setup(struct rsxx_cardinfo *card); void rsxx_dma_destroy(struct rsxx_cardinfo *card); int rsxx_dma_init(void); void rsxx_dma_cleanup(void); -int rsxx_dma_configure(struct rsxx_cardinfo *card); int rsxx_dma_queue_bio(struct rsxx_cardinfo *card, struct bio *bio, atomic_t *n_dmas, rsxx_dma_cb cb, void *cb_data); -int rsxx_dma_stripe_setup(struct rsxx_cardinfo *card, - unsigned int stripe_size8); -unsigned int rsxx_get_dma_tgt(struct rsxx_cardinfo *card, u64 addr8); -unsigned int rsxx_addr8_to_laddr(u64 addr8, struct rsxx_cardinfo *card); /***** cregs.c *****/ int rsxx_creg_write(struct rsxx_cardinfo *card, u32 addr, From ed55705dd5008b408c48a8459b8b34b01f3de985 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Mon, 18 Feb 2013 22:58:14 -0300 Subject: [PATCH 3017/4607] x86: pvclock kvm: align allocation size to page size To match whats mapped via vsyscalls to userspace. Reported-by: Peter Hurley Signed-off-by: Marcelo Tosatti --- arch/x86/kernel/kvmclock.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c index 220a360010f8..5bedbdddf1f2 100644 --- a/arch/x86/kernel/kvmclock.c +++ b/arch/x86/kernel/kvmclock.c @@ -218,6 +218,9 @@ static void kvm_shutdown(void) void __init kvmclock_init(void) { unsigned long mem; + int size; + + size = PAGE_ALIGN(sizeof(struct pvclock_vsyscall_time_info)*NR_CPUS); if (!kvm_para_available()) return; @@ -231,16 +234,14 @@ void __init kvmclock_init(void) printk(KERN_INFO "kvm-clock: Using msrs %x and %x", msr_kvm_system_time, msr_kvm_wall_clock); - mem = memblock_alloc(sizeof(struct pvclock_vsyscall_time_info)*NR_CPUS, - PAGE_SIZE); + mem = memblock_alloc(size, PAGE_SIZE); if (!mem) return; hv_clock = __va(mem); if (kvm_register_clock("boot clock")) { hv_clock = NULL; - memblock_free(mem, - sizeof(struct pvclock_vsyscall_time_info)*NR_CPUS); + memblock_free(mem, size); return; } pv_time_ops.sched_clock = kvm_clock_read; @@ -275,7 +276,7 @@ int __init kvm_setup_vsyscall_timeinfo(void) struct pvclock_vcpu_time_info *vcpu_time; unsigned int size; - size = sizeof(struct pvclock_vsyscall_time_info)*NR_CPUS; + size = PAGE_ALIGN(sizeof(struct pvclock_vsyscall_time_info)*NR_CPUS); preempt_disable(); cpu = smp_processor_id(); From fcf29481fb8e106daad6688f2e898226ee928992 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 18 Feb 2013 18:00:33 -0800 Subject: [PATCH 3018/4607] target: Fix lookup of dynamic NodeACLs during cached demo-mode operation This patch fixes a bug in core_tpg_check_initiator_node_acl() -> core_tpg_get_initiator_node_acl() where a dynamically created se_node_acl generated during session login would be skipped during subsequent lookup due to the '!acl->dynamic_node_acl' check, causing a new se_node_acl to be created with a duplicate ->initiatorname. This would occur when a fabric endpoint was configured with TFO->tpg_check_demo_mode()=1 + TPF->tpg_check_demo_mode_cache()=1 preventing the release of an existing se_node_acl during se_session shutdown. Also, drop the unnecessary usage of core_tpg_get_initiator_node_acl() within core_dev_init_initiator_node_lun_acl() that originally required the extra '!acl->dynamic_node_acl' check, and just pass the configfs provided se_node_acl pointer instead. Cc: Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_device.c | 13 ++++--------- drivers/target/target_core_fabric_configfs.c | 4 ++-- drivers/target/target_core_internal.h | 2 +- drivers/target/target_core_tpg.c | 10 ++-------- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 6fb82f1abe5c..2e4d655471bc 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -1226,24 +1226,18 @@ static struct se_lun *core_dev_get_lun(struct se_portal_group *tpg, u32 unpacked struct se_lun_acl *core_dev_init_initiator_node_lun_acl( struct se_portal_group *tpg, + struct se_node_acl *nacl, u32 mapped_lun, - char *initiatorname, int *ret) { struct se_lun_acl *lacl; - struct se_node_acl *nacl; - if (strlen(initiatorname) >= TRANSPORT_IQN_LEN) { + if (strlen(nacl->initiatorname) >= TRANSPORT_IQN_LEN) { pr_err("%s InitiatorName exceeds maximum size.\n", tpg->se_tpg_tfo->get_fabric_name()); *ret = -EOVERFLOW; return NULL; } - nacl = core_tpg_get_initiator_node_acl(tpg, initiatorname); - if (!nacl) { - *ret = -EINVAL; - return NULL; - } lacl = kzalloc(sizeof(struct se_lun_acl), GFP_KERNEL); if (!lacl) { pr_err("Unable to allocate memory for struct se_lun_acl.\n"); @@ -1254,7 +1248,8 @@ struct se_lun_acl *core_dev_init_initiator_node_lun_acl( INIT_LIST_HEAD(&lacl->lacl_list); lacl->mapped_lun = mapped_lun; lacl->se_lun_nacl = nacl; - snprintf(lacl->initiatorname, TRANSPORT_IQN_LEN, "%s", initiatorname); + snprintf(lacl->initiatorname, TRANSPORT_IQN_LEN, "%s", + nacl->initiatorname); return lacl; } diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index c57bbbc7a7d1..b932653358dd 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -355,8 +355,8 @@ static struct config_group *target_fabric_make_mappedlun( goto out; } - lacl = core_dev_init_initiator_node_lun_acl(se_tpg, mapped_lun, - config_item_name(acl_ci), &ret); + lacl = core_dev_init_initiator_node_lun_acl(se_tpg, se_nacl, + mapped_lun, &ret); if (!lacl) { ret = -EINVAL; goto out; diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index fdc51f8301a1..853bab60e362 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -46,7 +46,7 @@ struct se_lun *core_dev_add_lun(struct se_portal_group *, struct se_device *, u3 int core_dev_del_lun(struct se_portal_group *, u32); struct se_lun *core_get_lun_from_tpg(struct se_portal_group *, u32); struct se_lun_acl *core_dev_init_initiator_node_lun_acl(struct se_portal_group *, - u32, char *, int *); + struct se_node_acl *, u32, int *); int core_dev_add_initiator_node_lun_acl(struct se_portal_group *, struct se_lun_acl *, u32, u32); int core_dev_del_initiator_node_lun_acl(struct se_portal_group *, diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 5192ac0337f7..9169d6a5d7e4 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -111,16 +111,10 @@ struct se_node_acl *core_tpg_get_initiator_node_acl( struct se_node_acl *acl; spin_lock_irq(&tpg->acl_node_lock); - list_for_each_entry(acl, &tpg->acl_node_list, acl_list) { - if (!strcmp(acl->initiatorname, initiatorname) && - !acl->dynamic_node_acl) { - spin_unlock_irq(&tpg->acl_node_lock); - return acl; - } - } + acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname); spin_unlock_irq(&tpg->acl_node_lock); - return NULL; + return acl; } /* core_tpg_add_node_to_devs(): From fbbf8555a986ed31e54f006b6cc637ea4ff1425b Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Mon, 18 Feb 2013 18:31:37 -0800 Subject: [PATCH 3019/4607] target: Add missing mapped_lun bounds checking during make_mappedlun setup This patch adds missing bounds checking for the configfs provided mapped_lun value during target_fabric_make_mappedlun() setup ahead of se_lun_acl initialization. This addresses a potential OOPs when using a mapped_lun value that exceeds the hardcoded TRANSPORT_MAX_LUNS_PER_TPG-1 value within se_node_acl->device_list[]. Reported-by: Jan Engelhardt Cc: Jan Engelhardt Cc: Signed-off-by: Nicholas Bellinger --- drivers/target/target_core_fabric_configfs.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index b932653358dd..04c775cb3e65 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -354,6 +354,14 @@ static struct config_group *target_fabric_make_mappedlun( ret = -EINVAL; goto out; } + if (mapped_lun > (TRANSPORT_MAX_LUNS_PER_TPG-1)) { + pr_err("Mapped LUN: %lu exceeds TRANSPORT_MAX_LUNS_PER_TPG" + "-1: %u for Target Portal Group: %u\n", mapped_lun, + TRANSPORT_MAX_LUNS_PER_TPG-1, + se_tpg->se_tpg_tfo->tpg_get_tag(se_tpg)); + ret = -EINVAL; + goto out; + } lacl = core_dev_init_initiator_node_lun_acl(se_tpg, se_nacl, mapped_lun, &ret); From 8c189ea64eea01ca20d102ddb74d6936dd16c579 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Wed, 13 Feb 2013 15:18:38 -0500 Subject: [PATCH 3020/4607] ftrace: Call ftrace cleanup module notifier after all other notifiers Commit: c1bf08ac "ftrace: Be first to run code modification on modules" changed ftrace module notifier's priority to INT_MAX in order to process the ftrace nops before anything else could touch them (namely kprobes). This was the correct thing to do. Unfortunately, the ftrace module notifier also contains the ftrace clean up code. As opposed to the set up code, this code should be run *after* all the module notifiers have run in case a module is doing correct clean-up and unregisters its ftrace hooks. Basically, ftrace needs to do clean up on module removal, as it needs to know about code being removed so that it doesn't try to modify that code. But after it removes the module from its records, if a ftrace user tries to remove a probe, that removal will fail due as the record of that code segment no longer exists. Nothing really bad happens if the probe removal is called after ftrace did the clean up, but the ftrace removal function will return an error. Correct code (such as kprobes) will produce a WARN_ON() if it fails to remove the probe. As people get annoyed by frivolous warnings, it's best to do the ftrace clean up after everything else. By splitting the ftrace_module_notifier into two notifiers, one that does the module load setup that is run at high priority, and the other that is called for module clean up that is run at low priority, the problem is solved. Cc: stable@vger.kernel.org Reported-by: Frank Ch. Eigler Acked-by: Masami Hiramatsu Signed-off-by: Steven Rostedt --- kernel/trace/ftrace.c | 46 ++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index ce8c3d68292f..98ca94a41819 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3996,37 +3996,51 @@ static void ftrace_init_module(struct module *mod, ftrace_process_locs(mod, start, end); } -static int ftrace_module_notify(struct notifier_block *self, - unsigned long val, void *data) +static int ftrace_module_notify_enter(struct notifier_block *self, + unsigned long val, void *data) { struct module *mod = data; - switch (val) { - case MODULE_STATE_COMING: + if (val == MODULE_STATE_COMING) ftrace_init_module(mod, mod->ftrace_callsites, mod->ftrace_callsites + mod->num_ftrace_callsites); - break; - case MODULE_STATE_GOING: + return 0; +} + +static int ftrace_module_notify_exit(struct notifier_block *self, + unsigned long val, void *data) +{ + struct module *mod = data; + + if (val == MODULE_STATE_GOING) ftrace_release_mod(mod); - break; - } return 0; } #else -static int ftrace_module_notify(struct notifier_block *self, - unsigned long val, void *data) +static int ftrace_module_notify_enter(struct notifier_block *self, + unsigned long val, void *data) +{ + return 0; +} +static int ftrace_module_notify_exit(struct notifier_block *self, + unsigned long val, void *data) { return 0; } #endif /* CONFIG_MODULES */ -struct notifier_block ftrace_module_nb = { - .notifier_call = ftrace_module_notify, +struct notifier_block ftrace_module_enter_nb = { + .notifier_call = ftrace_module_notify_enter, .priority = INT_MAX, /* Run before anything that can use kprobes */ }; +struct notifier_block ftrace_module_exit_nb = { + .notifier_call = ftrace_module_notify_exit, + .priority = INT_MIN, /* Run after anything that can remove kprobes */ +}; + extern unsigned long __start_mcount_loc[]; extern unsigned long __stop_mcount_loc[]; @@ -4058,9 +4072,13 @@ void __init ftrace_init(void) __start_mcount_loc, __stop_mcount_loc); - ret = register_module_notifier(&ftrace_module_nb); + ret = register_module_notifier(&ftrace_module_enter_nb); if (ret) - pr_warning("Failed to register trace ftrace module notifier\n"); + pr_warning("Failed to register trace ftrace module enter notifier\n"); + + ret = register_module_notifier(&ftrace_module_exit_nb); + if (ret) + pr_warning("Failed to register trace ftrace module exit notifier\n"); set_ftrace_early_filters(); From 0be5c8ff58cf7a66019af2f1236daff731ed318c Mon Sep 17 00:00:00 2001 From: Yuanhan Liu Date: Thu, 24 Jan 2013 17:22:44 +0800 Subject: [PATCH 3021/4607] locking/stat: Fix a typo s/STATS/STAT Signed-off-by: Yuanhan Liu Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1359019365-23646-1-git-send-email-yuanhan.liu@linux.intel.com Signed-off-by: Ingo Molnar --- Documentation/lockstat.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/lockstat.txt b/Documentation/lockstat.txt index cef00d42ed5b..dd2f7b26ca30 100644 --- a/Documentation/lockstat.txt +++ b/Documentation/lockstat.txt @@ -65,7 +65,7 @@ that had to wait on lock acquisition. - CONFIGURATION -Lock statistics are enabled via CONFIG_LOCK_STATS. +Lock statistics are enabled via CONFIG_LOCK_STAT. - USAGE From f86f75548233a2be7379ac446e91729710d4a5f7 Mon Sep 17 00:00:00 2001 From: "Srivatsa S. Bhat" Date: Tue, 8 Jan 2013 18:35:58 +0530 Subject: [PATCH 3022/4607] lockdep: Rename print_unlock_inbalance_bug() to print_unlock_imbalance_bug() Fix the typo in the function name (s/inbalance/imbalance) Signed-off-by: Srivatsa S. Bhat Cc: rostedt@goodmis.org Cc: peterz@infradead.org Link: http://lkml.kernel.org/r/20130108130547.32733.79507.stgit@srivatsabhat.in.ibm.com Signed-off-by: Ingo Molnar --- kernel/lockdep.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 7981e5b2350d..5cf12e791603 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -3203,7 +3203,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, } static int -print_unlock_inbalance_bug(struct task_struct *curr, struct lockdep_map *lock, +print_unlock_imbalance_bug(struct task_struct *curr, struct lockdep_map *lock, unsigned long ip) { if (!debug_locks_off()) @@ -3246,7 +3246,7 @@ static int check_unlock(struct task_struct *curr, struct lockdep_map *lock, return 0; if (curr->lockdep_depth <= 0) - return print_unlock_inbalance_bug(curr, lock, ip); + return print_unlock_imbalance_bug(curr, lock, ip); return 1; } @@ -3317,7 +3317,7 @@ __lock_set_class(struct lockdep_map *lock, const char *name, goto found_it; prev_hlock = hlock; } - return print_unlock_inbalance_bug(curr, lock, ip); + return print_unlock_imbalance_bug(curr, lock, ip); found_it: lockdep_init_map(lock, name, key, 0); @@ -3384,7 +3384,7 @@ lock_release_non_nested(struct task_struct *curr, goto found_it; prev_hlock = hlock; } - return print_unlock_inbalance_bug(curr, lock, ip); + return print_unlock_imbalance_bug(curr, lock, ip); found_it: if (hlock->instance == lock) From c06b4f1947213cd0902610fafcabac02d49ad728 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 27 Dec 2012 11:49:44 +0900 Subject: [PATCH 3023/4607] watchdog: Use local_clock for get_timestamp() The get_timestamp() function is always called with current cpu, thus using local_clock() would be more appropriate and it makes the code shorter and cleaner IMHO. Signed-off-by: Namhyung Kim Acked-by: Don Zickus Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1356576585-28782-1-git-send-email-namhyung@kernel.org Signed-off-by: Ingo Molnar --- kernel/watchdog.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 75a2ab3d0b02..082ca6878a3f 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -112,9 +112,9 @@ static int get_softlockup_thresh(void) * resolution, and we don't need to waste time with a big divide when * 2^30ns == 1.074s. */ -static unsigned long get_timestamp(int this_cpu) +static unsigned long get_timestamp(void) { - return cpu_clock(this_cpu) >> 30LL; /* 2^30 ~= 10^9 */ + return local_clock() >> 30LL; /* 2^30 ~= 10^9 */ } static void set_sample_period(void) @@ -132,9 +132,7 @@ static void set_sample_period(void) /* Commands for resetting the watchdog */ static void __touch_watchdog(void) { - int this_cpu = smp_processor_id(); - - __this_cpu_write(watchdog_touch_ts, get_timestamp(this_cpu)); + __this_cpu_write(watchdog_touch_ts, get_timestamp()); } void touch_softlockup_watchdog(void) @@ -195,7 +193,7 @@ static int is_hardlockup(void) static int is_softlockup(unsigned long touch_ts) { - unsigned long now = get_timestamp(smp_processor_id()); + unsigned long now = get_timestamp(); /* Warn about unreasonable delays: */ if (time_after(now, touch_ts + get_softlockup_thresh())) From 5cd3f5affad2109fd1458aab3f6216f2181e26ea Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Thu, 24 Jan 2013 21:53:17 +0100 Subject: [PATCH 3024/4607] lockdep: Silence warning if CONFIG_LOCKDEP isn't set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit c9a4962881929df7f1ef6e63e1b9da304faca4dd ("nfsd: make client_lock per net") compiling nfs4state.o without CONFIG_LOCKDEP set, triggers this GCC warning: fs/nfsd/nfs4state.c: In function ‘free_client’: fs/nfsd/nfs4state.c:1051:19: warning: unused variable ‘nn’ [-Wunused-variable] The cause of that warning is that lockdep_assert_held() compiles away if CONFIG_LOCKDEP is not set. Silence this warning by using the argument to lockdep_assert_held() as a nop if CONFIG_LOCKDEP is not set. Signed-off-by: Paul Bolle Cc: Peter Zijlstra Cc: Stanislav Kinsbursky Cc: J. Bruce Fields Link: http://lkml.kernel.org/r/1359060797.1325.33.camel@x61.thuisdomein Signed-off-by: Ingo Molnar -- include/linux/lockdep.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) --- include/linux/lockdep.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 2bca44b0893c..f05631effc73 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -410,7 +410,7 @@ struct lock_class_key { }; #define lockdep_depth(tsk) (0) -#define lockdep_assert_held(l) do { } while (0) +#define lockdep_assert_held(l) do { (void)(l); } while (0) #define lockdep_recursing(tsk) (0) From ce6711f3d196f09ca0ed29a24dfad42d83912b20 Mon Sep 17 00:00:00 2001 From: Alex Shi Date: Tue, 5 Feb 2013 21:11:55 +0800 Subject: [PATCH 3025/4607] rwsem: Implement writer lock-stealing for better scalability Commit 5a505085f043 ("mm/rmap: Convert the struct anon_vma::mutex to an rwsem") changed struct anon_vma::mutex to an rwsem, which caused aim7 fork_test performance to drop by 50%. Yuanhan Liu did the following excellent analysis: https://lkml.org/lkml/2013/1/29/84 and found that the regression is caused by strict, serialized, FIFO sequential write-ownership of rwsems. Ingo suggested implementing opportunistic lock-stealing for the front writer task in the waitqueue. Yuanhan Liu implemented lock-stealing for spinlock-rwsems, which indeed recovered much of the regression - confirming the analysis that the main factor in the regression was the FIFO writer-fairness of rwsems. In this patch we allow lock-stealing to happen when the first waiter is also writer. With that change in place the aim7 fork_test performance is fully recovered on my Intel NHM EP, NHM EX, SNB EP 2S and 4S test-machines. Reported-by: lkp@linux.intel.com Reported-by: Yuanhan Liu Signed-off-by: Alex Shi Cc: David Howells Cc: Michel Lespinasse Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Cc: Anton Blanchard Cc: Arjan van de Ven Cc: paul.gortmaker@windriver.com Link: https://lkml.org/lkml/2013/1/29/84 Link: http://lkml.kernel.org/r/1360069915-31619-1-git-send-email-alex.shi@intel.com [ Small stylistic fixes, updated changelog. ] Signed-off-by: Ingo Molnar --- lib/rwsem.c | 75 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/lib/rwsem.c b/lib/rwsem.c index 8337e1b9bb8d..ad5e0df16ab4 100644 --- a/lib/rwsem.c +++ b/lib/rwsem.c @@ -2,6 +2,8 @@ * * Written by David Howells (dhowells@redhat.com). * Derived from arch/i386/kernel/semaphore.c + * + * Writer lock-stealing by Alex Shi */ #include #include @@ -60,7 +62,7 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wake_type) struct rwsem_waiter *waiter; struct task_struct *tsk; struct list_head *next; - signed long oldcount, woken, loop, adjustment; + signed long woken, loop, adjustment; waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list); if (!(waiter->flags & RWSEM_WAITING_FOR_WRITE)) @@ -72,30 +74,8 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wake_type) */ goto out; - /* There's a writer at the front of the queue - try to grant it the - * write lock. However, we only wake this writer if we can transition - * the active part of the count from 0 -> 1 - */ - adjustment = RWSEM_ACTIVE_WRITE_BIAS; - if (waiter->list.next == &sem->wait_list) - adjustment -= RWSEM_WAITING_BIAS; - - try_again_write: - oldcount = rwsem_atomic_update(adjustment, sem) - adjustment; - if (oldcount & RWSEM_ACTIVE_MASK) - /* Someone grabbed the sem already */ - goto undo_write; - - /* We must be careful not to touch 'waiter' after we set ->task = NULL. - * It is an allocated on the waiter's stack and may become invalid at - * any time after that point (due to a wakeup from another source). - */ - list_del(&waiter->list); - tsk = waiter->task; - smp_mb(); - waiter->task = NULL; - wake_up_process(tsk); - put_task_struct(tsk); + /* Wake up the writing waiter and let the task grab the sem: */ + wake_up_process(waiter->task); goto out; readers_only: @@ -157,12 +137,40 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wake_type) out: return sem; +} - /* undo the change to the active count, but check for a transition - * 1->0 */ - undo_write: +/* Try to get write sem, caller holds sem->wait_lock: */ +static int try_get_writer_sem(struct rw_semaphore *sem, + struct rwsem_waiter *waiter) +{ + struct rwsem_waiter *fwaiter; + long oldcount, adjustment; + + /* only steal when first waiter is writing */ + fwaiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list); + if (!(fwaiter->flags & RWSEM_WAITING_FOR_WRITE)) + return 0; + + adjustment = RWSEM_ACTIVE_WRITE_BIAS; + /* Only one waiter in the queue: */ + if (fwaiter == waiter && waiter->list.next == &sem->wait_list) + adjustment -= RWSEM_WAITING_BIAS; + +try_again_write: + oldcount = rwsem_atomic_update(adjustment, sem) - adjustment; + if (!(oldcount & RWSEM_ACTIVE_MASK)) { + /* No active lock: */ + struct task_struct *tsk = waiter->task; + + list_del(&waiter->list); + smp_mb(); + put_task_struct(tsk); + tsk->state = TASK_RUNNING; + return 1; + } + /* some one grabbed the sem already */ if (rwsem_atomic_update(-adjustment, sem) & RWSEM_ACTIVE_MASK) - goto out; + return 0; goto try_again_write; } @@ -210,6 +218,15 @@ rwsem_down_failed_common(struct rw_semaphore *sem, for (;;) { if (!waiter.task) break; + + raw_spin_lock_irq(&sem->wait_lock); + /* Try to get the writer sem, may steal from the head writer: */ + if (flags == RWSEM_WAITING_FOR_WRITE) + if (try_get_writer_sem(sem, &waiter)) { + raw_spin_unlock_irq(&sem->wait_lock); + return sem; + } + raw_spin_unlock_irq(&sem->wait_lock); schedule(); set_task_state(tsk, TASK_UNINTERRUPTIBLE); } From c0540606837af79b2ae101e5e7b2206e3844d150 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Wed, 6 Feb 2013 10:56:19 -0800 Subject: [PATCH 3026/4607] lockdep: Print more info when MAX_LOCK_DEPTH is exceeded This helps debug cases where a lock is acquired over and over without being released. Suggested-by: Steven Rostedt Signed-off-by: Ben Greear Cc: peterz@infradead.org Link: http://lkml.kernel.org/r/1360176979-4421-1-git-send-email-greearb@candelatech.com [ Changed the printout ordering. ] Signed-off-by: Ingo Molnar --- kernel/lockdep.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 5cf12e791603..8a0efac4f99d 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -3190,9 +3190,14 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, #endif if (unlikely(curr->lockdep_depth >= MAX_LOCK_DEPTH)) { debug_locks_off(); - printk("BUG: MAX_LOCK_DEPTH too low!\n"); + printk("BUG: MAX_LOCK_DEPTH too low, depth: %i max: %lu!\n", + curr->lockdep_depth, MAX_LOCK_DEPTH); printk("turning off the locking correctness validator.\n"); + + lockdep_print_held_locks(current); + debug_show_all_locks(); dump_stack(); + return 0; } From eece09ec213e93333010bf4c6bb9175b32229c54 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 17 Jul 2011 21:25:03 +0200 Subject: [PATCH 3027/4607] locking: Various static lock initializer fixes The static lock initializers want to be fed the proper name of the lock and not some random string. In mainline random strings are obfuscating the readability of debug output, but for RT they prevent the spinlock substitution. Fix it up. Signed-off-by: Thomas Gleixner --- drivers/char/random.c | 6 +++--- drivers/usb/chipidea/debug.c | 2 +- fs/file.c | 2 +- include/linux/idr.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/char/random.c b/drivers/char/random.c index 85e81ec1451e..594bda9dcfc8 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -445,7 +445,7 @@ static struct entropy_store input_pool = { .poolinfo = &poolinfo_table[0], .name = "input", .limit = 1, - .lock = __SPIN_LOCK_UNLOCKED(&input_pool.lock), + .lock = __SPIN_LOCK_UNLOCKED(input_pool.lock), .pool = input_pool_data }; @@ -454,7 +454,7 @@ static struct entropy_store blocking_pool = { .name = "blocking", .limit = 1, .pull = &input_pool, - .lock = __SPIN_LOCK_UNLOCKED(&blocking_pool.lock), + .lock = __SPIN_LOCK_UNLOCKED(blocking_pool.lock), .pool = blocking_pool_data }; @@ -462,7 +462,7 @@ static struct entropy_store nonblocking_pool = { .poolinfo = &poolinfo_table[1], .name = "nonblocking", .pull = &input_pool, - .lock = __SPIN_LOCK_UNLOCKED(&nonblocking_pool.lock), + .lock = __SPIN_LOCK_UNLOCKED(nonblocking_pool.lock), .pool = nonblocking_pool_data }; diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index 3bc244d2636a..a62c4a47d52c 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -222,7 +222,7 @@ static struct { } dbg_data = { .idx = 0, .tty = 0, - .lck = __RW_LOCK_UNLOCKED(lck) + .lck = __RW_LOCK_UNLOCKED(dbg_data.lck) }; /** diff --git a/fs/file.c b/fs/file.c index 2b3570b7caeb..3906d9577a18 100644 --- a/fs/file.c +++ b/fs/file.c @@ -516,7 +516,7 @@ struct files_struct init_files = { .close_on_exec = init_files.close_on_exec_init, .open_fds = init_files.open_fds_init, }, - .file_lock = __SPIN_LOCK_UNLOCKED(init_task.file_lock), + .file_lock = __SPIN_LOCK_UNLOCKED(init_files.file_lock), }; /* diff --git a/include/linux/idr.h b/include/linux/idr.h index de7e190f1af4..e5eb125effe6 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -136,7 +136,7 @@ struct ida { struct ida_bitmap *free_bitmap; }; -#define IDA_INIT(name) { .idr = IDR_INIT(name), .free_bitmap = NULL, } +#define IDA_INIT(name) { .idr = IDR_INIT((name).idr), .free_bitmap = NULL, } #define DEFINE_IDA(name) struct ida name = IDA_INIT(name) int ida_pre_get(struct ida *ida, gfp_t gfp_mask); From 066361a7c58cb6c8b18c7ce0ee8527bb1ce58460 Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Wed, 7 Dec 2011 12:48:42 +0100 Subject: [PATCH 3028/4607] intel_idle: Convert i7300_idle_lock to raw_spinlock 24 core Intel box's first exposure to 3.0.12-rt30-rc3 didn't go well. [ 27.104159] i7300_idle: loaded v1.55 [ 27.104192] BUG: scheduling while atomic: swapper/2/0/0x00000002 [ 27.104309] Pid: 0, comm: swapper/2 Tainted: G N 3.0.12-rt30-rc3-rt #1 [ 27.104317] Call Trace: [ 27.104338] [] dump_trace+0x85/0x2e0 [ 27.104372] [] thread_return+0x12b/0x30b [ 27.104381] [] schedule+0x29/0xb0 [ 27.104389] [] rt_spin_lock_slowlock+0xc5/0x240 [ 27.104401] [] i7300_idle_notifier+0x3f/0x360 [i7300_idle] [ 27.104415] [] notifier_call_chain+0x37/0x70 [ 27.104426] [] __atomic_notifier_call_chain+0x48/0x70 [ 27.104439] [] cpu_idle+0x89/0xb0 [ 27.104449] bad: scheduling from the idle thread! This lock is taken from interrupt disabled context in the guts of idle. Convert it to a raw_spinlock. Signed-off-by: Mike Galbraith Cc: Steven Rostedt Cc: Andy Henroid Link: http://lkml.kernel.org/r/1323258522.5057.73.camel@marge.simson.net Signed-off-by: Thomas Gleixner --- drivers/idle/i7300_idle.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/idle/i7300_idle.c b/drivers/idle/i7300_idle.c index fa080ebd568f..ffeebc7e9f1c 100644 --- a/drivers/idle/i7300_idle.c +++ b/drivers/idle/i7300_idle.c @@ -75,7 +75,7 @@ static unsigned long past_skip; static struct pci_dev *fbd_dev; -static spinlock_t i7300_idle_lock; +static raw_spinlock_t i7300_idle_lock; static int i7300_idle_active; static u8 i7300_idle_thrtctl_saved; @@ -457,7 +457,7 @@ static int i7300_idle_notifier(struct notifier_block *nb, unsigned long val, idle_begin_time = ktime_get(); } - spin_lock_irqsave(&i7300_idle_lock, flags); + raw_spin_lock_irqsave(&i7300_idle_lock, flags); if (val == IDLE_START) { cpumask_set_cpu(smp_processor_id(), idle_cpumask); @@ -506,7 +506,7 @@ static int i7300_idle_notifier(struct notifier_block *nb, unsigned long val, } } end: - spin_unlock_irqrestore(&i7300_idle_lock, flags); + raw_spin_unlock_irqrestore(&i7300_idle_lock, flags); return 0; } @@ -548,7 +548,7 @@ struct debugfs_file_info { static int __init i7300_idle_init(void) { - spin_lock_init(&i7300_idle_lock); + raw_spin_lock_init(&i7300_idle_lock); total_us = 0; if (i7300_idle_platform_probe(&fbd_dev, &ioat_dev, forceload)) From a6c0c943a15d0b3d6ac33760cb8f95c75f395895 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 10 Apr 2012 11:14:55 +0200 Subject: [PATCH 3029/4607] ntp: Make ntp_lock raw seconds_overflow() is called from hard interrupt context even on Preempt-RT. This requires the lock to be a raw_spinlock. Signed-off-by: Thomas Gleixner Cc: John Stultz Signed-off-by: Ingo Molnar --- kernel/time/ntp.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 24174b4d669b..bb1edfaafe3d 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -22,7 +22,7 @@ * NTP timekeeping variables: */ -DEFINE_SPINLOCK(ntp_lock); +DEFINE_RAW_SPINLOCK(ntp_lock); /* USER_HZ period (usecs): */ @@ -347,7 +347,7 @@ void ntp_clear(void) { unsigned long flags; - spin_lock_irqsave(&ntp_lock, flags); + raw_spin_lock_irqsave(&ntp_lock, flags); time_adjust = 0; /* stop active adjtime() */ time_status |= STA_UNSYNC; @@ -361,7 +361,7 @@ void ntp_clear(void) /* Clear PPS state variables */ pps_clear(); - spin_unlock_irqrestore(&ntp_lock, flags); + raw_spin_unlock_irqrestore(&ntp_lock, flags); } @@ -371,9 +371,9 @@ u64 ntp_tick_length(void) unsigned long flags; s64 ret; - spin_lock_irqsave(&ntp_lock, flags); + raw_spin_lock_irqsave(&ntp_lock, flags); ret = tick_length; - spin_unlock_irqrestore(&ntp_lock, flags); + raw_spin_unlock_irqrestore(&ntp_lock, flags); return ret; } @@ -394,7 +394,7 @@ int second_overflow(unsigned long secs) int leap = 0; unsigned long flags; - spin_lock_irqsave(&ntp_lock, flags); + raw_spin_lock_irqsave(&ntp_lock, flags); /* * Leap second processing. If in leap-insert state at the end of the @@ -478,7 +478,7 @@ int second_overflow(unsigned long secs) time_adjust = 0; out: - spin_unlock_irqrestore(&ntp_lock, flags); + raw_spin_unlock_irqrestore(&ntp_lock, flags); return leap; } @@ -660,7 +660,7 @@ int do_adjtimex(struct timex *txc) getnstimeofday(&ts); - spin_lock_irq(&ntp_lock); + raw_spin_lock_irq(&ntp_lock); if (txc->modes & ADJ_ADJTIME) { long save_adjust = time_adjust; @@ -702,7 +702,7 @@ int do_adjtimex(struct timex *txc) /* fill PPS status fields */ pps_fill_timex(txc); - spin_unlock_irq(&ntp_lock); + raw_spin_unlock_irq(&ntp_lock); txc->time.tv_sec = ts.tv_sec; txc->time.tv_usec = ts.tv_nsec; @@ -900,7 +900,7 @@ void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts) pts_norm = pps_normalize_ts(*phase_ts); - spin_lock_irqsave(&ntp_lock, flags); + raw_spin_lock_irqsave(&ntp_lock, flags); /* clear the error bits, they will be set again if needed */ time_status &= ~(STA_PPSJITTER | STA_PPSWANDER | STA_PPSERROR); @@ -913,7 +913,7 @@ void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts) * just start the frequency interval */ if (unlikely(pps_fbase.tv_sec == 0)) { pps_fbase = *raw_ts; - spin_unlock_irqrestore(&ntp_lock, flags); + raw_spin_unlock_irqrestore(&ntp_lock, flags); return; } @@ -928,7 +928,7 @@ void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts) time_status |= STA_PPSJITTER; /* restart the frequency calibration interval */ pps_fbase = *raw_ts; - spin_unlock_irqrestore(&ntp_lock, flags); + raw_spin_unlock_irqrestore(&ntp_lock, flags); pr_err("hardpps: PPSJITTER: bad pulse\n"); return; } @@ -945,7 +945,7 @@ void hardpps(const struct timespec *phase_ts, const struct timespec *raw_ts) hardpps_update_phase(pts_norm.nsec); - spin_unlock_irqrestore(&ntp_lock, flags); + raw_spin_unlock_irqrestore(&ntp_lock, flags); } EXPORT_SYMBOL(hardpps); From 65c9d1bbc9f32c568a4f7ad154c9a10b0028df52 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 16 Jul 2011 18:38:22 +0200 Subject: [PATCH 3030/4607] seqlock: Remove unused functions Signed-off-by: Thomas Gleixner --- include/linux/seqlock.h | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index 600060e25ec6..cb0599ca049c 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -69,17 +69,6 @@ static inline void write_sequnlock(seqlock_t *sl) spin_unlock(&sl->lock); } -static inline int write_tryseqlock(seqlock_t *sl) -{ - int ret = spin_trylock(&sl->lock); - - if (ret) { - ++sl->sequence; - smp_wmb(); - } - return ret; -} - /* Start of read calculation -- fetch last complete writer token */ static __always_inline unsigned read_seqbegin(const seqlock_t *sl) { @@ -269,14 +258,4 @@ static inline void write_seqcount_barrier(seqcount_t *s) #define write_sequnlock_bh(lock) \ do { write_sequnlock(lock); local_bh_enable(); } while(0) -#define read_seqbegin_irqsave(lock, flags) \ - ({ local_irq_save(flags); read_seqbegin(lock); }) - -#define read_seqretry_irqrestore(lock, iv, flags) \ - ({ \ - int ret = read_seqretry(lock, iv); \ - local_irq_restore(flags); \ - ret; \ - }) - #endif /* __LINUX_SEQLOCK_H */ From 6617feca15c68aad12f4a1edd8d8e2ef8d5b4ae5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 16 Jul 2011 18:40:26 +0200 Subject: [PATCH 3031/4607] seqlock: Use seqcount infrastructure No point in having different implementations for the same thing. Change the macro mess to inline functions where possible. Signed-off-by: Thomas Gleixner --- include/linux/seqlock.h | 180 +++++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 85 deletions(-) diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index cb0599ca049c..18299057402f 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -30,81 +30,12 @@ #include #include -typedef struct { - unsigned sequence; - spinlock_t lock; -} seqlock_t; - -/* - * These macros triggered gcc-3.x compile-time problems. We think these are - * OK now. Be cautious. - */ -#define __SEQLOCK_UNLOCKED(lockname) \ - { 0, __SPIN_LOCK_UNLOCKED(lockname) } - -#define seqlock_init(x) \ - do { \ - (x)->sequence = 0; \ - spin_lock_init(&(x)->lock); \ - } while (0) - -#define DEFINE_SEQLOCK(x) \ - seqlock_t x = __SEQLOCK_UNLOCKED(x) - -/* Lock out other writers and update the count. - * Acts like a normal spin_lock/unlock. - * Don't need preempt_disable() because that is in the spin_lock already. - */ -static inline void write_seqlock(seqlock_t *sl) -{ - spin_lock(&sl->lock); - ++sl->sequence; - smp_wmb(); -} - -static inline void write_sequnlock(seqlock_t *sl) -{ - smp_wmb(); - sl->sequence++; - spin_unlock(&sl->lock); -} - -/* Start of read calculation -- fetch last complete writer token */ -static __always_inline unsigned read_seqbegin(const seqlock_t *sl) -{ - unsigned ret; - -repeat: - ret = ACCESS_ONCE(sl->sequence); - if (unlikely(ret & 1)) { - cpu_relax(); - goto repeat; - } - smp_rmb(); - - return ret; -} - -/* - * Test if reader processed invalid data. - * - * If sequence value changed then writer changed data while in section. - */ -static __always_inline int read_seqretry(const seqlock_t *sl, unsigned start) -{ - smp_rmb(); - - return unlikely(sl->sequence != start); -} - - /* * Version using sequence counter only. * This can be used when code has its own mutex protecting the * updating starting before the write_seqcountbeqin() and ending * after the write_seqcount_end(). */ - typedef struct seqcount { unsigned sequence; } seqcount_t; @@ -207,7 +138,6 @@ static inline int __read_seqcount_retry(const seqcount_t *s, unsigned start) static inline int read_seqcount_retry(const seqcount_t *s, unsigned start) { smp_rmb(); - return __read_seqcount_retry(s, start); } @@ -241,21 +171,101 @@ static inline void write_seqcount_barrier(seqcount_t *s) s->sequence+=2; } -/* - * Possible sw/hw IRQ protected versions of the interfaces. - */ -#define write_seqlock_irqsave(lock, flags) \ - do { local_irq_save(flags); write_seqlock(lock); } while (0) -#define write_seqlock_irq(lock) \ - do { local_irq_disable(); write_seqlock(lock); } while (0) -#define write_seqlock_bh(lock) \ - do { local_bh_disable(); write_seqlock(lock); } while (0) +typedef struct { + struct seqcount seqcount; + spinlock_t lock; +} seqlock_t; -#define write_sequnlock_irqrestore(lock, flags) \ - do { write_sequnlock(lock); local_irq_restore(flags); } while(0) -#define write_sequnlock_irq(lock) \ - do { write_sequnlock(lock); local_irq_enable(); } while(0) -#define write_sequnlock_bh(lock) \ - do { write_sequnlock(lock); local_bh_enable(); } while(0) +/* + * These macros triggered gcc-3.x compile-time problems. We think these are + * OK now. Be cautious. + */ +#define __SEQLOCK_UNLOCKED(lockname) \ + { \ + .seqcount = SEQCNT_ZERO, \ + .lock = __SPIN_LOCK_UNLOCKED(lockname) \ + } + +#define seqlock_init(x) \ + do { \ + seqcount_init(&(x)->seqcount); \ + spin_lock_init(&(x)->lock); \ + } while (0) + +#define DEFINE_SEQLOCK(x) \ + seqlock_t x = __SEQLOCK_UNLOCKED(x) + +/* + * Read side functions for starting and finalizing a read side section. + */ +static inline unsigned read_seqbegin(const seqlock_t *sl) +{ + return read_seqcount_begin(&sl->seqcount); +} + +static inline unsigned read_seqretry(const seqlock_t *sl, unsigned start) +{ + return read_seqcount_retry(&sl->seqcount, start); +} + +/* + * Lock out other writers and update the count. + * Acts like a normal spin_lock/unlock. + * Don't need preempt_disable() because that is in the spin_lock already. + */ +static inline void write_seqlock(seqlock_t *sl) +{ + spin_lock(&sl->lock); + write_seqcount_begin(&sl->seqcount); +} + +static inline void write_sequnlock(seqlock_t *sl) +{ + write_seqcount_end(&sl->seqcount); + spin_unlock(&sl->lock); +} + +static inline void write_seqlock_bh(seqlock_t *sl) +{ + spin_lock_bh(&sl->lock); + write_seqcount_begin(&sl->seqcount); +} + +static inline void write_sequnlock_bh(seqlock_t *sl) +{ + write_seqcount_end(&sl->seqcount); + spin_unlock_bh(&sl->lock); +} + +static inline void write_seqlock_irq(seqlock_t *sl) +{ + spin_lock_irq(&sl->lock); + write_seqcount_begin(&sl->seqcount); +} + +static inline void write_sequnlock_irq(seqlock_t *sl) +{ + write_seqcount_end(&sl->seqcount); + spin_unlock_irq(&sl->lock); +} + +static inline unsigned long __write_seqlock_irqsave(seqlock_t *sl) +{ + unsigned long flags; + + spin_lock_irqsave(&sl->lock, flags); + write_seqcount_begin(&sl->seqcount); + return flags; +} + +#define write_seqlock_irqsave(lock, flags) \ + do { flags = __write_seqlock_irqsave(lock); } while (0) + +static inline void +write_sequnlock_irqrestore(seqlock_t *sl, unsigned long flags) +{ + write_seqcount_end(&sl->seqcount); + spin_unlock_irqrestore(&sl->lock, flags); +} #endif /* __LINUX_SEQLOCK_H */ From 9fb1b90ce0a847a8cc9492a6c1f347b5be1f33ff Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Mon, 16 Apr 2012 15:01:55 +0800 Subject: [PATCH 3032/4607] lockdep: Selftest: convert spinlock to raw spinlock To make the lockdep selftest working on RT we need to convert the spinlock tests to a raw spinlock. Otherwise we cannot run the irq context checks. For mainline this is just annotational as spinlocks are mapped to raw_spinlocks anyway. Signed-off-by: Yong Zhang Link: http://lkml.kernel.org/r/1334559716-18447-2-git-send-email-yong.zhang0@gmail.com Signed-off-by: Thomas Gleixner --- lib/locking-selftest.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c index 7aae0f2a5e0a..c3eb261a7df3 100644 --- a/lib/locking-selftest.c +++ b/lib/locking-selftest.c @@ -47,10 +47,10 @@ __setup("debug_locks_verbose=", setup_debug_locks_verbose); * Normal standalone locks, for the circular and irq-context * dependency tests: */ -static DEFINE_SPINLOCK(lock_A); -static DEFINE_SPINLOCK(lock_B); -static DEFINE_SPINLOCK(lock_C); -static DEFINE_SPINLOCK(lock_D); +static DEFINE_RAW_SPINLOCK(lock_A); +static DEFINE_RAW_SPINLOCK(lock_B); +static DEFINE_RAW_SPINLOCK(lock_C); +static DEFINE_RAW_SPINLOCK(lock_D); static DEFINE_RWLOCK(rwlock_A); static DEFINE_RWLOCK(rwlock_B); @@ -73,12 +73,12 @@ static DECLARE_RWSEM(rwsem_D); * but X* and Y* are different classes. We do this so that * we do not trigger a real lockup: */ -static DEFINE_SPINLOCK(lock_X1); -static DEFINE_SPINLOCK(lock_X2); -static DEFINE_SPINLOCK(lock_Y1); -static DEFINE_SPINLOCK(lock_Y2); -static DEFINE_SPINLOCK(lock_Z1); -static DEFINE_SPINLOCK(lock_Z2); +static DEFINE_RAW_SPINLOCK(lock_X1); +static DEFINE_RAW_SPINLOCK(lock_X2); +static DEFINE_RAW_SPINLOCK(lock_Y1); +static DEFINE_RAW_SPINLOCK(lock_Y2); +static DEFINE_RAW_SPINLOCK(lock_Z1); +static DEFINE_RAW_SPINLOCK(lock_Z2); static DEFINE_RWLOCK(rwlock_X1); static DEFINE_RWLOCK(rwlock_X2); @@ -107,10 +107,10 @@ static DECLARE_RWSEM(rwsem_Z2); */ #define INIT_CLASS_FUNC(class) \ static noinline void \ -init_class_##class(spinlock_t *lock, rwlock_t *rwlock, struct mutex *mutex, \ - struct rw_semaphore *rwsem) \ +init_class_##class(raw_spinlock_t *lock, rwlock_t *rwlock, \ + struct mutex *mutex, struct rw_semaphore *rwsem)\ { \ - spin_lock_init(lock); \ + raw_spin_lock_init(lock); \ rwlock_init(rwlock); \ mutex_init(mutex); \ init_rwsem(rwsem); \ @@ -168,10 +168,10 @@ static void init_shared_classes(void) * Shortcuts for lock/unlock API variants, to keep * the testcases compact: */ -#define L(x) spin_lock(&lock_##x) -#define U(x) spin_unlock(&lock_##x) +#define L(x) raw_spin_lock(&lock_##x) +#define U(x) raw_spin_unlock(&lock_##x) #define LU(x) L(x); U(x) -#define SI(x) spin_lock_init(&lock_##x) +#define SI(x) raw_spin_lock_init(&lock_##x) #define WL(x) write_lock(&rwlock_##x) #define WU(x) write_unlock(&rwlock_##x) @@ -911,7 +911,7 @@ GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion_soft) #define I2(x) \ do { \ - spin_lock_init(&lock_##x); \ + raw_spin_lock_init(&lock_##x); \ rwlock_init(&rwlock_##x); \ mutex_init(&mutex_##x); \ init_rwsem(&rwsem_##x); \ From 5042afe7fe32390e79910ecd0a1f0563d4bca38c Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 3 Jul 2009 08:29:30 -0500 Subject: [PATCH 3033/4607] generic: Use raw local irq variant for generic cmpxchg The interrupt disabled region is extremly tiny and therefor not latency relevant. Avoid cluttering the traces with those pointless entries. Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- include/asm-generic/cmpxchg-local.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/asm-generic/cmpxchg-local.h b/include/asm-generic/cmpxchg-local.h index 2533fddd34a6..d8d4c898c1bb 100644 --- a/include/asm-generic/cmpxchg-local.h +++ b/include/asm-generic/cmpxchg-local.h @@ -21,7 +21,7 @@ static inline unsigned long __cmpxchg_local_generic(volatile void *ptr, if (size == 8 && sizeof(unsigned long) != 8) wrong_size_cmpxchg(ptr); - local_irq_save(flags); + raw_local_irq_save(flags); switch (size) { case 1: prev = *(u8 *)ptr; if (prev == old) @@ -42,7 +42,7 @@ static inline unsigned long __cmpxchg_local_generic(volatile void *ptr, default: wrong_size_cmpxchg(ptr); } - local_irq_restore(flags); + raw_local_irq_restore(flags); return prev; } @@ -55,11 +55,11 @@ static inline u64 __cmpxchg64_local_generic(volatile void *ptr, u64 prev; unsigned long flags; - local_irq_save(flags); + raw_local_irq_save(flags); prev = *(u64 *)ptr; if (prev == old) *(u64 *)ptr = new; - local_irq_restore(flags); + raw_local_irq_restore(flags); return prev; } From fe2b05f7ca9f906be61dced5489f63b8b4d7c770 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 18 Feb 2013 09:52:08 +0100 Subject: [PATCH 3034/4607] futex: Revert "futex: Mark get_robust_list as deprecated" This reverts commit ec0c4274e33c0373e476b73e01995c53128f1257. get_robust_list() is in use and a removal would break existing user space. With the permission checks in place it's not longer a security hole. Remove the deprecation warnings. Signed-off-by: Thomas Gleixner Cc: Cyrill Gorcunov Cc: Richard Weinberger Cc: akpm@linux-foundation.org Cc: paul.gortmaker@windriver.com Cc: davej@redhat.com Cc: keescook@chromium.org Cc: stable@vger.kernel.org Cc: ebiederm@xmission.com --- kernel/futex.c | 2 -- kernel/futex_compat.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/kernel/futex.c b/kernel/futex.c index 19eb089ca003..887943044d46 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -2471,8 +2471,6 @@ SYSCALL_DEFINE3(get_robust_list, int, pid, if (!futex_cmpxchg_enabled) return -ENOSYS; - WARN_ONCE(1, "deprecated: get_robust_list will be deleted in 2013.\n"); - rcu_read_lock(); ret = -ESRCH; diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c index 83e368b005fc..a9642d528630 100644 --- a/kernel/futex_compat.c +++ b/kernel/futex_compat.c @@ -142,8 +142,6 @@ compat_sys_get_robust_list(int pid, compat_uptr_t __user *head_ptr, if (!futex_cmpxchg_enabled) return -ENOSYS; - WARN_ONCE(1, "deprecated: get_robust_list will be deleted in 2013.\n"); - rcu_read_lock(); ret = -ESRCH; From 41ef8f826692c8f65882bec0a8211bd4d1d2d19a Mon Sep 17 00:00:00 2001 From: Yuanhan Liu Date: Fri, 1 Feb 2013 18:59:16 +0800 Subject: [PATCH 3035/4607] rwsem-spinlock: Implement writer lock-stealing for better scalability We (Linux Kernel Performance project) found a regression introduced by commit: 5a505085f043 mm/rmap: Convert the struct anon_vma::mutex to an rwsem which converted all anon_vma::mutex locks rwsem write locks. The semantics are the same, but the behavioral difference is quite huge in some cases. After investigating it we found the root cause: mutexes support lock stealing while rwsems don't. Here is the link for the detailed regression report: https://lkml.org/lkml/2013/1/29/84 Ingo suggested adding write lock stealing to rwsems: "I think we should allow lock-steal between rwsem writers - that will not hurt fairness as most rwsem fairness concerns relate to reader vs. writer fairness" And here is the rwsem-spinlock version. With this patch, we got a double performance increase in one test box with following aim7 workfile: FILESIZE: 1M POOLSIZE: 10M 10 fork_test /usr/bin/time output w/o patch /usr/bin/time_output with patch -- Percent of CPU this job got: 369% Percent of CPU this job got: 537% Voluntary context switches: 640595016 Voluntary context switches: 157915561 We got a 45% increase in CPU usage and saved about 3/4 voluntary context switches. Reported-by: LKP project Suggested-by: Ingo Molnar Signed-off-by: Yuanhan Liu Cc: Alex Shi Cc: David Howells Cc: Michel Lespinasse Cc: Linus Torvalds Cc: Andrew Morton Cc: Peter Zijlstra Cc: Anton Blanchard Cc: Arjan van de Ven Cc: paul.gortmaker@windriver.com Link: http://lkml.kernel.org/r/1359716356-23865-1-git-send-email-yuanhan.liu@linux.intel.com Signed-off-by: Ingo Molnar --- lib/rwsem-spinlock.c | 69 +++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 45 deletions(-) diff --git a/lib/rwsem-spinlock.c b/lib/rwsem-spinlock.c index 7e0d6a58fc83..7542afbb22b3 100644 --- a/lib/rwsem-spinlock.c +++ b/lib/rwsem-spinlock.c @@ -73,20 +73,13 @@ __rwsem_do_wake(struct rw_semaphore *sem, int wakewrite) goto dont_wake_writers; } - /* if we are allowed to wake writers try to grant a single write lock - * if there's a writer at the front of the queue - * - we leave the 'waiting count' incremented to signify potential - * contention + /* + * as we support write lock stealing, we can't set sem->activity + * to -1 here to indicate we get the lock. Instead, we wake it up + * to let it go get it again. */ if (waiter->flags & RWSEM_WAITING_FOR_WRITE) { - sem->activity = -1; - list_del(&waiter->list); - tsk = waiter->task; - /* Don't touch waiter after ->task has been NULLed */ - smp_mb(); - waiter->task = NULL; - wake_up_process(tsk); - put_task_struct(tsk); + wake_up_process(waiter->task); goto out; } @@ -121,18 +114,10 @@ static inline struct rw_semaphore * __rwsem_wake_one_writer(struct rw_semaphore *sem) { struct rwsem_waiter *waiter; - struct task_struct *tsk; - - sem->activity = -1; waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list); - list_del(&waiter->list); + wake_up_process(waiter->task); - tsk = waiter->task; - smp_mb(); - waiter->task = NULL; - wake_up_process(tsk); - put_task_struct(tsk); return sem; } @@ -204,7 +189,6 @@ int __down_read_trylock(struct rw_semaphore *sem) /* * get a write lock on the semaphore - * - we increment the waiting count anyway to indicate an exclusive lock */ void __sched __down_write_nested(struct rw_semaphore *sem, int subclass) { @@ -214,37 +198,32 @@ void __sched __down_write_nested(struct rw_semaphore *sem, int subclass) raw_spin_lock_irqsave(&sem->wait_lock, flags); - if (sem->activity == 0 && list_empty(&sem->wait_list)) { - /* granted */ - sem->activity = -1; - raw_spin_unlock_irqrestore(&sem->wait_lock, flags); - goto out; - } - - tsk = current; - set_task_state(tsk, TASK_UNINTERRUPTIBLE); - /* set up my own style of waitqueue */ + tsk = current; waiter.task = tsk; waiter.flags = RWSEM_WAITING_FOR_WRITE; - get_task_struct(tsk); - list_add_tail(&waiter.list, &sem->wait_list); - /* we don't need to touch the semaphore struct anymore */ - raw_spin_unlock_irqrestore(&sem->wait_lock, flags); - - /* wait to be given the lock */ + /* wait for someone to release the lock */ for (;;) { - if (!waiter.task) + /* + * That is the key to support write lock stealing: allows the + * task already on CPU to get the lock soon rather than put + * itself into sleep and waiting for system woke it or someone + * else in the head of the wait list up. + */ + if (sem->activity == 0) break; - schedule(); set_task_state(tsk, TASK_UNINTERRUPTIBLE); + raw_spin_unlock_irqrestore(&sem->wait_lock, flags); + schedule(); + raw_spin_lock_irqsave(&sem->wait_lock, flags); } + /* got the lock */ + sem->activity = -1; + list_del(&waiter.list); - tsk->state = TASK_RUNNING; - out: - ; + raw_spin_unlock_irqrestore(&sem->wait_lock, flags); } void __sched __down_write(struct rw_semaphore *sem) @@ -262,8 +241,8 @@ int __down_write_trylock(struct rw_semaphore *sem) raw_spin_lock_irqsave(&sem->wait_lock, flags); - if (sem->activity == 0 && list_empty(&sem->wait_list)) { - /* granted */ + if (sem->activity == 0) { + /* got the lock */ sem->activity = -1; ret = 1; } From d3ff9338023236f39332b07b3afed76c490a5041 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 12 Feb 2013 19:41:47 +0000 Subject: [PATCH 3036/4607] mips: Make sure kernel memory is in iomem Kernel memory isn't necessarily added to the memory tables, so it wouldn't show up in /proc/iomem. This was breaking kdump, which requires these memory addresses to work correctly. Signed-off-by: Corey Minyard Acked-by: David Daney Cc: Ralf Baechle Patchwork: http://patchwork.linux-mips.org/patch/4937/ --- arch/mips/kernel/setup.c | 52 ++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 8c41187801ce..53462500c3cd 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -480,34 +480,44 @@ static int __init early_parse_mem(char *p) } early_param("mem", early_parse_mem); +static void __init arch_mem_addpart(phys_t mem, phys_t end, int type) +{ + phys_t size; + int i; + + size = end - mem; + if (!size) + return; + + /* Make sure it is in the boot_mem_map */ + for (i = 0; i < boot_mem_map.nr_map; i++) { + if (mem >= boot_mem_map.map[i].addr && + mem < (boot_mem_map.map[i].addr + + boot_mem_map.map[i].size)) + return; + } + add_memory_region(mem, size, type); +} + static void __init arch_mem_init(char **cmdline_p) { - phys_t init_mem, init_end, init_size; - extern void plat_mem_setup(void); /* call board setup routine */ plat_mem_setup(); - init_mem = PFN_UP(__pa_symbol(&__init_begin)) << PAGE_SHIFT; - init_end = PFN_DOWN(__pa_symbol(&__init_end)) << PAGE_SHIFT; - init_size = init_end - init_mem; - if (init_size) { - /* Make sure it is in the boot_mem_map */ - int i, found; - found = 0; - for (i = 0; i < boot_mem_map.nr_map; i++) { - if (init_mem >= boot_mem_map.map[i].addr && - init_mem < (boot_mem_map.map[i].addr + - boot_mem_map.map[i].size)) { - found = 1; - break; - } - } - if (!found) - add_memory_region(init_mem, init_size, - BOOT_MEM_INIT_RAM); - } + /* + * Make sure all kernel memory is in the maps. The "UP" and + * "DOWN" are opposite for initdata since if it crosses over + * into another memory section you don't want that to be + * freed when the initdata is freed. + */ + arch_mem_addpart(PFN_DOWN(__pa_symbol(&_text)) << PAGE_SHIFT, + PFN_UP(__pa_symbol(&_edata)) << PAGE_SHIFT, + BOOT_MEM_RAM); + arch_mem_addpart(PFN_UP(__pa_symbol(&__init_begin)) << PAGE_SHIFT, + PFN_DOWN(__pa_symbol(&__init_end)) << PAGE_SHIFT, + BOOT_MEM_INIT_RAM); pr_info("Determined physical RAM map:\n"); print_memory_map(); From 4893fc8856a81d2037c1c976cb320be6f00e84a0 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Tue, 12 Feb 2013 19:41:48 +0000 Subject: [PATCH 3037/4607] mips: reserve elfcorehdr /proc/vmcore wasn't showing up in kdump kernels. It turns that that for Octeon, the memory used by elfcorehdr wasn't being set aside properly and it was getting clobbered before /proc/vmcore could get it. So reserve the memory if it shows up in a memory area managed by the kernel. Signed-off-by: Corey Minyard Acked-by: David Daney Cc: Ralf Baechle Patchwork: http://patchwork.linux-mips.org/patch/4936/ --- arch/mips/kernel/setup.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 53462500c3cd..795f4379c0b6 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -480,6 +480,37 @@ static int __init early_parse_mem(char *p) } early_param("mem", early_parse_mem); +#ifdef CONFIG_PROC_VMCORE +unsigned long setup_elfcorehdr, setup_elfcorehdr_size; +static int __init early_parse_elfcorehdr(char *p) +{ + int i; + + setup_elfcorehdr = memparse(p, &p); + + for (i = 0; i < boot_mem_map.nr_map; i++) { + unsigned long start = boot_mem_map.map[i].addr; + unsigned long end = (boot_mem_map.map[i].addr + + boot_mem_map.map[i].size); + if (setup_elfcorehdr >= start && setup_elfcorehdr < end) { + /* + * Reserve from the elf core header to the end of + * the memory segment, that should all be kdump + * reserved memory. + */ + setup_elfcorehdr_size = end - setup_elfcorehdr; + break; + } + } + /* + * If we don't find it in the memory map, then we shouldn't + * have to worry about it, as the new kernel won't use it. + */ + return 0; +} +early_param("elfcorehdr", early_parse_elfcorehdr); +#endif + static void __init arch_mem_addpart(phys_t mem, phys_t end, int type) { phys_t size; @@ -547,6 +578,14 @@ static void __init arch_mem_init(char **cmdline_p) } bootmem_init(); +#ifdef CONFIG_PROC_VMCORE + if (setup_elfcorehdr && setup_elfcorehdr_size) { + printk(KERN_INFO "kdump reserved memory at %lx-%lx\n", + setup_elfcorehdr, setup_elfcorehdr_size); + reserve_bootmem(setup_elfcorehdr, setup_elfcorehdr_size, + BOOTMEM_DEFAULT); + } +#endif #ifdef CONFIG_KEXEC if (crashk_res.start != crashk_res.end) reserve_bootmem(crashk_res.start, From e3b25cead4b58fbf60270ba73a1669bf9e5635f5 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 18:51:57 +0000 Subject: [PATCH 3038/4607] MIPS: ath79: fix WMAC IRQ resource assignment The '.start' field of the IRQ resource assigned twice in ar934x_wmac_setup(). The second assignment must set the '.end' field. Fix it. Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4954/ Signed-off-by: John Crispin --- arch/mips/ath79/dev-wmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/ath79/dev-wmac.c b/arch/mips/ath79/dev-wmac.c index 4f6c4e389172..d71d745e3109 100644 --- a/arch/mips/ath79/dev-wmac.c +++ b/arch/mips/ath79/dev-wmac.c @@ -107,7 +107,7 @@ static void ar934x_wmac_setup(void) ath79_wmac_resources[0].start = AR934X_WMAC_BASE; ath79_wmac_resources[0].end = AR934X_WMAC_BASE + AR934X_WMAC_SIZE - 1; ath79_wmac_resources[1].start = ATH79_IP2_IRQ(1); - ath79_wmac_resources[1].start = ATH79_IP2_IRQ(1); + ath79_wmac_resources[1].end = ATH79_IP2_IRQ(1); t = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP); if (t & AR934X_BOOTSTRAP_REF_CLK_40) From 908987797076b848f01b32c21d61d0e152efc236 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 13:38:15 +0000 Subject: [PATCH 3039/4607] MIPS: ath79: add early printk support for the QCA955X SoCs The patch allows to see kernel messages on the QCA955X SoCs in early boot stage. Cc: Rodriguez, Luis Cc: Giori, Kathy Cc: QCA Linux Team Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4944/ Signed-off-by: John Crispin --- arch/mips/ath79/early_printk.c | 2 ++ arch/mips/include/asm/mach-ath79/ar71xx_regs.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/arch/mips/ath79/early_printk.c b/arch/mips/ath79/early_printk.c index dc938cb2ba58..b955fafc58ba 100644 --- a/arch/mips/ath79/early_printk.c +++ b/arch/mips/ath79/early_printk.c @@ -74,6 +74,8 @@ static void prom_putchar_init(void) case REV_ID_MAJOR_AR9341: case REV_ID_MAJOR_AR9342: case REV_ID_MAJOR_AR9344: + case REV_ID_MAJOR_QCA9556: + case REV_ID_MAJOR_QCA9558: _prom_putchar = prom_putchar_ar71xx; break; diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index a77f6ee70ec1..d02c2d4e600e 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -370,6 +370,8 @@ #define REV_ID_MAJOR_AR9341 0x0120 #define REV_ID_MAJOR_AR9342 0x1120 #define REV_ID_MAJOR_AR9344 0x2120 +#define REV_ID_MAJOR_QCA9556 0x0130 +#define REV_ID_MAJOR_QCA9558 0x1130 #define AR71XX_REV_ID_MINOR_MASK 0x3 #define AR71XX_REV_ID_MINOR_AR7130 0x0 From 2e6c91e392fd7be2ef0ba1e9a20e0ebe8ab79cf3 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 13:38:16 +0000 Subject: [PATCH 3040/4607] MIPS: ath79: add SoC detection code for the QCA955X SoCs Also add 'soc_is_qca955[68x]' helper functions and a Kconfig symbol for the SoC family. Cc: Rodriguez, Luis Cc: Giori, Kathy Cc: QCA Linux Team Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4943/ Signed-off-by: John Crispin --- arch/mips/ath79/Kconfig | 4 ++++ arch/mips/ath79/setup.c | 18 +++++++++++++++++- arch/mips/include/asm/mach-ath79/ar71xx_regs.h | 2 ++ arch/mips/include/asm/mach-ath79/ath79.h | 17 +++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig index f44feee2d67f..cffdc8e3b63b 100644 --- a/arch/mips/ath79/Kconfig +++ b/arch/mips/ath79/Kconfig @@ -88,6 +88,10 @@ config SOC_AR934X select PCI_AR724X if PCI def_bool n +config SOC_QCA955X + select USB_ARCH_HAS_EHCI + def_bool n + config PCI_AR724X def_bool n diff --git a/arch/mips/ath79/setup.c b/arch/mips/ath79/setup.c index 60d212ef8629..d5b3c9057018 100644 --- a/arch/mips/ath79/setup.c +++ b/arch/mips/ath79/setup.c @@ -164,13 +164,29 @@ static void __init ath79_detect_sys_type(void) rev = id & AR934X_REV_ID_REVISION_MASK; break; + case REV_ID_MAJOR_QCA9556: + ath79_soc = ATH79_SOC_QCA9556; + chip = "9556"; + rev = id & QCA955X_REV_ID_REVISION_MASK; + break; + + case REV_ID_MAJOR_QCA9558: + ath79_soc = ATH79_SOC_QCA9558; + chip = "9558"; + rev = id & QCA955X_REV_ID_REVISION_MASK; + break; + default: panic("ath79: unknown SoC, id:0x%08x", id); } ath79_soc_rev = rev; - sprintf(ath79_sys_type, "Atheros AR%s rev %u", chip, rev); + if (soc_is_qca955x()) + sprintf(ath79_sys_type, "Qualcomm Atheros QCA%s rev %u", + chip, rev); + else + sprintf(ath79_sys_type, "Atheros AR%s rev %u", chip, rev); pr_info("SoC: %s\n", ath79_sys_type); } diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index d02c2d4e600e..63a9f2b600b8 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -392,6 +392,8 @@ #define AR934X_REV_ID_REVISION_MASK 0xf +#define QCA955X_REV_ID_REVISION_MASK 0xf + /* * SPI block */ diff --git a/arch/mips/include/asm/mach-ath79/ath79.h b/arch/mips/include/asm/mach-ath79/ath79.h index 4f248c3d7b23..1557934aaca9 100644 --- a/arch/mips/include/asm/mach-ath79/ath79.h +++ b/arch/mips/include/asm/mach-ath79/ath79.h @@ -32,6 +32,8 @@ enum ath79_soc_type { ATH79_SOC_AR9341, ATH79_SOC_AR9342, ATH79_SOC_AR9344, + ATH79_SOC_QCA9556, + ATH79_SOC_QCA9558, }; extern enum ath79_soc_type ath79_soc; @@ -98,6 +100,21 @@ static inline int soc_is_ar934x(void) return soc_is_ar9341() || soc_is_ar9342() || soc_is_ar9344(); } +static inline int soc_is_qca9556(void) +{ + return ath79_soc == ATH79_SOC_QCA9556; +} + +static inline int soc_is_qca9558(void) +{ + return ath79_soc == ATH79_SOC_QCA9558; +} + +static inline int soc_is_qca955x(void) +{ + return soc_is_qca9556() || soc_is_qca9558(); +} + extern void __iomem *ath79_ddr_base; extern void __iomem *ath79_pll_base; extern void __iomem *ath79_reset_base; From 41583c05c15cd3adb848f9ee8316bf8084c961cb Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 13:38:17 +0000 Subject: [PATCH 3041/4607] MIPS: ath79: add clock setup code for the QCA955X SoCs The patch adds code to get various clock frequencies from the PLLs used in the QCA955x SoCs. Cc: Rodriguez, Luis Cc: Giori, Kathy Cc: QCA Linux Team Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4945/ Signed-off-by: John Crispin --- arch/mips/ath79/clock.c | 78 +++++++++++++++++++ .../mips/include/asm/mach-ath79/ar71xx_regs.h | 39 ++++++++++ 2 files changed, 117 insertions(+) diff --git a/arch/mips/ath79/clock.c b/arch/mips/ath79/clock.c index 579f452c0b45..555e603de619 100644 --- a/arch/mips/ath79/clock.c +++ b/arch/mips/ath79/clock.c @@ -295,6 +295,82 @@ static void __init ar934x_clocks_init(void) iounmap(dpll_base); } +static void __init qca955x_clocks_init(void) +{ + u32 pll, out_div, ref_div, nint, frac, clk_ctrl, postdiv; + u32 cpu_pll, ddr_pll; + u32 bootstrap; + + bootstrap = ath79_reset_rr(QCA955X_RESET_REG_BOOTSTRAP); + if (bootstrap & QCA955X_BOOTSTRAP_REF_CLK_40) + ath79_ref_clk.rate = 40 * 1000 * 1000; + else + ath79_ref_clk.rate = 25 * 1000 * 1000; + + pll = ath79_pll_rr(QCA955X_PLL_CPU_CONFIG_REG); + out_div = (pll >> QCA955X_PLL_CPU_CONFIG_OUTDIV_SHIFT) & + QCA955X_PLL_CPU_CONFIG_OUTDIV_MASK; + ref_div = (pll >> QCA955X_PLL_CPU_CONFIG_REFDIV_SHIFT) & + QCA955X_PLL_CPU_CONFIG_REFDIV_MASK; + nint = (pll >> QCA955X_PLL_CPU_CONFIG_NINT_SHIFT) & + QCA955X_PLL_CPU_CONFIG_NINT_MASK; + frac = (pll >> QCA955X_PLL_CPU_CONFIG_NFRAC_SHIFT) & + QCA955X_PLL_CPU_CONFIG_NFRAC_MASK; + + cpu_pll = nint * ath79_ref_clk.rate / ref_div; + cpu_pll += frac * ath79_ref_clk.rate / (ref_div * (1 << 6)); + cpu_pll /= (1 << out_div); + + pll = ath79_pll_rr(QCA955X_PLL_DDR_CONFIG_REG); + out_div = (pll >> QCA955X_PLL_DDR_CONFIG_OUTDIV_SHIFT) & + QCA955X_PLL_DDR_CONFIG_OUTDIV_MASK; + ref_div = (pll >> QCA955X_PLL_DDR_CONFIG_REFDIV_SHIFT) & + QCA955X_PLL_DDR_CONFIG_REFDIV_MASK; + nint = (pll >> QCA955X_PLL_DDR_CONFIG_NINT_SHIFT) & + QCA955X_PLL_DDR_CONFIG_NINT_MASK; + frac = (pll >> QCA955X_PLL_DDR_CONFIG_NFRAC_SHIFT) & + QCA955X_PLL_DDR_CONFIG_NFRAC_MASK; + + ddr_pll = nint * ath79_ref_clk.rate / ref_div; + ddr_pll += frac * ath79_ref_clk.rate / (ref_div * (1 << 10)); + ddr_pll /= (1 << out_div); + + clk_ctrl = ath79_pll_rr(QCA955X_PLL_CLK_CTRL_REG); + + postdiv = (clk_ctrl >> QCA955X_PLL_CLK_CTRL_CPU_POST_DIV_SHIFT) & + QCA955X_PLL_CLK_CTRL_CPU_POST_DIV_MASK; + + if (clk_ctrl & QCA955X_PLL_CLK_CTRL_CPU_PLL_BYPASS) + ath79_cpu_clk.rate = ath79_ref_clk.rate; + else if (clk_ctrl & QCA955X_PLL_CLK_CTRL_CPUCLK_FROM_CPUPLL) + ath79_cpu_clk.rate = ddr_pll / (postdiv + 1); + else + ath79_cpu_clk.rate = cpu_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> QCA955X_PLL_CLK_CTRL_DDR_POST_DIV_SHIFT) & + QCA955X_PLL_CLK_CTRL_DDR_POST_DIV_MASK; + + if (clk_ctrl & QCA955X_PLL_CLK_CTRL_DDR_PLL_BYPASS) + ath79_ddr_clk.rate = ath79_ref_clk.rate; + else if (clk_ctrl & QCA955X_PLL_CLK_CTRL_DDRCLK_FROM_DDRPLL) + ath79_ddr_clk.rate = cpu_pll / (postdiv + 1); + else + ath79_ddr_clk.rate = ddr_pll / (postdiv + 1); + + postdiv = (clk_ctrl >> QCA955X_PLL_CLK_CTRL_AHB_POST_DIV_SHIFT) & + QCA955X_PLL_CLK_CTRL_AHB_POST_DIV_MASK; + + if (clk_ctrl & QCA955X_PLL_CLK_CTRL_AHB_PLL_BYPASS) + ath79_ahb_clk.rate = ath79_ref_clk.rate; + else if (clk_ctrl & QCA955X_PLL_CLK_CTRL_AHBCLK_FROM_DDRPLL) + ath79_ahb_clk.rate = ddr_pll / (postdiv + 1); + else + ath79_ahb_clk.rate = cpu_pll / (postdiv + 1); + + ath79_wdt_clk.rate = ath79_ref_clk.rate; + ath79_uart_clk.rate = ath79_ref_clk.rate; +} + void __init ath79_clocks_init(void) { if (soc_is_ar71xx()) @@ -307,6 +383,8 @@ void __init ath79_clocks_init(void) ar933x_clocks_init(); else if (soc_is_ar934x()) ar934x_clocks_init(); + else if (soc_is_qca955x()) + qca955x_clocks_init(); else BUG(); diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index 63a9f2b600b8..7b00e12afc1c 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -225,6 +225,41 @@ #define AR934X_PLL_CPU_DDR_CLK_CTRL_DDRCLK_FROM_DDRPLL BIT(21) #define AR934X_PLL_CPU_DDR_CLK_CTRL_AHBCLK_FROM_DDRPLL BIT(24) +#define QCA955X_PLL_CPU_CONFIG_REG 0x00 +#define QCA955X_PLL_DDR_CONFIG_REG 0x04 +#define QCA955X_PLL_CLK_CTRL_REG 0x08 + +#define QCA955X_PLL_CPU_CONFIG_NFRAC_SHIFT 0 +#define QCA955X_PLL_CPU_CONFIG_NFRAC_MASK 0x3f +#define QCA955X_PLL_CPU_CONFIG_NINT_SHIFT 6 +#define QCA955X_PLL_CPU_CONFIG_NINT_MASK 0x3f +#define QCA955X_PLL_CPU_CONFIG_REFDIV_SHIFT 12 +#define QCA955X_PLL_CPU_CONFIG_REFDIV_MASK 0x1f +#define QCA955X_PLL_CPU_CONFIG_OUTDIV_SHIFT 19 +#define QCA955X_PLL_CPU_CONFIG_OUTDIV_MASK 0x3 + +#define QCA955X_PLL_DDR_CONFIG_NFRAC_SHIFT 0 +#define QCA955X_PLL_DDR_CONFIG_NFRAC_MASK 0x3ff +#define QCA955X_PLL_DDR_CONFIG_NINT_SHIFT 10 +#define QCA955X_PLL_DDR_CONFIG_NINT_MASK 0x3f +#define QCA955X_PLL_DDR_CONFIG_REFDIV_SHIFT 16 +#define QCA955X_PLL_DDR_CONFIG_REFDIV_MASK 0x1f +#define QCA955X_PLL_DDR_CONFIG_OUTDIV_SHIFT 23 +#define QCA955X_PLL_DDR_CONFIG_OUTDIV_MASK 0x7 + +#define QCA955X_PLL_CLK_CTRL_CPU_PLL_BYPASS BIT(2) +#define QCA955X_PLL_CLK_CTRL_DDR_PLL_BYPASS BIT(3) +#define QCA955X_PLL_CLK_CTRL_AHB_PLL_BYPASS BIT(4) +#define QCA955X_PLL_CLK_CTRL_CPU_POST_DIV_SHIFT 5 +#define QCA955X_PLL_CLK_CTRL_CPU_POST_DIV_MASK 0x1f +#define QCA955X_PLL_CLK_CTRL_DDR_POST_DIV_SHIFT 10 +#define QCA955X_PLL_CLK_CTRL_DDR_POST_DIV_MASK 0x1f +#define QCA955X_PLL_CLK_CTRL_AHB_POST_DIV_SHIFT 15 +#define QCA955X_PLL_CLK_CTRL_AHB_POST_DIV_MASK 0x1f +#define QCA955X_PLL_CLK_CTRL_CPUCLK_FROM_CPUPLL BIT(20) +#define QCA955X_PLL_CLK_CTRL_DDRCLK_FROM_DDRPLL BIT(21) +#define QCA955X_PLL_CLK_CTRL_AHBCLK_FROM_DDRPLL BIT(24) + /* * USB_CONFIG block */ @@ -264,6 +299,8 @@ #define AR934X_RESET_REG_BOOTSTRAP 0xb0 #define AR934X_RESET_REG_PCIE_WMAC_INT_STATUS 0xac +#define QCA955X_RESET_REG_BOOTSTRAP 0xb0 + #define MISC_INT_ETHSW BIT(12) #define MISC_INT_TIMER4 BIT(10) #define MISC_INT_TIMER3 BIT(9) @@ -341,6 +378,8 @@ #define AR934X_BOOTSTRAP_SDRAM_DISABLED BIT(1) #define AR934X_BOOTSTRAP_DDR1 BIT(0) +#define QCA955X_BOOTSTRAP_REF_CLK_40 BIT(4) + #define AR934X_PCIE_WMAC_INT_WMAC_MISC BIT(0) #define AR934X_PCIE_WMAC_INT_WMAC_TX BIT(1) #define AR934X_PCIE_WMAC_INT_WMAC_RXLP BIT(2) From 53330332f176eaa9567481c69bbad8b2176b4eb5 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 18:53:47 +0000 Subject: [PATCH 3042/4607] MIPS: ath79: add IRQ handling code for the QCA955X SoCs The IRQ routing in the QCA955x SoCs is slightly different from the routing implemented in the already supported SoCs. Cc: Rodriguez, Luis Cc: Giori, Kathy Cc: QCA Linux Team Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4955/ Signed-off-by: John Crispin --- arch/mips/ath79/irq.c | 110 ++++++++++++++++-- .../mips/include/asm/mach-ath79/ar71xx_regs.h | 32 +++++ arch/mips/include/asm/mach-ath79/irq.h | 6 +- 3 files changed, 140 insertions(+), 8 deletions(-) diff --git a/arch/mips/ath79/irq.c b/arch/mips/ath79/irq.c index df88d49bcb05..9c0e1761773f 100644 --- a/arch/mips/ath79/irq.c +++ b/arch/mips/ath79/irq.c @@ -103,7 +103,10 @@ static void __init ath79_misc_irq_init(void) if (soc_is_ar71xx() || soc_is_ar913x()) ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; - else if (soc_is_ar724x() || soc_is_ar933x() || soc_is_ar934x()) + else if (soc_is_ar724x() || + soc_is_ar933x() || + soc_is_ar934x() || + soc_is_qca955x()) ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; else BUG(); @@ -150,6 +153,88 @@ static void ar934x_ip2_irq_init(void) irq_set_chained_handler(ATH79_CPU_IRQ(2), ar934x_ip2_irq_dispatch); } +static void qca955x_ip2_irq_dispatch(unsigned int irq, struct irq_desc *desc) +{ + u32 status; + + disable_irq_nosync(irq); + + status = ath79_reset_rr(QCA955X_RESET_REG_EXT_INT_STATUS); + status &= QCA955X_EXT_INT_PCIE_RC1_ALL | QCA955X_EXT_INT_WMAC_ALL; + + if (status == 0) { + spurious_interrupt(); + goto enable; + } + + if (status & QCA955X_EXT_INT_PCIE_RC1_ALL) { + /* TODO: flush DDR? */ + generic_handle_irq(ATH79_IP2_IRQ(0)); + } + + if (status & QCA955X_EXT_INT_WMAC_ALL) { + /* TODO: flush DDR? */ + generic_handle_irq(ATH79_IP2_IRQ(1)); + } + +enable: + enable_irq(irq); +} + +static void qca955x_ip3_irq_dispatch(unsigned int irq, struct irq_desc *desc) +{ + u32 status; + + disable_irq_nosync(irq); + + status = ath79_reset_rr(QCA955X_RESET_REG_EXT_INT_STATUS); + status &= QCA955X_EXT_INT_PCIE_RC2_ALL | + QCA955X_EXT_INT_USB1 | + QCA955X_EXT_INT_USB2; + + if (status == 0) { + spurious_interrupt(); + goto enable; + } + + if (status & QCA955X_EXT_INT_USB1) { + /* TODO: flush DDR? */ + generic_handle_irq(ATH79_IP3_IRQ(0)); + } + + if (status & QCA955X_EXT_INT_USB2) { + /* TODO: flush DDR? */ + generic_handle_irq(ATH79_IP3_IRQ(1)); + } + + if (status & QCA955X_EXT_INT_PCIE_RC2_ALL) { + /* TODO: flush DDR? */ + generic_handle_irq(ATH79_IP3_IRQ(2)); + } + +enable: + enable_irq(irq); +} + +static void qca955x_irq_init(void) +{ + int i; + + for (i = ATH79_IP2_IRQ_BASE; + i < ATH79_IP2_IRQ_BASE + ATH79_IP2_IRQ_COUNT; i++) + irq_set_chip_and_handler(i, &dummy_irq_chip, + handle_level_irq); + + irq_set_chained_handler(ATH79_CPU_IRQ(2), qca955x_ip2_irq_dispatch); + + for (i = ATH79_IP3_IRQ_BASE; + i < ATH79_IP3_IRQ_BASE + ATH79_IP3_IRQ_COUNT; i++) + irq_set_chip_and_handler(i, &dummy_irq_chip, + handle_level_irq); + + irq_set_chained_handler(ATH79_CPU_IRQ(3), qca955x_ip3_irq_dispatch); +} + asmlinkage void plat_irq_dispatch(void) { unsigned long pending; @@ -185,6 +270,17 @@ asmlinkage void plat_irq_dispatch(void) * Issue a flush in the handlers to ensure that the driver sees * the update. */ + +static void ath79_default_ip2_handler(void) +{ + do_IRQ(ATH79_CPU_IRQ(2)); +} + +static void ath79_default_ip3_handler(void) +{ + do_IRQ(ATH79_CPU_IRQ(3)); +} + static void ar71xx_ip2_handler(void) { ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_PCI); @@ -209,11 +305,6 @@ static void ar933x_ip2_handler(void) do_IRQ(ATH79_CPU_IRQ(2)); } -static void ar934x_ip2_handler(void) -{ - do_IRQ(ATH79_CPU_IRQ(2)); -} - static void ar71xx_ip3_handler(void) { ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_USB); @@ -259,8 +350,11 @@ void __init arch_init_irq(void) ath79_ip2_handler = ar933x_ip2_handler; ath79_ip3_handler = ar933x_ip3_handler; } else if (soc_is_ar934x()) { - ath79_ip2_handler = ar934x_ip2_handler; + ath79_ip2_handler = ath79_default_ip2_handler; ath79_ip3_handler = ar934x_ip3_handler; + } else if (soc_is_qca955x()) { + ath79_ip2_handler = ath79_default_ip2_handler; + ath79_ip3_handler = ath79_default_ip3_handler; } else { BUG(); } @@ -271,4 +365,6 @@ void __init arch_init_irq(void) if (soc_is_ar934x()) ar934x_ip2_irq_init(); + else if (soc_is_qca955x()) + qca955x_irq_init(); } diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index 7b00e12afc1c..8782d8b097a3 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -300,6 +300,7 @@ #define AR934X_RESET_REG_PCIE_WMAC_INT_STATUS 0xac #define QCA955X_RESET_REG_BOOTSTRAP 0xb0 +#define QCA955X_RESET_REG_EXT_INT_STATUS 0xac #define MISC_INT_ETHSW BIT(12) #define MISC_INT_TIMER4 BIT(10) @@ -398,6 +399,37 @@ AR934X_PCIE_WMAC_INT_PCIE_RC1 | AR934X_PCIE_WMAC_INT_PCIE_RC2 | \ AR934X_PCIE_WMAC_INT_PCIE_RC3) +#define QCA955X_EXT_INT_WMAC_MISC BIT(0) +#define QCA955X_EXT_INT_WMAC_TX BIT(1) +#define QCA955X_EXT_INT_WMAC_RXLP BIT(2) +#define QCA955X_EXT_INT_WMAC_RXHP BIT(3) +#define QCA955X_EXT_INT_PCIE_RC1 BIT(4) +#define QCA955X_EXT_INT_PCIE_RC1_INT0 BIT(5) +#define QCA955X_EXT_INT_PCIE_RC1_INT1 BIT(6) +#define QCA955X_EXT_INT_PCIE_RC1_INT2 BIT(7) +#define QCA955X_EXT_INT_PCIE_RC1_INT3 BIT(8) +#define QCA955X_EXT_INT_PCIE_RC2 BIT(12) +#define QCA955X_EXT_INT_PCIE_RC2_INT0 BIT(13) +#define QCA955X_EXT_INT_PCIE_RC2_INT1 BIT(14) +#define QCA955X_EXT_INT_PCIE_RC2_INT2 BIT(15) +#define QCA955X_EXT_INT_PCIE_RC2_INT3 BIT(16) +#define QCA955X_EXT_INT_USB1 BIT(24) +#define QCA955X_EXT_INT_USB2 BIT(28) + +#define QCA955X_EXT_INT_WMAC_ALL \ + (QCA955X_EXT_INT_WMAC_MISC | QCA955X_EXT_INT_WMAC_TX | \ + QCA955X_EXT_INT_WMAC_RXLP | QCA955X_EXT_INT_WMAC_RXHP) + +#define QCA955X_EXT_INT_PCIE_RC1_ALL \ + (QCA955X_EXT_INT_PCIE_RC1 | QCA955X_EXT_INT_PCIE_RC1_INT0 | \ + QCA955X_EXT_INT_PCIE_RC1_INT1 | QCA955X_EXT_INT_PCIE_RC1_INT2 | \ + QCA955X_EXT_INT_PCIE_RC1_INT3) + +#define QCA955X_EXT_INT_PCIE_RC2_ALL \ + (QCA955X_EXT_INT_PCIE_RC2 | QCA955X_EXT_INT_PCIE_RC2_INT0 | \ + QCA955X_EXT_INT_PCIE_RC2_INT1 | QCA955X_EXT_INT_PCIE_RC2_INT2 | \ + QCA955X_EXT_INT_PCIE_RC2_INT3) + #define REV_ID_MAJOR_MASK 0xfff0 #define REV_ID_MAJOR_AR71XX 0x00a0 #define REV_ID_MAJOR_AR913X 0x00b0 diff --git a/arch/mips/include/asm/mach-ath79/irq.h b/arch/mips/include/asm/mach-ath79/irq.h index 23e2bba42482..5c9ca76a7ebf 100644 --- a/arch/mips/include/asm/mach-ath79/irq.h +++ b/arch/mips/include/asm/mach-ath79/irq.h @@ -10,7 +10,7 @@ #define __ASM_MACH_ATH79_IRQ_H #define MIPS_CPU_IRQ_BASE 0 -#define NR_IRQS 48 +#define NR_IRQS 51 #define ATH79_CPU_IRQ(_x) (MIPS_CPU_IRQ_BASE + (_x)) @@ -26,6 +26,10 @@ #define ATH79_IP2_IRQ_COUNT 2 #define ATH79_IP2_IRQ(_x) (ATH79_IP2_IRQ_BASE + (_x)) +#define ATH79_IP3_IRQ_BASE (ATH79_IP2_IRQ_BASE + ATH79_IP2_IRQ_COUNT) +#define ATH79_IP3_IRQ_COUNT 3 +#define ATH79_IP3_IRQ(_x) (ATH79_IP3_IRQ_BASE + (_x)) + #include_next #endif /* __ASM_MACH_ATH79_IRQ_H */ From f818ca3e6894d4a630a1ecc673c91df8fb6f6898 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 13:38:19 +0000 Subject: [PATCH 3043/4607] MIPS: ath79: add GPIO setup code for the QCA955X SoCs The existing code can handle the GPIO controller of the QCA955x SoCs. Add a minimal glue code to make it working. Cc: Rodriguez, Luis Cc: Giori, Kathy Cc: QCA Linux Team Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4947/ Signed-off-by: John Crispin --- arch/mips/ath79/gpio.c | 4 +++- arch/mips/include/asm/mach-ath79/ar71xx_regs.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/mips/ath79/gpio.c b/arch/mips/ath79/gpio.c index b7ed207e94a1..8d025b028bb1 100644 --- a/arch/mips/ath79/gpio.c +++ b/arch/mips/ath79/gpio.c @@ -194,12 +194,14 @@ void __init ath79_gpio_init(void) ath79_gpio_count = AR933X_GPIO_COUNT; else if (soc_is_ar934x()) ath79_gpio_count = AR934X_GPIO_COUNT; + else if (soc_is_qca955x()) + ath79_gpio_count = QCA955X_GPIO_COUNT; else BUG(); ath79_gpio_base = ioremap_nocache(AR71XX_GPIO_BASE, AR71XX_GPIO_SIZE); ath79_gpio_chip.ngpio = ath79_gpio_count; - if (soc_is_ar934x()) { + if (soc_is_ar934x() || soc_is_qca955x()) { ath79_gpio_chip.direction_input = ar934x_gpio_direction_input; ath79_gpio_chip.direction_output = ar934x_gpio_direction_output; } diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index 8782d8b097a3..4868ed5c149b 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -510,6 +510,7 @@ #define AR913X_GPIO_COUNT 22 #define AR933X_GPIO_COUNT 30 #define AR934X_GPIO_COUNT 23 +#define QCA955X_GPIO_COUNT 24 /* * SRIF block From 7d4c2af9bdbbe789fe4a93f32c5890d72cbf60a1 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 13:38:20 +0000 Subject: [PATCH 3044/4607] MIPS: ath79: add QCA955X specific glue to ath79_device_reset_{set, clear} The ath79_device_reset_* are causing BUG when those are used on the QCA955x SoCs. The patch adds the required code to avoid that. Cc: Rodriguez, Luis Cc: Giori, Kathy Cc: QCA Linux Team Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4948/ Signed-off-by: John Crispin --- arch/mips/ath79/common.c | 4 ++++ arch/mips/include/asm/mach-ath79/ar71xx_regs.h | 1 + 2 files changed, 5 insertions(+) diff --git a/arch/mips/ath79/common.c b/arch/mips/ath79/common.c index 5a4adfc9d79d..eb3966cd8cfc 100644 --- a/arch/mips/ath79/common.c +++ b/arch/mips/ath79/common.c @@ -72,6 +72,8 @@ void ath79_device_reset_set(u32 mask) reg = AR933X_RESET_REG_RESET_MODULE; else if (soc_is_ar934x()) reg = AR934X_RESET_REG_RESET_MODULE; + else if (soc_is_qca955x()) + reg = QCA955X_RESET_REG_RESET_MODULE; else BUG(); @@ -98,6 +100,8 @@ void ath79_device_reset_clear(u32 mask) reg = AR933X_RESET_REG_RESET_MODULE; else if (soc_is_ar934x()) reg = AR934X_RESET_REG_RESET_MODULE; + else if (soc_is_qca955x()) + reg = QCA955X_RESET_REG_RESET_MODULE; else BUG(); diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index 4868ed5c149b..bf50ddfc9d5c 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -299,6 +299,7 @@ #define AR934X_RESET_REG_BOOTSTRAP 0xb0 #define AR934X_RESET_REG_PCIE_WMAC_INT_STATUS 0xac +#define QCA955X_RESET_REG_RESET_MODULE 0x1c #define QCA955X_RESET_REG_BOOTSTRAP 0xb0 #define QCA955X_RESET_REG_EXT_INT_STATUS 0xac From 13992303fa705ae1e4acf4660c69687672996029 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 13:38:21 +0000 Subject: [PATCH 3045/4607] MIPS: ath79: register UART for the QCA955X SoCs Similarly to the preceding SoCs, the QCA955X SoCs also have a built-in NS16650 compatible UART. Register the platform device for that to make it usable. Cc: Rodriguez, Luis Cc: Giori, Kathy Cc: QCA Linux Team Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4949/ Signed-off-by: John Crispin --- arch/mips/ath79/dev-common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/mips/ath79/dev-common.c b/arch/mips/ath79/dev-common.c index 480f5eb9d300..9516aab27139 100644 --- a/arch/mips/ath79/dev-common.c +++ b/arch/mips/ath79/dev-common.c @@ -90,7 +90,8 @@ void __init ath79_register_uart(void) if (soc_is_ar71xx() || soc_is_ar724x() || soc_is_ar913x() || - soc_is_ar934x()) { + soc_is_ar934x() || + soc_is_qca955x()) { ath79_uart_data[0].uartclk = clk_get_rate(clk); platform_device_register(&ath79_uart_device); } else if (soc_is_ar933x()) { From e9c0d0aaa3a7a6e66135e8b44f3323143a635098 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 18:54:33 +0000 Subject: [PATCH 3046/4607] MIPS: ath79: add WMAC registration code for the QCA955X SoCs The SoC has a built-in wireless MAC. Register a platform device for that to make it usable with the ath9k driver. Cc: Rodriguez, Luis Cc: Giori, Kathy Cc: QCA Linux Team Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4956/ Signed-off-by: John Crispin --- arch/mips/ath79/Kconfig | 2 +- arch/mips/ath79/dev-wmac.c | 20 +++++++++++++++++++ .../mips/include/asm/mach-ath79/ar71xx_regs.h | 3 +++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig index cffdc8e3b63b..77926e331b17 100644 --- a/arch/mips/ath79/Kconfig +++ b/arch/mips/ath79/Kconfig @@ -108,7 +108,7 @@ config ATH79_DEV_USB def_bool n config ATH79_DEV_WMAC - depends on (SOC_AR913X || SOC_AR933X || SOC_AR934X) + depends on (SOC_AR913X || SOC_AR933X || SOC_AR934X || SOC_QCA955X) def_bool n endif diff --git a/arch/mips/ath79/dev-wmac.c b/arch/mips/ath79/dev-wmac.c index d71d745e3109..da190b1b87ce 100644 --- a/arch/mips/ath79/dev-wmac.c +++ b/arch/mips/ath79/dev-wmac.c @@ -116,6 +116,24 @@ static void ar934x_wmac_setup(void) ath79_wmac_data.is_clk_25mhz = true; } +static void qca955x_wmac_setup(void) +{ + u32 t; + + ath79_wmac_device.name = "qca955x_wmac"; + + ath79_wmac_resources[0].start = QCA955X_WMAC_BASE; + ath79_wmac_resources[0].end = QCA955X_WMAC_BASE + QCA955X_WMAC_SIZE - 1; + ath79_wmac_resources[1].start = ATH79_IP2_IRQ(1); + ath79_wmac_resources[1].end = ATH79_IP2_IRQ(1); + + t = ath79_reset_rr(QCA955X_RESET_REG_BOOTSTRAP); + if (t & QCA955X_BOOTSTRAP_REF_CLK_40) + ath79_wmac_data.is_clk_25mhz = false; + else + ath79_wmac_data.is_clk_25mhz = true; +} + void __init ath79_register_wmac(u8 *cal_data) { if (soc_is_ar913x()) @@ -124,6 +142,8 @@ void __init ath79_register_wmac(u8 *cal_data) ar933x_wmac_setup(); else if (soc_is_ar934x()) ar934x_wmac_setup(); + else if (soc_is_qca955x()) + qca955x_wmac_setup(); else BUG(); diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index bf50ddfc9d5c..47282120db1e 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -94,6 +94,9 @@ #define AR934X_SRIF_BASE (AR71XX_APB_BASE + 0x00116000) #define AR934X_SRIF_SIZE 0x1000 +#define QCA955X_WMAC_BASE (AR71XX_APB_BASE + 0x00100000) +#define QCA955X_WMAC_SIZE 0x20000 + /* * DDR_CTRL block */ From 0a5f3b1c9f20eb44142e3b37662de15c944f759d Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 13:38:23 +0000 Subject: [PATCH 3047/4607] MIPS: ath79: add PCI controller registration code for the QCA955X SoCs Add SoC specific PCI IRQ map, and register platform devices for the two built-in PCIe RCs. Cc: Rodriguez, Luis Cc: Giori, Kathy Cc: QCA Linux Team Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4951/ Signed-off-by: John Crispin --- arch/mips/ath79/Kconfig | 2 ++ arch/mips/ath79/pci.c | 36 +++++++++++++++++++ .../mips/include/asm/mach-ath79/ar71xx_regs.h | 13 +++++++ 3 files changed, 51 insertions(+) diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig index 77926e331b17..76a001e5fc26 100644 --- a/arch/mips/ath79/Kconfig +++ b/arch/mips/ath79/Kconfig @@ -90,6 +90,8 @@ config SOC_AR934X config SOC_QCA955X select USB_ARCH_HAS_EHCI + select HW_HAS_PCI + select PCI_AR724X if PCI def_bool n config PCI_AR724X diff --git a/arch/mips/ath79/pci.c b/arch/mips/ath79/pci.c index 4350c252bce5..730c0b03060d 100644 --- a/arch/mips/ath79/pci.c +++ b/arch/mips/ath79/pci.c @@ -49,6 +49,21 @@ static const struct ath79_pci_irq ar724x_pci_irq_map[] __initconst = { } }; +static const struct ath79_pci_irq qca955x_pci_irq_map[] __initconst = { + { + .bus = 0, + .slot = 0, + .pin = 1, + .irq = ATH79_PCI_IRQ(0), + }, + { + .bus = 1, + .slot = 0, + .pin = 1, + .irq = ATH79_PCI_IRQ(1), + }, +}; + int __init pcibios_map_irq(const struct pci_dev *dev, uint8_t slot, uint8_t pin) { int irq = -1; @@ -64,6 +79,9 @@ int __init pcibios_map_irq(const struct pci_dev *dev, uint8_t slot, uint8_t pin) soc_is_ar9344()) { ath79_pci_irq_map = ar724x_pci_irq_map; ath79_pci_nr_irqs = ARRAY_SIZE(ar724x_pci_irq_map); + } else if (soc_is_qca955x()) { + ath79_pci_irq_map = qca955x_pci_irq_map; + ath79_pci_nr_irqs = ARRAY_SIZE(qca955x_pci_irq_map); } else { pr_crit("pci %s: invalid irq map\n", pci_name((struct pci_dev *) dev)); @@ -225,6 +243,24 @@ int __init ath79_register_pci(void) AR724X_PCI_MEM_SIZE, 0, ATH79_IP2_IRQ(0)); + } else if (soc_is_qca9558()) { + pdev = ath79_register_pci_ar724x(0, + QCA955X_PCI_CFG_BASE0, + QCA955X_PCI_CTRL_BASE0, + QCA955X_PCI_CRP_BASE0, + QCA955X_PCI_MEM_BASE0, + QCA955X_PCI_MEM_SIZE, + 0, + ATH79_IP2_IRQ(0)); + + pdev = ath79_register_pci_ar724x(1, + QCA955X_PCI_CFG_BASE1, + QCA955X_PCI_CTRL_BASE1, + QCA955X_PCI_CRP_BASE1, + QCA955X_PCI_MEM_BASE1, + QCA955X_PCI_MEM_SIZE, + 1, + ATH79_IP3_IRQ(2)); } else { /* No PCI support */ return -ENODEV; diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index 47282120db1e..b7fa9d14d20f 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -94,6 +94,19 @@ #define AR934X_SRIF_BASE (AR71XX_APB_BASE + 0x00116000) #define AR934X_SRIF_SIZE 0x1000 +#define QCA955X_PCI_MEM_BASE0 0x10000000 +#define QCA955X_PCI_MEM_BASE1 0x12000000 +#define QCA955X_PCI_MEM_SIZE 0x02000000 +#define QCA955X_PCI_CFG_BASE0 0x14000000 +#define QCA955X_PCI_CFG_BASE1 0x16000000 +#define QCA955X_PCI_CFG_SIZE 0x1000 +#define QCA955X_PCI_CRP_BASE0 (AR71XX_APB_BASE + 0x000c0000) +#define QCA955X_PCI_CRP_BASE1 (AR71XX_APB_BASE + 0x00250000) +#define QCA955X_PCI_CRP_SIZE 0x1000 +#define QCA955X_PCI_CTRL_BASE0 (AR71XX_APB_BASE + 0x000f0000) +#define QCA955X_PCI_CTRL_BASE1 (AR71XX_APB_BASE + 0x00280000) +#define QCA955X_PCI_CTRL_SIZE 0x100 + #define QCA955X_WMAC_BASE (AR71XX_APB_BASE + 0x00100000) #define QCA955X_WMAC_SIZE 0x20000 From 82c46840ae6bd8a147c59cd51f636d913989324a Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 13:38:24 +0000 Subject: [PATCH 3048/4607] MIPS: ath79: add USB controller registration code for the QCA955X SoCs Register platfom devices for the built-in USB controllers of the SoCs. Cc: Rodriguez, Luis Cc: Giori, Kathy Cc: QCA Linux Team Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4952/ Signed-off-by: John Crispin --- arch/mips/ath79/dev-usb.c | 15 +++++++++++++++ arch/mips/include/asm/mach-ath79/ar71xx_regs.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/arch/mips/ath79/dev-usb.c b/arch/mips/ath79/dev-usb.c index 02124d02cf6e..8227265bcc2d 100644 --- a/arch/mips/ath79/dev-usb.c +++ b/arch/mips/ath79/dev-usb.c @@ -208,6 +208,19 @@ static void __init ar934x_usb_setup(void) &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); } +static void __init qca955x_usb_setup(void) +{ + ath79_usb_register("ehci-platform", 0, + QCA955X_EHCI0_BASE, QCA955X_EHCI_SIZE, + ATH79_IP3_IRQ(0), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); + + ath79_usb_register("ehci-platform", 1, + QCA955X_EHCI1_BASE, QCA955X_EHCI_SIZE, + ATH79_IP3_IRQ(1), + &ath79_ehci_pdata_v2, sizeof(ath79_ehci_pdata_v2)); +} + void __init ath79_register_usb(void) { if (soc_is_ar71xx()) @@ -222,6 +235,8 @@ void __init ath79_register_usb(void) ar933x_usb_setup(); else if (soc_is_ar934x()) ar934x_usb_setup(); + else if (soc_is_qca955x()) + qca955x_usb_setup(); else BUG(); } diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h index b7fa9d14d20f..4de183112917 100644 --- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h +++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h @@ -109,6 +109,9 @@ #define QCA955X_WMAC_BASE (AR71XX_APB_BASE + 0x00100000) #define QCA955X_WMAC_SIZE 0x20000 +#define QCA955X_EHCI0_BASE 0x1b000000 +#define QCA955X_EHCI1_BASE 0x1b400000 +#define QCA955X_EHCI_SIZE 0x1000 /* * DDR_CTRL block From 27ea052acb9eaca98cc90bf1b8738b6d0ea5bc2f Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 15 Feb 2013 13:38:25 +0000 Subject: [PATCH 3049/4607] MIPS: ath79: add support for the Qualcomm Atheros AP136-010 board Also enable the board in the default configuration. Cc: Rodriguez, Luis Cc: Giori, Kathy Cc: QCA Linux Team Signed-off-by: Gabor Juhos Patchwork: http://patchwork.linux-mips.org/patch/4953/ Signed-off-by: John Crispin --- arch/mips/ath79/Kconfig | 12 +++ arch/mips/ath79/Makefile | 1 + arch/mips/ath79/mach-ap136.c | 156 ++++++++++++++++++++++++++++++ arch/mips/ath79/machtypes.h | 1 + arch/mips/configs/ath79_defconfig | 1 + 5 files changed, 171 insertions(+) create mode 100644 arch/mips/ath79/mach-ap136.c diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig index 76a001e5fc26..3995e31a73e2 100644 --- a/arch/mips/ath79/Kconfig +++ b/arch/mips/ath79/Kconfig @@ -14,6 +14,18 @@ config ATH79_MACH_AP121 Say 'Y' here if you want your kernel to support the Atheros AP121 reference board. +config ATH79_MACH_AP136 + bool "Atheros AP136 reference board" + select SOC_QCA955X + select ATH79_DEV_GPIO_BUTTONS + select ATH79_DEV_LEDS_GPIO + select ATH79_DEV_SPI + select ATH79_DEV_USB + select ATH79_DEV_WMAC + help + Say 'Y' here if you want your kernel to support the + Atheros AP136 reference board. + config ATH79_MACH_AP81 bool "Atheros AP81 reference board" select SOC_AR913X diff --git a/arch/mips/ath79/Makefile b/arch/mips/ath79/Makefile index 2b54d98263f3..5c9ff692ff3c 100644 --- a/arch/mips/ath79/Makefile +++ b/arch/mips/ath79/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_ATH79_DEV_WMAC) += dev-wmac.o # Machines # obj-$(CONFIG_ATH79_MACH_AP121) += mach-ap121.o +obj-$(CONFIG_ATH79_MACH_AP136) += mach-ap136.o obj-$(CONFIG_ATH79_MACH_AP81) += mach-ap81.o obj-$(CONFIG_ATH79_MACH_DB120) += mach-db120.o obj-$(CONFIG_ATH79_MACH_PB44) += mach-pb44.o diff --git a/arch/mips/ath79/mach-ap136.c b/arch/mips/ath79/mach-ap136.c new file mode 100644 index 000000000000..479dd4b1d0d2 --- /dev/null +++ b/arch/mips/ath79/mach-ap136.c @@ -0,0 +1,156 @@ +/* + * Qualcomm Atheros AP136 reference board support + * + * Copyright (c) 2012 Qualcomm Atheros + * Copyright (c) 2012-2013 Gabor Juhos + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include +#include + +#include "machtypes.h" +#include "dev-gpio-buttons.h" +#include "dev-leds-gpio.h" +#include "dev-spi.h" +#include "dev-usb.h" +#include "dev-wmac.h" +#include "pci.h" + +#define AP136_GPIO_LED_STATUS_RED 14 +#define AP136_GPIO_LED_STATUS_GREEN 19 +#define AP136_GPIO_LED_USB 4 +#define AP136_GPIO_LED_WLAN_2G 13 +#define AP136_GPIO_LED_WLAN_5G 12 +#define AP136_GPIO_LED_WPS_RED 15 +#define AP136_GPIO_LED_WPS_GREEN 20 + +#define AP136_GPIO_BTN_WPS 16 +#define AP136_GPIO_BTN_RFKILL 21 + +#define AP136_KEYS_POLL_INTERVAL 20 /* msecs */ +#define AP136_KEYS_DEBOUNCE_INTERVAL (3 * AP136_KEYS_POLL_INTERVAL) + +#define AP136_WMAC_CALDATA_OFFSET 0x1000 +#define AP136_PCIE_CALDATA_OFFSET 0x5000 + +static struct gpio_led ap136_leds_gpio[] __initdata = { + { + .name = "qca:green:status", + .gpio = AP136_GPIO_LED_STATUS_GREEN, + .active_low = 1, + }, + { + .name = "qca:red:status", + .gpio = AP136_GPIO_LED_STATUS_RED, + .active_low = 1, + }, + { + .name = "qca:green:wps", + .gpio = AP136_GPIO_LED_WPS_GREEN, + .active_low = 1, + }, + { + .name = "qca:red:wps", + .gpio = AP136_GPIO_LED_WPS_RED, + .active_low = 1, + }, + { + .name = "qca:red:wlan-2g", + .gpio = AP136_GPIO_LED_WLAN_2G, + .active_low = 1, + }, + { + .name = "qca:red:usb", + .gpio = AP136_GPIO_LED_USB, + .active_low = 1, + } +}; + +static struct gpio_keys_button ap136_gpio_keys[] __initdata = { + { + .desc = "WPS button", + .type = EV_KEY, + .code = KEY_WPS_BUTTON, + .debounce_interval = AP136_KEYS_DEBOUNCE_INTERVAL, + .gpio = AP136_GPIO_BTN_WPS, + .active_low = 1, + }, + { + .desc = "RFKILL button", + .type = EV_KEY, + .code = KEY_RFKILL, + .debounce_interval = AP136_KEYS_DEBOUNCE_INTERVAL, + .gpio = AP136_GPIO_BTN_RFKILL, + .active_low = 1, + }, +}; + +static struct spi_board_info ap136_spi_info[] = { + { + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 25000000, + .modalias = "mx25l6405d", + } +}; + +static struct ath79_spi_platform_data ap136_spi_data = { + .bus_num = 0, + .num_chipselect = 1, +}; + +#ifdef CONFIG_PCI +static struct ath9k_platform_data ap136_ath9k_data; + +static int ap136_pci_plat_dev_init(struct pci_dev *dev) +{ + if (dev->bus->number == 1 && (PCI_SLOT(dev->devfn)) == 0) + dev->dev.platform_data = &ap136_ath9k_data; + + return 0; +} + +static void __init ap136_pci_init(u8 *eeprom) +{ + memcpy(ap136_ath9k_data.eeprom_data, eeprom, + sizeof(ap136_ath9k_data.eeprom_data)); + + ath79_pci_set_plat_dev_init(ap136_pci_plat_dev_init); + ath79_register_pci(); +} +#else +static inline void ap136_pci_init(void) {} +#endif /* CONFIG_PCI */ + +static void __init ap136_setup(void) +{ + u8 *art = (u8 *) KSEG1ADDR(0x1fff0000); + + ath79_register_leds_gpio(-1, ARRAY_SIZE(ap136_leds_gpio), + ap136_leds_gpio); + ath79_register_gpio_keys_polled(-1, AP136_KEYS_POLL_INTERVAL, + ARRAY_SIZE(ap136_gpio_keys), + ap136_gpio_keys); + ath79_register_spi(&ap136_spi_data, ap136_spi_info, + ARRAY_SIZE(ap136_spi_info)); + ath79_register_usb(); + ath79_register_wmac(art + AP136_WMAC_CALDATA_OFFSET); + ap136_pci_init(art + AP136_PCIE_CALDATA_OFFSET); +} + +MIPS_MACHINE(ATH79_MACH_AP136_010, "AP136-010", + "Atheros AP136-010 reference board", + ap136_setup); diff --git a/arch/mips/ath79/machtypes.h b/arch/mips/ath79/machtypes.h index af92e5c30d66..26254058c545 100644 --- a/arch/mips/ath79/machtypes.h +++ b/arch/mips/ath79/machtypes.h @@ -17,6 +17,7 @@ enum ath79_mach_type { ATH79_MACH_GENERIC = 0, ATH79_MACH_AP121, /* Atheros AP121 reference board */ + ATH79_MACH_AP136_010, /* Atheros AP136-010 reference board */ ATH79_MACH_AP81, /* Atheros AP81 reference board */ ATH79_MACH_DB120, /* Atheros DB120 reference board */ ATH79_MACH_PB44, /* Atheros PB44 reference board */ diff --git a/arch/mips/configs/ath79_defconfig b/arch/mips/configs/ath79_defconfig index ea87d43ba607..e3a3836508ec 100644 --- a/arch/mips/configs/ath79_defconfig +++ b/arch/mips/configs/ath79_defconfig @@ -1,5 +1,6 @@ CONFIG_ATH79=y CONFIG_ATH79_MACH_AP121=y +CONFIG_ATH79_MACH_AP136=y CONFIG_ATH79_MACH_AP81=y CONFIG_ATH79_MACH_DB120=y CONFIG_ATH79_MACH_PB44=y From 1e7decdb27ae89b2a0626635a8cf527f930bff1c Mon Sep 17 00:00:00 2001 From: David Daney Date: Sat, 16 Feb 2013 23:42:43 +0100 Subject: [PATCH 3050/4607] MIPS: Probe for and report hardware virtualization support. The presence of the MIPS Virtualization Application-Specific Extension is indicated by CP0_Config3[23]. Probe for this and report it in /proc/cpuinfo. Signed-off-by: David Daney Patchwork: http://patchwork.linux-mips.org/patch/4904/ Signed-off-by: John Crispin --- arch/mips/include/asm/cpu-features.h | 4 ++++ arch/mips/include/asm/cpu.h | 2 +- arch/mips/include/asm/mipsregs.h | 1 + arch/mips/kernel/cpu-probe.c | 2 ++ arch/mips/kernel/proc.c | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h index 00171cddb6d5..c6f64ef0681e 100644 --- a/arch/mips/include/asm/cpu-features.h +++ b/arch/mips/include/asm/cpu-features.h @@ -263,4 +263,8 @@ #define cpu_has_perf_cntr_intr_bit (cpu_data[0].options & MIPS_CPU_PCI) #endif +#ifndef cpu_has_vz +#define cpu_has_vz (cpu_data[0].ases & MIPS_ASE_VZ) +#endif + #endif /* __ASM_CPU_FEATURES_H */ diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h index 2de2fee16cc4..4dff3378d96f 100644 --- a/arch/mips/include/asm/cpu.h +++ b/arch/mips/include/asm/cpu.h @@ -336,6 +336,6 @@ enum cpu_type_enum { #define MIPS_ASE_DSP 0x00000010 /* Signal Processing ASE */ #define MIPS_ASE_MIPSMT 0x00000020 /* CPU supports MIPS MT */ #define MIPS_ASE_DSP2P 0x00000040 /* Signal Processing ASE Rev 2 */ - +#define MIPS_ASE_VZ 0x00000080 /* Virtualization ASE */ #endif /* _ASM_CPU_H */ diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index 9f47cda632ab..5df4cda4991a 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -596,6 +596,7 @@ #define MIPS_CONF3_RXI (_ULCAST_(1) << 12) #define MIPS_CONF3_ULRI (_ULCAST_(1) << 13) #define MIPS_CONF3_ISA (_ULCAST_(3) << 14) +#define MIPS_CONF3_VZ (_ULCAST_(1) << 23) #define MIPS_CONF4_MMUSIZEEXT (_ULCAST_(255) << 0) #define MIPS_CONF4_MMUEXTDEF (_ULCAST_(3) << 14) diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index ba169022fe1d..0c69d1d14080 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -442,6 +442,8 @@ static inline unsigned int decode_config3(struct cpuinfo_mips *c) c->options |= MIPS_CPU_ULRI; if (config3 & MIPS_CONF3_ISA) c->options |= MIPS_CPU_MICROMIPS; + if (config3 & MIPS_CONF3_VZ) + c->ases |= MIPS_ASE_VZ; return config3 & MIPS_CONF_M; } diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 239ae03f3330..453d55699fef 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -74,6 +74,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) if (cpu_has_dsp2) seq_printf(m, "%s", " dsp2"); if (cpu_has_mipsmt) seq_printf(m, "%s", " mt"); if (cpu_has_mmips) seq_printf(m, "%s", " micromips"); + if (cpu_has_vz) seq_printf(m, "%s", " vz"); seq_printf(m, "\n"); seq_printf(m, "shadow register sets\t: %d\n", From f7be4e754b61681467f873400cbaa42a013b8973 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Mon, 11 Feb 2013 20:51:49 +0000 Subject: [PATCH 3051/4607] MIPS: early_printk: drop __init annotations We cannot use __init for earlyprintk code or data, since the kernel parameter "keep_bootcon" allows leaving the boot console enabled. Currently MIPS will crash/hang/die if you use keep_bootcon. The patch fixes it at least on Lemote FuLoong mini-PC. Changes for other boards were done based on what I could find with grep... Signed-off-by: Aaro Koskinen Patchwork: http://patchwork.linux-mips.org/patch/4935/ Signed-off-by: John Crispin --- arch/mips/bcm63xx/early_printk.c | 4 ++-- arch/mips/kernel/early_printk.c | 5 ++--- arch/mips/loongson1/common/prom.c | 2 +- arch/mips/sgi-ip27/ip27-console.c | 2 +- arch/mips/txx9/generic/setup.c | 8 ++++---- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/arch/mips/bcm63xx/early_printk.c b/arch/mips/bcm63xx/early_printk.c index bf353c937df2..aa8f7f9cc7a4 100644 --- a/arch/mips/bcm63xx/early_printk.c +++ b/arch/mips/bcm63xx/early_printk.c @@ -10,7 +10,7 @@ #include #include -static void __init wait_xfered(void) +static void wait_xfered(void) { unsigned int val; @@ -22,7 +22,7 @@ static void __init wait_xfered(void) } while (1); } -void __init prom_putchar(char c) +void prom_putchar(char c) { wait_xfered(); bcm_uart0_writel(c, UART_FIFO_REG); diff --git a/arch/mips/kernel/early_printk.c b/arch/mips/kernel/early_printk.c index 9ae813eb782e..9e6440eaa455 100644 --- a/arch/mips/kernel/early_printk.c +++ b/arch/mips/kernel/early_printk.c @@ -14,8 +14,7 @@ extern void prom_putchar(char); -static void __init -early_console_write(struct console *con, const char *s, unsigned n) +static void early_console_write(struct console *con, const char *s, unsigned n) { while (n-- && *s) { if (*s == '\n') @@ -25,7 +24,7 @@ early_console_write(struct console *con, const char *s, unsigned n) } } -static struct console early_console __initdata = { +static struct console early_console = { .name = "early", .write = early_console_write, .flags = CON_PRINTBUFFER | CON_BOOT, diff --git a/arch/mips/loongson1/common/prom.c b/arch/mips/loongson1/common/prom.c index 1f8e49f9886d..54dee09b36cd 100644 --- a/arch/mips/loongson1/common/prom.c +++ b/arch/mips/loongson1/common/prom.c @@ -73,7 +73,7 @@ void __init prom_free_prom_memory(void) #define PORT(offset) (u8 *)(KSEG1ADDR(LS1X_UART0_BASE + offset)) -void __init prom_putchar(char c) +void prom_putchar(char c) { int timeout; diff --git a/arch/mips/sgi-ip27/ip27-console.c b/arch/mips/sgi-ip27/ip27-console.c index 984e561f0f7a..b952d5b1af86 100644 --- a/arch/mips/sgi-ip27/ip27-console.c +++ b/arch/mips/sgi-ip27/ip27-console.c @@ -31,7 +31,7 @@ static inline struct ioc3_uartregs *console_uart(void) return &ioc3->sregs.uarta; } -void __init prom_putchar(char c) +void prom_putchar(char c) { struct ioc3_uartregs *uart = console_uart(); diff --git a/arch/mips/txx9/generic/setup.c b/arch/mips/txx9/generic/setup.c index 560fe8991753..5524f2c7b05c 100644 --- a/arch/mips/txx9/generic/setup.c +++ b/arch/mips/txx9/generic/setup.c @@ -513,19 +513,19 @@ void __init txx9_sio_init(unsigned long baseaddr, int irq, } #ifdef CONFIG_EARLY_PRINTK -static void __init null_prom_putchar(char c) +static void null_prom_putchar(char c) { } -void (*txx9_prom_putchar)(char c) __initdata = null_prom_putchar; +void (*txx9_prom_putchar)(char c) = null_prom_putchar; -void __init prom_putchar(char c) +void prom_putchar(char c) { txx9_prom_putchar(c); } static void __iomem *early_txx9_sio_port; -static void __init early_txx9_sio_putchar(char c) +static void early_txx9_sio_putchar(char c) { #define TXX9_SICISR 0x0c #define TXX9_SITFIFO 0x1c From df1cc3da2134bc10e6edc62709013a10e03e4106 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 8 Feb 2013 09:45:14 +0000 Subject: [PATCH 3052/4607] MIPS: SMTC: fix implicit declaration of set_vi_handler This patch fixes the following implicit declaration while building with MIPS SMTC support enabled: arch/mips/kernel/smtc.c: In function 'setup_cross_vpe_interrupts': arch/mips/kernel/smtc.c:1205:2: error: implicit declaration of function 'set_vi_handler' [-Werror=implicit-function-declaration] cc1: all warnings being treated as errors Signed-off-by: Florian Fainelli Patchwork: http://patchwork.linux-mips.org/patch/4931/ Signed-off-by: John Crispin --- arch/mips/kernel/smtc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c index 1d47843d3cc0..0822232b62fc 100644 --- a/arch/mips/kernel/smtc.c +++ b/arch/mips/kernel/smtc.c @@ -41,6 +41,7 @@ #include #include #include +#include /* * SMTC Kernel needs to manipulate low-level CPU interrupt mask From 535237cecab2b078114be712c67e89a0db61965f Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 17 Feb 2013 01:16:15 +0100 Subject: [PATCH 3053/4607] MIPS: remove broken conditional inside vpe loader code The commit [1] breaks builds and results in the following error arch/mips/kernel/vpe.c: In function 'vpe_run': arch/mips/kernel/vpe.c:708:16: error: invalid type argument of '->' (have 'struct list_head') Taking a closer look at the conditional we notice that list_first_entry wont ever return NULL. The easiest fix is to just drop the dead code. [1] commit 3d2d03247632920aa21b42a0b032a4ffd44ce15e MIPS: vpe.c: Fix null pointer dereference in print arguments. Signed-off-by: John Crispin --- arch/mips/kernel/vpe.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c index 147cec19621d..0e0fdb783b7c 100644 --- a/arch/mips/kernel/vpe.c +++ b/arch/mips/kernel/vpe.c @@ -697,18 +697,7 @@ static int vpe_run(struct vpe * v) dmt_flag = dmt(); vpeflags = dvpe(); - if (!list_empty(&v->tc)) { - if ((t = list_entry(v->tc.next, struct tc, tc)) == NULL) { - evpe(vpeflags); - emt(dmt_flag); - local_irq_restore(flags); - - printk(KERN_WARNING - "VPE loader: TC %d is already in use.\n", - v->tc->index); - return -ENOEXEC; - } - } else { + if (list_empty(&v->tc)) { evpe(vpeflags); emt(dmt_flag); local_irq_restore(flags); @@ -720,6 +709,8 @@ static int vpe_run(struct vpe * v) return -ENOEXEC; } + t = list_first_entry(&v->tc, struct tc, tc); + /* Put MVPE's into 'configuration state' */ set_c0_mvpcontrol(MVPCONTROL_VPC); From 9a5467bf7b6e9e02ec9c3da4e23747c05faeaac6 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Tue, 5 Feb 2013 18:19:13 +0100 Subject: [PATCH 3054/4607] crypto: user - fix info leaks in report API Three errors resulting in kernel memory disclosure: 1/ The structures used for the netlink based crypto algorithm report API are located on the stack. As snprintf() does not fill the remainder of the buffer with null bytes, those stack bytes will be disclosed to users of the API. Switch to strncpy() to fix this. 2/ crypto_report_one() does not initialize all field of struct crypto_user_alg. Fix this to fix the heap info leak. 3/ For the module name we should copy only as many bytes as module_name() returns -- not as much as the destination buffer could hold. But the current code does not and therefore copies random data from behind the end of the module name, as the module name is always shorter than CRYPTO_MAX_ALG_NAME. Also switch to use strncpy() to copy the algorithm's name and driver_name. They are strings, after all. Signed-off-by: Mathias Krause Cc: Steffen Klassert Signed-off-by: Herbert Xu --- crypto/ablkcipher.c | 12 ++++++------ crypto/aead.c | 9 ++++----- crypto/ahash.c | 2 +- crypto/blkcipher.c | 6 +++--- crypto/crypto_user.c | 20 ++++++++++---------- crypto/pcompress.c | 3 +-- crypto/rng.c | 2 +- crypto/shash.c | 3 ++- 8 files changed, 28 insertions(+), 29 deletions(-) diff --git a/crypto/ablkcipher.c b/crypto/ablkcipher.c index 533de9550a82..7d4a8d28277e 100644 --- a/crypto/ablkcipher.c +++ b/crypto/ablkcipher.c @@ -388,9 +388,9 @@ static int crypto_ablkcipher_report(struct sk_buff *skb, struct crypto_alg *alg) { struct crypto_report_blkcipher rblkcipher; - snprintf(rblkcipher.type, CRYPTO_MAX_ALG_NAME, "%s", "ablkcipher"); - snprintf(rblkcipher.geniv, CRYPTO_MAX_ALG_NAME, "%s", - alg->cra_ablkcipher.geniv ?: ""); + strncpy(rblkcipher.type, "ablkcipher", sizeof(rblkcipher.type)); + strncpy(rblkcipher.geniv, alg->cra_ablkcipher.geniv ?: "", + sizeof(rblkcipher.geniv)); rblkcipher.blocksize = alg->cra_blocksize; rblkcipher.min_keysize = alg->cra_ablkcipher.min_keysize; @@ -469,9 +469,9 @@ static int crypto_givcipher_report(struct sk_buff *skb, struct crypto_alg *alg) { struct crypto_report_blkcipher rblkcipher; - snprintf(rblkcipher.type, CRYPTO_MAX_ALG_NAME, "%s", "givcipher"); - snprintf(rblkcipher.geniv, CRYPTO_MAX_ALG_NAME, "%s", - alg->cra_ablkcipher.geniv ?: ""); + strncpy(rblkcipher.type, "givcipher", sizeof(rblkcipher.type)); + strncpy(rblkcipher.geniv, alg->cra_ablkcipher.geniv ?: "", + sizeof(rblkcipher.geniv)); rblkcipher.blocksize = alg->cra_blocksize; rblkcipher.min_keysize = alg->cra_ablkcipher.min_keysize; diff --git a/crypto/aead.c b/crypto/aead.c index 4d04e12ffde8..547491e35c63 100644 --- a/crypto/aead.c +++ b/crypto/aead.c @@ -117,9 +117,8 @@ static int crypto_aead_report(struct sk_buff *skb, struct crypto_alg *alg) struct crypto_report_aead raead; struct aead_alg *aead = &alg->cra_aead; - snprintf(raead.type, CRYPTO_MAX_ALG_NAME, "%s", "aead"); - snprintf(raead.geniv, CRYPTO_MAX_ALG_NAME, "%s", - aead->geniv ?: ""); + strncpy(raead.type, "aead", sizeof(raead.type)); + strncpy(raead.geniv, aead->geniv ?: "", sizeof(raead.geniv)); raead.blocksize = alg->cra_blocksize; raead.maxauthsize = aead->maxauthsize; @@ -203,8 +202,8 @@ static int crypto_nivaead_report(struct sk_buff *skb, struct crypto_alg *alg) struct crypto_report_aead raead; struct aead_alg *aead = &alg->cra_aead; - snprintf(raead.type, CRYPTO_MAX_ALG_NAME, "%s", "nivaead"); - snprintf(raead.geniv, CRYPTO_MAX_ALG_NAME, "%s", aead->geniv); + strncpy(raead.type, "nivaead", sizeof(raead.type)); + strncpy(raead.geniv, aead->geniv, sizeof(raead.geniv)); raead.blocksize = alg->cra_blocksize; raead.maxauthsize = aead->maxauthsize; diff --git a/crypto/ahash.c b/crypto/ahash.c index 3887856c2dd6..793a27f2493e 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -404,7 +404,7 @@ static int crypto_ahash_report(struct sk_buff *skb, struct crypto_alg *alg) { struct crypto_report_hash rhash; - snprintf(rhash.type, CRYPTO_MAX_ALG_NAME, "%s", "ahash"); + strncpy(rhash.type, "ahash", sizeof(rhash.type)); rhash.blocksize = alg->cra_blocksize; rhash.digestsize = __crypto_hash_alg_common(alg)->digestsize; diff --git a/crypto/blkcipher.c b/crypto/blkcipher.c index e9e7244d5ef5..a79e7e9ab86e 100644 --- a/crypto/blkcipher.c +++ b/crypto/blkcipher.c @@ -499,9 +499,9 @@ static int crypto_blkcipher_report(struct sk_buff *skb, struct crypto_alg *alg) { struct crypto_report_blkcipher rblkcipher; - snprintf(rblkcipher.type, CRYPTO_MAX_ALG_NAME, "%s", "blkcipher"); - snprintf(rblkcipher.geniv, CRYPTO_MAX_ALG_NAME, "%s", - alg->cra_blkcipher.geniv ?: ""); + strncpy(rblkcipher.type, "blkcipher", sizeof(rblkcipher.type)); + strncpy(rblkcipher.geniv, alg->cra_blkcipher.geniv ?: "", + sizeof(rblkcipher.geniv)); rblkcipher.blocksize = alg->cra_blocksize; rblkcipher.min_keysize = alg->cra_blkcipher.min_keysize; diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index 35d700a97d79..f6d9baf77f0a 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -75,7 +75,7 @@ static int crypto_report_cipher(struct sk_buff *skb, struct crypto_alg *alg) { struct crypto_report_cipher rcipher; - snprintf(rcipher.type, CRYPTO_MAX_ALG_NAME, "%s", "cipher"); + strncpy(rcipher.type, "cipher", sizeof(rcipher.type)); rcipher.blocksize = alg->cra_blocksize; rcipher.min_keysize = alg->cra_cipher.cia_min_keysize; @@ -94,8 +94,7 @@ static int crypto_report_comp(struct sk_buff *skb, struct crypto_alg *alg) { struct crypto_report_comp rcomp; - snprintf(rcomp.type, CRYPTO_MAX_ALG_NAME, "%s", "compression"); - + strncpy(rcomp.type, "compression", sizeof(rcomp.type)); if (nla_put(skb, CRYPTOCFGA_REPORT_COMPRESS, sizeof(struct crypto_report_comp), &rcomp)) goto nla_put_failure; @@ -108,12 +107,14 @@ nla_put_failure: static int crypto_report_one(struct crypto_alg *alg, struct crypto_user_alg *ualg, struct sk_buff *skb) { - memcpy(&ualg->cru_name, &alg->cra_name, sizeof(ualg->cru_name)); - memcpy(&ualg->cru_driver_name, &alg->cra_driver_name, - sizeof(ualg->cru_driver_name)); - memcpy(&ualg->cru_module_name, module_name(alg->cra_module), - CRYPTO_MAX_ALG_NAME); + strncpy(ualg->cru_name, alg->cra_name, sizeof(ualg->cru_name)); + strncpy(ualg->cru_driver_name, alg->cra_driver_name, + sizeof(ualg->cru_driver_name)); + strncpy(ualg->cru_module_name, module_name(alg->cra_module), + sizeof(ualg->cru_module_name)); + ualg->cru_type = 0; + ualg->cru_mask = 0; ualg->cru_flags = alg->cra_flags; ualg->cru_refcnt = atomic_read(&alg->cra_refcnt); @@ -122,8 +123,7 @@ static int crypto_report_one(struct crypto_alg *alg, if (alg->cra_flags & CRYPTO_ALG_LARVAL) { struct crypto_report_larval rl; - snprintf(rl.type, CRYPTO_MAX_ALG_NAME, "%s", "larval"); - + strncpy(rl.type, "larval", sizeof(rl.type)); if (nla_put(skb, CRYPTOCFGA_REPORT_LARVAL, sizeof(struct crypto_report_larval), &rl)) goto nla_put_failure; diff --git a/crypto/pcompress.c b/crypto/pcompress.c index 04e083ff5373..7140fe70c7af 100644 --- a/crypto/pcompress.c +++ b/crypto/pcompress.c @@ -53,8 +53,7 @@ static int crypto_pcomp_report(struct sk_buff *skb, struct crypto_alg *alg) { struct crypto_report_comp rpcomp; - snprintf(rpcomp.type, CRYPTO_MAX_ALG_NAME, "%s", "pcomp"); - + strncpy(rpcomp.type, "pcomp", sizeof(rpcomp.type)); if (nla_put(skb, CRYPTOCFGA_REPORT_COMPRESS, sizeof(struct crypto_report_comp), &rpcomp)) goto nla_put_failure; diff --git a/crypto/rng.c b/crypto/rng.c index f3b7894dec00..e0a25c2456de 100644 --- a/crypto/rng.c +++ b/crypto/rng.c @@ -65,7 +65,7 @@ static int crypto_rng_report(struct sk_buff *skb, struct crypto_alg *alg) { struct crypto_report_rng rrng; - snprintf(rrng.type, CRYPTO_MAX_ALG_NAME, "%s", "rng"); + strncpy(rrng.type, "rng", sizeof(rrng.type)); rrng.seedsize = alg->cra_rng.seedsize; diff --git a/crypto/shash.c b/crypto/shash.c index f426330f1017..929058a68561 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -530,7 +530,8 @@ static int crypto_shash_report(struct sk_buff *skb, struct crypto_alg *alg) struct crypto_report_hash rhash; struct shash_alg *salg = __crypto_shash_alg(alg); - snprintf(rhash.type, CRYPTO_MAX_ALG_NAME, "%s", "shash"); + strncpy(rhash.type, "shash", sizeof(rhash.type)); + rhash.blocksize = alg->cra_blocksize; rhash.digestsize = salg->digestsize; From e336ed9647b06e3bb52995dbc51101cbdf39f2a2 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Tue, 5 Feb 2013 18:19:14 +0100 Subject: [PATCH 3055/4607] crypto: user - fix empty string test in report API The current test for empty strings fails because it is testing the address of a field, not a pointer. So the test will always be true. Test the first character in the string to not be null instead. Signed-off-by: Mathias Krause Cc: Steffen Klassert Signed-off-by: Herbert Xu --- crypto/crypto_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index f6d9baf77f0a..423a267022f4 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -196,7 +196,7 @@ static int crypto_report(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, struct crypto_dump_info info; int err; - if (!p->cru_driver_name) + if (!p->cru_driver_name[0]) return -EINVAL; alg = crypto_alg_match(p, 1); From 8fd61d34226014fe7886babfca6f45a7eff89d25 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Tue, 5 Feb 2013 18:19:15 +0100 Subject: [PATCH 3056/4607] crypto: user - ensure user supplied strings are nul-terminated To avoid misuse, ensure cru_name and cru_driver_name are always nul-terminated strings. Signed-off-by: Mathias Krause Signed-off-by: Herbert Xu --- crypto/crypto_user.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index 423a267022f4..dfd511fb39ee 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -30,6 +30,8 @@ #include "internal.h" +#define null_terminated(x) (strnlen(x, sizeof(x)) < sizeof(x)) + static DEFINE_MUTEX(crypto_cfg_mutex); /* The crypto netlink socket */ @@ -196,6 +198,9 @@ static int crypto_report(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, struct crypto_dump_info info; int err; + if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name)) + return -EINVAL; + if (!p->cru_driver_name[0]) return -EINVAL; @@ -260,6 +265,9 @@ static int crypto_update_alg(struct sk_buff *skb, struct nlmsghdr *nlh, struct nlattr *priority = attrs[CRYPTOCFGA_PRIORITY_VAL]; LIST_HEAD(list); + if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name)) + return -EINVAL; + if (priority && !strlen(p->cru_driver_name)) return -EINVAL; @@ -287,6 +295,9 @@ static int crypto_del_alg(struct sk_buff *skb, struct nlmsghdr *nlh, struct crypto_alg *alg; struct crypto_user_alg *p = nlmsg_data(nlh); + if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name)) + return -EINVAL; + alg = crypto_alg_match(p, 1); if (!alg) return -ENOENT; @@ -368,6 +379,9 @@ static int crypto_add_alg(struct sk_buff *skb, struct nlmsghdr *nlh, struct crypto_user_alg *p = nlmsg_data(nlh); struct nlattr *priority = attrs[CRYPTOCFGA_PRIORITY_VAL]; + if (!null_terminated(p->cru_name) || !null_terminated(p->cru_driver_name)) + return -EINVAL; + if (strlen(p->cru_driver_name)) exact = 1; From 62dc989921df2a98d1a73aacd085abe941cb9828 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 19 Feb 2013 02:24:26 +0200 Subject: [PATCH 3057/4607] kbuild: Fix missing '\n' for NEW symbols in yes "" | make oldconfig >conf.new According to Documentation/kbuild/kconfig.txt, the commands: yes "" | make oldconfig >conf.new grep "(NEW)" conf.new should list the new config symbols with their default values. However, currently there is no line break after each new symbol. When kconfig is interactive the user will type a new-line at this point, but when non-interactive kconfig must print it. Signed-off-by: Ben Hutchings Reference: http://bugs.debian.org/636029 [regid23@nt1.in: Adjusted Ben's work to apply cleanly to this tree] Reported-and-tested-by: Regid Ichira Reviewed-by: Jonathan Nieder Signed-off-by: Michal Marek --- scripts/kconfig/conf.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 4da3b4adfad2..e39fcd8143ea 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -36,6 +36,7 @@ enum input_mode { } input_mode = oldaskconfig; static int indent = 1; +static int tty_stdio; static int valid_stdin = 1; static int sync_kconfig; static int conf_cnt; @@ -108,6 +109,8 @@ static int conf_askvalue(struct symbol *sym, const char *def) case oldaskconfig: fflush(stdout); xfgets(line, 128, stdin); + if (!tty_stdio) + printf("\n"); return 1; default: break; @@ -495,6 +498,8 @@ int main(int ac, char **av) bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); + tty_stdio = isatty(0) && isatty(1) && isatty(2); + while ((opt = getopt_long(ac, av, "", long_opts, NULL)) != -1) { input_mode = (enum input_mode)opt; switch (opt) { @@ -621,7 +626,7 @@ int main(int ac, char **av) return 1; } } - valid_stdin = isatty(0) && isatty(1) && isatty(2); + valid_stdin = tty_stdio; } switch (input_mode) { From e664e8c098aef984dcd5f17e8a5c8fad2cdd8406 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 15 Feb 2013 15:01:06 -0700 Subject: [PATCH 3058/4607] iommu/tegra: assume CONFIG_OF in gart driver Tegra only supports, and always enables, device tree. Remove all ifdefs for DT support from the driver. Signed-off-by: Stephen Warren Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index 8219f1d596ee..86437575f94d 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -430,13 +430,11 @@ const struct dev_pm_ops tegra_gart_pm_ops = { .resume = tegra_gart_resume, }; -#ifdef CONFIG_OF static struct of_device_id tegra_gart_of_match[] = { { .compatible = "nvidia,tegra20-gart", }, { }, }; MODULE_DEVICE_TABLE(of, tegra_gart_of_match); -#endif static struct platform_driver tegra_gart_driver = { .probe = tegra_gart_probe, @@ -445,7 +443,7 @@ static struct platform_driver tegra_gart_driver = { .owner = THIS_MODULE, .name = "tegra-gart", .pm = &tegra_gart_pm_ops, - .of_match_table = of_match_ptr(tegra_gart_of_match), + .of_match_table = tegra_gart_of_match, }, }; From 573f4145024598990d7191523f86a6d5233be751 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 15 Feb 2013 15:01:07 -0700 Subject: [PATCH 3059/4607] iommu/tegra: assume CONFIG_OF in SMMU driver Tegra only supports, and always enables, device tree. Remove all ifdefs for DT support from the driver. Signed-off-by: Stephen Warren Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 117427e6004e..8b1d9f758076 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -1267,13 +1267,11 @@ const struct dev_pm_ops tegra_smmu_pm_ops = { .resume = tegra_smmu_resume, }; -#ifdef CONFIG_OF static struct of_device_id tegra_smmu_of_match[] = { { .compatible = "nvidia,tegra30-smmu", }, { }, }; MODULE_DEVICE_TABLE(of, tegra_smmu_of_match); -#endif static struct platform_driver tegra_smmu_driver = { .probe = tegra_smmu_probe, @@ -1282,7 +1280,7 @@ static struct platform_driver tegra_smmu_driver = { .owner = THIS_MODULE, .name = "tegra-smmu", .pm = &tegra_smmu_pm_ops, - .of_match_table = of_match_ptr(tegra_smmu_of_match), + .of_match_table = tegra_smmu_of_match, }, }; From 9d1ad66e3eae0faf3f19a618da74b4c377474845 Mon Sep 17 00:00:00 2001 From: Shlomo Pongratz Date: Tue, 19 Feb 2013 15:40:22 +0000 Subject: [PATCH 3060/4607] IPoIB: Fix ipoib_neigh hashing to use the correct daddr octets The hash function introduced in commit b63b70d877 ("IPoIB: Use a private hash table for path lookup in xmit path") was designd to use the 3 octets of the IPoIB HW address that holds the remote QPN. However, this currently isn't the case on little-endian machines, because the the code there uses the flags part (octet[0]) and not the last octet of the QPN (octet[3]). Fix this. The fix caused a checkpatch warning on line over 80 characters, to solve that changed the name of the temp variable that holds the daddr. Signed-off-by: Shlomo Pongratz Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/ulp/ipoib/ipoib.h | 2 ++ drivers/infiniband/ulp/ipoib/ipoib_main.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index 07ca6fd5546b..a7ac0977cb5e 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -117,6 +117,8 @@ enum { #define IPOIB_OP_CM (0) #endif +#define IPOIB_QPN_MASK ((__force u32) cpu_to_be32(0xFFFFFF)) + /* structs */ struct ipoib_header { diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 6fdc9e78da0d..a68628e4197a 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -844,10 +844,10 @@ static u32 ipoib_addr_hash(struct ipoib_neigh_hash *htbl, u8 *daddr) * different subnets. */ /* qpn octets[1:4) & port GUID octets[12:20) */ - u32 *daddr_32 = (u32 *) daddr; + u32 *d32 = (u32 *) daddr; u32 hv; - hv = jhash_3words(daddr_32[3], daddr_32[4], 0xFFFFFF & daddr_32[0], 0); + hv = jhash_3words(d32[3], d32[4], IPOIB_QPN_MASK & d32[0], 0); return hv & htbl->mask; } From 4b48680b5572ee78834c276548e16ac5316908cb Mon Sep 17 00:00:00 2001 From: Yan Burman Date: Tue, 19 Feb 2013 15:40:23 +0000 Subject: [PATCH 3061/4607] IPoIB: Add version and firmware info to ethtool reporting Implement version info as well as report firmware version and bus info of the underlying IB HW device. Signed-off-by: Yan Burman Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/ulp/ipoib/ipoib.h | 2 ++ drivers/infiniband/ulp/ipoib/ipoib_ethtool.c | 19 ++++++++++++++++++- drivers/infiniband/ulp/ipoib/ipoib_main.c | 5 +++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index a7ac0977cb5e..eb71aaa26a9a 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -762,4 +762,6 @@ extern int ipoib_debug_level; #define IPOIB_QPN(ha) (be32_to_cpup((__be32 *) ha) & 0xffffff) +extern const char ipoib_driver_version[]; + #endif /* _IPOIB_H */ diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c index 29bc7b5724ac..c4b3940845e6 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c @@ -39,7 +39,24 @@ static void ipoib_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { - strncpy(drvinfo->driver, "ipoib", sizeof(drvinfo->driver) - 1); + struct ipoib_dev_priv *priv = netdev_priv(netdev); + struct ib_device_attr *attr; + + attr = kmalloc(sizeof(*attr), GFP_KERNEL); + if (attr && !ib_query_device(priv->ca, attr)) + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%d.%d.%d", (int)(attr->fw_ver >> 32), + (int)(attr->fw_ver >> 16) & 0xffff, + (int)attr->fw_ver & 0xffff); + kfree(attr); + + strlcpy(drvinfo->bus_info, dev_name(priv->ca->dma_device), + sizeof(drvinfo->bus_info)); + + strlcpy(drvinfo->version, ipoib_driver_version, + sizeof(drvinfo->version)); + + strlcpy(drvinfo->driver, "ib_ipoib", sizeof(drvinfo->driver)); } static int ipoib_get_coalesce(struct net_device *dev, diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index a68628e4197a..4fe44eba2f9f 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -49,9 +49,14 @@ #include #include +#define DRV_VERSION "1.0.0" + +const char ipoib_driver_version[] = DRV_VERSION; + MODULE_AUTHOR("Roland Dreier"); MODULE_DESCRIPTION("IP-over-InfiniBand net driver"); MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(DRV_VERSION); int ipoib_sendq_size __read_mostly = IPOIB_TX_RING_SIZE; int ipoib_recvq_size __read_mostly = IPOIB_RX_RING_SIZE; From 5a2815f03c0fd5a091c95af93b7f1a17a971ac20 Mon Sep 17 00:00:00 2001 From: Itai Garbi Date: Tue, 19 Feb 2013 15:40:24 +0000 Subject: [PATCH 3062/4607] IPoIB: Don't attempt to release resources on error flow If the ipoib client info isn't found on the _remove_one callback, we must not attempt to scan the returned null list. Found by Coverity. Signed-off-by: Itai Garbi Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/ulp/ipoib/ipoib_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 4fe44eba2f9f..66d6da90982f 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1693,6 +1693,8 @@ static void ipoib_remove_one(struct ib_device *device) return; dev_list = ib_get_client_data(device, &ipoib_client); + if (!dev_list) + return; list_for_each_entry_safe(priv, tmp, dev_list, list) { ib_unregister_event_handler(&priv->event_handler); From 1c374741d03ff753c2e9bf15325e0fe0b3905ead Mon Sep 17 00:00:00 2001 From: Harninder Rai Date: Tue, 19 Feb 2013 14:43:58 +0530 Subject: [PATCH 3063/4607] powerpc/85xx: bsc9131 - Correct typo in SDHC device node BSC9131RDB doesn't have SDHC enabled. As a result of this typo, the node was not getting disabled from the device tree which was leading to linux hang during bootup Signed-off-by: Harninder Rai Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/bsc9131rdb.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/dts/bsc9131rdb.dtsi b/arch/powerpc/boot/dts/bsc9131rdb.dtsi index 638adda2c218..9e6c01339ccc 100644 --- a/arch/powerpc/boot/dts/bsc9131rdb.dtsi +++ b/arch/powerpc/boot/dts/bsc9131rdb.dtsi @@ -126,7 +126,7 @@ }; }; - sdhci@2e000 { + sdhc@2e000 { status = "disabled"; }; From 12c7e8f62de546bff9f8ffa5a03e0ad292bcf17d Mon Sep 17 00:00:00 2001 From: Harninder Rai Date: Tue, 19 Feb 2013 14:44:21 +0530 Subject: [PATCH 3064/4607] powerpc/85xx: l2sram - Add compatible string for BSC9131 platform Signed-off-by: Harninder Rai Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_85xx_l2ctlr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c b/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c index 8cf93f029e17..afc2dbf37011 100644 --- a/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c +++ b/arch/powerpc/sysdev/fsl_85xx_l2ctlr.c @@ -203,6 +203,7 @@ static struct of_device_id mpc85xx_l2ctlr_of_match[] = { { .compatible = "fsl,p1024-l2-cache-controller",}, { .compatible = "fsl,p1015-l2-cache-controller",}, { .compatible = "fsl,p1010-l2-cache-controller",}, + { .compatible = "fsl,bsc9131-l2-cache-controller",}, {}, }; From 52d3d06e706bdde3d6c5c386deb065c3b4c51618 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 19 Feb 2013 19:33:12 +0100 Subject: [PATCH 3065/4607] x86, cpu, amd: Fix WC+ workaround for older virtual hosts The WC+ workaround for F10h introduces a new MSR and kvm host #GPs on accesses to unknown MSRs if paravirt is not compiled in. Use the exception-handling MSR accessors so as not to break 3.8 and later guests booting on older hosts. Remove a redundant family check while at it. Cc: Gleb Natapov Cc: Boris Ostrovsky Signed-off-by: Borislav Petkov Link: http://lkml.kernel.org/r/1361298793-31834-1-git-send-email-bp@alien8.de Signed-off-by: H. Peter Anvin --- arch/x86/kernel/cpu/amd.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 721ef3208eb5..163af4a91d09 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -723,12 +723,14 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) * performance degradation for certain nested-paging guests. * Prevent this conversion by clearing bit 24 in * MSR_AMD64_BU_CFG2. + * + * NOTE: we want to use the _safe accessors so as not to #GP kvm + * guests on older kvm hosts. */ - if (c->x86 == 0x10) { - rdmsrl(MSR_AMD64_BU_CFG2, value); - value &= ~(1ULL << 24); - wrmsrl(MSR_AMD64_BU_CFG2, value); - } + + rdmsrl_safe(MSR_AMD64_BU_CFG2, &value); + value &= ~(1ULL << 24); + wrmsrl_safe(MSR_AMD64_BU_CFG2, value); } rdmsr_safe(MSR_AMD64_PATCH_LEVEL, &c->microcode, &dummy); From 2e32b7190641a184b8510d3e342400473ff1ab60 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 19 Feb 2013 19:33:13 +0100 Subject: [PATCH 3066/4607] x86, kvm: Add MSR_AMD64_BU_CFG2 to the list of ignored MSRs The "x86, AMD: Enable WC+ memory type on family 10 processors" patch currently in -tip added a workaround for AMD F10h CPUs which #GPs my guest when booted in kvm. This is because it accesses MSR_AMD64_BU_CFG2 which is not currently ignored by kvm. Do that because this MSR is only baremetal-relevant anyway. While at it, move the ignored MSRs at the beginning of kvm_set_msr_common so that we exit then and there. Acked-by: Gleb Natapov Cc: Boris Ostrovsky Cc: Andre Przywara Cc: Marcelo Tosatti Signed-off-by: Borislav Petkov Link: http://lkml.kernel.org/r/1361298793-31834-2-git-send-email-bp@alien8.de Signed-off-by: H. Peter Anvin --- arch/x86/kvm/x86.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c243b81e3c74..37040079cd6b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1881,6 +1881,14 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) u64 data = msr_info->data; switch (msr) { + case MSR_AMD64_NB_CFG: + case MSR_IA32_UCODE_REV: + case MSR_IA32_UCODE_WRITE: + case MSR_VM_HSAVE_PA: + case MSR_AMD64_PATCH_LOADER: + case MSR_AMD64_BU_CFG2: + break; + case MSR_EFER: return set_efer(vcpu, data); case MSR_K7_HWCR: @@ -1900,8 +1908,6 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return 1; } break; - case MSR_AMD64_NB_CFG: - break; case MSR_IA32_DEBUGCTLMSR: if (!data) { /* We support the non-activated case already */ @@ -1914,11 +1920,6 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) vcpu_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTLMSR 0x%llx, nop\n", __func__, data); break; - case MSR_IA32_UCODE_REV: - case MSR_IA32_UCODE_WRITE: - case MSR_VM_HSAVE_PA: - case MSR_AMD64_PATCH_LOADER: - break; case 0x200 ... 0x2ff: return set_msr_mtrr(vcpu, msr, data); case MSR_IA32_APICBASE: @@ -2253,6 +2254,7 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) case MSR_K8_INT_PENDING_MSG: case MSR_AMD64_NB_CFG: case MSR_FAM10H_MMIO_CONF_BASE: + case MSR_AMD64_BU_CFG2: data = 0; break; case MSR_P6_PERFCTR0: From 9d092603cc306ee6edfe917bf9ab8beb5f32d7bc Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Thu, 20 Dec 2012 10:31:11 +0000 Subject: [PATCH 3067/4607] xen-blkback: do not leak mode property "be->mode" is obtained from xenbus_read(), which does a kmalloc() for the message body. The short string is never released, so do it along with freeing "be" itself, and make sure the string isn't kept when backend_changed() doesn't complete successfully (which made it desirable to slightly re-structure that function, so that the error cleanup can be done in one place). Reported-by: Olaf Hering CC: stable@vger.kernel.org Signed-off-by: Jan Beulich Signed-off-by: Konrad Rzeszutek Wilk --- drivers/block/xen-blkback/xenbus.c | 49 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c index 63980722db41..5e237f630c47 100644 --- a/drivers/block/xen-blkback/xenbus.c +++ b/drivers/block/xen-blkback/xenbus.c @@ -367,6 +367,7 @@ static int xen_blkbk_remove(struct xenbus_device *dev) be->blkif = NULL; } + kfree(be->mode); kfree(be); dev_set_drvdata(&dev->dev, NULL); return 0; @@ -502,6 +503,7 @@ static void backend_changed(struct xenbus_watch *watch, = container_of(watch, struct backend_info, backend_watch); struct xenbus_device *dev = be->dev; int cdrom = 0; + unsigned long handle; char *device_type; DPRINTK(""); @@ -521,10 +523,10 @@ static void backend_changed(struct xenbus_watch *watch, return; } - if ((be->major || be->minor) && - ((be->major != major) || (be->minor != minor))) { - pr_warn(DRV_PFX "changing physical device (from %x:%x to %x:%x) not supported.\n", - be->major, be->minor, major, minor); + if (be->major | be->minor) { + if (be->major != major || be->minor != minor) + pr_warn(DRV_PFX "changing physical device (from %x:%x to %x:%x) not supported.\n", + be->major, be->minor, major, minor); return; } @@ -542,36 +544,33 @@ static void backend_changed(struct xenbus_watch *watch, kfree(device_type); } - if (be->major == 0 && be->minor == 0) { - /* Front end dir is a number, which is used as the handle. */ + /* Front end dir is a number, which is used as the handle. */ + err = strict_strtoul(strrchr(dev->otherend, '/') + 1, 0, &handle); + if (err) + return; - char *p = strrchr(dev->otherend, '/') + 1; - long handle; - err = strict_strtoul(p, 0, &handle); - if (err) - return; + be->major = major; + be->minor = minor; - be->major = major; - be->minor = minor; - - err = xen_vbd_create(be->blkif, handle, major, minor, - (NULL == strchr(be->mode, 'w')), cdrom); - if (err) { - be->major = 0; - be->minor = 0; - xenbus_dev_fatal(dev, err, "creating vbd structure"); - return; - } + err = xen_vbd_create(be->blkif, handle, major, minor, + !strchr(be->mode, 'w'), cdrom); + if (err) + xenbus_dev_fatal(dev, err, "creating vbd structure"); + else { err = xenvbd_sysfs_addif(dev); if (err) { xen_vbd_free(&be->blkif->vbd); - be->major = 0; - be->minor = 0; xenbus_dev_fatal(dev, err, "creating sysfs entries"); - return; } + } + if (err) { + kfree(be->mode); + be->mode = NULL; + be->major = 0; + be->minor = 0; + } else { /* We're potentially connected now */ xen_update_blkif_status(be->blkif); } From 01c681d4c70d64cb72142a2823f27c4146a02e63 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Wed, 16 Jan 2013 11:36:23 -0500 Subject: [PATCH 3068/4607] xen/blkback: Don't trust the handle from the frontend. The 'handle' is the device that the request is from. For the life-time of the ring we copy it from a request to a response so that the frontend is not surprised by it. But we do not need it - when we start processing I/Os we have our own 'struct phys_req' which has only most essential information about the request. In fact the 'vbd_translate' ends up over-writing the preq.dev with a value from the backend. This assignment of preq.dev with the 'handle' value is superfluous so lets not do it. Cc: stable@vger.kernel.org Acked-by: Jan Beulich Acked-by: Ian Campbell Signed-off-by: Konrad Rzeszutek Wilk --- drivers/block/xen-blkback/blkback.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 5ac841ff6cc7..1966a7cfd8ce 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -879,7 +879,6 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, goto fail_response; } - preq.dev = req->u.rw.handle; preq.sector_number = req->u.rw.sector_number; preq.nr_sects = 0; From f84adf4921ae3115502f44ff467b04bf2f88cf04 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Wed, 13 Feb 2013 13:01:55 -0500 Subject: [PATCH 3069/4607] xen-blkfront: drop the use of llist_for_each_entry_safe Replace llist_for_each_entry_safe with a while loop. llist_for_each_entry_safe can trigger a bug in GCC 4.1, so it's best to remove it and use a while loop and do the deletion manually. Specifically this bug can be triggered by hot-unplugging a disk, either by doing xm block-detach or by save/restore cycle. BUG: unable to handle kernel paging request at fffffffffffffff0 IP: [] blkif_free+0x63/0x130 [xen_blkfront] The crash call trace is: ... bad_area_nosemaphore+0x13/0x20 do_page_fault+0x25e/0x4b0 page_fault+0x25/0x30 ? blkif_free+0x63/0x130 [xen_blkfront] blkfront_resume+0x46/0xa0 [xen_blkfront] xenbus_dev_resume+0x6c/0x140 pm_op+0x192/0x1b0 device_resume+0x82/0x1e0 dpm_resume+0xc9/0x1a0 dpm_resume_end+0x15/0x30 do_suspend+0x117/0x1e0 When drilling down to the assembler code, on newer GCC it does .L29: cmpq $-16, %r12 #, persistent_gnt check je .L30 #, out of the loop .L25: ... code in the loop testq %r13, %r13 # n je .L29 #, back to the top of the loop cmpq $-16, %r12 #, persistent_gnt check movq 16(%r12), %r13 # .node.next, n jne .L25 #, back to the top of the loop .L30: While on GCC 4.1, it is: L78: ... code in the loop testq %r13, %r13 # n je .L78 #, back to the top of the loop movq 16(%rbx), %r13 # .node.next, n jmp .L78 #, back to the top of the loop Which basically means that the exit loop condition instead of being: &(pos)->member != NULL; is: ; which makes the loop unbound. Since xen-blkfront is the only user of the llist_for_each_entry_safe macro remove it from llist.h. Orabug: 16263164 CC: stable@vger.kernel.org Signed-off-by: Konrad Rzeszutek Wilk --- drivers/block/xen-blkfront.c | 13 ++++++++++--- include/linux/llist.h | 25 ------------------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 11043c18ac5a..c3dae2e0f290 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -791,7 +791,7 @@ static void blkif_restart_queue(struct work_struct *work) static void blkif_free(struct blkfront_info *info, int suspend) { struct llist_node *all_gnts; - struct grant *persistent_gnt; + struct grant *persistent_gnt, *tmp; struct llist_node *n; /* Prevent new requests being issued until we fix things up. */ @@ -805,10 +805,17 @@ static void blkif_free(struct blkfront_info *info, int suspend) /* Remove all persistent grants */ if (info->persistent_gnts_c) { all_gnts = llist_del_all(&info->persistent_gnts); - llist_for_each_entry_safe(persistent_gnt, n, all_gnts, node) { + persistent_gnt = llist_entry(all_gnts, typeof(*(persistent_gnt)), node); + while (persistent_gnt) { gnttab_end_foreign_access(persistent_gnt->gref, 0, 0UL); __free_page(pfn_to_page(persistent_gnt->pfn)); - kfree(persistent_gnt); + tmp = persistent_gnt; + n = persistent_gnt->node.next; + if (n) + persistent_gnt = llist_entry(n, typeof(*(persistent_gnt)), node); + else + persistent_gnt = NULL; + kfree(tmp); } info->persistent_gnts_c = 0; } diff --git a/include/linux/llist.h b/include/linux/llist.h index d0ab98f73d38..a5199f6d0e82 100644 --- a/include/linux/llist.h +++ b/include/linux/llist.h @@ -124,31 +124,6 @@ static inline void init_llist_head(struct llist_head *list) &(pos)->member != NULL; \ (pos) = llist_entry((pos)->member.next, typeof(*(pos)), member)) -/** - * llist_for_each_entry_safe - iterate safely against remove over some entries - * of lock-less list of given type. - * @pos: the type * to use as a loop cursor. - * @n: another type * to use as a temporary storage. - * @node: the fist entry of deleted list entries. - * @member: the name of the llist_node with the struct. - * - * In general, some entries of the lock-less list can be traversed - * safely only after being removed from list, so start with an entry - * instead of list head. This variant allows removal of entries - * as we iterate. - * - * If being used on entries deleted from lock-less list directly, the - * traverse order is from the newest to the oldest added entry. If - * you want to traverse from the oldest to the newest, you must - * reverse the order by yourself before traversing. - */ -#define llist_for_each_entry_safe(pos, n, node, member) \ - for ((pos) = llist_entry((node), typeof(*(pos)), member), \ - (n) = (pos)->member.next; \ - &(pos)->member != NULL; \ - (pos) = llist_entry(n, typeof(*(pos)), member), \ - (n) = (&(pos)->member != NULL) ? (pos)->member.next : NULL) - /** * llist_empty - tests whether a lock-less list is empty * @head: the list to test From 087ffecdaa1875cc683a7a5bc0695b3ebfce3bad Mon Sep 17 00:00:00 2001 From: Roger Pau Monne Date: Thu, 14 Feb 2013 11:12:09 +0100 Subject: [PATCH 3070/4607] xen-blkback: use balloon pages for persistent grants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With current persistent grants implementation we are not freeing the persistent grants after we disconnect the device. Since grant map operations change the mfn of the allocated page, and we can no longer pass it to __free_page without setting the mfn to a sane value, use balloon grant pages instead, as the gntdev device does. Signed-off-by: Roger Pau Monné Cc: stable@vger.kernel.org Cc: Konrad Rzeszutek Wilk Signed-off-by: Konrad Rzeszutek Wilk --- drivers/block/xen-blkback/blkback.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 1966a7cfd8ce..de1f319f7bd7 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -46,6 +46,7 @@ #include #include #include +#include #include "common.h" /* @@ -239,6 +240,7 @@ static void free_persistent_gnts(struct rb_root *root, unsigned int num) ret = gnttab_unmap_refs(unmap, NULL, pages, segs_to_unmap); BUG_ON(ret); + free_xenballooned_pages(segs_to_unmap, pages); segs_to_unmap = 0; } @@ -527,8 +529,8 @@ static int xen_blkbk_map(struct blkif_request *req, GFP_KERNEL); if (!persistent_gnt) return -ENOMEM; - persistent_gnt->page = alloc_page(GFP_KERNEL); - if (!persistent_gnt->page) { + if (alloc_xenballooned_pages(1, &persistent_gnt->page, + false)) { kfree(persistent_gnt); return -ENOMEM; } From 1f953b0dbc2549318afcc0a70af5542dffbce34a Mon Sep 17 00:00:00 2001 From: Bhanu Prakash Gollapudi Date: Mon, 28 Jan 2013 11:43:03 -0800 Subject: [PATCH 3071/4607] libfcoe: Check for unusable FCFs before looking for conflicting FCFs When there are multiple FCFs in the fabric, and one of them becomes unavailable, the fabric name for the unavailable FCF becomes 0 along with FIP_FL_AVAIL getting reset. In this case, FCF selection logic does not select any FCF as it first checks for conflicting FCFs (since fabric name is 0, it fails the condition), instead of first checking if it is usable or not. Fix it by first checking if FCF is usable and skip that FCF, and go to the next one in the list to check if it can be selected. Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: Robert Love --- drivers/scsi/fcoe/fcoe_ctlr.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index aff3c44a1cdc..7aca9fd8a11c 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -1559,15 +1559,6 @@ static struct fcoe_fcf *fcoe_ctlr_select(struct fcoe_ctlr *fip) fcf->fabric_name, fcf->vfid, fcf->fcf_mac, fcf->fc_map, fcoe_ctlr_mtu_valid(fcf), fcf->flogi_sent, fcf->pri); - if (fcf->fabric_name != first->fabric_name || - fcf->vfid != first->vfid || - fcf->fc_map != first->fc_map) { - LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, " - "or FC-MAP\n"); - return NULL; - } - if (fcf->flogi_sent) - continue; if (!fcoe_ctlr_fcf_usable(fcf)) { LIBFCOE_FIP_DBG(fip, "FCF for fab %16.16llx " "map %x %svalid %savailable\n", @@ -1577,6 +1568,15 @@ static struct fcoe_fcf *fcoe_ctlr_select(struct fcoe_ctlr *fip) "" : "un"); continue; } + if (fcf->fabric_name != first->fabric_name || + fcf->vfid != first->vfid || + fcf->fc_map != first->fc_map) { + LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, " + "or FC-MAP\n"); + return NULL; + } + if (fcf->flogi_sent) + continue; if (!best || fcf->pri < best->pri || best->flogi_sent) best = fcf; } From 8fcb6c4748822f8f594536e47b9b0701b6c33e7a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 19 Feb 2013 11:18:04 +0100 Subject: [PATCH 3072/4607] drm/cma-helper: fixup compilation /me grabs a few brown paper bags So it looks like I've broken compilation in commit 6aed8ec3f76a22217c9ae183d32b1aa990bed069 Author: Daniel Vetter Date: Sun Jan 20 17:32:21 2013 +0100 drm: review locking for drm_fb_helper_restore_fbdev_mode Fix it up again. v2: Only deref fbdev_cma once we're sure it's non-NULL, noticed by Thierry Reding. Reported-by: Wu Fengguang Signed-off-by: Daniel Vetter Reviewed-by: Thierry Reding Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fb_cma_helper.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index e851658f87d5..1c8549dae99a 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -377,10 +377,13 @@ EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); */ void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) { - drm_modeset_lock_all(dev); - if (fbdev_cma) + if (fbdev_cma) { + struct drm_device *dev = fbdev_cma->fb_helper.dev; + + drm_modeset_lock_all(dev); drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper); - drm_modeset_unlock_all(dev); + drm_modeset_unlock_all(dev); + } } EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode); From 196e077dc165a307efbd9e7569f81bbdbcf18f65 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 15 Feb 2013 13:36:27 -0200 Subject: [PATCH 3073/4607] drm: don't add inferred modes for monitors that don't support them If bit 0 of the features byte (0x18) is set to 0, then, according to the EDID spec, "the display is non-continuous frequency (multi-mode) and is only specified to accept the video timing formats that are listed in Base EDID and certain Extension Blocks". For more information, please see the EDID spec, check the notes of the table that explains the "Feature Support" byte (18h) and also the notes on the tables of the section that explains "Display Range Limits & Additional Timing Description Definition (tag #FDh)". Cc: stable@vger.kernel.org Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=45729 Reviewed-by: Alex Deucher Reviewed-by: Adam Jackson Signed-off-by: Paulo Zanoni Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index e1aca7b53987..67aa0dd68250 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2054,7 +2054,8 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) num_modes += add_cvt_modes(connector, edid); num_modes += add_standard_modes(connector, edid); num_modes += add_established_modes(connector, edid); - num_modes += add_inferred_modes(connector, edid); + if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) + num_modes += add_inferred_modes(connector, edid); num_modes += add_cea_modes(connector, edid); if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) From 35f8badc1cf652381fa3f82c1fbea39f4dbe87fd Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 15 Feb 2013 21:21:37 +0100 Subject: [PATCH 3074/4607] drm: Don't set the plane->fb to NULL on successfull set_plane MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to clear the local variable to get the refcounting right (since the reference drm_mode_setplane holds is transferred to the plane->fb pointer). But should be done _after_ we update the pointer. Breakage introduced in commit 6c2a75325c800de286166c693e0cd33c3a1c5ec8 Author: Daniel Vetter Date: Tue Dec 11 00:59:24 2012 +0100 drm: refcounting for sprite framebuffers Reported-by: Jesse Barnes Cc: Rob Clark Reviewed-by: Jesse Barnes Reviewed-by: Ville Syrjälä Reviewed-by: Thierry Reding Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f17077307c65..e7471b0880b7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1996,9 +1996,9 @@ int drm_mode_setplane(struct drm_device *dev, void *data, plane_req->src_w, plane_req->src_h); if (!ret) { old_fb = plane->fb; - fb = NULL; plane->crtc = crtc; plane->fb = fb; + fb = NULL; } drm_modeset_unlock_all(dev); From 16ea975eac671fa40a78594a116a44fef8e3f4a9 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 8 Jan 2013 15:04:28 -0600 Subject: [PATCH 3075/4607] drm/tilcdc: add TI LCD Controller DRM driver (v4) A simple DRM/KMS driver for the TI LCD Controller found in various smaller TI parts (AM33xx, OMAPL138, etc). This driver uses the CMA helpers. Currently only the TFP410 DVI encoder is supported (tested with beaglebone + DVI cape). There are also various LCD displays, for which support can be added (as I get hw to test on), and an external i2c HDMI encoder found on some boards. The display controller supports a single CRTC. And the encoder+ connector are split out into sub-devices. Depending on which LCD or external encoder is actually present, the appropriate output module(s) will be loaded. v1: original v2: fix fb refcnting and few other cleanups v3: get +/- vsync/hsync from timings rather than panel-info, add option DT max-bandwidth field so driver doesn't attempt to pick a display mode with too high memory bandwidth, and other small cleanups v4: remove some unneeded stuff from panel-info struct, properly set high bits for hfp/hsw/hbp for rev 2, add DT bindings docs Signed-off-by: Rob Clark Reviewed-by: Daniel Vetter Tested-by: Koen Kooi --- .../devicetree/bindings/drm/tilcdc/tfp410.txt | 21 + .../devicetree/bindings/drm/tilcdc/tilcdc.txt | 21 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/tilcdc/Kconfig | 10 + drivers/gpu/drm/tilcdc/Makefile | 8 + drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 602 +++++++++++++++++ drivers/gpu/drm/tilcdc/tilcdc_drv.c | 605 ++++++++++++++++++ drivers/gpu/drm/tilcdc/tilcdc_drv.h | 150 +++++ drivers/gpu/drm/tilcdc/tilcdc_regs.h | 154 +++++ drivers/gpu/drm/tilcdc/tilcdc_tfp410.c | 419 ++++++++++++ drivers/gpu/drm/tilcdc/tilcdc_tfp410.h | 26 + 12 files changed, 2019 insertions(+) create mode 100644 Documentation/devicetree/bindings/drm/tilcdc/tfp410.txt create mode 100644 Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt create mode 100644 drivers/gpu/drm/tilcdc/Kconfig create mode 100644 drivers/gpu/drm/tilcdc/Makefile create mode 100644 drivers/gpu/drm/tilcdc/tilcdc_crtc.c create mode 100644 drivers/gpu/drm/tilcdc/tilcdc_drv.c create mode 100644 drivers/gpu/drm/tilcdc/tilcdc_drv.h create mode 100644 drivers/gpu/drm/tilcdc/tilcdc_regs.h create mode 100644 drivers/gpu/drm/tilcdc/tilcdc_tfp410.c create mode 100644 drivers/gpu/drm/tilcdc/tilcdc_tfp410.h diff --git a/Documentation/devicetree/bindings/drm/tilcdc/tfp410.txt b/Documentation/devicetree/bindings/drm/tilcdc/tfp410.txt new file mode 100644 index 000000000000..a58ae7756fc6 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/tilcdc/tfp410.txt @@ -0,0 +1,21 @@ +Device-Tree bindings for tilcdc DRM TFP410 output driver + +Required properties: + - compatible: value should be "ti,tilcdc,tfp410". + - i2c: the phandle for the i2c device to use for DDC + +Recommended properties: + - pinctrl-names, pinctrl-0: the pincontrol settings to configure + muxing properly for pins that connect to TFP410 device + - powerdn-gpio: the powerdown GPIO, pulled low to power down the + TFP410 device (for DPMS_OFF) + +Example: + + dvicape { + compatible = "ti,tilcdc,tfp410"; + i2c = <&i2c2>; + pinctrl-names = "default"; + pinctrl-0 = <&bone_dvi_cape_dvi_00A1_pins>; + powerdn-gpio = <&gpio2 31 0>; + }; diff --git a/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt b/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt new file mode 100644 index 000000000000..e5f130159ae1 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/tilcdc/tilcdc.txt @@ -0,0 +1,21 @@ +Device-Tree bindings for tilcdc DRM driver + +Required properties: + - compatible: value should be "ti,am33xx-tilcdc". + - interrupts: the interrupt number + - reg: base address and size of the LCDC device + +Recommended properties: + - interrupt-parent: the phandle for the interrupt controller that + services interrupts for this device. + - ti,hwmods: Name of the hwmod associated to the LCDC + +Example: + + fb: fb@4830e000 { + compatible = "ti,am33xx-tilcdc"; + reg = <0x4830e000 0x1000>; + interrupt-parent = <&intc>; + interrupts = <36>; + ti,hwmods = "lcdc"; + }; diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ed9e3af17b31..ba74e75f7d54 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -215,3 +215,5 @@ source "drivers/gpu/drm/cirrus/Kconfig" source "drivers/gpu/drm/shmobile/Kconfig" source "drivers/gpu/drm/tegra/Kconfig" + +source "drivers/gpu/drm/tilcdc/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 6f58c81cfcbc..3af934dffe68 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -50,4 +50,5 @@ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-$(CONFIG_DRM_TEGRA) += tegra/ +obj-$(CONFIG_DRM_TILCDC) += tilcdc/ obj-y += i2c/ diff --git a/drivers/gpu/drm/tilcdc/Kconfig b/drivers/gpu/drm/tilcdc/Kconfig new file mode 100644 index 000000000000..ee9b592d59eb --- /dev/null +++ b/drivers/gpu/drm/tilcdc/Kconfig @@ -0,0 +1,10 @@ +config DRM_TILCDC + tristate "DRM Support for TI LCDC Display Controller" + depends on DRM && OF + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + help + Choose this option if you have an TI SoC with LCDC display + controller, for example AM33xx in beagle-bone, DA8xx, or + OMAP-L1xx. This driver replaces the FB_DA8XX fbdev driver. diff --git a/drivers/gpu/drm/tilcdc/Makefile b/drivers/gpu/drm/tilcdc/Makefile new file mode 100644 index 000000000000..1359cc29cd81 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/Makefile @@ -0,0 +1,8 @@ +ccflags-y := -Iinclude/drm -Werror + +tilcdc-y := \ + tilcdc_crtc.o \ + tilcdc_tfp410.o \ + tilcdc_drv.o + +obj-$(CONFIG_DRM_TILCDC) += tilcdc.o diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c new file mode 100644 index 000000000000..5dd3c7d031d5 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * 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, see . + */ + +#include + +#include "tilcdc_drv.h" +#include "tilcdc_regs.h" + +struct tilcdc_crtc { + struct drm_crtc base; + + const struct tilcdc_panel_info *info; + uint32_t dirty; + dma_addr_t start, end; + struct drm_pending_vblank_event *event; + int dpms; + wait_queue_head_t frame_done_wq; + bool frame_done; + + /* fb currently set to scanout 0/1: */ + struct drm_framebuffer *scanout[2]; + + /* for deferred fb unref's: */ + DECLARE_KFIFO_PTR(unref_fifo, struct drm_framebuffer *); + struct work_struct work; +}; +#define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) + +static void unref_worker(struct work_struct *work) +{ + struct tilcdc_crtc *tilcdc_crtc = container_of(work, struct tilcdc_crtc, work); + struct drm_device *dev = tilcdc_crtc->base.dev; + struct drm_framebuffer *fb; + + mutex_lock(&dev->mode_config.mutex); + while (kfifo_get(&tilcdc_crtc->unref_fifo, &fb)) + drm_framebuffer_unreference(fb); + mutex_unlock(&dev->mode_config.mutex); +} + +static void set_scanout(struct drm_crtc *crtc, int n) +{ + static const uint32_t base_reg[] = { + LCDC_DMA_FB_BASE_ADDR_0_REG, LCDC_DMA_FB_BASE_ADDR_1_REG, + }; + static const uint32_t ceil_reg[] = { + LCDC_DMA_FB_CEILING_ADDR_0_REG, LCDC_DMA_FB_CEILING_ADDR_1_REG, + }; + static const uint32_t stat[] = { + LCDC_END_OF_FRAME0, LCDC_END_OF_FRAME1, + }; + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct drm_device *dev = crtc->dev; + + pm_runtime_get_sync(dev->dev); + tilcdc_write(dev, base_reg[n], tilcdc_crtc->start); + tilcdc_write(dev, ceil_reg[n], tilcdc_crtc->end); + if (tilcdc_crtc->scanout[n]) { + if (kfifo_put(&tilcdc_crtc->unref_fifo, + (const struct drm_framebuffer **)&tilcdc_crtc->scanout[n])) { + struct tilcdc_drm_private *priv = dev->dev_private; + queue_work(priv->wq, &tilcdc_crtc->work); + } else { + dev_err(dev->dev, "unref fifo full!\n"); + drm_framebuffer_unreference(tilcdc_crtc->scanout[n]); + } + } + tilcdc_crtc->scanout[n] = crtc->fb; + drm_framebuffer_reference(tilcdc_crtc->scanout[n]); + tilcdc_crtc->dirty &= ~stat[n]; + pm_runtime_put_sync(dev->dev); +} + +static void update_scanout(struct drm_crtc *crtc) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_framebuffer *fb = crtc->fb; + struct drm_gem_cma_object *gem; + unsigned int depth, bpp; + + drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); + gem = drm_fb_cma_get_gem_obj(fb, 0); + + tilcdc_crtc->start = gem->paddr + fb->offsets[0] + + (crtc->y * fb->pitches[0]) + (crtc->x * bpp/8); + + tilcdc_crtc->end = tilcdc_crtc->start + + (crtc->mode.vdisplay * fb->pitches[0]); + + if (tilcdc_crtc->dpms == DRM_MODE_DPMS_ON) { + /* already enabled, so just mark the frames that need + * updating and they will be updated on vblank: + */ + tilcdc_crtc->dirty |= LCDC_END_OF_FRAME0 | LCDC_END_OF_FRAME1; + drm_vblank_get(dev, 0); + } else { + /* not enabled yet, so update registers immediately: */ + set_scanout(crtc, 0); + set_scanout(crtc, 1); + } +} + +static void start(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct tilcdc_drm_private *priv = dev->dev_private; + + if (priv->rev == 2) { + tilcdc_set(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); + msleep(1); + tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); + msleep(1); + } + + tilcdc_set(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); + tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_PALETTE_LOAD_MODE(DATA_ONLY)); + tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); +} + +static void stop(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + + tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); +} + +static void tilcdc_crtc_destroy(struct drm_crtc *crtc) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + + WARN_ON(tilcdc_crtc->dpms == DRM_MODE_DPMS_ON); + + drm_crtc_cleanup(crtc); + WARN_ON(!kfifo_is_empty(&tilcdc_crtc->unref_fifo)); + kfifo_free(&tilcdc_crtc->unref_fifo); + kfree(tilcdc_crtc); +} + +static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct drm_device *dev = crtc->dev; + + if (tilcdc_crtc->event) { + dev_err(dev->dev, "already pending page flip!\n"); + return -EBUSY; + } + + crtc->fb = fb; + tilcdc_crtc->event = event; + update_scanout(crtc); + + return 0; +} + +static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct tilcdc_drm_private *priv = dev->dev_private; + + /* we really only care about on or off: */ + if (mode != DRM_MODE_DPMS_ON) + mode = DRM_MODE_DPMS_OFF; + + if (tilcdc_crtc->dpms == mode) + return; + + tilcdc_crtc->dpms = mode; + + pm_runtime_get_sync(dev->dev); + + if (mode == DRM_MODE_DPMS_ON) { + pm_runtime_forbid(dev->dev); + start(crtc); + } else { + tilcdc_crtc->frame_done = false; + stop(crtc); + + /* if necessary wait for framedone irq which will still come + * before putting things to sleep.. + */ + if (priv->rev == 2) { + int ret = wait_event_timeout( + tilcdc_crtc->frame_done_wq, + tilcdc_crtc->frame_done, + msecs_to_jiffies(50)); + if (ret == 0) + dev_err(dev->dev, "timeout waiting for framedone\n"); + } + pm_runtime_allow(dev->dev); + } + + pm_runtime_put_sync(dev->dev); +} + +static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void tilcdc_crtc_prepare(struct drm_crtc *crtc) +{ + tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void tilcdc_crtc_commit(struct drm_crtc *crtc) +{ + tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON); +} + +static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct tilcdc_drm_private *priv = dev->dev_private; + const struct tilcdc_panel_info *info = tilcdc_crtc->info; + uint32_t reg, hbp, hfp, hsw, vbp, vfp, vsw; + int ret; + + ret = tilcdc_crtc_mode_valid(crtc, mode); + if (WARN_ON(ret)) + return ret; + + if (WARN_ON(!info)) + return -EINVAL; + + pm_runtime_get_sync(dev->dev); + + /* Configure the Burst Size and fifo threshold of DMA: */ + reg = tilcdc_read(dev, LCDC_DMA_CTRL_REG) & ~0x00000770; + switch (info->dma_burst_sz) { + case 1: + reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_1); + break; + case 2: + reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_2); + break; + case 4: + reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_4); + break; + case 8: + reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_8); + break; + case 16: + reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_16); + break; + default: + return -EINVAL; + } + reg |= (info->fifo_th << 8); + tilcdc_write(dev, LCDC_DMA_CTRL_REG, reg); + + /* Configure timings: */ + hbp = mode->htotal - mode->hsync_end; + hfp = mode->hsync_start - mode->hdisplay; + hsw = mode->hsync_end - mode->hsync_start; + vbp = mode->vtotal - mode->vsync_end; + vfp = mode->vsync_start - mode->vdisplay; + vsw = mode->vsync_end - mode->vsync_start; + + DBG("%dx%d, hbp=%u, hfp=%u, hsw=%u, vbp=%u, vfp=%u, vsw=%u", + mode->hdisplay, mode->vdisplay, hbp, hfp, hsw, vbp, vfp, vsw); + + /* Configure the AC Bias Period and Number of Transitions per Interrupt: */ + reg = tilcdc_read(dev, LCDC_RASTER_TIMING_2_REG) & ~0x000fff00; + reg |= LCDC_AC_BIAS_FREQUENCY(info->ac_bias) | + LCDC_AC_BIAS_TRANSITIONS_PER_INT(info->ac_bias_intrpt); + if (priv->rev == 2) { + reg |= (hfp & 0x300) >> 8; + reg |= (hbp & 0x300) >> 4; + reg |= (hsw & 0x3c0) << 21; + } + tilcdc_write(dev, LCDC_RASTER_TIMING_2_REG, reg); + + reg = (((mode->hdisplay >> 4) - 1) << 4) | + ((hbp & 0xff) << 24) | + ((hfp & 0xff) << 16) | + ((hsw & 0x3f) << 10); + if (priv->rev == 2) + reg |= (((mode->hdisplay >> 4) - 1) & 0x40) >> 3; + tilcdc_write(dev, LCDC_RASTER_TIMING_0_REG, reg); + + reg = ((mode->vdisplay - 1) & 0x3ff) | + ((vbp & 0xff) << 24) | + ((vfp & 0xff) << 16) | + ((vsw & 0x3f) << 10); + tilcdc_write(dev, LCDC_RASTER_TIMING_1_REG, reg); + + /* Configure display type: */ + reg = tilcdc_read(dev, LCDC_RASTER_CTRL_REG) & + ~(LCDC_TFT_MODE | LCDC_MONO_8BIT_MODE | LCDC_MONOCHROME_MODE | + LCDC_V2_TFT_24BPP_MODE | LCDC_V2_TFT_24BPP_UNPACK | 0x000ff000); + reg |= LCDC_TFT_MODE; /* no monochrome/passive support */ + if (info->tft_alt_mode) + reg |= LCDC_TFT_ALT_ENABLE; + if (priv->rev == 2) { + unsigned int depth, bpp; + + drm_fb_get_bpp_depth(crtc->fb->pixel_format, &depth, &bpp); + switch (bpp) { + case 16: + break; + case 32: + reg |= LCDC_V2_TFT_24BPP_UNPACK; + /* fallthrough */ + case 24: + reg |= LCDC_V2_TFT_24BPP_MODE; + break; + default: + dev_err(dev->dev, "invalid pixel format\n"); + return -EINVAL; + } + } + reg |= info->fdd < 12; + tilcdc_write(dev, LCDC_RASTER_CTRL_REG, reg); + + if (info->invert_pxl_clk) + tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_PIXEL_CLOCK); + else + tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_PIXEL_CLOCK); + + if (info->sync_ctrl) + tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_CTRL); + else + tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_CTRL); + + if (info->sync_edge) + tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_EDGE); + else + tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_EDGE); + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC); + else + tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC); + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_VSYNC); + else + tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_VSYNC); + + if (info->raster_order) + tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); + else + tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); + + + update_scanout(crtc); + tilcdc_crtc_update_clk(crtc); + + pm_runtime_put_sync(dev->dev); + + return 0; +} + +static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + update_scanout(crtc); + return 0; +} + +static void tilcdc_crtc_load_lut(struct drm_crtc *crtc) +{ +} + +static const struct drm_crtc_funcs tilcdc_crtc_funcs = { + .destroy = tilcdc_crtc_destroy, + .set_config = drm_crtc_helper_set_config, + .page_flip = tilcdc_crtc_page_flip, +}; + +static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = { + .dpms = tilcdc_crtc_dpms, + .mode_fixup = tilcdc_crtc_mode_fixup, + .prepare = tilcdc_crtc_prepare, + .commit = tilcdc_crtc_commit, + .mode_set = tilcdc_crtc_mode_set, + .mode_set_base = tilcdc_crtc_mode_set_base, + .load_lut = tilcdc_crtc_load_lut, +}; + +int tilcdc_crtc_max_width(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct tilcdc_drm_private *priv = dev->dev_private; + int max_width = 0; + + if (priv->rev == 1) + max_width = 1024; + else if (priv->rev == 2) + max_width = 2048; + + return max_width; +} + +int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode) +{ + struct tilcdc_drm_private *priv = crtc->dev->dev_private; + unsigned int bandwidth; + + if (mode->hdisplay > tilcdc_crtc_max_width(crtc)) + return MODE_VIRTUAL_X; + + /* width must be multiple of 16 */ + if (mode->hdisplay & 0xf) + return MODE_VIRTUAL_X; + + if (mode->vdisplay > 2048) + return MODE_VIRTUAL_Y; + + /* filter out modes that would require too much memory bandwidth: */ + bandwidth = mode->hdisplay * mode->vdisplay * drm_mode_vrefresh(mode); + if (bandwidth > priv->max_bandwidth) + return MODE_BAD; + + return MODE_OK; +} + +void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, + const struct tilcdc_panel_info *info) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + tilcdc_crtc->info = info; +} + +void tilcdc_crtc_update_clk(struct drm_crtc *crtc) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct tilcdc_drm_private *priv = dev->dev_private; + int dpms = tilcdc_crtc->dpms; + unsigned int lcd_clk, div; + int ret; + + pm_runtime_get_sync(dev->dev); + + if (dpms == DRM_MODE_DPMS_ON) + tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + + /* in raster mode, minimum divisor is 2: */ + ret = clk_set_rate(priv->disp_clk, crtc->mode.clock * 1000 * 2); + if (ret) { + dev_err(dev->dev, "failed to set display clock rate to: %d\n", + crtc->mode.clock); + goto out; + } + + lcd_clk = clk_get_rate(priv->clk); + div = lcd_clk / (crtc->mode.clock * 1000); + + DBG("lcd_clk=%u, mode clock=%d, div=%u", lcd_clk, crtc->mode.clock, div); + DBG("fck=%lu, dpll_disp_ck=%lu", clk_get_rate(priv->clk), clk_get_rate(priv->disp_clk)); + + /* Configure the LCD clock divisor. */ + tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(div) | + LCDC_RASTER_MODE); + + if (priv->rev == 2) + tilcdc_set(dev, LCDC_CLK_ENABLE_REG, + LCDC_V2_DMA_CLK_EN | LCDC_V2_LIDD_CLK_EN | + LCDC_V2_CORE_CLK_EN); + + if (dpms == DRM_MODE_DPMS_ON) + tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON); + +out: + pm_runtime_put_sync(dev->dev); +} + +irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct tilcdc_drm_private *priv = dev->dev_private; + uint32_t stat = tilcdc_read_irqstatus(dev); + + if ((stat & LCDC_SYNC_LOST) && (stat & LCDC_FIFO_UNDERFLOW)) { + stop(crtc); + dev_err(dev->dev, "error: %08x\n", stat); + tilcdc_clear_irqstatus(dev, stat); + start(crtc); + } else if (stat & LCDC_PL_LOAD_DONE) { + tilcdc_clear_irqstatus(dev, stat); + } else { + struct drm_pending_vblank_event *event; + unsigned long flags; + uint32_t dirty = tilcdc_crtc->dirty & stat; + + tilcdc_clear_irqstatus(dev, stat); + + if (dirty & LCDC_END_OF_FRAME0) + set_scanout(crtc, 0); + + if (dirty & LCDC_END_OF_FRAME1) + set_scanout(crtc, 1); + + drm_handle_vblank(dev, 0); + + spin_lock_irqsave(&dev->event_lock, flags); + event = tilcdc_crtc->event; + tilcdc_crtc->event = NULL; + if (event) + drm_send_vblank_event(dev, 0, event); + spin_unlock_irqrestore(&dev->event_lock, flags); + + if (dirty && !tilcdc_crtc->dirty) + drm_vblank_put(dev, 0); + } + + if (priv->rev == 2) { + if (stat & LCDC_FRAME_DONE) { + tilcdc_crtc->frame_done = true; + wake_up(&tilcdc_crtc->frame_done_wq); + } + tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0); + } + + return IRQ_HANDLED; +} + +void tilcdc_crtc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) +{ + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); + struct drm_pending_vblank_event *event; + struct drm_device *dev = crtc->dev; + unsigned long flags; + + /* Destroy the pending vertical blanking event associated with the + * pending page flip, if any, and disable vertical blanking interrupts. + */ + spin_lock_irqsave(&dev->event_lock, flags); + event = tilcdc_crtc->event; + if (event && event->base.file_priv == file) { + tilcdc_crtc->event = NULL; + event->base.destroy(&event->base); + drm_vblank_put(dev, 0); + } + spin_unlock_irqrestore(&dev->event_lock, flags); +} + +struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) +{ + struct tilcdc_crtc *tilcdc_crtc; + struct drm_crtc *crtc; + int ret; + + tilcdc_crtc = kzalloc(sizeof(*tilcdc_crtc), GFP_KERNEL); + if (!tilcdc_crtc) { + dev_err(dev->dev, "allocation failed\n"); + return NULL; + } + + crtc = &tilcdc_crtc->base; + + tilcdc_crtc->dpms = DRM_MODE_DPMS_OFF; + init_waitqueue_head(&tilcdc_crtc->frame_done_wq); + + ret = kfifo_alloc(&tilcdc_crtc->unref_fifo, 16, GFP_KERNEL); + if (ret) { + dev_err(dev->dev, "could not allocate unref FIFO\n"); + goto fail; + } + + INIT_WORK(&tilcdc_crtc->work, unref_worker); + + ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs); + if (ret < 0) + goto fail; + + drm_crtc_helper_add(crtc, &tilcdc_crtc_helper_funcs); + + return crtc; + +fail: + tilcdc_crtc_destroy(crtc); + return NULL; +} diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c new file mode 100644 index 000000000000..f6defbfaaffb --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -0,0 +1,605 @@ +/* + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * 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, see . + */ + +/* LCDC DRM driver, based on da8xx-fb */ + +#include "tilcdc_drv.h" +#include "tilcdc_regs.h" +#include "tilcdc_tfp410.h" + +#include "drm_fb_helper.h" + +static LIST_HEAD(module_list); + +void tilcdc_module_init(struct tilcdc_module *mod, const char *name, + const struct tilcdc_module_ops *funcs) +{ + mod->name = name; + mod->funcs = funcs; + INIT_LIST_HEAD(&mod->list); + list_add(&mod->list, &module_list); +} + +void tilcdc_module_cleanup(struct tilcdc_module *mod) +{ + list_del(&mod->list); +} + +static struct of_device_id tilcdc_of_match[]; + +static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev, + struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) +{ + return drm_fb_cma_create(dev, file_priv, mode_cmd); +} + +static void tilcdc_fb_output_poll_changed(struct drm_device *dev) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + if (priv->fbdev) + drm_fbdev_cma_hotplug_event(priv->fbdev); +} + +static const struct drm_mode_config_funcs mode_config_funcs = { + .fb_create = tilcdc_fb_create, + .output_poll_changed = tilcdc_fb_output_poll_changed, +}; + +static int modeset_init(struct drm_device *dev) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + struct tilcdc_module *mod; + + drm_mode_config_init(dev); + + priv->crtc = tilcdc_crtc_create(dev); + + list_for_each_entry(mod, &module_list, list) { + DBG("loading module: %s", mod->name); + mod->funcs->modeset_init(mod, dev); + } + + if ((priv->num_encoders = 0) || (priv->num_connectors == 0)) { + /* oh nos! */ + dev_err(dev->dev, "no encoders/connectors found\n"); + return -ENXIO; + } + + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc); + dev->mode_config.max_height = 2048; + dev->mode_config.funcs = &mode_config_funcs; + + return 0; +} + +#ifdef CONFIG_CPU_FREQ +static int cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct tilcdc_drm_private *priv = container_of(nb, + struct tilcdc_drm_private, freq_transition); + if (val == CPUFREQ_POSTCHANGE) { + if (priv->lcd_fck_rate != clk_get_rate(priv->clk)) { + priv->lcd_fck_rate = clk_get_rate(priv->clk); + tilcdc_crtc_update_clk(priv->crtc); + } + } + + return 0; +} +#endif + +/* + * DRM operations: + */ + +static int tilcdc_unload(struct drm_device *dev) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + struct tilcdc_module *mod, *cur; + + drm_kms_helper_poll_fini(dev); + drm_mode_config_cleanup(dev); + drm_vblank_cleanup(dev); + + pm_runtime_get_sync(dev->dev); + drm_irq_uninstall(dev); + pm_runtime_put_sync(dev->dev); + +#ifdef CONFIG_CPU_FREQ + cpufreq_unregister_notifier(&priv->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +#endif + + if (priv->clk) + clk_put(priv->clk); + + if (priv->mmio) + iounmap(priv->mmio); + + flush_workqueue(priv->wq); + destroy_workqueue(priv->wq); + + dev->dev_private = NULL; + + pm_runtime_disable(dev->dev); + + list_for_each_entry_safe(mod, cur, &module_list, list) { + DBG("destroying module: %s", mod->name); + mod->funcs->destroy(mod); + } + + kfree(priv); + + return 0; +} + +static int tilcdc_load(struct drm_device *dev, unsigned long flags) +{ + struct platform_device *pdev = dev->platformdev; + struct device_node *node = pdev->dev.of_node; + struct tilcdc_drm_private *priv; + struct resource *res; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev->dev, "failed to allocate private data\n"); + return -ENOMEM; + } + + dev->dev_private = priv; + + priv->wq = alloc_ordered_workqueue("tilcdc", 0); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev->dev, "failed to get memory resource\n"); + ret = -EINVAL; + goto fail; + } + + priv->mmio = ioremap_nocache(res->start, resource_size(res)); + if (!priv->mmio) { + dev_err(dev->dev, "failed to ioremap\n"); + ret = -ENOMEM; + goto fail; + } + + priv->clk = clk_get(dev->dev, "fck"); + if (IS_ERR(priv->clk)) { + dev_err(dev->dev, "failed to get functional clock\n"); + ret = -ENODEV; + goto fail; + } + + priv->disp_clk = clk_get(dev->dev, "dpll_disp_ck"); + if (IS_ERR(priv->clk)) { + dev_err(dev->dev, "failed to get display clock\n"); + ret = -ENODEV; + goto fail; + } + +#ifdef CONFIG_CPU_FREQ + priv->lcd_fck_rate = clk_get_rate(priv->clk); + priv->freq_transition.notifier_call = cpufreq_transition; + ret = cpufreq_register_notifier(&priv->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); + if (ret) { + dev_err(dev->dev, "failed to register cpufreq notifier\n"); + goto fail; + } +#endif + + if (of_property_read_u32(node, "max-bandwidth", &priv->max_bandwidth)) + priv->max_bandwidth = 1280 * 1024 * 60; + + pm_runtime_enable(dev->dev); + + /* Determine LCD IP Version */ + pm_runtime_get_sync(dev->dev); + switch (tilcdc_read(dev, LCDC_PID_REG)) { + case 0x4c100102: + priv->rev = 1; + break; + case 0x4f200800: + case 0x4f201000: + priv->rev = 2; + break; + default: + dev_warn(dev->dev, "Unknown PID Reg value 0x%08x, " + "defaulting to LCD revision 1\n", + tilcdc_read(dev, LCDC_PID_REG)); + priv->rev = 1; + break; + } + + pm_runtime_put_sync(dev->dev); + + ret = modeset_init(dev); + if (ret < 0) { + dev_err(dev->dev, "failed to initialize mode setting\n"); + goto fail; + } + + ret = drm_vblank_init(dev, 1); + if (ret < 0) { + dev_err(dev->dev, "failed to initialize vblank\n"); + goto fail; + } + + pm_runtime_get_sync(dev->dev); + ret = drm_irq_install(dev); + pm_runtime_put_sync(dev->dev); + if (ret < 0) { + dev_err(dev->dev, "failed to install IRQ handler\n"); + goto fail; + } + + platform_set_drvdata(pdev, dev); + + priv->fbdev = drm_fbdev_cma_init(dev, 16, + dev->mode_config.num_crtc, + dev->mode_config.num_connector); + + drm_kms_helper_poll_init(dev); + + return 0; + +fail: + tilcdc_unload(dev); + return ret; +} + +static void tilcdc_preclose(struct drm_device *dev, struct drm_file *file) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + + tilcdc_crtc_cancel_page_flip(priv->crtc, file); +} + +static void tilcdc_lastclose(struct drm_device *dev) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + drm_fbdev_cma_restore_mode(priv->fbdev); +} + +static irqreturn_t tilcdc_irq(DRM_IRQ_ARGS) +{ + struct drm_device *dev = arg; + struct tilcdc_drm_private *priv = dev->dev_private; + return tilcdc_crtc_irq(priv->crtc); +} + +static void tilcdc_irq_preinstall(struct drm_device *dev) +{ + tilcdc_clear_irqstatus(dev, 0xffffffff); +} + +static int tilcdc_irq_postinstall(struct drm_device *dev) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + + /* enable FIFO underflow irq: */ + if (priv->rev == 1) { + tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_UNDERFLOW_INT_ENA); + } else { + tilcdc_set(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_UNDERFLOW_INT_ENA); + } + + return 0; +} + +static void tilcdc_irq_uninstall(struct drm_device *dev) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + + /* disable irqs that we might have enabled: */ + if (priv->rev == 1) { + tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, + LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA); + tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA); + } else { + tilcdc_clear(dev, LCDC_INT_ENABLE_SET_REG, + LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA | + LCDC_V2_END_OF_FRAME0_INT_ENA | LCDC_V2_END_OF_FRAME1_INT_ENA | + LCDC_FRAME_DONE); + } + +} + +static void enable_vblank(struct drm_device *dev, bool enable) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + u32 reg, mask; + + if (priv->rev == 1) { + reg = LCDC_DMA_CTRL_REG; + mask = LCDC_V1_END_OF_FRAME_INT_ENA; + } else { + reg = LCDC_INT_ENABLE_SET_REG; + mask = LCDC_V2_END_OF_FRAME0_INT_ENA | + LCDC_V2_END_OF_FRAME1_INT_ENA | LCDC_FRAME_DONE; + } + + if (enable) + tilcdc_set(dev, reg, mask); + else + tilcdc_clear(dev, reg, mask); +} + +static int tilcdc_enable_vblank(struct drm_device *dev, int crtc) +{ + enable_vblank(dev, true); + return 0; +} + +static void tilcdc_disable_vblank(struct drm_device *dev, int crtc) +{ + enable_vblank(dev, false); +} + +#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_PM_SLEEP) +static const struct { + const char *name; + uint8_t rev; + uint8_t save; + uint32_t reg; +} registers[] = { +#define REG(rev, save, reg) { #reg, rev, save, reg } + /* exists in revision 1: */ + REG(1, false, LCDC_PID_REG), + REG(1, true, LCDC_CTRL_REG), + REG(1, false, LCDC_STAT_REG), + REG(1, true, LCDC_RASTER_CTRL_REG), + REG(1, true, LCDC_RASTER_TIMING_0_REG), + REG(1, true, LCDC_RASTER_TIMING_1_REG), + REG(1, true, LCDC_RASTER_TIMING_2_REG), + REG(1, true, LCDC_DMA_CTRL_REG), + REG(1, true, LCDC_DMA_FB_BASE_ADDR_0_REG), + REG(1, true, LCDC_DMA_FB_CEILING_ADDR_0_REG), + REG(1, true, LCDC_DMA_FB_BASE_ADDR_1_REG), + REG(1, true, LCDC_DMA_FB_CEILING_ADDR_1_REG), + /* new in revision 2: */ + REG(2, false, LCDC_RAW_STAT_REG), + REG(2, false, LCDC_MASKED_STAT_REG), + REG(2, false, LCDC_INT_ENABLE_SET_REG), + REG(2, false, LCDC_INT_ENABLE_CLR_REG), + REG(2, false, LCDC_END_OF_INT_IND_REG), + REG(2, true, LCDC_CLK_ENABLE_REG), + REG(2, true, LCDC_INT_ENABLE_SET_REG), +#undef REG +}; +#endif + +#ifdef CONFIG_DEBUG_FS +static int tilcdc_regs_show(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct tilcdc_drm_private *priv = dev->dev_private; + unsigned i; + + pm_runtime_get_sync(dev->dev); + + seq_printf(m, "revision: %d\n", priv->rev); + + for (i = 0; i < ARRAY_SIZE(registers); i++) + if (priv->rev >= registers[i].rev) + seq_printf(m, "%s:\t %08x\n", registers[i].name, + tilcdc_read(dev, registers[i].reg)); + + pm_runtime_put_sync(dev->dev); + + return 0; +} + +static int tilcdc_mm_show(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + return drm_mm_dump_table(m, dev->mm_private); +} + +static struct drm_info_list tilcdc_debugfs_list[] = { + { "regs", tilcdc_regs_show, 0 }, + { "mm", tilcdc_mm_show, 0 }, + { "fb", drm_fb_cma_debugfs_show, 0 }, +}; + +static int tilcdc_debugfs_init(struct drm_minor *minor) +{ + struct drm_device *dev = minor->dev; + struct tilcdc_module *mod; + int ret; + + ret = drm_debugfs_create_files(tilcdc_debugfs_list, + ARRAY_SIZE(tilcdc_debugfs_list), + minor->debugfs_root, minor); + + list_for_each_entry(mod, &module_list, list) + if (mod->funcs->debugfs_init) + mod->funcs->debugfs_init(mod, minor); + + if (ret) { + dev_err(dev->dev, "could not install tilcdc_debugfs_list\n"); + return ret; + } + + return ret; +} + +static void tilcdc_debugfs_cleanup(struct drm_minor *minor) +{ + struct tilcdc_module *mod; + drm_debugfs_remove_files(tilcdc_debugfs_list, + ARRAY_SIZE(tilcdc_debugfs_list), minor); + + list_for_each_entry(mod, &module_list, list) + if (mod->funcs->debugfs_cleanup) + mod->funcs->debugfs_cleanup(mod, minor); +} +#endif + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .poll = drm_poll, + .read = drm_read, + .fasync = drm_fasync, + .llseek = no_llseek, + .mmap = drm_gem_cma_mmap, +}; + +static struct drm_driver tilcdc_driver = { + .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, + .load = tilcdc_load, + .unload = tilcdc_unload, + .preclose = tilcdc_preclose, + .lastclose = tilcdc_lastclose, + .irq_handler = tilcdc_irq, + .irq_preinstall = tilcdc_irq_preinstall, + .irq_postinstall = tilcdc_irq_postinstall, + .irq_uninstall = tilcdc_irq_uninstall, + .get_vblank_counter = drm_vblank_count, + .enable_vblank = tilcdc_enable_vblank, + .disable_vblank = tilcdc_disable_vblank, + .gem_free_object = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = drm_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_cma_dumb_destroy, +#ifdef CONFIG_DEBUG_FS + .debugfs_init = tilcdc_debugfs_init, + .debugfs_cleanup = tilcdc_debugfs_cleanup, +#endif + .fops = &fops, + .name = "tilcdc", + .desc = "TI LCD Controller DRM", + .date = "20121205", + .major = 1, + .minor = 0, +}; + +/* + * Power management: + */ + +#ifdef CONFIG_PM_SLEEP +static int tilcdc_pm_suspend(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct tilcdc_drm_private *priv = ddev->dev_private; + unsigned i, n = 0; + + drm_kms_helper_poll_disable(ddev); + + /* Save register state: */ + for (i = 0; i < ARRAY_SIZE(registers); i++) + if (registers[i].save && (priv->rev >= registers[i].rev)) + priv->saved_register[n++] = tilcdc_read(ddev, registers[i].reg); + + return 0; +} + +static int tilcdc_pm_resume(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct tilcdc_drm_private *priv = ddev->dev_private; + unsigned i, n = 0; + + /* Restore register state: */ + for (i = 0; i < ARRAY_SIZE(registers); i++) + if (registers[i].save && (priv->rev >= registers[i].rev)) + tilcdc_write(ddev, registers[i].reg, priv->saved_register[n++]); + + drm_kms_helper_poll_enable(ddev); + + return 0; +} +#endif + +static const struct dev_pm_ops tilcdc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tilcdc_pm_suspend, tilcdc_pm_resume) +}; + +/* + * Platform driver: + */ + +static int tilcdc_pdev_probe(struct platform_device *pdev) +{ + /* bail out early if no DT data: */ + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "device-tree data is missing\n"); + return -ENXIO; + } + + return drm_platform_init(&tilcdc_driver, pdev); +} + +static int tilcdc_pdev_remove(struct platform_device *pdev) +{ + drm_platform_exit(&tilcdc_driver, pdev); + + return 0; +} + +static struct of_device_id tilcdc_of_match[] = { + { .compatible = "ti,am33xx-tilcdc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tilcdc_of_match); + +static struct platform_driver tilcdc_platform_driver = { + .probe = tilcdc_pdev_probe, + .remove = tilcdc_pdev_remove, + .driver = { + .owner = THIS_MODULE, + .name = "tilcdc", + .pm = &tilcdc_pm_ops, + .of_match_table = tilcdc_of_match, + }, +}; + +static int __init tilcdc_drm_init(void) +{ + DBG("init"); + tilcdc_tfp410_init(); + return platform_driver_register(&tilcdc_platform_driver); +} + +static void __exit tilcdc_drm_fini(void) +{ + DBG("fini"); + tilcdc_tfp410_fini(); + platform_driver_unregister(&tilcdc_platform_driver); +} + +module_init(tilcdc_drm_init); +module_exit(tilcdc_drm_fini); + +MODULE_AUTHOR("Rob Clark + * + * 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. + * + * 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, see . + */ + +#ifndef __TILCDC_DRV_H__ +#define __TILCDC_DRV_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct tilcdc_drm_private { + void __iomem *mmio; + + struct clk *disp_clk; /* display dpll */ + struct clk *clk; /* functional clock */ + int rev; /* IP revision */ + + /* don't attempt resolutions w/ higher W * H * Hz: */ + uint32_t max_bandwidth; + + /* register contents saved across suspend/resume: */ + u32 saved_register[12]; + +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; + unsigned int lcd_fck_rate; +#endif + + struct workqueue_struct *wq; + + struct drm_fbdev_cma *fbdev; + + struct drm_crtc *crtc; + + unsigned int num_encoders; + struct drm_encoder *encoders[8]; + + unsigned int num_connectors; + struct drm_connector *connectors[8]; +}; + +/* Sub-module for display. Since we don't know at compile time what panels + * or display adapter(s) might be present (for ex, off chip dvi/tfp410, + * hdmi encoder, various lcd panels), the connector/encoder(s) are split into + * separate drivers. If they are probed and found to be present, they + * register themselves with tilcdc_register_module(). + */ +struct tilcdc_module; + +struct tilcdc_module_ops { + /* create appropriate encoders/connectors: */ + int (*modeset_init)(struct tilcdc_module *mod, struct drm_device *dev); + void (*destroy)(struct tilcdc_module *mod); +#ifdef CONFIG_DEBUG_FS + /* create debugfs nodes (can be NULL): */ + int (*debugfs_init)(struct tilcdc_module *mod, struct drm_minor *minor); + /* cleanup debugfs nodes (can be NULL): */ + void (*debugfs_cleanup)(struct tilcdc_module *mod, struct drm_minor *minor); +#endif +}; + +struct tilcdc_module { + const char *name; + struct list_head list; + const struct tilcdc_module_ops *funcs; +}; + +void tilcdc_module_init(struct tilcdc_module *mod, const char *name, + const struct tilcdc_module_ops *funcs); +void tilcdc_module_cleanup(struct tilcdc_module *mod); + + +/* Panel config that needs to be set in the crtc, but is not coming from + * the mode timings. The display module is expected to call + * tilcdc_crtc_set_panel_info() to set this during modeset. + */ +struct tilcdc_panel_info { + + /* AC Bias Pin Frequency */ + uint32_t ac_bias; + + /* AC Bias Pin Transitions per Interrupt */ + uint32_t ac_bias_intrpt; + + /* DMA burst size */ + uint32_t dma_burst_sz; + + /* Bits per pixel */ + uint32_t bpp; + + /* FIFO DMA Request Delay */ + uint32_t fdd; + + /* TFT Alternative Signal Mapping (Only for active) */ + bool tft_alt_mode; + + /* Invert pixel clock */ + bool invert_pxl_clk; + + /* Horizontal and Vertical Sync Edge: 0=rising 1=falling */ + uint32_t sync_edge; + + /* Horizontal and Vertical Sync: Control: 0=ignore */ + uint32_t sync_ctrl; + + /* Raster Data Order Select: 1=Most-to-least 0=Least-to-most */ + uint32_t raster_order; + + /* DMA FIFO threshold */ + uint32_t fifo_th; +}; + +#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) + +struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev); +void tilcdc_crtc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file); +irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc); +void tilcdc_crtc_update_clk(struct drm_crtc *crtc); +void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, + const struct tilcdc_panel_info *info); +int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode); +int tilcdc_crtc_max_width(struct drm_crtc *crtc); + +#endif /* __TILCDC_DRV_H__ */ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_regs.h b/drivers/gpu/drm/tilcdc/tilcdc_regs.h new file mode 100644 index 000000000000..17fd1b45428a --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_regs.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * 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, see . + */ + +#ifndef __TILCDC_REGS_H__ +#define __TILCDC_REGS_H__ + +/* LCDC register definitions, based on da8xx-fb */ + +#include + +#include "tilcdc_drv.h" + +/* LCDC Status Register */ +#define LCDC_END_OF_FRAME1 BIT(9) +#define LCDC_END_OF_FRAME0 BIT(8) +#define LCDC_PL_LOAD_DONE BIT(6) +#define LCDC_FIFO_UNDERFLOW BIT(5) +#define LCDC_SYNC_LOST BIT(2) +#define LCDC_FRAME_DONE BIT(0) + +/* LCDC DMA Control Register */ +#define LCDC_DMA_BURST_SIZE(x) ((x) << 4) +#define LCDC_DMA_BURST_1 0x0 +#define LCDC_DMA_BURST_2 0x1 +#define LCDC_DMA_BURST_4 0x2 +#define LCDC_DMA_BURST_8 0x3 +#define LCDC_DMA_BURST_16 0x4 +#define LCDC_V1_END_OF_FRAME_INT_ENA BIT(2) +#define LCDC_V2_END_OF_FRAME0_INT_ENA BIT(8) +#define LCDC_V2_END_OF_FRAME1_INT_ENA BIT(9) +#define LCDC_DUAL_FRAME_BUFFER_ENABLE BIT(0) + +/* LCDC Control Register */ +#define LCDC_CLK_DIVISOR(x) ((x) << 8) +#define LCDC_RASTER_MODE 0x01 + +/* LCDC Raster Control Register */ +#define LCDC_PALETTE_LOAD_MODE(x) ((x) << 20) +#define PALETTE_AND_DATA 0x00 +#define PALETTE_ONLY 0x01 +#define DATA_ONLY 0x02 + +#define LCDC_MONO_8BIT_MODE BIT(9) +#define LCDC_RASTER_ORDER BIT(8) +#define LCDC_TFT_MODE BIT(7) +#define LCDC_V1_UNDERFLOW_INT_ENA BIT(6) +#define LCDC_V2_UNDERFLOW_INT_ENA BIT(5) +#define LCDC_V1_PL_INT_ENA BIT(4) +#define LCDC_V2_PL_INT_ENA BIT(6) +#define LCDC_MONOCHROME_MODE BIT(1) +#define LCDC_RASTER_ENABLE BIT(0) +#define LCDC_TFT_ALT_ENABLE BIT(23) +#define LCDC_STN_565_ENABLE BIT(24) +#define LCDC_V2_DMA_CLK_EN BIT(2) +#define LCDC_V2_LIDD_CLK_EN BIT(1) +#define LCDC_V2_CORE_CLK_EN BIT(0) +#define LCDC_V2_LPP_B10 26 +#define LCDC_V2_TFT_24BPP_MODE BIT(25) +#define LCDC_V2_TFT_24BPP_UNPACK BIT(26) + +/* LCDC Raster Timing 2 Register */ +#define LCDC_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) +#define LCDC_AC_BIAS_FREQUENCY(x) ((x) << 8) +#define LCDC_SYNC_CTRL BIT(25) +#define LCDC_SYNC_EDGE BIT(24) +#define LCDC_INVERT_PIXEL_CLOCK BIT(22) +#define LCDC_INVERT_HSYNC BIT(21) +#define LCDC_INVERT_VSYNC BIT(20) + +/* LCDC Block */ +#define LCDC_PID_REG 0x0 +#define LCDC_CTRL_REG 0x4 +#define LCDC_STAT_REG 0x8 +#define LCDC_RASTER_CTRL_REG 0x28 +#define LCDC_RASTER_TIMING_0_REG 0x2c +#define LCDC_RASTER_TIMING_1_REG 0x30 +#define LCDC_RASTER_TIMING_2_REG 0x34 +#define LCDC_DMA_CTRL_REG 0x40 +#define LCDC_DMA_FB_BASE_ADDR_0_REG 0x44 +#define LCDC_DMA_FB_CEILING_ADDR_0_REG 0x48 +#define LCDC_DMA_FB_BASE_ADDR_1_REG 0x4c +#define LCDC_DMA_FB_CEILING_ADDR_1_REG 0x50 + +/* Interrupt Registers available only in Version 2 */ +#define LCDC_RAW_STAT_REG 0x58 +#define LCDC_MASKED_STAT_REG 0x5c +#define LCDC_INT_ENABLE_SET_REG 0x60 +#define LCDC_INT_ENABLE_CLR_REG 0x64 +#define LCDC_END_OF_INT_IND_REG 0x68 + +/* Clock registers available only on Version 2 */ +#define LCDC_CLK_ENABLE_REG 0x6c +#define LCDC_CLK_RESET_REG 0x70 +#define LCDC_CLK_MAIN_RESET BIT(3) + + +/* + * Helpers: + */ + +static inline void tilcdc_write(struct drm_device *dev, u32 reg, u32 data) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + iowrite32(data, priv->mmio + reg); +} + +static inline u32 tilcdc_read(struct drm_device *dev, u32 reg) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + return ioread32(priv->mmio + reg); +} + +static inline void tilcdc_set(struct drm_device *dev, u32 reg, u32 mask) +{ + tilcdc_write(dev, reg, tilcdc_read(dev, reg) | mask); +} + +static inline void tilcdc_clear(struct drm_device *dev, u32 reg, u32 mask) +{ + tilcdc_write(dev, reg, tilcdc_read(dev, reg) & ~mask); +} + +/* the register to read/clear irqstatus differs between v1 and v2 of the IP */ +static inline u32 tilcdc_irqstatus_reg(struct drm_device *dev) +{ + struct tilcdc_drm_private *priv = dev->dev_private; + return (priv->rev == 2) ? LCDC_MASKED_STAT_REG : LCDC_STAT_REG; +} + +static inline u32 tilcdc_read_irqstatus(struct drm_device *dev) +{ + return tilcdc_read(dev, tilcdc_irqstatus_reg(dev)); +} + +static inline void tilcdc_clear_irqstatus(struct drm_device *dev, u32 mask) +{ + tilcdc_write(dev, tilcdc_irqstatus_reg(dev), mask); +} + +#endif /* __TILCDC_REGS_H__ */ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c new file mode 100644 index 000000000000..58d487ba2414 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "tilcdc_drv.h" + +struct tfp410_module { + struct tilcdc_module base; + struct i2c_adapter *i2c; + int gpio; +}; +#define to_tfp410_module(x) container_of(x, struct tfp410_module, base) + + +static const struct tilcdc_panel_info dvi_info = { + .ac_bias = 255, + .ac_bias_intrpt = 0, + .dma_burst_sz = 16, + .bpp = 16, + .fdd = 0x80, + .tft_alt_mode = 0, + .sync_edge = 0, + .sync_ctrl = 1, + .raster_order = 0, +}; + +/* + * Encoder: + */ + +struct tfp410_encoder { + struct drm_encoder base; + struct tfp410_module *mod; + int dpms; +}; +#define to_tfp410_encoder(x) container_of(x, struct tfp410_encoder, base) + + +static void tfp410_encoder_destroy(struct drm_encoder *encoder) +{ + struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder); + drm_encoder_cleanup(encoder); + kfree(tfp410_encoder); +} + +static void tfp410_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder); + + if (tfp410_encoder->dpms == mode) + return; + + if (mode == DRM_MODE_DPMS_ON) { + DBG("Power on"); + gpio_direction_output(tfp410_encoder->mod->gpio, 1); + } else { + DBG("Power off"); + gpio_direction_output(tfp410_encoder->mod->gpio, 0); + } + + tfp410_encoder->dpms = mode; +} + +static bool tfp410_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* nothing needed */ + return true; +} + +static void tfp410_encoder_prepare(struct drm_encoder *encoder) +{ + tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); + tilcdc_crtc_set_panel_info(encoder->crtc, &dvi_info); +} + +static void tfp410_encoder_commit(struct drm_encoder *encoder) +{ + tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static void tfp410_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* nothing needed */ +} + +static const struct drm_encoder_funcs tfp410_encoder_funcs = { + .destroy = tfp410_encoder_destroy, +}; + +static const struct drm_encoder_helper_funcs tfp410_encoder_helper_funcs = { + .dpms = tfp410_encoder_dpms, + .mode_fixup = tfp410_encoder_mode_fixup, + .prepare = tfp410_encoder_prepare, + .commit = tfp410_encoder_commit, + .mode_set = tfp410_encoder_mode_set, +}; + +static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev, + struct tfp410_module *mod) +{ + struct tfp410_encoder *tfp410_encoder; + struct drm_encoder *encoder; + int ret; + + tfp410_encoder = kzalloc(sizeof(*tfp410_encoder), GFP_KERNEL); + if (!tfp410_encoder) { + dev_err(dev->dev, "allocation failed\n"); + return NULL; + } + + tfp410_encoder->dpms = DRM_MODE_DPMS_OFF; + tfp410_encoder->mod = mod; + + encoder = &tfp410_encoder->base; + encoder->possible_crtcs = 1; + + ret = drm_encoder_init(dev, encoder, &tfp410_encoder_funcs, + DRM_MODE_ENCODER_TMDS); + if (ret < 0) + goto fail; + + drm_encoder_helper_add(encoder, &tfp410_encoder_helper_funcs); + + return encoder; + +fail: + tfp410_encoder_destroy(encoder); + return NULL; +} + +/* + * Connector: + */ + +struct tfp410_connector { + struct drm_connector base; + + struct drm_encoder *encoder; /* our connected encoder */ + struct tfp410_module *mod; +}; +#define to_tfp410_connector(x) container_of(x, struct tfp410_connector, base) + + +static void tfp410_connector_destroy(struct drm_connector *connector) +{ + struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); + drm_connector_cleanup(connector); + kfree(tfp410_connector); +} + +static enum drm_connector_status tfp410_connector_detect( + struct drm_connector *connector, + bool force) +{ + struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); + + if (drm_probe_ddc(tfp410_connector->mod->i2c)) + return connector_status_connected; + + return connector_status_unknown; +} + +static int tfp410_connector_get_modes(struct drm_connector *connector) +{ + struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); + struct edid *edid; + int ret = 0; + + edid = drm_get_edid(connector, tfp410_connector->mod->i2c); + + drm_mode_connector_update_edid_property(connector, edid); + + if (edid) { + ret = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + return ret; +} + +static int tfp410_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct tilcdc_drm_private *priv = connector->dev->dev_private; + /* our only constraints are what the crtc can generate: */ + return tilcdc_crtc_mode_valid(priv->crtc, mode); +} + +static struct drm_encoder *tfp410_connector_best_encoder( + struct drm_connector *connector) +{ + struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); + return tfp410_connector->encoder; +} + +static const struct drm_connector_funcs tfp410_connector_funcs = { + .destroy = tfp410_connector_destroy, + .dpms = drm_helper_connector_dpms, + .detect = tfp410_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, +}; + +static const struct drm_connector_helper_funcs tfp410_connector_helper_funcs = { + .get_modes = tfp410_connector_get_modes, + .mode_valid = tfp410_connector_mode_valid, + .best_encoder = tfp410_connector_best_encoder, +}; + +static struct drm_connector *tfp410_connector_create(struct drm_device *dev, + struct tfp410_module *mod, struct drm_encoder *encoder) +{ + struct tfp410_connector *tfp410_connector; + struct drm_connector *connector; + int ret; + + tfp410_connector = kzalloc(sizeof(*tfp410_connector), GFP_KERNEL); + if (!tfp410_connector) { + dev_err(dev->dev, "allocation failed\n"); + return NULL; + } + + tfp410_connector->encoder = encoder; + tfp410_connector->mod = mod; + + connector = &tfp410_connector->base; + + drm_connector_init(dev, connector, &tfp410_connector_funcs, + DRM_MODE_CONNECTOR_DVID); + drm_connector_helper_add(connector, &tfp410_connector_helper_funcs); + + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + ret = drm_mode_connector_attach_encoder(connector, encoder); + if (ret) + goto fail; + + drm_sysfs_connector_add(connector); + + return connector; + +fail: + tfp410_connector_destroy(connector); + return NULL; +} + +/* + * Module: + */ + +static int tfp410_modeset_init(struct tilcdc_module *mod, struct drm_device *dev) +{ + struct tfp410_module *tfp410_mod = to_tfp410_module(mod); + struct tilcdc_drm_private *priv = dev->dev_private; + struct drm_encoder *encoder; + struct drm_connector *connector; + + encoder = tfp410_encoder_create(dev, tfp410_mod); + if (!encoder) + return -ENOMEM; + + connector = tfp410_connector_create(dev, tfp410_mod, encoder); + if (!connector) + return -ENOMEM; + + priv->encoders[priv->num_encoders++] = encoder; + priv->connectors[priv->num_connectors++] = connector; + + return 0; +} + +static void tfp410_destroy(struct tilcdc_module *mod) +{ + struct tfp410_module *tfp410_mod = to_tfp410_module(mod); + + if (tfp410_mod->i2c) + i2c_put_adapter(tfp410_mod->i2c); + + if (!IS_ERR_VALUE(tfp410_mod->gpio)) + gpio_free(tfp410_mod->gpio); + + tilcdc_module_cleanup(mod); + kfree(tfp410_mod); +} + +static const struct tilcdc_module_ops tfp410_module_ops = { + .modeset_init = tfp410_modeset_init, + .destroy = tfp410_destroy, +}; + +/* + * Device: + */ + +static struct of_device_id tfp410_of_match[]; + +static int tfp410_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device_node *i2c_node; + struct tfp410_module *tfp410_mod; + struct tilcdc_module *mod; + struct pinctrl *pinctrl; + uint32_t i2c_phandle; + int ret = -EINVAL; + + /* bail out early if no DT data: */ + if (!node) { + dev_err(&pdev->dev, "device-tree data is missing\n"); + return -ENXIO; + } + + tfp410_mod = kzalloc(sizeof(*tfp410_mod), GFP_KERNEL); + if (!tfp410_mod) + return -ENOMEM; + + mod = &tfp410_mod->base; + + tilcdc_module_init(mod, "tfp410", &tfp410_module_ops); + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + dev_warn(&pdev->dev, "pins are not configured\n"); + + if (of_property_read_u32(node, "i2c", &i2c_phandle)) { + dev_err(&pdev->dev, "could not get i2c bus phandle\n"); + goto fail; + } + + i2c_node = of_find_node_by_phandle(i2c_phandle); + if (!i2c_node) { + dev_err(&pdev->dev, "could not get i2c bus node\n"); + goto fail; + } + + tfp410_mod->i2c = of_find_i2c_adapter_by_node(i2c_node); + if (!tfp410_mod->i2c) { + dev_err(&pdev->dev, "could not get i2c\n"); + goto fail; + } + + of_node_put(i2c_node); + + tfp410_mod->gpio = of_get_named_gpio_flags(node, "powerdn-gpio", + 0, NULL); + if (IS_ERR_VALUE(tfp410_mod->gpio)) { + dev_warn(&pdev->dev, "No power down GPIO\n"); + } else { + ret = gpio_request(tfp410_mod->gpio, "DVI_PDn"); + if (ret) { + dev_err(&pdev->dev, "could not get DVI_PDn gpio\n"); + goto fail; + } + } + + return 0; + +fail: + tfp410_destroy(mod); + return ret; +} + +static int tfp410_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct of_device_id tfp410_of_match[] = { + { .compatible = "ti,tilcdc,tfp410", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tfp410_of_match); + +struct platform_driver tfp410_driver = { + .probe = tfp410_probe, + .remove = tfp410_remove, + .driver = { + .owner = THIS_MODULE, + .name = "tfp410", + .of_match_table = tfp410_of_match, + }, +}; + +int __init tilcdc_tfp410_init(void) +{ + return platform_driver_register(&tfp410_driver); +} + +void __exit tilcdc_tfp410_fini(void) +{ + platform_driver_unregister(&tfp410_driver); +} diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.h b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.h new file mode 100644 index 000000000000..5b800f1f6aa5 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * 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, see . + */ + +#ifndef __TILCDC_TFP410_H__ +#define __TILCDC_TFP410_H__ + +/* sub-module for tfp410 dvi adaptor */ + +int tilcdc_tfp410_init(void); +void tilcdc_tfp410_fini(void); + +#endif /* __TILCDC_TFP410_H__ */ From e7792ce2da5ded80861db787ace9b57ecf7bc96c Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 8 Jan 2013 19:21:02 -0600 Subject: [PATCH 3076/4607] drm/i2c: nxp-tda998x (v3) Driver for the NXP TDA998X i2c hdmi encoder slave. v1: original v2: fix npix/nline programming v3: add Kconfig, fix dup'd MODULE_DESCRIPTION Signed-off-by: Rob Clark Reviewed-by: Daniel Vetter Tested-by: Koen Kooi --- drivers/gpu/drm/i2c/Kconfig | 6 + drivers/gpu/drm/i2c/Makefile | 3 + drivers/gpu/drm/i2c/tda998x_drv.c | 906 ++++++++++++++++++++++++++++++ 3 files changed, 915 insertions(+) create mode 100644 drivers/gpu/drm/i2c/tda998x_drv.c diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index 16118363509a..4d341db462a2 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig @@ -19,4 +19,10 @@ config DRM_I2C_SIL164 when used in pairs) TMDS transmitters, used in some nVidia video cards. +config DRM_I2C_NXP_TDA998X + tristate "NXP Semiconductors TDA998X HDMI encoder" + default m if DRM_TILCDC + help + Support for NXP Semiconductors TDA998X HDMI encoders. + endmenu diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile index 92862563e7ee..43aa33baebed 100644 --- a/drivers/gpu/drm/i2c/Makefile +++ b/drivers/gpu/drm/i2c/Makefile @@ -5,3 +5,6 @@ obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o sil164-y := sil164_drv.o obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o + +tda998x-y := tda998x_drv.o +obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c new file mode 100644 index 000000000000..e68b58a1aaf9 --- /dev/null +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -0,0 +1,906 @@ +/* + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * 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, see . + */ + + + +#include + +#include +#include +#include +#include + + +#define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) + +struct tda998x_priv { + struct i2c_client *cec; + uint16_t rev; + uint8_t current_page; + int dpms; +}; + +#define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv) + +/* The TDA9988 series of devices use a paged register scheme.. to simplify + * things we encode the page # in upper bits of the register #. To read/ + * write a given register, we need to make sure CURPAGE register is set + * appropriately. Which implies reads/writes are not atomic. Fun! + */ + +#define REG(page, addr) (((page) << 8) | (addr)) +#define REG2ADDR(reg) ((reg) & 0xff) +#define REG2PAGE(reg) (((reg) >> 8) & 0xff) + +#define REG_CURPAGE 0xff /* write */ + + +/* Page 00h: General Control */ +#define REG_VERSION_LSB REG(0x00, 0x00) /* read */ +#define REG_MAIN_CNTRL0 REG(0x00, 0x01) /* read/write */ +# define MAIN_CNTRL0_SR (1 << 0) +# define MAIN_CNTRL0_DECS (1 << 1) +# define MAIN_CNTRL0_DEHS (1 << 2) +# define MAIN_CNTRL0_CECS (1 << 3) +# define MAIN_CNTRL0_CEHS (1 << 4) +# define MAIN_CNTRL0_SCALER (1 << 7) +#define REG_VERSION_MSB REG(0x00, 0x02) /* read */ +#define REG_SOFTRESET REG(0x00, 0x0a) /* write */ +# define SOFTRESET_AUDIO (1 << 0) +# define SOFTRESET_I2C_MASTER (1 << 1) +#define REG_DDC_DISABLE REG(0x00, 0x0b) /* read/write */ +#define REG_CCLK_ON REG(0x00, 0x0c) /* read/write */ +#define REG_I2C_MASTER REG(0x00, 0x0d) /* read/write */ +# define I2C_MASTER_DIS_MM (1 << 0) +# define I2C_MASTER_DIS_FILT (1 << 1) +# define I2C_MASTER_APP_STRT_LAT (1 << 2) +#define REG_INT_FLAGS_0 REG(0x00, 0x0f) /* read/write */ +#define REG_INT_FLAGS_1 REG(0x00, 0x10) /* read/write */ +#define REG_INT_FLAGS_2 REG(0x00, 0x11) /* read/write */ +# define INT_FLAGS_2_EDID_BLK_RD (1 << 1) +#define REG_ENA_VP_0 REG(0x00, 0x18) /* read/write */ +#define REG_ENA_VP_1 REG(0x00, 0x19) /* read/write */ +#define REG_ENA_VP_2 REG(0x00, 0x1a) /* read/write */ +#define REG_ENA_AP REG(0x00, 0x1e) /* read/write */ +#define REG_VIP_CNTRL_0 REG(0x00, 0x20) /* write */ +# define VIP_CNTRL_0_MIRR_A (1 << 7) +# define VIP_CNTRL_0_SWAP_A(x) (((x) & 7) << 4) +# define VIP_CNTRL_0_MIRR_B (1 << 3) +# define VIP_CNTRL_0_SWAP_B(x) (((x) & 7) << 0) +#define REG_VIP_CNTRL_1 REG(0x00, 0x21) /* write */ +# define VIP_CNTRL_1_MIRR_C (1 << 7) +# define VIP_CNTRL_1_SWAP_C(x) (((x) & 7) << 4) +# define VIP_CNTRL_1_MIRR_D (1 << 3) +# define VIP_CNTRL_1_SWAP_D(x) (((x) & 7) << 0) +#define REG_VIP_CNTRL_2 REG(0x00, 0x22) /* write */ +# define VIP_CNTRL_2_MIRR_E (1 << 7) +# define VIP_CNTRL_2_SWAP_E(x) (((x) & 7) << 4) +# define VIP_CNTRL_2_MIRR_F (1 << 3) +# define VIP_CNTRL_2_SWAP_F(x) (((x) & 7) << 0) +#define REG_VIP_CNTRL_3 REG(0x00, 0x23) /* write */ +# define VIP_CNTRL_3_X_TGL (1 << 0) +# define VIP_CNTRL_3_H_TGL (1 << 1) +# define VIP_CNTRL_3_V_TGL (1 << 2) +# define VIP_CNTRL_3_EMB (1 << 3) +# define VIP_CNTRL_3_SYNC_DE (1 << 4) +# define VIP_CNTRL_3_SYNC_HS (1 << 5) +# define VIP_CNTRL_3_DE_INT (1 << 6) +# define VIP_CNTRL_3_EDGE (1 << 7) +#define REG_VIP_CNTRL_4 REG(0x00, 0x24) /* write */ +# define VIP_CNTRL_4_BLC(x) (((x) & 3) << 0) +# define VIP_CNTRL_4_BLANKIT(x) (((x) & 3) << 2) +# define VIP_CNTRL_4_CCIR656 (1 << 4) +# define VIP_CNTRL_4_656_ALT (1 << 5) +# define VIP_CNTRL_4_TST_656 (1 << 6) +# define VIP_CNTRL_4_TST_PAT (1 << 7) +#define REG_VIP_CNTRL_5 REG(0x00, 0x25) /* write */ +# define VIP_CNTRL_5_CKCASE (1 << 0) +# define VIP_CNTRL_5_SP_CNT(x) (((x) & 3) << 1) +#define REG_MAT_CONTRL REG(0x00, 0x80) /* write */ +# define MAT_CONTRL_MAT_SC(x) (((x) & 3) << 0) +# define MAT_CONTRL_MAT_BP (1 << 2) +#define REG_VIDFORMAT REG(0x00, 0xa0) /* write */ +#define REG_REFPIX_MSB REG(0x00, 0xa1) /* write */ +#define REG_REFPIX_LSB REG(0x00, 0xa2) /* write */ +#define REG_REFLINE_MSB REG(0x00, 0xa3) /* write */ +#define REG_REFLINE_LSB REG(0x00, 0xa4) /* write */ +#define REG_NPIX_MSB REG(0x00, 0xa5) /* write */ +#define REG_NPIX_LSB REG(0x00, 0xa6) /* write */ +#define REG_NLINE_MSB REG(0x00, 0xa7) /* write */ +#define REG_NLINE_LSB REG(0x00, 0xa8) /* write */ +#define REG_VS_LINE_STRT_1_MSB REG(0x00, 0xa9) /* write */ +#define REG_VS_LINE_STRT_1_LSB REG(0x00, 0xaa) /* write */ +#define REG_VS_PIX_STRT_1_MSB REG(0x00, 0xab) /* write */ +#define REG_VS_PIX_STRT_1_LSB REG(0x00, 0xac) /* write */ +#define REG_VS_LINE_END_1_MSB REG(0x00, 0xad) /* write */ +#define REG_VS_LINE_END_1_LSB REG(0x00, 0xae) /* write */ +#define REG_VS_PIX_END_1_MSB REG(0x00, 0xaf) /* write */ +#define REG_VS_PIX_END_1_LSB REG(0x00, 0xb0) /* write */ +#define REG_VS_PIX_STRT_2_MSB REG(0x00, 0xb3) /* write */ +#define REG_VS_PIX_STRT_2_LSB REG(0x00, 0xb4) /* write */ +#define REG_VS_PIX_END_2_MSB REG(0x00, 0xb7) /* write */ +#define REG_VS_PIX_END_2_LSB REG(0x00, 0xb8) /* write */ +#define REG_HS_PIX_START_MSB REG(0x00, 0xb9) /* write */ +#define REG_HS_PIX_START_LSB REG(0x00, 0xba) /* write */ +#define REG_HS_PIX_STOP_MSB REG(0x00, 0xbb) /* write */ +#define REG_HS_PIX_STOP_LSB REG(0x00, 0xbc) /* write */ +#define REG_VWIN_START_1_MSB REG(0x00, 0xbd) /* write */ +#define REG_VWIN_START_1_LSB REG(0x00, 0xbe) /* write */ +#define REG_VWIN_END_1_MSB REG(0x00, 0xbf) /* write */ +#define REG_VWIN_END_1_LSB REG(0x00, 0xc0) /* write */ +#define REG_DE_START_MSB REG(0x00, 0xc5) /* write */ +#define REG_DE_START_LSB REG(0x00, 0xc6) /* write */ +#define REG_DE_STOP_MSB REG(0x00, 0xc7) /* write */ +#define REG_DE_STOP_LSB REG(0x00, 0xc8) /* write */ +#define REG_TBG_CNTRL_0 REG(0x00, 0xca) /* write */ +# define TBG_CNTRL_0_FRAME_DIS (1 << 5) +# define TBG_CNTRL_0_SYNC_MTHD (1 << 6) +# define TBG_CNTRL_0_SYNC_ONCE (1 << 7) +#define REG_TBG_CNTRL_1 REG(0x00, 0xcb) /* write */ +# define TBG_CNTRL_1_VH_TGL_0 (1 << 0) +# define TBG_CNTRL_1_VH_TGL_1 (1 << 1) +# define TBG_CNTRL_1_VH_TGL_2 (1 << 2) +# define TBG_CNTRL_1_VHX_EXT_DE (1 << 3) +# define TBG_CNTRL_1_VHX_EXT_HS (1 << 4) +# define TBG_CNTRL_1_VHX_EXT_VS (1 << 5) +# define TBG_CNTRL_1_DWIN_DIS (1 << 6) +#define REG_ENABLE_SPACE REG(0x00, 0xd6) /* write */ +#define REG_HVF_CNTRL_0 REG(0x00, 0xe4) /* write */ +# define HVF_CNTRL_0_SM (1 << 7) +# define HVF_CNTRL_0_RWB (1 << 6) +# define HVF_CNTRL_0_PREFIL(x) (((x) & 3) << 2) +# define HVF_CNTRL_0_INTPOL(x) (((x) & 3) << 0) +#define REG_HVF_CNTRL_1 REG(0x00, 0xe5) /* write */ +# define HVF_CNTRL_1_FOR (1 << 0) +# define HVF_CNTRL_1_YUVBLK (1 << 1) +# define HVF_CNTRL_1_VQR(x) (((x) & 3) << 2) +# define HVF_CNTRL_1_PAD(x) (((x) & 3) << 4) +# define HVF_CNTRL_1_SEMI_PLANAR (1 << 6) +#define REG_RPT_CNTRL REG(0x00, 0xf0) /* write */ + + +/* Page 02h: PLL settings */ +#define REG_PLL_SERIAL_1 REG(0x02, 0x00) /* read/write */ +# define PLL_SERIAL_1_SRL_FDN (1 << 0) +# define PLL_SERIAL_1_SRL_IZ(x) (((x) & 3) << 1) +# define PLL_SERIAL_1_SRL_MAN_IZ (1 << 6) +#define REG_PLL_SERIAL_2 REG(0x02, 0x01) /* read/write */ +# define PLL_SERIAL_2_SRL_NOSC(x) (((x) & 3) << 0) +# define PLL_SERIAL_2_SRL_PR(x) (((x) & 0xf) << 4) +#define REG_PLL_SERIAL_3 REG(0x02, 0x02) /* read/write */ +# define PLL_SERIAL_3_SRL_CCIR (1 << 0) +# define PLL_SERIAL_3_SRL_DE (1 << 2) +# define PLL_SERIAL_3_SRL_PXIN_SEL (1 << 4) +#define REG_SERIALIZER REG(0x02, 0x03) /* read/write */ +#define REG_BUFFER_OUT REG(0x02, 0x04) /* read/write */ +#define REG_PLL_SCG1 REG(0x02, 0x05) /* read/write */ +#define REG_PLL_SCG2 REG(0x02, 0x06) /* read/write */ +#define REG_PLL_SCGN1 REG(0x02, 0x07) /* read/write */ +#define REG_PLL_SCGN2 REG(0x02, 0x08) /* read/write */ +#define REG_PLL_SCGR1 REG(0x02, 0x09) /* read/write */ +#define REG_PLL_SCGR2 REG(0x02, 0x0a) /* read/write */ +#define REG_AUDIO_DIV REG(0x02, 0x0e) /* read/write */ +#define REG_SEL_CLK REG(0x02, 0x11) /* read/write */ +# define SEL_CLK_SEL_CLK1 (1 << 0) +# define SEL_CLK_SEL_VRF_CLK(x) (((x) & 3) << 1) +# define SEL_CLK_ENA_SC_CLK (1 << 3) +#define REG_ANA_GENERAL REG(0x02, 0x12) /* read/write */ + + +/* Page 09h: EDID Control */ +#define REG_EDID_DATA_0 REG(0x09, 0x00) /* read */ +/* next 127 successive registers are the EDID block */ +#define REG_EDID_CTRL REG(0x09, 0xfa) /* read/write */ +#define REG_DDC_ADDR REG(0x09, 0xfb) /* read/write */ +#define REG_DDC_OFFS REG(0x09, 0xfc) /* read/write */ +#define REG_DDC_SEGM_ADDR REG(0x09, 0xfd) /* read/write */ +#define REG_DDC_SEGM REG(0x09, 0xfe) /* read/write */ + + +/* Page 10h: information frames and packets */ + + +/* Page 11h: audio settings and content info packets */ +#define REG_AIP_CNTRL_0 REG(0x11, 0x00) /* read/write */ +# define AIP_CNTRL_0_RST_FIFO (1 << 0) +# define AIP_CNTRL_0_SWAP (1 << 1) +# define AIP_CNTRL_0_LAYOUT (1 << 2) +# define AIP_CNTRL_0_ACR_MAN (1 << 5) +# define AIP_CNTRL_0_RST_CTS (1 << 6) +#define REG_ENC_CNTRL REG(0x11, 0x0d) /* read/write */ +# define ENC_CNTRL_RST_ENC (1 << 0) +# define ENC_CNTRL_RST_SEL (1 << 1) +# define ENC_CNTRL_CTL_CODE(x) (((x) & 3) << 2) + + +/* Page 12h: HDCP and OTP */ +#define REG_TX3 REG(0x12, 0x9a) /* read/write */ +#define REG_TX33 REG(0x12, 0xb8) /* read/write */ +# define TX33_HDMI (1 << 1) + + +/* Page 13h: Gamut related metadata packets */ + + + +/* CEC registers: (not paged) + */ +#define REG_CEC_FRO_IM_CLK_CTRL 0xfb /* read/write */ +# define CEC_FRO_IM_CLK_CTRL_GHOST_DIS (1 << 7) +# define CEC_FRO_IM_CLK_CTRL_ENA_OTP (1 << 6) +# define CEC_FRO_IM_CLK_CTRL_IMCLK_SEL (1 << 1) +# define CEC_FRO_IM_CLK_CTRL_FRO_DIV (1 << 0) +#define REG_CEC_RXSHPDLEV 0xfe /* read */ +# define CEC_RXSHPDLEV_RXSENS (1 << 0) +# define CEC_RXSHPDLEV_HPD (1 << 1) + +#define REG_CEC_ENAMODS 0xff /* read/write */ +# define CEC_ENAMODS_DIS_FRO (1 << 6) +# define CEC_ENAMODS_DIS_CCLK (1 << 5) +# define CEC_ENAMODS_EN_RXSENS (1 << 2) +# define CEC_ENAMODS_EN_HDMI (1 << 1) +# define CEC_ENAMODS_EN_CEC (1 << 0) + + +/* Device versions: */ +#define TDA9989N2 0x0101 +#define TDA19989 0x0201 +#define TDA19989N2 0x0202 +#define TDA19988 0x0301 + +static void +cec_write(struct drm_encoder *encoder, uint16_t addr, uint8_t val) +{ + struct i2c_client *client = to_tda998x_priv(encoder)->cec; + uint8_t buf[] = {addr, val}; + int ret; + + ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); + if (ret < 0) + dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr); +} + +static uint8_t +cec_read(struct drm_encoder *encoder, uint8_t addr) +{ + struct i2c_client *client = to_tda998x_priv(encoder)->cec; + uint8_t val; + int ret; + + ret = i2c_master_send(client, &addr, sizeof(addr)); + if (ret < 0) + goto fail; + + ret = i2c_master_recv(client, &val, sizeof(val)); + if (ret < 0) + goto fail; + + return val; + +fail: + dev_err(&client->dev, "Error %d reading from cec:0x%x\n", ret, addr); + return 0; +} + +static void +set_page(struct drm_encoder *encoder, uint16_t reg) +{ + struct tda998x_priv *priv = to_tda998x_priv(encoder); + + if (REG2PAGE(reg) != priv->current_page) { + struct i2c_client *client = drm_i2c_encoder_get_client(encoder); + uint8_t buf[] = { + REG_CURPAGE, REG2PAGE(reg) + }; + int ret = i2c_master_send(client, buf, sizeof(buf)); + if (ret < 0) + dev_err(&client->dev, "Error %d writing to REG_CURPAGE\n", ret); + + priv->current_page = REG2PAGE(reg); + } +} + +static int +reg_read_range(struct drm_encoder *encoder, uint16_t reg, char *buf, int cnt) +{ + struct i2c_client *client = drm_i2c_encoder_get_client(encoder); + uint8_t addr = REG2ADDR(reg); + int ret; + + set_page(encoder, reg); + + ret = i2c_master_send(client, &addr, sizeof(addr)); + if (ret < 0) + goto fail; + + ret = i2c_master_recv(client, buf, cnt); + if (ret < 0) + goto fail; + + return ret; + +fail: + dev_err(&client->dev, "Error %d reading from 0x%x\n", ret, reg); + return ret; +} + +static uint8_t +reg_read(struct drm_encoder *encoder, uint16_t reg) +{ + uint8_t val = 0; + reg_read_range(encoder, reg, &val, sizeof(val)); + return val; +} + +static void +reg_write(struct drm_encoder *encoder, uint16_t reg, uint8_t val) +{ + struct i2c_client *client = drm_i2c_encoder_get_client(encoder); + uint8_t buf[] = {REG2ADDR(reg), val}; + int ret; + + set_page(encoder, reg); + + ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); + if (ret < 0) + dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); +} + +static void +reg_write16(struct drm_encoder *encoder, uint16_t reg, uint16_t val) +{ + struct i2c_client *client = drm_i2c_encoder_get_client(encoder); + uint8_t buf[] = {REG2ADDR(reg), val >> 8, val}; + int ret; + + set_page(encoder, reg); + + ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); + if (ret < 0) + dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); +} + +static void +reg_set(struct drm_encoder *encoder, uint16_t reg, uint8_t val) +{ + reg_write(encoder, reg, reg_read(encoder, reg) | val); +} + +static void +reg_clear(struct drm_encoder *encoder, uint16_t reg, uint8_t val) +{ + reg_write(encoder, reg, reg_read(encoder, reg) & ~val); +} + +static void +tda998x_reset(struct drm_encoder *encoder) +{ + /* reset audio and i2c master: */ + reg_set(encoder, REG_SOFTRESET, SOFTRESET_AUDIO | SOFTRESET_I2C_MASTER); + msleep(50); + reg_clear(encoder, REG_SOFTRESET, SOFTRESET_AUDIO | SOFTRESET_I2C_MASTER); + msleep(50); + + /* reset transmitter: */ + reg_set(encoder, REG_MAIN_CNTRL0, MAIN_CNTRL0_SR); + reg_clear(encoder, REG_MAIN_CNTRL0, MAIN_CNTRL0_SR); + + /* PLL registers common configuration */ + reg_write(encoder, REG_PLL_SERIAL_1, 0x00); + reg_write(encoder, REG_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(1)); + reg_write(encoder, REG_PLL_SERIAL_3, 0x00); + reg_write(encoder, REG_SERIALIZER, 0x00); + reg_write(encoder, REG_BUFFER_OUT, 0x00); + reg_write(encoder, REG_PLL_SCG1, 0x00); + reg_write(encoder, REG_AUDIO_DIV, 0x03); + reg_write(encoder, REG_SEL_CLK, SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK); + reg_write(encoder, REG_PLL_SCGN1, 0xfa); + reg_write(encoder, REG_PLL_SCGN2, 0x00); + reg_write(encoder, REG_PLL_SCGR1, 0x5b); + reg_write(encoder, REG_PLL_SCGR2, 0x00); + reg_write(encoder, REG_PLL_SCG2, 0x10); +} + +/* DRM encoder functions */ + +static void +tda998x_encoder_set_config(struct drm_encoder *encoder, void *params) +{ +} + +static void +tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct tda998x_priv *priv = to_tda998x_priv(encoder); + + /* we only care about on or off: */ + if (mode != DRM_MODE_DPMS_ON) + mode = DRM_MODE_DPMS_OFF; + + if (mode == priv->dpms) + return; + + switch (mode) { + case DRM_MODE_DPMS_ON: + /* enable audio and video ports */ + reg_write(encoder, REG_ENA_AP, 0xff); + reg_write(encoder, REG_ENA_VP_0, 0xff); + reg_write(encoder, REG_ENA_VP_1, 0xff); + reg_write(encoder, REG_ENA_VP_2, 0xff); + /* set muxing after enabling ports: */ + reg_write(encoder, REG_VIP_CNTRL_0, + VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3)); + reg_write(encoder, REG_VIP_CNTRL_1, + VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1)); + reg_write(encoder, REG_VIP_CNTRL_2, + VIP_CNTRL_2_SWAP_E(4) | VIP_CNTRL_2_SWAP_F(5)); + break; + case DRM_MODE_DPMS_OFF: + /* disable audio and video ports */ + reg_write(encoder, REG_ENA_AP, 0x00); + reg_write(encoder, REG_ENA_VP_0, 0x00); + reg_write(encoder, REG_ENA_VP_1, 0x00); + reg_write(encoder, REG_ENA_VP_2, 0x00); + break; + } + + priv->dpms = mode; +} + +static void +tda998x_encoder_save(struct drm_encoder *encoder) +{ + DBG(""); +} + +static void +tda998x_encoder_restore(struct drm_encoder *encoder) +{ + DBG(""); +} + +static bool +tda998x_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static int +tda998x_encoder_mode_valid(struct drm_encoder *encoder, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static void +tda998x_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct tda998x_priv *priv = to_tda998x_priv(encoder); + uint16_t hs_start, hs_end, line_start, line_end; + uint16_t vwin_start, vwin_end, de_start, de_end; + uint16_t ref_pix, ref_line, pix_start2; + uint8_t reg, div, rep; + + hs_start = mode->hsync_start - mode->hdisplay; + hs_end = mode->hsync_end - mode->hdisplay; + line_start = 1; + line_end = 1 + mode->vsync_end - mode->vsync_start; + vwin_start = mode->vtotal - mode->vsync_start; + vwin_end = vwin_start + mode->vdisplay; + de_start = mode->htotal - mode->hdisplay; + de_end = mode->htotal; + + pix_start2 = 0; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + pix_start2 = (mode->htotal / 2) + hs_start; + + /* TODO how is this value calculated? It is 2 for all common + * formats in the tables in out of tree nxp driver (assuming + * I've properly deciphered their byzantine table system) + */ + ref_line = 2; + + /* this might changes for other color formats from the CRTC: */ + ref_pix = 3 + hs_start; + + div = 148500 / mode->clock; + + DBG("clock=%d, div=%u", mode->clock, div); + DBG("hs_start=%u, hs_end=%u, line_start=%u, line_end=%u", + hs_start, hs_end, line_start, line_end); + DBG("vwin_start=%u, vwin_end=%u, de_start=%u, de_end=%u", + vwin_start, vwin_end, de_start, de_end); + DBG("ref_line=%u, ref_pix=%u, pix_start2=%u", + ref_line, ref_pix, pix_start2); + + /* mute the audio FIFO: */ + reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO); + + /* set HDMI HDCP mode off: */ + reg_set(encoder, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS); + reg_clear(encoder, REG_TX33, TX33_HDMI); + + reg_write(encoder, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(0)); + /* no pre-filter or interpolator: */ + reg_write(encoder, REG_HVF_CNTRL_0, HVF_CNTRL_0_PREFIL(0) | + HVF_CNTRL_0_INTPOL(0)); + reg_write(encoder, REG_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0)); + reg_write(encoder, REG_VIP_CNTRL_4, VIP_CNTRL_4_BLANKIT(0) | + VIP_CNTRL_4_BLC(0)); + reg_clear(encoder, REG_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR); + + reg_clear(encoder, REG_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IZ); + reg_clear(encoder, REG_PLL_SERIAL_3, PLL_SERIAL_3_SRL_DE); + reg_write(encoder, REG_SERIALIZER, 0); + reg_write(encoder, REG_HVF_CNTRL_1, HVF_CNTRL_1_VQR(0)); + + /* TODO enable pixel repeat for pixel rates less than 25Msamp/s */ + rep = 0; + reg_write(encoder, REG_RPT_CNTRL, 0); + reg_write(encoder, REG_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) | + SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK); + + reg_write(encoder, REG_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) | + PLL_SERIAL_2_SRL_PR(rep)); + + reg_write16(encoder, REG_VS_PIX_STRT_2_MSB, pix_start2); + reg_write16(encoder, REG_VS_PIX_END_2_MSB, pix_start2); + + /* set color matrix bypass flag: */ + reg_set(encoder, REG_MAT_CONTRL, MAT_CONTRL_MAT_BP); + + /* set BIAS tmds value: */ + reg_write(encoder, REG_ANA_GENERAL, 0x09); + + reg_clear(encoder, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_MTHD); + + reg_write(encoder, REG_VIP_CNTRL_3, 0); + reg_set(encoder, REG_VIP_CNTRL_3, VIP_CNTRL_3_SYNC_HS); + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + reg_set(encoder, REG_VIP_CNTRL_3, VIP_CNTRL_3_V_TGL); + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + reg_set(encoder, REG_VIP_CNTRL_3, VIP_CNTRL_3_H_TGL); + + reg_write(encoder, REG_VIDFORMAT, 0x00); + reg_write16(encoder, REG_NPIX_MSB, mode->hdisplay - 1); + reg_write16(encoder, REG_NLINE_MSB, mode->vdisplay - 1); + reg_write16(encoder, REG_VS_LINE_STRT_1_MSB, line_start); + reg_write16(encoder, REG_VS_LINE_END_1_MSB, line_end); + reg_write16(encoder, REG_VS_PIX_STRT_1_MSB, hs_start); + reg_write16(encoder, REG_VS_PIX_END_1_MSB, hs_start); + reg_write16(encoder, REG_HS_PIX_START_MSB, hs_start); + reg_write16(encoder, REG_HS_PIX_STOP_MSB, hs_end); + reg_write16(encoder, REG_VWIN_START_1_MSB, vwin_start); + reg_write16(encoder, REG_VWIN_END_1_MSB, vwin_end); + reg_write16(encoder, REG_DE_START_MSB, de_start); + reg_write16(encoder, REG_DE_STOP_MSB, de_end); + + if (priv->rev == TDA19988) { + /* let incoming pixels fill the active space (if any) */ + reg_write(encoder, REG_ENABLE_SPACE, 0x01); + } + + reg_write16(encoder, REG_REFPIX_MSB, ref_pix); + reg_write16(encoder, REG_REFLINE_MSB, ref_line); + + reg = TBG_CNTRL_1_VHX_EXT_DE | + TBG_CNTRL_1_VHX_EXT_HS | + TBG_CNTRL_1_VHX_EXT_VS | + TBG_CNTRL_1_DWIN_DIS | /* HDCP off */ + TBG_CNTRL_1_VH_TGL_2; + if (mode->flags & (DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC)) + reg |= TBG_CNTRL_1_VH_TGL_0; + reg_set(encoder, REG_TBG_CNTRL_1, reg); + + /* must be last register set: */ + reg_clear(encoder, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE); +} + +static enum drm_connector_status +tda998x_encoder_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + uint8_t val = cec_read(encoder, REG_CEC_RXSHPDLEV); + return (val & CEC_RXSHPDLEV_HPD) ? connector_status_connected : + connector_status_disconnected; +} + +static int +read_edid_block(struct drm_encoder *encoder, uint8_t *buf, int blk) +{ + uint8_t offset, segptr; + int ret, i; + + /* enable EDID read irq: */ + reg_set(encoder, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); + + offset = (blk & 1) ? 128 : 0; + segptr = blk / 2; + + reg_write(encoder, REG_DDC_ADDR, 0xa0); + reg_write(encoder, REG_DDC_OFFS, offset); + reg_write(encoder, REG_DDC_SEGM_ADDR, 0x60); + reg_write(encoder, REG_DDC_SEGM, segptr); + + /* enable reading EDID: */ + reg_write(encoder, REG_EDID_CTRL, 0x1); + + /* flag must be cleared by sw: */ + reg_write(encoder, REG_EDID_CTRL, 0x0); + + /* wait for block read to complete: */ + for (i = 100; i > 0; i--) { + uint8_t val = reg_read(encoder, REG_INT_FLAGS_2); + if (val & INT_FLAGS_2_EDID_BLK_RD) + break; + msleep(1); + } + + if (i == 0) + return -ETIMEDOUT; + + ret = reg_read_range(encoder, REG_EDID_DATA_0, buf, EDID_LENGTH); + if (ret != EDID_LENGTH) { + dev_err(encoder->dev->dev, "failed to read edid block %d: %d", + blk, ret); + return ret; + } + + reg_clear(encoder, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); + + return 0; +} + +static uint8_t * +do_get_edid(struct drm_encoder *encoder) +{ + int j = 0, valid_extensions = 0; + uint8_t *block, *new; + bool print_bad_edid = drm_debug & DRM_UT_KMS; + + if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL) + return NULL; + + /* base block fetch */ + if (read_edid_block(encoder, block, 0)) + goto fail; + + if (!drm_edid_block_valid(block, 0, print_bad_edid)) + goto fail; + + /* if there's no extensions, we're done */ + if (block[0x7e] == 0) + return block; + + new = krealloc(block, (block[0x7e] + 1) * EDID_LENGTH, GFP_KERNEL); + if (!new) + goto fail; + block = new; + + for (j = 1; j <= block[0x7e]; j++) { + uint8_t *ext_block = block + (valid_extensions + 1) * EDID_LENGTH; + if (read_edid_block(encoder, ext_block, j)) + goto fail; + + if (!drm_edid_block_valid(ext_block, j, print_bad_edid)) + goto fail; + + valid_extensions++; + } + + if (valid_extensions != block[0x7e]) { + block[EDID_LENGTH-1] += block[0x7e] - valid_extensions; + block[0x7e] = valid_extensions; + new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL); + if (!new) + goto fail; + block = new; + } + + return block; + +fail: + dev_warn(encoder->dev->dev, "failed to read EDID\n"); + kfree(block); + return NULL; +} + +static int +tda998x_encoder_get_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct edid *edid = (struct edid *)do_get_edid(encoder); + int n = 0; + + if (edid) { + drm_mode_connector_update_edid_property(connector, edid); + n = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + return n; +} + +static int +tda998x_encoder_create_resources(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + DBG(""); + return 0; +} + +static int +tda998x_encoder_set_property(struct drm_encoder *encoder, + struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + DBG(""); + return 0; +} + +static void +tda998x_encoder_destroy(struct drm_encoder *encoder) +{ + struct tda998x_priv *priv = to_tda998x_priv(encoder); + drm_i2c_encoder_destroy(encoder); + kfree(priv); +} + +static struct drm_encoder_slave_funcs tda998x_encoder_funcs = { + .set_config = tda998x_encoder_set_config, + .destroy = tda998x_encoder_destroy, + .dpms = tda998x_encoder_dpms, + .save = tda998x_encoder_save, + .restore = tda998x_encoder_restore, + .mode_fixup = tda998x_encoder_mode_fixup, + .mode_valid = tda998x_encoder_mode_valid, + .mode_set = tda998x_encoder_mode_set, + .detect = tda998x_encoder_detect, + .get_modes = tda998x_encoder_get_modes, + .create_resources = tda998x_encoder_create_resources, + .set_property = tda998x_encoder_set_property, +}; + +/* I2C driver functions */ + +static int +tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + return 0; +} + +static int +tda998x_remove(struct i2c_client *client) +{ + return 0; +} + +static int +tda998x_encoder_init(struct i2c_client *client, + struct drm_device *dev, + struct drm_encoder_slave *encoder_slave) +{ + struct drm_encoder *encoder = &encoder_slave->base; + struct tda998x_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->current_page = 0; + priv->cec = i2c_new_dummy(client->adapter, 0x34); + priv->dpms = DRM_MODE_DPMS_OFF; + + encoder_slave->slave_priv = priv; + encoder_slave->slave_funcs = &tda998x_encoder_funcs; + + /* wake up the device: */ + cec_write(encoder, REG_CEC_ENAMODS, + CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI); + + tda998x_reset(encoder); + + /* read version: */ + priv->rev = reg_read(encoder, REG_VERSION_LSB) | + reg_read(encoder, REG_VERSION_MSB) << 8; + + /* mask off feature bits: */ + priv->rev &= ~0x30; /* not-hdcp and not-scalar bit */ + + switch (priv->rev) { + case TDA9989N2: dev_info(dev->dev, "found TDA9989 n2"); break; + case TDA19989: dev_info(dev->dev, "found TDA19989"); break; + case TDA19989N2: dev_info(dev->dev, "found TDA19989 n2"); break; + case TDA19988: dev_info(dev->dev, "found TDA19988"); break; + default: + DBG("found unsupported device: %04x", priv->rev); + goto fail; + } + + /* after reset, enable DDC: */ + reg_write(encoder, REG_DDC_DISABLE, 0x00); + + /* set clock on DDC channel: */ + reg_write(encoder, REG_TX3, 39); + + /* if necessary, disable multi-master: */ + if (priv->rev == TDA19989) + reg_set(encoder, REG_I2C_MASTER, I2C_MASTER_DIS_MM); + + cec_write(encoder, REG_CEC_FRO_IM_CLK_CTRL, + CEC_FRO_IM_CLK_CTRL_GHOST_DIS | CEC_FRO_IM_CLK_CTRL_IMCLK_SEL); + + return 0; + +fail: + /* if encoder_init fails, the encoder slave is never registered, + * so cleanup here: + */ + if (priv->cec) + i2c_unregister_device(priv->cec); + kfree(priv); + encoder_slave->slave_priv = NULL; + encoder_slave->slave_funcs = NULL; + return -ENXIO; +} + +static struct i2c_device_id tda998x_ids[] = { + { "tda998x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tda998x_ids); + +static struct drm_i2c_encoder_driver tda998x_driver = { + .i2c_driver = { + .probe = tda998x_probe, + .remove = tda998x_remove, + .driver = { + .name = "tda998x", + }, + .id_table = tda998x_ids, + }, + .encoder_init = tda998x_encoder_init, +}; + +/* Module initialization */ + +static int __init +tda998x_init(void) +{ + DBG(""); + return drm_i2c_encoder_register(THIS_MODULE, &tda998x_driver); +} + +static void __exit +tda998x_exit(void) +{ + DBG(""); + drm_i2c_encoder_unregister(&tda998x_driver); +} + +MODULE_AUTHOR("Rob Clark Date: Tue, 22 Jan 2013 16:02:21 -0600 Subject: [PATCH 3077/4607] drm/tilcdc: add encoder slave (v2) Add output panel driver for i2c encoder slaves. v1: original v2: add DT bindings docs, and minor updates for review comments Signed-off-by: Rob Clark Reviewed-by: Daniel Vetter Tested-by: Koen Kooi --- .../devicetree/bindings/drm/tilcdc/slave.txt | 18 + drivers/gpu/drm/tilcdc/Makefile | 1 + drivers/gpu/drm/tilcdc/tilcdc_drv.c | 5 +- drivers/gpu/drm/tilcdc/tilcdc_slave.c | 376 ++++++++++++++++++ drivers/gpu/drm/tilcdc/tilcdc_slave.h | 26 ++ 5 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/drm/tilcdc/slave.txt create mode 100644 drivers/gpu/drm/tilcdc/tilcdc_slave.c create mode 100644 drivers/gpu/drm/tilcdc/tilcdc_slave.h diff --git a/Documentation/devicetree/bindings/drm/tilcdc/slave.txt b/Documentation/devicetree/bindings/drm/tilcdc/slave.txt new file mode 100644 index 000000000000..3d2c52460dca --- /dev/null +++ b/Documentation/devicetree/bindings/drm/tilcdc/slave.txt @@ -0,0 +1,18 @@ +Device-Tree bindings for tilcdc DRM encoder slave output driver + +Required properties: + - compatible: value should be "ti,tilcdc,slave". + - i2c: the phandle for the i2c device the encoder slave is connected to + +Recommended properties: + - pinctrl-names, pinctrl-0: the pincontrol settings to configure + muxing properly for pins that connect to TFP410 device + +Example: + + hdmi { + compatible = "ti,tilcdc,slave"; + i2c = <&i2c0>; + pinctrl-names = "default"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + }; diff --git a/drivers/gpu/drm/tilcdc/Makefile b/drivers/gpu/drm/tilcdc/Makefile index 1359cc29cd81..aa9097edbaf8 100644 --- a/drivers/gpu/drm/tilcdc/Makefile +++ b/drivers/gpu/drm/tilcdc/Makefile @@ -3,6 +3,7 @@ ccflags-y := -Iinclude/drm -Werror tilcdc-y := \ tilcdc_crtc.o \ tilcdc_tfp410.o \ + tilcdc_slave.o \ tilcdc_drv.o obj-$(CONFIG_DRM_TILCDC) += tilcdc.o diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index f6defbfaaffb..25f3add2a754 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -20,6 +20,7 @@ #include "tilcdc_drv.h" #include "tilcdc_regs.h" #include "tilcdc_tfp410.h" +#include "tilcdc_slave.h" #include "drm_fb_helper.h" @@ -587,6 +588,7 @@ static int __init tilcdc_drm_init(void) { DBG("init"); tilcdc_tfp410_init(); + tilcdc_slave_init(); return platform_driver_register(&tilcdc_platform_driver); } @@ -594,10 +596,11 @@ static void __exit tilcdc_drm_fini(void) { DBG("fini"); tilcdc_tfp410_fini(); + tilcdc_slave_fini(); platform_driver_unregister(&tilcdc_platform_driver); } -module_init(tilcdc_drm_init); +late_initcall(tilcdc_drm_init); module_exit(tilcdc_drm_fini); MODULE_AUTHOR("Rob Clark + * + * 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. + * + * 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, see . + */ + +#include +#include +#include +#include +#include + +#include "tilcdc_drv.h" + +struct slave_module { + struct tilcdc_module base; + struct i2c_adapter *i2c; +}; +#define to_slave_module(x) container_of(x, struct slave_module, base) + +static const struct tilcdc_panel_info slave_info = { + .bpp = 16, + .ac_bias = 255, + .ac_bias_intrpt = 0, + .dma_burst_sz = 16, + .fdd = 0x80, + .tft_alt_mode = 0, + .sync_edge = 0, + .sync_ctrl = 1, + .raster_order = 0, +}; + + +/* + * Encoder: + */ + +struct slave_encoder { + struct drm_encoder_slave base; + struct slave_module *mod; +}; +#define to_slave_encoder(x) container_of(to_encoder_slave(x), struct slave_encoder, base) + +static inline struct drm_encoder_slave_funcs * +get_slave_funcs(struct drm_encoder *enc) +{ + return to_encoder_slave(enc)->slave_funcs; +} + +static void slave_encoder_destroy(struct drm_encoder *encoder) +{ + struct slave_encoder *slave_encoder = to_slave_encoder(encoder); + if (get_slave_funcs(encoder)) + get_slave_funcs(encoder)->destroy(encoder); + drm_encoder_cleanup(encoder); + kfree(slave_encoder); +} + +static void slave_encoder_prepare(struct drm_encoder *encoder) +{ + drm_i2c_encoder_prepare(encoder); + tilcdc_crtc_set_panel_info(encoder->crtc, &slave_info); +} + +static const struct drm_encoder_funcs slave_encoder_funcs = { + .destroy = slave_encoder_destroy, +}; + +static const struct drm_encoder_helper_funcs slave_encoder_helper_funcs = { + .dpms = drm_i2c_encoder_dpms, + .mode_fixup = drm_i2c_encoder_mode_fixup, + .prepare = slave_encoder_prepare, + .commit = drm_i2c_encoder_commit, + .mode_set = drm_i2c_encoder_mode_set, + .save = drm_i2c_encoder_save, + .restore = drm_i2c_encoder_restore, +}; + +static const struct i2c_board_info info = { + I2C_BOARD_INFO("tda998x", 0x70) +}; + +static struct drm_encoder *slave_encoder_create(struct drm_device *dev, + struct slave_module *mod) +{ + struct slave_encoder *slave_encoder; + struct drm_encoder *encoder; + int ret; + + slave_encoder = kzalloc(sizeof(*slave_encoder), GFP_KERNEL); + if (!slave_encoder) { + dev_err(dev->dev, "allocation failed\n"); + return NULL; + } + + slave_encoder->mod = mod; + + encoder = &slave_encoder->base.base; + encoder->possible_crtcs = 1; + + ret = drm_encoder_init(dev, encoder, &slave_encoder_funcs, + DRM_MODE_ENCODER_TMDS); + if (ret) + goto fail; + + drm_encoder_helper_add(encoder, &slave_encoder_helper_funcs); + + ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), mod->i2c, &info); + if (ret) + goto fail; + + return encoder; + +fail: + slave_encoder_destroy(encoder); + return NULL; +} + +/* + * Connector: + */ + +struct slave_connector { + struct drm_connector base; + + struct drm_encoder *encoder; /* our connected encoder */ + struct slave_module *mod; +}; +#define to_slave_connector(x) container_of(x, struct slave_connector, base) + +static void slave_connector_destroy(struct drm_connector *connector) +{ + struct slave_connector *slave_connector = to_slave_connector(connector); + drm_connector_cleanup(connector); + kfree(slave_connector); +} + +static enum drm_connector_status slave_connector_detect( + struct drm_connector *connector, + bool force) +{ + struct drm_encoder *encoder = to_slave_connector(connector)->encoder; + return get_slave_funcs(encoder)->detect(encoder, connector); +} + +static int slave_connector_get_modes(struct drm_connector *connector) +{ + struct drm_encoder *encoder = to_slave_connector(connector)->encoder; + return get_slave_funcs(encoder)->get_modes(encoder, connector); +} + +static int slave_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_encoder *encoder = to_slave_connector(connector)->encoder; + struct tilcdc_drm_private *priv = connector->dev->dev_private; + int ret; + + ret = tilcdc_crtc_mode_valid(priv->crtc, mode); + if (ret != MODE_OK) + return ret; + + return get_slave_funcs(encoder)->mode_valid(encoder, mode); +} + +static struct drm_encoder *slave_connector_best_encoder( + struct drm_connector *connector) +{ + struct slave_connector *slave_connector = to_slave_connector(connector); + return slave_connector->encoder; +} + +static int slave_connector_set_property(struct drm_connector *connector, + struct drm_property *property, uint64_t value) +{ + struct drm_encoder *encoder = to_slave_connector(connector)->encoder; + return get_slave_funcs(encoder)->set_property(encoder, + connector, property, value); +} + +static const struct drm_connector_funcs slave_connector_funcs = { + .destroy = slave_connector_destroy, + .dpms = drm_helper_connector_dpms, + .detect = slave_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = slave_connector_set_property, +}; + +static const struct drm_connector_helper_funcs slave_connector_helper_funcs = { + .get_modes = slave_connector_get_modes, + .mode_valid = slave_connector_mode_valid, + .best_encoder = slave_connector_best_encoder, +}; + +static struct drm_connector *slave_connector_create(struct drm_device *dev, + struct slave_module *mod, struct drm_encoder *encoder) +{ + struct slave_connector *slave_connector; + struct drm_connector *connector; + int ret; + + slave_connector = kzalloc(sizeof(*slave_connector), GFP_KERNEL); + if (!slave_connector) { + dev_err(dev->dev, "allocation failed\n"); + return NULL; + } + + slave_connector->encoder = encoder; + slave_connector->mod = mod; + + connector = &slave_connector->base; + + drm_connector_init(dev, connector, &slave_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + drm_connector_helper_add(connector, &slave_connector_helper_funcs); + + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + get_slave_funcs(encoder)->create_resources(encoder, connector); + + ret = drm_mode_connector_attach_encoder(connector, encoder); + if (ret) + goto fail; + + drm_sysfs_connector_add(connector); + + return connector; + +fail: + slave_connector_destroy(connector); + return NULL; +} + +/* + * Module: + */ + +static int slave_modeset_init(struct tilcdc_module *mod, struct drm_device *dev) +{ + struct slave_module *slave_mod = to_slave_module(mod); + struct tilcdc_drm_private *priv = dev->dev_private; + struct drm_encoder *encoder; + struct drm_connector *connector; + + encoder = slave_encoder_create(dev, slave_mod); + if (!encoder) + return -ENOMEM; + + connector = slave_connector_create(dev, slave_mod, encoder); + if (!connector) + return -ENOMEM; + + priv->encoders[priv->num_encoders++] = encoder; + priv->connectors[priv->num_connectors++] = connector; + + return 0; +} + +static void slave_destroy(struct tilcdc_module *mod) +{ + struct slave_module *slave_mod = to_slave_module(mod); + + tilcdc_module_cleanup(mod); + kfree(slave_mod); +} + +static const struct tilcdc_module_ops slave_module_ops = { + .modeset_init = slave_modeset_init, + .destroy = slave_destroy, +}; + +/* + * Device: + */ + +static struct of_device_id slave_of_match[]; + +static int slave_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device_node *i2c_node; + struct slave_module *slave_mod; + struct tilcdc_module *mod; + struct pinctrl *pinctrl; + uint32_t i2c_phandle; + int ret = -EINVAL; + + /* bail out early if no DT data: */ + if (!node) { + dev_err(&pdev->dev, "device-tree data is missing\n"); + return -ENXIO; + } + + slave_mod = kzalloc(sizeof(*slave_mod), GFP_KERNEL); + if (!slave_mod) + return -ENOMEM; + + mod = &slave_mod->base; + + tilcdc_module_init(mod, "slave", &slave_module_ops); + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + dev_warn(&pdev->dev, "pins are not configured\n"); + + if (of_property_read_u32(node, "i2c", &i2c_phandle)) { + dev_err(&pdev->dev, "could not get i2c bus phandle\n"); + goto fail; + } + + i2c_node = of_find_node_by_phandle(i2c_phandle); + if (!i2c_node) { + dev_err(&pdev->dev, "could not get i2c bus node\n"); + goto fail; + } + + slave_mod->i2c = of_find_i2c_adapter_by_node(i2c_node); + if (!slave_mod->i2c) { + dev_err(&pdev->dev, "could not get i2c\n"); + goto fail; + } + + of_node_put(i2c_node); + + return 0; + +fail: + slave_destroy(mod); + return ret; +} + +static int slave_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct of_device_id slave_of_match[] = { + { .compatible = "ti,tilcdc,slave", }, + { }, +}; +MODULE_DEVICE_TABLE(of, slave_of_match); + +struct platform_driver slave_driver = { + .probe = slave_probe, + .remove = slave_remove, + .driver = { + .owner = THIS_MODULE, + .name = "slave", + .of_match_table = slave_of_match, + }, +}; + +int __init tilcdc_slave_init(void) +{ + return platform_driver_register(&slave_driver); +} + +void __exit tilcdc_slave_fini(void) +{ + platform_driver_unregister(&slave_driver); +} diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.h b/drivers/gpu/drm/tilcdc/tilcdc_slave.h new file mode 100644 index 000000000000..2f8504848320 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * 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, see . + */ + +#ifndef __TILCDC_SLAVE_H__ +#define __TILCDC_SLAVE_H__ + +/* sub-module for i2c slave encoder output */ + +int tilcdc_slave_init(void); +void tilcdc_slave_fini(void); + +#endif /* __TILCDC_SLAVE_H__ */ From 0d4bbaf9f3e5b9f52150ddc5a4ee8b0ab83a440b Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 18 Dec 2012 17:34:16 -0600 Subject: [PATCH 3078/4607] drm/tilcdc: add support for LCD panels (v5) Add an output panel driver for LCD panels. Tested with LCD3 cape on beaglebone. v1: original v2: s/of_find_node_by_name()/of_get_child_by_name()/ from Pantelis Antoniou v3: add backlight support v4: rebase to latest of video timing helpers v5: remove some unneeded fields from panel-info struct, add DT bindings docs Signed-off-by: Rob Clark Tested-by: Koen Kooi --- .../devicetree/bindings/drm/tilcdc/panel.txt | 59 +++ drivers/gpu/drm/tilcdc/Kconfig | 3 + drivers/gpu/drm/tilcdc/Makefile | 1 + drivers/gpu/drm/tilcdc/tilcdc_drv.c | 3 + drivers/gpu/drm/tilcdc/tilcdc_panel.c | 436 ++++++++++++++++++ drivers/gpu/drm/tilcdc/tilcdc_panel.h | 26 ++ 6 files changed, 528 insertions(+) create mode 100644 Documentation/devicetree/bindings/drm/tilcdc/panel.txt create mode 100644 drivers/gpu/drm/tilcdc/tilcdc_panel.c create mode 100644 drivers/gpu/drm/tilcdc/tilcdc_panel.h diff --git a/Documentation/devicetree/bindings/drm/tilcdc/panel.txt b/Documentation/devicetree/bindings/drm/tilcdc/panel.txt new file mode 100644 index 000000000000..9301c330d1a6 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/tilcdc/panel.txt @@ -0,0 +1,59 @@ +Device-Tree bindings for tilcdc DRM generic panel output driver + +Required properties: + - compatible: value should be "ti,tilcdc,panel". + - panel-info: configuration info to configure LCDC correctly for the panel + - ac-bias: AC Bias Pin Frequency + - ac-bias-intrpt: AC Bias Pin Transitions per Interrupt + - dma-burst-sz: DMA burst size + - bpp: Bits per pixel + - fdd: FIFO DMA Request Delay + - sync-edge: Horizontal and Vertical Sync Edge: 0=rising 1=falling + - sync-ctrl: Horizontal and Vertical Sync: Control: 0=ignore + - raster-order: Raster Data Order Select: 1=Most-to-least 0=Least-to-most + - fifo-th: DMA FIFO threshold + - display-timings: typical videomode of lcd panel. Multiple video modes + can be listed if the panel supports multiple timings, but the 'native-mode' + should be the preferred/default resolution. Refer to + Documentation/devicetree/bindings/video/display-timing.txt for display + timing binding details. + +Recommended properties: + - pinctrl-names, pinctrl-0: the pincontrol settings to configure + muxing properly for pins that connect to TFP410 device + +Example: + + /* Settings for CDTech_S035Q01 / LCD3 cape: */ + lcd3 { + compatible = "ti,tilcdc,panel"; + pinctrl-names = "default"; + pinctrl-0 = <&bone_lcd3_cape_lcd_pins>; + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <16>; + fdd = <0x80>; + sync-edge = <0>; + sync-ctrl = <1>; + raster-order = <0>; + fifo-th = <0>; + }; + display-timings { + native-mode = <&timing0>; + timing0: 320x240 { + hactive = <320>; + vactive = <240>; + hback-porch = <21>; + hfront-porch = <58>; + hsync-len = <47>; + vback-porch = <11>; + vfront-porch = <23>; + vsync-len = <2>; + clock-frequency = <8000000>; + hsync-active = <0>; + vsync-active = <0>; + }; + }; + }; diff --git a/drivers/gpu/drm/tilcdc/Kconfig b/drivers/gpu/drm/tilcdc/Kconfig index ee9b592d59eb..ae14fd6ea924 100644 --- a/drivers/gpu/drm/tilcdc/Kconfig +++ b/drivers/gpu/drm/tilcdc/Kconfig @@ -4,6 +4,9 @@ config DRM_TILCDC select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER select DRM_GEM_CMA_HELPER + select OF_VIDEOMODE + select OF_DISPLAY_TIMING + select BACKLIGHT_CLASS_DEVICE help Choose this option if you have an TI SoC with LCDC display controller, for example AM33xx in beagle-bone, DA8xx, or diff --git a/drivers/gpu/drm/tilcdc/Makefile b/drivers/gpu/drm/tilcdc/Makefile index aa9097edbaf8..deda656b10e7 100644 --- a/drivers/gpu/drm/tilcdc/Makefile +++ b/drivers/gpu/drm/tilcdc/Makefile @@ -4,6 +4,7 @@ tilcdc-y := \ tilcdc_crtc.o \ tilcdc_tfp410.o \ tilcdc_slave.o \ + tilcdc_panel.o \ tilcdc_drv.o obj-$(CONFIG_DRM_TILCDC) += tilcdc.o diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 25f3add2a754..c5b592dc1970 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -21,6 +21,7 @@ #include "tilcdc_regs.h" #include "tilcdc_tfp410.h" #include "tilcdc_slave.h" +#include "tilcdc_panel.h" #include "drm_fb_helper.h" @@ -589,6 +590,7 @@ static int __init tilcdc_drm_init(void) DBG("init"); tilcdc_tfp410_init(); tilcdc_slave_init(); + tilcdc_panel_init(); return platform_driver_register(&tilcdc_platform_driver); } @@ -597,6 +599,7 @@ static void __exit tilcdc_drm_fini(void) DBG("fini"); tilcdc_tfp410_fini(); tilcdc_slave_fini(); + tilcdc_panel_fini(); platform_driver_unregister(&tilcdc_platform_driver); } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c new file mode 100644 index 000000000000..580b74e2022b --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2012 Texas Instruments + * Author: Rob Clark + * + * 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. + * + * 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, see . + */ + +#include +#include +#include +#include